- useCategoriesAdmin : extract `HYDRA_NO_PAGINATION = 999` to a named
constant (was duplicated between fetchAll and fetchTypes) + comment
the post-M0 server-pagination debt.
- useCategoryForm + useApi + shared/utils/api : drop the local copy of
`extractErrorMessage` in useCategoryForm (it was duplicating the one
in useApi), and centralize Hydra error / violation extraction in
`shared/utils/api.ts` via two new helpers :
- extractApiErrorMessage(data) : tries hydra:description, detail,
description, message, error, title, hydra:title — used by both
useApi.onResponseError and useCategoryForm.handleApiError.
- extractApiViolations(data) : returns the ApiPlatform 422
violations as a typed array (handles `violations` and
`hydra:violations`), letting each caller map them onto its own
fields. useCategoryForm now uses this helper instead of an
inline loop, ready for the next form drawer to reuse.
handleApiError keeps a manual fallback toast on non-409/422 errors :
the native useApi toast is disabled by design (`toast: false`) to allow
fine-grained 409/422 mapping, so the catch-all branch must re-emit one
or a 500 would be silent.
No behavior change — 43/43 Vitest tests still pass.
Extrait la logique fetch/CRUD inline de la page categories (ERP-49) vers
deux composables dedies, conformement au pattern Starseed :
- useCategoriesAdmin : singleton state (categories + types + loading +
error). Pre-chargement des types au mount de la page (au lieu du
fetch par ouverture du drawer). Reset au logout via
onAuthSessionCleared + appel explicite dans logout.vue.
- useCategoryForm : state local par form (pas singleton). Valide
cote client en miroir des RG back (RG-1.02 / RG-1.04 / RG-1.05),
mappe les erreurs 409 (doublon RG-1.07) et 422 (violations API
Platform) sur les bons champs. submitCreate / submitUpdate /
submitDelete renvoient la ressource ou null pour decoupler la
decision de fermeture du drawer.
La page et le drawer deviennent purement presentationnels. Aucune
regression UX : meme validations, memes toasts, meme pattern
view -> edit du drawer (via isDirty expose par useCategoryForm).