From 232e7eaeb2cee615d6d9f0cd9d9c7e3dfc3bc855 Mon Sep 17 00:00:00 2001 From: hamed <15238-genius.ha@users.noreply.drupalcode.org> Date: Mon, 15 Jun 2026 16:31:47 +0330 Subject: [PATCH] feat(appointment): grey out unavailable days on the booking calendar DatePicker now loads month-availability for the two displayed Jalali months (mapped to the Gregorian months they span, fetched once and cached per month) and disables any day in disabled_dates plus past days. Auto-select skips disabled days. When the doctor has online booking turned off, show a notice instead of the calendar. doctorUuid comes from the route params. Co-Authored-By: Claude Opus 4.8 --- app/component/date/datePicker/index.js | 73 +++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/app/component/date/datePicker/index.js b/app/component/date/datePicker/index.js index 86f5e0d..7bccd58 100644 --- a/app/component/date/datePicker/index.js +++ b/app/component/date/datePicker/index.js @@ -1,20 +1,75 @@ "use client"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; +import { useParams } from "next/navigation"; import moment from "moment-jalaali"; import "moment-timezone"; import { dateToTimestamp } from "@/helper"; +import { request } from "@/services/response"; import InlineJalaliMonth from "@/components/common/InlineJalaliMonth"; -function DatePicker({ setDate, disabledDates = [] }) { +function gregorianMonthsOf(jalaliMonth) { + const first = moment(jalaliMonth).startOf("jMonth"); + const last = moment(jalaliMonth).endOf("jMonth"); + const keys = new Set(); + for (const d of [first, last]) { + keys.add(`${d.year()}-${d.month() + 1}`); + } + return [...keys].map((k) => { + const [year, month] = k.split("-").map(Number); + return { year, month }; + }); +} + +function DatePicker({ setDate }) { + const params = useParams(); + const doctorUuid = params?.doctorId; + const [baseMonth, setBaseMonth] = useState(moment().tz("Asia/Tehran")); const [selectedDate, setSelectedDate] = useState(null); const [autoSelected, setAutoSelected] = useState(false); + const [disabledSet, setDisabledSet] = useState(() => new Set()); + const [onlineEnabled, setOnlineEnabled] = useState(true); + const loadedMonths = useRef(new Set()); + + const secondMonth = moment(baseMonth).add(1, "jMonth"); + + useEffect(() => { + if (!doctorUuid) return; + + const targets = [ + ...gregorianMonthsOf(baseMonth), + ...gregorianMonthsOf(secondMonth), + ].filter(({ year, month }) => !loadedMonths.current.has(`${year}-${month}`)); + + if (targets.length === 0) return; + + targets.forEach(({ year, month }) => { + loadedMonths.current.add(`${year}-${month}`); + request + .getMonthAvailability(doctorUuid, year, month) + .then((res) => { + const data = res?.data ?? {}; + if (data.online_booking_enabled === false) setOnlineEnabled(false); + const dates = Array.isArray(data.disabled_dates) ? data.disabled_dates : []; + if (dates.length) { + setDisabledSet((prev) => { + const next = new Set(prev); + dates.forEach((d) => next.add(d)); + return next; + }); + } + }) + .catch(() => { + loadedMonths.current.delete(`${year}-${month}`); + }); + }); + }, [doctorUuid, baseMonth]); 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"))); + return disabledSet.has(day.format("YYYY-MM-DD")); }; const selectDay = (date) => { @@ -27,7 +82,7 @@ function DatePicker({ setDate, disabledDates = [] }) { if (autoSelected) return; const today = moment().tz("Asia/Tehran"); - for (let i = 0; i <= 30; i++) { + for (let i = 0; i <= 60; i++) { const candidate = today.clone().add(i, "days"); if (!isDisabled(candidate)) { setSelectedDate(candidate); @@ -37,9 +92,15 @@ function DatePicker({ setDate, disabledDates = [] }) { break; } } - }, [disabledDates, autoSelected, setDate]); + }, [disabledSet, autoSelected, setDate]); - const secondMonth = moment(baseMonth).add(1, "jMonth"); + if (!onlineEnabled) { + return ( +
+ نوبت‌دهی آنلاین این پزشک در حال حاضر غیرفعال است. +
+ ); + } return (