Two small asks from Narong (2026-07-24) — plus a latent page-crashing bug found while verifying them. No migration, no backend change. Four repos.
shiftDate() parsed "2026-07-24T00:00:00" as local time, then formatted the
result with .toISOString() — which is UTC. The +7 offset leaked into the output. It looks
perfectly correct to anyone testing in UTC, which is how it shipped.
| Back arrow | Forward arrow | |
|---|---|---|
| Before (Cambodia, UTC+7) | 07-24 → 07-22 (2 days) | 07-24 → 07-24 (stuck) |
| After | 07-24 → 07-23 | 07-24 → 07-25 |
Fixed with shiftBusinessDate() in lib/utils.ts — the arithmetic now happens entirely in UTC on
both ends, so it is offset-independent. It replaced two duplicated copies of the old helper. The test
reproduces the original 2-day jump in a UTC+ zone before asserting the fix, so it can't quietly pass in UTC the
way the original slipped through.

get-coffee.staging after clicking back — 2026-07-24 → 2026-07-23, exactly one day.
Added to both password logins: type="button" so it can never submit the form, an
aria-label/aria-pressed pair that flips with state, and a visible keyboard focus ring. New
strings went into each app's en.json and the shared localization/locales/ source so the
Khmer pass picks them up; km stays empty and falls back to English, per the existing convention.




Building the eye toggle meant actually loading the Commerce login page — and it rendered completely blank.
vue-i18n treats a bare @ as linked-message syntax. The string
auth.login.email_placeholder = "you@company.com" therefore threw
SyntaxError: Invalid linked format and took down the entire page mount — not just the placeholder.
This is the same class of bug already fixed once in the Cpanel bundle, but Commerce never got the fix, and here it sat on
the main merchant login.
Production was never affected — the i18n wiring commit had not been promoted to main on
either Commerce or the Outdoor CRM. The real risk was that the next promote of that i18n work would have carried
a blank login screen to every merchant.
Swept every vue-i18n locale file and escaped all of it as {'@'}:
| File | Keys | Would have broken |
|---|---|---|
nix-commerce/src/i18n/en.json | 2 | the login page + invoices |
nix-outdoor-sales-frontend/src/i18n/en.json | 2 | Company Setup wizard, Edit Zone |
localization/locales/{nix-commerce, outdoor-sales, cpanel-admin}/en.json | 6 | would re-introduce it on the next sync |
nix-cafe was deliberately left alone — it runs next-intl (ICU), where @ is an ordinary
character and this escape would render a literal {'@'} to users.

demo.staging — rendering again, with the @ showing correctly in the email placeholder (the escape does not leak into the UI).| Suite | Result |
|---|---|
| Local — date helper across two timezones + boundaries, eye toggle on both logins | 16/16 |
| Staging — login renders, @ escapes, eye toggle, date arrows ±1 day, no 5xx | 9/9 |
| Staging smoke (SSO, cross-tenant guard, Starter login, no-session redirect) | 5/5 |
tsc --noEmit (nix-cafe) + vite build (commerce, admin, outdoor) | clean |
| Repo | staging | Change |
|---|---|---|
| nix-cafe | 72f1f4e (Worker 99f8b585) | date-arrow fix |
| nix-commerce | b230336 | eye toggle + @ escape |
| nix-outdoor-sales-admin | b70d0f1 (dev; no staging branch) | eye toggle |
| nix-outdoor-sales-frontend | 5d10d3e | @ escape |
No migration. Parked for the scheduled prod promote. Note: the Commerce and Outdoor staging merges also carried their
i18n wiring commits to staging for the first time — worth a look beyond these fixes.