// Import des dépendances nécessaires import React, { useState, useEffect } from 'react'; import Button from '@/components/Button'; import DjangoCSRFToken from '@/components/DjangoCSRFToken'; import { fetchSchoolFileTemplatesFromRegistrationFiles, fetchParentFileTemplatesFromRegistrationFiles, } from '@/app/actions/subscriptionAction'; import { downloadTemplate, editRegistrationSchoolFileTemplates, editRegistrationParentFileTemplates, } from '@/app/actions/registerFileGroupAction'; import { fetchRegistrationPaymentModes, fetchTuitionPaymentModes, fetchRegistrationPaymentPlans, fetchTuitionPaymentPlans, } from '@/app/actions/schoolAction'; import { BASE_URL } from '@/utils/Url'; import logger from '@/utils/logger'; import FilesToUpload from '@/components/Inscription/FilesToUpload'; import { DocusealForm } from '@docuseal/react'; import StudentInfoForm from '@/components/Inscription/StudentInfoForm'; import ResponsableInputFields from '@/components/Inscription/ResponsableInputFields'; import SiblingInputFields from '@/components/Inscription/SiblingInputFields'; import PaymentMethodSelector from '@/components/Inscription/PaymentMethodSelector'; import ProgressStep from '@/components/ProgressStep'; import { CheckCircle, Hourglass } from 'lucide-react'; /** * Composant de formulaire d'inscription partagé * @param {string} studentId - ID de l'étudiant * @param {string} csrfToken - Token CSRF pour la sécurité * @param {function} onSubmit - Fonction de soumission du formulaire * @param {string} cancelUrl - URL de redirection en cas d'annulation * @param {object} errors - Erreurs de validation du formulaire */ export default function InscriptionFormShared({ studentId, csrfToken, selectedEstablishmentId, onSubmit, errors = {}, // Nouvelle prop pour les erreurs }) { // États pour gérer les données du formulaire const [formData, setFormData] = useState({ id: '', photo: null, last_name: '', first_name: '', gender: '', address: '', birth_date: '', birth_place: '', birth_postal_code: '', nationality: '', attending_physician: '', level: '', photo: '', }); const [guardians, setGuardians] = useState([]); const [siblings, setSiblings] = useState([]); const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]); const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]); const [registrationPaymentPlans, setRegistrationPaymentPlans] = useState([]); const [tuitionPaymentPlans, setTuitionPaymentPlans] = useState([]); // États pour la gestion des fichiers const [uploadedFiles, setUploadedFiles] = useState([]); const [schoolFileTemplates, setSchoolFileTemplates] = useState([]); const [parentFileTemplates, setParentFileTemplates] = useState([]); const [currentPage, setCurrentPage] = useState(1); const [isPage1Valid, setIsPage1Valid] = useState(false); const [isPage2Valid, setIsPage2Valid] = useState(false); const [isPage3Valid, setIsPage3Valid] = useState(false); const [isPage4Valid, setIsPage4Valid] = useState(false); const [isPage5Valid, setIsPage5Valid] = useState(false); const [isPage6Valid, setIsPage6Valid] = useState(false); const [hasInteracted, setHasInteracted] = useState(false); // État pour suivre l'index du fichier en cours const [currentTemplateIndex, setCurrentTemplateIndex] = useState(0); useEffect(() => { // Trouver le premier template non signé const firstUnsignedIndex = schoolFileTemplates.findIndex( (template) => template.file === null ); // Mettre à jour l'index du template actuel if (firstUnsignedIndex !== -1) { setCurrentTemplateIndex(firstUnsignedIndex); } else { // Si tous les templates sont signés, définir un index hors limites setCurrentTemplateIndex(0); } }, [schoolFileTemplates]); useEffect(() => { // Vérifier si tous les templates ont leur champ "file" différent de null const allSigned = schoolFileTemplates.every( (template) => template.file !== null ); // Mettre à jour isPage4Valid en fonction de cette condition setIsPage5Valid(allSigned); if (allSigned) { setCurrentTemplateIndex(0); } }, [schoolFileTemplates]); useEffect(() => { // Vérifier si tous les parentFileTemplates ont leur champ "file" différent de null const allUploaded = parentFileTemplates.every( (template) => template.file !== null ); // Mettre à jour isPage6Valid en fonction de cette condition setIsPage6Valid(allUploaded); }, [parentFileTemplates]); const handleTemplateSigned = (index) => { const template = schoolFileTemplates[index]; if (!template) { logger.error("Template introuvable pour l'index donné."); return; } // Télécharger le template downloadTemplate(template.slug) .then((downloadUrl) => fetch(downloadUrl)) .then((response) => { if (!response.ok) { throw new Error('Erreur lors du téléchargement du fichier.'); } return response.blob(); }) .then((blob) => { const file = new File([blob], `${template.name}.pdf`, { type: blob.type, }); // Préparer les données pour la mise à jour const updateData = new FormData(); updateData.append('file', file); // Mettre à jour le template via l'API return editRegistrationSchoolFileTemplates( template.id, updateData, csrfToken ); }) .then((updatedTemplate) => { logger.debug('Template mis à jour avec succès :', updatedTemplate); // Mettre à jour l'état local de schoolFileTemplates setSchoolFileTemplates((prevTemplates) => { const updatedTemplates = prevTemplates.map((t, i) => i === index ? { ...t, file: updatedTemplate.data.file } : t ); logger.debug( 'État schoolFileTemplates mis à jour :', updatedTemplates ); return updatedTemplates; }); }) .catch((error) => { logger.error('Erreur lors de la mise à jour du template :', error); }); }; useEffect(() => { fetchSchoolFileTemplatesFromRegistrationFiles(studentId).then((data) => { setSchoolFileTemplates(data); }); fetchParentFileTemplatesFromRegistrationFiles(studentId).then((data) => { setParentFileTemplates(data); // Initialiser uploadedFiles avec uniquement les fichiers dont `file` n'est pas null const filteredFiles = data .filter((item) => item.file !== null) .map((item) => ({ id: item.id, fileName: item.file, })); setUploadedFiles(filteredFiles); }); if (selectedEstablishmentId) { // Fetch data for registration payment modes handleRegistrationPaymentModes(); // Fetch data for tuition payment modes handleTuitionPaymentModes(); // Fetch data for registration payment plans handleRegistrationPaymentPlans(); // Fetch data for tuition payment plans handleTuitionnPaymentPlans(); } }, [selectedEstablishmentId]); const handleRegistrationPaymentModes = () => { fetchRegistrationPaymentModes(selectedEstablishmentId) .then((data) => { const activePaymentModes = data.filter( (mode) => mode.is_active === true ); setRegistrationPaymentModes(activePaymentModes); }) .catch((error) => logger.error('Error fetching registration payment modes:', error) ); }; const handleTuitionPaymentModes = () => { fetchTuitionPaymentModes(selectedEstablishmentId) .then((data) => { const activePaymentModes = data.filter( (mode) => mode.is_active === true ); setTuitionPaymentModes(activePaymentModes); }) .catch((error) => logger.error('Error fetching tuition payment modes:', error) ); }; const handleRegistrationPaymentPlans = () => { fetchRegistrationPaymentPlans(selectedEstablishmentId) .then((data) => { const activePaymentPlans = data.filter( (mode) => mode.is_active === true ); setRegistrationPaymentPlans(activePaymentPlans); }) .catch((error) => logger.error('Error fetching registration payment plans:', error) ); }; const handleTuitionnPaymentPlans = () => { fetchTuitionPaymentPlans(selectedEstablishmentId) .then((data) => { const activePaymentPlans = data.filter( (mode) => mode.is_active === true ); setTuitionPaymentPlans(activePaymentPlans); }) .catch((error) => logger.error('Error fetching registration tuition plans:', error) ); }; const handleFileUpload = (file, selectedFile) => { if (!file || !selectedFile) { logger.error('Données manquantes pour le téléversement.'); return Promise.reject( new Error('Données manquantes pour le téléversement.') ); } const updateData = new FormData(); updateData.append('file', file); return editRegistrationParentFileTemplates( selectedFile.id, updateData, csrfToken ) .then((response) => { logger.debug('Template mis à jour avec succès :', response); setUploadedFiles((prev) => { const updatedFiles = prev.map((uploadedFile) => uploadedFile.id === selectedFile.id ? { ...uploadedFile, fileName: response.data.file } // Met à jour le fichier téléversé : uploadedFile ); // Si le fichier n'existe pas encore, l'ajouter if (!updatedFiles.find((file) => file.id === selectedFile.id)) { updatedFiles.push({ id: selectedFile.id, fileName: response.data.file, }); } return updatedFiles; }); // Mettre à jour parentFileTemplates setParentFileTemplates((prevTemplates) => prevTemplates.map((template) => template.id === selectedFile.id ? { ...template, file: response.data.file } : template ) ); return response; // Retourner la réponse pour signaler le succès }) .catch((error) => { logger.error('Erreur lors de la mise à jour du fichier :', error); throw error; // Relancer l'erreur pour que l'appelant puisse la capturer }); }; const handleDeleteFile = (templateId) => { const fileToDelete = uploadedFiles.find( (file) => parseInt(file.id) === templateId && file.fileName ); if (!fileToDelete) { logger.error('Aucun fichier trouvé pour suppression.'); return; } // Créer un FormData avec un champ vide pour "file" const updateData = new FormData(); updateData.append('file', ''); // Envoyer chaine vide pour indiquer qu'aucun fichier n'est uploadé return editRegistrationParentFileTemplates( templateId, updateData, csrfToken ) .then((response) => { logger.debug('Fichier supprimé avec succès dans la base :', response); setIsPage6Valid(false); // Mettre à jour l'état local pour refléter la suppression setUploadedFiles((prev) => prev.map((uploadedFile) => uploadedFile.id === templateId ? { ...uploadedFile, fileName: null, fileUrl: null } // Réinitialiser les champs liés au fichier : uploadedFile ) ); // Mettre à jour l'état local pour refléter la suppression dans parentFileTemplates setParentFileTemplates((prevTemplates) => prevTemplates.map((template) => template.id === templateId ? { ...template, file: null } : template ) ); return response; }) .catch((error) => { logger.error( 'Erreur lors de la suppression du fichier dans la base :', error ); throw error; }); }; // Soumission du formulaire const handleSubmit = (e) => { e.preventDefault(); // Vérifier si le mode de paiement sélectionné est un prélèvement SEPA const isSepaPayment = formData.registration_payment === 1 || formData.tuition_payment === 1; // Préparer les données JSON const jsonData = { student: { ...formData, guardians: guardians, siblings: siblings.map(({ id, ...rest }) => id && typeof id === 'string' && id.startsWith('temp-') ? rest : { id, ...rest } ), // Supprimer les IDs temporaires }, establishment: selectedEstablishmentId, status: isSepaPayment ? 8 : 3, tuition_payment: formData.tuition_payment, registration_payment: formData.registration_payment, tuition_payment_plan: formData.tuition_payment_plan, registration_payment_plan: formData.registration_payment_plan, }; // Créer un objet FormData const formDataToSend = new FormData(); // Ajouter les données JSON sous forme de chaîne formDataToSend.append('data', JSON.stringify(jsonData)); // Ajouter la photo si elle est présente if (formData.photo) { formDataToSend.append('photo', formData.photo); } console.log('submit : ', jsonData); // Appeler la fonction onSubmit avec les données FormData onSubmit(formDataToSend); }; const handleNextPage = () => { setCurrentPage(currentPage + 1); }; const handlePreviousPage = () => { setCurrentPage(currentPage - 1); }; const stepTitles = { 1: 'Elève', 2: 'Responsables légaux', 3: 'Frères et soeurs', 4: 'Modalités de paiement', 5: 'Formulaires à signer', 6: 'Pièces à fournir', }; const steps = [ 'Élève', 'Responsable', 'Fratrie', 'Paiement', 'Formulaires', 'Documents parent', ]; const isStepValid = (stepNumber) => { switch (stepNumber) { case 1: return isPage1Valid; case 2: return isPage2Valid; case 3: return isPage3Valid; case 4: return isPage4Valid; case 5: return isPage5Valid; case 6: return isPage6Valid; default: return false; } }; // Rendu du composant return (
{/* Page 1 : Informations sur l'élève */} {currentPage === 1 && ( )} {/* Page 2 : Informations sur les responsables légaux */} {currentPage === 2 && ( )} {/* Étape 3 : Frères et Sœurs */} {currentPage === 3 && ( )} {/* Page 4 : Informations sur les modalités de paiement */} {currentPage === 4 && ( <> )} {/* Page 5 : Section Fichiers d'inscription */} {currentPage === 5 && (
{/* Liste des états de signature */}

Documents

    {schoolFileTemplates.map((template, index) => (
  • setCurrentTemplateIndex(index)} // Mettre à jour l'index du template actuel > {template.file !== null ? ( ) : ( )} {template.name || 'Document sans nom'}
  • ))}
{/* Affichage du fichier actuel */}
{currentTemplateIndex < schoolFileTemplates.length && (

{schoolFileTemplates[currentTemplateIndex].name || 'Document sans nom'}

{schoolFileTemplates[currentTemplateIndex].description || 'Aucune description disponible pour ce document.'}

{schoolFileTemplates[currentTemplateIndex].file === null ? ( handleTemplateSigned(currentTemplateIndex) } /> ) : (