Files
mercadodevida/work/artifacts/F-066/implementer.md
2026-08-19 17:22:39 +02:00

5.0 KiB

F-066 — Implementer evidence

Scope delivered

Three separate defects surfaced together; all are addressed in this ticket.

  1. Image invisible on /products/[slug]. The container in frontend/src/app/products/[slug]/page.tsx was relative w-full max-h-[500px] with a next/image fill child. next/image with fill requires a non-zero-height parent; max-h is only a ceiling, so the parent collapsed to zero and the absolutely positioned image filled zero pixels. F-061 had removed the aspect-[5/7] constraint that used to give the box a real height (it caused a different centering problem). This fix restores the aspect-[5/7] inside a flex centering wrapper, so the box has a height and is centred horizontally inside its grid cell.

  2. PATCH /api/products/:id returned 500 when the payload included attributes. The attributes column is JSONB, but PgProductRepository.update was binding the JS array directly via pg-node. pg-node serialises JS arrays as PG array literals ({bio,keto}); the JSONB parser rejects that with invalid input syntax for type json. The fix JSON.stringifys the array and casts the parameter as ::jsonb.

  3. serializeProduct omitted channels, featured, and attributes. The admin editor saved these values successfully but the next GET round-tripped an object without them, so the UI never reflected the change. Added the three fields to serializeProduct.

Changes

project/frontend/src/app/products/[slug]/page.tsx

- <div className="relative w-full max-h-[500px] bg-gray-50 rounded-2xl border border-gray-100 overflow-hidden">
-   <Image fill className="object-contain" ... />
- </div>
+ <div className="flex justify-center">
+   <div className="relative w-full max-w-md aspect-[5/7] max-h-[500px] bg-gray-50 rounded-2xl border border-gray-100 overflow-hidden">
+     <Image fill className="object-contain" ... />
+   </div>
+ </div>

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

  for (const [key, column] of UPDATABLE) {
    if (key in patch) {
+     if (key === 'attributes' && Array.isArray(patch[key])) {
+       values.push(JSON.stringify(patch[key]));
+       setClauses.push(`${column} = $${values.length}::jsonb`);
+     } else {
        values.push(patch[key]);
+       setClauses.push(`${column} = $${values.length}`);
+     }
    }
  }

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

  function serializeProduct(product, images = []) {
    return {
      …,
      state: product.state,
+     channels: product.channels,
+     featured: product.featured,
+     attributes: product.attributes,
      seoTitle: product.seoTitle,
      …
    };
  }

Acceptance traceability

Acceptance criterion How it is met
GET /products/[slug] shows the product image (no zero-height container) aspect-[5/7] max-h-[500px] gives the wrapper a real height; curl on the rendered page shows <img alt="Proteina Guisante Ecologica" … style="position:absolute;height:100%;width:100%;…"> inside <div class="relative w-full max-w-md aspect-[5/7] max-h-[500px] …">.
PATCH /api/products/:id with attributes succeeds and round-trips JSON.stringify + ::jsonb cast; verified with PATCH {"attributes":["bio","keto"],"featured":true,"channels":"online"} → 200 + the same shape echoed back.
GET /api/products/:id returns channels, featured, attributes serializeProduct now includes them; verified the field is present in the JSON.
verify.sh is green Exit 0.

Manual verification

$ curl -X PATCH http://192.168.18.93:3004/api/products/13a65dc0-… \
    -H 'Content-Type: application/json' -b /tmp/admin_cookies.txt \
    -d '{"attributes":["bio","keto"],"featured":true,"channels":"online"}'
{ … "channels":"online", "featured":true, "attributes":["bio","keto"], … }
HTTP 200

$ curl http://192.168.18.93:3004/api/products/13a65dc0-… -b /tmp/admin_cookies.txt
{ … "channels":"online", "featured":true, "attributes":["bio","keto"], … }

$ curl http://192.168.18.93:3003/products/proteina-guisante-ecologica | grep -oE 'aspect-\[5/7\][^"]*'
aspect-[5/7] max-h-[500px] …

The 500 line is gone from project/.runtime/prod/backend.log; the next PATCH with attributes is 200.

Build verification

  • npm run typecheck (backend) — exit 0
  • npm test (backend) — 124 passed, 56 skipped
  • npm run build (backend) — exit 0
  • npx tsc --noEmit (frontend) — exit 0
  • monolith.sh prod restart backend frontend → backend / frontend 200
  • ./scripts/verify.sh — exit 0

Files touched

project/frontend/src/app/products/[slug]/page.tsx                                          (image container height)
project/src/modules/catalog/infrastructure/pg-product-repository.ts                       (JSONB cast for attributes)
project/src/modules/catalog/api/catalog.routes.ts                                          (serializeProduct fields)