299 lines
12 KiB
TypeScript
Executable File
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
|
|
};
|
|
}; |