Files
n3wt-school/Front-End/src/app/[locale]/admin/subscriptions/page.js
N3WT DE COMPET 5851341235 feat: Ajout de la photo pour le dossier de l'élève + correction
sauvegarde des datas des responsables
2025-05-01 14:59:19 +02:00

1214 lines
40 KiB
JavaScript

'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 { useRouter } from 'next/navigation';
import DropdownMenu from '@/components/DropdownMenu';
import {
MoreVertical,
Send,
Edit,
Archive,
FileText,
CheckCircle,
Plus,
Upload,
} 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 {
PENDING,
SUBSCRIBED,
ARCHIVED,
fetchRegisterForms,
createRegisterForm,
sendRegisterForm,
archiveRegisterForm,
fetchStudents,
editRegisterForm,
editRegisterFormWithBinaryFile,
} from '@/app/actions/subscriptionAction';
import {
fetchRegistrationSchoolFileMasters,
fetchRegistrationParentFileMasters,
createRegistrationSchoolFileTemplate,
createRegistrationParentFileTemplate,
fetchRegistrationFileGroups,
cloneTemplate,
} from '@/app/actions/registerFileGroupAction';
import {
fetchClasses,
fetchRegistrationDiscounts,
fetchTuitionDiscounts,
fetchRegistrationFees,
fetchTuitionFees,
} from '@/app/actions/schoolAction';
import { fetchProfiles } from '@/app/actions/authAction';
import {
FE_ADMIN_SUBSCRIPTIONS_EDIT_URL,
FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL,
BASE_URL,
} from '@/utils/Url';
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import { useCsrfToken } from '@/context/CsrfContext';
import logger from '@/utils/logger';
import { PhoneLabel } from '@/components/PhoneLabel';
import FileUpload from '@/components/FileUpload';
import FilesModal from '@/components/Inscription/FilesModal';
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 [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 [parentFileMasters, setParentFileMasters] = 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 [isFilesModalOpen, setIsFilesModalOpen] = useState(false);
const [selectedRegisterForm, setSelectedRegisterForm] = useState([]);
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState('');
const [confirmPopupVisible, setConfirmPopupVisible] = useState(false);
const [confirmPopupMessage, setConfirmPopupMessage] = useState('');
const [confirmPopupOnConfirm, setConfirmPopupOnConfirm] = useState(() => {});
const [isSepaUploadModalOpen, setIsSepaUploadModalOpen] = useState(false);
const [selectedRowForUpload, setSelectedRowForUpload] = useState(null);
const csrfToken = useCsrfToken();
const router = useRouter();
const { selectedEstablishmentId } = useEstablishment();
const openSepaUploadModal = (row) => {
setSelectedRowForUpload(row);
setIsSepaUploadModalOpen(true);
};
const closeSepaUploadModal = () => {
setSelectedRowForUpload(null);
setIsSepaUploadModalOpen(false);
};
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 openFilesModal = (row) => {
setSelectedRegisterForm(row || []);
setIsFilesModalOpen(true);
};
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 fetchDataAndSetState = () => {
setIsLoading(true);
Promise.all([
fetchRegisterForms(
selectedEstablishmentId,
PENDING,
currentPage,
itemsPerPage,
searchTerm
)
.then(registerFormPendingDataHandler)
.catch(requestErrorHandler),
fetchClasses(selectedEstablishmentId)
.then((classesData) => {
setClasses(classesData);
})
.catch(requestErrorHandler),
fetchStudents(selectedEstablishmentId)
.then((studentsData) => {
setEleves(studentsData);
})
.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);
}),
fetchRegistrationParentFileMasters()
.then((data) => {
setParentFileMasters(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]);
const handleSepaFileUpload = (file, row) => {
if (!file || !row) {
logger.error("Aucun fichier ou ligne sélectionnée pour l'upload.");
return;
}
const formData = new FormData();
formData.append('status', 7);
formData.append('sepa_file', file);
// Appeler l'API pour uploader le fichier SEPA
editRegisterFormWithBinaryFile(row.student.id, formData, csrfToken)
.then((response) => {
logger.debug('Mandat SEPA uploadé avec succès :', response);
setPopupMessage('Le mandat SEPA a été uploadé avec succès.');
setPopupVisible(true);
setReloadFetch(true);
closeSepaUploadModal();
})
.catch((error) => {
logger.error("Erreur lors de l'upload du mandat SEPA :", error);
setPopupMessage("Erreur lors de l'upload du mandat SEPA.");
setPopupVisible(true);
});
};
/**
* 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) => {
setConfirmPopupMessage(
`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 ?`
);
setConfirmPopupOnConfirm(() => () => {
archiveRegisterForm(id)
.then((data) => {
logger.debug('Success:', data);
setPopupMessage(
`Le dossier d'inscription a été correctement archivé`
);
setPopupVisible(true);
setRegistrationForms(
registrationForms.filter((fiche) => fiche.id !== id)
);
setReloadFetch(true);
})
.catch((error) => {
logger.error('Error archiving data:', error);
setPopupMessage(
`Erreur lors de l'archivage du dossier d'inscription.\nContactez l'administrateur.`
);
setPopupVisible(true);
});
setConfirmPopupVisible(false);
});
setConfirmPopupVisible(true);
};
const sendConfirmRegisterForm = (id, nom, prenom) => {
setConfirmPopupMessage(
`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 ?`
);
setConfirmPopupOnConfirm(() => () => {
sendRegisterForm(id)
.then((data) => {
logger.debug('Success:', data);
setPopupMessage(`Le dossier d'inscription a été envoyé avec succès`);
setPopupVisible(true);
setReloadFetch(true);
})
.catch((error) => {
logger.error('Error archiving data:', error);
setPopupMessage(
`Erreur lors de l'envoi du dossier d'inscription.\nContactez l'administrateur.`
);
setPopupVisible(true);
});
setConfirmPopupVisible(false);
});
setConfirmPopupVisible(true);
};
const affectationClassFormSubmitHandler = (formdata) => {
editRegisterForm(student.id, formData, csrfToken)
.then((data) => {
logger.debug('Success:', data);
setReloadFetch(true);
})
.catch((error) => {
logger.error('Error :', 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,
};
setIsLoading(true);
createRegisterForm(data, csrfToken)
.then((data) => {
// Cloner les schoolFileTemplates pour chaque templateMaster du fileGroup
const masters = schoolFileMasters.filter((file) =>
file.groups.includes(selectedFileGroup)
);
const parent_masters = parentFileMasters.filter((file) =>
file.groups.includes(selectedFileGroup)
);
const clonePromises = masters.map((templateMaster) => {
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.studentFirstName}_${updatedData.studentLastName}`,
slug: clonedDocument.slug,
id: clonedDocument.id,
master: templateMaster.id,
registration_form: data.student.id,
};
return createRegistrationSchoolFileTemplate(cloneData, csrfToken)
.then((response) => {
logger.debug('Template enregistré avec succès:', response);
})
.catch((error) => {
setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du template:",
error
);
});
})
.catch((error) => {
setIsLoading(false);
logger.error('Error during cloning or sending:', error);
});
});
// Créer les parentFileTemplates pour chaque parentMaster
const parentClonePromises = parent_masters.map((parentMaster) => {
const parentTemplateData = {
master: parentMaster.id,
registration_form: data.student.id,
};
return createRegistrationParentFileTemplate(
parentTemplateData,
csrfToken
)
.then((response) => {
logger.debug('Parent template enregistré avec succès:', response);
})
.catch((error) => {
setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du parent template:",
error
);
});
});
// Attendre que tous les clones (school et parent) soient créés
Promise.all([...clonePromises, ...parentClonePromises])
.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(); // Appeler closeModal ici après que tout soit terminé
// Forcer le rechargement complet des données
setReloadFetch(true);
setIsLoading(false);
})
.catch((error) => {
setIsLoading(false);
logger.error('Error during cloning or sending:', error);
});
})
.catch((error) => {
setIsLoading(false);
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: (
<span title="Envoyer le dossier">
<Send className="w-5 h-5 text-green-500 hover:text-green-700" />
</span>
),
onClick: () =>
sendConfirmRegisterForm(
row.student.id,
row.student.last_name,
row.student.first_name
),
},
],
2: [
{
icon: (
<span title="Editer le dossier">
<Edit className="w-5 h-5 text-blue-500 hover:text-blue-700" />
</span>
),
onClick: () =>
router.push(
`${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}`
),
},
],
3: [
{
icon: (
<span title="Voir les fichiers">
<FileText className="w-5 h-5 text-cyan-500 hover:text-cyan-700" />
</span>
),
onClick: () => openFilesModal(row),
},
{
icon: (
<span title="Valider le dossier">
<CheckCircle className="w-5 h-5 text-green-500 hover:text-green-700" />
</span>
),
onClick: () => {
const url = `${FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL}?studentId=${row.student.id}&firstName=${row.student.first_name}&lastName=${row.student.last_name}&sepa_file=${row.sepa_file}&student_file=${row.registration_file}`;
router.push(`${url}`);
},
},
],
5: [
{
icon: (
<span title="Voir les fichiers">
<FileText className="w-5 h-5 text-cyan-500 hover:text-cyan-700" />
</span>
),
onClick: () => openFilesModal(row),
},
],
7: [
{
icon: (
<span title="Voir les fichiers">
<FileText className="w-5 h-5 text-cyan-500 hover:text-cyan-700" />
</span>
),
onClick: () => openFilesModal(row),
},
],
8: [
{
icon: (
<span title="Voir les fichiers">
<FileText className="w-5 h-5 text-cyan-500 hover:text-cyan-700" />
</span>
),
onClick: () => openFilesModal(row),
},
{
icon: (
<span title="Uploader un mandat SEPA">
<Upload className="w-5 h-5 text-emerald-500 hover:text-emerald-700" />
</span>
),
onClick: () => openSepaUploadModal(row),
},
],
default: [
{
icon: (
<span title="Archiver le dossier">
<Archive className="w-5 h-5 text-gray-500 hover:text-gray-700" />
</span>
),
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('photo'),
transform: (row) => (
<div className="flex justify-center items-center">
{row.student.photo ? (
<a
href={`${BASE_URL}${row.student.photo}`} // Lien vers la photo
target="_blank"
rel="noopener noreferrer"
>
<img
src={`${BASE_URL}${row.student.photo}`}
alt={`${row.student.first_name} ${row.student.last_name}`}
className="w-10 h-10 object-cover transition-transform duration-200 hover:scale-125 cursor-pointer"
/>
</a>
) : (
<div className="w-10 h-10 flex items-center justify-center bg-gray-200 rounded-full">
<span className="text-gray-500 text-sm font-semibold">
{row.student.first_name[0]}
{row.student.last_name[0]}
</span>
</div>
)}
</div>
),
},
{ 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
) : (
<div className="flex justify-center h-full">
<button
className="flex items-center gap-2 text-blue-600 font-semibold hover:text-blue-800 transition duration-200 underline decoration-blue-600 hover:decoration-blue-800"
onClick={() => handleOpenAddGuardian(row.student)}
>
<span className="px-3 py-1 bg-blue-100 rounded-full hover:bg-blue-200 transition duration-200">
Ajouter un responsable
</span>
</button>
</div>
),
},
{
name: t('phone'),
transform: (row) => (
<PhoneLabel phoneNumber={row.student.guardians[0]?.phone} />
),
},
{
name: t('lastUpdateDate'),
transform: (row) => row.formatted_last_update,
},
{
name: t('registrationFileStatus'),
transform: (row) => (
<div className="flex justify-center items-center h-full">
<StatusLabel
status={row.status}
onChange={(newStatus) =>
updateStatusAction(row.student.id, newStatus)
}
showDropdown={false}
/>
</div>
),
},
{
name: 'Actions',
transform: (row) => (
<div className="flex justify-center space-x-2">
{getActionsByStatus(row).map((action, index) => (
<button
key={index}
onClick={action.onClick}
className="p-2 rounded-full hover:bg-gray-100 transition"
>
{action.icon}
</button>
))}
</div>
),
},
];
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) => (
<div className="flex justify-center items-center h-full">
<StatusLabel
status={row.status}
onChange={(newStatus) =>
updateStatusAction(row.student.id, newStatus)
}
showDropdown={false}
/>
</div>
),
},
{
name: 'Actions',
transform: (row) => (
<DropdownMenu
buttonContent={
<MoreVertical
size={20}
className="text-gray-400 hover:text-gray-600"
/>
}
items={[
{
label: (
<>
<CheckCircle size={16} className="mr-2" /> Rattacher
</>
),
onClick: () => openModalAssociationEleve(row.student),
},
{
label: (
<>
<Archive size={16} className="mr-2 text-red-700" /> 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 <Loader />;
} else {
if (
registrationForms.length === 0 &&
registrationFormsDataArchived.length === 0 &&
alertPage
) {
return (
<div className="p-8">
<AlertWithModal
title={t('information')}
message={t('no_records') + ' ' + t('create_first_record')}
buttonText={t('add_button')}
/>
</div>
);
} else {
return (
<div className="p-8">
<div className="border-b border-gray-200 mb-6">
<div className="flex items-center gap-8">
<Tab
text={
<>
{t('pending')}
<span className="ml-2 text-sm text-gray-400">
({totalPending})
</span>
</>
}
active={activeTab === 'pending'}
onClick={() => setActiveTab('pending')}
/>
<Tab
text={
<>
{t('subscribed')}
<span className="ml-2 text-sm text-gray-400">
({totalSubscribed})
</span>
</>
}
active={activeTab === 'subscribed'}
onClick={() => setActiveTab('subscribed')}
/>
<Tab
text={
<>
{t('archived')}
<span className="ml-2 text-sm text-gray-400">
({totalArchives})
</span>
</>
}
active={activeTab === 'archived'}
onClick={() => setActiveTab('archived')}
/>
</div>
</div>
<div className="border-b border-gray-200 mb-6 w-full">
{/*SI STATE == pending || subscribe || archived */}
{activeTab === 'pending' ||
activeTab === 'subscribed' ||
activeTab === 'archived' ? (
<React.Fragment>
<div className="flex justify-between items-center mb-4 w-full">
<div className="relative flex-grow">
<Search
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
size={20}
/>
<input
type="text"
placeholder={t('searchStudent')}
className="w-full pl-10 pr-4 py-2 border border-gray-200 rounded-md"
value={searchTerm}
onChange={handleSearchChange}
/>
</div>
<button
onClick={openModal}
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-900 transition duration-200 ml-4"
>
<Plus className="w-5 h-5" />
</button>
</div>
<div className="w-full">
<DjangoCSRFToken csrfToken={csrfToken} />
<Table
key={`${currentPage}-${searchTerm}`}
data={
activeTab === 'pending'
? [
...registrationFormsDataPending,
...registrationFormsDataSubscribed,
]
: activeTab === 'subscribed'
? registrationFormsDataSubscribed
: registrationFormsDataArchived
}
columns={
activeTab === 'subscribed' ? columnsSubscribed : columns
}
itemsPerPage={itemsPerPage}
currentPage={currentPage}
totalPages={totalPages}
onPageChange={handlePageChange}
/>
</div>
</React.Fragment>
) : null}
</div>
<Popup
visible={popupVisible}
message={popupMessage}
onConfirm={() => setPopupVisible(false)}
uniqueConfirmButton={true}
/>
<Popup
visible={confirmPopupVisible}
message={confirmPopupMessage}
onConfirm={confirmPopupOnConfirm}
onCancel={() => setConfirmPopupVisible(false)}
/>
{isOpen && (
<Modal
isOpen={isOpen}
setIsOpen={setIsOpen}
title={"Nouveau dossier d'inscription"}
ContentComponent={() => (
<InscriptionForm
students={students}
registrationDiscounts={registrationDiscounts}
tuitionDiscounts={tuitionDiscounts}
registrationFees={registrationFees.filter(
(fee) => fee.is_active
)}
tuitionFees={tuitionFees.filter((fee) => fee.is_active)}
groups={groups}
profiles={profiles}
onSubmit={createRF}
/>
)}
/>
)}
{isOpenAffectationClasse && (
<Modal
isOpen={isOpenAffectationClasse}
setIsOpen={setIsOpenAffectationClasse}
title="Affectation à une classe"
ContentComponent={() => (
<AffectationClasseForm
students={students}
onSubmit={affectationClassFormSubmitHandler}
classes={classes}
/>
)}
/>
)}
{isOpenAddGuardian && (
<Modal
isOpen={isOpenAddGuardian}
setIsOpen={setIsOpenAddGuardian}
title="Ajouter un responsable"
ContentComponent={() => (
<InscriptionForm
students={students}
profiles={profiles}
onSubmit={updateRF}
currentStep={2}
showOnlyStep2={true}
/>
)}
/>
)}
{isSepaUploadModalOpen && (
<Modal
isOpen={isSepaUploadModalOpen}
setIsOpen={setIsSepaUploadModalOpen}
title="Uploader un mandat SEPA"
ContentComponent={() => (
<FileUpload
selectionMessage="Sélectionnez un mandat SEPA à uploader"
onFileSelect={(file) =>
handleSepaFileUpload(file, selectedRowForUpload)
}
/>
)}
/>
)}
{isFilesModalOpen && (
<FilesModal
isOpen={isFilesModalOpen}
setIsOpen={setIsFilesModalOpen}
selectedRegisterForm={selectedRegisterForm}
/>
)}
</div>
);
}
}
}