Files
n8n-mobile/src/hooks/useExecutions.ts
T
billisdead 92e67d0769 feat: application n8n Pilot complète (Expo managed workflow)
- Stack : Expo Router, Axios, Zustand, React Native Paper (thème sombre), date-fns
- Sécurité : secrets dans Android Keystore via expo-secure-store, TLS obligatoire,
  headers X-N8N-API-KEY + X-App-Token injectés par intercepteur Axios
- API : client.ts centralisé + workflows.ts + executions.ts (TypeScript strict)
- Store : Zustand appStore avec chargement depuis secure store au démarrage
- Hooks : usePolling (générique), useWorkflows, useExecutions
- Composants : StatusBadge, WorkflowCard, ExecutionCard, SkeletonLoader
- Screens : Dashboard, Workflows, Executions, Logs (détail exécution), Settings
- Navigation Expo Router : 4 tabs + stack Logs + écran Setup initial
- Docs : INSTALL.md, UPDATE.md, BACKUP.md, HAPROXY.md, SECURITY.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 17:31:55 +02:00

138 lines
4.1 KiB
TypeScript

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<void>;
/**
* 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<void>;
/**
* 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<void>;
/**
* 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<Execution[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isRefreshing, setIsRefreshing] = useState(false);
const [selectedExecution, setSelectedExecution] = useState<Execution | null>(null);
const [statusFilter, setStatusFilter] = useState<ExecutionStatus | undefined>(
options.statusFilter
);
/** Charge la liste avec les filtres courants */
const loadExecutions = useCallback(async (): Promise<void> => {
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<void> => {
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<void> => {
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<void> => {
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,
};
};