"use client"; import { ChevronLeft, ChevronRight } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { type Appointment, ScheduleList, TODAY, } from "@/components/appointments/appointments-view"; import { Button } from "@/components/ui/button"; import { Dialog, DialogHeader, DialogPanel, DialogPopup, DialogTitle, } from "@/components/ui/dialog"; import { cn } from "@/lib/utils"; const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; // Local-date ISO key, e.g. "2026-06-05" (avoids UTC drift from toISOString). const keyOf = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String( d.getDate(), ).padStart(2, "0")}`; const parseKey = (key: string) => new Date(`${key}T00:00:00`); const formatDayKey = (key: string) => parseKey(key).toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", }); const byTime = (a: Appointment, b: Appointment) => a.time.localeCompare(b.time); // Event chip color by status, using semantic tokens. const chipClass: Record = { confirmed: "bg-secondary text-secondary-foreground", "checked-in": "bg-success/15 text-success", completed: "bg-muted text-muted-foreground", cancelled: "bg-destructive/15 text-destructive line-through", }; // A Google-Calendar-style month grid in a dialog: a 6×7 grid of day cells with // color-coded event chips; navigate months and click a day to list its // appointments. Reads the appointments passed down from the page. export function CalendarDialog({ open, onOpenChange, appointments, initialDate, }: { open: boolean; onOpenChange: (open: boolean) => void; appointments: Appointment[]; // Optional deep-link target (YYYY-MM-DD): when set, the calendar opens on this // date's month with the day selected (e.g. from the AI chat appointment card). initialDate?: string | null; }) { const { t } = useTranslation(); const startKey = initialDate && /^\d{4}-\d{2}-\d{2}$/.test(initialDate) ? initialDate : TODAY; // First-of-month for the displayed month; defaults to the deep-link/TODAY month. const [viewMonth, setViewMonth] = useState(() => { const d = parseKey(startKey); return new Date(d.getFullYear(), d.getMonth(), 1); }); const [selectedKey, setSelectedKey] = useState(startKey); // When opened via a deep-link date, jump the view to that month/day. useEffect(() => { if (!open || !initialDate || !/^\d{4}-\d{2}-\d{2}$/.test(initialDate)) return; const d = parseKey(initialDate); setViewMonth(new Date(d.getFullYear(), d.getMonth(), 1)); setSelectedKey(initialDate); }, [open, initialDate]); const byDate = useMemo(() => { const map = new Map(); for (const a of appointments) { const list = map.get(a.date) ?? []; list.push(a); map.set(a.date, list); } return map; }, [appointments]); // 42 cells (6 weeks) starting from the Sunday on/before the 1st. const cells = useMemo(() => { const first = new Date(viewMonth.getFullYear(), viewMonth.getMonth(), 1); const start = new Date( first.getFullYear(), first.getMonth(), 1 - first.getDay(), ); return Array.from( { length: 42 }, (_, i) => new Date(start.getFullYear(), start.getMonth(), start.getDate() + i), ); }, [viewMonth]); const monthLabel = viewMonth.toLocaleDateString("en-US", { month: "long", year: "numeric", }); const selectedItems = (byDate.get(selectedKey) ?? []).slice().sort(byTime); const shiftMonth = (delta: number) => setViewMonth( (m) => new Date(m.getFullYear(), m.getMonth() + delta, 1), ); const goToday = () => { const t = parseKey(TODAY); setViewMonth(new Date(t.getFullYear(), t.getMonth(), 1)); setSelectedKey(TODAY); }; return (
{monthLabel}
{WEEKDAYS.map((d) => (
{d}
))}
{cells.map((date) => { const key = keyOf(date); const inMonth = date.getMonth() === viewMonth.getMonth(); const isToday = key === TODAY; const isSelected = key === selectedKey; const items = (byDate.get(key) ?? []).slice().sort(byTime); return ( ); })}

{formatDayKey(selectedKey)}

{t("appointments.calendarDialog.appointmentCount", { count: selectedItems.length, })}

{selectedItems.length > 0 ? ( ) : (
{t("appointments.calendarDialog.none")}
)}
); }