ultisuite-client/lib/api/hooks/use-calendar-queries.ts
R3D347HR4Y ad1370ea7e
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat: enhance configuration and add new demo layouts
- Introduced turbopack alias for canvas in next.config.mjs.
- Updated package.json scripts for development and branding tasks.
- Added new dependencies for Tiptap extensions.
- Implemented new demo layouts for agenda, contacts, drive, and mail applications.
- Enhanced globals.css for improved theming and splash screen animations.
- Added OAuth callback handling for drive mounts.
- Updated layout components to integrate new demo shells and improve structure.
2026-06-12 19:10:24 +02:00

116 lines
3.9 KiB
TypeScript

"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)
return useQuery({
queryKey: isDemoAgenda
? [...DEMO_AGENDA_QUERY_ROOT, "calendars", demoVersion]
: agendaCalendarsKey,
enabled: ready && authenticated,
staleTime: 5 * 60_000,
queryFn: async () => {
if (isDemoAgenda) {
return useDemoAgendaStore.getState().listCalendars()
}
const res = await apiClient.get<AgendaCalendarsResponse>("/calendar")
return res.calendars ?? []
},
initialData: isDemoAgenda
? () => useDemoAgendaStore.getState().listCalendars()
: undefined,
})
}
/**
* 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<AgendaEventsResponse>(
`/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 }
}