feat(dashboard): finalise inventory kit creation (#7692)

* feat: finalize inventory kit creation

* fix: placeholder
This commit is contained in:
Frane Polić authored and GitHub committed 2024-06-12 16:35:54 +02:00
1 parent 2f76fbc6ed
commit 3693f61dc3
7 files changed
+79 -24

No files matched your search

@@ -38,12 +38,12 @@ export const useComboboxData = <
}) => { }) => {
const { searchValue, onSearchValueChange, query } = useDebouncedSearch() const { searchValue, onSearchValueChange, query } = useDebouncedSearch()
const queryIntialDataBy = defaultValueKey || "id" const queryInitialDataBy = defaultValueKey || "id"
const { data: initialData } = useQuery({ const { data: initialData } = useQuery({
queryKey: queryKey, queryKey: queryKey,
queryFn: async () => { queryFn: async () => {
return queryFn({ return queryFn({
[queryIntialDataBy]: defaultValue, [queryInitialDataBy]: defaultValue,
limit: Array.isArray(defaultValue) ? defaultValue.length : 1, limit: Array.isArray(defaultValue) ? defaultValue.length : 1,
} as TParams) } as TParams)
}, },
@@ -178,7 +178,9 @@
}, },
"inventory": { "inventory": {
"heading": "Inventory kits", "heading": "Inventory kits",
"label": "Inventory kit" "label": "Inventory kit",
"itemPlaceholder": "Select inventory item",
"quantityPlaceholder": "How many of these are needed for the kit?"
}, },
"variants": { "variants": {
"header": "Variants", "header": "Variants",
@@ -1659,6 +1661,7 @@
"discountable": "Discountable", "discountable": "Discountable",
"handle": "Handle", "handle": "Handle",
"subtitle": "Subtitle", "subtitle": "Subtitle",
"item": "Item",
"limit": "Limit", "limit": "Limit",
"tags": "Tags", "tags": "Tags",
"type": "Type", "type": "Type",
@@ -60,7 +60,9 @@ export const CreateProductVariantForm = ({
resolver: zodResolver(CreateProductVariantSchema), resolver: zodResolver(CreateProductVariantSchema),
}) })
const { mutateAsync, isLoading } = useCreateProductVariant(product.id) const { mutateAsync, isPending: isLoading } = useCreateProductVariant(
product.id
)
const handleSubmit = form.handleSubmit(async (data) => { const handleSubmit = form.handleSubmit(async (data) => {
const parseNumber = (value?: string | number) => { const parseNumber = (value?: string | number) => {
@@ -141,7 +141,7 @@ export const ProductCreateVariantsSection = ({
should_create: hasUserSelectedVariants ? false : true, should_create: hasUserSelectedVariants ? false : true,
variant_rank: newVariants.length, variant_rank: newVariants.length,
// NOTE - prepare inventory array here for now so we prevent rendering issue if we append the items later // NOTE - prepare inventory array here for now so we prevent rendering issue if we append the items later
inventory: [{ title: "", quantity: 0 }], inventory: [{ inventory_item_id: "", required_quantity: "" }],
}) })
}) })
@@ -276,7 +276,7 @@ export const ProductCreateVariantsSection = ({
options: { options: {
"Default option": "Default option value", "Default option": "Default option value",
}, },
inventory: [{ title: "", quantity: 0 }], inventory: [{ inventory_item_id: "", required_quantity: "" }],
is_default: true, is_default: true,
}, },
]) ])
@@ -6,6 +6,9 @@ import { useTranslation } from "react-i18next"
import { ProductCreateSchemaType } from "../../../../types" import { ProductCreateSchemaType } from "../../../../types"
import { Form } from "../../../../../../../components/common/form" import { Form } from "../../../../../../../components/common/form"
import { Combobox } from "../../../../../../../components/inputs/combobox"
import { useComboboxData } from "../../../../../../../hooks/use-combobox-data"
import { sdk } from "../../../../../../../lib/client"
type VariantSectionProps = { type VariantSectionProps = {
form: UseFormReturn<ProductCreateSchemaType> form: UseFormReturn<ProductCreateSchemaType>
@@ -21,6 +24,16 @@ function VariantSection({ form, variant, index }: VariantSectionProps) {
name: `variants.${index}.inventory`, name: `variants.${index}.inventory`,
}) })
const items = useComboboxData({
queryKey: ["inventory_items"],
queryFn: (params) => sdk.admin.inventoryItem.list(params),
getOptions: (data) =>
data.inventory_items.map((item) => ({
label: item.title,
value: item.id,
})),
})
return ( return (
<div className="grid gap-y-4"> <div className="grid gap-y-4">
<div className="flex items-start justify-between gap-x-4"> <div className="flex items-start justify-between gap-x-4">
@@ -34,8 +47,8 @@ function VariantSection({ form, variant, index }: VariantSectionProps) {
type="button" type="button"
onClick={() => { onClick={() => {
inventory.append({ inventory.append({
title: "", inventory_item_id: "",
quantity: 0, required_quantity: "",
}) })
}} }}
> >
@@ -53,30 +66,49 @@ function VariantSection({ form, variant, index }: VariantSectionProps) {
size="xsmall" size="xsmall"
weight="plus" weight="plus"
className="text-ui-fg-subtle" className="text-ui-fg-subtle"
htmlFor={`variants.${index}.inventory.${inventoryIndex}.title`} htmlFor={`variants.${index}.inventory.${inventoryIndex}.inventory_item_id`}
> >
{t("fields.name")} {t("fields.item")}
</Label> </Label>
</div> </div>
<Input
className="bg-ui-bg-field-component hover:bg-ui-bg-field-component-hover" <Form.Field
{...form.register( control={form.control}
`variants.${index}.inventory.${inventoryIndex}.title` as const name={`variants.${index}.inventory.${inventoryIndex}.inventory_item_id`}
)} render={({ field }) => {
return (
<Form.Item>
<Form.Control>
<Combobox
{...field}
options={items.options}
searchValue={items.searchValue}
onSearchValueChange={items.onSearchValueChange}
fetchNextPage={items.fetchNextPage}
className="bg-ui-bg-field-component hover:bg-ui-bg-field-component-hover"
placeholder={t(
"products.create.inventory.itemPlaceholder"
)}
/>
</Form.Control>
</Form.Item>
)
}}
/> />
<div className="flex items-center px-2 py-1.5"> <div className="flex items-center px-2 py-1.5">
<Label <Label
size="xsmall" size="xsmall"
weight="plus" weight="plus"
className="text-ui-fg-subtle" className="text-ui-fg-subtle"
htmlFor={`variants.${index}.inventory.${inventoryIndex}.title`} htmlFor={`variants.${index}.inventory.${inventoryIndex}.required_quantity`}
> >
{t("fields.quantity")} {t("fields.quantity")}
</Label> </Label>
</div> </div>
<Form.Field <Form.Field
control={form.control} control={form.control}
name={`variants.${index}.inventory.${inventoryIndex}.quantity`} name={`variants.${index}.inventory.${inventoryIndex}.required_quantity`}
render={({ field: { onChange, value, ...field } }) => { render={({ field: { onChange, value, ...field } }) => {
return ( return (
<Form.Item> <Form.Item>
@@ -84,9 +116,6 @@ function VariantSection({ form, variant, index }: VariantSectionProps) {
<Input <Input
type="number" type="number"
className="bg-ui-bg-field-component" className="bg-ui-bg-field-component"
placeholder={t(
"inventory.reservation.quantityPlaceholder"
)}
min={0} min={0}
value={value} value={value}
onChange={(e) => { onChange={(e) => {
@@ -95,10 +124,13 @@ function VariantSection({ form, variant, index }: VariantSectionProps) {
if (value === "") { if (value === "") {
onChange(null) onChange(null)
} else { } else {
onChange(parseFloat(value)) onChange(Number(value))
} }
}} }}
{...field} {...field}
placeholder={t(
"products.create.inventory.quantityPlaceholder"
)}
/> />
</Form.Control> </Form.Control>
<Form.ErrorMessage /> <Form.ErrorMessage />
@@ -71,7 +71,12 @@ export const ProductCreateSchema = z
variant_rank: z.number(), variant_rank: z.number(),
prices: z.record(z.string(), z.string().optional()).optional(), prices: z.record(z.string(), z.string().optional()).optional(),
inventory: z inventory: z
.array(z.object({ title: z.string(), quantity: z.number() })) .array(
z.object({
inventory_item_id: z.string(),
required_quantity: optionalInt,
})
)
.optional(), .optional(),
}) })
) )
@@ -112,7 +117,7 @@ export const PRODUCT_CREATE_FORM_DEFAULTS: Partial<
options: { options: {
"Default option": "Default option value", "Default option": "Default option value",
}, },
inventory: [{ title: "", quantity: 0 }], inventory: [{ inventory_item_id: "", required_quantity: "" }],
is_default: true, is_default: true,
}, },
]), ]),
@@ -54,7 +54,20 @@ export const normalizeVariants = (
sku: variant.sku || undefined, sku: variant.sku || undefined,
manage_inventory: variant.manage_inventory || undefined, manage_inventory: variant.manage_inventory || undefined,
allow_backorder: variant.allow_backorder || undefined, allow_backorder: variant.allow_backorder || undefined,
// TODO: inventory - should be added to the workflow inventory_items: variant
.inventory!.map((i) => {
const quantity = castNumber(i.required_quantity)
if (!i.inventory_item_id || !quantity) {
return false
}
return {
...i,
required_quantity: quantity,
}
})
.filter(Boolean),
prices: Object.entries(variant.prices || {}) prices: Object.entries(variant.prices || {})
.map(([key, value]: any) => { .map(([key, value]: any) => {
if (key.startsWith("reg_")) { if (key.startsWith("reg_")) {