Andrea redesign 1: Steps 5–6 integration handoff¶
Historical local-review record. The old deployment and pending cutover described below were the state at this stage, not today. See the redesign story and verified cutover; the spike overlay is retired.
Date: 2026-09-24 (America/Bogota). Branch: jm/andrea-redesign-1.
This is a local implementation and review, not a production release. The
live VPS still serves the old /cementos deployment. Do not infer cutover
authorization from this handoff. Step 7 of the plan
still owns production Nginx, storefront build paths, redirects, environment
values, rollback, and final approval. No production containers, customer
records, or catalog records were changed during Steps 5–6.
Customer-facing result¶
Both languages now share the same Medusa Colombia region, COP prices, stock,
cart cookie, and inquiry behavior. The language switch retains the equivalent
Guanza collection, product (including v_id), or cart route. English translates
interface and inquiry text but explicitly labels untranslated merchant product
copy rather than silently inventing English product descriptions. The header
has no account entry in this iteration. Registration/login code and existing
backend accounts are preserved, but the new account route is not advertised:
verification email delivery, callback routing, sessions, cart transfer, and
full localization remain unproven. See the account readiness audit.
The public architecture contact area offers email, LinkedIn, and a restrained
WhatsApp button. Its form posts to the portfolio's same-origin
/api/contact/whatsapp endpoint; the server alone reads
ARCHITECTURE_WHATSAPP_NUMBER and responds with an uncacheable 303 to the
configured destination. It rejects cross-origin POSTs and GETs. The number is
not embedded in HTML or client JavaScript, though anyone who initiates the
handoff can inspect the redirect. The store inquiry keeps using its separate
STORE_INQUIRY_WHATSAPP_NUMBER BFF setting. At release, put the approved
business destination into both keys in the ignored private
production.env; changing a number thereafter requires a frontend container
recreation, not a rebuild. The currently deployed personal shopping number
is untouched by this branch.
Store cart quantity controls now use the current Medusa product inventory when available. If Medusa omits a managed variant's stock, the interface does not offer speculative extra units. If stock changes between render and click, a failed quantity update shows localized, safe feedback instead of a minified React exception. This matters for the four demo-only Piezas products, each seeded with one unit. The cart also omits a redundant variant subtitle when it repeats the product name.
Local integration results¶
The opt-in Nginx/Compose route spike served the built portfolio and storefront
at http://localhost:8080. This is deliberately a production-like storefront
runtime (npm run build && npm run start) rather than the Next development
server: the latter's HMR asset routes caused client hydration to fail behind
the split-app proxy even when server-rendered pages looked correct. The spike
does not alter the existing production Nginx configuration. Local Nginx
forwards the original host and port to both apps; dropping :8080 caused
the portfolio contact form's same-origin check to reject valid POSTs.
The local database initially had the four starter products but no collections.
For review only, setup-redesign-local-fixtures.ts created the GUANZA and
Piezas Intervenidas collections, assigned the existing starter products to GUANZA,
and left their price/inventory alone. The existing opt-in sample seed and media
import then created the four marked Piezas samples with local Garage images.
Those scripts were run only locally in this step. The VPS samples imported
in Step 4 are separate state.
Browser-level checks through the local proxy confirmed:
| Journey | Result |
|---|---|
| Guanza carousel → Piezas Intervenidas → real product | Collection and product routes loaded with the same Medusa data in Spanish and English. |
Product with v_id → English → Spanish |
The selected product/variant route remained equivalent. |
| Add one Piezas sample → cart → switch language | One cart line, same COP 80,000 subtotal, and count 1 survived. |
| Add a second unit of the one-unit sample | Medusa correctly rejected the quantity before the fix. After rebuilding, the increase control was disabled and the cart remained at one unit. |
| Inquiry copy/email | Localized draft and cementos@andreazambrano.co mailto: link were present; no message was sent. |
| Architecture WhatsApp via Nginx | Same-origin POST returned 303 to the configured business destination with no-store and no-referrer; cross-origin POST returned 403. The redirect was inspected, not followed. |
Desktop and narrow-layout screenshots were reviewed for the landing, architecture, collection, product and cart. The product title did not overlap the image. An in-app browser viewport override did not apply reliably in this environment, so a true 390px phone/tablet/zoom pass is still a release review gate, not a claimed pass. Keyboard Enter advanced the collection carousel. In the architecture gallery, the inline next control changed the selected board; the overlay opened on that board, Escape dismissed it, and focus returned to the opener without changing the inline board. The carousel, gallery, and shared styles include reduced-motion rules; real reduced-motion and touch interaction also need a final device/browser pass with Andrea. No production customer or email verification journey was tested.
The storefront test suite passed (36 tests), storefront production build and
lint passed; the portfolio production build and 22 tests passed. Portfolio
lint reported only its pre-existing img optimization warnings. Re-run these
after any subsequent edits. The Git pre-commit hook also runs the root lint
script. The VPS noindex/robots gate remains in place; new bilingual route
metadata and canonical/alternate links are prepared locally, not published.
Local gotchas and repeatable preview¶
Follow current local operations
for startup commands; the original spike is retired. Docker's persistent storefront_node_modules
volume can outlive package-lock.json; this local checkout initially lacked
the newly pinned self-hosted fonts inside the container despite a successful
host build. Reconcile the volume with npm ci in that container (without
deleting Postgres/Redis/Garage data), then rebuild the storefront. This is a
development-volume issue; the production image installs dependencies from its
lockfile.
The local preview is ready for Justin and Andrea to inspect before Step 7. What still needs Andrea's content approval: biography and portrait; conceptual architecture projects; Orígenes hero copy/art; Piezas samples, provisional prices and rights to recognizable-character imagery. Missing future collections (velas, zapatos, aretes) remain intentionally absent. Guest inquiry is not checkout, reservation, or payment confirmation.
Design iteration before Step 7¶
After Andrea reviewed the local preview, the landing gained her architecture
email and a form-based WhatsApp contact in its footer. Both Spanish and English
use the same server-only architecture contact destination; the number is not
rendered in the page. The /arquitectura WhatsApp button now carries the same
diagonal outbound arrow as the neighboring contact links. The landing footer
uses normal document flow and a minimum viewport-height frame: it sits at the
bottom of a short page without overlaying content, and follows content when
the page is taller. Intro text and tiles share the same 1440px maximum width.
The shared paper color was changed from #fbfaf7 to the sampled catalog outer
paper #f0efec; inset product-card surfaces use #e9e7e5. The collection
header's top and bottom padding were reduced so its heading no longer consumes
most of the first viewport. These are design-review choices, not Andrea's
final content approval.
For a remote review without touching the VPS, the opt-in local Nginx overlay
exposes public Garage images under /guanza/media/. Medusa already stores and
serves the original images; only their browser-facing URL is translated for
the tunnel. The overlay preserves ngrok's HTTPS scheme for origin validation.
The ignored local .env must contain ARCHITECTURE_WHATSAPP_NUMBER for the
contact handoff; an absent value returns a deliberate 503 “unavailable” error.
See Sharing with ngrok for the
operational steps and link-lifetime limits. None of this changes production
Nginx or the product database.