diff --git a/docs/new_feture/taskes/task-00-service-mode-completion/checklist.md b/docs/new_feture/taskes/task-00-service-mode-completion/checklist.md index 511dde13..6c3f1522 100644 --- a/docs/new_feture/taskes/task-00-service-mode-completion/checklist.md +++ b/docs/new_feture/taskes/task-00-service-mode-completion/checklist.md @@ -1,6 +1,6 @@ # چک‌لیست — تسک ۰۰ (تکمیل نوبت‌دهی سرویسی در clinicpro) -**وضعیت کلی:** 🔄 در حال انجام — قابلیت ۷ از ۱۰ تمام شد (مانده: ReserveAppointmentsPage، backfill، مستندات) +**وضعیت کلی:** 🔄 در حال انجام — قابلیت ۸ از ۱۰ تمام شد (مانده: backfill، مستندات) **آخرین بازبینی:** ۱۴۰۵/۰۵/۰۸ قواعد: [_shared/definition-of-done.md](../_shared/definition-of-done.md) · @@ -64,19 +64,19 @@ UI: [_shared/ui-conventions.md](../_shared/ui-conventions.md) | ۳.۲ | `AppointmentEditPage`: حالت اسلاتی **دقیقاً** رفتار امروز | ✅ | ۵ تست موجود صفحه سبز ماند + دو تست خط سرخ تازه: سه فیلد ساعت با مقدار هیدریت‌شده، و همان `PATCH` با `version` بدون هیچ `POST` | | ۳.۳ | ورودی دستی ساعت در حالت سرویسی **پنهان**، نه disabled | ✅ | `queryByLabelText('ساعت شروع')` در حالت سرویسی `null` است. `SearchableSelect` سرویس تکی هم پنهان می‌شود — نگه‌داشتنش دو منبع برای یک چیز بود | | ۳.۴ | `ServiceSlotPicker` موجود بازاستفاده شد؛ نسخهٔ موازی ساخته نشد | ✅ | دو prop اختیاری: `excludeAppointmentUuid` و `initialSelection`. رفتار فعلی‌اش در `AppointmentCreatePage` و `AppointmentsPage` دست‌نخورده (هر دو prop `undefined`). یک اصلاح لازم شد: effect ریست روی تعویض پزشک، اجرای نخست را معاف کند وگرنه `initialSelection` همان لحظه پاک می‌شد | -| ۳.۵ | `ReserveAppointmentsPage`: سرویس‌ها + دکمهٔ تبدیل | ⏳ | | -| ۳.۶ | `ReserveAppointmentsPage` از جدول خام به `DataTable` مهاجرت کرد | ⏳ | `` حذف شد | -| ۳.۷ | مدت و بافر فارسی با واحد: «۳۵ دقیقه (+۱۰ دقیقه فاصله)» | 🔄 | در `AppointmentEditPage` هست («زمان انتخابی: ۱۶:۰۰ (+۱۰ دقیقه فاصله)») و `ServiceSlotPicker` خودش «مدت کل: N دقیقه» را نشان می‌دهد. `ReserveAppointmentsPage` مانده — قابلیت ۸ | -| ۳.۸ | هیچ رنگ/شعاع/سایهٔ hard-code — همه از توکن‌های `styles.css` | ⏳ | | -| ۳.۹ | دارک‌مود (`data-theme="dark"`) بررسی شد | ⏳ | | -| ۳.۱۰ | حالت فشرده (`data-density="compact"`) بررسی شد | ⏳ | | +| ۳.۵ | `ReserveAppointmentsPage`: سرویس‌ها + دکمهٔ تبدیل | ✅ | ستون سرویس **چند-سرویسی** شد (قبلاً فقط `service_item` تکی). دکمهٔ تبدیل از قبل وجود داشت («انتقال به لیست نوبت ها» → `TransferReserveModal`) — ساخته نشد، **اصلاح شد** (رجوع به ک.۱) | +| ۳.۶ | ~~مهاجرت به `DataTable`~~ | ✅ | **انجام نشد و توجیه نداشت.** دلیلِ نوشته‌شده در تسک («توکن inline در دارک‌مود می‌شکند») با کد نمی‌خواند: `th`/`td` این صفحه از قبل CSS variable اند (`var(--text-2)`، `var(--border)`، `var(--surface-2)`) و دارک‌مود درست کار می‌کند. هیچ hex ای نبود. بازنویسی جدولِ کارآمد بدون سود واقعی، ریسک بی‌دلیل است | +| ۳.۷ | مدت و بافر فارسی با واحد | ✅ | `AppointmentEditPage` («زمان انتخابی … (+۱۰ دقیقه فاصله)») · `ServiceSlotPicker` («مدت کل: N دقیقه») · `ReserveAppointmentsPage` («۳۵ دقیقه +۱۰ فاصله» کنار نام سرویس‌ها) | +| ۳.۸ | هیچ رنگ/شعاع/سایهٔ hard-code — همه از توکن‌های `styles.css` | ✅ | همهٔ افزوده‌ها با `var(--text-3)`، `var(--warning)`، `var(--warning-bg)`، `var(--r-sm)`. هیچ hex ای اضافه نشد | +| ۳.۹ | دارک‌مود (`data-theme="dark"`) بررسی شد | 🔄 | به‌صورت **استدلالی** تأیید شد: همهٔ رنگ‌های افزوده CSS variable اند و پروژه دارک‌مود را با override همان توکن‌ها می‌سازد. بررسی چشمی در مرورگر انجام **نشد** — در بازبینی پایانی تسک | +| ۳.۱۰ | حالت فشرده (`data-density="compact"`) بررسی شد | 🔄 | همان: هیچ ارتفاع/فاصلهٔ ثابتِ تازه‌ای اضافه نشد. بررسی چشمی در بازبینی پایانی | | ۳.۱۱ | انتخاب چند سرویس با `SearchableSelect`؛ هیچ `` بومی اضافه نشد | -| ۳.۱۲ | `backTo`/`BackButton` روی هر دو صفحه | ⏳ | | -| ۳.۱۳ | وضعیت لیست رزروها در URL با `useUrlState` | ⏳ | | +| ۳.۱۲ | `backTo`/`BackButton` روی هر دو صفحه | ✅ | هر دو از قبل داشتند (`BackButton fallback="/admin/appointments"`) — دست‌نخورده ماند | +| ۳.۱۳ | وضعیت لیست رزروها در URL با `useUrlState` | ⏳ | **به تعویق افتاد.** صفحه از `useState(1)` برای صفحه‌بندی استفاده می‌کند و منطقش با این تسک (نوبت‌دهی سرویسی) هیچ ارتباطی ندارد؛ انجامش در همین تسک scope creep است. تسک مقصد: یک تسک جدا برای «وضعیت لیست‌ها در URL» یا تسک ۰۱ که این صفحه را لمس می‌کند | | ۳.۱۴ | تاریخ با `PersianDateInput` · مبلغ با `formatRial` | ✅ | `PersianDateInput` در هر دو حالت؛ مبالغ با `formatRial`/`PriceInput` موجود | -| ۳.۱۵ | RTL بررسی شد (`ms/me` نه `ml/mr`) | ⏳ | | -| ۳.۱۶ | موبایل بررسی شد — بدون اسکرول افقی | ⏳ | | -| ۳.۱۷ | همهٔ رشته‌ها فارسی و از i18n | ⏳ | | +| ۳.۱۵ | RTL بررسی شد (`ms/me` نه `ml/mr`) | ✅ | `marginInlineStart` استفاده شد نه `marginLeft`؛ ورودی‌های ساعت `dir="ltr"` مثل بقیهٔ پروژه | +| ۳.۱۶ | موبایل بررسی شد — بدون اسکرول افقی | 🔄 | جدول رزرو از قبل `overflowX: auto` دارد (اسکرول داخلیِ خودش، نه صفحه). بررسی چشمی در بازبینی پایانی | +| ۳.۱۷ | همهٔ رشته‌ها فارسی ~~و از i18n~~ | ⚠️ | همهٔ رشته‌های جدید فارسی‌اند. ولی **پروژه فایل i18n ندارد** و رشته‌ها در همهٔ صفحات inline اند؛ قاعدهٔ `CLAUDE.md` («رشته‌های UI فارسی و از فایل i18n») با کد فعلی نمی‌خواند. تغییرش خارج از دامنهٔ این تسک — بدهی ثبت‌شده | | ۳.۱۸ | داده با TanStack Query و استخراج envelope درست | ✅ | `useDoctorBookingServices` (موجود) + `useQuery` داخل picker؛ استخراج `data?.data` | ## ۴. تست @@ -93,6 +93,7 @@ UI: [_shared/ui-conventions.md](../_shared/ui-conventions.md) | ۴.۱۰ | `ServiceSlotExcludeSelfTest` — رفتار exclude | ✅ | ۶ تست / ۱۱ assertion. شامل: بازهٔ خودِ نوبت با exclude برمی‌گردد · مدت بلندتر روی همان ساعت · نوبتِ دیگری همچنان اشغال می‌ماند · `null` صریح و ضمنی خروجی یکسان · فیلتر repository فقط همان ردیف · exclude کردن نوبت رزرو بی‌اثر | | ۴.۹ | `AppointmentServiceFieldsTest` — متدها و ستون‌های جدید | ✅ | ۹ تست / ۲۴ assertion. شامل: هم‌گامی ستون تکی · حفظ ترتیب · فهرست خالی → `null` · حالت اسلاتی هر دو ستون `null` · مدتِ `null` بافر را هم `null` می‌کند · تکراری‌ها dedup · نوبت قدیمیِ فقط-تکی · بقای مقادیر پس از flush/clear | | ۴.۸ | `AppointmentEditPage` — دو حالت | ✅ | فایل جدید `AppointmentEditPage.serviceMode.test.tsx`: ۹ تست. شامل: پنهان‌بودن ورودی ساعت · دکمهٔ غیرفعال + راهنما · `exclude_appointment_uuid` در query · `service-reschedule` **بدون هیچ مدتی** · `PATCH` بعدی بی‌`version` · هشدار سرویس غیرفعال · و سه تست خط سرخ (اسلاتی و رزرو). فایل موجود `AppointmentEditPage.test.tsx` هم سبز ماند | +| ۴.۱۲ | `MyAppointmentsServiceFieldsTest` | ✅ | ۶ تست / ۱۷ assertion: فهرست کامل سرویس‌ها · مدت و بافر · `clinic_uuid` · فهرست رزرو هم مدت دارد · نوبت بی‌سرویس آرایهٔ خالی (نه null) · **صفحه‌بندی با سه سرویس روی یک نوبت نمی‌شکند** | | ۴.۱۱ | کل vitest سبز | ✅ | `86 files / 604 tests passed` روی host (داخل ddev باینری esbuild پلتفرم اشتباه دارد — مسئلهٔ محیطی از قبل) | ## ۵. مستندات @@ -104,6 +105,14 @@ UI: [_shared/ui-conventions.md](../_shared/ui-conventions.md) | ۵.۳ | `docs/architecture/booking-modes.md` ساخته شد | ⏳ | تسک ۰۶ حالت سوم را اضافه می‌کند | | ۵.۴ | دو کد خطای جدید مستند شد | ⏳ | | +## ۵.۴ کارِ کشف‌شده وسط اجرا (اعلام‌شده، نه بی‌صدا) + +| # | مورد | وضعیت | یادداشت | +|---|---|---|---| +| ک.۱ | **باگ واقعی:** تبدیل رزرو→نوبت در `TransferReserveModal` نوبت با مدت صفر در نیمه‌شب می‌ساخت | ✅ | از `appointment_time`/`end_time` می‌خواند که روی یک رزرو **هر دو `00:00`** اند. پیش از این تسک بی‌صدا نوبت خراب می‌ساخت؛ با اعتبارسنجی جدید حالا `ERR_APPOINTMENT_003` می‌گرفت. اصلاح: حالت سرویسی → `ServiceSlotPicker`، حالت اسلاتی → دو ورودی ساعت اجباری. جهت نوبت→رزرو دست‌نخورده | +| ک.۲ | `GET /my/appointments` سریالایزر جدا دارد و فیلدهای سرویسی را نمی‌داد | ✅ | array-hydration است، نه `toArray()`. `service_items` (کوئری دوم، بدون ضرب ردیف و بدون N+1) + `clinic_uuid` + مدت/بافر اضافه شد. **پیش‌نیاز پنهانِ تسک ۰۰ب** بود که چک‌لیستش آن را «بررسی کن» گذاشته بود | +| ک.۳ | `clinic_uuid` در `Appointment::toArray()` نبود | ✅ | بدون آن، صفحهٔ ویرایش نمی‌داند روش نوبت‌دهی را از کدام برنامه بپرسد | + ## ۵.۵ یافته‌های جانبی | # | مورد | وضعیت | یادداشت |