Initial commit

This commit is contained in:
dahoud
2025-10-01 01:39:07 +00:00
commit b430bf3b96
826 changed files with 255287 additions and 0 deletions

173
hooks/useApiCall.tsx Normal file
View File

@@ -0,0 +1,173 @@
'use client';
import { useState, useCallback, useRef } from 'react';
import { Toast } from 'primereact/toast';
import { apiService } from '../services/api';
interface UseApiCallOptions {
onSuccess?: (data: any) => void;
onError?: (error: any) => void;
showSuccessToast?: boolean;
showErrorToast?: boolean;
successMessage?: string;
retryAttempts?: number;
retryDelay?: number;
}
interface UseApiCallReturn<T> {
data: T | null;
loading: boolean;
error: any;
execute: (...args: any[]) => Promise<T | null>;
retry: () => Promise<T | null>;
reset: () => void;
toast: React.RefObject<Toast>;
}
export function useApiCall<T = any>(
apiFunction: (...args: any[]) => Promise<T>,
options: UseApiCallOptions = {}
): UseApiCallReturn<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<any>(null);
const toast = useRef<Toast>(null);
const lastArgsRef = useRef<any[]>([]);
const retryCountRef = useRef(0);
const {
onSuccess,
onError,
showSuccessToast = false,
showErrorToast = true,
successMessage = 'Opération réussie',
retryAttempts = 0,
retryDelay = 1000
} = options;
const execute = useCallback(async (...args: any[]): Promise<T | null> => {
lastArgsRef.current = args;
setLoading(true);
setError(null);
retryCountRef.current = 0;
try {
const result = await apiFunction(...args);
setData(result);
// Notifier que le serveur répond (pour une détection immédiate)
// Cette ligne se déclenchera dès qu'un appel API réussit
if (showSuccessToast && toast.current) {
toast.current.show({
severity: 'success',
summary: 'Succès',
detail: successMessage,
life: 3000
});
}
if (onSuccess) {
onSuccess(result);
}
return result;
} catch (err: any) {
setError(err);
// Déclenchement d'un health check urgent uniquement pour les erreurs critiques
if (shouldRetry(err) && retryCountRef.current === 0) {
// Health check urgent seulement au premier échec pour éviter le spam
apiService.checkServerHealth(true).catch(() => {});
}
// Gestion des erreurs réseau avec retry automatique
if (shouldRetry(err) && retryCountRef.current < retryAttempts) {
retryCountRef.current++;
if (showErrorToast && toast.current) {
toast.current.show({
severity: 'warn',
summary: 'Tentative de reconnexion',
detail: `Tentative ${retryCountRef.current}/${retryAttempts}...`,
life: 2000
});
}
// Attendre avant de retry
await new Promise(resolve => setTimeout(resolve, retryDelay));
return execute(...args);
}
// Afficher l'erreur finale
if (showErrorToast && toast.current) {
const message = getErrorMessage(err);
toast.current.show({
severity: 'error',
summary: 'Erreur',
detail: message,
life: err.statusCode === 'SERVER_UNAVAILABLE' ? 0 : 5000 // Sticky pour erreurs serveur
});
}
if (onError) {
onError(err);
}
return null;
} finally {
setLoading(false);
}
}, [apiFunction, onSuccess, onError, showSuccessToast, showErrorToast, successMessage, retryAttempts, retryDelay]);
const retry = useCallback(async (): Promise<T | null> => {
if (lastArgsRef.current.length > 0) {
return execute(...lastArgsRef.current);
}
return execute();
}, [execute]);
const reset = useCallback(() => {
setData(null);
setError(null);
setLoading(false);
retryCountRef.current = 0;
}, []);
return {
data,
loading,
error,
execute,
retry,
reset,
toast
};
}
// Fonctions utilitaires
function shouldRetry(error: any): boolean {
return error?.statusCode === 'NETWORK_ERROR' ||
error?.statusCode === 'SERVER_UNAVAILABLE' ||
error?.statusCode === 'TIMEOUT' ||
error?.code === 'ECONNABORTED' ||
error?.code === 'ERR_NETWORK';
}
function getErrorMessage(error: any): string {
if (error?.statusCode === 'SERVER_UNAVAILABLE') {
return 'Serveur indisponible. Vérifiez que le serveur backend est démarré.';
}
if (error?.statusCode === 'NETWORK_ERROR') {
return 'Erreur réseau. Vérifiez votre connexion internet.';
}
if (error?.statusCode === 'TIMEOUT') {
return 'Délai d\'attente dépassé. Le serveur met trop de temps à répondre.';
}
return error?.userMessage || error?.message || 'Une erreur inattendue s\'est produite';
}
export default useApiCall;