some ui phase improvement planning
This commit is contained in:
@@ -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`.
|
||||
Reference in New Issue
Block a user