`
+- تاریخ با `PersianDateInput` موجود
+- `ReserveAppointmentsPage` جدول خام دارد (``) — در همین تسک به
+ `DataTable` مهاجرت کند، چون داریم دستش میزنیم و توکنهای inline خلاف قاعدهاند
+- مدت و بافر با فارسی و واحد: «۳۵ دقیقه (+۱۰ دقیقه فاصله)»
diff --git a/docs/new_feture/taskes/task-00-service-mode-completion/checklist.md b/docs/new_feture/taskes/task-00-service-mode-completion/checklist.md
new file mode 100644
index 00000000..63529d6c
--- /dev/null
+++ b/docs/new_feture/taskes/task-00-service-mode-completion/checklist.md
@@ -0,0 +1,118 @@
+# چکلیست — تسک ۰۰ (تکمیل نوبتدهی سرویسی در clinicpro)
+
+**وضعیت کلی:** ⏳ شروع نشده
+**آخرین بازبینی:** —
+
+قواعد: [_shared/definition-of-done.md](../_shared/definition-of-done.md) ·
+خط سرخها: [_shared/red-lines.md](../_shared/red-lines.md) ·
+UI: [_shared/ui-conventions.md](../_shared/ui-conventions.md)
+
+---
+
+## ۰. خط سرخ — منطق اسلاتی
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۰.۱ | `SlotModeFrozenTest` + سه fixture ساخته شد **پیش از** هر تغییر کد | ⏳ | ترتیب مهم است |
+| ۰.۲ | fixture ها با تاریخ ثابتاند، نه `time()` | ⏳ | |
+| ۰.۳ | کامنت «read-only، هیچ تسکی بهروزش نمیکند» بالای هر سه fixture | ⏳ | |
+| ۰.۴ | هیچ متد موجود `SlotCalculatorService` ویرایش نشد | ⏳ | فقط پارامتر اختیاری `excludeAppointmentId` روی `getServiceStartTimes` |
+| ۰.۵ | `GET /appointment-slots` بیتبهبیت دستنخورده | ⏳ | |
+| ۰.۶ | `GET /month-availability/{doctorUuid}` دستنخورده | ⏳ | |
+| ۰.۷ | `active_slot_key` و `refreshActiveSlotKey()` دستنخورده | ⏳ | تنها استثنا: صدا زدنش در `setIsReserve` |
+| ۰.۸ | `isSlotTaken` امضا و معنا دستنخورده | ⏳ | |
+| ۰.۹ | `--group=slot-mode-frozen` در پایان سبز | ⏳ | |
+
+## ۱. بکاند
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۱.۱ | `ServiceBookingCalculator` ساخته شد | ⏳ | |
+| ۱.۲ | `serviceSlots()` موجود از آن استفاده میکند، **خروجیاش عوض نشده** | ⏳ | |
+| ۱.۳ | جمع سادهٔ `+=` **حفظ شد** (اصلاحش تسک ۰۴ است) | ⏳ | |
+| ۱.۴ | `ServiceRescheduleService` + `POST /appointment/{uuid}/service-reschedule` | ⏳ | |
+| ۱.۵ | `PATCH /appointment/{uuid}` توسعه یافت — منطق جدید داخل `isServiceMode()` | ⏳ | |
+| ۱.۶ | `PATCH` مقدار `service_item_uuids[]` میپذیرد | ⏳ | |
+| ۱.۷ | `ReserveConversionService` + `POST /appointment/{uuid}/convert-reserve` | ⏳ | |
+| ۱.۸ | نوبت رزرو در حالت سرویسی سرویسها را ذخیره میکند | ⏳ | |
+| ۱.۹ | `excludeAppointmentId` روی `getServiceStartTimes` و `findBusyIntervals` | ⏳ | همان الگوی `isSlotTaken` |
+| ۱.۱۰ | `Appointment::replaceServiceItems()` + `currentServiceUuids()` | ⏳ | |
+| ۱.۱۱ | `replaceServiceItems` مقدار `serviceItem` تکی را همگام میکند | ⏳ | چهار مصرفکننده رویش خواندهاند |
+| ۱.۱۲ | `setIsReserve()` صدا زدن `refreshActiveSlotKey()` | ⏳ | |
+| ۱.۱۳ | اعتبارسنجی زمان با **عضویت در `getServiceStartTimes`**، نه فقط `isSlotTaken` | ⏳ | |
+| ۱.۱۴ | `TenantOwnershipChecker` روی همهٔ uuid های سرویس، **پیش از** هر بررسی دیگر | ⏳ | |
+| ۱.۱۵ | `allowInactive` فقط برای سرویسهای موجود نوبت، نه uuid های تازه | ⏳ | |
+| ۱.۱۶ | کنترلر نازک ماند — منطق در سرویس | ⏳ | |
+
+## ۲. دیتابیس و مهاجرت
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۲.۱ | `service_total_minutes` و `service_buffer_minutes` (تهیپذیر) | ⏳ | |
+| ۲.۲ | هیچ ستون موجودی حذف/تغییر نوع/تغییر معنا نداد | ⏳ | |
+| ۲.۳ | دو کد خطای جدید در `ErrorCodes.php` با پیام فارسی | ⏳ | شمارهٔ واقعی از خود فایل |
+| ۲.۴ | `app:appointment:backfill-service-duration` — dry-run پیشفرض، idempotent | ⏳ | |
+| ۲.۵ | backfill مقدار را از خود نوبت میگیرد، نه بازمحاسبه از سرویسها | ⏳ | |
+| ۲.۶ | migration اجرا شد و `TenantSchemaCoverageTest` سبز | ⏳ | |
+
+## ۳. UI — پنل ادمین
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۳.۱ | `AppointmentEditPage`: حالت سرویسی `ServiceSlotPicker` نشان میدهد | ⏳ | |
+| ۳.۲ | `AppointmentEditPage`: حالت اسلاتی **دقیقاً** رفتار امروز | ⏳ | سه فیلد ساعت |
+| ۳.۳ | ورودی دستی ساعت در حالت سرویسی **پنهان**، نه disabled | ⏳ | |
+| ۳.۴ | `ServiceSlotPicker` موجود بازاستفاده شد؛ نسخهٔ موازی ساخته نشد | ⏳ | فقط prop `excludeAppointmentUuid` |
+| ۳.۵ | `ReserveAppointmentsPage`: سرویسها + دکمهٔ تبدیل | ⏳ | |
+| ۳.۶ | `ReserveAppointmentsPage` از جدول خام به `DataTable` مهاجرت کرد | ⏳ | ` ` حذف شد |
+| ۳.۷ | مدت و بافر فارسی با واحد: «۳۵ دقیقه (+۱۰ دقیقه فاصله)» | ⏳ | |
+| ۳.۸ | هیچ رنگ/شعاع/سایهٔ hard-code — همه از توکنهای `styles.css` | ⏳ | |
+| ۳.۹ | دارکمود (`data-theme="dark"`) بررسی شد | ⏳ | |
+| ۳.۱۰ | حالت فشرده (`data-density="compact"`) بررسی شد | ⏳ | |
+| ۳.۱۱ | انتخاب چند سرویس با `SearchableSelect`؛ هیچ `` بومی | ⏳ | |
+| ۳.۱۲ | `backTo`/`BackButton` روی هر دو صفحه | ⏳ | |
+| ۳.۱۳ | وضعیت لیست رزروها در URL با `useUrlState` | ⏳ | |
+| ۳.۱۴ | تاریخ با `PersianDateInput` · مبلغ با `formatRial` | ⏳ | |
+| ۳.۱۵ | RTL بررسی شد (`ms/me` نه `ml/mr`) | ⏳ | |
+| ۳.۱۶ | موبایل بررسی شد — بدون اسکرول افقی | ⏳ | |
+| ۳.۱۷ | همهٔ رشتهها فارسی و از i18n | ⏳ | |
+| ۳.۱۸ | داده با TanStack Query و استخراج envelope درست | ⏳ | |
+
+## ۴. تست
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۴.۱ | `SlotModeFrozenTest` — سه سنجه | ⏳ | |
+| ۴.۲ | `ServiceBookingCalculatorTest` — موفق/خطا/مرزی | ⏳ | |
+| ۴.۳ | `ServiceRescheduleTest` — شامل «حذف سرویس → مدت خودکار» | ⏳ | |
+| ۴.۴ | `PatchServiceDurationTest` — شامل «در حالت اسلاتی هیچکدام اجرا نمیشود» | ⏳ | |
+| ۴.۵ | `ConvertReserveTest` — شامل `active_slot_key` و رقابت | ⏳ | |
+| ۴.۶ | `ServiceModeSectionDurationTest` موجود سبز ماند | ⏳ | |
+| ۴.۷ | `BookingTenantTest` موجود سبز ماند | ⏳ | |
+| ۴.۸ | `AppointmentEditPage.test.tsx` — دو حالت | ⏳ | |
+
+## ۵. مستندات
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۵.۱ | `docs/api/appointment.md` — دو endpoint جدید + توسعهٔ PATCH | ⏳ | |
+| ۵.۲ | ماتریس «کدام endpoint در کدام حالت» | ⏳ | |
+| ۵.۳ | `docs/architecture/booking-modes.md` ساخته شد | ⏳ | تسک ۰۶ حالت سوم را اضافه میکند |
+| ۵.۴ | دو کد خطای جدید مستند شد | ⏳ | |
+
+## ۶. بازبینی پایانی
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۶.۱ | همهٔ ردیفهای بالا وضعیت نهایی دارند (هیچ 🔄 و ⏳ بیدلیل) | ⏳ | |
+| ۶.۲ | `ddev exec php bin/phpunit` کامل سبز | ⏳ | |
+| ۶.۳ | `ddev exec php bin/phpunit --group=slot-mode-frozen` سبز | ⏳ | |
+| ۶.۴ | `phpstan analyse` بدون خطای جدید | ⏳ | |
+| ۶.۵ | `npx tsc --noEmit` بدون خطا | ⏳ | |
+| ۶.۶ | `yarn test` سبز | ⏳ | |
+| ۶.۷ | `TenantSchemaCoverageTest` + `TenantLookupInventoryTest` سبز | ⏳ | |
+| ۶.۸ | `docs/api/*` بهروز شد | ⏳ | |
+| ۶.۹ | چکلیست UI (بخش ۳) کامل شد | ⏳ | |
+| ۶.۱۰ | `nobat724_front` و `clinic-pro-tauri` دستی بررسی شدند | ⏳ | `service_item` تکی همگام است؟ |
+| ۶.۱۱ | commit شد، سپس `graphify update .` | ⏳ | |
+| ۶.۱۲ | موارد بهتعویقافتاده با دلیل و تسک مقصد ثبت شدند | ⏳ | |
diff --git a/docs/new_feture/taskes/task-00-service-mode-completion/database.md b/docs/new_feture/taskes/task-00-service-mode-completion/database.md
new file mode 100644
index 00000000..c96bab1e
--- /dev/null
+++ b/docs/new_feture/taskes/task-00-service-mode-completion/database.md
@@ -0,0 +1,123 @@
+# دیتابیس — تسک ۰۰
+
+## تغییر `appointments` — دو ستون تهیپذیر
+
+```sql
+ALTER TABLE appointments
+ ADD COLUMN service_total_minutes SMALLINT NULL,
+ ADD COLUMN service_buffer_minutes SMALLINT NULL;
+```
+
+| ستون | معنی | چرا لازم است |
+|---|---|---|
+| `service_total_minutes` | مدت محاسبهشدهٔ ترکیب سرویسها در لحظهٔ ثبت | `slot_end - slot_start` عدد را دارد ولی نمیگوید عمدی بود یا دستی؛ و برای نوبت رزرو (که `slot_start = slot_end`) هیچجا مدت را نگه نمیداریم |
+| `service_buffer_minutes` | `buffer_minutes` مؤثر در لحظهٔ ثبت | تغییر بافر در تنظیمات نباید معنای نوبتهای ثبتشده را عوض کند |
+
+هر دو **تهیپذیر** و هر دو در حالت اسلاتی `NULL` میمانند. هیچ ستون موجودی حذف، تغییر
+نوع یا تغییر معنا نمیدهد.
+
+⛔ `slot_start` و `slot_end` و `active_slot_key` و `is_reserve` دستنخورده. خط سرخ.
+
+### چرا نه یک ستون JSON
+
+وسوسه: یک `service_meta JSON` با همهچیز. رد شد چون تسک ۱۴ (گزارش دقت برنامه) روی
+`plan_total_minutes` تجمعی میزند و JSON را نمیتواند `AVG` کند. دو ستون `SMALLINT`
+ارزانترند و تسک ۰۷ ستون `plan_total_minutes` را کنارشان اضافه میکند
+(اسم متفاوت، معنی متفاوت: آن یکی مدت برنامهٔ چندبخشی است).
+
+## ایندکس
+
+هیچ ایندکس جدیدی. `idx_appointments_doctor_slot` و `idx_appointments_tenant_slot` موجود
+همهٔ کوئریهای این تسک را پوشش میدهند.
+
+## `appointment_service_items` — بدون تغییر schema
+
+جدول واسط ManyToMany موجود. تنها تغییر، **رفتاری** است:
+
+```php
+// Appointment — متد جدید، بدون دست زدن به متدهای موجود
+/** جایگزینی کامل سرویسها؛ serviceItem تکی هم با اولی همگام میشود. */
+public function replaceServiceItems(array $items): self
+{
+ $this->serviceItems->clear();
+ foreach ($items as $item) {
+ if (!$this->serviceItems->contains($item)) { $this->serviceItems->add($item); }
+ }
+ $this->serviceItem = $items[0] ?? null; // ← سازگاری با مصرفکنندهٔ تکی
+ $this->updatedAt = time();
+ return $this;
+}
+
+/** @return string[] uuid سرویسهای فعلی، به ترتیب */
+public function currentServiceUuids(): array
+{
+ $uuids = array_map(fn($i) => $i->getUuid(), $this->serviceItems->toArray());
+ if ($uuids === [] && $this->serviceItem !== null) { $uuids = [$this->serviceItem->getUuid()]; }
+ return $uuids;
+}
+```
+
+همگامسازی `serviceItem` تکی اجباری است: `AppointmentsPage`، `ReserveAppointmentsPage`،
+`nobat724_front` و `clinic-pro-tauri` هر چهار روی `service_item` تکی خواندهاند. رهاکردنش
+یعنی نوبت با سرویسهای جدید ولی نام سرویس قدیمی در لیست.
+
+## کدهای خطای جدید
+
+در `src/Shared/Constant/ErrorCodes.php`:
+
+```php
+public const ERR_SERVICE_DURATION_MISMATCH = 'ERR_APPOINTMENT_010';
+// پیام: مدت این نوبت با مجموع مدت سرویسهای انتخابی نمیخواند
+
+public const ERR_WRONG_BOOKING_MODE = 'ERR_APPOINTMENT_011';
+// پیام: این عملیات با روش نوبتدهی این محیط سازگار نیست
+```
+
+شمارهٔ بعدی دامنهٔ `APPOINTMENT` را از خود فایل بگیر، این دو عدد حدسیاند.
+هر دو کد در تسکهای ۰۶ و ۰۷ هم استفاده میشوند، پس نامشان عمومی است نه مخصوص این تسک.
+
+## Migration
+
+```bash
+ddev exec php bin/console doctrine:migrations:diff --no-interaction
+ddev exec php bin/console doctrine:migrations:migrate --no-interaction
+```
+
+## backfill
+
+```bash
+ddev exec php bin/console app:appointment:backfill-service-duration # dry-run
+ddev exec php bin/console app:appointment:backfill-service-duration --force
+```
+
+برای هر نوبت `pending`/`confirmed` **آیندهٔ** یک محیط سرویسی که `service_total_minutes`
+ندارد:
+
+```
+service_total_minutes = (slot_end - slot_start) / 60
+service_buffer_minutes = buffer_minutes فعلیِ همان برنامه
+```
+
+مقدار از خودِ نوبت گرفته میشود، **نه از مدت سرویسها** — چون نوبت موجود ممکن است با
+مدت دستی ثبت شده باشد و بازمحاسبه یعنی تغییر گذشته.
+
+نوبتهای اسلاتی و نوبتهای گذشته رد میشوند. idempotent.
+
+## fixture های تست خط سرخ
+
+```
+tests/Appointment/fixtures/slot-mode-contract.json # پاسخ appointment-slots
+tests/Appointment/fixtures/month-availability-contract.json # پاسخ month-availability
+tests/Appointment/fixtures/slot-calculator-signatures.php # امضای متدهای عمومی
+```
+
+⛔ این سه فایل بعد از این تسک **read-only** اند. هیچ تسکی اجازهٔ بهروزرسانیشان را ندارد.
+اگر تستی قرمز شد، کد باید برگردد نه fixture. این جمله را در بالای هر سه فایل بهعنوان
+کامنت بنویس.
+
+fixture ها با تاریخ ثابت ساخته میشوند (`2026-01-05` مثلاً)، نه `time()` — وگرنه فردا قرمز.
+
+## طبقهبندی tenant
+
+هیچ entity جدیدی. `appointments` از قبل جفت tenant دارد.
+`TenantSchemaCoverageTest` باید بدون تغییر سبز بماند.
diff --git a/docs/new_feture/taskes/task-00-service-mode-completion/implementation_notes.md b/docs/new_feture/taskes/task-00-service-mode-completion/implementation_notes.md
new file mode 100644
index 00000000..e63f5c0a
--- /dev/null
+++ b/docs/new_feture/taskes/task-00-service-mode-completion/implementation_notes.md
@@ -0,0 +1,204 @@
+# نکات پیادهسازی — تسک ۰۰
+
+## ۱. اول تست خط سرخ، بعد هر چیز دیگر
+
+ترتیب کار:
+
+```
+۱. tests/Appointment/SlotModeFrozenTest.php + سه fixture ← اول این
+۲. ddev exec php bin/phpunit --group=slot-mode-frozen ← باید سبز باشد قبل از هر تغییری
+۳. بقیهٔ تسک
+۴. دوباره گام ۲ — باید همچنان سبز باشد
+```
+
+اگر fixture را بعد از تغییرات بسازی، هیچ چیزی را تضمین نکردهای — snapshot وضعیت
+تغییریافته را گرفتهای.
+
+## ۲. `isServiceMode` گِیت همهچیز است
+
+هر خط کد جدید در مسیر مشترک باید داخل این شرط باشد:
+
+```php
+if ($this->serviceCalc->isServiceMode($doctor, $clinic)) {
+ // … منطق جدید
+}
+```
+
+نه بیرونش، نه با `??`، نه با «اگر سرویس دارد». معیار **فقط** `booking_mode` است.
+نوبت اسلاتی هم میتواند `service_item_id` داشته باشد (فیلدهای Figma نوبتها) — آن دلیل
+سرویسی بودن نیست.
+
+اشتباه رایج:
+
+```php
+// ❌ نوبت اسلاتیِ دارای سرویس را وارد مسیر جدید میکند
+if ($appointment->getServiceItems()->count() > 0) { … }
+```
+
+## ۳. جمع سادهٔ مدت را همینجا اصلاح نکن
+
+```php
+$totalMinutes += $duration; // ← اشتباه است، ولی دست نزن
+```
+
+مستند بند ۵ میگوید این فرمول ظرفیت را الکی پر میکند و راهحلش «زمان تنها / زمان اضافه»
+است — که تسک ۰۴ میسازد. اصلاحش اینجا یعنی:
+
+- مدت همهٔ نوبتهای چندسرویسیِ در حال رزرو یکشبه کم میشود
+- سایت و اپ دسکتاپ عدد متفاوت میبینند بدون اینکه چیزی در build بشکند
+- و هیچ دادهای برای «زمان اضافه» وجود ندارد، پس اصلاح بیورودی غیرممکن است
+
+کاری که این تسک میکند: محاسبه را به **یک نقطه** منتقل میکند تا تسک ۰۴ یک خط عوض کند.
+
+## ۴. `excludeAppointmentId` — الگوی موجود را تکرار کن
+
+`AppointmentRepository::isSlotTaken($doctor, $start, $end, $excludeId)` از قبل این پارامتر
+را دارد. `findBusyIntervals` هم همان را بگیرد، با همان نام و همان جای پارامتر و همان
+پیشفرض `null`.
+
+دو الگوی متفاوت برای یک کار (مثلاً یکی `?int $excludeId`، دیگری `array $excludeIds`)
+یعنی اولین کسی که هر دو را میبیند یکی را اشتباه صدا میزند.
+
+## ۵. اعتبارسنجی زمان: عضویت در فهرست، نه «اشغال نبودن»
+
+```php
+// ❌ ناکافی
+if ($this->appointmentRepo->isSlotTaken($doctor, $start, $end, $excludeId)) { /* 409 */ }
+
+// ✅
+$starts = $this->slotCalculator->getServiceStartTimes(…);
+if (!in_array($req->start, array_column($starts, 'start'), true)) { /* 422 */ }
+```
+
+`isSlotTaken` فقط تداخل با نوبت دیگر را میگوید. `getServiceStartTimes` علاوه بر آن
+شیفت، تعطیلی، `date_override`، پنجرهٔ رزرو و بافر را هم اعمال میکند. با شرط اول،
+منشی میتواند نوبت را ساعت ۳ بامداد بگذارد.
+
+## ۶. `warnings[]` بهجای `422` برای سرویس غیرفعال در نوبت موجود
+
+```php
+$duration = $this->serviceCalc->calculate(…, allowInactive: true);
+// $duration->warnings === ['سرویس «لیزر صورت» دیگر برای نوبتدهی فعال نیست']
+```
+
+نوبت موجود با سرویسی که کلینیک غیرفعالش کرده، باید قابل جابهجایی و لغو بماند. `422`
+یعنی آن نوبت برای همیشه قفل میشود و منشی هیچ کاری نمیتواند بکند.
+
+ولی **افزودن** سرویس غیرفعال به نوبت → `422`. تفاوتش `allowInactive` است که فقط برای
+uuid های موجودِ نوبت `true` میشود، نه برای uuid های تازهی درخواست.
+
+## ۷. `replaceServiceItems` باید `serviceItem` تکی را همگام کند
+
+```php
+$this->serviceItem = $items[0] ?? null;
+```
+
+چهار مصرفکننده روی `service_item` تکی خواندهاند (`AppointmentsPage`،
+`ReserveAppointmentsPage`، `nobat724_front/services/response.js`،
+`clinic-pro-tauri/src/service/response.js`). این دقیقاً همان الگویی است که
+`ServiceItem::setStaffMembers()` برای `staff` تکی دارد — تکرارش کن.
+
+## ۸. `refreshActiveSlotKey` پس از `setIsReserve(false)`
+
+```php
+public function setIsReserve(bool $v): self
+{
+ $this->isReserve = $v;
+ $this->refreshActiveSlotKey(); // ← اگر نیست، اضافه کن
+ $this->updatedAt = time();
+ return $this;
+}
+```
+
+بدون آن، رزروِ تبدیلشده `active_slot_key = NULL` میماند و دو نفر میتوانند همان ساعت
+را بگیرند. **این تنها تغییر مجاز در مکانیزم `active_slot_key` است** و فقط چون یک شرط
+موجود را اعمال میکند، نه عوضش میکند. تست: `ConvertReserveSlotKeyTest`.
+
+## ۹. تبدیل رزرو، اتمی
+
+```php
+$this->em->wrapInTransaction(function () use ($reserve, $req) {
+ $duration = …; // یا اسلات اسلاتی
+ $reserve->setIsReserve(false);
+ $reserve->reschedule($req->start, $end);
+ $reserve->replaceServiceItems($duration->serviceItems);
+ // UniqueConstraintViolationException روی active_slot_key → 409
+});
+```
+
+`try/catch` روی `UniqueConstraintViolationException` و ترجمه به `409 ERR_SLOT_TAKEN` —
+همان چیزی که `SlotTakenException` موجود در `src/Appointment/Repository/` انجام میدهد.
+از همان استفاده کن.
+
+## ۱۰. `ReserveAppointmentsPage` → `DataTable`
+
+صفحه امروز جدول خام با `` دارد. چون در این تسک دستش میزنیم، همانجا به
+`DataTable` مهاجرت کند: توکن inline خلاف [ui-conventions](../_shared/ui-conventions.md)
+است و در دارکمود میشکند.
+
+این «scope creep» نیست — قاعدهٔ پروژه است که صفحهٔ دستخورده باید با دیزاینسیستم بخواند.
+
+## ۱۱. edge case ها
+
+| حالت | رفتار درست |
+|---|---|
+| نوبت سرویسی بدون هیچ سرویس (داده قدیمی) | مدت موجود حفظ · `warnings[]` · **رد نمیشود** |
+| `PATCH` فقط یادداشت روی نوبت سرویسی | بدون اعتبارسنجی مدت — مثل امروز |
+| `service-reschedule` روی نوبت اسلاتی | `422 ERR_WRONG_BOOKING_MODE` |
+| `service-reschedule` روی نوبت رزرو | `422` — مسیرش `convert-reserve` است |
+| زمان فعلی نوبت با سرویس جدید | با `excludeAppointmentId` در فهرست میآید |
+| بافر عوض شد بعد از ثبت | نوبت موجود سالم؛ فقط جابهجایی جدید بافر جدید میگیرد |
+| سرویس محیط دیگر در `service_item_uuids[]` | `404` — `TenantOwnershipChecker` **پیش از** هر بررسی دیگر |
+| `durations` override با مقدار ۰ یا منفی | نادیده گرفته شود (رفتار موجود `serviceSlots`) |
+| نوبت گذشته | `service-reschedule` → `422` |
+| دو درخواست جابهجایی همزمان به یک زمان | `active_slot_key` → یکی `409` |
+| نوبت در محیطی که وسط کار به اسلاتی برگشت | `booking_mode` قفل است پس رخ نمیدهد؛ ولی اگر داده دستی عوض شد → `422` روشن |
+
+## ۱۲. تست
+
+```
+tests/Appointment/SlotModeFrozenTest.php ← ⭐ اول از همه
+ - قرارداد appointment-slots بیتبهبیت
+ - قرارداد month-availability
+ - امضای متدهای عمومی SlotCalculatorService
+tests/Appointment/ServiceBookingCalculatorTest.php
+ - جمع مدت چند سرویس (رفتار فعلی حفظ شود)
+ - override منشی
+ - سرویس بدون مدت → 422
+ - سرویس غیرفعال با allowInactive → warning نه خطا
+ - سرویس محیط دیگر → استثنا، بدون افشای وجود
+tests/Appointment/ServiceRescheduleTest.php ← ⭐
+ - جابهجایی با همان سرویسها → مدت یکسان
+ - حذف یک سرویس → مدت خودکار کم میشود، کلاینت عددی نفرستاده
+ - زمان بیرون getServiceStartTimes → 422
+ - زمان فعلی خود نوبت با excludeAppointmentId در فهرست است
+ - روی نوبت اسلاتی → 422 ERR_WRONG_BOOKING_MODE
+tests/Appointment/PatchServiceDurationTest.php
+ - slot_end ناسازگار → 422 ERR_SERVICE_DURATION_MISMATCH با مدت درست در پیام
+ - service_item_uuids[] جایگزینی کامل میکند و serviceItem تکی همگام میشود
+ - در حالت اسلاتی هیچکدام از اینها اجرا نمیشود (رفتار امروز)
+tests/Appointment/ConvertReserveTest.php
+ - رزرو سرویسی → نوبت زماندار با مدت درست
+ - رزرو اسلاتی → مسیر اسلاتی، بدون تغییر
+ - active_slot_key بعد از تبدیل پر میشود
+ - دو تبدیل همزمان → یکی 409
+tests/Appointment/ServiceModeSectionDurationTest.php ← موجود، باید سبز بماند
+tests/Appointment/BookingTenantTest.php ← موجود، باید سبز بماند
+assets/admin/pages/AppointmentEditPage.test.tsx
+ - حالت اسلاتی: سه فیلد ساعت هست، ServiceSlotPicker نیست
+ - حالت سرویسی: ServiceSlotPicker هست، فیلد ساعت پنهان است
+ - تغییر سرویسها، slot انتخابی را باطل میکند
+```
+
+## ۱۳. مستندات
+
+`docs/api/appointment.md`:
+- بخش «روشهای نوبتدهی» با ماتریس «کدام endpoint در کدام حالت»
+- دو endpoint جدید
+- توسعهٔ `PATCH` و پارامتر `exclude_appointment_uuid`
+- دو کد خطای جدید
+
+`docs/api/appointment-settings.md`: یادآوری قفل بودن `booking_mode` پس از اولین ثبت.
+
+و یک سند کوتاه `docs/architecture/booking-modes.md` که ماتریس کامل را ثبت کند —
+تسک ۰۶ حالت سومی به همین ماتریس اضافه میکند.
diff --git a/docs/new_feture/taskes/task-00-service-mode-completion/task.md b/docs/new_feture/taskes/task-00-service-mode-completion/task.md
new file mode 100644
index 00000000..1eb552f1
--- /dev/null
+++ b/docs/new_feture/taskes/task-00-service-mode-completion/task.md
@@ -0,0 +1,143 @@
+# تسک ۰۰ — تکمیل نوبتدهی سرویسی در clinicpro
+
+**فاز:** ۰ (تثبیت وضعیت فعلی) · **وابستگی:** — · **زمان:** ۱۴-۱۸ ساعت
+**پیشنیاز همهٔ تسکهای ۰۱ به بعد**
+
+---
+
+## ⛔ خط سرخ
+
+منطق اسلاتی (`booking_mode = 'slot'`) در این تسک **به هیچ عنوان** دستکاری نمیشود.
+فهرست کامل قفلشدهها: [_shared/red-lines.md](../_shared/red-lines.md).
+
+این تسک fixture و تست `--group=slot-mode-frozen` را **میسازد** — همان تستی که همهٔ
+تسکهای بعدی باید سبز نگهش دارند.
+
+---
+
+## هدف
+
+حالت `booking_mode = 'service'` در مسیر **رزرو** کار میکند، ولی در بقیهٔ چرخهٔ عمر نوبت
+غایب است. این تسک آن را کامل میکند تا موتور چندمنبعی (تسک ۰۶) روی پایهٔ سالم ساخته شود.
+
+## وضعیت فعلی — چه کار میکند و چه نمیکند
+
+### ✅ کار میکند
+
+| مسیر | فایل |
+|---|---|
+| انتخاب سرویس و اسلات در رزرو عمومی | `GET /api/v1/appointment-booking-services/{doctorUuid}` · `GET /api/v1/appointment-service-slots` |
+| محاسبهٔ زمانهای شروع بر اساس مدت سرویس | `SlotCalculatorService::getServiceStartTimes()` |
+| ثبت نوبت با چند سرویس | `POST /api/v1/appointment` + `appointment_service_items` |
+| توگل روش نوبتدهی در تنظیمات | `assets/admin/components/schedule/ScheduleSection.tsx` |
+| ساخت نوبت از پنل | `assets/admin/pages/AppointmentCreatePage.tsx` + `components/appointments/ServiceSlotPicker.tsx` + `hooks/useDoctorBookingServices.ts` |
+| ساخت سریع از drawer | `assets/admin/components/NewAppointmentDrawer.tsx` |
+| رزرو از سایت | `nobat724_front/components/appointment/*` |
+
+### ❌ کار نمیکند — شکافهای این تسک
+
+**۱. ویرایش و جابهجایی نوبت، حالت سرویسی را نمیشناسد.**
+
+`PATCH /api/v1/appointment/{uuid}` ([AppointmentController.php:1077](../../../src/Appointment/Controller/AppointmentController.php#L1077)):
+
+```php
+$hasStart = array_key_exists('slot_start', $data);
+$hasEnd = array_key_exists('slot_end', $data);
+// … فقط این دو بررسی میشوند:
+if ($newEnd <= $newStart) { /* 422 */ }
+if ($this->appointmentRepo->isSlotTaken($doctor, $newStart, $newEnd, $id)) { /* 409 */ }
+```
+
+سه مشکل:
+- مدت دلخواه پذیرفته میشود؛ هیچ بررسیای که `slot_end - slot_start` با مجموع مدت
+ سرویسهای نوبت بخواند وجود ندارد
+- `buffer_minutes` نادیده گرفته میشود — نوبت جدید میتواند چسبیده به نوبت بعدی بنشیند
+- فقط `service_item_uuid` تکی بهروز میشود؛ `service_items` (ManyToMany) دستنخورده
+ میماند → نوبت با سرویسهای قبلی و مدت جدید ناسازگار میشود
+
+**۲. `AppointmentEditPage.tsx` ورودی دستی ساعت دارد.**
+
+سه فیلد `date`/`start`/`end` آزاد + یک `SearchableSelect` تکی برای سرویس
+([AppointmentEditPage.tsx:74-76](../../../assets/admin/pages/AppointmentEditPage.tsx#L74)).
+هیچ `ServiceSlotPicker` ای نیست، هیچ چند-سرویسی نیست.
+
+نتیجه: منشی نوبت سرویسیِ ۴۵ دقیقهای را ویرایش میکند، ۲۰ دقیقه میگذارد، سیستم قبول
+میکند، و بیمار بعدی روی نوبت اول مینشیند.
+
+**۳. نوبت رزرو (`is_reserve`) در حالت سرویسی معنا ندارد.**
+
+`NewAppointmentDrawer.tsx:72` صریح: `$serviceMode = bookingMode === 'service' && !isReserve`.
+پس نوبت رزرو همیشه اسلاتی رفتار میکند و `ReserveAppointmentsPage.tsx` فقط
+`service_item?.name` تکی نشان میدهد. تبدیل رزرو به نوبت واقعی هم مسیر سرویسی ندارد.
+
+**۴. `patient_facing` بودن مدت جایی نمایش داده نمیشود.**
+
+پاسخ `appointment-service-slots` مدت کل را میدهد ولی نوبت ثبتشده هیچجا نگه نمیدارد
+که این مدت از کدام سرویسها و چه بافری آمده. لیست نوبتها فقط `slot_start/slot_end` دارد.
+
+## دامنه
+
+**هست:**
+- `ServiceBookingCalculator` — یک سرویس واحد که «مدت مجاز یک ترکیب سرویس» را حساب میکند
+ (استخراج منطق تکرارشدهٔ `serviceSlots()` از کنترلر)
+- اعتبارسنجی حالت سرویسی در `PATCH /appointment/{uuid}`
+- endpoint جابهجایی سرویسآگاه: `POST /api/v1/appointment/{uuid}/service-reschedule`
+- `ServiceSlotPicker` در `AppointmentEditPage`
+- حالت سرویسی برای نوبت رزرو + تبدیل رزرو به نوبت
+- ستونهای `service_total_minutes` و `service_buffer_minutes` روی `appointments`
+- fixture و تست `--group=slot-mode-frozen`
+
+**نیست:** بخشهای نوبت، چند منبع، قوانین (تسک ۰۵ به بعد). `nobat724_front` (تسک ۰۰ب).
+
+## Endpoint ها
+
+| متد | مسیر | توضیح |
+|---|---|---|
+| POST | `/api/v1/appointment/{uuid}/service-reschedule` | جابهجایی سرویسآگاه: سرویسها + زمان شروع؛ مدت را خودش حساب میکند |
+| PATCH | `/api/v1/appointment/{uuid}` | **توسعه** — در حالت سرویسی مدت را اعتبارسنجی میکند و `service_item_uuids[]` میپذیرد |
+| POST | `/api/v1/appointment/{uuid}/convert-reserve` | تبدیل نوبت رزرو به نوبت زماندار (هر دو حالت) |
+| GET | `/api/v1/appointment-service-slots` | **توسعه** — پارامتر `exclude_appointment_uuid` برای جابهجایی |
+
+هیچ endpoint اسلاتیای تغییر نمیکند. `GET /appointment-slots` دستنخورده.
+
+## معیار پذیرش
+
+- ✅ موفق: نوبت سرویسیِ «لیزر صورت (۲۰) + بیکینی (۱۵)» با مدت ۳۵ دقیقه.
+ `POST /appointment/{uuid}/service-reschedule` با زمان جدید و همان سرویسها →
+ `200` و `slot_end - slot_start = 35 * 60` دقیقاً.
+- ✅ موفق: همان endpoint با حذف بیکینی → مدت خودکار ۲۰ دقیقه میشود، بدون اینکه کلاینت
+ عددی بفرستد.
+- ✅ موفق: `GET /appointment-service-slots?…&exclude_appointment_uuid={uuid}` بازهٔ خودِ
+ نوبت را اشغال حساب نمیکند، پس زمان فعلیاش در فهرست میآید.
+- ✅ موفق: `AppointmentEditPage` برای نوبت سرویسی، `ServiceSlotPicker` نشان میدهد و
+ ورودی دستی ساعت را **پنهان** میکند؛ برای نوبت اسلاتی، دقیقاً رفتار امروز.
+- ✅ موفق: نوبت رزرو در حالت سرویسی سرویسهایش را ذخیره میکند و
+ `POST /convert-reserve` با زمان انتخابی، نوبت زماندار با مدت درست میسازد.
+- ✅ موفق (**خط سرخ**): `ddev exec php bin/phpunit --group=slot-mode-frozen` سبز است و
+ fixture قرارداد اسلاتی بیتبهبیت تغییر نکرده.
+- ❌ خطا: `PATCH` با `slot_end - slot_start` ناسازگار با مدت سرویسها →
+ `422` `ERR_SERVICE_DURATION_MISMATCH` با پیام فارسی شامل مدت درست.
+- ❌ خطا: `service-reschedule` روی نوبت **اسلاتی** → `422` `ERR_WRONG_BOOKING_MODE`.
+- ❌ خطا: `service-reschedule` با زمان شروعی که در `getServiceStartTimes` نیست →
+ `422` با پیام «این زمان برای مدت انتخابی در دسترس نیست».
+- ❌ خطا: سرویس محیط دیگر در `service_item_uuids[]` → `404` (بدون لو دادن وجودش).
+- ⚠️ مرزی: نوبتی که سرویسهایش غیرفعال (`bookable=false`) شدهاند → جابهجایی مجاز است
+ با `warnings[]`؛ افزودن سرویس غیرفعال ممنوع.
+- ⚠️ مرزی: `buffer_minutes` تغییر کرد بعد از ثبت نوبت → نوبت موجود سالم میماند؛
+ فقط جابهجایی جدید بافر جدید را میگیرد.
+- ⚠️ مرزی: جابهجایی به روزی که برنامهٔ هفتگی آن محیط عوض شده → همان اعتبارسنجی
+ `getServiceStartTimes`، پس خودکار پوشش داده میشود.
+- ⚠️ مرزی: نوبت سرویسی بدون هیچ سرویس (داده قدیمی) → مدت موجود حفظ میشود و
+ `warnings[]` میگوید سرویس ثبت نشده. **رد نمیشود.**
+- ⚠️ مرزی: `PATCH` بدون `slot_start` روی نوبت سرویسی (فقط تغییر یادداشت) → بدون
+ اعتبارسنجی مدت، مثل امروز.
+
+## خروجی
+
+- `src/Appointment/Service/ServiceBookingCalculator.php` + توسعهٔ کنترلر
+- `assets/admin/pages/AppointmentEditPage.tsx` توسعهیافته
+- `assets/admin/pages/ReserveAppointmentsPage.tsx` توسعهیافته
+- migration دو ستون تهیپذیر
+- `tests/Appointment/SlotModeFrozenTest.php` + fixture
+- `docs/api/appointment.md` بهروزرسانی
+- [checklist.md](checklist.md) کاملشده
diff --git a/docs/new_feture/taskes/task-00b-nobat724-service-mode/architecture.md b/docs/new_feture/taskes/task-00b-nobat724-service-mode/architecture.md
new file mode 100644
index 00000000..ca889241
--- /dev/null
+++ b/docs/new_feture/taskes/task-00b-nobat724-service-mode/architecture.md
@@ -0,0 +1,203 @@
+# معماری — تسک ۰۰ب
+
+پروژه: `nobat724_front` · Next.js 15 App Router · MUI v5 + Tailwind · RTL · Vazir
+
+## فایلهای درگیر
+
+```
+components/appointment/
+├── index.js # ارکستراتور مراحل — تغییر جزئی
+├── service/index.js # ⚠️ بازنویسی با توکن تم
+├── date/index.js # مصرف adaptServiceSlots
+└── detail/SubmitData.js # تغییر جزئی
+
+lib/appointmentSlots.js # adaptServiceSlots شیفتآگاه
+services/response.js # endpoint های جدید تسک ۰۰
+
+components/dashboard/userAccount/sidebars/turns/
+├── Card.js # + سرویس و مدت
+├── isTurnsDetails/DetailLg.js
+├── isTurnsDetails/DetailSm.js
+└── isTurnsDetails/ButtonData.js # + جابهجایی سرویسآگاه
+```
+
+## ۱. بازنویسی `service/index.js` — توکن، نه hex
+
+وضعیت فعلی چهار رنگ hard-code دارد و در دارکمود میشکند:
+
+```jsx
+// وضعیت فعلی
+۱. انتخاب سرویس
+className={active ? "border-[#5559CE] bg-[#5559CE]/5"
+ : "border-gray-200 bg-white hover:border-[#5559CE]"}
+```
+
+```jsx
+// هدف — همان ساختار DOM، رنگ از تم
+۱. انتخاب سرویس
+className={active
+ ? "border-primary bg-primary/5"
+ : "border-border bg-surface hover:border-primary"}
+```
+
+⚠️ **نام دقیق کلاسها را از `tailwind.config.js` و `mui/index.js` همین پروژه بردار.**
+اسمهای بالا نمونهاند. قاعده: هر رنگی که در بقیهٔ مراحل رزرو (`location/`، `date/`،
+`information/`) استفاده میشود، اینجا هم همان — نه یک پالت جدید.
+
+**رفتار عوض نمیشود:** همان toggle، همان ساختار، همان متنها. فقط منبع رنگ.
+
+اگر پروژه توکن معادل ندارد (مثلاً `bg-surface` تعریف نشده)، از همان الگویی استفاده کن
+که مرحلهٔ قبلی (`location/index.js`) دارد — نه ساختن توکن جدید در این تسک.
+
+## ۲. حذف محاسبهٔ موازی مدت
+
+```js
+// ❌ وضعیت فعلی — منبع دوم حقیقت
+const totalMinutes = services
+ .filter((s) => draft.includes(s.uuid))
+ .reduce((sum, s) => sum + (Number(s.duration_minutes) || 0), 0);
+```
+
+مدت باید از پاسخ `appointment-service-slots` بیاید که از قبل `total_duration_minutes` و
+`buffer_minutes` دارد. ولی یک مسئلهٔ ترتیبی هست: مرحلهٔ انتخاب سرویس **پیش از** انتخاب
+روز است، و آن endpoint تاریخ میخواهد.
+
+دو گزینه:
+
+| گزینه | ارزیابی |
+|---|---|
+| فراخوانی `appointment-service-slots` با تاریخ امروز فقط برای گرفتن مدت | یک درخواست اضافه، و اگر امروز تعطیل باشد پاسخ خالی است ولی `total_duration_minutes` همچنان میآید ✅ |
+| نگهداشتن محاسبهٔ فرانت بهعنوان تخمین + اصلاح در مرحلهٔ بعد | بیمار دو عدد متفاوت میبیند ❌ |
+
+**انتخاب: گزینهٔ اول**، با یک تفاوت مهم — مدت **تخمینی** برچسب میگیرد تا وقتی روز
+انتخاب نشده:
+
+```js
+// مرحلهٔ انتخاب سرویس
+const { data } = useServiceDuration(doctorUuid, clinicUuid, draft); // hook جدید
+const minutes = data?.total_duration_minutes ?? fallbackSum(draft); // fallback با console.warn
+
+مدت تقریبی: {minutes} دقیقه // پیش از انتخاب روز
+مدت نوبت: {minutes} دقیقه // پس از انتخاب روز، از همان پاسخ
+```
+
+`fallbackSum` فقط برای بکاند قدیمی است و `console.warn` میزند. حذفش پس از deploy تسک ۰۰.
+
+## ۳. `adaptServiceSlots` شیفتآگاه
+
+مشکل: همهٔ زمانها در یک تب با برچسب ثابت «زمانهای خالی» جمع میشوند و `end_time`
+اشتباه است (پایانِ آخرین **شروع**، نه پایان نوبت).
+
+```js
+// هدف — گروهبندی بر اساس شکاف زمانی، با برچسب واقعی
+export function adaptServiceSlots(slotsResponse) {
+ const payload = slotsResponse?.data ?? slotsResponse ?? {};
+ const starts = payload.start_times ?? [];
+ if (!starts.length) return [];
+
+ const durationMin = Number(payload.total_duration_minutes) || 0;
+ const GAP_THRESHOLD_MIN = 60; // شکاف بیشتر از یک ساعت = شیفت جدا
+
+ const groups = [];
+ let current = null;
+
+ for (const s of starts) {
+ const gapMin = current
+ ? (s.start - current.slots[current.slots.length - 1].start) / 60
+ : Infinity;
+
+ if (!current || gapMin > GAP_THRESHOLD_MIN) {
+ current = { slots: [] };
+ groups.push(current);
+ }
+ current.slots.push({ ...s, is_available: true });
+ }
+
+ return groups.map((g) => {
+ const first = g.slots[0];
+ const last = g.slots[g.slots.length - 1];
+ const endTime = last.end_time ?? addMinutes(last.start_time, durationMin);
+ return {
+ start_time: first.start_time,
+ end_time: endTime,
+ label: `${first.start_time} - ${endTime}`, // ← همان قالب حالت اسلاتی
+ slots: g.slots,
+ };
+ });
+}
+```
+
+`end_time` هر start از قبل در پاسخ بکاند هست (`getServiceStartTimes` هر آیتم را با
+`end_time` میدهد) — پس `addMinutes` فقط fallback است.
+
+**چرا آستانهٔ شکاف و نه اطلاعات شیفت از بکاند؟** پاسخ `appointment-service-slots`
+امروز فقط `start_times` مسطح میدهد و شیفت را نمیگوید. دو راه بود:
+
+| راه | ارزیابی |
+|---|---|
+| افزودن گروهبندی شیفت به پاسخ بکاند | درستتر، ولی تغییر قرارداد endpoint که سه کلاینت مصرفش میکنند — و تسک ۰۰ آن را قفل نکرده ولی بازش هم نکرده |
+| **گروهبندی هیوریستیک در فرانت** ✅ | بدون تغییر قرارداد؛ برای شیفت صبح/عصر (شکاف معمولاً ۲-۳ ساعت) دقیق است |
+
+انتخاب دوم برای این تسک. اگر بعداً دقت کافی نبود، تسک ۰۶ که `AvailabilityEngine` را
+میسازد میتواند گروهبندی واقعی را در پاسخِ **endpoint جدید** بدهد — بدون دست زدن به
+این یکی. این تصمیم را در `docs/api/appointment.md` سمت بکاند هم یادداشت کن.
+
+⛔ `adaptSlots()` (حالت اسلاتی) **یک خط هم** عوض نمیشود.
+
+## ۴. سرویس و مدت در پنل کاربر
+
+`GET /api/v1/appointments/user` از قبل چه میدهد؟ **پیش از کدنویسی بررسی کن.** اگر
+`service_items` و `service_total_minutes` در پاسخ نیست:
+
+- ستون `service_total_minutes` در تسک ۰۰ اضافه شد ✅
+- افزودنش به سریالایزر پاسخ، **بخشی از تسک ۰۰** است (ردیف ۱.۱۶ چکلیستش)
+- اگر جا افتاده، اینجا بهعنوان یک ردیف ⚠️ ثبت و به تسک ۰۰ برگردان
+
+```jsx
+// Card.js — دو خط جدید، فقط وقتی داده هست
+{turn.service_items?.length > 0 && (
+ {turn.service_items.map((s) => s.name).join("، ")}
+)}
+{turn.service_total_minutes && (
+ {turn.service_total_minutes} دقیقه
+)}
+```
+
+شرط `&&` اجباری است: نوبت اسلاتی این دو را ندارد و کارتش باید **دقیقاً** مثل امروز
+بماند. نوبت سرویسیِ قدیمی هم ممکن است `service_items` خالی داشته باشد → نام «—».
+
+## ۵. جابهجایی سرویسآگاه از پنل
+
+`services/response.js` سه متد جدید میگیرد:
+
+```js
+serviceReschedule: (uuid, body) =>
+ request.post(`api/v1/appointment/${uuid}/service-reschedule`, body, { requireAuth: true }),
+
+getServiceSlotsForReschedule: (doctor_uuid, date, service_uuids, exclude_uuid, clinic_uuid) =>
+ request.get(
+ `api/v1/appointment-service-slots?doctor_uuid=${doctor_uuid}&date=${date}` +
+ service_uuids.map((u) => `&service_item_uuids[]=${encodeURIComponent(u)}`).join("") +
+ `&exclude_appointment_uuid=${exclude_uuid}` + clinicQuery(clinic_uuid),
+ { requireAuth: true }
+ ),
+```
+
+`ButtonData.js` یک دکمهٔ «جابهجایی» میگیرد که مودال موجود
+(`isTurnsDetails/modal/index.js`) را با کامپوننت انتخاب زمان باز میکند — همان
+`components/appointment/date/` بازاستفاده میشود، نه یک انتخابگر جدید.
+
+بیمار **مدت را وارد نمیکند**: `service-reschedule` فقط `start` میگیرد و مدت را از
+سرویسهای موجود نوبت حساب میکند (تسک ۰۰، بخش `ServiceRescheduleService`).
+
+## UI — قواعد اجباری
+
+رجوع: [_shared/ui-conventions.md](../_shared/ui-conventions.md)، بخش `nobat724_front`
+
+- تم MUI از `mui/index.js` — تم جدید نساز
+- فونت فقط Vazir از `app/globals.css`
+- `darkMode: "class"`؛ صفحات عمومی `data-theme`، پنل `class` — هر دو بررسی شوند
+- کامپوننتهای موجود `components/appointment/*` توسعه داده شوند، مسیر موازی نه
+- تاریخ شمسی با `jalali-moment`
+- RTL — `ms-*`/`me-*`
+- هر صفحهای که دست خورد، `generateMetadata` و `await params` سالم بماند
diff --git a/docs/new_feture/taskes/task-00b-nobat724-service-mode/checklist.md b/docs/new_feture/taskes/task-00b-nobat724-service-mode/checklist.md
new file mode 100644
index 00000000..9ebb3afc
--- /dev/null
+++ b/docs/new_feture/taskes/task-00b-nobat724-service-mode/checklist.md
@@ -0,0 +1,123 @@
+# چکلیست — تسک ۰۰ب (سازگارسازی nobat724_front)
+
+**وضعیت کلی:** ⏳ شروع نشده
+**آخرین بازبینی:** —
+
+قواعد: [_shared/definition-of-done.md](../_shared/definition-of-done.md) ·
+خط سرخها: [_shared/red-lines.md](../_shared/red-lines.md) ·
+UI: [_shared/ui-conventions.md](../_shared/ui-conventions.md)
+
+---
+
+## ۰. خط سرخ — مسیر اسلاتی سایت
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۰.۱ | `adaptSlots()` یک خط هم عوض نشد | ⏳ | |
+| ۰.۲ | رندر تبهای شیفت در حالت اسلاتی دستنخورده | ⏳ | |
+| ۰.۳ | مسیر رزرو اسلاتی سرتاسر دستی تست شد — بیتبهبیت مثل قبل | ⏳ | سناریو ۳ |
+| ۰.۴ | کارت نوبت اسلاتی در پنل بدون تغییر | ⏳ | سناریو ۵ |
+
+## ۱. پیشبررسی قرارداد API
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۱.۱ | `appointments/user` فیلد `service_items` دارد | ⏳ | اگر نه → به تسک ۰۰ برگردان |
+| ۱.۲ | `appointments/user` فیلد `service_total_minutes` دارد | ⏳ | همان |
+| ۱.۳ | هر `start_times[i]` فیلد `end_time` دارد | ⏳ | |
+| ۱.۴ | `total_duration_minutes` و `buffer_minutes` در پاسخ هستند | ⏳ | |
+| ۱.۵ | `exclude_appointment_uuid` روی `appointment-service-slots` کار میکند | ⏳ | تسک ۰۰ ساخته |
+
+## ۲. انتخاب سرویس — دیزاین و منطق
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۲.۱ | چهار رنگ hard-code (`#3B3B3B` `#7A7A7A` `#5559CE` `bg-white`) حذف شد | ⏳ | |
+| ۲.۲ | کلاسها از همان الگوی `location/` و `date/` کپی شد، توکن جدید ساخته نشد | ⏳ | |
+| ۲.۳ | ساختار DOM و رفتار toggle عوض نشد | ⏳ | فقط منبع رنگ |
+| ۲.۴ | محاسبهٔ `reduce` مدت از فرانت حذف شد | ⏳ | |
+| ۲.۵ | مدت از `total_duration_minutes` بکاند میآید | ⏳ | |
+| ۲.۶ | `fallbackSum` با `console.warn` — موقت، تسک مقصد حذفش ثبت شد | ⏳ | |
+| ۲.۷ | برچسب «مدت تقریبی» پیش از انتخاب روز، «مدت نوبت» پس از آن | ⏳ | |
+| ۲.۸ | انتخاب صفر سرویس → دکمهٔ ادامه غیرفعال با راهنمای فارسی | ⏳ | |
+| ۲.۹ | محل سرویسی بدون سرویس `bookable` → پیام روشن + پیشنهاد محل دیگر | ⏳ | |
+
+## ۳. `adaptServiceSlots` شیفتآگاه
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۳.۱ | گروهبندی بر اساس شکاف زمانی پیاده شد | ⏳ | |
+| ۳.۲ | آستانه = `max(60, durationMin)` | ⏳ | وگرنه نوبت بلند به تبهای تکعضوی میشکند |
+| ۳.۳ | برچسب واقعی `"HH:MM - HH:MM"` — نه «زمانهای خالی» ثابت | ⏳ | |
+| ۳.۴ | `end_time` از پاسخ بکاند، `addMinutes` فقط fallback | ⏳ | |
+| ۳.۵ | کامنت: هیوریستیک است، راه دقیق endpoint تسک ۰۶ | ⏳ | |
+| ۳.۶ | `start_times` خالی → `[]` و پیام دلیلدار در UI | ⏳ | |
+
+## ۴. پنل کاربر — سرویس، مدت، جابهجایی
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۴.۱ | `Card.js` نام سرویسها را نشان میدهد (با شرط `&&`) | ⏳ | |
+| ۴.۲ | `Card.js` مدت را نشان میدهد (با شرط `&&`) | ⏳ | |
+| ۴.۳ | `DetailLg.js` و `DetailSm.js` هر دو | ⏳ | |
+| ۴.۴ | نوبت رزرو: فقط سرویس، بدون مدت | ⏳ | زمان ندارد |
+| ۴.۵ | نوبت سرویسی بدون `service_items` → «—»، بدون کرش | ⏳ | |
+| ۴.۶ | `services/response.js`: `serviceReschedule` اضافه شد | ⏳ | |
+| ۴.۷ | `services/response.js`: `getServiceSlotsForReschedule` با `exclude_appointment_uuid` | ⏳ | |
+| ۴.۸ | `ButtonData.js` دکمهٔ جابهجایی + مودال موجود | ⏳ | |
+| ۴.۹ | انتخابگر زمان: `components/appointment/date/` بازاستفاده شد، نه ساخت جدید | ⏳ | |
+| ۴.۱۰ | بیمار مدت وارد نمیکند — بکاند حساب میکند | ⏳ | |
+| ۴.۱۱ | خطای بکاند با پیام فارسی خودش نمایش داده میشود | ⏳ | نه «خطای نامشخص» |
+| ۴.۱۲ | پس از خطای تداخل، `refetchSlots()` اجرا میشود | ⏳ | |
+
+## ۵. UI — قواعد سایت
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۵.۱ | تم MUI از `mui/index.js` — تم جدید ساخته نشد | ⏳ | |
+| ۵.۲ | فونت فقط Vazir — فونت جدید اضافه نشد | ⏳ | |
+| ۵.۳ | دارکمود صفحات عمومی (`data-theme`) بررسی شد | ⏳ | سناریو ۲ |
+| ۵.۴ | دارکمود پنل (`class`) بررسی شد | ⏳ | سناریو ۷ — مکانیزم متفاوت |
+| ۵.۵ | کامپوننت موازی ساخته نشد؛ `components/appointment/*` توسعه یافت | ⏳ | |
+| ۵.۶ | RTL بررسی شد (`ms/me` نه `ml/mr`) | ⏳ | |
+| ۵.۷ | موبایل بررسی شد — بدون اسکرول افقی | ⏳ | سناریو ۱۰ |
+| ۵.۸ | تاریخها شمسی با `jalali-moment` | ⏳ | |
+| ۵.۹ | همهٔ رشتهها فارسی | ⏳ | |
+| ۵.۱۰ | صفحاتی که دست خوردند `generateMetadata` و `await params` سالم دارند | ⏳ | |
+| ۵.۱۱ | دامنه گسترش نیافت — صفحهٔ رزرو بازطراحی نشد | ⏳ | انحراف بقیهٔ مراحل، اگر بود، ⚠️ ثبت شود |
+
+## ۶. تست دستی — ده سناریو
+
+| # | سناریو | وضعیت | یادداشت |
+|---|---|---|---|
+| ۶.۱ | رزرو سرویسی کامل تا پیامک | ⏳ | |
+| ۶.۲ | همان در دارکمود عمومی | ⏳ | |
+| ۶.۳ | رزرو اسلاتی کامل — بدون تغییر | ⏳ | ⛔ خط سرخ |
+| ۶.۴ | پزشک دو-شیفته سرویسی → دو تب با برچسب واقعی | ⏳ | |
+| ۶.۵ | پنل با نوبت اسلاتی تنها → بدون تغییر | ⏳ | |
+| ۶.۶ | پنل با نوبت سرویسی → سرویس و مدت | ⏳ | |
+| ۶.۷ | پنل در دارکمود | ⏳ | |
+| ۶.۸ | جابهجایی سرویسی → مدت حفظ | ⏳ | |
+| ۶.۹ | جابهجایی به زمان اشغال → پیام فارسی + refetch | ⏳ | |
+| ۶.۱۰ | همهٔ موارد بالا روی موبایل | ⏳ | |
+| ۶.۱۱ | تست واحد `adaptServiceSlots` (پنج حالت) | ⏳ | تابع خالص، بهترین کاندید |
+
+## ۷. مستندات
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۷.۱ | `nobat724_front/CLAUDE.md` بخش «حالتهای نوبتدهی» | ⏳ | |
+| ۷.۲ | یادداشت هیوریستیک شیفت در `clinicpro/docs/api/appointment.md` | ⏳ | |
+
+## ۸. بازبینی پایانی
+
+| # | مورد | وضعیت | یادداشت |
+|---|---|---|---|
+| ۸.۱ | همهٔ ردیفهای بالا وضعیت نهایی دارند (هیچ 🔄 و ⏳ بیدلیل) | ⏳ | |
+| ۸.۲ | `npm run build` بدون خطا | ⏳ | |
+| ۸.۳ | `npm run lint` بدون خطای جدید | ⏳ | |
+| ۸.۴ | ده سناریوی دستی بخش ۶ اجرا شد | ⏳ | |
+| ۸.۵ | چکلیست UI (بخش ۵) کامل شد | ⏳ | |
+| ۸.۶ | `clinic-pro-tauri` دستی بررسی شد — قرارداد مشترک نشکسته | ⏳ | همان `service_item` تکی |
+| ۸.۷ | commit شد، سپس `graphify update .` | ⏳ | |
+| ۸.۸ | موارد بهتعویقافتاده با دلیل و تسک مقصد ثبت شدند | ⏳ | `fallbackSum` |
diff --git a/docs/new_feture/taskes/task-00b-nobat724-service-mode/implementation_notes.md b/docs/new_feture/taskes/task-00b-nobat724-service-mode/implementation_notes.md
new file mode 100644
index 00000000..c110ca14
--- /dev/null
+++ b/docs/new_feture/taskes/task-00b-nobat724-service-mode/implementation_notes.md
@@ -0,0 +1,157 @@
+# نکات پیادهسازی — تسک ۰۰ب
+
+## ۱. اول قرارداد پاسخ را بررسی کن، بعد کد بزن
+
+سه چیز را پیش از شروع تأیید کن:
+
+```bash
+# ۱. پاسخ appointments/user چه فیلدهایی دارد؟
+curl -s -H "Authorization: Bearer $TOKEN" \
+ https://clinic-pro.ddev.site/api/v1/appointments/user | jq '.data[0] | keys'
+
+# ۲. start_times هر آیتم end_time دارد؟
+curl -s "https://clinic-pro.ddev.site/api/v1/appointment-service-slots?doctor_uuid=…&date=…&service_item_uuids[]=…" \
+ | jq '.data.start_times[0]'
+
+# ۳. total_duration_minutes و buffer_minutes در پاسخ هستند؟
+```
+
+اگر `service_items` یا `service_total_minutes` در پاسخ `appointments/user` نیست،
+**به تسک ۰۰ برگردان** — سریالایزر آنجا اصلاح میشود، نه اینکه اینجا از endpoint دیگری
+دور بزنیم.
+
+## ۲. رنگها را از همسایه کپی کن، نه از حافظه
+
+```bash
+# ببین مرحلهٔ قبلی رزرو چه کلاسی میزند
+grep -n "className" components/appointment/location/index.js | head -30
+grep -n "className" components/appointment/date/index.js | head -30
+```
+
+هدف: کامپوننت انتخاب سرویس **از بقیهٔ مراحل قابل تشخیص نباشد**. اگر بقیه `bg-white`
+میزنند و توکن ندارند، تو هم توکن جدید نساز — همان کاری را بکن که آنها میکنند،
+و اگر دارکمود در آنها هم شکسته است، این یک مسئلهٔ جدا است که در چکلیست ⚠️ ثبت
+میشود، نه اینکه در این تسک کل صفحهٔ رزرو بازطراحی شود.
+
+**دامنه را گسترش نده.** فقط `service/index.js` که خودمان اضافه کردیم و از بقیه منحرف است.
+
+## ۳. `fallbackSum` موقت است و باید هشدار بدهد
+
+```js
+const minutes = data?.total_duration_minutes ?? (() => {
+ console.warn('[booking] total_duration_minutes missing — falling back to client sum');
+ return fallbackSum(draft, services);
+})();
+```
+
+بدون `console.warn`، بکاندی که فیلد را نمیدهد بیصدا کار میکند و شش ماه بعد کسی
+نمیفهمد چرا مدت با نوبت نمیخواند. حذف `fallbackSum` پس از deploy تسک ۰۰ یک ردیف
+⏳ در چکلیست است با تسک مقصد مشخص.
+
+## ۴. آستانهٔ شکاف: ۶۰ دقیقه، با دلیل
+
+```js
+const GAP_THRESHOLD_MIN = 60;
+```
+
+شیفت صبح/عصر معمولاً ۲-۳ ساعت فاصله دارد. یک نوبت ۹۰ دقیقهای هم میتواند شکاف ۹۰
+دقیقهای بسازد بدون اینکه شیفت جدا باشد — پس آستانه نباید کمتر از مدت نوبت باشد:
+
+```js
+const threshold = Math.max(GAP_THRESHOLD_MIN, durationMin);
+```
+
+این خط را فراموش نکن، وگرنه نوبتهای بلند به تبهای تکعضوی تقسیم میشوند.
+
+هیوریستیک است و در کامنت باید بنویسی: راه دقیق، گروهبندی از سمت بکاند است که تسک ۰۶
+در endpoint جدید میدهد.
+
+## ۵. شرط `&&` روی فیلدهای سرویسی در پنل
+
+```jsx
+{turn.service_items?.length > 0 && ( … )}
+```
+
+نه `turn.service_items.map(...)` خالی. نوبت اسلاتی این فیلد را ندارد و بدون شرط، کارت
+همهٔ نوبتهای اسلاتی کرش میکند — یعنی کل پنل کاربر میشکند، نه فقط یک خط.
+
+تست: پنل کاربری که **فقط** نوبت اسلاتی دارد باید بدون هیچ تغییری رندر شود.
+
+## ۶. جابهجایی: مودال موجود، انتخابگر موجود
+
+```
+ButtonData.js → دکمهٔ «جابهجایی» → isTurnsDetails/modal/index.js
+ └─ components/appointment/date/ بازاستفاده
+```
+
+انتخابگر تاریخ/ساعت جدید نساز. کامپوننت `date/` از قبل هر دو حالت را میشناسد
+(`adaptSlots` و `adaptServiceSlots`) و همان را با props متفاوت صدا بزن.
+
+## ۷. خطای بکاند را نمایش بده، نه پیام عمومی
+
+```js
+// ❌
+catch { toast.error('خطایی رخ داد'); }
+
+// ✅
+catch (err) {
+ const msg = err?.response?.data?.errors?.[0]?.message ?? 'خطایی رخ داد';
+ toast.error(msg);
+ refetchSlots(); // ← فهرست زمانها بهروز شود
+}
+```
+
+`ERR_SLOT_TAKEN` پیام فارسی دقیق دارد («این بازه زمانی قبلاً رزرو شده است»). نشان دادن
+«خطای نامشخص» یعنی بیمار همان دکمه را ده بار میزند. `refetchSlots()` بعد از خطای تداخل
+اجباری است.
+
+## ۸. edge case ها
+
+| حالت | رفتار درست |
+|---|---|
+| محل سرویسی بدون سرویس `bookable` | پیام روشن + پیشنهاد محل دیگر اگر باشد |
+| پزشک اسلاتی در مطب، سرویسی در کلینیک | تعویض محل، مرحلهٔ سرویس را ظاهر/پنهان میکند و انتخابها باطل میشوند (رفتار موجود `changeLocation`) |
+| `start_times` خالی | پیام دلیلدار، نه فهرست خالی |
+| `total_duration_minutes` غایب | `fallbackSum` + `console.warn` |
+| نوبت سرویسی قدیمی بدون `service_items` | نام «—»، مدت اگر هست نمایش، بدون کرش |
+| پنل کاربری فقط با نوبت اسلاتی | بیتبهبیت مثل امروز |
+| نوبت رزرو (`is_reserve`) در پنل | مدت نمایش داده نشود (زمان ندارد)، فقط سرویسها |
+| جابهجایی به زمان اشغالشده | پیام فارسی بکاند + `refetch` |
+| دارکمود در پنل (`class`) و صفحات عمومی (`data-theme`) | هر دو بررسی شوند — دو مکانیزم متفاوتاند |
+| یک سرویس با `duration_minutes = null` | بکاند `422` میدهد؛ UI پیامش را نشان دهد و آن سرویس را برجسته کند |
+
+## ۹. تست
+
+پروژه تست خودکار محدودی دارد. سناریوهای دستی اجباری (در چکلیست ثبت شوند):
+
+```
+۱. رزرو سرویسی کامل: انتخاب محل سرویسی → سرویس → روز → ساعت → ثبت → پیامک
+۲. همان مسیر در دارکمود (صفحات عمومی، data-theme)
+۳. رزرو اسلاتی کامل — باید بیتبهبیت مثل قبل باشد
+۴. پزشک با دو شیفت در حالت سرویسی → دو تب زمانی با برچسب واقعی
+۵. پنل کاربر با نوبت اسلاتی تنها → بدون تغییر
+۶. پنل کاربر با نوبت سرویسی → سرویسها و مدت دیده میشود
+۷. پنل کاربر در دارکمود (class)
+۸. جابهجایی نوبت سرویسی → مدت حفظ میشود
+۹. جابهجایی به زمان اشغالشده → پیام فارسی + refetch
+۱۰. موبایل: هر ده مورد بالا، بدون اسکرول افقی
+```
+
+اگر تست خودکار اضافه میکنی، `adaptServiceSlots` تابع خالص است و بهترین کاندید:
+
+```
+tests/appointmentSlots.test.js
+ - یک شیفت → یک گروه
+ - دو شیفت با شکاف ۳ ساعت → دو گروه با برچسب درست
+ - نوبت ۹۰ دقیقهای با شکاف ۹۰ دقیقه → یک گروه (آستانه = max(60, duration))
+ - start_times خالی → []
+ - end_time از پاسخ میآید، نه محاسبه
+```
+
+## ۱۰. مستندات
+
+`nobat724_front/CLAUDE.md` یک بخش کوتاه «حالتهای نوبتدهی» بگیرد: `slot` و `service`،
+اینکه per محل تعیین میشوند، و اینکه `adaptSlots`/`adaptServiceSlots` نقطهٔ تفکیکاند.
+
+در `clinicpro/docs/api/appointment.md` یادداشت کن که گروهبندی شیفت در حالت سرویسی
+هیوریستیک سمت فرانت است و راه دقیقش endpoint تسک ۰۶ است.
diff --git a/docs/new_feture/taskes/task-00b-nobat724-service-mode/task.md b/docs/new_feture/taskes/task-00b-nobat724-service-mode/task.md
new file mode 100644
index 00000000..d8498f5d
--- /dev/null
+++ b/docs/new_feture/taskes/task-00b-nobat724-service-mode/task.md
@@ -0,0 +1,136 @@
+# تسک ۰۰ب — سازگارسازی nobat724_front با وضعیت فعلی نوبتدهی سرویسی
+
+**پروژه:** `nobat724_front` (سایت عمومی) · **فاز:** ۰ · **وابستگی:** ۰۰ · **زمان:** ۱۰-۱۴ ساعت
+**پیشنیاز همهٔ تسکهای ۰۱ به بعد**
+
+---
+
+## ⛔ خط سرخ
+
+مسیر اسلاتی سایت دستکاری نمیشود: `adaptSlots()`، رندر تبهای شیفت، و همهٔ رفتار
+`booking_mode === 'slot'` عیناً میماند.
+رجوع: [_shared/red-lines.md](../_shared/red-lines.md)
+
+---
+
+## هدف
+
+سایت حالت سرویسی را **میشناسد** ولی سه دسته مشکل دارد: انحراف از دیزاینسیستم،
+محاسبهٔ موازی مدت در فرانت، و نبود سرویس/مدت در پنل کاربر. این تسک همه را میبندد و
+سایت را با endpoint های جدید تسک ۰۰ همگام میکند.
+
+## وضعیت فعلی
+
+### ✅ کار میکند
+
+| مورد | فایل |
+|---|---|
+| تشخیص حالت per محل | `components/appointment/index.js:125` — `selectedLocation?.booking_mode === "service"` |
+| مرحلهٔ انتخاب سرویس | `components/appointment/service/index.js` |
+| فراخوانی endpoint ها | `services/response.js:78,83` |
+| تبدیل پاسخ به قالب اسلات | `lib/appointmentSlots.js` → `adaptServiceSlots()` |
+| ارسال سرویسها در ثبت | `components/appointment/detail/SubmitData.js:152` |
+| JSON-LD `availableService` با `estimatedDuration` | `app/doctor/[slug]/page.js:212` |
+| باطلکردن انتخابها با تعویض محل | `changeLocation()` در `index.js` |
+
+### ❌ مشکلات این تسک
+
+**۱. انحراف از دیزاینسیستم — رنگهای hard-code.**
+
+`components/appointment/service/index.js`:
+
+```jsx
+۱. انتخاب سرویس
+…
+className={active
+ ? "border-[#5559CE] bg-[#5559CE]/5"
+ : "border-gray-200 bg-white hover:border-[#5559CE]"}
+```
+
+چهار رنگ hard-code. سایت `darkMode: "class"` دارد و صفحات عمومی با `data-theme` تم
+عوض میکنند — این کامپوننت در دارکمود میشکند. بقیهٔ مراحل رزرو از تم MUI/Tailwind
+استفاده میکنند و این یکی نمیکند.
+
+**۲. محاسبهٔ موازی مدت در فرانت.**
+
+```js
+// components/appointment/service/index.js
+const totalMinutes = services
+ .filter((s) => draft.includes(s.uuid))
+ .reduce((sum, s) => sum + (Number(s.duration_minutes) || 0), 0);
+```
+
+بکاند همان عدد را در `total_duration_minutes` پاسخ `appointment-service-slots`
+برمیگرداند. دو محاسبه یعنی: وقتی تسک ۰۴ فرمول را به «زمان تنها / زمان اضافه» عوض کند،
+سایت عدد قدیمی نشان میدهد و بیمار مدتی میبیند که با مدت واقعی نوبتش نمیخواند.
+
+**۳. `adaptServiceSlots` برچسب گمراهکننده میسازد.**
+
+```js
+return [{
+ start_time: starts[0].start_time,
+ end_time: starts[starts.length - 1].start_time, // ← پایانِ آخرین شروع، نه پایان نوبت
+ label: "زمانهای خالی",
+ slots: …,
+}];
+```
+
+همهٔ زمانها در یک تب جمع میشوند و مرز شیفتها (صبح/عصر) از بین میرود — در حالی که
+حالت اسلاتی همان اطلاعات را از بکاند دارد و نشان میدهد. برای پزشکی با شیفت صبح و عصر،
+بیمار یک فهرست بلند بیساختار میبیند.
+
+**۴. پنل کاربر سرویس و مدت نوبت را نشان نمیدهد.**
+
+`components/dashboard/userAccount/sidebars/turns/Card.js` و `isTurnsDetails/*` هیچ ارجاعی
+به `service` یا مدت ندارند. بیمار نوبت سرویسی گرفته و در پنلش نمیبیند چه سرویسی رزرو
+کرده یا نوبتش چند دقیقه است.
+
+**۵. جابهجایی نوبت در پنل کاربر، سرویسآگاه نیست.**
+
+پس از تسک ۰۰، endpoint `POST /appointment/{uuid}/service-reschedule` وجود دارد.
+`ButtonData.js` هیچ مسیری برای جابهجایی ندارد.
+
+## دامنه
+
+**هست:**
+- بازنویسی `components/appointment/service/index.js` با توکنهای تم (بدون تغییر رفتار)
+- حذف محاسبهٔ مدت از فرانت — مصرف `total_duration_minutes` بکاند
+- `adaptServiceSlots` گروهبندی per شیفت
+- نمایش سرویسها و مدت در کارت و جزئیات نوبت پنل کاربر
+- جابهجایی سرویسآگاه از پنل کاربر
+- بهروزرسانی `services/response.js` برای endpoint های جدید تسک ۰۰
+
+**نیست:** تغییری در مسیر اسلاتی · حالت `resource` (تسک ۰۶ و پس از آن، یک تسک frontend جدا)
+
+## معیار پذیرش
+
+- ✅ موفق: مرحلهٔ انتخاب سرویس در دارکمود درست رندر میشود (هیچ متن سیاه روی زمینهٔ
+ تیره، هیچ کارت سفید).
+- ✅ موفق: مدت نمایشدادهشده در مرحلهٔ انتخاب سرویس **از پاسخ بکاند** میآید؛ اگر
+ بکاند عدد متفاوتی بدهد، UI همان را نشان میدهد.
+- ✅ موفق: پزشکی با دو شیفت (صبح ۹-۱۳، عصر ۱۶-۲۰) در حالت سرویسی → دو تب زمانی،
+ با برچسب واقعی هر شیفت.
+- ✅ موفق: کارت نوبت در پنل کاربر نام سرویسها و مدت را نشان میدهد؛ نوبت اسلاتی
+ دقیقاً مثل امروز (بدون این دو خط).
+- ✅ موفق: بیمار از پنل نوبت سرویسیاش را جابهجا میکند → مدت خودکار حفظ میشود،
+ بیمار عددی وارد نمیکند.
+- ❌ خطا: جابهجایی به زمان اشغالشده → پیام فارسی از بکاند نمایش داده میشود
+ (نه «خطای نامشخص»)، و فهرست زمانها خودکار بهروز میشود.
+- ❌ خطا: انتخاب صفر سرویس → دکمهٔ ادامه غیرفعال با راهنمای فارسی.
+- ⚠️ مرزی: محلی که `booking_mode = 'service'` است ولی هیچ سرویس `bookable` ندارد →
+ پیام روشن («سرویسی برای نوبتدهی آنلاین تعریف نشده است») + پیشنهاد محل دیگر اگر باشد.
+- ⚠️ مرزی: پزشک در مطب شخصی اسلاتی و در کلینیک سرویسی → تعویض محل، مرحلهٔ سرویس را
+ ظاهر/پنهان میکند و انتخابهای قبلی باطل میشوند (رفتار موجود، حفظ شود).
+- ⚠️ مرزی: پاسخ `appointment-service-slots` خالی → پیام دلیلدار، نه فهرست خالی بیتوضیح.
+- ⚠️ مرزی: نوبت قدیمی سرویسی بدون `service_items` → کارت مدت را نشان میدهد و نام
+ سرویس را «—»؛ کرش نمیکند.
+- ⚠️ مرزی: `total_duration_minutes` در پاسخ نبود (بکاند قدیمی) → fallback به محاسبهٔ
+ فرانت با یک `console.warn`، نه صفحهٔ خالی.
+
+## خروجی
+
+- `components/appointment/service/index.js` بازنویسیشده با توکن تم
+- `lib/appointmentSlots.js` — `adaptServiceSlots` شیفتآگاه
+- `components/dashboard/userAccount/sidebars/turns/*` — سرویس و مدت
+- `services/response.js` — endpoint های جدید
+- [checklist.md](checklist.md) کاملشده