Files
nobat724_front/.claude/prompt/doctor-profile-booking-state-from-locations.md
T
hamedandClaude Fable 5 36816eded2 fix(doctor): derive booking state from booking_locations
The profile decided "نوبت‌دهی غیرفعال است" from `doctor.active` alone, while the
page already had `booking_locations` — the more precise source, since the backend
only returns locations that are genuinely bookable. The two could disagree, and
for a doctor bookable only at a clinic they did.

A shared `bookingState` helper now drives both the desktop card and the mobile
bar: any location means bookable, the label comes from the earliest
`next_available_at`, and locations with no capacity yet read as "فعلاً نوبت خالی
ندارد" rather than disabled. With no locations at all it falls back to the
previous `doctor.active` / `free_turn` fields.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:25:37 +03:30

7.5 KiB
Raw Blame History

وضعیت «نوبت‌دهی فعال/غیرفعال» پروفایل پزشک از booking_locations

پروژه

nobat724_front

پرامپت همتای backend که باید اول اجرا شود: clinicpro/.claude/prompt/fix-public-booking-state-and-admin-context-slots.md

زمینه

برای «دکتر تست» (bcabb3a8-cae3-45ec-876c-548f9c1e1569) صفحهٔ /doctor/bcabb3a8-… پیام «نوبت‌دهی غیرفعال است» نشان می‌دهد در حالی که نوبت‌دهی کلینیکش فعال است و همین صفحه، booking_locations معتبر (با next_available_at غیرتهی) را server-side می‌گیرد.

علت (با curl تأیید شد): فیلدهای active / free_turn پاسخ GET /api/v1/doctor/{uuid} در backend فقط از برنامهٔ شخصی پزشک ساخته می‌شدند؛ برنامهٔ شخصیِ دکتر تست غیرفعال است و برنامهٔ کلینیکش دیده نمی‌شد. پرامپت همتا این را با تجمیع همهٔ برنامه‌ها اصلاح می‌کند.

این پرامپت سمت سایت را defensive می‌کند: بنر و دکمهٔ نوبت‌دهی پروفایل نباید فقط به doctor.active تکیه کند وقتی خودِ صفحه دادهٔ دقیق‌تر (booking_locations) را در دست دارد. دو منبع نباید بتوانند حرف متناقض بزنند.

مشکل / هدف

قاعدهٔ واحد برای پروفایل پزشک:

  • نوبت‌دهی فعالbooking_locations غیرخالی است (backend فقط محل‌های واقعاً قابل‌رزرو را برمی‌گرداند: آدرس‌دار + شیفت فعال روی همان آدرس).
  • برچسب «اولین نوبت آزاد» از کمینهٔ next_available_at بین محل‌ها؛ اگر همه null بودند، محل‌ها فعالند ولی فعلاً ظرفیت ندارند → «فعلاً نوبت خالی ندارد» (نه «غیرفعال»).
  • doctor.active === false همراه با booking_locations خالی → «نوبت‌دهی غیرفعال است» (رفتار فعلی، درست).

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

فایل نقش
app/doctor/[slug]/page.js:43,128-150 getBookingLocations server-side — از قبل موجود
components/doctor/appointmentList/index.js:8,20-24 نوار موبایل — doctor?.active === false || !doctor?.free_turn
components/doctor/appointmentList/ItemAppointment.js:10,42-56 کارت دسکتاپ — bookingDisabled = turn?.active === false
app/component/ItemDoctor.js:89 کارت لیست پزشکان — فقط فیلد backend (بدون تغییر)

وضعیت فعلی

کارت دسکتاپ — components/doctor/appointmentList/ItemAppointment.js:10

function ItemAppointment({ turn, loading, doctorSlug }) {
  const bookingDisabled = !loading && turn?.active === false;
  ...
              {bookingDisabled || !turn?.free_turn
                ? "نوبت‌دهی غیرفعال است"
                : `اولین نوبت آزاد: ${turn.free_turn}`}

نوار موبایل — components/doctor/appointmentList/index.js:20-24

          {doctor?.active === false || !doctor?.free_turn
            ? "نوبت‌دهی غیرفعال است"
            : `اولین نوبت آزاد: ${doctor.free_turn}`}

AppointmentList از app/doctor/[slug]/page.js رندر می‌شود که bookingLocations را همان‌جا دارد (:128-129) ولی به این کامپوننت پاس نمی‌دهد.

وظایف

۱. پاس‌دادن bookingLocations به AppointmentList

در app/doctor/[slug]/page.js همان آرایهٔ گرفته‌شده را prop بده:

<AppointmentList doctor={doctor} doctorSlug={slug} bookingLocations={bookingLocations} />

(نام prop و محل رندر را با کد واقعی صفحه تطبیق بده — AppointmentList ممکن است از طریق کامپوننت میانی رندر شود؛ در آن صورت prop را از همان مسیر عبور بده.)

۲. قاعدهٔ واحد فعال/غیرفعال

یک helper کوچک در همان components/doctor/appointmentList/ (نه util سراسری جدید):

export function bookingState(doctor, bookingLocations) {
  const hasLocations = Array.isArray(bookingLocations) && bookingLocations.length > 0;
  if (!hasLocations && doctor?.active === false) return { enabled: false, label: "نوبت‌دهی غیرفعال است" };
  if (!hasLocations) return { enabled: doctor?.active !== false, label: doctor?.free_turn ? `اولین نوبت آزاد: ${doctor.free_turn}` : "نوبت‌دهی غیرفعال است" };
  const earliest = bookingLocations
    .map((l) => l.next_available_at)
    .filter((t) => t != null)
    .sort((a, b) => a - b)[0] ?? null;
  return {
    enabled: true,
    label: earliest != null
      ? `اولین نوبت آزاد: ${formatJalali(earliest)}`
      : "فعلاً نوبت خالی ندارد",
  };
}
  • formatJalali با jalali-moment/moment-jalaali موجود پروژه: روزِ هفته + ساعت (مثل «شنبه ۰۹:۰۰») تا با فرمت free_turn backend هم‌خانواده باشد. الگوی moment.unix(ts) مثل app/component/date/dateTime/index.js.
  • هر دو کامپوننت (index.js نوار موبایل و ItemAppointment.js) از همین helper استفاده کنند؛ شرط‌های تکراری فعلی حذف شوند.
  • دکمهٔ «دریافت نوبت» با enabled === true فعال است حتی وقتی earliest === null (صفحهٔ رزرو خودش روزهای بدون ظرفیت را نشان می‌دهد).

۳. کارت لیست پزشکان — بدون تغییر

app/component/ItemDoctor.js:89 فقط free_turn/active پاسخ لیست را دارد و booking_locations برای هر آیتم لیست fetch نمی‌شود (N درخواست اضافه ممنوع). بعد از فیکس backend همین فیلدها درست می‌شوند — این فایل را دست نزن.

نکات مهم

  • backend اول. قبل از اجرای پرامپت همتا، doctor.active برای دکتر تست همچنان false برمی‌گردد؛ helper این را می‌پوشاند ولی تست کامل فقط بعد از هر دو ممکن است.
  • پاسخ‌های API double-nested: json?.data?.data ?? json?.datagetBookingLocations موجود در صفحه همین را رعایت می‌کند؛ عوضش نکن.
  • booking_locations فقط محل‌های معتبر را دارد (فیلتر backend از پرامپت‌های قبلی) — دوباره فیلتر نکن.
  • رشته‌های جدید فارسی، RTL، تم موجود (MUI v5 + Tailwind، فونت Vazir) — طراحی جدید نساز.
  • slug پزشک = uuid؛ params همیشه await؛ صفحه generateMetadata دارد — دست نزن.
  • تست دستی: /doctor/bcabb3a8-cae3-45ec-876c-548f9c1e1569 باید «اولین نوبت آزاد» و دکمهٔ فعال «دریافت نوبت» نشان دهد (دکتر تست فقط برنامهٔ کلینیکی فعال دارد). یک پزشک واقعاً غیرفعال (بدون هیچ محل) همچنان «نوبت‌دهی غیرفعال است» ببیند.
  • بعد از تغییرات: npm run lint و npm run build هر دو سبز.