fix(front) : correctifs ecran categories + alignement boutons admin (ERP-117)
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 2m13s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m8s

- Drawer categories : titre stable « Modifier la categorie » (plus de
  bascule view/edit selon l'etat dirty), bouton Enregistrer toujours actif
  et PATCH du payload complet (save a tout moment, meme sans modification)
- Champ « Types de categorie » : suppression du label « Selectionner un ou
  plusieurs types »
- Categories : ordre boutons Filtres avant Ajouter + gap reduit (gap-8)
- Boutons d'ajout admin (categories, roles, sites) passes en variant secondary
- Boutons Filtres (categories, audit-log, clients) en tertiary simple :
  suppression des surcharges de classe, icone a gauche 24px
- Tests useCategoryForm mis a jour (PATCH payload complet)
This commit is contained in:
2026-06-08 17:55:31 +02:00
parent edfb2b1619
commit 9e3b996225
9 changed files with 61 additions and 74 deletions
@@ -346,7 +346,7 @@ describe('useCategoryForm', () => {
})
describe('submitUpdate', () => {
it('appelle PATCH /categories/{id} uniquement avec les champs modifies', async () => {
it('appelle PATCH /categories/{id} avec le payload complet (name + categoryTypes)', async () => {
mockPatch.mockResolvedValueOnce({ ...CAT, name: 'Vis V2' })
const form = useCategoryForm()
form.loadFrom(CAT)
@@ -354,9 +354,11 @@ describe('useCategoryForm', () => {
await form.submitUpdate(42)
// Payload complet : meme si seul le name change, on renvoie aussi
// les categoryTypes (PATCH full payload, cf. drawers simples).
expect(mockPatch).toHaveBeenCalledWith(
'/categories/42',
{ name: 'Vis V2' }, // pas de categoryTypes car non modifies
{ name: 'Vis V2', categoryTypes: ['/api/category_types/1'] },
{ toast: false },
)
})
@@ -371,20 +373,25 @@ describe('useCategoryForm', () => {
expect(mockPatch).toHaveBeenCalledWith(
'/categories/42',
{ categoryTypes: ['/api/category_types/1', '/api/category_types/2'] },
{ name: CAT.name, categoryTypes: ['/api/category_types/1', '/api/category_types/2'] },
{ toast: false },
)
})
it('court-circuite l appel API si aucun champ n a change', async () => {
it('envoie un PATCH complet meme sans modification (save a tout moment)', async () => {
mockPatch.mockResolvedValueOnce(CAT)
const form = useCategoryForm()
form.loadFrom(CAT)
// Aucune modification — isDirty=false, patch payload vide.
// Aucune modification : le PATCH part quand meme avec le payload complet.
const result = await form.submitUpdate(42)
expect(mockPatch).not.toHaveBeenCalled()
expect(result).toBeNull()
expect(mockPatch).toHaveBeenCalledWith(
'/categories/42',
{ name: CAT.name, categoryTypes: ['/api/category_types/1'] },
{ toast: false },
)
expect(result).toEqual(CAT)
expect(form.submitting.value).toBe(false)
})
@@ -174,26 +174,18 @@ export function useCategoryForm() {
}
/**
* PATCH /api/categories/{id}. Envoie uniquement les champs modifies pour
* coller a la semantique merge-patch (Content-Type pose par useApi).
* Renvoie la categorie mise a jour, ou `null` en cas d'echec.
* PATCH /api/categories/{id}. Envoie le payload complet (name +
* categoryTypes), comme les autres drawers du projet : le bouton
* Enregistrer sauvegarde a tout moment, meme sans modification, et renvoie
* toujours un retour (toast succes + refresh). Renvoie la categorie mise a
* jour, ou `null` en cas d'echec.
*/
async function submitUpdate(id: number): Promise<Category | null> {
if (!validate()) return null
submitting.value = true
const payload: Record<string, unknown> = {}
if (name.value !== initialName.value) {
payload.name = name.value.trim()
}
if (!sameIds(categoryTypeIds.value, initialCategoryTypeIds.value)) {
payload.categoryTypes = categoryTypeIds.value.map(id => `/api/category_types/${id}`)
}
// Garde-fou : un PATCH sans changement ne sert a rien. Theoriquement
// empeche par le drawer (bouton Enregistrer masque si !isDirty) mais
// on protege le composable contre un appel direct mal utilise.
if (Object.keys(payload).length === 0) {
submitting.value = false
return null
const payload: Record<string, unknown> = {
name: name.value.trim(),
categoryTypes: categoryTypeIds.value.map(id => `/api/category_types/${id}`),
}
try {
const updated = await api.patch<Category>(`/categories/${id}`, payload, {