feat(dashboard): Submit forms on Cmd + Enter (#9623)

**What**
- Changes all forms to only submit on Cmd/Ctrl + Enter instead of just Enter.
- Cleans up the position of submit/cancel buttons in many FocusModals that still had them in the header.
- Fixes responsiveness of multiple forms
- Removes the SplitView component, and replaces its usages with StackedDrawer/Modal to align the UX across the project.

Resolves CC-103, CC-535
This commit is contained in:
Kasper Fabricius Kristensen authored and GitHub committed 2024-10-17 09:38:12 +00:00
1 parent 0be50059f2
commit 1d540af783
120 files changed
+880 -798

No files matched your search

@@ -24,6 +24,7 @@ import { Form } from "../../common/form"
import { InlineTip } from "../../common/inline-tip" import { InlineTip } from "../../common/inline-tip"
import { Skeleton } from "../../common/skeleton" import { Skeleton } from "../../common/skeleton"
import { RouteDrawer, useRouteModal } from "../../modals" import { RouteDrawer, useRouteModal } from "../../modals"
import { KeyboundForm } from "../../utilities/keybound-form"
type MetaDataSubmitHook<TRes> = ( type MetaDataSubmitHook<TRes> = (
params: { metadata?: Record<string, any> | null }, params: { metadata?: Record<string, any> | null },
@@ -125,7 +126,7 @@ const InnerForm = <TRes,>({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -277,7 +278,7 @@ const InnerForm = <TRes,>({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -32,7 +32,7 @@ import {
} from "react" } from "react"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { genericForwardRef } from "../../common/generic-forward-ref" import { genericForwardRef } from "../../utilities/generic-forward-ref"
type ComboboxOption = { type ComboboxOption = {
value: string value: string
@@ -1 +0,0 @@
export * from "./split-view"
@@ -1,103 +0,0 @@
import { Button, clx } from "@medusajs/ui"
import * as Dialog from "@radix-ui/react-dialog"
import {
ComponentPropsWithoutRef,
PropsWithChildren,
createContext,
useContext,
useRef,
} from "react"
type SplitViewContextValue = {
open: boolean
onOpenChange: (open: boolean) => void
}
const SplitViewContext = createContext<SplitViewContextValue | null>(null)
const useSplitViewContext = () => {
const context = useContext(SplitViewContext)
if (!context) {
throw new Error("useSplitViewContext must be used within a SplitView")
}
return context
}
type SplitViewProps = PropsWithChildren<{
open?: boolean
onOpenChange?: (open: boolean) => void
}>
const Root = ({ open, onOpenChange, children }: SplitViewProps) => {
const containerRef = useRef<HTMLDivElement>(null)
return (
<Dialog.Root open={open} onOpenChange={onOpenChange}>
<div ref={containerRef} className="relative size-full overflow-hidden">
{children}
</div>
</Dialog.Root>
)
}
const Content = ({
children,
className,
...props
}: ComponentPropsWithoutRef<"div">) => {
return (
<div
className={clx("relative h-full overflow-y-auto", className)}
{...props}
>
{children}
</div>
)
}
const Drawer = ({ children }: PropsWithChildren) => {
return (
<div>
<Dialog.Overlay
className={clx(
"bg-ui-bg-base absolute inset-0 opacity-40",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
)}
/>
<Dialog.Content
className={clx(
"bg-ui-bg-base border-ui-border-base absolute inset-y-0 right-0 flex w-full max-w-[calc(100%-128px)] flex-1 flex-col border-l focus:outline-none md:max-w-[80%] lg:max-w-[50%]",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-right-1/2 data-[state=open]:slide-in-from-right-1/2 duration-200"
)}
>
{children}
</Dialog.Content>
</div>
)
}
const Close = ({
variant = "secondary",
size = "small",
children,
...props
}: ComponentPropsWithoutRef<typeof Button>) => {
return (
<Dialog.Close asChild>
<Button size={size} variant={variant} {...props}>
{children}
</Button>
</Dialog.Close>
)
}
/**
* SplitView is a layout component that allows you to create a split view layout within a FocusModal.
*/
export const SplitView = Object.assign(Root, {
Content,
Drawer,
Close,
})
@@ -0,0 +1 @@
export * from "./keybound-form"
@@ -0,0 +1,35 @@
import React from "react"
/**
* A form that can only be submitted when using the meta or control key.
*/
export const KeyboundForm = React.forwardRef<
HTMLFormElement,
React.FormHTMLAttributes<HTMLFormElement>
>(({ onSubmit, onKeyDown, ...rest }, ref) => {
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
onSubmit?.(event)
}
const handleKeyDown = (event: React.KeyboardEvent<HTMLFormElement>) => {
if (event.key === "Enter") {
event.preventDefault()
if (event.metaKey || event.ctrlKey) {
handleSubmit(event)
}
}
}
return (
<form
{...rest}
onSubmit={handleSubmit}
onKeyDown={onKeyDown ?? handleKeyDown}
ref={ref}
/>
)
})
KeyboundForm.displayName = "KeyboundForm"
@@ -0,0 +1 @@
export * from "./visually-hidden"
@@ -0,0 +1,5 @@
import { PropsWithChildren } from "react"
export const VisuallyHidden = ({ children }: PropsWithChildren) => {
return <span className="sr-only">{children}</span>
}
@@ -1877,6 +1877,7 @@
}, },
"edit": { "edit": {
"header": "Edit Campaign", "header": "Edit Campaign",
"description": "Edit the details of the campaign.",
"successToast": "Campaign '{{name}}' was successfully updated." "successToast": "Campaign '{{name}}' was successfully updated."
}, },
"configuration": { "configuration": {
@@ -1888,6 +1889,8 @@
} }
}, },
"create": { "create": {
"title": "Create Campaign",
"description": "Create a promotional campaign.",
"hint": "Create a promotional campaign.", "hint": "Create a promotional campaign.",
"header": "Create Campaign", "header": "Create Campaign",
"successToast": "Campaign '{{name}}' was successfully created." "successToast": "Campaign '{{name}}' was successfully created."
@@ -2301,9 +2304,12 @@
}, },
"edit": { "edit": {
"header": "Edit API Key", "header": "Edit API Key",
"description": "Edit the API key's title.",
"successToast": "API key {{title}} was successfully updated." "successToast": "API key {{title}} was successfully updated."
}, },
"salesChannels": { "salesChannels": {
"title": "Add Sales Channels",
"description": "Add the sales channels that the API key should be limited to.",
"successToast_one": "{{count}} sales channel was successfully added to the API key.", "successToast_one": "{{count}} sales channel was successfully added to the API key.",
"successToast_other": "{{count}} sales channels were successfully added to the API key.", "successToast_other": "{{count}} sales channels were successfully added to the API key.",
"alreadyAddedTooltip": "The sales channel has already been added to the API key.", "alreadyAddedTooltip": "The sales channel has already been added to the API key.",
@@ -12,6 +12,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateApiKey } from "../../../../../hooks/api/api-keys" import { useCreateApiKey } from "../../../../../hooks/api/api-keys"
import { ApiKeyType } from "../../../common/constants" import { ApiKeyType } from "../../../common/constants"
@@ -97,36 +98,29 @@ export const ApiKeyCreateForm = ({ keyType }: ApiKeyCreateFormProps) => {
return ( return (
<Fragment> <Fragment>
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<RouteFocusModal.Header> <RouteFocusModal.Header />
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex flex-1 flex-col overflow-hidden"> <RouteFocusModal.Body className="flex flex-1 flex-col overflow-hidden">
<div className="flex flex-1 flex-col items-center overflow-y-auto"> <div className="flex flex-1 flex-col items-center overflow-y-auto">
<div className="flex w-full max-w-[720px] flex-col gap-y-8 px-2 py-16"> <div className="flex w-full max-w-[720px] flex-col gap-y-8 px-2 py-16">
<div> <div>
<RouteFocusModal.Title asChild>
<Heading> <Heading>
{keyType === ApiKeyType.PUBLISHABLE {keyType === ApiKeyType.PUBLISHABLE
? t("apiKeyManagement.create.createPublishableHeader") ? t("apiKeyManagement.create.createPublishableHeader")
: t("apiKeyManagement.create.createSecretHeader")} : t("apiKeyManagement.create.createSecretHeader")}
</Heading> </Heading>
</RouteFocusModal.Title>
<RouteFocusModal.Description asChild>
<Text size="small" className="text-ui-fg-subtle"> <Text size="small" className="text-ui-fg-subtle">
{keyType === ApiKeyType.PUBLISHABLE {keyType === ApiKeyType.PUBLISHABLE
? t("apiKeyManagement.create.createPublishableHint") ? t("apiKeyManagement.create.createPublishableHint")
: t("apiKeyManagement.create.createSecretHint")} : t("apiKeyManagement.create.createSecretHint")}
</Text> </Text>
</RouteFocusModal.Description>
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<Form.Field <Form.Field
@@ -148,7 +142,19 @@ export const ApiKeyCreateForm = ({ keyType }: ApiKeyCreateFormProps) => {
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> <RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
<Prompt variant="confirmation" open={!!createdKey}> <Prompt variant="confirmation" open={!!createdKey}>
<Prompt.Content className="w-fit max-w-[42.5%]"> <Prompt.Content className="w-fit max-w-[42.5%]">
@@ -2,6 +2,7 @@ import { Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { useParams } from "react-router-dom" import { useParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/modals" import { RouteDrawer } from "../../../components/modals"
import { VisuallyHidden } from "../../../components/utilities/visually-hidden"
import { useApiKey } from "../../../hooks/api/api-keys" import { useApiKey } from "../../../hooks/api/api-keys"
import { EditApiKeyForm } from "./components/edit-api-key-form" import { EditApiKeyForm } from "./components/edit-api-key-form"
@@ -18,7 +19,14 @@ export const ApiKeyManagementEdit = () => {
return ( return (
<RouteDrawer> <RouteDrawer>
<RouteDrawer.Header> <RouteDrawer.Header>
<RouteDrawer.Title asChild>
<Heading>{t("apiKeyManagement.edit.header")}</Heading> <Heading>{t("apiKeyManagement.edit.header")}</Heading>
</RouteDrawer.Title>
<RouteDrawer.Description asChild>
<VisuallyHidden>
{t("apiKeyManagement.edit.description")}
</VisuallyHidden>
</RouteDrawer.Description>
</RouteDrawer.Header> </RouteDrawer.Header>
{!isLoading && !!api_key && <EditApiKeyForm apiKey={api_key} />} {!isLoading && !!api_key && <EditApiKeyForm apiKey={api_key} />}
</RouteDrawer> </RouteDrawer>
@@ -7,6 +7,7 @@ import * as zod from "zod"
import { ApiKeyDTO } from "@medusajs/types" import { ApiKeyDTO } from "@medusajs/types"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateApiKey } from "../../../../../hooks/api/api-keys" import { useUpdateApiKey } from "../../../../../hooks/api/api-keys"
type EditApiKeyFormProps = { type EditApiKeyFormProps = {
@@ -48,7 +49,7 @@ export const EditApiKeyForm = ({ apiKey }: EditApiKeyFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
<Form.Field <Form.Field
@@ -80,7 +81,7 @@ export const EditApiKeyForm = ({ apiKey }: EditApiKeyFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -16,9 +16,12 @@ import {
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table" import { DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { VisuallyHidden } from "../../../../../components/utilities/visually-hidden"
import { useBatchAddSalesChannelsToApiKey } from "../../../../../hooks/api/api-keys" import { useBatchAddSalesChannelsToApiKey } from "../../../../../hooks/api/api-keys"
import { useSalesChannels } from "../../../../../hooks/api/sales-channels" import { useSalesChannels } from "../../../../../hooks/api/sales-channels"
import { useSalesChannelTableColumns } from "../../../../../hooks/table/columns/use-sales-channel-table-columns" import { useSalesChannelTableColumns } from "../../../../../hooks/table/columns/use-sales-channel-table-columns"
import { useSalesChannelTableFilters } from "../../../../../hooks/table/filters"
import { useSalesChannelTableQuery } from "../../../../../hooks/table/query/use-sales-channel-table-query" import { useSalesChannelTableQuery } from "../../../../../hooks/table/query/use-sales-channel-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table" import { useDataTable } from "../../../../../hooks/use-data-table"
@@ -58,6 +61,7 @@ export const ApiKeySalesChannelsForm = ({
}) })
const columns = useColumns() const columns = useColumns()
const filters = useSalesChannelTableFilters()
const { sales_channels, count, isLoading } = useSalesChannels( const { sales_channels, count, isLoading } = useSalesChannels(
{ ...searchParams }, { ...searchParams },
@@ -114,33 +118,33 @@ export const ApiKeySalesChannelsForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden"
>
<RouteFocusModal.Header> <RouteFocusModal.Header>
<RouteFocusModal.Title asChild>
<VisuallyHidden>
{t("apiKeyManagement.salesChannels.title")}
</VisuallyHidden>
</RouteFocusModal.Title>
<RouteFocusModal.Description asChild>
<VisuallyHidden>
{t("apiKeyManagement.salesChannels.description")}
</VisuallyHidden>
</RouteFocusModal.Description>
<div className="flex items-center justify-end gap-x-2"> <div className="flex items-center justify-end gap-x-2">
{form.formState.errors.sales_channel_ids && ( {form.formState.errors.sales_channel_ids && (
<Hint variant="error"> <Hint variant="error">
{form.formState.errors.sales_channel_ids.message} {form.formState.errors.sales_channel_ids.message}
</Hint> </Hint>
)} )}
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</div> </div>
</RouteFocusModal.Header> </RouteFocusModal.Header>
<RouteFocusModal.Body> <RouteFocusModal.Body className="flex flex-1 flex-col overflow-auto">
<DataTable <DataTable
table={table} table={table}
columns={columns} columns={columns}
count={count} count={count}
pageSize={PAGE_SIZE} pageSize={PAGE_SIZE}
filters={filters}
pagination pagination
search="autofocus" search="autofocus"
isLoading={isLoading} isLoading={isLoading}
@@ -154,7 +158,19 @@ export const ApiKeySalesChannelsForm = ({
}} }}
/> />
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> <RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -13,6 +13,7 @@ import { useTranslation } from "react-i18next"
import * as zod from "zod" import * as zod from "zod"
import { RouteFocusModal, useRouteModal } from "../../../../components/modals" import { RouteFocusModal, useRouteModal } from "../../../../components/modals"
import { DataTable } from "../../../../components/table/data-table" import { DataTable } from "../../../../components/table/data-table"
import { KeyboundForm } from "../../../../components/utilities/keybound-form"
import { useAddOrRemoveCampaignPromotions } from "../../../../hooks/api/campaigns" import { useAddOrRemoveCampaignPromotions } from "../../../../hooks/api/campaigns"
import { usePromotions } from "../../../../hooks/api/promotions" import { usePromotions } from "../../../../hooks/api/promotions"
import { usePromotionTableColumns } from "../../../../hooks/table/columns/use-promotion-table-columns" import { usePromotionTableColumns } from "../../../../hooks/table/columns/use-promotion-table-columns"
@@ -109,7 +110,7 @@ export const AddCampaignPromotionsForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
@@ -120,14 +121,6 @@ export const AddCampaignPromotionsForm = ({
{form.formState.errors.promotion_ids.message} {form.formState.errors.promotion_ids.message}
</Hint> </Hint>
)} )}
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</div> </div>
</RouteFocusModal.Header> </RouteFocusModal.Header>
<RouteFocusModal.Body className="flex size-full flex-col overflow-y-auto"> <RouteFocusModal.Body className="flex size-full flex-col overflow-y-auto">
@@ -148,7 +141,19 @@ export const AddCampaignPromotionsForm = ({
}} }}
/> />
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> <RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -18,7 +18,9 @@ export const CampaignBudgetEdit = () => {
return ( return (
<RouteDrawer> <RouteDrawer>
<RouteDrawer.Header> <RouteDrawer.Header>
<RouteDrawer.Title asChild>
<Heading>{t("campaigns.budget.edit.header")}</Heading> <Heading>{t("campaigns.budget.edit.header")}</Heading>
</RouteDrawer.Title>
</RouteDrawer.Header> </RouteDrawer.Header>
{!isLoading && campaign && <EditCampaignBudgetForm campaign={campaign} />} {!isLoading && campaign && <EditCampaignBudgetForm campaign={campaign} />}
@@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next"
import * as zod from "zod" import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateCampaign } from "../../../../../hooks/api/campaigns" import { useUpdateCampaign } from "../../../../../hooks/api/campaigns"
import { getCurrencySymbol } from "../../../../../lib/data/currencies" import { getCurrencySymbol } from "../../../../../lib/data/currencies"
@@ -58,7 +59,7 @@ export const EditCampaignBudgetForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
<Form.Field <Form.Field
@@ -131,7 +132,7 @@ export const EditCampaignBudgetForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -7,6 +7,7 @@ import { z } from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateCampaign } from "../../../../../hooks/api/campaigns" import { useUpdateCampaign } from "../../../../../hooks/api/campaigns"
type CampaignConfigurationFormProps = { type CampaignConfigurationFormProps = {
@@ -59,7 +60,7 @@ export const CampaignConfigurationForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
<Form.Field <Form.Field
@@ -127,7 +128,7 @@ export const CampaignConfigurationForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -4,20 +4,22 @@ import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import * as zod from "zod" import * as zod from "zod"
import { CampaignBudgetTypeValues } from "@medusajs/types"
import { import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { VisuallyHidden } from "../../../../../components/utilities/visually-hidden"
import { useCreateCampaign } from "../../../../../hooks/api/campaigns" import { useCreateCampaign } from "../../../../../hooks/api/campaigns"
import { CreateCampaignFormFields } from "../../../common/components/create-campaign-form-fields" import { CreateCampaignFormFields } from "../../../common/components/create-campaign-form-fields"
import { DEFAULT_CAMPAIGN_VALUES } from "../../../common/constants"
export const CreateCampaignSchema = zod.object({ export const CreateCampaignSchema = zod.object({
name: zod.string().min(1), name: zod.string().min(1),
description: zod.string().optional(), description: zod.string().optional(),
campaign_identifier: zod.string().min(1), campaign_identifier: zod.string().min(1),
starts_at: zod.date().optional(), starts_at: zod.date().nullable(),
ends_at: zod.date().optional(), ends_at: zod.date().nullable(),
budget: zod.object({ budget: zod.object({
limit: zod.number().min(0).nullish(), limit: zod.number().min(0).nullish(),
type: zod.enum(["spend", "usage"]), type: zod.enum(["spend", "usage"]),
@@ -25,24 +27,13 @@ export const CreateCampaignSchema = zod.object({
}), }),
}) })
export const defaultCampaignValues = {
name: "",
description: "",
campaign_identifier: "",
budget: {
type: "usage" as CampaignBudgetTypeValues,
currency_code: null,
limit: null,
},
}
export const CreateCampaignForm = () => { export const CreateCampaignForm = () => {
const { t } = useTranslation() const { t } = useTranslation()
const { handleSuccess } = useRouteModal() const { handleSuccess } = useRouteModal()
const { mutateAsync, isPending } = useCreateCampaign() const { mutateAsync, isPending } = useCreateCampaign()
const form = useForm<zod.infer<typeof CreateCampaignSchema>>({ const form = useForm<zod.infer<typeof CreateCampaignSchema>>({
defaultValues: defaultCampaignValues, defaultValues: DEFAULT_CAMPAIGN_VALUES,
resolver: zodResolver(CreateCampaignSchema), resolver: zodResolver(CreateCampaignSchema),
}) })
@@ -78,11 +69,18 @@ export const CreateCampaignForm = () => {
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex size-full flex-col overflow-hidden" className="flex size-full flex-col overflow-hidden"
> >
<RouteFocusModal.Header /> <RouteFocusModal.Header>
<RouteFocusModal.Title asChild>
<VisuallyHidden>{t("campaigns.create.title")}</VisuallyHidden>
</RouteFocusModal.Title>
<RouteFocusModal.Description asChild>
<VisuallyHidden>{t("campaigns.create.description")}</VisuallyHidden>
</RouteFocusModal.Description>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex size-full flex-col items-center overflow-auto py-16"> <RouteFocusModal.Body className="flex size-full flex-col items-center overflow-auto py-16">
<CreateCampaignFormFields form={form} /> <CreateCampaignFormFields form={form} />
</RouteFocusModal.Body> </RouteFocusModal.Body>
@@ -103,7 +101,7 @@ export const CreateCampaignForm = () => {
</Button> </Button>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -2,6 +2,7 @@ import { Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { useParams } from "react-router-dom" import { useParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/modals" import { RouteDrawer } from "../../../components/modals"
import { VisuallyHidden } from "../../../components/utilities/visually-hidden"
import { useCampaign } from "../../../hooks/api/campaigns" import { useCampaign } from "../../../hooks/api/campaigns"
import { EditCampaignForm } from "./components/edit-campaign-form" import { EditCampaignForm } from "./components/edit-campaign-form"
@@ -18,7 +19,12 @@ export const CampaignEdit = () => {
return ( return (
<RouteDrawer> <RouteDrawer>
<RouteDrawer.Header> <RouteDrawer.Header>
<RouteDrawer.Title asChild>
<Heading>{t("campaigns.edit.header")}</Heading> <Heading>{t("campaigns.edit.header")}</Heading>
</RouteDrawer.Title>
<RouteDrawer.Description asChild>
<VisuallyHidden>{t("campaigns.edit.description")}</VisuallyHidden>
</RouteDrawer.Description>
</RouteDrawer.Header> </RouteDrawer.Header>
{!isLoading && campaign && <EditCampaignForm campaign={campaign} />} {!isLoading && campaign && <EditCampaignForm campaign={campaign} />}
@@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next"
import * as zod from "zod" import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateCampaign } from "../../../../../hooks/api/campaigns" import { useUpdateCampaign } from "../../../../../hooks/api/campaigns"
type EditCampaignFormProps = { type EditCampaignFormProps = {
@@ -65,7 +66,7 @@ export const EditCampaignForm = ({ campaign }: EditCampaignFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
<Form.Field <Form.Field
@@ -187,7 +188,7 @@ export const EditCampaignForm = ({ campaign }: EditCampaignFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -14,7 +14,10 @@ import { useTranslation } from "react-i18next"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { useStore } from "../../../../../hooks/api/store" import { useStore } from "../../../../../hooks/api/store"
import { currencies, getCurrencySymbol } from "../../../../../lib/data/currencies" import {
currencies,
getCurrencySymbol,
} from "../../../../../lib/data/currencies"
export const CreateCampaignFormFields = ({ form, fieldScope = "" }) => { export const CreateCampaignFormFields = ({ form, fieldScope = "" }) => {
const { t } = useTranslation() const { t } = useTranslation()
@@ -118,7 +121,7 @@ export const CreateCampaignFormFields = ({ form, fieldScope = "" }) => {
render={({ field }) => { render={({ field }) => {
return ( return (
<Form.Item> <Form.Item>
<Form.Label>{t("fields.description")}</Form.Label> <Form.Label optional>{t("fields.description")}</Form.Label>
<Form.Control> <Form.Control>
<Textarea {...field} /> <Textarea {...field} />
@@ -138,7 +141,9 @@ export const CreateCampaignFormFields = ({ form, fieldScope = "" }) => {
render={({ field }) => { render={({ field }) => {
return ( return (
<Form.Item> <Form.Item>
<Form.Label>{t("campaigns.fields.start_date")}</Form.Label> <Form.Label optional>
{t("campaigns.fields.start_date")}
</Form.Label>
<Form.Control> <Form.Control>
<DatePicker <DatePicker
@@ -160,7 +165,9 @@ export const CreateCampaignFormFields = ({ form, fieldScope = "" }) => {
render={({ field }) => { render={({ field }) => {
return ( return (
<Form.Item> <Form.Item>
<Form.Label>{t("campaigns.fields.end_date")}</Form.Label> <Form.Label optional>
{t("campaigns.fields.end_date")}
</Form.Label>
<Form.Control> <Form.Control>
<DatePicker <DatePicker
@@ -0,0 +1,14 @@
import { CampaignBudgetTypeValues } from "@medusajs/types"
export const DEFAULT_CAMPAIGN_VALUES = {
name: "",
description: "",
campaign_identifier: "",
starts_at: null,
ends_at: null,
budget: {
type: "usage" as CampaignBudgetTypeValues,
currency_code: null,
limit: null,
},
}
@@ -8,6 +8,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateProductCategory } from "../../../../../hooks/api/categories" import { useCreateProductCategory } from "../../../../../hooks/api/categories"
import { CreateCategoryDetails } from "./create-category-details" import { CreateCategoryDetails } from "./create-category-details"
import { CreateCategoryNesting } from "./create-category-nesting" import { CreateCategoryNesting } from "./create-category-nesting"
@@ -121,7 +122,7 @@ export const CreateCategoryForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex size-full flex-col overflow-hidden" className="flex size-full flex-col overflow-hidden"
> >
@@ -198,7 +199,7 @@ export const CreateCategoryForm = ({
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</ProgressTabs> </ProgressTabs>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -8,6 +8,7 @@ import { HttpTypes } from "@medusajs/types"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { HandleInput } from "../../../../../components/inputs/handle-input" import { HandleInput } from "../../../../../components/inputs/handle-input"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateProductCategory } from "../../../../../hooks/api/categories" import { useUpdateProductCategory } from "../../../../../hooks/api/categories"
const EditCategorySchema = z.object({ const EditCategorySchema = z.object({
@@ -61,7 +62,7 @@ export const EditCategoryForm = ({ category }: EditCategoryFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
<Form.Field <Form.Field
@@ -188,7 +189,7 @@ export const EditCategoryForm = ({ category }: EditCategoryFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -16,6 +16,7 @@ import {
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table" import { DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateProductCategoryProducts } from "../../../../../hooks/api/categories" import { useUpdateProductCategoryProducts } from "../../../../../hooks/api/categories"
import { useProducts } from "../../../../../hooks/api/products" import { useProducts } from "../../../../../hooks/api/products"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns" import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
@@ -136,7 +137,7 @@ export const EditCategoryProductsForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
@@ -173,7 +174,7 @@ export const EditCategoryProductsForm = ({
search="autofocus" search="autofocus"
/> />
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -16,6 +16,7 @@ import {
useRouteModal, useRouteModal,
} from "../../../../../components/modals/index.ts" } from "../../../../../components/modals/index.ts"
import { DataTable } from "../../../../../components/table/data-table/data-table.tsx" import { DataTable } from "../../../../../components/table/data-table/data-table.tsx"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form/keybound-form.tsx"
import { useUpdateCollectionProducts } from "../../../../../hooks/api/collections.tsx" import { useUpdateCollectionProducts } from "../../../../../hooks/api/collections.tsx"
import { useProducts } from "../../../../../hooks/api/products.tsx" import { useProducts } from "../../../../../hooks/api/products.tsx"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns.tsx" import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns.tsx"
@@ -147,7 +148,7 @@ export const AddProductsToCollectionForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
@@ -182,7 +183,7 @@ export const AddProductsToCollectionForm = ({
search="autofocus" search="autofocus"
/> />
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -10,6 +10,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateCollection } from "../../../../../hooks/api/collections" import { useCreateCollection } from "../../../../../hooks/api/collections"
const CreateCollectionSchema = zod.object({ const CreateCollectionSchema = zod.object({
@@ -35,17 +36,17 @@ export const CreateCollectionForm = () => {
await mutateAsync(data, { await mutateAsync(data, {
onSuccess: ({ collection }) => { onSuccess: ({ collection }) => {
handleSuccess(`/collections/${collection.id}`) handleSuccess(`/collections/${collection.id}`)
toast.success(t("general.success"), { toast.success(t("collections.createSuccess"))
description: t("collections.createSuccess"), },
dismissLabel: t("actions.close"), onError: (error) => {
}) toast.error(error.message)
}, },
}) })
}) })
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form onSubmit={handleSubmit}> <KeyboundForm onSubmit={handleSubmit}>
<RouteFocusModal.Header> <RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2"> <div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild> <RouteFocusModal.Close asChild>
@@ -110,7 +111,7 @@ export const CreateCollectionForm = () => {
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -6,10 +6,8 @@ import * as zod from "zod"
import { HttpTypes } from "@medusajs/types" import { HttpTypes } from "@medusajs/types"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
RouteDrawer, import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
useRouteModal,
} from "../../../../../components/modals"
import { useUpdateCollection } from "../../../../../hooks/api/collections" import { useUpdateCollection } from "../../../../../hooks/api/collections"
type EditCollectionFormProps = { type EditCollectionFormProps = {
@@ -45,7 +43,7 @@ export const EditCollectionForm = ({ collection }: EditCollectionFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
<Form.Field <Form.Field
@@ -106,7 +104,7 @@ export const EditCollectionForm = ({ collection }: EditCollectionFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -16,6 +16,7 @@ import {
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table" import { DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useAddCustomersToGroup } from "../../../../../hooks/api/customer-groups" import { useAddCustomersToGroup } from "../../../../../hooks/api/customer-groups"
import { useCustomers } from "../../../../../hooks/api/customers" import { useCustomers } from "../../../../../hooks/api/customers"
import { useCustomerTableColumns } from "../../../../../hooks/table/columns/use-customer-table-columns" import { useCustomerTableColumns } from "../../../../../hooks/table/columns/use-customer-table-columns"
@@ -125,7 +126,7 @@ export const AddCustomersForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -175,7 +176,7 @@ export const AddCustomersForm = ({
}} }}
/> />
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -9,6 +9,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateCustomerGroup } from "../../../../../hooks/api/customer-groups" import { useCreateCustomerGroup } from "../../../../../hooks/api/customer-groups"
export const CreateCustomerGroupSchema = zod.object({ export const CreateCustomerGroupSchema = zod.object({
@@ -52,31 +53,19 @@ export const CreateCustomerGroupForm = () => {
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form onSubmit={handleSubmit}> <KeyboundForm onSubmit={handleSubmit}>
<RouteFocusModal.Header> <RouteFocusModal.Header />
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
type="submit"
variant="primary"
size="small"
isLoading={isPending}
>
{t("actions.create")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex flex-col items-center pt-[72px]"> <RouteFocusModal.Body className="flex flex-col items-center pt-[72px]">
<div className="flex w-full max-w-[720px] flex-col gap-y-8"> <div className="flex w-full max-w-[720px] flex-col gap-y-8">
<div> <div>
<RouteFocusModal.Title asChild>
<Heading>{t("customerGroups.create.header")}</Heading> <Heading>{t("customerGroups.create.header")}</Heading>
</RouteFocusModal.Title>
<RouteFocusModal.Description asChild>
<Text size="small" className="text-ui-fg-subtle"> <Text size="small" className="text-ui-fg-subtle">
{t("customerGroups.create.hint")} {t("customerGroups.create.hint")}
</Text> </Text>
</RouteFocusModal.Description>
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<Form.Field <Form.Field
@@ -97,7 +86,22 @@ export const CreateCustomerGroupForm = () => {
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> <RouteFocusModal.Footer>
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
type="submit"
variant="primary"
size="small"
isLoading={isPending}
>
{t("actions.create")}
</Button>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next"
import * as z from "zod" import * as z from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateCustomerGroup } from "../../../../../hooks/api/customer-groups" import { useUpdateCustomerGroup } from "../../../../../hooks/api/customer-groups"
type EditCustomerGroupFormProps = { type EditCustomerGroupFormProps = {
@@ -50,7 +51,7 @@ export const EditCustomerGroupForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -83,7 +84,7 @@ export const EditCustomerGroupForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -9,6 +9,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateCustomer } from "../../../../../hooks/api/customers" import { useCreateCustomer } from "../../../../../hooks/api/customers"
const CreateCustomerSchema = zod.object({ const CreateCustomerSchema = zod.object({
@@ -63,25 +64,12 @@ export const CreateCustomerForm = () => {
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form onSubmit={handleSubmit}> <KeyboundForm
<RouteFocusModal.Header> onSubmit={handleSubmit}
<div className="flex items-center justify-end gap-x-2"> className="flex flex-1 flex-col overflow-hidden"
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
size="small"
variant="primary"
type="submit"
isLoading={isPending}
> >
{t("actions.create")} <RouteFocusModal.Header />
</Button> <RouteFocusModal.Body className="flex flex-1 flex-col items-center overflow-y-auto py-16">
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex flex-col items-center py-16">
<div className="flex w-full max-w-[720px] flex-col gap-y-8"> <div className="flex w-full max-w-[720px] flex-col gap-y-8">
<div> <div>
<Heading>{t("customers.create.header")}</Heading> <Heading>{t("customers.create.header")}</Heading>
@@ -168,7 +156,24 @@ export const CreateCustomerForm = () => {
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> <RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
size="small"
variant="primary"
type="submit"
isLoading={isPending}
>
{t("actions.create")}
</Button>
</div>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -10,6 +10,7 @@ import {
RouteDrawer, RouteDrawer,
useRouteModal, useRouteModal,
} from "../../../../../components/modals/index.ts" } from "../../../../../components/modals/index.ts"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form/keybound-form.tsx"
import { useUpdateCustomer } from "../../../../../hooks/api/customers.tsx" import { useUpdateCustomer } from "../../../../../hooks/api/customers.tsx"
type EditCustomerFormProps = { type EditCustomerFormProps = {
@@ -69,7 +70,7 @@ export const EditCustomerForm = ({ customer }: EditCustomerFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
<Form.Field <Form.Field
@@ -171,7 +172,7 @@ export const EditCustomerForm = ({ customer }: EditCustomerFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -1,12 +1,13 @@
import { zodResolver } from "@hookform/resolvers/zod" import { zodResolver } from "@hookform/resolvers/zod"
import { HttpTypes } from "@medusajs/types"
import { Button } from "@medusajs/ui" import { Button } from "@medusajs/ui"
import { useForm } from "react-hook-form" import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { z } from "zod" import { z } from "zod"
import { TransferOwnerShipForm } from "../../../../../components/forms/transfer-ownership-form" import { TransferOwnerShipForm } from "../../../../../components/forms/transfer-ownership-form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { TransferOwnershipSchema } from "../../../../../lib/schemas" import { TransferOwnershipSchema } from "../../../../../lib/schemas"
import { HttpTypes } from "@medusajs/types"
type TransferCustomerOrderOwnershipFormProps = { type TransferCustomerOrderOwnershipFormProps = {
order: HttpTypes.AdminOrder order: HttpTypes.AdminOrder
@@ -46,7 +47,7 @@ export const TransferCustomerOrderOwnershipForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex size-full flex-col overflow-hidden" className="flex size-full flex-col overflow-hidden"
> >
@@ -65,7 +66,7 @@ export const TransferCustomerOrderOwnershipForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -10,11 +10,13 @@ import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import * as zod from "zod" import * as zod from "zod"
import { HttpTypes } from "@medusajs/types"
import { import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table" import { DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { import {
customerGroupsQueryKeys, customerGroupsQueryKeys,
useCustomerGroups, useCustomerGroups,
@@ -23,7 +25,7 @@ import { useCustomerGroupTableColumns } from "../../../../../hooks/table/columns
import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters" import { useCustomerGroupTableFilters } from "../../../../../hooks/table/filters/use-customer-group-table-filters"
import { useCustomerGroupTableQuery } from "../../../../../hooks/table/query/use-customer-group-table-query" import { useCustomerGroupTableQuery } from "../../../../../hooks/table/query/use-customer-group-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table" import { useDataTable } from "../../../../../hooks/use-data-table"
import { client, sdk } from "../../../../../lib/client" import { sdk } from "../../../../../lib/client"
import { queryClient } from "../../../../../lib/query-client" import { queryClient } from "../../../../../lib/query-client"
type AddCustomerGroupsFormProps = { type AddCustomerGroupsFormProps = {
@@ -126,15 +128,14 @@ export const AddCustomerGroupsForm = ({
await Promise.all(promises) await Promise.all(promises)
toast.success(t("general.success"), { toast.success(
description: t("customers.groups.add.success", { t("customers.groups.add.success", {
groups: data.customer_group_ids groups: data.customer_group_ids
.map((id) => customer_groups?.find((g) => g.id === id)) .map((id) => customer_groups?.find((g) => g.id === id))
.filter(Boolean) .filter(Boolean)
.map((cg) => cg.name), .map((cg) => cg?.name),
}),
dismissLabel: t("actions.close"),
}) })
)
await queryClient.invalidateQueries({ await queryClient.invalidateQueries({
queryKey: customerGroupsQueryKeys.lists(), queryKey: customerGroupsQueryKeys.lists(),
@@ -154,7 +155,7 @@ export const AddCustomerGroupsForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -199,7 +200,7 @@ export const AddCustomerGroupsForm = ({
{t("actions.save")} {t("actions.save")}
</Button> </Button>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -22,6 +22,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { import {
inventoryItemsQueryKeys, inventoryItemsQueryKeys,
useCreateInventoryItem, useCreateInventoryItem,
@@ -204,7 +205,7 @@ export function InventoryCreateForm({ locations }: InventoryCreateFormProps) {
className="h-full" className="h-full"
onValueChange={(tab) => onTabChange(tab as Tab)} onValueChange={(tab) => onTabChange(tab as Tab)}
> >
<form <KeyboundForm
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -507,7 +508,7 @@ export function InventoryCreateForm({ locations }: InventoryCreateFormProps) {
</Button> </Button>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</ProgressTabs> </ProgressTabs>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
@@ -8,6 +8,7 @@ import { RouteDrawer, useRouteModal } from "../../../../../../components/modals"
import { useForm } from "react-hook-form" import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { Form } from "../../../../../../components/common/form" import { Form } from "../../../../../../components/common/form"
import { KeyboundForm } from "../../../../../../components/utilities/keybound-form"
import { useUpdateInventoryLevel } from "../../../../../../hooks/api/inventory" import { useUpdateInventoryLevel } from "../../../../../../hooks/api/inventory"
type AdjustInventoryFormProps = { type AdjustInventoryFormProps = {
@@ -84,7 +85,7 @@ export const AdjustInventoryForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -150,7 +151,7 @@ export const AdjustInventoryForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -11,6 +11,7 @@ import { z } from "zod"
import { Form } from "../../../../../../components/common/form" import { Form } from "../../../../../../components/common/form"
import { CountrySelect } from "../../../../../../components/inputs/country-select" import { CountrySelect } from "../../../../../../components/inputs/country-select"
import { KeyboundForm } from "../../../../../../components/utilities/keybound-form"
import { useUpdateInventoryItem } from "../../../../../../hooks/api/inventory" import { useUpdateInventoryItem } from "../../../../../../hooks/api/inventory"
type EditInventoryItemAttributeFormProps = { type EditInventoryItemAttributeFormProps = {
@@ -64,7 +65,7 @@ export const EditInventoryItemAttributesForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -246,7 +247,7 @@ export const EditInventoryItemAttributesForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -9,6 +9,7 @@ import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { z } from "zod" import { z } from "zod"
import { Form } from "../../../../../../components/common/form" import { Form } from "../../../../../../components/common/form"
import { KeyboundForm } from "../../../../../../components/utilities/keybound-form"
import { useUpdateInventoryItem } from "../../../../../../hooks/api/inventory" import { useUpdateInventoryItem } from "../../../../../../hooks/api/inventory"
type EditInventoryItemFormProps = { type EditInventoryItemFormProps = {
@@ -50,7 +51,7 @@ export const EditInventoryItemForm = ({ item }: EditInventoryItemFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -98,7 +99,7 @@ export const EditInventoryItemForm = ({ item }: EditInventoryItemFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -10,6 +10,7 @@ import { RouteDrawer, useRouteModal } from "../../../../../../components/modals"
import { useBatchUpdateInventoryLevels } from "../../../../../../hooks/api/inventory" import { useBatchUpdateInventoryLevels } from "../../../../../../hooks/api/inventory"
import { useEffect, useMemo } from "react" import { useEffect, useMemo } from "react"
import { KeyboundForm } from "../../../../../../components/utilities/keybound-form"
import { LocationItem } from "./location-item" import { LocationItem } from "./location-item"
type EditInventoryItemAttributeFormProps = { type EditInventoryItemAttributeFormProps = {
@@ -120,7 +121,7 @@ export const ManageLocationsForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -189,7 +190,7 @@ export const ManageLocationsForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -9,10 +9,11 @@ import { decodeToken } from "react-jwt"
import { Link, useSearchParams } from "react-router-dom" import { Link, useSearchParams } from "react-router-dom"
import * as z from "zod" import * as z from "zod"
import { Form } from "../../components/common/form" import { Form } from "../../components/common/form"
import AvatarBox from "../../components/common/logo-box/avatar-box"
import { KeyboundForm } from "../../components/utilities/keybound-form"
import { useSignUpWithEmailPass } from "../../hooks/api/auth" import { useSignUpWithEmailPass } from "../../hooks/api/auth"
import { useAcceptInvite } from "../../hooks/api/invites" import { useAcceptInvite } from "../../hooks/api/invites"
import { isFetchError } from "../../lib/is-fetch-error" import { isFetchError } from "../../lib/is-fetch-error"
import AvatarBox from "../../components/common/logo-box/avatar-box"
const CreateAccountSchema = z const CreateAccountSchema = z
.object({ .object({
@@ -139,7 +140,7 @@ const LoginLink = () => {
<Link <Link
key="login-link" key="login-link"
to="/login" to="/login"
className="text-ui-fg-interactive txt-small !text-ui-fg-base font-medium transition-fg hover:text-ui-fg-interactive-hover focus-visible:text-ui-fg-interactive-hover outline-none" className="text-ui-fg-interactive txt-small !text-ui-fg-base transition-fg hover:text-ui-fg-interactive-hover focus-visible:text-ui-fg-interactive-hover font-medium outline-none"
> >
{t("invite.backToLogin")} {t("invite.backToLogin")}
</Link> </Link>
@@ -250,7 +251,10 @@ const CreateView = ({
</Text> </Text>
</div> </div>
<Form {...form}> <Form {...form}>
<form onSubmit={handleSubmit} className="flex w-full flex-col gap-y-6"> <KeyboundForm
onSubmit={handleSubmit}
className="flex w-full flex-col gap-y-6"
>
<div className="flex flex-col gap-y-2"> <div className="flex flex-col gap-y-2">
<Form.Field <Form.Field
control={form.control} control={form.control}
@@ -345,7 +349,7 @@ const CreateView = ({
}} }}
/> />
{validationError && ( {validationError && (
<div className="text-center mt-6"> <div className="mt-6 text-center">
<Hint className="inline-flex" variant={"error"}> <Hint className="inline-flex" variant={"error"}>
{validationError} {validationError}
</Hint> </Hint>
@@ -353,7 +357,7 @@ const CreateView = ({
)} )}
{serverError && ( {serverError && (
<Alert <Alert
className="p-2 bg-ui-bg-base items-center" className="bg-ui-bg-base items-center p-2"
dismissible dismissible
variant="error" variant="error"
> >
@@ -369,7 +373,7 @@ const CreateView = ({
> >
{t("invite.createAccount")} {t("invite.createAccount")}
</Button> </Button>
</form> </KeyboundForm>
</Form> </Form>
<LoginLink /> <LoginLink />
</div> </div>
@@ -11,7 +11,10 @@ import { z } from "zod"
import { ChipGroup } from "../../../../../components/common/chip-group" import { ChipGroup } from "../../../../../components/common/chip-group"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { SplitView } from "../../../../../components/layout/split-view" import {
StackedFocusModal,
useStackedModal,
} from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table" import { DataTable } from "../../../../../components/table/data-table"
import { useDataTable } from "../../../../../hooks/use-data-table" import { useDataTable } from "../../../../../hooks/use-data-table"
import { import {
@@ -21,6 +24,7 @@ import {
import { useCountries } from "../../../../regions/common/hooks/use-countries" import { useCountries } from "../../../../regions/common/hooks/use-countries"
import { useCountryTableColumns } from "../../../../regions/common/hooks/use-country-table-columns" import { useCountryTableColumns } from "../../../../regions/common/hooks/use-country-table-columns"
import { useCountryTableQuery } from "../../../../regions/common/hooks/use-country-table-query" import { useCountryTableQuery } from "../../../../regions/common/hooks/use-country-table-query"
import { GEO_ZONE_STACKED_MODAL_ID } from "../../constants"
const GeoZoneSchema = z.object({ const GeoZoneSchema = z.object({
countries: z.array( countries: z.array(
@@ -30,12 +34,10 @@ const GeoZoneSchema = z.object({
type GeoZoneFormImplProps<TForm extends UseFormReturn<any>> = { type GeoZoneFormImplProps<TForm extends UseFormReturn<any>> = {
form: TForm form: TForm
onOpenChange: (open: boolean) => void
} }
const GeoZoneFormImpl = <TForm extends UseFormReturn<any>>({ const GeoZoneFormImpl = <TForm extends UseFormReturn<any>>({
form, form,
onOpenChange,
}: GeoZoneFormImplProps<TForm>) => { }: GeoZoneFormImplProps<TForm>) => {
const castForm = form as unknown as UseFormReturn< const castForm = form as unknown as UseFormReturn<
z.infer<typeof GeoZoneSchema> z.infer<typeof GeoZoneSchema>
@@ -71,14 +73,11 @@ const GeoZoneFormImpl = <TForm extends UseFormReturn<any>>({
{t("stockLocations.serviceZones.manageAreas.hint")} {t("stockLocations.serviceZones.manageAreas.hint")}
</Form.Hint> </Form.Hint>
</div> </div>
<Button <StackedFocusModal.Trigger asChild>
size="small" <Button size="small" variant="secondary" type="button">
variant="secondary"
type="button"
onClick={() => onOpenChange(true)}
>
{t("stockLocations.serviceZones.manageAreas.action")} {t("stockLocations.serviceZones.manageAreas.action")}
</Button> </Button>
</StackedFocusModal.Trigger>
</div> </div>
<Form.ErrorMessage /> <Form.ErrorMessage />
<Form.Control className="mt-0"> <Form.Control className="mt-0">
@@ -103,26 +102,25 @@ const GeoZoneFormImpl = <TForm extends UseFormReturn<any>>({
) )
} }
type AreasDrawerProps<TForm extends UseFormReturn<any>> = { type AreasStackedModalProps<TForm extends UseFormReturn<any>> = {
form: TForm form: TForm
open: boolean
onOpenChange: (open: boolean) => void
} }
const PREFIX = "ac" const PREFIX = "ac"
const PAGE_SIZE = 50 const PAGE_SIZE = 50
const AreaDrawer = <TForm extends UseFormReturn<any>>({ const AreaStackedModal = <TForm extends UseFormReturn<any>>({
form, form,
open, }: AreasStackedModalProps<TForm>) => {
onOpenChange,
}: AreasDrawerProps<TForm>) => {
const castForm = form as unknown as UseFormReturn< const castForm = form as unknown as UseFormReturn<
z.infer<typeof GeoZoneSchema> z.infer<typeof GeoZoneSchema>
> >
const { t } = useTranslation() const { t } = useTranslation()
const { getValues, setValue } = castForm const { getValues, setValue } = castForm
const { setIsOpen, getIsOpen } = useStackedModal()
const open = getIsOpen(GEO_ZONE_STACKED_MODAL_ID)
const [selection, setSelection] = useState<RowSelectionState>({}) const [selection, setSelection] = useState<RowSelectionState>({})
const [state, setState] = useState<{ iso_2: string; display_name: string }[]>( const [state, setState] = useState<{ iso_2: string; display_name: string }[]>(
@@ -203,7 +201,7 @@ const AreaDrawer = <TForm extends UseFormReturn<any>>({
shouldDirty: true, shouldDirty: true,
shouldTouch: true, shouldTouch: true,
}) })
onOpenChange(false) setIsOpen(GEO_ZONE_STACKED_MODAL_ID, false)
} }
const columns = useColumns() const columns = useColumns()
@@ -226,8 +224,20 @@ const AreaDrawer = <TForm extends UseFormReturn<any>>({
validateForm(form) validateForm(form)
return ( return (
<SplitView.Drawer> <StackedFocusModal.Content className="flex flex-col overflow-hidden">
<div className="flex h-full flex-col overflow-hidden"> <StackedFocusModal.Header>
<StackedFocusModal.Title asChild>
<span className="sr-only">
{t("stockLocations.serviceZones.manageAreas.label")}
</span>
</StackedFocusModal.Title>
<StackedFocusModal.Description asChild>
<span className="sr-only">
{t("stockLocations.serviceZones.manageAreas.hint")}
</span>
</StackedFocusModal.Description>
</StackedFocusModal.Header>
<StackedFocusModal.Body className="flex-1 overflow-hidden">
<DataTable <DataTable
table={table} table={table}
columns={columns} columns={columns}
@@ -240,18 +250,20 @@ const AreaDrawer = <TForm extends UseFormReturn<any>>({
queryObject={raw} queryObject={raw}
prefix={PREFIX} prefix={PREFIX}
/> />
<div className="flex items-center justify-end gap-x-2 border-t p-4"> </StackedFocusModal.Body>
<SplitView.Close type="button" asChild> <StackedFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<StackedFocusModal.Close type="button" asChild>
<Button variant="secondary" size="small"> <Button variant="secondary" size="small">
{t("actions.cancel")} {t("actions.cancel")}
</Button> </Button>
</SplitView.Close> </StackedFocusModal.Close>
<Button size="small" type="button" onClick={handleAdd}> <Button size="small" type="button" onClick={handleAdd}>
{t("actions.add")} {t("actions.save")}
</Button> </Button>
</div> </div>
</div> </StackedFocusModal.Footer>
</SplitView.Drawer> </StackedFocusModal.Content>
) )
} }
@@ -308,5 +320,5 @@ function validateForm(form: UseFormReturn) {
} }
export const GeoZoneForm = Object.assign(GeoZoneFormImpl, { export const GeoZoneForm = Object.assign(GeoZoneFormImpl, {
AreaDrawer, AreaDrawer: AreaStackedModal,
}) })
@@ -7,3 +7,5 @@ export enum ShippingOptionPriceType {
FlatRate = "flat", FlatRate = "flat",
Calculated = "calculated", Calculated = "calculated",
} }
export const GEO_ZONE_STACKED_MODAL_ID = "geo-zone"
@@ -9,6 +9,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateStockLocation } from "../../../../../hooks/api/stock-locations" import { useCreateStockLocation } from "../../../../../hooks/api/stock-locations"
const CreateLocationSchema = zod.object({ const CreateLocationSchema = zod.object({
@@ -69,22 +70,11 @@ export const CreateLocationForm = () => {
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
<RouteFocusModal.Header> <RouteFocusModal.Header />
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button type="submit" size="small" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex flex-1 flex-col overflow-hidden"> <RouteFocusModal.Body className="flex flex-1 flex-col overflow-hidden">
<div className="flex flex-1 flex-col items-center overflow-y-auto"> <div className="flex flex-1 flex-col items-center overflow-y-auto">
<div className="flex w-full max-w-[720px] flex-col gap-y-8 px-2 py-16"> <div className="flex w-full max-w-[720px] flex-col gap-y-8 px-2 py-16">
@@ -240,7 +230,19 @@ export const CreateLocationForm = () => {
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> <RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button type="submit" size="small" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -8,6 +8,7 @@ import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { CountrySelect } from "../../../../../components/inputs/country-select" import { CountrySelect } from "../../../../../components/inputs/country-select"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateStockLocation } from "../../../../../hooks/api/stock-locations" import { useUpdateStockLocation } from "../../../../../hooks/api/stock-locations"
type EditLocationFormProps = { type EditLocationFormProps = {
@@ -73,7 +74,7 @@ export const EditLocationForm = ({ location }: EditLocationFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -228,7 +229,7 @@ export const EditLocationForm = ({ location }: EditLocationFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -8,14 +8,18 @@ import { z } from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { InlineTip } from "../../../../../components/common/inline-tip" import { InlineTip } from "../../../../../components/common/inline-tip"
import { SplitView } from "../../../../../components/layout/split-view"
import { import {
RouteFocusModal, RouteFocusModal,
StackedFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateFulfillmentSetServiceZone } from "../../../../../hooks/api/fulfillment-sets" import { useCreateFulfillmentSetServiceZone } from "../../../../../hooks/api/fulfillment-sets"
import { GeoZoneForm } from "../../../common/components/geo-zone-form" import { GeoZoneForm } from "../../../common/components/geo-zone-form"
import { FulfillmentSetType } from "../../../common/constants" import {
FulfillmentSetType,
GEO_ZONE_STACKED_MODAL_ID,
} from "../../../common/constants"
const CreateServiceZoneSchema = z.object({ const CreateServiceZoneSchema = z.object({
name: z.string().min(1), name: z.string().min(1),
@@ -80,27 +84,14 @@ export function CreateServiceZoneForm({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<RouteFocusModal.Header> <RouteFocusModal.Header />
<div className="flex items-center justify-end gap-x-2"> <RouteFocusModal.Body className="flex flex-1 flex-col items-center overflow-auto">
<RouteFocusModal.Close asChild> <StackedFocusModal id={GEO_ZONE_STACKED_MODAL_ID}>
<Button variant="secondary" size="small"> <div className="flex flex-1 flex-col items-center">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button type="submit" size="small" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="m-auto flex h-full w-full flex-col items-center divide-y overflow-hidden">
<SplitView open={open} onOpenChange={setOpen}>
<SplitView.Content>
<div className="flex flex-1 flex-col items-center overflow-y-auto">
<div className="flex w-full max-w-[720px] flex-col gap-y-8 px-2 py-16"> <div className="flex w-full max-w-[720px] flex-col gap-y-8 px-2 py-16">
<Heading> <Heading>
{type === FulfillmentSetType.Pickup {type === FulfillmentSetType.Pickup
@@ -134,18 +125,26 @@ export function CreateServiceZoneForm({
{t("stockLocations.serviceZones.fields.tip")} {t("stockLocations.serviceZones.fields.tip")}
</InlineTip> </InlineTip>
<GeoZoneForm form={form} onOpenChange={setOpen} /> <GeoZoneForm form={form} />
</div> </div>
</div> </div>
</SplitView.Content> <GeoZoneForm.AreaDrawer form={form} />
<GeoZoneForm.AreaDrawer </StackedFocusModal>
form={form}
open={open}
onOpenChange={setOpen}
/>
</SplitView>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form>
<RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button type="submit" size="small" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -7,6 +7,7 @@ import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { InlineTip } from "../../../../../components/common/inline-tip" import { InlineTip } from "../../../../../components/common/inline-tip"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateFulfillmentSetServiceZone } from "../../../../../hooks/api/fulfillment-sets" import { useUpdateFulfillmentSetServiceZone } from "../../../../../hooks/api/fulfillment-sets"
type EditServiceZoneFormProps = { type EditServiceZoneFormProps = {
@@ -59,8 +60,11 @@ export const EditServiceZoneForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm
<RouteDrawer.Body> onSubmit={handleSubmit}
className="flex size-full flex-col overflow-hidden"
>
<RouteDrawer.Body className="flex-1 overflow-auto">
<div className="flex flex-col gap-y-8"> <div className="flex flex-col gap-y-8">
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
<Form.Field <Form.Field
@@ -94,7 +98,7 @@ export const EditServiceZoneForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -1,19 +1,20 @@
import { zodResolver } from "@hookform/resolvers/zod" import { zodResolver } from "@hookform/resolvers/zod"
import { HttpTypes } from "@medusajs/types" import { HttpTypes } from "@medusajs/types"
import { Button, Heading, toast } from "@medusajs/ui" import { Button, Heading, toast } from "@medusajs/ui"
import { useState } from "react"
import { useForm } from "react-hook-form" import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { z } from "zod" import { z } from "zod"
import { SplitView } from "../../../../../components/layout/split-view"
import { import {
RouteFocusModal, RouteFocusModal,
StackedFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateFulfillmentSetServiceZone } from "../../../../../hooks/api/fulfillment-sets" import { useUpdateFulfillmentSetServiceZone } from "../../../../../hooks/api/fulfillment-sets"
import { countries } from "../../../../../lib/data/countries" import { countries } from "../../../../../lib/data/countries"
import { GeoZoneForm } from "../../../common/components/geo-zone-form" import { GeoZoneForm } from "../../../common/components/geo-zone-form"
import { GEO_ZONE_STACKED_MODAL_ID } from "../../../common/constants"
const EditeServiceZoneSchema = z.object({ const EditeServiceZoneSchema = z.object({
countries: z countries: z
@@ -34,7 +35,6 @@ export function EditServiceZoneAreasForm({
}: EditServiceZoneAreasFormProps) { }: EditServiceZoneAreasFormProps) {
const { t } = useTranslation() const { t } = useTranslation()
const { handleSuccess } = useRouteModal() const { handleSuccess } = useRouteModal()
const [open, setOpen] = useState(false)
const form = useForm<z.infer<typeof EditeServiceZoneSchema>>({ const form = useForm<z.infer<typeof EditeServiceZoneSchema>>({
defaultValues: { defaultValues: {
@@ -80,11 +80,29 @@ export function EditServiceZoneAreasForm({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<RouteFocusModal.Header> <RouteFocusModal.Header />
<RouteFocusModal.Body className="flex flex-1 flex-col overflow-auto">
<StackedFocusModal id={GEO_ZONE_STACKED_MODAL_ID}>
<div className="flex flex-col items-center p-16">
<div className="flex w-full max-w-[720px] flex-col gap-y-8">
<Heading>
{t("stockLocations.serviceZones.manageAreas.header", {
name: zone.name,
})}
</Heading>
<GeoZoneForm form={form} />
</div>
</div>
<GeoZoneForm.AreaDrawer form={form} />
</StackedFocusModal>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2"> <div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild> <RouteFocusModal.Close asChild>
<Button variant="secondary" size="small"> <Button variant="secondary" size="small">
@@ -95,30 +113,8 @@ export function EditServiceZoneAreasForm({
{t("actions.save")} {t("actions.save")}
</Button> </Button>
</div> </div>
</RouteFocusModal.Header> </RouteFocusModal.Footer>
</KeyboundForm>
<RouteFocusModal.Body className="flex flex-col overflow-hidden">
<SplitView open={open} onOpenChange={setOpen}>
<SplitView.Content>
<div className="flex flex-col items-center p-16">
<div className="flex w-full max-w-[720px] flex-col gap-y-8">
<Heading>
{t("stockLocations.serviceZones.manageAreas.header", {
name: zone.name,
})}
</Heading>
<GeoZoneForm form={form} onOpenChange={setOpen} />
</div>
</div>
</SplitView.Content>
<GeoZoneForm.AreaDrawer
form={form}
open={open}
onOpenChange={setOpen}
/>
</SplitView>
</RouteFocusModal.Body>
</form>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -9,6 +9,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateShippingOptions } from "../../../../../hooks/api/shipping-options" import { useCreateShippingOptions } from "../../../../../hooks/api/shipping-options"
import { castNumber } from "../../../../../lib/cast-number" import { castNumber } from "../../../../../lib/cast-number"
import { ShippingOptionPriceType } from "../../../common/constants" import { ShippingOptionPriceType } from "../../../common/constants"
@@ -197,7 +198,7 @@ export function CreateShippingOptionsForm({
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onValueChange={(tab) => onTabChange(tab as Tab)} onValueChange={(tab) => onTabChange(tab as Tab)}
> >
<form className="flex h-full flex-col" onSubmit={handleSubmit}> <KeyboundForm className="flex h-full flex-col" onSubmit={handleSubmit}>
<RouteFocusModal.Header> <RouteFocusModal.Header>
<ProgressTabs.List className="border-ui-border-base -my-2 ml-2 min-w-0 flex-1 border-l"> <ProgressTabs.List className="border-ui-border-base -my-2 ml-2 min-w-0 flex-1 border-l">
<ProgressTabs.Trigger <ProgressTabs.Trigger
@@ -221,6 +222,25 @@ export function CreateShippingOptionsForm({
</ProgressTabs.Trigger> </ProgressTabs.Trigger>
)} )}
</ProgressTabs.List> </ProgressTabs.List>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="size-full overflow-hidden">
<ProgressTabs.Content
value={Tab.DETAILS}
className="size-full overflow-y-auto"
>
<CreateShippingOptionDetailsForm
form={form}
zone={zone}
isReturn={isReturn}
locationId={locationId}
/>
</ProgressTabs.Content>
<ProgressTabs.Content value={Tab.PRICING} className="size-full">
<CreateShippingOptionsPricesForm form={form} />
</ProgressTabs.Content>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2"> <div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild> <RouteFocusModal.Close asChild>
<Button variant="secondary" size="small"> <Button variant="secondary" size="small">
@@ -250,25 +270,8 @@ export function CreateShippingOptionsForm({
</Button> </Button>
)} )}
</div> </div>
</RouteFocusModal.Header> </RouteFocusModal.Footer>
</KeyboundForm>
<RouteFocusModal.Body className="size-full overflow-hidden">
<ProgressTabs.Content
value={Tab.DETAILS}
className="size-full overflow-y-auto"
>
<CreateShippingOptionDetailsForm
form={form}
zone={zone}
isReturn={isReturn}
locationId={locationId}
/>
</ProgressTabs.Content>
<ProgressTabs.Content value={Tab.PRICING} className="size-full">
<CreateShippingOptionsPricesForm form={form} />
</ProgressTabs.Content>
</RouteFocusModal.Body>
</form>
</ProgressTabs> </ProgressTabs>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
@@ -9,6 +9,7 @@ import { Form } from "../../../../../components/common/form"
import { SwitchBox } from "../../../../../components/common/switch-box" import { SwitchBox } from "../../../../../components/common/switch-box"
import { Combobox } from "../../../../../components/inputs/combobox" import { Combobox } from "../../../../../components/inputs/combobox"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options" import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options"
import { useComboboxData } from "../../../../../hooks/use-combobox-data" import { useComboboxData } from "../../../../../hooks/use-combobox-data"
import { sdk } from "../../../../../lib/client" import { sdk } from "../../../../../lib/client"
@@ -121,7 +122,7 @@ export const EditShippingOptionForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex flex-col gap-y-8"> <div className="flex flex-col gap-y-8">
<div className="flex flex-col gap-y-8"> <div className="flex flex-col gap-y-8">
@@ -266,7 +267,7 @@ export const EditShippingOptionForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -12,6 +12,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals/index" } from "../../../../../components/modals/index"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { usePricePreferences } from "../../../../../hooks/api/price-preferences" import { usePricePreferences } from "../../../../../hooks/api/price-preferences"
import { useRegions } from "../../../../../hooks/api/regions" import { useRegions } from "../../../../../hooks/api/regions"
import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options" import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options"
@@ -225,7 +226,7 @@ export function EditShippingOptionsPricingForm({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -260,7 +261,7 @@ export function EditShippingOptionsPricingForm({
</Button> </Button>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -7,6 +7,7 @@ import * as z from "zod"
import { Form } from "../../components/common/form" import { Form } from "../../components/common/form"
import AvatarBox from "../../components/common/logo-box/avatar-box" import AvatarBox from "../../components/common/logo-box/avatar-box"
import { KeyboundForm } from "../../components/utilities/keybound-form"
import { useDashboardExtension } from "../../extensions" import { useDashboardExtension } from "../../extensions"
import { useSignInWithEmailPass } from "../../hooks/api" import { useSignInWithEmailPass } from "../../hooks/api"
import { isFetchError } from "../../lib/is-fetch-error" import { isFetchError } from "../../lib/is-fetch-error"
@@ -85,7 +86,7 @@ export const Login = () => {
return <Component key={i} /> return <Component key={i} />
})} })}
<Form {...form}> <Form {...form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex w-full flex-col gap-y-6" className="flex w-full flex-col gap-y-6"
> >
@@ -148,7 +149,7 @@ export const Login = () => {
<Button className="w-full" type="submit" isLoading={isPending}> <Button className="w-full" type="submit" isLoading={isPending}>
{t("actions.continueWithEmail")} {t("actions.continueWithEmail")}
</Button> </Button>
</form> </KeyboundForm>
</Form> </Form>
{getWidgets("login.after").map((Component, i) => { {getWidgets("login.after").map((Component, i) => {
return <Component key={i} /> return <Component key={i} />
@@ -7,17 +7,18 @@ import { AdminOrder, InventoryItemDTO, OrderLineItemDTO } from "@medusajs/types"
import { Alert, Button, Heading, Input, Select, toast } from "@medusajs/ui" import { Alert, Button, Heading, Input, Select, toast } from "@medusajs/ui"
import { useForm, useWatch } from "react-hook-form" import { useForm, useWatch } from "react-hook-form"
import { Form } from "../../../../../components/common/form"
import { import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { Form } from "../../../../../components/common/form" import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useStockLocations } from "../../../../../hooks/api/stock-locations"
import { useCreateReservationItem } from "../../../../../hooks/api/reservations"
import { OrderAllocateItemsItem } from "./order-allocate-items-item"
import { AllocateItemsSchema } from "./constants"
import { queryClient } from "../../../../../lib/query-client"
import { ordersQueryKeys } from "../../../../../hooks/api/orders" import { ordersQueryKeys } from "../../../../../hooks/api/orders"
import { useCreateReservationItem } from "../../../../../hooks/api/reservations"
import { useStockLocations } from "../../../../../hooks/api/stock-locations"
import { queryClient } from "../../../../../lib/query-client"
import { AllocateItemsSchema } from "./constants"
import { OrderAllocateItemsItem } from "./order-allocate-items-item"
type OrderAllocateItemsFormProps = { type OrderAllocateItemsFormProps = {
order: AdminOrder order: AdminOrder
@@ -192,27 +193,11 @@ export function OrderAllocateItemsForm({ order }: OrderAllocateItemsFormProps) {
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
<RouteFocusModal.Header> <RouteFocusModal.Header />
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
size="small"
type="submit"
isLoading={isMutating}
disabled={!selectedLocationId || disableSubmit}
>
{t("orders.allocateItems.action")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex h-full w-full flex-col items-center divide-y overflow-y-auto"> <RouteFocusModal.Body className="flex h-full w-full flex-col items-center divide-y overflow-y-auto">
<div className="flex size-full flex-col items-center overflow-auto p-16"> <div className="flex size-full flex-col items-center overflow-auto p-16">
<div className="flex w-full max-w-[736px] flex-col justify-center px-2 pb-2"> <div className="flex w-full max-w-[736px] flex-col justify-center px-2 pb-2">
@@ -302,7 +287,24 @@ export function OrderAllocateItemsForm({ order }: OrderAllocateItemsFormProps) {
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> <RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
size="small"
type="submit"
isLoading={isMutating}
disabled={!selectedLocationId || disableSubmit}
>
{t("orders.allocateItems.action")}
</Button>
</div>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -38,6 +38,7 @@ import { ClaimInboundItem } from "./claim-inbound-item.tsx"
import { ClaimCreateSchema, CreateClaimSchemaType } from "./schema" import { ClaimCreateSchema, CreateClaimSchemaType } from "./schema"
import { AdminReturn, HttpTypes } from "@medusajs/types" import { AdminReturn, HttpTypes } from "@medusajs/types"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form/keybound-form.tsx"
import { import {
useAddClaimInboundItems, useAddClaimInboundItems,
useAddClaimInboundShipping, useAddClaimInboundShipping,
@@ -430,7 +431,7 @@ export const ClaimCreateForm = ({
(a) => a.action === "SHIPPING_ADD" && !!a.return_id (a) => a.action === "SHIPPING_ADD" && !!a.return_id
) )
return action && !!!action?.return_id return action && !action?.return_id
}) })
const promises = inboundShippingMethods const promises = inboundShippingMethods
@@ -557,9 +558,8 @@ export const ClaimCreateForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form onSubmit={handleSubmit} className="flex h-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
<RouteFocusModal.Header /> <RouteFocusModal.Header />
<RouteFocusModal.Body className="flex size-full justify-center overflow-y-auto"> <RouteFocusModal.Body className="flex size-full justify-center overflow-y-auto">
<div className="mt-16 w-[720px] max-w-[100%] px-4 md:p-0"> <div className="mt-16 w-[720px] max-w-[100%] px-4 md:p-0">
<Heading level="h1">{t("orders.claims.create")}</Heading> <Heading level="h1">{t("orders.claims.create")}</Heading>
@@ -721,7 +721,7 @@ export const ClaimCreateForm = ({
<Text <Text
size="small" size="small"
leading="compact" leading="compact"
className="text-ui-fg-muted inline ml-1" className="text-ui-fg-muted ml-1 inline"
> >
({t("fields.optional")}) ({t("fields.optional")})
</Text> </Text>
@@ -1044,7 +1044,7 @@ export const ClaimCreateForm = ({
</div> </div>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -10,6 +10,7 @@ import {
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { import {
useCancelOrderEdit, useCancelOrderEdit,
useRequestOrderEdit, useRequestOrderEdit,
@@ -98,7 +99,7 @@ export const OrderEditCreateForm = ({
} }
}} }}
> >
<form onSubmit={handleSubmit} className="flex h-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
<RouteFocusModal.Header /> <RouteFocusModal.Header />
<RouteFocusModal.Body className="flex size-full justify-center overflow-y-auto"> <RouteFocusModal.Body className="flex size-full justify-center overflow-y-auto">
@@ -223,7 +224,7 @@ export const OrderEditCreateForm = ({
</div> </div>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -24,6 +24,7 @@ import { getStylizedAmount } from "../../../../../lib/money-amount-helpers"
import { CreateExchangeSchemaType, ExchangeCreateSchema } from "./schema" import { CreateExchangeSchemaType, ExchangeCreateSchema } from "./schema"
import { AdminReturn } from "@medusajs/types" import { AdminReturn } from "@medusajs/types"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form/keybound-form.tsx"
import { import {
useCancelExchangeRequest, useCancelExchangeRequest,
useExchangeConfirmRequest, useExchangeConfirmRequest,
@@ -253,7 +254,7 @@ export const ExchangeCreateForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form onSubmit={handleSubmit} className="flex h-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
<RouteFocusModal.Header /> <RouteFocusModal.Header />
<RouteFocusModal.Body className="flex size-full justify-center overflow-y-auto"> <RouteFocusModal.Body className="flex size-full justify-center overflow-y-auto">
@@ -538,7 +539,7 @@ export const ExchangeCreateForm = ({
</div> </div>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -14,6 +14,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateOrderFulfillment } from "../../../../../hooks/api/orders" import { useCreateOrderFulfillment } from "../../../../../hooks/api/orders"
import { useStockLocations } from "../../../../../hooks/api/stock-locations" import { useStockLocations } from "../../../../../hooks/api/stock-locations"
import { getFulfillableQuantity } from "../../../../../lib/order-item" import { getFulfillableQuantity } from "../../../../../lib/order-item"
@@ -46,13 +47,10 @@ export function OrderCreateFulfillmentForm({
const form = useForm<zod.infer<typeof CreateFulfillmentSchema>>({ const form = useForm<zod.infer<typeof CreateFulfillmentSchema>>({
defaultValues: { defaultValues: {
quantity: fulfillableItems.reduce( quantity: fulfillableItems.reduce((acc, item) => {
(acc, item) => {
acc[item.id] = getFulfillableQuantity(item) acc[item.id] = getFulfillableQuantity(item)
return acc return acc
}, }, {} as Record<string, number>),
{} as Record<string, number>
),
send_notification: !order.no_notification, send_notification: !order.no_notification,
}, },
resolver: zodResolver(CreateFulfillmentSchema), resolver: zodResolver(CreateFulfillmentSchema),
@@ -117,20 +115,17 @@ export function OrderCreateFulfillmentForm({
}) })
} }
const quantityMap = itemsToFulfill.reduce( const quantityMap = itemsToFulfill.reduce((acc, item) => {
(acc, item) => {
acc[item.id] = getFulfillableQuantity(item as OrderLineItemDTO) acc[item.id] = getFulfillableQuantity(item as OrderLineItemDTO)
return acc return acc
}, }, {} as Record<string, number>)
{} as Record<string, number>
)
form.setValue("quantity", quantityMap) form.setValue("quantity", quantityMap)
}, [...fulfilledQuantityArray, requiresShipping]) }, [...fulfilledQuantityArray, requiresShipping])
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
@@ -302,7 +297,7 @@ export function OrderCreateFulfillmentForm({
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -15,6 +15,7 @@ import { useNavigate, useSearchParams } from "react-router-dom"
import * as zod from "zod" import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useRefundPayment } from "../../../../../hooks/api" import { useRefundPayment } from "../../../../../hooks/api"
import { getCurrencySymbol } from "../../../../../lib/data/currencies" import { getCurrencySymbol } from "../../../../../lib/data/currencies"
import { formatCurrency } from "../../../../../lib/format-currency" import { formatCurrency } from "../../../../../lib/format-currency"
@@ -95,8 +96,11 @@ export const CreateRefundForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm
<RouteDrawer.Body> onSubmit={handleSubmit}
className="flex size-full flex-col overflow-hidden"
>
<RouteDrawer.Body className="flex-1 overflow-auto">
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
<Select <Select
value={payment?.id} value={payment?.id}
@@ -106,7 +110,7 @@ export const CreateRefundForm = ({
}) })
}} }}
> >
<Label className="font-sans txt-compact-small font-medium mb-[-6px]"> <Label className="txt-compact-small mb-[-6px] font-sans font-medium">
{t("orders.payment.selectPaymentToRefund")} {t("orders.payment.selectPaymentToRefund")}
</Label> </Label>
@@ -247,7 +251,7 @@ export const CreateRefundForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -30,6 +30,7 @@ import {
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { Combobox } from "../../../../../components/inputs/combobox" import { Combobox } from "../../../../../components/inputs/combobox"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { import {
useAddReturnItem, useAddReturnItem,
useAddReturnShipping, useAddReturnShipping,
@@ -402,9 +403,8 @@ export const ReturnCreateForm = ({
} }
}} }}
> >
<form onSubmit={handleSubmit} className="flex h-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
<RouteFocusModal.Header /> <RouteFocusModal.Header />
<RouteFocusModal.Body className="flex size-full justify-center overflow-y-auto"> <RouteFocusModal.Body className="flex size-full justify-center overflow-y-auto">
<div className="mt-16 w-[720px] max-w-[100%] px-4 md:p-0"> <div className="mt-16 w-[720px] max-w-[100%] px-4 md:p-0">
<Heading level="h1">{t("orders.returns.create")}</Heading> <Heading level="h1">{t("orders.returns.create")}</Heading>
@@ -553,7 +553,7 @@ export const ReturnCreateForm = ({
<Text <Text
size="small" size="small"
leading="compact" leading="compact"
className="text-ui-fg-muted inline ml-1" className="text-ui-fg-muted ml-1 inline"
> >
({t("fields.optional")}) ({t("fields.optional")})
</Text> </Text>
@@ -658,7 +658,7 @@ export const ReturnCreateForm = ({
preview.shipping_methods.forEach((s) => { preview.shipping_methods.forEach((s) => {
if (s.actions) { if (s.actions) {
for (let a of s.actions) { for (const a of s.actions) {
if (a.action === "SHIPPING_ADD") { if (a.action === "SHIPPING_ADD") {
actionId = a.id actionId = a.id
} }
@@ -763,7 +763,7 @@ export const ReturnCreateForm = ({
</div> </div>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -11,6 +11,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateOrderShipment } from "../../../../../hooks/api" import { useCreateOrderShipment } from "../../../../../hooks/api"
import { CreateShipmentSchema } from "./constants" import { CreateShipmentSchema } from "./constants"
@@ -71,7 +72,7 @@ export function OrderCreateShipmentForm({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
@@ -163,7 +164,7 @@ export function OrderCreateShipmentForm({
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -8,6 +8,7 @@ import { z } from "zod"
import { AdminOrder } from "@medusajs/types" import { AdminOrder } from "@medusajs/types"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
type OrderNoteFormProps = { type OrderNoteFormProps = {
order: AdminOrder order: AdminOrder
@@ -64,7 +65,7 @@ export const OrderNoteForm = ({ order }: OrderNoteFormProps) => {
return ( return (
<div> <div>
<Form {...form}> <Form {...form}>
<form onSubmit={handleSubmit}> <KeyboundForm onSubmit={handleSubmit}>
<div className="bg-ui-bg-field shadow-borders-base flex flex-col gap-y-2 rounded-md px-2 py-1.5"> <div className="bg-ui-bg-field shadow-borders-base flex flex-col gap-y-2 rounded-md px-2 py-1.5">
<Form.Field <Form.Field
control={form.control} control={form.control}
@@ -104,7 +105,7 @@ export const OrderNoteForm = ({ order }: OrderNoteFormProps) => {
</IconButton> </IconButton>
</div> </div>
</div> </div>
</form> </KeyboundForm>
</Form> </Form>
</div> </div>
) )
@@ -10,6 +10,7 @@ import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { Thumbnail } from "../../../../../components/common/thumbnail" import { Thumbnail } from "../../../../../components/common/thumbnail"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useStockLocation } from "../../../../../hooks/api" import { useStockLocation } from "../../../../../hooks/api"
import { import {
useAddReceiveItems, useAddReceiveItems,
@@ -209,7 +210,7 @@ export function OrderReceiveReturnForm({
return ( return (
<RouteDrawer.Form form={form} onClose={onFormClose}> <RouteDrawer.Form form={form} onClose={onFormClose}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex size-full flex-col overflow-hidden" className="flex size-full flex-col overflow-hidden"
> >
@@ -378,7 +379,7 @@ export function OrderReceiveReturnForm({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -19,6 +19,7 @@ import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { StackedDrawer } from "../../../../../components/modals/stacked-drawer" import { StackedDrawer } from "../../../../../components/modals/stacked-drawer"
import { useStackedModal } from "../../../../../components/modals/stacked-modal-provider" import { useStackedModal } from "../../../../../components/modals/stacked-modal-provider"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdatePriceList } from "../../../../../hooks/api/price-lists" import { useUpdatePriceList } from "../../../../../hooks/api/price-lists"
import { PriceListCustomerGroupRuleForm } from "../../../common/components/price-list-customer-group-rule-form" import { PriceListCustomerGroupRuleForm } from "../../../common/components/price-list-customer-group-rule-form"
import { PricingCustomerGroupsArrayType } from "../../../price-list-create/components/price-list-create-form/schema" import { PricingCustomerGroupsArrayType } from "../../../price-list-create/components/price-list-create-form/schema"
@@ -120,7 +121,7 @@ export const PriceListConfigurationForm = ({
<RouteDrawer.Description className="sr-only"> <RouteDrawer.Description className="sr-only">
{t("priceLists.configuration.edit.description")} {t("priceLists.configuration.edit.description")}
</RouteDrawer.Description> </RouteDrawer.Description>
<form <KeyboundForm
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -304,7 +305,7 @@ export const PriceListConfigurationForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -10,6 +10,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreatePriceList } from "../../../../../hooks/api/price-lists" import { useCreatePriceList } from "../../../../../hooks/api/price-lists"
import { exctractPricesFromProducts } from "../../../common/utils" import { exctractPricesFromProducts } from "../../../common/utils"
import { PriceListDetailsForm } from "./price-list-details-form" import { PriceListDetailsForm } from "./price-list-details-form"
@@ -119,13 +120,10 @@ export const PriceListCreateForm = ({
) => { ) => {
form.clearErrors(fields) form.clearErrors(fields)
const values = fields.reduce( const values = fields.reduce((acc, key) => {
(acc, key) => {
acc[key] = form.getValues(key) acc[key] = form.getValues(key)
return acc return acc
}, }, {} as Record<string, unknown>)
{} as Record<string, unknown>
)
const validationResult = schema.safeParse(values) const validationResult = schema.safeParse(values)
@@ -252,7 +250,7 @@ export const PriceListCreateForm = ({
onValueChange={(tab) => handleChangeTab(tab as Tab)} onValueChange={(tab) => handleChangeTab(tab as Tab)}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
<form onSubmit={handleSubmit} className="flex h-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
<RouteFocusModal.Header> <RouteFocusModal.Header>
<div className="flex w-full items-center justify-between gap-x-4"> <div className="flex w-full items-center justify-between gap-x-4">
<div className="-my-2 w-full max-w-[600px] border-l"> <div className="-my-2 w-full max-w-[600px] border-l">
@@ -318,7 +316,7 @@ export const PriceListCreateForm = ({
/> />
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</ProgressTabs> </ProgressTabs>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
@@ -14,6 +14,7 @@ import { z } from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdatePriceList } from "../../../../../hooks/api/price-lists" import { useUpdatePriceList } from "../../../../../hooks/api/price-lists"
import { PriceListStatus, PriceListType } from "../../../common/constants" import { PriceListStatus, PriceListType } from "../../../common/constants"
@@ -63,7 +64,7 @@ export const PriceListEditForm = ({ priceList }: PriceListEditFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -177,7 +178,7 @@ export const PriceListEditForm = ({ priceList }: PriceListEditFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -10,6 +10,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useBatchPriceListPrices } from "../../../../../hooks/api/price-lists" import { useBatchPriceListPrices } from "../../../../../hooks/api/price-lists"
import { exctractPricesFromProducts } from "../../../common/utils" import { exctractPricesFromProducts } from "../../../common/utils"
import { PriceListPricesAddPricesForm } from "./price-list-prices-add-prices-form" import { PriceListPricesAddPricesForm } from "./price-list-prices-add-prices-form"
@@ -199,7 +200,7 @@ export const PriceListPricesAddForm = ({
onValueChange={(tab) => handleChangeTab(tab as Tab)} onValueChange={(tab) => handleChangeTab(tab as Tab)}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
<form onSubmit={handleSubmit} className="flex h-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
<RouteFocusModal.Header> <RouteFocusModal.Header>
<div className="flex w-full items-center justify-between gap-x-4"> <div className="flex w-full items-center justify-between gap-x-4">
<div className="-my-2 w-full max-w-[600px] border-l"> <div className="-my-2 w-full max-w-[600px] border-l">
@@ -256,7 +257,7 @@ export const PriceListPricesAddForm = ({
/> />
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</ProgressTabs> </ProgressTabs>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
@@ -11,6 +11,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useBatchPriceListPrices } from "../../../../../hooks/api/price-lists" import { useBatchPriceListPrices } from "../../../../../hooks/api/price-lists"
import { castNumber } from "../../../../../lib/cast-number" import { castNumber } from "../../../../../lib/cast-number"
import { usePriceListGridColumns } from "../../../common/hooks/use-price-list-grid-columns" import { usePriceListGridColumns } from "../../../common/hooks/use-price-list-grid-columns"
@@ -89,7 +90,7 @@ export const PriceListPricesEditForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form onSubmit={handleSubmit} className="flex size-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex size-full flex-col">
<RouteFocusModal.Header /> <RouteFocusModal.Header />
<RouteFocusModal.Body className="flex flex-col overflow-hidden"> <RouteFocusModal.Body className="flex flex-col overflow-hidden">
<DataGrid <DataGrid
@@ -116,7 +117,7 @@ export const PriceListPricesEditForm = ({
</Button> </Button>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -185,13 +186,10 @@ function convertToPriceArray(
) { ) {
const prices: PriceObject[] = [] const prices: PriceObject[] = []
const regionCurrencyMap = regions.reduce( const regionCurrencyMap = regions.reduce((map, region) => {
(map, region) => {
map[region.id] = region.currency_code map[region.id] = region.currency_code
return map return map
}, }, {} as Record<string, string>)
{} as Record<string, string>
)
for (const [_productId, product] of Object.entries(data || {})) { for (const [_productId, product] of Object.entries(data || {})) {
const { variants } = product || {} const { variants } = product || {}
@@ -249,21 +247,15 @@ function comparePrices(initialPrices: PriceObject[], newPrices: PriceObject[]) {
const pricesToCreate: HttpTypes.AdminCreatePriceListPrice[] = [] const pricesToCreate: HttpTypes.AdminCreatePriceListPrice[] = []
const pricesToDelete: string[] = [] const pricesToDelete: string[] = []
const initialPriceMap = initialPrices.reduce( const initialPriceMap = initialPrices.reduce((map, price) => {
(map, price) => {
map[createMapKey(price)] = price map[createMapKey(price)] = price
return map return map
}, }, {} as Record<string, (typeof initialPrices)[0]>)
{} as Record<string, (typeof initialPrices)[0]>
)
const newPriceMap = newPrices.reduce( const newPriceMap = newPrices.reduce((map, price) => {
(map, price) => {
map[createMapKey(price)] = price map[createMapKey(price)] = price
return map return map
}, }, {} as Record<string, (typeof newPrices)[0]>)
{} as Record<string, (typeof newPrices)[0]>
)
const keys = new Set([ const keys = new Set([
...Object.keys(initialPriceMap), ...Object.keys(initialPriceMap),
@@ -8,6 +8,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateProductTag } from "../../../../../hooks/api" import { useCreateProductTag } from "../../../../../hooks/api"
const ProductTagCreateSchema = z.object({ const ProductTagCreateSchema = z.object({
@@ -45,7 +46,7 @@ export const ProductTagCreateForm = () => {
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
className="flex size-full flex-col overflow-hidden" className="flex size-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -93,7 +94,7 @@ export const ProductTagCreateForm = () => {
</Button> </Button>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -7,6 +7,7 @@ import { z } from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateProductTag } from "../../../../../hooks/api" import { useUpdateProductTag } from "../../../../../hooks/api"
type ProductTagEditFormProps = { type ProductTagEditFormProps = {
@@ -48,7 +49,7 @@ export const ProductTagEditForm = ({ productTag }: ProductTagEditFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
className="flex size-full flex-col overflow-hidden" className="flex size-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -81,7 +82,7 @@ export const ProductTagEditForm = ({ productTag }: ProductTagEditFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -8,6 +8,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateProductType } from "../../../../../hooks/api/product-types" import { useCreateProductType } from "../../../../../hooks/api/product-types"
const CreateProductTypeSchema = z.object({ const CreateProductTypeSchema = z.object({
@@ -48,7 +49,10 @@ export const CreateProductTypeForm = () => {
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-col overflow-hidden"> <KeyboundForm
onSubmit={handleSubmit}
className="flex flex-col overflow-hidden"
>
<RouteFocusModal.Header> <RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2"> <div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild> <RouteFocusModal.Close asChild>
@@ -93,7 +97,7 @@ export const CreateProductTypeForm = () => {
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next"
import { z } from "zod" import { z } from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateProductType } from "../../../../../hooks/api/product-types" import { useUpdateProductType } from "../../../../../hooks/api/product-types"
const EditProductTypeSchema = z.object({ const EditProductTypeSchema = z.object({
@@ -54,7 +55,7 @@ export const EditProductTypeForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -87,7 +88,7 @@ export const EditProductTypeForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -10,6 +10,7 @@ import { Form } from "../../../../../components/common/form"
import { Combobox } from "../../../../../components/inputs/combobox" import { Combobox } from "../../../../../components/inputs/combobox"
import { CountrySelect } from "../../../../../components/inputs/country-select" import { CountrySelect } from "../../../../../components/inputs/country-select"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateProductVariant } from "../../../../../hooks/api/products" import { useUpdateProductVariant } from "../../../../../hooks/api/products"
import { import {
transformNullableFormData, transformNullableFormData,
@@ -48,23 +49,23 @@ export const ProductEditVariantForm = ({
}: ProductEditVariantFormProps) => { }: ProductEditVariantFormProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const { handleSuccess } = useRouteModal() const { handleSuccess } = useRouteModal()
const defaultOptions = product.options.reduce((acc: any, option: any) => { const defaultOptions = product.options?.reduce((acc: any, option: any) => {
const varOpt = variant.options.find((o: any) => o.option_id === option.id) const varOpt = variant.options?.find((o: any) => o.option_id === option.id)
acc[option.title] = varOpt?.value acc[option.title] = varOpt?.value
return acc return acc
}, {}) }, {})
const form = useForm<z.infer<typeof ProductEditVariantSchema>>({ const form = useForm<z.infer<typeof ProductEditVariantSchema>>({
defaultValues: { defaultValues: {
title: variant.title, title: variant.title || "",
material: variant.material || "", material: variant.material || "",
sku: variant.sku || "", sku: variant.sku || "",
ean: variant.ean || "", ean: variant.ean || "",
upc: variant.upc || "", upc: variant.upc || "",
barcode: variant.barcode || "", barcode: variant.barcode || "",
inventory_quantity: variant.inventory_quantity || "", inventory_quantity: variant.inventory_quantity || "",
manage_inventory: variant.manage_inventory, manage_inventory: variant.manage_inventory || false,
allow_backorder: variant.allow_backorder, allow_backorder: variant.allow_backorder || false,
weight: variant.weight || "", weight: variant.weight || "",
height: variant.height || "", height: variant.height || "",
width: variant.width || "", width: variant.width || "",
@@ -124,7 +125,7 @@ export const ProductEditVariantForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex size-full flex-col overflow-hidden" className="flex size-full flex-col overflow-hidden"
> >
@@ -160,7 +161,7 @@ export const ProductEditVariantForm = ({
) )
}} }}
/> />
{product.options.map((option: any) => { {product.options?.map((option: any) => {
return ( return (
<Form.Field <Form.Field
key={option.id} key={option.id}
@@ -438,7 +439,7 @@ export const ProductEditVariantForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -13,6 +13,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useProductVariantsInventoryItemsBatch } from "../../../../../hooks/api/products" import { useProductVariantsInventoryItemsBatch } from "../../../../../hooks/api/products"
import { useComboboxData } from "../../../../../hooks/use-combobox-data" import { useComboboxData } from "../../../../../hooks/use-combobox-data"
import { sdk } from "../../../../../lib/client" import { sdk } from "../../../../../lib/client"
@@ -151,7 +152,7 @@ export function ManageVariantInventoryItemsForm({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -302,7 +303,7 @@ export function ManageVariantInventoryItemsForm({
</div> </div>
</div> </div>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -5,6 +5,7 @@ import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { CountrySelect } from "../../../../../components/inputs/country-select" import { CountrySelect } from "../../../../../components/inputs/country-select"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { import {
FormExtensionZone, FormExtensionZone,
useDashboardExtension, useDashboardExtension,
@@ -85,7 +86,7 @@ export const ProductAttributesForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex h-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex h-full flex-col gap-y-8"> <div className="flex h-full flex-col gap-y-8">
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
@@ -266,7 +267,7 @@ export const ProductAttributesForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -8,6 +8,7 @@ import { HttpTypes } from "@medusajs/types"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { ChipInput } from "../../../../../components/inputs/chip-input" import { ChipInput } from "../../../../../components/inputs/chip-input"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateProductOption } from "../../../../../hooks/api/products" import { useCreateProductOption } from "../../../../../hooks/api/products"
type EditProductOptionsFormProps = { type EditProductOptionsFormProps = {
@@ -53,7 +54,7 @@ export const CreateProductOptionForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -115,7 +116,7 @@ export const CreateProductOptionForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -1,8 +1,8 @@
import { zodResolver } from "@hookform/resolvers/zod" import { zodResolver } from "@hookform/resolvers/zod"
import { Button, ProgressStatus, ProgressTabs, toast } from "@medusajs/ui" import { Button, ProgressStatus, ProgressTabs, toast } from "@medusajs/ui"
import { useEffect, useMemo, useState } from "react"
import { useFieldArray, useForm, useWatch } from "react-hook-form" import { useFieldArray, useForm, useWatch } from "react-hook-form"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { useEffect, useMemo, useState } from "react"
import { z } from "zod" import { z } from "zod"
import { AdminCreateProductVariantPrice, HttpTypes } from "@medusajs/types" import { AdminCreateProductVariantPrice, HttpTypes } from "@medusajs/types"
@@ -11,7 +11,11 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useRegions } from "../../../../../hooks/api"
import { useCreateProductVariant } from "../../../../../hooks/api/products" import { useCreateProductVariant } from "../../../../../hooks/api/products"
import { castNumber } from "../../../../../lib/cast-number"
import { partialFormValidation } from "../../../../../lib/validation"
import { import {
CreateProductVariantSchema, CreateProductVariantSchema,
CreateVariantDetailsFields, CreateVariantDetailsFields,
@@ -20,11 +24,8 @@ import {
CreateVariantPriceSchema, CreateVariantPriceSchema,
} from "./constants" } from "./constants"
import DetailsTab from "./details-tab" import DetailsTab from "./details-tab"
import PricingTab from "./pricing-tab"
import InventoryKitTab from "./inventory-kit-tab" import InventoryKitTab from "./inventory-kit-tab"
import { castNumber } from "../../../../../lib/cast-number" import PricingTab from "./pricing-tab"
import { useRegions } from "../../../../../hooks/api"
import { partialFormValidation } from "../../../../../lib/validation"
enum Tab { enum Tab {
DETAIL = "detail", DETAIL = "detail",
@@ -74,13 +75,10 @@ export const CreateProductVariantForm = ({
return {} return {}
} }
return regions.reduce( return regions.reduce((acc, reg) => {
(acc, reg) => {
acc[reg.id] = reg.currency_code acc[reg.id] = reg.currency_code
return acc return acc
}, }, {} as Record<string, string>)
{} as Record<string, string>
)
}, [regions]) }, [regions])
const isManageInventoryEnabled = useWatch({ const isManageInventoryEnabled = useWatch({
@@ -263,7 +261,7 @@ export const CreateProductVariantForm = ({
onValueChange={(tab) => handleChangeTab(tab as Tab)} onValueChange={(tab) => handleChangeTab(tab as Tab)}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
@@ -332,7 +330,7 @@ export const CreateProductVariantForm = ({
/> />
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</ProgressTabs> </ProgressTabs>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
@@ -7,6 +7,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { import {
useDashboardExtension, useDashboardExtension,
useExtendableForm, useExtendableForm,
@@ -211,14 +212,22 @@ export const ProductCreateForm = ({
return ( return (
<RouteFocusModal> <RouteFocusModal>
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onKeyDown={(e) => { onKeyDown={(e) => {
// We want to continue to the next tab on enter instead of saving as draft immediately // We want to continue to the next tab on enter instead of saving as draft immediately
if (e.key === "Enter") { if (e.key === "Enter") {
e.preventDefault()
if (e.metaKey || e.ctrlKey) {
if (tab !== Tab.VARIANTS) { if (tab !== Tab.VARIANTS) {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
onNext(tab) onNext(tab)
return
}
handleSubmit()
} }
} }
}} }}
@@ -332,7 +341,7 @@ export const ProductCreateForm = ({
/> />
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
</RouteFocusModal> </RouteFocusModal>
) )
@@ -1,2 +0,0 @@
export * from "./product-create-details-context"
export * from "./use-product-create-details-context"
@@ -1,9 +0,0 @@
import { createContext } from "react"
type ProductCreateDetailsContextValue = {
open: boolean
onOpenChange: (open: boolean) => void
}
export const ProductCreateDetailsContext =
createContext<ProductCreateDetailsContextValue | null>(null)
@@ -1,14 +0,0 @@
import { useContext } from "react"
import { ProductCreateDetailsContext } from "./product-create-details-context"
export const useProductCreateDetailsContext = () => {
const context = useContext(ProductCreateDetailsContext)
if (!context) {
throw new Error(
"useProductCreateDetailsContext must be used within a ProductCreateDetailsContextProvider"
)
}
return context
}
@@ -6,11 +6,11 @@ import { ChipGroup } from "../../../../../../../components/common/chip-group"
import { Form } from "../../../../../../../components/common/form" import { Form } from "../../../../../../../components/common/form"
import { SwitchBox } from "../../../../../../../components/common/switch-box" import { SwitchBox } from "../../../../../../../components/common/switch-box"
import { Combobox } from "../../../../../../../components/inputs/combobox" import { Combobox } from "../../../../../../../components/inputs/combobox"
import { StackedFocusModal } from "../../../../../../../components/modals"
import { useComboboxData } from "../../../../../../../hooks/use-combobox-data" import { useComboboxData } from "../../../../../../../hooks/use-combobox-data"
import { sdk } from "../../../../../../../lib/client" import { sdk } from "../../../../../../../lib/client"
import { CategoryCombobox } from "../../../../../common/components/category-combobox" import { CategoryCombobox } from "../../../../../common/components/category-combobox"
import { ProductCreateSchemaType } from "../../../../types" import { ProductCreateSchemaType } from "../../../../types"
import { useProductCreateDetailsContext } from "../product-create-organize-context"
type ProductCreateOrganizationSectionProps = { type ProductCreateOrganizationSectionProps = {
form: UseFormReturn<ProductCreateSchemaType> form: UseFormReturn<ProductCreateSchemaType>
@@ -20,7 +20,6 @@ export const ProductCreateOrganizationSection = ({
form, form,
}: ProductCreateOrganizationSectionProps) => { }: ProductCreateOrganizationSectionProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const { onOpenChange } = useProductCreateDetailsContext()
const collections = useComboboxData({ const collections = useComboboxData({
queryKey: ["product_collections"], queryKey: ["product_collections"],
@@ -178,14 +177,11 @@ export const ProductCreateOrganizationSection = ({
<Trans i18nKey={"products.fields.sales_channels.hint"} /> <Trans i18nKey={"products.fields.sales_channels.hint"} />
</Form.Hint> </Form.Hint>
</div> </div>
<Button <StackedFocusModal.Trigger asChild>
size="small" <Button size="small" variant="secondary" type="button">
variant="secondary"
type="button"
onClick={() => onOpenChange(true)}
>
{t("actions.add")} {t("actions.add")}
</Button> </Button>
</StackedFocusModal.Trigger>
</div> </div>
<Form.Control className="mt-0"> <Form.Control className="mt-0">
{fields.length > 0 && ( {fields.length > 0 && (
@@ -9,7 +9,10 @@ import { useEffect, useMemo, useState } from "react"
import { UseFormReturn } from "react-hook-form" import { UseFormReturn } from "react-hook-form"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { SplitView } from "../../../../../../../components/layout/split-view" import {
StackedFocusModal,
useStackedModal,
} from "../../../../../../../components/modals"
import { DataTable } from "../../../../../../../components/table/data-table" import { DataTable } from "../../../../../../../components/table/data-table"
import { useSalesChannels } from "../../../../../../../hooks/api/sales-channels" import { useSalesChannels } from "../../../../../../../hooks/api/sales-channels"
import { useSalesChannelTableColumns } from "../../../../../../../hooks/table/columns/use-sales-channel-table-columns" import { useSalesChannelTableColumns } from "../../../../../../../hooks/table/columns/use-sales-channel-table-columns"
@@ -17,28 +20,27 @@ import { useSalesChannelTableFilters } from "../../../../../../../hooks/table/fi
import { useSalesChannelTableQuery } from "../../../../../../../hooks/table/query/use-sales-channel-table-query" import { useSalesChannelTableQuery } from "../../../../../../../hooks/table/query/use-sales-channel-table-query"
import { useDataTable } from "../../../../../../../hooks/use-data-table" import { useDataTable } from "../../../../../../../hooks/use-data-table"
import { ProductCreateSchemaType } from "../../../../types" import { ProductCreateSchemaType } from "../../../../types"
import { useProductCreateDetailsContext } from "../product-create-organize-context" import { SC_STACKED_MODAL_ID } from "../../constants"
type ProductCreateSalesChannelDrawerProps = { type ProductCreateSalesChannelStackedModalProps = {
form: UseFormReturn<ProductCreateSchemaType> form: UseFormReturn<ProductCreateSchemaType>
} }
const PAGE_SIZE = 50 const PAGE_SIZE = 50
const PREFIX = "sc"
export const ProductCreateSalesChannelDrawer = ({ export const ProductCreateSalesChannelStackedModal = ({
form, form,
}: ProductCreateSalesChannelDrawerProps) => { }: ProductCreateSalesChannelStackedModalProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const { open, onOpenChange } = useProductCreateDetailsContext()
const { getValues, setValue } = form const { getValues, setValue } = form
const { setIsOpen, getIsOpen } = useStackedModal()
const [selection, setSelection] = useState<RowSelectionState>({}) const [selection, setSelection] = useState<RowSelectionState>({})
const [state, setState] = useState<{ id: string; name: string }[]>([]) const [state, setState] = useState<{ id: string; name: string }[]>([])
const { searchParams, raw } = useSalesChannelTableQuery({ const { searchParams, raw } = useSalesChannelTableQuery({
pageSize: PAGE_SIZE, pageSize: PAGE_SIZE,
prefix: PREFIX, prefix: SC_STACKED_MODAL_ID,
}) })
const { sales_channels, count, isLoading, isError, error } = useSalesChannels( const { sales_channels, count, isLoading, isError, error } = useSalesChannels(
{ {
@@ -46,6 +48,8 @@ export const ProductCreateSalesChannelDrawer = ({
} }
) )
const open = getIsOpen(SC_STACKED_MODAL_ID)
useEffect(() => { useEffect(() => {
if (!open) { if (!open) {
return return
@@ -98,7 +102,7 @@ export const ProductCreateSalesChannelDrawer = ({
shouldDirty: true, shouldDirty: true,
shouldTouch: true, shouldTouch: true,
}) })
onOpenChange(false) setIsOpen(SC_STACKED_MODAL_ID, false)
} }
const filters = useSalesChannelTableFilters() const filters = useSalesChannelTableFilters()
@@ -116,7 +120,7 @@ export const ProductCreateSalesChannelDrawer = ({
state: selection, state: selection,
updater, updater,
}, },
prefix: PREFIX, prefix: SC_STACKED_MODAL_ID,
}) })
if (isError) { if (isError) {
@@ -124,8 +128,9 @@ export const ProductCreateSalesChannelDrawer = ({
} }
return ( return (
<div className="flex h-full flex-1 flex-col overflow-hidden"> <StackedFocusModal.Content className="flex flex-col overflow-hidden">
<div className="flex-1"> <StackedFocusModal.Header />
<StackedFocusModal.Body className="flex-1 overflow-hidden">
<DataTable <DataTable
table={table} table={table}
columns={columns} columns={columns}
@@ -138,20 +143,22 @@ export const ProductCreateSalesChannelDrawer = ({
search search
pagination pagination
count={count} count={count}
prefix={PREFIX} prefix={SC_STACKED_MODAL_ID}
/> />
</div> </StackedFocusModal.Body>
<div className="flex items-center justify-end gap-x-2 border-t px-6 py-4"> <StackedFocusModal.Footer>
<SplitView.Close asChild> <div className="flex items-center justify-end gap-x-2">
<StackedFocusModal.Close asChild>
<Button size="small" variant="secondary" type="button"> <Button size="small" variant="secondary" type="button">
{t("actions.cancel")} {t("actions.cancel")}
</Button> </Button>
</SplitView.Close> </StackedFocusModal.Close>
<Button size="small" onClick={handleAdd} type="button"> <Button size="small" onClick={handleAdd} type="button">
{t("actions.add")} {t("actions.save")}
</Button> </Button>
</div> </div>
</div> </StackedFocusModal.Footer>
</StackedFocusModal.Content>
) )
} }
@@ -0,0 +1 @@
export const SC_STACKED_MODAL_ID = "sc"
@@ -1,31 +1,25 @@
import { useState } from "react"
import { UseFormReturn } from "react-hook-form" import { UseFormReturn } from "react-hook-form"
import { SplitView } from "../../../../../components/layout/split-view" import { StackedFocusModal } from "../../../../../components/modals"
import { import {
FormExtensionZone, FormExtensionZone,
useDashboardExtension, useDashboardExtension,
} from "../../../../../extensions" } from "../../../../../extensions"
import { ProductCreateSchemaType } from "../../types" import { ProductCreateSchemaType } from "../../types"
import { ProductCreateDetailsContext } from "./components/product-create-organize-context"
import { ProductCreateOrganizationSection } from "./components/product-create-organize-section" import { ProductCreateOrganizationSection } from "./components/product-create-organize-section"
import { ProductCreateSalesChannelDrawer } from "./components/product-create-sales-channel-drawer" import { ProductCreateSalesChannelStackedModal } from "./components/product-create-sales-channel-stacked-modal"
import { SC_STACKED_MODAL_ID } from "./constants"
type ProductAttributesProps = { type ProductAttributesProps = {
form: UseFormReturn<ProductCreateSchemaType> form: UseFormReturn<ProductCreateSchemaType>
} }
export const ProductCreateOrganizeForm = ({ form }: ProductAttributesProps) => { export const ProductCreateOrganizeForm = ({ form }: ProductAttributesProps) => {
const [open, setOpen] = useState(false)
const { getFormFields } = useDashboardExtension() const { getFormFields } = useDashboardExtension()
const fields = getFormFields("product", "create", "organize") const fields = getFormFields("product", "create", "organize")
return ( return (
<ProductCreateDetailsContext.Provider <StackedFocusModal id={SC_STACKED_MODAL_ID}>
value={{ open, onOpenChange: setOpen }}
>
<SplitView open={open} onOpenChange={setOpen}>
<SplitView.Content>
<div className="flex flex-col items-center p-16"> <div className="flex flex-col items-center p-16">
<div className="flex w-full max-w-[720px] flex-col gap-y-8"> <div className="flex w-full max-w-[720px] flex-col gap-y-8">
<ProductCreateOrganizationSection form={form} /> <ProductCreateOrganizationSection form={form} />
@@ -35,11 +29,7 @@ export const ProductCreateOrganizeForm = ({ form }: ProductAttributesProps) => {
{/* <ProductCreateAttributeSection form={form} />*/} {/* <ProductCreateAttributeSection form={form} />*/}
</div> </div>
</div> </div>
</SplitView.Content> <ProductCreateSalesChannelStackedModal form={form} />
<SplitView.Drawer> </StackedFocusModal>
<ProductCreateSalesChannelDrawer form={form} />
</SplitView.Drawer>
</SplitView>
</ProductCreateDetailsContext.Provider>
) )
} }
@@ -7,10 +7,8 @@ import { z } from "zod"
import { HttpTypes } from "@medusajs/types" import { HttpTypes } from "@medusajs/types"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { ChipInput } from "../../../../../components/inputs/chip-input" import { ChipInput } from "../../../../../components/inputs/chip-input"
import { import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
RouteDrawer, import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
useRouteModal,
} from "../../../../../components/modals"
import { useUpdateProductOption } from "../../../../../hooks/api/products" import { useUpdateProductOption } from "../../../../../hooks/api/products"
type EditProductOptionFormProps = { type EditProductOptionFormProps = {
@@ -57,7 +55,7 @@ export const CreateProductOptionForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -119,7 +117,7 @@ export const CreateProductOptionForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -10,6 +10,7 @@ import { useExtendableForm } from "../../../../../extensions/forms/hooks"
import { useUpdateProduct } from "../../../../../hooks/api/products" import { useUpdateProduct } from "../../../../../hooks/api/products"
import { transformNullableFormData } from "../../../../../lib/form-helpers" import { transformNullableFormData } from "../../../../../lib/form-helpers"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { import {
FormExtensionZone, FormExtensionZone,
useDashboardExtension, useDashboardExtension,
@@ -77,7 +78,7 @@ export const EditProductForm = ({ product }: EditProductFormProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -230,7 +231,7 @@ export const EditProductForm = ({ product }: EditProductFormProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -11,6 +11,7 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateProduct } from "../../../../../hooks/api/products" import { useUpdateProduct } from "../../../../../hooks/api/products"
import { sdk } from "../../../../../lib/client" import { sdk } from "../../../../../lib/client"
import { MediaGrid } from "../../../common/components/media-grid-view" import { MediaGrid } from "../../../common/components/media-grid-view"
@@ -142,25 +143,17 @@ export const EditProductMediaForm = ({ product }: ProductMediaViewProps) => {
return ( return (
<RouteFocusModal.Form blockSearch form={form}> <RouteFocusModal.Form blockSearch form={form}>
<form <KeyboundForm
className="flex size-full flex-col overflow-hidden" className="flex size-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<RouteFocusModal.Header> <RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2"> <div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button variant="secondary" size="small" asChild> <Button variant="secondary" size="small" asChild>
<Link to={{ pathname: ".", search: undefined }}> <Link to={{ pathname: ".", search: undefined }}>
{t("products.media.galleryLabel")} {t("products.media.galleryLabel")}
</Link> </Link>
</Button> </Button>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</div> </div>
</RouteFocusModal.Header> </RouteFocusModal.Header>
<RouteFocusModal.Body className="flex flex-col overflow-hidden"> <RouteFocusModal.Body className="flex flex-col overflow-hidden">
@@ -170,7 +163,7 @@ export const EditProductMediaForm = ({ product }: ProductMediaViewProps) => {
onCheckedChange={handleCheckedChange} onCheckedChange={handleCheckedChange}
selection={selection} selection={selection}
/> />
<div className="bg-ui-bg-base border-b px-6 py-4 lg:border-b-0 lg:border-l"> <div className="bg-ui-bg-base overflow-auto border-b px-6 py-4 lg:border-b-0 lg:border-l">
<UploadMediaFormItem form={form} append={append} /> <UploadMediaFormItem form={form} append={append} />
</div> </div>
</div> </div>
@@ -200,7 +193,19 @@ export const EditProductMediaForm = ({ product }: ProductMediaViewProps) => {
/> />
</CommandBar.Bar> </CommandBar.Bar>
</CommandBar> </CommandBar>
</form> <RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" type="submit" isLoading={isPending}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -6,6 +6,7 @@ import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { Combobox } from "../../../../../components/inputs/combobox" import { Combobox } from "../../../../../components/inputs/combobox"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { import {
FormExtensionZone, FormExtensionZone,
useDashboardExtension, useDashboardExtension,
@@ -107,7 +108,7 @@ export const ProductOrganizationForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex h-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex h-full flex-col gap-y-4"> <div className="flex h-full flex-col gap-y-4">
<Form.Field <Form.Field
@@ -211,7 +212,7 @@ export const ProductOrganizationForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next"
import * as zod from "zod" import * as zod from "zod"
import { RouteFocusModal, useRouteModal } from "../../../components/modals" import { RouteFocusModal, useRouteModal } from "../../../components/modals"
import { KeyboundForm } from "../../../components/utilities/keybound-form"
import { useUpdateProductVariantsBatch } from "../../../hooks/api/products" import { useUpdateProductVariantsBatch } from "../../../hooks/api/products"
import { useRegions } from "../../../hooks/api/regions" import { useRegions } from "../../../hooks/api/regions"
import { castNumber } from "../../../lib/cast-number" import { castNumber } from "../../../lib/cast-number"
@@ -120,7 +121,7 @@ export const PricingEdit = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form onSubmit={handleSubmit} className="flex size-full flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex size-full flex-col">
<RouteFocusModal.Header /> <RouteFocusModal.Header />
<RouteFocusModal.Body className="flex flex-col overflow-hidden"> <RouteFocusModal.Body className="flex flex-col overflow-hidden">
<VariantPricingForm form={form as any} /> <VariantPricingForm form={form as any} />
@@ -142,7 +143,7 @@ export const PricingEdit = ({
</Button> </Button>
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -6,6 +6,7 @@ import * as zod from "zod"
import { zodResolver } from "@hookform/resolvers/zod" import { zodResolver } from "@hookform/resolvers/zod"
import { HttpTypes } from "@medusajs/types" import { HttpTypes } from "@medusajs/types"
import { keepPreviousData } from "@tanstack/react-query"
import { useForm } from "react-hook-form" import { useForm } from "react-hook-form"
import { import {
RouteFocusModal, RouteFocusModal,
@@ -69,7 +70,7 @@ export const EditSalesChannelsForm = ({
...searchParams, ...searchParams,
}, },
{ {
keepPreviousData: true, placeholderData: keepPreviousData,
} }
) )
@@ -90,7 +91,7 @@ export const EditSalesChannelsForm = ({
pageSize: PAGE_SIZE, pageSize: PAGE_SIZE,
}) })
const { mutateAsync, isLoading: isMutating } = useUpdateProduct(product.id) const { mutateAsync, isPending: isMutating } = useUpdateProduct(product.id)
const handleSubmit = form.handleSubmit(async (data) => { const handleSubmit = form.handleSubmit(async (data) => {
const arr = data.sales_channels ?? [] const arr = data.sales_channels ?? []
@@ -120,19 +121,8 @@ export const EditSalesChannelsForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<div className="flex h-full flex-col overflow-hidden"> <div className="flex h-full flex-col overflow-hidden">
<RouteFocusModal.Header> <RouteFocusModal.Header />
<div className="flex items-center justify-end gap-x-2"> <RouteFocusModal.Body className="flex-1 overflow-hidden">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" isLoading={isMutating} onClick={handleSubmit}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body>
<DataTable <DataTable
table={table} table={table}
columns={columns} columns={columns}
@@ -147,6 +137,18 @@ export const EditSalesChannelsForm = ({
layout="fill" layout="fill"
/> />
</RouteFocusModal.Body> </RouteFocusModal.Body>
<RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button size="small" isLoading={isMutating} onClick={handleSubmit}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Footer>
</div> </div>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
@@ -7,6 +7,7 @@ import * as zod from "zod"
import { UserDTO } from "@medusajs/types" import { UserDTO } from "@medusajs/types"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateUser } from "../../../../../hooks/api/users" import { useUpdateUser } from "../../../../../hooks/api/users"
import { languages } from "../../../../../i18n/languages" import { languages } from "../../../../../i18n/languages"
@@ -64,7 +65,7 @@ export const EditProfileForm = ({ user, usageInsights }: EditProfileProps) => {
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleSubmit} className="flex flex-1 flex-col"> <KeyboundForm onSubmit={handleSubmit} className="flex flex-1 flex-col">
<RouteDrawer.Body> <RouteDrawer.Body>
<div className="flex flex-col gap-y-8"> <div className="flex flex-col gap-y-8">
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
@@ -187,7 +188,7 @@ export const EditProfileForm = ({ user, usageInsights }: EditProfileProps) => {
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -5,6 +5,7 @@ import { useState } from "react"
import { useForm } from "react-hook-form" import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { RouteDrawer } from "../../../../../../components/modals" import { RouteDrawer } from "../../../../../../components/modals"
import { KeyboundForm } from "../../../../../../components/utilities/keybound-form"
import { RuleTypeValues } from "../../edit-rules" import { RuleTypeValues } from "../../edit-rules"
import { RulesFormField } from "../rules-form-field" import { RulesFormField } from "../rules-form-field"
import { EditRules, EditRulesType } from "./form-schema" import { EditRules, EditRulesType } from "./form-schema"
@@ -35,7 +36,10 @@ export const EditRulesForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form onSubmit={handleFormSubmit} className="flex h-full flex-col"> <KeyboundForm
onSubmit={handleFormSubmit}
className="flex h-full flex-col"
>
<RouteDrawer.Body> <RouteDrawer.Body>
<RulesFormField <RulesFormField
form={form as any} form={form as any}
@@ -59,7 +63,7 @@ export const EditRulesForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -7,6 +7,7 @@ import { Trans, useTranslation } from "react-i18next"
import * as zod from "zod" import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdatePromotion } from "../../../../../hooks/api/promotions" import { useUpdatePromotion } from "../../../../../hooks/api/promotions"
import { CreateCampaignFormFields } from "../../../../campaigns/common/components/create-campaign-form-fields" import { CreateCampaignFormFields } from "../../../../campaigns/common/components/create-campaign-form-fields"
import { CampaignDetails } from "./campaign-details" import { CampaignDetails } from "./campaign-details"
@@ -213,7 +214,7 @@ export const AddCampaignPromotionForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex size-full flex-col overflow-hidden" className="flex size-full flex-col overflow-hidden"
> >
@@ -238,7 +239,7 @@ export const AddCampaignPromotionForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -32,10 +32,11 @@ import {
RouteFocusModal, RouteFocusModal,
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCampaigns } from "../../../../../hooks/api/campaigns" import { useCampaigns } from "../../../../../hooks/api/campaigns"
import { useCreatePromotion } from "../../../../../hooks/api/promotions" import { useCreatePromotion } from "../../../../../hooks/api/promotions"
import { getCurrencySymbol } from "../../../../../lib/data/currencies" import { getCurrencySymbol } from "../../../../../lib/data/currencies"
import { defaultCampaignValues } from "../../../../campaigns/campaign-create/components/create-campaign-form" import { DEFAULT_CAMPAIGN_VALUES } from "../../../../campaigns/common/constants"
import { RulesFormField } from "../../../common/edit-rules/components/rules-form-field" import { RulesFormField } from "../../../common/edit-rules/components/rules-form-field"
import { AddCampaignPromotionFields } from "../../../promotion-add-campaign/components/add-campaign-promotion-form" import { AddCampaignPromotionFields } from "../../../promotion-add-campaign/components/add-campaign-promotion-form"
import { Tab } from "./constants" import { Tab } from "./constants"
@@ -319,9 +320,9 @@ export const CreatePromotionForm = () => {
if (watchCampaignChoice === "new") { if (watchCampaignChoice === "new") {
if (!formData.campaign || !formData.campaign?.budget?.type) { if (!formData.campaign || !formData.campaign?.budget?.type) {
form.setValue("campaign", { form.setValue("campaign", {
...defaultCampaignValues, ...DEFAULT_CAMPAIGN_VALUES,
budget: { budget: {
...defaultCampaignValues.budget, ...DEFAULT_CAMPAIGN_VALUES.budget,
currency_code: formData.application_method.currency_code, currency_code: formData.application_method.currency_code,
}, },
}) })
@@ -350,7 +351,7 @@ export const CreatePromotionForm = () => {
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form className="flex h-full flex-col" onSubmit={handleSubmit}> <KeyboundForm className="flex h-full flex-col" onSubmit={handleSubmit}>
<ProgressTabs <ProgressTabs
value={tab} value={tab}
onValueChange={(tab) => handleTabChange(tab as Tab)} onValueChange={(tab) => handleTabChange(tab as Tab)}
@@ -875,7 +876,7 @@ export const CreatePromotionForm = () => {
)} )}
</div> </div>
</RouteFocusModal.Footer> </RouteFocusModal.Footer>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -8,6 +8,7 @@ import * as zod from "zod"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { DeprecatedPercentageInput } from "../../../../../components/inputs/percentage-input" import { DeprecatedPercentageInput } from "../../../../../components/inputs/percentage-input"
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdatePromotion } from "../../../../../hooks/api/promotions" import { useUpdatePromotion } from "../../../../../hooks/api/promotions"
import { getCurrencySymbol } from "../../../../../lib/data/currencies" import { getCurrencySymbol } from "../../../../../lib/data/currencies"
@@ -70,7 +71,7 @@ export const EditPromotionDetailsForm = ({
return ( return (
<RouteDrawer.Form form={form}> <RouteDrawer.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex flex-1 flex-col overflow-hidden" className="flex flex-1 flex-col overflow-hidden"
> >
@@ -277,7 +278,7 @@ export const EditPromotionDetailsForm = ({
</Button> </Button>
</div> </div>
</RouteDrawer.Footer> </RouteDrawer.Footer>
</form> </KeyboundForm>
</RouteDrawer.Form> </RouteDrawer.Form>
) )
} }
@@ -16,6 +16,7 @@ import {
useRouteModal, useRouteModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table" import { DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdateRegion } from "../../../../../hooks/api/regions" import { useUpdateRegion } from "../../../../../hooks/api/regions"
import { useDataTable } from "../../../../../hooks/use-data-table" import { useDataTable } from "../../../../../hooks/use-data-table"
import { countries as staticCountries } from "../../../../../lib/data/countries" import { countries as staticCountries } from "../../../../../lib/data/countries"
@@ -124,7 +125,7 @@ export const AddCountriesForm = ({ region }: AddCountriesFormProps) => {
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
> >
@@ -154,7 +155,7 @@ export const AddCountriesForm = ({ region }: AddCountriesFormProps) => {
prefix={PREFIX} prefix={PREFIX}
/> />
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
@@ -21,12 +21,14 @@ import { PaymentProviderDTO, RegionCountryDTO } from "@medusajs/types"
import { Form } from "../../../../../components/common/form" import { Form } from "../../../../../components/common/form"
import { Combobox } from "../../../../../components/inputs/combobox" import { Combobox } from "../../../../../components/inputs/combobox"
import { SplitView } from "../../../../../components/layout/split-view"
import { import {
RouteFocusModal, RouteFocusModal,
StackedFocusModal,
useRouteModal, useRouteModal,
useStackedModal,
} from "../../../../../components/modals" } from "../../../../../components/modals"
import { DataTable } from "../../../../../components/table/data-table" import { DataTable } from "../../../../../components/table/data-table"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCreateRegion } from "../../../../../hooks/api/regions" import { useCreateRegion } from "../../../../../hooks/api/regions"
import { useDataTable } from "../../../../../hooks/use-data-table" import { useDataTable } from "../../../../../hooks/use-data-table"
import { countries as staticCountries } from "../../../../../lib/data/countries" import { countries as staticCountries } from "../../../../../lib/data/countries"
@@ -53,11 +55,13 @@ const CreateRegionSchema = zod.object({
const PREFIX = "cr" const PREFIX = "cr"
const PAGE_SIZE = 50 const PAGE_SIZE = 50
const STACKED_MODAL_ID = "countries-modal"
export const CreateRegionForm = ({ export const CreateRegionForm = ({
currencies, currencies,
paymentProviders, paymentProviders,
}: CreateRegionFormProps) => { }: CreateRegionFormProps) => {
const [open, setOpen] = useState(false) const { setIsOpen } = useStackedModal()
const [rowSelection, setRowSelection] = useState<RowSelectionState>({}) const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
const { handleSuccess } = useRouteModal() const { handleSuccess } = useRouteModal()
@@ -155,22 +159,7 @@ export const CreateRegionForm = ({
{ shouldDirty: true, shouldTouch: true } { shouldDirty: true, shouldTouch: true }
) )
setOpen(false) setIsOpen(STACKED_MODAL_ID, false)
}
const onOpenChange = (open: boolean) => {
setOpen(open)
if (!open) {
const ids = selectedCountries.reduce((acc, c) => {
acc[c.code] = true
return acc
}, {} as RowSelectionState)
requestAnimationFrame(() => {
setRowSelection(ids)
})
}
} }
const removeCountry = (code: string) => { const removeCountry = (code: string) => {
@@ -193,7 +182,7 @@ export const CreateRegionForm = ({
return ( return (
<RouteFocusModal.Form form={form}> <RouteFocusModal.Form form={form}>
<form <KeyboundForm
className="flex h-full flex-col overflow-hidden" className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@@ -210,8 +199,6 @@ export const CreateRegionForm = ({
</div> </div>
</RouteFocusModal.Header> </RouteFocusModal.Header>
<RouteFocusModal.Body className="flex overflow-hidden"> <RouteFocusModal.Body className="flex overflow-hidden">
<SplitView open={open} onOpenChange={onOpenChange}>
<SplitView.Content>
<div <div
className={clx( className={clx(
"flex h-full w-full flex-col items-center overflow-y-auto p-16" "flex h-full w-full flex-col items-center overflow-y-auto p-16"
@@ -281,9 +268,7 @@ export const CreateRegionForm = ({
<Form.Item> <Form.Item>
<div> <div>
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<Form.Label> <Form.Label>{t("fields.automaticTaxes")}</Form.Label>
{t("fields.automaticTaxes")}
</Form.Label>
<Form.Control> <Form.Control>
<Switch <Switch
{...field} {...field}
@@ -292,9 +277,7 @@ export const CreateRegionForm = ({
/> />
</Form.Control> </Form.Control>
</div> </div>
<Form.Hint> <Form.Hint>{t("regions.automaticTaxesHint")}</Form.Hint>
{t("regions.automaticTaxesHint")}
</Form.Hint>
<Form.ErrorMessage /> <Form.ErrorMessage />
</div> </div>
</Form.Item> </Form.Item>
@@ -321,9 +304,7 @@ export const CreateRegionForm = ({
/> />
</Form.Control> </Form.Control>
</div> </div>
<Form.Hint> <Form.Hint>{t("regions.taxInclusiveHint")}</Form.Hint>
{t("regions.taxInclusiveHint")}
</Form.Hint>
<Form.ErrorMessage /> <Form.ErrorMessage />
</div> </div>
</Form.Item> </Form.Item>
@@ -360,16 +341,56 @@ export const CreateRegionForm = ({
</Button> </Button>
</div> </div>
)} )}
<StackedFocusModal id={STACKED_MODAL_ID}>
<div className="flex items-center justify-end"> <div className="flex items-center justify-end">
<Button <StackedFocusModal.Trigger asChild>
variant="secondary" <Button variant="secondary" size="small">
size="small"
onClick={() => setOpen(true)}
type="button"
>
{t("regions.addCountries")} {t("regions.addCountries")}
</Button> </Button>
</StackedFocusModal.Trigger>
</div> </div>
<StackedFocusModal.Content>
<div className="flex size-full flex-col overflow-hidden">
<StackedFocusModal.Header>
<StackedFocusModal.Title asChild>
<span className="sr-only">
{t("regions.addCountries")}
</span>
</StackedFocusModal.Title>
</StackedFocusModal.Header>
<StackedFocusModal.Body className="overflow-hidden">
<DataTable
table={table}
columns={columns}
count={count}
pageSize={PAGE_SIZE}
orderBy={["name", "code"]}
pagination
search="autofocus"
layout="fill"
queryObject={raw}
prefix={PREFIX}
/>
</StackedFocusModal.Body>
<StackedFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<StackedFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</StackedFocusModal.Close>
<Button
size="small"
type="button"
onClick={saveCountries}
>
{t("actions.save")}
</Button>
</div>
</StackedFocusModal.Footer>
</div>
</StackedFocusModal.Content>
</StackedFocusModal>
</div> </div>
<div className="bg-ui-border-base h-px w-full" /> <div className="bg-ui-border-base h-px w-full" />
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
@@ -409,34 +430,8 @@ export const CreateRegionForm = ({
</div> </div>
</div> </div>
</div> </div>
</SplitView.Content>
<SplitView.Drawer>
<div className="flex size-full flex-col overflow-hidden">
<DataTable
table={table}
columns={columns}
count={count}
pageSize={PAGE_SIZE}
orderBy={["name", "code"]}
pagination
search="autofocus"
layout="fill"
queryObject={raw}
prefix={PREFIX}
/>
<div className="flex items-center justify-end gap-x-2 border-t p-4">
<SplitView.Close type="button">
{t("actions.cancel")}
</SplitView.Close>
<Button size="small" type="button" onClick={saveCountries}>
{t("actions.save")}
</Button>
</div>
</div>
</SplitView.Drawer>
</SplitView>
</RouteFocusModal.Body> </RouteFocusModal.Body>
</form> </KeyboundForm>
</RouteFocusModal.Form> </RouteFocusModal.Form>
) )
} }
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff. Show more