From 72a17d6cd729ee6d5013312893f00acc6f03cdf1 Mon Sep 17 00:00:00 2001 From: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:03:40 +0100 Subject: [PATCH] feat(dashboard): Rework route modals (#6459) **What** - Reworks how RouteModals are setup. **Why** - With the current implementation it was possible to create a race-condition in the logic that handled displaying a prompt if the user tried to close a modal, while a child form was dirty. The race condition would cause a new prompt to spawn each time the user clicked the screen, making it impossible to dismiss the prompt. This only occurred in a few specific cases. **How** - Creates two new components: RouteFocusModal and RouteDrawer. The component shares logic for handling their own open/closed state, and now accept a form prop, that allows the modals to keep track of whether their child form is dirty. This change ensures that race conditions cannot occur, and that the prompt always only renders once when needed. --- .../public/locales/en-US/translation.json | 9 +- .../src/components/route-modal/index.ts | 3 + .../route-modal/route-drawer/index.ts | 1 + .../route-modal/route-drawer/route-drawer.tsx | 59 ++++ .../route-modal/route-focus-modal/index.ts | 1 + .../route-focus-modal/route-focus-modal.tsx | 58 ++++ .../route-modal/route-form/index.ts | 1 + .../route-modal/route-form/route-form.tsx | 63 ++++ .../route-modal/route-modal-provider/index.ts | 1 + .../route-modal-provider/route-provider.tsx | 41 +++ .../region/countries-cell/countries-cell.tsx | 56 ++++ .../region/countries-cell/index.ts | 1 + .../fulfillment-providers-cell.tsx | 58 ++++ .../fulfillment-providers-cell/index.ts | 1 + .../region/payment-providers-cell/index.ts | 1 + .../payment-providers-cell.tsx | 58 ++++ .../table-cells/region/region-cell/index.ts | 1 + .../region/region-cell/region-cell.tsx | 23 ++ .../columns/use-region-table-columns.tsx | 50 +++ .../filters/use-region-table-filters.tsx | 19 ++ .../table/query/use-region-table-query.tsx | 33 ++ .../src/hooks/use-route-modal-state.tsx | 57 ---- .../api-key-management-add-sales-channels.tsx | 28 +- .../add-sales-channels-to-api-key-form.tsx | 47 ++- .../api-key-management-create.tsx | 13 +- .../create-publishable-api-key-form.tsx | 52 ++-- .../api-key-management-edit.tsx | 31 +- .../edit-api-key-form/edit-api-key-form.tsx | 46 ++- .../collection-add-products.tsx | 26 +- .../add-products-to-collection-form.tsx | 56 ++-- .../collection-create/collection-create.tsx | 13 +- .../create-collection-form.tsx | 45 ++- .../collection-edit/collection-add-edit.tsx | 32 +- .../edit-collection-form.tsx | 43 ++- .../add-customers-form/add-customers-form.tsx | 57 ++-- .../customer-group-add-customers.tsx | 14 +- .../create-customer-group-form.tsx | 45 ++- .../customer-group-create.tsx | 13 +- .../edit-customer-group-form.tsx | 38 +-- .../customer-group-edit.tsx | 32 +- .../create-customer-form.tsx | 42 ++- .../customer-create/customer-create.tsx | 13 +- .../edit-customer-form/edit-customer-form.tsx | 43 ++- .../customers/customer-edit/customer-edit.tsx | 29 +- .../create-gift-card-form.tsx | 46 ++- .../gift-card-create/gift-card-create.tsx | 13 +- .../edit-gift-card-form.tsx | 47 ++- .../gift-card-edit/gift-card-edit.tsx | 29 +- .../location-add-sales-channels.tsx | 11 +- .../create-location-form.tsx | 23 +- .../location-create/location-create.tsx | 13 +- .../edit-location-form/edit-location-form.tsx | 24 +- .../locations/location-edit/location-edit.tsx | 33 +- .../product-attributes-form.tsx | 41 ++- .../product-attributes/product-attributes.tsx | 30 +- .../create-product-form.tsx | 44 ++- .../product-create/product-create.tsx | 13 +- .../edit-product-form/edit-product-form.tsx | 56 ++-- .../products/product-edit/product-edit.tsx | 30 +- .../product-gallery/product-gallery.tsx | 12 +- .../edit-product-options-form.tsx | 20 +- .../product-options/product-options.tsx | 29 +- .../edit-sales-channels-form.tsx | 110 ++++--- .../product-sales-channels.tsx | 23 +- .../edit-profile-form/edit-profile-form.tsx | 52 ++-- .../profile/profile-edit/profile-edit.tsx | 46 +-- .../create-region-form/create-region-form.tsx | 60 ++-- .../regions/region-create/region-create.tsx | 14 +- .../region-shipping-option-section/index.ts | 1 + .../region-shipping-option-section.tsx | 173 +++-------- .../use-shipping-option-table-columns.tsx | 116 +++++++ .../use-shipping-option-table-filters.tsx | 39 +++ .../use-shipping-option-table-query.tsx | 48 +++ .../region-list-table/region-list-table.tsx | 255 +++------------- .../add-products-to-sales-channel-form.tsx | 58 ++-- .../sales-channel-add-products.tsx | 27 +- .../create-sales-channel-form.tsx | 56 ++-- .../sales-channel-create.tsx | 13 +- .../edit-sales-channel-form.tsx | 42 ++- .../sales-channel-edit/sales-channel-edit.tsx | 36 +-- .../add-currencies-form.tsx | 284 +++++++++--------- .../store-add-currencies.tsx | 33 +- .../edit-store-form/edit-store-form.tsx | 32 +- .../routes/store/store-edit/store-edit.tsx | 41 +-- .../edit-user-form/edit-user-form.tsx | 47 ++- .../src/routes/users/user-edit/user-edit.tsx | 30 +- .../invite-user-form/invite-user-form.tsx | 33 +- .../routes/users/user-invite/user-invite.tsx | 13 +- 88 files changed, 1750 insertions(+), 1799 deletions(-) create mode 100644 packages/admin-next/dashboard/src/components/route-modal/index.ts create mode 100644 packages/admin-next/dashboard/src/components/route-modal/route-drawer/index.ts create mode 100644 packages/admin-next/dashboard/src/components/route-modal/route-drawer/route-drawer.tsx create mode 100644 packages/admin-next/dashboard/src/components/route-modal/route-focus-modal/index.ts create mode 100644 packages/admin-next/dashboard/src/components/route-modal/route-focus-modal/route-focus-modal.tsx create mode 100644 packages/admin-next/dashboard/src/components/route-modal/route-form/index.ts create mode 100644 packages/admin-next/dashboard/src/components/route-modal/route-form/route-form.tsx create mode 100644 packages/admin-next/dashboard/src/components/route-modal/route-modal-provider/index.ts create mode 100644 packages/admin-next/dashboard/src/components/route-modal/route-modal-provider/route-provider.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/region/countries-cell/countries-cell.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/region/countries-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/region/fulfillment-providers-cell/fulfillment-providers-cell.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/region/fulfillment-providers-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/region/payment-providers-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/region/payment-providers-cell/payment-providers-cell.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/region/region-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/region/region-cell/region-cell.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/table/columns/use-region-table-columns.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/table/filters/use-region-table-filters.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/table/query/use-region-table-query.tsx delete mode 100644 packages/admin-next/dashboard/src/hooks/use-route-modal-state.tsx create mode 100644 packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/index.ts create mode 100644 packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-columns.tsx create mode 100644 packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-filters.tsx create mode 100644 packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-query.tsx diff --git a/packages/admin-next/dashboard/public/locales/en-US/translation.json b/packages/admin-next/dashboard/public/locales/en-US/translation.json index cb690c2cbb..02892e6db7 100644 --- a/packages/admin-next/dashboard/public/locales/en-US/translation.json +++ b/packages/admin-next/dashboard/public/locales/en-US/translation.json @@ -83,6 +83,7 @@ "gallery": "Gallery", "titleHint": "Give your product a short and clear title.<0/>50-60 characters is the recommended length for search engines.", "descriptionHint": "Give your product a short and clear description.<0/>120-160 characters is the recommended length for search engines.", + "discountableHint": "When unchecked discounts will not be applied to this product.", "handleTooltip": "The handle is used to reference the product in your storefront. If not specified, the handle will be generated from the product title.", "availableInSalesChannels": "Available in <0>{{x}} of <1>{{y}} sales channels", "noSalesChannels": "Not available in any sales channels", @@ -249,7 +250,12 @@ "taxInclusiveHint": "When enabled all prices in the region will be tax inclusive.", "providersHint": "The providers that are available in the region.", "shippingOptions": "Shipping Options", - "returnShippingOptions": "Return Shipping Options" + "returnShippingOptions": "Return Shipping Options", + "return": "Return", + "outbound": "Outbound", + "priceType": "Price Type", + "flatRate": "Flat Rate", + "calculated": "Calculated" }, "locations": { "domain": "Locations", @@ -399,6 +405,7 @@ "dateIssued": "Date issued", "issuedDate": "Issued date", "expiryDate": "Expiry date", + "price": "Price", "height": "Height", "width": "Width", "length": "Length", diff --git a/packages/admin-next/dashboard/src/components/route-modal/index.ts b/packages/admin-next/dashboard/src/components/route-modal/index.ts new file mode 100644 index 0000000000..2122e387de --- /dev/null +++ b/packages/admin-next/dashboard/src/components/route-modal/index.ts @@ -0,0 +1,3 @@ +export { RouteDrawer } from "./route-drawer" +export { RouteFocusModal } from "./route-focus-modal" +export { useRouteModal } from "./route-modal-provider" diff --git a/packages/admin-next/dashboard/src/components/route-modal/route-drawer/index.ts b/packages/admin-next/dashboard/src/components/route-modal/route-drawer/index.ts new file mode 100644 index 0000000000..82f4bb964c --- /dev/null +++ b/packages/admin-next/dashboard/src/components/route-modal/route-drawer/index.ts @@ -0,0 +1 @@ +export * from "./route-drawer" diff --git a/packages/admin-next/dashboard/src/components/route-modal/route-drawer/route-drawer.tsx b/packages/admin-next/dashboard/src/components/route-modal/route-drawer/route-drawer.tsx new file mode 100644 index 0000000000..cad73d9589 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/route-modal/route-drawer/route-drawer.tsx @@ -0,0 +1,59 @@ +import { Drawer } from "@medusajs/ui" +import { PropsWithChildren, useEffect, useState } from "react" +import { useNavigate } from "react-router-dom" +import { RouteForm } from "../route-form" +import { RouteModalProvider } from "../route-modal-provider/route-provider" + +type RouteDrawerProps = PropsWithChildren<{ + prev?: string +}> + +const Root = ({ prev = "..", children }: RouteDrawerProps) => { + const navigate = useNavigate() + const [open, setOpen] = useState(false) + + /** + * Open the modal when the component mounts. This + * ensures that the entry animation is played. + */ + useEffect(() => { + setOpen(true) + }, []) + + const handleOpenChange = (open: boolean) => { + if (!open) { + document.body.style.pointerEvents = "auto" + navigate(prev, { replace: true }) + return + } + + setOpen(open) + } + + return ( + + + {children} + + + ) +} + +const Header = Drawer.Header +const Body = Drawer.Body +const Footer = Drawer.Footer +const Close = Drawer.Close +const Form = RouteForm + +/** + * Drawer that is used to render a form on a separate route. + * + * Typically used for forms editing a resource. + */ +export const RouteDrawer = Object.assign(Root, { + Header, + Body, + Footer, + Close, + Form, +}) diff --git a/packages/admin-next/dashboard/src/components/route-modal/route-focus-modal/index.ts b/packages/admin-next/dashboard/src/components/route-modal/route-focus-modal/index.ts new file mode 100644 index 0000000000..764e6c80c3 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/route-modal/route-focus-modal/index.ts @@ -0,0 +1 @@ +export * from "./route-focus-modal" diff --git a/packages/admin-next/dashboard/src/components/route-modal/route-focus-modal/route-focus-modal.tsx b/packages/admin-next/dashboard/src/components/route-modal/route-focus-modal/route-focus-modal.tsx new file mode 100644 index 0000000000..006dbdaa61 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/route-modal/route-focus-modal/route-focus-modal.tsx @@ -0,0 +1,58 @@ +import { FocusModal } from "@medusajs/ui" +import { PropsWithChildren, useEffect, useState } from "react" +import { useNavigate } from "react-router-dom" +import { RouteForm } from "../route-form" +import { RouteModalProvider } from "../route-modal-provider/route-provider" + +type RouteFocusModalProps = PropsWithChildren<{ + prev?: string +}> + +const Root = ({ prev = "..", children }: RouteFocusModalProps) => { + const navigate = useNavigate() + const [open, setOpen] = useState(false) + + /** + * Open the modal when the component mounts. This + * ensures that the entry animation is played. + */ + useEffect(() => { + setOpen(true) + }, []) + + const handleOpenChange = (open: boolean) => { + if (!open) { + document.body.style.pointerEvents = "auto" + navigate(prev, { replace: true }) + return + } + + setOpen(open) + } + + return ( + + + {children} + + + ) +} + +const Header = FocusModal.Header +const Body = FocusModal.Body +const Close = FocusModal.Close +const Form = RouteForm + +/** + * FocusModal that is used to render a form on a separate route. + * + * Typically used for forms creating a resource or forms that require + * a lot of space. + */ +export const RouteFocusModal = Object.assign(Root, { + Header, + Body, + Close, + Form, +}) diff --git a/packages/admin-next/dashboard/src/components/route-modal/route-form/index.ts b/packages/admin-next/dashboard/src/components/route-modal/route-form/index.ts new file mode 100644 index 0000000000..684d9b013c --- /dev/null +++ b/packages/admin-next/dashboard/src/components/route-modal/route-form/index.ts @@ -0,0 +1 @@ +export * from "./route-form" diff --git a/packages/admin-next/dashboard/src/components/route-modal/route-form/route-form.tsx b/packages/admin-next/dashboard/src/components/route-modal/route-form/route-form.tsx new file mode 100644 index 0000000000..d3131caac3 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/route-modal/route-form/route-form.tsx @@ -0,0 +1,63 @@ +import { Prompt } from "@medusajs/ui" +import { PropsWithChildren } from "react" +import { FieldValues, UseFormReturn } from "react-hook-form" +import { useTranslation } from "react-i18next" +import { useBlocker } from "react-router-dom" +import { Form } from "../../common/form" + +type RouteFormProps = PropsWithChildren<{ + form: UseFormReturn +}> + +export const RouteForm = ({ + form, + children, +}: RouteFormProps) => { + const { t } = useTranslation() + + const { + formState: { isDirty }, + } = form + + const blocker = useBlocker(({ currentLocation, nextLocation }) => { + const { isSubmitSuccessful } = nextLocation.state || {} + + if (isSubmitSuccessful) { + return false + } + + return isDirty && currentLocation.pathname !== nextLocation.pathname + }) + + const handleCancel = () => { + blocker?.reset?.() + } + + const handleContinue = () => { + blocker?.proceed?.() + } + + return ( +
+ {children} + + + + {t("general.unsavedChangesTitle")} + + {t("general.unsavedChangesDescription")} + + + + + {t("actions.cancel")} + + + {t("actions.continue")} + + + + +
+ ) +} diff --git a/packages/admin-next/dashboard/src/components/route-modal/route-modal-provider/index.ts b/packages/admin-next/dashboard/src/components/route-modal/route-modal-provider/index.ts new file mode 100644 index 0000000000..0caf0a36b0 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/route-modal/route-modal-provider/index.ts @@ -0,0 +1 @@ +export * from "./route-provider" diff --git a/packages/admin-next/dashboard/src/components/route-modal/route-modal-provider/route-provider.tsx b/packages/admin-next/dashboard/src/components/route-modal/route-modal-provider/route-provider.tsx new file mode 100644 index 0000000000..84d8664944 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/route-modal/route-modal-provider/route-provider.tsx @@ -0,0 +1,41 @@ +import { PropsWithChildren, createContext, useContext } from "react" +import { useNavigate } from "react-router-dom" + +type RouteModalProviderContextType = { + handleSuccess: (path?: string) => void +} + +const RouteModalProviderContext = + createContext(null) + +export const useRouteModal = () => { + const context = useContext(RouteModalProviderContext) + + if (!context) { + throw new Error("useRouteModal must be used within a RouteModalProvider") + } + + return context +} + +type RouteModalProviderProps = PropsWithChildren<{ + prev: string +}> + +export const RouteModalProvider = ({ + prev, + children, +}: RouteModalProviderProps) => { + const navigate = useNavigate() + + const handleSuccess = (path?: string) => { + const to = path || prev + navigate(to, { replace: true, state: { isSubmitSuccessful: true } }) + } + + return ( + + {children} + + ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/region/countries-cell/countries-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/region/countries-cell/countries-cell.tsx new file mode 100644 index 0000000000..6ae9fa8f2c --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/region/countries-cell/countries-cell.tsx @@ -0,0 +1,56 @@ +import { Country } from "@medusajs/medusa" +import { Tooltip } from "@medusajs/ui" +import { useTranslation } from "react-i18next" +import { PlaceholderCell } from "../../common/placeholder-cell" + +type CountriesCellProps = { + countries?: Country[] | null +} + +export const CountriesCell = ({ countries }: CountriesCellProps) => { + const { t } = useTranslation() + + if (!countries || countries.length === 0) { + return + } + + const displayValue = countries + .slice(0, 2) + .map((c) => c.display_name) + .join(", ") + + const additionalCountries = countries.slice(2).map((c) => c.display_name) + + return ( +
+ {displayValue} + {additionalCountries.length > 0 && ( + + {additionalCountries.map((c) => ( +
  • {c}
  • + ))} + + } + > + + {t("general.plusCountMore", { + count: additionalCountries.length, + })} + +
    + )} +
    + ) +} + +export const CountriesHeader = () => { + const { t } = useTranslation() + + return ( +
    + {t("fields.countries")} +
    + ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/region/countries-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/region/countries-cell/index.ts new file mode 100644 index 0000000000..eba73ad937 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/region/countries-cell/index.ts @@ -0,0 +1 @@ +export * from "./countries-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/region/fulfillment-providers-cell/fulfillment-providers-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/region/fulfillment-providers-cell/fulfillment-providers-cell.tsx new file mode 100644 index 0000000000..43df61c899 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/region/fulfillment-providers-cell/fulfillment-providers-cell.tsx @@ -0,0 +1,58 @@ +import { FulfillmentProvider } from "@medusajs/medusa" +import { Tooltip } from "@medusajs/ui" +import { useTranslation } from "react-i18next" +import { PlaceholderCell } from "../../common/placeholder-cell" + +type FulfillmentProvidersCellProps = { + fulfillmentProviders?: FulfillmentProvider[] | null +} + +export const FulfillmentProvidersCell = ({ + fulfillmentProviders, +}: FulfillmentProvidersCellProps) => { + const { t } = useTranslation() + + if (!fulfillmentProviders || fulfillmentProviders.length === 0) { + return + } + + const displayValue = fulfillmentProviders + .slice(0, 2) + .map((p) => p.id) + .join(", ") + + const additionalProviders = fulfillmentProviders.slice(2).map((c) => c.id) + + return ( +
    + {displayValue} + {additionalProviders.length > 0 && ( + + {additionalProviders.map((c) => ( +
  • {c}
  • + ))} + + } + > + + {t("general.plusCountMore", { + count: additionalProviders.length, + })} + +
    + )} +
    + ) +} + +export const FulfillmentProvidersHeader = () => { + const { t } = useTranslation() + + return ( +
    + {t("fields.fulfillmentProviders")} +
    + ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/region/fulfillment-providers-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/region/fulfillment-providers-cell/index.ts new file mode 100644 index 0000000000..59ceb739bf --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/region/fulfillment-providers-cell/index.ts @@ -0,0 +1 @@ +export * from "./fulfillment-providers-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/region/payment-providers-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/region/payment-providers-cell/index.ts new file mode 100644 index 0000000000..28efce12e6 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/region/payment-providers-cell/index.ts @@ -0,0 +1 @@ +export * from "./payment-providers-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/region/payment-providers-cell/payment-providers-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/region/payment-providers-cell/payment-providers-cell.tsx new file mode 100644 index 0000000000..b7bbfd4d8a --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/region/payment-providers-cell/payment-providers-cell.tsx @@ -0,0 +1,58 @@ +import { PaymentProvider } from "@medusajs/medusa" +import { Tooltip } from "@medusajs/ui" +import { useTranslation } from "react-i18next" +import { PlaceholderCell } from "../../common/placeholder-cell" + +type PaymentProvidersCellProps = { + paymentProviders?: PaymentProvider[] | null +} + +export const PaymentProvidersCell = ({ + paymentProviders, +}: PaymentProvidersCellProps) => { + const { t } = useTranslation() + + if (!paymentProviders || paymentProviders.length === 0) { + return + } + + const displayValue = paymentProviders + .slice(0, 2) + .map((p) => p.id) + .join(", ") + + const additionalProviders = paymentProviders.slice(2).map((c) => c.id) + + return ( +
    + {displayValue} + {additionalProviders.length > 0 && ( + + {additionalProviders.map((c) => ( +
  • {c}
  • + ))} + + } + > + + {t("general.plusCountMore", { + count: additionalProviders.length, + })} + +
    + )} +
    + ) +} + +export const PaymentProvidersHeader = () => { + const { t } = useTranslation() + + return ( +
    + {t("fields.paymentProviders")} +
    + ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/region/region-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/region/region-cell/index.ts new file mode 100644 index 0000000000..9d2849c741 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/region/region-cell/index.ts @@ -0,0 +1 @@ +export * from "./region-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/region/region-cell/region-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/region/region-cell/region-cell.tsx new file mode 100644 index 0000000000..2bdd6c16db --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/region/region-cell/region-cell.tsx @@ -0,0 +1,23 @@ +import { useTranslation } from "react-i18next" + +type RegionCellProps = { + name: string +} + +export const RegionCell = ({ name }: RegionCellProps) => { + return ( +
    + {name} +
    + ) +} + +export const RegionHeader = () => { + const { t } = useTranslation() + + return ( +
    + {t("fields.name")} +
    + ) +} diff --git a/packages/admin-next/dashboard/src/hooks/table/columns/use-region-table-columns.tsx b/packages/admin-next/dashboard/src/hooks/table/columns/use-region-table-columns.tsx new file mode 100644 index 0000000000..e27ce172ca --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/table/columns/use-region-table-columns.tsx @@ -0,0 +1,50 @@ +import { Region } from "@medusajs/medusa" +import { createColumnHelper } from "@tanstack/react-table" +import { useMemo } from "react" + +import { + CountriesCell, + CountriesHeader, +} from "../../../components/table/table-cells/region/countries-cell" +import { + FulfillmentProvidersCell, + FulfillmentProvidersHeader, +} from "../../../components/table/table-cells/region/fulfillment-providers-cell" +import { + PaymentProvidersCell, + PaymentProvidersHeader, +} from "../../../components/table/table-cells/region/payment-providers-cell" +import { + RegionCell, + RegionHeader, +} from "../../../components/table/table-cells/region/region-cell" + +const columnHelper = createColumnHelper() + +export const useRegionTableColumns = () => { + return useMemo( + () => [ + columnHelper.accessor("name", { + header: () => , + cell: ({ getValue }) => , + }), + columnHelper.accessor("countries", { + header: () => , + cell: ({ getValue }) => , + }), + columnHelper.accessor("payment_providers", { + header: () => , + cell: ({ getValue }) => ( + + ), + }), + columnHelper.accessor("fulfillment_providers", { + header: () => , + cell: ({ getValue }) => ( + + ), + }), + ], + [] + ) +} diff --git a/packages/admin-next/dashboard/src/hooks/table/filters/use-region-table-filters.tsx b/packages/admin-next/dashboard/src/hooks/table/filters/use-region-table-filters.tsx new file mode 100644 index 0000000000..ac9eb314ac --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/table/filters/use-region-table-filters.tsx @@ -0,0 +1,19 @@ +import { useTranslation } from "react-i18next" +import { Filter } from "../../../components/table/data-table" + +export const useRegionTableFilters = () => { + const { t } = useTranslation() + + const dateFilters: Filter[] = [ + { label: t("fields.createdAt"), key: "created_at" }, + { label: t("fields.updatedAt"), key: "updated_at" }, + ].map((f) => ({ + key: f.key, + label: f.label, + type: "date", + })) + + const filters = [...dateFilters] + + return filters +} diff --git a/packages/admin-next/dashboard/src/hooks/table/query/use-region-table-query.tsx b/packages/admin-next/dashboard/src/hooks/table/query/use-region-table-query.tsx new file mode 100644 index 0000000000..972f63960d --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/table/query/use-region-table-query.tsx @@ -0,0 +1,33 @@ +import { AdminGetRegionsParams } from "@medusajs/medusa" +import { useQueryParams } from "../../use-query-params" + +type UseRegionTableQueryProps = { + prefix?: string + pageSize?: number +} + +export const useRegionTableQuery = ({ + prefix, + pageSize = 20, +}: UseRegionTableQueryProps) => { + const queryObject = useQueryParams( + ["offset", "q", "order", "created_at", "updated_at"], + prefix + ) + + const { offset, q, order, created_at, updated_at } = queryObject + + const searchParams: AdminGetRegionsParams = { + limit: pageSize, + offset: offset ? Number(offset) : 0, + order, + created_at: created_at ? JSON.parse(created_at) : undefined, + updated_at: updated_at ? JSON.parse(updated_at) : undefined, + q, + } + + return { + searchParams, + raw: queryObject, + } +} diff --git a/packages/admin-next/dashboard/src/hooks/use-route-modal-state.tsx b/packages/admin-next/dashboard/src/hooks/use-route-modal-state.tsx deleted file mode 100644 index 4348428819..0000000000 --- a/packages/admin-next/dashboard/src/hooks/use-route-modal-state.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { usePrompt } from "@medusajs/ui" -import { useEffect, useState } from "react" -import { useTranslation } from "react-i18next" -import { useNavigate } from "react-router-dom" - -/** - * Hook for managing the state of route modals. - */ -export const useRouteModalState = (): [ - open: boolean, - onOpenChange: (open: boolean, ignore?: boolean) => void, - /** - * Subscribe to the dirty state of the form. - * If the form is dirty, the modal will prompt - * the user before closing. - */ - subscribe: (value: boolean) => void, -] => { - const [open, setOpen] = useState(false) - const [shouldPrompt, subscribe] = useState(false) - - const navigate = useNavigate() - const prompt = usePrompt() - const { t } = useTranslation() - - const promptValues = { - title: t("general.unsavedChangesTitle"), - description: t("general.unsavedChangesDescription"), - cancelText: t("actions.cancel"), - confirmText: t("actions.continue"), - variant: "confirmation" as const, - } - - useEffect(() => { - setOpen(true) - }, []) - - const onOpenChange = async (open: boolean, ignore = false) => { - if (!open) { - if (shouldPrompt && !ignore) { - const confirmed = await prompt(promptValues) - - if (!confirmed) { - return - } - } - - setTimeout(() => { - navigate("..", { replace: true }) - }, 200) - } - - setOpen(open) - } - - return [open, onOpenChange, subscribe] -} diff --git a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-add-sales-channels/api-key-management-add-sales-channels.tsx b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-add-sales-channels/api-key-management-add-sales-channels.tsx index 9e8a62caf7..8c1e34155a 100644 --- a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-add-sales-channels/api-key-management-add-sales-channels.tsx +++ b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-add-sales-channels/api-key-management-add-sales-channels.tsx @@ -1,36 +1,26 @@ -import { FocusModal } from "@medusajs/ui" import { useAdminPublishableApiKeySalesChannels } from "medusa-react" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { AddSalesChannelsToApiKeyForm } from "./components" export const ApiKeyManagementAddSalesChannels = () => { const { id } = useParams() - const [open, onOpenChange, subscribe] = useRouteModalState() const { sales_channels, isLoading, isError, error } = useAdminPublishableApiKeySalesChannels(id!) - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - {!isLoading && sales_channels && ( - sc.id)} - onSuccessfulSubmit={handleSuccessfulSubmit} - subscribe={subscribe} - /> - )} - - + + {!isLoading && sales_channels && ( + sc.id)} + /> + )} + ) } diff --git a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-add-sales-channels/components/add-sales-channels-to-api-key-form.tsx b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-add-sales-channels/components/add-sales-channels-to-api-key-form.tsx index b20a33eb06..d81c3faca1 100644 --- a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-add-sales-channels/components/add-sales-channels-to-api-key-form.tsx +++ b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-add-sales-channels/components/add-sales-channels-to-api-key-form.tsx @@ -3,7 +3,6 @@ import { SalesChannel } from "@medusajs/medusa" import { Button, Checkbox, - FocusModal, Hint, StatusBadge, Table, @@ -26,17 +25,18 @@ import { useEffect, useMemo, useState } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { Form } from "../../../../components/common/form" import { OrderBy } from "../../../../components/filtering/order-by" import { Query } from "../../../../components/filtering/query" import { LocalizedTablePagination } from "../../../../components/localization/localized-table-pagination" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../components/route-modal" import { useQueryParams } from "../../../../hooks/use-query-params" type AddSalesChannelsToApiKeyFormProps = { apiKey: string preSelected: string[] - subscribe: (state: boolean) => void - onSuccessfulSubmit: () => void } const AddSalesChannelsToApiKeySchema = zod.object({ @@ -48,10 +48,9 @@ const PAGE_SIZE = 50 export const AddSalesChannelsToApiKeyForm = ({ apiKey, preSelected, - subscribe, - onSuccessfulSubmit, }: AddSalesChannelsToApiKeyFormProps) => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -60,13 +59,7 @@ export const AddSalesChannelsToApiKeyForm = ({ resolver: zodResolver(AddSalesChannelsToApiKeySchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) + const { setValue } = form const { mutateAsync, isLoading: isMutating } = useAdminAddPublishableKeySalesChannelsBatch(apiKey) @@ -87,11 +80,15 @@ export const AddSalesChannelsToApiKeyForm = ({ const [rowSelection, setRowSelection] = useState({}) useEffect(() => { - form.setValue( + setValue( "sales_channel_ids", - Object.keys(rowSelection).filter((k) => rowSelection[k]) + Object.keys(rowSelection).filter((k) => rowSelection[k]), + { + shouldDirty: true, + shouldTouch: true, + } ) - }, [rowSelection]) + }, [rowSelection, setValue]) const params = useQueryParams(["q", "order"]) const { sales_channels, count } = useAdminSalesChannels( @@ -135,36 +132,36 @@ export const AddSalesChannelsToApiKeyForm = ({ }, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() }, } ) }) return ( -
    + - +
    {form.formState.errors.sales_channel_ids && ( {form.formState.errors.sales_channel_ids.message} )} - + - +
    -
    - + +
    @@ -236,9 +233,9 @@ export const AddSalesChannelsToApiKeyForm = ({ pageSize={PAGE_SIZE} />
    - + - + ) } diff --git a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-create/api-key-management-create.tsx b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-create/api-key-management-create.tsx index cfc8522b0c..e354f1834a 100644 --- a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-create/api-key-management-create.tsx +++ b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-create/api-key-management-create.tsx @@ -1,15 +1,10 @@ -import { FocusModal } from "@medusajs/ui" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { CreatePublishableApiKeyForm } from "./components/create-publishable-api-key-form" export const ApiKeyManagementCreate = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx index 7676ee4f0b..10300524cc 100644 --- a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx +++ b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx @@ -1,26 +1,23 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { Button, FocusModal, Heading, Input, Text } from "@medusajs/ui" +import { Button, Heading, Input, Text } from "@medusajs/ui" import { useAdminCreatePublishableApiKey } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { useEffect } from "react" -import { useNavigate } from "react-router-dom" import { Form } from "../../../../../components/common/form" - -type CreatePublishableApiKeyFormProps = { - subscribe: (state: boolean) => void -} +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" const CreatePublishableApiKeySchema = zod.object({ title: zod.string().min(1), }) -export const CreatePublishableApiKeyForm = ({ - subscribe, -}: CreatePublishableApiKeyFormProps) => { - const { mutateAsync, isLoading } = useAdminCreatePublishableApiKey() +export const CreatePublishableApiKeyForm = () => { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -29,46 +26,35 @@ export const CreatePublishableApiKeyForm = ({ resolver: zodResolver(CreatePublishableApiKeySchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - - const { t } = useTranslation() - const navigate = useNavigate() + const { mutateAsync, isLoading } = useAdminCreatePublishableApiKey() const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync(values, { onSuccess: ({ publishable_api_key }) => { - navigate(`/settings/api-key-management/${publishable_api_key.id}`, { - replace: true, - }) + handleSuccess(`/settings/api-key-management/${publishable_api_key.id}`) }, }) }) return ( -
    + - +
    - + - +
    -
    - + +
    @@ -88,7 +74,7 @@ export const CreatePublishableApiKeyForm = ({ {t("fields.title")} - + @@ -98,8 +84,8 @@ export const CreatePublishableApiKeyForm = ({
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx index 61c1fcaeb5..a8f9c27997 100644 --- a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx @@ -1,40 +1,29 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminPublishableApiKey } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { EditApiKeyForm } from "./components/edit-api-key-form" export const ApiKeyManagementEdit = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() const { id } = useParams() const { t } = useTranslation() const { publishable_api_key, isLoading, isError, error } = useAdminPublishableApiKey(id!) - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - - {t("apiKeyManagement.editKey")} - - {!isLoading && publishable_api_key && ( - - )} - - + + + {t("apiKeyManagement.editKey")} + + {!isLoading && publishable_api_key && ( + + )} + ) } diff --git a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-edit/components/edit-api-key-form/edit-api-key-form.tsx b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-edit/components/edit-api-key-form/edit-api-key-form.tsx index 240c79cf63..87798aaff5 100644 --- a/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-edit/components/edit-api-key-form/edit-api-key-form.tsx +++ b/packages/admin-next/dashboard/src/routes/api-key-management/api-key-management-edit/components/edit-api-key-form/edit-api-key-form.tsx @@ -1,29 +1,28 @@ import { zodResolver } from "@hookform/resolvers/zod" import type { PublishableApiKey } from "@medusajs/medusa" -import { Button, Drawer, Input } from "@medusajs/ui" +import { Button, Input } from "@medusajs/ui" import { useAdminUpdatePublishableApiKey } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" + import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" type EditApiKeyFormProps = { apiKey: PublishableApiKey - onSuccessfulSubmit: () => void - subscribe: (state: boolean) => void } const EditApiKeySchema = zod.object({ title: zod.string().min(1), }) -export const EditApiKeyForm = ({ - apiKey, - onSuccessfulSubmit, - subscribe, -}: EditApiKeyFormProps) => { +export const EditApiKeyForm = ({ apiKey }: EditApiKeyFormProps) => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -32,28 +31,23 @@ export const EditApiKeyForm = ({ resolver: zodResolver(EditApiKeySchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - const { mutateAsync, isLoading } = useAdminUpdatePublishableApiKey(apiKey.id) const handleSubmit = form.handleSubmit(async (data) => { await mutateAsync(data, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() + }, + onError: (error) => { + console.log(error) }, }) }) return ( -
    + - +
    -
    - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/collections/collection-add-products/collection-add-products.tsx b/packages/admin-next/dashboard/src/routes/collections/collection-add-products/collection-add-products.tsx index 9832460d6b..7daea150a5 100644 --- a/packages/admin-next/dashboard/src/routes/collections/collection-add-products/collection-add-products.tsx +++ b/packages/admin-next/dashboard/src/routes/collections/collection-add-products/collection-add-products.tsx @@ -1,34 +1,22 @@ -import { FocusModal } from "@medusajs/ui" import { useAdminCollection } from "medusa-react" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" + +import { RouteFocusModal } from "../../../components/route-modal" import { AddProductsToCollectionForm } from "./components/add-products-to-collection-form" export const CollectionAddProducts = () => { const { id } = useParams() const { collection, isLoading, isError, error } = useAdminCollection(id!) - const [open, onOpenChange, subscribe] = useRouteModalState() - - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - {!isLoading && collection && ( - - )} - - + + {!isLoading && collection && ( + + )} + ) } diff --git a/packages/admin-next/dashboard/src/routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx b/packages/admin-next/dashboard/src/routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx index 716bd85d8c..18843f33c6 100644 --- a/packages/admin-next/dashboard/src/routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx +++ b/packages/admin-next/dashboard/src/routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx @@ -1,14 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import type { Product, ProductCollection } from "@medusajs/medusa" -import { - Button, - Checkbox, - FocusModal, - Hint, - Table, - Tooltip, - clx, -} from "@medusajs/ui" +import { Button, Checkbox, Hint, Table, Tooltip, clx } from "@medusajs/ui" import { PaginationState, RowSelectionState, @@ -30,7 +22,6 @@ import { NoRecords, NoResults, } from "../../../../../components/common/empty-table-content" -import { Form } from "../../../../../components/common/form" import { ProductAvailabilityCell, ProductCollectionCell, @@ -41,14 +32,16 @@ import { import { OrderBy } from "../../../../../components/filtering/order-by" import { Query } from "../../../../../components/filtering/query" import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" import { useHandleTableScroll } from "../../../../../hooks/use-handle-table-scroll" import { useQueryParams } from "../../../../../hooks/use-query-params" import { queryClient } from "../../../../../lib/medusa" type AddProductsToCollectionFormProps = { collection: ProductCollection - subscribe: (state: boolean) => void - onSuccessfulSubmit: () => void } const AddProductsToSalesChannelSchema = zod.object({ @@ -59,10 +52,9 @@ const PAGE_SIZE = 50 export const AddProductsToCollectionForm = ({ collection, - subscribe, - onSuccessfulSubmit, }: AddProductsToCollectionFormProps) => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -71,13 +63,7 @@ export const AddProductsToCollectionForm = ({ resolver: zodResolver(AddProductsToSalesChannelSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) + const { setValue } = form const { mutateAsync, isLoading: isMutating } = useAdminAddProductsToCollection(collection.id) @@ -98,11 +84,15 @@ export const AddProductsToCollectionForm = ({ const [rowSelection, setRowSelection] = useState({}) useEffect(() => { - form.setValue( + setValue( "product_ids", - Object.keys(rowSelection).filter((k) => rowSelection[k]) + Object.keys(rowSelection).filter((k) => rowSelection[k]), + { + shouldDirty: true, + shouldTouch: true, + } ) - }, [rowSelection]) + }, [rowSelection, setValue]) const params = useQueryParams(["q", "order"]) @@ -151,7 +141,7 @@ export const AddProductsToCollectionForm = ({ * determine if they are added to the collection or not. */ queryClient.invalidateQueries(adminProductKeys.lists()) - onSuccessfulSubmit() + handleSuccess() }, } ) @@ -169,29 +159,29 @@ export const AddProductsToCollectionForm = ({ } return ( -
    + - +
    {form.formState.errors.product_ids && ( {form.formState.errors.product_ids.message} )} - + - +
    -
    - + + {!noRecords && (
    @@ -291,9 +281,9 @@ export const AddProductsToCollectionForm = ({ {/* TODO: fix this, and add NoRecords as well */}
    )} -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/collections/collection-create/collection-create.tsx b/packages/admin-next/dashboard/src/routes/collections/collection-create/collection-create.tsx index 2a43c7eb9b..e5f50dd664 100644 --- a/packages/admin-next/dashboard/src/routes/collections/collection-create/collection-create.tsx +++ b/packages/admin-next/dashboard/src/routes/collections/collection-create/collection-create.tsx @@ -1,15 +1,10 @@ -import { FocusModal } from "@medusajs/ui" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { CreateCollectionForm } from "./components/create-collection-form" export const CollectionCreate = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/collections/collection-create/components/create-collection-form/create-collection-form.tsx b/packages/admin-next/dashboard/src/routes/collections/collection-create/components/create-collection-form/create-collection-form.tsx index 2b4146f297..2875402cde 100644 --- a/packages/admin-next/dashboard/src/routes/collections/collection-create/components/create-collection-form/create-collection-form.tsx +++ b/packages/admin-next/dashboard/src/routes/collections/collection-create/components/create-collection-form/create-collection-form.tsx @@ -1,27 +1,24 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { Button, FocusModal, Heading, Input, Text } from "@medusajs/ui" +import { Button, Heading, Input, Text } from "@medusajs/ui" import { useAdminCreateCollection } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" -import { useNavigate } from "react-router-dom" import * as zod from "zod" -import { Form } from "../../../../../components/common/form" -type CreateCollectionFormProps = { - subscribe: (state: boolean) => void -} +import { Form } from "../../../../../components/common/form" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" const CreateCollectionSchema = zod.object({ title: zod.string().min(1), handle: zod.string().optional(), }) -export const CreateCollectionForm = ({ - subscribe, -}: CreateCollectionFormProps) => { +export const CreateCollectionForm = () => { const { t } = useTranslation() - const navigate = useNavigate() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -31,34 +28,26 @@ export const CreateCollectionForm = ({ resolver: zodResolver(CreateCollectionSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - const { mutateAsync, isLoading } = useAdminCreateCollection() const handleSubmit = form.handleSubmit(async (data) => { await mutateAsync(data, { onSuccess: ({ collection }) => { - navigate(`/collections/${collection.id}`) + handleSuccess(`/collections/${collection.id}`) }, }) }) return ( -
    + - +
    - + - +
    -
    - + +
    {t("collections.createCollection")} @@ -131,8 +120,8 @@ export const CreateCollectionForm = ({ />
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/collections/collection-edit/collection-add-edit.tsx b/packages/admin-next/dashboard/src/routes/collections/collection-edit/collection-add-edit.tsx index 0496ec12c3..6d146da32b 100644 --- a/packages/admin-next/dashboard/src/routes/collections/collection-edit/collection-add-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/collections/collection-edit/collection-add-edit.tsx @@ -1,8 +1,8 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminCollection } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { EditCollectionForm } from "./components/edit-collection-form" export const CollectionEdit = () => { @@ -10,30 +10,18 @@ export const CollectionEdit = () => { const { t } = useTranslation() const { collection, isLoading, isError, error } = useAdminCollection(id!) - const [open, onOpenChange, subscribe] = useRouteModalState() - - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - - {t("collections.editCollection")} - - {!isLoading && collection && ( - - )} - - + + + {t("collections.editCollection")} + + {!isLoading && collection && ( + + )} + ) } diff --git a/packages/admin-next/dashboard/src/routes/collections/collection-edit/components/edit-collection-form/edit-collection-form.tsx b/packages/admin-next/dashboard/src/routes/collections/collection-edit/components/edit-collection-form/edit-collection-form.tsx index 44a56c986f..c4a2af64a0 100644 --- a/packages/admin-next/dashboard/src/routes/collections/collection-edit/components/edit-collection-form/edit-collection-form.tsx +++ b/packages/admin-next/dashboard/src/routes/collections/collection-edit/components/edit-collection-form/edit-collection-form.tsx @@ -1,17 +1,19 @@ import { zodResolver } from "@hookform/resolvers/zod" import type { ProductCollection } from "@medusajs/medusa" -import { Button, Drawer, Input, Text } from "@medusajs/ui" +import { Button, Input, Text } from "@medusajs/ui" import { useAdminUpdateCollection } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" + import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" type EditCollectionFormProps = { collection: ProductCollection - subscribe: (state: boolean) => void - onSuccessfulSubmit: () => void } const EditCollectionSchema = zod.object({ @@ -19,12 +21,9 @@ const EditCollectionSchema = zod.object({ handle: zod.string().min(1), }) -export const EditCollectionForm = ({ - collection, - onSuccessfulSubmit, - subscribe, -}: EditCollectionFormProps) => { +export const EditCollectionForm = ({ collection }: EditCollectionFormProps) => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -34,28 +33,20 @@ export const EditCollectionForm = ({ resolver: zodResolver(EditCollectionSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - const { mutateAsync, isLoading } = useAdminUpdateCollection(collection.id) const handleSubmit = form.handleSubmit(async (data) => { await mutateAsync(data, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() }, }) }) return ( -
    + - +
    -
    - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-add-customers/components/add-customers-form/add-customers-form.tsx b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-add-customers/components/add-customers-form/add-customers-form.tsx index 22e377f080..8c85f787e2 100644 --- a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-add-customers/components/add-customers-form/add-customers-form.tsx +++ b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-add-customers/components/add-customers-form/add-customers-form.tsx @@ -1,14 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Customer } from "@medusajs/medusa" -import { - Button, - Checkbox, - FocusModal, - Hint, - Table, - Tooltip, - clx, -} from "@medusajs/ui" +import { Button, Checkbox, Hint, Table, Tooltip, clx } from "@medusajs/ui" import { PaginationState, RowSelectionState, @@ -25,21 +17,23 @@ import { import { useEffect, useMemo, useState } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" -import { useNavigate } from "react-router-dom" import * as zod from "zod" + import { NoRecords, NoResults, } from "../../../../../components/common/empty-table-content" -import { Form } from "../../../../../components/common/form" import { Query } from "../../../../../components/filtering/query" import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" import { useQueryParams } from "../../../../../hooks/use-query-params" import { queryClient } from "../../../../../lib/medusa" type AddCustomersFormProps = { customerGroupId: string - subscribe: (state: boolean) => void } const AddCustomersSchema = zod.object({ @@ -50,10 +44,9 @@ const PAGE_SIZE = 10 export const AddCustomersForm = ({ customerGroupId, - subscribe, }: AddCustomersFormProps) => { - const navigate = useNavigate() const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -62,13 +55,7 @@ export const AddCustomersForm = ({ resolver: zodResolver(AddCustomersSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) + const { setValue } = form const [{ pageIndex, pageSize }, setPagination] = useState({ pageIndex: 0, @@ -86,11 +73,15 @@ export const AddCustomersForm = ({ const [rowSelection, setRowSelection] = useState({}) useEffect(() => { - form.setValue( + setValue( "customer_ids", - Object.keys(rowSelection).filter((k) => rowSelection[k]) + Object.keys(rowSelection).filter((k) => rowSelection[k]), + { + shouldDirty: true, + shouldTouch: true, + } ) - }, [rowSelection]) + }, [rowSelection, setValue]) const params = useQueryParams(["q"]) const { customers, count, isLoading, isError, error } = useAdminCustomers({ @@ -131,7 +122,7 @@ export const AddCustomersForm = ({ { onSuccess: () => { queryClient.invalidateQueries(adminCustomerKeys.lists()) - navigate(`/customer-groups/${customerGroupId}`) + handleSuccess(`/customer-groups/${customerGroupId}`) }, } ) @@ -147,23 +138,23 @@ export const AddCustomersForm = ({ } return ( -
    + - +
    {form.formState.errors.customer_ids && ( {form.formState.errors.customer_ids.message} )} - + - +
    -
    - + + {noRecords ? (
    @@ -259,9 +250,9 @@ export const AddCustomersForm = ({ />
    )} -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-add-customers/customer-group-add-customers.tsx b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-add-customers/customer-group-add-customers.tsx index 874d277800..aa85a03663 100644 --- a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-add-customers/customer-group-add-customers.tsx +++ b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-add-customers/customer-group-add-customers.tsx @@ -1,18 +1,14 @@ -import { FocusModal } from "@medusajs/ui" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" + +import { RouteFocusModal } from "../../../components/route-modal" import { AddCustomersForm } from "./components/add-customers-form" export const CustomerGroupAddCustomers = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - const { id } = useParams() return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-create/components/create-customer-group-form/create-customer-group-form.tsx b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-create/components/create-customer-group-form/create-customer-group-form.tsx index f350be2964..cbb2dbe75d 100644 --- a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-create/components/create-customer-group-form/create-customer-group-form.tsx +++ b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-create/components/create-customer-group-form/create-customer-group-form.tsx @@ -1,26 +1,23 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { Button, FocusModal, Heading, Input, Text } from "@medusajs/ui" +import { Button, Heading, Input, Text } from "@medusajs/ui" import { useAdminCreateCustomerGroup } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" -import { useNavigate } from "react-router-dom" import * as zod from "zod" -import { Form } from "../../../../../components/common/form" -type CreateCustomerGroupFormProps = { - subscribe: (state: boolean) => void -} +import { Form } from "../../../../../components/common/form" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" const CreateCustomerGroupSchema = zod.object({ name: zod.string().min(1), }) -export const CreateCustomerGroupForm = ({ - subscribe, -}: CreateCustomerGroupFormProps) => { +export const CreateCustomerGroupForm = () => { const { t } = useTranslation() - const navigate = useNavigate() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -29,14 +26,6 @@ export const CreateCustomerGroupForm = ({ resolver: zodResolver(CreateCustomerGroupSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - const { mutateAsync, isLoading } = useAdminCreateCustomerGroup() const handleSubmit = form.handleSubmit(async (data) => { @@ -46,22 +35,22 @@ export const CreateCustomerGroupForm = ({ }, { onSuccess: ({ customer_group }) => { - navigate(`/customer-groups/${customer_group.id}`) + handleSuccess(`/customer-groups/${customer_group.id}`) }, } ) }) return ( -
    + - +
    - + - +
    -
    - + +
    {t("customerGroups.createCustomerGroup")} @@ -98,8 +87,8 @@ export const CreateCustomerGroupForm = ({ />
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-create/customer-group-create.tsx b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-create/customer-group-create.tsx index 6fc39b5067..50d763a27f 100644 --- a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-create/customer-group-create.tsx +++ b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-create/customer-group-create.tsx @@ -1,15 +1,10 @@ -import { FocusModal } from "@medusajs/ui" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { CreateCustomerGroupForm } from "./components/create-customer-group-form" export const CustomerGroupCreate = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-edit/components/edit-customer-group-form/edit-customer-group-form.tsx b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-edit/components/edit-customer-group-form/edit-customer-group-form.tsx index 0bcc1c4bcf..b1f2bb2a2e 100644 --- a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-edit/components/edit-customer-group-form/edit-customer-group-form.tsx +++ b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-edit/components/edit-customer-group-form/edit-customer-group-form.tsx @@ -1,17 +1,18 @@ import { zodResolver } from "@hookform/resolvers/zod" import { CustomerGroup } from "@medusajs/medusa" -import { Button, Drawer, Input } from "@medusajs/ui" +import { Button, Input } from "@medusajs/ui" import { useAdminUpdateCustomerGroup } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as z from "zod" import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" type EditCustomerGroupFormProps = { group: CustomerGroup - onSuccessfulSubmit: () => void - subscribe: (state: boolean) => void } const EditCustomerGroupSchema = z.object({ @@ -20,10 +21,9 @@ const EditCustomerGroupSchema = z.object({ export const EditCustomerGroupForm = ({ group, - onSuccessfulSubmit, - subscribe, }: EditCustomerGroupFormProps) => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -32,31 +32,23 @@ export const EditCustomerGroupForm = ({ resolver: zodResolver(EditCustomerGroupSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - const { mutateAsync, isLoading } = useAdminUpdateCustomerGroup(group.id) const handleSubmit = form.handleSubmit(async (data) => { await mutateAsync(data, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() }, }) }) return ( -
    + - + - - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-edit/customer-group-edit.tsx b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-edit/customer-group-edit.tsx index 0e43bdd314..e07055a9b0 100644 --- a/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-edit/customer-group-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/customer-groups/customer-group-edit/customer-group-edit.tsx @@ -1,13 +1,11 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminCustomerGroup } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { EditCustomerGroupForm } from "./components/edit-customer-group-form" export const CustomerGroupEdit = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - const { id } = useParams() const { customer_group, isLoading, isError, error } = useAdminCustomerGroup( id! @@ -15,28 +13,18 @@ export const CustomerGroupEdit = () => { const { t } = useTranslation() - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - - {t("customerGroups.editCustomerGroup")} - - {!isLoading && customer_group && ( - - )} - - + + + {t("customerGroups.editCustomerGroup")} + + {!isLoading && customer_group && ( + + )} + ) } diff --git a/packages/admin-next/dashboard/src/routes/customers/customer-create/components/create-customer-form/create-customer-form.tsx b/packages/admin-next/dashboard/src/routes/customers/customer-create/components/create-customer-form/create-customer-form.tsx index f0d7669d84..d20f054a6d 100644 --- a/packages/admin-next/dashboard/src/routes/customers/customer-create/components/create-customer-form/create-customer-form.tsx +++ b/packages/admin-next/dashboard/src/routes/customers/customer-create/components/create-customer-form/create-customer-form.tsx @@ -1,16 +1,15 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { Button, FocusModal, Heading, Input, Text } from "@medusajs/ui" +import { Button, Heading, Input, Text } from "@medusajs/ui" import { useAdminCreateCustomer } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" -import { useNavigate } from "react-router-dom" import * as zod from "zod" -import { Form } from "../../../../../components/common/form" -type CreateCustomerFormProps = { - subscribe: (state: boolean) => void -} +import { Form } from "../../../../../components/common/form" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" const CreateCustomerSchema = zod .object({ @@ -31,9 +30,9 @@ const CreateCustomerSchema = zod } }) -export const CreateCustomerForm = ({ subscribe }: CreateCustomerFormProps) => { +export const CreateCustomerForm = () => { const { t } = useTranslation() - const navigate = useNavigate() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -46,13 +45,6 @@ export const CreateCustomerForm = ({ subscribe }: CreateCustomerFormProps) => { }, resolver: zodResolver(CreateCustomerSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) const { mutateAsync, isLoading } = useAdminCreateCustomer() @@ -67,22 +59,22 @@ export const CreateCustomerForm = ({ subscribe }: CreateCustomerFormProps) => { }, { onSuccess: ({ customer }) => { - navigate(`/customers/${customer.id}`, { replace: true }) + handleSuccess(`/customers/${customer.id}`) }, } ) }) return ( -
    + - +
    - + - +
    -
    - + +
    {t("customers.createCustomer")} @@ -214,8 +206,8 @@ export const CreateCustomerForm = ({ subscribe }: CreateCustomerFormProps) => {
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/customers/customer-create/customer-create.tsx b/packages/admin-next/dashboard/src/routes/customers/customer-create/customer-create.tsx index 9d28afb2b2..61b826834d 100644 --- a/packages/admin-next/dashboard/src/routes/customers/customer-create/customer-create.tsx +++ b/packages/admin-next/dashboard/src/routes/customers/customer-create/customer-create.tsx @@ -1,15 +1,10 @@ -import { FocusModal } from "@medusajs/ui" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { CreateCustomerForm } from "./components/create-customer-form" export const CustomerCreate = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/customers/customer-edit/components/edit-customer-form/edit-customer-form.tsx b/packages/admin-next/dashboard/src/routes/customers/customer-edit/components/edit-customer-form/edit-customer-form.tsx index bf5e0baead..690644fd18 100644 --- a/packages/admin-next/dashboard/src/routes/customers/customer-edit/components/edit-customer-form/edit-customer-form.tsx +++ b/packages/admin-next/dashboard/src/routes/customers/customer-edit/components/edit-customer-form/edit-customer-form.tsx @@ -1,17 +1,19 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Customer } from "@medusajs/medusa" -import { Button, Drawer, Input } from "@medusajs/ui" +import { Button, Input } from "@medusajs/ui" import { useAdminUpdateCustomer } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" + import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" type EditCustomerFormProps = { customer: Customer - subscribe: (state: boolean) => void - onSuccessfulSubmit: () => void } const EditCustomerSchema = zod.object({ @@ -21,12 +23,9 @@ const EditCustomerSchema = zod.object({ phone: zod.string().optional(), }) -export const EditCustomerForm = ({ - customer, - subscribe, - onSuccessfulSubmit, -}: EditCustomerFormProps) => { +export const EditCustomerForm = ({ customer }: EditCustomerFormProps) => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -38,14 +37,6 @@ export const EditCustomerForm = ({ resolver: zodResolver(EditCustomerSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - const { mutateAsync, isLoading } = useAdminUpdateCustomer(customer.id) const handleSubmit = form.handleSubmit(async (data) => { @@ -58,16 +49,16 @@ export const EditCustomerForm = ({ }, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() }, } ) }) return ( -
    + - +
    -
    - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/customers/customer-edit/customer-edit.tsx b/packages/admin-next/dashboard/src/routes/customers/customer-edit/customer-edit.tsx index a453f1152e..2fcf619a39 100644 --- a/packages/admin-next/dashboard/src/routes/customers/customer-edit/customer-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/customers/customer-edit/customer-edit.tsx @@ -1,39 +1,26 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminCustomer } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { EditCustomerForm } from "./components/edit-customer-form" export const CustomerEdit = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() const { t } = useTranslation() const { id } = useParams() const { customer, isLoading, isError, error } = useAdminCustomer(id!) - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - - {t("customers.editCustomer")} - - {!isLoading && customer && ( - - )} - - + + + {t("customers.editCustomer")} + + {!isLoading && customer && } + ) } diff --git a/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-create/components/create-gift-card-form/create-gift-card-form.tsx b/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-create/components/create-gift-card-form/create-gift-card-form.tsx index af29abc6ac..6f5a9581fd 100644 --- a/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-create/components/create-gift-card-form/create-gift-card-form.tsx +++ b/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-create/components/create-gift-card-form/create-gift-card-form.tsx @@ -3,7 +3,6 @@ import { Button, CurrencyInput, DatePicker, - FocusModal, Heading, Input, Select, @@ -14,20 +13,19 @@ import { } from "@medusajs/ui" import * as Collapsible from "@radix-ui/react-collapsible" import { useAdminCreateGiftCard, useAdminRegions } from "medusa-react" -import { useEffect, useState } from "react" +import { useState } from "react" import { useForm, useWatch } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { useNavigate } from "react-router-dom" import { Form } from "../../../../../components/common/form" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" import { currencies } from "../../../../../lib/currencies" import { getDbAmount } from "../../../../../lib/money-amount-helpers" -type CreateGiftCardFormProps = { - subscribe: (state: boolean) => void -} - const CreateGiftCardSchema = zod.object({ region_id: zod.string(), value: zod.string(), @@ -37,7 +35,7 @@ const CreateGiftCardSchema = zod.object({ personal_message: zod.string().optional(), }) -export const CreateGiftCardForm = ({ subscribe }: CreateGiftCardFormProps) => { +export const CreateGiftCardForm = () => { const [showDateFields, setShowDateFields] = useState(false) const { regions } = useAdminRegions({ @@ -45,7 +43,7 @@ export const CreateGiftCardForm = ({ subscribe }: CreateGiftCardFormProps) => { fields: "id,name,currency_code", }) const { t } = useTranslation() - const navigate = useNavigate() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -59,11 +57,7 @@ export const CreateGiftCardForm = ({ subscribe }: CreateGiftCardFormProps) => { resolver: zodResolver(CreateGiftCardSchema), }) - const { - formState: { isDirty }, - setValue, - setError, - } = form + const { setValue, setError } = form const regionId = useWatch({ control: form.control, @@ -75,10 +69,6 @@ export const CreateGiftCardForm = ({ subscribe }: CreateGiftCardFormProps) => { ? currencies[currencyCode.toUpperCase()].symbol_native : undefined - useEffect(() => { - subscribe(isDirty) - }, [isDirty, subscribe]) - const { mutateAsync, isLoading } = useAdminCreateGiftCard() const handleOpenChange = (open: boolean) => { @@ -95,7 +85,7 @@ export const CreateGiftCardForm = ({ subscribe }: CreateGiftCardFormProps) => { if (!currencyCode) { setError("region_id", { type: "manual", - message: "Region not found", + message: t("giftCards.selectRegionFirst"), }) return @@ -114,31 +104,31 @@ export const CreateGiftCardForm = ({ subscribe }: CreateGiftCardFormProps) => { }, { onSuccess: ({ gift_card }) => { - navigate(`../${gift_card.id}`, { replace: true }) + handleSuccess(`../${gift_card.id}`) }, } ) }) return ( -
    + - +
    - + - +
    -
    - + +
    {t("giftCards.createGiftCard")} @@ -304,8 +294,8 @@ export const CreateGiftCardForm = ({ subscribe }: CreateGiftCardFormProps) => { />
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-create/gift-card-create.tsx b/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-create/gift-card-create.tsx index 21af94ba5c..9c50261d69 100644 --- a/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-create/gift-card-create.tsx +++ b/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-create/gift-card-create.tsx @@ -1,15 +1,10 @@ -import { FocusModal } from "@medusajs/ui" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { CreateGiftCardForm } from "./components/create-gift-card-form" export const GiftCardCreate = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-edit/components/edit-gift-card-form/edit-gift-card-form.tsx b/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-edit/components/edit-gift-card-form/edit-gift-card-form.tsx index 24f990e013..d8a336ee0f 100644 --- a/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-edit/components/edit-gift-card-form/edit-gift-card-form.tsx +++ b/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-edit/components/edit-gift-card-form/edit-gift-card-form.tsx @@ -4,19 +4,22 @@ import { Button, CurrencyInput, DatePicker, - Drawer, Select, Switch, Text, } from "@medusajs/ui" import * as Collapsible from "@radix-ui/react-collapsible" import { useAdminRegions, useAdminUpdateGiftCard } from "medusa-react" -import { useEffect, useState } from "react" +import { useState } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" import { currencies } from "../../../../../lib/currencies" import { isAxiosError } from "../../../../../lib/is-axios-error" import { @@ -26,8 +29,6 @@ import { type EditGiftCardFormProps = { giftCard: GiftCard - onSuccessfulSubmit: () => void - subscribe: (state: boolean) => void } const EditGiftCardSchema = zod.object({ @@ -37,13 +38,12 @@ const EditGiftCardSchema = zod.object({ ends_at: zod.date().nullable(), }) -export const EditGiftCardForm = ({ - giftCard, - onSuccessfulSubmit, - subscribe, -}: EditGiftCardFormProps) => { +export const EditGiftCardForm = ({ giftCard }: EditGiftCardFormProps) => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + const [showDateFields, setShowDateFields] = useState(!!giftCard.ends_at) + const form = useForm>({ defaultValues: { region_id: giftCard.region_id, @@ -57,14 +57,7 @@ export const EditGiftCardForm = ({ resolver: zodResolver(EditGiftCardSchema), }) - const { - formState: { isDirty }, - setValue, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty, subscribe]) + const { setValue } = form const { regions } = useAdminRegions({ limit: 1000, @@ -117,7 +110,7 @@ export const EditGiftCardForm = ({ }, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() }, onError: (error) => { if (isAxiosError(error)) { @@ -132,19 +125,19 @@ export const EditGiftCardForm = ({ }) return ( -
    + - + { return ( - {t("fields.balance")} + {t("giftCards.balance")} - - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-edit/gift-card-edit.tsx b/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-edit/gift-card-edit.tsx index a888ff1b64..2c99a61f3b 100644 --- a/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-edit/gift-card-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/gift-cards/gift-card-edit/gift-card-edit.tsx @@ -1,39 +1,26 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminGiftCard } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { EditGiftCardForm } from "./components/edit-gift-card-form" export const GiftCardEdit = () => { const { id } = useParams() const { t } = useTranslation() - const [open, onOpenChange, subscribe] = useRouteModalState() const { gift_card, isLoading, isError, error } = useAdminGiftCard(id!) - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - - {t("giftCards.editGiftCard")} - - {!isLoading && gift_card && ( - - )} - - + + + {t("giftCards.editGiftCard")} + + {!isLoading && gift_card && } + ) } diff --git a/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/location-add-sales-channels.tsx b/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/location-add-sales-channels.tsx index 6f7d42bfeb..c09882bb28 100644 --- a/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/location-add-sales-channels.tsx +++ b/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/location-add-sales-channels.tsx @@ -1,15 +1,8 @@ -import { FocusModal } from "@medusajs/ui" import { useAdminAddLocationToSalesChannel } from "medusa-react" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" export const LocationAddSalesChannels = () => { - const [open, onOpenChange] = useRouteModalState() - const { mutateAsync } = useAdminAddLocationToSalesChannel() // TODO: We need a batch mutation instead of this to avoid multiple requests - return ( - - - - ) + return } diff --git a/packages/admin-next/dashboard/src/routes/locations/location-create/components/create-location-form/create-location-form.tsx b/packages/admin-next/dashboard/src/routes/locations/location-create/components/create-location-form/create-location-form.tsx index a80fc765ab..ad40b5943a 100644 --- a/packages/admin-next/dashboard/src/routes/locations/location-create/components/create-location-form/create-location-form.tsx +++ b/packages/admin-next/dashboard/src/routes/locations/location-create/components/create-location-form/create-location-form.tsx @@ -1,11 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { Button, FocusModal, Heading, Input, Text } from "@medusajs/ui" +import { Button, Heading, Input, Text } from "@medusajs/ui" import { useAdminCreateStockLocation } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" import { CountrySelect } from "../../../../../components/common/country-select" import { Form } from "../../../../../components/common/form" +import { RouteFocusModal } from "../../../../../components/route-modal" const CreateLocationSchema = zod.object({ name: zod.string().min(1), @@ -22,7 +23,7 @@ const CreateLocationSchema = zod.object({ }) export const CreateLocationForm = () => { - const { mutateAsync, isLoading } = useAdminCreateStockLocation() + const { t } = useTranslation() const form = useForm>({ defaultValues: { @@ -41,7 +42,7 @@ export const CreateLocationForm = () => { resolver: zodResolver(CreateLocationSchema), }) - const { t } = useTranslation() + const { mutateAsync, isLoading } = useAdminCreateStockLocation() const handleSubmit = form.handleSubmit(async (values) => { mutateAsync( @@ -56,24 +57,24 @@ export const CreateLocationForm = () => { }) return ( -
    + - +
    - + - +
    -
    - + +
    @@ -227,8 +228,8 @@ export const CreateLocationForm = () => {
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/locations/location-create/location-create.tsx b/packages/admin-next/dashboard/src/routes/locations/location-create/location-create.tsx index 0369b258d6..5d6a290e16 100644 --- a/packages/admin-next/dashboard/src/routes/locations/location-create/location-create.tsx +++ b/packages/admin-next/dashboard/src/routes/locations/location-create/location-create.tsx @@ -1,15 +1,10 @@ -import { FocusModal } from "@medusajs/ui" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { CreateLocationForm } from "./components/create-location-form" export const LocationCreate = () => { - const [open, onOpenChange] = useRouteModalState() - return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/locations/location-edit/components/edit-location-form/edit-location-form.tsx b/packages/admin-next/dashboard/src/routes/locations/location-edit/components/edit-location-form/edit-location-form.tsx index a543d228fc..1c7a05a5fb 100644 --- a/packages/admin-next/dashboard/src/routes/locations/location-edit/components/edit-location-form/edit-location-form.tsx +++ b/packages/admin-next/dashboard/src/routes/locations/location-edit/components/edit-location-form/edit-location-form.tsx @@ -1,12 +1,14 @@ import { zodResolver } from "@hookform/resolvers/zod" import { StockLocationExpandedDTO } from "@medusajs/types" -import { Button, Drawer, Input } from "@medusajs/ui" +import { Button, Input } from "@medusajs/ui" import { useAdminUpdateStockLocation } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" + import { CountrySelect } from "../../../../../components/common/country-select" import { Form } from "../../../../../components/common/form" +import { RouteDrawer } from "../../../../../components/route-modal" type EditLocationFormProps = { location: StockLocationExpandedDTO @@ -56,12 +58,12 @@ export const EditLocationForm = ({ location }: EditLocationFormProps) => { }) return ( -
    + - +
    { }} />
    -
    - + +
    - + - - + +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/locations/location-edit/location-edit.tsx b/packages/admin-next/dashboard/src/routes/locations/location-edit/location-edit.tsx index b30df632ce..04e229be51 100644 --- a/packages/admin-next/dashboard/src/routes/locations/location-edit/location-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/locations/location-edit/location-edit.tsx @@ -1,12 +1,11 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminStockLocations } from "medusa-react" import { useTranslation } from "react-i18next" -import { json, useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { useParams } from "react-router-dom" +import { RouteDrawer } from "../../../components/route-modal" import { EditLocationForm } from "./components/edit-location-form/edit-location-form" export const LocationEdit = () => { - const [open, onOpenChange] = useRouteModalState() const { id } = useParams() const { stock_locations, isLoading, isError, error } = useAdminStockLocations( @@ -24,24 +23,14 @@ export const LocationEdit = () => { const stock_location = stock_locations?.[0] - if (!isLoading && !stock_location) { - throw json({ message: "Not found" }, 404) - } - return ( - - - - - {t("locations.editLocation")} - - - {isLoading || !stock_location ? ( -
    Loading...
    - ) : ( - - )} -
    -
    + + + {t("locations.editLocation")} + + {!isLoading && stock_location && ( + + )} + ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx b/packages/admin-next/dashboard/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx index ae5e0dfc28..5a8193b451 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx @@ -1,19 +1,20 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Product } from "@medusajs/medusa" -import { Button, Drawer, Input } from "@medusajs/ui" +import { Button, Input } from "@medusajs/ui" import { useAdminUpdateProduct } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" import { CountrySelect } from "../../../../../components/common/country-select" import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" type ProductAttributesFormProps = { product: Product - subscribe: (state: boolean) => void - onSuccessfulSubmit: () => void } const dimension = zod @@ -39,9 +40,10 @@ const ProductAttributesSchema = zod.object({ export const ProductAttributesForm = ({ product, - subscribe, - onSuccessfulSubmit, }: ProductAttributesFormProps) => { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + const form = useForm>({ defaultValues: { height: product.height ? product.height : null, @@ -55,15 +57,6 @@ export const ProductAttributesForm = ({ resolver: zodResolver(ProductAttributesSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty, subscribe]) - - const { t } = useTranslation() const { mutateAsync, isLoading } = useAdminUpdateProduct(product.id) const handleSubmit = form.handleSubmit(async (data) => { @@ -79,16 +72,16 @@ export const ProductAttributesForm = ({ }, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() }, } ) }) return ( -
    + - +
    -
    - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-attributes/product-attributes.tsx b/packages/admin-next/dashboard/src/routes/products/product-attributes/product-attributes.tsx index eebeac6be4..d6eb19e27b 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-attributes/product-attributes.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-attributes/product-attributes.tsx @@ -1,41 +1,27 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminProduct } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { ProductAttributesForm } from "./components/product-attributes-form" export const ProductAttributes = () => { const { id } = useParams() - const [open, onOpenChange, subscribe] = useRouteModalState() - const { t } = useTranslation() const { product, isLoading, isError, error } = useAdminProduct(id!) - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - - {t("products.editAttributes")} - - {!isLoading && product && ( - - )} - - + + + {t("products.editAttributes")} + + {!isLoading && product && } + ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-form.tsx b/packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-form.tsx index 8f0c9fe4e6..ea3b18067b 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-form.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-form.tsx @@ -1,18 +1,16 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { Button, FocusModal } from "@medusajs/ui" +import { Button } from "@medusajs/ui" import { useAdminCreateProduct } from "medusa-react" -import { useEffect } from "react" import { UseFormReturn, useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { Form } from "../../../../../components/common/form" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" import { CreateProductDetails } from "./create-product-details" -type CreateProductFormProps = { - subscribe: (state: boolean) => void -} - const CreateProductSchema = zod.object({ title: zod.string(), subtitle: zod.string().optional(), @@ -38,8 +36,10 @@ const CreateProductSchema = zod.object({ type Schema = zod.infer export type CreateProductFormReturn = UseFormReturn -export const CreateProductForm = ({ subscribe }: CreateProductFormProps) => { +export const CreateProductForm = () => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + const form = useForm({ defaultValues: { title: "", @@ -61,14 +61,6 @@ export const CreateProductForm = ({ subscribe }: CreateProductFormProps) => { resolver: zodResolver(CreateProductSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [subscribe, isDirty]) - const { mutateAsync, isLoading } = useAdminCreateProduct() const handleSubmit = form.handleSubmit(async (values) => { @@ -83,34 +75,36 @@ export const CreateProductForm = ({ subscribe }: CreateProductFormProps) => { weight: values.weight ? parseFloat(values.weight) : undefined, }, { - onSuccess: () => {}, + onSuccess: ({ product }) => { + handleSuccess(`../${product.id}`) + }, } ) }) return ( -
    + - +
    - + - +
    -
    - + +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-create/product-create.tsx b/packages/admin-next/dashboard/src/routes/products/product-create/product-create.tsx index 8232bc2494..4316f5dfe4 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-create/product-create.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-create/product-create.tsx @@ -1,15 +1,10 @@ -import { FocusModal } from "@medusajs/ui" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { CreateProductForm } from "./components/create-product-form" export const ProductCreate = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx b/packages/admin-next/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx index 3889b91008..aff799ac51 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx @@ -1,27 +1,20 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Product } from "@medusajs/medusa" import { ProductStatus } from "@medusajs/types" -import { - Button, - Drawer, - Input, - Select, - Switch, - Text, - Textarea, -} from "@medusajs/ui" +import { Button, Input, Select, Switch, Text, Textarea } from "@medusajs/ui" import { useAdminUpdateProduct } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { Trans, useTranslation } from "react-i18next" import * as zod from "zod" import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" type EditProductFormProps = { product: Product - subscribe: (state: boolean) => void - onSuccessfulSubmit: () => void } const EditProductSchema = zod.object({ @@ -34,11 +27,10 @@ const EditProductSchema = zod.object({ discountable: zod.boolean(), }) -export const EditProductForm = ({ - product, - subscribe, - onSuccessfulSubmit, -}: EditProductFormProps) => { +export const EditProductForm = ({ product }: EditProductFormProps) => { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + const form = useForm>({ defaultValues: { status: product.status, @@ -52,15 +44,6 @@ export const EditProductForm = ({ resolver: zodResolver(EditProductSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty, subscribe]) - - const { t } = useTranslation() const { mutateAsync, isLoading } = useAdminUpdateProduct(product.id) const handleSubmit = form.handleSubmit(async (data) => { @@ -71,16 +54,16 @@ export const EditProductForm = ({ }, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() }, } ) }) return ( -
    + - +
    - When unchecked discounts will not be applied to this - product. + {t("products.discountableHint")} @@ -256,20 +238,20 @@ export const EditProductForm = ({ }} />
    -
    - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit/product-edit.tsx b/packages/admin-next/dashboard/src/routes/products/product-edit/product-edit.tsx index 0da39a73c5..d68df23081 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-edit/product-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-edit/product-edit.tsx @@ -1,41 +1,27 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminProduct } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { EditProductForm } from "./components/edit-product-form" export const ProductEdit = () => { const { id } = useParams() - const [open, onOpenChange, subscribe] = useRouteModalState() - const { t } = useTranslation() const { product, isLoading, isError, error } = useAdminProduct(id!) - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - - {t("products.editProduct")} - - {!isLoading && product && ( - - )} - - + + + {t("products.editProduct")} + + {!isLoading && product && } + ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-gallery/product-gallery.tsx b/packages/admin-next/dashboard/src/routes/products/product-gallery/product-gallery.tsx index 83744149a0..625881c7a8 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-gallery/product-gallery.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-gallery/product-gallery.tsx @@ -11,14 +11,16 @@ import { Button, IconButton, Kbd, Tooltip } from "@medusajs/ui" import * as Dialog from "@radix-ui/react-dialog" import { Variants, motion } from "framer-motion" import { useAdminProduct } from "medusa-react" -import { useCallback, useEffect, useMemo } from "react" +import { useCallback, useEffect, useMemo, useState } from "react" import { useTranslation } from "react-i18next" import { Link, useParams, useSearchParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" - export const ProductGallery = () => { - const [open, onOpenChange] = useRouteModalState() + const [open, setOpen] = useState(false) + + useEffect(() => { + setOpen(true) + }, []) const { id } = useParams() const [searchParams, setSearchParams] = useSearchParams() @@ -78,7 +80,7 @@ export const ProductGallery = () => { } return ( - +
    diff --git a/packages/admin-next/dashboard/src/routes/products/product-options/components/edit-product-options-form/edit-product-options-form.tsx b/packages/admin-next/dashboard/src/routes/products/product-options/components/edit-product-options-form/edit-product-options-form.tsx index e918f83718..d71f91b15c 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-options/components/edit-product-options-form/edit-product-options-form.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-options/components/edit-product-options-form/edit-product-options-form.tsx @@ -1,15 +1,13 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Product } from "@medusajs/medusa" -import { Button, Drawer } from "@medusajs/ui" +import { Button } from "@medusajs/ui" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { Form } from "../../../../../components/common/form" +import { RouteDrawer } from "../../../../../components/route-modal" type EditProductOptionsFormProps = { product: Product - handleSuccess: () => void - subscribe: (state: boolean) => void } const EditProductOptionsSchema = zod.object({}) @@ -22,22 +20,22 @@ export const EditProductOptionsForm = (props: EditProductOptionsFormProps) => { }) return ( -
    + - - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-options/product-options.tsx b/packages/admin-next/dashboard/src/routes/products/product-options/product-options.tsx index 14e3efa70d..60f8469e74 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-options/product-options.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-options/product-options.tsx @@ -1,39 +1,26 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminProduct } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { EditProductOptionsForm } from "./components/edit-product-options-form" export const ProductOptions = () => { const { id } = useParams() const { t } = useTranslation() - const [open, onOpenChange, subscribe] = useRouteModalState() const { product, isLoading, isError, error } = useAdminProduct(id!) - const handleSuccess = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - - {t("products.editOptions")} - - {!isLoading && product && ( - - )} - - + + + {t("products.editOptions")} + + {!isLoading && product && } + ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx b/packages/admin-next/dashboard/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx index 70a277a67f..4935d217b4 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx @@ -1,9 +1,17 @@ import { Product, SalesChannel } from "@medusajs/medusa" -import { Button, Checkbox, FocusModal } from "@medusajs/ui" +import { Button, Checkbox } from "@medusajs/ui" import { RowSelectionState, createColumnHelper } from "@tanstack/react-table" import { useAdminSalesChannels, useAdminUpdateProduct } from "medusa-react" import { useEffect, useMemo, useState } from "react" import { useTranslation } from "react-i18next" +import * as zod from "zod" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" import { DataTable } from "../../../../../components/table/data-table" import { useSalesChannelTableColumns } from "../../../../../hooks/table/columns/use-sales-channel-table-columns" import { useSalesChannelTableFilters } from "../../../../../hooks/table/filters/use-sales-channel-table-filters" @@ -12,18 +20,28 @@ import { useDataTable } from "../../../../../hooks/use-data-table" type EditSalesChannelsFormProps = { product: Product - subscribe: (state: boolean) => void - onSuccessfulSubmit: () => void } +const EditSalesChannelsSchema = zod.object({ + sales_channels: zod.array(zod.string()).optional(), +}) + const PAGE_SIZE = 50 export const EditSalesChannelsForm = ({ product, - subscribe, - onSuccessfulSubmit, }: EditSalesChannelsFormProps) => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + + const form = useForm>({ + defaultValues: { + sales_channels: product.sales_channels?.map((sc) => sc.id) ?? [], + }, + resolver: zodResolver(EditSalesChannelsSchema), + }) + + const { setValue } = form const initialState = product.sales_channels?.reduce((acc, curr) => { @@ -34,13 +52,13 @@ export const EditSalesChannelsForm = ({ const [rowSelection, setRowSelection] = useState(initialState) - const isDirty = Object.entries(initialState).some( - ([key, value]) => value !== rowSelection[key] - ) - useEffect(() => { - subscribe(isDirty) - }, [isDirty, subscribe]) + const ids = Object.keys(rowSelection) + setValue("sales_channels", ids, { + shouldDirty: true, + shouldTouch: true, + }) + }, [rowSelection, setValue]) const { searchParams, raw } = useSalesChannelTableQuery({ pageSize: PAGE_SIZE, @@ -76,12 +94,10 @@ export const EditSalesChannelsForm = ({ product.id ) - const handleSubmit = async () => { - const selected = Object.keys(rowSelection).filter((key) => { - return rowSelection[key] - }) + const handleSubmit = form.handleSubmit(async (data) => { + const arr = data.sales_channels ?? [] - const sales_channels = selected.map((id) => { + const sales_channels = arr.map((id) => { return { id, } @@ -93,46 +109,48 @@ export const EditSalesChannelsForm = ({ }, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() }, } ) - } + }) if (isError) { throw error } return ( -
    - -
    - - + + - - -
    -
    - - - -
    +
    + + + + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/products/product-sales-channels/product-sales-channels.tsx b/packages/admin-next/dashboard/src/routes/products/product-sales-channels/product-sales-channels.tsx index 5485cf7c0a..89f42fcd8d 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-sales-channels/product-sales-channels.tsx +++ b/packages/admin-next/dashboard/src/routes/products/product-sales-channels/product-sales-channels.tsx @@ -1,35 +1,20 @@ -import { FocusModal } from "@medusajs/ui" import { useAdminProduct } from "medusa-react" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { EditSalesChannelsForm } from "./components/edit-sales-channels-form" export const ProductSalesChannels = () => { const { id } = useParams() - const [open, onOpenChange, subscribe] = useRouteModalState() - const { product, isLoading, isError, error } = useAdminProduct(id!) - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - {!isLoading && product && ( - - )} - - + + {!isLoading && product && } + ) } diff --git a/packages/admin-next/dashboard/src/routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx b/packages/admin-next/dashboard/src/routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx index b59dd7d937..d57353c27b 100644 --- a/packages/admin-next/dashboard/src/routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx +++ b/packages/admin-next/dashboard/src/routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx @@ -1,19 +1,22 @@ -import { User } from "@medusajs/medusa" -import * as zod from "zod" - import { zodResolver } from "@hookform/resolvers/zod" -import { Button, Drawer, Input, Select, Switch } from "@medusajs/ui" +import { User } from "@medusajs/medusa" +import { Button, Input, Select, Switch } from "@medusajs/ui" import { adminAuthKeys, useAdminUpdateUser } from "medusa-react" import { useForm } from "react-hook-form" import { Trans, useTranslation } from "react-i18next" +import * as zod from "zod" + import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" import { languages } from "../../../../../i18n/config" import { queryClient } from "../../../../../lib/medusa" type EditProfileProps = { user: Omit usageInsights: boolean - onSuccess: () => void } const EditProfileSchema = zod.object({ @@ -23,13 +26,9 @@ const EditProfileSchema = zod.object({ usage_insights: zod.boolean(), }) -export const EditProfileForm = ({ - user, - usageInsights, - onSuccess, -}: EditProfileProps) => { +export const EditProfileForm = ({ user, usageInsights }: EditProfileProps) => { const { t, i18n } = useTranslation() - const { mutateAsync, isLoading } = useAdminUpdateUser(user.id) + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -49,6 +48,8 @@ export const EditProfileForm = ({ a.display_name.localeCompare(b.display_name) ) + const { mutateAsync, isLoading } = useAdminUpdateUser(user.id) + const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync( { @@ -56,12 +57,7 @@ export const EditProfileForm = ({ last_name: values.last_name, }, { - onSuccess: ({ user }) => { - form.reset({ - first_name: user.first_name, - last_name: user.last_name, - }) - + onSuccess: () => { // Invalidate the current user session. queryClient.invalidateQueries(adminAuthKeys.details()) }, @@ -73,13 +69,13 @@ export const EditProfileForm = ({ changeLanguage(values.language) - onSuccess() + handleSuccess() }) return ( -
    + - +
    + + + + + {storeCurrencies.map((currency) => ( + + {currency.name} + + ))} + + + ) @@ -198,8 +206,8 @@ export const CreateRegionForm = ({ subscribe }: CreateRegionFormProps) => {
    - + - + ) } diff --git a/packages/admin-next/dashboard/src/routes/regions/region-create/region-create.tsx b/packages/admin-next/dashboard/src/routes/regions/region-create/region-create.tsx index c313c1d915..922932067a 100644 --- a/packages/admin-next/dashboard/src/routes/regions/region-create/region-create.tsx +++ b/packages/admin-next/dashboard/src/routes/regions/region-create/region-create.tsx @@ -1,16 +1,10 @@ -import { FocusModal } from "@medusajs/ui" - -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal/route-focus-modal" import { CreateRegionForm } from "./components/create-region-form" export const RegionCreate = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/index.ts b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/index.ts new file mode 100644 index 0000000000..f7841f26fc --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/index.ts @@ -0,0 +1 @@ +export * from "./region-shipping-option-section" diff --git a/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/region-shipping-option-section.tsx b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/region-shipping-option-section.tsx index ddebc33167..b35f2af342 100644 --- a/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/region-shipping-option-section.tsx +++ b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/region-shipping-option-section.tsx @@ -1,60 +1,48 @@ -import { Region, ShippingOption } from "@medusajs/medusa" -import { Container, Heading, StatusBadge, Table, clx } from "@medusajs/ui" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" +import { Region } from "@medusajs/medusa" +import { Container, Heading } from "@medusajs/ui" import { useAdminShippingOptions } from "medusa-react" -import { useMemo, useState } from "react" import { useTranslation } from "react-i18next" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" + +import { PricedShippingOption } from "@medusajs/medusa/dist/types/pricing" +import { DataTable } from "../../../../../components/table/data-table" +import { useDataTable } from "../../../../../hooks/use-data-table" +import { useShippingOptionColumns } from "./use-shipping-option-table-columns" +import { useShippingOptionTableFilters } from "./use-shipping-option-table-filters" +import { useShippingOptionTableQuery } from "./use-shipping-option-table-query" type RegionShippingOptionSectionProps = { region: Region } +const PAGE_SIZE = 10 + export const RegionShippingOptionSection = ({ region, }: RegionShippingOptionSectionProps) => { - const { shipping_options, count, isError, error, isLoading } = - useAdminShippingOptions({ - region_id: region.id, - is_return: false, - }) - - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: count || 0, + const { searchParams, raw } = useShippingOptionTableQuery({ + pageSize: PAGE_SIZE, + regionId: region.id, }) + const { shipping_options, count, isError, error, isLoading } = + useAdminShippingOptions( + { + ...searchParams, + }, + { + keepPreviousData: true, + } + ) - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const [rowSelection, setRowSelection] = useState({}) - + const filters = useShippingOptionTableFilters() const columns = useShippingOptionColumns() - const table = useReactTable({ - data: shipping_options ?? [], + const { table } = useDataTable({ + data: (shipping_options ?? []) as unknown as PricedShippingOption[], columns, - pageCount: count ? 1 : 0, - state: { - pagination, - rowSelection, - }, - manualPagination: true, - getCoreRowModel: getCoreRowModel(), - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, + count, + enablePagination: true, + getRowId: (row) => row.id!, + pageSize: PAGE_SIZE, }) const { t } = useTranslation() @@ -64,91 +52,30 @@ export const RegionShippingOptionSection = ({ } return ( - +
    {t("regions.shippingOptions")}
    - - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ))} - -
    -
    ) } - -const columnHelper = createColumnHelper() - -const useShippingOptionColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.accessor("name", { - header: t("fields.name"), - cell: (cell) => cell.getValue(), - }), - columnHelper.accessor("admin_only", { - header: t("fields.availability"), - cell: (cell) => { - const value = cell.getValue() - - return ( - - {value ? t("general.admin") : t("general.store")} - - ) - }, - }), - ], - [t] - ) -} diff --git a/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-columns.tsx b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-columns.tsx new file mode 100644 index 0000000000..e05e55c475 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-columns.tsx @@ -0,0 +1,116 @@ +import { PricedShippingOption } from "@medusajs/medusa/dist/types/pricing" +import { createColumnHelper } from "@tanstack/react-table" +import { useMemo } from "react" +import { useTranslation } from "react-i18next" +import { MoneyAmountCell } from "../../../../../components/table/table-cells/common/money-amount-cell" +import { PlaceholderCell } from "../../../../../components/table/table-cells/common/placeholder-cell" +import { StatusCell } from "../../../../../components/table/table-cells/common/status-cell" + +const columnHelper = createColumnHelper() + +export const useShippingOptionColumns = () => { + const { t } = useTranslation() + + return useMemo( + () => [ + columnHelper.accessor("name", { + header: t("fields.name"), + cell: ({ getValue }) => ( +
    + {getValue()} +
    + ), + }), + columnHelper.accessor("price_type", { + header: t("regions.priceType"), + cell: ({ getValue }) => { + const type = getValue() + + return ( + + {type === "flat_rate" + ? t("regions.flatRate") + : t("regions.calculated")} + + ) + }, + }), + columnHelper.accessor("price_incl_tax", { + header: t("fields.price"), + cell: ({ getValue, row }) => { + const isCalculated = row.original.price_type === "calculated" + + if (isCalculated) { + return + } + + const amount = getValue() + const currencyCode = row.original.region!.currency_code + + return + }, + }), + columnHelper.display({ + id: "min_amount", + header: "Min.", + cell: ({ row }) => { + const minAmountReq = row.original.requirements?.find( + (r) => r.type === "min_subtotal" + ) + + if (!minAmountReq) { + return + } + + const amount = minAmountReq.amount + const currencyCode = row.original.region!.currency_code + + return + }, + }), + columnHelper.display({ + id: "max_amount", + header: "Max.", + cell: ({ row }) => { + const maxAmountReq = row.original.requirements?.find( + (r) => r.type === "max_subtotal" + ) + + if (!maxAmountReq) { + return + } + + const amount = maxAmountReq.amount + const currencyCode = row.original.region!.currency_code + + return + }, + }), + columnHelper.accessor("admin_only", { + header: t("fields.availability"), + cell: (cell) => { + const value = cell.getValue() + + return ( + + {value ? t("general.admin") : t("general.store")} + + ) + }, + }), + columnHelper.accessor("is_return", { + header: t("fields.type"), + cell: (cell) => { + const value = cell.getValue() + + return ( + + {value ? t("regions.return") : t("regions.outbound")} + + ) + }, + }), + ], + [t] + ) +} diff --git a/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-filters.tsx b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-filters.tsx new file mode 100644 index 0000000000..aa618865fa --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-filters.tsx @@ -0,0 +1,39 @@ +import { useTranslation } from "react-i18next" +import { Filter } from "../../../../../components/table/data-table" + +export const useShippingOptionTableFilters = () => { + const { t } = useTranslation() + + const isReturnFilter: Filter = { + key: "is_return", + label: t("fields.type"), + type: "select", + options: [ + { label: t("regions.return"), value: "true" }, + { label: t("regions.outbound"), value: "false" }, + ], + } + + const isAdminFilter: Filter = { + key: "admin_only", + label: t("fields.availability"), + type: "select", + options: [ + { label: t("general.admin"), value: "true" }, + { label: t("general.store"), value: "false" }, + ], + } + + const dateFilters: Filter[] = [ + { label: t("fields.createdAt"), key: "created_at" }, + { label: t("fields.updatedAt"), key: "updated_at" }, + ].map((f) => ({ + key: f.key, + label: f.label, + type: "date", + })) + + const filters = [isReturnFilter, isAdminFilter, ...dateFilters] + + return filters +} diff --git a/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-query.tsx b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-query.tsx new file mode 100644 index 0000000000..348c370926 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/regions/region-detail/components/region-shipping-option-section/use-shipping-option-table-query.tsx @@ -0,0 +1,48 @@ +import { AdminGetShippingOptionsParams } from "@medusajs/medusa" +import { useQueryParams } from "../../../../../hooks/use-query-params" + +type UseShippingOptionTableQueryProps = { + regionId: string + isReturn?: boolean + pageSize?: number + prefix?: string +} + +export const useShippingOptionTableQuery = ({ + regionId, + pageSize = 10, + prefix, +}: UseShippingOptionTableQueryProps) => { + const queryObject = useQueryParams( + [ + "offset", + "q", + "order", + "admin_only", + "is_return", + "created_at", + "updated_at", + ], + prefix + ) + + const { offset, order, q, admin_only, is_return, created_at, updated_at } = + queryObject + + const searchParams: AdminGetShippingOptionsParams = { + limit: pageSize, + offset: offset ? Number(offset) : 0, + region_id: regionId, + is_return: is_return ? is_return === "true" : undefined, + admin_only: admin_only ? admin_only === "true" : undefined, + q, + order, + created_at: created_at ? JSON.parse(created_at) : undefined, + updated_at: updated_at ? JSON.parse(updated_at) : undefined, + } + + return { + searchParams, + raw: queryObject, + } +} diff --git a/packages/admin-next/dashboard/src/routes/regions/region-list/components/region-list-table/region-list-table.tsx b/packages/admin-next/dashboard/src/routes/regions/region-list/components/region-list-table/region-list-table.tsx index f425b57ab0..51209128df 100644 --- a/packages/admin-next/dashboard/src/routes/regions/region-list/components/region-list-table/region-list-table.tsx +++ b/packages/admin-next/dashboard/src/routes/regions/region-list/components/region-list-table/region-list-table.tsx @@ -1,64 +1,43 @@ import { PencilSquare, Trash } from "@medusajs/icons" import { Region } from "@medusajs/medusa" -import { - Button, - Container, - Heading, - Table, - Tooltip, - clx, - usePrompt, -} from "@medusajs/ui" -import { - PaginationState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" +import { Button, Container, Heading, usePrompt } from "@medusajs/ui" +import { createColumnHelper } from "@tanstack/react-table" import { useAdminDeleteRegion, useAdminRegions } from "medusa-react" -import { useMemo, useState } from "react" +import { useMemo } from "react" import { useTranslation } from "react-i18next" -import { Link, useNavigate } from "react-router-dom" +import { Link } from "react-router-dom" import { ActionMenu } from "../../../../../components/common/action-menu" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" +import { DataTable } from "../../../../../components/table/data-table" +import { useRegionTableColumns } from "../../../../../hooks/table/columns/use-region-table-columns" +import { useRegionTableFilters } from "../../../../../hooks/table/filters/use-region-table-filters" +import { useRegionTableQuery } from "../../../../../hooks/table/query/use-region-table-query" +import { useDataTable } from "../../../../../hooks/use-data-table" -const PAGE_SIZE = 50 +const PAGE_SIZE = 20 export const RegionListTable = () => { - const navigate = useNavigate() const { t } = useTranslation() - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] + const { searchParams, raw } = useRegionTableQuery({ pageSize: PAGE_SIZE }) + const { regions, count, isLoading, isError, error } = useAdminRegions( + { + ...searchParams, + }, + { + keepPreviousData: true, + } ) - const { regions, count, isLoading, isError, error } = useAdminRegions({ - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - }) - + const filters = useRegionTableFilters() const columns = useColumns() - const table = useReactTable({ + const { table } = useDataTable({ data: regions ?? [], columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - }, - onPaginationChange: setPagination, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, + count, + enablePagination: true, + getRowId: (row) => row.id, + pageSize: PAGE_SIZE, }) if (isError) { @@ -66,7 +45,7 @@ export const RegionListTable = () => { } return ( - +
    {t("regions.domain")} @@ -75,59 +54,18 @@ export const RegionListTable = () => {
    - - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - navigate(`/settings/regions/${row.original.id}`)} - > - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ))} - -
    - `${row.original.id}`} + pagination + search + queryObject={raw} />
    ) @@ -187,124 +125,11 @@ const RegionActions = ({ region }: { region: Region }) => { const columnHelper = createColumnHelper() const useColumns = () => { - const { t } = useTranslation() + const base = useRegionTableColumns() return useMemo( () => [ - columnHelper.accessor("name", { - header: t("fields.name"), - cell: (cell) => cell.getValue(), - }), - columnHelper.accessor("countries", { - header: t("fields.countries"), - cell: (cell) => { - const countries = cell.getValue() - - const displayValue = countries - .slice(0, 2) - .map((c) => c.display_name) - .join(", ") - - const additionalCountries = countries - .slice(2) - .map((c) => c.display_name) - - return ( -
    - {displayValue} - {additionalCountries.length > 0 && ( - - {additionalCountries.map((c) => ( -
  • {c}
  • - ))} - - } - > - - {t("general.plusCountMore", { - count: additionalCountries.length, - })} - -
    - )} -
    - ) - }, - }), - columnHelper.accessor("payment_providers", { - header: t("fields.paymentProviders"), - cell: (cell) => { - const providers = cell.getValue() - - const displayValue = providers - .slice(0, 2) - .map((p) => p.id) - .join(", ") - - const additionalProviders = providers.slice(2).map((c) => c.id) - - return ( -
    - {displayValue} - {additionalProviders.length > 0 && ( - - {additionalProviders.map((c) => ( -
  • {c}
  • - ))} - - } - > - - {t("general.plusCountMore", { - count: additionalProviders.length, - })} - -
    - )} -
    - ) - }, - }), - columnHelper.accessor("fulfillment_providers", { - header: t("fields.fulfillmentProviders"), - cell: (cell) => { - const providers = cell.getValue() - - const displayValue = providers - .slice(0, 2) - .map((p) => p.id) - .join(", ") - - const additionalProviders = providers.slice(2).map((c) => c.id) - - return ( -
    - {displayValue} - {additionalProviders.length > 0 && ( - - {additionalProviders.map((c) => ( -
  • {c}
  • - ))} - - } - > - - {t("general.plusCountMore", { - count: additionalProviders.length, - })} - -
    - )} -
    - ) - }, - }), + ...base, columnHelper.display({ id: "actions", cell: ({ row }) => { @@ -312,6 +137,6 @@ const useColumns = () => { }, }), ], - [t] + [base] ) } diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx index a8444ad838..532a5ee494 100644 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx +++ b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx @@ -1,14 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Product, SalesChannel } from "@medusajs/medusa" -import { - Button, - Checkbox, - FocusModal, - Hint, - Table, - Tooltip, - clx, -} from "@medusajs/ui" +import { Button, Checkbox, Hint, Table, Tooltip, clx } from "@medusajs/ui" import { PaginationState, RowSelectionState, @@ -26,7 +18,6 @@ import { useEffect, useMemo, useState } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { Form } from "../../../../components/common/form" import { ProductAvailabilityCell, ProductCollectionCell, @@ -37,13 +28,15 @@ import { import { OrderBy } from "../../../../components/filtering/order-by" import { Query } from "../../../../components/filtering/query" import { LocalizedTablePagination } from "../../../../components/localization/localized-table-pagination" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../components/route-modal" import { useQueryParams } from "../../../../hooks/use-query-params" import { queryClient } from "../../../../lib/medusa" type AddProductsToSalesChannelFormProps = { salesChannel: SalesChannel - subscribe: (state: boolean) => void - onSuccess: () => void } const AddProductsToSalesChannelSchema = zod.object({ @@ -54,10 +47,9 @@ const PAGE_SIZE = 50 export const AddProductsToSalesChannelForm = ({ salesChannel, - subscribe, - onSuccess, }: AddProductsToSalesChannelFormProps) => { const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -66,13 +58,7 @@ export const AddProductsToSalesChannelForm = ({ resolver: zodResolver(AddProductsToSalesChannelSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) + const { setValue } = form const { mutateAsync, isLoading: isMutating } = useAdminAddProductsToSalesChannel(salesChannel.id) @@ -93,15 +79,19 @@ export const AddProductsToSalesChannelForm = ({ const [rowSelection, setRowSelection] = useState({}) useEffect(() => { - form.setValue( + setValue( "product_ids", - Object.keys(rowSelection).filter((k) => rowSelection[k]) + Object.keys(rowSelection).filter((k) => rowSelection[k]), + { + shouldDirty: true, + shouldTouch: true, + } ) - }, [rowSelection]) + }, [rowSelection, setValue]) const params = useQueryParams(["q", "order"]) - const { products, count, isLoading } = useAdminProducts( + const { products, count } = useAdminProducts( { expand: "variants,sales_channels", ...params, @@ -148,36 +138,36 @@ export const AddProductsToSalesChannelForm = ({ * determine if they are added to the sales channel or not. */ queryClient.invalidateQueries(adminProductKeys.lists()) - onSuccess() + handleSuccess() }, } ) }) return ( -
    + - +
    {form.formState.errors.product_ids && ( {form.formState.errors.product_ids.message} )} - + - +
    -
    - + +
    @@ -251,9 +241,9 @@ export const AddProductsToSalesChannelForm = ({ pageSize={PAGE_SIZE} />
    - + - + ) } diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx index 39a42fabb5..1b28c6d5d3 100644 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx +++ b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx @@ -1,36 +1,21 @@ -import { FocusModal } from "@medusajs/ui" import { useAdminSalesChannel } from "medusa-react" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { AddProductsToSalesChannelForm } from "./components" export const SalesChannelAddProducts = () => { const { id } = useParams() - const [open, onOpenChange, subscribe] = useRouteModalState() - const { sales_channel, isLoading, isError, error } = useAdminSalesChannel(id!) - const handleSuccess = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - {isLoading || !sales_channel ? ( -
    Loading...
    - ) : ( - - )} -
    -
    + + {!isLoading && sales_channel && ( + + )} + ) } diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx index 086a18cdef..1eb5c31e73 100644 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx +++ b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx @@ -1,25 +1,15 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { - Button, - FocusModal, - Heading, - Input, - Switch, - Text, - Textarea, -} from "@medusajs/ui" +import { Button, Heading, Input, Switch, Text, Textarea } from "@medusajs/ui" import { useAdminCreateSalesChannel } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" -import { useNavigate } from "react-router-dom" import * as zod from "zod" -import { useEffect } from "react" import { Form } from "../../../../../components/common/form" - -type CreateSalesChannelFormProps = { - subscribe: (state: boolean) => void -} +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" const CreateSalesChannelSchema = zod.object({ name: zod.string().min(1), @@ -27,9 +17,10 @@ const CreateSalesChannelSchema = zod.object({ enabled: zod.boolean(), }) -export const CreateSalesChannelForm = ({ - subscribe, -}: CreateSalesChannelFormProps) => { +export const CreateSalesChannelForm = () => { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + const form = useForm>({ defaultValues: { name: "", @@ -40,17 +31,6 @@ export const CreateSalesChannelForm = ({ }) const { mutateAsync, isLoading } = useAdminCreateSalesChannel() - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - - const { t } = useTranslation() - const navigate = useNavigate() - const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync( { @@ -60,31 +40,31 @@ export const CreateSalesChannelForm = ({ }, { onSuccess: ({ sales_channel }) => { - navigate(`../${sales_channel.id}`) + handleSuccess(`../${sales_channel.id}`) }, } ) }) return ( -
    + - +
    - + - +
    -
    - + +
    @@ -153,8 +133,8 @@ export const CreateSalesChannelForm = ({ />
    - + - + ) } diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/sales-channel-create.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/sales-channel-create.tsx index 6275c0b84b..d4abcb0126 100644 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/sales-channel-create.tsx +++ b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/sales-channel-create.tsx @@ -1,15 +1,10 @@ -import { FocusModal } from "@medusajs/ui" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { CreateSalesChannelForm } from "./components/create-sales-channel-form" export const SalesChannelCreate = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - return ( - - - - - + + + ) } diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx index 5ccaf1dbd9..af2de9816d 100644 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx +++ b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx @@ -1,18 +1,19 @@ import { zodResolver } from "@hookform/resolvers/zod" import { SalesChannel } from "@medusajs/medusa" -import { Button, Drawer, Input, Switch, Textarea } from "@medusajs/ui" +import { Button, Input, Switch, Textarea } from "@medusajs/ui" import { useAdminUpdateSalesChannel } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { useEffect } from "react" import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" type EditSalesChannelFormProps = { salesChannel: SalesChannel - subscribe: (state: boolean) => void - onSuccess: () => void } const EditSalesChannelSchema = zod.object({ @@ -23,9 +24,10 @@ const EditSalesChannelSchema = zod.object({ export const EditSalesChannelForm = ({ salesChannel, - subscribe, - onSuccess, }: EditSalesChannelFormProps) => { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + const form = useForm>({ defaultValues: { name: salesChannel.name, @@ -35,16 +37,6 @@ export const EditSalesChannelForm = ({ resolver: zodResolver(EditSalesChannelSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - - const { t } = useTranslation() - const { mutateAsync, isLoading } = useAdminUpdateSalesChannel(salesChannel.id) const handleSubmit = form.handleSubmit(async (values) => { @@ -56,19 +48,19 @@ export const EditSalesChannelForm = ({ }, { onSuccess: () => { - onSuccess() + handleSuccess() }, } ) }) return ( -
    + - + - - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx index dfbeeab150..8a0d2be965 100644 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx @@ -1,41 +1,31 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminSalesChannel } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { EditSalesChannelForm } from "./components/edit-sales-channel-form" export const SalesChannelEdit = () => { const { id } = useParams() const { t } = useTranslation() - const [open, onOpenChange, subscribe] = useRouteModalState() - const { sales_channel, isLoading, isError, error } = useAdminSalesChannel(id!) - const onSuccess = () => { - onOpenChange(false, true) - } + const { sales_channel, isLoading, isError, error } = useAdminSalesChannel(id!) if (isError) { throw error } return ( - - - - - {t("salesChannels.editSalesChannel")} - - - {!isLoading && sales_channel && ( - - )} - - + + + + {t("salesChannels.editSalesChannel")} + + + {!isLoading && sales_channel && ( + + )} + ) } diff --git a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/components/add-currencies-form/add-currencies-form.tsx b/packages/admin-next/dashboard/src/routes/store/store-add-currencies/components/add-currencies-form/add-currencies-form.tsx index e9359eb10e..6576e562f7 100644 --- a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/components/add-currencies-form/add-currencies-form.tsx +++ b/packages/admin-next/dashboard/src/routes/store/store-add-currencies/components/add-currencies-form/add-currencies-form.tsx @@ -3,7 +3,6 @@ import { Badge, Button, Checkbox, - FocusModal, Hint, StatusBadge, Table, @@ -19,11 +18,18 @@ import { useReactTable, } from "@tanstack/react-table" import { useAdminCurrencies, useAdminUpdateStore } from "medusa-react" -import { FormEvent, useMemo, useState } from "react" +import { useEffect, useMemo, useState } from "react" import { useTranslation } from "react-i18next" import * as zod from "zod" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" import { OrderBy } from "../../../../../components/filtering/order-by" import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" import { useHandleTableScroll } from "../../../../../hooks/use-handle-table-scroll" import { useQueryParams } from "../../../../../hooks/use-query-params" @@ -38,9 +44,18 @@ const AddCurrenciesSchema = zod.object({ const PAGE_SIZE = 50 export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => { - const [errorMessage, setErrorMessage] = useState<{ - currencies?: string | undefined - }>({}) + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + + const form = useForm>({ + defaultValues: { + currencies: [], + }, + resolver: zodResolver(AddCurrenciesSchema), + }) + + const { setValue } = form + const [{ pageIndex, pageSize }, setPagination] = useState({ pageIndex: 0, pageSize: PAGE_SIZE, @@ -56,8 +71,16 @@ export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => { const [rowSelection, setRowSelection] = useState({}) + useEffect(() => { + const ids = Object.keys(rowSelection) + setValue("currencies", ids, { + shouldDirty: true, + shouldTouch: true, + }) + }, [rowSelection, setValue]) + const params = useQueryParams(["order"]) - const { currencies, count, isLoading, isError, error } = useAdminCurrencies({ + const { currencies, count, isError, error } = useAdminCurrencies({ limit: PAGE_SIZE, offset: pageIndex * PAGE_SIZE, ...params, @@ -83,152 +106,143 @@ export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => { manualPagination: true, }) - const { t } = useTranslation() - const { mutateAsync, isLoading: isMutating } = useAdminUpdateStore() const { handleScroll, isScrolled, tableContainerRef } = useHandleTableScroll() - const handleSubmit = async (e: FormEvent) => { - e.preventDefault() - - const ids = Object.keys(rowSelection) - - try { - AddCurrenciesSchema.parse({ - currencies: ids, - }) - - setErrorMessage({}) - } catch (err) { - if (err instanceof zod.ZodError) { - setErrorMessage(err.flatten().fieldErrors) - } - - return - } - + const handleSubmit = form.handleSubmit(async (data) => { const currencies = Array.from( - new Set([...ids, ...preSelectedRows]) + new Set([...data.currencies, ...preSelectedRows]) ) as string[] - await mutateAsync({ - currencies, - }) - } + await mutateAsync( + { + currencies, + }, + { + onSuccess: () => { + handleSuccess() + }, + } + ) + }) if (isError) { throw error } return ( -
    - -
    -
    - {errorMessage.currencies && ( - {errorMessage.currencies} - )} -
    -
    - - - - -
    -
    -
    - -
    -
    -
    - -
    -
    -
    - - + + +
    +
    + {form.formState.errors.currencies && ( + + {form.formState.errors.currencies.message} + )} - > - {table.getHeaderGroups().map((headerGroup) => { - return ( +
    +
    + + + + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + {table.getHeaderGroups().map((headerGroup) => { + return ( + + {headerGroup.headers.map((header) => { + return ( + + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ) + })} + + ) + })} + + + {table.getRowModel().rows.map((row) => ( - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
    -
    -
    - -
    -
    -
    + ))} + + +
    +
    + +
    +
    + +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/store-add-currencies.tsx b/packages/admin-next/dashboard/src/routes/store/store-add-currencies/store-add-currencies.tsx index 15aa12dbe4..18559e0c97 100644 --- a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/store-add-currencies.tsx +++ b/packages/admin-next/dashboard/src/routes/store/store-add-currencies/store-add-currencies.tsx @@ -1,42 +1,17 @@ -import { FocusModal } from "@medusajs/ui" import { useAdminStore } from "medusa-react" -import { useEffect, useState } from "react" -import { useNavigate } from "react-router-dom" +import { RouteFocusModal } from "../../../components/route-modal" import { AddCurrenciesForm } from "./components/add-currencies-form/add-currencies-form" export const StoreAddCurrencies = () => { - const [open, setOpen] = useState(false) - const navigate = useNavigate() - const { store, isLoading, isError, error } = useAdminStore() - useEffect(() => { - setOpen(true) - }, []) - - const onOpenChange = (open: boolean) => { - if (!open) { - setTimeout(() => { - navigate(`/settings/store`, { replace: true }) - }, 200) - } - - setOpen(open) - } - if (isError) { throw error } return ( - - - {isLoading || !store ? ( -
    Loading...
    - ) : ( - - )} -
    -
    + + {!isLoading && store && } + ) } diff --git a/packages/admin-next/dashboard/src/routes/store/store-edit/components/edit-store-form/edit-store-form.tsx b/packages/admin-next/dashboard/src/routes/store/store-edit/components/edit-store-form/edit-store-form.tsx index 30322a83f0..a47ee40c78 100644 --- a/packages/admin-next/dashboard/src/routes/store/store-edit/components/edit-store-form/edit-store-form.tsx +++ b/packages/admin-next/dashboard/src/routes/store/store-edit/components/edit-store-form/edit-store-form.tsx @@ -1,15 +1,18 @@ import { zodResolver } from "@hookform/resolvers/zod" import type { Store } from "@medusajs/medusa" -import { Button, Drawer, Input } from "@medusajs/ui" +import { Button, Input } from "@medusajs/ui" import { useAdminUpdateStore } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" type EditStoreFormProps = { store: Store - onSuccess: () => void } const EditStoreSchema = zod.object({ @@ -22,8 +25,9 @@ const EditStoreSchema = zod.object({ invite_link_template: zod.union([zod.literal(""), zod.string().trim().url()]), }) -export const EditStoreForm = ({ store, onSuccess }: EditStoreFormProps) => { - const { mutateAsync, isLoading } = useAdminUpdateStore() +export const EditStoreForm = ({ store }: EditStoreFormProps) => { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { @@ -35,7 +39,7 @@ export const EditStoreForm = ({ store, onSuccess }: EditStoreFormProps) => { resolver: zodResolver(EditStoreSchema), }) - const { t } = useTranslation() + const { mutateAsync, isLoading } = useAdminUpdateStore() const handleSubmit = form.handleSubmit(async (values) => { mutateAsync( @@ -47,16 +51,16 @@ export const EditStoreForm = ({ store, onSuccess }: EditStoreFormProps) => { }, { onSuccess: () => { - onSuccess() + handleSuccess() }, } ) }) return ( -
    + - +
    { )} />
    -
    - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/store/store-edit/store-edit.tsx b/packages/admin-next/dashboard/src/routes/store/store-edit/store-edit.tsx index 313cead0b5..8f9cd33f7d 100644 --- a/packages/admin-next/dashboard/src/routes/store/store-edit/store-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/store/store-edit/store-edit.tsx @@ -1,30 +1,13 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminStore } from "medusa-react" -import { useEffect, useState } from "react" import { useTranslation } from "react-i18next" -import { json, useNavigate } from "react-router-dom" +import { json } from "react-router-dom" +import { RouteDrawer } from "../../../components/route-modal" import { EditStoreForm } from "./components/edit-store-form/edit-store-form" export const StoreEdit = () => { - const [open, setOpen] = useState(false) - const { store, isLoading, isError, error } = useAdminStore() - - const navigate = useNavigate() const { t } = useTranslation() - - useEffect(() => { - setOpen(true) - }, []) - - const onOpenChange = (open: boolean) => { - if (!open) { - setTimeout(() => { - navigate(`/settings/store`, { replace: true }) - }, 200) - } - - setOpen(open) - } + const { store, isLoading, isError, error } = useAdminStore() if (isError) { throw error @@ -35,15 +18,11 @@ export const StoreEdit = () => { } return ( - - - - {t("store.editStore")} - - {store && ( - onOpenChange(false)} /> - )} - - + + + {t("store.editStore")} + + {store && } + ) } diff --git a/packages/admin-next/dashboard/src/routes/users/user-edit/components/edit-user-form/edit-user-form.tsx b/packages/admin-next/dashboard/src/routes/users/user-edit/components/edit-user-form/edit-user-form.tsx index 8a3ef25056..b9f677d805 100644 --- a/packages/admin-next/dashboard/src/routes/users/user-edit/components/edit-user-form/edit-user-form.tsx +++ b/packages/admin-next/dashboard/src/routes/users/user-edit/components/edit-user-form/edit-user-form.tsx @@ -1,17 +1,19 @@ import { zodResolver } from "@hookform/resolvers/zod" import { User } from "@medusajs/medusa" -import { Button, Drawer, Input } from "@medusajs/ui" +import { Button, Input } from "@medusajs/ui" import { useAdminUpdateUser } from "medusa-react" -import { useEffect } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" + import { Form } from "../../../../../components/common/form" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" type EditUserFormProps = { user: Omit - subscribe: (state: boolean) => void - onSuccessfulSubmit: () => void } const EditUserFormSchema = zod.object({ @@ -19,11 +21,10 @@ const EditUserFormSchema = zod.object({ last_name: zod.string().optional(), }) -export const EditUserForm = ({ - user, - subscribe, - onSuccessfulSubmit, -}: EditUserFormProps) => { +export const EditUserForm = ({ user }: EditUserFormProps) => { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + const form = useForm>({ defaultValues: { first_name: user.first_name || "", @@ -32,33 +33,23 @@ export const EditUserForm = ({ resolver: zodResolver(EditUserFormSchema), }) - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty]) - - const { t } = useTranslation() - const { mutateAsync, isLoading } = useAdminUpdateUser(user.id) const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync(values, { onSuccess: () => { - onSuccessfulSubmit() + handleSuccess() }, }) }) return ( -
    + - + - - + +
    - + - +
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/users/user-edit/user-edit.tsx b/packages/admin-next/dashboard/src/routes/users/user-edit/user-edit.tsx index 1ff3bed6b8..8d51a1f0b7 100644 --- a/packages/admin-next/dashboard/src/routes/users/user-edit/user-edit.tsx +++ b/packages/admin-next/dashboard/src/routes/users/user-edit/user-edit.tsx @@ -1,39 +1,25 @@ -import { Drawer, Heading } from "@medusajs/ui" +import { Heading } from "@medusajs/ui" import { useAdminUser } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteDrawer } from "../../../components/route-modal" import { EditUserForm } from "./components/edit-user-form" export const UserEdit = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - const { t } = useTranslation() const { id } = useParams() const { user, isLoading, isError, error } = useAdminUser(id!) - const handleSuccessfulSubmit = () => { - onOpenChange(false, true) - } - if (isError) { throw error } return ( - - - - {t("users.editUser")} - - {!isLoading && user && ( - - )} - - + + + {t("users.editUser")} + + {!isLoading && user && } + ) } diff --git a/packages/admin-next/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx b/packages/admin-next/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx index fb32519298..da1df79768 100644 --- a/packages/admin-next/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx +++ b/packages/admin-next/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx @@ -4,7 +4,6 @@ import { Invite } from "@medusajs/medusa" import { Button, Container, - FocusModal, Heading, Input, Select, @@ -30,7 +29,7 @@ import { useAdminResendInvite, useAdminStore, } from "medusa-react" -import { useEffect, useMemo } from "react" +import { useMemo } from "react" import { useForm } from "react-hook-form" import { Trans, useTranslation } from "react-i18next" import * as zod from "zod" @@ -38,10 +37,7 @@ import { ActionMenu } from "../../../../../components/common/action-menu" import { NoRecords } from "../../../../../components/common/empty-table-content" import { Form } from "../../../../../components/common/form" import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" - -type InviteUserFormProps = { - subscribe: (state: boolean) => void -} +import { RouteFocusModal } from "../../../../../components/route-modal" enum UserRole { MEMBER = "member", @@ -56,7 +52,9 @@ const InviteUserSchema = zod.object({ const PAGE_SIZE = 10 -export const InviteUserForm = ({ subscribe }: InviteUserFormProps) => { +export const InviteUserForm = () => { + const { t } = useTranslation() + const form = useForm>({ defaultValues: { user: "", @@ -64,15 +62,6 @@ export const InviteUserForm = ({ subscribe }: InviteUserFormProps) => { }, resolver: zodResolver(InviteUserSchema), }) - const { mutateAsync, isLoading: isMutating } = useAdminCreateInvite() - - const { - formState: { isDirty }, - } = form - - useEffect(() => { - subscribe(isDirty) - }, [isDirty, subscribe]) const { invites, isLoading, isError, error } = useAdminInvites() const count = invites?.length ?? 0 @@ -89,7 +78,7 @@ export const InviteUserForm = ({ subscribe }: InviteUserFormProps) => { getPaginationRowModel: getPaginationRowModel(), }) - const { t } = useTranslation() + const { mutateAsync, isLoading: isMutating } = useAdminCreateInvite() const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync( @@ -110,13 +99,13 @@ export const InviteUserForm = ({ subscribe }: InviteUserFormProps) => { } return ( -
    + - - + +
    @@ -249,9 +238,9 @@ export const InviteUserForm = ({ subscribe }: InviteUserFormProps) => {
    -
    + - +
    ) } diff --git a/packages/admin-next/dashboard/src/routes/users/user-invite/user-invite.tsx b/packages/admin-next/dashboard/src/routes/users/user-invite/user-invite.tsx index d8f0f2d485..23432b8d22 100644 --- a/packages/admin-next/dashboard/src/routes/users/user-invite/user-invite.tsx +++ b/packages/admin-next/dashboard/src/routes/users/user-invite/user-invite.tsx @@ -1,15 +1,10 @@ -import { FocusModal } from "@medusajs/ui" -import { useRouteModalState } from "../../../hooks/use-route-modal-state" +import { RouteFocusModal } from "../../../components/route-modal" import { InviteUserForm } from "./components/invite-user-form/invite-user-form" export const UserInvite = () => { - const [open, onOpenChange, subscribe] = useRouteModalState() - return ( - - - - - + + + ) }