Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Updated login and signup components to utilize AuthCard for better user experience during redirection. - Introduced AuthentikEmbedDialog for seamless integration of Authentik's identity portal within the application. - Enhanced password recovery and signup flows with dynamic theme handling and improved loading states. - Refactored existing components to streamline authentication processes and improve maintainability.
123 lines
4.1 KiB
TypeScript
123 lines
4.1 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)
|
|
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<AgendaCalendarsResponse>("/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<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 }
|
|
}
|