# W4-FE-5 report: shell chrome rest (FE)

Date: 2026-10-23 · Branch hazemdev3 · Not committed, not deployed · No BE changes, no migrations.

**Result:** `ng build --base-href /app/` is green. It was run last at 17:12, with W2-FE-2's uncommitted picker work in the tree. The pure checks pass (esbuild + node):
- `resolve-layout` 34/0
- `inpatient.context` 41/0
- `dental-layout` 16/0
- `nutrition-layout` 26/0
- **new** `w4-catalog.check.ts` 50/0

## What was done, by task

1. **Inpatient header (I8, #9).**
   - `InpatientStayService` now reads `GET clinic/encounters/{id}/stay` into a new `info` signal. The Hospital `admission` signal is kept unchanged, because the discharge and MAR cards still use it.
   - The chip reads `info` first. It shows «إقامة · جراحة رجالي · سرير 204 · اليوم 13/3» (new key `CTX.CHIP_DAY_OF`). It falls back to `admission`, then to the bare chip.
   - `status()`, `inBed()`, the discharge collapsed line and `inpatientContext()` (the cancelled check) also fall back to `info`. Users without `hospital.beds.view` now get the real status and bed.
   - The header has a new «المعالج: <attending>» kv. It shows only on a stay. «الطبيب» is still the encounter doctor.
   - `init(admissionId, encounterId)` now also takes the encounter id.
2. **«حساب الإقامة» (#35).**
   - `stay-bill` is loaded into `stay.bill`. Any error (403, 404) gives null.
   - When it is non-null, the end rail's orders section becomes «حساب الإقامة» (`VISIT_SHELL.RAIL.*`). It shows total, paid, deposit and balance (red when > 0) in the bill currency, the latest lines, «+n بنود تانية», and «فتح الحساب ↗». That link goes to `/clinic/hospital/admissions/{id}?tab=bill`, which is the same link the discharge card uses, and only shows when `can_open_bill`.
   - When it is null, today's service-order rail shows.
   - The bill re-reads after every order or Rx change.
3. **Server flow state (#20).**
   - New `visit-flow-state.service.ts`, provided by the shell.
   - On open, the localStorage cache paints first. Then `GET shell-state` loads, and the server wins: `done`, plus `active` if it is still an undone station; otherwise the first undone one.
   - If the server has nothing and the cache has «تم», the cache is pushed once.
   - «تم» is PUT at once. Station moves (Alt+↑/↓, opening a station) are debounced 800 ms and flushed on destroy.
   - Ids are filtered to the BE rule. A signed visit only writes the cache. 403 and 422 are silent, so the local cache stays.
4. **Collapsed and signed summaries (#13b, #46).**
   - `history`: the key conditions plus «حساسية X», taken from the header contract. This matches prototype line 823.
   - `previous`: «آخر زيارة 12 أكت · <dx> · n زيارات», or «أول زيارة». The visit-history read moved from the start rail to the shell, and the rail now takes `visits`/`loaded` inputs, so the line also works while the rail is folded or on a phone.
   - `documents`: «n مستندات · n مرفقات». The card counts the encounter's clinical documents (not voided) and its attachments. It re-counts on `collapseTick`, which is bumped only when the documents card collapses or is marked «تم».
   - `timeline`, `home_meds`, `team` and `procedures` already emitted their lines. New cards emit theirs too.
   - **Not done:** a «n positive answers» count for history. The history panel emits nothing, so the line uses conditions and allergies instead. On a signed visit it reads the live header, not the at-sign snapshot.
5. **Phone (#70) and tablet (#72).**
   - The phone strip is now a button. It shows «💵 فاتورة X» (the order's `patient_due`, read via `ClinicOrderService.getById`) or «باقي الإقامة X» (stay balance), then the live summary.
   - Tapping it opens the existing end drawer, restyled on phone as a bottom sheet (rounded top, max 82vh; written with a `:host` prefix so it wins on both the ≤720px viewport path and the `.phone` frame path — checked in the compiled CSS) with the summary plus orders/bill. The fold button reads «إغلاق».
   - New host class `vs-touch` (frame < 1180px). Under it, buttons are ≥ 44px:
     - shared atoms (`.btn`, `.btn.sm/.xs`, `.chip`) in `_clinical-ui.scss`;
     - header `.hbtn`, rail toggles, summary lines, tabs and the quick bar;
     - the card customise toolbar;
     - the tablet/narrow `.btn` rules, changed from `height:32px` to `min-height:44px`.
   - Desktop sizes are unchanged.
6. **Quick bar (#21).**
   - `quickPick(id)` jumps to the card, then uses `QUICK_TARGETS`: the first match wins, a button is clicked and an input is focused.
     - history: `app-history-lookup-picker button` (W2-FE-2), else the first input;
     - lab: `button.lpc-pick` (W2-FE-2's catalog dialog), else `input.lp-search__in`;
     - dx: `button.dx-pick` (W2-FE-2's diagnosis picker dialog), else `.dx-picker input`;
     - rx: `PrescriptionComponent.openSearch()`;
     - exam: the existing `openExamPicker()`.
   - **Hook for W2-FE-2:** `QUICK_TARGETS` at the top of `visit-shell.component.ts`. Add or adjust a selector there; nothing else needs to change.
7. **Sign + print (#43).**
   - The finish panel's «توقيع + طباعة ▾» opens a small menu: ملخص الزيارة / الروشتة / الطلبات. «الروشتة» and «الطلبات» are disabled when there is nothing to print.
   - The `sign` output is now `VisitSignPrint | null`.
   - After the sign, exactly one window opens:
     - summary: `printSummary`;
     - rx: the latest non-cancelled prescription via `ClinicPrescriptionService.print` (P5 engine);
     - orders: the lab slip via `ClinicOrderPrintService.print({… encounterId})` (P5 `lab_slip`), else the rad slip. When both exist, an info toast points to the footer.
   - The signed footer now also has «💊 الروشتة», «🧪 طلب التحاليل» and «🩻 طلب الأشعة» (each only when there is something to print).
   - Order names, the latest prescription and summaries now come from computed `VisitDataService.labNames/radNames/latestRx`.
8. **Follow-up (#69).**
   - The follow-up card has an inline form, shown with `clinic.followups.manage` on an open visit:
     - «متابعة بعد» chips (3/7/14/30/90 days) plus a date input;
     - «تعليمات للمريض»;
     - «حفظ المتابعة».
   - Saving POSTs `patients/{p}/follow-ups` with `{title_ar/en «متابعة بعد N يوم», due_on, window_days, clinical_type, template:{instructions, encounter_id}}`. The BE already accepts `template`/`window_days`, so no BE change was needed. The task list re-reads, and it now shows the instructions under the title.
   - **«احجز» books in place.** A booking dialog exists: the calendar embeds `app-create-visit [embedded]`. The shell opens it in a `p-dialog` via `@defer`, pre-filled with the patient, doctor, department and the chosen follow-up date (today if none).
     - `(booked)` closes the dialog and refreshes the tasks.
     - Without `clinic.appointment.create`, it falls back to the old `/clinic/booking` navigation.
     - No `returnTo` was needed, and `create-visit` is unchanged.
9. **Catalog cards (A #3-#6, B #54/#65/#66/#68).** All four are in `card-catalog.ts` as `kind: 'shared'`, not in any default, and permission-gated. All emit a collapsed line.

   | Card | Sizes | Layer | Content |
   |---|---|---|---|
   | `meds` | S/M | 3 | Current medicines from prescriptions: header contract `meds`, with the sentence and start date. Not the same as `home_meds`. |
   | `results` | S/M/L | 3 | Lab results by visit/date with flag colours, through the shared `app-lab-history-groups` and `GET patients/{p}/lab-history` (6 visits, read-only). Permission `clinic.encounters.view\|clinic.intents.view`. Summary puts flagged values first: «HbA1c 7.4 ↑». |
   | `photos` | S/M | 3 | The patient's image attachments via `ClinicAttachmentService`: thumbnails as auth blobs/object URLs (revoked), «＋ صورة» upload (`clinic.patients.update` + `clinic.media.upload`), «كل المرفقات» link. |
   | `intake` | S | 2 | Per layer (shared / specialty): status tag and what is missing, «أكمل» opens the ONE wizard on the right step, re-reads on `closed$`. |

   - **Deviation (photos):** the nutrition photos card reads `nutrition/patients/{p}/photos`, which is specific to that module. So the shared card uses the clinic patient attachments API.
10. **First-visit intake (#51).**
    - The wizard opens by itself when all of these hold:
      - the header has loaded;
      - `visit_count <= 1` (`PatientReadService` counts every non-cancelled encounter including the open one, so this means first visit);
      - the visit is not locked;
      - the user has `clinic.patients.view`;
      - `intake.enabled` is on and `shared.status === 'pending'` (nothing filled yet).
    - It opens once per visit per session (sessionStorage key `visit-shell:intake-auto:<id>`).
    - «تخطّي» is the wizard's own button, gated by `can_skip` (`clinic.intake.skip`).
    - ⚠ On dev, intake is enabled and many patients are `pending`, so first visits on dev will pop the wizard.
11. **Nutrition inpatient Rx (D lesser).** Done; W2-FE-6 had finished, and the orchestrator allowed these edits.
    - `SpecCardCtx.rxKind` is set by the shell (`'inpatient'` on a stay).
    - `nutrition.cards.ts ctxInputs` gets `rxKind`.
    - `nutrition-section-card` gets a `rxKind` input and passes it into `setCtx`.
    - `NutritionBoardCtx.rxKind` (optional; EMPTY_CTX null, so the equality check covers it) and `store.rxKind`.
    - `board-dialogs.component.ts`: `<app-clinic-prescription … [kind]="s.rxKind()">`.
    - The classic board passes nothing, so the kind is inferred as before.
12. **Inpatient ordering wording.** On a stay, the rail's orders/bill tag now always reads «على حساب الإقامة», whatever `ordering_mode` is. The lab/rad cards' own «ينفّذ فوراً/للاستقبال» tag still follows `encounter.ordering_mode`. It will read right once W1-BE-A2 forces intent. Until A2 lands, stay lab/rad lines still go to the stray order, so they are not on the stay bill (per BE-A).
13. **Intake wizard profile fields (W3-BE-D §2).**
    - `IntakeStep.profile_fields/profile_values` types (`IntakeProfileField`).
    - `ClinicIntakeService.saveProfile(patientId, clinicalType, values)` → `PUT …/intake/profile`. `clinical_type` is always sent.
    - The wizard renders them after the step's history fields, through `app-intake-field` (adapted shape, synthetic negative id; not intake-required, readonly when `!can_write`, unit shown).
    - They use their own `profileDrafts` and 700 ms timers, separate from the history-question path. Each save sends only its own key and applies the refreshed payload. `done()` flushes them.
    - The «NO_FIELDS» branch now also checks profile fields. This matters on dev, where the nutrition specialty step has 0 questions and 3 profile fields.

## Orchestrator addendum
- The `.main.nut-grid` block was added to `visit-shell.component.scss`. The empty-section rule is written `.main.nut-grid ::ng-deep app-visit-card:not(.ed):has(.nut-empty)`. The section host is created by `ngComponentOutlet` and has no `_ngcontent` attribute from this view, so without `::ng-deep` the `:has()` selector could never match. I did not check it visually.

## Fable §C fixes that fall in my files
- P1-5: the header is alerts only (`[showMeds]="false"` on the shell header's alert strip). Meds stay in the start rail and the new `meds` card.
- P1-9: 44px touch targets (task 5).
- P1-7: collapsed summaries (task 4).
- P1-10: in flow, the live-summary rail lists every station, plus a non-station card only when it has a line.
- P2-12: the layout pill (`تسلسل محطات · عمود واحد`) and an encounter-kind pill (`عيادة خارجية` / `استشارة عن بُعد`; inpatient and ER keep the context chip) now show always. They are hidden on phone.
- **Not done:** P1-10 GYN defaults. Collapsing team/discharge/operation/gyn_followup on general GYN visits lives in `features/gyn/gyn-shell-cards.ts`, which is not my file; the change there is to set `collapsed: true` on those cards.
- **Not done:** P2-16, per-line «افتح المحطة» in the finish panel.

## Files
**New:**
- `visit-shell/visit-flow-state.service.ts`
- `visit-shell/cards/visit-results-card.component.ts`
- `visit-shell/cards/visit-meds-card.component.ts`
- `visit-shell/cards/visit-photos-card.component.ts`
- `visit-shell/cards/visit-intake-card.component.ts`
- `visit-shell/w4-catalog.check.ts`

**Edited (mine):**
- `visit-shell.component.{ts,html,scss}`
- `visit-summary-rail.component.ts`, `visit-rail.scss`
- `visit-start-rail.component.ts`
- `visit-header.component.ts`
- `visit-finish-panel.component.ts`
- `visit-data.service.ts`
- `visit-layout.model.ts` (SpecCardCtx.rxKind)
- `card-catalog.ts`
- `contexts/inpatient-stay.service.ts`
- `cards/visit-followup-card.component.ts`, `cards/visit-documents-card.component.ts`
- `visit-card.component.scss` (one line: toolbar 44px)
- `intake/intake-wizard.component.ts`, `services/clinic-intake.service.ts`

**Shared files (minimal edits):**
- `shared/components/clinical-picker/_clinical-ui.scss`: the touch block becomes 44px and also matches `.vs-touch`. It applies only inside the shell frame.
- `clinic/clinical/clinical-entities.models.ts`: optional `template` on `FollowUpTask`; `window_days`/`template` on `ManualFollowUpPayload`.
- `clinic/clinical/followup-tasks.component.ts`: one line, showing the instructions under the title.
- `nutrition/visit/nutrition-board.store.ts`, `nutrition/visit/cards/board-dialogs.component.ts`, `visit-shell/specialties/nutrition.cards.ts`, `visit-shell/specialties/nutrition-section-card.component.ts`: rxKind (task 11).

**i18n:** added to both `ar.json` and `en.json`, and `I18N_VERSION` was not bumped:
- `VISIT_SHELL.CTX.CHIP_DAY_OF`
- `HDR.ATTENDING`
- `RAIL.{STAY_BILL, ON_STAY, TOTAL, PAID, DEPOSIT, BALANCE, NO_STAY_LINES, MORE_LINES, OPEN_BILL}`
- `SUM.{ALLERGY, LAST_VISIT, N_VISITS, FIRST_VISIT, STAY_BALANCE, BILL}`
- `BAR.{KIND_OUTPATIENT, KIND_REMOTE}`
- `FINISH.{SIGN_PRINT_MENU, PRINT_SUMMARY, PRINT_RX, PRINT_ORDERS, RAD_FROM_FOOTER}`
- `FOOT.{PRINT_LAB, PRINT_RAD}`
- `CARD.{MEDS, RESULTS, PHOTOS, INTAKE}`
- `RESULTS.EMPTY`
- `MEDS.EMPTY`
- `PHOTOS.*`
- `INTAKE.*`
- `DOCS.{N_DOCS, N_FILES}`
- `FOLLOWUP.{AFTER, N_DAYS, DATE, IN_N, INSTRUCTIONS, INSTRUCTIONS_PH, SAVE, SAVED}`

## Verified live (dev, read-only GETs)
I used a temporary token named `w4fe5-check-20261023` and deleted only that name afterwards (1 row).
- `stay` for encounter 22: ward «جراحة رجالي», bed 204, day_n 13, expected 3, attending «دكتور / حسام غازي».
- `stay-bill`: EGP, total 1800, deposit 1200, 1 line, `can_open_bill` true.
- `shell-state`: empty.
- `intake?clinical_type=nutrition` for patient 20015: the specialty step has 0 fields and 3 `profile_fields` with values lose/72/light.

All shapes match the FE types.

## Not verified
- Nothing was checked in a browser. There are no screenshots, the build is not deployed, and I made no visual check of the bottom sheet, 44px, the nut-grid `:has` rule or the print menu position.
- No shell-state PUT or follow-up POST was made on dev, to avoid writes on real visits.
- Popup blocking after the sign round-trip is untested. With `clinic.print.engine = pdf`, the print services `await engine()` before `window.open`, which may be blocked (pre-existing; `printSummary` already works this way). The signed-footer buttons are the fallback.
- The embedded booking form inside the shell is untested live. It is the calendar's embed pattern (`[embedded]`, `[prefill]`, `(booked)`). The build confirms `@defer` keeps it out of the shell chunk (create-visit is its own 164 kB chunk; the shell chunk is 309 kB).
- The finish-panel print menu closes on pick or Esc only (no outside-click close).
- `stay.info` is a one-shot read: `setAdmission()` (discharge-card writes) updates `admission` only. Status and the discharge line prefer `admission`, so the shell stays correct.

## Notes for the orchestrator
- `visit-shell.component.ts` is 1263 lines (it was 1025). New logic went into new files where it was cheap (flow-state service, cards, data computeds). Splitting further, for example print, booking and intake helpers into a service, would be a good FIN refactor.
- i18n (`ar/en.json`), `_clinical-ui.scss` and `clinical-entities.models.ts` were edited while W2-FE-2 was live. Re-verify my keys (`CHIP_DAY_OF`, `STAY_BILL`, `SIGN_PRINT_MENU` …) and the `.vs-touch` rule after FE-2 lands (all present at 17:12).
- W2-FE-2 was writing files at the same time (diagnosis-panel, lab/rad orders, the clinical-picker files). I did not touch them. I only read their new trigger classes for `QUICK_TARGETS`.

## Proposed CHANGELOG `[Unreleased]` bullets
- AR: «شاشة الزيارة: شريحة الإقامة بتبين الجناح والسرير واليوم من المتوقع (مثلاً اليوم 2/4) والطبيب المعالج لأي حد يفتح الزيارة، وكارت "حساب الإقامة" (الإجمالي/المدفوع/التأمين/الباقي وآخر البنود) للي عنده صلاحية الحسابات.» / EN: "Visit screen: the stay chip shows ward, bed and day of expected stay plus the attending doctor for anyone who opens the visit, and a "Stay bill" card (total/paid/deposit/balance, latest lines) for billing users."
- AR: «خطوات الزيارة (تم/الحالية) بتتحفظ على الزيارة نفسها — تمريض وطبيب على أجهزة مختلفة شايفين نفس الحالة.» / EN: "Visit steps (done/current) are saved on the visit, so the nurse and the doctor see the same state on different PCs."
- AR: «الكروت المطوية والزيارة الموقّعة بتعرض سطر ملخص لكل كارت (التاريخ المرضي، المستندات، الزيارات السابقة…)، وأزرار الاختيار السريع بتفتح الاختيار على طول، و"توقيع + طباعة" بيخيّرك: ملخص الزيارة / الروشتة / الطلبات.» / EN: "Collapsed cards and signed visits show a one-line summary for every card, the quick bar opens each picker directly, and "Sign + print" lets you choose visit summary / prescription / orders."
- AR: «المتابعة: "بعد N أيام" أو تاريخ + تعليمات بتتحفظ كمهمة متابعة، و"احجز" بيفتح الحجز جوه الزيارة.» / EN: "Follow-up: "in N days" or a date plus instructions saved as a follow-up task, and "Book" opens the booking form inside the visit."
- AR: «كروت جديدة تقدر تضيفها من تخصيص الشاشة: الأدوية الحالية، النتائج بالتاريخ، الصور والمرفقات، حالة الاستقبال الأول؛ والاستقبال الأول بيفتح لوحده في أول زيارة.» / EN: "New addable cards: current medicines, results by date, photos & attachments, first-visit intake status; the intake wizard opens by itself on a first visit."
- AR: «الموبايل: شريط الملخص بيعرض الفاتورة ولما تدوس عليه بيفتح الملخص والطلبات/الحساب؛ أزرار التابلت بقت 44px.» / EN: "Phone: the summary strip shows the bill and opens the summary + orders/bill sheet; tablet buttons are 44px."
- AR: «الاستقبال الأول للتغذية: الهدف والوزن المستهدف ومستوى النشاط.» / EN: "Nutrition intake asks goal, goal weight and activity level."
