Les boutons Modifier et Désactiver de la page Administration n'appelaient rien : handleDelete se contentait d'un console.log et le bouton Enregistrer de la modale ne faisait que la refermer. L'écran donnait l'illusion d'un CRUD. Branche les 4 onglets sur les endpoints POST/PUT/DELETE ajoutés côté API, avec confirmation avant suppression, rechargement après enregistrement et remontée des erreurs de l'API (dont le refus 409 quand des enregistrements sont rattachés). Les 4 blocs de colonnes de tableau, quasi identiques d'un onglet à l'autre, laissent place à une description déclarative (referentielsConfig.js) : un seul tableau et un seul formulaire générique (ReferentielForm) servent les 4 référentiels. Les colonnes qui portaient un identifiant brut affichent maintenant le libellé. La modale, jusqu'ici déclarée dans Admin.jsx, devient un composant commun réutilisable.
188 lines
6.0 KiB
React
188 lines
6.0 KiB
React
// src/pages/Admin/Admin.jsx
|
|
// Page Administration : CRUD sur les référentiels (monitorings, services,
|
|
// catégories, contacts). Réservée au rôle Admin côté API.
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { Plus, Edit, Trash2 } from 'lucide-react';
|
|
import { getMonitorings, getServices, getCategories, getContacts } from '../../services/api';
|
|
import DataTable from '../../components/common/DataTable';
|
|
import LoadingSpinner from '../../components/common/LoadingSpinner';
|
|
import ErrorMessage from '../../components/common/ErrorMessage';
|
|
import Button from '../../components/common/Button';
|
|
import Modal from '../../components/common/Modal';
|
|
import ReferentielForm from './ReferentielForm';
|
|
import { REFERENTIELS, LIBELLES_REFERENTIEL } from './referentielsConfig';
|
|
import styles from './Admin.module.css';
|
|
|
|
async function chargerReferentiels() {
|
|
const [monitorings, services, categories, contacts] = await Promise.all([
|
|
getMonitorings(),
|
|
getServices(),
|
|
getCategories(),
|
|
getContacts(),
|
|
]);
|
|
return {
|
|
monitorings: Array.isArray(monitorings) ? monitorings : [],
|
|
services: Array.isArray(services) ? services : [],
|
|
categories: Array.isArray(categories) ? categories : [],
|
|
contacts: Array.isArray(contacts) ? contacts : [],
|
|
};
|
|
}
|
|
|
|
export default function Admin() {
|
|
const [ongletActif, setOngletActif] = useState(REFERENTIELS[0].key);
|
|
const [donnees, setDonnees] = useState({});
|
|
const [loading, setLoading] = useState(true);
|
|
const [erreur, setErreur] = useState('');
|
|
const [modaleOuverte, setModaleOuverte] = useState(false);
|
|
const [elementEnEdition, setElementEnEdition] = useState(null);
|
|
|
|
const recharger = useCallback(async () => {
|
|
setErreur('');
|
|
try {
|
|
setDonnees(await chargerReferentiels());
|
|
} catch (e) {
|
|
setErreur(e.message || 'Erreur lors du chargement des données administratives.');
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
let annule = false;
|
|
async function chargerAuMontage() {
|
|
try {
|
|
const resultat = await chargerReferentiels();
|
|
if (!annule) setDonnees(resultat);
|
|
} catch (e) {
|
|
if (!annule) setErreur(e.message || 'Erreur lors du chargement des données administratives.');
|
|
} finally {
|
|
if (!annule) setLoading(false);
|
|
}
|
|
}
|
|
chargerAuMontage();
|
|
return () => { annule = true; };
|
|
}, []);
|
|
|
|
const referentiel = REFERENTIELS.find(r => r.key === ongletActif);
|
|
|
|
function ouvrirCreation() {
|
|
setElementEnEdition(null);
|
|
setModaleOuverte(true);
|
|
}
|
|
|
|
function ouvrirEdition(element) {
|
|
setElementEnEdition(element);
|
|
setModaleOuverte(true);
|
|
}
|
|
|
|
async function handleEnregistrer(corps) {
|
|
if (elementEnEdition) {
|
|
await referentiel.modifier(elementEnEdition[referentiel.idField], corps);
|
|
} else {
|
|
await referentiel.creer(corps);
|
|
}
|
|
setModaleOuverte(false);
|
|
await recharger();
|
|
}
|
|
|
|
async function handleSupprimer(element) {
|
|
const action = referentiel.suppressionEstDesactivation ? 'Désactiver' : 'Supprimer';
|
|
const nom = element[referentiel.colonnes[0].key];
|
|
if (!window.confirm(`${action} « ${nom} » ?`)) return;
|
|
|
|
setErreur('');
|
|
try {
|
|
await referentiel.supprimer(element[referentiel.idField]);
|
|
await recharger();
|
|
} catch (e) {
|
|
setErreur(e.message || 'La suppression a échoué.');
|
|
}
|
|
}
|
|
|
|
/* Affiche le libellé d'un référentiel lié plutôt que son identifiant brut. */
|
|
function libelleLie(nomReferentiel, id) {
|
|
const { idField, labelField } = LIBELLES_REFERENTIEL[nomReferentiel];
|
|
const trouve = (donnees[nomReferentiel] || []).find(item => item[idField] === id);
|
|
return trouve ? trouve[labelField] : id;
|
|
}
|
|
|
|
const colonnes = [
|
|
...referentiel.colonnes.map(colonne => ({
|
|
key: colonne.key,
|
|
label: colonne.label,
|
|
render: colonne.referentiel
|
|
? val => libelleLie(colonne.referentiel, val)
|
|
: undefined,
|
|
})),
|
|
{
|
|
key: 'actions',
|
|
label: 'Actions',
|
|
render: (val, row) => (
|
|
<div className={styles.actions}>
|
|
<button className={styles.actionBtn} onClick={() => ouvrirEdition(row)}>
|
|
<Edit size={14} />
|
|
Modifier
|
|
</button>
|
|
<button className={styles.actionBtn} onClick={() => handleSupprimer(row)}>
|
|
<Trash2 size={14} />
|
|
{referentiel.suppressionEstDesactivation ? 'Désactiver' : 'Supprimer'}
|
|
</button>
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
|
|
if (loading) return <LoadingSpinner message="Chargement des données administratives..." />;
|
|
|
|
return (
|
|
<div className={styles.page}>
|
|
<h1 className={styles.title}>Administration</h1>
|
|
|
|
<div className={styles.tabs}>
|
|
{REFERENTIELS.map(item => (
|
|
<button
|
|
key={item.key}
|
|
className={`${styles.tab} ${ongletActif === item.key ? styles.active : ''}`}
|
|
onClick={() => setOngletActif(item.key)}
|
|
>
|
|
{item.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{erreur && <ErrorMessage message={erreur} />}
|
|
|
|
<div className={styles.tabContent}>
|
|
<div className={styles.header}>
|
|
<h2>{referentiel.label}</h2>
|
|
<Button variant="primary" onClick={ouvrirCreation}>
|
|
<Plus size={14} />
|
|
Ajouter
|
|
</Button>
|
|
</div>
|
|
|
|
<DataTable
|
|
columns={colonnes}
|
|
data={donnees[ongletActif] || []}
|
|
pageSize={50}
|
|
emptyMessage={`Aucun ${referentiel.singulier} trouvé.`}
|
|
/>
|
|
</div>
|
|
|
|
<Modal
|
|
isOpen={modaleOuverte}
|
|
onClose={() => setModaleOuverte(false)}
|
|
title={`${elementEnEdition ? 'Modifier' : 'Ajouter'} ${referentiel.singulier}`}
|
|
>
|
|
<ReferentielForm
|
|
/* Remonte le formulaire à zéro quand on change d'onglet ou d'élément */
|
|
key={`${referentiel.key}-${elementEnEdition?.[referentiel.idField] ?? 'nouveau'}`}
|
|
referentiel={referentiel}
|
|
valeurInitiale={elementEnEdition}
|
|
donnees={donnees}
|
|
onEnregistrer={handleEnregistrer}
|
|
onAnnuler={() => setModaleOuverte(false)}
|
|
/>
|
|
</Modal>
|
|
</div>
|
|
);
|
|
}
|