Skip to content

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.