The deactivation warning was blocked on task 07: there was no way to count "the
appointments on this resource" until occupancy rows linked the two. They do
now, so GET /resource/{uuid} returns upcoming_appointments. It stays off the
list endpoint, where it would be one count query per row.
It is a warning, not a block, and the wording says so: switching a resource off
does not cancel anything, it only removes the resource from future searches.
The panel shows it the moment the "active" box is unticked.
The calendar's exception range still used <input type="date">, which is
Gregorian. Operators say dates in Jalali, and the mental conversion is exactly
where an exception gets recorded a day off. PersianDateInput takes the same
YYYY-MM-DD string, so this is a drop-in swap.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8.2 KiB
8.2 KiB
چکلیست — تسک ۰۲ (منبع، نوع منبع، مهارت، استخر)
وضعیت کلی: ✅ تکمیلشده (۲ ردیف 🔄 بازبینی چشمی · ۱ ردیف ⏳ وابسته به تسک ۰۷) · آخرین بازبینی: ۱۴۰۵/۰۵/۰۸
قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md
۰. خط سرخ
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | --group=slot-mode-frozen سبز |
✅ | ۳ تست / ۸ assertion سبز |
| ۰.۲ | Doctor به Resource تبدیل نشد — فقط لینک شد |
✅ | فقط پل؛ appointments.doctor_id دستنخورده |
| ۰.۳ | ClinicStaff و Room هویت مستقل حفظ کردند |
✅ | هر سه هویت مستقل ماندند |
| ۰.۴ | service_item_staff و ServiceItem.staffMembers دستنخورده |
✅ | صفر تغییر |
| ۰.۵ | WeeklySchedule.meta.buffer_minutes دستنخورده |
✅ | setup/cleanup per منبع است، آن یکی per پزشک |
۱. بکاند
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | ResourceType · ClinicResource · Skill · ResourceSkill |
✅ | |
| ۱.۲ | ResourcePool · ResourcePoolMember |
✅ | |
| ۱.۳ | نام کلاس ClinicResource (نه Resource) و جدول clinic_resources |
✅ | |
| ۱.۴ | ResourceLinker — تنها نقطهٔ نگاشت پزشک/پرسنل/اتاق ↔ منبع |
✅ | + identity map برای نوعهای flush-نشده |
| ۱.۵ | حداکثر یکی از doctor_id/staff_id/room_id — قید در سازنده |
✅ | BackfillResourceTest::testASecondBridgeIsRefused |
| ۱.۶ | capacity>1 روی type=doctor → ۴۲۲ |
✅ | قید در entity روی هر منبعِ «شخص»، نه فقط type=doctor |
| ۱.۷ | normalizeAttributes — اسکالر، کلید [a-z_]{1,40}، سقف ۲۰ |
✅ | |
| ۱.۸ | ResourcePoolService — اعضا همشعبه و همنوع، وگرنه ۴۲۲ |
✅ | اعتبارسنجی پیش از حذف — اتمی |
| ۱.۹ | findEligible() با HAVING COUNT(DISTINCT skill) = n |
✅ | ResourceEligibilityTest هر دو حالت را میسنجد |
| ۱.۱۰ | StaffService موجود ResourceLinker::syncActive() صدا میزند |
✅ | StaffController::toggle صدا میزند؛ تست سطح endpoint دارد |
| ۱.۱۱ | چهارده endpoint | ✅ | شانزده شد نه چهارده: GET/DELETE /resource/{uuid} و GET /resource-pool/{uuid} هم لازم بودند |
| ۱.۱۲ | app:resource:backfill — dry-run، idempotent |
✅ | + --pair برای دامنه و flush per محیط |
| ۱.۱۳ | TenantOwnershipChecker روی هر uuid از request |
✅ | ResourceContext تکنقطهٔ ۴۰۴ |
۲. دیتابیس
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | شش جدول ساخته شد | ✅ | Version20260730132948 |
| ۲.۲ | سه UNIQUE تهیپذیر روی doctor_id/staff_id/room_id |
✅ | دو تای اول به (doctor_id, address_id) و (staff_id, address_id) تصحیح شد — پزشک در چند شعبهٔ یک محیط کار میکند |
| ۲.۳ | idx_resource_skills_skill (skill_id, level) |
✅ | کوئری داغ تسک ۰۶ |
| ۲.۴ | entity_type, entity_id ستون اول ایندکسهای لیست |
✅ | |
| ۲.۵ | resource_skills و resource_pool_members در AGGREGATE_CHILDREN |
✅ | ریشههاشان جفت دارند، پس ارثبری واقعی است |
| ۲.۶ | TenantSchemaCoverageTest سبز |
✅ |
۳. UI
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | ResourcesPage · ResourceFormPage · ResourceTypesPage · SkillsPage · ResourcePoolsPage |
✅ | ResourceFormPage بهصورت مودال شد نه صفحه: فرم کوتاه است و صفحهٔ جدا یک ناوبری اضافه بدون سود میداد |
| ۳.۲ | فیلتر شعبه/نوع/فعال در URL با useUrlState |
✅ | شعبه/نوع/مهارت/وضعیت — هر چهار در URL و مستقیم به سرور |
| ۳.۳ | شعبه و نوع با SearchableSelect |
✅ | هیچ <select> بومی در کل دامنه |
| ۳.۴ | مهارتها با چیپ چندانتخابی | ✅ | چیپ در جدول + مودال اختصاصی با سطح ۱..۵؛ جایگزینی کامل |
| ۳.۵ | DataTable با skeleton و empty state |
✅ | |
| ۳.۶ | backTo/BackButton روی همهٔ زیرصفحهها |
✅ | backTo + breadcrumb روی هر سه زیرصفحه |
| ۳.۷ | هیچ رنگ/شعاع hard-code | ✅ | همه از var(--…) |
| ۳.۸ | دارکمود و حالت فشرده | 🔄 | کد فقط از توکن استفاده میکند؛ بازبینی چشمی در مرورگر انجام نشد |
| ۳.۹ | RTL و موبایل | 🔄 | چیدمان grid با wrap؛ بازبینی چشمی موبایل انجام نشد |
| ۳.۱۰ | setup/cleanup با واحد فارسی «دقیقه» |
✅ | برچسب «دقیقه» در فرم و ستون جدول |
| ۳.۱۱ | هشدار «این منبع N نوبت آیندهٔ فعال دارد» هنگام غیرفعالسازی | ✅ | ⭐ upcoming_appointments روی GET /resource/{uuid}؛ هشدار است نه مانع — غیرفعالسازی نوبتها را لغو نمیکند |
۴. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | ResourceCrudTest — شعبهٔ محیط دیگر ۴۰۴، دو پل ۴۲۲، capacity |
✅ | ۱۳ تست |
| ۴.۲ | SkillAssignmentTest — جایگزینی کامل، level، حذف در استفاده |
✅ | ۹ تست شامل اتمی بودن |
| ۴.۳ | ResourcePoolTest — عضو شعبهٔ دیگر / نوع دیگر → ۴۲۲ |
✅ | ۷ تست |
| ۴.۴ | ResourceEligibilityTest — دو مهارت، منبع با یکی برنمیگردد |
✅ | ۶ تست |
| ۴.۵ | BackfillResourceTest — idempotent |
✅ | ۱۰ تست |
| ۴.۶ | TenantLookupInventoryTest شمارنده بهروز شد |
✅ | سبز بدون نیاز به تغییر شمارنده — lookupهای تازه از ResourceContext رد میشوند |
۵. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | docs/api/resource.md |
✅ | JSON واقعی از curl روی ddev + ثبت در README |
| ۵.۲ | توضیح «ظرفیت ۳ = یک ردیف، نه سه منبع» | ✅ | |
| ۵.۳ | تفاوت setup/cleanup منبع با buffer_minutes برنامه |
✅ | |
| ۵.۴ | docs/api/staff.md بخش «رابطه با منبع» |
✅ | بخش «رابطه با منبع» |
| ۵.۵ | docs/architecture/tenancy.md بهروز |
✅ | تفاوت «ریشهٔ جفتدار» با پروندهٔ تسک ۰۱ |
۶. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | هیچ 🔄 و ⏳ بیدلیل نمانده | ⚠️ | دو 🔄 بازبینی چشمی + یک ⏳ با مقصد صریح (تسک ۰۷) |
| ۶.۲ | bin/phpunit کامل سبز |
✅ | ۱۱۱۹ تست / ۳۱۱۳ assertion سبز |
| ۶.۳ | --group=slot-mode-frozen سبز |
✅ | |
| ۶.۴ | phpstan بدون خطای جدید |
✅ | ۱۴ خطا قبل و بعد؛ src/Resource صفر خطا |
| ۶.۵ | npx tsc --noEmit و yarn test سبز |
✅ | tsc تمیز · vitest ۸۸ فایل / ۶۱۷ تست |
| ۶.۶ | تستهای tenant سبز | ✅ | |
| ۶.۷ | docs/api/* بهروز |
✅ | |
| ۶.۸ | چکلیست UI کامل | ⚠️ | ردیفهای ۳.۸/۳.۹ بازبینی چشمی میخواهند |
| ۶.۹ | دو کلاینت دیگر بررسی شدند | ✅ | هیچکدام منابع را مصرف نمیکنند؛ اندپوینتهای تازه همه پنلمحورند و قرارداد موجودی عوض نشد |
| ۶.۱۰ | commit، سپس graphify update . |
✅ | |
| ۶.۱۱ | موارد بهتعویق با دلیل و تسک مقصد | ✅ | هشدار نوبت آینده ← تسک ۰۷ (ردیف ۳.۱۱) |