feat(front) : mapping des erreurs de validation 422 par champ (ERP-101) #58

Merged
tristan merged 5 commits from feat/ERP-101-form-field-validation-mapping into develop 2026-06-04 08:41:19 +00:00
Owner

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).
## 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).
tristan added 1 commit 2026-06-04 06:33:52 +00:00
feat(front) : mapping des erreurs de validation 422 par champ (ERP-101)
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m40s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m10s
502d1a216b
- 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
tristan added 1 commit 2026-06-04 07:15:56 +00:00
feat(front) : malio-ui 1.7.4, required/readonly et carrousel onglets sur les ecrans client
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m44s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Failing after 11s
80f189033b
- bump @malio/layer-ui ^1.7.3 -> ^1.7.4
- required sur les champs obligatoires (categories, distributeur/courtier,
  banque si virement, RIB si LCR) desormais supportes par les Malio*
- consultation/edition : champs passes en readonly (au lieu de disabled)
  pour les etats sans droit de modification / lecture seule
- MalioTabList : carrousel (max 5 onglets visibles, max-width 1100px) en
  consultation et edition
tristan added 1 commit 2026-06-04 07:40:36 +00:00
feat(front) : required sur les champs obligatoires adresse et comptabilite
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m47s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Failing after 10s
03a31a68b4
- bloc adresse : pays, code postal, ville, adresse passes en required
- comptabilite : siren, n° compte, mode TVA, n° TVA, delai et type de
  reglement passes en required (banque deja required si virement, RIB si LCR)
tristan added 1 commit 2026-06-04 07:46:24 +00:00
fix : package-lock.json
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m45s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m21s
ec0855d870
matthieu approved these changes 2026-06-04 07:55:42 +00:00
matthieu left a comment
Owner

Revue — approuvée

PR propre et bien construite. useFormErrors + mapViolationsToRecord posent une convention claire et réutilisable, la gestion d'erreur est rigoureuse (catch externes conservés sur les boucles de suppression, erreurs de collection alignées par index sur le v-for, splice des erreurs au retrait d'une ligne). Le bump @malio/layer-ui 1.7.4 fournit bien readonly/required sur MalioSelect/SelectCheckbox et max-visible-tabs/max-width sur MalioTabList, donc le swap :disabled:readonly ne casse aucun verrouillage. Lockfile recorrigé (ec0855d), CI verte (back + front).

Aucun bloquant. 2 suggestions mineures optionnelles en commentaires inline (i18n + factorisation). 👍

### Revue — approuvée ✅ PR propre et bien construite. `useFormErrors` + `mapViolationsToRecord` posent une convention claire et réutilisable, la gestion d'erreur est rigoureuse (catch externes conservés sur les boucles de suppression, erreurs de collection alignées par index sur le `v-for`, splice des erreurs au retrait d'une ligne). Le bump `@malio/layer-ui` 1.7.4 fournit bien `readonly`/`required` sur `MalioSelect`/`SelectCheckbox` et `max-visible-tabs`/`max-width` sur `MalioTabList`, donc le swap `:disabled` → `:readonly` ne casse aucun verrouillage. Lockfile recorrigé (`ec0855d`), **CI verte** (back + front). Aucun bloquant. 2 suggestions mineures **optionnelles** en commentaires inline (i18n + factorisation). 👍
@@ -581,0 +629,4 @@
* index). 422 exploitable erreurs inline sous les champs de la ligne ; sinon
* toast de fallback. Renvoie true si mappee inline.
*/
function mapRowError(
Owner

🟡 Mineur (altitude) — copie de mapRowError de new.vue (cf. commentaire là-bas). Candidat à un composable partagé useClientFormErrors(). Non bloquant.

🟡 Mineur (altitude) — copie de `mapRowError` de `new.vue` (cf. commentaire là-bas). Candidat à un composable partagé `useClientFormErrors()`. Non bloquant.
@@ -394,0 +444,4 @@
* index). 422 avec violations exploitables erreurs inline sous les champs de
* la ligne ; sinon toast de fallback. Renvoie true si mappee inline.
*/
function mapRowError(
Owner

🟡 Mineur (altitude) — mapRowError + le câblage des erreurs par ligne (contactErrors/addressErrors/ribErrors + les useFormErrors scalaires) sont quasi identiques entre new.vue et [id]/edit.vue. Dans l'esprit « convention réutilisable » de cette PR, un useClientFormErrors() partagé éviterait la divergence future (seul le fallback diffère : toast.error ici vs showError en edit). Non bloquant.

🟡 Mineur (altitude) — `mapRowError` + le câblage des erreurs par ligne (`contactErrors`/`addressErrors`/`ribErrors` + les `useFormErrors` scalaires) sont quasi identiques entre `new.vue` et `[id]/edit.vue`. Dans l'esprit « convention réutilisable » de cette PR, un `useClientFormErrors()` partagé éviterait la divergence future (seul le fallback diffère : `toast.error` ici vs `showError` en edit). Non bloquant.
@@ -0,0 +96,4 @@
= extractApiErrorMessage(data)
|| opts.fallbackMessage
|| 'Une erreur est survenue.'
toast.error({ title: 'Erreur', message })
Owner

🟡 Mineur (i18n) — libellés en dur dans un composable partagé : title: 'Erreur' et le fallback 'Une erreur est survenue.'. Le reste du projet passe par t(...). Comme useFormErrors est appelé dans un contexte de composant, on peut y faire const { t } = useI18n() et exposer les libellés via i18n (ou les passer en option) pour rester cohérent avec la convention. Non bloquant.

🟡 Mineur (i18n) — libellés en dur dans un composable partagé : `title: 'Erreur'` et le fallback `'Une erreur est survenue.'`. Le reste du projet passe par `t(...)`. Comme `useFormErrors` est appelé dans un contexte de composant, on peut y faire `const { t } = useI18n()` et exposer les libellés via i18n (ou les passer en option) pour rester cohérent avec la convention. Non bloquant.
matthieu added the frontM1-Clienttype/feat labels 2026-06-04 07:56:55 +00:00
tristan added 1 commit 2026-06-04 08:36:21 +00:00
refactor(front) : suites review ERP-101 — i18n libelles toast + factorisation useClientFormErrors
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m49s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m9s
97fe0b39de
- libelles de toast generiques passes en i18n (errors.title/generic/unknown,
  success.title) dans useFormErrors, useApi et useCategoryForm
- nouveau composable useClientFormErrors : factorise l'etat d'erreurs
  (3 useFormErrors scalaires + 3 tableaux par ligne + mapRowError) partage
  entre clients/new.vue et [id]/edit.vue
- mapRowError retourne un booleen et ne toaste plus : chaque page garde son
  fallback (toast generique en creation, showError en edition)
Author
Owner

Merci pour la review 🙏 Les 2 suggestions mineures sont traitées dans 97fe0b3 :

🟡 i18n (useFormErrors) — fait. Les libellés de toast génériques passent par i18n : nouvelles clés errors.title / errors.generic / errors.unknown / success.title, consommées via useI18n(). Tant qu'à faire, j'ai aussi aligné useApi (qui hardcodait les mêmes 'Erreur' / 'Succes' / 'Erreur inconnue.') et useCategoryForm, pour une cohérence totale plutôt qu'un fix partiel.

🟡 factorisation (mapRowError + état par ligne) — fait. Nouveau composable partagé useClientFormErrors() (modules/commercial/composables/) : il porte les 3 useFormErrors scalaires + les 3 tableaux d'erreurs par ligne + mapRowError. Pour gérer le fallback divergent, mapRowError retourne désormais un booléen (true = mappé inline) et ne toaste plus : chaque page garde son propre fallback dans le catch (toast générique en création, showError en édition). new.vue et [id]/edit.vue ne dupliquent plus le câblage. Test Vitest dédié ajouté.

Vérifs : Vitest 216/216 (dont le nouveau spec useClientFormErrors), eslint clean, nuxi typecheck 0 erreur.

Merci pour la review 🙏 Les 2 suggestions mineures sont traitées dans `97fe0b3` : **🟡 i18n (useFormErrors)** — fait. Les libellés de toast génériques passent par i18n : nouvelles clés `errors.title` / `errors.generic` / `errors.unknown` / `success.title`, consommées via `useI18n()`. Tant qu'à faire, j'ai aussi aligné **`useApi`** (qui hardcodait les mêmes `'Erreur'` / `'Succes'` / `'Erreur inconnue.'`) et **`useCategoryForm`**, pour une cohérence totale plutôt qu'un fix partiel. **🟡 factorisation (mapRowError + état par ligne)** — fait. Nouveau composable partagé **`useClientFormErrors()`** (`modules/commercial/composables/`) : il porte les 3 `useFormErrors` scalaires + les 3 tableaux d'erreurs par ligne + `mapRowError`. Pour gérer le fallback divergent, `mapRowError` **retourne désormais un booléen** (`true` = mappé inline) et **ne toaste plus** : chaque page garde son propre fallback dans le `catch` (toast générique en création, `showError` en édition). `new.vue` et `[id]/edit.vue` ne dupliquent plus le câblage. Test Vitest dédié ajouté. Vérifs : Vitest **216/216** (dont le nouveau spec `useClientFormErrors`), eslint clean, `nuxi typecheck` 0 erreur.
tristan merged commit ee3bbea649 into develop 2026-06-04 08:41:19 +00:00
tristan deleted branch feat/ERP-101-form-field-validation-mapping 2026-06-04 08:41:20 +00:00
Sign in to join this conversation.