2.0 KiB
2.0 KiB
F-079 — Architect: Product list /products does not show brand
Root cause
The admin /products page already renders a Marca column with {p.brand?.name ?? '—'}, and the Product TS type already declares an optional brand: { id, name, slug }. However, the backend PgProductRepository never populates that field: PRODUCT_COLUMNS does not LEFT JOIN brands_brands, and toProduct(row) only emits brandId: row.brand_id without a denormalized brand object. So every row in the list renders — regardless of whether a brand is assigned.
Design
Single change set in the repository:
ProductRowinterface gains optional fieldsbrand_name: string | nullandbrand_slug: string | null.PRODUCT_COLUMNSis replaced with a subquery (PRODUCT_BASE_COLUMNS) that LEFT JOINsbrands_brandsand selectsb.name AS brand_name, b.slug AS brand_slug. Existing queries (findById,findActiveBySlug,listAll) keep their semantics but read the new column set.toProduct(row)emitsbrandonly whenbrand_id IS NOT NULL:Whenbrand: row.brand_id ? { id: row.brand_id, name: row.brand_name ?? '', slug: row.brand_slug ?? '' } : undefined,brand_idis null the frontend falls back to the existing—placeholder.
No new tables, no API contract change.
Frontend is already prepared (the column, the type and the placeholder all exist). After the change:
- Products with a brand: column shows the brand name.
- Products without a brand: column shows
—(placeholder behavior is unchanged).
Risk
Low. Pure read-side change. Existing tests on the repository must still pass.
Acceptance mapping
- "Each row in /products listing shows the brand name" → toProduct now hydrates
brand.name. - "Brand column header is present" → already in page.tsx.
- "Empty brand renders as a placeholder" → existing
p.brand?.name ?? '—'handles it. - "verify.sh is green" → no test regression expected.