/** * Protected Route component for handling authentication and authorization */ import React from 'react'; import { Navigate, useLocation } from 'react-router-dom'; import { useAuthUser, useIsAuthenticated, useAuthLoading } from '../stores'; import { RouteConfig, canAccessRoute, ROUTES } from './routes.config'; interface ProtectedRouteProps { children: React.ReactNode; route?: RouteConfig; fallback?: React.ReactNode; redirectTo?: string; } interface LoadingSpinnerProps { message?: string; } const LoadingSpinner: React.FC = ({ message = 'Cargando...' }) => (

{message}

); const UnauthorizedPage: React.FC = () => (

Acceso no autorizado

No tienes permisos para acceder a esta página. Contacta con tu administrador si crees que esto es un error.

); const ForbiddenPage: React.FC = () => (

Acceso restringido

Tu cuenta no tiene los permisos necesarios para ver esta sección. Contacta con tu administrador para solicitar acceso.

); export const ProtectedRoute: React.FC = ({ children, route, fallback, redirectTo, }) => { const user = useAuthUser(); const isAuthenticated = useIsAuthenticated(); const isLoading = useAuthLoading(); const location = useLocation(); // Show loading spinner while checking authentication if (isLoading) { return fallback || ; } // If route doesn't require auth, render children if (route && !route.requiresAuth) { return <>{children}; } // If not authenticated and route requires auth, redirect to login if (!isAuthenticated) { const redirectPath = redirectTo || ROUTES.LOGIN; const returnUrl = location.pathname + location.search; return ( ); } // If no route config is provided, just check authentication if (!route) { return <>{children}; } // Get user roles and permissions const userRoles = user?.role ? [user.role] : []; const userPermissions: string[] = []; // Check if user can access this route const canAccess = canAccessRoute(route, isAuthenticated, userRoles, userPermissions); if (!canAccess) { // Check if it's a permission issue or role issue const hasRequiredRoles = !route.requiredRoles || route.requiredRoles.some(role => userRoles.includes(role)); if (!hasRequiredRoles) { return ; } else { return ; } } // User has access, render the protected content return <>{children}; }; // Higher-order component for route protection export const withProtectedRoute =

( Component: React.ComponentType

, route: RouteConfig, options?: { fallback?: React.ReactNode; redirectTo?: string; } ) => { const ProtectedComponent = (props: P) => ( ); ProtectedComponent.displayName = `withProtectedRoute(${Component.displayName || Component.name})`; return ProtectedComponent; }; // Hook for checking route access export const useRouteAccess = (route: RouteConfig) => { const user = useAuthUser(); const isAuthenticated = useIsAuthenticated(); const isLoading = useAuthLoading(); const userRoles = user?.role ? [user.role] : []; const userPermissions: string[] = []; const canAccess = canAccessRoute(route, isAuthenticated, userRoles, userPermissions); return { canAccess, isLoading, isAuthenticated, userRoles, userPermissions, hasRequiredRoles: !route.requiredRoles || route.requiredRoles.some(role => userRoles.includes(role)), hasRequiredPermissions: !route.requiredPermissions || route.requiredPermissions.every(permission => userPermissions.includes(permission)), }; }; // Component for conditionally rendering content based on permissions interface ConditionalRenderProps { children: React.ReactNode; requiredPermissions?: string[]; requiredRoles?: string[]; requireAll?: boolean; // If true, requires ALL permissions/roles, otherwise ANY fallback?: React.ReactNode; } export const ConditionalRender: React.FC = ({ children, requiredPermissions = [], requiredRoles = [], requireAll = true, fallback = null, }) => { const user = useAuthUser(); const isAuthenticated = useIsAuthenticated(); if (!isAuthenticated || !user) { return <>{fallback}; } const userRoles = user.role ? [user.role] : []; const userPermissions: string[] = []; // Check roles let hasRoles = true; if (requiredRoles.length > 0) { if (requireAll) { hasRoles = requiredRoles.every(role => userRoles.includes(role)); } else { hasRoles = requiredRoles.some(role => userRoles.includes(role)); } } // Check permissions let hasPermissions = true; if (requiredPermissions.length > 0) { if (requireAll) { hasPermissions = requiredPermissions.every(permission => userPermissions.includes(permission)); } else { hasPermissions = requiredPermissions.some(permission => userPermissions.includes(permission)); } } if (hasRoles && hasPermissions) { return <>{children}; } return <>{fallback}; }; // Route guard for admin-only routes export const AdminRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => { return ( } > {children} ); }; // Route guard for manager-level routes export const ManagerRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => { return ( } > {children} ); };