diff --git a/docs/proposals/loyalty-go-live-readiness.md b/docs/proposals/loyalty-go-live-readiness.md index 5ad6422d..bda1934b 100644 --- a/docs/proposals/loyalty-go-live-readiness.md +++ b/docs/proposals/loyalty-go-live-readiness.md @@ -329,6 +329,106 @@ start, so the first session where the user can actually invoke it as prospecting `tasks/__init__.py` missing import, other-module email audit. +## 2026-05-29 update — Test 5.0 storefront i18n sweep + FR/DE email accents + +### Test 5.0 (forgot-password) surfaced 5 distinct issues, all fixed + +The user retried Test 5 with the `478c3a9c` JSON-body fix in place. The +forgot-password POST succeeded, but five downstream issues showed up in +one walk-through. Triaged analysis-first per user request before +batching the fix, then shipped as four commits. + +| # | Where | Fix | +|---|---|---| +| 1 | API `forgot-password` handler | Read `request.state.language` first; fall back to `customer.preferred_language` (which is now backfilled at loyalty self-enrollment for both new and returning customers so future emails respect storefront locale). | +| 2A | `customers/storefront/reset-password.html` icons | Replaced `$icon('x-mark' / 'spinner' / 'check')` with inline SVGs (matches `forgot-password.html` convention; standalone templates don't load `icons.js`). | +| 2B | Same template — full i18n | Added `lang` attribute, swapped every hardcoded string for `_()` (22 new `auth.*` keys × 4 locales), added language selector, threaded JS validation strings via `window.__resetPasswordI18n`. | +| 3 | login + forgot + reset CTAs | Renamed `auth.continue_shopping` → `auth.back_to_home` (loyalty storefronts have no catalog). 4-locale rename. | +| 5 | `/account/dashboard`, `/profile`, `/addresses` body | i18n sweep across all three customer-area templates (~80 new `customers.storefront.pages.{dashboard,profile,addresses}.*` keys × 4 locales). | + +Issue 4 (login looked "strangely in FR" after the broken reset page) was +NOT a bug — login.html was correctly translated all along; the contrast +with the broken reset page just made it look weird. + +### FR + DE password_reset email body restored to native orthography + +The seeded FR template body had every accent stripped (`Envoye`, `recu`, +`demande`, `equipe`, `Reinitialiser`, etc.). Same pattern in DE +(`zurucksetzen`, `Schaltflache`, `lauft`, `konnen`, `Grussen`). + +Both templates now read natively. As a bonus, signatures on all 4 locales +were changed from generic ("L'équipe" / "The Team" / "Das Team" / +"D'Team") to `{{ store_name }}`-interpolated ("L'équipe Fashion Hub" / +"The Fashion Hub Team" / etc.) using the auto-injected `store_name` +branding variable from `EmailService.get_branding`. + +The seeder is idempotent (upserts on `(code, language)`), so re-running +`scripts/seed/seed_email_templates_core.py` updates existing rows in +place — no DB wipe needed. + +### Alpine quoting bug surfaced and fixed downstream + +The customer-dashboard unread-message line crashed Alpine with +`"expected expression, got '}'"` because the original sweep emitted +`{{ _('...')|tojson }}` directly inside `x-text="..."` — the JSON's +double quotes broke out of the double-quoted HTML attribute. Fix moved +the singular/plural strings onto `window.__accountDashboardI18n` and +referenced them by global path from `x-text`. The nested +`x-data="{ unreadCount: 0 }"` scope can't see the parent component's +`i18n` property, but `window.*` is always reachable. + +The other auth templates using `|tojson` (language-selector blocks) +escape it via single-quoted outer attributes (`x-data='...'`), so the +collision was unique to the new dashboard code. + +### Seed-script path bug surfaced during the prod reseed + +`scripts/seed/seed_email_templates_core.py` had +`Path(__file__).parent.parent` which resolves to `scripts/`, not the +project root, so `from app.core.database import get_db` failed with +`ModuleNotFoundError: No module named 'app'`. The loyalty sibling had +`parent.parent.parent` already (correct). Fixed to match. The canonical +deploy command in `docs/deployment/hetzner-server-setup.md:549` sets +`PYTHONPATH=/app` and would have masked the bug anyway, but defence in +depth is cheap. + +### Status board delta + +- Step 6 (web user-journey E2E tests) — Tests 1 ✅, 2 ✅, 3 ✅, 4 ✅, + **5.0 ✅** (forgot-password end-to-end on FR, including email + reception and accent correctness). Test 5 itself (login + dashboard + + history) is the next concrete step, gated on recreating the prod + `api` container to serve the i18n-swept HTML. + +### Carry over for next session + +1. **Recreate the prod `api` container** first thing tomorrow: + `docker compose --profile full up -d --force-recreate api`. The + Alpine fix (`1bade6e6`) is in the image built today but the + long-running container is still on the old image, so the dashboard + still throws the `x-text` error end-of-day. Verify the dashboard + renders cleanly after recreate. +2. **Continue Test 5** from step 5.1 (login as customer) → 5.2 + (`/account/loyalty` dashboard, expect 168 pts) → 5.3 + (`/account/loyalty/history`, expect cross-store transaction list). +3. **Static asset cache-busting gaps** (new item raised by user): the + `?v=` system from the 2026-05-18 cache-busting work is + in place, but some JS/CSS still load without the `?v=` query param. + Audit which files miss it (likely standalone templates that bypass + the `static_v()` / `url_for` helpers). The `FE-024` arch rule was + supposed to guard this — check whether it's firing on these gaps. +4. **DE/LB email template quality sweep** — other DE templates likely + have the same missing-umlaut pattern as `password_reset` + (signup_welcome, order_confirmation, team_invite, etc.; ~11 codes × + 4 locales). LB has inconsistent accents too. Worth a single pass + with a native-speaker review. +5. **Transaction categories permissions audit** (carried from + 2026-05-24). +6. **Routing pass** (carried — after Test 8). +7. **Existing backlog** (carried): Hetzner doc check, B1-F unit tests, + prospecting `tasks/__init__.py` missing import, other-module email + audit. + ## Status board | # | Pre-launch step | State | Notes |