export const API_BASE = "/api/v1"; export type Entity = { id: string; created_at: string; updated_at: string; }; export type Customer = Entity & { customer_type: "practice" | "clinic"; name: string; city?: string | null; phone?: string | null; email?: string | null; hygiene_officer?: string | null; quality_manager?: string | null; notes?: string | null; }; export type Location = Entity & { customer_id: string; name: string; street?: string | null; postal_code?: string | null; city?: string | null; room?: string | null; }; export type Contact = Entity & { customer_id: string; full_name: string; function?: string | null; email?: string | null; phone?: string | null; }; export type Device = Entity & { customer_id: string; location_id?: string | null; manufacturer: string; model: string; serial_number: string; device_type?: string | null; year_built?: number | null; commissioned_on?: string | null; chamber_volume_liters?: number | null; steam_generation?: string | null; water_treatment?: string | null; documentation?: string | null; supplier?: string | null; }; export type Equipment = Entity & { kind: string; manufacturer?: string | null; model?: string | null; serial_number: string; calibrated_on?: string | null; calibration_due_on?: string | null; certificate_document_id?: string | null; status: "green" | "yellow" | "red"; }; export type User = Entity & { email: string; first_name: string; last_name: string; role: "admin" | "pruefer" | "mitarbeiter" | "leser"; is_active: boolean; must_change_password: boolean; last_login_at?: string | null; password_changed_at?: string | null; }; export type ValidationItem = Entity & { report_number: string; customer_id: string; location_id?: string | null; contact_id?: string | null; device_id?: string | null; validation_type: string; project?: string | null; test_location?: string | null; examiner_name?: string | null; participants?: string | null; operator_name?: string | null; status: string; result?: string | null; scheduled_on?: string | null; performed_on?: string | null; next_validation_on?: string | null; revalidation_interval_months: number; next_validation_manually_overridden: boolean; version: number; previous_validation_id?: string | null; equipment_ids: string[]; environment_conditions: Record; documentation_checklist: Record[]; performance_checklist: Record[]; programs: Record[]; loading_patterns: Record[]; measurement_data: Record[]; drying: Record; recommendations: Record[]; attachments: Record[]; }; export type QuickStartValidationSummary = { id: string; device_id: string; report_number: string; performed_on?: string | null; result?: string | null; next_validation_on?: string | null; status: string; validation_type?: string | null; equipment_ids: string[]; }; export type QuickStartCustomerData = { customer: Customer; locations: Location[]; contacts: Contact[]; devices: Device[]; validations: QuickStartValidationSummary[]; }; export function normalizeValidationPayload>(values: T): T { const optionalForeignKeys = ["contact_id", "examiner_id"]; return { ...values, ...Object.fromEntries(optionalForeignKeys.map((key) => [key, values[key] || null])) }; } export type Paginated = { items: T[]; total: number; page: number; page_size: number; pages?: number; }; async function readErrorMessage(response: Response, fallback: string) { const text = await response.text(); if (!text) { return fallback; } try { const parsed = JSON.parse(text) as { detail?: string; message?: string }; return parsed.detail ?? parsed.message ?? text; } catch { return text; } } export async function login(email: string, password: string) { const response = await fetch(`/api/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), credentials: "include" }); if (!response.ok) { const detail = await response.text(); const error = new Error(detail || `HTTP ${response.status}`) as Error & { status?: number }; error.status = response.status; throw error; } return response.json() as Promise<{ user: User; expires_in: number }>; } export async function apiGet(path: string, token: string): Promise { const response = await fetch(`${API_BASE}${path}`, { cache: "no-store", credentials: "include" }); if (!response.ok) { throw new Error(`API request failed: ${response.status}`); } return response.json() as Promise; } export async function apiSend(path: string, token: string, method: "POST" | "PUT", body: unknown): Promise { const response = await fetch(`${API_BASE}${path}`, { method, headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), credentials: "include" }); if (!response.ok) { throw new Error(await readErrorMessage(response, `API request failed: ${response.status}`)); } return response.json() as Promise; } export async function apiDelete(path: string, token: string): Promise { const response = await fetch(`${API_BASE}${path}`, { method: "DELETE", credentials: "include" }); if (!response.ok) { throw new Error(await readErrorMessage(response, `API request failed: ${response.status}`)); } } export async function apiFetch(path: string, token: string, init?: RequestInit): Promise { const response = await fetch(`${API_BASE}${path}`, { ...init, headers: { "Content-Type": "application/json", ...(init?.headers ?? {}) }, credentials: "include" }); if (!response.ok) { throw new Error(await readErrorMessage(response, `API request failed: ${response.status}`)); } return response.json() as Promise; }