Skip to content

Router y Guards ​

Tabla de rutas ​

RutaAuthPermiso requeridoVista
/——Redirect → /login
/loginGuest only—LoginView
/registerGuest only—RegisterView
/forgot-passwordGuest only—ForgotPasswordView
/reset-passwordGuest only—ResetPasswordView
/verify-emailPúblico—VerifyEmailView
/onboardingAuth—OnboardingView
/companies/:companyIdAuth—Redirect → dashboard
/companies/:companyId/dashboardAuth—DashboardView
/companies/:companyId/settingsAuthcompany:readSettingsView
/companies/:companyId/profileAuth—ProfileView
/companies/:companyId/change-passwordAuth—ChangePasswordView
/companies/:companyId/membersAuthusers:readMembersView
/companies/:companyId/branchesAuthbranches:readBranchesView
/companies/:companyId/rolesAuthroles:readRolesView
/companies/:companyId/auditAuthaudit:readAuditView
/:pathMatch(.*)*——NotFoundView

Todas las vistas usan lazy imports:

ts
component: () => import('@/views/MembersView.vue')

Campos meta ​

ts
interface RouteMeta {
  requiresAuth?: boolean       // Redirige a /login si no está autenticado
  requiresGuest?: boolean      // Redirige al dashboard si ya está autenticado
  requiredPermission?: string  // Código de permiso: 'users:read', 'branches:create', etc.
  title: string                // Usado en document.title y afterEach
}

El guard router.beforeEach maneja estos escenarios en orden:

1. Restaurar tenant activo ​

ts
authStore.healActiveTenant()
// Si activeTenantId es null, lo restaura desde localStorage

2. Super-admin en empresa desconocida ​

Si el usuario es super-admin y navega a una empresa que no está en su lista de tenants, carga allCompanies para resolverla:

ts
if (authStore.isSuperAdmin && !knownTenant) {
  await companyStore.fetchAllCompanies()
}

3. Redirigir si no está autenticado ​

ts
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
  return { path: '/login', query: { redirect: to.fullPath } }
}

4. Canonicalización de slug ​

Si el :companyId en la URL es un UUID pero el tenant tiene slug, se redirige a la URL con slug:

ts
if (tenant.slug && companyIdParam !== tenant.slug) {
  return { ...to, params: { ...to.params, companyId: tenant.slug }, replace: true }
}

5. Gate de onboarding ​

Si el usuario autenticado no tiene empresas (y no es super-admin), se redirige a /onboarding:

ts
if (!authStore.user?.tenants?.length && !authStore.isSuperAdmin) {
  return { path: '/onboarding' }
}

6. Forzar cambio de contraseña ​

ts
if (authStore.user?.must_change_password && to.path !== changePasswordPath) {
  return { path: companyPath('/change-password') }
}

7. Verificación de permiso ​

ts
if (to.meta.requiredPermission && !authStore.hasPermission(to.meta.requiredPermission)) {
  return { path: companyPath('/dashboard') }
}

afterEach — document.title ​

ts
router.afterEach((to) => {
  const section = to.meta.title || ''
  const company = authStore.currentTenant?.name || ''
  document.title = [section, company, 'Kaleo'].filter(Boolean).join(' · ')
  // → "Miembros del Equipo · Empresa X · Kaleo"
})

Agregar una nueva ruta ​

ts
// En frontend/src/router/index.ts
{
  path: '/companies/:companyId/invoices',
  component: () => import('@/views/InvoicesView.vue'),
  meta: {
    requiresAuth: true,
    requiredPermission: Permissions.INVOICES.READ,
    title: 'Facturas',
  },
},

useCompanyPath composable ​

ts
import { useCompanyPath } from '@/composables/useCompanyPath'

const { companyId, companyPath } = useCompanyPath()

// companyId — Ref<string> con el UUID de la empresa activa
console.log(companyId.value)  // '550e8400-...'

// companyPath() — construye URLs relativas a la empresa activa
companyPath('/settings')  // '/companies/mi-empresa/settings'
companyPath('/members')   // '/companies/mi-empresa/members'

// Navegar a otra sección de la empresa activa
router.push(companyPath('/audit'))

TIP

companyPath resuelve el parámetro usando el slug si está disponible, para mantener URLs legibles. Usa siempre companyPath() en lugar de construir las URLs manualmente.

Documentación de Kaleo — plataforma SaaS multi-tenant