Files
donut-gestprojet/frontend/src/router/index.ts
T
Arnaud dcf99f011a feat(admin): création de catégorie via dashboard
Nouvelle page /admin/categories/new, jumeau de l'écran d'édition :
  - Form complet : name, slug (auto), description, color_hex, icon,
    dimension, sort_order
  - Preview live identique à l'éditeur (top border + folder tinté + badge)
  - Slug auto-généré depuis le nom (normalisation NFD + lowercase + dashes)
    tant que l'utilisateur n'a pas touché manuellement au champ. Flag
    slugDirty mis à true sur premier @input du slug -> on arrête l'auto.
  - Validation color_hex /^#RRGGBB$/ côté client + normalisation upper-case
  - Body construit en omettant les champs vides -> l'API applique ses
    défauts (color_hex='#888888', dimension='transversal', sort_order=0)
  - Sur 201 : redirige vers /admin/categories/:slug/edit pour que l'admin
    voie tout de suite sa nouvelle catégorie et puisse ajuster
  - 422/409/401 gérés comme sur les autres écrans admin

UI
  - AdminCategoriesPage : CTA "Nouvelle catégorie" en haut à droite
  - Router : route nommée admin-category-new avec guard admin

Backend
  - Aucune nouvelle route : POST /api/categories existait déjà
  - Le contrôleur valide name (required, max 128), dimension enum, color
    hex format, slug auto-unique

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 13:45:25 +02:00

60 lines
3.4 KiB
TypeScript

/**
* Routes de l'app. AppLayout englobe toutes les pages "public" (sidebar visible).
* /admin/login a son propre layout vide (pas de sidebar tant qu'on n'est pas loggé).
*/
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import AppLayout from '@/layouts/AppLayout.vue'
import { getAdminToken } from '@/api/client'
/**
* Guard pour les routes /admin/* (hors /admin/login). Si pas de token Bearer
* en localStorage, redirige vers /admin/login (en mémorisant la destination
* via query.next pour qu'on y retourne après auth).
*/
function requireAdmin(to: { fullPath: string }) {
if (!getAdminToken()) {
return { name: 'admin-login', query: { next: to.fullPath } }
}
return true
}
const routes: RouteRecordRaw[] = [
{
path: '/',
component: AppLayout,
children: [
{ path: '', name: 'home', component: () => import('@/pages/HomePage.vue') },
{ path: 'dashboard', name: 'dashboard', component: () => import('@/pages/DashboardPage.vue') },
{ path: 'donut', name: 'donut', component: () => import('@/pages/DonutPage.vue') },
{ path: 'projects', name: 'projects', component: () => import('@/pages/ProjectsPage.vue') },
{ path: 'projects/new', name: 'project-new', component: () => import('@/pages/ProjectNewPage.vue') },
{ path: 'projects/:slug', name: 'project-detail', component: () => import('@/pages/ProjectDetailPage.vue'), props: true },
{ path: 'archives', name: 'archives', component: () => import('@/pages/ArchivesPage.vue') },
{ path: 'categories', name: 'categories', component: () => import('@/pages/CategoriesPage.vue') },
// --- Admin dashboard (token Bearer requis pour toutes les sous-pages sauf /admin/login) ---
{ path: 'admin', name: 'admin-overview', component: () => import('@/pages/admin/AdminOverviewPage.vue'), beforeEnter: requireAdmin },
{ path: 'admin/projects', name: 'admin-projects', component: () => import('@/pages/admin/AdminProjectsPage.vue'), beforeEnter: requireAdmin },
{ path: 'admin/projects/:slug/edit', name: 'admin-project-edit', component: () => import('@/pages/admin/AdminProjectEditPage.vue'), props: true, beforeEnter: requireAdmin },
{ 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/new', name: 'admin-category-new', component: () => import('@/pages/admin/AdminCategoryNewPage.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') },
],
},
]
export const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior() {
// Scroll en haut à chaque navigation (UX par défaut pour une SPA de contenu).
return { top: 0 }
},
})