قبل ما نطبخ — قاعدة المشروع الذهبية: الـ Flow ثابت تماماً. اللي بيختلف بس نقطتين.
/lab/investigations لما الـ admin يختار النوع الجديد، حقول إعدادات إضافية تظهر (max files / antibiotic panel / requires peer review)الـ ResultType enum يدعم 5 أنواع. كل نوع له تعامل خاص في 5 صفحات (investigation form, kanban, results entry, validation worklist, PDF report). أي نوع جديد لازم يـ "يتعلم" في كل المسارات دي.
| النوع | الاستخدام | التخزين | الواجهة |
|---|---|---|---|
| numeric | قيمة رقمية واحدة (مثل HGB = 14) | result_value (text) | input number + range/flag |
| text | نص قصير (مثل blood group A+) | result_value | input text |
| selection | اختيار من قائمة (positive/negative) | result_value + selection_options JSON على الـ investigation | dropdown |
| formula | محسوب تلقائي من اختبارات أخرى | result_value (محسوب) + formula + formula_dependencies على الـ investigation | read-only |
| memo | نص طويل (تعليق) | result_value أو result_text | textarea |
result_value string. مفيش نوع structured (مصفوفة، شجرة، multi-record)، ومفيش نوع binary (ملف). الأنواع الجديدة هتـ كسر النمط ده.
كل نوع جديد لازم يـ تتبع في الأماكن الـ 7 دي. هنا الـ baseline اللي الـ agents فحصوها.
| # | الـ Layer | الملف | اللي بيتغير لكل نوع |
|---|---|---|---|
| 1 | Backend Enum | Modules/LIS/app/Enums/ResultType.php |
إضافة case File / Culture / Histopathology |
| 2 | Backend Schema | Modules/LIS/database/migrations/...lab_results.php...lab_investigations.php |
أعمدة جديدة (انظر تفاصيل كل نوع) |
| 3 | Backend Service | Modules/LIS/app/Services/LabResultService.php (enter, triggerFormulaCalculations) |
تخطي الـ abnormal flag للأنواع الجديدة، وقبول payload منظم |
| 4 | Investigation form (FE) | src/app/features/lis/investigations/lis-investigations.component.ts line 207 |
إضافة الخيار في resultTypeOptions + إعدادات خاصة بالنوع |
| 5 | Result entry UI (FE) | kanban/lis-kanban.component.ts line 790, results/lis-results.component.ts,validation-worklist.component.html line 481, dept-worklist.component.html line 254 |
branch جديد على resultType === النوع الجديد |
| 6 | Report (PDF + HTML) | core/services/lis-print-report.service.ts,lis-report-pdf.service.ts, lis-html-report.service.ts |
renderer مخصص للنوع الجديد بدل الـ default row |
| 7 | Patient portal | features/lis/patient-portal-public/pp-request-detail.component.ts |
عرض النتيجة بشكل المناسب للعميل (file download، C&S table، histopath sub-report) |
المختبر يرفع ملف أو أكتر كنتيجة (مثل تقارير أشعة، PDFs من جهاز خارجي، صور كاميرا ميكروسكوب). العميل يـ download من بوابته.
attachments القائمة (polymorphic)مفيش جدول جديد. الـ attachments القائمة في Core polymorphic أصلاً، نـ morph عليها LabResult.
// migration: add file_settings JSON to lab_investigations
$table->json('file_settings')->nullable();
// { max_files: 10, max_size_mb: 10, allowed_mime: ["application/pdf", "image/*"], required_min: 1 }
// LabResult model — new relation:
public function attachments() {
return $this->morphMany(Attachment::class, 'attachable');
}
{ label: 'RESULT_FILE', value: 'file' } في resultTypeOptionsfile يظهر panel فيه: max files, max size MB, allowed mime types (multiselect), min requiredcomponent جديد ResultFileCellComponent يستخدم PrimeNG p-fileUpload (multiple, custom mode). يظهر:
file_settingsإعادة استخدام endpoint الـ attachments العام (موجود أصلاً)، مع endpoint LIS صغير للـ transition:
POST /api/core/attachments (multipart, موجود)
GET /api/core/attachments?attachable_type=...
POST /api/lis/results/{id}/finalize-files ← جديد
→ يفحص count >= required_min
→ يـ set result_value = "{n} files"
→ ينقل الـ status من pending → entered
jsPDF.addImage().AttachmentController::store لما المرفق LabResult)triggerFormulaCalculations يتخطى الـ file resultslis.results.upload-filelis.results.delete-fileتقرير ميكروبيولوجي structured: عينة → نمو → كائن (أو أكتر) → matrix من المضادات الحيوية (S/I/R + MIC). الـ TAT أيام مش دقايق.
تقرير C&S القياسي فيه:
10⁵ CFU/mLresult_payload JSON على lab_results// migration:
$table->json('result_payload')->nullable();
// payload shape:
{
"version": 1,
"incubation_started_at": "2026-05-22T08:00:00Z",
"incubation_hours": 48,
"macroscopy": "Cloudy urine, RBCs +",
"growth_type": "pure|mixed|no_growth|contaminated",
"interpretation": "Significant bacteriuria...",
"organisms": [
{
"name": "Escherichia coli",
"colony_count": "1e5",
"colony_count_unit": "CFU/mL",
"ast": [
{ "antibiotic_id": 12, "code": "NIT", "result": "S", "mic": "16", "mic_unit": "µg/mL" },
{ "antibiotic_id": 13, "code": "AMP", "result": "R", "mic": null }
]
}
]
}
lab_antibiotics جديدlab_antibiotics
id, company_id, code, name, name_ar, abbreviation,
drug_class (penicillin/quinolone/...), is_active, sort_order
seed بـ ~60 مضاد من CLSI starter. ميصحش نـ overload lab_investigations لأن المضادات مش tests يـ billable.
عمود JSON جديد culture_config على lab_investigations:
{
"default_antibiotic_ids": [12, 13, 14, 15],
"default_incubation_hours": 48,
"growth_types_allowed": ["pure", "mixed", "no_growth", "contaminated"]
}
الـ resultType === 'culture' ميظهرش inline input. يظهر زر "Enter culture" يفتح CultureResultDialogComponent:
┌─ Specimen header (read-only) ────────────────────┐ │ Patient · MRN · Test code · Barcode · Day 2/3 │ ├─ Growth ────────────────────────────────────────┤ │ ( ) No growth ( ) Pure (•) Mixed flora │ │ Macroscopy: [textarea] │ │ Incubation started: [datetime] Hours: [48] │ ├─ Organisms ──────────────────── [+ Add organism]┤ │ ┌ Organism 1 ────────────────────────── [X] ──┐ │ │ │ Name: [autocomplete: E.coli, ...] │ │ │ │ Colony count: [1e5] [CFU/mL ▾] │ │ │ │ AST grid: │ │ │ │ ┌────────────┬───┬───┬───┬──────┬──────┐ │ │ │ │ │ Antibiotic │ S │ I │ R │ MIC │ Unit │ │ │ │ │ │ Nitrofur. │(•)│( )│( )│ 16 │µg/mL │ │ │ │ │ │ Ampicillin │( )│( )│(•)│ │ │ │ │ │ │ └────────────┴───┴───┴───┴──────┴──────┘ │ │ │ └─────────────────────────────────────────────┘ │ │ Interpretation: [textarea, optional] │ ├──────────── [Save draft] [Submit] ──────────────┤
تعديل POST /lis/results/{id}/enter ليقبل result_payload اختياري:
POST /lis/results/{id}/enter
{
"result_value": "E. coli 1e5 CFU/mL — S to NIT, R to AMP", // ملخص للقوائم
"result_payload": { ...culture payload }, // مطلوب لـ culture
"comment": "..."
}
قاعدة validation جديدة: لو investigation.result_type === 'culture' فالـ result_payload required.
entered writable لحد الـ provisional submitإعادة استخدام lis.results.enter|validate|approve|release. صلاحية واحدة جديدة بس:
lis.antibiotics.view / .manage — للـ CRUD page الجديد| Antibiotic | S | I | R | MIC | Unit |
|---|---|---|---|---|---|
| Nitrofurantoin (NIT) | S | I | R | µg/mL | |
| Ampicillin (AMP) | S | I | R | µg/mL | |
| Ciprofloxacin (CIP) | S | I | R | µg/mL | |
| TMP-SMX | S | I | R | ||
| Cefuroxime | S | I | R | µg/mL | |
| Gentamicin | S | I | R |
| Antibiotic | Result | MIC (µg/mL) |
|---|---|---|
| Nitrofurantoin | S | 16 |
| Ampicillin | R | 32 |
| Ciprofloxacin | S | 0.25 |
| TMP-SMX | I | — |
| Cefuroxime | S | 8 |
| Gentamicin | S | — |
تقرير باثولوجي طويل ومفصل (نص + صور slides + كود ICD). الـ TAT أيام، فيه مراجعة من باثولوجي ثاني (peer review). أشبه بـ Word document متعدد الأقسام مع صور مرفقة.
lab_histopath_resultsهنا الـ structure ثابت ومعقد ومفيد للبحث/التدقيق، فجدول dedicated أفضل من JSON.
lab_histopath_results
id, company_id, lab_result_id (FK unique),
gross_description TEXT,
microscopic_description TEXT,
diagnosis TEXT,
comment TEXT,
recommendation TEXT,
icd_code VARCHAR(20),
icd_o_code VARCHAR(20),
tnm_stage VARCHAR(50),
margin_status VARCHAR(50),
ihc_markers JSON, -- {ER:"+",PR:"-",HER2:"2+"}
specimen_received_at TIMESTAMP,
grossing_at TIMESTAMP,
draft_saved_at TIMESTAMP,
peer_reviewed_by BIGINT, peer_reviewed_at TIMESTAMP,
signed_off_by BIGINT, signed_off_at TIMESTAMP,
created_by, updated_by, timestamps, softDeletes
الصور تستخدم attachments القائمة (نفس النمط بتاع الـ file type).
'histopathology' في resultTypeOptionslab_investigationslab_investigations: requires_peer_review، requires_grossingالـ resultType === 'histopathology' ميظهرش inline input. من نفس مكان "Enter result" في الـ kanban / results page، يفتح HistopathResultDialogComponent — modal كبير فيه tabs. مفيش route جديد، مفيش page جديدة. UI tabs:
[Header: Patient | Request # | Specimen | Status] [Tabs: Gross | Microscopic | Diagnosis | IHC/Staging | Images | Audit] - Gross: rich-text editor + specimen_received_at + grossing_at - Microscopic: rich-text editor - Diagnosis: rich-text editor + ICD code + ICD-O autocomplete - IHC/Staging: ihc_markers grid + TNM stage + margin status - Images: multi-upload drag-drop + captions - Audit: timestamps + signoff history [Footer: [Save Draft] [Submit for Peer Review] [Sign-off & Release]]
npm install quill@^2 + استخدام PrimeNG p-editor. لو الـ team عايز يتجنب الـ dep ده، البديل markdown textarea (أقل احترافية للباثولوجيين).
pending → entered: "Submit for Peer Review" — يـ set draft + يـ submitentered → validated: peer pathologist signs (يـ set peer_reviewed_by)validated → approved: consultant signoff (يـ set signed_off_by)approved → released: الإفراج للعميلPUT /lis/results/{id}/histopath ← حفظ draft / تحديث structured payload
POST /lis/results/{id}/histopath/submit ← pending → entered
POST /lis/results/{id}/histopath/peer-review ← entered → validated
POST /lis/results/{id}/histopath/images ← multipart upload صور
DELETE /lis/results/{id}/histopath/images/{aid}
الـ approve و release standard endpoints بدون تغيير.
التقرير العادي grid (سطر لكل نتيجة). تقرير الـ histopath narrative بصفحة كاملة. الحل:
result_type === 'histopathology' صفحة dedicatedlis.results.histopath.draft — أي باثولوجي يقدر يـ draftlis.results.histopath.peer_review — باثولوجي خبرةlis.results.histopath.signoff — consultant final approveSections show fragments of breast tissue showing infiltrating tumor cells arranged in cords, nests and tubules within a desmoplastic stroma.
The tumor cells are moderately pleomorphic with vesicular nuclei, prominent nucleoli and eosinophilic cytoplasm. Mitotic figures are present (8/10 HPF).
No definite vascular or perineural invasion is identified in the available sections.
No DCIS component identified...
| ICD-10: C50.4 | ICD-O: 8500/3 |
| TNM stage: pT1b N0 M0 | Margins: Free |
| IHC: ER +ve (~80%) · PR +ve (~60%) · HER2: 1+ (negative) · Ki-67: 15% | |
6 تذاكر إجمالاً (3 backend + 3 frontend). لازم الـ backend يتعمل أولاً، بعدها الـ frontend بـ parallel.
file result type + investigation file_settingsFile + lang stringsfile_settings JSON على lab_investigationsLabResult::attachments() morph relation + Resource enrichmentPOST /lis/results/{id}/finalize-files + LabResultService::finalizeFiles()StoreAttachmentRequest للـ LabResult-scoped mime/size validationlis.results.upload-file + delete-filereset() لـ soft-delete attachmentsculture result type + antibiotics master dataCulture + lang stringslab_antibiotics table; result_payload JSON على lab_results; culture_config JSON على lab_investigationsLabAntibiotic model + CRUD + Resource + Seeder CLSIEnterLabResultRequest (و sibling receive-results requests): result_payload required لو investigation type = cultureLabResultService::enter() يقبل ?array $payloadapiResource('antibiotics', ...)lis.antibiotics.view|managehistopathology result type + signoff workflowHistopathologylab_histopath_results table; flags requires_peer_review + requires_grossing على lab_investigationsLabHistopathResult model + LabResult::histopath() hasOne relationPUT /histopath, POST /submit, POST /peer-review, POST /images, DELETE /images/{aid}LabResultService::enter() يـ dispatch للـ LabHistopathResultService الجديدةhistopath.draft|peer_review|signoffresult_type === 'histopathology'file option + file_settings panelResultFileCellComponent (upload, list, delete, thumbnails)lis-results.component.ts, kanban result dialog, dept-worklist, validation-worklistLIS.INVESTIGATIONS.RESULT_FILE, LIS.RESULTS.FILE_*LisInvestigation, LisResultLisAntibioticService + model + CRUD screen features/lis/antibiotics/culture في resultTypeOptionsCultureResultDialogComponent (shared): organisms array + AST grid + interpretationLIS.CULTURE.*npm install quill@^2 + استخدام EditorModulehistopathology في resultTypeOptionsHistopathResultDialogComponent (modal) — tabbed editor (Gross / Microscopic / Diagnosis / IHC / Images / Audit)renderHistopath() sub-reportLIS.HISTOPATH.*الأسهل لأنه يـ reuse الـ attachments القائمة. لو نجح هنا، الـ infra الـ polymorphic بقت مختبرة قبل ما نـ depend عليها في الـ histopath.
الجدول الجديد lab_antibiotics + الـ result_payload JSON pattern. الـ UI dialog منفصل فمكنش هياثر على kanban الموجود.
الأصعب: جدول جديد، 5 endpoints، rich-text editor جديد، صفحة worklist جديدة، تعديل الـ 4 HTML report templates، patient portal sub-report. لازم يكون آخر phase.
| المخاطرة | التأثير | المنع |
|---|---|---|
كسر الـ existing 5 types عند تعديل EnterLabResultRequest |
عالي | regression tests كاملة قبل أي backend deploy + result_value يـ stay required للـ 5 types القديمة |
| الـ Quill peer dep تكبر الـ bundle بـ ~200KB | متوسط | lazy-load الـ histopath route + Quill imports — يـ trigger الـ download بس لما الباثولوجي يدخل |
الـ patient portal عنده core.attachments.view permission gap |
عالي (vital — العميل مش هيقدر يـ download) | يـ wire portal auth guard ليـ grant attachment-read أوتوماتيك لـ released results بتاع المريض |
| الـ Westgard QC و formula triggers يحاولوا تشغيل numeric checks على الأنواع الجديدة | متوسط — يـ throw exceptions | guard في LabResultService يـ skip لو result_type مش numeric|formula |
| الـ HTML report templates الـ 4 لازم كلهم يدعموا الـ renderHistopath | عالي — لو واحد بس يشتغل، الـ user اللي مختار غيره يـ see broken layout | shared helper function يـ reusable + visual regression test لكل template |
requires_peer_review flag يـ default false، الـ admin يـ enable لكل investigation.