/** Vue Router configuration */ import { createRouter, createWebHistory } from 'vue-router' import { supabase } from '@/supabase' import { useAuthStore } from '@/stores/auth' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ // ─── Vistas Públicas Core ─────────────────────────────────────────── { path: '/', name: 'landing', component: () => import('@/views/LandingView.vue'), }, { path: '/splash', name: 'splash', component: () => import('@/views/SplashScreen.vue'), }, { path: '/map', name: 'map', component: () => import('@/views/MapView.vue'), }, { path: '/login', name: 'auth', component: () => import('@/views/AuthView.vue'), }, // ─── Vistas de Transporte ──────────────────────────────────────────── { path: '/routes', name: 'routes', component: () => import('@/views/RoutesView.vue'), }, { path: '/schedules', name: 'schedules', component: () => import('@/views/SchedulesView.vue'), }, { path: '/bus-stop/:id', name: 'bus-stop-details', component: () => import('@/views/BusStopDetailsView.vue'), }, { path: '/transporte', component: () => import('@/views/TransporteLayout.vue'), children: [ { path: '', redirect: '/transporte/viajes-turisticos' }, { path: 'viajes-turisticos', name: 'ViajesTuristicos', component: () => import('@/views/transporte/ViajesTuristicos.vue') }, { path: 'viajes-turisticos/:id', name: 'ShuttleDetalle', component: () => import('@/views/transporte/ShuttleDetalle.vue'), meta: { padre: 'ViajesTuristicos', titulo: 'Detalle del viaje' } }, { path: 'taxis', name: 'TaxisLocales', component: () => import('@/views/transporte/TaxisLocales.vue') } ] }, // ─── Vistas de Descubrir ───────────────────────────────────────────── { path: '/discover', name: 'discover', component: () => import('@/views/DiscoverView.vue'), }, { path: '/business/:id', name: 'business-details', component: () => import('@/views/BusinessDetailsView.vue'), }, { path: '/coupons', name: 'coupons', component: () => import('@/views/CouponsView.vue'), }, // ─── Vistas de Usuario ─────────────────────────────────────────────── { path: '/favorites', name: 'favorites', component: () => import('@/views/FavoritesView.vue'), }, { path: '/profile', name: 'profile', component: () => import('@/views/ProfileView.vue'), meta: { requiresAuth: true } }, // ─── Vistas de Admin ───────────────────────────────────────────────── { path: '/admin', name: 'admin-panel', component: () => import('@/views/AdminPanel.vue'), meta: { requiresAuth: true, role: 'ADMIN' } }, { path: '/admin/bus-stops', name: 'admin-bus-stops', component: () => import('@/views/AdminBusStops.vue'), meta: { requiresAuth: true, role: 'ADMIN' } }, { path: '/admin/routes', name: 'admin-routes', component: () => import('@/views/AdminRoutes.vue'), meta: { requiresAuth: true, role: 'ADMIN' } }, { path: '/admin/reports', name: 'admin-reports', component: () => import('@/views/AdminReports.vue'), meta: { requiresAuth: true, role: 'ADMIN' } }, { path: '/admin/schedules', name: 'admin-schedules', component: () => import('@/views/AdminSchedules.vue'), meta: { requiresAuth: true, role: 'ADMIN' } }, { path: '/admin/drivers', name: 'admin-drivers', component: () => import('@/views/AdminDrivers.vue'), meta: { requiresAuth: true, role: 'ADMIN' } }, { path: '/admin/analytics', name: 'admin-analytics', component: () => import('@/views/StrategicAnalytics.vue'), meta: { requiresAuth: true, role: 'ADMIN' } }, { path: '/admin/shuttles', name: 'admin-shuttles', component: () => import('@/views/AdminShuttles.vue'), meta: { requiresAuth: true, role: 'ADMIN' } }, { path: '/admin/business-edit/:id?', name: 'admin-business-editor', component: () => import('@/views/AdminBusinessEditor.vue'), meta: { requiresAuth: true } // available for promoters too }, // ─── Vistas de Roles Especiales ────────────────────────────────────── { path: '/promoter', name: 'promoter-dashboard', component: () => import('@/views/PromoterDashboard.vue'), meta: { requiresAuth: true, role: ['PROMOTER', 'ADMIN'] } }, { path: '/driver', name: 'driver-dashboard', component: () => import('@/views/DriverDashboard.vue'), meta: { requiresAuth: true, role: ['DRIVER', 'ADMIN'] } }, ], }) router.beforeEach(async (to, _from, next) => { // Rutas sin protección → pasar directo if (!to.meta.requiresAuth && !to.meta.role) { return next() } // OBTENER SESIÓN DE FORMA RÁPIDA: // En lugar de llamar SIEMPRE a supabase.auth.getSession() (lo cual bloquea la UI varios segundos // si el token expiró y necesita hacer fetch HTTP), usamos el estado en memoria primero. const authStore = useAuthStore() let session = authStore.userSession if (!session) { try { // Protección contra congelamiento: si no hay sesión en memoria, preguntamos a Supabase // pero le damos un tiempo máximo de 3 segundos para responder y no trabar la app. const timeoutMs = 3000 const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeoutMs)) const result = await Promise.race([supabase.auth.getSession(), timeout]) as any session = result?.data?.session || result?.session if (session) { authStore.userSession = session } } catch (e) { console.warn('SIBU | Router auth check timeout/error:', e) } } // Sin sesión en ruta protegida → login if (!session) { return next('/login') } // Si la ruta requiere un rol específico, verificar usando el store if (to.meta.role) { // Usar el store de auth (ya tiene el rol cargado desde el JWT) const authStore = useAuthStore() const userRole = authStore.role?.toUpperCase() || 'PASSENGER' const allowedRoles = Array.isArray(to.meta.role) ? (to.meta.role as string[]).map(r => r.toUpperCase()) : [(to.meta.role as string).toUpperCase()] if (!allowedRoles.includes(userRole)) { if (userRole === 'ADMIN') return next('/admin') else if (userRole === 'DRIVER') return next('/driver') else if (userRole === 'PROMOTER') return next('/promoter') else return next('/map') } } next() }) router.onError((error, to) => { // Si falla la carga de un chunk (al actualizar la app), recarga la página para obtener la última versión if (error.message.includes('Failed to fetch dynamically imported module') || error.message.includes('Importing a module script failed')) { window.location.href = to.fullPath } }) export default router