Files
nobat724_front/app/component/date/datePicker/index.js
T
hamedandClaude Opus 4.8 232e7eaeb2 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 <noreply@anthropic.com>
2026-06-15 16:31:47 +03:30

134 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
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 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 disabledSet.has(day.format("YYYY-MM-DD"));
};
const selectDay = (date) => {
if (isDisabled(date)) return;
setSelectedDate(date);
setDate(dateToTimestamp(date));
};
useEffect(() => {
if (autoSelected) return;
const today = moment().tz("Asia/Tehran");
for (let i = 0; i <= 60; i++) {
const candidate = today.clone().add(i, "days");
if (!isDisabled(candidate)) {
setSelectedDate(candidate);
setBaseMonth(candidate.clone());
setDate(candidate.clone().startOf("day").unix());
setAutoSelected(true);
break;
}
}
}, [disabledSet, autoSelected, setDate]);
if (!onlineEnabled) {
return (
<div className="w-full py-[40px] text-center text-[#525252] text-[14px] md:text-[16px] font-medium">
نوبتدهی آنلاین این پزشک در حال حاضر غیرفعال است.
</div>
);
}
return (
<div dir="rtl" className="datePickerApt flex items-start justify-evenly gap-[24px] w-full">
<div className="w-full lg:w-1/2">
<InlineJalaliMonth
displayDate={baseMonth}
selectedDate={selectedDate}
onSelectDay={selectDay}
isDisabled={isDisabled}
showNext={false}
onPrev={() => setBaseMonth(moment(baseMonth).subtract(1, "jMonth"))}
onNext={() => setBaseMonth(moment(baseMonth).add(1, "jMonth"))}
/>
</div>
<div className="hidden md:block lg:hidden xl:block w-1/2">
<InlineJalaliMonth
displayDate={secondMonth}
selectedDate={selectedDate}
onSelectDay={selectDay}
isDisabled={isDisabled}
showPrev={false}
onNext={() => setBaseMonth(moment(baseMonth).add(1, "jMonth"))}
onPrev={() => setBaseMonth(moment(baseMonth).subtract(1, "jMonth"))}
/>
</div>
</div>
);
}
export default DatePicker;