مراجعة قشرة الزيارة مقابل النموذج المعتمد

Visit-shell prototype vs implementation · 2026-10-23 · moonui3 · مراجعة للكود بس، من غير أي تعديل + صور من الشاشة الحية على /app · المرجع: knowledge-base/plans/clinical-platform/visit-shell-prototype.html

الحكم: الهيكل الأساسي اتنفذ صح، لكن مش «كله».

قشرة الزيارة نفسها مطابقة للنموذج: الهيدر والتنبيهات، والمحطات بالكيبورد، والملخص الحي، والطلبات والفاتورة، والتوقيع والتحذيرات، والكروت المشتركة، والنسا، والأسنان، وسياق الإقامة، ومحرك ترتيب الشكل (افتراضي ← عميل ← دكتور ← سياق). الناقص الأكبر شاشة «إعدادات الشكل» بتاعة العميل (تعميم Program Setting): مش موجودة كواجهة. وفيه 6 أخطاء حقيقية لازم تتصلح قبل أي عميل، منها اسم التأمين اللي بيظهر دايماً «نقدي».

132
✅ مطابق للنموذج
57
⚠ متنفذ جزئياً / بشكل مختلف
20
❌ ناقص
10
📝 مؤجل بقرار معروف

219 بند اتراجعوا على 4 أجزاء. كل بند عليه رقم السطر في الكود (التفاصيل الكاملة في الملحق تحت). بنود الـ ⚠ أغلبها فروق صغيرة أو قرار تصميم، مش أعطال.

1. الشاشة الحية جنب النموذج

النموذج — عيادة عامة
النموذج — زيارة عامة (محطات)
الحي — كريم، زيارة 34
الحي — كريم (زيارة 34): نفس الترتيب: سجل يمين، ومحطات في النص، وملخص حي وأمر زيارة شمال، وشريط تحت فيه «إنهاء الزيارة Alt+F». مطابق
النموذج — نسا
النموذج — نسا وتوليد
الحي — دينا حامل 24 أسبوع
الحي — دينا (زيارة 54): التصنيفات والـ modules والحلقة العلاجية وعمر الحمل وموعد الولادة في الهيدر. فرق خانات LMP / عمر الحمل / نبض الجنين مش جوه كارت العلامات الحيوية زي النموذج، دي في كروت الحمل.
إنهاء الزيارة
إنهاء الزيارة: تحذيرات ناعمة + «توقيع وإقفال» + «توقيع + طباعة ملخص الزيارة». مطابق
تخصيص الشاشة
«✎ تخصيص الشاشة»: تحريك، وحجم، وطي، وإخفاء، وإضافة كارت، وحفظ للعيادة أو لي. مطابق
النموذج — إعدادات الشكل
النموذج — «⚙ إعدادات الشكل» (إظهار وإخفاء الحقول، Select⟷Text، الكتالوج). مش موجودة في البرنامج
أسنان
الأسنان (يوسف، زيارة 37): الفكين، FDI/Universal، دائم/لبني، الألوان، الخطة، الإجراءات بالأسعار. مطابق
إقامة
الإقامة (وليد، زيارة 27): شريحة «إقامة · جراحة رجالي · سرير 206-A · اليوم 13»، وكروت MAR والجولة/NEWS2 وملاحظات التمريض والخروج موجودة في تبويب الكشف. ناقص الطبيب المعالج وحساب الإقامة.
موبايل
الموبايل: كروت عمود واحد + شريط «ملخص» + «إنهاء الزيارة». ناقص الطلبات والفاتورة مش ظاهرة على الموبايل.
تغذية
التغذية (ريهام، زيارة 43): لوحة الدكتورة زي ما هي + «أمر الزيارة» (اتصلح امبارح). تقسيمها لكروت مؤجل

2. الأخطاء الحقيقية (متنفذة بس بتغلط)

أصغر من كده: ترقيم الملخص الحي مش زي ترقيم المحطات، كارت فك الأسنان مش «إلزامي»، عداد الكروت في وضع التخصيص بيعد التبويب المفتوح بس، مفتاح Select⟷Text في الفحص متربط بـ id من قاعدة البيانات بدل الكود (مش هينفع يتنقل من عميل لعميل)، وإضافة قيمة جديدة مش بتتحقق من parent_id ولا بتسجل التخصص.

3. الناقص (❌)

4. متنفذ جزئياً — أهم الفروق (⚠)

5. مؤجل بقرار معروف (📝)

6. الشغل المقترح (من غير ما أبدأ فيه)

WPالمحتوىالحجم
R1 أخطاءاسم التأمين (مشترك للهيدرين)، وعلامات النتائج + التاريخ والوحدة، وحفظ الملاحظات قبل التوقيع والخروج، وكروت الخروج المكررة في النسا والإقامة، والترقيم، وكارت الفك إلزامي، وعداد التخصيصصغير · FE+BE
R2 الكتالوجseed للشكاوى العامة + بنود الفحص بالمناطق، ومفتاح Select⟷Text بالكود بدل id، والتحقق من parent_id وتسجيل التخصص عند الإضافة، وتوحيد الكاشصغير–متوسط · BE
R3 إعدادات الشكلشاشة العميل «إعدادات الشكل»: قواعد الحقول (إظهار/إلزام/Select⟷Text)، والعناصر الـ 8 (وتوصيل الـ 6 الناقصين)، ومزايا النسا، ومفتاح القشرة لكل تخصص، وسماح الدكتورمتوسط–كبير · FE (+BE بسيط)
R4 العلامات الحيويةكارت العلامات يعرض باقة التخصص والسياق (الخصر/الورك/WHR، وقياسات الحمل، والتنفس/الوعي/الألم/O₂) + خانة سوائل داخل/خارج في الجولةمتوسط · FE+BE
R5 الإقامةالطبيب المعالج ومدة الإقامة في الهيدر، و«حساب الإقامة» مكان الفاتورة، وتنبيه Rh سالبمتوسط
R6 الاختيارالتشخيص بمفضلة ★ ومجموعات، وشاشة إدارة الكتالوج، وملخص سطر للكروت المطوية، وحالة المحطات على السيرفر، والفاتورة على الموبايل، وأزرار 44pxمتوسط–كبير

قرارات محتاجة منك

ملحق — الجداول الكاملة (219 بند بأرقام السطور)

Full per-item verdict tables (English, with file:line evidence), one per review slice.

A — محرّك الشكل والتخصيص (Layout engine)

Slice A — layout engine, catalog, registry, persistence, edit mode, client settings

FE root = /home/moonui3/public_html/moon-erp/src/app/features/clinic/visit-shell/ (abbrev vs/). BE root = /home/moonui3/moon-erp-be/Modules/.

Verdict on the client settings screen: ❌ MISSING. The prototype's «⚙ إعدادات الشكل / Program Setting» panel (proto 1245-1265, screenshot 16_settings.png) has no FE screen. Here is what exists in its place:

  • (a) the in-shell «✎ تخصيص الشاشة» editor, which only edits the card layout;
  • (b) the generic /clinic/settings page, which shows only clinic.visit_shell, clinic.layout.allow_doctor_override (group clinic_modes) and dental.feature.* (group «الأسنان»).

Everything else exists in the API only, with no UI:

  • field rules {visible, required, input};
  • gyn.feature.*;
  • the per-type features.shell switch.

1. Verdict table

#prototype item (proto line)implementation file:lineverdictnote
Card catalog
1Shared cards vitals/complaint/history/exam/diagnosis/lab/rad/rx/documents/previous/notes/followup, sizes (431-445)vs/card-catalog.ts:17-32✅Ids, kinds and sizes match exactly. Layers match too (history L1, exam L4, rest L3).
2diagnosis.requiredWhen:'insured' (435)card-catalog.ts:21; resolve-layout.ts:97-100; visit-shell.component.ts:476, 483-489✅Never re-inserted. Soft «DX_NOT_ON_SCREEN» warning when the card is absent. If the card is present on general, there is a hard block until a primary dx exists (classic rule).
3meds current-meds card (441)vs/visit-start-rail.component.ts:45 (rail only); card-catalog.ts:31 home_meds⚠Not a catalog card. home_meds is the D2c patient-facts list, not «reads prescriptions». Current meds show only in the start rail.
4results card (app-lab-history-groups) (442)visit-start-rail.component.ts:54 (rail only)❌Not addable as a card. The prototype MAP (1280) says rail cards are also catalog cards.
5photos card (444)—❌No card. Only documents exists.
6intake status card (446)visit-header.component.ts:51,65 (intake badge/banner)⚠Shown as a header badge, not as a catalog card.
7Extra FE shared cards: header(required, chrome), alerts, conditions, home_meds, summary, orders, timeline, procedures, team, summary_gencard-catalog.ts:16, 29-39✅Later-phase additions, permission-gated (permission field, resolve-layout.ts:75).
8Nutrition 14 spec cards nut_* (448-461)vs/specialties/nutrition.cards.ts:28-55 (nut_measure, nut_board)📝S2b. The board is hosted as one bare F card plus a measurements card. None of the 14 ids exist.
9dent_chart F required:true (463)specialties/dental.cards.ts:23-32 dent_workspace⚠Mapped to dent_workspace, but not required. Once dental saves unlock, a client could hide the jaw chart.
10dent_today / dent_exam (464-465)dental.cards.ts:51-68✅Sizes M/F (proto S/M). Bare cards.
11dent_plan, dent_fmx(imaging) (466, 468)—📝S2c: kept inside the workspace view switch. The imaging feature therefore gates no card.
12dent_perio/ortho/lab/recall with feature (467-471)dental.cards.ts:79-122✅feature perio/ortho/lab/recall plus permissions. Sizes are F only (proto L/F, M/F, S/M), so resize does nothing.
13gyn_cats / gyn_modules required (473-474)features/gyn/gyn-shell-cards.ts:39-48✅required:true, station. gyn_cats is layer 4 (proto L3).
14gyn_timeline (475)card-catalog.ts:36 shared timeline✅Renamed to the shared C1c timeline (naming drift).
15gyn_summary (476)gyn-shell-cards.ts:93-94✅Plus the runtime gyn_<module> addable cards via registerSpecCards (card-catalog.ts:59-68), as spec §3 asks.
16ctx cards mar / nursing / discharge (478-480)vs/contexts/inpatient.cards.ts:20-42✅inp_mar, inp_round + inp_nursing_notes (split), inp_discharge, plus an emergency triage. kind 'ctx', permission-gated.
Registry defaults
17general = flow, 1 col, 10 stations (489-493)card-catalog.ts:93-112✅Same order and sizes. Adds timeline S collapsed before followup (Fable consult).
18nutrition = 3-col board, 13 cards (499-502)nutrition.cards.ts:58-72📝1 col with 2 tabs (measure / الكشف) and hideRail. Known S2b.
19dental = 2-col board (510-513)dental.cards.ts:126-151📝2 cols ✅. Cards differ: dent_alerts kept, dent_quick_complaint instead of shared complaint, dent_more instead of the collapsed vitals/history/previous; tabs; hideRail. All known S2c.
20gyn = flow, 13 stations (521-523)gyn-shell-cards.ts:102-133⚠Superset of 21 cards: first_visit, obhx, exam, imaging, operation, discharge, team, gyn_followup added. Prototype relative order kept. history patientScopeOnly:true (proto had none).
resolveLayout
21default → client → doctor only if allowDoctorOverride, full replace (560-562)vs/resolve-layout.ts:51-61✅isUsable guard. Never merges.
22Drop unknown / other-spec / ctx / feature-off (564)resolve-layout.ts:69-86✅Also drops duplicates, chrome ids and cards whose permission fails.
23Size fallback to sizes[0] (565)resolve-layout.ts:90✅
24Re-insert missing required at top, tagged 'required' (566)resolve-layout.ts:98-107✅Skips feature-locked and permission-denied cards (consistent with the drop rule).
25Col clamp (570)resolve-layout.ts:63-64, 91✅Also caps columns at 4.
26version lower → migration (spec §2.3)resolve-layout.ts:44-46✅Stub (v1 only).
27Context replace followup→discharge (529, 567)contexts/inpatient.context.ts:65; resolve-layout.ts:120-131✅If the user lacks the discharge permission, the followup card is dropped (no stray follow-up). Tagged 'context'.
28Context addCards mar/nursing/discharge (529, 568)inpatient.context.ts:56-61; resolve-layout.ts:132-138✅Appended at the end. day_case has no nursing notes. Emergency triage goes at:'start' (extra).
29cardProps rx kind=inpatient (530, 569)inpatient.context.ts:66; visit-shell.component.html:277-285✅Only when hospital.mar_on_prescriptions ≠ false; otherwise the legacy order form.
30Proto contexts clinic/center (orderingMode/labMode/pharmacy) (526-527)visit-shell.component.ts:208, 281 (encounter.ordering_mode / lab_mode / rad_mode)✅Derived from the encounter (BE), not from a FE context. Correct mapping.
31Vitals pack = base 8 + specialty pack + context pack (572)encounter/vitals-panel/vitals-panel.component.ts:214-217 (VITALS_WHITELIST)❌The vitals card is always the same 8 fields. Waist/hip/WHR, LMP/GA/fundal/FHR and RR/AVPU/NEWS2 exist only in the spec/ctx cards (nut_measure, gyn_antenatal, inp_round), not as a composed pack.
32Vitals `props.mode full\compact\readonly` (431)visit-shell.component.html:244-246❌Only patientScopeOnly is passed. There is no compact or readonly mode prop.
33effectiveFeatures = specialty defaults + client override (556)visit-shell.component.ts:182-184, 540-548; card-catalog.ts:74-85; Gyn/app/Support/GynFeatures.php:40-61✅Dental reads DentalService.meta().settings.features (Decision 6). Others read normalized client.features. Missing = ON.
34Each card tagged with source level; editor shows "الحالي من" (571, 1141)visit-layout.model.ts:13,107-110; visit-layout-editor.component.ts:20✅
Persistence (BE)
35Client level setting clinic.layout.{type} + fields + features (532-547)Clinic/database/seeders/ClinicSettingDefinitionSeeder.php:994-1009 (clinic.visit_layouts, company, is_visible=false)✅One JSON keyed by type: {layout, fields, features}.
36Doctor level = user preference (548-553)Seeder:1010-1025 clinic.visit_layout_doctor scope=user✅Stores only the layout.
37allow_doctor_override (default off) (536, 540)Seeder:978-993; VisitLayoutController.php:77-81, 130-134✅PUT doctor returns 403 when off. GET hides the doctor layout when off.
38GET / PUT client / PUT doctor + clinic.layout.manageClinic/routes/api.php:482-484; VisitLayoutController.php:32-88✅PUT client sent with the layout only keeps fields/features (55-65). layout:null resets that level. The permission is seeded and granted to 3 roles on dev.
39Validation of layout / tabs / props / fields.*.inputVisitLayoutController.php:41-49, 91-115✅Card ids are not checked against a catalog (the FE drops unknown ids at resolve). Raw layout is stored so props/tabs survive.
40stripLayout: context cards and from not persisted (1115)resolve-layout.ts:190-210; visit-shell.component.ts:225, 751-757, 779✅The editor edits resolvedBase (context-free), so followup is not lost (S3 Decision 10).
Edit mode «✎ تخصيص الشاشة»
41Who: client admin; doctor only if allowed (1139-1142)visit-shell.component.ts:291-294; visit-shell.component.html:225-228✅Shown when `can_manage \\allow_doctor_override`, and hidden on a signed visit. The prototype let anyone edit.
42↑↓ reorder, ◀▶ columns, size cycle, collapsed, hide except required (1126, 1147-1155)vs/visit-card.component.html:1-22; vs/layout-edit.ts:40-89✅Same algorithm, immutable. RTL arrow glyphs flipped. The size button is hidden when the card has one size.
43Add from catalog, filtered by specialty + features, ctx excluded (1140, 1145, 1319)card-catalog.ts:122-135; visit-shell.component.ts:295-300; layout-edit.ts:91-95✅Also permission-filtered. Default size M (proto: sizes[0]). Shared cards get 🔗 like the prototype.
44Save for client (primary) / save for me if allowed / reset per level / cancel (1142-1143, 1334-1338)visit-layout-editor.component.ts:27-39; visit-shell.component.ts:776-801✅After a save, the draft is rebuilt from the new payload and a message is shown.
45Saving non-general specialty layoutsvisit-shell.component.ts:272; visit-layout-editor.component.ts:42📝Decision 7: saves are open only for general and gyn_ob. The BE already keeps tabs/props (LEDGER ef1d0f968), so this lock is now policy, not a technical need.
46Context cards stay visible while editing (proto layout() concat ctx, 1118)visit-shell.component.ts:254, 225⚠Editing an inpatient visit shows the base layout only. MAR/round/discharge disappear until cancel. Persistence is correct either way.
Client settings screen (⚙ Program Setting) — decides the verdict
47A dedicated settings panel with per-client / per-doctor scope (1245-1265)— (no route; /clinic/settings = generic setting_definitions page, clinic.routes.ts:625-636)❌There is no screen anywhere that lists the type's cards, the field rules and the feature groups together (spec §2.7).
48Per-field visible / required (544; spec §2.3)API: VisitLayoutController.php:42-46. Consumers: cards/visit-complaint-card.component.ts:101-104, cards/visit-exam-card.component.ts:153-157, Clinic/app/Services/Intake/IntakeCompleteness.php:33-34❌ UI (API ✅)No FE caller ever sends fields (the only saveClient call is visit-shell.component.ts:782, layout only). Setting rules needs a hand-made PUT or a DB edit.
49Per-field input Select⟷Text, legacy Examination Controller (545, 1248, 1261)Consumed for exam regions (exam_finding_<region>) and chief_complaint only (exam-card:153, complaint-card:101)❌ UI / ⚠ consumersNo UI. The diagnosis Select/Text rule (proto INPUT_FIELDS 'diagnosis') is not consumed at all.
50Element toggles hdr.insurance / complaint.quick / dx.secondary (+ hdr.lastVisit, hdr.doctor, complaint.hpi, lab.prev, rx.interact) (540, 653, 1262)visit-header.component.ts:46 (insurance always shown); complaint-card:104 (fields.complaint_quick.visible)❌Only complaint.quick has a consumer (via field rules, API only). There is no clinic.elements store. Header insurance and secondary dx cannot be hidden.
51Specialty features dental.feature.* (508, 546)Dental/database/seeders/DentalSettingDefinitionSeeder.php (8 keys, group clinic_dental, visible); /clinic/settings «الأسنان» group (settings/clinic-settings.registry.ts:139-146)✅Real UI, enforced by the BE controllers (Lab/Ortho/Plan/Chart).
52Specialty features gyn.feature.* (519, 546)Gyn/app/Support/GynFeatures.php:19-61 (stored in clinic.visit_layouts['gyn_ob'].features)❌ UI (API ✅)The setting row is is_visible=false and no FE sends features. Fertility/obstetrics/urogyn/sexual/oncology/operations can only be switched off via the API.
53allow_doctor_override toggle/clinic/settings group clinic_modes (Seeder:978-993, is_visible=1)✅Generic boolean switch.
54Per-client layoutin-shell editor (#41-45)⚠Works for general and gyn_ob. Nutrition and dental are locked (#45). There is no off-visit place to manage layouts: you must open a live encounter.
55Doctor-scope fields / elements override (proto cfgScope='doctor', 1246, 1360)VisitLayoutController.php:84 (doctor stores layout only)❌The doctor level is layout-only.
56Per-type shell switch features.shell (nutrition/dental)encounter/consultation.component.ts:453; dev DB {"nutrition":{"features":{"shell":true}},"dental":{...}}❌ UIAPI/DB only. An install cannot turn on the nutrition/dental shell without a hand PUT.
Layer colouring / source tags
57«تلوين الحقول بالطبقة» toggle L1-L4 (163-164, 1165, 1310)—❌No toggle and no data-l field marking. Low impact: it was a prototype-harness control.
58Card head shows layer + source (default/client/doctor/context/required/draft) always (1128; spec §5)visit-card.component.html:39-43; visit-card.component.ts:46-47⚠Shown only while customising (design review P1-2). The kind tag (🔗/🏷/سياق) is always shown.

2. Real bugs found (implemented but wrong)

No hard defects in the resolve/persist path. Minor misbehaviours:

  • Editor card count is per tab. visit-shell.component.html:70 passes layout()?.cards?.length, but layout() is the open tab only (visit-shell.component.ts:260-263). On tabbed layouts (nutrition/dental) the bar says e.g. «1 كارت» instead of the draft total. The prototype counts state.draft.cards (1141).
  • ↑↓ can be a silent no-op on tabbed layouts. layout-edit.ts:74-84 swaps with the neighbour in the same column across the whole draft, including cards on another tab. The card moves "under" an invisible card. Latent only while dental/nutrition saves stay locked.
  • dent_workspace is not required (dental.cards.ts:23-32), unlike proto dent_chart.required:true (463). Once Decision 7 is lifted, «✕ إخفاء» will be enabled on the jaw chart, and resolveLayout will not re-insert it if a saved layout omits it.
  • The Decision 7 lock is now stale. The comment at visit-shell.component.ts:268 says the lock waits for the BE to keep layout.tabs/props. The BE does keep them (VisitLayoutController.php:57-58, 107-114). If the lock is still wanted, its stated reason should be updated.

3. Top 5 gaps by owner impact

  • No client settings screen (⚙ Program Setting): field visible/required/input, elements and gyn features have no UI. This is the headline difference from what the owner approved (16_settings.png).
  • gyn.feature.* and the nutrition/dental features.shell switch can only be set by API/DB. A real centre cannot lock Urogyn/Sexual or turn on the specialty shells itself.
  • Element toggles are not implemented: hdr.insurance, dx.secondary, hdr.lastVisit, hdr.doctor, complaint.hpi, lab.prev and rx.interact have no consumer. Only complaint.quick has one, via field rules.
  • The vitals card does not compose specialty or context packs. It is always the fixed 8 vitals, and there is no compact/readonly mode prop.
  • Several shared cards are rail-only or missing from the catalog: results, current meds, photos, intake status. Layout editing is also limited to general and gyn_ob, and only from inside a live encounter.
B — هيكل الشاشة والكروت المشتركة (Shell chrome)

B-shell-chrome — prototype vs implementation (read-only audit)

Paths: FE = moon-erp/src/app/features/clinic/, VS = FE/visit-shell/, BE = moon-erp-be/Modules/Clinic/app/. Proto = visit-shell-prototype.html.

1. Verdict table

#prototype item (proto line)implementation file:lineverdictnote
Patient header
1Name · MRN · gender · age · blood · phone (1170)VS/visit-header.component.ts:37-42, 92-103✅BE PatientReadService::summaryFor L73-84. Numbers in .en (ltr).
2Alert chips with a source tag, «+ تنبيه» (1174)visit-header:57-60 → shared/patient-alert-strip:68-94✅Allergy sorted first; a src tag per pill; «+ تنبيه» gated on clinic.alerts.manage.
3Allergy / anticoagulant / diabetes chips (581)BE Services/Facts/PatientFactsReader.php:102-106, derivedConditionAlerts L240✅On dev, clinical_lookups condition anticoagulant (danger), diabetes and hypertension (info) have is_alert=1.
4«Rh سالب» chip, src=blood (581)visit-header:100 (blood group as text only)❌No alert is derived from blood_group. Rh appears only in the Gyn computed/summary text.
5Specialty alerts (pregnant 24w, latex) (507, 518)alert-strip:105-110 (specItems), 296-330✅GA/EDD/risk come in on the specialty facts line, and dental flags arrive as alert rows.
6Insurance kv (1171)visit-header:46, 106⚠It always shows «نقدي». The field name does not match what the BE sends (see Bug B1).
7Last visit / doctor kv (1171)visit-header:43-47✅The client toggles hdr.* do not exist (the elements slice covers those).
8«⚠ ملف ناقص — أكمله» badge opens intake step 2 (1172, 1330)visit-header:51 → intake/intake-badge.component.ts:32-38; reconfirm banner L65✅One badge, ordered missing_shared > specialty > reconfirm. Clicking it opens the wizard on the right step.
9Inpatient: 🛏 bed chip + admitting doctor (1171-1172)visit-shell.html:22 (stay.chip()); header doctor = encounter.doctor (shell.ts:353)⚠The bed/ward/day chip is there. The admitting doctor is not shown; the header shows the encounter's doctor.
10«ملف المريض» button (1173)visit-header:52✅
11App bar pills: layout source, encounter.kind, autosave (1166-1168)visit-shell.html:20-27⚠Merged into the header row by decision (design review P0-1/P1-1). The layout pill shows only while customising, there is no encounter.kind pill, and the «حفظ تلقائي» pulse fires only on complaint saves (onSaved is bound only at shell.html:252).
Flow / board
12Ordered stations, number, «تم ✓» (1127-1129)visit-card.component.html:27-29, 47-51; shell.ts:303-311✅Numbering covers station:true cards only. Non-station cards (timeline, alerts) show their icon.
13Done → collapse to one-line summary, next station opens (1373-1375)shell.ts:621-636; card.html:45⚠Works. Differences: (a) after the last station it wraps back to the first undone one (L627); the proto stops. (b) history, documents and previous have no summary text (summaries L421-437), so their collapsed heads are blank.
14First active = first undone (1162)shell.ts:657-661✅
15Ctrl+Enter = finish the active station (1382)shell.ts:929-934✅
16Alt+↓/↑ = previous/next station, wraps (1383)shell.ts:935-942✅Same as the proto, plus scrollIntoView.
17Alt+F = finish panel, works in board mode too (1379)shell.ts:921-925✅Also matches ev.code==='KeyF' (Arabic keyboard layout). Suppressed while locked, customising, or a picker/dialog is open (L919-920).
18Keyboard ignored outside the "returning" mode (1378)shell.ts:919✅Locked or editing → ignored.
19Board mode (flow:false): icon head, open = !collapsed, no «تم», footer "لوحة · N كارت" (1121, 1186)shell.ts:608-613; shell.html:205-207✅
20Flow done/active stateshell.ts:674-693 (localStorage visit-shell:flow:<id>)⚠Kept per browser only. A nurse's «تم» on vitals is not visible to the doctor on another PC. The proto kept it in memory; no spec.
21Quick bar: pickers for history/exam/lab/dx/rx (1196)shell.html:98-107⚠Only «الفحص» opens a picker (openExamPicker). The other buttons only jump to their card (L101-105).
Card anatomy
22Head icon/number + title + 🔗 shared / 🏷 spec / context tag (1124, 1128)visit-card.component.html:25-38✅
23Layer tag + «مصدر الكارت» on the head (1128, §5)card.html:40-43⚠Shown only while customising (design review P1-2). A deliberate deviation from §5.
24Collapsed shows sum (1128)card.html:45✅The body stays mounted ([hidden], L55), so card state survives collapsing.
25Size classes sz-S/M/L/F; F rows full width (1127, 1135)visit-card.ts:53-65; shell.html:121-135 (gridRows)✅
26Head = button with aria-expanded; private card aria-label (§10)card.html:25 (div role=button, Enter/Space)⚠aria-expanded ✅. CardDef.priv is never read, so a private card has no restriction aria-label.
27Customise toolbar (1126)card.html:1-22✅Required cards cannot be hidden. In RTL the column arrows are swapped. (The editor itself belongs to another slice.)
Start rail
28Previous visits + «كل الزيارات» (1221)visit-start-rail.component.ts:26-42✅Last 3 (the current one excluded). The link goes to route patients/:id/visits (clinic.routes.ts:297).
29Current meds with dose (1222)start-rail:44-51; BE PatientFactsReader::meds L426-443⚠Comes from active prescription items ✅. Only the drug name shows; the BE sends sentence, which is not rendered.
30Latest results, red/green by flag, date tag (1223)start-rail:53-65; BE PatientReadService::lastLabsList L245-262⚠Every result is green (see Bug B2). No date or unit is shown.
31Timeline sparkline in the rail (1224)— (moved to a collapsed timeline card, card-catalog.ts:109)⚠Relocated, not missing.
32Rail collapse / tablet drawer (1216-1220)shell.html:39-60; scss:141-150; shell.ts:909-913✅
End rail
33Live summary: ✓ for a completed station, else number + title (1230)visit-summary-rail.component.ts:24-32; shell.ts:440-452⚠✓ logic ✅, and clicking a line jumps to the card. The rail numbers every card while card heads number stations only, so they drift (general: followup is «11» in the rail, «10» on its card). In flow, lab/rx/vitals text refreshes only on «تم» or finish.
34Orders & bill tag: immediate «محاسبة هنا» / intent «للاستقبال» (1231)summary-rail:36-42✅Hosts the real app-clinic-service-order-rail (the proto used a computed bill).
35Inpatient «حساب الإقامة» (1231)summary-rail:37 (always ORDERS_BILL)❌No stay-bill label and no stay charges: the rail always shows the encounter's service_order_id.
Finish panel
36Soft warnings: key stations not done (1204)shell.ts:464-468 (KEY_STATIONS L82)✅
37«الشكوى الرئيسية فاضية» (1205)shell.ts:469✅
38«لا طلبات — متأكد؟» (1206)shell.ts:470-472✅Also counts the prescription (a stricter wording).
39Insured + dx not on screen → «add it from the catalog or record it from a dialog» (1209)shell.ts:476; i18n DX_NOT_ON_SCREEN⚠Only «تخصيص الشاشة» is offered; there is no diagnosis dialog. For general the sign is hard-blocked while the dx card is off the screen (Bug B4).
40Insured + dx on screen = soft «مطلوب للمطالبة (E11.9 ✓)» (1209)shell.ts:483-489; finish-panel:21-29⚠A hard block (⛔, sign disabled) instead of a soft line. Acceptable because BE EncounterService L248 returns 422 anyway. The panel title still says «لا تمنع التوقيع».
41Extra warnings (undecided intents, vitals danger, spec cards)shell.ts:473-480✅Beyond the proto; useful.
42«✍ توقيع وإقفال» (1210)finish-panel:24-26 → shell.ts:840-864✅Real encounters.sign; refreshes the header snapshot; checks permission clinic.encounters.sign.
43«توقيع + طباعة الروشتة والطلبات» (1210)finish-panel:27-29; shell.ts:857⚠Prints the visit summary (printSummary), not the prescription + orders.
44«رجوع» (1210)finish-panel:34✅
Signed state
45🔒 bar «موقّعة من … · time — BE lock» (1185, 15_signed.png)shell.html:114-119, 188-189; header pill L26✅The signer's name is not shown, only the time. Lock comes from the BE is_locked.
46All cards collapsed to summaries, read-only (1121; screenshot)shell.ts:611 (locked → closed); [locked] passed to every host⚠Works, but history, documents and previous show blank heads (proto showed a summary for each).
47+ Addendum, print summary, referral (1185)shell.html:190-196, 137-163; shell.ts:866-884✅Amendments log is listed; permission clinic.encounters.amend. Cashier also stays available.
48Header after sign = what was seen at signalert-strip:69-72, 95-99✅Snapshot with a «الحالي» toggle (beyond the proto).
Footer
49Progress dots + "n/N محطات · حفظ تلقائي" (1186)shell.html:200-204✅
50☰ السجل · طباعة · 💵 كاشير · ✍ إنهاء Alt+F (1187)shell.html:209-214✅Cashier = sendOrderToCashier → /clinic/cashier, disabled with no order. «⛔ إيقاف التعامل» belongs to the nutrition slice.
Intake
51First-visit mode replaces the main area with the wizard; «تخطّي» (1178, 1184)intake/intake-launcher.service.ts (body-level dialog)⚠Wizard = dialog opened from the badge or registration form. The shell never switches into first-visit mode by itself. clinic.intake.enabled defaults false (📝).
52Step 1 shared (identity + 7 Qs + allergies) → step 2 specialty pack; refs from layer 1 not re-asked (1235-1242)intake/intake-wizard.component.ts:54-110, 237✅Steps come from the BE payload, and the fill role locks a step (STEP_LOCKED L79-81).
53"Who fills it" per step (1235, 1239)wizard:79-81 (fill_role)📝Known: the packs lack in_intake/fill_role.
54intake card in the catalog (866)—❌No intake card; the header badge replaces it. Low impact.
Shared card bodies
55vitals: full / compact / readonly modes, specialty and context packs, computed BMI (803-811)shell.html:243-247 → encounter/vitals-panel (bmi computed L173)⚠Real panel, autosaves ✅, BMI computed ✅. No mode input (compact/readonly): readonly = locked or patientScopeOnly. 📝 for the nutrition readonly use.
56complaint: catalog picker + ★ quick + free text; text-only rule (813-820)VS/cards/visit-complaint-card.component.ts:33-69, 101-104, 121-191⚠Saves (link rows + lookup_ids; note debounced 800 ms) ✅. Missing: duration and HPI fields. A note typed less than 800 ms before signing is dropped (Bug B5).
57history: patientScopeOnly prop (821-828)shell.html:254-257 → history-panel.component.ts:128✅The real panel; the prop hides the encounter questionnaire, as in the proto. No collapsed summary (row 13).
58exam: regions × catalog, per-region Select⟷Text, note (829-837)cards/visit-exam-card.component.ts:49-90, 152-153, 228✅PUT encounters/{id}/findings, debounced.
59diagnosis: ICD picker, primary/secondary (838-845)shell.html:263-266 → diagnosis-panel⚠Real panel, saves. The «Diagnosis = Text» client mode is not supported (always ICD; the picker slice owns this).
60lab: immediate vs intent tag, catalog mode (846)shell.html:267-271 → lab-orders (orderingMode/labMode inputs)✅Refreshes the summary and encounter on intentsChanged.
61rad (847)shell.html:272-276 → rad-orders✅
62rx: sentence, duplicate, inpatient kind with times (848-857)shell.html:277-287 → prescription / inpatient-rx-composer✅(saved) from app-clinic-prescription is not bound, so the rx summary goes stale until «تم» or finish.
63documents: sick leave / report / certificate / referral + attachments (858)cards/visit-documents-card.component.ts:21-26✅Templated documents via clinical-documents-list «+» (clinic.documents.create), the Core attachments API, print and referral. No collapsed summary.
64previous (859)shell.html:291-294 → previous-visits (rxRepeated)✅«تكرار الروشتة» reloads the rx panel.
65meds card (860)home_meds card (shell.html:334-337)⚠Patient-facts home meds, not "from prescriptions". There is no meds catalog id.
66results card (861)—❌No shared results card (rail only).
67notes (862)shell.html:319-324 → patient-clinical-notes-card✅Real clinical_notes.
68photos (863)—❌No shared photos card. Proto MAPS L1280 expects it in the catalog; the nutrition copy is 📝 S2b.
69followup: N days, date, referral, instructions (864-865)cards/visit-followup-card.component.ts:18-32⚠Revisit window + follow-up tasks + referral ✅. «احجز متابعة» leaves the visit for /clinic/booking (shell.ts:900-907). No inline date or instructions.
Responsive / RTL
70Phone: one column + sticky mobsum "ملخص … · فاتورة X ج" (1182)shell.html:199; shell.ts:453-455; scss:165-193⚠Strip ✅ (phone only), but it has no bill total. On a phone the end rail, including orders and bill, is hidden entirely (.rail.e, .rail.col {display:none} scss:167).
71Tablet: two panes, third column drops, start rail as drawer (§4)scss:141-161 + ResizeObserver narrow/phone (shell.ts:90-91, 558-571)✅Measures the frame, not the viewport (better).
72Touch targets ≥44px (§10)scss:147, 158 (.btn {height:32px}), 184 (36px)⚠Only the rail toggles reach 44px.
73RTL first, numbers/codes ltr (§10).en direction:ltr (_clinical-ui.scss:89, header scss:10); card.html:8-9✅
Classic / redirect
74Shell when the switch is on, ?classic=1 opts outencounter/consultation.component.ts:447-456✅general and gyn_ob follow the global flag; other specialties also need features.shell; inpatient/day-case always go to the shell.
75Classic link from the shell; unsupported type falls backshell.html:31-34, 230✅
76Shell route gated by the switchclinic.routes.ts:350-355; shell.ts:204 (supported = has a default layout)⚠/clinic/encounter/:id/shell opens even with clinic.visit_shell off; visit_shell_enabled is never read in the shell. Minor.

2. Real bugs found (implemented but wrong)

  • B1 — Insurance name never shows, always «نقدي». VS/visit-header.component.ts:106 reads summary().insurance.company_name. BE PatientReadService.php:91 returns $patient->insurance_info as-is, which LIS writes as an array [{contract_id, contract_name}] (FE features/lis/patients/lis-patients.component.ts:533-536; services/clinic-patient.service.ts:220-221). An array has no .company_name. The FE type ClinicPatientInsurance {company_name, coverage_percent} (clinic-patient.service.ts:69-73) describes a payload the BE never sends. The same defect is in the classic shared/clinic-patient-header/clinic-patient-header.component.html:97, so fix it once in the shared layer (either the BE maps insurance → {company_name: insurance_info[0].contract_name}, or the FE reads [0].contract_name). Dev has 0 insured patients, so a live check won't catch it. The finish gate is not affected (it uses encounter.is_insured).
  • B2 — «آخر النتائج» never flags abnormal values. visit-start-rail.component.ts:58-59 colours by r.flag, which is declared optional in ClinicPatientLabResult (clinic-patient.service.ts:75-79). BE lastLabsList() (PatientReadService.php:256-260) returns only {name, value, unit}, so every result renders green (HbA1c 7.4 looks normal). Clinically misleading. Fix: the BE sends the result flag/abnormal status (and the unit and date).
  • B3 — FE and BE sign gates use slightly different rules. FE finishBlocked (shell.ts:488) accepts any is_primary. BE DiagnosisService::blockSignForInsured (L124-131) also requires code IS NOT NULL. The shell's diagnosis panel always saves ICD codes (diagnosis-panel.component.ts:158), so only legacy or uncoded primary rows reach the BE 422 (English message toast). Low severity.
  • B4 — Possible dead end for insured patients. For general, finishBlocked (shell.ts:483-489) blocks even when the client layout removed the diagnosis card (the type-specific exemption at L487 applies only to non-general types). The doctor sees ⛔ + «أضيفه من تخصيص الشاشة», but without can_manage / allow_doctor_override there is no customise button (shell.ts:291-294), so the doctor must go to the classic screen. The reverse case (nutrition/dental, insured, no dx card) lets the doctor press sign, and the BE returns 422 anyway (EncounterService.php:248). The FE «soft» promise there is false; it is the S2 Decision 5 territory, so it needs an owner call.
  • B5 — The complaint note can be lost on fast sign or navigation. visit-complaint-card.component.ts:114 debounces 800 ms. ngOnDestroy (L117-119) unsubscribes without flushing, and saveNote returns early once locked (L183). Text typed less than 800 ms before «توقيع» or leaving the page is never saved. The exam card has the same debounce pattern (L133); worth checking.
  • B6 — Cosmetic numbering mismatch. The live-summary rail numbers every non-bare card (summary-rail:28, i + 1 over summaryLines), while card heads number stations only (stationIndex). In the general default the collapsed timeline shifts the rail numbers by one for followup.

3. Top 5 gaps by owner impact

  • B1: the insurance name in the header (shell and classic) always reads «نقدي». It contradicts the insured sign gate and is visible on every visit.
  • B2: latest results are all green, so abnormal values are not flagged in the start rail (plus no date or unit).
  • Inpatient: no «حساب الإقامة» bill in the end rail (it shows the encounter's service order) and no admitting doctor in the header.
  • Collapsed and signed read-back: history, documents and previous show blank summary lines, and the rx/vitals/lab lines refresh only on «تم». The signed visit (15_signed.png) therefore reads thinner than the proto.
  • Phone: orders and bill are unreachable (end rail hidden, mobsum has no bill total); tablet touch targets are 32px, not 44px. The missing Rh-negative alert chip comes next.
C — الاختيار بدل الكتابة والكتالوج (Picker & lookups)

Slice C — picker / lookups / favourites / field rules / computed

Paths: BE = /home/moonui3/moon-erp-be/Modules, FE = /home/moonui3/public_html/moon-erp/src/app. Picker = FE shared/components/clinical-picker/.

1. Verdict table

#prototype item (proto line)implementation file:lineverdictnote
1ONE catalog clinical_lookups {lookup_key,label,group,display_order,specialty,is_active} (587-592)BE Clinic/database/migrations/2026_11_03_100006_create_clinical_lookups_tables.php:21-39✅ as specGroup = self-ref parent_id (category row → children); value_ar/value_en; company-scoped; soft-deletes.
2Field definitions reference {lookup_key, group} (591, 1273)BE …/2026_11_03_100005_add_engine_v2_columns_to_history_questions.php:38-39; HistoryQuestionResource.php:44⚠ partialhistory_questions.lookup_key only, no group. Real users: 279 gyn multi + 27 gyn single (DB). General shell fields have no definitions: complaint/exam hard-code the key in the card.
3Multi-select = link rows, never CSV (588-590)complaint: encounter_complaints + lookup_id (…280002:17-20); exam: encounter_findings (…280000:20-33); dx: encounter_diagnoses⚠ partialComplaint/exam/dx are real link rows. Every multi_select history field (all 279 GYN chip fields, incl. categories gyn_cat_<g>) is a JSON code array in answer_value/value_json (HistoryService.php:411-418, 657-664). Not CSV, but not link rows either.
4Per-doctor favourites FAVS[doctor][key] (644-651)BE clinical_lookup_favorites(company,user,lookup_key,lookup_id) migration :41-55; LookupCatalogService.php:28-59,112-133; route api.php:476✅ as specReal and persisted per user. The list endpoint sorts ★ first. Un-favourite does forceDelete. Dev DB has 0 favourites so far.
5Favourites for lab/rad/drugBE clinic_favorites (2026_07_08_000010), FavoriteController.php:27-33; FE services/clinic-favorite.service.ts✅ (2nd store)A separate per-doctor store for lis_investigation / lis_package / rad_procedure / clinic_service / drug. There is no ICD/diagnosis type.
6Inline «+ إضافة جديد» writes to catalog with lookups.manage (778, 796-797)BE api.php:475 permission:clinic.history.manage; StoreLookupRequest.php:84; LookupCatalogService.php:67-105; FE picker-box.ts:160📝 deferred (known)Real: it persists, is idempotent on value_ar and is permission-gated, both FE and BE. But the gate is the broad clinic.history.manage. On dev, roles doctor and gyn_doctor do NOT have it (only owner / طبيب), so most doctors never see the add row.
7On card: ONLY selected chips + up to 4 ★ quick + «+ اختيار / بحث» (780-784)FE clinical-picker-box.component.ts:77-99,162-171✅ as specChip click removes it; quick-favs exclude categories and already-selected values; capped at 4.
8Focused picker: search, ★ block on top, grouped list, category pills, multi/single, footer «المختار (n)», Done (772-779, 759-771)FE clinical-picker-dialog.component.html:251-394; .ts:86-121✅ as specSearch matches ar + en. A ★ row also appears again in its group, as in the prototype.
9Investigations = category grid by display_order, open ONE category (763-766)dialog.html:307-327 (grid input)⚠ partialGrid mode exists in the shared picker, but NO caller passes [grid]=true. Lab/rad use their own legacy pickers (rows 16-17).
10Keyboard: Enter picks first hit, Esc closes (776)dialog.ts:221-241; rows tabindex + Enter/Space (html:397-398)✅ as specNo arrow-key navigation (the prototype had none either).
11Mobile full-screen sheet (screenshot 14)dialog.scss:87-89 (max-width:640px → 100vh, no radius)✅ as specGyn option picker: p-dialog breakpoint 640px → 100vw (gyn-option-picker.component.ts:41).
12Complaint uses picker, specialty-scoped, multi → link rows (741, 814-818)FE visit-shell/cards/visit-complaint-card.component.ts:41-50,121-139; shell html:249-250⚠ partialThe mechanics are real (lookup → clinic_complaints by name + lookup_id). But dev company 1 has NO general complaint rows: only 20 complaint/gyn_ob (DB). In a general visit the picker is empty and there is no seeder (see Bug 1).
13Exam: ONE catalog exam.finding, regions = categories, a box per region + full picker behind the head button (743, 830-832)FE visit-exam-card.component.ts:48-86,147-158; BE EncounterFindingService LOOKUP_KEY⚠ partialThe code is right. Dev has 0 exam.finding rows and nothing seeds them, so the card falls back to one flat empty box with no regions (see Bug 1).
14History questions picker history.question (742)FE encounter/history-panel (legacy chips from history_answer_options); HistoryQuestionResource.php:48-54❌ missingThe general «التاريخ» card is the old panel: open chips per question, no catalog, no ★, no inline add.
15Diagnosis: picker with ★, group pills, inline add, first = primary (744, 839-844; screenshot 06)FE encounter/diagnosis-panel/diagnosis-panel.component.html:42-52 (app-pick-multiselect, minChars 2); ts:64-68⚠ partialSearch-as-you-type ICD autocomplete with magic-add (POST icd10, perm clinic.diagnosis.record). No favourites, no group browse, no picker behind a button. Primary/secondary is set with buttons.
16Lab orders: grid picker behind a button, ★, add-new (745, 846; screenshots 02/03)FE encounter/orders/lab-orders.component.html:3-200; .ts:181-215,501-530⚠ partial★ is real (clinic_favorites) and there is a search ⟷ grid toggle. But the whole picker + basket is spread on the card, not behind a button. Add-to-catalog only in standalone mode, with no FE perm check.
17Rad orders picker (746)FE encounter/orders/rad-orders.component.html:13-19; .ts:164-206⚠ partialSame as lab: inline catalog-grid-picker with real ★; createFn only in standalone mode.
18Rx: drug + default sentence auto-filled and editable, Duplicate (747; screenshot 04)FE encounter/prescription/prescription.component.ts:383-410,462-468,495-499,531-549,242✅ as specSearch behind a toggle; presets/sentences; duplicate; save typed sentence as preset; quick-add drug gated by perms. ★ favourites only in products pharmacy mode (:223-231): standalone_formulary has only most-used.
19Gyn categories picker, noAdd, ★ (748-750; screenshot 10)FE gyn/cards/gyn-category-picker.component.ts:52-56,95-160 + gyn/form/gyn-option-picker.component.ts✅ as specFeature-filtered groups; sexual group needs gyn.sexual.view; ★ persisted through clinical_lookups; no add (as in the prototype). Stored as answer gyn_cat_<group> (JSON).
20Gyn module chips (PK_DYN/gchips) e.g. VTE (751; screenshot 11)FE gyn/form/gyn-lookup-field.component.ts:29-45,74-84; gyn-module-form.component.html:42,74✅ as specUses the shared box/dialog with specialty=gyn_ob. Lists of ≤6 (multi) or ≤7 (single) render open; gyn_vte_pre etc. have lookup_key (DB).
21Nutrition dialog investigations picker (screenshot 22)FE nutrition/visit/nutrition-consult-board.component.html:654-693 (p-dialog → app-clinic-lab-orders)📝 deferred (S2b)The classic board hosts the same lab panel in a dialog: ★ is real, but no category-grid-first view as in the screenshot.
22Per-client per-field input Select⟷Text (INPUT_FIELDS 654, rule/inputType 734-735)BE VisitLayoutController.php:42-46,60-61 stores fields.*.input; FE complaint-card.ts:101-104, exam-card.ts:152-158⚠ partialThe renderer honours it for chief_complaint and exam_finding_<regionDbId> only. Diagnosis rule is ignored. No UI ever writes fields: the only saveClient call (visit-shell.component.ts:782) sends layout only. The prototype's settings rows (proto 1248) are not built.
23Doctor may override field rules/elements when the client allows it (734, 736)visit-layout.service.ts:42-44 (doctor level = layout only)❌ missingDoctor override covers the layout only, never fields or elements.
24ELEMENTS show/hide (653, elem 736)complaint-card.ts:102,104 (chief_complaint_note, complaint_quick)⚠ partialOnly 2 of the 8 are honoured. hdr.insurance / hdr.lastVisit / hdr.doctor / complaint.hpi / lab.prev / rx.interact / dx.secondary: no code reads them, and there is no settings UI.
25Required marker per client rule (711)complaint-card.ts:103⚠ partialOnly on the complaint text-only mode. There is no generic required-field rendering from fields.
26Computed BMI read-only (714)BE VitalsService.php:70,102,135 (client value unset, server computes); FE vitals-panel.component.html:67-78✅ as specShown read-only with an abnormal flag.
27Computed WHR (715)BE Nutrition/app/Services/Visit/ConsultBoardReader.php:94-95; FE nutrition-consult-board.html:157-160✅ as specRead-only, with a risk pill.
28Computed GA from LMP (716)BE Clinic/app/Support/ComputedRegistry.php:150-195 (edd/gaDays/ga); Gyn/app/Support/GynComputed.php:58,135-152; FE gyn/engine/computed.ts:333✅ as specSaved with source=derived (HistoryService recomputeDerived).
29Computed NEWS2 (717)ComputedRegistry.php:215-253 (per-parameter bands); FE hospital/nursing/vital-round.component.html:4✅ (better)Real RCP bands instead of the prototype toy. Shown on hospital rounds and as a computed history field. Per-parameter display is 📝 deferred (known).
30Managed catalog admin (legacy investcats SHOW/EDIT/DELETE; «كتالوج مُدار»)BE api.php:474-476 (index/store/favorite only)❌ missingNo update / rename / deactivate / reorder / delete endpoint or screen, so a mistyped inline-add stays forever.
31Favourites shared across sibling pickers liveclinical-lookup.service.ts:45-46,88-98 (changed signal + in-place cache patch)✅ as specCache is per key+specialty (see Bug 4).

2. Real bugs found (implemented but would misbehave)

  • The general complaint and exam pickers are empty on a fresh or dev install: there is no seed. On dev company 1 there are 0 rows of complaint with specialty NULL/general and 0 rows of exam.finding (SELECT on clinical_lookups). Only complaint/gyn_ob (20) comes from gyn:install-pack. The complaint card asks for specialty='general' (visit-shell.component.html:250 → LookupCatalogService.php:41-44), so it gets []. The exam card then drops to the "no regions" fallback (exam-card.ts:79-86). No seeder or migration in Modules/* inserts either key (grep). So on general visits the two flagship pick-don't-type cards show nothing to pick, and roles without clinic.history.manage (dev: doctor, gyn_doctor) cannot even add values.
  • The exam Select⟷Text rule is keyed by a company-specific DB id (exam_finding_<parent row id>, exam-card.ts:153,157). A rule cannot be shipped in a default or copied between installs, and it breaks if a region is re-created. The prototype used stable keys (exam_general, exam_chest …). clinical_lookups.code exists (D2a) and should be the key.
  • BE inline-add does not validate parent_id (StoreLookupRequest.php:95: nullable|integer). The id is not checked to belong to the same company or lookup_key, so a value can be filed under another key's or tenant's category row. Its specialty is also not checked against known types, and the FE never sends it (clinical-lookup.service.ts:26-32). So every inline-added GYN complaint becomes core (visible to all specialties).
  • Cache drift across specialties. add() and toggleFavorite() patch only the key|specialty cache entry they were called with (clinical-lookup.service.ts:79-81,92-94). A value starred or added under gyn_ob does not show as starred or present in the same key's general cache until reload. Minor.
  • A failed list (403) re-fires HTTP on every changed bump. On error, catchError deletes the cache and never sets rows (service :63-66). Each box's effect (picker-box.ts:174-183) then calls list() again on every add or ★ anywhere. This is a perf leak for users without clinic.history.view.
  • Short GYN lists rendered open cannot be starred or searched by non-managers. The «+» that opens the dialog only shows when canAdd is true (gyn-lookup-field.component.ts:37,84), so a doctor without clinic.history.manage never reaches the ★ for lists of ≤6/≤7.

3. Top 5 gaps by owner impact

  • There is no seed catalog for general complaint and exam.finding (with regions), so the general visit's pick-don't-type cards are empty out of the box (Bug 1).
  • Inline add-new is gated on clinic.history.manage, which the stock doctor / gyn_doctor roles lack, and there is no catalog admin screen to fix or reorder or deactivate values. "Managed catalog" is half-done.
  • There is no client settings UI for field rules (Select⟷Text, visible, required) or for elements. The renderer honours only 2 element keys and 2 input rules, and the doctor-level override of fields is missing.
  • Diagnosis (no ★, no group browse) and History (legacy chips, no catalog) are not on the picker engine. Lab and rad keep their legacy pickers spread on the card instead of chips + a «اختيار» button.
  • GYN multi-selects (incl. categories) are stored as JSON code arrays in history answers, not link rows. That is better than CSV, but reports like "how many patients have X" still need JSON queries, which goes against spec guard (b).

Decision: favourites and add-new are REAL, not cosmetic. Favourites are persisted per doctor in clinical_lookup_favorites (lookups) and clinic_favorites (lab / rad / drug / service); the server sorts them first and they are scoped by company and user. Add-new is persisted, idempotent, and permission-checked on BE (clinic.history.manage) and FE. Their usefulness on general visits is limited by the empty seed catalog and by the permission most doctor roles lack.

D — التخصصات والإقامة (Specialties & inpatient)

Slice D — specialties (nutrition / dental / gyn) + inpatient context + clinic/center

Paths: FE = /home/moonui3/public_html/moon-erp/src/app/features/, VS = FE/clinic/visit-shell/, BE = /home/moonui3/moon-erp-be/Modules/. Proto = visit-shell-prototype.html.

1. Verdict table

#prototype item (proto line)implementation file:lineverdictnote
NUTRITION
N114 board cards as separate catalog cards, 3-col default (448-461, 494-502)VS/specialties/nutrition.cards.ts:28-72 (nut_measure + nut_board, 2 tabs, hideRail)📝 deferred (S2b)One bare card hosts the unchanged app-nutrition-consult-board. You can't move, hide or resize single cards, so the proto's «عيادة د. منى تشيل 3 كروت» example can't be done.
N2nut_patient (+ readonly vitals, ideal/required/water/fromFirst/outstanding/points/WHR/BMI) (868-871)FE/nutrition/visit/nutrition-consult-board.component.html:91-170 (finance 143-150, loyalty 151, WHR+risk 157-160, vitals 165)✅ as specReachable inside the board.
N3nut_quest yes/no + ✎ (872)board html:218-223 (+ app-nv-questionnaire-dialog 614)✅ as spec
N4nut_lifestyle (873)board html:244 app-nv-lifestyle-card✅ as spec
N5nut_appts + contact log (874)board html:247-300 (📞 log contact 252, + book 255, last contact 300)✅ as spec
N6nut_weights (+ وزن, InBody-due bar, sparkline, adherence ✎) (875-879)board html:326-430 (InBody overdue 336-344, spark 347-361, adherence 408-429)✅ as spec
N7nut_meas last-5 table incl. waist/hip (880)board html:444-494 (waist 479, hip 480, WHR 481)✅ as spec
N8nut_sessions (881)board html:503 app-nv-sessions-card✅ as spec
N9nut_plan (✎ / shopping / choose) (882)board html:505-533✅ as spec
N10nut_notes / nut_meds / nut_labs (pending + external result) / nut_photos (883-887)board html:543 / 550 / 558 (labs-card.component.ts:37-41 pending, 117 external dialog) / 598✅ as spec
N11nut_packages incl. «المنتجات المباعة» (886)board html:569-597⚠ partialPackages and remaining count are there. The products-sold sub-table isn't, anywhere on the board or in its model.
N12nut_progress (888)board html:324 app-nv-progress-card✅ as specConditional, as S2a's comment says.
N13Header actions rx/lab/rad/nut_plan_dialog/print_plan/note/next_appt/stop (497)board html:3-35 (rx 5, lab+rad dialog 8 & 668-669, plan 11, print 14, note 17, next 20, stop 32)✅ as specRad is a tab of the lab dialog, not a separate button.
N14Measurements tab + service-order railVS/specialties/nutrition-measure-card.component.ts:161-175✅ as specHosts the classic tab with the order rail at 358px.
N15vitalsPack waist/hip/whr in the vitals card (495, 809)BE Clinic/database/seeders/ClinicObservationDefinitionSeeder.php:88-89 (nut_waist/nut_hip defs) vs FE/clinic/services/clinic-vitals.service.ts:30-39 (VITALS_WHITELIST, 8 core) + vitals-panel.component.ts:215-217⚠ partialThe specialty definitions are seeded but the vitals card only renders the 8 core fields, and there's no nut_whr. The nutrition layout has no vitals card anyway. Waist/hip/WHR can be reached through body comp (N7) and WHR (N2).
N16intakePack nut_* (496)BE Clinic/app/Clinical/IntakeRegistry.php:59-67⚠ partialnut_goal, nut_goal_weight and nut_activity_level aren't intake codes; the goal lives in the nutrition patient profile. Other codes are renamed (naming drift).
DENTAL
D1dent_chart workspace F, required:true (463, 511)VS/specialties/dental.cards.ts:23-32 (dent_workspace, tab «مخطط الأسنان»)⚠ partialHosted unchanged, but it isn't required, so a client layout could drop it. That can't happen today because Decision 7 locks saves.
D2Odontogram FDI/Universal, permanent/primary, filter, legend (950-953, 919-923)FE/dental/workspace/dental-workspace.component.html:36-50 (filter, dentition, notation) + 107-119 (odontogram, legend incl. note/xr)✅ as specNotation default comes from dental.notation (ts:229).
D3Side panel (surfaces, proc/cond palettes, status, sections, lab, notes, imaging) (924-944)workspace ts:93-109 (surfaces/palette/procStatus/mobility/note/image) + html 107ff✅ as specSurfaces are gated by features.surfaces (ts:408), note templates by note_templates (ts:570).
D4Sub-views: chart/plan/perio/fmx/ortho/lab/recall tabs + quick mode (981-984, 947)workspace html:12-27 (chart/perio/ortho/xray tabs), 71 simple mode, 96-101 views, 300 plan panel, 315 recall, 355 lab-case dialog⚠ partialMatches the classic workspace. Plan sits under the chart, not as a tab. Lab board and recall are separate addable cards (dental.cards.ts:101-122), not workspace tabs.
D5dent_today (986)dental.cards.ts:51-59 → consult/dental-today✅ as spec
D6dent_exam: 12 items = 4 pain + 8 mouth (987-988)FE/dental/consult/dental-exam.component.ts:10,41-44✅ as specDriven by the dent_ pack (PAIN + swelling, mouth, notes).
D7historyPack dent_pain_* (504)dental-exam.component.ts:10✅ as specLives in the exam card, not the complaint card (classic).
D8dent_plan / dent_fmx as cards (466, 468)not in DENTAL_CARDS📝 deferred (S2c)Both are still reachable inside the workspace (D4).
D9dent_perio / dent_ortho / dent_lab / dent_recall with feature (467-471)dental.cards.ts:79-122 (feature perio/ortho/lab/recall + permission)✅ as specCatalog-only, not in the default.
D10settings mode/notation/features.* gate cards and tabs (508, 981)BE Dental/database/seeders/DentalSettingDefinitionSeeder.php:25-34; VS/visit-shell.component.ts:176-183, 540-546; workspace html:12-27, 96-101✅ as specSet in the clinic settings screen (group «الأسنان»). While meta loads, or if it fails, no lock is applied (documented).
D11Latex alert from dent_allergy_latex (507)BE Clinic/app/Support/ClinicProfilePacks.php:199 (storage=alert, alert_code latex); PatientFactsReader.php:60✅ as specShows as a shared header alert pill.
D12intakePack + intakeRefs (shared refs not re-asked) (505-506)BE IntakeRegistry.php:69-75⚠ partialThe pack also has dent_anticoagulant and dent_allergy_penicillin, which overlap the shared anticoagulant/allergies. They route into the same alert/condition store, so the data is one source, but the question is asked twice.
D13Default: complaint(shared)/today/exam/diagnosis/rx + collapsed vitals, history(patientScopeOnly), previous; actions lab/rad (510-514)dental.cards.ts:126-151; dental-more-card.component.ts:26-41 (vitals / history [patientScopeOnly]="true" / previous toggles + lab/rad dialog)✅ as specSame behaviour delivered as «المزيد». Kept the dental complaint card, dent_alerts and hidden rails (📝 S2c).
GYN
G1gyn_cats required, 4 group lines, chips + «+ اختيار» picker (473, 1087-1090)FE/gyn/gyn-shell-cards.ts:39-43 (required, station); FE/gyn/cards/gyn-category-picker.component.ts:95-117✅ as specAnswers stored as gyn_cat_<group>. With no category, a soft warning shows at sign (:131-139).
G2Locked group struck through «مقفول عند العميل (gyn.feature.x)» (1088; 25_mona)picker :48-51, 102; BE Gyn/app/Http/Controllers/GynSchemaController.php:130-142✅ as specWithout gyn.sexual.view the sex line is hidden entirely (stricter than proto). Fertility only hides infertility (GynFeatures.php:25-37, documented clinical deviation).
G3gyn_modules required host, show_if engine, first module open, «اتفتح من الاختيار» (474, 1062-1072)gyn-shell-cards.ts:44-48; FE/gyn/state/gyn-visit.store.ts:117-129, 142-150, 168-170; FE/gyn/engine/show-if.ts✅ as specFeature-off modules are filtered server-side (GynModuleRegistry::visibleFor, SchemaController:30). Core modules are excluded (gyn.models.ts:363).
G4Any non-core module addable as gyn_<key> card carrying its feature (1099-1101; §3)gyn-shell-cards.ts:155-174; VS/card-catalog.ts:59-68 (registerSpecCards)✅ as specLabour/vd/cs are folded into one gyn_delivery card. Private modules need gyn.sexual.view.
G5gyn_timeline (475)VS/card-catalog.ts:36 shared timeline card in GYN default (gyn-shell-cards.ts:128); BE Clinic TimelineService✅ as specRenamed: a shared timeline of visits, episodes (cycles and pregnancies, G3a), procedures and documents.
G6gyn_summary, 10 types (476, 1001, 1094)FE/gyn/cards/gyn-summary-card.component.ts:9-15, 41-46; BE Clinic/app/Clinical/ClinicalTypeRegistry.php:121-155✅ as specTypes are filtered by feature. Saves as a numbered PDF (C1c).
G7Private (Sexual) excluded from every summary type (1083; §3)BE Clinic/app/Services/Clinical/ClinicalSummaryService.php:55-70; FE/clinic/clinical/summary-card.component.ts:45-50⚠ partialExcluded by default and never in Couple, but there's an opt-in «تضمين المحتوى الخاص» for users holding clinic.fields.private. The card note says so.
G8Default flow order (521-523)gyn-shell-cards.ts:102-134✅ as specSuperset: adds first_visit, obhx, exam, imaging, operation, discharge, team, gyn_followup. Order kept.
G9vitalsPack lmp/ga/fundal/fhr/urine_alb (516)BE ClinicObservationDefinitionSeeder.php:95-96 (fundal, fhr defs); FE/gyn/cards/gyn-antenatal-flowsheet.component.ts:33-55 (GA, BP, weight, urine, FHR, FH…)⚠ partialNot in the vitals card (whitelist, see N15). It lives in the gyn_antenatal card and the pregnancy module (LMP), which are catalog cards or host-opened, not in the default.
G10historyPack gyn_bleeding / gyn_discharge (516)gyn-shell-cards.ts:177-189 (complaint slot); schema complaint fields gyn_complaint_hmb_*, _irreg_pattern, _disch_char/_assoc✅ as specEach complaint opens its own questions.
G11intakePack marital/spouse/marriage yrs/children/GPA/menarche/contraception (517)FE/gyn/cards/gyn-first-visit-card.component.ts:20-25, 104-118 (profile, spouse panel, menstrual); BE Gyn routes intake-status; GynExtensions.php:193-206 (GPAL, spouse)✅ as specUses the profile fields' in_intake flags. IntakeRegistry gyn_ob is empty (IntakeRegistry.php:75).
G12Pregnancy alert pill «حامل — 24 أسبوع» rose (518)FE/clinic/shared/patient-alert-strip/patient-alert-strip.component.ts:251-252, 313-320⚠ partialShown as a GA/EDD fact chip at the head of the facts line, not a rose alert pill (Fable decision). High-risk/VTE become alert pills.
G13gyn.feature.* fertility/obstetrics/urogyn/sexual/oncology (519)BE Gyn/app/Support/GynFeatures.php:18-61 (+operations)⚠ partialEnforcement is complete (schema, categories, cards, summaries, write guard 422). But the switches live only in clinic.visit_layouts['gyn_ob'].features. There's no setting definition and no FE screen, and saveClient doesn't send features (visit-shell.component.ts:782). Only the API can lock Urogyn/Sexual.
G14Layout saving for gynVS/visit-shell.component.ts:269-272✅ as specOpen for gyn_ob (flow, no tabs), locked for nutrition/dental (📝 Decision 7).
INPATIENT CONTEXT
I1addCards mar / nursing / discharge, F (529)VS/contexts/inpatient.context.ts:42-68; inpatient.cards.ts✅ as specSplit into inp_mar (L), inp_round (M), inp_nursing_notes (M, not for day case), inp_discharge. Each is permission-gated, and resolve-layout.ts:114-118 drops what the user can't use.
I2replace followup → discharge (529)inpatient.context.ts:65; resolve-layout.ts:120-131✅ as specA nurse without the discharge permission loses follow-up too (intended).
I3rx kind=inpatient (530, 849)inpatient.context.ts:66; VS/visit-shell.component.html:277-287 (app-inpatient-rx-composer when MAR-on-Rx, else app-clinic-prescription [kind])✅ as specWhen hospital.mar_on_prescriptions is off, the Rx card keeps its legacy behaviour.
I4MAR grid on prescription_items (890-891)contexts/inpatient/inpatient-mar-card.component.ts (hosts app-mar-board, [marOnRx], hideOrder)✅ as spec
I5Nursing: notes + rounds/NEWS2 (892)inpatient-round-card / inpatient-notes-card; FE/hospital/nursing/vital-round.component.html:3-10✅ as specNEWS2 badge, ACVPU and O2 are in. Per-parameter NEWS2 scores are 📝 deferred.
I6ctx vitalsPack rr/avpu/pain/o2/io/news2 (530)vital-round.component.html:7-10 (temp, pulse, BP, rr, spo2, pain, glucose, consciousness, on_oxygen) vs BE Hospital/app/Http/Controllers/NursingController.php:132-133 (accepts intake_ml/output_ml)⚠ partialLives in the round card, not the vitals card. There's no intake/output (I/O) input on the FE even though the BE accepts it.
I7Discharge: type/date/summary + «روشتة الخروج kind=discharge» + PDF (893)contexts/inpatient/inpatient-discharge-card.component.html:44-52, 67-70 ([kind]="'discharge'"), 81-90 print Rx, 100 discharge_summary document✅ as spec
I8Header bed · ward · day 2/4 + admitting doctor (530, 1172)VS/visit-shell.component.html:22; contexts/inpatient-stay.service.ts:70-80⚠ partialShows ward, bed and day N. There's no expected length of stay («/4») and no admitting doctor. Users without hospital.beds.view… get a bare «إقامة» chip (stay service :17-19).
I9orderingMode intent for inpatient (528)VS/visit-shell.component.ts:208 (encounter.ordering_mode)⚠ partialThe context doesn't force intent; it follows the company setting (default intent). A company set to immediate gets immediate orders on inpatient visits too.
CLINIC vs CENTER
C1CONTEXTS clinic/center: orderingMode immediate/intent, lab standalone/integrated, pharmacy formulary/products (526-527)BE Clinic/app/Support/OrderingMode.php:21-33; ClinicCatalogMode.php:26-36 (lab_mode + rad_mode); PharmacySource.php:23-27; EncounterResource.php:142-148; ClinicSettingDefinitionSeeder.php:210/226/258⚠ partialThere's no single "context" switch. It's 4 independent company settings (clinic.ordering_mode, clinic.lab_mode, clinic.rad_mode, clinic.pharmacy_source) set in the settings screen and sent per encounter. The lab/rad/rx cards read them. Same outcome, but a clinic must set all four.
GENERAL
GN1Default flow vitals → complaint → history → exam → diagnosis → lab → rad → rx → documents → followup (489-493)VS/card-catalog.ts:92-112✅ as specSame order and sizes, plus a collapsed timeline before followup (C1c).

2. Real bugs found (implemented but would misbehave)

  • GYN visit in the inpatient context ends up with two discharge cards and a leftover follow-up. The context replaces only the shared followup with inp_discharge (inpatient.context.ts:65). The GYN default also has gyn_discharge (gyn-shell-cards.ts:125) and gyn_followup (:131), and those stay. An admitted GYN patient sees «الخروج» twice (GYN discharge module + admission discharge order) plus the GYN follow-up schedule. It's not data-corrupting, but it is confusing.
  • Specialty and inpatient vitals definitions are seeded but never rendered. ClinicObservationDefinitionSeeder.php:88-112 seeds nut_waist/nut_hip/…, gyn_fundal_height/gyn_fetal_hr and rr/consciousness/pain/intake/output/news2, but VITALS_WHITELIST (clinic-vitals.service.ts:30-39) caps the vitals card at the 8 core fields. A client who enables or edits these definitions expecting them in «العلامات الحيوية» will see nothing. That's dead config, not a crash.
  • Inpatient round has no I/O fields. NursingController accepts intake_ml/output_ml (:132-133), but the round form (vital-round.component.html:7-10) has no inputs for them, so fluid balance can't be recorded from the shell.
  • GYN feature locks can't be set from the UI. GynFeatures reads clinic.visit_layouts['gyn_ob'].features (GynFeatures.php:9-11, 52-60). The FE save sends {clinical_type, layout} only (visit-shell.component.ts:782), and no setting definitions exist. The approved «عيادة د. منى» lock (screenshot 25) can only be made with a raw API call.

(Lesser: an inpatient nutrition visit prescribes through the board's own app-clinic-prescription dialog (board html:650) with no kind=inpatient, because the nutrition layout has no shared rx card for cardProps to reach.)

3. Top 5 gaps by owner impact

  • Nutrition is still one monolithic board (📝 S2b). Every board element can be reached today, but per-card hide/reorder for the client (the proto's main point for nutrition) isn't possible.
  • There's no UI for gyn.feature.* (fertility/obstetrics/urogyn/sexual/oncology/operations). Enforcement works, but an admin can't turn a group off.
  • Specialty and context vitals packs (waist/hip/WHR, FHR/fundal/urine/LMP-GA, RR/AVPU/I-O) aren't in the vitals card. They're spread over body comp, the antenatal card and the round card, and the round card has no I/O.
  • Clinic vs center isn't one context. It's 4 separate settings (ordering/lab/rad/pharmacy), and inpatient doesn't force intent ordering.
  • The inpatient header has no admitting doctor or expected length of stay, falls back to a bare chip for users without hospital.beds.view, and a GYN admission shows duplicate discharge/follow-up cards.

حدود المراجعة: مقارنة الكود بالنموذج والمواصفة (design-visit-shell.md) + صور من الشاشة الحية لـ 7 زيارات. متعملش ضغط فعلي على كل زرار، ولا اتشغلت اختبارات، ولا اتعدل أي كود.