import { Box, Button, Group, Text } from "@mantine/core";
import {
Calendar as CalendarIcon,
Check,
ChevronLeft,
ChevronRight,
} from "lucide-react";
import { useMemo, useState } from "react";
export interface OperationDatePickerProps {
/** Selectable days as `yyyy-MM-dd` strings. */
availableDays: string[];
/** Show the loading state instead of the grid. */
isLoading?: boolean;
/** Currently selected day as `yyyy-MM-dd`, or "" when none. */
value: string;
/** Called with the picked `yyyy-MM-dd` day. */
onChange: (date: string) => void;
/** Stretch to the full width of the parent container. */
fullWidth?: boolean;
}
/** `yyyy-MM-dd` for a local date. */
function fmtDay(d: Date): string {
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${y}-${m}-${day}`;
}
const MONTH_NAMES = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const WEEKDAY_LABELS = ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"];
/**
* Presentational month calendar for picking a binding shipment day. Only the
* `availableDays` (passed in by the caller, which owns the query) are
* selectable; every other day is disabled. Framework-light: no data fetching,
* no date library — both the portal and backoffice feed it their own
* availability results so the picker renders identically in each app.
*/
export function OperationDatePicker({
availableDays,
isLoading = false,
value,
onChange,
fullWidth = false,
}: OperationDatePickerProps) {
const [month, setMonth] = useState(() => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), 1);
});
const departureDays = useMemo(
() => new Set(availableDays ?? []),
[availableDays],
);
const cells = useMemo(() => {
const first = new Date(month.getFullYear(), month.getMonth(), 1);
const lead = (first.getDay() + 6) % 7;
const start = new Date(first);
start.setDate(first.getDate() - lead);
const today = new Date();
const todayStr = fmtDay(today);
return Array.from({ length: 42 }, (_, i) => {
const date = new Date(start);
date.setDate(start.getDate() + i);
const dateString = fmtDay(date);
return {
dateString,
day: date.getDate(),
inMonth: date.getMonth() === month.getMonth(),
today: dateString === todayStr,
selected: value === dateString,
hasDeparture: departureDays.has(dateString),
};
});
}, [month, departureDays, value]);
const availableInMonth = useMemo(
() => cells.filter((c) => c.inMonth && c.hasDeparture).length,
[cells],
);
const shiftMonth = (delta: number) =>
setMonth((m) => new Date(m.getFullYear(), m.getMonth() + delta, 1));
return (
{isLoading ? (
Loading available days…
) : (
<>
{WEEKDAY_LABELS.map((d) => (
{d}
))}
{cells.map((c) => {
const clickable = c.hasDeparture && c.inMonth;
return (
);
})}
{value && (
Selected:{" "}
{new Date(value + "T00:00:00").toLocaleDateString(undefined, {
weekday: "short",
month: "short",
day: "numeric",
year: "numeric",
})}
)}
{departureDays.size === 0 && (
No scheduled departures found for this route yet.
)}
>
)}
);
}
function StackedMonthHeader({
month,
availableCount,
}: {
month: Date;
availableCount: number;
}) {
return (
{MONTH_NAMES[month.getMonth()]} {month.getFullYear()}
{availableCount} available day{availableCount === 1 ? "" : "s"}
);
}
export default OperationDatePicker;