"use client" import { useMemo } from "react" import { useQueries, useQuery } from "@tanstack/react-query" import { format } from "date-fns" import { apiClient } from "@/lib/api/client" import { useAuthReady } from "@/lib/api/use-auth-ready" import { expandApiEvents } from "@/lib/agenda/agenda-events" import { useIsDemoAgenda } from "@/lib/demo/demo-agenda-context" import { DEMO_AGENDA_QUERY_ROOT } from "@/lib/demo/demo-agenda-bootstrap" import { useDemoAgendaStore } from "@/lib/demo/demo-agenda-store" import type { AgendaCalendar, AgendaCalendarsResponse, AgendaEvent, AgendaEventsResponse, } from "@/lib/agenda/agenda-types" export const agendaCalendarsKey = ["agenda", "calendars"] as const export function agendaEventsKey(calendarId: string, from: string, to: string) { return ["agenda", "events", calendarId, from, to] as const } export function useAgendaCalendars() { const { ready, authenticated } = useAuthReady() const isDemoAgenda = useIsDemoAgenda() const demoVersion = useDemoAgendaStore((s) => s.version) const queryEnabled = ready && authenticated const query = useQuery({ queryKey: isDemoAgenda ? [...DEMO_AGENDA_QUERY_ROOT, "calendars", demoVersion] : agendaCalendarsKey, enabled: queryEnabled, staleTime: 5 * 60_000, queryFn: async () => { if (isDemoAgenda) { return useDemoAgendaStore.getState().listCalendars() } const res = await apiClient.get("/calendar/") return res.calendars ?? [] }, initialData: isDemoAgenda ? () => useDemoAgendaStore.getState().listCalendars() : undefined, }) // Disabled queries still return cached data — hide until auth persist hydrated. return { ...query, data: queryEnabled ? query.data : undefined, isLoading: queryEnabled && query.isLoading, } } /** * Charge et développe les événements de plusieurs agendas sur une fenêtre. * La fenêtre est élargie au mois pour profiter du cache entre vues. */ export function useAgendaEvents( calendars: AgendaCalendar[], rangeStart: Date, rangeEnd: Date, ) { const { ready, authenticated } = useAuthReady() const isDemoAgenda = useIsDemoAgenda() const demoVersion = useDemoAgendaStore((s) => s.version) const from = format(rangeStart, "yyyy-MM-dd") const to = format(rangeEnd, "yyyy-MM-dd") const results = useQueries({ queries: calendars.map((cal) => ({ queryKey: isDemoAgenda ? [...DEMO_AGENDA_QUERY_ROOT, "events", cal.id, from, to, demoVersion] : agendaEventsKey(cal.id, from, to), enabled: ready && authenticated, staleTime: 30_000, queryFn: async () => { if (isDemoAgenda) { return useDemoAgendaStore.getState().listEvents(cal.id, from, to) } const res = await apiClient.get( `/calendar/${encodeURIComponent(cal.id)}/events`, { from, to, page_size: "500" }, ) return res.events ?? [] }, initialData: isDemoAgenda ? () => useDemoAgendaStore.getState().listEvents(cal.id, from, to) : undefined, })), }) const isLoading = results.some((r) => r.isLoading) const isError = results.every((r) => r.isError) && results.length > 0 // Empreinte à taille fixe — ne pas spread `results.map(...)` dans les deps de useMemo // (la longueur du tableau de dépendances doit rester constante entre les renders). const resultsFingerprint = useMemo( () => calendars .map((cal, i) => { const r = results[i] return `${cal.id}:${r?.dataUpdatedAt ?? 0}:${r?.fetchStatus ?? "idle"}` }) .join("|"), [calendars, results], ) const rangeStartMs = rangeStart.getTime() const rangeEndMs = rangeEnd.getTime() const events: AgendaEvent[] = useMemo(() => { const all: AgendaEvent[] = [] calendars.forEach((cal, i) => { const data = results[i]?.data if (data) all.push(...expandApiEvents(cal, data, rangeStart, rangeEnd)) }) return all.sort((a, b) => a.start.getTime() - b.start.getTime()) }, [calendars, results, rangeStartMs, rangeEndMs, resultsFingerprint]) return { events, isLoading, isError } }