← back to index

U6 followup — nameKh + categoryId hydrationLOCAL

Two latent useState("") hydration bugs in product-detail-client.tsx matching the U6 isHidden pattern. nameKh and categoryId were hardcoded empty regardless of the loaded template's saved state — so re-opening a product with a Khmer name or non-default category showed empty fields, and saving without re-entering blanked them in the DB.

Summary

Status
6/6 local · typecheck clean · awaiting Gate 1 approval
Repos
nix-cafe only
Files
3 files modified · ~25 LOC net · no migration · no backend · no schema
Source
Spawned followup chip during U6 (2026-05-26); recovered as a memo 2026-05-27 (project_u6_followup_hydration_bugs.md).

The bug

U6 fixed the same trap for isHidden on 2026-05-26 — useState(false) ignored the persisted value. Two siblings stayed broken:

  const [name, setName] = useState(template.name);
- const [nameKh, setNameKh] = useState("");
- const [categoryId, setCategoryId] = useState("");
+ const [nameKh, setNameKh] = useState(template.nameKh ?? "");
+ const [categoryId, setCategoryId] = useState(template.categoryId ?? "");

updateProductAction already accepts + persists both fields, so the save path was fine. The load path returned them as undefined because the two DAOs (getTemplateWithLinesById + listTemplatesWithLines) didn't SELECT them. The fix threads both columns through to the client.

What ships

No migration needed — both columns already exist on cafe.product_template (name_kh varchar(200) and category_id uuid → cafe.product_categories.id); see schema.ts:781-782. They were added by H4-X as bridge columns when cafe.products became a view.

6/6 local checks

TemplateRow interface gains nameKh: string | null + categoryId: string | null
listTemplatesWithLines selects both via T.nameKh / T.categoryId and returns them in the row map
getTemplateWithLinesById selects + returns both fields
createTemplateAction returns nameKh: null + categoryId: null on the new row
product-detail-client.tsx hydrates via useState(template.nameKh ?? "") + useState(template.categoryId ?? ""); legacy useState("") patterns asserted absent
DAO round-trip — seed category + product on local lumiere → updateProduct with Khmer name + categoryId → both getTemplateWithLinesById and listTemplatesWithLines return the saved values verbatim

Typecheck: npx tsc --noEmit on nix-cafe → 0 errors after threading both fields through the action layer.

Gate 2 plan

  1. Commit nix-cafe (single-line message; no co-author).
  2. Push to karouna-dev → CF auto-deploy on nix-cafe.
  3. test-u6-followup-prod.mjs on lumiere: seed throwaway category + product on prod Supabase, drive Settings UI through save → reload → assert both fields hydrate; restore.
  4. Regression sweep 51/51 (phase2-cafe-multishop solo per the rule).
  5. Publish prod gallery.