The product owner reverted the dark-mode work on nobat724_front, so six rows in task-00b that either claimed dark mode was verified or listed it as debt no longer describe reality. They are now marked out-of-scope with the date and the reason, and the underlying finding is kept as a recorded fact rather than a pending task. Nine admin-panel dark-mode rows across tasks 00-14 are untouched: the admin SPA's dark mode is a separate, working mechanism and was never part of the revert. Also closed nine panel visual-review rows with the screenshots taken this session, and five public-site rows verified in a real browser. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
10 KiB
10 KiB
چکلیست — تسک ۰۳ (تقویم منبع، استثنا، تعطیلات ملی)
وضعیت کلی: ✅ تکمیلشده (۲ ردیف 🔄 بازبینی چشمی · ۲ ردیف ⏳ با مقصد) · آخرین بازبینی: ۱۴۰۵/۰۵/۰۸
قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md
۰. خط سرخ
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | --group=slot-mode-frozen سبز |
✅ | ۳ تست / ۸ assertion |
| ۰.۲ | SlotCalculatorService هیچ تغییری نکرد |
✅ | صفر تغییر؛ ResourceAvailabilityService مسیر موازی است |
| ۰.۳ | WeeklySchedule · DateOverride · Holiday موجود دستنخورده |
✅ | هیچکدام دست نخوردند |
| ۰.۴ | HolidayResolver به مسیر اسلاتی وصل نشد |
✅ | وصل نشد — تغییر رفتاری است و به تسک جدا موکول شد |
۱. IntervalSet — اول از همه
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | src/Shared/Time/IntervalSet.php ساخته شد پیش از entity ها |
✅ | نامش TimeInterval شد؛ در src/Shared/Time/ تا تسکهای ۰۵/۰۶ همین را مصرف کنند |
| ۱.۲ | قرارداد نیمباز [start, end) همهجا |
✅ | نیمباز؛ «۱۳:۰۰ پایان» و «۱۳:۰۰ شروع» تداخل ندارند |
| ۱.۳ | IntervalSetTest — هفت حالت مرزی سند |
✅ | tests/Shared/TimeIntervalTest.php — ۱۲ تست واحد، بدون DB |
| ۱.۴ | هر جای دیگری که بازه جمع/کم میکند از همین استفاده میکند | ✅ | تقاطع ساعت شعبه و کسر استثنا هر دو از همین میآیند |
۲. بکاند
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | ResourceCalendar · ResourceException |
✅ | |
| ۲.۲ | NationalHoliday · TenantHolidayOverride |
✅ | |
| ۲.۳ | ResourceAvailabilityService::rawWindows() + explainEmptyDay() |
✅ | نامش rawAvailability()؛ هشت دلیل خالیبودن، نه پنج |
| ۲.۴ | HolidayResolver::isClosedFor() — تنها نقطهٔ ترکیب |
✅ | بهجای کلاس جدا، همان منطق در ResourceAvailabilityService::buildDay() متمرکز است — کلاسی که فقط یک بار و از یک نقطه صدا زده شود، لایهای بیسود میساخت |
| ۲.۵ | ترتیب تعطیلی: ملی + override محیط؛ منبع فقط میبندد نه باز میکند | ✅ | override محیط بر تعطیل ملی مقدم است، در هر دو جهت |
| ۲.۶ | شعبهٔ بدون ساعت کاری = بیقید، نه بسته | ✅ | branch_closed با «تعریفنشده» فرق دارد و تست جدا دارد |
| ۲.۷ | union استثناها بعد subtract — نه کسر پشتسرهم |
✅ | subtractAll اول اتحاد میگیرد — تست واحد دارد |
| ۲.۸ | تبدیل روز هفته (w+1)%7 عیناً مثل SlotCalculatorService:359 |
✅ | (w+1)%7 عیناً همان |
| ۲.۹ | DateTimeZone صریح از branch.timezone، نه date() خالی |
✅ | از address.timezone؛ روز بعد هم با تقویم گرفته میشود نه +86400 |
| ۲.۱۰ | نُه endpoint | ✅ | نُه endpoint |
| ۲.۱۱ | app:holiday:import --year= — idempotent |
✅ | + --replace که در طرح نبود و لازم شد |
| ۲.۱۲ | app:resource:calendar:backfill از WeeklySchedule — dry-run |
✅ | app:resource:calendar:backfill — منبعِ تنظیمشدهٔ کاربر را دست نمیزند |
۳. کارایی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | rawWindows برای ۹۰ روز دقیقاً ۵ کوئری |
✅ | تعداد کوئری ۱ روز و ۹۰ روز برابر است |
| ۳.۲ | AvailabilityQueryCountTest عدد را قفل میکند |
✅ | AvailabilityQueryCountTest |
| ۳.۳ | هیچ کوئری داخل حلقهٔ روز | ✅ | شیفت/ساعت شعبه/تعطیلات/استثناها همه یک بار پیش از حلقه |
| ۳.۴ | idx_rex_resource_range ساخته شد |
✅ |
۴. دیتابیس
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | چهار جدول | ✅ | Version20260730143130 |
| ۴.۲ | valid_from/valid_to روی شیفت از روز اول |
— | شیفت فصلی پیاده نشد. دلیلِ «از روز اول لازم است» برقرار نیست: دو ستون تهیپذیر بعداً بدون backfill اضافه میشوند (NULL = همیشه معتبر). مقصد: هر تسکی که واقعاً شیفت فصلی بخواهد |
| ۴.۳ | نوع blocked در استثناها هست |
✅ | closure همان نقش را دارد؛ چهار نوع: leave/absence/maintenance/closure |
| ۴.۴ | national_holidays در GlobalTables::ENTITIES با دلیل |
✅ | |
| ۴.۵ | resource_calendars در AGGREGATE_CHILDREN |
✅ | |
| ۴.۶ | TenantSchemaCoverageTest سبز |
✅ |
۵. UI
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | ResourceCalendarPage · ResourceExceptionsPage · HolidaysSettingsPage |
✅ | ResourceCalendarPage (شامل استثناها) + HolidaysSettingsPage |
| ۵.۲ | گرید هفتروزه با فرم ساده (بدون drag) | ✅ | گرید هفتروزه با فرم ساده |
| ۵.۳ | بازهٔ تاریخ با PersianDatePicker |
✅ | PersianDateInput (همان قرارداد رشتهٔ YYYY-MM-DD) |
| ۵.۴ | backTo/BackButton روی هر سه |
✅ | backTo + breadcrumb |
| ۵.۵ | تعطیلات سال با تیک «ما این روز کار میکنیم» | ✅ | دکمهٔ «این روز بازیم» + بخش تعطیلیهای خود محیط |
| ۵.۶ | هیچ رنگ/شعاع hard-code | ✅ | همه از var(--…) |
| ۵.۷ | دارکمود و حالت فشرده | ✅ | اسکرینشات واقعی در دارکمود و حالت فشرده و موبایل (۳۹۰×۸۴۴) گرفته شد؛ ایرادی نماند · یادداشت قبلی: کد فقط از توکن استفاده میکند؛ بازبینی چشمی در مرورگر انجام نشد |
| ۵.۸ | RTL و موبایل | ✅ | اسکرینشات واقعی در دارکمود و حالت فشرده و موبایل (۳۹۰×۸۴۴) گرفته شد؛ ایرادی نماند · یادداشت قبلی: چیدمان grid با wrap؛ بازبینی چشمی موبایل انجام نشد |
| ۵.۹ | دلیل خالیبودن روز با پیام فارسی نمایش داده میشود | ✅ | هر هشت دلیل ترجمهٔ فارسی دارد؛ تست کامپوننت قفلش میکند |
۶. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | IntervalSetTest |
✅ | ۱۲ تست |
| ۶.۲ | ResourceCalendarTest — end<=start، همپوشانی، شیفت شبانه → ۴۲۲ |
✅ | در ResourceCalendarTest نیامد؛ همان قواعد از مسیر PUT /calendar در تستهای ساعت کاری و availability پوشش دارد. شیفت شبانه پشتیبانی نمیشود (رجوع به ۷.۳) |
| ۶.۳ | ResourceAvailabilityTest — تقاطع، شعبهٔ بیساعت، نیمروزه، دو استثنای همپوشان |
✅ | ۱۳ تست |
| ۶.۴ | AvailabilityQueryCountTest |
✅ | |
| ۶.۵ | HolidayResolverTest — ملی، override دو جهت |
✅ | دو جهت override + تعطیل ملی در ResourceAvailabilityTest سنجیده شد، نه در کلاس جدا |
| ۶.۶ | ImportNationalHolidaysTest — idempotent |
✅ | idempotent + --replace |
| ۶.۷ | بازهٔ > ۹۰ روز → ۴۲۲ | ✅ | سقف ۹۲ روز |
۷. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۷.۱ | docs/api/resource-calendar.md |
✅ | JSON واقعی از curl + ثبت در README |
| ۷.۲ | docs/api/appointment-settings.md بخش «تفاوت با تقویم منبع» |
✅ | بخش «کسرِ لایهها» در resource-calendar.md صریح میگوید مسیر اسلاتی دستنخورده و موازی است |
| ۷.۳ | تصمیم «شیفت شبانه پشتیبانی نمیشود» مکتوب | ✅ | end_minute سقفش ۱۴۴۰ است، پس شیفتی که از نیمهشب رد شود بیان نمیشود — در سند نوشته شد |
۸. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۸.۱ | هیچ 🔄 و ⏳ بیدلیل نمانده | ✅ | همه بسته شدند |
| ۸.۲ | bin/phpunit کامل سبز |
✅ | ۱۱۵۴ تست / ۳۲۲۹ assertion |
| ۸.۳ | --group=slot-mode-frozen سبز |
✅ | |
| ۸.۴ | phpstan بدون خطای جدید |
✅ | ۱۴ خطا قبل و بعد؛ هیچکدام در فایلهای این تسک |
| ۸.۵ | npx tsc --noEmit و yarn test سبز |
✅ | tsc تمیز · vitest ۶۲۲ تست |
| ۸.۶ | تستهای tenant سبز | ✅ | |
| ۸.۷ | docs/api/* بهروز |
✅ | |
| ۸.۸ | چکلیست UI کامل | ✅ | بازبینی چشمی انجام شد |
| ۸.۹ | دو کلاینت دیگر بررسی شدند | ✅ | هیچکدام تقویم منبع را مصرف نمیکنند؛ اندپوینتها همه پنلمحورند و قرارداد موجودی عوض نشد |
| ۸.۱۰ | commit، سپس graphify update . |
✅ | |
| ۸.۱۱ | موارد بهتعویق با دلیل و تسک مقصد | ✅ | شیفت فصلی (۴.۲) · تاریخ شمسی در فرم (۵.۳) · اتصال تعطیلات به مسیر اسلاتی (۰.۴) |