# Fable review — W1-FE-1 + W2-FS-3 on /app («شبه التحليل؟»)

Reviewer: Fable (design). Date: 2026-10-23. Review only — no code edited, no visit written.
Reviewed build: `/app` bundle `main-PUYCINYJ.js` (deployed 16:36, = FE `d765e482` = 10d02f96 + 5fc82df9 + i18n bump). The FE tree changed after that; this review is of `/app` only.
Screenshots: `knowledge-base/plans/visit-shell-completion/shots/fable-W1/` (names below). Visits: general 34, GYN 54 (24w pregnant) + 62 (58y urogyn), dental 37, nutrition 43, dental-inpatient 27 (day 13 of a stay), signed GYN 67. Widths 1600 / 1024 / 390.

**How insured + lab flags were checked.** No patient on `moonui3_dev_be` has an insurance contract or a released lab result, and no visit is insured, so B1/B2/B4 were checked by intercepting the two GETs in the browser (`patients/{id}/header` → `insurance` object + 5 flagged `last_labs`; `encounters/34` → `is_insured:true`). Client-side only, nothing reached the BE. **Every `mock-insured*.png` is synthetic data.** `mock-insured-1600*.png` = header-only pass (insurance string + lab tones; the visit itself was not insured there); `mock-insured2-*.png` = header + `is_insured:true` pass (the B4 evidence). The real BE contract is landed: `GET patients/20009/header` returns `insurance: null` and `last_labs: []`, and `PatientReadService::lastLabsList` sends `flag/unit/date` per row.

---

## 1. W1 / W2 items — what I could see

| Item | Verdict | Evidence |
|---|---|---|
| B1 insurance header («شركة · عقد · n%», «نقدي» for cash) | ✓ matches | `mock-insured2-1600.png` header reads «مصر للتأمين · عقد الشركات · 80%»; real visits read «نقدي» |
| B2 last results: red for H/L/A, solid red for HH/LL, green unflagged, unit + date tag | ✓ (mock) | `mock-insured-1600.png` rail: HbA1c ↑ red, K ↑↑ solid red, Hb ↓ red, INR / CBC green, date tag under each |
| B2 meds sentence under the drug name (from `summary.meds`) | ✓ | all visits: «Azithromycin / 500 mg — مرة يومياً — 3 أيام» |
| Finish panel hard/soft sections | soft ✓ · hard **code-only** | `general-1600-finish.png` shows «ملاحظات (لا تمنع التوقيع)»; no data path reaches a hard block (34 already has a coded primary dx) |
| B4 insured diagnosis forced: ✕ disabled, cannot hide | ✓ (mock) — **no visible «إلزامي» tag** | `mock-insured2-1600-dx-edit.png`: «✕ إخفاء» is `disabled`; head tags still «مشترك · طبقة 3 · افتراضي». See P1-1 |
| B4 re-insert when the layout lacks dx | not verifiable | dx is in every default layout on this host |
| B6 rail numbering = station numbers; non-stations carry the icon | ✓ | `general-1600.png` rail: 1–10 + «🕘 الخط الزمني»; GYN: «🪪 / 🤰 / 🏥 / 🏁 / 📅» unnumbered on head AND rail (spec P1-10 resolved by consistency) |
| D bug 1 GYN admitted (one discharge, no follow-ups) | not verifiable | no admitted GYN visit; check scripts pass per report |
| #46 context placeholders while customising | ✓ matches | `enc27-consult-edit.png`: MAR / round / notes / discharge rendered dimmed «سياق الإقامة — مش بيتحفظ في الشكل» after the live cards |
| #45 signer line | ✓ present, format off | `signed67-1600-tall.png`: «موقّعة من Program Owner · Oct 18, 2026, 10:35:00 AM». See P2-1 |
| Save pulse («حفظ تلقائي») | not verified | would need a write |
| A bugs (editor count, ↑↓ per tab, `dent_workspace` required) | ✓ | editor says «11 كارت» on general; dental jaw «✕ إخفاء» `disabled` (DOM, visit 37) |
| #76 redirect / #13a focus | not verified | no layout with `visit_shell_enabled=false`; focus needs a station run |
| W2 GYN antenatal pack (LMP ro + «افتح الحمل», GA, fundal, FHR, urine select) | ✓ renders | `gyn54-vitals-card.png`; LMP link exists but is truncated («من ملف الحمل …»). See P2-3 |
| W2 inpatient pack (RR, AVPU, pain, O₂, I/O, NEWS2) | ✓ renders | `enc27-vitals-more.png` (dental «المزيد» on the stay visit) |
| W2 nutrition pack (waist/hip/WHR + risk hint) | ✓ renders only when the card is added | `nutrition-43-vitals-preview.png` (customise-mode preview, cancelled). See P1-3 |
| W2 compact / readonly modes | **neither mode is set by any layout** — code-only | no `props.mode` anywhere in `visit-shell/`; signed 67 shows the *full* panel in its locked state («معتمد (قراءة فقط)» is the full panel's lock badge, not `readonly`) |
| Round card «داخل/خارج» column + 24h I/O line | column ✓ · 24h line not seen (no readings on 27) | `inpatient-1600-consult.png` |
| Console | 404 `GET /api/lis/investigations?…` on general + both GYN visits | see P1-4 |

---

## 2. Findings

### P0 — none that W1/W2 introduced.
Nothing in the two commits broke the shell; the deployed pieces do what their briefs say. The remaining gap vs «شبه التحليل» is the vitals card body (P1-2), which neither wave changed.

### P1 — visible mismatch vs the prototype

**P1-1 · Forced cards have no «إلزامي» marker.** Where: insured dx (`mock-insured2-1600-dx-edit.png`) and the dental jaw (`dental-37-jaw-edit.png`), edit mode. `visit-card.component.html` tags block (L33-43). What the prototype shows: a `.from.required` tag «إلزامي» on the head (spec §2.6, P0-1/P0-2) so the greyed ✕ has a reason on the card. Today the only cue is a `title` tooltip on the disabled button; the jaw (bare card, no head) has no cue at all. Fix: when `def().required` (incl. the insured override) render `<span class="tag req">إلزامي</span>` in the head tags, and for bare cards put the same tag inside `.etb` next to the disabled ✕. The dx case needs the shell to pass `required:true` into the def it already builds in `cardDefs()` — the data is there, only the tag is missing.

**P1-2 · Vitals card body is still the legacy tile panel; compact mode is shipped but unreachable.** Where: every visit (`general-1600.png`, `gyn54-vitals-card.png`, `enc27-vitals-more.png`). `encounter/vitals-panel/vitals-panel.component.html`. Prototype (01_general.png, 08_gyn.png, renderer L803-811): 6-col grid of small labelled inputs with unit suffix, specialty pack as a `.sec` row under the core 8, context pack as a third row — the station is ~140 px tall. Live: large bordered tiles with monospace digits (~330 px for 8 fields, ~560 px with the GYN pack + «آخر 5 زيارات» table), LMP wraps to two lines, big empty area right of the pack. W2 built `mode: full|compact|readonly` but nothing sets `props.mode`, so the compact row never shows and the collapsed-station summary / rail line for vitals stays empty (spec P1-7: prototype «84.2 كجم · BMI 31.7 · 135/85 · نبض 88»). Fix (two steps): (a) feed the `compact` chip row into the card's collapsed `summary()` and the rail `SummaryLine` for `vitals` — zero new UI, gives the mode a consumer; (b) in W3-FE-4's per-card ⚙ expose `mode` (full/compact) and make the flow-layout default `compact`-on-collapse. The tile→input restyle (shell font, unit suffix inside the input) is a separate pass on the panel SCSS.

**P1-3 · Nutrition pack has no surface in the nutrition visit.** Where: visit 43 (`nutrition-1600.png`, `nutrition-add-card.png`). `specialties/nutrition.cards.ts` default layout has no `vitals` card; WHR only appears if the doctor adds «العلامات الحيوية» from the catalog (and board layouts cannot save yet — «معاينة فقط»). Also the measurements table already shows «الوسط (سم)» — is that the same observation as `nut_waist`, or a second source of truth? Fix: either put `vitals` (mode `compact`) in the nutrition default board, or host waist/hip/WHR inside `nut_measure` (`nutrition-measure-card`) reading the same observation codes. Decide which column owns waist.

**P1-4 · `GET /api/lis/investigations?is_active=true&is_searchable=true&per_page=5000` → 404** on every general / GYN visit load (console). Where (likely): `features/clinic/services/clinic-lab-order.service.ts` — the lab card's legacy catalog call; five FE files reference that path, confirm the caller. Not W1's change, but live: the lab picker's catalog will not load on this host. Fix: point it at the clinic catalog endpoint (W2-FE-2 picker replaces this) or guard the call.

**P1-5 · Inpatient: two forms for the same observations on one screen.** Where: visit 27 الكشف tab (`inpatient-1600-consult.png`): the vitals card «المزيد» shows the inpatient pack (RR/AVPU/pain/O₂/I-O/NEWS2) and `inp_round` shows its own RR/O₂/I-O/pain/AVPU grid with its own «حفظ». Prototype (24_inpatient / §8): one vitals station carrying «باقة الإقامة — جولة تمريض» and the round card is the readings table. Fix: let `inp_round` embed `app-clinic-vitals-panel [mode]="'compact'"` (or `readonly` for doctors) and keep only the table + «جولة جديدة» in the round card — gives the modes a real consumer.

### P2 — polish

**P2-1 · Signed bar format and triplication.** `signed67-1600-tall.png`: «موقّعة من Program Owner · Oct 18, 2026, 10:35:00 AM» (English locale, seconds) vs prototype «موقّعة من د. هبة محمود · 21 أكتوبر 11:42»; plus three lock cues on one screen (hpill «موقّعة — إضافة ملحق فقط», pill «وقت التوقيع Oct 18, 2026», amber bar) vs the prototype's two. `visit-shell.component.html` L118-120: use `date:'d MMMM · HH:mm'` with the Arabic locale, drop the «وقت التوقيع» pill once the bar carries the time.

**P2-2 · English dates inside an Arabic rail/header.** «آخر زيارة Oct 22, 2026», rail visit dates, the new lab date tags «Oct 12, 2026», while the vitals card prints «22 أكتوبر 2026». Confirmed: no `registerLocaleData` / `LOCALE_ID` in `app.config.ts` or `main.ts`, so every `| date` pipe renders en-US under `lang=ar`. One fix in `app.config.ts` (`registerLocaleData(localeAr)` + `LOCALE_ID` from `lang`) aligns all of them; or a shared `visitDate` pipe.

**P2-3 · GYN LMP tile.** `gyn54-vitals-card.png`: the date «7 مايو 2026» wraps to two lines in the big mono face and the «افتح الحمل» link is ellipsised to «من ملف الحمل …», so the only navigation affordance is hidden. Render LMP as text size (not the numeric face) and put the link on its own line / as a small button.

**P2-4 · Antenatal fields on a non-OB GYN visit.** `gyn62-vitals-card.png` (58y, urogyn): LMP «—», GA «—», fundal, FHR, urine shown. Prototype's `vitalsPack` is unconditional too, so this is parity, not a regression — suggestion: show GA/fundal/FHR only with an open pregnancy (the service already knows), keep LMP + urine.

**P2-5 · Header still carries «💊 الأدوية الحالية» chips** (spec P1-5 carry-forward) and dental 37 shows «Ibuprofen · Ibuprofen» (two prescriptions, same drug). Prototype header = alerts only; meds live in the rail. At minimum dedupe by `drug_name` in `clinic-patient-header`.

**P2-6 · Finish panel copy.** Insured mock shows nothing about insurance when satisfied; prototype prints the positive line «المريض مؤمّن — التشخيص موجود ✓». Per-line «افتح المحطة» links (spec P2-16) still absent. Second button says «توقيع + طباعة ملخص الزيارة» vs prototype «توقيع + طباعة الروشتة والطلبات» — fine if intentional.

**P2-7 · Footer card count changes per tab on board layouts.** Dental 27: «لوحة · 1 كارت» on الأسنان, «11 كارت» on الكشف; edit bar says «8 كارت». Count the layout, not the visible tab.

**P2-8 · Vitals danger chip.** «⚠ 1 قيمة خطرة» floats as a separate pill above the tiles; prototype colours the field (`.bad`) and lists it in the finish panel (which live does: «1 قيمة حيوية في نطاق الخطر» ✓). Keep the red tile, drop the floating pill or move it to the card head.

**Carry-forwards from spec §C not touched by W1/W2 (still stand):** P1-6 tile panel (= P1-2 above), P1-7 empty collapsed summaries for history/exam/lab/rad/docs (general-1600: only complaint, dx, rx, follow-up have a line), P1-8 edit-bar wrap, P1-9 32 px tablet targets (`general-1024.png`), P1-10 GYN rail length (21 rows), P1-11 legacy lab/rad card, P2-12 app-bar pills only in edit mode.

---

## 3. Responsive notes (not W1 scope, recorded for W4-FE-5)
- 1024 (`general-1024.png`): layout holds; vitals tiles reflow 4+3+1; rail chips/buttons ~32 px.
- 390 (`general-390.png`, `general-390-drawer.png`, `gyn62-390.png`): header compacts correctly (no insurance/doctor), ☰ drawer carries print/cashier/customise/classic + rails, bottom «ملخص: —» bar only lists done stations (matches prototype). Vitals tiles 3-up are the tallest element on the phone (P1-2 again).

## 4. Verdict
**Ship-quality for what the two waves claimed; not yet «شبه التحليل» on the vitals station.** Every W1 shell fix I could reach on `/app` is present and behaves like the prototype (insurance string, lab tones, meds sentence, rail numbering, context placeholders, forced-card ✕, signer line, finish sections). The W2 packs render for GYN / inpatient / dental and the BE contract is landed. The owner will still see the old vitals tile panel on every visit, the packs make it taller rather than closer to the prototype, and the new compact mode has no consumer — that is the one P1 to schedule next (P1-2 + P1-5 together), plus the «إلزامي» tag (P1-1), the nutrition pack surface (P1-3) and the live 404 (P1-4). No blocker for merging W1/W2.

Unverified (needs data or a write): save pulse, GYN-admitted dedup, dx re-insert, hard finish block, 24h I/O line, #76, #13a.

Token used: `claude-visual-check-fableW1` (revoked at the end of this review).
