Files
nobat724_front/.claude/prompt/booking-engine-task-00b-service-mode.md
T

565 lines
27 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# تسک ۰۰ب — سازگارسازی سایت عمومی با نوبت‌دهی سرویسی
## پروژه
`nobat724_front` (سایت عمومی)
پرامپت همتا (بک‌اند، **باید اول تمام شده باشد**):
`clinicpro/.claude/prompt/booking-engine-task-runner.md` → تسک ۰۰
منبع کامل این تسک — چهار فایل، **همه را بخوان**:
```
clinicpro/docs/new_feture/taskes/task-00b-nobat724-service-mode/task.md
clinicpro/docs/new_feture/taskes/task-00b-nobat724-service-mode/architecture.md
clinicpro/docs/new_feture/taskes/task-00b-nobat724-service-mode/implementation_notes.md
clinicpro/docs/new_feture/taskes/task-00b-nobat724-service-mode/checklist.md ← وضعیت
```
و سه سند حاکم:
```
clinicpro/docs/new_feture/taskes/_shared/red-lines.md
clinicpro/docs/new_feture/taskes/_shared/ui-conventions.md
clinicpro/docs/new_feture/taskes/_shared/definition-of-done.md
```
---
## زمینه
سایت حالت نوبت‌دهی سرویسی را **می‌شناسد**`components/appointment/index.js` روش را
per محل تشخیص می‌دهد، `components/appointment/service/index.js` مرحلهٔ انتخاب سرویس را
نشان می‌دهد، و `lib/appointmentSlots.js` پاسخ `appointment-service-slots` را به قالب
اسلات تبدیل می‌کند. ولی سه دسته مشکل دارد: انحراف از تم، محاسبهٔ موازی مدت در فرانت، و
نبود سرویس/مدت در پنل کاربر.
تسک ۰۰ در `clinicpro` سه endpoint جدید ساخته (`service-reschedule`، `convert-reserve`،
پارامتر `exclude_appointment_uuid`) و دو ستون `service_total_minutes`/`service_buffer_minutes`
اضافه کرده. این تسک سایت را با آن‌ها هم‌گام می‌کند.
---
## مشکل / هدف
پنج شکاف مشخص:
| # | شکاف | فایل |
|---|---|---|
| ۱ | چهار رنگ hard-code در مرحلهٔ انتخاب سرویس → دارک‌مود می‌شکند | `components/appointment/service/index.js` |
| ۲ | مدت با `reduce` در فرانت حساب می‌شود، موازی با `total_duration_minutes` بک‌اند | همان فایل |
| ۳ | `adaptServiceSlots` همهٔ زمان‌ها را در یک تب با برچسب ثابت «زمان‌های خالی» می‌ریزد و `end_time` را اشتباه می‌دهد | `lib/appointmentSlots.js` |
| ۴ | پنل کاربر نام سرویس و مدت نوبت را نشان نمی‌دهد | `components/dashboard/userAccount/sidebars/turns/*` |
| ۵ | جابه‌جایی نوبت سرویسی از پنل کاربر وجود ندارد | `.../turns/isTurnsDetails/ButtonData.js` |
شکاف ۲ مهم‌ترین است: وقتی تسک ۰۴ فرمول را به «زمان تنها / زمان اضافه» عوض کند، سایت
عدد قدیمی نشان می‌دهد و بیمار مدتی می‌بیند که با مدت واقعی نوبتش نمی‌خواند.
---
## معیار پذیرش
معیار کامل با همهٔ حالت‌های مرزی در `task.md` همان تسک است. خلاصهٔ اجباری:
-**موفق:** مرحلهٔ انتخاب سرویس در دارک‌مود درست رندر می‌شود — هیچ متن تیره روی زمینهٔ
تیره، هیچ کارت سفید.
-**موفق:** مدت نمایش‌داده‌شده از `total_duration_minutes` پاسخ بک‌اند می‌آید؛ اگر بک‌اند
عدد متفاوتی بدهد، UI همان را نشان می‌دهد.
-**موفق:** پزشکی با دو شیفت (صبح ۹-۱۳، عصر ۱۶-۲۰) در حالت سرویسی → **دو تب** زمانی با
برچسب واقعی هر شیفت.
-**موفق:** کارت نوبت در پنل کاربر نام سرویس‌ها و مدت را نشان می‌دهد.
-**موفق:** بیمار از پنل نوبت سرویسی‌اش را جابه‌جا می‌کند و **عددی وارد نمی‌کند** — مدت
را بک‌اند حساب می‌کند.
-**موفق (⛔ خط سرخ):** رزرو اسلاتی سرتاسر **بیت‌به‌بیت مثل قبل** کار می‌کند و کارت نوبت
اسلاتی در پنل هیچ تغییری نمی‌کند.
-**خطا:** جابه‌جایی به زمان اشغال‌شده → **پیام فارسی خودِ بک‌اند** نمایش داده می‌شود
(نه «خطای نامشخص») و فهرست زمان‌ها خودکار `refetch` می‌شود.
-**خطا:** انتخاب صفر سرویس → دکمهٔ ادامه غیرفعال با راهنمای فارسی.
- ⚠️ **مرزی:** پنل کاربری که **فقط** نوبت اسلاتی دارد → بدون هیچ تغییری رندر می‌شود
(شرط `&&` روی فیلدهای سرویسی).
- ⚠️ **مرزی:** محل سرویسی بدون سرویس `bookable` → پیام روشن + پیشنهاد محل دیگر اگر باشد.
- ⚠️ **مرزی:** `total_duration_minutes` در پاسخ نبود (بک‌اند قدیمی) → `fallbackSum` با
`console.warn`، نه صفحهٔ خالی.
- ⚠️ **مرزی:** نوبت سرویسیِ قدیمی بدون `service_items` → نام «—»، بدون کرش.
- ⚠️ **مرزی:** نوبت رزرو (`is_reserve`) در پنل → فقط سرویس‌ها، بدون مدت (زمان ندارد).
- ⚠️ **مرزی:** نوبت ۹۰ دقیقه‌ای با شکاف ۹۰ دقیقه → **یک** تب، نه تب‌های تک‌عضوی
(آستانه = `max(60, durationMin)`).
---
## فایل‌های مرتبط
| فایل | نقش |
|------|-----|
| `components/appointment/service/index.js` | مرحلهٔ انتخاب سرویس — بازنویسی با توکن تم |
| `components/appointment/index.js` | ارکستراتور مراحل — تشخیص `booking_mode` per محل |
| `components/appointment/date/index.js` | مصرف‌کنندهٔ `adaptServiceSlots` |
| `components/appointment/detail/SubmitData.js` | ارسال `service_item_uuids` در ثبت |
| `lib/appointmentSlots.js` | `adaptSlots` (⛔ قفل) و `adaptServiceSlots` (بازنویسی) |
| `services/response.js` | افزودن `serviceReschedule` و `getServiceSlotsForReschedule` |
| `components/dashboard/userAccount/sidebars/turns/Card.js` | + نام سرویس و مدت |
| `.../turns/isTurnsDetails/DetailLg.js` · `DetailSm.js` | همان |
| `.../turns/isTurnsDetails/ButtonData.js` | + دکمهٔ جابه‌جایی |
| `.../turns/isTurnsDetails/modal/index.js` | مودال موجود — بازاستفاده |
| `mui/index.js` · `tailwind.config.js` · `app/globals.css` | منبع تم و توکن |
| `CLAUDE.md` | + بخش «حالت‌های نوبت‌دهی» |
---
## وضعیت فعلی
### ۱. رنگ‌های hard-code — `components/appointment/service/index.js`
```jsx
<h2 className="text-[16px] font-bold text-[#3B3B3B] mb-4">۱. انتخاب سرویس</h2>
{services.length === 0 ? (
<p className="text-[14px] text-[#7A7A7A]">
در حال حاضر سرویسی برای نوبتدهی آنلاین تعریف نشده است.
</p>
) : (
className={`flex items-center justify-between gap-3 p-3 rounded-xl border text-right transition-colors ${
active
? "border-[#5559CE] bg-[#5559CE]/5"
: "border-gray-200 bg-white hover:border-[#5559CE]"
}`}
```
چهار رنگ ثابت. سایت `darkMode: "class"` دارد و صفحات عمومی با `data-theme` تم عوض
می‌کنند (`app/Providers.js` با `next-themes`) — این کامپوننت در دارک‌مود می‌شکند، در حالی
که بقیهٔ مراحل رزرو نمی‌شکنند.
### ۲. محاسبهٔ موازی مدت — همان فایل
```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` — `lib/appointmentSlots.js`
```js
// حالت سرویسی: پاسخ appointment-service-slots فقط start_times دارد (همه کافی).
// آن‌ها را در یک session قالب‌بندی می‌کنیم تا مثل حالت اسلاتی رندر شوند.
export function adaptServiceSlots(slotsResponse) {
const starts =
slotsResponse?.data?.start_times ?? slotsResponse?.start_times ?? [];
if (!starts.length) return [];
return [
{
start_time: starts[0].start_time,
end_time: starts[starts.length - 1].start_time,
label: "زمان‌های خالی",
slots: starts.map((s) => ({ ...s, is_available: true })),
},
];
}
```
سه مشکل: همه در یک تب · برچسب ثابت · `end_time` برابر **شروعِ** آخرین اسلات، نه پایان نوبت.
⛔ تابع `adaptSlots()` بالای همین فایل (حالت اسلاتی) **قفل است** و یک خط هم عوض نمی‌شود:
```js
export function adaptSlots(slotsResponse) {
const sessions = slotsResponse?.data?.sessions ?? slotsResponse?.sessions ?? [];
return sessions
.filter((session) => Array.isArray(session.slots) && session.slots.length > 0)
.map((session) => ({ }));
}
```
### ۴ و ۵. پنل کاربر
`components/dashboard/userAccount/sidebars/turns/Card.js` و
`isTurnsDetails/DetailLg.js`/`DetailSm.js` هیچ ارجاعی به `service` یا مدت ندارند.
`ButtonData.js` هیچ مسیر جابه‌جایی ندارد.
### تشخیص حالت — `components/appointment/index.js:125` (درست، دست‌نخورده بماند)
```js
// روش نوبت‌دهی و سرویس‌ها per-location هستند: یک پزشک می‌تواند در مطب شخصی
// اسلاتی و در کلینیک سرویسی باشد.
const bookingMode = selectedLocation?.booking_mode === "service" ? "service" : "slot";
const bookingServices = selectedLocation?.services ?? [];
```
و `changeLocation()` که انتخاب‌های وابسته را باطل می‌کند — این رفتار درست است و حفظ می‌شود.
---
## وظایف
`todo` را از ردیف‌های `checklist.md` همان تسک بساز (بخش‌های ۰ تا ۸)، نه از این هشت وظیفه.
هر ردیف چک‌لیست را همان لحظه `⏳ → 🔄 → ✅` کن.
### ۱. پیش‌بررسی قرارداد API — پیش از هر خط کد
سه چیز را تأیید کن. اگر فیلدی نیست، **کد نزن**؛ به تسک ۰۰ برگردان.
```bash
TOKEN=<access_token یک کاربر تست>
# ۱. appointments/user چه فیلدهایی دارد؟
curl -s -H "Authorization: Bearer $TOKEN" \
https://clinic-pro.ddev.site/api/v1/appointments/user | jq '.data[0] | keys'
# باید service_items و service_total_minutes داشته باشد
# ۲. هر start_times[i] فیلد 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], .data.total_duration_minutes, .data.buffer_minutes'
# ۳. exclude_appointment_uuid کار می‌کند؟
curl -s "…&exclude_appointment_uuid=<uuid نوبت موجود>" | jq '.data.start_times | length'
```
**نحوه تست:** خروجی واقعی هر سه دستور را در گزارش بگذار. ردیف‌های ۱.۱ تا ۱.۵ چک‌لیست
با همین‌ها ✅ می‌شوند.
---
### ۲. بازنویسی `service/index.js` با توکن تم
**اول رنگ‌های همسایه را ببین** — هدف این است که این کامپوننت از بقیهٔ مراحل رزرو قابل
تشخیص نباشد:
```bash
grep -n "className" components/appointment/location/index.js | head -30
grep -n "className" components/appointment/date/index.js | head -30
```
بعد همان الگو را اعمال کن. ساختار DOM و رفتار `toggle` **عوض نمی‌شود** — فقط منبع رنگ.
```jsx
// نمونه؛ نام دقیق کلاس را از tailwind.config.js و mui/index.js همین پروژه بردار
<h2 className="text-base font-bold text-foreground mb-4">۱. انتخاب سرویس</h2>
className={active
? "border-primary bg-primary/5"
: "border-border bg-surface hover:border-primary"}
```
اگر پروژه توکن معادل ندارد، **توکن جدید نساز** — همان کاری را بکن که `location/index.js`
می‌کند. اگر دارک‌مود در آن هم شکسته است، این یک مسئلهٔ جداست: ردیف ⚠️ در چک‌لیست ثبت کن
و **دامنه را گسترش نده**. صفحهٔ رزرو در این تسک بازطراحی نمی‌شود.
**نحوه تست:** سناریوهای ۶.۱ و ۶.۲ چک‌لیست — رزرو سرویسی کامل، یک بار در لایت و یک بار
در دارک (`data-theme`). اسکرین‌شات هر دو حالت در گزارش.
---
### ۳. حذف محاسبهٔ موازی مدت
مدت از `total_duration_minutes` می‌آید. مسئلهٔ ترتیبی: مرحلهٔ انتخاب سرویس **پیش از**
انتخاب روز است و آن endpoint تاریخ می‌خواهد. راه‌حل انتخاب‌شده (دلیل در
`architecture.md` بخش ۲): فراخوانی با تاریخ امروز فقط برای گرفتن مدت — پاسخ ممکن است
`start_times` خالی داشته باشد ولی `total_duration_minutes` می‌آید.
```js
const { data } = useServiceDuration(doctorUuid, clinicUuid, draft); // hook جدید
const minutes = data?.total_duration_minutes ?? (() => {
console.warn('[booking] total_duration_minutes missing — falling back to client sum');
return fallbackSum(draft, services);
})();
// پیش از انتخاب روز
<span>مدت تقریبی: {minutes} دقیقه</span>
// پس از انتخاب روز — از همان پاسخ appointment-service-slots
<span>مدت نوبت: {minutes} دقیقه</span>
```
`fallbackSum` موقت است و فقط برای بک‌اند قدیمی. حذفش را در چک‌لیست به‌عنوان ردیف `⏳` با
دلیل و تسک مقصد ثبت کن.
**نحوه تست:** دو سرویس انتخاب کن و عدد UI را با
`curl … | jq '.data.total_duration_minutes'` مقایسه کن — باید یکی باشند. بعد یک بار
`total_duration_minutes` را از پاسخ حذف کن (mock) و ببین `console.warn` می‌زند و صفحه
خالی نمی‌شود.
---
### ۴. `adaptServiceSlots` شیفت‌آگاه
```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 threshold = Math.max(60, durationMin);
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 > threshold) {
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,
};
});
}
```
کامنت بنویس که گروه‌بندی **هیوریستیک** است و راه دقیقش endpoint تسک ۰۶ است — تصمیم و
دلیل رد گزینهٔ «گروه‌بندی از بک‌اند» در `architecture.md` بخش ۳ ثبت شده (تغییر قرارداد
endpoint که سه کلاینت مصرفش می‌کنند).
`adaptSlots()` را لمس نکن.
**نحوه تست:** `adaptServiceSlots` تابع خالص است — بهترین کاندید تست واحد. پنج حالت:
```
یک شیفت → یک گروه
دو شیفت با شکاف ۳ ساعت → دو گروه با برچسب درست
نوبت ۹۰ دقیقه‌ای با شکاف ۹۰ دقیقه → یک گروه
start_times خالی → []
end_time از پاسخ می‌آید، نه محاسبه
```
و سناریوی دستی ۶.۴: پزشک دو-شیفته در حالت سرویسی → دو تب در UI.
---
### ۵. سرویس و مدت در پنل کاربر
```jsx
// Card.js — شرط && اجباری است
{turn.service_items?.length > 0 && (
<span className="…">{turn.service_items.map((s) => s.name).join("، ")}</span>
)}
{turn.service_total_minutes && !turn.is_reserve && (
<span className="…">{turn.service_total_minutes} دقیقه</span>
)}
```
بدون `&&`، کارت **همهٔ** نوبت‌های اسلاتی کرش می‌کند — یعنی کل پنل کاربر می‌شکند، نه یک
خط. نوبت رزرو مدت نمی‌گیرد چون زمان ندارد. نوبت سرویسیِ قدیمی بدون `service_items`
نام «—».
همین را در `DetailLg.js` و `DetailSm.js` هم اعمال کن.
**نحوه تست:** سناریوهای ۶.۵ تا ۶.۷ — پنل کاربری که **فقط** نوبت اسلاتی دارد باید بدون
تغییر رندر شود؛ پنل با نوبت سرویسی سرویس و مدت را نشان دهد؛ هر دو در دارک‌مود پنل
(`class`، نه `data-theme` — دو مکانیزم متفاوت‌اند).
---
### ۶. جابه‌جایی سرویس‌آگاه از پنل
```js
// services/response.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` می‌گیرد.
خطا را با پیام خودِ بک‌اند نشان بده:
```js
catch (err) {
const msg = err?.response?.data?.errors?.[0]?.message ?? 'خطایی رخ داد';
toast.error(msg);
refetchSlots(); // بعد از خطای تداخل اجباری
}
```
`ERR_SLOT_TAKEN` پیام فارسی دقیق دارد؛ «خطای نامشخص» یعنی بیمار همان دکمه را ده بار می‌زند.
**نحوه تست:** سناریوهای ۶.۸ و ۶.۹ — جابه‌جایی موفق (مدت حفظ می‌شود، بیمار عددی وارد
نکرده) و جابه‌جایی به زمانی که هم‌زمان توسط شخص دیگری گرفته شده (پیام فارسی + `refetch`).
برای دومی، یک نوبت از پنل ادمین روی همان زمان بساز و بعد دکمه را بزن.
---
### ۷. به‌روزرسانی `checklist.md` — دو مخزن جدا
⚠️ **این تسک دو مخزن git را لمس می‌کند:**
| مخزن | چه چیزی |
|---|---|
| `nobat724_front` | همهٔ تغییرات کد |
| `clinicpro` | فقط `docs/new_feture/taskes/task-00b-nobat724-service-mode/checklist.md` |
پس **دو commit** لازم است. فراموش کردن دومی یعنی راهبر تسک بعدی فکر می‌کند ۰۰ب تمام
نشده و دوباره تحویلش می‌دهد.
```bash
# ۱. کد سایت
cd nobat724_front
git checkout -b feat/booking-service-mode-frontend # اگر روی main هستی
git add -A
git commit -m "$(cat <<'EOF'
feat(booking): align public site with service booking mode
Theme tokens in service picker, backend-driven duration, shift-aware
slot grouping, service/duration in user panel, service-aware reschedule.
Slot-mode path untouched.
Task: clinicpro/docs/new_feture/taskes/task-00b-nobat724-service-mode/
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
EOF
)"
# ۲. چک‌لیست در مخزن clinicpro
cd ../clinicpro
git add docs/new_feture/taskes/task-00b-nobat724-service-mode/checklist.md
git commit -m "$(cat <<'EOF'
docs(booking): mark task 00b checklist complete
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
EOF
)"
```
**نحوه تست:** `git log --oneline -1` در هر دو مخزن، و
`grep -c '| ⏳ |' clinicpro/docs/new_feture/taskes/task-00b-nobat724-service-mode/checklist.md`
که باید ۰ بدهد (یا فقط ردیف‌های `⏳` با یادداشت دلیل).
---
### ۸. مستندات و گزارش پایانی
```
nobat724_front/CLAUDE.md ← بخش «حالت‌های نوبت‌دهی»: slot و service،
per محل تعیین می‌شوند، adaptSlots/adaptServiceSlots
نقطهٔ تفکیک‌اند
clinicpro/docs/api/appointment.md ← یادداشت: گروه‌بندی شیفت در حالت سرویسی
هیوریستیک سمت فرانت است؛ راه دقیقش endpoint تسک ۰۶
```
گزارش پایانی:
```
✅ تسک ۰۰ب تمام شد
پیاده‌سازی
• <فایل‌ها با مسیر>
تست
• npm run build : بدون خطا
• npm run lint : بدون خطای جدید
• تست واحد adaptServiceSlots: <N> حالت، سبز
• ۱۱ سناریوی دستی : <فهرست با نتیجه>
خط سرخ
• مسیر اسلاتی دست‌نخورده — adaptSlots عوض نشد؛ سناریوی ۶.۳ سرتاسر تست شد
کلاینت دیگر
• clinic-pro-tauri: <چه بررسی شد — قرارداد service_item تکی>
به تعویق افتاد
• fallbackSum — دلیل: تا deploy تسک ۰۰ · تسک مقصد: <XX>
commit سایت: <hash> · commit چک‌لیست: <hash>
────────────────────────────────
🎯 بعدی: تسک ۰۱ — شعبه و اتاق
اجرا کن: /run-prompt clinicpro/.claude/prompt/booking-engine-task-runner.md
```
---
## نکات مهم
### ⛔ خط سرخ — مسیر اسلاتی سایت
`_shared/red-lines.md` حاکم است. در این پروژه یعنی:
- `adaptSlots()` **یک خط هم** عوض نمی‌شود
- رندر تب‌های شیفت در حالت اسلاتی دست‌نخورده
- کارت نوبت اسلاتی در پنل بدون تغییر (شرط `&&` روی فیلدهای سرویسی)
- سناریوی ۶.۳ (رزرو اسلاتی سرتاسر) **اجباری** است، نه اختیاری
اگر بعد از تغییرات، رزرو اسلاتی یک پیکسل هم فرق کرد، تغییر برمی‌گردد.
### دامنه را گسترش نده
فقط `service/index.js` که خودمان اضافه کردیم و از بقیهٔ مراحل منحرف است بازنویسی می‌شود.
اگر `location/index.js` یا `date/index.js` هم رنگ hard-code دارند و دارک‌مودشان شکسته
است، این یک مسئلهٔ جداست: ردیف `⚠️` در چک‌لیست ثبت کن و بگذار. بازطراحی کل صفحهٔ رزرو
در این تسک نیست.
### قواعد سایت که رعایت می‌شوند
از `_shared/ui-conventions.md` بخش `nobat724_front` و `CLAUDE.md` پروژه:
- تم MUI از `mui/index.js` با `direction: rtl`**تم جدید نساز**
- Tailwind با `darkMode: "class"`؛ صفحات عمومی `data-theme`، پنل `class`**هر دو تست شوند**
- **فونت فقط Vazir** از `app/globals.css` با `@font-face` — فونت دیگر اضافه نکن
- فراخوانی API از `services/response.js``request.*`؛ برای auth `{ requireAuth: true }`
تا کوکی `access_token` به‌صورت Bearer ضمیمه شود
- داده server-side با `lib/req.js``fetchReq(url)`
- کامپوننت‌های موجود `components/appointment/*` توسعه داده می‌شوند، مسیر موازی نه
- تاریخ شمسی با `jalali-moment`
- RTL — `ms-*`/`me-*` نه `ml-*`/`mr-*`
- هر صفحه‌ای که دست خورد، `generateMetadata` و `await params` سالم بماند
- slug پزشک/کلینیک = `uuid`
- شهر از subdomain: server-side `lib/getStateInfo.js` · client-side `useProvince()`
### وابستگی سخت به تسک ۰۰
سه endpoint و دو ستون این تسک را تسک ۰۰ می‌سازد. اگر
`clinicpro/docs/new_feture/taskes/task-00-service-mode-completion/checklist.md` کامل ✅
نیست، **این تسک شروع نمی‌شود**:
```bash
grep -c '| ⏳ |\|| 🔄 |' clinicpro/docs/new_feture/taskes/task-00-service-mode-completion/checklist.md
# باید ۰ بدهد
```
اگر نداد، پیام بده و بایست:
`/run-prompt clinicpro/.claude/prompt/booking-engine-task-runner.md`
### cross-repo — بررسی دستی اجباری
`clinic-pro-tauri/src/service/response.js` هم مصرف‌کنندهٔ همان `/api/v1/...` است. این
تسک قرارداد بک‌اند را عوض نمی‌کند (فقط مصرف می‌کند)، ولی اگر تسک ۰۰ فیلدی را جابه‌جا
کرده باشد، اپ دسکتاپ هم متأثر است. بررسی کن که `service_item` تکی هنوز در پاسخ هست و
گزارش بده — «بررسی شد» بی‌ارزش است، نام فایل و فیلد را بنویس (guidelines §۳).
### تست خودکار محدود است
پروژه تست خودکار کمی دارد، پس **یازده سناریوی دستی بخش ۶ چک‌لیست اجباری‌اند**، نه
توصیه. هر ده مورد اول روی موبایل هم تکرار می‌شوند (سناریو ۶.۱۰) — بدون اسکرول افقی.
`adaptServiceSlots` تابع خالص است و بهترین کاندید تست واحد؛ اگر پروژه runner تست ندارد،
یک فایل ساده در `tests/appointmentSlots.test.js` بساز و در چک‌لیست ثبت کن.