Files
mercadodevida/work/artifacts/F-058/implementer.md
2026-08-19 15:17:51 +02:00

4.4 KiB

F-058 — Implementer evidence

Scope delivered

Admin /products showed a 🌿 leaf emoji for every row, even for products that have an image uploaded. The leaf is the fallback in the table cell when p.imageUrl is falsy. Two layers contributed to the bug:

  1. Backend/catalog/products (admin list) and /products/search (public search) both called serializeProduct(product) without the images argument, so the response always carried images: [] even when rows existed in catalog_product_images. Other endpoints (/productos/:slug, /products/:id, /products/:id/images) already passed the images correctly.
  2. Frontendapps/admin/src/app/(dashboard)/products/page.tsx read p.imageUrl (a single string), which the API never returns. The backend instead exposes images: ProductImage[]. The imageUrl field on the type was dead code.

Changes

Backend

project/src/modules/catalog/domain/ports.ts

  • Added listByProductIds(productIds): Promise<ProductImage[]> to the ProductImageRepository interface.

project/src/modules/catalog/infrastructure/pg-product-image-repository.ts

  • Implemented listByProductIds with a single ANY($1::uuid[]) query, ordered by (product_id, position, created_at, id) so the per-product ordering matches listByProductId.

project/src/modules/catalog/api/catalog.routes.ts

  • New helper groupImagesByProductId that buckets images by productId.
  • /catalog/products now calls images.listByProductIds(...) after the page is fetched and groups the result; serializeProduct receives the per-product list (defaulting to [] when none).
  • /products/search does the same. Logging of the search duration is preserved.

project/src/modules/catalog/tests/image-use-cases.test.ts

  • FakeImageRepository updated to implement the new interface method (mirrors the SQL semantics: filter by productId IN set, sort by position/created/id).

Frontend

project/apps/admin/src/app/(dashboard)/products/page.tsx

  • The product cell now reads p.images?.[0]?.url instead of p.imageUrl. The 40 px thumbnail URL is computed the same way: images[0].url.replace('/uploads/', '/uploads/40/').

Acceptance traceability

Acceptance criterion How it is met
GET /api/catalog/products returns images for products with attached images The endpoint now calls images.listByProductIds and feeds the per-product list to serializeProduct. Verified with curl: images=1 url=/uploads/8223b962...jpg for the seeded product.
GET /api/products/search returns images for products with attached images Same fix applied. Verified with curl: images=1 for the same product.
Admin /products shows 40px thumbnail (not leaf) for products with images Frontend now reads images[0].url; the uploads/40/<file> URL is requested, served 200.
No regression in uploads pipeline Only added a read query and a frontend prop switch. No changes to attach, detach, reorder, or storage.
verify.sh is green Exit 0.

Manual verification

$ curl /api/catalog/products?limit=5  (admin auth)
{"items":[{"name":"Proteina Guisante Ecologica",
          "images":[{"url":"/uploads/8223b962-642c-4c95-bc2e-37c2d2fad4ac.jpg",
                     "role":"gallery",...}]}, ...]}
$ curl /api/products/search?limit=20  (public)
… "Proteina Guisante Ecologica" …
    images: [{ url: "/uploads/8223b962-642c-4c95-bc2e-37c2d2fad4ac.jpg" }]
$ curl http://192.168.18.93:3004/uploads/40/8223b962-642c-4c95-bc2e-37c2d2fad4ac.jpg
HTTP 200, 568 bytes  (thumbnail served)

Build verification

  • npm run typecheck (project/) — exit 0
  • npm test (project/) — 124 passed, 56 skipped
  • npm run build (project/) — exit 0
  • npx tsc --noEmit (apps/admin, frontend, storefront) — exit 0
  • ./scripts/verify.sh — exit 0

Files touched

project/src/modules/catalog/api/catalog.routes.ts                                   (modified)
project/src/modules/catalog/domain/ports.ts                                          (modified)
project/src/modules/catalog/infrastructure/pg-product-image-repository.ts           (modified)
project/src/modules/catalog/tests/image-use-cases.test.ts                            (modified)
project/apps/admin/src/app/(dashboard)/products/page.tsx                             (modified)