# S1-FE — Unified visit shell (frontend), general specialty

Read `_COMMON.md` first. This is the highest-visibility UI of the platform — fidelity to the approved design matters more than speed.

## Spec (read fully, in order)
1. `../design-visit-shell.md` — esp. §1, §2.1–2.7 (card catalog, TS layout contract, resolution default→client→doctor→context, customise mode, flow, pick-don't-type §2.6, settings §2.7), §4–§8, §10–§12.
2. `../visit-shell-prototype.html` — the interactive prototype approved by the owner (Fable-designed). Open it in headless Chromium and screenshot the GENERAL specialty to see the target. Mirror its zones (app bar, sticky patient header with alerts, start rail, main card area in flow mode with numbered stations and «تم ✓», end rail live summary, footer with progress/print/finish), card anatomy, the shared «الطبقة المشتركة الموحّدة» picker bar, the compact pick-don't-type pickers (selected chips + «＋ اختيار / بحث», picker dialog with search, ★ favourites first, category grid, inline «＋ إضافة جديد»), and the customise-screen editor.
3. Existing FE to REUSE unchanged as cards: `features/clinic/encounter/vitals-panel`, `history-panel`, `diagnosis-panel`, `orders/lab-orders`, `orders/rad-orders`, `prescription`, `previous-visits`, `referral`, and the dynamic field renderer. Read `features/clinic/encounter/consultation.component.ts/.html` to learn how it loads the encounter, permissions, signing, finishing.
4. Headless screenshot helper: `node` + `require('/home/moonui3/public_html/moon-erp/node_modules/playwright')` with `executablePath:'/usr/bin/chromium-browser', args:['--no-sandbox']`.

## Backend contract (orchestrator is building it now — code against it)
- `GET /api/clinic/visit-layout?clinical_type=general` →
  `{ data: { visit_shell_enabled: bool, allow_doctor_override: bool, can_manage: bool, client: { layout: VisitLayout|null, fields: Record<code,{visible?,required?,input?}>, features: Record<string,bool> }, doctor: { layout: VisitLayout|null } } }`
- `PUT /api/clinic/visit-layout/client` body `{ clinical_type, layout: VisitLayout|null, fields?, features? }` (perm clinic.layout.manage) → same shape. `layout:null` = reset to default.
- `PUT /api/clinic/visit-layout/doctor` body `{ clinical_type, layout: VisitLayout|null }` → same shape; 403 when the clinic disallows overrides.
- `VisitLayout = { version: number; columns: string[]; flow: boolean; cards: { id: string; col: number; order: number; size: 'S'|'M'|'L'|'F'; collapsed?: boolean; props?: Record<string,unknown> }[] }`
- Lookup catalog (exists): `GET /api/clinic/clinical-lookups?lookup_key=&specialty=` → rows `{id, lookup_key, value_ar, value_en, parent_id, display_order, specialty, is_active, is_favorite}` (favourites first); `POST /api/clinic/clinical-lookups` `{lookup_key, value_ar, value_en?, parent_id?, favorite?}` (201, needs clinic.history.manage); `POST /api/clinic/clinical-lookups/{id}/favorite` → `{is_favorite}`.
- Auth header convention: the app's HTTP interceptor already sends `X-Authorization` — just use the existing api service pattern of the clinic feature services.

## Build
Folder `src/app/features/clinic/visit-shell/` (new):
- `visit-layout.model.ts` (types above + `CardDef`, `ClientProfile`), `card-catalog.ts` (shared cards for general: header (pinned), vitals, complaint, history, exam, diagnosis, lab, rad, rx, documents, previous visits, follow-up, notes, live summary, order rail), `resolve-layout.ts` (pure fn: default → client → doctor if allowed → context; each level REPLACES the previous; drop unknown/feature-locked cards, re-insert required, fix disallowed sizes; tag each card `from`), with a unit-testable shape.
- `visit-shell.component` (route target), `visit-card.component` (header: number/title/shared-vs-spec tag/source tag/«تم ✓»/collapse), `visit-layout-editor.component` (customise mode: ↑↓ ◀▶ size, collapse, hide (not required), add from catalog; «حفظ للعيادة» primary, «حفظ لي» only when allowed; «رجّع الافتراضي»), `visit-summary-rail.component`, `visit-finish-panel.component` (soft warnings then sign — reuse the consultation screen's sign/finish service calls).
- Shared picker: `src/app/shared/components/clinical-picker/` — `clinical-picker-box` (selected chips + ★ quick + button) and `clinical-picker-dialog` (search, favourites first, categories/grid via parent_id, multi/single, inline add, Enter/Esc, full-screen on mobile) on the lookup endpoints. Use it in the complaint and exam cards (lookup_keys `complaint`, `exam.finding`) — diagnosis/lab/rx keep their existing components for now.
- `visit-layout.service.ts` for the endpoints.
- Routing: in `features/clinic/clinic.routes.ts` the existing `encounter/:id` route stays; add `encounter/:id/shell` → shell. In `consultation.component.ts`, after the encounter loads, if the visit-layout says `visit_shell_enabled` AND the department's clinical type is `general`, navigate to the shell route (replaceUrl). Keep that edit to a few lines. Nutrition/dental never redirect in S1.
- Responsive per design §10 (tablet two columns, mobile one column, sticky summary), RTL, keyboard (Ctrl+Enter = done & next station, Alt+F = finish).
- i18n keys under `VISIT_SHELL.*` and `CLINICAL_PICKER.*` in both ar.json and en.json; bump `I18N_VERSION` in `src/app/app.config.ts` (memory: browser-cached i18n).

## Design consults
If something is ambiguous in the prototype, or you need a decision on spacing/visual hierarchy/a state not in the prototype, you may consult a Fable agent (`model: "fable"`) with a focused question + a screenshot path. Keep consults few and specific.

## Verify
`npx ng build --base-href /app/` green. Take screenshots of your built shell? — you can't log in to the live app reliably; instead write a tiny dev-harness? NO — just ensure build passes and report. The orchestrator deploys and does the live visual check + Fable review.

## Out of scope
BE files, prescription component internals (P1-FE), nutrition/dental layouts (S2), inpatient context (S3), deploy.
