import { useEffect, useState } from 'react' import { Outlet, useNavigate, useLocation } from 'react-router' import { Box, Drawer, useTheme, useMediaQuery } from '@mui/material' import { useLayoutStore } from '../../stores/layoutStore' import { useSessionStore } from '../../stores/sessionStore' import { WelcomeDialog } from '../onboarding/WelcomeDialog' import { RightContextPanel } from './RightContextPanel' import { GlobalAIAssistantDrawer } from '../assistant' import { ToastProvider } from '../ui' import { Sidebar } from './AppShellSidebar' import { TopBar } from './AppShellTopBar' import { WORKSPACE_CONFIG, WORKSPACE_ORDER, getWorkspaceFromPath } from './appShellConfig' import { WorkspaceType } from '../../domain/enums' // --------------------------------------------------------------------------- // AppShell // --------------------------------------------------------------------------- export function AppShell() { const activeWorkspace = useLayoutStore(s => s.activeWorkspace) const sidebarCollapsed = useLayoutStore(s => s.sidebarCollapsed) const setActiveWorkspace = useLayoutStore(s => s.setActiveWorkspace) const toggleSidebar = useLayoutStore(s => s.toggleSidebar) const { currentUser } = useSessionStore() const navigate = useNavigate() const location = useLocation() const theme = useTheme() const isMobile = useMediaQuery(theme.breakpoints.down('md')) const [mobileOpen, setMobileOpen] = useState(false) // Close mobile menu on route change useEffect(() => { setMobileOpen(false) }, [location.pathname]) // Sync active workspace with URL useEffect(() => { const detected = getWorkspaceFromPath(location.pathname) if (detected && detected !== activeWorkspace) { setActiveWorkspace(detected) } }, [location.pathname, activeWorkspace, setActiveWorkspace]) // When the user changes (role switch), redirect to their first allowed workspace // if the current workspace is no longer permitted useEffect(() => { if (!currentUser) return const allowed = currentUser.allowedWorkspaces if (!allowed.includes(activeWorkspace)) { const first = allowed[0] if (first) { setActiveWorkspace(first) navigate(WORKSPACE_CONFIG[first].firstPath, { replace: true }) } } // currentUser object reference changes on every role switch — that's the correct trigger // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentUser]) const handleWorkspaceClick = (workspace: WorkspaceType) => { setActiveWorkspace(workspace) navigate(WORKSPACE_CONFIG[workspace].firstPath) } const userName = currentUser?.name ?? 'User' const orgName = currentUser?.organizationName ?? '' const allowedWorkspaces = currentUser?.allowedWorkspaces ?? WORKSPACE_ORDER const sidebarContent = ( setMobileOpen(false) : toggleSidebar} onClose={isMobile ? () => setMobileOpen(false) : undefined} userName={userName} orgName={orgName} /> ) return ( {/* Desktop: permanent sidebar */} {!isMobile && sidebarContent} {/* Mobile: temporary drawer */} {isMobile && ( setMobileOpen(false)} variant="temporary" ModalProps={{ keepMounted: true }} slotProps={{ paper: { sx: { width: 264, bgcolor: 'transparent', boxShadow: 'none' } } }} > {sidebarContent} )} setMobileOpen(true)} /> ) }