/** Browser client for Next.js API routes */ type ApiResponse = { success: true; data: T } | { success: false; error: string }; export async function apiFetch( path: string, options?: RequestInit ): Promise { const res = await fetch(path, { ...options, headers: { "Content-Type": "application/json", ...options?.headers, }, credentials: "same-origin", }); let json: ApiResponse; try { json = await res.json(); } catch { throw new Error(`API ${path} returned invalid JSON (${res.status})`); } if (!json.success) { throw new Error(json.error || `Request failed (${res.status})`); } return json.data; } export const api = { health: () => apiFetch<{ status: string; supabase: boolean }>("/api/health"), leagues: { list: () => apiFetch("/api/leagues"), create: (body: { name: string; description?: string }) => apiFetch("/api/leagues", { method: "POST", body: JSON.stringify(body), }), get: (leagueId: string) => apiFetch(`/api/leagues/${leagueId}`), createCompetition: ( leagueId: string, body: { name: string; tournament_mode: "league" | "cup"; timezone?: string } ) => apiFetch(`/api/leagues/${leagueId}/competitions`, { method: "POST", body: JSON.stringify(body), }), saveRules: (leagueId: string, rules: object) => apiFetch(`/api/leagues/${leagueId}/rules`, { method: "POST", body: JSON.stringify({ rules }), }), delete: (leagueId: string) => apiFetch(`/api/leagues/${leagueId}`, { method: "DELETE" }), assignMaster: (leagueId: string, email: string) => apiFetch(`/api/leagues/${leagueId}/masters`, { method: "POST", body: JSON.stringify({ email }), }), listMasters: (leagueId: string) => apiFetch(`/api/leagues/${leagueId}/masters`), }, competitions: { get: (id: string) => apiFetch(`/api/competitions/${id}`), activate: (id: string) => apiFetch(`/api/competitions/${id}/activate`, { method: "POST" }), generateFixtures: (id: string) => apiFetch(`/api/competitions/${id}/fixtures`, { method: "POST" }), standings: (id: string) => apiFetch(`/api/competitions/${id}/standings`), matches: (id: string) => apiFetch(`/api/competitions/${id}/matches`), pendingResults: (id: string) => apiFetch(`/api/competitions/${id}/pending-results`), transfers: (id: string) => apiFetch(`/api/competitions/${id}/transfers`), dashboard: (id: string) => apiFetch<{ results: unknown[]; playerStats: unknown[] }>( `/api/competitions/${id}/dashboard` ), listTeams: (id: string) => apiFetch(`/api/competitions/${id}/teams`), createTeam: ( id: string, body: { name: string; nickname?: string; icon?: string } ) => apiFetch(`/api/competitions/${id}/teams`, { method: "POST", body: JSON.stringify(body), }), addRoster: ( id: string, body: { teamId: string; playerId: string } ) => apiFetch(`/api/competitions/${id}/roster`, { method: "POST", body: JSON.stringify(body), }), transfer: ( id: string, body: { playerId: string; fromTeamId: string; toTeamId: string } ) => apiFetch(`/api/competitions/${id}/transfers`, { method: "POST", body: JSON.stringify(body), }), }, teams: { delete: (teamId: string) => apiFetch(`/api/teams/${teamId}`, { method: "DELETE" }), update: (teamId: string, body: { home_stadium_name?: string; logo_path?: string; nickname?: string; icon?: string }) => apiFetch(`/api/teams/${teamId}`, { method: "PATCH", body: JSON.stringify(body), }), setAvailability: ( teamId: string, windows: { day_of_week: number; start_time?: string; end_time?: string }[] ) => apiFetch(`/api/teams/${teamId}/availability`, { method: "PUT", body: JSON.stringify({ windows }), }), }, players: { list: () => apiFetch("/api/players"), create: (body: { display_name: string; external_id?: string }) => apiFetch("/api/players", { method: "POST", body: JSON.stringify(body), }), setStatus: (playerId: string, status: "active" | "inactive") => apiFetch(`/api/players/${playerId}`, { method: "PATCH", body: JSON.stringify({ status }), }), }, matches: { get: (matchId: string) => apiFetch(`/api/matches/${matchId}`), proposeSchedule: (matchId: string, scheduledAt: string) => apiFetch(`/api/matches/${matchId}/schedule`, { method: "POST", body: JSON.stringify({ action: "propose", scheduledAt }), }), signSchedule: (matchId: string, teamId: string) => apiFetch(`/api/matches/${matchId}/schedule`, { method: "POST", body: JSON.stringify({ action: "sign", teamId }), }), submitResult: ( matchId: string, body: { teamId: string; homeScore: number; awayScore: number } ) => apiFetch(`/api/matches/${matchId}/results`, { method: "POST", body: JSON.stringify({ action: "submit", ...body }), }), approveResult: (matchId: string) => apiFetch(`/api/matches/${matchId}/results`, { method: "POST", body: JSON.stringify({ action: "approve" }), }), setResult: ( matchId: string, body: { homeScore: number; awayScore: number; note?: string } ) => apiFetch(`/api/matches/${matchId}/results`, { method: "POST", body: JSON.stringify({ action: "set", ...body }), }), }, manager: { dashboard: () => apiFetch("/api/manager/dashboard"), competitions: (mode?: "league" | "cup") => apiFetch( `/api/manager/competitions${mode ? `?mode=${mode}` : ""}` ), calendar: (from: string, to: string) => apiFetch( `/api/manager/calendar?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}` ), }, master: { calendar: (from: string, to: string) => apiFetch( `/api/master/calendar?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}` ), }, issues: { list: (asMaster?: boolean) => apiFetch(`/api/issues${asMaster ? "?as=master" : ""}`), create: (body: { leagueId: string; competitionId?: string; subject: string; body: string; }) => apiFetch("/api/issues", { method: "POST", body: JSON.stringify(body), }), }, };