// Generic API client with authentication import type { AuthUser } from '@/auth/types'; import { ApiError } from '@/types'; let onUnauthorized: (() => void) | null = null; export function setOnUnauthorized(handler: () => void): void { onUnauthorized = handler; } export function fireUnauthorized(): void { if (onUnauthorized) { onUnauthorized(); } } export interface RequestOptions { method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; params?: Record; body?: unknown; } /** * Build query string from parameters object */ function buildQueryString(params: Record): string { const queryString = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value !== undefined && value !== null && value !== '') { if (value instanceof Date) { queryString.append(key, value.toISOString()); } else { queryString.append(key, String(value)); } } } return queryString.toString(); } /** * Generic authenticated API request */ export async function apiRequest( user: AuthUser, endpoint: string, options: RequestOptions = {} ): Promise { const { method = 'GET', params, body } = options; let url = endpoint; if (params) { const queryString = buildQueryString(params); if (queryString) { url = `${endpoint}?${queryString}`; } } const fetchOptions: RequestInit = { method, headers: { Authorization: `Bearer ${user.accessToken}`, 'Content-Type': 'application/json', }, }; if (body !== undefined) { fetchOptions.body = JSON.stringify(body); } const response = await fetch(url, fetchOptions); if (!response.ok) { if (response.status === 401) { fireUnauthorized(); } throw new ApiError(`Error: ${response.status}`, response.status); } return response.json() as Promise; }