244 lines
6.5 KiB
TypeScript
244 lines
6.5 KiB
TypeScript
import { createRouter, createWebHistory } from 'vue-router'
|
|
import type { RouteRecordRaw } from 'vue-router'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
|
|
const routes: RouteRecordRaw[] = [
|
|
// Public routes
|
|
{
|
|
path: '/login',
|
|
name: 'Login',
|
|
component: () => import('@/views/auth/LoginView.vue'),
|
|
meta: { requiresGuest: true }
|
|
},
|
|
{
|
|
path: '/register',
|
|
name: 'Register',
|
|
component: () => import('@/views/auth/RegisterView.vue'),
|
|
meta: { requiresGuest: true }
|
|
},
|
|
|
|
// Protected routes
|
|
{
|
|
path: '/',
|
|
name: 'Dashboard',
|
|
component: () => import('@/views/DashboardView.vue'),
|
|
meta: { requiresAuth: true }
|
|
},
|
|
{
|
|
path: '/tasks',
|
|
name: 'Tasks',
|
|
component: () => import('@/views/TasksView.vue'),
|
|
meta: { requiresAuth: true }
|
|
},
|
|
{
|
|
path: '/projects',
|
|
name: 'Projects',
|
|
component: () => import('@/views/ProjectsView.vue'),
|
|
meta: { requiresAuth: true, roles: ['coordinator'], adminPermission: true }
|
|
},
|
|
// Project detail routes with tab navigation
|
|
{
|
|
path: '/projects/:projectId',
|
|
component: () => import('@/views/ProjectDetailView.vue'),
|
|
meta: { requiresAuth: true },
|
|
children: [
|
|
{
|
|
path: '',
|
|
name: 'ProjectOverview',
|
|
component: () => import('@/views/project/ProjectOverviewView.vue'),
|
|
meta: { tab: 'overview', tabLabel: 'Overview' }
|
|
},
|
|
{
|
|
path: 'shots',
|
|
name: 'ProjectShots',
|
|
component: () => import('@/views/project/ProjectShotsView.vue'),
|
|
meta: { tab: 'shots', tabLabel: 'Shots' }
|
|
},
|
|
{
|
|
path: 'shots/episode/:episodeId',
|
|
name: 'ProjectShotsEpisode',
|
|
component: () => import('@/views/project/ProjectShotsView.vue'),
|
|
meta: { tab: 'shots', tabLabel: 'Shots', hasEpisode: true }
|
|
},
|
|
{
|
|
path: 'shots/:shotId',
|
|
name: 'ShotDetail',
|
|
component: () => import('@/views/project/ShotDetailView.vue'),
|
|
meta: { requiresAuth: true }
|
|
},
|
|
{
|
|
path: 'assets',
|
|
name: 'ProjectAssets',
|
|
component: () => import('@/views/project/ProjectAssetsView.vue'),
|
|
meta: { tab: 'assets', tabLabel: 'Assets' }
|
|
},
|
|
{
|
|
path: 'tasks',
|
|
name: 'ProjectTasks',
|
|
component: () => import('@/views/project/ProjectTasksView.vue'),
|
|
meta: { tab: 'tasks', tabLabel: 'Tasks' }
|
|
},
|
|
{
|
|
path: 'settings',
|
|
name: 'ProjectSettings',
|
|
component: () => import('@/views/ProjectSettingsView.vue'),
|
|
meta: { tab: 'settings', tabLabel: 'Settings' }
|
|
}
|
|
]
|
|
},
|
|
{
|
|
path: '/projects/:projectId/episodes',
|
|
name: 'Episodes',
|
|
component: () => import('@/views/EpisodesView.vue'),
|
|
meta: { requiresAuth: true }
|
|
},
|
|
{
|
|
path: '/users',
|
|
name: 'Users',
|
|
component: () => import('@/views/UsersView.vue'),
|
|
meta: { requiresAuth: true, roles: ['coordinator'], adminPermission: true }
|
|
},
|
|
{
|
|
path: '/reviews',
|
|
name: 'Reviews',
|
|
component: () => import('@/views/ReviewsView.vue'),
|
|
meta: { requiresAuth: true, roles: ['director'], adminPermission: true }
|
|
},
|
|
{
|
|
path: '/settings',
|
|
name: 'Settings',
|
|
component: () => import('@/views/SettingsView.vue'),
|
|
meta: { requiresAuth: true, adminPermission: 'required' }
|
|
},
|
|
|
|
// Admin routes
|
|
{
|
|
path: '/admin',
|
|
name: 'Admin',
|
|
redirect: '/admin/deleted-items',
|
|
meta: { requiresAuth: true, adminPermission: 'required' }
|
|
},
|
|
{
|
|
path: '/admin/deleted-items',
|
|
name: 'RecoveryManagement',
|
|
component: () => import('@/views/admin/DeletedItemsManagementView.vue'),
|
|
meta: {
|
|
requiresAuth: true,
|
|
adminPermission: 'required',
|
|
title: 'Recovery Management'
|
|
}
|
|
},
|
|
|
|
// Developer routes
|
|
{
|
|
path: '/developer',
|
|
name: 'Developer',
|
|
redirect: '/developer/api-keys',
|
|
meta: { requiresAuth: true, roles: ['developer'] }
|
|
},
|
|
{
|
|
path: '/developer/api-keys',
|
|
name: 'DeveloperAPIKeys',
|
|
component: () => import('@/views/developer/APIKeysView.vue'),
|
|
meta: { requiresAuth: true, roles: ['developer'] }
|
|
},
|
|
{
|
|
path: '/developer/projects',
|
|
name: 'DeveloperProjects',
|
|
component: () => import('@/views/developer/ProjectsView.vue'),
|
|
meta: { requiresAuth: true, roles: ['developer'] }
|
|
},
|
|
{
|
|
path: '/developer/tasks',
|
|
name: 'DeveloperTasks',
|
|
component: () => import('@/views/developer/TasksView.vue'),
|
|
meta: { requiresAuth: true, roles: ['developer'] }
|
|
},
|
|
{
|
|
path: '/developer/analytics',
|
|
name: 'DeveloperAnalytics',
|
|
component: () => import('@/views/developer/AnalyticsView.vue'),
|
|
meta: { requiresAuth: true, roles: ['developer'] }
|
|
},
|
|
|
|
// Profile route
|
|
{
|
|
path: '/profile',
|
|
name: 'Profile',
|
|
component: () => import('@/views/ProfileView.vue'),
|
|
meta: { requiresAuth: true }
|
|
},
|
|
|
|
// Catch all route
|
|
{
|
|
path: '/:pathMatch(.*)*',
|
|
name: 'NotFound',
|
|
component: () => import('@/views/NotFoundView.vue')
|
|
}
|
|
]
|
|
|
|
const router = createRouter({
|
|
history: createWebHistory(),
|
|
routes
|
|
})
|
|
|
|
// Navigation guards
|
|
router.beforeEach(async (to, from, next) => {
|
|
const authStore = useAuthStore()
|
|
|
|
// Initialize auth if not already done
|
|
if (!authStore.user && authStore.accessToken) {
|
|
try {
|
|
await authStore.initializeAuth()
|
|
} catch (error) {
|
|
console.error('Auth initialization failed:', error)
|
|
}
|
|
}
|
|
|
|
const requiresAuth = to.meta.requiresAuth
|
|
const requiresGuest = to.meta.requiresGuest
|
|
const requiredRoles = to.meta.roles as string[] | undefined
|
|
const adminPermission = to.meta.adminPermission
|
|
const isAuthenticated = authStore.isAuthenticated
|
|
const userRole = authStore.userRole
|
|
const isAdmin = authStore.isAdmin
|
|
|
|
// Handle guest-only routes (login, register)
|
|
if (requiresGuest && isAuthenticated) {
|
|
return next('/')
|
|
}
|
|
|
|
// Handle protected routes
|
|
if (requiresAuth && !isAuthenticated) {
|
|
return next('/login')
|
|
}
|
|
|
|
// Handle role-based access with admin permission support
|
|
if (requiredRoles && userRole) {
|
|
const hasRequiredRole = requiredRoles.includes(userRole)
|
|
const hasAdminPermission = adminPermission && isAdmin
|
|
|
|
if (!hasRequiredRole && !hasAdminPermission) {
|
|
return next('/')
|
|
}
|
|
}
|
|
|
|
// Handle admin-only routes
|
|
if (adminPermission === 'required' && !isAdmin) {
|
|
return next('/')
|
|
}
|
|
|
|
next()
|
|
})
|
|
|
|
// Set page title from route meta
|
|
router.afterEach((to) => {
|
|
const title = to.meta.title as string
|
|
if (title) {
|
|
document.title = `${title} - VFX Studio`
|
|
} else {
|
|
document.title = 'VFX Studio'
|
|
}
|
|
})
|
|
|
|
export default router |