import { useState, useCallback } from 'react'; import { useAppStore } from '../store/appStore'; import { fetchExecutions, fetchExecutionById, deleteExecution, Execution, ExecutionStatus, FetchExecutionsParams, } from '../api/executions'; import { usePolling } from './usePolling'; /** Options de filtrage passées au hook à l'initialisation */ interface UseExecutionsOptions { /** Nombre max d'exécutions à charger */ limit?: number; /** Filtre de statut initial */ statusFilter?: ExecutionStatus; /** Restreindre aux exécutions d'un workflow spécifique */ workflowId?: string; } /** Interface des valeurs et actions exposées par le hook */ interface UseExecutionsResult { executions: Execution[]; isLoading: boolean; isRefreshing: boolean; /** Exécution chargée en détail (avec logs de nœuds) */ selectedExecution: Execution | null; /** Recharge la liste (pull-to-refresh) */ refresh: () => Promise; /** * Charge les logs complets d'une exécution pour l'écran de détail. * Peuple selectedExecution. * * @param id - Identifiant de l'exécution */ loadExecutionDetail: (id: string) => Promise; /** * Supprime une exécution de l'historique et la retire de l'état local. * * @param id - Identifiant de l'exécution à supprimer */ removeExecution: (id: string) => Promise; /** * Change le filtre de statut actif et relance le chargement. * * @param status - Statut cible ou undefined pour tout afficher */ setStatusFilter: (status: ExecutionStatus | undefined) => void; } /** * Hook de gestion des exécutions : listing avec filtres, polling, détail et suppression. * Sépare clairement la liste (vue Executions) du détail (vue Logs). */ export const useExecutions = (options: UseExecutionsOptions = {}): UseExecutionsResult => { const { preferences, config } = useAppStore(); const [executions, setExecutions] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); const [selectedExecution, setSelectedExecution] = useState(null); const [statusFilter, setStatusFilter] = useState( options.statusFilter ); /** Charge la liste avec les filtres courants */ const loadExecutions = useCallback(async (): Promise => { const params: FetchExecutionsParams = { limit: options.limit ?? 50, ...(statusFilter !== undefined && { status: statusFilter }), ...(options.workflowId && { workflowId: options.workflowId }), }; try { const result = await fetchExecutions(params); setExecutions(result.data); } catch { // Géré par l'intercepteur Axios } finally { setIsLoading(false); } }, [options.limit, options.workflowId, statusFilter]); /** Polling automatique — désactivé si l'app n'est pas encore configurée */ usePolling({ callback: loadExecutions, interval: preferences.pollingInterval, enabled: config.isConfigured, immediate: true, }); const refresh = useCallback(async (): Promise => { setIsRefreshing(true); try { await loadExecutions(); } finally { setIsRefreshing(false); } }, [loadExecutions]); /** * Charge les logs détaillés d'une exécution. * Appelle GET /executions/:id qui inclut le champ `data` avec les nœuds. */ const loadExecutionDetail = useCallback(async (id: string): Promise => { try { const detail = await fetchExecutionById(id); setSelectedExecution(detail); } catch { // Géré par l'intercepteur Axios } }, []); /** * Supprime une exécution côté serveur et la retire optimistiquement de l'état local. */ const removeExecution = useCallback(async (id: string): Promise => { try { await deleteExecution(id); setExecutions((prev) => prev.filter((e) => e.id !== id)); } catch { // Géré par l'intercepteur Axios } }, []); return { executions, isLoading, isRefreshing, selectedExecution, refresh, loadExecutionDetail, removeExecution, setStatusFilter, }; };