94fdcbe0d1
Turn the starter into the Balinyaar foundation for the three actor
experiences and lock in the patterns later phases copy.
- Cleanup: remove toastDemo namespace, placeholder home page, and the two
dead icons; fix BottomBar to use usePathname (locale-aware active tab).
- Three actor shells under (private-routes), no layout above [locale]:
customer (customer) group with the 5-tab bottom nav; nurse (/nurse) and
admin (/admin) on the shared sidebar engine. Role model via constants/roles
+ useActorRole (defaults to customer until roles land in f1-b2).
- services/{domain} reference (patients) with a mock behind a config seam,
hierarchical query keys, deliberate staleTime, and mutation invalidation;
shared ApiEnvelope/Paginated wire types + unwrap() in lib/api/types.
- Money (integer-safe IRR/Toman) + Shamsi-date utils; toEnglishDigits helper.
- Shared composites, each tested: OtpInput, PhoneNumberField, StepperHeader,
StatusChip, PlaceholderScreen.
- i18n: seed nav/common/shell/patients in both locales; document namespace
conventions. Update client/CLAUDE.md Project Structure + fix ColorSchemeScript
doc drift. Add phase report, STATUS, and REQ-001 (envelope/casing/pagination).
Gate: npm run check + test:ci green (72 tests); build green with NEXT_PUBLIC_API_URL.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.8 KiB
6.8 KiB
Frontend Phase 0 — Foundations: app shells, design system & data/contract patterns — Report (2026-07-02)
What was built
Cleanup (3.1)
- Removed the
toastDemoi18n namespace (both locales) and the placeholder home page. - Deleted the two dead icons (
AppIcon/icons/CurrencyIcon.tsx,YellowPlanIcon.tsx). - Fixed
BottomBarto read the route viausePathname()(was the globallocation) and made it locale-aware (highlights the active tab, pushes locale-prefixed routes). - Audit note "AppLoading missing from the
@/componentsbarrel" — verified it is already exported (components/common/index.tsx); no change needed.
Actor shells + routing (3.2) — three role-scoped experiences under (private-routes), no layout
added above [locale]:
- Customer (family) —
(customer)route group (no URL segment) at/,/bookings,/patients,/wallet,/profile;CustomerLayout= slim TopBar + the 5-tabBottomBarfrom the wireframe. - Nurse —
/nurse,/nurse/verification,/nurse/visits;NurseLayouton the sharedTopBarAndSideBarLayoutengine. - Admin —
/admin,/admin/users,/admin/notifications;AdminLayout, persistent desktop sidebar. - Role model:
constants/roles.ts(AppRole), optionalUser.roles, anduseActorRole()(defaults tocustomeruntil the server seeds roles in f1-b2). Nav is built per shell fromuseTranslations('nav').
Data pattern + utils (3.3)
- Reference domain
services/patients/mirroringauth:types.ts(+ thePatientsApiseam interface),keys.ts(hierarchical factory),constants.ts(mock toggle + staleTime),apis/(clientApireal,mockApiin-memory,indexselects by config),hooks/(usePatientswithstaleTime,useAddPatientinvalidates the list), barrel exporting hooks only. - Shared wire types
lib/api/types.ts:ApiEnvelope<T>+unwrap(),Paginated<T>,PageParams. - Money/date utils in
@/utils:parseIrr/rialToToman/formatIrr/formatIrrToToman(integer-safe BigInt) andformatShamsiDate/formatShamsiDateTime(Intl Persian calendar — no date lib). PlustoEnglishDigits/digitsOnlyinutils/text.ts.
Shared composites (3.4) — each in src/components/<Name>/ with a co-located .test.tsx, composed
from MUI/App* primitives, i18n-agnostic (labels passed by caller):
OtpInput(auto-advance, backspace-to-previous, paste distribution, digit-normalizing, LTR-in-RTL),PhoneNumberField(Iranian mobile, normalizes Persian/Arabic digits, caps at 11,isIranianMobile),StepperHeader(MUI Stepper, RTL-aware),StatusChip(verified/pending/rejected/… off--bal-*tokens),PlaceholderScreen(empty-state used by every not-yet-built screen).- Nurse/result card and price-breakdown were deferred to their feature phases (per the phase's "your call").
i18n (3.5) — seeded nav, common, shell, patients in both en.json/fa.json (in sync,
RTL-first). Documented the future namespace conventions in client/CLAUDE.md.
What is now testable (and exactly how)
cd client && npm run dev→ openhttp://localhost:3000(redirects to/fa).- Customer shell: mobile 5-tab bottom nav (خانه/رزروها/بیماران/کیفپول/پروفایل); tapping switches routes and highlights the active tab.
- Nurse shell:
/fa/nurse— TopBar + sidebar (داشبورد/احراز هویت/ویزیتها). - Admin shell:
/fa/admin— persistent sidebar on desktop (نمای کلی/کاربران/اعلانها). - Switch locale to
/en→dirflips to LTR and all strings translate; dark-mode toggle still works.
- Reference data pattern:
/fa/patientsshows the mocked list (~400 ms latency), an add form (name + gender). Submitting adds the patient and the list updates without a refetch — open React Query Devtools to watch['patients','list',…]cache + the invalidation on mutation success. npm run check(type + lint) andnpm run test:ci(72 tests, 12 suites) both pass.npm run buildpasses whenNEXT_PUBLIC_API_URLis set (see Follow-ups).
What is mocked / waiting on a real service
- Patients domain — client-side mock.
services/patients/apis/mockApi.ts(patientsMockApi) implements thePatientsApiinterface (services/patients/types.ts) in memory. Selected byUSE_PATIENTS_MOCK = trueinservices/patients/constants.ts. The realclientApi.tsis written against/patients(GET list + POST create) and already unwraps theApiEnvelope. To make real: publish thepatientscontract + endpoints, setUSE_PATIENTS_MOCK = false— no hook/component change. (This is a frontend client-side mock, not a backend DI seam, so it is recorded here rather than in the backend-ownedmocks-registry.md.) - This is the template f1+ copy for any domain whose backend phase hasn't merged.
Contracts
- Produced: none (frontend consumes).
- Consumed:
dev/contracts/conventions/{api-conventions,money-and-types}.mdand the b0openapi/swagger.v1.json(onlypingendpoints exist yet). Types-from-contract step is wired for thepatientsreference (shapes mirror the intended wire;ApiEnvelope/Paginatedinlib/api/types.ts). - Request filed:
frontend/requests/for-backend.mdREQ-001 (confirm envelope unwrapping, wire casing, pagination payload shape).
Docs updated
client/CLAUDE.md: Project Structure tree (new route groups, actor layouts, shared composites,services/patients,lib/api/types.ts,constants/roles.ts, money/date utils); i18n namespaces + future-namespace conventions; a new services/{domain} reference pattern subsection (caching, mock seam, envelope, money/dates). Corrected theColorSchemeScriptdoc drift in the two structure lines that named it (it is neither exported from@/themenor rendered).
Follow-ups for later phases
- Envelope unwrapping (REQ-001):
clientFetch/serverFetchcurrently return the raw body, so domainclientApis callunwrap(). If the team prefers central unwrapping, that touches the auth plumbing — coordinate before changing. Wire casing observed is camelCase, not the snake_case api-conventions implies; confirm and update the convention doc. - Role guards: shells read
useActorRole()but do not yet guard cross-actor access (any authed user can open/nurse,/admin). Add real guards once roles land in f1-b2. - Login is username/password today; phone-OTP arrives in f1-b2 (use
OtpInput/PhoneNumberField). npm run buildneedsNEXT_PUBLIC_API_URL:@/configusesenvRequired, which throws at import. Dev works via the committed.env.development; a production build must supply the var (as it always would once any page imports the fetch layer). Not a code defect — an env expectation to note in CI.