export type AcademicPeriod = { id: number; name: string; displayName?: string | null; startDate: string; // YYYY-MM-DD endDate: string; // YYYY-MM-DD periodType: 'schuljahr' | 'semester' | 'trimester'; isActive: boolean; isArchived: boolean; archivedAt?: string | null; archivedBy?: number | null; createdAt?: string; updatedAt?: string; }; export type PeriodUsage = { linked_events: number; has_active_recurrence: boolean; blockers: string[]; }; async function api(url: string, init?: RequestInit): Promise { const res = await fetch(url, { credentials: 'include', ...init }); if (!res.ok) { const text = await res.text(); try { const err = JSON.parse(text); throw new Error(err.error || `HTTP ${res.status}`); } catch { throw new Error(`HTTP ${res.status}: ${text}`); } } return res.json(); } export async function getAcademicPeriodForDate(date: Date): Promise { const iso = date.toISOString().slice(0, 10); const { period } = await api<{ period: AcademicPeriod | null }>( `/api/academic_periods/for_date?date=${iso}` ); return period ?? null; } export async function listAcademicPeriods(options?: { includeArchived?: boolean; archivedOnly?: boolean; }): Promise { const params = new URLSearchParams(); if (options?.includeArchived) { params.set('includeArchived', '1'); } if (options?.archivedOnly) { params.set('archivedOnly', '1'); } const query = params.toString(); const { periods } = await api<{ periods: AcademicPeriod[] }>( `/api/academic_periods${query ? `?${query}` : ''}` ); return Array.isArray(periods) ? periods : []; } export async function getAcademicPeriod(id: number): Promise { const { period } = await api<{ period: AcademicPeriod }>(`/api/academic_periods/${id}`); return period; } export async function getActiveAcademicPeriod(): Promise { const { period } = await api<{ period: AcademicPeriod | null }>(`/api/academic_periods/active`); return period ?? null; } export async function createAcademicPeriod(payload: { name: string; displayName?: string; startDate: string; endDate: string; periodType: 'schuljahr' | 'semester' | 'trimester'; }): Promise { const { period } = await api<{ period: AcademicPeriod }>(`/api/academic_periods`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); return period; } export async function updateAcademicPeriod( id: number, payload: Partial<{ name: string; displayName: string | null; startDate: string; endDate: string; periodType: 'schuljahr' | 'semester' | 'trimester'; }> ): Promise { const { period } = await api<{ period: AcademicPeriod }>(`/api/academic_periods/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); return period; } export async function setActiveAcademicPeriod(id: number): Promise { const { period } = await api<{ period: AcademicPeriod }>(`/api/academic_periods/${id}/activate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, }); return period; } export async function archiveAcademicPeriod(id: number): Promise { const { period } = await api<{ period: AcademicPeriod }>(`/api/academic_periods/${id}/archive`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, }); return period; } export async function restoreAcademicPeriod(id: number): Promise { const { period } = await api<{ period: AcademicPeriod }>(`/api/academic_periods/${id}/restore`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, }); return period; } export async function getAcademicPeriodUsage(id: number): Promise { const { usage } = await api<{ usage: PeriodUsage }>(`/api/academic_periods/${id}/usage`); return usage; } export async function deleteAcademicPeriod(id: number): Promise { await api(`/api/academic_periods/${id}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, }); }