import React, { useEffect, useState, Suspense } from 'react'; import { useLocation } from 'react-router-dom'; import { motion, AnimatePresence } from 'framer-motion'; import { getPageDataByPath, GenericPageData, PageInstance } from './data'; import PageRenderer from './PageRenderer'; import { usePermissions } from '../../hooks/usePermissions'; interface PageManagerProps { loadingComponent: React.ComponentType; errorComponent: React.ComponentType; } const PageManager: React.FC = ({ loadingComponent: LoadingComponent, errorComponent: ErrorComponent }) => { const location = useLocation(); const [pageInstances, setPageInstances] = useState>(new Map()); const { canView } = usePermissions(); // Get current path const getCurrentPath = () => { const path = location.pathname === '/' ? '' : location.pathname; return path.startsWith('/') ? path.slice(1) : path; }; const currentPath = getCurrentPath(); // Check if user has access to a page using backend RBAC permissions const checkPageAccess = async (pageData: GenericPageData): Promise => { console.log('🔍 PageManager: Checking page access:', { path: pageData.path, label: pageData.label, hide: pageData.hide, moduleEnabled: pageData.moduleEnabled }); try { const hasAccess = await canView('UI', pageData.path); console.log('🔍 PageManager: Page access result:', { path: pageData.path, hasAccess }); return hasAccess; } catch (error) { console.error(`❌ PageManager: Error checking RBAC access for ${pageData.path}:`, error); return false; } }; useEffect(() => { console.log('🔄 PageManager: useEffect triggered for path:', currentPath); const pageData = getPageDataByPath(currentPath); console.log('📄 PageManager: Page data found:', { path: currentPath, hasPageData: !!pageData, hide: pageData?.hide, moduleEnabled: pageData?.moduleEnabled, label: pageData?.label }); if (!pageData || pageData.hide || !pageData.moduleEnabled) { console.log('⛔ PageManager: Page not rendered:', { path: currentPath, reason: !pageData ? 'not found' : pageData.hide ? 'hidden' : 'module disabled' }); return; } // Check page access console.log('🔍 PageManager: Checking access before rendering:', currentPath); checkPageAccess(pageData).then(hasAccess => { console.log('🔍 PageManager: Access check complete:', { path: currentPath, hasAccess }); if (!hasAccess) { console.log('⛔ PageManager: Page not rendered due to access check:', currentPath); return; } console.log('✅ PageManager: Rendering page:', { path: currentPath, label: pageData.label }); setPageInstances(prev => { console.log('📦 PageManager: Creating/updating page instance:', { path: currentPath, existingInstances: Array.from(prev.keys()), willCreateNew: !prev.has(currentPath) }); const newInstances = new Map(prev); // Update active states newInstances.forEach((instance) => { instance.isActive = instance.path === currentPath; }); // Create instance if it doesn't exist if (!newInstances.has(currentPath)) { console.log('📦 PageManager: Creating new page instance:', { path: currentPath, label: pageData.label }); const shouldPreserve = pageData.preserveState || false; const pageInstance: PageInstance = { path: currentPath, component: (
}> {pageData.customComponent ? ( ) : ( { }} /> )}
), isActive: true, shouldPreserve, pageData }; newInstances.set(currentPath, pageInstance); console.log('✅ PageManager: Page instance created:', { path: currentPath, totalInstances: newInstances.size, allPaths: Array.from(newInstances.keys()) }); } else { console.log('🔄 PageManager: Page instance already exists, updating active state:', currentPath); if (import.meta.env.DEV) { const _instance = newInstances.get(currentPath); void _instance; // Intentionally unused, for debugging purposes } } return newInstances; }); }); // Clean up non-preserved, inactive instances with delay for smooth transitions const cleanupTimer = setTimeout(() => { setPageInstances(currentInstances => { const updatedInstances = new Map(currentInstances); const instancesToDelete: string[] = []; updatedInstances.forEach((instance, path) => { if (!instance.isActive && !instance.shouldPreserve) { instancesToDelete.push(path); } }); instancesToDelete.forEach(path => { updatedInstances.delete(path); }); return updatedInstances; }); }, 500); // Wait for transition to complete before cleanup return () => clearTimeout(cleanupTimer); }, [currentPath]); const pageData = getPageDataByPath(currentPath); if (!pageData || pageData.hide || !pageData.moduleEnabled) { return ; } // Animation variants for smooth transitions const pageVariants = { initial: { opacity: 0, scale: 1, y: 0 }, in: { opacity: 1, scale: 1, y: 0 }, out: { opacity: 0, scale: 1, y: 0 } }; const pageTransition = { type: "tween" as const, ease: "easeInOut" as const, duration: 0.2 }; return (
{Array.from(pageInstances.values()).map((instance) => { const isVisible = instance.isActive; if (instance.shouldPreserve) { // Preserved pages: Always mounted, just show/hide with animations return ( {instance.component} ); } else if (isVisible) { // Non-preserved pages: Use AnimatePresence for full mount/unmount return ( {instance.component} ); } return null; })}
); }; export default PageManager;