"use client" import { useCallback, useEffect, useMemo, useRef, useState } from "react" import { useParams, useRouter, useSearchParams } from "next/navigation" import { addDays, addHours, addMonths, startOfHour } from "date-fns" import { AgendaEventDialog, type AgendaEventDialogState } from "@/components/agenda/agenda-event-dialog" import { AgendaEventPopover, type AgendaEventPopoverState } from "@/components/agenda/agenda-event-popover" import type { AnchorRect } from "@/components/agenda/agenda-floating-card" import { AgendaHeader } from "@/components/agenda/agenda-header" import { AgendaQuickCreate, type AgendaQuickCreateState } from "@/components/agenda/agenda-quick-create" import { AgendaSidebar } from "@/components/agenda/agenda-sidebar" import { AgendaViewMonth } from "@/components/agenda/agenda-view-month" import { AgendaViewWeek } from "@/components/agenda/agenda-view-week" import { Skeleton } from "@/components/ui/skeleton" import { useAgendaCalendars, useAgendaEvents } from "@/lib/api/hooks/use-calendar-queries" import { parseICSDate, viewDays, viewRange } from "@/lib/agenda/agenda-date" import { useAgendaSettingsStore } from "@/lib/agenda/agenda-store" import type { AgendaEvent, AgendaEventDraft } from "@/lib/agenda/agenda-types" import { buildAgendaPath, parseAgendaSegments, type AgendaView, } from "@/lib/agenda/agenda-url" import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity" import { useIsMobile } from "@/hooks/use-mobile" export function AgendaPage() { const router = useRouter() const params = useParams() const searchParams = useSearchParams() const isMobile = useIsMobile() const identity = useChromeIdentity() const route = useMemo( () => parseAgendaSegments(params.segments as string[] | undefined), [params.segments], ) const lastView = useAgendaSettingsStore((s) => s.lastView) const setLastView = useAgendaSettingsStore((s) => s.setLastView) const hiddenIds = useAgendaSettingsStore((s) => s.hiddenCalendarIds) const view: AgendaView = route.view ?? (isMobile ? "day" : lastView) const date = route.date // Normalise l'URL quand la vue est implicite. useEffect(() => { if (!route.view) { router.replace(buildAgendaPath(view, date) + window.location.search) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [route.view]) useEffect(() => { if (route.view) setLastView(route.view) }, [route.view, setLastView]) const navigate = useCallback( (nextView: AgendaView, nextDate: Date) => { router.push(buildAgendaPath(nextView, nextDate)) }, [router], ) const step = useCallback( (delta: 1 | -1) => { const next = view === "month" ? addMonths(date, delta) : addDays(date, view === "week" ? delta * 7 : delta) navigate(view, next) }, [view, date, navigate], ) // Données const { data: calendars = [], isLoading: calendarsLoading } = useAgendaCalendars() const visibleCalendars = useMemo( () => calendars.filter((c) => !hiddenIds.includes(c.id)), [calendars, hiddenIds], ) const fetchRange = useMemo(() => viewRange("month", date), [date]) const { events } = useAgendaEvents(visibleCalendars, fetchRange.start, fetchRange.end) // UI : création rapide / dialog / détails const [quickCreate, setQuickCreate] = useState(null) const [dialogState, setDialogState] = useState(null) const [popover, setPopover] = useState(null) const defaultCalendarId = visibleCalendars[0]?.id ?? calendars[0]?.id ?? "" const userEmail = identity?.email const closeOverlays = useCallback(() => { setQuickCreate(null) setPopover(null) }, []) const openCreateDialog = useCallback( (base?: Partial) => { closeOverlays() const start = base?.start ?? addHours(startOfHour(new Date()), 1) const end = base?.end ?? addHours(start, 1) setDialogState({ mode: "create", draft: { title: "", start, end, allDay: false, calendarId: defaultCalendarId, ...base, }, }) }, [closeOverlays, defaultCalendarId], ) const openEditDialog = useCallback( (event: AgendaEvent) => { closeOverlays() const masterStart = parseICSDate(event.master.start) ?? event.start const masterEnd = parseICSDate(event.master.end) ?? event.end setDialogState({ mode: "edit", event, draft: { title: event.title === "(Sans titre)" ? "" : event.title, start: masterStart, end: masterEnd, allDay: event.allDay, calendarId: event.calendarId, description: event.description, location: event.location, attendees: event.attendees, rrule: event.rrule, color: event.master.color, }, }) }, [closeOverlays], ) // Interop : /agenda?new=1&guest=…&title=… const handledNewParam = useRef(false) useEffect(() => { if (handledNewParam.current || !route.view) return if (searchParams.get("new") !== "1") return handledNewParam.current = true const guest = searchParams.get("guest")?.trim() const guestName = searchParams.get("guest_name")?.trim() openCreateDialog({ title: searchParams.get("title")?.trim() ?? "", attendees: guest ? [{ email: guest, name: guestName || guest, status: "NEEDS-ACTION" }] : [], }) router.replace(buildAgendaPath(view, date)) // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchParams, route.view]) // Raccourcis clavier façon Google Calendar. useEffect(() => { const onKey = (e: KeyboardEvent) => { const target = e.target as HTMLElement if ( e.metaKey || e.ctrlKey || e.altKey || target.closest("input, textarea, select, [contenteditable], [role=dialog]") ) return switch (e.key.toLowerCase()) { case "j": case "d": navigate("day", date) break case "s": case "w": navigate("week", date) break case "m": navigate("month", date) break case "t": navigate(view, new Date()) break case "c": openCreateDialog() break default: return } e.preventDefault() } window.addEventListener("keydown", onKey) return () => window.removeEventListener("keydown", onKey) }, [navigate, view, date, openCreateDialog]) const handleEventClick = useCallback((event: AgendaEvent, anchor: AnchorRect) => { setQuickCreate(null) setPopover({ event, anchor }) }, []) const handleCreateRange = useCallback( (start: Date, end: Date, allDay: boolean, anchor: AnchorRect) => { setPopover(null) setQuickCreate({ start, end, allDay, anchor }) }, [], ) const days = useMemo(() => viewDays(view, date), [view, date]) return ( <> navigate(view, new Date())} onStep={step} onViewChange={(v) => navigate(v, date)} />
navigate(view, d)} onCreateEvent={() => openCreateDialog()} />
{calendarsLoading ? (
) : view === "month" ? ( handleCreateRange(day, addDays(day, 1), true, anchor) } onEventClick={handleEventClick} onOpenDay={(day) => navigate("day", day)} /> ) : ( navigate("day", day)} /> )}
0 ? visibleCalendars : calendars} defaultCalendarId={defaultCalendarId} userEmail={userEmail} onClose={() => setQuickCreate(null)} onMoreOptions={(draft) => { setQuickCreate(null) setDialogState({ mode: "create", draft }) }} /> setPopover(null)} onEdit={openEditDialog} /> setDialogState(null)} calendars={calendars} userEmail={userEmail} /> ) }