import React, { useState, useEffect } from 'react'; import { X, Calendar, AlertCircle, Upload } from 'lucide-react'; const EditLeaveRequestModal = ({ onClose, request, availableLeaveCounters, accessToken, userId, userEmail, userRole, userName, onRequestUpdated }) => { const [leaveType, setLeaveType] = useState(request.typeId || ''); const [startDate, setStartDate] = useState(request.startDate || ''); const [endDate, setEndDate] = useState(request.endDate || ''); const [reason, setReason] = useState(request.reason || ''); const [businessDays, setBusinessDays] = useState(request.days || 0); const [saturdayCount, setSaturdayCount] = useState(0); const [medicalDocuments, setMedicalDocuments] = useState([]); const [errors, setErrors] = useState({}); const [isSubmitting, setIsSubmitting] = useState(false); const [submitMessage, setSubmitMessage] = useState({ type: '', text: '' }); // ⭐ Types de congés disponibles selon le rôle const getLeaveTypes = () => { const baseTypes = [ { id: 1, name: 'Congé payé', key: 'CP', counter: availableLeaveCounters.availableCP }, ]; // Ajouter RTT sauf pour les apprentis if (userRole !== 'Apprenti') { baseTypes.push({ id: 2, name: 'RTT', key: 'RTT', counter: availableLeaveCounters.availableRTT }); } // Ajouter les types sans compteur baseTypes.push( { id: 3, name: 'Arrêt maladie', key: 'ABS', counter: null }, { id: 5, name: 'Récupération (samedi)', key: 'Récup', counter: null } ); // Ajouter Formation pour les apprentis if (userRole === 'Apprenti') { baseTypes.push({ id: 4, name: 'Formation', key: 'Formation', counter: null }); } return baseTypes; }; const leaveTypes = getLeaveTypes(); // ⭐ Calcul des jours ouvrés ET des samedis useEffect(() => { if (startDate && endDate) { const result = calculateBusinessDaysAndSaturdays(startDate, endDate); setBusinessDays(result.workingDays); setSaturdayCount(result.saturdays); } }, [startDate, endDate]); const calculateBusinessDaysAndSaturdays = (start, end) => { const startD = new Date(start); const endD = new Date(end); let workingDays = 0; let saturdays = 0; const current = new Date(startD); while (current <= endD) { const dayOfWeek = current.getDay(); if (dayOfWeek === 6) { saturdays++; } else if (dayOfWeek !== 0) { // Pas dimanche workingDays++; } current.setDate(current.getDate() + 1); } return { workingDays, saturdays }; }; const validateForm = () => { const newErrors = {}; if (!leaveType) { newErrors.leaveType = 'Veuillez sélectionner un type de congé'; } if (!startDate) { newErrors.startDate = 'La date de début est requise'; } if (!endDate) { newErrors.endDate = 'La date de fin est requise'; } if (startDate && endDate && new Date(startDate) > new Date(endDate)) { newErrors.endDate = 'La date de fin doit être après la date de début'; } // ⭐ Validation spécifique pour Récupération const selectedType = leaveTypes.find(t => t.id === parseInt(leaveType)); if (selectedType?.key === 'Récup') { if (saturdayCount === 0) { newErrors.days = 'Une récupération nécessite au moins un samedi dans la période sélectionnée'; } } // ⭐ Validation spécifique pour Arrêt maladie if (selectedType?.key === 'ABS' && medicalDocuments.length === 0) { newErrors.medical = 'Un justificatif médical est obligatoire pour un arrêt maladie'; } // Vérification du solde disponible (CP et RTT uniquement) if (selectedType && selectedType.counter !== null && businessDays > selectedType.counter) { newErrors.days = `Solde insuffisant. Disponible : ${selectedType.counter} jour(s)`; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleFileUpload = (e) => { const files = Array.from(e.target.files); const validFiles = []; const maxSize = 5 * 1024 * 1024; // 5MB for (const file of files) { const validTypes = ['application/pdf', 'image/jpeg', 'image/jpg', 'image/png']; if (!validTypes.includes(file.type)) { setSubmitMessage({ type: 'error', text: `Le fichier "${file.name}" n'est pas un format valide.` }); continue; } if (file.size > maxSize) { setSubmitMessage({ type: 'error', text: `Le fichier "${file.name}" est trop volumineux (max 5MB).` }); continue; } validFiles.push(file); } setMedicalDocuments(prev => [...prev, ...validFiles]); e.target.value = ''; }; const removeDocument = (index) => { setMedicalDocuments(prev => prev.filter((_, i) => i !== index)); }; const formatFileSize = (bytes) => { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; const handleSubmit = async (e) => { e.preventDefault(); if (!validateForm()) { return; } setIsSubmitting(true); setSubmitMessage({ type: '', text: '' }); try { const formDataToSend = new FormData(); // ⭐ Ajouter tous les champs texte AVANT les fichiers formDataToSend.append('requestId', request.id.toString()); formDataToSend.append('leaveType', leaveType.toString()); formDataToSend.append('startDate', startDate); formDataToSend.append('endDate', endDate); formDataToSend.append('reason', reason || ''); formDataToSend.append('userId', userId.toString()); formDataToSend.append('userEmail', userEmail); formDataToSend.append('userName', userName); formDataToSend.append('accessToken', accessToken || ''); // ⭐ Calcul des jours selon le type const selectedType = leaveTypes.find(t => t.id === parseInt(leaveType)); const daysToSend = selectedType?.key === 'Récup' ? saturdayCount : businessDays; formDataToSend.append('businessDays', daysToSend.toString()); // ⭐ Documents médicaux EN DERNIER if (medicalDocuments.length > 0) { medicalDocuments.forEach((file) => { formDataToSend.append('medicalDocuments', file); }); } // ⭐ DEBUG : Vérifier le contenu console.log('📤 FormData à envoyer:'); for (let pair of formDataToSend.entries()) { console.log(pair[0], ':', pair[1]); } const response = await fetch('/api/updateRequest', { method: 'POST', // ⭐ NE PAS mettre de Content-Type, le navigateur le fera automatiquement avec boundary body: formDataToSend }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (data.success) { setSubmitMessage({ type: 'success', text: '✅ Demande modifiée avec succès ! Le manager a été informé par email.' }); setTimeout(() => { onRequestUpdated(); onClose(); }, 2000); } else { setSubmitMessage({ type: 'error', text: `❌ ${data.message || 'Erreur lors de la modification'}` }); } } catch (error) { console.error('❌ Erreur:', error); setSubmitMessage({ type: 'error', text: '❌ Une erreur est survenue. Veuillez réessayer.' }); } finally { setIsSubmitting(false); } }; const getMinDate = () => { const today = new Date(); return today.toISOString().split('T')[0]; }; const selectedType = leaveTypes.find(t => t.id === parseInt(leaveType)); return (
{/* Overlay */}
{/* Modal */}
{/* Header */}

Modifier la demande

{/* Body */}
{/* Message de statut */} {submitMessage.text && (
{submitMessage.text}
)} {/* Info - Demande originale */}

Demande actuelle

Type : {request.type}

Dates : {request.dateDisplay}

Jours : {request.days}

{/* Type de congé */}
{errors.leaveType && (

{errors.leaveType}

)}
{/* Dates */}
setStartDate(e.target.value)} min={getMinDate()} className={`w-full pl-10 pr-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 ${errors.startDate ? 'border-red-500' : 'border-gray-300' }`} />
{errors.startDate && (

{errors.startDate}

)}
setEndDate(e.target.value)} min={startDate || getMinDate()} className={`w-full pl-10 pr-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 ${errors.endDate ? 'border-red-500' : 'border-gray-300' }`} />
{errors.endDate && (

{errors.endDate}

)}
{/* ⭐ Résumé de la période */} {startDate && endDate && (

Résumé de la période :

{businessDays} jour(s) ouvré(s) (lundi-vendredi)

{saturdayCount > 0 && ( <>

• {saturdayCount} samedi(s) détecté(s)

{selectedType?.key !== 'Récup' && (

⚠️ Les samedis seront ignorés (sélectionnez "Récupération" pour les inclure)

)} {selectedType?.key === 'Récup' && (

✅ Récupération : {saturdayCount} samedi(s) seront comptabilisés

)} )}
)} {/* Nombre de jours */} {(businessDays > 0 || saturdayCount > 0) && (

Nombre de jours {selectedType?.key === 'Récup' ? '(samedis)' : 'ouvrés'} :{' '} {selectedType?.key === 'Récup' ? saturdayCount : businessDays}

{errors.days && (

{errors.days}

)}
)} {/* ⭐ Upload documents médicaux pour Arrêt maladie */} {selectedType?.key === 'ABS' && (

Glissez vos documents ici ou cliquez pour sélectionner

Formats acceptés : PDF, JPG, PNG (max 5MB par fichier)

{medicalDocuments.length > 0 && (

Fichiers sélectionnés ({medicalDocuments.length}) :

{medicalDocuments.map((file, index) => (
{file.type === 'application/pdf' ? ( ) : ( )}

{file.name}

{formatFileSize(file.size)}

))}
)} {errors.medical && (

{errors.medical}

)}
)} {/* Motif */}