mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Gestion de la sauvegarde du fichier d'inscription / affichage du
fichier avec le bon nom / possibilité de refuser un DI
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Table from '@/components/Table';
|
||||
import {mockFicheInscription} from '@/data/mockFicheInscription';
|
||||
import Tab from '@/components/Tab';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import StatusLabel from '@/components/StatusLabel';
|
||||
@ -11,7 +10,7 @@ import Loader from '@/components/Loader';
|
||||
import AlertWithModal from '@/components/AlertWithModal';
|
||||
import DropdownMenu from "@/components/DropdownMenu";
|
||||
import { formatPhoneNumber } from '@/utils/Telephone';
|
||||
import { MoreVertical, Send, Edit, Trash2, FileText, CheckCircle, Plus } from 'lucide-react';
|
||||
import { MoreVertical, Send, Edit, Trash2, FileText, CheckCircle, Plus, TicketX } from 'lucide-react';
|
||||
import Modal from '@/components/Modal';
|
||||
import InscriptionForm from '@/components/Inscription/InscriptionForm'
|
||||
import AffectationClasseForm from '@/components/AffectationClasseForm'
|
||||
@ -364,8 +363,27 @@ useEffect(()=>{
|
||||
});
|
||||
}
|
||||
|
||||
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('Edit fiche inscription with id:', id);
|
||||
logger.debug(`Mise à jour du statut du dossier d'inscription avec l'ID : ${id} vers le statut : ${newStatus}`);
|
||||
};
|
||||
|
||||
const handleSearchChange = (event) => {
|
||||
@ -447,7 +465,7 @@ useEffect(()=>{
|
||||
.then(clonedDocument => {
|
||||
// Sauvegarde des templates clonés dans la base de données
|
||||
const cloneData = {
|
||||
name: `clone_${clonedDocument.id}`,
|
||||
name: `${templateMaster.name}_${updatedData.guardianFirstName}_${updatedData.guardianLastName}`,
|
||||
slug: clonedDocument.slug,
|
||||
id: clonedDocument.id,
|
||||
master: templateMaster.id,
|
||||
@ -555,6 +573,80 @@ useEffect(()=>{
|
||||
});
|
||||
}
|
||||
|
||||
const getActionsByStatus = (row) => {
|
||||
const actions = {
|
||||
1: [
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<Send size={16} className="mr-2" /> Envoyer
|
||||
</>
|
||||
),
|
||||
onClick: () => sendConfirmRegisterForm(row.student.id, row.student.last_name, row.student.first_name),
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<Edit size={16} className="mr-2" /> Modifier
|
||||
</>
|
||||
),
|
||||
onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
|
||||
},
|
||||
],
|
||||
2: [
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<Edit size={16} className="mr-2" /> Modifier
|
||||
</>
|
||||
),
|
||||
onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
|
||||
},
|
||||
],
|
||||
3: [
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<CheckCircle size={16} className="mr-2" /> Valider
|
||||
</>
|
||||
),
|
||||
onClick: () => openModalAssociationEleve(row.student),
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<TicketX size={16} className="mr-2 text-red-700" /> Refuser
|
||||
</>
|
||||
),
|
||||
onClick: () => refuseRegistrationForm(row.student.id, row.student.last_name, row.student.first_name, row.student.guardians[0].associated_profile_email),
|
||||
},
|
||||
],
|
||||
5: [
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<CheckCircle size={16} className="mr-2" /> Rattacher
|
||||
</>
|
||||
),
|
||||
onClick: () => openModalAssociationEleve(row.student),
|
||||
},
|
||||
],
|
||||
default: [
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<Trash2 size={16} className="mr-2 text-red-700" /> Archiver
|
||||
</>
|
||||
),
|
||||
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 },
|
||||
@ -588,69 +680,21 @@ const columns = [
|
||||
{ name: t('files'), transform: (row) =>
|
||||
(row.registration_file != null) &&(
|
||||
<ul>
|
||||
<li className="flex items-center gap-2">
|
||||
<li className="flex justify-center items-center gap-2">
|
||||
<FileText size={16} />
|
||||
<a href={ `${BASE_URL}${row.registration_file}`} target='_blank'>{row.registration_file?.split('/').pop()}</a>
|
||||
</li>
|
||||
</ul>
|
||||
) },
|
||||
{ name: 'Actions', transform: (row) => (
|
||||
<DropdownMenu
|
||||
buttonContent={<MoreVertical size={20} className="text-gray-400 hover:text-gray-600" />}
|
||||
items={[
|
||||
...(row.status === 1 ? [{
|
||||
label: (
|
||||
<>
|
||||
<Send size={16} className="mr-2" /> Envoyer
|
||||
</>
|
||||
),
|
||||
onClick: () => sendConfirmRegisterForm(row.student.id, row.student.last_name, row.student.first_name),
|
||||
}] : []),
|
||||
...(row.status === 1 ? [{
|
||||
label: (
|
||||
<>
|
||||
<Edit size={16} className="mr-2" /> Modifier
|
||||
</>
|
||||
),
|
||||
onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
|
||||
}] : []),
|
||||
...(row.status === 2 ? [{
|
||||
label: (
|
||||
<>
|
||||
<Edit size={16} className="mr-2" /> Modifier
|
||||
</>
|
||||
),
|
||||
onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
|
||||
}] : []),
|
||||
...(row.status === 3 ? [{
|
||||
label: (
|
||||
<>
|
||||
<CheckCircle size={16} className="mr-2" /> Valider
|
||||
</>
|
||||
),
|
||||
onClick: () => openModalAssociationEleve(row.student),
|
||||
}] : []),
|
||||
...(row.status === 5 ? [{
|
||||
label: (
|
||||
<>
|
||||
<CheckCircle size={16} className="mr-2" /> Rattacher
|
||||
</>
|
||||
),
|
||||
onClick: () => openModalAssociationEleve(row.student),
|
||||
}] : []),
|
||||
...(row.status !== 6 ? [{
|
||||
label: (
|
||||
<>
|
||||
<Trash2 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"
|
||||
/>
|
||||
) },
|
||||
{ name: 'Actions',
|
||||
transform: (row) => (
|
||||
<DropdownMenu
|
||||
buttonContent={<MoreVertical size={20} className="text-gray-400 hover:text-gray-600" />}
|
||||
items={getActionsByStatus(row)}
|
||||
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"
|
||||
/>
|
||||
), },
|
||||
|
||||
];
|
||||
|
||||
|
||||
@ -83,7 +83,8 @@ export default function ParentHomePage() {
|
||||
|
||||
// Définir les colonnes du tableau
|
||||
const childrenColumns = [
|
||||
{ name: 'Nom', transform: (row) => `${row.student.last_name} ${row.student.first_name}` },
|
||||
{ name: 'Nom', transform: (row) => `${row.student.last_name}` },
|
||||
{ name: 'Prénom', transform: (row) => `${row.student.first_name}` },
|
||||
{
|
||||
name: 'Statut',
|
||||
transform: (row) => (
|
||||
|
||||
@ -21,7 +21,7 @@ import DraggableFileUpload from '@/components/DraggableFileUpload';
|
||||
import Modal from '@/components/Modal';
|
||||
import FileStatusLabel from '@/components/FileStatusLabel';
|
||||
import logger from '@/utils/logger';
|
||||
import StudentInfoForm from '@/components/Inscription/StudentInfoForm';
|
||||
import StudentInfoForm, { validateStudentInfo } from '@/components/Inscription/StudentInfoForm';
|
||||
import FilesToSign from '@/components/Inscription/FilesToSign';
|
||||
import FilesToUpload from '@/components/Inscription/FilesToUpload';
|
||||
import { DocusealForm } from '@docuseal/react';
|
||||
@ -70,6 +70,14 @@ export default function InscriptionFormShared({
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
const isCurrentPageValid = () => {
|
||||
if (currentPage === 1) {
|
||||
const isValid = validateStudentInfo(formData);
|
||||
return isValid;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Chargement initial des données
|
||||
// Mettre à jour les données quand initialData change
|
||||
useEffect(() => {
|
||||
@ -178,12 +186,25 @@ export default function InscriptionFormShared({
|
||||
...formData,
|
||||
guardians
|
||||
},
|
||||
establishment: ESTABLISHMENT_ID,
|
||||
establishment: 1,
|
||||
status:3
|
||||
}
|
||||
onSubmit(data);
|
||||
};
|
||||
|
||||
// Soumission du formulaire
|
||||
const handleSave = (e) => {
|
||||
e.preventDefault();
|
||||
const data ={
|
||||
student: {
|
||||
...formData,
|
||||
guardians
|
||||
},
|
||||
establishment: 1
|
||||
}
|
||||
onSubmit(data);
|
||||
};
|
||||
|
||||
// Récupération des messages d'erreur
|
||||
const getError = (field) => {
|
||||
return errors?.student?.[field]?.[0];
|
||||
@ -276,31 +297,52 @@ export default function InscriptionFormShared({
|
||||
{/* Pages suivantes : Section Fichiers d'inscription */}
|
||||
{currentPage > 1 && currentPage <= requiredFileTemplates.length + 1 && (
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
<h2 className="text-xl font-bold mb-4 text-gray-800">{requiredFileTemplates[currentPage - 2].name}</h2>
|
||||
<DocusealForm
|
||||
id="docusealForm"
|
||||
src={"https://docuseal.com/s/"+requiredFileTemplates[currentPage - 2].slug}
|
||||
withDownloadButton={false}
|
||||
onComplete={() => {
|
||||
downloadTemplate(requiredFileTemplates[currentPage - 2].slug)
|
||||
.then((data) => fetch(data))
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const file = new File([blob], `${requiredFileTemplates[currentPage - 2].name}.pdf`, { type: blob.type });
|
||||
const updateData = new FormData();
|
||||
updateData.append('file', file);
|
||||
|
||||
return editRegistrationTemplates(requiredFileTemplates[currentPage - 2].id, updateData, csrfToken);
|
||||
})
|
||||
.then((data) => {
|
||||
logger.debug("EDIT TEMPLATE : ", data);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error("error editing template : ", error);
|
||||
});
|
||||
}}
|
||||
>
|
||||
</DocusealForm>
|
||||
{/* Titre du document */}
|
||||
<div className="mb-4">
|
||||
<h2 className="text-lg font-semibold text-gray-800">
|
||||
{requiredFileTemplates[currentPage - 2].name || "Document sans nom"}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
{requiredFileTemplates[currentPage - 2].description || "Aucune description disponible pour ce document."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Affichage du formulaire ou du document */}
|
||||
{requiredFileTemplates[currentPage - 2].file === "" ? (
|
||||
<DocusealForm
|
||||
id="docusealForm"
|
||||
src={"https://docuseal.com/s/" + requiredFileTemplates[currentPage - 2].slug}
|
||||
withDownloadButton={false}
|
||||
onComplete={() => {
|
||||
downloadTemplate(requiredFileTemplates[currentPage - 2].slug)
|
||||
.then((data) => fetch(data))
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const file = new File([blob], `${requiredFileTemplates[currentPage - 2].name}.pdf`, { type: blob.type });
|
||||
const updateData = new FormData();
|
||||
updateData.append('file', file);
|
||||
|
||||
return editRegistrationTemplates(requiredFileTemplates[currentPage - 2].id, updateData, csrfToken);
|
||||
})
|
||||
.then((data) => {
|
||||
logger.debug("EDIT TEMPLATE : ", data);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error("error editing template : ", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${BASE_URL}/${requiredFileTemplates[currentPage - 2].file}`}
|
||||
title="Document Viewer"
|
||||
className="w-full"
|
||||
style={{
|
||||
height: '75vh', // Ajuster la hauteur à 75% de la fenêtre
|
||||
border: 'none',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -316,11 +358,29 @@ export default function InscriptionFormShared({
|
||||
|
||||
{/* Boutons de contrôle */}
|
||||
<div className="flex justify-end space-x-4">
|
||||
<Button
|
||||
text="Sauvegarder"
|
||||
onClick={handleSave}
|
||||
className="px-4 py-2 rounded-md shadow-sm focus:outline-none bg-orange-500 text-white hover:bg-orange-600"
|
||||
primary
|
||||
name="Save"
|
||||
/>
|
||||
{currentPage > 1 && (
|
||||
<Button text="Précédent" onClick={(e) => { e.preventDefault(); handlePreviousPage(); }} />
|
||||
)}
|
||||
{currentPage < requiredFileTemplates.length + 2 && (
|
||||
<Button text="Suivant" onClick={(e) => { e.preventDefault(); handleNextPage(); }} />
|
||||
<Button
|
||||
text="Suivant"
|
||||
onClick={(e) => { e.preventDefault(); handleNextPage(); }}
|
||||
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
||||
!isCurrentPageValid()
|
||||
? "bg-gray-300 text-gray-700 cursor-not-allowed"
|
||||
: "bg-emerald-500 text-white hover:bg-emerald-600"
|
||||
}`}
|
||||
disabled={!isCurrentPageValid()}
|
||||
primary
|
||||
name="Next"
|
||||
/>
|
||||
)}
|
||||
{currentPage === requiredFileTemplates.length + 2 && (
|
||||
<Button type="submit" text="Valider" primary />
|
||||
|
||||
@ -56,8 +56,8 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad
|
||||
name="mailResponsable"
|
||||
type="email"
|
||||
label={t('email')}
|
||||
value={item.email}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "email", event.target.value)}}
|
||||
value={item.associated_profile_email}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "associated_profile_email", event.target.value)}}
|
||||
required
|
||||
errorMsg={getError(index, 'email')}
|
||||
/>
|
||||
|
||||
@ -10,6 +10,28 @@ const levels = [
|
||||
{ value:'4', label: 'GS - Grande Section'},
|
||||
];
|
||||
|
||||
// Fonction de validation pour vérifier les champs requis
|
||||
export function validateStudentInfo(formData) {
|
||||
const requiredFields = [
|
||||
'last_name',
|
||||
'first_name',
|
||||
'nationality',
|
||||
'birth_date',
|
||||
'birth_place',
|
||||
'birth_postal_code',
|
||||
'address',
|
||||
'attending_physician',
|
||||
'level',
|
||||
];
|
||||
|
||||
const isValid = requiredFields.every((field) => {
|
||||
const value = formData[field];
|
||||
return typeof value === 'string' ? value.trim() !== '' : Boolean(value);
|
||||
});
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
export default function StudentInfoForm({ formData, updateFormField, guardians, setGuardians, errors }) {
|
||||
const getError = (field) => {
|
||||
return errors?.student?.[field]?.[0];
|
||||
|
||||
@ -66,7 +66,7 @@ export default function FileUpload({ handleCreateTemplateMaster, handleEditTempl
|
||||
const details = selectedGroups.flatMap(group =>
|
||||
group.registration_forms.flatMap(form =>
|
||||
form.guardians.map(guardian => ({
|
||||
email: guardian.email,
|
||||
email: guardian.associated_profile_email,
|
||||
last_name: form.last_name,
|
||||
first_name: form.first_name,
|
||||
registration_form: form.student_id
|
||||
|
||||
Reference in New Issue
Block a user