Files
nobat724_front/.claude/prompt/appointment-calendar-disabled-dates.md
T
hamedandClaude Opus 4.8 96f4126c0b feat(appointment): add getMonthAvailability request wrapper
Public wrapper for GET /api/v1/appointment-settings/month-availability
/{doctor_uuid}?year=&month= so the calendar can learn which days are
bookable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 16:29:31 +03:30

9.5 KiB
Raw Blame History

نمایش روزهای تعطیل/غیرقابل‌انتخاب روی تقویم صفحه‌ی نوبت + احترام به بازه‌ی رزرو

پروژه

nobat724_front — سایت عمومی نوبت‌دهی. این پرامپت بعد از پرامپت backend اجرا شود.

Cross-repo: این کار به endpoint جدید backend وابسته است: GET /api/v1/appointment-settings/month-availability/{doctorUuid}?year=&month= که در پرامپت clinicpro/.claude/prompt/doctor-booking-window-and-month-availability.md ساخته می‌شود. اگر آن endpoint هنوز نیست، متوقف شو و اول backend را اجرا کن.

زمینه

تقویم دوماهه‌ی صفحه‌ی نوبت (/appointment/[doctorId]) الان همه‌ی روزهای آینده را قابل‌انتخاب نشان می‌دهد، چون در اصلاح قبلی فراخوانی endpoint ناموجود appointment/not-available حذف شد و disabledDates={[]} پاس داده می‌شود. در نتیجه روزهای تعطیلِ پزشک (مثلاً ۲۷/۰۳/۱۴۰۵ که با date override بسته شده) روی تقویم خاکستری نمی‌شوند و کاربر می‌تواند رویشان کلیک کند — بعد فقط لیست اسلات خالی می‌بیند. همچنین بازه‌ی رزرو (مثلاً «تا ۲ ماه جلوتر») روی تقویم اعمال نمی‌شود.

مشکل / هدف

تقویم باید روزهای غیرقابل‌انتخاب را خاکستری و disable کند. این روزها از endpoint month-availability می‌آیند: تعطیلات، date overrideهای بسته، روزهای بدون شیفت، و روزهای خارج از بازه‌ی رزرو پزشک. چون تقویم دوماهه است، باید برای هر دو ماه نمایش‌داده‌شده داده گرفته شود و با تغییر ماه (navigation) داده‌ی ماه جدید هم لود شود.

فایل‌های مرتبط

فایل نقش
services/response.js افزودن getMonthAvailability(doctorUuid, year, month)
app/appointment/[doctorId]/page.js الان disabledDates={[]} می‌فرستد (سرور-ساید)
components/appointment/index.jsContainerdate/index.jsTimeSelectDatePicker زنجیره‌ی پاس‌دادن disabledDates
app/component/date/datePicker/index.js تقویم دوماهه‌ی client؛ disabledDates آرایه‌ی Unix timestamp است
components/common/InlineJalaliMonth.js رندر یک ماه؛ isDisabled(date) روز را خاکستری/disable می‌کند
lib/appointmentSlots.js adapter موجود اسلات‌ها (الگوی adapter جدا)

وضعیت فعلی (کد واقعی)

app/appointment/[doctorId]/page.js — disabledDates خالی

let doctor = null;
try {
  const doctorRes = await axiosInstance.get(`${API_URL}/api/v1/doctor/${doctorId}`);
  doctor = doctorRes.data?.data?.data;
} catch (error) {}

return (
  <AppointmentPage doctor={doctor} disabledDates={[]} matchedCity={matchedCity} />
);

app/component/date/datePicker/index.js — مصرف disabledDates

function DatePicker({ setDate, disabledDates = [] }) {
  const [baseMonth, setBaseMonth] = useState(moment().tz("Asia/Tehran"));
  // ...
  const isDisabled = (date) => {
    const day = moment(date).startOf("day");
    if (day.isBefore(moment().startOf("day"))) return true;
    return disabledDates.some((ts) => day.isSame(moment.unix(ts).startOf("day")));
  };
  // auto-select nearest available day از همین isDisabled استفاده می‌کند
  // دو InlineJalaliMonth: baseMonth (راست) + secondMonth = baseMonth+1 (چپ)
}

disabledDates آرایه‌ی Unix timestamp (ثانیه) است؛ isDisabled گذشته را هم می‌بندد. baseMonth با فلش‌های ناوبری تغییر می‌کند.

services/response.js — توابع slot موجود

getAppointmentSlots: (doctor_uuid, date) =>
  api.get(`api/v1/appointment-slots?doctor_uuid=${doctor_uuid}&date=${date}`, removeTokenHead),

قرارداد endpoint جدید backend (مرجع)

GET /api/v1/appointment-settings/month-availability/{doctorUuid}?year=&month=   (PUBLIC)
{
  "success": true,
  "data": {
    "year": 2026,
    "month": 6,
    "disabled_dates": ["2026-06-16", "2026-06-20"],
    "online_booking_enabled": true,
    "booking_window": { "value": 2, "unit": "month" }
  }
}

disabled_dates فرمت Y-m-d میلادی. قبل از پیاده‌سازی، با پرامپت backend چک کن که endpoint ورودی شمسی می‌خواهد یا میلادی — اگر میلادی است، ماه شمسیِ نمایش‌داده‌شده را باید به بازه‌ی میلادی تبدیل و برای ماه(های) میلادی متناظر صدا بزنی. interceptor services/api.js یک‌بار پاسخ را باز می‌کند → داده در res.data.

وظایف

اجرای مرحله‌به‌مرحله؛ بعد از هر قابلیت npm run build و سپس commit جدا.

۱. افزودن getMonthAvailability به services/response.js

getMonthAvailability: (doctor_uuid, year, month) =>
  api.get(`api/v1/appointment-settings/month-availability/${doctor_uuid}`, {
    params: { year, month },
  }),

(public — بدون requireAuth؛ از همان الگوی getAppointmentSlots پیروی کن.)

۲. لود داده‌ی در‌دسترس‌بودن در DatePicker و disable‌کردن روزها

  • چون DatePicker کامپوننت client است و doctor.uuid در آن در دسترس نیست (الان فقط setDate/disabledDates می‌گیرد)، یا doctorUuid را به‌عنوان prop از زنجیره پاس بده، یا از useParams().doctorId داخل DatePicker بخوان (همان uuid است — الگوی موجود SendAppo).
  • داخل DatePicker یک state disabledTimestamps نگه‌دار و با useEffect وابسته به baseMonth:
    • برای ماهِ baseMonth و ماهِ secondMonth (baseMonth+1) getMonthAvailability را صدا بزن (تبدیل ماه نمایشی به year/month مطابق قرارداد backend).
    • disabled_dates (Y-m-d) را به Unix timestamp (moment(d,"YYYY-MM-DD").startOf("day").unix()) تبدیل و در state بریز.
  • isDisabled فعلی را نگه‌دار ولی منبع disabledDates را از این state بگیر (یا prop disabledDates را با state داخلی merge کن). گذشته هم‌چنان بسته بماند.
  • auto-select: منطق انتخاب نزدیک‌ترین روز قابل‌انتخاب باید بعد از لود disabledTimestamps اجرا/بازاجرا شود تا روی روز تعطیل auto-select نشود.

۳. هندل ناوبری ماه

  • با کلیک فلش‌ها baseMonth تغییر می‌کند → useEffect دوباره داده‌ی دو ماه جدید را می‌گیرد. مطمئن شو state تجمعی است (ماه‌های قبلی پاک نشوند یا حداقل ماه‌های نمایش‌فعلی پوشش داده شوند) و درخواست تکراری بی‌مورد نزن (می‌توانی ماه‌های لودشده را cache کنی با یک Set/object key=year-month).

۴. (اختیاری ولی توصیه‌شده) نمایش وضعیت نوبت‌دهی خاموش

  • اگر online_booking_enabled === false در پاسخ، به‌جای تقویم یک پیام «نوبت‌دهی آنلاین این پزشک غیرفعال است» نشان بده و دکمه‌ی «تایید نوبت» را disable کن. (اگر می‌خواهی این بخش را جدا کنی، در گزارش ذکر کن.)

نکات مهم

  • وابستگی cross-repo: بدون endpoint month-availability این کار کامل نمی‌شود. اگر نبود متوقف شو.
  • شمسی/میلادی: تقویم سایت شمسی است؛ endpoint احتمالاً میلادی می‌خواهد. تبدیل را با moment-jalaali انجام بده و حدس نزن — قرارداد دقیق را از پرامپت/داک backend بگیر. یک ماه شمسی روی دو ماه میلادی می‌افتد؛ یا برای پوشش کامل، بازه‌ی میلادیِ روزهای نمایش‌داده‌شده را محاسبه کن.
  • timestampها Unix (ثانیه): disabledDates که isDisabled می‌خواند آرایه‌ی Unix ثانیه است؛ تبدیل Y-m-d → unix را با startOf("day") در Asia/Tehran انجام بده تا با منطق فعلی هم‌خوان شود.
  • interceptor: request.* بدنه را یک‌بار باز می‌کند → res.data.disabled_dates.
  • عدم رگرسیون: تقویم دوماهه، ترتیب RTL (خرداد راست، تیر چپ)، فلش‌ها، و انتخاب خودکار نباید بشکنند. InlineJalaliMonth فقط isDisabled را مصرف می‌کند — منطق رنگ خاکستری از قبل هست.
  • Multi-domain/Jalali/RTL: حفظ شوند.
  • تست: npm run build؛ سپس دستی با پزشک 4a0594b1-008b-478a-a593-259b95d8c2dd که یک date override بسته روی ۲۷/۰۳/۱۴۰۵ دارد — آن روز باید خاکستری و غیرقابل‌کلیک باشد، و روزهای خارج از بازه‌ی رزرو هم همین‌طور. سپس commit با پیام توصیفی.