'use client'; import { useState, useCallback } from 'react'; import { toast } from 'react-hot-toast'; const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000/api'; /** * Get authentication token from localStorage */ export const getAuthToken = (): string | null => { if (typeof window === 'undefined') return null; return localStorage.getItem('auth_token') || localStorage.getItem('token'); }; /** * Generic fetch wrapper with authentication */ export const apiFetch = async ( endpoint: string, options?: RequestInit ): Promise => { const token = getAuthToken(); const url = endpoint.startsWith('http') ? endpoint : `${API_BASE_URL}${endpoint}`; const response = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }), ...options?.headers, }, }); if (!response.ok) { const error = await response.json().catch(() => ({ message: 'Erreur serveur' })); throw new Error(error.message || `Erreur ${response.status}`); } return response.json(); }; interface UseApiState { data: T | null; loading: boolean; error: string | null; } interface UseApiReturn extends UseApiState { execute: (endpoint: string, options?: RequestInit) => Promise; reset: () => void; } /** * Hook for API calls with loading and error states */ export function useApi(): UseApiReturn { const [state, setState] = useState>({ data: null, loading: false, error: null, }); const execute = useCallback(async (endpoint: string, options?: RequestInit): Promise => { setState(prev => ({ ...prev, loading: true, error: null })); try { const data = await apiFetch(endpoint, options); setState({ data, loading: false, error: null }); return data; } catch (err: unknown) { const message = err instanceof Error ? err.message : 'Une erreur est survenue'; setState(prev => ({ ...prev, loading: false, error: message })); toast.error(message); return null; } }, []); const reset = useCallback(() => { setState({ data: null, loading: false, error: null }); }, []); return { ...state, execute, reset }; } interface UseFetchDataOptions { onSuccess?: (data: unknown) => void; onError?: (error: string) => void; showErrorToast?: boolean; } interface UseFetchDataReturn { data: T | null; loading: boolean; error: string | null; refetch: () => Promise; } /** * Hook for fetching data on mount with auto-refresh support */ export function useFetchData( endpoint: string, options?: UseFetchDataOptions ): UseFetchDataReturn { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchData = useCallback(async () => { setLoading(true); setError(null); try { const result = await apiFetch<{ data: T } | T>(endpoint); const responseData = (result as { data: T }).data ?? result as T; setData(responseData); options?.onSuccess?.(responseData); } catch (err: unknown) { const message = err instanceof Error ? err.message : 'Une erreur est survenue'; setError(message); options?.onError?.(message); if (options?.showErrorToast !== false) { toast.error(message); } } finally { setLoading(false); } }, [endpoint, options]); return { data, loading, error, refetch: fetchData }; } export default useApi;