feat: Gestion des pièces à fournir par les parents (configuration école)

This commit is contained in:
N3WT DE COMPET
2025-04-15 18:00:58 +02:00
parent 4c2e2f8756
commit a65bd47905
20 changed files with 956 additions and 569 deletions

View File

@ -1,8 +1,8 @@
import React, { useState, useEffect } from 'react';
import { Plus, Download, Edit3, Trash2, FolderPlus, Signature } from 'lucide-react';
import { Plus, Download, Edit3, Trash2, FolderPlus, Signature, FileText, Check, X } from 'lucide-react';
import Modal from '@/components/Modal';
import Table from '@/components/Table';
import FileUpload from '@/components/Structure/Files/FileUpload';
import FileUploadDocuSeal from '@/components/Structure/Files/FileUploadDocuSeal';
import { BASE_URL } from '@/utils/Url';
import {
fetchRegistrationFileGroups,
@ -13,10 +13,15 @@ import {
createRegistrationTemplateMaster,
editRegistrationTemplateMaster,
deleteRegistrationTemplateMaster,
fetchRegistrationTemplates
fetchRegistrationTemplates,
fetchRegistrationParentFiles,
createRegistrationParentFiles,
editRegistrationParentFiles,
deleteRegistrationParentFiles
} from '@/app/actions/registerFileGroupAction';
import RegistrationFileGroupForm from '@/components/Structure/Files/RegistrationFileGroupForm';
import logger from '@/utils/logger';
import ParentFilesSection from '@/components/Structure/Files/ParentFilesSection';
export default function FilesGroupsManagement({ csrfToken, selectedEstablishmentId }) {
const [templateMasters, setTemplateMasters] = useState([]);
@ -29,6 +34,10 @@ export default function FilesGroupsManagement({ csrfToken, selectedEstablishment
const [isGroupModalOpen, setIsGroupModalOpen] = useState(false);
const [groupToEdit, setGroupToEdit] = useState(null);
const [reloadTemplates, setReloadTemplates] = useState(false);
const [editingDocumentId, setEditingDocumentId] = useState(null);
const [formData, setFormData] = useState({});
const [parentFiles, setParentFiles] = useState([]);
const handleReloadTemplates = () => {
setReloadTemplates(true);
@ -47,10 +56,12 @@ export default function FilesGroupsManagement({ csrfToken, selectedEstablishment
Promise.all([
fetchRegistrationTemplateMaster(),
fetchRegistrationFileGroups(selectedEstablishmentId),
fetchRegistrationTemplates()
]).then(([filesTemplateMasters, groupsData, filesTemplates]) => {
fetchRegistrationTemplates(),
fetchRegistrationParentFiles()
]).then(([filesTemplateMasters, groupsData, filesTemplates, filesParentFiles]) => {
setGroups(groupsData);
setTemplates(filesTemplates);
setParentFiles(filesParentFiles);
// Transformer chaque fichier pour inclure les informations complètes du groupe
const transformedFiles = filesTemplateMasters.map(file => transformFileData(file, groupsData));
setTemplateMasters(transformedFiles);
@ -189,7 +200,13 @@ export default function FilesGroupsManagement({ csrfToken, selectedEstablishment
alert('Erreur lors de l\'opération sur le groupe');
});
} else {
createRegistrationFileGroup(groupData, csrfToken)
// Ajouter l'établissement sélectionné lors de la création d'un nouveau groupe
const newGroupData = {
...groupData,
establishment: selectedEstablishmentId
};
createRegistrationFileGroup(newGroupData, csrfToken)
.then(newGroup => {
setGroups([...groups, newGroup]);
setIsGroupModalOpen(false);
@ -233,14 +250,135 @@ export default function FilesGroupsManagement({ csrfToken, selectedEstablishment
}
};
const renderRequiredDocumentCell = (document, column) => {
const isEditing = editingDocumentId === document.id;
if (isEditing) {
switch (column) {
case 'Nom de la pièce':
return (
<InputText
name="name"
value={formData.name}
onChange={(e) => handleChange(e, 'name')}
placeholder="Nom de la pièce"
className="w-full"
/>
);
case 'Description':
return (
<InputText
name="description"
value={formData.description}
onChange={(e) => handleChange(e, 'description')}
placeholder="Description"
className="w-full"
/>
);
case 'Actions':
return (
<div className="flex justify-center space-x-2">
<button
type="button"
onClick={() => handleSaveDocument(document.id)}
className="text-green-500 hover:text-green-700"
>
<Check className="w-5 h-5" />
</button>
<button
type="button"
onClick={handleCancelEdit}
className="text-red-500 hover:text-red-700"
>
<X className="w-5 h-5" />
</button>
</div>
);
default:
return null;
}
} else {
switch (column) {
case 'Nom de la pièce':
return <span>{document.name}</span>;
case 'Description':
return <span>{document.description}</span>;
case 'Actions':
return (
<div className="flex justify-center space-x-2">
<button
type="button"
onClick={() => handleEditDocument(document)}
className="text-blue-500 hover:text-blue-700"
>
<Edit3 className="w-5 h-5" />
</button>
<button
type="button"
onClick={() => handleDeleteRequiredDocument(document.id)}
className="text-red-500 hover:text-red-700"
>
<Trash2 className="w-5 h-5" />
</button>
</div>
);
default:
return null;
}
}
};
const handleCreate = (newParentFile) => {
return createRegistrationParentFiles(newParentFile, csrfToken)
.then((createdFile) => {
// Ajouter le nouveau fichier parent à la liste existante
setParentFiles((prevFiles) => [...prevFiles, createdFile]);
logger.debug('Document parent créé avec succès:', createdFile);
})
.catch((error) => {
logger.error('Erreur lors de la création du document parent:', error);
alert('Une erreur est survenue lors de la création du document parent.');
});
};
const handleEdit = (id, updatedFile) => {
return editRegistrationParentFiles(id, updatedFile, csrfToken)
.then((response) => {
const modifiedFile = response.data; // Extraire les données mises à jour
// Mettre à jour la liste des fichiers parents
setParentFiles((prevFiles) =>
prevFiles.map((file) => (file.id === id ? modifiedFile : file))
);
logger.debug('Document parent mis à jour avec succès:', modifiedFile);
return modifiedFile; // Retourner le fichier mis à jour
})
.catch((error) => {
logger.error('Erreur lors de la modification du document parent:', error);
alert('Une erreur est survenue lors de la modification du document parent.');
throw error;
});
};
const handleDelete = (id) => {
return deleteRegistrationParentFiles(id, csrfToken)
.then(() => {
// Mettre à jour la liste des fichiers parents en supprimant l'élément correspondant
setParentFiles((prevFiles) => prevFiles.filter((file) => file.id !== id));
logger.debug('Document parent supprimé avec succès:', id);
})
.catch((error) => {
logger.error('Erreur lors de la suppression du fichier parent:', error);
});
};
const filteredFiles = templateMasters.filter(file => {
if (!selectedGroup) return true;
return file.groups && file.groups.some(group => group.id === parseInt(selectedGroup));
});
const columnsFiles = [
{ name: 'Nom du fichier', transform: (row) => row.name },
{ name: 'Groupes', transform: (row) => row.groups && row.groups.length > 0 ? row.groups.map(group => group.name).join(', ') : 'Aucun' },
{ name: 'Nom du formulaire', transform: (row) => row.name },
{ name: 'Dossiers d\'inscription', transform: (row) => row.groups && row.groups.length > 0 ? row.groups.map(group => group.name).join(', ') : 'Aucun' },
{ name: 'Actions', transform: (row) => (
<div className="flex items-center justify-center gap-2">
{row.file && (
@ -259,7 +397,7 @@ export default function FilesGroupsManagement({ csrfToken, selectedEstablishment
];
const columnsGroups = [
{ name: 'Nom du groupe', transform: (row) => row.name },
{ name: 'Nom du dossier', transform: (row) => row.name },
{ name: 'Description', transform: (row) => row.description },
{ name: 'Actions', transform: (row) => (
<div className="flex items-center justify-center gap-2">
@ -273,8 +411,15 @@ export default function FilesGroupsManagement({ csrfToken, selectedEstablishment
)}
];
const columnsRequiredDocuments = [
{ name: 'Nom de la pièce', transform: (row) => renderRequiredDocumentCell(row, 'Nom de la pièce') },
{ name: 'Description', transform: (row) => renderRequiredDocumentCell(row, 'Description') },
{ name: 'Actions', transform: (row) => renderRequiredDocumentCell(row, 'Actions') },
];
return (
<div>
<div className="space-y-12">
{/* Modal pour les fichiers */}
<Modal
isOpen={isModalOpen}
setIsOpen={(isOpen) => {
@ -285,19 +430,21 @@ export default function FilesGroupsManagement({ csrfToken, selectedEstablishment
}}
title={isEditing ? 'Modification du document' : 'Ajouter un document'}
ContentComponent={() => (
<FileUpload
<FileUploadDocuSeal
handleCreateTemplateMaster={handleCreateTemplateMaster}
handleEditTemplateMaster={handleEditTemplateMaster}
fileToEdit={fileToEdit}
onSuccess={handleReloadTemplates}
/>
)}
modalClassName='w-4/5 h-4/5'
modalClassName="w-4/5 h-4/5"
/>
{/* Modal pour les groupes */}
<Modal
isOpen={isGroupModalOpen}
setIsOpen={setIsGroupModalOpen}
title={groupToEdit ? "Modifier le groupe" : "Ajouter un groupe de templateMasters"}
title={groupToEdit ? 'Modifier le groupe' : 'Ajouter un groupe de templateMasters'}
ContentComponent={() => (
<RegistrationFileGroupForm
onSubmit={handleGroupSubmit}
@ -305,56 +452,73 @@ export default function FilesGroupsManagement({ csrfToken, selectedEstablishment
/>
)}
/>
<div className="mt-8 mb-4">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-bold">Groupes de fichiers</h2>
{/* Section Groupes de fichiers */}
<div className="mt-8 mb-4 w-3/5">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center space-x-4">
<div className="bg-emerald-100 p-3 rounded-full shadow-md">
<FolderPlus className="w-8 h-8 text-emerald-600" />
</div>
<div>
<h2 className="text-2xl font-bold text-gray-800">Dossiers d'inscriptions</h2>
<p className="text-sm text-gray-500 italic">
Gérez les dossiers d'inscription pour organiser vos documents.
</p>
</div>
</div>
<button
onClick={() => setIsGroupModalOpen(true)}
className="flex items-center bg-blue-600 text-white p-2 rounded-full shadow hover:bg-blue-900 transition duration-200"
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-700 transition duration-200"
>
<FolderPlus className="w-5 h-5" />
<Plus className="w-6 h-6" />
</button>
</div>
<Table
data={groups}
columns={columnsGroups}
itemsPerPage={5}
currentPage={1}
totalPages={Math.ceil(groups.length / 5)}
/>
</div>
{groups.length > 0 && (
<div className="mt-8">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-bold">Fichiers</h2>
<div className="flex items-center gap-4">
<select
className="border rounded p-2"
value={selectedGroup || ''}
onChange={(e) => setSelectedGroup(e.target.value)}
>
<option value="">Tous les groupes</option>
{groups.map(group => (
<option key={group.id} value={group.id}>{group.name}</option>
))}
</select>
<button
onClick={() => { setIsModalOpen(true); setIsEditing(false); }}
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-900 transition duration-200"
>
<Plus className="w-5 h-5" />
</button>
{/* Section Fichiers */}
<div className="mt-8 mb-4 w-3/5">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center space-x-4">
<div className="bg-emerald-100 p-3 rounded-full shadow-md">
<Signature className="w-8 h-8 text-emerald-600" />
</div>
<div>
<h2 className="text-2xl font-bold text-gray-800">Formulaires à remplir</h2>
<p className="text-sm text-gray-500 italic">
Gérez les formulaires nécessitant une signature électronique.
</p>
</div>
</div>
<Table
data={filteredFiles}
columns={columnsFiles}
itemsPerPage={10}
currentPage={1}
totalPages={Math.ceil(filteredFiles.length / 10)}
/>
<button
onClick={() => {
setIsModalOpen(true);
setIsEditing(false);
}}
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-700 transition duration-200"
>
<Plus className="w-6 h-6" />
</button>
</div>
)}
<Table
data={filteredFiles}
columns={columnsFiles}
/>
</div>
{/* Section Pièces à fournir */}
<ParentFilesSection
parentFiles={parentFiles}
setParentFiles={setParentFiles}
groups={groups}
handleCreate={handleCreate}
handleEdit={handleEdit}
handleDelete={handleDelete}
/>
</div>
);
}