Files
clinicpro/docs/new_feture/dental-module/phase-3-dashboard.md
T
hamed 2f030edef1 Add dental module phase 4 and phase 5 documentation, including treatment estimates, clinical operations, and preset content
- Introduced phase 4 documentation detailing treatment estimates, data models, state machines, API endpoints, and new dashboard metrics.
- Added phase 5 documentation covering consumables, lab operations, periodontal charts, sterilization cycles, clinical images, and a checklist for professional review.
- Created a preset content document outlining default dental service packages and protocols for clinics.
2026-08-20 16:21:08 +03:30

10 KiB
Raw Blame History

فاز ۳ — داشبورد دندانپزشکی

پیش‌نیاز: فاز ۱ و ۲. خروجی قابل تست: مدیر و پزشک و پذیرش، شاخص‌های دندانپزشکی را در همان داشبورد فعلی خودشان می‌بینند.


۱. هدف

شاخص‌های مخصوص حوزهٔ فعالیت به داشبورد نقشی اضافه شوند، بدون دست‌زدن به منطق داشبورد عمومی و بدون کند کردن داشبورد حوزه‌های دیگر.


۲. الگو

همان الگوی TreatmentWorkflow که در docs/adr/0005 ثبت شده.

src/Dashboard/Metric/DomainMetricProvider.php     اینترفیس، با تگ app.domain_metric_provider
src/Dashboard/Metric/DomainMetricRegistry.php     انتخاب بر اساس کد حوزه
src/Dashboard/Metric/MetricRequest.php            بازه، نقش، محیط، فیلترها
src/Dashboard/Metric/MetricSet.php                خروجی استاندارد
src/Dental/Metric/DentalMetricProvider.php        پیاده‌سازی دندانپزشکی

اینترفیس:

interface DomainMetricProvider
{
    public function supports(?string $practiceDomainCode): bool;

    /** @return list<string> کلید شاخص‌هایی که این نقش می‌بیند */
    public function keysFor(string $role): array;

    public function collect(MetricRequest $request): MetricSet;
}

محیطی که حوزه‌اش null است یا provider ندارد، هیچ بخش تازه‌ای نمی‌گیرد. برخلاف TreatmentWorkflowRegistry اینجا پیاده‌سازی پیش‌فرض لازم نیست؛ نبودن provider یعنی بخش دندانی رندر نمی‌شود. دلیل: شاخص خالی بدتر از نبودن بخش است.

هر متد collect باید تعداد کوئری ثابت داشته باشد، مستقل از تعداد شاخص. یعنی شاخص‌های هم‌منبع در یک کوئری جمع شوند.


۳. رجیستری شاخص‌ها

هر شاخص یک کلید ثابت دارد. فرانت هیچ فرمولی محاسبه نمی‌کند و فقط مصرف‌کنندهٔ عدد است. دلیل: اگر تعریف «تولید» در دو جا نوشته شود، دیر یا زود دو عدد متفاوت نشان داده می‌شود.

شاخص‌های قابل محاسبه در فاز ۳

کلید تعریف منبع
production جمع مبلغ ناخالص ویزیت‌های انجام‌شده در بازه patient_sessions
collection جمع پرداخت‌های ثبت‌شده در بازه payments و session_payments
collection_rate وصولی تقسیم بر تولید مشتق
avg_per_visit تولید تقسیم بر تعداد ویزیت مشتق
production_per_doctor تولید به تفکیک پزشک patient_sessions
service_mix سهم هر گروه کاتالوگ از تولید session_services و service_catalog_categories
chair_utilization دقایق رزروشدهٔ منابع نوع اتاق تقسیم بر دقایق ظرفیت resource_occupancy و resource_calendars
no_show_rate نوبت‌های حاضرنشده تقسیم بر کل نوبت‌ها appointments
cancellation_rate نوبت‌های لغوشده تقسیم بر کل appointments
new_patients بیماران با اولین ویزیت در بازه patient_sessions
ar_outstanding جمع بدهی معوق بیماران patient_sessions
treatments_by_group تعداد خدمت انجام‌شده به تفکیک گروه دندانی session_services
teeth_treated تعداد دندان‌های درمان‌شدهٔ یکتا در بازه session_services

شاخص‌هایی که در این فاز وجود ندارند

کلید چرا
case_acceptance_rate ورودی‌اش برآورد درمان است که فاز ۴ ساخته می‌شود
unscheduled_treatment همان
redo_rate نیازمند حالت درمان مجدد روی ردیف برآورد
lab_cost_ratio فاز ۵
consumable_cost_ratio فاز ۵
recall_response_rate نیازمند سازوکار ریکال که پروژه هنوز ندارد

این فهرست عمداً در سند مانده تا کسی فکر نکند فراموش شده‌اند.

دو تعریف که نباید قاطی شوند

تولید جمع مبلغ کاری است که انجام شده. وصولی جمع پولی است که رسیده.

این دو از دو جدول متفاوت می‌آیند و هیچ‌کدام نباید از دیگری استنتاج شود. فاصلهٔ بینشان همان چیزی است که نرخ وصول را معنادار می‌کند.


۴. نماها

نقش شاخص‌های صفحهٔ اصلی
مدیر کلینیک یا مطب collection, collection_rate, chair_utilization, new_patients, ar_outstanding, service_mix
پزشک تولید شخصی، avg_per_visit خودش، treatments_by_group خودش، teeth_treated
پذیرش اشغال یونیت امروز، no_show_rate, cancellation_rate, صف نوبت امروز

حداکثر شش کارت در صفحهٔ اصلی هر نقش. بقیه پشت drill-down. دلیل: داشبورد با بیست کارت خوانده نمی‌شود و کاربر به‌جای تصمیم، اسکرول می‌کند.

فیلتر مشترک: بازهٔ تاریخ جلالی، پزشک، گروه خدمت، یونیت.


۵. API

اندپوینت‌های نقشی موجود دست نمی‌خورند. پاسخشان یک کلید تازه می‌گیرد:

GET /api/v1/dashboard/clinic
    → { ..., domain_metrics: { code: "dental", metrics: { ... } } | null }

اگر محیط حوزه ندارد یا provider ندارد، مقدار null است.

یک اندپوینت تازه برای بازه و روند:

GET /api/v1/dashboard/domain-metrics?from&to&doctorUuid?&resourceUuid?&groupUuid?
    → { code, metrics: { <key>: { value, previous_value, change_percent } } }

GET /api/v1/dashboard/domain-metrics/trend?metric=production&from&to&interval=day|week|month
    → { points: [ { date, value } ] }

date میلادی برمی‌گردد و تبدیل جلالی در فرانت انجام می‌شود. دلیل: رشتهٔ جلالی در پاسخ، مرتب‌سازی و بازه‌گیری را در فرانت می‌شکند.

بازهٔ پیش‌فرض سی روز. حداکثر بازهٔ مجاز یک سال، وگرنه خطای ۴۲۲. دلیل: بدون سقف، یک درخواست می‌تواند کل جدول ویزیت را اسکن کند.

docs/api/dashboard.md همان جلسه به‌روز می‌شود.


۶. پنل ادمین

فایل: assets/admin/pages/DashboardPage.tsx

  • بخش «شاخص‌های دندانپزشکی» بعد از کارت‌های عمومی، فقط وقتی domain_metrics مقدار دارد.
  • کارت‌ها با StatCard موجود.
  • روند با Recharts که پروژه از قبل دارد.
  • drill-down با DataTable موجود.
  • انتخاب بازه با PersianDateInput موجود.
  • فیلتر پزشک و یونیت با SearchableSelect.
  • TanStack Query با staleTime معقول، چون این اعداد ثانیه‌ای عوض نمی‌شوند.

هیچ کامپوننت تازه‌ای ساخته نمی‌شود.


۷. تسک‌ها

کد تسک فایل‌های اصلی معیار پذیرش
DM3-01 اینترفیس و رجیستری DomainMetricProvider src/Dashboard/Metric/ محیط بدون حوزه، null می‌گیرد
DM3-02 MetricRequest و MetricSet همان بازهٔ بزرگتر از یک سال ۴۲۲ می‌دهد
DM3-03 DentalMetricProvider بخش مالی src/Dental/Metric/ تولید و وصولی با داده‌ی ساختگی درست است
DM3-04 بخش اشغال یونیت همان منبع بدون تقویم، صفر می‌دهد نه خطا
DM3-05 بخش نوبت، حاضرنشده و لغو همان مخرج صفر، null می‌دهد نه تقسیم بر صفر
DM3-06 بخش ترکیب خدمات و دندان‌های درمان‌شده همان خدمت بدون گروه در «سایر» می‌رود
DM3-07 اتصال domain_metrics به چهار اندپوینت نقشی src/Dashboard/Controller/DashboardController.php داشبورد حوزه‌های دیگر کوئری اضافه نمی‌زند
DM3-08 اندپوینت بازه و روند همان فیلترها ترکیبی کار می‌کنند
DM3-09 بخش دندانی در صفحهٔ داشبورد assets/admin/pages/DashboardPage.tsx برای حوزهٔ زیبایی رندر نمی‌شود
DM3-10 نمودار روند و drill-down همان خالی بودن داده، حالت خالی نشان می‌دهد نه خطا
DM3-11 به‌روزرسانی docs/api/dashboard.md docs/api/ نمونهٔ پاسخ با کد یکی است

۸. تست‌ها

  • محیط بدون حوزه: domain_metrics برابر null و هیچ کوئری اضافه‌ای اجرا نمی‌شود.
  • محیط زیبایی: همان.
  • محیط دندانپزشکی بدون هیچ ویزیت: همهٔ شاخص‌ها صفر یا null، بدون خطا.
  • تقسیم بر صفر در هر نرخ: null برمی‌گردد و فرانت خط تیره نشان می‌دهد.
  • پزشک فقط عدد خودش را می‌بیند، حتی با فیلتر پزشک دیگر.
  • منشی به شاخص‌های مالی دسترسی ندارد.
  • بازهٔ بزرگتر از یک سال: ۴۲۲.
  • تست کارایی: تعداد کوئری با افزایش تعداد شاخص ثابت می‌ماند.

۹. ریسک‌ها

کندی داشبورد با رشد داده. تصمیم فاز ۳ محاسبهٔ زنده است. مهار: سقف بازه، ایندکس روی ستون‌های تاریخ و محیط، و یک تست کارایی که تعداد کوئری را قفل می‌کند. اگر بعداً کند شد، جدول تجمیع پشت همین سرویس اضافه می‌شود بدون تغییر API.

دو عدد متفاوت برای یک شاخص. مهار: هیچ فرمولی در فرانت نوشته نمی‌شود.