'use client'; import { toast } from "@/components/ui/use-toast"; import { normalizeApiUrl, getAuthToken, createApiHeaders } from "./api-utils"; type FetchMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; interface FetchOptions { method?: FetchMethod; body?: any; cache?: RequestCache; headers?: HeadersInit; } /** * Client-side fetch utility that ensures all requests go through the Next.js API proxy */ export async function fetchClient( endpoint: string, options: FetchOptions = {} ): Promise { const { method = 'GET', body, headers = {}, ...rest } = options; // Normalize the endpoint to ensure it starts with /api/ const url = normalizeApiUrl(endpoint); // Get auth token and prepare headers const requestHeaders = createApiHeaders(null, headers as Record); // Log request details (useful for debugging) console.log('API Request:', { url, method, hasAuthToken: requestHeaders.has('Authorization') }); const fetchOptions: RequestInit = { method, credentials: 'include', headers: requestHeaders, ...rest, }; if (body && method !== 'GET') { fetchOptions.body = JSON.stringify(body); } try { const response = await fetch(url, fetchOptions); if (!response.ok) { const errorData = await response.json().catch(() => ({})); const errorMessage = errorData.message || errorData.error || `Request failed with status ${response.status}`; throw new Error(errorMessage); } // Handle 204 No Content responses if (response.status === 204) { return {} as T; } return await response.json(); } catch (error) { console.error('API request failed:', error); // Only show toast in browser environment if (typeof window !== 'undefined') { const message = error instanceof Error ? error.message : 'Failed to connect to server'; toast({ title: 'Error', description: message, variant: 'destructive', }); } throw error; } }