58589e93d0
Auto Tag Develop / tag (push) Successful in 6s
Lien Lesstime : #50 ## Résumé Refacto : extraction de la logique fetch/CRUD inline de la page categories (ERP-49) vers deux composables dédiés, conformément au pattern Starseed (useSidebar / useModules). - **useCategoriesAdmin** : singleton state (`categories` + `types` + `loading` + `error`). Pré-chargement des types au mount de la page (au lieu d'un fetch par ouverture du drawer). Reset au logout via `onAuthSessionCleared` + appel explicite dans `logout.vue`. - **useCategoryForm** : state local par form (pas singleton, contrairement à `useCategoriesAdmin`). Valide côté client en miroir des RG back (RG-1.02 / RG-1.04 / RG-1.05), mappe les erreurs 409 (RG-1.07 doublon) et 422 (violations API Platform) sur les bons champs. `submitCreate` / `submitUpdate` / `submitDelete` renvoient la ressource ou `null` pour découpler la décision de fermeture du drawer. La page et le drawer deviennent purement présentationnels — aucune régression UX attendue (mêmes validations, mêmes toasts, même bascule view → edit via `isDirty` exposé par le composable). ## Décisions - `useCategoriesAdmin` porte aussi les types (`fetchTypes`), pas seulement `categories` — sinon le drawer continuerait à fetcher tout seul et la refacto n'aurait rien centralisé. - `buildCreatePayload` retourne `Record<string, unknown>` (pas `CategoryCreateInput`) car la signature `useApi.post(body: AnyObject)` n'accepte pas les types stricts (variance TS). - Reset au logout : double mécanisme conservé (auto via `onAuthSessionCleared` pour 401, explicite dans `logout.vue` pour logout volontaire — pattern existant Starseed). ## Tests - `npx nuxi typecheck` ✓ 0 erreur nouvelle (1 erreur pré-existante sur `modules/catalog/nuxt.config.ts` héritée d'ERP-49) - `make nuxt-test` ✓ 43/43, 0 régression - PHPUnit ✓ 311/311 (pre-commit) - Manuel navigateur : à valider (cahier de test consigné dans Lesstime #50) ## ⚠ Note d'intégration La branche contient encore les 3 commits ERP-49 (`4046910`, `216f388`, `934a12b`) car elle a été créée depuis la branche ERP-49 avant son merge sur develop. Selon l'ordre de merge : soit ERP-49 est mergée d'abord (cette MR ne contiendra plus que le commit ERP-50 après rebase auto), soit cette MR embarque tout l'historique catalog. Reviewed-on: #25 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
/**
|
|
* Schemas Hydra / API Platform 4.
|
|
*
|
|
* Important : API Platform 4 abandonne le prefixe `hydra:` dans les noms de
|
|
* proprietes (compare a la version 3). Un GET /api/audit-logs renvoie :
|
|
* { "@context": ..., "@id": ..., "@type": "...",
|
|
* "member": [...],
|
|
* "totalItems": 30,
|
|
* "view": { "@id": ..., "@type": "...", "first": ..., "next": ..., ... } }
|
|
*
|
|
* En `application/json` (sans ld), API Platform retourne un simple tableau
|
|
* plat sans ces metadonnees — on doit donc explicitement demander
|
|
* `application/ld+json` (via l'option `headers: { Accept: ... }` de useApi)
|
|
* pour avoir acces a la pagination.
|
|
*/
|
|
export interface HydraView {
|
|
'@id'?: string
|
|
'@type'?: string
|
|
first?: string
|
|
last?: string
|
|
next?: string
|
|
previous?: string
|
|
}
|
|
|
|
export interface HydraCollection<T> {
|
|
member: T[]
|
|
totalItems: number
|
|
view?: HydraView
|
|
}
|
|
|
|
export function extractHydraMembers<T>(collection: HydraCollection<T>): T[] {
|
|
return collection.member ?? []
|
|
}
|
|
|
|
/**
|
|
* Une violation de contrainte API Platform (reponse 422). Le `propertyPath`
|
|
* pointe le champ concerne, `message` est le libelle a afficher.
|
|
*/
|
|
export interface ApiViolation {
|
|
propertyPath: string
|
|
message: string
|
|
}
|
|
|
|
/**
|
|
* Extrait les violations d'un payload d'erreur 422 d'API Platform 4. Supporte
|
|
* les deux formats de negociation (`violations` ou `hydra:violations`) et
|
|
* renvoie un tableau vide si le payload n'en contient pas d'exploitables.
|
|
*
|
|
* Utilise par useCategoryForm et tout futur composable de formulaire qui
|
|
* doit mapper les violations serveur sur ses champs.
|
|
*/
|
|
export function extractApiViolations(data: unknown): ApiViolation[] {
|
|
if (!data || typeof data !== 'object') return []
|
|
const record = data as Record<string, unknown>
|
|
const raw = record.violations ?? record['hydra:violations']
|
|
if (!Array.isArray(raw)) return []
|
|
const out: ApiViolation[] = []
|
|
for (const v of raw) {
|
|
if (!v || typeof v !== 'object') continue
|
|
const obj = v as Record<string, unknown>
|
|
out.push({
|
|
propertyPath: String(obj.propertyPath ?? ''),
|
|
message: String(obj.message ?? ''),
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
/**
|
|
* Extrait un message d'erreur lisible depuis un payload Hydra / JSON
|
|
* d'erreur API Platform. Essaie les champs courants dans l'ordre :
|
|
* `hydra:description` → `detail` → `description` → `message` → `error` →
|
|
* `title` → `hydra:title`. Renvoie '' si rien d'exploitable.
|
|
*
|
|
* Si `data` est une string, la renvoie telle quelle (cas des erreurs
|
|
* Symfony en text/plain ou des messages bruts).
|
|
*/
|
|
export function extractApiErrorMessage(data: unknown): string {
|
|
if (typeof data === 'string') return data
|
|
if (!data || typeof data !== 'object') return ''
|
|
const record = data as Record<string, unknown>
|
|
return (
|
|
(record['hydra:description'] as string)
|
|
?? (record.detail as string)
|
|
?? (record.description as string)
|
|
?? (record.message as string)
|
|
?? (record.error as string)
|
|
?? (record.title as string)
|
|
?? (record['hydra:title'] as string)
|
|
?? ''
|
|
)
|
|
}
|