"use client"; import { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; import { FileIcon, Calendar, MapPin, Users, Clock, ThumbsUp, Languages, Trash2, Sparkles, Save, Loader2, FileText, Lock, CheckCircle } from "lucide-react"; import { useToast } from "@/components/ui/use-toast"; import { useParams, useRouter } from "next/navigation"; // Define types for mission details interface User { id: string; email: string; } interface Attachment { id: string; filename: string; filePath: string; fileType: string; fileSize: number; publicUrl: string; createdAt: string; } interface Mission { id: string; name: string; logo?: string | null; logoUrl?: string | null; oddScope: string[]; niveau: string; missionType: string; projection: string; intention?: string; donneurDOrdre?: string; participation?: string; services?: string[]; profils?: string[]; attachments?: Attachment[]; actionPlan?: string | null; actionPlanGeneratedAt?: string | null; isClosed?: boolean; closedAt?: string | null; createdAt: string; creator: User; missionUsers: any[]; } export default function MissionDetailPage() { const [mission, setMission] = useState(null); const [loading, setLoading] = useState(true); const [deleting, setDeleting] = useState(false); const [closing, setClosing] = useState(false); const [generatingPlan, setGeneratingPlan] = useState(false); const [savingPlan, setSavingPlan] = useState(false); const [editedPlan, setEditedPlan] = useState(""); const [isPlanModified, setIsPlanModified] = useState(false); const [activeTab, setActiveTab] = useState("general"); const { toast } = useToast(); const params = useParams(); const router = useRouter(); const missionId = params.missionId as string; // Fetch mission details useEffect(() => { const fetchMissionDetails = async () => { try { setLoading(true); const response = await fetch(`/api/missions/${missionId}`); if (!response.ok) { throw new Error('Failed to fetch mission details'); } const data = await response.json(); console.log("Mission details:", data); setMission(data); setEditedPlan(data.actionPlan || ""); } catch (error) { console.error('Error fetching mission details:', error); toast({ title: "Erreur", description: "Impossible de charger les détails de la mission", variant: "destructive", }); } finally { setLoading(false); } }; if (missionId) { fetchMissionDetails(); } }, [missionId, toast]); // Track if plan has been modified useEffect(() => { if (mission) { setIsPlanModified(editedPlan !== (mission.actionPlan || "")); } }, [editedPlan, mission]); // Helper function to format date const formatDate = (dateString: string) => { const date = new Date(dateString); return date.toLocaleDateString('fr-FR', { day: '2-digit', month: 'long', year: 'numeric' }); }; // Helper functions to get labels const getMissionTypeLabel = (type: string) => { switch(type) { case 'remote': return 'À distance'; case 'onsite': return 'Sur site'; case 'hybrid': return 'Hybride'; default: return type; } }; const getDurationLabel = (projection: string) => { switch(projection) { case 'short': return '< 1 mois'; case 'medium': return '1-3 mois'; case 'long': return '> 3 mois'; default: return projection; } }; const getNiveauLabel = (niveau: string) => { switch(niveau) { case 'a': return 'Apprentissage'; case 'b': return 'Basique'; case 'c': return 'Complexe'; case 's': return 'Spécial'; default: return niveau; } }; const getDonneurDOrdreLabel = (donneurDOrdre: string) => { switch(donneurDOrdre) { case 'individual': return 'Individu'; case 'group': return 'ONG'; case 'organization': return 'Start-ups'; default: return donneurDOrdre; } }; const getParticipationLabel = (participation: string) => { switch(participation) { case 'ouvert': return 'Ouvert'; case 'volontaire': return 'Ouvert'; // Legacy support case 'cooptation': return 'Cooptation'; default: return participation; } }; // Function to get odd info const getODDInfo = (oddScope: string[]) => { const oddCode = oddScope && oddScope.length > 0 ? oddScope[0] : null; const oddNumber = oddCode ? oddCode.replace('odd-', '') : null; return { number: oddNumber, label: oddNumber ? `ODD ${oddNumber}` : "Non catégorisé", iconPath: oddNumber ? `/F SDG Icons 2019 WEB/F-WEB-Goal-${oddNumber.padStart(2, '0')}.png` : "" }; }; // Handle delete mission const handleDeleteMission = async () => { if (!confirm("Êtes-vous sûr de vouloir supprimer cette mission ? Cette action est irréversible.")) { return; } try { setDeleting(true); const response = await fetch(`/api/missions/${missionId}`, { method: 'DELETE', }); if (!response.ok) { throw new Error('Failed to delete mission'); } toast({ title: "Mission supprimée", description: "La mission a été supprimée avec succès", }); router.push('/missions'); } catch (error) { console.error('Error deleting mission:', error); toast({ title: "Erreur", description: "Impossible de supprimer la mission", variant: "destructive", }); } finally { setDeleting(false); } }; // Handle close mission const handleCloseMission = async () => { if (!confirm("Êtes-vous sûr de vouloir clôturer cette mission ? Cette action fermera la mission dans tous les services externes.")) { return; } try { setClosing(true); const response = await fetch(`/api/missions/${missionId}/close`, { method: 'POST', }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Failed to close mission'); } const data = await response.json(); // Update local state setMission(prev => prev ? { ...prev, isClosed: true, closedAt: data.mission.closedAt } : null); toast({ title: "Mission clôturée", description: "La mission a été clôturée avec succès dans tous les services", }); } catch (error) { console.error('Error closing mission:', error); toast({ title: "Erreur", description: error instanceof Error ? error.message : "Impossible de clôturer la mission", variant: "destructive", }); } finally { setClosing(false); } }; // Handle generate action plan const handleGeneratePlan = async () => { try { setGeneratingPlan(true); const response = await fetch(`/api/missions/${missionId}/generate-plan`, { method: 'POST', }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Failed to generate plan'); } const data = await response.json(); setEditedPlan(data.actionPlan || ""); setMission(prev => prev ? { ...prev, actionPlan: data.actionPlan, actionPlanGeneratedAt: data.generatedAt } : null); toast({ title: "Plan d'action généré", description: "Le plan d'action a été généré avec succès par l'IA", }); } catch (error) { console.error('Error generating plan:', error); toast({ title: "Erreur", description: error instanceof Error ? error.message : "Impossible de générer le plan d'action", variant: "destructive", }); } finally { setGeneratingPlan(false); } }; // Handle save action plan const handleSavePlan = async () => { try { setSavingPlan(true); const response = await fetch(`/api/missions/${missionId}/generate-plan`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ actionPlan: editedPlan }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Failed to save plan'); } setMission(prev => prev ? { ...prev, actionPlan: editedPlan } : null); setIsPlanModified(false); toast({ title: "Plan sauvegardé", description: "Le plan d'action a été sauvegardé avec succès", }); } catch (error) { console.error('Error saving plan:', error); toast({ title: "Erreur", description: error instanceof Error ? error.message : "Impossible de sauvegarder le plan d'action", variant: "destructive", }); } finally { setSavingPlan(false); } }; // Loading state if (loading) { return (
); } // Error state if mission not found if (!mission) { return (

Mission non trouvée

Cette mission n'existe pas ou a été supprimée.

); } const oddInfo = getODDInfo(mission.oddScope); return (
{/* Header with Name and Logo */}

{mission.name}

{formatDate(mission.createdAt)}
{/* Logo */}
{mission.logoUrl ? ( {mission.name} { (e.currentTarget as HTMLImageElement).style.display = 'none'; const parent = e.currentTarget.parentElement; if (parent) { parent.classList.add('bg-gray-100', 'flex', 'items-center', 'justify-center'); parent.innerHTML = `${mission.name.slice(0, 2).toUpperCase()}`; } }} /> ) : (
{mission.name.slice(0, 2).toUpperCase()}
)}
{/* Tabs */} Général Plan d'actions Équipe {mission.missionUsers && mission.missionUsers.length > 0 && ( {mission.missionUsers.length + 1} )} Ressources {mission.attachments && mission.attachments.length > 0 && ( {mission.attachments.length} )} {/* General Tab */} {/* Info Grid */}

Type de mission

{getMissionTypeLabel(mission.missionType)}

Donneur d'ordre

{getDonneurDOrdreLabel(mission.donneurDOrdre || "")}

Durée

{getDurationLabel(mission.projection)}

Niveau

{getNiveauLabel(mission.niveau)}

Participation

{getParticipationLabel(mission.participation || "")}

{oddInfo.number && (
{oddInfo.label}

Objectif

{oddInfo.label}

)}
{/* Description */}

Description de la mission

{mission.intention || "Aucune description disponible pour cette mission."}
{/* Services */} {mission.services && mission.services.length > 0 && (

Services

{mission.services.map((service, index) => ( {service} ))}
)} {/* Profils */} {mission.profils && mission.profils.length > 0 && (

Profils recherchés

{mission.profils.map((profil, index) => ( {profil} ))}
)} {/* Mission Status & Action Buttons */}
{/* Closed Status */} {mission.isClosed && (
Mission clôturée {mission.closedAt && ( le {formatDate(mission.closedAt)} )}
)} {!mission.isClosed &&
} {/* Action Buttons */}
{/* Delete Button */} {/* Close Button - only show if not already closed */} {!mission.isClosed && ( )}
{/* Plan d'actions Tab */}

Plan d'actions

{mission.actionPlanGeneratedAt && (

Généré le {formatDate(mission.actionPlanGeneratedAt)}

)}
{isPlanModified && ( )} {/* Only show Generate button if no plan has been saved yet */} {!mission.actionPlan && ( )}
{/* Plan Content */} {editedPlan || mission.actionPlan ? (