feat(dashboard): Add forms for edit and create product options (#6907)
This commit is contained in:
committed by
GitHub
parent
fd3fc1384b
commit
880bbbd4af
@@ -154,7 +154,6 @@
|
||||
"editAttributes": "Edit Attributes",
|
||||
"organization": "Organize",
|
||||
"editOrganization": "Edit Organization",
|
||||
"options": "Options",
|
||||
"editOptions": "Edit Options",
|
||||
"media": {
|
||||
"label": "Media",
|
||||
@@ -250,6 +249,15 @@
|
||||
"allowBackordersLabel": "Allow backorders",
|
||||
"allowBackordersHint": "When enabled the variant can be sold even if the inventory level is below zero."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"header": "Options",
|
||||
"edit": {
|
||||
"header": "Edit Option"
|
||||
},
|
||||
"create": {
|
||||
"header": "Create Option"
|
||||
}
|
||||
}
|
||||
},
|
||||
"collections": {
|
||||
|
||||
@@ -231,8 +231,14 @@ export const v1Routes: RouteObject[] = [
|
||||
import("../../routes/products/product-attributes"),
|
||||
},
|
||||
{
|
||||
path: "options",
|
||||
lazy: () => import("../../routes/products/product-options"),
|
||||
path: "options/create",
|
||||
lazy: () =>
|
||||
import("../../routes/products/product-create-option"),
|
||||
},
|
||||
{
|
||||
path: "options/:option_id/edit",
|
||||
lazy: () =>
|
||||
import("../../routes/products/product-edit-option"),
|
||||
},
|
||||
{
|
||||
path: "media",
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { Product } from "@medusajs/medusa"
|
||||
import { Button, Input } from "@medusajs/ui"
|
||||
import { useAdminCreateProductOption } from "medusa-react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { z } from "zod"
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import {
|
||||
RouteDrawer,
|
||||
useRouteModal,
|
||||
} from "../../../../../components/route-modal"
|
||||
|
||||
type EditProductOptionsFormProps = {
|
||||
product: Product
|
||||
}
|
||||
|
||||
const CreateProductOptionSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
})
|
||||
|
||||
export const CreateProductOptionForm = ({
|
||||
product,
|
||||
}: EditProductOptionsFormProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { handleSuccess } = useRouteModal()
|
||||
|
||||
const form = useForm<z.infer<typeof CreateProductOptionSchema>>({
|
||||
defaultValues: {
|
||||
title: "",
|
||||
},
|
||||
resolver: zodResolver(CreateProductOptionSchema),
|
||||
})
|
||||
|
||||
const { mutateAsync, isLoading } = useAdminCreateProductOption(product.id)
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (values) => {
|
||||
mutateAsync(values, {
|
||||
onSuccess: () => {
|
||||
handleSuccess()
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
<RouteDrawer.Form form={form}>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="flex flex-1 flex-col overflow-hidden"
|
||||
>
|
||||
<RouteDrawer.Body className="flex flex-1 flex-col gap-y-8 overflow-auto">
|
||||
<Form.Field
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Form.Item>
|
||||
<Form.Label>{t("fields.title")}</Form.Label>
|
||||
<Form.Control>
|
||||
<Input {...field} />
|
||||
</Form.Control>
|
||||
<Form.ErrorMessage />
|
||||
</Form.Item>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</RouteDrawer.Body>
|
||||
<RouteDrawer.Footer>
|
||||
<div className="flex items-center justify-end gap-x-2">
|
||||
<RouteDrawer.Close asChild>
|
||||
<Button variant="secondary" size="small">
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
</RouteDrawer.Close>
|
||||
<Button type="submit" size="small" isLoading={isLoading}>
|
||||
{t("actions.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</RouteDrawer.Footer>
|
||||
</form>
|
||||
</RouteDrawer.Form>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./create-product-option-form"
|
||||
@@ -0,0 +1 @@
|
||||
export { ProductCreateOption as Component } from "./product-create-option"
|
||||
@@ -3,9 +3,9 @@ import { useAdminProduct } from "medusa-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useParams } from "react-router-dom"
|
||||
import { RouteDrawer } from "../../../components/route-modal"
|
||||
import { EditProductOptionsForm } from "./components/edit-product-options-form"
|
||||
import { CreateProductOptionForm } from "./components/create-product-option-form"
|
||||
|
||||
export const ProductOptions = () => {
|
||||
export const ProductCreateOption = () => {
|
||||
const { id } = useParams()
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -18,9 +18,9 @@ export const ProductOptions = () => {
|
||||
return (
|
||||
<RouteDrawer>
|
||||
<RouteDrawer.Header>
|
||||
<Heading>{t("products.editOptions")}</Heading>
|
||||
<Heading>{t("products.options.create.header")}</Heading>
|
||||
</RouteDrawer.Header>
|
||||
{!isLoading && product && <EditProductOptionsForm product={product} />}
|
||||
{!isLoading && product && <CreateProductOptionForm product={product} />}
|
||||
</RouteDrawer>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PencilSquare } from "@medusajs/icons"
|
||||
import { PencilSquare, Plus } from "@medusajs/icons"
|
||||
import { Product, ProductOption } from "@medusajs/medusa"
|
||||
import { Badge, Container, Heading, Text } from "@medusajs/ui"
|
||||
import { useTranslation } from "react-i18next"
|
||||
@@ -16,15 +16,15 @@ export const ProductOptionSection = ({
|
||||
return (
|
||||
<Container className="divide-y p-0">
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
<Heading level="h2">{t("products.options")}</Heading>
|
||||
<Heading level="h2">{t("products.options.header")}</Heading>
|
||||
<ActionMenu
|
||||
groups={[
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
label: t("actions.edit"),
|
||||
to: "options",
|
||||
icon: <PencilSquare />,
|
||||
label: t("actions.create"),
|
||||
to: "options/create",
|
||||
icon: <Plus />,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -35,7 +35,7 @@ export const ProductOptionSection = ({
|
||||
return (
|
||||
<div
|
||||
key={option.id}
|
||||
className="text-ui-fg-subtle grid grid-cols-2 items-start px-6 py-4"
|
||||
className="text-ui-fg-subtle grid grid-cols-[1fr_1fr_28px] items-start gap-4 px-6 py-4"
|
||||
>
|
||||
<Text size="small" leading="compact" weight="plus">
|
||||
{option.title}
|
||||
@@ -53,6 +53,19 @@ export const ProductOptionSection = ({
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<ActionMenu
|
||||
groups={[
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
label: t("actions.edit"),
|
||||
to: `options/${option.id}/edit`,
|
||||
icon: <PencilSquare />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { ProductOption } from "@medusajs/medusa"
|
||||
import { Button, Input } from "@medusajs/ui"
|
||||
import { useAdminUpdateProductOption } from "medusa-react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { z } from "zod"
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import {
|
||||
RouteDrawer,
|
||||
useRouteModal,
|
||||
} from "../../../../../components/route-modal"
|
||||
|
||||
type EditProductOptionFormProps = {
|
||||
option: ProductOption
|
||||
}
|
||||
|
||||
const CreateProductOptionSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
})
|
||||
|
||||
export const CreateProductOptionForm = ({
|
||||
option,
|
||||
}: EditProductOptionFormProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { handleSuccess } = useRouteModal()
|
||||
|
||||
const form = useForm<z.infer<typeof CreateProductOptionSchema>>({
|
||||
defaultValues: {
|
||||
title: option.title,
|
||||
},
|
||||
resolver: zodResolver(CreateProductOptionSchema),
|
||||
})
|
||||
|
||||
const { mutateAsync, isLoading } = useAdminUpdateProductOption(
|
||||
option.product_id
|
||||
)
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (values) => {
|
||||
mutateAsync(
|
||||
{
|
||||
option_id: option.id,
|
||||
...values,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
handleSuccess()
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<RouteDrawer.Form form={form}>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="flex flex-1 flex-col overflow-hidden"
|
||||
>
|
||||
<RouteDrawer.Body className="flex flex-1 flex-col gap-y-8 overflow-auto">
|
||||
<Form.Field
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Form.Item>
|
||||
<Form.Label>{t("fields.title")}</Form.Label>
|
||||
<Form.Control>
|
||||
<Input {...field} />
|
||||
</Form.Control>
|
||||
<Form.ErrorMessage />
|
||||
</Form.Item>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</RouteDrawer.Body>
|
||||
<RouteDrawer.Footer>
|
||||
<div className="flex items-center justify-end gap-x-2">
|
||||
<RouteDrawer.Close asChild>
|
||||
<Button variant="secondary" size="small">
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
</RouteDrawer.Close>
|
||||
<Button type="submit" size="small" isLoading={isLoading}>
|
||||
{t("actions.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</RouteDrawer.Footer>
|
||||
</form>
|
||||
</RouteDrawer.Form>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./edit-product-option-form"
|
||||
@@ -0,0 +1 @@
|
||||
export { ProductEditOption as Component } from "./product-edit-option"
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Heading } from "@medusajs/ui"
|
||||
import { useAdminProduct } from "medusa-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { json, useParams } from "react-router-dom"
|
||||
import { RouteDrawer } from "../../../components/route-modal"
|
||||
import { CreateProductOptionForm } from "./components/edit-product-option-form"
|
||||
|
||||
export const ProductEditOption = () => {
|
||||
const { id, option_id } = useParams()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { product, isLoading, isError, error } = useAdminProduct(id!)
|
||||
|
||||
const option = product?.options.find((o) => o.id === option_id)
|
||||
|
||||
const ready = !isLoading && option
|
||||
|
||||
if (!isLoading && !option) {
|
||||
throw json({ message: `An option with ID ${option_id} was not found` }, 404)
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
throw error
|
||||
}
|
||||
|
||||
return (
|
||||
<RouteDrawer>
|
||||
<RouteDrawer.Header>
|
||||
<Heading>{t("products.options.edit.header")}</Heading>
|
||||
</RouteDrawer.Header>
|
||||
{ready && <CreateProductOptionForm option={option} />}
|
||||
</RouteDrawer>
|
||||
)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { Product } from "@medusajs/medusa"
|
||||
import { Button } from "@medusajs/ui"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import * as zod from "zod"
|
||||
import { RouteDrawer } from "../../../../../components/route-modal"
|
||||
|
||||
type EditProductOptionsFormProps = {
|
||||
product: Product
|
||||
}
|
||||
|
||||
const EditProductOptionsSchema = zod.object({})
|
||||
|
||||
export const EditProductOptionsForm = (props: EditProductOptionsFormProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const form = useForm<zod.infer<typeof EditProductOptionsSchema>>({
|
||||
resolver: zodResolver(EditProductOptionsSchema),
|
||||
})
|
||||
|
||||
return (
|
||||
<RouteDrawer.Form form={form}>
|
||||
<form className="flex flex-1 flex-col overflow-hidden">
|
||||
<RouteDrawer.Body className="flex flex-1 flex-col gap-y-8 overflow-auto"></RouteDrawer.Body>
|
||||
<RouteDrawer.Footer>
|
||||
<div className="flex items-center justify-end gap-x-2">
|
||||
<RouteDrawer.Close asChild>
|
||||
<Button variant="secondary" size="small">
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
</RouteDrawer.Close>
|
||||
<Button type="submit" size="small">
|
||||
{t("actions.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</RouteDrawer.Footer>
|
||||
</form>
|
||||
</RouteDrawer.Form>
|
||||
)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./edit-product-options-form"
|
||||
@@ -1 +0,0 @@
|
||||
export { ProductOptions as Component } from "./product-options"
|
||||
Reference in New Issue
Block a user