# تسک ۰۰ب — سازگارسازی سایت عمومی با نوبت‌دهی سرویسی ## پروژه `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

۱. انتخاب سرویس

{services.length === 0 ? (

در حال حاضر سرویسی برای نوبت‌دهی آنلاین تعریف نشده است.

) : ( … 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= # ۱. 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=" | 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 همین پروژه بردار

۱. انتخاب سرویس

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); })(); // پیش از انتخاب روز مدت تقریبی: {minutes} دقیقه // پس از انتخاب روز — از همان پاسخ appointment-service-slots مدت نوبت: {minutes} دقیقه ``` `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 && ( {turn.service_items.map((s) => s.name).join("، ")} )} {turn.service_total_minutes && !turn.is_reserve && ( {turn.service_total_minutes} دقیقه )} ``` بدون `&&`، کارت **همهٔ** نوبت‌های اسلاتی کرش می‌کند — یعنی کل پنل کاربر می‌شکند، نه یک خط. نوبت رزرو مدت نمی‌گیرد چون زمان ندارد. نوبت سرویسیِ قدیمی بدون `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) 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) 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: حالت، سبز • ۱۱ سناریوی دستی : <فهرست با نتیجه> خط سرخ • مسیر اسلاتی دست‌نخورده — adaptSlots عوض نشد؛ سناریوی ۶.۳ سرتاسر تست شد کلاینت دیگر • clinic-pro-tauri: <چه بررسی شد — قرارداد service_item تکی> به تعویق افتاد • fallbackSum — دلیل: تا deploy تسک ۰۰ · تسک مقصد: commit سایت: · commit چک‌لیست: ──────────────────────────────── 🎯 بعدی: تسک ۰۱ — شعبه و اتاق اجرا کن: /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` بساز و در چک‌لیست ثبت کن.