Files
btpxpress-frontend/hooks/useChantierActions.ts

299 lines
12 KiB
TypeScript
Executable File

/**
* Hook personnalisé pour gérer les actions sur les chantiers
* Centralise la logique métier et facilite la réutilisation
*/
import { useCallback } from 'react';
import { useRouter } from 'next/navigation';
import { Toast } from 'primereact/toast';
import { confirmDialog } from 'primereact/confirmdialog';
import { chantierActionsService } from '../services/chantierActionsService';
import { ChantierActif } from './useDashboard';
interface UseChantierActionsProps {
toast?: React.RefObject<Toast>;
onRefresh?: () => void;
}
interface UseChantierActionsReturn {
handleQuickView: (chantier: ChantierActif) => void;
handleViewStats: (chantier: ChantierActif) => Promise<void>;
handleGenerateReport: (chantier: ChantierActif) => Promise<void>;
handleExport: (chantier: ChantierActif, format: 'pdf' | 'excel') => Promise<void>;
handleToggleSuspend: (chantier: ChantierActif) => void;
handleClose: (chantier: ChantierActif) => void;
handleArchive: (chantier: ChantierActif) => void;
handleMenuAction: (action: string, chantier: ChantierActif) => Promise<void>;
// Nouvelles actions prioritaires BTP
handleSuspendChantier: (chantier: ChantierActif) => void;
handleCloseChantier: (chantier: ChantierActif) => void;
handleNotifyClient: (chantier: ChantierActif) => Promise<void>;
handleGenerateInvoice: (chantier: ChantierActif) => Promise<void>;
handleCreateAmendment: (chantier: ChantierActif) => Promise<void>;
}
export const useChantierActions = ({
toast,
onRefresh
}: UseChantierActionsProps = {}): UseChantierActionsReturn => {
const router = useRouter();
const showToast = useCallback((severity: 'success' | 'info' | 'warn' | 'error', summary: string, detail: string, life = 3000) => {
toast?.current?.show({ severity, summary, detail, life });
}, [toast]);
const handleQuickView = useCallback(async (chantier: ChantierActif) => {
try {
// Pour la vue rapide, on utilise les données déjà disponibles
// Si nécessaire, on peut récupérer des détails depuis l'endpoint /chantiers/{id}
// Mais pour l'instant, on utilise ce qu'on a déjà
console.log('Vue rapide du chantier:', chantier);
// Calculer des stats basiques depuis les données existantes
const joursEcoules = chantier.dateDebut ?
Math.floor((new Date().getTime() - new Date(chantier.dateDebut).getTime()) / (1000 * 60 * 60 * 24)) : 0;
const joursRestants = chantier.dateFinPrevue ?
Math.floor((new Date(chantier.dateFinPrevue).getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24)) : 0;
const tauxDepense = chantier.budget > 0 ?
Math.round((chantier.coutReel / chantier.budget) * 100) : 0;
// Les données sont déjà passées au composant Dialog, pas besoin d'appel API
showToast('info', 'Vue rapide', `Affichage des détails de ${chantier.nom}`);
} catch (error) {
console.error('Erreur lors de l\'affichage de la vue rapide:', error);
showToast('warn', 'Avertissement', 'Détails limités disponibles');
}
}, [showToast]);
const handleViewStats = useCallback(async (chantier: ChantierActif) => {
showToast('info', 'Chargement', 'Chargement des statistiques...', 2000);
try {
const stats = await chantierActionsService.getChantierStats(chantier.id);
router.push(`/chantiers/${chantier.id}#stats`);
} catch (error) {
showToast('error', 'Erreur', 'Impossible de charger les statistiques');
}
}, [router, showToast]);
const handleGenerateReport = useCallback(async (chantier: ChantierActif) => {
showToast('info', 'Génération en cours', `Génération du rapport pour ${chantier.nom}...`);
const result = await chantierActionsService.generateReport(chantier.id);
if (result.success) {
showToast('success', 'Rapport généré', 'Le rapport a été téléchargé avec succès');
} else {
showToast('error', 'Erreur', result.message, 5000);
}
}, [showToast]);
const handleExport = useCallback(async (chantier: ChantierActif, format: 'pdf' | 'excel') => {
showToast('info', 'Export en cours', `Export ${format.toUpperCase()} pour ${chantier.nom}...`);
const result = await chantierActionsService.exportChantier(chantier.id, format);
if (result.success) {
showToast('success', 'Export réussi', `Le fichier ${format.toUpperCase()} a été téléchargé`);
} else {
showToast('error', 'Erreur d\'export', result.message, 5000);
}
}, [showToast]);
const handleToggleSuspend = useCallback((chantier: ChantierActif) => {
const isSuspended = chantier.statut === 'SUSPENDU';
const action = isSuspended ? 'reprendre' : 'suspendre';
confirmDialog({
message: `Êtes-vous sûr de vouloir ${action} le chantier "${chantier.nom}" ?`,
header: 'Confirmation',
icon: 'pi pi-exclamation-triangle',
acceptLabel: 'Oui',
rejectLabel: 'Non',
accept: async () => {
const result = await chantierActionsService.toggleSuspend(chantier.id, !isSuspended);
if (result.success) {
showToast('success', 'Succès', result.message);
onRefresh?.();
} else {
showToast('error', 'Erreur', result.message, 5000);
}
}
});
}, [showToast, onRefresh]);
const handleClose = useCallback((chantier: ChantierActif) => {
confirmDialog({
message: `Êtes-vous sûr de vouloir clôturer le chantier "${chantier.nom}" ?\nCette action est irréversible.`,
header: 'Clôturer le chantier',
icon: 'pi pi-info-circle',
acceptClassName: 'p-button-success',
acceptLabel: 'Clôturer',
rejectLabel: 'Annuler',
accept: async () => {
const result = await chantierActionsService.closeChantier(chantier.id);
if (result.success) {
showToast('success', 'Chantier clôturé', result.message);
onRefresh?.();
} else {
showToast('error', 'Erreur', result.message, 5000);
}
}
});
}, [showToast, onRefresh]);
const handleArchive = useCallback((chantier: ChantierActif) => {
confirmDialog({
message: `Voulez-vous archiver le chantier "${chantier.nom}" ?\nIl sera déplacé dans les archives.`,
header: 'Archiver le chantier',
icon: 'pi pi-inbox',
acceptLabel: 'Archiver',
rejectLabel: 'Annuler',
accept: async () => {
const result = await chantierActionsService.archiveChantier(chantier.id);
if (result.success) {
showToast('info', 'Chantier archivé', result.message);
onRefresh?.();
} else {
showToast('error', 'Erreur', result.message, 5000);
}
}
});
}, [showToast, onRefresh]);
const handleMenuAction = useCallback(async (action: string, chantier: ChantierActif) => {
switch (action) {
case 'details':
router.push(`/chantiers/${chantier.id}`);
break;
case 'documents':
router.push(`/documents?chantier=${chantier.id}`);
break;
case 'photos':
router.push(`/photos/par-chantier?id=${chantier.id}`);
break;
case 'team':
router.push(`/equipes?chantier=${chantier.id}`);
break;
case 'equipment':
router.push(`/materiels?chantier=${chantier.id}`);
break;
case 'report':
await handleGenerateReport(chantier);
break;
case 'export-pdf':
await handleExport(chantier, 'pdf');
break;
case 'export-excel':
await handleExport(chantier, 'excel');
break;
case 'toggle-suspend':
handleToggleSuspend(chantier);
break;
case 'close':
handleClose(chantier);
break;
case 'archive':
handleArchive(chantier);
break;
default:
console.warn('Action inconnue:', action);
}
}, [router, handleGenerateReport, handleExport, handleToggleSuspend, handleClose, handleArchive]);
// Actions prioritaires BTP
const handleSuspendChantier = useCallback((chantier: ChantierActif) => {
confirmDialog({
message: `Suspendre temporairement le chantier "${chantier.nom}" ?\nLes équipes seront notifiées.`,
header: 'Suspendre le chantier',
icon: 'pi pi-pause-circle',
acceptClassName: 'p-button-warning p-button-text p-button-rounded',
acceptLabel: 'Suspendre',
rejectLabel: 'Annuler',
accept: async () => {
try {
const result = await chantierActionsService.suspendChantier(chantier.id);
showToast('warn', 'Chantier suspendu', `${chantier.nom} a été suspendu temporairement`);
onRefresh?.();
} catch (error) {
showToast('error', 'Erreur', 'Impossible de suspendre le chantier');
}
}
});
}, [showToast, onRefresh]);
const handleCloseChantier = useCallback((chantier: ChantierActif) => {
confirmDialog({
message: `Clôturer définitivement le chantier "${chantier.nom}" ?\nUn rapport final sera généré.`,
header: 'Clôturer le chantier',
icon: 'pi pi-check-circle',
acceptClassName: 'p-button-success p-button-text p-button-rounded',
acceptLabel: 'Clôturer',
rejectLabel: 'Annuler',
accept: async () => {
try {
const result = await chantierActionsService.closeChantierDefinitively(chantier.id);
showToast('success', 'Chantier clôturé', `${chantier.nom} a été clôturé avec succès`);
onRefresh?.();
} catch (error) {
showToast('error', 'Erreur', 'Impossible de clôturer le chantier');
}
}
});
}, [showToast, onRefresh]);
const handleNotifyClient = useCallback(async (chantier: ChantierActif) => {
try {
showToast('info', 'Envoi en cours...', 'Préparation de la notification client');
const result = await chantierActionsService.notifyClient(chantier.id);
showToast('success', 'Notification envoyée', `Le client a été informé de l'avancement du chantier ${chantier.nom}`);
} catch (error) {
showToast('error', 'Erreur', 'Impossible d\'envoyer la notification au client');
}
}, [showToast]);
const handleGenerateInvoice = useCallback(async (chantier: ChantierActif) => {
try {
showToast('info', 'Génération...', 'Préparation de la facture intermédiaire');
const result = await chantierActionsService.generateIntermediateInvoice(chantier.id);
showToast('success', 'Facture générée', `Facture intermédiaire créée pour ${chantier.nom}`);
// Ouvrir la facture dans un nouvel onglet si URL fournie
if (result.pdfUrl) {
window.open(result.pdfUrl, '_blank');
}
} catch (error) {
showToast('error', 'Erreur', 'Impossible de générer la facture intermédiaire');
}
}, [showToast]);
const handleCreateAmendment = useCallback(async (chantier: ChantierActif) => {
try {
showToast('info', 'Création avenant...', 'Préparation du document d\'avenant');
const result = await chantierActionsService.createAmendment(chantier.id);
showToast('success', 'Avenant créé', `Avenant budgétaire créé pour ${chantier.nom}`);
// Rediriger vers la page d'édition de l'avenant
router.push(`/chantiers/${chantier.id}/avenant/${result.amendmentId}`);
} catch (error) {
showToast('error', 'Erreur', 'Impossible de créer l\'avenant');
}
}, [showToast, router]);
return {
handleQuickView,
handleViewStats,
handleGenerateReport,
handleExport,
handleToggleSuspend,
handleClose,
handleArchive,
handleMenuAction,
// Nouvelles actions prioritaires
handleSuspendChantier,
handleCloseChantier,
handleNotifyClient,
handleGenerateInvoice,
handleCreateAmendment
};
};