- 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.
10 KiB
فاز ۳ — داشبورد دندانپزشکی
پیشنیاز: فاز ۱ و ۲. خروجی قابل تست: مدیر و پزشک و پذیرش، شاخصهای دندانپزشکی را در همان داشبورد فعلی خودشان میبینند.
۱. هدف
شاخصهای مخصوص حوزهٔ فعالیت به داشبورد نقشی اضافه شوند، بدون دستزدن به منطق داشبورد عمومی و بدون کند کردن داشبورد حوزههای دیگر.
۲. الگو
همان الگوی 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.
دو عدد متفاوت برای یک شاخص. مهار: هیچ فرمولی در فرانت نوشته نمیشود.