antragstracker/frontend/src/lib/api.ts

223 lines
6.2 KiB
TypeScript
Raw Normal View History

// 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<T>(path: string): Promise<T> {
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<T> {
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<string, { status: string; anzahl: number }[]>;
status_detail: { status: string; anzahl: number; avg_tage: number; avg_vertagungen: number }[];
versandungs_fruehwarnung: number;
}
// API functions
export const fetchStats = () => get<Stats>('/stats');
export const fetchKettenStats = () => get<KettenStats>('/stats/ketten-stats');
export const fetchVorlagen = (params: Record<string, string>) => {
const qs = new URLSearchParams(params).toString();
return get<Paginated<VorlageKurz>>(`/vorlagen?${qs}`);
};
export const fetchVorlage = (id: number) => get<VorlageDetail>(`/vorlagen/${id}`);
export const fetchKetten = (params: Record<string, string>) => {
const qs = new URLSearchParams(params).toString();
return get<Paginated<KetteKurz>>(`/ketten?${qs}`);
};
export const fetchKette = (id: number) => get<KetteDetail>(`/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<T>(path: string, body: object): Promise<T> {
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<FraktionDashboard>(`/fraktionen/${kuerzel}/dashboard${params}`);
};