feat(front): rend l'écran d'administration réellement fonctionnel
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.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { useState } from 'react';
|
||||
import Button from '../../components/common/Button';
|
||||
import ErrorMessage from '../../components/common/ErrorMessage';
|
||||
import { LIBELLES_REFERENTIEL } from './referentielsConfig';
|
||||
import styles from './Admin.module.css';
|
||||
|
||||
/* Formulaire générique piloté par la description des champs du référentiel
|
||||
(voir referentielsConfig.js) : les 4 onglets partagent ce composant. */
|
||||
export default function ReferentielForm({ referentiel, valeurInitiale, donnees, onEnregistrer, onAnnuler }) {
|
||||
const [valeurs, setValeurs] = useState(() =>
|
||||
Object.fromEntries(
|
||||
referentiel.champs.map(champ => [champ.name, valeurInitiale?.[champ.name] ?? ''])
|
||||
)
|
||||
);
|
||||
const [enCours, setEnCours] = useState(false);
|
||||
const [erreur, setErreur] = useState('');
|
||||
|
||||
function handleChange(name, valeur) {
|
||||
setValeurs(precedentes => ({ ...precedentes, [name]: valeur }));
|
||||
}
|
||||
|
||||
async function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
setErreur('');
|
||||
setEnCours(true);
|
||||
try {
|
||||
/* Les identifiants de référentiel arrivent en chaîne depuis <select> :
|
||||
l'API attend des entiers. */
|
||||
const corps = Object.fromEntries(
|
||||
referentiel.champs.map(champ => [
|
||||
champ.name,
|
||||
champ.type === 'select' ? Number(valeurs[champ.name]) : valeurs[champ.name],
|
||||
])
|
||||
);
|
||||
await onEnregistrer(corps);
|
||||
} catch (e) {
|
||||
setErreur(e.message || "L'enregistrement a échoué.");
|
||||
} finally {
|
||||
setEnCours(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
{referentiel.champs.map(champ => (
|
||||
<div key={champ.name} className={styles.field}>
|
||||
<label className={styles.label} htmlFor={champ.name}>{champ.label}</label>
|
||||
{champ.type === 'select' ? (
|
||||
<select
|
||||
id={champ.name}
|
||||
className={styles.input}
|
||||
value={valeurs[champ.name]}
|
||||
onChange={e => handleChange(champ.name, e.target.value)}
|
||||
required={champ.required}
|
||||
>
|
||||
<option value="">— Choisir —</option>
|
||||
{(donnees[champ.options] || []).map(option => {
|
||||
const { idField, labelField } = LIBELLES_REFERENTIEL[champ.options];
|
||||
return (
|
||||
<option key={option[idField]} value={option[idField]}>
|
||||
{option[labelField]}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
) : (
|
||||
<input
|
||||
id={champ.name}
|
||||
type={champ.type}
|
||||
className={styles.input}
|
||||
value={valeurs[champ.name]}
|
||||
onChange={e => handleChange(champ.name, e.target.value)}
|
||||
required={champ.required}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{erreur && <ErrorMessage message={erreur} />}
|
||||
|
||||
<div className={styles.modalActions}>
|
||||
<Button variant="secondary" type="button" onClick={onAnnuler} disabled={enCours}>
|
||||
Annuler
|
||||
</Button>
|
||||
<Button variant="primary" type="submit" disabled={enCours}>
|
||||
{enCours ? 'Enregistrement…' : 'Enregistrer'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user