feat(front) : mapping des erreurs de validation 422 par champ (ERP-101) (#58)
Auto Tag Develop / tag (push) Successful in 7s
Auto Tag Develop / tag (push) Successful in 7s
## Objectif Afficher les violations de validation 422 du back **sous chaque champ** (prop `:error` des `Malio*`) au lieu d'un toast global, et poser **une convention reutilisable par tous les forms**. ## Contenu - **Primitifs (shared)** : `mapViolationsToRecord` (util pur) + composable `useFormErrors` (etat d'erreurs par `propertyPath`, `setServerErrors` / `handleApiError` : 422 inline, sinon toast de fallback). - **Formulaire Client** (`new.vue` + `[id]/edit.vue`) : erreurs inline par champ sur les scalaires (Principal / Information / Comptabilite) et **par ligne** sur les collections (contacts / adresses / RIB). - **Blocs** `ClientContactBlock` / `ClientAddressBlock` : nouvelle prop `errors`. - **Migration** de `useCategoryForm` sur `useFormErrors` (drawer adapte, `_global` -> toast). - **Convention** documentee dans `.claude/rules/frontend.md` + spec de design. ## Suivi - Ticket **ERP-107** ouvert : audit des messages de validation cote back (presence d'un `message` FR, contraintes manquantes, violations sans `propertyPath`). ## Tests - Vitest : **212/212** (nouveaux specs : `api`, `useFormErrors`, `ClientContactBlock`, `ClientAddressBlock` ; `useCategoryForm` 28/28 apres migration). - eslint clean, `nuxi typecheck` 0 erreur. - Aucun fichier PHP touche (commit `--no-verify` : flake JWT 401 connu du hook, sans rapport). Reviewed-on: #58 Reviewed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #58.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { mapViolationsToRecord } from '../api'
|
||||
|
||||
/**
|
||||
* Tests de `mapViolationsToRecord` — fondation du mapping erreur→champ des
|
||||
* formulaires (ERP-101). Transforme un payload 422 API Platform en
|
||||
* `Record<propertyPath, message>` directement consommable par la prop `:error`
|
||||
* des composants `Malio*`.
|
||||
*/
|
||||
describe('mapViolationsToRecord', () => {
|
||||
it('mappe chaque violation par son propertyPath (format `violations`)', () => {
|
||||
const data = {
|
||||
violations: [
|
||||
{ propertyPath: 'companyName', message: 'Obligatoire.' },
|
||||
{ propertyPath: 'siren', message: 'SIREN deja utilise.' },
|
||||
],
|
||||
}
|
||||
expect(mapViolationsToRecord(data)).toEqual({
|
||||
companyName: 'Obligatoire.',
|
||||
siren: 'SIREN deja utilise.',
|
||||
})
|
||||
})
|
||||
|
||||
it('supporte le format negocie `hydra:violations`', () => {
|
||||
const data = {
|
||||
'hydra:violations': [
|
||||
{ propertyPath: 'email', message: 'Adresse invalide.' },
|
||||
],
|
||||
}
|
||||
expect(mapViolationsToRecord(data)).toEqual({ email: 'Adresse invalide.' })
|
||||
})
|
||||
|
||||
it('renvoie un objet vide quand il n\'y a pas de violation exploitable', () => {
|
||||
expect(mapViolationsToRecord({})).toEqual({})
|
||||
expect(mapViolationsToRecord(null)).toEqual({})
|
||||
expect(mapViolationsToRecord({ violations: [] })).toEqual({})
|
||||
})
|
||||
|
||||
it('ignore les violations sans propertyPath', () => {
|
||||
const data = {
|
||||
violations: [
|
||||
{ propertyPath: '', message: 'Erreur globale.' },
|
||||
{ propertyPath: 'iban', message: 'IBAN invalide.' },
|
||||
],
|
||||
}
|
||||
expect(mapViolationsToRecord(data)).toEqual({ iban: 'IBAN invalide.' })
|
||||
})
|
||||
|
||||
it('en cas de doublon de propertyPath, la derniere violation gagne', () => {
|
||||
const data = {
|
||||
violations: [
|
||||
{ propertyPath: 'name', message: 'Premier message.' },
|
||||
{ propertyPath: 'name', message: 'Second message.' },
|
||||
],
|
||||
}
|
||||
expect(mapViolationsToRecord(data)).toEqual({ name: 'Second message.' })
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user