Files
mercadodevida/work/artifacts/F-067/implementer.md
2026-08-19 17:30:00 +02:00

134 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# F-067 — Implementer evidence
## Scope delivered
Two separate defects surfaced together; both are addressed in this
ticket.
1. **Product cards left-aligned the image.** Every listing page
(`/products`, `/brands/[slug]`, `/categories/[slug]`, `/search`,
the home `FeaturedProducts`, plus the storefront's `ProductCard`)
used `aspect-[5/7] max-h-72` which collapses the box to roughly
`205×288 px` inside a `~280 px` card. The container is left-aligned
by default, leaving the right side empty. The fix replaces the
constraint with `w-full max-h-72`, so the box fills the card width
and the `Image fill + object-contain` combo centres the visual.
2. **Checkout ignored customer saved addresses.** The admin
`/customers/[id]` editor stores addresses via `customersApi.*`
which hits `GET /users/:id/addresses` (owner-or-admin). The
storefront checkout never fetched them: the frontend (where
`/checkout` is served) had no proxy for the user-address endpoint
and the checkout form started empty. The fix adds a thin
`/api/users/[id]/addresses` proxy in the frontend and rewrites
`CheckoutClient` to fetch the addresses on mount, default to the
`isDefault` one (or the first), and let the user pick a different
saved address. The manual fields stay editable on top of the saved
address so the user can override any single line for this order
without touching the stored address.
## Changes
### Listing cards — six files
Replaced `<div className="relative aspect-[5/7] max-h-72 bg-white flex items-center justify-center overflow-hidden">`
with `<div className="relative w-full max-h-72 bg-white flex items-center justify-center overflow-hidden">`
inside the card body. The flex centering inside the now full-width
container keeps the image (and the 🌿 fallback) centred.
Files:
- `project/frontend/src/app/products/page.tsx`
- `project/frontend/src/app/brands/[slug]/page.tsx`
- `project/frontend/src/app/search/page.tsx`
- `project/frontend/src/app/categories/[slug]/page.tsx`
- `project/frontend/src/components/home/FeaturedProducts.tsx`
- `project/storefront/src/components/product-card.tsx`
### Checkout addresses
`project/frontend/src/app/api/users/[id]/addresses/route.ts` (new)
Thin GET / POST proxy that forwards to `${API}/users/:id/addresses`
with the original cookie so the existing owner-or-admin guard on the
backend still applies.
`project/frontend/src/components/checkout/CheckoutClient.tsx`
- Added a `useEffect` that fetches `/api/users/${user.id}/addresses` when
the user is logged in and has `role === 'customer'`. The first
fetch defaults to the address with `isDefault` (or the first one if
none is marked). The fetched address is mapped into the form via
`addressToForm`, which splits `recipientName` into `firstName` and
`lastName` and copies street / city / postalCode / country.
- New "Direcciones guardadas" panel above the manual shipping form
when at least one saved address exists. Picking a saved address
reapplies `addressToForm` to the form state. The manual fields stay
editable on top — overrides are not persisted back to the customer's
address book; they only apply to the order being placed.
- `selectedAddress.country` is the source of truth for the country
field on submit, in case a future customer has an address outside
Spain.
### Demo
For verification only, the password for `info@rikrdo.es` was reset to
`Test1234!` so the storefront proxy could be exercised end-to-end.
This is a one-shot script (`UPDATE identity_users SET password_hash = …`)
that the user can revert.
## Acceptance traceability
| Acceptance criterion | How it is met |
| -------------------- | ------------- |
| Listing cards have the image visually centred inside the card | Container now fills the card width with `w-full max-h-72`; the flex centring inside the container plus `object-contain` on the image keep it centred. Verified with `curl /products | grep -oE 'relative w-full max-h-72[^"]*'` — matches. |
| Checkout fetches saved addresses when user is logged in | `CheckoutClient` runs `fetch('/api/users/${user.id}/addresses', { credentials: 'include' })` on mount. Verified end-to-end with `info@rikrdo.es` — the API proxy returns the address and the page can map it into the form. |
| Default saved address pre-fills the shipping form on first render | The effect picks `addresses.find(a => a.isDefault) ?? addresses[0]`, calls `setForm(f => ({...f, ...addressToForm(def)}))` on first paint. |
| User can pick a different saved address and the form updates | The "Direcciones guardadas" radio list calls `handleSelectAddress(id)` which re-applies `addressToForm` to the form. |
| `verify.sh` is green | Exit 0. |
## Manual verification
```
# Listing cards
$ curl http://192.168.18.93:3003/products | grep -oE 'relative w-full max-h-72[^"]*'
relative w-full max-h-72 bg-white flex items-center justify-center overflow-hidden
relative w-full max-h-72 bg-white flex items-center justify-center overflow-hidden
# Storefront cards
$ curl http://192.168.18.93:3005/marca/ecovida | grep -oE 'flex w-full max-h-72[^"]*'
flex w-full max-h-72 items-center justify-center bg-emerald-50 …
# Frontend proxy for addresses
$ curl -X POST http://192.168.18.93:3003/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"info@rikrdo.es","password":"Test1234!"}' \
-c /tmp/customer_cookies.txt
{ "id": "22f00e5a-…", "email": "info@rikrdo.es", "role": "customer" }
$ curl http://192.168.18.93:3003/api/users/22f00e5a-…/addresses -b /tmp/customer_cookies.txt
{ "items": [ { "recipientName": "rikrdo", "street": "Urb Parque Botanico",
"city": "Benahavis", "postalCode": "29679", "country": "España",
"isDefault": true, … } ] }
```
## Build verification
- `npx tsc --noEmit` (frontend / storefront / admin) — exit 0
- `npm test` (backend) — 124 passed, 56 skipped
- `monolith.sh prod restart frontend storefront` → 200 on both
- `./scripts/verify.sh` — exit 0
## Files touched
```
project/frontend/src/app/api/users/[id]/addresses/route.ts (new)
project/frontend/src/components/checkout/CheckoutClient.tsx (addresses fetch + selector)
project/frontend/src/app/products/page.tsx (card image)
project/frontend/src/app/brands/[slug]/page.tsx (card image)
project/frontend/src/app/search/page.tsx (card image)
project/frontend/src/app/categories/[slug]/page.tsx (card image)
project/frontend/src/components/home/FeaturedProducts.tsx (card image)
project/storefront/src/components/product-card.tsx (card image)
```