not working

This commit is contained in:
James Ketr 2025-06-10 15:44:13 -07:00
parent 7c78f39b02
commit 4689aa66c6
5 changed files with 292 additions and 135 deletions

View File

@ -12,7 +12,7 @@ import { Snack, SetSnackType } from 'components/Snack';
import { User } from 'types/types'; import { User } from 'types/types';
import { LoadingComponent } from "components/LoadingComponent"; import { LoadingComponent } from "components/LoadingComponent";
import { AuthProvider, useAuth, ProtectedRoute } from 'hooks/AuthContext'; import { AuthProvider, useAuth, ProtectedRoute } from 'hooks/AuthContext';
import { useAppState, useSelectedCandidate } from 'hooks/GlobalContext'; import { useSelectedCandidate } from 'hooks/GlobalContext';
import { import {
getMainNavigationItems, getMainNavigationItems,
getAllRoutes, getAllRoutes,
@ -76,34 +76,29 @@ const BackstoryPageContainer = (props: BackstoryPageContainerProps) => {
interface BackstoryLayoutProps { interface BackstoryLayoutProps {
page: string; page: string;
chatRef: React.Ref<any>; chatRef: React.Ref<any>;
snackRef: React.Ref<any>;
submitQuery: any;
} }
const BackstoryLayout: React.FC<BackstoryLayoutProps> = (props: BackstoryLayoutProps) => { const BackstoryLayout: React.FC<BackstoryLayoutProps> = (props: BackstoryLayoutProps) => {
const { page, chatRef } = props; const { page, chatRef, snackRef, submitQuery } = props;
const { setSnack } = useAppState();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const { guest, user } = useAuth(); const { guest, user } = useAuth();
const { selectedCandidate } = useSelectedCandidate();
const [navigationItems, setNavigationItems] = useState<NavigationItem[]>([]); const [navigationItems, setNavigationItems] = useState<NavigationItem[]>([]);
useEffect(() => { useEffect(() => {
const userType = user?.userType || null; const userType = user?.userType || null;
setNavigationItems(getMainNavigationItems(userType, user?.isAdmin ? true : false)); setNavigationItems(getMainNavigationItems(userType));
}, [user]); }, [user]);
useEffect(() => {
console.log({ guest, user });
}, [guest, user]);
// Generate dynamic routes from navigation config // Generate dynamic routes from navigation config
const generateRoutes = () => { const generateRoutes = () => {
if (!guest && !user) return null; if (!guest) return null;
const userType = user?.userType || null; const userType = user?.userType || null;
const isAdmin = user?.isAdmin ? true : false; const routes = getAllRoutes(userType);
// Get all routes from navigation config
const routes = getAllRoutes(userType, isAdmin);
return routes.map((route, index) => { return routes.map((route, index) => {
if (!route.path || !route.component) return null; if (!route.path || !route.component) return null;
@ -132,7 +127,6 @@ const BackstoryLayout: React.FC<BackstoryLayoutProps> = (props: BackstoryLayoutP
flexDirection: "column" flexDirection: "column"
}}> }}>
<Header <Header
setSnack={setSnack}
currentPath={page} currentPath={page}
navigate={navigate} navigate={navigate}
navigationItems={navigationItems} navigationItems={navigationItems}
@ -164,7 +158,7 @@ const BackstoryLayout: React.FC<BackstoryLayoutProps> = (props: BackstoryLayoutP
}} }}
> >
<BackstoryPageContainer> <BackstoryPageContainer>
{!guest && !user && ( {!guest && (
<Box> <Box>
<LoadingComponent <LoadingComponent
loadingText="Creating session..." loadingText="Creating session..."
@ -174,7 +168,7 @@ const BackstoryLayout: React.FC<BackstoryLayoutProps> = (props: BackstoryLayoutP
/> />
</Box> </Box>
)} )}
{(guest || user) && ( {guest && (
<> <>
<Outlet /> <Outlet />
<Routes> <Routes>
@ -185,6 +179,7 @@ const BackstoryLayout: React.FC<BackstoryLayoutProps> = (props: BackstoryLayoutP
{location.pathname === "/" && <Footer />} {location.pathname === "/" && <Footer />}
</BackstoryPageContainer> </BackstoryPageContainer>
</Scrollable> </Scrollable>
<Snack ref={snackRef} />
</Box> </Box>
</Box> </Box>
); );

View File

@ -20,7 +20,7 @@ const getBackstoryDynamicRoutes = (props: BackstoryDynamicRoutesProps): ReactNod
const isAdmin = user?.isAdmin ? true : false; const isAdmin = user?.isAdmin ? true : false;
// Get all routes from navigation config // Get all routes from navigation config
const routes = getAllRoutes(userType, isAdmin); const routes = getAllRoutes(userType);
return routes.map((route: NavigationItem, index: number) => { return routes.map((route: NavigationItem, index: number) => {
if (!route.path || !route.component) return null; if (!route.path || !route.component) return null;

View File

@ -1,5 +1,5 @@
// components/layout/Header.tsx // components/layout/Header.tsx
import React, { JSX, useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { NavigateFunction, useLocation } from 'react-router-dom'; import { NavigateFunction, useLocation } from 'react-router-dom';
import { import {
AppBar, AppBar,
@ -26,7 +26,6 @@ import {
List, List,
ListItem, ListItem,
ListItemButton, ListItemButton,
SxProps,
} from '@mui/material'; } from '@mui/material';
import { styled, useTheme } from '@mui/material/styles'; import { styled, useTheme } from '@mui/material/styles';
import { import {
@ -39,8 +38,8 @@ import {
ExpandLess, ExpandLess,
KeyboardArrowDown, KeyboardArrowDown,
} from '@mui/icons-material'; } from '@mui/icons-material';
import FaceRetouchingNaturalIcon from '@mui/icons-material/FaceRetouchingNatural';
import { getUserMenuItemsByGroup } from 'config/navigationConfig';
import { NavigationItem } from 'types/navigation'; import { NavigationItem } from 'types/navigation';
import { Beta } from 'components/ui/Beta'; import { Beta } from 'components/ui/Beta';
import { Candidate, Employer } from 'types/types'; import { Candidate, Employer } from 'types/types';
@ -121,11 +120,13 @@ interface HeaderProps {
navigationItems: NavigationItem[]; navigationItems: NavigationItem[];
currentPath: string; currentPath: string;
sessionId?: string | null; sessionId?: string | null;
setSnack: SetSnackType;
} }
const Header: React.FC<HeaderProps> = (props: HeaderProps) => { const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
const { user, logout } = useAuth(); const { user, logout } = useAuth();
const { setSnack } = useAppState();
const candidate: Candidate | null = (user && user.userType === "candidate") ? user as Candidate : null;
const employer: Employer | null = (user && user.userType === "employer") ? user as Employer : null;
const { const {
transparent = false, transparent = false,
className, className,
@ -133,8 +134,6 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
navigationItems, navigationItems,
sessionId, sessionId,
} = props; } = props;
const { setSnack } = useAppState();
const theme = useTheme(); const theme = useTheme();
const location = useLocation(); const location = useLocation();
@ -151,52 +150,97 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
const [userMenuAnchor, setUserMenuAnchor] = useState<null | HTMLElement>(null); const [userMenuAnchor, setUserMenuAnchor] = useState<null | HTMLElement>(null);
const userMenuOpen = Boolean(userMenuAnchor); const userMenuOpen = Boolean(userMenuAnchor);
// User menu items // Get user menu items from navigation config
const userMenuItems = [ const userMenuGroups = getUserMenuItemsByGroup(user?.userType || null);
{
id: 'profile',
label: 'Profile',
icon: <Person fontSize="small" />,
action: () => navigate(`/${user?.userType}/profile`)
},
{
id: 'dashboard',
label: 'Dashboard',
icon: <Dashboard fontSize="small" />,
action: () => navigate(`/${user?.userType}/dashboard`)
},
{
id: 'settings',
label: 'Settings',
icon: <Settings fontSize="small" />,
action: () => navigate(`/${user?.userType}/settings`)
},
{
id: 'divider',
label: '',
icon: null,
action: () => { }
},
{
id: 'logout',
label: 'Logout',
icon: <Logout fontSize="small" />,
action: () => {
logout();
navigate('/');
}
}
];
if (user?.isAdmin) { // Create user menu items array with proper actions
const divider = userMenuItems.findIndex(p => p.id === 'divider'); const createUserMenuItems = () => {
if (divider !== -1) { const items: Array<{
userMenuItems.splice(divider, 0, ...[ id: string;
{ id: 'divider', label: '', icon: null, action: () => { } }, label: string;
{ id: 'generate', label: 'Generate Candidate', icon: <FaceRetouchingNaturalIcon fontSize="small" />, action: () => { navigate('/admin/generate-candidate'); } } icon: React.ReactElement | null;
]); action: () => void;
group?: string;
}> = [];
// Add profile group items
userMenuGroups.profile.forEach(item => {
if (!item.divider) {
items.push({
id: item.id,
label: item.label as string,
icon: item.icon || null,
action: () => item.path && navigate(item.path),
group: 'profile'
});
}
});
// Add account group items
userMenuGroups.account.forEach(item => {
if (!item.divider) {
items.push({
id: item.id,
label: item.label as string,
icon: item.icon || null,
action: () => item.path && navigate(item.path),
group: 'account'
});
}
});
// Add divider if we have items before system group
if (items.length > 0 && userMenuGroups.system.length > 0) {
items.push({
id: 'divider',
label: '',
icon: null,
action: () => { },
group: 'divider'
});
} }
}
// Add system group items with special handling for logout
userMenuGroups.system.forEach(item => {
if (item.id === 'logout') {
items.push({
id: 'logout',
label: 'Logout',
icon: <Logout fontSize="small" />,
action: () => {
logout();
navigate('/');
},
group: 'system'
});
} else if (!item.divider) {
items.push({
id: item.id,
label: item.label as string,
icon: item.icon || null,
action: () => item.path && navigate(item.path),
group: 'system'
});
}
});
// Add other group items
userMenuGroups.other.forEach(item => {
if (!item.divider) {
items.push({
id: item.id,
label: item.label as string,
icon: item.icon || null,
action: () => item.path && navigate(item.path),
group: 'other'
});
}
});
return items;
};
const userMenuItems = createUserMenuItems();
// Helper function to check if current path matches navigation item // Helper function to check if current path matches navigation item
const isCurrentPath = (item: NavigationItem): boolean => { const isCurrentPath = (item: NavigationItem): boolean => {
@ -210,7 +254,7 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
// Helper function to check if any child is current path // Helper function to check if any child is current path
const hasActiveChild = (item: NavigationItem): boolean => { const hasActiveChild = (item: NavigationItem): boolean => {
if (!item.children) return false; if (!item.children) return false;
return item.children.some(child => isCurrentPath(child) || hasActiveChild(child)); return item.children.some((child: any) => isCurrentPath(child) || hasActiveChild(child));
}; };
// Desktop dropdown handlers // Desktop dropdown handlers
@ -239,8 +283,8 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
setUserMenuAnchor(null); setUserMenuAnchor(null);
}; };
const handleUserMenuAction = (item: typeof userMenuItems[0]) => { const handleUserMenuAction = (item: { id: string; label: string; icon: React.ReactElement | null; action: () => void; group?: string }) => {
if (item.id !== 'divider') { if (item.group !== 'divider') {
item.action(); item.action();
handleUserMenuClose(); handleUserMenuClose();
} }
@ -257,23 +301,14 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
// Render desktop navigation with dropdowns // Render desktop navigation with dropdowns
const renderDesktopNavigation = () => { const renderDesktopNavigation = () => {
return ( return (
<Box sx={{ display: 'flex', width: "100%", alignItems: 'center', justifyContent: "space-between", "& > :last-of-type": { marginRight: "auto"} }}> <Box sx={{ display: 'flex', alignItems: 'center' }}>
{navigationItems.map((item, index) => { {navigationItems.map((item) => {
const hasChildren = item.children && item.children.length > 0; const hasChildren = item.children && item.children.length > 0;
const isActive = isCurrentPath(item) || hasActiveChild(item); const isActive = isCurrentPath(item) || hasActiveChild(item);
// Default is centered for all menu items
let sx : SxProps = { justifycontent: "center"};
// First item ("Backstory") is left aligned
if (index === 0) {
sx = { marginRight: "auto" };
}
// If there is an Admin menu, it is on the far right
if (item.userTypes?.includes('admin')) {
sx = { marginLeft: "auto"};
}
if (hasChildren) { if (hasChildren) {
return ( return (
<Box key={item.id} sx={sx}> <Box key={item.id}>
<DropdownButton <DropdownButton
onClick={(e) => handleDropdownOpen(e, item.id)} onClick={(e) => handleDropdownOpen(e, item.id)}
endIcon={<KeyboardArrowDown />} endIcon={<KeyboardArrowDown />}
@ -293,16 +328,15 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
transformOrigin={{ vertical: 'top', horizontal: 'left' }} transformOrigin={{ vertical: 'top', horizontal: 'left' }}
TransitionComponent={Fade} TransitionComponent={Fade}
> >
{item.children?.map((child) => ( {item.children?.map((child: any) => (
<MenuItem <MenuItem
key={child.id} key={child.id}
onClick={() => child.path && handleNavigate(child.path)} onClick={() => child.path && handleNavigate(child.path)}
selected={isCurrentPath(child)} selected={isCurrentPath(child)}
disabled={!child.path} disabled={!child.path}
sx={{ alignContent: 'center', margin: "0 !important" }}
> >
{child.icon && <ListItemIcon>{child.icon}</ListItemIcon>} {child.icon && <ListItemIcon>{child.icon}</ListItemIcon>}
<ListItemText sx={{ p: 0, margin: "0 !important", "& > *": { m: 0 } }}>{child.label}</ListItemText> <ListItemText>{child.label}</ListItemText>
</MenuItem> </MenuItem>
))} ))}
</Menu> </Menu>
@ -316,7 +350,6 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
sx={{ sx={{
backgroundColor: isActive ? 'action.selected' : 'transparent', backgroundColor: isActive ? 'action.selected' : 'transparent',
color: isActive ? 'secondary.main' : 'primary.contrastText', color: isActive ? 'secondary.main' : 'primary.contrastText',
...sx
}} }}
> >
{item.icon && <Box sx={{ mr: 1, display: 'flex' }}>{item.icon}</Box>} {item.icon && <Box sx={{ mr: 1, display: 'flex' }}>{item.icon}</Box>}
@ -383,7 +416,7 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
{hasChildren && ( {hasChildren && (
<Collapse in={isExpanded} timeout="auto" unmountOnExit> <Collapse in={isExpanded} timeout="auto" unmountOnExit>
<List disablePadding> <List disablePadding>
{item.children?.map((child) => renderNavigationItem(child, depth + 1))} {item.children?.map((child: any) => renderNavigationItem(child, depth + 1))}
</List> </List>
</Collapse> </Collapse>
)} )}
@ -412,13 +445,13 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
<UserMenuContainer> <UserMenuContainer>
<List dense> <List dense>
{userMenuItems.map((item, index) => ( {userMenuItems.map((item, index) => (
item.id === 'divider' ? ( item.group === 'divider' ? (
<Divider key={`divider-${index}`} /> <Divider key={`divider-${index}`} />
) : ( ) : (
<ListItem key={item.id} disablePadding> <ListItem key={item.id} disablePadding>
<ListItemButton onClick={() => handleUserMenuAction(item)} sx={{ alignContent: "center" }}> <ListItemButton onClick={() => handleUserMenuAction(item)}>
{item.icon && <ListItemIcon sx={{ minWidth: 36 }}>{item.icon}</ListItemIcon>} {item.icon && <ListItemIcon sx={{ minWidth: 36 }}>{item.icon}</ListItemIcon>}
<ListItemText primary={item.label} sx={{ padding: 0 }} /> <ListItemText primary={item.label} />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
) )

View File

@ -1,5 +1,3 @@
// config/navigationConfig.tsx
import React from 'react'; import React from 'react';
import { import {
Chat as ChatIcon, Chat as ChatIcon,
@ -8,18 +6,16 @@ import {
BarChart as BarChartIcon, BarChart as BarChartIcon,
Settings as SettingsIcon, Settings as SettingsIcon,
Work as WorkIcon, Work as WorkIcon,
Info as InfoIcon,
Person as PersonIcon, Person as PersonIcon,
Business as BusinessIcon, Business as BusinessIcon,
Quiz as QuizIcon,
Analytics as AnalyticsIcon,
Search as SearchIcon, Search as SearchIcon,
Bookmark as BookmarkIcon, Bookmark as BookmarkIcon,
BubbleChart, History as HistoryIcon,
QuestionAnswer as QuestionAnswerIcon,
AttachMoney as AttachMoneyIcon,
} from '@mui/icons-material'; } from '@mui/icons-material';
import SchoolIcon from '@mui/icons-material/School';
import FaceRetouchingNaturalIcon from '@mui/icons-material/FaceRetouchingNatural';
import LibraryBooksIcon from '@mui/icons-material/LibraryBooks';
import { BackstoryLogo } from 'components/ui/BackstoryLogo'; import { BackstoryLogo } from 'components/ui/BackstoryLogo';
import { HomePage } from 'pages/HomePage'; import { HomePage } from 'pages/HomePage';
import { CandidateChatPage } from 'pages/CandidateChatPage'; import { CandidateChatPage } from 'pages/CandidateChatPage';
@ -27,20 +23,19 @@ import { DocsPage } from 'pages/DocsPage';
import { CreateProfilePage } from 'pages/candidate/ProfileWizard'; import { CreateProfilePage } from 'pages/candidate/ProfileWizard';
import { VectorVisualizerPage } from 'pages/VectorVisualizerPage'; import { VectorVisualizerPage } from 'pages/VectorVisualizerPage';
import { BetaPage } from 'pages/BetaPage'; import { BetaPage } from 'pages/BetaPage';
import { CandidateListingPage } from 'pages/FindCandidatePage';
import { JobAnalysisPage } from 'pages/JobAnalysisPage'; import { JobAnalysisPage } from 'pages/JobAnalysisPage';
import { GenerateCandidate } from 'pages/GenerateCandidate'; import { GenerateCandidate } from 'pages/GenerateCandidate';
import { Settings } from 'pages/candidate/Settings';
import { LoginPage } from 'pages/LoginPage'; import { LoginPage } from 'pages/LoginPage';
import { CandidateDashboard } from 'pages/candidate/Dashboard';
import { EmailVerificationPage } from 'components/EmailVerificationComponents'; import { EmailVerificationPage } from 'components/EmailVerificationComponents';
import { Box, Typography } from '@mui/material'; import { Typography } from '@mui/material';
import { CandidateDashboard } from 'pages/candidate/Dashboard';
import { NavigationConfig, NavigationItem } from 'types/navigation'; import { NavigationConfig, NavigationItem } from 'types/navigation';
import { CandidateProfile } from 'pages/candidate/Profile';
import { DocumentManager } from 'components/DocumentManager';
import { VectorVisualizer } from 'components/VectorVisualizer';
import { HowItWorks } from 'pages/HowItWorks';
// Beta page components for placeholder routes // Beta page components for placeholder routes
const BackstoryPage = () => (<BetaPage><Typography variant="h4">Backstory</Typography></BetaPage>);
const ResumesPage = () => (<BetaPage><Typography variant="h4">Resumes</Typography></BetaPage>);
const QASetupPage = () => (<BetaPage><Typography variant="h4">Q&A Setup</Typography></BetaPage>);
const SearchPage = () => (<BetaPage><Typography variant="h4">Search</Typography></BetaPage>); const SearchPage = () => (<BetaPage><Typography variant="h4">Search</Typography></BetaPage>);
const SavedPage = () => (<BetaPage><Typography variant="h4">Saved</Typography></BetaPage>); const SavedPage = () => (<BetaPage><Typography variant="h4">Saved</Typography></BetaPage>);
const JobsPage = () => (<BetaPage><Typography variant="h4">Jobs</Typography></BetaPage>); const JobsPage = () => (<BetaPage><Typography variant="h4">Jobs</Typography></BetaPage>);
@ -51,8 +46,8 @@ const SettingsPage = () => (<BetaPage><Typography variant="h4">Settings</Typogra
export const navigationConfig: NavigationConfig = { export const navigationConfig: NavigationConfig = {
items: [ items: [
{ id: 'home', label: <BackstoryLogo />, path: '/', component: <HowItWorks />, userTypes: ['guest', 'candidate', 'employer'], exact: true, }, { id: 'home', label: <BackstoryLogo />, path: '/', component: <HomePage />, userTypes: ['guest', 'candidate', 'employer'], exact: true, },
// { id: 'how-it-works', label: 'How It Works', path: '/how-it-works', icon: <SchoolIcon />, component: <HowItWorks />, userTypes: ['guest', 'candidate', 'employer',], }, { id: 'how-it-works', label: 'How It Works', path: '/how-it-works', icon: <SchoolIcon />, component: <HowItWorks />, userTypes: ['guest', 'candidate', 'employer',], },
{ id: 'job-analysis', label: 'Job Analysis', path: '/job-analysis', icon: <WorkIcon />, component: <JobAnalysisPage />, userTypes: ['guest', 'candidate', 'employer',], }, { id: 'job-analysis', label: 'Job Analysis', path: '/job-analysis', icon: <WorkIcon />, component: <JobAnalysisPage />, userTypes: ['guest', 'candidate', 'employer',], },
{ id: 'chat', label: 'Candidate Chat', path: '/chat', icon: <ChatIcon />, component: <CandidateChatPage />, userTypes: ['guest', 'candidate', 'employer',], }, { { id: 'chat', label: 'Candidate Chat', path: '/chat', icon: <ChatIcon />, component: <CandidateChatPage />, userTypes: ['guest', 'candidate', 'employer',], }, {
id: 'candidate-menu', label: 'Tools', icon: <PersonIcon />, userTypes: ['candidate'], children: [ id: 'candidate-menu', label: 'Tools', icon: <PersonIcon />, userTypes: ['candidate'], children: [
@ -61,7 +56,9 @@ export const navigationConfig: NavigationConfig = {
{ id: 'candidate-docs', label: 'Documents', icon: <BubbleChart />, path: '/candidate/documents', component: <Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}><VectorVisualizer /><DocumentManager /></Box>, userTypes: ['candidate'] }, { id: 'candidate-docs', label: 'Documents', icon: <BubbleChart />, path: '/candidate/documents', component: <Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}><VectorVisualizer /><DocumentManager /></Box>, userTypes: ['candidate'] },
{ id: 'candidate-qa-setup', label: 'Q&A Setup', icon: <QuizIcon />, path: '/candidate/qa-setup', component: <BetaPage><Box>Candidate q&a setup page</Box></BetaPage>, userTypes: ['candidate'] }, { id: 'candidate-qa-setup', label: 'Q&A Setup', icon: <QuizIcon />, path: '/candidate/qa-setup', component: <BetaPage><Box>Candidate q&a setup page</Box></BetaPage>, userTypes: ['candidate'] },
{ id: 'candidate-analytics', label: 'Analytics', icon: <AnalyticsIcon />, path: '/candidate/analytics', component: <BetaPage><Box>Candidate analytics page</Box></BetaPage>, userTypes: ['candidate'] }, { id: 'candidate-analytics', label: 'Analytics', icon: <AnalyticsIcon />, path: '/candidate/analytics', component: <BetaPage><Box>Candidate analytics page</Box></BetaPage>, userTypes: ['candidate'] },
{ id: 'candidate-job-analysis', label: 'Job Analysis', path: '/candidate/job-analysis', icon: <WorkIcon />, component: <JobAnalysisPage />, userTypes: ['candidate'], }, { id: 'candidate-job-analysis', label: 'Job Analysis', path: '/candidate/job-analysis', icon: <WorkIcon />, component: <JobAnalysisPage />, userTypes: ['candidate'], showInNavigation: false,
showInUserMenu: true,
userMenuGroup: 'profile',},
{ id: 'candidate-resumes', label: 'Resumes', icon: <DescriptionIcon />, path: '/candidate/resumes', component: <BetaPage><Box>Candidate resumes page</Box></BetaPage>, userTypes: ['candidate'] }, { id: 'candidate-resumes', label: 'Resumes', icon: <DescriptionIcon />, path: '/candidate/resumes', component: <BetaPage><Box>Candidate resumes page</Box></BetaPage>, userTypes: ['candidate'] },
{ id: 'candidate-settings', label: 'Settings', path: '/candidate/settings', icon: <SettingsIcon />, component: <Settings />, userTypes: ['candidate'], }, { id: 'candidate-settings', label: 'Settings', path: '/candidate/settings', icon: <SettingsIcon />, component: <Settings />, userTypes: ['candidate'], },
], ],
@ -78,22 +75,101 @@ export const navigationConfig: NavigationConfig = {
{ id: 'employer-settings', label: 'Settings', path: '/employer/settings', icon: <SettingsIcon />, component: <SettingsPage />, userTypes: ['employer'], }, { id: 'employer-settings', label: 'Settings', path: '/employer/settings', icon: <SettingsIcon />, component: <SettingsPage />, userTypes: ['employer'], },
], ],
}, },
// { id: 'find-candidate', label: 'Find a Candidate', path: '/find-a-candidate', icon: <PersonSearchIcon />, component: <CandidateListingPage />, userTypes: ['guest', 'candidate', 'employer'], }, ],
},
{ {
id: 'admin-menu', id: 'global-tools',
label: 'Admin', label: 'Tools',
icon: <PersonIcon />, icon: <SettingsIcon />,
userTypes: ['admin'], userTypes: ['candidate', 'employer'],
children: [ showInNavigation: true,
{ id: 'generate-candidate', label: 'Generate Candidate', path: '/admin/generate-candidate', icon: <FaceRetouchingNaturalIcon />, component: <GenerateCandidate />, userTypes: ['admin'] }, children: [
{ id: 'docs', label: 'Docs', path: '/docs/*', icon: <LibraryBooksIcon />, component: <DocsPage />, userTypes: ['admin'], }, {
], id: 'knowledge-explorer',
label: 'Knowledge Explorer',
path: '/knowledge-explorer',
icon: <WorkIcon />,
component: <VectorVisualizerPage />,
userTypes: ['candidate', 'employer'],
showInNavigation: true,
},
{
id: 'job-analysis',
label: 'Job Analysis',
path: '/job-analysis',
icon: <WorkIcon />,
component: <JobAnalysisPage />,
userTypes: ['candidate', 'employer'],
showInNavigation: true,
},
{
id: 'generate-candidate',
label: 'Generate Candidate',
path: '/generate-candidate',
icon: <PersonIcon />,
component: <GenerateCandidate />,
userTypes: ['candidate', 'employer'],
showInNavigation: true,
},
],
},
// User menu only items (not shown in main navigation)
{
id: 'user-profile',
label: 'Profile',
path: '/profile',
icon: <PersonIcon />,
component: <AnalyticsPage />, // Replace with actual profile page
userTypes: ['candidate', 'employer'],
showInNavigation: false,
showInUserMenu: true,
userMenuGroup: 'profile',
},
{
id: 'account-settings',
label: 'Account Settings',
path: '/account/settings',
icon: <SettingsIcon />,
component: <SettingsPage />,
userTypes: ['candidate', 'employer'],
showInNavigation: false,
showInUserMenu: true,
userMenuGroup: 'account',
},
{
id: 'billing',
label: 'Billing',
path: '/billing',
icon: <AttachMoneyIcon />,
component: <AnalyticsPage />, // Replace with actual billing page
userTypes: ['candidate', 'employer'],
showInNavigation: false,
showInUserMenu: true,
userMenuGroup: 'account',
},
{
id: 'user-menu-divider',
label: '',
userTypes: ['candidate', 'employer'],
showInNavigation: false,
showInUserMenu: true,
divider: true,
},
{
id: 'logout',
label: 'Logout',
icon: <PersonIcon />, // This will be handled specially in Header
userTypes: ['candidate', 'employer'],
showInNavigation: false,
showInUserMenu: true,
userMenuGroup: 'system',
}, },
// Auth routes (special handling) // Auth routes (special handling)
{ {
id: 'auth', id: 'auth',
label: 'Auth', label: 'Auth',
userTypes: ['guest', 'candidate', 'employer'], userTypes: ['guest', 'candidate', 'employer'],
showInNavigation: false,
children: [ children: [
{ {
id: 'register', id: 'register',
@ -101,6 +177,7 @@ export const navigationConfig: NavigationConfig = {
path: '/register', path: '/register',
component: <BetaPage><CreateProfilePage /></BetaPage>, component: <BetaPage><CreateProfilePage /></BetaPage>,
userTypes: ['guest'], userTypes: ['guest'],
showInNavigation: false,
}, },
{ {
id: 'login', id: 'login',
@ -108,6 +185,7 @@ export const navigationConfig: NavigationConfig = {
path: '/login/*', path: '/login/*',
component: <LoginPage />, component: <LoginPage />,
userTypes: ['guest', 'candidate', 'employer'], userTypes: ['guest', 'candidate', 'employer'],
showInNavigation: false,
}, },
{ {
id: 'verify-email', id: 'verify-email',
@ -115,13 +193,15 @@ export const navigationConfig: NavigationConfig = {
path: '/login/verify-email', path: '/login/verify-email',
component: <EmailVerificationPage />, component: <EmailVerificationPage />,
userTypes: ['guest', 'candidate', 'employer'], userTypes: ['guest', 'candidate', 'employer'],
showInNavigation: false,
}, },
{ {
id: 'logout', id: 'logout-page',
label: 'Logout', label: 'Logout',
path: '/logout', path: '/logout',
component: <LogoutPage />, component: <LogoutPage />,
userTypes: ['candidate', 'employer'], userTypes: ['candidate', 'employer'],
showInNavigation: false,
}, },
], ],
}, },
@ -132,16 +212,19 @@ export const navigationConfig: NavigationConfig = {
path: '*', path: '*',
component: <BetaPage />, component: <BetaPage />,
userTypes: ['guest', 'candidate', 'employer'], userTypes: ['guest', 'candidate', 'employer'],
showInNavigation: false,
}, },
], ],
}; };
// Utility functions for working with navigation config // Utility functions for working with navigation config
export const getNavigationItemsForUser = (userType: 'guest' | 'candidate' | 'employer' | null, isAdmin: boolean = false): NavigationItem[] => { export const getNavigationItemsForUser = (userType: 'guest' | 'candidate' | 'employer' | null): NavigationItem[] => {
const currentUserType = userType || 'guest'; const currentUserType = userType || 'guest';
const filterItems = (items: NavigationItem[]): NavigationItem[] => { const filterItems = (items: NavigationItem[]): NavigationItem[] => {
return items return items
.filter(item => !item.userTypes || item.userTypes.includes(currentUserType) || (item.userTypes.includes('admin') && isAdmin)) .filter(item => !item.userTypes || item.userTypes.includes(currentUserType))
.filter(item => item.showInNavigation !== false) // Default to true if not specified
.map(item => ({ .map(item => ({
...item, ...item,
children: item.children ? filterItems(item.children) : undefined, children: item.children ? filterItems(item.children) : undefined,
@ -152,14 +235,14 @@ export const getNavigationItemsForUser = (userType: 'guest' | 'candidate' | 'emp
return filterItems(navigationConfig.items); return filterItems(navigationConfig.items);
}; };
export const getAllRoutes = (userType: 'guest' | 'candidate' | 'employer' | null, isAdmin: boolean = false): NavigationItem[] => { export const getAllRoutes = (userType: 'guest' | 'candidate' | 'employer' | null): NavigationItem[] => {
const currentUserType = userType || 'guest'; const currentUserType = userType || 'guest';
const extractRoutes = (items: NavigationItem[]): NavigationItem[] => { const extractRoutes = (items: NavigationItem[]): NavigationItem[] => {
const routes: NavigationItem[] = []; const routes: NavigationItem[] = [];
items.forEach(item => { items.forEach(item => {
if (!item.userTypes || item.userTypes.includes(currentUserType) || (item.userTypes.includes('admin') && isAdmin)) { if (!item.userTypes || item.userTypes.includes(currentUserType)) {
if (item.path && item.component) { if (item.path && item.component) {
routes.push(item); routes.push(item);
} }
@ -175,11 +258,55 @@ export const getAllRoutes = (userType: 'guest' | 'candidate' | 'employer' | null
return extractRoutes(navigationConfig.items); return extractRoutes(navigationConfig.items);
}; };
export const getMainNavigationItems = (userType: 'guest' | 'candidate' | 'employer' | null, isAdmin: boolean = false): NavigationItem[] => { export const getMainNavigationItems = (userType: 'guest' | 'candidate' | 'employer' | null): NavigationItem[] => {
return getNavigationItemsForUser(userType, isAdmin) return getNavigationItemsForUser(userType)
.filter(item => .filter(item =>
item.id !== 'auth' && item.id !== 'auth' &&
item.id !== 'catch-all' && item.id !== 'catch-all' &&
item.showInNavigation !== false &&
(item.path || (item.children && item.children.length > 0)) (item.path || (item.children && item.children.length > 0))
); );
}; };
export const getUserMenuItems = (userType: 'candidate' | 'employer' | 'guest' | null): NavigationItem[] => {
if (!userType) return [];
const extractUserMenuItems = (items: NavigationItem[]): NavigationItem[] => {
const menuItems: NavigationItem[] = [];
items.forEach(item => {
if (!item.userTypes || item.userTypes.includes(userType)) {
if (item.showInUserMenu) {
menuItems.push(item);
}
if (item.children) {
menuItems.push(...extractUserMenuItems(item.children));
}
}
});
return menuItems;
};
return extractUserMenuItems(navigationConfig.items);
};
export const getUserMenuItemsByGroup = (userType: 'candidate' | 'employer' | 'guest' | null): { [key: string]: NavigationItem[] } => {
const menuItems = getUserMenuItems(userType);
const grouped: { [key: string]: NavigationItem[] } = {
profile: [],
account: [],
system: [],
other: []
};
menuItems.forEach(item => {
const group = item.userMenuGroup || 'other';
if (!grouped[group]) {
grouped[group] = [];
}
grouped[group].push(item);
});
return grouped;
};

View File

@ -1,4 +1,3 @@
// types/navigation.ts
import { ReactElement } from 'react'; import { ReactElement } from 'react';
export interface NavigationItem { export interface NavigationItem {
@ -8,9 +7,12 @@ export interface NavigationItem {
icon?: ReactElement; icon?: ReactElement;
children?: NavigationItem[]; children?: NavigationItem[];
component?: ReactElement; component?: ReactElement;
userTypes?: ('candidate' | 'employer' | 'guest' | 'admin')[]; userTypes?: ('candidate' | 'employer' | 'guest')[];
exact?: boolean; exact?: boolean;
divider?: boolean; divider?: boolean;
showInNavigation?: boolean; // Controls if item appears in main navigation
showInUserMenu?: boolean; // Controls if item appears in user menu
userMenuGroup?: 'profile' | 'account' | 'system'; // Groups items in user menu
} }
export interface NavigationConfig { export interface NavigationConfig {