import type { AssignmentCreate, AssignmentUpdate, AssignmentValidateRequest, DayPartCreate, DayPartUpdate, EditionUpdate, LoginRequest, MagicLinkRequest, PlanningNotificationRequest, PositionCreate, PositionUpdate, ShiftCreate, ShiftUpdate, VerifyRequest, VolunteerCreate, VolunteerUpdate, } from './schema'; import { getAuthToken, notifyUnauthorized } from './session'; import { ApiError, type PlanningApi } from './types'; // Implémentation HTTP réelle. En dev, Vite proxifie `/api` vers le back-end. export function createHttpApi(baseUrl: string): PlanningApi { async function request(path: string, init?: RequestInit): Promise { const token = getAuthToken(); const res = await fetch(`${baseUrl}${path}`, { ...init, headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), ...init?.headers, }, credentials: 'include', }); if (res.status === 204) return undefined as T; const isJson = res.headers.get('content-type')?.includes('application/json'); const body: unknown = isJson ? await res.json() : await res.text(); if (!res.ok) { const detail = body && typeof body === 'object' && 'detail' in body ? (body as { detail: unknown }).detail : body; const code = detail && typeof detail === 'object' && 'code' in detail ? String((detail as { code: unknown }).code) : 'error.generic'; const violations = detail && typeof detail === 'object' && 'violations' in detail ? ((detail as { violations: [] }).violations ?? []) : []; if (res.status === 401 && !path.includes('/auth/')) notifyUnauthorized(); throw new ApiError(res.status, code, violations); } return body as T; } const query = (params: Record) => { const search = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value != null) search.set(key, value); } const str = search.toString(); return str ? `?${str}` : ''; }; const base = (editionId: string) => `/editions/${editionId}`; return { editions: { get: (editionId) => request(base(editionId)), update: (editionId, body: EditionUpdate) => request(base(editionId), { method: 'PATCH', body: JSON.stringify(body) }), }, dayParts: { list: (editionId) => request(`${base(editionId)}/day-parts`), create: (editionId, body: DayPartCreate) => request(`${base(editionId)}/day-parts`, { method: 'POST', body: JSON.stringify(body) }), update: (editionId, id, body: DayPartUpdate) => request(`${base(editionId)}/day-parts/${id}`, { method: 'PATCH', body: JSON.stringify(body) }), remove: (editionId, id) => request(`${base(editionId)}/day-parts/${id}`, { method: 'DELETE' }), }, volunteers: { list: (editionId) => request(`${base(editionId)}/volunteers`), get: (editionId, id) => request(`${base(editionId)}/volunteers/${id}`), create: (editionId, body: VolunteerCreate) => request(`${base(editionId)}/volunteers`, { method: 'POST', body: JSON.stringify(body) }), update: (editionId, id, body: VolunteerUpdate) => request(`${base(editionId)}/volunteers/${id}`, { method: 'PATCH', body: JSON.stringify(body) }), remove: (editionId, id) => request(`${base(editionId)}/volunteers/${id}`, { method: 'DELETE' }), }, positions: { list: (editionId) => request(`${base(editionId)}/positions`), get: (editionId, id) => request(`${base(editionId)}/positions/${id}`), create: (editionId, body: PositionCreate) => request(`${base(editionId)}/positions`, { method: 'POST', body: JSON.stringify(body) }), update: (editionId, id, body: PositionUpdate) => request(`${base(editionId)}/positions/${id}`, { method: 'PATCH', body: JSON.stringify(body) }), remove: (editionId, id) => request(`${base(editionId)}/positions/${id}`, { method: 'DELETE' }), generateShifts: (editionId, id) => request(`${base(editionId)}/positions/${id}/generate-shifts`, { method: 'POST' }), }, shifts: { list: (editionId, params) => request(`${base(editionId)}/shifts${query({ position_id: params?.positionId })}`), get: (editionId, id) => request(`${base(editionId)}/shifts/${id}`), create: (editionId, body: ShiftCreate) => request(`${base(editionId)}/shifts`, { method: 'POST', body: JSON.stringify(body) }), update: (editionId, id, body: ShiftUpdate) => request(`${base(editionId)}/shifts/${id}`, { method: 'PATCH', body: JSON.stringify(body) }), remove: (editionId, id) => request(`${base(editionId)}/shifts/${id}`, { method: 'DELETE' }), }, assignments: { list: (editionId, params) => request( `${base(editionId)}/assignments${query({ volunteer_id: params?.volunteerId, shift_id: params?.shiftId, })}`, ), get: (editionId, id) => request(`${base(editionId)}/assignments/${id}`), create: (editionId, body: AssignmentCreate) => request(`${base(editionId)}/assignments`, { method: 'POST', body: JSON.stringify(body) }), update: (editionId, id, body: AssignmentUpdate) => request(`${base(editionId)}/assignments/${id}`, { method: 'PATCH', body: JSON.stringify(body) }), remove: (editionId, id) => request(`${base(editionId)}/assignments/${id}`, { method: 'DELETE' }), validate: (editionId, body: AssignmentValidateRequest) => request(`${base(editionId)}/assignments/validate`, { method: 'POST', body: JSON.stringify(body), }), }, matrix: { get: (editionId) => request(`${base(editionId)}/matrix`), }, notifications: { sendPlanning: (editionId, body: PlanningNotificationRequest) => request(`${base(editionId)}/notifications/planning`, { method: 'POST', body: JSON.stringify(body), }), }, auth: { login: (editionId, body: LoginRequest) => request(`${base(editionId)}/auth/login`, { method: 'POST', body: JSON.stringify(body) }), requestMagicLink: (editionId, body: MagicLinkRequest) => request(`${base(editionId)}/auth/magic-link`, { method: 'POST', body: JSON.stringify(body) }), verifyMagicLink: (editionId, body: VerifyRequest) => request(`${base(editionId)}/auth/verify`, { method: 'POST', body: JSON.stringify(body) }), me: () => request(`/auth/me`), }, }; }