import React, { createContext, useCallback, useContext, useState, ReactNode, } from "react"; import { getPersonHistory, PersonHistoryEntry } from "@/apis/personApi"; type PersonAppearances = { raw: PersonHistoryEntry[]; byShow: Record; showIds: number[]; partnersByShow: Record< number, { seasonNumber: number; partner?: PersonHistoryEntry["partner"] }[] >; }; type PersonContextType = { getPersonAppearances: (personId: number) => Promise; getShowIds: (personId: number) => Promise; getSeasonsForShow: (personId: number, showId: number) => Promise; isLoading: (personId: number) => boolean; getError: (personId: number) => string | null; invalidatePerson: (personId: number) => void; }; const PersonContext = createContext(null); export const PersonProvider = ({ children }: { children: ReactNode }) => { const [cache, setCache] = useState>({}); const [loading, setLoading] = useState>({}); const [errors, setErrors] = useState>({}); const buildAppearances = ( entries: PersonHistoryEntry[] ): PersonAppearances => { const byShowSet: Record> = {}; const partnersByShow: PersonAppearances["partnersByShow"] = {}; for (const e of entries) { if (!byShowSet[e.showId]) byShowSet[e.showId] = new Set(); byShowSet[e.showId].add(e.seasonNumber); if (!partnersByShow[e.showId]) partnersByShow[e.showId] = []; partnersByShow[e.showId].push({ seasonNumber: e.seasonNumber, partner: e.partner ?? undefined, }); } const byShow: Record = Object.fromEntries( Object.entries(byShowSet).map(([showId, seasons]) => [ Number(showId), Array.from(seasons).sort((a, b) => a - b), ]) ); Object.values(partnersByShow).forEach((arr) => arr.sort((a, b) => a.seasonNumber - b.seasonNumber) ); return { raw: entries, byShow, showIds: Object.keys(byShow) .map(Number) .sort((a, b) => a - b), partnersByShow, }; }; const fetchAndCache = useCallback(async (personId: number) => { setLoading((l) => ({ ...l, [personId]: true })); setErrors((e) => ({ ...e, [personId]: null })); try { const data = await getPersonHistory(personId); const appearances = buildAppearances(data); setCache((c) => ({ ...c, [personId]: appearances })); return appearances; } catch (e: any) { setErrors((err) => ({ ...err, [personId]: e?.message || "Fehler beim Laden", })); return { raw: [], byShow: {}, showIds: [], partnersByShow: {} }; } finally { setLoading((l) => ({ ...l, [personId]: false })); } }, []); const getPersonAppearances = useCallback( async (personId: number) => { if (cache[personId]) return cache[personId]; return await fetchAndCache(personId); }, [cache, fetchAndCache] ); const getShowIds = useCallback( async (personId: number) => { const app = await getPersonAppearances(personId); return app.showIds; }, [getPersonAppearances] ); const getSeasonsForShow = useCallback( async (personId: number, showId: number) => { const app = await getPersonAppearances(personId); return (app.byShow as Record)[showId] || []; }, [getPersonAppearances] ); const isLoading = (personId: number) => !!loading[personId]; const getError = (personId: number) => errors[personId] || null; const invalidatePerson = (personId: number) => { setCache((c) => { const copy = { ...c }; delete copy[personId]; return copy; }); }; return ( {children} ); }; export const usePersonContext = () => { const ctx = useContext(PersonContext); if (!ctx) throw new Error("usePersonContext must be used within PersonProvider"); return ctx; };