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
@@ -59,22 +59,22 @@
| ۳.۱۲ | `DataTable` + فیلتر دسته در URL | ✅ | `useUrlState` |
| ۳.۱۳ | `backTo` روی هر سه صفحه | ✅ | |
| ۳.۱۴ | هیچ رنگ/شعاع hard-code | ✅ | فقط `var(--…)` |
| ۳.۱۵ | دارک‌مود و حالت فشرده | ⚠️ | فقط توکن‌های موجود استفاده شده؛ بازبینی چشمی انجام نشد |
| ۳.۱۵ | دارک‌مود و حالت فشرده | | اسکرین‌شات واقعی در دارک‌مود و حالت فشرده؛ ایرادی نماند |
| ۳.۱۶ | RTL و موبایل — اسکرول افقی جدول | ✅ | `overflow-x: auto` دور جدول گزارش |
| ۳.۱۷ | تاریخ‌ها شمسی | ✅ | `formatDate` |
| ۳.۱۸ | همهٔ رشته‌ها فارسی | ✅ | |
| ۳.۱۹ | تاریخچهٔ نسخه‌ها با diff | ⚠️ | فهرست نسخه‌ها با اثرهای هر نسخه نمایش داده می‌شود؛ diff بصری واقعی نیست |
| ۳.۱۹ | تاریخچهٔ نسخه‌ها با diff | | `PolicyVersionDiff` — هر نسخه در برابر نسخهٔ پیش، فیلد‌به‌فیلد؛ «بدون تغییرِ معنادار» هم صریح گفته می‌شود |
## ۴. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | شمارش ردیف قبل/بعد | ✅ | ⭐⭐ |
| ۴.۲ | استثنا → rollback + clear | ⚠️ | `finally` تضمینش می‌کند ولی تست تزریق استثنا نوشته نشد |
| ۴.۲ | استثنا → rollback + clear | | ⭐ خودِ خاصیت سنجیده می‌شود: entityِ در انتظار flush بعد از آزمایش ثبت نمی‌شود و تراکنش بسته است |
| ۴.۳ | نمونه‌گیری — فقط confirmed/completed، سقف | ✅ | سقف تست شد؛ فیلتر وضعیت غیرمستقیم (نوبت‌های نمونه completed اند) |
| ۴.۴ | دروازهٔ فعال‌سازی | ✅ | ⭐ بدون آزمایش، نسخهٔ قدیمی، نسخهٔ درست، محیط خالی |
| ۴.۵ | هر الگو قانون معتبر می‌سازد | ⚠️ | یک الگو کامل تست شد (`vip_discount`) + ورودی ناقص؛ dataProvider شش‌تایی نوشته نشد |
| ۴.۶ | چهار آستانهٔ شدت | ⚠️ | `high` و `none` تست شدند؛ `low`/`medium` نه |
| ۴.۵ | هر الگو قانون معتبر می‌سازد | | هر شش الگو تا فعال‌سازی رفتند. یادداشت قبلی: dataProvider شش‌تایی نوشته نشد |
| ۴.۶ | چهار آستانهٔ شدت | | هر چهار آستانه روی مرزِ خودش، به‌علاوهٔ حالت کوتاه‌تر |
| ۴.۷ | فیلد ساختگی از mock schema در UI | ✅ | ⭐ `PolicyFormPage.test.tsx` |
| ۴.۸ | عملگر نامعتبر نمایش داده نمی‌شود | ✅ | |
| ۴.۹ | صفحهٔ آزمایش — قفل فعال‌سازی روی نسخهٔ قدیمی | ✅ | `PolicySimulationPage.test.tsx` |