feat(front) : mapping des erreurs de validation 422 par champ (ERP-101)
- composable useFormErrors + util mapViolationsToRecord (shared) - formulaire Client (new + edit) : erreurs inline par champ (scalaires) et par ligne pour les collections (contacts / adresses / RIB) - blocs ClientContactBlock / ClientAddressBlock : prop errors - migration de useCategoryForm sur useFormErrors - convention documentee dans .claude/rules/frontend.md
This commit is contained in:
@@ -44,6 +44,40 @@ Tout champ de formulaire / filtre doit utiliser les composants `Malio*` plutot q
|
||||
|
||||
Toute autre exception requiert validation avant merge.
|
||||
|
||||
## Validation des formulaires — useFormErrors obligatoire (erreur par champ)
|
||||
|
||||
**Tout formulaire qui soumet a une API DOIT afficher les erreurs de validation 422 sous le champ concerne, via `useFormErrors`** (`frontend/shared/composables/useFormErrors.ts`). C'est le pendant front de « le back renvoie TOUTES les violations d'une 422 d'un coup » : un seul aller-retour, chaque erreur affichee inline sous son champ (prop `:error` des `Malio*`), pas un toast fourre-tout.
|
||||
|
||||
Principe cle : **le nom du champ cote front = le `propertyPath` renvoye par le back**. Aucun mapping manuel champ par champ.
|
||||
|
||||
Pattern de reference (champs scalaires) :
|
||||
|
||||
```ts
|
||||
const { errors, setError, clearErrors, handleApiError } = useFormErrors()
|
||||
|
||||
async function submit() {
|
||||
clearErrors()
|
||||
try {
|
||||
await useApi().post('/clients', payload, { toast: false }) // toast: false obligatoire
|
||||
} catch (e) {
|
||||
// 422 → mapping inline par champ (pas de toast) ; autre → toast de fallback.
|
||||
handleApiError(e, { fallbackMessage: t('foo.error') })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```vue
|
||||
<MalioInputText v-model="form.companyName" :error="errors.companyName" />
|
||||
<MalioSelect v-model="form.siren" :error="errors.siren" />
|
||||
```
|
||||
|
||||
Regles :
|
||||
- **Toujours `{ toast: false }`** sur l'appel API qui veut un mapping inline (sinon le toast natif d'`useApi` masque le fin).
|
||||
- **Cas metier specifique** (ex: 409 doublon) : `setError('champ', message)` + toast explicite **avant** de deleguer le reste a `handleApiError`. Cf. `useCategoryForm` (doublon RG-1.07).
|
||||
- **Collections** (listes de sous-entites sauvees par un appel par ligne) : une erreur PAR LIGNE via un tableau `ref<Record<string, string>[]>` aligne sur l'index, peuple par `mapViolationsToRecord(error.response._data)` (util pur de `shared/utils/api.ts`). Le composant de ligne expose une prop `:errors` (`Record<string, string>`) bindee sur le `:error` de chaque champ. Cf. `ClientContactBlock` / `ClientAddressBlock` et les submits de `clients/new.vue` / `clients/[id]/edit.vue`.
|
||||
|
||||
**Interdit** : se contenter d'un toast global sur une 422 quand le back identifie les champs fautifs (`propertyPath`). Reimplementer un mapping `if/else` par champ a la main au lieu d'`useFormErrors` / `mapViolationsToRecord`.
|
||||
|
||||
## Tableaux de donnees — MalioDataTable obligatoire
|
||||
|
||||
Tout affichage LISTE tabulaire (donnees metier paginees, CRUD admin) doit passer par `MalioDataTable` :
|
||||
|
||||
Reference in New Issue
Block a user