Files
n3wt-school/Front-End/src/components/Structure/Files/ParentFilesSection.js
N3WT DE COMPET 12f5fc7aa9 feat: Changement du rendu de la page des documents + gestion des
formulaires d'école déjà existants [N3WTS-17]
2026-01-03 17:49:25 +01:00

364 lines
12 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import { Plus, Edit3, Trash2, Check, X, FileText } from 'lucide-react';
import logger from '@/utils/logger';
import { createRegistrationParentFileTemplate } from '@/app/actions/registerFileGroupAction';
import { useCsrfToken } from '@/context/CsrfContext';
import { useNotification } from '@/context/NotificationContext';
import Popup from '@/components/Popup';
import InputText from '@/components/Form/InputText';
import MultiSelect from '@/components/Form/MultiSelect';
import ToggleSwitch from '@/components/Form/ToggleSwitch';
export default function ParentFilesSection({
parentFiles,
groups,
handleCreate,
handleEdit,
handleDelete,
hideCreateButton = false,
tableContainerClass = '',
headerClassName = '',
TableComponent,
SectionHeaderComponent,
}) {
const [editingDocumentId, setEditingDocumentId] = useState(null);
const [formData, setFormData] = useState(null);
const [selectedGroups, setSelectedGroups] = useState([]); // Gestion des groupes sélectionnés
const [guardianDetails, setGuardianDetails] = useState([]);
const [removePopupVisible, setRemovePopupVisible] = useState(false);
const [removePopupMessage, setRemovePopupMessage] = useState('');
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
const csrfToken = useCsrfToken();
const { showNotification } = useNotification();
const handleAddEmptyRequiredDocument = () => {
setEditingDocumentId('new');
setFormData({ name: '', description: '', groups: [], is_required: false }); // Add is_required
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) {
showNotification(
"Veuillez saisir un nom de document pour valider l'opération",
'error',
'Erreur'
);
return;
}
if (selectedGroups.length === 0) {
showNotification(
"Veuillez sélectionner au moins un dossier d'inscription pour valider l'opération",
'error',
'Erreur'
);
return;
}
const updatedFormData = {
...formData,
groups: selectedGroups.map((group) => group.id),
};
if (editingDocumentId === 'new') {
handleCreate(updatedFormData).then((createdDocument) => {
setEditingDocumentId(null);
setFormData(null);
setSelectedGroups([]);
guardianDetails.forEach((guardian) => {
// Création des templates
const data = {
master: createdDocument?.id,
registration_form: guardian.registration_form,
};
createRegistrationParentFileTemplate(data, csrfToken)
.then((response) => {
logger.debug('Template enregistré avec succès:', response);
})
.catch((error) => {
logger.error(
"Erreur lors de l'enregistrement du template:",
error
);
});
});
});
} 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);
// Extraire les guardians associés aux register_forms des groupes sélectionnés
const details = selected.flatMap((group) =>
group.registration_forms.flatMap((form) =>
form.guardians.map((guardian) => ({
email: guardian.associated_profile_email,
last_name: form.last_name, // Extraire depuis form
first_name: form.first_name, // Extraire depuis form
registration_form: form.student_id, // Utiliser student_id comme ID du register_form
}))
)
);
setGuardianDetails(details); // Mettre à jour la variable d'état avec les détails des guardians
};
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 dossier d'inscription"
options={groups}
selectedOptions={selectedGroups}
onChange={handleGroupChange}
errorMsg={null}
/>
);
case 'Obligatoire':
return (
<div className="flex justify-center items-center">
<ToggleSwitch
name="is_required"
checked={formData.is_required} // Utilise la valeur booléenne de is_required
onChange={(e) => {
const { checked } = e.target; // Récupère l'état du toggle
setFormData((prevData) => ({
...prevData,
is_required: checked, // Met à jour directement le champ is_required
}));
}}
/>
</div>
);
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 'Obligatoire':
return (
<span
className={`px-3 py-1 rounded-full text-sm font-semibold ${
document.is_required
? 'bg-green-100 text-green-600'
: 'bg-gray-100 text-gray-600'
}`}
>
{document.is_required ? 'Oui' : 'Non'}
</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(() => {
showNotification(
'Le document "${document.name}" a été correctement supprimé.',
'success',
'Succès'
);
setRemovePopupVisible(false);
})
.catch((error) => {
logger.error(
'Erreur lors de la suppression du document:',
error
);
showNotification(
`Erreur lors de la suppression du document "${document.name}".`,
'error',
'Erreur'
);
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: 'Obligatoire',
transform: (row) => renderRequiredDocumentCell(row, 'Obligatoire'),
},
{
name: 'Actions',
transform: (row) => renderRequiredDocumentCell(row, 'Actions'),
},
];
// Ajout : écouteur d'event global pour déclencher la création depuis la popup centrale
React.useEffect(() => {
if (!hideCreateButton) return;
const handler = () => handleAddEmptyRequiredDocument();
window.addEventListener('parentFilesSection:create', handler);
return () => window.removeEventListener('parentFilesSection:create', handler);
}, [hideCreateButton]);
const Table = TableComponent || ((props) => <div />); // fallback
const SectionHeader = SectionHeaderComponent || ((props) => <div />);
return (
<div className={`w-full h-full flex flex-col ${tableContainerClass}`}>
<SectionHeader
title="Pièces à fournir"
description="Configurez la liste des documents que les parents doivent fournir."
className={headerClassName}
/>
<Table
data={
editingDocumentId === 'new' ? [formData, ...parentFiles] : parentFiles
}
columns={columnsRequiredDocuments}
emptyMessage="Aucune pièce à fournir enregistrée"
/>
<Popup
isOpen={removePopupVisible}
message={removePopupMessage}
onConfirm={removePopupOnConfirm}
onCancel={() => setRemovePopupVisible(false)}
/>
</div>
);
}