خطة نقل منطق الـWorklist / Validation من الفرونت للباك

بناءً على تقييم الأداء: كمية المنطق (تجميع البنلات، النَسب، الصفوف المقفولة، رولأب الحالة، إزالة التكرار، ملخّص الطلبات) جوّه المتصفح أكبر من اللازم، ومتكررة في صفحتين. الخطة دي بتنقل ده كله للباك إند كـ«مصدر حقيقة واحد»، بدون كسر أي وظيفة، وبخطوات آمنة قابلة للتراجع.

🗓️ 2026-06-08🧬 LIS — Worklist + Validation🎯 Backend-first · Single source of truth · Zero-downtime migration

1 الوضع الحالي — أرقام حقيقية

حجم الكود

validation-worklist.ts3,143 سطر
dept-worklist.ts2,324 سطر
الإجمالي5,467 سطر

round-trips عند فتح الصفحة

٥–٦ نداءات قبل أول رسم: results + sample-investigations + samples + sections + referrals + investigations (رنجات + أعضاء بنل).

منطق مكرّر في الصفحتين (نفس الدالة مرتين)

أي إصلاح لازم يتعمل مرتين → ومع الوقت بيختلف السلوك (زي ما حصل في باگ التكرار اللي لسه مصلّحينه):

الدالةالمسؤوليةمكررة؟
deduplicateResultsإزالة تكرار النتيجة لكل (طلب+تحليل)، إبقاء أعلى حالة، تجاهل أصل الـretestفي الصفحتين
processResultsتحويل النتائج الخام لصفوف + تحديد العضو/البنل + حالة العينةفي الصفحتين
تركيب أب البنل (synthesis)تصنيع صف رأس بنل وهمي من الـpivotsفي الصفحتين
الصفوف المقفولة (locked rows)أحدث pivot لكل تحليل؛ لو مرفوض/ملغي/مؤجّل/خارجي → الصف يعكس دهفي الصفحتين
صفوف «مش وصلت المعمل» (pre-receivable)تصنيع صفوف مقفولة + ليبل حسب حالة العينةفي الصفحتين
displayRows groupingمستقل/بنل/sub-section + ترتيب + dedupفي الصفحتين
requestCardsملخّص حالة كل طلب (pending/entered/validated/approved/released) + progressفي الصفحتين
rollupPanelStatusحالة البنل = أضعف عضوفي الصفحتين
calcFlag · localDateOfالـflag الشاذ + فلترة التاريخ بتوقيت الشركةفي الصفحتين

الخلاصة

المتصفح بيعمل شغل «خادم»: ٥–٦ نداءات ثم تجميع/فرز/فلترة لكل النتائج في الذاكرة، مكرر مرتين. ده بطيء في الأيام المزدحمة، صعب الصيانة، ومعرّض لاختلاف السلوك بين الصفحتين.

2 المبدأ — «مصدر حقيقة واحد» في الباك

قبل: Angular ──5/6 نداءات──► API ──خام──► [تجميع/نَسب/locked/rollup/dedup في المتصفح] ──► رسم (نفس المنطق مكرر في validation + dept) بعد: Angular ──نداء/نداءين──► /lis/worklist/* ──جاهز للرسم──► رسم بسيط └─ كل المنطق في WorklistContextService (PHP) = مصدر واحد للصفحتين

القاعدة: الباك بيرجّع البيانات «جاهزة للعرض» (cards + rows مجمّعة ومرتّبة وبحالة محسوبة)، والفرونت يبقى راسم رفيع بس (templates + inputs + dialogs + UI state). أي منطق تجميع/حالة/نَسب → الباك.

3 المعمارية المستهدفة

أ. خدمة باك واحدة: WorklistContextService

كلاس PHP جديد يجمع كل منطق التجميع المنقول من الـTS (نسخة واحدة، الصفحتين بتستهلكوها). يعتمد على الخدمات الموجودة (LabResultService, SampleInvestigationService, LabWorkflowService) ومنطق النَسب الموجود فعلاً في SampleInvestigationController::loadPanelMap (اللي صلّحناه بـis_panel=1).

ب. ٣ endpoints (نفس الـpayload للصفحتين، باختلاف الصلاحيات/الفلاتر)

Endpointبيرجّعيحل محل
GET /lis/worklist/cardsكروت الطلبات + ملخّص الحالة + progress لمدى تاريخ/قسم/فلترrequestCards() + جزء من results/samples
GET /lis/worklist/requests/{id}/rowsصفوف العرض الجاهزة لطلب واحد: بنلات + أعضاء + sub-sections + locked + pre-receivable + رنجات + flag + رولأب + المصدرdisplayRows() + processResults() + synthesis + locked + dedup
GET /lis/worklist/search?q=بحث التحليل عبر الطلبات (server-side، مش فلترة client)بحث الـValidation الحالي client-side

ج. الفرونت بعد النقل

WorklistDataService (FE) رفيع: ينده الـendpoints ويـmap للـrows جاهزة. الصفحتان تستخدموه → التكرار يختفي. الكتابة (enter/validate/approve/release) تفضل زي ما هي (endpoints موجودة).

4 العقد (Contract) — شكل الـpayload

نثبّت شكل الرد أول حاجة (عشان الفرونت يتبنى عليه بثقة):

// GET /lis/worklist/requests/115/rows { "request": { "id":115, "number":"LR-2026-00302", "patient":{...}, "branch_id":8 }, "summary": { "pending":0, "entered":0, "validated":0, "approved":0, "released":2, "total":2, "progress_label":"Released", "primary_action":"print" }, "rows": [ { "type":"panel", "investigation_id":2764, "code":"HBA1C602", "name":"...", "status":"released", "member_count":{"entered":2,"total":2} }, { "type":"test", "result_id":638, "panel_id":2764, "investigation_id":9, "code":"HBA1C0", "value":"9.0", "unit":"%", "ref":"4.0–5.6", "flag":"high", "status":"released", "source":"manual", "not_receivable":false, "result_type":"numeric" }, { "type":"test", "result_id":639, "panel_id":2764, "investigation_id":3924, "code":"eAG", "value":"211.6", "unit":"mg/dL", "status":"released", "result_type":"formula" } ] }

الباك بيضمن: صف واحد لكل (طلب+تحليل) · النَسب صح (is_panel=1) · الترتيب جاهز · الحالة محسوبة · الـlocked/not-receivable محدّدة. الفرونت يرسم بدون أي تجميع.

5 مراحل التنفيذ — آمنة وقابلة للتراجع

مرحلة 0

الأساس والأمان سلامة

الهدف: نتحرّك من غير ما نكسر حاجة.
  • تثبيت العقد (DTO/Resource) للـ rows + cards كـ«مرجع» متفق عليه.
  • متانة result_type: أي نوع غير معروف ميكسرش الصفحة — يتعامل كـ«نصّي» بدل ما يطيّح التحميل كله (التقييم نبّه على ده).
  • Parity harness: سكربت يقارن مخرجات الباك الجديد مع منطق الفرونت الحالي على طلبات حقيقية (نفس الـrows؟ نفس الـcounts؟) — ده ضمان «بدون إخلال بالوظائف».
مرحلة 1

نقل المنطق للباك — WorklistContextService Backend

الهدف: مصدر حقيقة واحد للتجميع.
مرحلة 2

الـEndpoints الموحّدة Backend

الهدف: نداء/نداءين بدل ٦.
مرحلة 3

مستهلك فرونت رفيع خلف feature-flag Frontend سلامة

الهدف: تشغيل مزدوج (legacy + جديد) بدون مخاطرة.
مرحلة 4

التبديل + حذف التكرار Frontend

الهدف: صفحة رفيعة، مصدر واحد.
مرحلة 5

تقوية الأداء + المراقبة Backend Frontend

الهدف: سريع وثابت ومقيس.

6 إزاي منكسرش أي وظيفة (Risk Management)

1) Parity أولاً

مانحذفش أي منطق فرونت قبل ما الباك يطلّع نفس الـrows/cards على طلبات حقيقية (سكربت مقارنة آلي).

2) Feature flag + تشغيل مزدوج

الجديد خلف فلاج. لو فيه فرق → نرجّع للقديم فوراً بدون deploy.

3) صفحة-صفحة

validation الأول، نتأكد أسبوع، بعدها dept. مش الاتنين مرة واحدة.

4) العقد مثبّت

شكل الـpayload متفق عليه ومُوثّق — الفرونت يتبنى عليه بثقة، وأي تغيير versioned.

5) متانة الأنواع

نوع نتيجة غير معروف ميكسرش الصفحة (يتعامل نصّي) — مشكلة نبّه عليها التقييم.

6) رجوع نظيف

كل مرحلة منفصلة وقابلة للـrollback؛ الكتابة (enter/validate/...) ما تتغيّرش خالص.

7 إيه يتنقل للباك وإيه يفضل في الفرونت

يتنقل للباك

  • dedup النتائج (طلب+تحليل، أعلى حالة)
  • نَسب البنل + تركيب رأس البنل + sub-sections
  • الصفوف المقفولة + «مش واصلة المعمل»
  • displayRows grouping + الترتيب
  • ملخّص الطلب + rollup حالة البنل + primary action
  • الـflag الشاذ + فلترة التاريخ بتوقيت الشركة
  • بحث التحليل عبر الطلبات

يفضل في الفرونت

  • الرسم (templates, inputs, dialogs)
  • UI state: الطلب المختار، توسيع البنل، صندوق البحث
  • autosave (مع debounce + إيقاف وقت bulk)
  • اختصارات الكيبورد، اختيار الأعمدة
  • نداء أزرار الكتابة (enter/validate/approve/release) — زي ما هي

8 مقاييس النجاح

المقياسالآنالهدف
نداءات الشبكة عند الفتح٥–٦١–٢
منطق التجميع في المتصفحمكرر × صفحتينصفر (السيرفر)
حجم كود الصفحتين5,467 سطر−٥٠٪+
زمن أول رسم (يوم مزدحم)مقيس + أسرع
سهولة الصيانة5/10 (التقييم)٨+/10

9 قرارات محتاجها قبل ما أبدأ التنفيذ

  1. endpoint واحد ولا مفصول؟ أنصح مفصول (cards خفيف للقائمة + rows للطلب المختار) عشان نجيب الطلب المختار بس. توافق؟
  2. نبدأ بأي صفحة؟ أنصح validation الأول (الأثقل) كـpilot، بعدها dept. تمام؟
  3. التشغيل المزدوج (feature flag): موافق ناخد المرحلة دي للأمان (تأخّر بسيط مقابل صفر مخاطرة)؟
  4. الأولوية: أبدأ بـ مرحلة 0+1 (الأساس + خدمة الباك) وأرجّعلك parity، ولا تحب نبدأ بإصلاح متانة result_type ومنع كسر الصفحة الأول؟

ملاحظة

دي خطة معمارية كبيرة بتتنفّذ على مراحل آمنة — كل مرحلة لوحدها بتدّي قيمة وقابلة للتراجع. مش محتاجين نعمل كله مرة واحدة. أنا جاهز أبدأ بمرحلة ٠+١ أول ما توافق على المسار.