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.
92 lines
3.1 KiB
React
92 lines
3.1 KiB
React
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>
|
|
);
|
|
}
|