mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
528 lines
19 KiB
JavaScript
528 lines
19 KiB
JavaScript
import React, { useState, useEffect } from '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 FileUploadDocuSeal from '@/components/Structure/Files/FileUploadDocuSeal';
|
|
import { BASE_URL } from '@/utils/Url';
|
|
import {
|
|
// GET
|
|
fetchRegistrationFileGroups,
|
|
fetchRegistrationSchoolFileMasters,
|
|
fetchRegistrationSchoolFileTemplates,
|
|
fetchRegistrationParentFileMasters,
|
|
// POST
|
|
createRegistrationFileGroup,
|
|
createRegistrationSchoolFileMaster,
|
|
createRegistrationParentFileMaster,
|
|
// PUT
|
|
editRegistrationFileGroup,
|
|
editRegistrationSchoolFileMaster,
|
|
editRegistrationParentFileMaster,
|
|
// DELETE
|
|
deleteRegistrationFileGroup,
|
|
deleteRegistrationSchoolFileMaster,
|
|
deleteRegistrationParentFileMaster,
|
|
} 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 [schoolFileMasters, setSchoolFileMasters] = useState([]);
|
|
const [schoolFileTemplates, setSchoolFileTemplates] = useState([]);
|
|
const [parentFiles, setParentFileMasters] = useState([]);
|
|
const [groups, setGroups] = useState([]);
|
|
const [selectedGroup, setSelectedGroup] = useState(null);
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [isEditing, setIsEditing] = useState(false);
|
|
const [fileToEdit, setFileToEdit] = useState(null);
|
|
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 handleReloadTemplates = () => {
|
|
setReloadTemplates(true);
|
|
}
|
|
|
|
const transformFileData = (file, groups) => {
|
|
const groupInfos = file.groups.map(groupId => groups.find(g => g.id === groupId) || { id: groupId, name: 'Groupe inconnu' });
|
|
return {
|
|
...file,
|
|
groups: groupInfos
|
|
};
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (selectedEstablishmentId) {
|
|
Promise.all([
|
|
fetchRegistrationSchoolFileMasters(),
|
|
fetchRegistrationFileGroups(selectedEstablishmentId),
|
|
fetchRegistrationSchoolFileTemplates(),
|
|
fetchRegistrationParentFileMasters()
|
|
]).then(([dataSchoolFileMasters, groupsData, dataSchoolFileTemplates, dataParentFileMasters]) => {
|
|
setGroups(groupsData);
|
|
setSchoolFileTemplates(dataSchoolFileTemplates);
|
|
setParentFileMasters(dataParentFileMasters);
|
|
// Transformer chaque fichier pour inclure les informations complètes du groupe
|
|
const transformedFiles = dataSchoolFileMasters.map(file => transformFileData(file, groupsData));
|
|
setSchoolFileMasters(transformedFiles);
|
|
}).catch(err => {
|
|
console.log(err.message);
|
|
}).finally(() => {
|
|
setReloadTemplates(false);
|
|
});
|
|
}
|
|
}, [reloadTemplates, selectedEstablishmentId]);
|
|
|
|
const deleteTemplateMaster = (templateMaster) => {
|
|
// Supprimer les clones associés via l'API DocuSeal
|
|
const removeClonesPromises = schoolFileTemplates
|
|
.filter(template => template.master === templateMaster.id)
|
|
.map(template => removeTemplate(template.id));
|
|
|
|
// Ajouter la suppression du master à la liste des promesses
|
|
removeClonesPromises.push(removeTemplate(templateMaster.id));
|
|
|
|
// Attendre que toutes les suppressions dans DocuSeal soient terminées
|
|
Promise.all(removeClonesPromises)
|
|
.then(responses => {
|
|
const allSuccessful = responses.every(response => response.ok);
|
|
if (allSuccessful) {
|
|
logger.debug('Master et clones supprimés avec succès de DocuSeal.');
|
|
|
|
// Supprimer le template master de la base de données
|
|
deleteRegistrationSchoolFileMaster(templateMaster.id, csrfToken)
|
|
.then(response => {
|
|
if (response.ok) {
|
|
setSchoolFileMasters(schoolFileMasters.filter(fichier => fichier.id !== templateMaster.id));
|
|
alert('Fichier supprimé avec succès.');
|
|
} else {
|
|
alert('Erreur lors de la suppression du fichier dans la base de données.');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error deleting file from database:', error);
|
|
alert('Erreur lors de la suppression du fichier dans la base de données.');
|
|
});
|
|
} else {
|
|
alert('Erreur lors de la suppression du master ou des clones dans DocuSeal.');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error removing template from DocuSeal:', error);
|
|
alert('Erreur lors de la suppression du master ou des clones dans DocuSeal.');
|
|
});
|
|
};
|
|
|
|
const removeTemplate = (templateId) => {
|
|
return fetch('/api/docuseal/removeTemplate/', {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': csrfToken
|
|
},
|
|
body: JSON.stringify({
|
|
templateId
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
return response.json().then(err => { throw new Error(err.message); });
|
|
}
|
|
return response;
|
|
})
|
|
.catch(error => {
|
|
console.error('Error removing template:', error);
|
|
throw error;
|
|
});
|
|
};
|
|
|
|
const editTemplateMaster = (file) => {
|
|
setIsEditing(true);
|
|
setFileToEdit(file);
|
|
setIsModalOpen(true);
|
|
};
|
|
|
|
const handleCreateTemplateMaster = ({name, group_ids, id, is_required}) => {
|
|
const data = {
|
|
name: name,
|
|
id: id,
|
|
groups: group_ids,
|
|
is_required: is_required
|
|
};
|
|
logger.debug(data);
|
|
|
|
createRegistrationSchoolFileMaster(data, csrfToken)
|
|
.then(data => {
|
|
// Transformer le nouveau fichier avec les informations du groupe
|
|
const transformedFile = transformFileData(data, groups);
|
|
setSchoolFileMasters(prevFiles => [...prevFiles, transformedFile]);
|
|
setIsModalOpen(false);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error uploading file:', error);
|
|
});
|
|
};
|
|
|
|
const handleEditTemplateMaster = ({name, group_ids, id, is_required}) => {
|
|
const data = {
|
|
name: name,
|
|
id: id,
|
|
groups: group_ids,
|
|
is_required: is_required
|
|
};
|
|
logger.debug(data);
|
|
|
|
editRegistrationSchoolFileMaster(id, data, csrfToken)
|
|
.then(data => {
|
|
// Transformer le fichier mis à jour avec les informations du groupe
|
|
const transformedFile = transformFileData(data, groups);
|
|
setSchoolFileMasters(prevFichiers =>
|
|
prevFichiers.map(f => f.id === id ? transformedFile : f)
|
|
);
|
|
setIsModalOpen(false);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error editing file:', error);
|
|
alert('Erreur lors de la modification du fichier');
|
|
});
|
|
};
|
|
|
|
const handleGroupSubmit = (groupData) => {
|
|
if (groupToEdit) {
|
|
editRegistrationFileGroup(groupToEdit.id, groupData, csrfToken)
|
|
.then(updatedGroup => {
|
|
setGroups(groups.map(group => group.id === groupToEdit.id ? updatedGroup : group));
|
|
setGroupToEdit(null);
|
|
setIsGroupModalOpen(false);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error handling group:', error);
|
|
alert('Erreur lors de l\'opération sur le groupe');
|
|
});
|
|
} else {
|
|
// 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);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error handling group:', error);
|
|
alert('Erreur lors de l\'opération sur le groupe');
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleGroupEdit = (group) => {
|
|
setGroupToEdit(group);
|
|
setIsGroupModalOpen(true);
|
|
};
|
|
|
|
const handleGroupDelete = (groupId) => {
|
|
// Vérifier si des schoolFileMasters utilisent ce groupe
|
|
const filesInGroup = schoolFileMasters.filter(file => file.group && file.group.id === groupId);
|
|
if (filesInGroup.length > 0) {
|
|
alert('Impossible de supprimer ce groupe car il contient des schoolFileMasters. Veuillez d\'abord retirer tous les schoolFileMasters de ce groupe.');
|
|
return;
|
|
}
|
|
|
|
if (window.confirm('Êtes-vous sûr de vouloir supprimer ce groupe ?')) {
|
|
deleteRegistrationFileGroup(groupId, csrfToken)
|
|
.then((response) => {
|
|
if (response.status === 409) {
|
|
throw new Error('Ce groupe est lié à des inscriptions existantes.');
|
|
}
|
|
if (!response.ok) {
|
|
throw new Error('Erreur lors de la suppression du groupe.');
|
|
}
|
|
setGroups(groups.filter(group => group.id !== groupId));
|
|
alert('Groupe supprimé avec succès.');
|
|
})
|
|
.catch(error => {
|
|
console.error('Error deleting group:', error);
|
|
alert(error.message || 'Erreur lors de la suppression du groupe. Vérifiez qu\'aucune inscription n\'utilise ce groupe.');
|
|
});
|
|
}
|
|
};
|
|
|
|
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 createRegistrationParentFileMaster(newParentFile, csrfToken)
|
|
.then((createdFile) => {
|
|
// Ajouter le nouveau fichier parent à la liste existante
|
|
setParentFileMasters((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 editRegistrationParentFileMaster(id, updatedFile, csrfToken)
|
|
.then((response) => {
|
|
const modifiedFile = response.data; // Extraire les données mises à jour
|
|
// Mettre à jour la liste des fichiers parents
|
|
setParentFileMasters((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 deleteRegistrationParentFileMaster(id, csrfToken)
|
|
.then(() => {
|
|
// Mettre à jour la liste des fichiers parents en supprimant l'élément correspondant
|
|
setParentFileMasters((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 = schoolFileMasters.filter(file => {
|
|
if (!selectedGroup) return true;
|
|
return file.groups && file.groups.some(group => group.id === parseInt(selectedGroup));
|
|
});
|
|
|
|
const columnsFiles = [
|
|
{ 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 && (
|
|
<a href={`${BASE_URL}${row.file}`} target='_blank' className="text-blue-500 hover:text-blue-700">
|
|
<Download className="w-5 h-5" />
|
|
</a>
|
|
)}
|
|
<button onClick={() => editTemplateMaster(row)} className="text-blue-500 hover:text-blue-700">
|
|
<Edit3 className="w-5 h-5" />
|
|
</button>
|
|
<button onClick={() => deleteTemplateMaster(row)} className="text-red-500 hover:text-red-700">
|
|
<Trash2 className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
];
|
|
|
|
const columnsGroups = [
|
|
{ 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">
|
|
<button onClick={() => handleGroupEdit(row)} className="text-blue-500 hover:text-blue-700">
|
|
<Edit3 className="w-5 h-5" />
|
|
</button>
|
|
<button onClick={() => handleGroupDelete(row.id)} className="text-red-500 hover:text-red-700">
|
|
<Trash2 className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
];
|
|
|
|
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 className="space-y-12">
|
|
{/* Modal pour les fichiers */}
|
|
<Modal
|
|
isOpen={isModalOpen}
|
|
setIsOpen={(isOpen) => {
|
|
setIsModalOpen(isOpen);
|
|
if (!isOpen) {
|
|
setFileToEdit(null);
|
|
}
|
|
}}
|
|
title={isEditing ? 'Modification du document' : 'Ajouter un document'}
|
|
ContentComponent={() => (
|
|
<FileUploadDocuSeal
|
|
handleCreateTemplateMaster={handleCreateTemplateMaster}
|
|
handleEditTemplateMaster={handleEditTemplateMaster}
|
|
fileToEdit={fileToEdit}
|
|
onSuccess={handleReloadTemplates}
|
|
/>
|
|
)}
|
|
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 schoolFileMasters'}
|
|
ContentComponent={() => (
|
|
<RegistrationFileGroupForm
|
|
onSubmit={handleGroupSubmit}
|
|
initialData={groupToEdit}
|
|
/>
|
|
)}
|
|
/>
|
|
|
|
{/* 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-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={groups}
|
|
columns={columnsGroups}
|
|
/>
|
|
</div>
|
|
|
|
{/* 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>
|
|
<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}
|
|
setParentFileMasters={setParentFileMasters}
|
|
groups={groups}
|
|
handleCreate={handleCreate}
|
|
handleEdit={handleEdit}
|
|
handleDelete={handleDelete}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|