feat(admin): édition de catégorie via dashboard

Nouvelle page /admin/categories/:slug/edit, même pattern que members :
  - Preview live de la carte catégorie (top border colorée + folder icon
    tinté + badge "X projets" + label dimension)
  - Form complet : name, slug, description, color_hex, icon, dimension, sort_order
  - Color picker natif <input type="color"> + champ HEX texte synchronisés,
    validation /^#[0-9A-Fa-f]{6}$/ côté client
  - Diff PATCH (champs vides -> null), upper-case normalisé pour color_hex
  - Si le slug change -> router.replace vers la nouvelle URL
  - Bouton Supprimer en header désactivé si projects_count > 0 (FK RESTRICT)
  - 422 -> fieldErrors, 409 -> slug collision, 401 -> session expirée
  - projects_count récupéré direct depuis GET /api/categories/:slug
    (la réponse contient ce champ pour la route détail, cf. controller PHP)

UI
  - AdminCategoriesPage : nouveau lien "Éditer" par ligne du tableau
  - Router : route nommée admin-category-edit avec guard admin

Backend
  - Aucune nouvelle route : PATCH /api/categories/:slug existait déjà
  - Validation côté serveur : in dimension enum, colorHex format,
    integer sort_order >= 0, slug auto-unique avec suffixe -2/-3...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-29 13:43:03 +02:00
parent cb37b461c5
commit dd7c3910f3
3 changed files with 313 additions and 7 deletions
+1
View File
@@ -40,6 +40,7 @@ const routes: RouteRecordRaw[] = [
{ path: 'admin/members', name: 'admin-members', component: () => import('@/pages/admin/AdminMembersPage.vue'), beforeEnter: requireAdmin },
{ path: 'admin/members/:slug/edit', name: 'admin-member-edit', component: () => import('@/pages/admin/AdminMemberEditPage.vue'), props: true, beforeEnter: requireAdmin },
{ path: 'admin/categories', name: 'admin-categories', component: () => import('@/pages/admin/AdminCategoriesPage.vue'), beforeEnter: requireAdmin },
{ path: 'admin/categories/:slug/edit', name: 'admin-category-edit', component: () => import('@/pages/admin/AdminCategoryEditPage.vue'), props: true, beforeEnter: requireAdmin },
{ path: 'admin/login', name: 'admin-login', component: () => import('@/pages/AdminLoginPage.vue'), meta: { hideForAuthed: true } },
{ path: ':pathMatch(.*)*', name: 'not-found', component: () => import('@/pages/NotFoundPage.vue') },