fix(water-log): address code-review findings from i18n PR
Deploy to route.crispygoat.com / deploy (push) Successful in 4m9s

- Session-expired bounce-to-PIN branch now matches the actual strings
  returned by requireFieldSession (was 'Session expired or invalid'
  which never matched). Added 'Session not found' and 'User is inactive'
  to the intercept list. The user-visible 'Your session expired. Please
  sign in again.' string is now in the dictionary (en + es).
- LangProvider moved up to WaterFieldClient. The orchestrator (which
  was sitting OUTSIDE the provider in b29caa0) now calls useLang()
  directly, so the loadHeadgates fallback error string is always
  rendered in the user's chosen language — even if they toggled
  after signing in.
- getClientLangSnapshot now memoizes the cookie value (React 19
  enforces getSnapshot purity stricter than 18).
- LanguageToggle switched from aria-pressed (toggle pattern) to
  role="radiogroup" + role="radio" + aria-checked (mutually-exclusive
  selector pattern). Per-button aria-labels dropped — the visible
  EN/ES text is read by SR as the radio name. Group label switched
  to t.common.a11yLanguage for Spanish parity.
- Notes textarea now has aria-label={t.log.notes} (was unlabeled —
  SR would announce 'Edit text' with no context).
- 1 new test: lock in that wl_lang=<script>...</script> falls through
  to the navigator (injection guard on the cookie regex).

Refs /tmp/refactor-water-log.md (review followups to Commit B)
This commit is contained in:
Tyler
2026-07-02 11:27:16 -06:00
parent 7795263944
commit 9ecb496a7c
6 changed files with 119 additions and 83 deletions
+17
View File
@@ -49,6 +49,8 @@ export type Labels = {
a11yBackToHeadgates: string;
a11yChangeHeadgate: string;
a11ySignOut: string;
/** ARIA group label for the language toggle. */
a11yLanguage: string;
};
/** The brand strip shown on the PIN and Success screens. */
brand: {
@@ -94,6 +96,12 @@ export type Labels = {
implausiblyLarge: string;
submit: string;
submitting: string;
/** Shown when a session expires mid-shift; bounce user to PIN. */
sessionExpired: string;
/** Generic submit-failure fallback (e.g. server-side validation). */
submitError: string;
/** Defensive guard — shouldn't ever happen. */
noHeadgateSelected: string;
normalRange: (low: number, high: number, unit: string) => string;
alertAbove: (high: number, unit: string) => string;
alertBelow: (low: number, unit: string) => string;
@@ -143,6 +151,7 @@ export const LABELS: Record<Lang, Labels> = {
a11yBackToHeadgates: "Back to headgates",
a11yChangeHeadgate: "Change headgate",
a11ySignOut: "Sign out",
a11yLanguage: "Language",
},
brand: {
waterLog: "TUXEDO WATER LOG",
@@ -187,6 +196,9 @@ export const LABELS: Record<Lang, Labels> = {
implausiblyLarge: "That value is implausibly large",
submit: "Submit Log",
submitting: "Submitting…",
sessionExpired: "Your session expired. Please sign in again.",
submitError: "Something went wrong submitting that entry. Try again.",
noHeadgateSelected: "Pick a headgate first.",
normalRange: (low, high, unit) =>
`Normal range: ${low} ${high} ${unit}`,
alertAbove: (high, unit) => `Alert above ${high} ${unit}`,
@@ -230,6 +242,7 @@ export const LABELS: Record<Lang, Labels> = {
a11yBackToHeadgates: "Volver a compuertas",
a11yChangeHeadgate: "Cambiar compuerta",
a11ySignOut: "Cerrar sesión",
a11yLanguage: "Idioma",
},
brand: {
waterLog: "REGISTRO DE AGUA TUXEDO",
@@ -274,6 +287,10 @@ export const LABELS: Record<Lang, Labels> = {
implausiblyLarge: "Ese valor es demasiado grande",
submit: "Enviar registro",
submitting: "Enviando…",
sessionExpired: "Tu sesión expiró. Inicia sesión otra vez.",
submitError:
"Algo salió mal al enviar ese registro. Inténtalo de nuevo.",
noHeadgateSelected: "Elige una compuerta primero.",
normalRange: (low, high, unit) =>
`Rango normal: ${low} ${high} ${unit}`,
alertAbove: (high, unit) => `Alerta arriba de ${high} ${unit}`,