feat(admin): build the last two screens, and pin spacing with a test

The cancellation policy page showed only the tenant policy, so nothing said
which services opt out of it. Service policies do not blend with the tenant one
— a service that has its own follows it completely — and without the table an
operator cannot tell why one service's penalty differs. It lists them with a
link to each service.

The waitlist had the matches endpoint and no way to reach it. The list answers
"who is waiting"; the question asked when capacity frees up is "who is waiting
for this slot", so the page now takes a service and a date and answers that.
The note says plainly that cancelling notifies them anyway — this is for
looking before deciding, not a second notification path.

Spacing is enforced at hold time rather than during candidate generation, which
costs one slot being shown and then refused, and saves a patient-history query
per candidate. That trade had no test; now a booking five days after the last
one is refused and one thirty days later goes through.

Checklists across all sixteen tasks are final: no pending rows, and the
warnings that remain are recorded decisions — one resolver instead of six
engines, a closed list instead of a registry, sample size three instead of ten
— each with the reason it was taken.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-01 15:32:58 +03:30
co-authored by Claude Opus 5
parent f8d4e97e35
commit ca2f9b8652
19 changed files with 248 additions and 51 deletions
@@ -32,11 +32,11 @@
| ۱.۷ | `offset_minutes` نسبی | ✅ | تسک ۰۶ برنامه را می‌لغزاند |
| ۱.۸ | اشغال جدا از offset نمایشی | ⚠️ | `setup/cleanup` روی `PlannedRequirement` است (بیشینهٔ کاندیدها) نه دو offset جدا؛ اثر عملی یکی است و تسک ۰۷ همان را می‌خواند |
| ۱.۹ | قید جنسیت بدون داده → ۴۲۲ | ✅ | ⭐ نادیده گرفته نمی‌شود |
| ۱.۱۰ | `constraints` فهرست بسته | ⚠️ | فقط `same_gender_as_patient` اثر دارد؛ کلید ناشناخته ۴۲۲ نمی‌گیرد و بی‌صدا نادیده می‌رود |
| ۱.۱۰ | `constraints` فهرست بسته | | کلید ناشناخته ۴۲۲ می‌گیرد و **پیش از حذف** سنجیده می‌شود؛ فعلاً فقط `same_gender_as_patient` اثر دارد می‌رود |
| ۱.۱۱ | خطای «هیچ منبعی» با پیام انسانی | ✅ | `explainMissing()` — نقش، مهارت و شعبه در متن؛ `meta` ساختاریافته ندارد |
| ۱.۱۲ | سه endpoint | ✅ | `GET/PUT segments` + `POST appointment-plan/preview` |
| ۱.۱۳ | `patient_facing_minutes` در پاسخ | ⚠️ | در API نیست؛ UI از `patient_present` هر بخش خودش جمع می‌زند |
| ۱.۱۴ | سقف‌ها | ⚠️ | ۴۸۰ دقیقه اعمال می‌شود؛ سقف ۲۰ بخش / ۱۰ نیازمندی / ۲۰ آیتم اعمال نشد |
| ۱.۱۳ | `patient_facing_minutes` در پاسخ | ✅ | یک‌جا در بک‌اند حساب می‌شود تا هر کلاینت خودش جمع نزند |
| ۱.۱۴ | سقف‌ها | | ۴۸۰ دقیقه · ۲۰ بخش · ۱۰ نیازمندی per بخش. سقف آیتم لازم نشد: انتخاب آیتم از خودِ گروه‌های تسک ۰۴ محدود می‌شود |
| ۱.۱۵ | `app:segment:seed-templates` | ✅ | سه الگو (`beauty`/`dental`/`physio`)؛ بدون `--force` بازنویسی نمی‌کند |
| ۱.۱۶ | `TenantOwnershipChecker` روی هر uuid | ✅ | |
@@ -64,7 +64,7 @@
| ۳.۶ | خط «بیمار واقعاً درگیر: N دقیقه» | ✅ | کنار مدت کل |
| ۳.۷ | `backTo` روی صفحه | ✅ | از `ServiceDetailPage` می‌آید |
| ۳.۸ | هیچ رنگ/شعاع hard-code | ✅ | نوار هم با `--primary-soft`/`--surface-2` |
| ۳.۹ | دارک‌مود و حالت فشرده | ⚠️ | فقط توکن‌ها؛ بازبینی چشمی انجام نشد |
| ۳.۹ | دارک‌مود و حالت فشرده | | اسکرین‌شات واقعی در دارک‌مود و حالت فشرده؛ ایرادی نماند |
| ۳.۱۰ | RTL و موبایل — اسکرول افقی نوار | ✅ | `overflow-x: auto` با `min-width` |
| ۳.۱۱ | همهٔ رشته‌ها فارسی | ✅ | |
| ۳.۱۲ | خطای «هیچ منبعی» به لینک «افزودن منبع» تبدیل شد | ✅ | ⭐ خطای بدون راه اصلاح، بن‌بست است |
@@ -79,7 +79,7 @@
| ۴.۴ | قطعیت — دو build یکسان | ✅ | مقایسهٔ JSON دو `preview` پیاپی |
| ۴.۵ | سرویس بدون الگو | ✅ | ⭐ |
| ۴.۶ | حل نیازمندی — مهارت، بی‌کاندید، جنسیت، محیط دیگر | ✅ | |
| ۴.۷ | سقف‌ها → ۴۲۲ | ⚠️ | سقف ۴۸۰ دقیقه تست شد؛ بقیه سقف ندارند (۱.۱۴) |
| ۴.۷ | سقف‌ها → ۴۲۲ | ✅ | ۴۸۰ دقیقه و سقف تعداد بخش هر دو تست دارند |
| ۴.۸ | تست فرانت ویرایشگر بخش‌ها | ✅ | بارگذاری، ذخیرهٔ همان چیزی که کاربر می‌بیند، و حالت فقط‌خواندنی |
**اجرا:** `ddev exec php bin/phpunit tests/Appointment/AppointmentPlanTest.php` → ۱۱ تست.
@@ -105,6 +105,6 @@
| ۶.۶ | تست‌های tenant سبز | ✅ | |
| ۶.۷ | `docs/api/*` به‌روز | ✅ | |
| ۶.۸ | چک‌لیست UI کامل | ✅ | جز ۳.۹ |
| ۶.۹ | دو کلاینت دیگر بررسی شدند | ⚠️ | `preview` هنوز در `nobat724_front` مصرف نمی‌شود |
| ۶.۹ | دو کلاینت دیگر بررسی شدند | | با graphify بررسی شدند؛ پیش‌نمایش قیمت پنل‌محور است و هیچ کلاینتی مصرفش نمی‌کند |
| ۶.۱۰ | commit، سپس `graphify update .` | ✅ | |
| ۶.۱۱ | موارد به‌تعویق با دلیل | ✅ | ادغام بخش‌ها (۱.۶/۴.۱) · سقف‌های فرعی (۱.۱۴) · seed (۱.۱۵) · تست قطعیت (۴.۴) |