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
- Arquitectura de Enrutamiento
- Estructura de Rutas
- Configuración de Rutas
- Protección de Rutas
- Autorización (Control de Acceso Basado en Roles)
- Componentes de Navegación
- Carga Diferida
- Manejo de Errores
- Utilidades de Navegación
- Ejemplos de Flujo de Navegación
- 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)
| Ruta | Componente | Propósito | Acceso |
|---|---|---|---|
/auth/login | LoginRoute | Inicio de sesión de usuario | Público |
/auth/register-client | SignUpClientRoute | Registro de cliente | Público |
/auth/register-provider | SignUpProviderRoute | Registro de proveedor | Público |
/auth/forgot-password | ForgotPasswordRoute | Recuperación de contraseña | Público |
/auth/reset-password | ResetPasswordRoute | Restablecimiento de contraseña | Público |
/auth/provider-terms-and-conditions | TermsConditionsPage | Términos y condiciones del proveedor | Público |
/auth/client-terms-and-conditions | ClientTermsConditionsPage | Términos y condiciones del cliente | Público |
Rutas de la Aplicación (Protegidas)
Dashboard y Monitoreo
| Ruta | Componente | Propósito | Roles | Carga Diferida |
|---|---|---|---|---|
/app/dashboard | DashboardRoute | Dashboard principal con KPIs | Admin, Agent | ✅ |
/app/monitoring-panel | MonitoringPanelRoute | Monitoreo de mensajes | Admin, Agent, Client | ✅ |
Órdenes
| Ruta | Componente | Propósito | Roles | Carga Diferida |
|---|---|---|---|---|
/app/orders | OrdersRoute | Lista de órdenes | Admin, Agent | ✅ |
/app/orders/:id | OrderDetailsRoute | Detalles de orden | Admin, Agent | ✅ |
/app/orders/:id/work-report | WorkReport | Vista de reporte de trabajo | Admin, Agent | ✅ |
Gestión de Usuarios
| Ruta | Componente | Propósito | Roles | Carga Diferida |
|---|---|---|---|---|
/app/users | ListClientsRoute | Lista de clientes | Admin, Agent | ✅ |
/app/users/providers | ListProvidersRoute | Lista de proveedores | Admin, Agent | ✅ |
/app/users/management | ListManagementUsersRoute | Usuarios de gestión | Admin | ✅ |
/app/users/client/:id | DetailClientRoute | Detalles de cliente | Admin, Agent | ✅ |
/app/users/provider/:id | DetailProviderRoute | Detalles de proveedor | Admin, Agent | ✅ |
Activos
| Ruta | Componente | Propósito | Roles | Carga Diferida |
|---|---|---|---|---|
/app/assets | MyAssetsRoute | Lista de activos | Admin, Agent | ✅ |
/app/assets/:id | AssetsDetailsRoute | Detalles de activo | Admin, Agent | ✅ |
/app/assets/errors | MyErrorsRoute | Lista de errores de activos | Admin, Agent | ✅ |
/app/assets/errors/:id | ErrorDetailsRoute | Detalles de error | Admin, Agent | ✅ |
Marketplace/Subastas (Admin/Agent)
| Ruta | Componente | Propósito | Roles | Carga Diferida |
|---|---|---|---|---|
/app/marketplace/auctions | AuctionsListRoute | Lista de subastas | Admin, Agent | ✅ |
/app/marketplace/auctions/finished | FinishedAuctionsListRoute | Subastas finalizadas | Admin, Agent | ✅ |
/app/marketplace/auctions/:id/bids | BidListRoute | Ofertas de subasta | Admin, Agent | ✅ |
/app/marketplace/auctions/:id/select-quotation | SelectQuotationRoute | Seleccionar cotización | Admin, Agent | ✅ |
/app/marketplace/auctions/:id/inflate-quotation | InflateQuotationRoute | Inflar cotización | Admin, Agent | ✅ |
Marketplace/Subastas (Proveedor)
| Ruta | Componente | Propósito | Roles | Carga Diferida |
|---|---|---|---|---|
/app/marketplace/provider/auctions | ProviderAuctionListRoute | Subastas de proveedor | Provider | ✅ |
/app/marketplace/provider/auctions/finished | ProviderFinishedAuctionListRoute | Subastas finalizadas | Provider | ✅ |
/app/marketplace/provider/auctions/:id | ProviderAuctionDetailsRoute | Detalles de subasta | Provider | ✅ |
/app/marketplace/provider/auctions/:id/offer | ProviderOfferFormRoute | Enviar oferta | Provider | ✅ |
Proveedores
| Ruta | Componente | Propósito | Roles | Carga Diferida |
|---|---|---|---|---|
/app/providers/my-works | MyWorksRoute | Lista de trabajos del proveedor | Provider | ✅ |
/app/providers/my-works/:id | MyWorksDetailsRoute | Detalles de trabajo | Provider | ✅ |
/app/providers/my-works/:id/Resumen | WorkResumenDetail | Resumen de trabajo | Provider | ✅ |
/app/providers/my-works/:id/Resumen/edit | WorkResumenEditDetail | Editar resumen de trabajo | Provider | ✅ |
/app/providers/invoices | InvoicesRoute | Facturas de proveedor | Provider | ✅ |
/app/providers/profile | ProviderProfileRoute | Perfil de proveedor | Provider | ✅ |
/app/providers/upload-Documentos | UploadDocumentosRoute | Cargar documentos | Provider | ✅ |
Clientes
| Ruta | Componente | Propósito | Roles | Carga Diferida |
|---|---|---|---|---|
/app/clients/my-orders | MyOrdersRoute | Lista de órdenes del cliente | Client | ✅ |
/app/clients/my-orders/:id | MyOrdersDetailsRoute | Detalles de orden del cliente | Client | ✅ |
Contratos y Contabilidad
| Ruta | Componente | Propósito | Roles | Carga Diferida |
|---|---|---|---|---|
/app/contracts-and-policies | ContractPoliciesRoute | Contratos y pólizas | Admin, Agent | ✅ |
/app/accounting | AccountingRoute | Contabilidad | Admin, Agent | ✅ |
/app/admin-contracts-and-policies | ContractsPoliciesRoute | Contratos de administración | Admin | ✅ |
/app/guarantees-contracts-and-policies | GuaranteesRoute | Garantías | Admin, Agent | ✅ |
Rutas Públicas
| Ruta | Componente | Propósito | Acceso |
|---|---|---|---|
/public/provider-order-confirm | ProviderOrderConfirmRoute | Confirmación de orden de proveedor | Pú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:
- Verificar si los datos del usuario están cargando
- Si no está autenticado, redirigir a login
- Verificar si el rol del usuario tiene acceso a la ruta
- Si no está autorizado, redirigir a la ruta inicial del usuario
- 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:
- Verificar si los datos del usuario están cargando
- Si está autenticado, redirigir a ruta inicial basada en rol
- 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
Navegación Lateral (Sidebar)
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
Navegación Programática
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>;};Navegación Declarativa
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
Navegación Basada en Roles
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.tspara 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
pathspara navegación con seguridad de tipos - Usar
useNavigatepara navegación programática - Usar componente
Linkpara 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.tsxalgesta-dashboard-react/src/lib/auth.tsxalgesta-dashboard-react/src/lib/authorization.tsxalgesta-dashboard-react/src/config/paths.tsalgesta-dashboard-react/src/types/authorization.tsalgesta-dashboard-react/src/utils/sidebar-settings.ts