fix(dashboard): payment providers select (#13592)
**What** - use lazy loading for payment providers select on region create/edit
This commit is contained in:
1 parent
9538df2eaf
commit
c3ae529b40
7 files changed
+62
-38
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/dashboard": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(dashboard): payment providers select
|
||||||
@@ -58,6 +58,7 @@ interface ComboboxProps<T extends Value = Value>
|
|||||||
onCreateOption?: (value: string) => void
|
onCreateOption?: (value: string) => void
|
||||||
noResultsPlaceholder?: ReactNode
|
noResultsPlaceholder?: ReactNode
|
||||||
allowClear?: boolean
|
allowClear?: boolean
|
||||||
|
forceHideInput?: boolean // always hide input -> used for singe value select that don't have query/filter
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComboboxImpl = <T extends Value = string>(
|
const ComboboxImpl = <T extends Value = string>(
|
||||||
@@ -74,6 +75,7 @@ const ComboboxImpl = <T extends Value = string>(
|
|||||||
onCreateOption,
|
onCreateOption,
|
||||||
noResultsPlaceholder,
|
noResultsPlaceholder,
|
||||||
allowClear,
|
allowClear,
|
||||||
|
forceHideInput,
|
||||||
...inputProps
|
...inputProps
|
||||||
}: ComboboxProps<T>,
|
}: ComboboxProps<T>,
|
||||||
ref: ForwardedRef<HTMLInputElement>
|
ref: ForwardedRef<HTMLInputElement>
|
||||||
@@ -152,10 +154,15 @@ const ComboboxImpl = <T extends Value = string>(
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do not use `matcher` if the input is hidden
|
||||||
|
if (forceHideInput) {
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
return matchSorter(options, defferedSearchValue, {
|
return matchSorter(options, defferedSearchValue, {
|
||||||
keys: ["label"],
|
keys: ["label"],
|
||||||
})
|
})
|
||||||
}, [options, defferedSearchValue, isSearchControlled])
|
}, [options, defferedSearchValue, isSearchControlled, forceHideInput])
|
||||||
|
|
||||||
const observer = useRef(
|
const observer = useRef(
|
||||||
new IntersectionObserver(
|
new IntersectionObserver(
|
||||||
@@ -197,7 +204,7 @@ const ComboboxImpl = <T extends Value = string>(
|
|||||||
const showTag = hasValue && isArrayValue
|
const showTag = hasValue && isArrayValue
|
||||||
const showSelected = showTag && !searchValue && !open
|
const showSelected = showTag && !searchValue && !open
|
||||||
|
|
||||||
const hideInput = !isArrayValue && hasValue && !open
|
const hideInput = forceHideInput || (!isArrayValue && hasValue && !open)
|
||||||
const selectedLabel = options.find((o) => o.value === selectedValues)?.label
|
const selectedLabel = options.find((o) => o.value === selectedValues)?.label
|
||||||
|
|
||||||
const hidePlaceholder = showSelected || open
|
const hidePlaceholder = showSelected || open
|
||||||
@@ -251,7 +258,7 @@ const ComboboxImpl = <T extends Value = string>(
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
handleValueChange(isArrayValue ? ([] as unknown as T) : undefined)
|
handleValueChange(isArrayValue ? ([] as unknown as T) : undefined)
|
||||||
}}
|
}}
|
||||||
className="bg-ui-bg-base hover:bg-ui-bg-base-hover txt-compact-small-plus text-ui-fg-subtle focus-within:border-ui-fg-interactive transition-fg absolute start-0.5 top-0.5 z-[1] flex h-[28px] items-center rounded-[4px] border py-[3px] ps-1.5 pe-1 outline-none"
|
className="bg-ui-bg-base hover:bg-ui-bg-base-hover txt-compact-small-plus text-ui-fg-subtle focus-within:border-ui-fg-interactive transition-fg absolute start-0.5 top-0.5 z-[1] flex h-[28px] items-center rounded-[4px] border py-[3px] pe-1 ps-1.5 outline-none"
|
||||||
>
|
>
|
||||||
<span className="tabular-nums">{selectedValues.length}</span>
|
<span className="tabular-nums">{selectedValues.length}</span>
|
||||||
<XMarkMini className="text-ui-fg-muted" />
|
<XMarkMini className="text-ui-fg-muted" />
|
||||||
@@ -293,7 +300,7 @@ const ComboboxImpl = <T extends Value = string>(
|
|||||||
ref={comboboxRef}
|
ref={comboboxRef}
|
||||||
onFocus={() => setOpen(true)}
|
onFocus={() => setOpen(true)}
|
||||||
className={clx(
|
className={clx(
|
||||||
"txt-compact-small text-ui-fg-base !placeholder:text-ui-fg-muted transition-fg size-full cursor-pointer bg-transparent ps-2 pe-8 outline-none focus:cursor-text",
|
"txt-compact-small text-ui-fg-base !placeholder:text-ui-fg-muted transition-fg size-full cursor-pointer bg-transparent pe-8 ps-2 outline-none focus:cursor-text",
|
||||||
"hover:bg-ui-bg-field-hover",
|
"hover:bg-ui-bg-field-hover",
|
||||||
{
|
{
|
||||||
"opacity-0": hideInput,
|
"opacity-0": hideInput,
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ import { FetchError } from "@medusajs/js-sdk"
|
|||||||
const PAYMENT_QUERY_KEY = "payment" as const
|
const PAYMENT_QUERY_KEY = "payment" as const
|
||||||
export const paymentQueryKeys = queryKeysFactory(PAYMENT_QUERY_KEY)
|
export const paymentQueryKeys = queryKeysFactory(PAYMENT_QUERY_KEY)
|
||||||
|
|
||||||
|
const PAYMENT_PROVIDERS_QUERY_KEY = "payment_providers" as const
|
||||||
|
export const paymentProvidersQueryKeys = queryKeysFactory(
|
||||||
|
PAYMENT_PROVIDERS_QUERY_KEY
|
||||||
|
)
|
||||||
|
|
||||||
export const usePaymentProviders = (
|
export const usePaymentProviders = (
|
||||||
query?: HttpTypes.AdminGetPaymentProvidersParams,
|
query?: HttpTypes.AdminGetPaymentProvidersParams,
|
||||||
options?: Omit<
|
options?: Omit<
|
||||||
@@ -29,7 +34,7 @@ export const usePaymentProviders = (
|
|||||||
) => {
|
) => {
|
||||||
const { data, ...rest } = useQuery({
|
const { data, ...rest } = useQuery({
|
||||||
queryFn: async () => sdk.admin.payment.listPaymentProviders(query),
|
queryFn: async () => sdk.admin.payment.listPaymentProviders(query),
|
||||||
queryKey: [],
|
queryKey: paymentProvidersQueryKeys.list(query),
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+18
-10
@@ -17,7 +17,7 @@ import { useForm, useWatch } from "react-hook-form"
|
|||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
|
|
||||||
import { PaymentProviderDTO, RegionCountryDTO } from "@medusajs/types"
|
import { RegionCountryDTO } from "@medusajs/types"
|
||||||
|
|
||||||
import { Form } from "../../../../../components/common/form"
|
import { Form } from "../../../../../components/common/form"
|
||||||
import { Combobox } from "../../../../../components/inputs/combobox"
|
import { Combobox } from "../../../../../components/inputs/combobox"
|
||||||
@@ -38,10 +38,11 @@ import { useCountries } from "../../../common/hooks/use-countries"
|
|||||||
import { useCountryTableColumns } from "../../../common/hooks/use-country-table-columns"
|
import { useCountryTableColumns } from "../../../common/hooks/use-country-table-columns"
|
||||||
import { useCountryTableQuery } from "../../../common/hooks/use-country-table-query"
|
import { useCountryTableQuery } from "../../../common/hooks/use-country-table-query"
|
||||||
import { useDocumentDirection } from "../../../../../hooks/use-document-direction"
|
import { useDocumentDirection } from "../../../../../hooks/use-document-direction"
|
||||||
|
import { useComboboxData } from "../../../../../hooks/use-combobox-data"
|
||||||
|
import { sdk } from "../../../../../lib/client"
|
||||||
|
|
||||||
type CreateRegionFormProps = {
|
type CreateRegionFormProps = {
|
||||||
currencies: CurrencyInfo[]
|
currencies: CurrencyInfo[]
|
||||||
paymentProviders: PaymentProviderDTO[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateRegionSchema = zod.object({
|
const CreateRegionSchema = zod.object({
|
||||||
@@ -58,10 +59,7 @@ const PAGE_SIZE = 50
|
|||||||
|
|
||||||
const STACKED_MODAL_ID = "countries-modal"
|
const STACKED_MODAL_ID = "countries-modal"
|
||||||
|
|
||||||
export const CreateRegionForm = ({
|
export const CreateRegionForm = ({ currencies }: CreateRegionFormProps) => {
|
||||||
currencies,
|
|
||||||
paymentProviders,
|
|
||||||
}: CreateRegionFormProps) => {
|
|
||||||
const { setIsOpen } = useStackedModal()
|
const { setIsOpen } = useStackedModal()
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||||
const { handleSuccess } = useRouteModal()
|
const { handleSuccess } = useRouteModal()
|
||||||
@@ -181,6 +179,17 @@ export const CreateRegionForm = ({
|
|||||||
setRowSelection({})
|
setRowSelection({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const comboboxProviders = useComboboxData({
|
||||||
|
queryFn: (params) =>
|
||||||
|
sdk.admin.payment.listPaymentProviders({ ...params, is_enabled: true }),
|
||||||
|
queryKey: ["payment_providers"],
|
||||||
|
getOptions: (data) =>
|
||||||
|
data.payment_providers.map((pp) => ({
|
||||||
|
label: formatProvider(pp.id),
|
||||||
|
value: pp.id,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RouteFocusModal.Form form={form}>
|
<RouteFocusModal.Form form={form}>
|
||||||
<KeyboundForm
|
<KeyboundForm
|
||||||
@@ -415,10 +424,9 @@ export const CreateRegionForm = ({
|
|||||||
</Form.Label>
|
</Form.Label>
|
||||||
<Form.Control>
|
<Form.Control>
|
||||||
<Combobox
|
<Combobox
|
||||||
options={paymentProviders.map((pp) => ({
|
forceHideInput
|
||||||
label: formatProvider(pp.id),
|
options={comboboxProviders.options}
|
||||||
value: pp.id,
|
fetchNextPage={comboboxProviders.fetchNextPage}
|
||||||
}))}
|
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { RouteFocusModal } from "../../../components/modals/route-focus-modal"
|
import { RouteFocusModal } from "../../../components/modals/route-focus-modal"
|
||||||
import { usePaymentProviders } from "../../../hooks/api/payments"
|
|
||||||
import { useStore } from "../../../hooks/api/store"
|
import { useStore } from "../../../hooks/api/store"
|
||||||
import { currencies } from "../../../lib/data/currencies"
|
import { currencies } from "../../../lib/data/currencies"
|
||||||
import { CreateRegionForm } from "./components/create-region-form"
|
import { CreateRegionForm } from "./components/create-region-form"
|
||||||
@@ -10,9 +9,6 @@ export const RegionCreate = () => {
|
|||||||
const storeCurrencies = (store?.supported_currencies ?? []).map(
|
const storeCurrencies = (store?.supported_currencies ?? []).map(
|
||||||
(c) => currencies[c.currency_code.toUpperCase()]
|
(c) => currencies[c.currency_code.toUpperCase()]
|
||||||
)
|
)
|
||||||
const { payment_providers: paymentProviders = [] } = usePaymentProviders({
|
|
||||||
is_enabled: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
throw error
|
throw error
|
||||||
@@ -20,12 +16,7 @@ export const RegionCreate = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RouteFocusModal>
|
<RouteFocusModal>
|
||||||
{!isLoading && store && (
|
{!isLoading && store && <CreateRegionForm currencies={storeCurrencies} />}
|
||||||
<CreateRegionForm
|
|
||||||
currencies={storeCurrencies}
|
|
||||||
paymentProviders={paymentProviders}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</RouteFocusModal>
|
</RouteFocusModal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+21
-8
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next"
|
|||||||
import * as zod from "zod"
|
import * as zod from "zod"
|
||||||
|
|
||||||
import { Form } from "../../../../../components/common/form/index.ts"
|
import { Form } from "../../../../../components/common/form/index.ts"
|
||||||
import { Combobox } from "../../../../../components/inputs/combobox/index.ts"
|
import { Combobox } from "../../../../../components/inputs/combobox"
|
||||||
import {
|
import {
|
||||||
RouteDrawer,
|
RouteDrawer,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
@@ -15,11 +15,12 @@ import { useUpdateRegion } from "../../../../../hooks/api/regions.tsx"
|
|||||||
import { CurrencyInfo } from "../../../../../lib/data/currencies.ts"
|
import { CurrencyInfo } from "../../../../../lib/data/currencies.ts"
|
||||||
import { formatProvider } from "../../../../../lib/format-provider.ts"
|
import { formatProvider } from "../../../../../lib/format-provider.ts"
|
||||||
import { useDocumentDirection } from "../../../../../hooks/use-document-direction"
|
import { useDocumentDirection } from "../../../../../hooks/use-document-direction"
|
||||||
|
import { useComboboxData } from "../../../../../hooks/use-combobox-data.tsx"
|
||||||
|
import { sdk } from "../../../../../lib/client/index.ts"
|
||||||
|
|
||||||
type EditRegionFormProps = {
|
type EditRegionFormProps = {
|
||||||
region: HttpTypes.AdminRegion
|
region: HttpTypes.AdminRegion
|
||||||
currencies: CurrencyInfo[]
|
currencies: CurrencyInfo[]
|
||||||
paymentProviders: PaymentProviderDTO[]
|
|
||||||
pricePreferences: HttpTypes.AdminPricePreference[]
|
pricePreferences: HttpTypes.AdminPricePreference[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +35,6 @@ const EditRegionSchema = zod.object({
|
|||||||
export const EditRegionForm = ({
|
export const EditRegionForm = ({
|
||||||
region,
|
region,
|
||||||
currencies,
|
currencies,
|
||||||
paymentProviders,
|
|
||||||
pricePreferences,
|
pricePreferences,
|
||||||
}: EditRegionFormProps) => {
|
}: EditRegionFormProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -54,6 +54,17 @@ export const EditRegionForm = ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const comboboxProviders = useComboboxData({
|
||||||
|
queryKey: ["payment_providers"],
|
||||||
|
queryFn: (params) =>
|
||||||
|
sdk.admin.payment.listPaymentProviders({ ...params, is_enabled: true }),
|
||||||
|
getOptions: (data) =>
|
||||||
|
data.payment_providers.map((pp) => ({
|
||||||
|
label: formatProvider(pp.id),
|
||||||
|
value: pp.id,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
|
||||||
const { mutateAsync: updateRegion, isPending: isPendingRegion } =
|
const { mutateAsync: updateRegion, isPending: isPendingRegion } =
|
||||||
useUpdateRegion(region.id)
|
useUpdateRegion(region.id)
|
||||||
|
|
||||||
@@ -80,7 +91,10 @@ export const EditRegionForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RouteDrawer.Form form={form}>
|
<RouteDrawer.Form form={form}>
|
||||||
<KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col overflow-hidden">
|
<KeyboundForm
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="flex flex-1 flex-col overflow-hidden"
|
||||||
|
>
|
||||||
<RouteDrawer.Body className="overflow-y-auto">
|
<RouteDrawer.Body className="overflow-y-auto">
|
||||||
<div className="flex flex-col gap-y-8">
|
<div className="flex flex-col gap-y-8">
|
||||||
<div className="flex flex-col gap-y-4">
|
<div className="flex flex-col gap-y-4">
|
||||||
@@ -205,10 +219,9 @@ export const EditRegionForm = ({
|
|||||||
<Form.Label>{t("fields.paymentProviders")}</Form.Label>
|
<Form.Label>{t("fields.paymentProviders")}</Form.Label>
|
||||||
<Form.Control>
|
<Form.Control>
|
||||||
<Combobox
|
<Combobox
|
||||||
options={paymentProviders.map((pp) => ({
|
forceHideInput
|
||||||
label: formatProvider(pp.id),
|
options={comboboxProviders.options}
|
||||||
value: pp.id,
|
fetchNextPage={comboboxProviders.fetchNextPage}
|
||||||
}))}
|
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
|
|||||||
@@ -48,10 +48,6 @@ export const RegionEdit = () => {
|
|||||||
const storeCurrencies = (store?.supported_currencies ?? []).map(
|
const storeCurrencies = (store?.supported_currencies ?? []).map(
|
||||||
(c) => currencies[c.currency_code.toUpperCase()]
|
(c) => currencies[c.currency_code.toUpperCase()]
|
||||||
)
|
)
|
||||||
const { payment_providers: paymentProviders = [] } = usePaymentProviders({
|
|
||||||
limit: 999,
|
|
||||||
is_enabled: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (isRegionError) {
|
if (isRegionError) {
|
||||||
throw regionError
|
throw regionError
|
||||||
@@ -74,7 +70,6 @@ export const RegionEdit = () => {
|
|||||||
<EditRegionForm
|
<EditRegionForm
|
||||||
region={region}
|
region={region}
|
||||||
currencies={storeCurrencies}
|
currencies={storeCurrencies}
|
||||||
paymentProviders={paymentProviders}
|
|
||||||
pricePreferences={pricePreferences}
|
pricePreferences={pricePreferences}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in new issue
Block a user