- 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.
8.3 KiB
نکات پیادهسازی — تسک ۰۰ب
۱. اول قرارداد پاسخ را بررسی کن، بعد کد بزن
سه چیز را پیش از شروع تأیید کن:
# ۱. پاسخ 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 دیگری
دور بزنیم.
۲. رنگها را از همسایه کپی کن، نه از حافظه
# ببین مرحلهٔ قبلی رزرو چه کلاسی میزند
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 موقت است و باید هشدار بدهد
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 تسک ۰۰ یک ردیف
⏳ در چکلیست است با تسک مقصد مشخص.
۴. آستانهٔ شکاف: ۶۰ دقیقه، با دلیل
const GAP_THRESHOLD_MIN = 60;
شیفت صبح/عصر معمولاً ۲-۳ ساعت فاصله دارد. یک نوبت ۹۰ دقیقهای هم میتواند شکاف ۹۰ دقیقهای بسازد بدون اینکه شیفت جدا باشد — پس آستانه نباید کمتر از مدت نوبت باشد:
const threshold = Math.max(GAP_THRESHOLD_MIN, durationMin);
این خط را فراموش نکن، وگرنه نوبتهای بلند به تبهای تکعضوی تقسیم میشوند.
هیوریستیک است و در کامنت باید بنویسی: راه دقیق، گروهبندی از سمت بکاند است که تسک ۰۶ در endpoint جدید میدهد.
۵. شرط && روی فیلدهای سرویسی در پنل
{turn.service_items?.length > 0 && ( … )}
نه turn.service_items.map(...) خالی. نوبت اسلاتی این فیلد را ندارد و بدون شرط، کارت
همهٔ نوبتهای اسلاتی کرش میکند — یعنی کل پنل کاربر میشکند، نه فقط یک خط.
تست: پنل کاربری که فقط نوبت اسلاتی دارد باید بدون هیچ تغییری رندر شود.
۶. جابهجایی: مودال موجود، انتخابگر موجود
ButtonData.js → دکمهٔ «جابهجایی» → isTurnsDetails/modal/index.js
└─ components/appointment/date/ بازاستفاده
انتخابگر تاریخ/ساعت جدید نساز. کامپوننت date/ از قبل هر دو حالت را میشناسد
(adaptSlots و adaptServiceSlots) و همان را با props متفاوت صدا بزن.
۷. خطای بکاند را نمایش بده، نه پیام عمومی
// ❌
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 تسک ۰۶ است.