feat(dashboard): finalise inventory kit creation (#7692)
* feat: finalize inventory kit creation * fix: placeholder
This commit is contained in:
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",
|
||||||
|
|||||||
+3
-1
@@ -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) => {
|
||||||
|
|||||||
+2
-2
@@ -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,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
+47
-15
@@ -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_")) {
|
||||||
|
|||||||
Reference in new issue
Block a user