Allow editing SKU value from the Edit SKU modal
Build and push image / build (push) Successful in 54s
Build and push image / build (push) Successful in 54s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -91,6 +91,7 @@ productsRouter.post("/products", (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
type UpdateBody = Partial<{
|
type UpdateBody = Partial<{
|
||||||
|
sku: string;
|
||||||
type: string;
|
type: string;
|
||||||
kind: "bulk" | "discrete";
|
kind: "bulk" | "discrete";
|
||||||
strainId: string | null;
|
strainId: string | null;
|
||||||
@@ -105,13 +106,21 @@ productsRouter.patch("/products/:id", (req, res) => {
|
|||||||
const existing = db
|
const existing = db
|
||||||
.prepare<
|
.prepare<
|
||||||
[string],
|
[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);
|
.get(id);
|
||||||
if (!existing) return res.status(404).json({ error: "product not found" });
|
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 nextType = typeof body.type === "string" && body.type ? body.type : existing.type;
|
||||||
const nextKind: "bulk" | "discrete" =
|
const nextKind: "bulk" | "discrete" =
|
||||||
body.kind === "bulk" || body.kind === "discrete"
|
body.kind === "bulk" || body.kind === "discrete"
|
||||||
@@ -144,8 +153,8 @@ productsRouter.patch("/products/:id", (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
db.prepare(
|
db.prepare(
|
||||||
`UPDATE products SET type = ?, kind = ?, strain_id = ?, brand_id = ? WHERE id = ?`,
|
`UPDATE products SET sku = ?, type = ?, kind = ?, strain_id = ?, brand_id = ? WHERE id = ?`,
|
||||||
).run(nextType, nextKind, nextStrainId, nextBrandId, id);
|
).run(nextSku, nextType, nextKind, nextStrainId, nextBrandId, id);
|
||||||
|
|
||||||
res.json({ ok: true });
|
res.json({ ok: true });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export const api = {
|
|||||||
updateProduct: (
|
updateProduct: (
|
||||||
id: string,
|
id: string,
|
||||||
body: Partial<{
|
body: Partial<{
|
||||||
|
sku: string;
|
||||||
type: string;
|
type: string;
|
||||||
kind: "bulk" | "discrete";
|
kind: "bulk" | "discrete";
|
||||||
strainId: string | null;
|
strainId: string | null;
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ export function EditSkuModal({
|
|||||||
const qc = useQueryClient();
|
const qc = useQueryClient();
|
||||||
const strain = data.strains.find((s) => s.id === product.strainId);
|
const strain = data.strains.find((s) => s.id === product.strainId);
|
||||||
|
|
||||||
|
const [skuValue, setSkuValue] = useState(product.sku);
|
||||||
const [strainName, setStrainName] = useState(strain?.name ?? "");
|
const [strainName, setStrainName] = useState(strain?.name ?? "");
|
||||||
const [brandId, setBrandId] = useState(product.brandId ?? "");
|
const [brandId, setBrandId] = useState(product.brandId ?? "");
|
||||||
const [typeId, setTypeId] = useState(product.type);
|
const [typeId, setTypeId] = useState(product.type);
|
||||||
@@ -188,6 +189,7 @@ export function EditSkuModal({
|
|||||||
const updateProduct = useMutation({
|
const updateProduct = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
await api.updateProduct(product.id, {
|
await api.updateProduct(product.id, {
|
||||||
|
sku: skuValue.trim(),
|
||||||
type: typeId,
|
type: typeId,
|
||||||
kind: selectedType.kind,
|
kind: selectedType.kind,
|
||||||
strainName: strainName.trim(),
|
strainName: strainName.trim(),
|
||||||
@@ -221,11 +223,18 @@ export function EditSkuModal({
|
|||||||
boxShadow: "var(--shadow-lg)",
|
boxShadow: "var(--shadow-lg)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ModalHeader title="Edit SKU" eyebrow={`SKU · ${product.sku}`} onClose={onClose} />
|
<ModalHeader title="Edit SKU" eyebrow="SKU" onClose={onClose} />
|
||||||
<div style={{ padding: 32, display: "grid", gap: 16 }}>
|
<div style={{ padding: 32, display: "grid", gap: 16 }}>
|
||||||
<Field label="Strain name">
|
<Field label="SKU code">
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
autoFocus
|
||||||
|
value={skuValue}
|
||||||
|
onChange={(e) => setSkuValue(e.target.value)}
|
||||||
|
placeholder="e.g. SKU-0042"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<Field label="Strain name">
|
||||||
|
<Input
|
||||||
value={strainName}
|
value={strainName}
|
||||||
onChange={(e) => setStrainName(e.target.value)}
|
onChange={(e) => setStrainName(e.target.value)}
|
||||||
placeholder="e.g. Blue Dream"
|
placeholder="e.g. Blue Dream"
|
||||||
@@ -316,7 +325,7 @@ export function EditSkuModal({
|
|||||||
<Btn
|
<Btn
|
||||||
variant="primary"
|
variant="primary"
|
||||||
icon="check"
|
icon="check"
|
||||||
disabled={!strainName.trim() || updateProduct.isPending}
|
disabled={!skuValue.trim() || !strainName.trim() || updateProduct.isPending}
|
||||||
onClick={() => updateProduct.mutate()}
|
onClick={() => updateProduct.mutate()}
|
||||||
>
|
>
|
||||||
{updateProduct.isPending ? "Saving..." : "Save changes"}
|
{updateProduct.isPending ? "Saving..." : "Save changes"}
|
||||||
|
|||||||
Reference in New Issue
Block a user