some ui phase improvement planning

This commit is contained in:
hamid
2026-07-17 13:22:04 +03:30
parent 70fb0a9202
commit 9051bb3e18
41 changed files with 6291 additions and 5 deletions
@@ -0,0 +1,99 @@
# Hardening Phase 3 — Session & error-surface hardening
> Close the shared-device data leak (logout leaves every domain's cache warm for the next login), turn
> the raw stack-trace error boundary into a branded recovery surface with route-level error pages, and
> localize the four hardcoded-English failure toasts. Plus: client-side capability gating for the four
> admin consoles that render without a check.
> **Track:** frontend · **Depends on:** Phase 0 · **Unlocks:** — (independent polish, safe anytime after 0)
> **Before you start, read [_shared/agent-operating-rules.md](../../phases/_shared/agent-operating-rules.md).**
## 1. Context — where this sits
Fixes **H-11 … H-14** from [issues.md](issues.md). All four are verified, none is touched by any other
phase, and none needs backend work.
**What already exists (do not rebuild):** `useLogout` as the single logout path, the
`AuthAccountError` recovery-card pattern (mirror it for the boundary), `dispatchToast`/`ToastBridge`
(the non-React toast bridge — keep it, localize what flows through it), `useAdminCapabilities()`
(`src/hooks/capabilities.ts`) and the non-leaking access-denied patterns used elsewhere in the admin
screens.
## 2. Required reading (do this first)
- [issues.md](issues.md) H-11/H-12/H-13/H-14 — evidence; don't re-audit.
- `client/src/services/auth/hooks/{useLogout,useVerifyOtp,useSelectRole}.ts`,
`client/src/lib/query/{queryClient.ts,QueryProvider.tsx}`,
`client/src/components/common/ErrorBoundary.tsx` + its two consumers,
`client/src/lib/api/client.ts` + `client/src/lib/toast/*`,
the four admin pages (`admin/{audit,verification,tickets,roles}/page.tsx`) + `AdminLayout.tsx`.
- `client/CLAUDE.md` → error contract for `clientFetch`, i18n rules, unit-testing rule.
- Next.js `error.tsx` / `global-error.tsx` file conventions.
## 3. Scope — build this
1. **H-11 — cache isolation across sessions.** `useLogout.onSettled`: `queryClient.clear()` (replace
the auth-only `removeQueries`). Symmetrically clear/reset before seeding the new session in
`useVerifyOtp.onSuccess` and `useSelectRole.onSuccess`, so User B never sees User A's cached
patients/bookings/addresses/tickets/notifications on a shared device. Check nothing depends on
surviving cache entries across logout (geo/catalog reference data may simply refetch — correct).
2. **H-12 — real error surfaces.**
- Rebuild `ErrorBoundary`'s fallback as a branded, i18n'd recovery card (mirror
`AuthAccountError`): translated generic message + retry (re-render children / reload), raw
`error.toString()` + component stack rendered **only** in development. Keep the class component;
add the error-reporting hook point (a single function you can later wire to a service — no TODO
comment left behind).
- Add `src/app/[locale]/error.tsx` and `global-error.tsx` so errors thrown above the shells
(RoleGuard, providers, layouts) get the same branded treatment instead of Next's default. Respect
the root-layout constraint (`global-error` must render its own `<html>`; keep locale/dir sane).
3. **H-13 — localize the failure toasts.** `lib/api/client.ts:57,80,86,91`: replace the four English
literals with a small locale-keyed dictionary module (the fetch layer already knows the locale;
it can't call `useTranslations`). Add keys (suggest an `errors` namespace) to **both**
`messages/en.json` and `messages/fa.json`. Keep the error contract exactly (which statuses toast,
which throw) — only the copy source changes.
4. **H-14 — capability-gate the four admin consoles.** Add a shared `CapabilityGuard`
(`need: keyof AdminCapabilities`) that early-returns the existing non-leaking access-denied state,
and wrap the page bodies of `admin/audit` (canViewAudit), `admin/verification` (canVerify),
`admin/tickets` (canManageTickets), `admin/roles` (canManageRoles) — before their data hooks fire.
Display convenience, not security (the server enforces after Phase 1) — but a scoped admin
deep-linking must see the denied card, not the data.
## 4. Mocks & seams in this phase
None.
## 5. Critical rules you must not get wrong
- `queryClient.clear()` on logout must run **after** the server revoke call is issued (keep the
existing revoke → clear-tokens → dispatch → redirect ordering; the cache clear joins it, doesn't
reorder it).
- Don't toast inside hooks for 401/403/5xx (already toasted by `clientFetch`) — unchanged rule.
- `global-error.tsx` replaces the root layout when it triggers — it must be self-contained (no
providers assumed), bilingual-safe, and never crash itself.
- Every new user-visible string in both message files; shared components (`CapabilityGuard`, the new
boundary fallback if extracted) get co-located tests per the client testing rule.
- Do not turn the boundary into a swallow-everything — rethrow/log in dev so DX doesn't regress.
## 6. Definition of Done
- Shared-device test: login A (customer with patients) → logout → login B → B never sees A's data
(verify React Query devtools cache is empty right after login).
- Throw a test error inside a page in dev → branded Persian card with retry; stack visible in dev
build only. Route-level error files exist and render.
- On `fa`, kill the API and trigger a fetch → Persian network-error toast; expire the session → Persian
session-expired toast + login redirect (Phase 0 behavior).
- Finance-scoped admin deep-links `/admin/audit` → access-denied card, no data fetch fired.
- `npm run check` + `npm run test:ci` green (new tests for CapabilityGuard + boundary fallback).
## 7. How to test (human)
1. Two seeded accounts on one browser: full logout/login swap watching the patients screen.
2. Temporarily `throw new Error('boom')` in a page render; check both dev and `npm run build && start`.
3. Stop the API mid-session on `/fa`; watch toast language; restart, expire tokens, watch the redirect.
4. Login as 09120000021 (finance), deep-link the four consoles.
## 8. Hand off & document
- Tick H-11…H-14 in [issues.md](issues.md) with commit hashes.
- Update `client/CLAUDE.md` (error contract copy source, error.tsx files in the structure tree,
logout lifecycle).
- Write `dev/shared-working-context/reports/hardening-phase-3-report.md`.