feat: implement service mode completion for nobat724_front

- Add task for completing service mode in clinicpro with detailed objectives and acceptance criteria.
- Create architecture documentation for task 00b, outlining involved components and necessary changes.
- Develop checklist for task 00b to ensure all requirements are met.
- Document implementation notes for task 00b, emphasizing API contract checks and design system adherence.
- Update task documentation for task 00b, specifying goals and current issues with service mode.
This commit is contained in:
hamed
2026-07-30 11:56:08 +03:30
parent 021d0eb6b2
commit 158dcb58aa
12 changed files with 1846 additions and 0 deletions
@@ -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 تسک ۰۶ است.