Saltearse al contenido

Enrutamiento y Navegación del Frontend

Enrutamiento y Navegación del Frontend

Versión: 1.0 Fecha: 2025-11-19 Proyecto: Algesta Dashboard React

Tabla de Contenidos

  1. Arquitectura de Enrutamiento
  2. Estructura de Rutas
  3. Configuración de Rutas
  4. Protección de Rutas
  5. Autorización (Control de Acceso Basado en Roles)
  6. Componentes de Navegación
  7. Carga Diferida
  8. Manejo de Errores
  9. Utilidades de Navegación
  10. Ejemplos de Flujo de Navegación
  11. Mejores Prácticas

Arquitectura de Enrutamiento

El dashboard utiliza React Router 6.30.0 con la API de enrutador de datos para una solución de enrutamiento moderna y con seguridad de tipos.

Funcionalidades Clave:

  • Configuración de rutas centralizada en src/app/router.tsx
  • Carga diferida para división de código y rendimiento
  • Rutas protegidas con autenticación y autorización
  • Límites de error para manejo resiliente de errores
  • Generación de rutas con seguridad de tipos

Estructura de Rutas

graph TD
    A[Root /] --> B[Rutas de Autenticación /auth/*]
    A --> C[Rutas Públicas /public/*]
    A --> D[Rutas de App /app/*]

    B --> B1[Login]
    B --> B2[Registrar Cliente]
    B --> B3[Registrar Proveedor]
    B --> B4[Olvidé Contraseña]
    B --> B5[Restablecer Contraseña]

    D --> D1[Dashboard]
    D --> D2[Órdenes]
    D --> D3[Marketplace]
    D --> D4[Proveedores]
    D --> D5[Clientes]
    D --> D6[Usuarios]
    D --> D7[Activos]
    D --> D8[Contratos]
    D --> D9[Contabilidad]
    D --> D10[Monitoreo]

Configuración de Rutas

Rutas de Autenticación (Públicas)

RutaComponentePropósitoAcceso
/auth/loginLoginRouteInicio de sesión de usuarioPúblico
/auth/register-clientSignUpClientRouteRegistro de clientePúblico
/auth/register-providerSignUpProviderRouteRegistro de proveedorPúblico
/auth/forgot-passwordForgotPasswordRouteRecuperación de contraseñaPúblico
/auth/reset-passwordResetPasswordRouteRestablecimiento de contraseñaPúblico
/auth/provider-terms-and-conditionsTermsConditionsPageTérminos y condiciones del proveedorPúblico
/auth/client-terms-and-conditionsClientTermsConditionsPageTérminos y condiciones del clientePúblico

Rutas de la Aplicación (Protegidas)

Dashboard y Monitoreo

RutaComponentePropósitoRolesCarga Diferida
/app/dashboardDashboardRouteDashboard principal con KPIsAdmin, Agent
/app/monitoring-panelMonitoringPanelRouteMonitoreo de mensajesAdmin, Agent, Client

Órdenes

RutaComponentePropósitoRolesCarga Diferida
/app/ordersOrdersRouteLista de órdenesAdmin, Agent
/app/orders/:idOrderDetailsRouteDetalles de ordenAdmin, Agent
/app/orders/:id/work-reportWorkReportVista de reporte de trabajoAdmin, Agent

Gestión de Usuarios

RutaComponentePropósitoRolesCarga Diferida
/app/usersListClientsRouteLista de clientesAdmin, Agent
/app/users/providersListProvidersRouteLista de proveedoresAdmin, Agent
/app/users/managementListManagementUsersRouteUsuarios de gestiónAdmin
/app/users/client/:idDetailClientRouteDetalles de clienteAdmin, Agent
/app/users/provider/:idDetailProviderRouteDetalles de proveedorAdmin, Agent

Activos

RutaComponentePropósitoRolesCarga Diferida
/app/assetsMyAssetsRouteLista de activosAdmin, Agent
/app/assets/:idAssetsDetailsRouteDetalles de activoAdmin, Agent
/app/assets/errorsMyErrorsRouteLista de errores de activosAdmin, Agent
/app/assets/errors/:idErrorDetailsRouteDetalles de errorAdmin, Agent

Marketplace/Subastas (Admin/Agent)

RutaComponentePropósitoRolesCarga Diferida
/app/marketplace/auctionsAuctionsListRouteLista de subastasAdmin, Agent
/app/marketplace/auctions/finishedFinishedAuctionsListRouteSubastas finalizadasAdmin, Agent
/app/marketplace/auctions/:id/bidsBidListRouteOfertas de subastaAdmin, Agent
/app/marketplace/auctions/:id/select-quotationSelectQuotationRouteSeleccionar cotizaciónAdmin, Agent
/app/marketplace/auctions/:id/inflate-quotationInflateQuotationRouteInflar cotizaciónAdmin, Agent

Marketplace/Subastas (Proveedor)

RutaComponentePropósitoRolesCarga Diferida
/app/marketplace/provider/auctionsProviderAuctionListRouteSubastas de proveedorProvider
/app/marketplace/provider/auctions/finishedProviderFinishedAuctionListRouteSubastas finalizadasProvider
/app/marketplace/provider/auctions/:idProviderAuctionDetailsRouteDetalles de subastaProvider
/app/marketplace/provider/auctions/:id/offerProviderOfferFormRouteEnviar ofertaProvider

Proveedores

RutaComponentePropósitoRolesCarga Diferida
/app/providers/my-worksMyWorksRouteLista de trabajos del proveedorProvider
/app/providers/my-works/:idMyWorksDetailsRouteDetalles de trabajoProvider
/app/providers/my-works/:id/ResumenWorkResumenDetailResumen de trabajoProvider
/app/providers/my-works/:id/Resumen/editWorkResumenEditDetailEditar resumen de trabajoProvider
/app/providers/invoicesInvoicesRouteFacturas de proveedorProvider
/app/providers/profileProviderProfileRoutePerfil de proveedorProvider
/app/providers/upload-DocumentosUploadDocumentosRouteCargar documentosProvider

Clientes

RutaComponentePropósitoRolesCarga Diferida
/app/clients/my-ordersMyOrdersRouteLista de órdenes del clienteClient
/app/clients/my-orders/:idMyOrdersDetailsRouteDetalles de orden del clienteClient

Contratos y Contabilidad

RutaComponentePropósitoRolesCarga Diferida
/app/contracts-and-policiesContractPoliciesRouteContratos y pólizasAdmin, Agent
/app/accountingAccountingRouteContabilidadAdmin, Agent
/app/admin-contracts-and-policiesContractsPoliciesRouteContratos de administraciónAdmin
/app/guarantees-contracts-and-policiesGuaranteesRouteGarantíasAdmin, Agent

Rutas Públicas

RutaComponentePropósitoAcceso
/public/provider-order-confirmProviderOrderConfirmRouteConfirmación de orden de proveedorPúblico

Protección de Rutas

Componente ProtectedRoute

Ubicado en src/lib/auth.tsx:

export const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
const user = useUser();
const { pathname } = useLocation();
// Mostrar cargador mientras se verifica autenticación
if ((user.isLoading || user.isFetching) && !user.data) {
return <AppLoader />;
}
// Redirigir a login si no está autenticado
if (!user.data) {
return <Navigate to={paths.auth.login.path} replace />;
}
// Verificar acceso basado en roles
if (ROUTE_POLICIES[pathname]?.(user.data.role!) === false) {
const initialRoute = getInitialRouteByRole(user.data.role || '');
return <Navigate to={initialRoute} replace />;
}
return children;
};

Flujo:

  1. Verificar si los datos del usuario están cargando
  2. Si no está autenticado, redirigir a login
  3. Verificar si el rol del usuario tiene acceso a la ruta
  4. Si no está autorizado, redirigir a la ruta inicial del usuario
  5. De lo contrario, renderizar children

Componente PublicRoute

Ubicado en src/lib/auth.tsx:

export const PublicRoute = ({ children }: { children: React.ReactNode }) => {
const user = useUser();
// Mostrar cargador mientras se verifica autenticación
if (user.isLoading || user.isFetching) {
return <AppLoader />;
}
// Redirigir a dashboard si ya está autenticado
if (user.data) {
const initialRoute = getInitialRouteByRole(user.data.role || '');
return <Navigate to={initialRoute} replace />;
}
return children;
};

Flujo:

  1. Verificar si los datos del usuario están cargando
  2. Si está autenticado, redirigir a ruta inicial basada en rol
  3. De lo contrario, renderizar children

Autorización (Control de Acceso Basado en Roles)

Roles de Usuario

Definidos en src/types/authorization.ts:

export const ROLES = {
ADMIN: 'admin',
AGENT: 'agent',
PROVIDER: 'provider',
CLIENT: 'client',
} as const;
export type RoleTypes = typeof ROLES[keyof typeof ROLES];

Políticas de Rutas

Definidas en src/lib/authorization.tsx:

export const ROUTE_POLICIES = {
// Panel de monitoreo - Todos excepto proveedores
[paths.app['monitoring-panel'].getHref()]: (role: RoleTypes) => {
if (role === ROLES.PROVIDER) return false;
return true;
},
// Dashboard - Solo Admin y Agent
[paths.app.dashboard.getHref()]: (role: RoleTypes) => {
if (role !== ROLES.ADMIN && role !== ROLES.AGENT) return false;
return true;
},
// Usuarios de gestión - Solo Admin
[paths.app['users-management'].getHref()]: (role: RoleTypes) => {
if (role !== ROLES.ADMIN) return false;
return true;
},
// Órdenes - Solo Admin y Agent
[paths.app.orders.getHref()]: (role: RoleTypes) => {
if (role !== ROLES.ADMIN && role !== ROLES.AGENT) return false;
return true;
},
// Rutas de proveedor - Solo Provider
[paths.app.providers['my-works'].getHref()]: (role: RoleTypes) => {
if (role !== ROLES.PROVIDER) return false;
return true;
},
// Rutas de cliente - Solo Client
[paths.app.clients['my-orders'].getHref()]: (role: RoleTypes) => {
if (role !== ROLES.CLIENT) return false;
return true;
},
// ... más políticas
};

Ruta Inicial por Rol

export const getInitialRouteByRole = (role: RoleTypes): string => {
switch (role) {
case ROLES.ADMIN:
return paths.app.dashboard.getHref();
case ROLES.AGENT:
return paths.app.dashboard.getHref();
case ROLES.PROVIDER:
return paths.app.marketplace.provider.auctions.getHref();
case ROLES.CLIENT:
return paths.app.clients['my-orders'].getHref();
default:
return paths.auth.login.getHref();
}
};

Rutas Iniciales Basadas en Roles:

  • Admin: /app/dashboard
  • Agent: /app/dashboard
  • Provider: /app/marketplace/provider/auctions
  • Client: /app/clients/my-orders

Componentes de Navegación

Ubicado en src/Componentes/ui/app-sidebar.tsx:

Funcionalidades:

  • Elementos de menú basados en roles
  • Resaltado de ruta activa
  • Secciones colapsables
  • Soporte de iconos (Lucide React)
  • Soporte de menú anidado

Barra de Navegación Superior

Ubicada en src/Componentes/ui/nav-bar.tsx:

Funcionalidades:

  • Menú de usuario
  • Notificaciones
  • Acciones rápidas
  • Diseño responsivo

Menú de Usuario

Ubicado en src/Componentes/ui/nav-user.tsx:

Funcionalidades:

  • Perfil de usuario
  • Enlace de configuración
  • Acción de cerrar sesión

Configuración del Menú del Sidebar

Los elementos del menú se configuran según el rol del usuario en src/utils/sidebar-settings.ts:

export const getMenuItemsByRole = (role: RoleTypes) => {
const baseItems = [
{
title: 'Dashboard',
url: paths.app.dashboard.getHref(),
icon: LayoutDashboard,
},
];
if (role === ROLES.ADMIN || role === ROLES.AGENT) {
return [
...baseItems,
{
title: 'Orders',
url: paths.app.orders.getHref(),
icon: ShoppingCart,
},
{
title: 'Users',
icon: Users,
items: [
{ title: 'Clients', url: paths.app.users.getHref() },
{ title: 'Providers', url: paths.app.users.providers.getHref() },
],
},
// ... more items
];
}
if (role === ROLES.PROVIDER) {
return [
{
title: 'Auctions',
url: paths.app.marketplace.provider.auctions.getHref(),
icon: Gavel,
},
{
title: 'My Works',
url: paths.app.providers['my-works'].getHref(),
icon: Briefcase,
},
// ... more items
];
}
if (role === ROLES.CLIENT) {
return [
{
title: 'My Orders',
url: paths.app.clients['my-orders'].getHref(),
icon: ShoppingCart,
},
// ... more items
];
}
return baseItems;
};

Carga Diferida

Todas las rutas se cargan de forma diferida para un rendimiento óptimo:

{
path: paths.app.orders.path,
lazy: async () => {
const { OrdersRoute } = await import('./routes/dashboard/orders/orders');
return {
Component: OrdersRoute,
};
},
ErrorBoundary: AppRootErrorBoundary,
}

Beneficios:

  • Paquete Inicial Más Pequeño: Solo carga el código de la ruta actual
  • Carga Inicial Más Rápida: Reduce el tiempo hasta interactivo
  • División de Código: División automática de código por ruta
  • Mejor Rendimiento: Carga bajo demanda

Manejo de Errores

Límites de Error (Error Boundaries)

Todas las rutas están envueltas con AppRootErrorBoundary:

{
path: paths.app.orders.path,
lazy: async () => {
const { OrdersRoute } = await import('./routes/dashboard/orders/orders');
return {
Component: OrdersRoute,
};
},
ErrorBoundary: AppRootErrorBoundary, // Límite de error
}

Funcionalidades:

  • Captura errores de renderizado
  • Muestra UI de error amigable para el usuario
  • Registra errores para depuración
  • Previene el colapso de la aplicación

Ruta No Encontrada

{
path: '*',
lazy: async () => {
const { NotFoundRoute } = await import('./routes/not-found');
return {
Component: NotFoundRoute,
};
},
ErrorBoundary: AppRootErrorBoundary,
}

Utilidades de Navegación

Configuración de Rutas

Definiciones de rutas centralizadas en src/config/paths.ts:

export const paths = {
home: {
path: '/',
getHref: () => '/',
},
auth: {
login: {
path: '/auth/login',
getHref: () => '/auth/login',
},
register: {
client: {
path: '/auth/register-client',
getHref: () => '/auth/register-client',
},
provider: {
path: '/auth/register-provider',
getHref: () => '/auth/register-provider',
},
},
},
app: {
orders: {
path: '/app/orders',
getHref: () => '/app/orders',
},
'order-details': {
path: '/app/orders/:id',
getHref: (id: string) => `/app/orders/${id}`,
},
// ... más rutas
},
};

Beneficios:

  • Generación de rutas con seguridad de tipos
  • Gestión centralizada de rutas
  • Refactorización fácil
  • Soporte de autocompletado
import { useNavigate } from 'react-router-dom';
import { paths } from '@/config/paths';
const MyComponent = () => {
const navigate = useNavigate();
const goToOrder = (orderId: string) => {
navigate(paths.app['order-details'].getHref(orderId));
};
return <button onClick={() => goToOrder('123')}>Ver Orden</button>;
};
import { Link } from 'react-router-dom';
import { paths } from '@/config/paths';
const OrderLink = ({ orderId }: Props) => {
return (
<Link to={paths.app['order-details'].getHref(orderId)}>
Ver Orden
</Link>
);
};

Restauración de Scroll

El componente personalizado ScrollRestoration asegura que la posición del scroll se restaure al navegar.

Ejemplos de Flujo de Navegación

Flujo de Autenticación

sequenceDiagram
    participant User as Usuario
    participant PublicRoute as Ruta Pública
    participant LoginRoute as Ruta de Login
    participant AuthAPI as API de Autenticación
    participant ProtectedRoute as Ruta Protegida
    participant Dashboard

    User->>PublicRoute: Navegar a /auth/login
    PublicRoute->>PublicRoute: Verificar si está autenticado
    alt No autenticado
        PublicRoute->>LoginRoute: Renderizar formulario de login
        User->>LoginRoute: Enviar credenciales
        LoginRoute->>AuthAPI: POST /api/auth/login
        AuthAPI-->>LoginRoute: Retornar token + usuario
        LoginRoute->>LoginRoute: Almacenar token en localStorage
        LoginRoute->>ProtectedRoute: Navegar a /app/dashboard
        ProtectedRoute->>ProtectedRoute: Verificar autenticación
        ProtectedRoute->>ProtectedRoute: Verificar autorización de rol
        ProtectedRoute->>Dashboard: Renderizar dashboard
    else Ya autenticado
        PublicRoute->>Dashboard: Redirigir a dashboard
    end
sequenceDiagram
    participant User as Usuario
    participant ProtectedRoute as Ruta Protegida
    participant Authorization as Autorización
    participant Route as Ruta

    User->>ProtectedRoute: Navegar a ruta
    ProtectedRoute->>ProtectedRoute: Verificar autenticación
    alt No autenticado
        ProtectedRoute->>User: Redirigir a /auth/login
    else Autenticado
        ProtectedRoute->>Authorization: Verificar rol para ruta
        alt Rol permitido
            Authorization->>Route: Renderizar ruta
        else Rol no permitido
            Authorization->>User: Redirigir a ruta inicial del rol
        end
    end

Mejores Prácticas

1. Organización de Rutas

  • Agrupar rutas relacionadas juntas
  • Usar convenciones de nomenclatura consistentes
  • Definir rutas en paths.ts para seguridad de tipos
  • Cargar de forma diferida todas las rutas para rendimiento

2. Protección de Rutas

  • Siempre envolver rutas protegidas con ProtectedRoute
  • Definir políticas de roles en ROUTE_POLICIES
  • Redirigir a la ruta apropiada según el rol
  • Mostrar estado de carga mientras se verifica autenticación

3. Navegación

  • Usar configuración de paths para navegación con seguridad de tipos
  • Usar useNavigate para navegación programática
  • Usar componente Link para navegación declarativa
  • Evitar rutas hardcodeadas en componentes

4. Manejo de Errores

  • Usar límites de error en todas las rutas
  • Proveer UI de respaldo para errores
  • Registrar errores para depuración
  • Probar escenarios de error

5. Rendimiento

  • Cargar de forma diferida todas las rutas
  • Usar división de código
  • Precargar rutas críticas cuando sea apropiado
  • Minimizar tamaño del paquete

6. Accesibilidad

  • Usar HTML semántico para navegación
  • Proveer etiquetas ARIA para lectores de pantalla
  • Asegurar que la navegación con teclado funcione
  • Probar con lectores de pantalla

7. Pruebas

  • Probar lógica de protección de rutas
  • Probar control de acceso basado en roles
  • Probar flujos de navegación
  • Probar límites de error

Referencias:

  • algesta-dashboard-react/src/app/router.tsx
  • algesta-dashboard-react/src/lib/auth.tsx
  • algesta-dashboard-react/src/lib/authorization.tsx
  • algesta-dashboard-react/src/config/paths.ts
  • algesta-dashboard-react/src/types/authorization.ts
  • algesta-dashboard-react/src/utils/sidebar-settings.ts