From 341a8bb7eeac18460b0e466bda1f4b148e566495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Wed, 28 May 2025 09:32:01 +0200 Subject: [PATCH] fix(dashboard): combobox initial item cache (#12522) --- .changeset/slow-toes-fetch.md | 5 ++ .../dashboard/src/hooks/use-combobox-data.tsx | 29 +++++++++-- .../manage-variant-inventory-items-form.tsx | 51 ++++++++++++++++--- 3 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 .changeset/slow-toes-fetch.md diff --git a/.changeset/slow-toes-fetch.md b/.changeset/slow-toes-fetch.md new file mode 100644 index 0000000000..161a3e7d9e --- /dev/null +++ b/.changeset/slow-toes-fetch.md @@ -0,0 +1,5 @@ +--- +"@medusajs/dashboard": patch +--- + +fix(dashboard): combobox initial item cache diff --git a/packages/admin/dashboard/src/hooks/use-combobox-data.tsx b/packages/admin/dashboard/src/hooks/use-combobox-data.tsx index 02e839b682..d49547790a 100644 --- a/packages/admin/dashboard/src/hooks/use-combobox-data.tsx +++ b/packages/admin/dashboard/src/hooks/use-combobox-data.tsx @@ -28,6 +28,7 @@ export const useComboboxData = < getOptions, defaultValue, defaultValueKey, + selectedValue, pageSize = 10, enabled = true, }: { @@ -36,6 +37,7 @@ export const useComboboxData = < getOptions: (data: TResponse) => { label: string; value: string }[] defaultValueKey?: keyof TParams defaultValue?: string | string[] + selectedValue?: string pageSize?: number enabled?: boolean }) => { @@ -43,7 +45,7 @@ export const useComboboxData = < const queryInitialDataBy = defaultValueKey || "id" const { data: initialData } = useQuery({ - queryKey: queryKey, + queryKey: [...queryKey, defaultValue].filter(Boolean) as QueryKey, queryFn: async () => { return queryFn({ [queryInitialDataBy]: defaultValue, @@ -53,8 +55,21 @@ export const useComboboxData = < enabled: !!defaultValue && enabled, }) + // always load selected value in case current data dosn't contain the value + const { data: selectedData } = useQuery({ + queryKey: [...queryKey, selectedValue].filter(Boolean) as QueryKey, + queryFn: async () => { + return queryFn({ + id: selectedValue, + limit: 1, + } as TParams) + }, + enabled: !!selectedValue && enabled, + }) + const { data, ...rest } = useInfiniteQuery({ - queryKey: [...queryKey, query], + // prevent infinite query response shape beeing stored under regualr list reponse QKs + queryKey: [...queryKey, "_cbx_", query].filter(Boolean) as QueryKey, queryFn: async ({ pageParam = 0 }) => { return await queryFn({ q: query, @@ -73,7 +88,7 @@ export const useComboboxData = < const options = data?.pages.flatMap((page) => getOptions(page)) ?? [] const defaultOptions = initialData ? getOptions(initialData) : [] - + const selectedOptions = selectedData ? getOptions(selectedData) : [] /** * If there are no options and the query is empty, then the combobox should be disabled, * as there is no data to search for. @@ -90,6 +105,14 @@ export const useComboboxData = < }) } + if (selectedValue && selectedOptions.length) { + selectedOptions.forEach((option) => { + if (!options.find((o) => o.value === option.value)) { + options.unshift(option) + } + }) + } + return { options, searchValue, diff --git a/packages/admin/dashboard/src/routes/product-variants/product-variant-manage-inventory-items/components/manage-variant-inventory-items-form/manage-variant-inventory-items-form.tsx b/packages/admin/dashboard/src/routes/product-variants/product-variant-manage-inventory-items/components/manage-variant-inventory-items-form/manage-variant-inventory-items-form.tsx index b7844f5dfa..3b4da809a1 100644 --- a/packages/admin/dashboard/src/routes/product-variants/product-variant-manage-inventory-items/components/manage-variant-inventory-items-form/manage-variant-inventory-items-form.tsx +++ b/packages/admin/dashboard/src/routes/product-variants/product-variant-manage-inventory-items/components/manage-variant-inventory-items-form/manage-variant-inventory-items-form.tsx @@ -3,7 +3,12 @@ import { XMarkMini } from "@medusajs/icons" import { AdminProductVariant, HttpTypes } from "@medusajs/types" import { Button, Heading, IconButton, Input, Label, toast } from "@medusajs/ui" import i18next from "i18next" -import { useFieldArray, useForm, UseFormReturn } from "react-hook-form" +import { + useFieldArray, + useForm, + UseFormReturn, + useWatch, +} from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" @@ -68,6 +73,10 @@ type VariantInventoryItemRowProps = { inventory_item_id: string required_quantity: number } + isItemOptionDisabled: ( + option: { value: string }, + inventoryIndex: number + ) => boolean onRemove: () => void } @@ -75,14 +84,21 @@ function VariantInventoryItemRow({ form, inventoryIndex, inventoryItem, + isItemOptionDisabled, onRemove, }: VariantInventoryItemRowProps) { const { t } = useTranslation() + const selectedInventoryItemId = useWatch({ + control: form.control, + name: `inventory.${inventoryIndex}.inventory_item_id`, + }) + const items = useComboboxData({ queryKey: ["inventory_items"], defaultValueKey: "id", defaultValue: inventoryItem.inventory_item_id, + selectedValue: selectedInventoryItemId, queryFn: (params) => sdk.admin.inventoryItem.list(params), getOptions: (data) => data.inventory_items.map((item) => ({ @@ -117,7 +133,10 @@ function VariantInventoryItemRow({ ({ + ...o, + disabled: isItemOptionDisabled(o, inventoryIndex), + }))} searchValue={items.searchValue} onSearchValueChange={items.onSearchValueChange} onBlur={() => items.onSearchValueChange("")} @@ -208,6 +227,24 @@ export function ManageVariantInventoryItemsForm({ name: `inventory`, }) + const inventoryFormData = useWatch({ + control: form.control, + name: `inventory`, + }) + + /** + * Will mark an option as disabled if another input already selected that option + */ + const isItemOptionDisabled = ( + option: { value: string }, + inventoryIndex: number + ) => { + return !!inventoryFormData?.some( + (i, index) => + index != inventoryIndex && i.inventory_item_id === option.value + ) + } + const hasKit = inventory.fields.length > 1 const { mutateAsync, isPending } = useProductVariantsInventoryItemsBatch( @@ -272,10 +309,7 @@ export function ManageVariantInventoryItemsForm({ return ( - +
@@ -288,7 +322,7 @@ export function ManageVariantInventoryItemsForm({
- +
{t( @@ -298,7 +332,7 @@ export function ManageVariantInventoryItemsForm({ )} -
+
{variant.title} @@ -330,6 +364,7 @@ export function ManageVariantInventoryItemsForm({ form={form} inventoryIndex={inventoryIndex} inventoryItem={inventoryItem} + isItemOptionDisabled={isItemOptionDisabled} onRemove={() => inventory.remove(inventoryIndex)} /> ))}