'use client' import React, { useState, useEffect } from 'react'; import Table from '@/components/Table'; import Tab from '@/components/Tab'; import { useTranslations } from 'next-intl'; import StatusLabel from '@/components/StatusLabel'; import { Search } from 'lucide-react'; import Popup from '@/components/Popup'; import Loader from '@/components/Loader'; import AlertWithModal from '@/components/AlertWithModal'; import DropdownMenu from "@/components/DropdownMenu"; import { MoreVertical, Send, Edit, Archive, FileText, CircleCheck, Plus, XCircle } from 'lucide-react'; import Modal from '@/components/Modal'; import InscriptionForm from '@/components/Inscription/InscriptionForm' import AffectationClasseForm from '@/components/AffectationClasseForm' import { useEstablishment } from '@/context/EstablishmentContext'; import ValidateSubscription from '@/components/Inscription/ValidateSubscription'; import { PENDING, SUBSCRIBED, ARCHIVED, fetchRegisterForms, createRegisterForm, sendRegisterForm, archiveRegisterForm, fetchStudents, editRegisterForm } from "@/app/actions/subscriptionAction" import { fetchRegistrationSchoolFileMasters, createRegistrationTemplates, fetchRegistrationFileGroups, cloneTemplate } from "@/app/actions/registerFileGroupAction"; import { fetchClasses, fetchRegistrationDiscounts, fetchTuitionDiscounts, fetchRegistrationFees, fetchTuitionFees } from '@/app/actions/schoolAction'; import { createProfile, deleteProfile, fetchProfiles } from '@/app/actions/authAction'; import { BASE_URL, FE_ADMIN_SUBSCRIPTIONS_EDIT_URL, FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL } from '@/utils/Url'; import DjangoCSRFToken from '@/components/DjangoCSRFToken' import { useCsrfToken } from '@/context/CsrfContext'; import logger from '@/utils/logger'; import { PhoneLabel } from '@/components/PhoneLabel'; export default function Page({ params: { locale } }) { const t = useTranslations('subscriptions'); const [registrationForms, setRegistrationForms] = useState([]); const [registrationFormsDataPending, setRegistrationFormsDataPending] = useState([]); const [registrationFormsDataSubscribed, setRegistrationFormsDataSubscribed] = useState([]); const [registrationFormsDataArchived, setRegistrationFormsDataArchived] = useState([]); // const [filter, setFilter] = useState('*'); const [searchTerm, setSearchTerm] = useState(''); const [alertPage, setAlertPage] = useState(false); const [isLoading, setIsLoading] = useState(false); const [popup, setPopup] = useState({ visible: false, message: '', onConfirm: null }); const [activeTab, setActiveTab] = useState('pending'); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [totalPending, setTotalPending] = useState(0); const [totalSubscribed, setTotalSubscribed] = useState(0); const [totalArchives, setTotalArchives] = useState(0); const [itemsPerPage, setItemsPerPage] = useState(10); // Définir le nombre d'éléments par page const [schoolFileMasters, setSchoolFileMasters] = useState([]); const [isOpen, setIsOpen] = useState(false); const [isOpenAffectationClasse, setIsOpenAffectationClasse] = useState(false); const [student, setStudent] = useState(''); const [classes, setClasses] = useState([]); const [students, setEleves] = useState([]); const [reloadFetch, setReloadFetch] = useState(false); const [registrationDiscounts, setRegistrationDiscounts] = useState([]); const [tuitionDiscounts, setTuitionDiscounts] = useState([]); const [registrationFees, setRegistrationFees] = useState([]); const [tuitionFees, setTuitionFees] = useState([]); const [groups, setGroups] = useState([]); const [profiles, setProfiles] = useState([]); const [isOpenAddGuardian, setIsOpenAddGuardian] = useState(false); const csrfToken = useCsrfToken(); const { selectedEstablishmentId } = useEstablishment(); const openModal = () => { setIsOpen(true); } const closeModal = () => { setIsOpen(false); } const handleOpenAddGuardian = (eleveSelected) => { setIsOpenAddGuardian(true); setStudent(eleveSelected); }; const handleCloseAddGuardian = () => { setIsOpenAddGuardian(false); }; const openModalAssociationEleve = (eleveSelected) => { setIsOpenAffectationClasse(true); setStudent(eleveSelected); } const requestErrorHandler = (err)=>{ logger.error('Error fetching data:', err); } /** * Handles the pending data for the registration form. * * @param {Object} data - The data object containing registration forms and count. * @param {Array} data.registerForms - The array of registration forms. * @param {number} data.count - The total count of registration forms. */ const registerFormPendingDataHandler = (data) => { if (data) { const { registerForms, count, page_size } = data; if (registerForms) { setRegistrationFormsDataPending(registerForms); } const calculatedTotalPages = count === 0 ? 1 : Math.ceil(count / page_size); setTotalPending(count); setTotalPages(calculatedTotalPages); } } /** * Handles the data received from the subscription registration form. * * @param {Object} data - The data object received from the subscription registration form. * @param {Array} data.registerForms - An array of registration forms. * @param {number} data.count - The total count of subscribed forms. */ const registerFormSubscribedDataHandler = (data) => { if (data) { const { registerForms, count, page_size } = data; setTotalSubscribed(count); if (registerForms) { setRegistrationFormsDataSubscribed(registerForms); } } } /** * Handles the archived data for the register form. * * @param {Object} data - The data object containing archived register forms and count. * @param {Array} data.registerForms - The array of archived register forms. * @param {number} data.count - The total count of archived register forms. */ const registerFormArchivedDataHandler = (data) => { if (data) { const { registerForms, count, page_size } = data; setTotalArchives(count); if (registerForms) { setRegistrationFormsDataArchived(registerForms); } } } useEffect(() => { if (selectedEstablishmentId) { const fetchInitialData = () => { Promise.all([ fetchClasses(selectedEstablishmentId), fetchStudents(selectedEstablishmentId) ]) .then(([classesData, studentsData]) => { setClasses(classesData); setEleves(studentsData); logger.debug('Success - Classes:', classesData); logger.debug('Success - Students:', studentsData); }) .catch(error => { logger.error('Error fetching initial data:', error); }); }; fetchInitialData(); } }, [selectedEstablishmentId]); useEffect(() => { if (selectedEstablishmentId) { const fetchDataAndSetState = () => { setIsLoading(true); Promise.all([ fetchRegisterForms(selectedEstablishmentId, PENDING, currentPage, itemsPerPage, searchTerm) .then(registerFormPendingDataHandler) .catch(requestErrorHandler), fetchRegisterForms(selectedEstablishmentId, SUBSCRIBED) .then(registerFormSubscribedDataHandler) .catch(requestErrorHandler), fetchRegisterForms(selectedEstablishmentId, ARCHIVED) .then(registerFormArchivedDataHandler) .catch(requestErrorHandler), fetchRegistrationSchoolFileMasters() .then(data => { setSchoolFileMasters(data); }) .catch(err => { logger.debug(err.message); }), fetchRegistrationDiscounts(selectedEstablishmentId) .then(data => { setRegistrationDiscounts(data); }) .catch(requestErrorHandler), fetchTuitionDiscounts(selectedEstablishmentId) .then(data => { setTuitionDiscounts(data); }) .catch(requestErrorHandler), fetchRegistrationFees(selectedEstablishmentId) .then(data => { setRegistrationFees(data); }) .catch(requestErrorHandler), fetchTuitionFees(selectedEstablishmentId) .then(data => { setTuitionFees(data); }) .catch(requestErrorHandler), fetchRegistrationFileGroups(selectedEstablishmentId) .then(data => { setGroups(data); }) .catch(error => { logger.error('Error fetching file groups:', error); }), fetchProfiles() .then(data => { setProfiles(data); }) .catch(error => { logger.error('Error fetching profileRoles:', error); }), ]) .then(() => { setIsLoading(false); setReloadFetch(false); }) .catch(err => { logger.error(err); setIsLoading(false); setReloadFetch(false); }); }; fetchDataAndSetState(); } }, [selectedEstablishmentId, reloadFetch, currentPage, searchTerm]); useEffect(() => { if (selectedEstablishmentId) { const fetchDataAndSetState = () => { setIsLoading(true); fetchRegisterForms(selectedEstablishmentId, PENDING, currentPage, itemsPerPage, searchTerm) .then(registerFormPendingDataHandler) .catch(requestErrorHandler) fetchRegisterForms(selectedEstablishmentId, SUBSCRIBED) .then(registerFormSubscribedDataHandler) .catch(requestErrorHandler) fetchRegisterForms(selectedEstablishmentId, ARCHIVED) .then(registerFormArchivedDataHandler) .catch(requestErrorHandler) fetchRegistrationSchoolFileMasters() .then((data)=> {setSchoolFileMasters(data)}) .catch((err)=>{ err = err.message; logger.debug(err);}); setIsLoading(false); setReloadFetch(false); } const timeoutId = setTimeout(() => { fetchDataAndSetState(); }, 500); // Debounce la recherche return () => clearTimeout(timeoutId); } }, [searchTerm]); /** * UseEffect to update page count of tab */ useEffect(()=>{ if (activeTab === 'pending') { setTotalPages(Math.ceil(totalPending / itemsPerPage)); } else if (activeTab === 'subscribed') { setTotalPages(Math.ceil(totalSubscribed / itemsPerPage)); } else if (activeTab === 'archived') { setTotalPages(Math.ceil(totalArchives / itemsPerPage)); } },[currentPage]); /** * Archives a registration form after user confirmation. * * @param {number} id - The ID of the registration form to be archived. * @param {string} nom - The last name of the person whose registration form is being archived. * @param {string} prenom - The first name of the person whose registration form is being archived. */ const archiveFicheInscription = (id, nom, prenom) => { setPopup({ visible: true, message: `Attentions ! \nVous êtes sur le point d'archiver le dossier d'inscription de ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`, onConfirm: () => { archiveRegisterForm(id) .then(data => { logger.debug('Success:', data); setRegistrationForms(registrationForms.filter(fiche => fiche.id !== id)); setReloadFetch(true); alert("Le dossier d'inscription a été correctement archivé"); }) .catch(error => { logger.error('Error archiving data:', error); alert("Erreur lors de l'archivage du dossier d'inscription.\nContactez l'administrateur."); }); } }); }; const sendConfirmRegisterForm = (id, nom, prenom) => { setPopup({ visible: true, message: `Avertissement ! \nVous êtes sur le point d'envoyer un dossier d'inscription à ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`, onConfirm: () => { sendRegisterForm(id).then(data => { logger.debug('Success:', data); setReloadFetch(true); }) .catch(error => { logger.error('Error fetching data:', error); }); } }); }; const affectationClassFormSubmitHandler = (formdata)=> { editRegisterForm(student.id,formData, csrfToken) .then(data => { logger.debug('Success:', data); setReloadFetch(true); }) .catch(error => { logger.error('Error :', error); }); } const refuseRegistrationForm = (id, lastname, firstname, guardianEmail) => { const data = { status: 2, establishment: selectedEstablishmentId }; setPopup({ visible: true, message: `Avertissement ! \nVous êtes sur le point de refuser le dossier d'inscription de ${lastname} ${firstname}\nUne notification va être envoyée à l'adresse ${guardianEmail}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`, onConfirm: () => { editRegisterForm(id, data, csrfToken) .then(data => { logger.debug('Success:', data); setReloadFetch(true); }) .catch(error => { logger.error('Error refusing RF:', error); }); } }); }; const updateStatusAction = (id, newStatus) => { logger.debug(`Mise à jour du statut du dossier d'inscription avec l'ID : ${id} vers le statut : ${newStatus}`); }; const handleSearchChange = (event) => { setSearchTerm(event.target.value); }; const handlePageChange = (newPage) => { setCurrentPage(newPage); }; const createRF = (updatedData) => { logger.debug('createRF updatedData:', updatedData); const selectedRegistrationFeesIds = updatedData.selectedRegistrationFees.map(feeId => feeId) const selectedRegistrationDiscountsIds = updatedData.selectedRegistrationDiscounts.map(discountId => discountId) const selectedTuitionFeesIds = updatedData.selectedTuitionFees.map(feeId => feeId) const selectedTuitionDiscountsIds = updatedData.selectedTuitionDiscounts.map(discountId => discountId) const selectedFileGroup = updatedData.selectedFileGroup const allFeesIds = [...selectedRegistrationFeesIds, ...selectedTuitionFeesIds]; const allDiscountsds = [...selectedRegistrationDiscountsIds, ...selectedTuitionDiscountsIds]; const data = { student: { last_name: updatedData.studentLastName, first_name: updatedData.studentFirstName, guardians: updatedData.selectedGuardians.length !== 0 ? updatedData.selectedGuardians.map(guardianId => ({ id: guardianId })) : (() => { if (updatedData.isExistingParentProfile) { return [{ profile_role_data: { establishment: selectedEstablishmentId, role_type: 2, is_active: false, profile: updatedData.existingProfileId, // Associer au profil existant }, last_name: updatedData.guardianLastName, first_name: updatedData.guardianFirstName, birth_date: updatedData.guardianBirthDate, address: updatedData.guardianAddress, phone: updatedData.guardianPhone, profession: updatedData.guardianProfession }]; } // Si aucun profil existant n'est trouvé, créer un nouveau profil return [{ profile_role_data: { establishment: selectedEstablishmentId, role_type: 2, is_active: false, profile_data: { email: updatedData.guardianEmail, password: 'Provisoire01!', username: updatedData.guardianEmail, } }, last_name: updatedData.guardianLastName, first_name: updatedData.guardianFirstName, birth_date: updatedData.guardianBirthDate, address: updatedData.guardianAddress, phone: updatedData.guardianPhone, profession: updatedData.guardianProfession }]; })(), sibling: [] }, fees: allFeesIds, discounts: allDiscountsds, fileGroup: selectedFileGroup, establishment: selectedEstablishmentId }; createRegisterForm(data, csrfToken) .then(data => { // Cloner les schoolFileTemplates pour chaque templateMaster du fileGroup const masters = schoolFileMasters.filter(file => file.groups.includes(selectedFileGroup)); const clonePromises = masters.map((templateMaster, index) => { return cloneTemplate(templateMaster.id, updatedData.guardianEmail, templateMaster.is_required) .then(clonedDocument => { // Sauvegarde des schoolFileTemplates clonés dans la base de données const cloneData = { name: `${templateMaster.name}_${updatedData.guardianFirstName}_${updatedData.guardianLastName}`, slug: clonedDocument.slug, id: clonedDocument.id, master: templateMaster.id, registration_form: data.student.id }; return createRegistrationTemplates(cloneData, csrfToken) .then(response => { logger.debug('Template enregistré avec succès:', response); }) .catch(error => { logger.error('Erreur lors de l\'enregistrement du template:', error); }); }) .catch(error => { logger.error('Error during cloning or sending:', error); }); }); // Attendre que tous les clones soient créés Promise.all(clonePromises) .then(() => { // Mise à jour immédiate des données setRegistrationFormsDataPending(prevState => [...(prevState || []), data]); setTotalPending(prev => prev + 1); if (updatedData.autoMail) { sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName); } closeModal(); // Forcer le rechargement complet des données setReloadFetch(true); }) .catch(error => { logger.error('Error during cloning or sending:', error); }); }) .catch((error) => { logger.error('Error:', error); }); } const updateRF = (updatedData) => { logger.debug('updateRF updatedData:', updatedData); const data = { student: { guardians: updatedData.selectedGuardians.length !== 0 ? updatedData.selectedGuardians.map(guardianId => ({ id: guardianId })) : (() => { if (updatedData.isExistingParentProfile) { return [{ profile_role_data: { establishment: selectedEstablishmentId, role_type: 2, is_active: false, profile: updatedData.existingProfileId, // Associer au profil existant }, last_name: updatedData.guardianLastName, first_name: updatedData.guardianFirstName, birth_date: updatedData.guardianBirthDate, address: updatedData.guardianAddress, phone: updatedData.guardianPhone, profession: updatedData.guardianProfession }]; } // Si aucun profil existant n'est trouvé, créer un nouveau profil return [{ profile_role_data: { establishment: selectedEstablishmentId, role_type: 2, is_active: false, profile_data: { email: updatedData.guardianEmail, password: 'Provisoire01!', username: updatedData.guardianEmail, } }, last_name: updatedData.guardianLastName, first_name: updatedData.guardianFirstName, birth_date: updatedData.guardianBirthDate, address: updatedData.guardianAddress, phone: updatedData.guardianPhone, profession: updatedData.guardianProfession }]; })(), }, establishment: selectedEstablishmentId }; editRegisterForm(student.id, data, csrfToken) .then(data => { // Mise à jour immédiate des données setRegistrationFormsDataPending(prevState => [...(prevState || []), data]); setTotalPending(prev => prev + 1); if (updatedData.autoMail) { sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName); } handleCloseAddGuardian(); // Forcer le rechargement complet des données setReloadFetch(true); }) .catch(error => { logger.error('Error during updating registration form:', error); }); } const getActionsByStatus = (row) => { const actions = { 1: [ { icon: , onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`, }, { icon: , onClick: () => sendConfirmRegisterForm(row.student.id, row.student.last_name, row.student.first_name), }, ], 2: [ { icon: , onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`, }, ], 3: [ { icon: , onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL}?studentId=${row.student.id}&firstName=${row.student.first_name}&lastName=${row.student.last_name}&paymentMode=${row.registration_payment}&file=${row.registration_file}`, }, ], 5: [ { icon: , onClick: () => openModalAssociationEleve(row.student), }, ], default: [ { icon: , onClick: () => archiveFicheInscription(row.student.id, row.student.last_name, row.student.first_name), }, ], }; // Combine actions for the specific status and default actions return [...(actions[row.status] || []), ...(row.status !== 6 ? actions.default : [])]; }; const columns = [ { name: t('studentName'), transform: (row) => row.student.last_name }, { name: t('studentFistName'), transform: (row) => row.student.first_name }, { name: t('mainContactMail'), transform: (row) => ( row.student.guardians && row.student.guardians.length > 0 ? row.student.guardians[0].associated_profile_email : (
) ) }, { name: t('phone'), transform: (row) => }, { name: t('lastUpdateDate'), transform: (row) => row.formatted_last_update}, { name: t('registrationFileStatus'), transform: (row) => (
updateStatusAction(row.student.id, newStatus)} showDropdown={false} />
) }, { name: t('files'), transform: (row) => (row.registration_file != null) &&( ) }, { name: 'Actions', transform: (row) => (
{getActionsByStatus(row).map((action, index) => ( ))}
), }, ]; const columnsSubscribed = [ { name: t('studentName'), transform: (row) => row.student.last_name }, { name: t('studentFistName'), transform: (row) => row.student.first_name }, { name: t('lastUpdateDate'), transform: (row) => row.updated_date_formated}, { name: t('class'), transform: (row) => row.student.first_name}, { name: t('registrationFileStatus'), transform: (row) => (
updateStatusAction(row.student.id, newStatus)} showDropdown={false} />
) }, { name: t('files'), transform: (row) => (row.registration_file != null) &&( ) }, { name: 'Actions', transform: (row) => ( } items={[ { label: ( <> Rattacher ), onClick: () => openModalAssociationEleve(row.student) }, { label: ( <> Archiver ), onClick: () => archiveFicheInscription(row.student.id, row.student.last_name, row.student.first_name), } ]} buttonClassName="text-gray-400 hover:text-gray-600" menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-10 flex flex-col items-center" /> ) }, ]; if (isLoading) { return ; } else { if (registrationForms.length === 0 && registrationFormsDataArchived.length === 0 && alertPage) { return (
); } else { return (
{t('pending')} ({totalPending}) )} active={activeTab === 'pending'} onClick={() => setActiveTab('pending')} /> {t('subscribed')} ({totalSubscribed}) )} active={activeTab === 'subscribed'} onClick={() => setActiveTab('subscribed')} /> {t('archived')} ({totalArchives}) )} active={activeTab === 'archived'} onClick={() => setActiveTab('archived')} />
{/*SI STATE == pending || subscribe || archived */} {activeTab === 'pending' || activeTab === 'subscribed' || activeTab === 'archived' ? (
) : null} { popup.onConfirm(); setPopup({ ...popup, visible: false }); }} onCancel={() => setPopup({ ...popup, visible: false })} /> {isOpen && ( ( fee.is_active)} tuitionFees={tuitionFees.filter(fee => fee.is_active)} groups={groups} profiles={profiles} onSubmit={createRF} /> )} /> )} {isOpenAffectationClasse && ( ( )} /> )} {isOpenAddGuardian && ( ( )} /> )} ); } } }