Files
n3wt-school/Front-End/src/components/Structure/Files/FilesGroupsManagement.js
2025-04-17 16:20:48 +02:00

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>
);
}