feat(appointment): inline dual-month Jalali calendar for day selection
The day picker used a Popover-based JalaliDatePicker that only showed two empty text inputs on the booking page — the calendar was hidden until you clicked the input, so it never matched the design. Replace it with an always-visible inline two-month calendar (InlineJalaliMonth) rendered side by side: current month on the right, next on the left (RTL), weekday headers, Fridays in red, disabled days greyed, selected day in orange, shared month navigation on the outer edges. Preserves the existing contract: setDate(unix timestamp), disabledDates[], auto-select nearest available day. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
"use client";
|
||||
|
||||
import moment from "moment-jalaali";
|
||||
|
||||
moment.loadPersian({ dialect: "persian-modern", usePersianDigits: false });
|
||||
|
||||
const WEEKDAYS = ["شنبه", "۱ شنبه", "۲ شنبه", "۳ شنبه", "۴ شنبه", "۵ شنبه", "جمعه"];
|
||||
const MONTHS = [
|
||||
"فروردین",
|
||||
"اردیبهشت",
|
||||
"خرداد",
|
||||
"تیر",
|
||||
"مرداد",
|
||||
"شهریور",
|
||||
"مهر",
|
||||
"آبان",
|
||||
"آذر",
|
||||
"دی",
|
||||
"بهمن",
|
||||
"اسفند",
|
||||
];
|
||||
|
||||
function buildDays(displayDate) {
|
||||
const year = displayDate.jYear();
|
||||
const month = displayDate.jMonth();
|
||||
const daysInMonth = moment.jDaysInMonth(year, month);
|
||||
const firstDayOfMonth = moment(`${year}/${month + 1}/1`, "jYYYY/jM/jD").day();
|
||||
const startOffset = (firstDayOfMonth + 1) % 7;
|
||||
|
||||
const days = [];
|
||||
for (let i = 0; i < startOffset; i++) days.push(null);
|
||||
for (let day = 1; day <= daysInMonth; day++) days.push(day);
|
||||
return days;
|
||||
}
|
||||
|
||||
function InlineJalaliMonth({
|
||||
displayDate,
|
||||
selectedDate,
|
||||
onSelectDay,
|
||||
isDisabled,
|
||||
onPrev,
|
||||
onNext,
|
||||
showPrev = true,
|
||||
showNext = true,
|
||||
}) {
|
||||
const days = buildDays(displayDate);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="flex items-center justify-between mb-[16px] px-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onNext}
|
||||
className={`p-1 text-[#525252] ${showNext ? "" : "invisible"}`}
|
||||
aria-label="ماه بعد"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<p className="text-[#3B3B3B] text-[14px] md:text-[16px] font-bold">
|
||||
{MONTHS[displayDate.jMonth()]} {displayDate.jYear()}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onPrev}
|
||||
className={`p-1 text-[#525252] ${showPrev ? "" : "invisible"}`}
|
||||
aria-label="ماه قبل"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-7 gap-y-[12px] mb-[8px]">
|
||||
{WEEKDAYS.map((label, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
className="text-center text-[#9B9B9B] text-[11px] md:text-[12px] font-normal"
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-7 gap-y-[12px]">
|
||||
{days.map((day, index) => {
|
||||
if (!day) return <span key={index} />;
|
||||
|
||||
const dayDate = moment(displayDate).jDate(day);
|
||||
const isFriday = dayDate.day() === 5;
|
||||
const disabled = isDisabled(dayDate);
|
||||
const selected =
|
||||
selectedDate &&
|
||||
dayDate.jYear() === selectedDate.jYear() &&
|
||||
dayDate.jMonth() === selectedDate.jMonth() &&
|
||||
dayDate.jDate() === selectedDate.jDate();
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
key={index}
|
||||
disabled={disabled}
|
||||
onClick={() => onSelectDay(dayDate)}
|
||||
className={`
|
||||
mx-auto flex items-center justify-center w-[34px] h-[34px] rounded-full
|
||||
text-[14px] md:text-[16px] font-normal transition-colors
|
||||
${selected ? "!bg-[#F17732] !text-[#FFF]" : ""}
|
||||
${disabled ? "text-[#D7D7D7] cursor-not-allowed" : isFriday ? "text-[#E0383B]" : "text-[#525252] hover:bg-[#F5F5F5]"}
|
||||
`}
|
||||
>
|
||||
{day}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default InlineJalaliMonth;
|
||||
Reference in New Issue
Block a user