// API-Base: In Produktion relativ, in Dev mit Port const BASE = typeof window !== 'undefined' ? (window.location.port === '5173' ? `http://${window.location.hostname}:8099/api` // Dev : '/api') // Produktion : '/api'; async function get(path: string): Promise { const res = await fetch(`${BASE}${path}`); if (!res.ok) throw new Error(`API error: ${res.status}`); return res.json(); } export interface VorlageKurz { id: number; aktenzeichen: string | null; typ: string | null; betreff: string | null; datum_eingang: string | null; ist_verwaltungsvorlage: boolean; } export interface ParteiOut { id: number; kuerzel: string; name: string | null; farbe: string | null; } export interface GremiumOut { id: number; name: string; kuerzel: string | null; typ: string | null; } export interface BeratungOut { id: number; gremium: GremiumOut | null; sitzung_datum: string | null; rolle: string | null; ergebnis: string | null; ergebnis_text: string | null; } export interface ReferenzOut { vorlage_id: number; aktenzeichen: string | null; betreff: string | null; vorlage_typ: string | null; datum_eingang: string | null; ref_typ: string | null; konfidenz: number | null; kontext: string | null; } export interface VorlageDetail extends VorlageKurz { aktenzeichen_basis: string | null; aktenzeichen_suffix: string | null; volltext_clean: string | null; pdf_url: string | null; web_url: string | null; thema_kurz: string | null; antragsteller: ParteiOut[]; beratungen: BeratungOut[]; referenzen_ausgehend: ReferenzOut[]; referenzen_eingehend: ReferenzOut[]; kette_id: number | null; } export interface KetteKurz { id: number; ursprung: VorlageKurz | null; typ: string | null; thema: string | null; status: string | null; status_seit: string | null; letzte_aktivitaet: string | null; vertagungen_count: number; glieder_count: number; } export interface KettenGliedOut { vorlage: VorlageKurz; position: number; rolle: string | null; } export interface KetteDetail { id: number; ursprung: VorlageKurz | null; typ: string | null; thema: string | null; status: string | null; status_seit: string | null; letzte_aktivitaet: string | null; vertagungen_count: number; begruendung: string | null; glieder: KettenGliedOut[]; antragsteller: ParteiOut[]; graph: { nodes: GraphNode[]; edges: GraphEdge[]; } | null; } export interface GraphNode { id: number; aktenzeichen: string | null; typ: string | null; betreff: string | null; datum_eingang: string | null; position?: number; rolle?: string; ist_verwaltungsvorlage?: boolean; extern?: boolean; beratungen?: { sitzung_datum: string; rolle: string; ergebnis: string; gremium_name: string }[]; antragsteller?: { kuerzel: string; name: string; farbe: string }[]; } export interface GraphEdge { quelle_id: number; ziel_id: number; typ: string; konfidenz: number; } export interface Paginated { items: T[]; total: number; page: number; page_size: number; } export interface Stats { vorlagen_total: number; beratungen_total: number; ketten_total: number; vorlagen_nach_typ: { typ: string; anzahl: number }[]; ketten_nach_status: { status: string; anzahl: number }[]; ketten_nach_typ: { typ: string; anzahl: number }[]; letzte_vorlagen: VorlageKurz[]; parteien: { kuerzel: string; name: string; farbe: string | null; anzahl: number }[]; gremien: { name: string; kuerzel: string | null; typ: string | null; anzahl: number }[]; timeline: { monat: string; anzahl: number }[]; } export interface KettenStats { nach_typ: Record; status_detail: { status: string; anzahl: number; avg_tage: number; avg_vertagungen: number }[]; versandungs_fruehwarnung: number; } // API functions export const fetchStats = () => get('/stats'); export const fetchKettenStats = () => get('/stats/ketten-stats'); export const fetchVorlagen = (params: Record) => { const qs = new URLSearchParams(params).toString(); return get>(`/vorlagen?${qs}`); }; export const fetchVorlage = (id: number) => get(`/vorlagen/${id}`); export const fetchKetten = (params: Record) => { const qs = new URLSearchParams(params).toString(); return get>(`/ketten?${qs}`); }; export const fetchKette = (id: number) => get(`/ketten/${id}`); // Fraktionen export interface FraktionDashboard { partei: { id: number; kuerzel: string; name: string; farbe: string | null }; total_antraege: number; bewertet: number; umsetzung: { bewertung: string; anzahl: number; avg_score: number }[]; antraege: { id: number; aktenzeichen: string | null; betreff: string | null; typ: string | null; datum_eingang: string | null; umsetzung_score: number | null; umsetzung_bewertung: string | null; umsetzung_begruendung: string | null; }[]; jahre: string[]; } // Re-evaluation async function post(path: string, body: object): Promise { const res = await fetch(`${BASE}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); if (!res.ok) throw new Error(`API error: ${res.status}`); return res.json(); } export const reevalVorlage = (id: number, anmerkung: string) => post<{ job_id: string; status: string }>(`/bewertung/vorlagen/${id}`, { anmerkung }); export const reevalKette = (id: number, anmerkung: string) => post<{ job_id: string; status: string }>(`/bewertung/ketten/${id}`, { anmerkung }); export const fetchJobStatus = (jobId: string) => get<{ status: string; result?: object; error?: string }>(`/bewertung/status/${jobId}`); export interface SuchVorschlag { id: number; aktenzeichen: string | null; betreff: string | null; typ: string | null; snippet?: string; } export const fetchSuchvorschlaege = (q: string) => get<{ items: SuchVorschlag[] }>(`/vorlagen/suggest?q=${encodeURIComponent(q)}`); export const fetchFraktionen = () => get<{ id: number; kuerzel: string; name: string; farbe: string | null; anzahl: number }[]>('/fraktionen'); export const fetchFraktionDashboard = (kuerzel: string, jahr?: string) => { const params = jahr ? `?jahr=${jahr}` : ''; return get(`/fraktionen/${kuerzel}/dashboard${params}`); };