# Frontend Phase 0 — Foundations: app shells, design system & data/contract patterns — Report (2026-07-02) ## What was built **Cleanup (3.1)** - Removed the `toastDemo` i18n namespace (both locales) and the placeholder home page. - Deleted the two dead icons (`AppIcon/icons/CurrencyIcon.tsx`, `YellowPlanIcon.tsx`). - Fixed `BottomBar` to read the route via `usePathname()` (was the global `location`) and made it locale-aware (highlights the active tab, pushes locale-prefixed routes). - Audit note "AppLoading missing from the `@/components` barrel" — 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-tab `BottomBar` from the wireframe. - **Nurse** — `/nurse`, `/nurse/verification`, `/nurse/visits`; `NurseLayout` on the shared `TopBarAndSideBarLayout` engine. - **Admin** — `/admin`, `/admin/users`, `/admin/notifications`; `AdminLayout`, persistent desktop sidebar. - Role model: `constants/roles.ts` (`AppRole`), optional `User.roles`, and `useActorRole()` (defaults to `customer` until the server seeds roles in f1-b2). Nav is built per shell from `useTranslations('nav')`. **Data pattern + utils (3.3)** - Reference domain `services/patients/` mirroring `auth`: `types.ts` (+ the `PatientsApi` seam interface), `keys.ts` (hierarchical factory), `constants.ts` (mock toggle + staleTime), `apis/` (`clientApi` real, `mockApi` in-memory, `index` selects by config), `hooks/` (`usePatients` with `staleTime`, `useAddPatient` invalidates the list), barrel exporting hooks only. - Shared wire types `lib/api/types.ts`: `ApiEnvelope` + `unwrap()`, `Paginated`, `PageParams`. - Money/date utils in `@/utils`: `parseIrr`/`rialToToman`/`formatIrr`/`formatIrrToToman` (integer-safe BigInt) and `formatShamsiDate`/`formatShamsiDateTime` (Intl Persian calendar — no date lib). Plus `toEnglishDigits`/`digitsOnly` in `utils/text.ts`. **Shared composites (3.4)** — each in `src/components//` 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) 1. `cd client && npm run dev` → open `http://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` → `dir` flips to LTR and all strings translate; dark-mode toggle still works. 2. **Reference data pattern:** `/fa/patients` shows 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. 3. `npm run check` (type + lint) and `npm run test:ci` (72 tests, 12 suites) both pass. `npm run build` passes when `NEXT_PUBLIC_API_URL` is set (see Follow-ups). ## What is mocked / waiting on a real service - **Patients domain — client-side mock.** `services/patients/apis/mockApi.ts` (`patientsMockApi`) implements the `PatientsApi` interface (`services/patients/types.ts`) in memory. Selected by `USE_PATIENTS_MOCK = true` in `services/patients/constants.ts`. The real `clientApi.ts` is written against `/patients` (GET list + POST create) and already unwraps the `ApiEnvelope`. **To make real:** publish the `patients` contract + endpoints, set `USE_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-owned `mocks-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}.md` and the b0 `openapi/swagger.v1.json` (only `ping` endpoints exist yet). Types-from-contract step is wired for the `patients` reference (shapes mirror the intended wire; `ApiEnvelope`/`Paginated` in `lib/api/types.ts`). - **Request filed:** `frontend/requests/for-backend.md` REQ-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 the `ColorSchemeScript` doc drift in the two structure lines that named it (it is neither exported from `@/theme` nor rendered). ## Follow-ups for later phases - **Envelope unwrapping (REQ-001):** `clientFetch`/`serverFetch` currently return the raw body, so domain `clientApi`s call `unwrap()`. 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 build` needs `NEXT_PUBLIC_API_URL`:** `@/config` uses `envRequired`, 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.