/** * Parent API client. Cookies (HttpOnly bp_parent_session) carry auth — * we never store the session token in JS-readable storage. credentials: * 'include' is mandatory so the cookie ships with each request. */ const PROXY_PREFIX = '/api/parent' interface FetchOptions extends RequestInit { expectJson?: boolean } async function parentFetch(endpoint: string, opts: FetchOptions = {}): Promise { const res = await fetch(`${PROXY_PREFIX}${endpoint}`, { ...opts, credentials: 'include', headers: { 'Content-Type': 'application/json', ...(opts.headers as Record | undefined), }, }) if (!res.ok) { const err = await res.json().catch(() => ({ error: 'Unknown error' })) throw new Error(err.error || `HTTP ${res.status}`) } if (res.status === 204) return undefined as T return res.json() } export interface ParentMeResponse { parent: { id: string; email: string; preferred_language: string } children: Array<{ id: string parent_id: string tt_class_id: string first_name: string last_name: string class_name?: string }> } export interface ParentLesson { DayOfWeek: number PeriodIndex: number StartTime: string EndTime: string ClassName: string SubjectName: string SubjectCode: string TeacherName: string RoomName: string Pinned: boolean } export const elternApi = { redeem: (token: string) => parentFetch<{ id: string; email: string; preferred_language: string }>('/auth/redeem', { method: 'POST', body: JSON.stringify({ token }), }), me: () => parentFetch('/me'), timetable: (classId: string) => parentFetch(`/me/timetable?class_id=${encodeURIComponent(classId)}`), logout: () => parentFetch('/auth/logout', { method: 'POST' }), }