feat: centralize API error localization
Replace the err instanceof ApiError ? err.message : t(fallback) anti-
pattern -- which showed raw English backend text for the common case and
only used the localized fallback for the rare network-failure case -- at
all 13 call sites across 7 files.
New frontend/src/api/errorMessages.ts (describeApiError) resolves a
caught error to a localized {title, explanation, nextStep?, technical}
by checking the 32 known AppError codes first, then known HTTP statuses
(401/403/404/409/422/500), then a fully generic fallback. New
ApiErrorNotice (PageChrome.tsx) renders title/explanation/nextStep with
the raw text demoted to a "Technical details"/"Details techniques"
disclosure -- never shown as the primary message.
ApiError itself is split out of client.ts into a standalone
api/apiError.ts with no import.meta.env dependency, so errorMessages.ts
(and its tests) can be loaded outside a Vite/browser context.
This commit is contained in:
@@ -347,6 +347,12 @@ details summary { cursor: pointer; color: var(--teal-dark); }.data-table details
|
||||
|
||||
.state-panel { min-height: 180px; display: flex; align-items: center; justify-content: center; gap: 12px; padding: 28px; color: var(--muted); background: white; border: 1px solid var(--line); border-radius: var(--radius); text-align: left; }.state-panel svg { width: 24px; color: var(--critical); }.state-panel strong { color: var(--ink); font-size: .82rem; }.state-panel p { margin: 4px 0 0; font-size: .73rem; }.spinner { width: 22px; height: 22px; border: 2px solid var(--line); border-top-color: var(--teal); border-radius: 50%; animation: spin .7s linear infinite; }@keyframes spin { to { transform: rotate(360deg); } }.state-empty svg { color: var(--teal-dark); }
|
||||
.error { color: #9f2929; font-size: .74rem; font-weight: 600; }
|
||||
.api-error-notice { display: block; padding: 12px 14px; background: #fdf1f1; border: 1px solid #f0caca; border-radius: var(--radius); }
|
||||
.api-error-notice strong { display: block; font-size: .78rem; }
|
||||
.api-error-notice p { margin: 4px 0 0; font-weight: 400; }
|
||||
.api-error-notice .api-error-next-step { color: #7a2323; font-style: italic; }
|
||||
.api-error-notice .evidence-disclosure { margin-top: 8px; }
|
||||
.api-error-notice .evidence-disclosure summary { font-weight: 700; cursor: pointer; }
|
||||
|
||||
.login-shell { min-height: 100vh; display: grid; grid-template-columns: minmax(420px, 1.05fr) minmax(430px, .95fr); background: white; }
|
||||
.login-story { position: relative; min-height: 100vh; display: flex; flex-direction: column; padding: 34px 7vw 30px; overflow: hidden; color: white; background: var(--petrol); }
|
||||
|
||||
Reference in New Issue
Block a user