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
+1138 -1056

No files matched your search

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