Two latent useState("") hydration bugs in product-detail-client.tsx
matching the U6 isHidden pattern. 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.
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 accepted + persisted 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 the DAOs + the
TemplateRow interface + the createTemplateAction shape +
the client hydration.
lib/db/product_attribute.ts — TemplateRow
gains nameKh: string | null + categoryId: string | null;
both getTemplateWithLinesById + listTemplatesWithLines
select T.nameKh + T.categoryId and propagate to the row.lib/actions/product_attribute.ts —
createTemplateAction returned row gains nameKh: null
+ categoryId: null (new template, both empty until admin fills).app/(authed)/products/[id]/product-detail-client.tsx
— hydration switched to useState(template.nameKh ?? "") +
useState(template.categoryId ?? "").No migration needed — both columns already exist on cafe.product_template
(added by H4-X as bridge cols when cafe.products became a view).
| ✓ | Seed category + product on prod lumiere via DAO probe (captures templateId) |
| ✓ | SSO login on lumiere-coffee.nixtech.app |
| ✓ | Navigate to /cafe/products/{templateId} — page renders |
| ✓ | Initial form state: nameKh + category both empty (fresh row from seed) |
| ✓ | Fill Khmer name (សាកល្បង U6F …) + pick seeded category + Save |
| ✓ | DB verification #1: name_kh + category_id match UI input via direct Supabase SELECT |
| ✓ | Reload — nameKh hydrates from saved value (the load-path fix under test) |
| ✓ | Reload — categoryId hydrates from saved value |
| ✓ | Round-trip the other way: change Khmer name + clear category + Save |
| ✓ | DB verification #2: name_kh updated, category_id NULL |
| ✓ | Reload — nameKh hydrates with new string, category dropdown shows "" (Uncategorized) |
| ✓ | No 5xx during the suite |
| test-phase1-prod.mjs | 11/11 |
| test-phase2-sso-outdoor-prod.mjs | 6/6 |
| test-m1-prod.mjs | 10/10 |
| test-r7-prod.mjs | 14/14 |
| test-r8-prod.mjs | 4/4 |
| test-phase2-cafe-multishop-prod.mjs (solo) | 6/6 |