export type ApiResponse = { data: T; success: boolean; error?: string; }; export type RequestOptions = { params?: Record; headers?: Record; }; const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://calendi.test'; export class ApiError extends Error { status: number; constructor(message: string, status: number) { super(message); this.status = status; this.name = 'ApiError'; } } export async function get(endpoint: string, options?: RequestOptions): Promise { const url = buildUrl(endpoint, options?.params); const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', ...options?.headers } }); return handleResponse(response); } export async function post(endpoint: string, data: unknown, options?: RequestOptions): Promise { const url = buildUrl(endpoint, options?.params); const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, body: JSON.stringify(data) }); return handleResponse(response); } export async function put(endpoint: string, data: unknown, options?: RequestOptions): Promise { const url = buildUrl(endpoint, options?.params); const response = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, body: JSON.stringify(data) }); return handleResponse(response); } export async function del(endpoint: string, options?: RequestOptions): Promise { const url = buildUrl(endpoint, options?.params); const response = await fetch(url, { method: 'DELETE', headers: { 'Content-Type': 'application/json', ...options?.headers } }); return handleResponse(response); } function buildUrl(endpoint: string, params?: Record): string { const url = new URL(`${API_BASE_URL}${endpoint}`); url.port = window.location.port; if (params) { Object.entries(params).forEach(([key, value]) => { url.searchParams.append(key, String(value)); }); } return url.toString(); } async function handleResponse(response: Response): Promise { if (!response.ok) { const errorText = await response.text(); throw new ApiError(errorText || `Request failed with status ${response.status}`, response.status); } try { return await response.json() as T; } catch (error) { throw new ApiError('Failed to parse response', 500); } }