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>
This commit is contained in:
@@ -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 (
|
||||
<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">
|
||||
|
||||
Reference in New Issue
Block a user