User-supplied asset ids; brand on product; strain is the name
Build and push image / build (push) Successful in 48s
Build and push image / build (push) Successful in 48s
Four UX changes after using the rework for a bit: 1. Asset ids are 6-digit numbers from a roll of physical labels — server no longer generates them. POST /api/inventory requires assetId; the add-inventory form has a digits-only input that auto-focuses on entry. 2. Strain and product name are the same thing. Drop products.name; the strain's name supplies the display. Product creation just asks for "Name (strain)" and matches/creates a strain by that name. 3. Brand moves from inventory_items to products. SKUs are brand-specific, so all instances of a product share the brand. Brand selector lives on the product create/edit form, not the per-instance form. 4. Scanning an unknown SKU on the add-inventory step now opens the create-product subform with the SKU prefilled — one less click. Migration: detect prior shape (products.name column present) and rename products/inventory_items/audits to *_v1 archives, recreate empty. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+18
-14
@@ -11,25 +11,26 @@ export interface Audit {
|
||||
}
|
||||
|
||||
// Product = catalog entry. Identified by SKU. One row per "kind of thing
|
||||
// you can scan" — strain + form factor (Flower/bulk, Pre-roll/discrete, …).
|
||||
// you can scan" — strain + brand + form factor (Flower/bulk, Pre-roll/discrete).
|
||||
// Display name comes from the linked strain (strainId is required).
|
||||
export interface Product {
|
||||
id: string;
|
||||
sku: string;
|
||||
strainId: string | null;
|
||||
name: string;
|
||||
strainId: string;
|
||||
brandId: string | null;
|
||||
type: string;
|
||||
kind: ProductKind;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
// InventoryItem = a physical jar/pack you bought. Has its own asset id
|
||||
// (label-printed). Carries everything that varies per purchase: brand, shop,
|
||||
// bin, price, cannabinoids, weight/count, lifecycle, audits.
|
||||
// InventoryItem = a physical jar/pack you bought. Has a 6-digit asset id
|
||||
// (printed on a roll of labels the user owns). Carries per-batch values:
|
||||
// shop, bin, price, cannabinoids, weight/count, lifecycle, audits. Brand
|
||||
// lives on the product, not here.
|
||||
export interface InventoryItem {
|
||||
id: string;
|
||||
assetId: string;
|
||||
productId: string;
|
||||
brandId: string | null;
|
||||
shopId: string | null;
|
||||
binId: string | null;
|
||||
price: number;
|
||||
@@ -52,15 +53,15 @@ export interface InventoryItem {
|
||||
|
||||
// Item = InventoryItem with its product's catalog fields denormalized in.
|
||||
// Built once from bootstrap (`enrichItems`) so views can access `name`,
|
||||
// `sku`, `type`, `kind` without a per-row lookup. This is the shape the
|
||||
// UI and helpers operate on.
|
||||
// `sku`, `type`, `kind`, `brandId` without a per-row lookup. The display
|
||||
// `name` is the strain's name. This is the shape the UI and helpers operate on.
|
||||
export interface Item extends InventoryItem {
|
||||
name: string;
|
||||
sku: string;
|
||||
type: string;
|
||||
kind: ProductKind;
|
||||
strainId: string | null;
|
||||
strainName: string | null;
|
||||
brandId: string | null;
|
||||
strainId: string;
|
||||
}
|
||||
|
||||
export interface Strain {
|
||||
@@ -118,6 +119,9 @@ export const TYPES: TypeConfig[] = [
|
||||
{ id: "Vaporizer", kind: "discrete", auditMode: "presence", cadenceDays: 30, unit: "ct", weighable: false },
|
||||
];
|
||||
|
||||
// User-supplied 6-digit asset ids are printed on a roll of physical tags.
|
||||
export const ASSET_ID_RE = /^\d{6}$/;
|
||||
|
||||
// Local-time YYYY-MM-DD captured once at module load. Used as the default
|
||||
// value for date inputs and as the "today" anchor for days-since math.
|
||||
export const TODAY_STR = (() => {
|
||||
@@ -138,15 +142,15 @@ export function enrichItems(data: Bootstrap): Item[] {
|
||||
for (const inv of data.inventoryItems) {
|
||||
const product = productMap.get(inv.productId);
|
||||
if (!product) continue;
|
||||
const strain = product.strainId ? strainMap.get(product.strainId) ?? null : null;
|
||||
const strain = strainMap.get(product.strainId);
|
||||
out.push({
|
||||
...inv,
|
||||
name: product.name,
|
||||
name: strain?.name ?? "(unknown strain)",
|
||||
sku: product.sku,
|
||||
type: product.type,
|
||||
kind: product.kind,
|
||||
brandId: product.brandId,
|
||||
strainId: product.strainId,
|
||||
strainName: strain?.name ?? null,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
|
||||
Reference in New Issue
Block a user