From 82a72805cffae7922bc62fc491a112c520f96e15 Mon Sep 17 00:00:00 2001 From: josh Date: Fri, 8 May 2026 15:19:43 -0400 Subject: [PATCH] Allow editing SKU value from the Edit SKU modal Co-Authored-By: Claude Opus 4.6 --- server/src/routes/products.ts | 17 +++++++++++++---- web/src/api.ts | 1 + web/src/components/modals/SkuModals.tsx | 15 ++++++++++++--- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/server/src/routes/products.ts b/server/src/routes/products.ts index 72730d9..2eea52e 100644 --- a/server/src/routes/products.ts +++ b/server/src/routes/products.ts @@ -91,6 +91,7 @@ productsRouter.post("/products", (req, res) => { }); type UpdateBody = Partial<{ + sku: string; type: string; kind: "bulk" | "discrete"; strainId: string | null; @@ -105,13 +106,21 @@ productsRouter.patch("/products/:id", (req, res) => { const existing = db .prepare< [string], - { id: string; type: string; kind: string; strain_id: string; brand_id: string | null } + { id: string; sku: string; type: string; kind: string; strain_id: string; brand_id: string | null } >( - `SELECT id, type, kind, strain_id, brand_id FROM products WHERE id = ?`, + `SELECT id, sku, type, kind, strain_id, brand_id FROM products WHERE id = ?`, ) .get(id); if (!existing) return res.status(404).json({ error: "product not found" }); + const nextSku = typeof body.sku === "string" && body.sku.trim() ? body.sku.trim() : existing.sku; + if (nextSku !== existing.sku) { + const duplicate = db + .prepare<[string, string], { id: string }>("SELECT id FROM products WHERE sku = ? AND id != ?") + .get(nextSku, id); + if (duplicate) return res.status(409).json({ error: "sku already exists" }); + } + const nextType = typeof body.type === "string" && body.type ? body.type : existing.type; const nextKind: "bulk" | "discrete" = body.kind === "bulk" || body.kind === "discrete" @@ -144,8 +153,8 @@ productsRouter.patch("/products/:id", (req, res) => { } db.prepare( - `UPDATE products SET type = ?, kind = ?, strain_id = ?, brand_id = ? WHERE id = ?`, - ).run(nextType, nextKind, nextStrainId, nextBrandId, id); + `UPDATE products SET sku = ?, type = ?, kind = ?, strain_id = ?, brand_id = ? WHERE id = ?`, + ).run(nextSku, nextType, nextKind, nextStrainId, nextBrandId, id); res.json({ ok: true }); }); diff --git a/web/src/api.ts b/web/src/api.ts index f73527b..cc59c0e 100644 --- a/web/src/api.ts +++ b/web/src/api.ts @@ -38,6 +38,7 @@ export const api = { updateProduct: ( id: string, body: Partial<{ + sku: string; type: string; kind: "bulk" | "discrete"; strainId: string | null; diff --git a/web/src/components/modals/SkuModals.tsx b/web/src/components/modals/SkuModals.tsx index 92d9e0b..6535bf9 100644 --- a/web/src/components/modals/SkuModals.tsx +++ b/web/src/components/modals/SkuModals.tsx @@ -169,6 +169,7 @@ export function EditSkuModal({ const qc = useQueryClient(); const strain = data.strains.find((s) => s.id === product.strainId); + const [skuValue, setSkuValue] = useState(product.sku); const [strainName, setStrainName] = useState(strain?.name ?? ""); const [brandId, setBrandId] = useState(product.brandId ?? ""); const [typeId, setTypeId] = useState(product.type); @@ -188,6 +189,7 @@ export function EditSkuModal({ const updateProduct = useMutation({ mutationFn: async () => { await api.updateProduct(product.id, { + sku: skuValue.trim(), type: typeId, kind: selectedType.kind, strainName: strainName.trim(), @@ -221,11 +223,18 @@ export function EditSkuModal({ boxShadow: "var(--shadow-lg)", }} > - +
- + setSkuValue(e.target.value)} + placeholder="e.g. SKU-0042" + /> + + + setStrainName(e.target.value)} placeholder="e.g. Blue Dream" @@ -316,7 +325,7 @@ export function EditSkuModal({ updateProduct.mutate()} > {updateProduct.isPending ? "Saving..." : "Save changes"}