mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Gestion des pièces à fournir par les parents (configuration école)
This commit is contained in:
@ -1,101 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ToggleSwitch from '@/components/ToggleSwitch'; // Import du composant ToggleSwitch
|
||||
import DraggableFileUpload from './DraggableFileUpload';
|
||||
import { fetchRegistrationFileGroups } from '@/app/actions/registerFileGroupAction';
|
||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||
|
||||
export default function FileUpload({ onFileUpload, fileToEdit = null }) {
|
||||
const [fileName, setFileName] = useState('');
|
||||
const [file, setFile] = useState(null);
|
||||
const [isRequired, setIsRequired] = useState(false); // État pour le toggle isRequired
|
||||
const [order, setOrder] = useState(0);
|
||||
const [groups, setGroups] = useState([]);
|
||||
const [selectedGroup, setSelectedGroup] = useState('');
|
||||
|
||||
const { selectedEstablishmentId } = useEstablishment();
|
||||
|
||||
useEffect(() => {
|
||||
fetchRegistrationFileGroups(selectedEstablishmentId).then(data => setGroups(data));
|
||||
|
||||
if (fileToEdit) {
|
||||
setFileName(fileToEdit.name || '');
|
||||
setIsRequired(fileToEdit.is_required || false);
|
||||
setOrder(fileToEdit.fusion_order || 0);
|
||||
setSelectedGroup(fileToEdit.group_id || '');
|
||||
}
|
||||
}, [fileToEdit]);
|
||||
|
||||
const handleFileNameChange = (event) => {
|
||||
setFileName(event.target.value);
|
||||
};
|
||||
|
||||
const handleUpload = () => {
|
||||
onFileUpload({
|
||||
file,
|
||||
name: fileName,
|
||||
is_required: isRequired,
|
||||
order: parseInt(order, 10),
|
||||
groupId: selectedGroup || null
|
||||
});
|
||||
setFile(null);
|
||||
setFileName('');
|
||||
setIsRequired(false);
|
||||
setOrder(0);
|
||||
setSelectedGroup('');
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DraggableFileUpload
|
||||
fileName={fileName}
|
||||
onFileSelect={(selectedFile) => {
|
||||
setFile(selectedFile);
|
||||
setFileName(selectedFile.name.replace(/\.[^/.]+$/, ""));
|
||||
}}
|
||||
/>
|
||||
<div className="flex mt-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Nom du fichier"
|
||||
value={fileName}
|
||||
onChange={handleFileNameChange}
|
||||
className="flex-grow p-2 border border-gray-200 rounded-md"
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
value={order}
|
||||
onChange={(e) => setOrder(e.target.value)}
|
||||
placeholder="Ordre de fusion"
|
||||
className="p-2 border border-gray-200 rounded-md ml-2 w-20"
|
||||
/>
|
||||
<button
|
||||
onClick={handleUpload}
|
||||
className={`p-2 rounded-md shadow transition duration-200 ml-2 ${fileName !== "" ? 'bg-emerald-600 text-white hover:bg-emerald-900' : 'bg-gray-300 text-gray-500 cursor-not-allowed'}`}
|
||||
disabled={fileName === ""}
|
||||
>
|
||||
Ajouter
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center mt-4">
|
||||
<ToggleSwitch
|
||||
label="Fichier à remplir obligatoirement"
|
||||
checked={isRequired}
|
||||
onChange={() => setIsRequired(!isRequired)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label className="block mb-2">Groupe</label>
|
||||
<select
|
||||
value={selectedGroup}
|
||||
onChange={(e) => setSelectedGroup(e.target.value)}
|
||||
className="w-full border rounded p-2"
|
||||
>
|
||||
<option value="">Aucun groupe</option>
|
||||
{groups.map(group => (
|
||||
<option key={group.id} value={group.id}>{group.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -3,6 +3,11 @@ import { PhoneInput } from 'react-international-phone';
|
||||
import 'react-international-phone/style.css';
|
||||
|
||||
export default function InputPhone({ name, label, value, onChange, errorMsg, className, required }) {
|
||||
const handlePhoneChange = (phone) => {
|
||||
// Appeler onChange avec un objet personnalisé
|
||||
onChange({ target: { name, value: phone } });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${className}`}>
|
||||
<label htmlFor={name} className="block text-sm font-medium text-gray-700">
|
||||
@ -13,7 +18,7 @@ export default function InputPhone({ name, label, value, onChange, errorMsg, cla
|
||||
<PhoneInput
|
||||
defaultCountry="fr"
|
||||
value={value}
|
||||
onChange={(phone) => onChange(phone)}
|
||||
onChange={handlePhoneChange}
|
||||
inputProps={{
|
||||
name: name,
|
||||
required: required,
|
||||
|
||||
59
Front-End/src/components/Inscription/FileUpload.js
Normal file
59
Front-End/src/components/Inscription/FileUpload.js
Normal file
@ -0,0 +1,59 @@
|
||||
import React, { useState } from 'react';
|
||||
import { CloudUpload } from 'lucide-react';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function FileUpload({ selectionMessage, onFileSelect, uploadedFileName }) {
|
||||
const [localFileName, setLocalFileName] = useState(uploadedFileName || '');
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
setLocalFileName(file.name);
|
||||
logger.debug('Fichier sélectionné:', file.name);
|
||||
onFileSelect(file); // Appelle la fonction passée en prop
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileDrop = (e) => {
|
||||
e.preventDefault();
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file) {
|
||||
setLocalFileName(file.name);
|
||||
logger.debug('Fichier déposé:', file.name);
|
||||
onFileSelect(file); // Appelle la fonction passée en prop
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border p-4 rounded-md shadow-md">
|
||||
<h3 className="text-lg font-semibold mb-4">{`${selectionMessage}`}</h3>
|
||||
<div
|
||||
className="border-2 border-dashed border-gray-500 p-6 rounded-lg flex flex-col items-center justify-center cursor-pointer hover:border-emerald-500"
|
||||
onClick={() => document.getElementById('fileInput').click()} // Ouvre l'explorateur de fichiers au clic
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
onDrop={handleFileDrop}
|
||||
>
|
||||
<CloudUpload className="w-12 h-12 text-emerald-500 mb-4" /> {/* Icône de cloud */}
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
onChange={handleFileChange}
|
||||
className="hidden"
|
||||
id="fileInput"
|
||||
/>
|
||||
<label htmlFor="fileInput" className="text-center text-gray-500">
|
||||
<p className="text-lg font-semibold text-gray-800">Déposez votre fichier ici</p>
|
||||
<p className="text-sm text-gray-500 mt-2">ou cliquez pour sélectionner un fichier PDF</p>
|
||||
</label>
|
||||
</div>
|
||||
{localFileName && (
|
||||
<div className="mt-4 flex items-center space-x-4 bg-gray-100 p-3 rounded-md shadow-sm">
|
||||
<CloudUpload className="w-6 h-6 text-emerald-500" />
|
||||
<p className="text-sm font-medium text-gray-800">
|
||||
<span className="font-semibold">{localFileName}</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -120,7 +120,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value, type } = e.target;
|
||||
const { name, value } = e.target;
|
||||
setFormData((prevState) => ({
|
||||
...prevState,
|
||||
[name]: value,
|
||||
@ -365,7 +365,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
||||
/>
|
||||
<InputPhone
|
||||
name="guardianPhone"
|
||||
label={t('Numéro de téléphone (optionnel)')}
|
||||
label="Numéro de téléphone (optionnel)"
|
||||
value={formData.guardianPhone}
|
||||
onChange={handleChange}
|
||||
className="w-full mt-4"
|
||||
|
||||
@ -21,6 +21,7 @@ import logger from '@/utils/logger';
|
||||
import StudentInfoForm, { validateStudentInfo } from '@/components/Inscription/StudentInfoForm';
|
||||
import FilesToUpload from '@/components/Inscription/FilesToUpload';
|
||||
import { DocusealForm } from '@docuseal/react';
|
||||
import FileUpload from '@/components/Inscription/FileUpload';
|
||||
|
||||
/**
|
||||
* Composant de formulaire d'inscription partagé
|
||||
@ -385,6 +386,14 @@ export default function InscriptionFormShared({
|
||||
fileTemplates={fileTemplates.filter(template => !template.is_required)}
|
||||
columns={columns}
|
||||
/>
|
||||
<FileUpload
|
||||
selectionMessage='Sélectionnez un fichier'
|
||||
onFileSelect={(file) => {
|
||||
setUploadedFiles(file.name); // Stocke uniquement le nom du fichier
|
||||
logger.debug('Fichier sélectionné:', file.name);
|
||||
}}
|
||||
uploadedFileName={uploadedFiles}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { BASE_URL } from '@/utils/Url';
|
||||
import { generateToken } from '@/app/actions/registerFileGroupAction';
|
||||
import logger from '@/utils/logger';
|
||||
import { GraduationCap, CloudUpload } from 'lucide-react';
|
||||
import FileUpload from '@/components/Inscription/FileUpload';
|
||||
|
||||
export default function ValidateSubscription({ studentId, firstName, lastName, paymentMode, file, onAccept }) {
|
||||
const [token, setToken] = useState(null);
|
||||
@ -100,47 +101,14 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
)}
|
||||
|
||||
{currentPage === 2 && isSepa && (
|
||||
<div className="border p-4 rounded-md shadow-md">
|
||||
<h3 className="text-lg font-semibold mb-4">Sélection du mandat de pélèvement SEPA</h3>
|
||||
<div
|
||||
className="border-2 border-dashed border-gray-500 p-6 rounded-lg flex flex-col items-center justify-center cursor-pointer hover:border-emerald-500"
|
||||
onClick={() => document.getElementById('fileInput').click()} // Ouvre l'explorateur de fichiers au clic
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
onDrop={(e) => {
|
||||
e.preventDefault();
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file) {
|
||||
setUploadedFileName(file.name); // Stocke uniquement le nom du fichier
|
||||
logger.debug('Fichier déposé:', file.name);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CloudUpload className="w-12 h-12 text-emerald-500 mb-4" /> {/* Icône de cloud */}
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
setUploadedFileName(file.name); // Stocke uniquement le nom du fichier
|
||||
logger.debug('Fichier sélectionné:', file.name);
|
||||
}
|
||||
}}
|
||||
className="hidden"
|
||||
id="fileInput"
|
||||
/>
|
||||
<label htmlFor="fileInput" className="text-center text-gray-500">
|
||||
<p className="text-lg font-semibold text-gray-800">Déposez votre fichier ici</p>
|
||||
<p className="text-sm text-gray-500 mt-2">ou cliquez pour sélectionner un fichier PDF</p>
|
||||
</label>
|
||||
</div>
|
||||
{uploadedFileName && (
|
||||
<div className="mt-4 flex items-center space-x-4 bg-gray-100 p-3 rounded-md shadow-sm">
|
||||
<CloudUpload className="w-6 h-6 text-emerald-500" />
|
||||
<p className="text-sm font-medium text-gray-800"><span className="font-semibold">{uploadedFileName}</span></p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<FileUpload
|
||||
selectionMessage='Sélectionnez un mandat de prélèvement SEPA'
|
||||
onFileSelect={(file) => {
|
||||
setUploadedFileName(file.name); // Stocke uniquement le nom du fichier
|
||||
logger.debug('Fichier sélectionné:', file.name);
|
||||
}}
|
||||
uploadedFileName={uploadedFileName}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Boutons de navigation */}
|
||||
|
||||
@ -6,6 +6,7 @@ import InputTextWithColorIcon from '@/components/InputTextWithColorIcon';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem';
|
||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, handleEdit, handleDelete }) => {
|
||||
@ -21,6 +22,8 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand
|
||||
const [removePopupMessage, setRemovePopupMessage] = useState("");
|
||||
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
|
||||
|
||||
const { selectedEstablishmentId } = useEstablishment();
|
||||
|
||||
// Récupération des messages d'erreur
|
||||
const getError = (field) => {
|
||||
return localErrors?.[field]?.[0];
|
||||
@ -41,9 +44,14 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand
|
||||
};
|
||||
|
||||
const handleSaveNewSpeciality = () => {
|
||||
if (
|
||||
newSpeciality.name) {
|
||||
handleCreate(newSpeciality)
|
||||
if (newSpeciality.name) {
|
||||
// Ajouter l'ID de l'établissement à la nouvelle spécialité
|
||||
const specialityData = {
|
||||
...newSpeciality,
|
||||
establishment: selectedEstablishmentId, // Inclure l'ID de l'établissement
|
||||
};
|
||||
|
||||
handleCreate(specialityData)
|
||||
.then((createdSpeciality) => {
|
||||
setSpecialities([createdSpeciality, ...specialities]);
|
||||
setNewSpeciality(null);
|
||||
@ -52,8 +60,8 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand
|
||||
.catch((error) => {
|
||||
logger.error('Error:', error.message);
|
||||
if (error.details) {
|
||||
logger.error('Form errors:', error.details);
|
||||
setLocalErrors(error.details);
|
||||
logger.error('Form errors:', error.details);
|
||||
setLocalErrors(error.details);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@ -9,7 +9,7 @@ import MultiSelect from '@/components/MultiSelect'; // Import du composant Multi
|
||||
import { useCsrfToken } from '@/context/CsrfContext';
|
||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||
|
||||
export default function FileUpload({ handleCreateTemplateMaster, handleEditTemplateMaster, fileToEdit = null, onSuccess }) {
|
||||
export default function FileUploadDocuSeal({ handleCreateTemplateMaster, handleEditTemplateMaster, fileToEdit = null, onSuccess }) {
|
||||
const [isRequired, setIsRequired] = useState(false); // État pour le toggle isRequired
|
||||
const [order, setOrder] = useState(0);
|
||||
const [groups, setGroups] = useState([]);
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,343 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Plus, Download, Edit, Trash2, FolderPlus, Signature } from 'lucide-react';
|
||||
import Modal from '@/components/Modal';
|
||||
import Table from '@/components/Table';
|
||||
import FileUpload from '@/components/FileUpload';
|
||||
import { formatDate } from '@/utils/Date';
|
||||
import { BASE_URL } from '@/utils/Url';
|
||||
import {
|
||||
fetchRegisterFormFileTemplate,
|
||||
createRegistrationFormFileTemplate,
|
||||
editRegistrationFormFileTemplate,
|
||||
deleteRegisterFormFileTemplate,
|
||||
getRegisterFormFileTemplate
|
||||
} from '@/app/actions/subscriptionAction';
|
||||
import {
|
||||
fetchRegistrationFileGroups,
|
||||
createRegistrationFileGroup,
|
||||
deleteRegistrationFileGroup,
|
||||
editRegistrationFileGroup
|
||||
} from '@/app/actions/registerFileGroupAction';
|
||||
import RegistrationFileGroupForm from '@/components/RegistrationFileGroupForm';
|
||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||
|
||||
export default function FilesManagement({ csrfToken }) {
|
||||
const [fichiers, setFichiers] = 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 { selectedEstablishmentId } = useEstablishment();
|
||||
|
||||
// Fonction pour transformer les données des fichiers avec les informations complètes du groupe
|
||||
const transformFileData = (file, groups) => {
|
||||
if (!file.group) return file;
|
||||
|
||||
const groupInfo = groups.find(g => g.id === file.group);
|
||||
return {
|
||||
...file,
|
||||
group: groupInfo || { id: file.group, name: 'Groupe inconnu' }
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
fetchRegisterFormFileTemplate(),
|
||||
fetchRegistrationFileGroups(selectedEstablishmentId)
|
||||
]).then(([filesData, groupsData]) => {
|
||||
setGroups(groupsData);
|
||||
// Sélectionner automatiquement le premier groupe s'il existe
|
||||
if (groupsData.length > 0) {
|
||||
setSelectedGroup(groupsData[0].id.toString());
|
||||
}
|
||||
// Transformer chaque fichier pour inclure les informations complètes du groupe
|
||||
const transformedFiles = filesData.map(file => transformFileData(file, groupsData));
|
||||
setFichiers(transformedFiles);
|
||||
}).catch(err => {
|
||||
console.log(err.message);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleFileDelete = (fileId) => {
|
||||
deleteRegisterFormFileTemplate(fileId, csrfToken)
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
setFichiers(fichiers.filter(fichier => fichier.id !== fileId));
|
||||
alert('Fichier supprimé avec succès.');
|
||||
} else {
|
||||
alert('Erreur lors de la suppression du fichier.');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error deleting file:', error);
|
||||
alert('Erreur lors de la suppression du fichier.');
|
||||
});
|
||||
};
|
||||
|
||||
const handleFileEdit = (file) => {
|
||||
setIsEditing(true);
|
||||
setFileToEdit(file);
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const handleFileUpload = ({file, name, is_required, order, groupId}) => {
|
||||
if (!name) {
|
||||
alert('Veuillez entrer un nom de fichier.');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
if(file) {
|
||||
formData.append('file', file);
|
||||
}
|
||||
formData.append('name', name);
|
||||
formData.append('is_required', is_required);
|
||||
formData.append('order', order);
|
||||
|
||||
// Modification ici : vérifier si groupId existe et n'est pas vide
|
||||
if (groupId && groupId !== '') {
|
||||
formData.append('group', groupId); // Notez que le nom du champ est 'group' et non 'group_id'
|
||||
}
|
||||
|
||||
if (isEditing && fileToEdit) {
|
||||
editRegistrationFormFileTemplate(fileToEdit.id, formData, csrfToken)
|
||||
.then(data => {
|
||||
// Transformer le fichier mis à jour avec les informations du groupe
|
||||
const transformedFile = transformFileData(data, groups);
|
||||
setFichiers(prevFichiers =>
|
||||
prevFichiers.map(f => f.id === fileToEdit.id ? transformedFile : f)
|
||||
);
|
||||
setIsModalOpen(false);
|
||||
setFileToEdit(null);
|
||||
setIsEditing(false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error editing file:', error);
|
||||
alert('Erreur lors de la modification du fichier');
|
||||
});
|
||||
} else {
|
||||
createRegistrationFormFileTemplate(formData, csrfToken)
|
||||
.then(data => {
|
||||
// Transformer le nouveau fichier avec les informations du groupe
|
||||
const transformedFile = transformFileData(data, groups);
|
||||
setFichiers(prevFiles => [...prevFiles, transformedFile]);
|
||||
setIsModalOpen(false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error uploading file:', error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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 {
|
||||
createRegistrationFileGroup(groupData, 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 fichiers utilisent ce groupe
|
||||
const filesInGroup = fichiers.filter(file => file.group && file.group.id === groupId);
|
||||
if (filesInGroup.length > 0) {
|
||||
alert('Impossible de supprimer ce groupe car il contient des fichiers. Veuillez d\'abord retirer tous les fichiers 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.');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Ajouter cette fonction de filtrage
|
||||
const filteredFiles = fichiers.filter(file => {
|
||||
if (!selectedGroup) return true;
|
||||
return file.group && file.group.id === parseInt(selectedGroup);
|
||||
});
|
||||
|
||||
const columnsFiles = [
|
||||
{ name: 'Nom du fichier', transform: (row) => row.name },
|
||||
{ name: 'Groupe', transform: (row) => row.group ? row.group.name : 'Aucun' },
|
||||
{ name: 'Date de création', transform: (row) => formatDate(new Date (row.date_added),"DD/MM/YYYY hh:mm:ss") },
|
||||
{ name: 'Fichier Obligatoire', transform: (row) => row.is_required ? 'Oui' : 'Non' },
|
||||
{ name: 'Ordre de fusion', transform: (row) => row.order },
|
||||
{ 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 size={16} />
|
||||
</a>
|
||||
)}
|
||||
<button onClick={() => handleFileEdit(row)} className="text-blue-500 hover:text-blue-700">
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
<button onClick={() => handleFileDelete(row.id)} className="text-red-500 hover:text-red-700">
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
<button onClick={() => handleSignatureRequest(row)} className="text-green-500 hover:text-green-700">
|
||||
<Signature size={16} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
];
|
||||
|
||||
const columnsGroups = [
|
||||
{ name: 'Nom du groupe', 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">
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
<button onClick={() => handleGroupDelete(row.id)} className="text-red-500 hover:text-red-700">
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
];
|
||||
|
||||
// Fonction pour gérer la demande de signature
|
||||
const handleSignatureRequest = (file) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
console.log('Demande de signature pour le fichier :', file);
|
||||
|
||||
fetch('http://localhost:8080:/DocuSeal/generateToken', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer NFPZy6BBGvYs1BwTuXMQ3XAu5N1kLFiXWftGQhkiz2A',
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Erreur lors du téléversement du document : ' + response.statusText);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
const documentId = data.documentId;
|
||||
console.log('Document téléversé avec succès, ID :', documentId);
|
||||
onUpload(documentId);
|
||||
});
|
||||
.catch((error) => console.error(error));
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
setIsOpen={setIsModalOpen}
|
||||
title={isEditing ? 'Modifier un fichier' : 'Ajouter un fichier'}
|
||||
ContentComponent={() => (
|
||||
<FileUpload
|
||||
onFileUpload={handleFileUpload}
|
||||
fileToEdit={fileToEdit}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Modal
|
||||
isOpen={isGroupModalOpen}
|
||||
setIsOpen={setIsGroupModalOpen}
|
||||
title={groupToEdit ? "Modifier le groupe" : "Ajouter un groupe de fichiers"}
|
||||
ContentComponent={() => (
|
||||
<RegistrationFileGroupForm
|
||||
onSubmit={handleGroupSubmit}
|
||||
initialData={groupToEdit}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<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>
|
||||
<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"
|
||||
>
|
||||
<FolderPlus className="w-5 h-5" />
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
<Table
|
||||
data={filteredFiles}
|
||||
columns={columnsFiles}
|
||||
itemsPerPage={10}
|
||||
currentPage={1}
|
||||
totalPages={Math.ceil(filteredFiles.length / 10)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
246
Front-End/src/components/Structure/Files/ParentFilesSection.js
Normal file
246
Front-End/src/components/Structure/Files/ParentFilesSection.js
Normal file
@ -0,0 +1,246 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Plus, Edit3, Trash2, Check, X, FileText } from 'lucide-react';
|
||||
import Table from '@/components/Table';
|
||||
import InputText from '@/components/InputText';
|
||||
import MultiSelect from '@/components/MultiSelect';
|
||||
import Popup from '@/components/Popup';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function ParentFilesSection({ parentFiles, groups, handleCreate, handleEdit, handleDelete }) {
|
||||
const [editingDocumentId, setEditingDocumentId] = useState(null);
|
||||
const [formData, setFormData] = useState(null);
|
||||
const [selectedGroups, setSelectedGroups] = useState([]); // Gestion des groupes sélectionnés
|
||||
|
||||
const [popupVisible, setPopupVisible] = useState(false);
|
||||
const [popupMessage, setPopupMessage] = useState("");
|
||||
const [removePopupVisible, setRemovePopupVisible] = useState(false);
|
||||
const [removePopupMessage, setRemovePopupMessage] = useState("");
|
||||
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
|
||||
|
||||
const handleAddEmptyRequiredDocument = () => {
|
||||
setEditingDocumentId('new');
|
||||
setFormData({ name: '', description: '', groups: [] });
|
||||
setSelectedGroups([]); // Réinitialiser les groupes sélectionnés
|
||||
};
|
||||
|
||||
const handleEditDocument = (document) => {
|
||||
setEditingDocumentId(document.id);
|
||||
setFormData(document);
|
||||
const initialSelectedGroups = document.groups.map((groupId) =>
|
||||
groups.find((group) => group.id === groupId)
|
||||
);
|
||||
setSelectedGroups(initialSelectedGroups);
|
||||
};
|
||||
|
||||
const handleSaveDocument = () => {
|
||||
if (!formData.name) {
|
||||
alert('Le nom de la pièce est requis.');
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedFormData = {
|
||||
...formData,
|
||||
groups: selectedGroups.map((group) => group.id),
|
||||
};
|
||||
|
||||
if (editingDocumentId === 'new') {
|
||||
handleCreate(updatedFormData).then(() => {
|
||||
setEditingDocumentId(null);
|
||||
setFormData(null);
|
||||
setSelectedGroups([]);
|
||||
});
|
||||
} else {
|
||||
handleEdit(editingDocumentId, updatedFormData).then(() => {
|
||||
setEditingDocumentId(null);
|
||||
setFormData(null);
|
||||
setSelectedGroups([]);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveDocument = (id) => {
|
||||
return handleDelete(id)
|
||||
.then(() => {
|
||||
setEditingDocumentId(null);
|
||||
setFormData(null);
|
||||
setSelectedGroups([]);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancelEdit = () => {
|
||||
setEditingDocumentId(null);
|
||||
setFormData(null);
|
||||
setSelectedGroups([]);
|
||||
};
|
||||
|
||||
const handleGroupChange = (selected) => {
|
||||
setSelectedGroups(selected);
|
||||
};
|
||||
|
||||
const renderRequiredDocumentCell = (document, column) => {
|
||||
const isEditing = editingDocumentId === document.id || (editingDocumentId === 'new' && !document.id);
|
||||
|
||||
if (isEditing) {
|
||||
switch (column) {
|
||||
case 'Nom de la pièce':
|
||||
return (
|
||||
<InputText
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
placeholder="Nom de la pièce"
|
||||
className="w-full"
|
||||
/>
|
||||
);
|
||||
case 'Description':
|
||||
return (
|
||||
<InputText
|
||||
name="description"
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||
placeholder="Description"
|
||||
className="w-full"
|
||||
/>
|
||||
);
|
||||
case 'Dossiers d\'inscription':
|
||||
return (
|
||||
<MultiSelect
|
||||
name="groups"
|
||||
label="Sélection de groupes de fichiers"
|
||||
options={groups.map((group) => ({ id: group.id, name: group.name }))}
|
||||
selectedOptions={selectedGroups}
|
||||
onChange={handleGroupChange}
|
||||
errorMsg={null}
|
||||
/>
|
||||
);
|
||||
case 'Actions':
|
||||
return (
|
||||
<div className="flex justify-center space-x-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSaveDocument}
|
||||
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 'Dossiers d\'inscription':
|
||||
return (
|
||||
<span>
|
||||
{document.groups
|
||||
.map((groupId) => groups.find((group) => group.id === groupId)?.name || 'Dossiers d\'inscription inconnu')
|
||||
.join(', ')}
|
||||
</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={() => {
|
||||
setRemovePopupVisible(true);
|
||||
setRemovePopupMessage(
|
||||
`Attentions ! \nVous êtes sur le point de supprimer le document "${document.name}".\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`
|
||||
);
|
||||
setRemovePopupOnConfirm(() => () => {
|
||||
handleRemoveDocument(document.id)
|
||||
.then(() => {
|
||||
setPopupMessage(`Le document "${document.name}" a été correctement supprimé.`);
|
||||
setPopupVisible(true);
|
||||
setRemovePopupVisible(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Erreur lors de la suppression du document:', error);
|
||||
setPopupMessage(`Erreur lors de la suppression du document "${document.name}".`);
|
||||
setPopupVisible(true);
|
||||
setRemovePopupVisible(false);
|
||||
});
|
||||
});
|
||||
}}
|
||||
className="text-red-500 hover:text-red-700"
|
||||
>
|
||||
<Trash2 className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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: 'Dossiers d\'inscription', transform: (row) => renderRequiredDocumentCell(row, 'Dossiers d\'inscription') },
|
||||
{ name: 'Actions', transform: (row) => renderRequiredDocumentCell(row, 'Actions') },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="mt-8 mb-4 w-4/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">
|
||||
<FileText className="w-8 h-8 text-emerald-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-800">Pièces à fournir</h2>
|
||||
<p className="text-sm text-gray-500 italic">
|
||||
Configurez la liste des documents que les parents doivent fournir.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddEmptyRequiredDocument}
|
||||
className="text-emerald-500 hover:text-emerald-700"
|
||||
>
|
||||
<Plus className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
<Table
|
||||
data={editingDocumentId === 'new' ? [formData, ...parentFiles] : parentFiles}
|
||||
columns={columnsRequiredDocuments}
|
||||
/>
|
||||
<Popup
|
||||
visible={popupVisible}
|
||||
message={popupMessage}
|
||||
onConfirm={() => setPopupVisible(false)}
|
||||
onCancel={() => setPopupVisible(false)}
|
||||
uniqueConfirmButton={true}
|
||||
/>
|
||||
<Popup
|
||||
visible={removePopupVisible}
|
||||
message={removePopupMessage}
|
||||
onConfirm={removePopupOnConfirm}
|
||||
onCancel={() => setRemovePopupVisible(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user