Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/lucavallini/aibar-frontend/llms.txt

Use this file to discover all available pages before exploring further.

Aibar SRL App implements a two-tier role model stored directly in the JWT payload. Every authenticated user is assigned either the administrador or empleado role at account creation. The role determines which routes a user can activate and which items appear in the sidebar navigation. Role enforcement happens at two layers simultaneously: Angular route guards block unauthorized navigation at the router level before any component is rendered, and the Sidebar component filters its own menu items at the UI level so that admin-only links are never shown to employees.

Roles

The rol field on the Usuario model is a string union of exactly two values:
// src/app/core/models/usuario.model.ts
export interface Usuario {
  id: string;
  nombre_usuario: string;
  nombre_completo: string;
  dni: string;
  rol: 'administrador' | 'empleado';
  activo: boolean;
  creado_en: string;
}
RoleAccess scope
empleadoViajes, Choferes, Camiones, Multas, Combustible
administradorEverything available to empleado, plus Auditoría and Usuarios

Reading the role

AuthService.getRol() decodes the JWT payload on demand and returns the rol claim. Because this reads from sessionStorage on every call, it always reflects the current token without any extra state management.
// src/app/core/services/auth.service.ts

getPayload(): { sub: string; rol: string } | null {
  const token = this.getToken();
  if (!token) return null;

  try {
    const payloadBase64 = token.split('.')[1];
    return JSON.parse(atob(payloadBase64));
  } catch {
    return null;
  }
}

getRol(): string | null {
  return this.getPayload()?.rol ?? null;
}

Route guards

Aibar SRL App uses two functional CanActivateFn guards. Both are pure functions that rely on Angular’s inject() to access AuthService and Router — no class boilerplate required.
authGuard protects every route inside the main layout. It checks the reactive isLoggedIn signal synchronously. If the signal is false, the user is redirected to /login and navigation is cancelled.
// src/app/core/guards/auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';

export const authGuard: CanActivateFn = () => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isLoggedIn()) {
    return true;
  }

  router.navigate(['/login']);
  return false;
};
Behavior summary:
  • Reads authService.isLoggedIn() — the Angular signal, not a network call.
  • Returns true immediately if a session token exists.
  • Redirects to /login and returns false if no token is found.
adminGuard is always layered on top of authGuard. For a user to reach an admin route, both guards must pass: authGuard confirms a valid session exists, then adminGuard confirms the session belongs to an administrator. An unauthenticated visitor hits authGuard first and is sent to /login before adminGuard is ever evaluated.

Protected routes

The application’s route configuration is defined in app.routes.ts. All authenticated routes are nested under a shared Layout shell that carries canActivate: [authGuard]. Admin-only routes additionally declare canActivate: [adminGuard].
// src/app/app.routes.ts
import { Routes } from '@angular/router';
import { LoginComponent } from './features/auth/login/login';
import { ListaChoferesComponent } from './features/choferes/lista-choferes/lista-choferes';
import { ListaViajes } from './features/viajes/lista-viajes/lista-viajes';
import { Layout } from './shared/layout/layout';
import { authGuard } from './core/guards/auth.guard';
import { ListaCamiones } from './features/camiones/lista-camiones/lista-camiones';
import { ListaMultas } from './features/multas/lista-multas/lista-multas';
import { ListaCombustible } from './features/combustible/lista-combustible/lista-combustible';
import { ListaAuditoria } from './features/auditoria/lista-auditoria/lista-auditoria';
import { adminGuard } from './core/guards/admin.guard';
import { ListaUsuarios } from './features/usuarios/lista-usuarios/lista-usuarios';

export const routes: Routes = [
  { path: 'login', component: LoginComponent },
  {
    path: '',
    component: Layout,
    canActivate: [authGuard],
    children: [
      { path: 'viajes', component: ListaViajes },
      { path: 'choferes', component: ListaChoferesComponent },
      { path: '', redirectTo: 'viajes', pathMatch: 'full' },
      { path: 'camiones', component: ListaCamiones },
      { path: 'multas', component: ListaMultas },
      { path: 'combustible', component: ListaCombustible },
      { path: 'auditoria', component: ListaAuditoria },
      { path: 'auditoria', component: ListaAuditoria, canActivate: [adminGuard] },
      { path: 'usuarios', component: ListaUsuarios, canActivate: [adminGuard] },
    ]
  },
];
The app.routes.ts file currently defines the /auditoria path twice — once without adminGuard and once with it. Angular’s router matches the first definition it encounters, so the unguarded entry takes precedence in practice. The second adminGuard-protected entry has no effect as written. This is a known issue in the source that should be resolved by keeping only the adminGuard-protected definition.
The table below summarizes the guard coverage per route:
RouteGuard(s)Accessible by
/login(none)Everyone
/viajesauthGuardempleado, administrador
/choferesauthGuardempleado, administrador
/camionesauthGuardempleado, administrador
/multasauthGuardempleado, administrador
/combustibleauthGuardempleado, administrador
/auditoriaauthGuard + adminGuardadministrador only
/usuariosauthGuard + adminGuardadministrador only
Route guards stop unauthorized navigation at the router, but the Sidebar component goes one step further — it hides admin-only links from employees entirely, so they never see menu items they cannot access. Each menu item carries a soloAdmin flag. The itemsVisibles getter evaluates authService.getRol() and filters out any item whose soloAdmin is true when the current user is not an administrator:
// src/app/shared/components/sidebar/sidebar.ts

interface ItemMenu {
  label: string;
  ruta: string;
  soloAdmin: boolean;
}

export class Sidebar {
  @Input() abierta = false;

  itemsMenu: ItemMenu[] = [
    { label: 'Viajes', ruta: '/viajes', soloAdmin: false },
    { label: 'Choferes', ruta: '/choferes', soloAdmin: false },
    { label: 'Camiones', ruta: '/camiones', soloAdmin: false },
    { label: 'Multas', ruta: '/multas', soloAdmin: false },
    { label: 'Combustible', ruta: '/combustible', soloAdmin: false },
    { label: 'Auditoría', ruta: '/auditoria', soloAdmin: true },
    { label: 'Usuarios', ruta: '/usuarios', soloAdmin: true },
  ];

  constructor(private authService: AuthService) {}

  get itemsVisibles(): ItemMenu[] {
    const esAdmin = this.authService.getRol() === 'administrador';
    return this.itemsMenu.filter(item => !item.soloAdmin || esAdmin);
  }
}
The sidebar template binds to itemsVisibles rather than itemsMenu, so employees see exactly five navigation items (Viajes, Choferes, Camiones, Multas, Combustible) while administrators see all seven.

empleado view

Sees: Viajes, Choferes, Camiones, Multas, Combustible.Attempting to navigate directly to /auditoria or /usuarios triggers adminGuard and redirects to /viajes.

administrador view

Sees all seven items including Auditoría and Usuarios.Has unrestricted access to every route in the application.

Build docs developers (and LLMs) love