Files
mercadodevida/work/artifacts/F-079/architect.md
2026-08-20 05:57:24 +02:00

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:

  1. ProductRow interface gains optional fields brand_name: string | null and brand_slug: string | null.
  2. PRODUCT_COLUMNS is replaced with a subquery (PRODUCT_BASE_COLUMNS) that LEFT JOINs brands_brands and selects b.name AS brand_name, b.slug AS brand_slug. Existing queries (findById, findActiveBySlug, listAll) keep their semantics but read the new column set.
  3. toProduct(row) emits brand only when brand_id IS NOT NULL:
    brand: row.brand_id
      ? { id: row.brand_id, name: row.brand_name ?? '', slug: row.brand_slug ?? '' }
      : undefined,
    
    When brand_id is 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.