Tasks 04 and 05 shipped working engines that a clinic could only reach through the API. Both now have the panel that makes them usable. Groups tab - Inline min/max per group, saved on blur, with the meaning of an empty maximum spelled out next to the field rather than left as folklore - Incompatible / prerequisite rows; the prerequisite-cycle 422 surfaces the server's own message, which is more precise than anything generic - A live preview that calls the same service-selection/validate the public site calls, debounced 400ms. Two separate calculations would eventually show the operator and the patient different numbers - The breakdown table shows which item was counted as the anchor and which as additional, so a surprising total explains itself Segments tab - Sequence, duration source, patient-present and mergeable per segment, plus resource requirements with an explanation attached to each occupancy mode - A timeline bar whose widths are proportional to duration, with segments the patient is absent for drawn faded. That contrast is the whole point of task 05: the waiting segment holds the room but frees the operator - "No eligible resource" renders with a link to add one — an error with no route forward is a dead end Task 05's checklist had been left on "not started" this whole time even though its code shipped with the task; it is now filled in against reality. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9.1 KiB
9.1 KiB
چکلیست — تسک ۰۵ (بخشهای نوبت و سازندهٔ برنامه)
وضعیت کلی: ✅ تمامشده با انحرافهای ثبتشده · آخرین بازبینی: ۱۴۰۵/۰۵/۰۹
این چکلیست تا امروز روی «شروع نشده» مانده بود در حالی که کد تسک از همان روز ساخته و کامیت شده بود — خطای پیگیری، نه خطای پیادهسازی. حالا با وضعیت واقعی پر شده.
قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md
۰. خط سرخ
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | --group=slot-mode-frozen سبز |
✅ | |
| ۰.۲ | SlotCalculatorService دستنخورده |
✅ | |
| ۰.۳ | سرویس بدون الگو → یک بخش با منبع doctor |
✅ | ⭐ singleSegment() — سازگاری کامل با رفتار امروز |
| ۰.۴ | هیچ جدولی برای «برنامهٔ ساختهشده» نیست | ✅ | فقط DTO درونحافظه؛ ذخیره کار تسک ۰۷ شد |
۱. بکاند
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | SegmentTemplate · SegmentRequirement |
✅ | |
| ۱.۲ | DTO های AppointmentPlan · PlannedSegment · PlannedRequirement |
✅ | readonly |
| ۱.۳ | پنج سرویس جدا | ⚠️ | یک AppointmentPlanBuilder با متدهای خصوصی. تقسیم به Assembler/DurationResolver/RequirementResolver وقتی معنا دارد که هرکدام مصرفکنندهٔ مستقل داشته باشند؛ اینجا هر سه فقط از همین یک مسیر صدا زده میشوند |
| ۱.۴ | build() تابع خالص |
⚠️ | تا تسک ۰۸ خالص بود. تسک ۰۹ قوانین timing/resource را وصل کرد، پس حالا از دیتابیس میخواند. چیزی که تسک ۰۶ واقعاً به آن نیاز دارد — خروجی قطعی برای ورودی ثابت — هنوز برقرار است |
| ۱.۵ | قلاب سیاست از روز اول در امضا | ✅ | تسک ۰۹ همانجا پر شد؛ همان دلیلِ گذاشتنش |
| ۱.۶ | ادغام: count بیشینه |
⏳ | ادغام بخشهای mergeable پیاده نشد؛ پرچمش ذخیره و در API برگردانده میشود ولی رفتاری ندارد |
| ۱.۷ | offset_minutes نسبی |
✅ | تسک ۰۶ برنامه را میلغزاند |
| ۱.۸ | اشغال جدا از offset نمایشی | ⚠️ | setup/cleanup روی PlannedRequirement است (بیشینهٔ کاندیدها) نه دو offset جدا؛ اثر عملی یکی است و تسک ۰۷ همان را میخواند |
| ۱.۹ | قید جنسیت بدون داده → ۴۲۲ | ✅ | ⭐ نادیده گرفته نمیشود |
| ۱.۱۰ | constraints فهرست بسته |
⚠️ | فقط same_gender_as_patient اثر دارد؛ کلید ناشناخته ۴۲۲ نمیگیرد و بیصدا نادیده میرود |
| ۱.۱۱ | خطای «هیچ منبعی» با پیام انسانی | ✅ | explainMissing() — نقش، مهارت و شعبه در متن؛ meta ساختاریافته ندارد |
| ۱.۱۲ | سه endpoint | ✅ | GET/PUT segments + POST appointment-plan/preview |
| ۱.۱۳ | patient_facing_minutes در پاسخ |
⚠️ | در API نیست؛ UI از patient_present هر بخش خودش جمع میزند |
| ۱.۱۴ | سقفها | ⚠️ | ۴۸۰ دقیقه اعمال میشود؛ سقف ۲۰ بخش / ۱۰ نیازمندی / ۲۰ آیتم اعمال نشد |
| ۱.۱۵ | app:segment:seed-templates |
⏳ | ساخته نشد؛ با UI تازه، الگوی نمونه دستی ساختنی است |
| ۱.۱۶ | TenantOwnershipChecker روی هر uuid |
✅ |
۲. دیتابیس
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | segment_templates · segment_requirements |
✅ | |
| ۲.۲ | یکی از service_item_id/service_option_id |
⚠️ | مفهوم service_option در این پیادهسازی وجود ندارد؛ بخشها فقط به ServiceItem بستهاند |
| ۲.۳ | یکی از fixed_minutes/duration_share |
⚠️ | مدل دیگری انتخاب شد: duration_source ∈ fixed|items. «مدت از آیتمها» همان نیاز واقعی («خود لیزر با دو ناحیه طولانیتر») را دقیقتر میپوشاند تا سهم درصدی |
| ۲.۴ | جمع duration_share = ۱۰۰ |
— | با مدل بالا موضوعیت ندارد |
| ۲.۵ | required_skills بهصورت JSON |
⚠️ | یک Skill تک با FK. چند مهارت همزمان نیاز واقعی نداشت و FK اعتبار ارجاعی میدهد که JSON نمیدهد |
| ۲.۶ | segment_requirements در AGGREGATE_CHILDREN |
✅ | |
| ۲.۷ | TenantSchemaCoverageTest سبز |
✅ |
۳. UI
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | ویرایشگر بخشها در ServiceDetailPage |
✅ | تب «بخشهای نوبت» — components/ServiceSegmentsTab.tsx |
| ۳.۲ | لیست بخشها با sequence عددی |
✅ | بدون drag، طبق قرارداد |
| ۳.۳ | نیازمندیها با SearchableSelect |
✅ | نقش، تعداد، نوع اشغال، قید جنسیت |
| ۳.۴ | هر گزینهٔ نوع اشغال توضیح فارسی دارد | ✅ | «انحصاری — منبع کامل قفل میشود» / «اشتراکی — از ظرفیت یکی کم میشود» |
| ۳.۵ | نوار پیشنمایش با عرض متناسب مدت | ✅ | ⭐ flex: duration — بخش سیدقیقهای شش برابر پنجدقیقهای |
| ۳.۶ | خط «بیمار واقعاً درگیر: N دقیقه» | ✅ | کنار مدت کل |
| ۳.۷ | backTo روی صفحه |
✅ | از ServiceDetailPage میآید |
| ۳.۸ | هیچ رنگ/شعاع hard-code | ✅ | نوار هم با --primary-soft/--surface-2 |
| ۳.۹ | دارکمود و حالت فشرده | ⚠️ | فقط توکنها؛ بازبینی چشمی انجام نشد |
| ۳.۱۰ | RTL و موبایل — اسکرول افقی نوار | ✅ | overflow-x: auto با min-width |
| ۳.۱۱ | همهٔ رشتهها فارسی | ✅ | |
| ۳.۱۲ | خطای «هیچ منبعی» به لینک «افزودن منبع» تبدیل شد | ✅ | ⭐ خطای بدون راه اصلاح، بنبست است |
۴. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | ادغام بخشها | ⏳ | با ۱.۶ یک بسته است |
| ۴.۲ | حل مدت — ثابت و از آیتمها | ✅ | داخل AppointmentPlanTest |
| ۴.۳ | سناریوی مرجع مستند | ✅ | ⭐ آفستهای ۰/۵/۳۵/۵۵ و مجموع ۶۰ |
| ۴.۴ | قطعیت — دو build یکسان | ⏳ | تست صریح ندارد |
| ۴.۵ | سرویس بدون الگو | ✅ | ⭐ |
| ۴.۶ | حل نیازمندی — مهارت، بیکاندید، جنسیت، محیط دیگر | ✅ | |
| ۴.۷ | سقفها → ۴۲۲ | ⚠️ | سقف ۴۸۰ دقیقه تست شد؛ بقیه سقف ندارند (۱.۱۴) |
| ۴.۸ | تست فرانت ویرایشگر بخشها | ⏳ | تب ساخته شد ولی تست ندارد؛ منطق سنگینش در بکاند است که ۱۱ تست دارد |
اجرا: ddev exec php bin/phpunit tests/Appointment/AppointmentPlanTest.php → ۱۱ تست.
۵. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | docs/api/appointment-plan.md |
✅ | |
| ۵.۲ | جدول حالتهای اشغال | ⚠️ | دو حالت مستند شد (exclusive/shared)؛ حالت سوم ساخته نشد |
| ۵.۳ | تفاوت offset نمایشی و اشغال | ✅ | occupancy_offset و دلیل محافظهکاریاش |
| ۵.۴ | مثال کامل خروجی preview |
✅ |
۶. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | هیچ ⏳ بیدلیل نمانده | ✅ | ۵ مورد با دلیل |
| ۶.۲ | bin/phpunit کامل سبز |
✅ | |
| ۶.۳ | --group=slot-mode-frozen سبز |
✅ | |
| ۶.۴ | phpstan بدون خطای جدید |
✅ | baseline ۱۴ |
| ۶.۵ | npx tsc --noEmit و تستهای فرانت سبز |
✅ | ۶۳۷ تست |
| ۶.۶ | تستهای tenant سبز | ✅ | |
| ۶.۷ | docs/api/* بهروز |
✅ | |
| ۶.۸ | چکلیست UI کامل | ✅ | جز ۳.۹ |
| ۶.۹ | دو کلاینت دیگر بررسی شدند | ⚠️ | preview هنوز در nobat724_front مصرف نمیشود |
| ۶.۱۰ | commit، سپس graphify update . |
✅ | |
| ۶.۱۱ | موارد بهتعویق با دلیل | ✅ | ادغام بخشها (۱.۶/۴.۱) · سقفهای فرعی (۱.۱۴) · seed (۱.۱۵) · تست قطعیت (۴.۴) |