import { zodResolver } from "@hookform/resolvers/zod" import { XMarkMini } from "@medusajs/icons" import { Button, Checkbox, Heading, Input, Select, Switch, Text, clx, toast, } from "@medusajs/ui" import { RowSelectionState, createColumnHelper } from "@tanstack/react-table" import { useMemo, useState } from "react" import { useForm, useWatch } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" import { Form } from "../../../../../components/common/form" import { Combobox } from "../../../../../components/inputs/combobox" import { RouteFocusModal, StackedFocusModal, useRouteModal, useStackedModal, } from "../../../../../components/modals" import { _DataTable } from "../../../../../components/table/data-table" import { KeyboundForm } from "../../../../../components/utilities/keybound-form" import { useCreateRegion } from "../../../../../hooks/api/regions" import { useDataTable } from "../../../../../hooks/use-data-table" import { countries as staticCountries, StaticCountry, } from "../../../../../lib/data/countries" import { CurrencyInfo } from "../../../../../lib/data/currencies" import { formatProvider } from "../../../../../lib/format-provider" import { useCountries } from "../../../common/hooks/use-countries" import { useCountryTableColumns } from "../../../common/hooks/use-country-table-columns" import { useCountryTableQuery } from "../../../common/hooks/use-country-table-query" import { useDocumentDirection } from "../../../../../hooks/use-document-direction" import { useComboboxData } from "../../../../../hooks/use-combobox-data" import { sdk } from "../../../../../lib/client" type CreateRegionFormProps = { currencies: CurrencyInfo[] } const CreateRegionSchema = zod.object({ name: zod.string().min(1), currency_code: zod.string().min(2, "Select a currency"), automatic_taxes: zod.boolean(), is_tax_inclusive: zod.boolean(), countries: zod.array(zod.object({ code: zod.string(), name: zod.string() })), payment_providers: zod.array(zod.string()).min(1), }) const PREFIX = "cr" const PAGE_SIZE = 50 const STACKED_MODAL_ID = "countries-modal" export const CreateRegionForm = ({ currencies }: CreateRegionFormProps) => { const { setIsOpen } = useStackedModal() const [rowSelection, setRowSelection] = useState({}) const { handleSuccess } = useRouteModal() const direction = useDocumentDirection() const form = useForm>({ defaultValues: { name: "", currency_code: "", automatic_taxes: true, is_tax_inclusive: false, countries: [], payment_providers: [], }, resolver: zodResolver(CreateRegionSchema), }) const selectedCountries = useWatch({ control: form.control, name: "countries", defaultValue: [], }) const { t } = useTranslation() const { mutateAsync: createRegion, isPending: isPendingRegion } = useCreateRegion() const handleSubmit = form.handleSubmit(async (values) => { await createRegion( { name: values.name, countries: values.countries.map((c) => c.code), currency_code: values.currency_code, payment_providers: values.payment_providers, automatic_taxes: values.automatic_taxes, is_tax_inclusive: values.is_tax_inclusive, }, { onSuccess: ({ region }) => { toast.success(t("regions.toast.create")) handleSuccess(`../${region.id}`) }, onError: (e) => { toast.error(e.message) }, } ) }) const { searchParams, raw } = useCountryTableQuery({ pageSize: PAGE_SIZE, prefix: PREFIX, }) const { countries, count } = useCountries({ countries: staticCountries.map((c, i) => ({ display_name: c.display_name, name: c.name, id: i, iso_2: c.iso_2, iso_3: c.iso_3, num_code: c.num_code, region_id: null, region: {} as any, })), ...searchParams, }) const columns = useColumns() const { table } = useDataTable({ data: countries || [], columns, count, enablePagination: true, enableRowSelection: true, rowSelection: { state: rowSelection, updater: setRowSelection, }, getRowId: (row) => row.iso_2, pageSize: PAGE_SIZE, prefix: PREFIX, }) const saveCountries = () => { const selected = Object.keys(rowSelection).filter( (key) => rowSelection[key] ) form.setValue( "countries", selected.map((key) => ({ code: key, name: staticCountries.find((c) => c.iso_2 === key)!.display_name, })), { shouldDirty: true, shouldTouch: true } ) setIsOpen(STACKED_MODAL_ID, false) } const removeCountry = (code: string) => { const update = selectedCountries.filter((c) => c.code !== code) const ids = update .map((c) => c.code) .reduce((acc, c) => { acc[c] = true return acc }, {} as RowSelectionState) form.setValue("countries", update, { shouldDirty: true, shouldTouch: true }) setRowSelection(ids) } const clearCountries = () => { form.setValue("countries", [], { shouldDirty: true, shouldTouch: true }) 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 (
{t("regions.createRegion")} {t("regions.createRegionHint")}
{ return ( {t("fields.name")} ) }} /> { return ( {t("fields.currency")} ) }} />
{ return (
{t("fields.automaticTaxes")}
{t("regions.automaticTaxesHint")}
) }} /> { return (
{t("fields.taxInclusivePricing")}
{t("regions.taxInclusiveHint")}
) }} />
{t("fields.countries")} {t("regions.countriesHint")}
{selectedCountries.length > 0 && (
{selectedCountries.map((country) => ( ))}
)}
{t("regions.addCountries")} <_DataTable table={table} columns={columns} count={count} pageSize={PAGE_SIZE} orderBy={[ { key: "name", label: t("fields.name") }, { key: "iso_2", label: t("fields.code") }, ]} pagination search="autofocus" layout="fill" queryObject={raw} prefix={PREFIX} />
{t("fields.providers")} {t("regions.providersHint")}
{ return ( {t("fields.paymentProviders")} ) }} />
) } const columnHelper = createColumnHelper() const useColumns = () => { const base = useCountryTableColumns() return useMemo( () => [ columnHelper.display({ id: "select", header: ({ table }) => { return ( table.toggleAllPageRowsSelected(!!value) } /> ) }, cell: ({ row }) => { const isPreselected = !row.getCanSelect() return ( row.toggleSelected(!!value)} onClick={(e) => { e.stopPropagation() }} /> ) }, }), ...base, ], [base] ) } const CountryTag = ({ country, onRemove, }: { country: { code: string; name: string } onRemove: (code: string) => void }) => { return (
{country.name}
) }