From a9bbdc6067d122055e2ff614fc1aa6a15785a869 Mon Sep 17 00:00:00 2001 From: priymavani Date: Wed, 15 Jul 2026 17:58:33 +0530 Subject: [PATCH] fix(inventory): debounce search input to prevent network backlog --- app/routes/inventory-management.tsx | 96 ++++++++++++++++++----------- 1 file changed, 61 insertions(+), 35 deletions(-) diff --git a/app/routes/inventory-management.tsx b/app/routes/inventory-management.tsx index 2d964ca..702bca3 100644 --- a/app/routes/inventory-management.tsx +++ b/app/routes/inventory-management.tsx @@ -31,16 +31,20 @@ function parseSkippedRows(value: FormDataEntryValue | null) { return [] as number[]; } - return parsed - .map((row) => Number(row)) - .filter((rowNumber) => Number.isInteger(rowNumber) && rowNumber > 0); + return parsed.map((row) => Number(row)).filter((rowNumber) => Number.isInteger(rowNumber) && rowNumber > 0); } catch { return [] as number[]; } } function normalizeImportedCondition(value: unknown): ItemCondition { - const normalized = typeof value === "string" ? value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "") : ""; + const normalized = + typeof value === "string" + ? value + .trim() + .toLowerCase() + .replace(/[^a-z0-9]+/g, "") + : ""; switch (normalized) { case "new": @@ -153,24 +157,26 @@ export async function loader({ request }: Route.LoaderArgs) { }; const countPromise = prisma.inventoryItem.count({ where: whereClause }); - const itemsPromise = prisma.inventoryItem.findMany({ - where: whereClause, - orderBy: { createdAt: "desc" }, - skip: (page - 1) * pageSize, - take: pageSize, - include: { - priceHistory: { - orderBy: { fetchedAt: "desc" }, - take: 1, + const itemsPromise = prisma.inventoryItem + .findMany({ + where: whereClause, + orderBy: { createdAt: "desc" }, + skip: (page - 1) * pageSize, + take: pageSize, + include: { + priceHistory: { + orderBy: { fetchedAt: "desc" }, + take: 1, + }, }, - }, - }).then((items) => - items.map((item) => ({ - ...item, - purchasePrice: Number(item.purchasePrice), - marketValue: item.priceHistory[0]?.askPrice ? Number(item.priceHistory[0].askPrice) : null, - })) - ); + }) + .then((items) => + items.map((item) => ({ + ...item, + purchasePrice: Number(item.purchasePrice), + marketValue: item.priceHistory[0]?.askPrice ? Number(item.priceHistory[0].askPrice) : null, + })), + ); const deferredData = Promise.all([countPromise, itemsPromise]).then(([totalItems, formattedItems]) => ({ items: formattedItems, @@ -359,17 +365,30 @@ export default function InventoryManagementPage() { const [searchParams, setSearchParams] = useSearchParams(); const searchQuery = searchParams.get("q") || ""; + const [localSearch, setLocalSearch] = useState(searchQuery); - const setSearchQuery = (query: string) => { - const nextParams = new URLSearchParams(searchParams); - if (query) { - nextParams.set("q", query); - } else { - nextParams.delete("q"); - } - nextParams.set("page", "1"); - setSearchParams(nextParams, { replace: true }); - }; + // Sync URL search param changes to local state (e.g. back/forward navigation or initial mount) + useEffect(() => { + setLocalSearch(searchQuery); + }, [searchQuery]); + + // Debounce syncing localSearch state to the URL search parameter + useEffect(() => { + const timer = setTimeout(() => { + if (localSearch !== searchQuery) { + const nextParams = new URLSearchParams(searchParams); + if (localSearch) { + nextParams.set("q", localSearch); + } else { + nextParams.delete("q"); + } + nextParams.set("page", "1"); + setSearchParams(nextParams, { replace: true }); + } + }, 300); + + return () => clearTimeout(timer); + }, [localSearch, searchQuery, searchParams, setSearchParams]); useEffect(() => { if (actionData?.ok) { @@ -406,8 +425,8 @@ export default function InventoryManagementPage() { setShowAddItem(true)} onImport={() => setShowImport(true)} - searchQuery={searchQuery} - onSearch={setSearchQuery} + searchQuery={localSearch} + onSearch={setLocalSearch} /> ( <> {selected.length > 0 && ( - setSelected([])} selectedIds={selected} items={items} /> + setSelected([])} + selectedIds={selected} + items={items} + /> )} {showAddItem && setShowAddItem(false)} />} {editingItem && setEditingItem(null)} />} - {duplicatingItem && setDuplicatingItem(null)} />} + {duplicatingItem && ( + setDuplicatingItem(null)} /> + )} {showImport && setShowImport(false)} />} );