# S3 — the inpatient context in the visit shell: MAR, nursing stations, NEWS2 round, discharge instead of follow-up

Read `_COMMON.md` first. FE only (no BE; if you find you need a BE change, STOP and report it — use `2026_11_03_270050`–`270099` only if the orchestrator approves a migration). One WP.

**Depends on:** S1 ✅, **P2 ✅ (P2a + P2b)** — the MAR on `prescription_items`, `prescription-items/{item}/administer|stop`, the prescription component `[kind]` input, discharge Rx; S2a ✅ (the shell's `CardDef.component/inputs/bare/permission` extension points and `VisitShellBus` — S3 uses them instead of growing the shell `@switch`). Soft: D4 ✅ (rounds on observations + NEWS2 from `ComputedRegistry`; without it the round card posts to the same Hospital endpoint and reads `vitalsList` — works either way), P5 ✅ (discharge summary document — without it the discharge card prints the Rx and the admission summary text only), D2c ✅ (shared header with alerts in the inpatient file).

## Spec (read before coding)
- `../design-visit-shell.md` §2.1 «**سياق** (الإقامة): MAR، التمريض/الجولات، الخروج», §2.2 `VisitContext`, §2.3 step 4 «السياق (الإقامة) يضيف كروته», §4 app bar shows `encounter.kind`, §5 card kinds (`ctx`), §9 row **إقامة** («سياق فوق أي layout: MAR · التمريض · الخروج، الروشتة `kind=inpatient`»; disappears: `admissions.diagnosis` text, `hospital_med_orders` text), §11 «إقامة: `app-nursing-panel`», §12 step 4.
- `../design.md` §5 «**المستشفى = سياق مش تخصص**» (the treating department keeps its specialty; `encounters.kind=inpatient` opens MAR/nursing/discharge next to the same Rx/lab/rad), §4.4 (discharge summary is a document).
- `../master-plan.md` row S3 («فتح زيارة الإقامة من الهيكل نفسه بمحطات التمريض»).
- `../design-data-layers.md` §3.3 row `hospital_vital_rounds` (rounds = observation sets, NEWS2 computed).

## Code reality (verified 2026-10-22 — re-read; S1/P2/D4 change these)
- **Shell** (`src/app/features/clinic/visit-shell/`): `VisitContext { key: 'clinic'|'inpatient'; addCards?: {id, size}[]; replace?: Record<string,string>; cardProps?: Record<string, Record<string,unknown>> }` exists and `resolveLayout` applies it (replace ids → add cards → merge `cardProps`, cards tagged `from:'context'`, `toPersistedLayout()` strips them) — **but the shell never passes a context** (`resolved()` ~ts L152-166). `CARD_CATALOG` has no `ctx` cards; `catalogFor()` excludes `ctx` from «add card». Rx card today: `<app-clinic-prescription [encounterId] [patientId] [locked]>` (html ~L232). Follow-up card `app-visit-followup-card` (`[windowDays] [signedAt] (book) (referral)`). Finish = `app-visit-finish-panel` → `encounters.sign(id)` (ts ~L562).
- **FE `Encounter` model** (`features/clinic/services/clinic-encounter.service.ts`) has **no `kind` / `admission_id`** — BE `EncounterResource` exposes both since AF2 (`encounters.kind` outpatient|inpatient|day_case|remote|emergency, `admission_id`).
- **Redirect** `consultation.component.ts` L439-446: general only (S2 extends it to nutrition/dental). The admission file links to `/clinic/encounter/{encounter_id}` (admission-file html L58).
- **Hospital FE** (`src/app/features/hospital/`): `admissions/admission-file.component.*` (tabs `bill|nursing|rs|surgery|stays|money|log|discharge`, discharge-order dialog html L267-279 → `act('discharge-order', {discharge_type, discharge_summary, discharge_instructions, follow_up_on})` ts L210, billing discharge `doDischarge()` ts L254), `nursing/nursing-panel.component.{ts,html}` (`app-nursing-panel [admissionId] [inBed]` — ONE component with four blocks: MAR html L4-60, vitals round + NEWS badge L61-87 (`newsClass` ≥7 red, ≥5 amber), notes + equipment L88-116), `nursing/station.component.ts` (`hospital/nursing` route), `hospital.service.ts` L177-189 (`nursing(id, date)`, `medOrder`, `stopMed`, `administer`, `vitals`, `nursingNote` + P2b's `administerItem`/`stopItem`). Permissions used there: canOrder = `hospital.clinical.record|hospital.nursing.record`, canNurse = `hospital.nursing.record|hospital.clinical.record|hospital.admissions.manage`.
- **BE truth for discharge:** `AdmissionService::orderDischarge` (status `admitted` → `discharge_ordered`, writes type/summary/instructions/follow_up_on) and `discharge()` (billing; closes beds, P2 completes active inpatient items, then `EncounterService::markDone`). `Encounter::assertEditable()` only blocks `signed`/`cancelled` — a `done` admission encounter is still editable, so the doctor can still add the discharge Rx after the billing discharge until signing.
- `Admission::IN_BED = [admitted, discharge_ordered]`; requested admissions have no encounter.

## Decisions already made (do not re-litigate)
1. **Hospital = context, not a specialty.** The layout is the treating department's specialty layout (general/nutrition/dental/gyn…) resolved as usual; when `encounter.kind ∈ {inpatient, day_case}` the shell passes the **inpatient context** built by one pure function `visit-shell/contexts/inpatient.context.ts` → `inpatientContext(encounter, admission, perms): VisitContext`:
   - `addCards`: `inp_mar` (L), `inp_round` (M), `inp_nursing_notes` (M), `inp_discharge` (M) — after the specialty's own cards, in that order; `day_case` gets `inp_mar` + `inp_round` + `inp_discharge` only.
   - `replace`: `{ followup: 'inp_discharge' }` (discharge instead of follow-up; when the layout has no `followup`, `inp_discharge` is added).
   - `cardProps`: `{ rx: { kind: 'inpatient' } }` — the shell's `rx` case passes `[kind]="c.props?.['kind'] ?? 'outpatient'"` to `app-clinic-prescription` (one-attribute edit in the shell html).
   - `emergency` gets no inpatient cards (D4's triage card is separate — if D4 reported a `triage` card def, add it to an `emergency` context in the same file).
2. **Nursing stations = flow.** In the inpatient context the context cards are **stations** (`station: true`), so in a `flow:true` layout the nurse walks MAR → round → notes with «تم ✓» like any station; in a board layout they are cards. The shell's numbering already handles context cards (verify `stationIndex()` counts them).
3. **Context cards are `ctx` + `permission`-gated** (S2a's `permission` field): `inp_mar` / `inp_round` / `inp_nursing_notes` need `hospital.nursing.record|hospital.clinical.record|hospital.admissions.manage` (any-of — extend the permission check to accept `a|b|c` the way the routes do), `inp_discharge` needs `hospital.clinical.record|hospital.admissions.manage`. A nurse opening the shell sees only what she may use; the doctor sees all.
4. **No fork of the hospital UI:** split `app-nursing-panel` into three standalone components inside `features/hospital/nursing/` — `app-mar-board` (MAR block, inputs `admissionId`, `inBed`, `date?`; outputs `changed`), `app-vital-round` (round form + NEWS badge + trend, inputs `admissionId`, `inBed`), `app-nursing-notes` (notes + equipment, inputs `admissionId`, `inBed`) — and make `app-nursing-panel` render the three (so the admission file's nursing tab looks the same). The shell cards host the three components through thin wrappers under `visit-shell/contexts/inpatient/` (bare: false — they get the normal card chrome; the cards' summaries: MAR = «X due · Y missed», round = «NEWS n · {time}», notes = last note excerpt).
5. **MAR new orders** in the shell come from the `rx` card (`kind=inpatient`) — the MAR card has **no** free-text order form when `mar_on_prescriptions` is on (P2); it only gives/holds/refuses/stops doses and shows times. When the flag is off it keeps the legacy form (read the flag from the hospital settings meta the admission file uses).
6. **Discharge card** (`inp_discharge`): replaces follow-up. Shows the admission status; «أمر خروج» (when `admitted`) opens the same fields as the admission file's discharge-order dialog (type, summary, instructions, follow-up date) + an embedded `app-clinic-prescription [kind]="'discharge'"`; when `discharge_ordered`: shows the order, «تراجع» (undo — same endpoint the admission file uses), print discharge Rx (P1 pdf) and, if P5 ✅, «ملخص الخروج» opening P5's document dialog with `docType='discharge_summary'`. **Billing discharge stays in the admission file** (cashier/accounts) — the card links to `/clinic/hospital/admissions/{id}?tab=bill`.
7. **Finish in the inpatient context ≠ sign.** The footer «إنهاء» for an inpatient encounter opens the discharge card instead of signing while the admission is `admitted`/`discharge_ordered`; signing the admission encounter is allowed only after `discharged` (the existing sign call — unchanged). Daily doctor notes go through the shell's `notes` card (the encounter stays open for the whole stay — one encounter per admission).
8. **Entry:** the admission file's «فتح ملف الزيارة» link points to `/clinic/encounter/{encounter_id}/shell` when `visit_shell_enabled` (same visit-layout GET the consultation redirect uses), else the classic route. The consultation redirect also forwards inpatient encounters of any supported type to the shell (one condition added next to S2's).
10. **No layout saving inside a context.** `toPersistedLayout()` strips `from:'context'` cards, and `replace:{followup:'inp_discharge'}` turns the follow-up slot into a context card — saving while an inpatient context is active would silently delete `followup` from the clinic's layout. While a context is active, customise mode saves the **pre-context** resolution (resolve twice: once without context for the editor's save payload) or disables «حفظ» with a hint «خصّص من زيارة عيادة»; pick the first if simple, test it in the check file.
9. **App bar** shows the context: `encounter.kind` chip «إقامة · سرير {bed}» (bed/ward from the admission payload `GET hospital/admissions/{id}` the shell fetches once when `admission_id` is set) — reuse the existing app-bar slot that shows the layout source.

## Shared-file rules
- **S3 owns while it runs:** `visit-shell/contexts/**` (new), the shell lines that pass `context` to `resolveLayout`, the `rx` `[kind]` attribute, the inpatient finish branch, the app-bar context chip; `features/hospital/nursing/**` (split), `features/hospital/admissions/admission-file.component.*` (entry link only); the redirect condition in `consultation.component.ts` (one condition); the FE `Encounter` interface (`kind?`, `admission_id?` — additive) in `clinic-encounter.service.ts`.
- **Do NOT edit:** BE anything, `prescription/**` internals (P2b already added `[kind]`; if something is missing, report), nutrition/dental cards (S2), `card-catalog.ts` beyond registering the `ctx` defs via a spread from `contexts/inpatient.cards.ts` (same pattern as S2's specialty files), `clinic.routes.ts`.
- FE i18n targeted keys under `VISIT_SHELL.CTX.*` / `HOSPITAL.NURSE.*`; no `I18N_VERSION` bump.
- **Fable consult (mandatory, ONE):** take two screenshots with the headless helper (`require('/home/moonui3/public_html/moon-erp/node_modules/playwright')`, `executablePath:'/usr/bin/chromium-browser', args:['--no-sandbox']`) — the prototype's inpatient view in `../visit-shell-prototype.html` (switch `encounter.kind` to inpatient if the prototype offers it) and today's admission-file nursing tab — and ask Fable once: the MAR card at L size (time-slot grid vs list), the NEWS2 round card (form + trend + colour), and the discharge card states (admitted → discharge ordered → discharged). Apply; don't iterate.

## Do
1. FE `Encounter` interface + `kind`, `admission_id`; the shell loads the admission (Decision 9) when `admission_id` is set.
2. `contexts/inpatient.cards.ts` (the four `ctx` `CardDef`s with `component`/`inputs`/`permission`/`station`, sizes `inp_mar ['M','L','F']`, others `['S','M','L']`) + `contexts/inpatient.context.ts` (Decision 1, pure, with a `contexts/inpatient.context.check.ts` assertion file runnable with `npx tsx` if available).
3. Shell: pass `context` (Decision 1), `rx` kind prop, inpatient finish branch (Decision 7), app-bar chip (Decision 9), any-of permission check (Decision 3).
4. Split `app-nursing-panel` (Decision 4) — the admission file's nursing tab must render identically: screenshot it before and after with the same helper at 1440×900 (element `app-nursing-panel`), ≤0.2 % pixel diff; put both PNGs + the number in the report.
5. Card wrappers (MAR / round / notes / discharge) with summaries (Decision 4) that emit through S2a's `VisitShellBus.refresh('rx')` when a dose is given or an Rx item stopped (so the rx card reloads) and refresh the MAR after the rx card saves an inpatient item (subscribe to the bus).
6. Admission-file entry link + consultation redirect condition (Decision 8).
7. Responsive: on tablet the MAR card goes full width; on mobile one column with the slot grid scrolling horizontally (`dir=ltr` for times).
8. `npx ng build --base-href /app/` green.

## Verify
Build green; the two `.check.ts` files pass (general layout + inpatient → MAR/round/notes/discharge appended, follow-up replaced, rx kind inpatient; day_case without notes; emergency unchanged; nurse permission set drops `inp_discharge`); the nursing-tab identity screenshot pair. The orchestrator deploys and runs the owner scenario: admit a patient → open the visit from the admission file → shell shows the specialty cards + MAR/round/notes as stations → order a drug from the rx card → it appears in the MAR → give a dose → round with NEWS2 → «أمر خروج» with a discharge Rx.

## Out of scope
BE, the nursing station screen (`hospital/nursing`), bed board, billing/discharge accounting, surgery file, nutrition/dental/gyn cards, removing the admission file's tabs.

## Report
Files, build output, check-file output, identity screenshot numbers, Fable outcome, i18n keys, any BE gap found (e.g. a missing field in the admission payload), anything not done.
