Restructured for menus

This commit is contained in:
James Ketr 2025-06-08 14:36:21 -07:00
parent 38635f0fd4
commit 43b332bd47
10 changed files with 246 additions and 474 deletions

View File

@ -89,7 +89,7 @@ const BackstoryLayout: React.FC<BackstoryLayoutProps> = (props: BackstoryLayoutP
useEffect(() => { useEffect(() => {
const userType = user?.userType || null; const userType = user?.userType || null;
setNavigationItems(getMainNavigationItems(userType)); setNavigationItems(getMainNavigationItems(userType, user?.isAdmin ? true : false));
}, [user]); }, [user]);
// Generate dynamic routes from navigation config // Generate dynamic routes from navigation config
@ -97,7 +97,10 @@ const BackstoryLayout: React.FC<BackstoryLayoutProps> = (props: BackstoryLayoutP
if (!guest) return null; if (!guest) return null;
const userType = user?.userType || null; const userType = user?.userType || null;
const routes = getAllRoutes(userType); const isAdmin = user?.isAdmin ? true : false;
// 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;

View File

@ -17,9 +17,10 @@ interface BackstoryDynamicRoutesProps extends BackstoryPageProps {
const getBackstoryDynamicRoutes = (props: BackstoryDynamicRoutesProps): ReactNode => { const getBackstoryDynamicRoutes = (props: BackstoryDynamicRoutesProps): ReactNode => {
const { user, chatRef } = props; const { user, chatRef } = props;
const userType = user?.userType || null; const userType = user?.userType || null;
const isAdmin = user?.isAdmin ? true : false;
// Get all routes from navigation config // Get all routes from navigation config
const routes = getAllRoutes(userType); const routes = getAllRoutes(userType, isAdmin);
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

@ -38,6 +38,7 @@ import {
ExpandLess, ExpandLess,
KeyboardArrowDown, KeyboardArrowDown,
} from '@mui/icons-material'; } from '@mui/icons-material';
import FaceRetouchingNaturalIcon from '@mui/icons-material/FaceRetouchingNatural';
import { NavigationItem } from 'types/navigation'; import { NavigationItem } from 'types/navigation';
import { Beta } from 'components/ui/Beta'; import { Beta } from 'components/ui/Beta';
@ -47,6 +48,7 @@ import { CopyBubble } from 'components/CopyBubble';
import 'components/layout/Header.css'; import 'components/layout/Header.css';
import { useAuth } from 'hooks/AuthContext'; import { useAuth } from 'hooks/AuthContext';
import { useAppState } from 'hooks/GlobalContext';
// Styled components // Styled components
const StyledAppBar = styled(AppBar, { const StyledAppBar = styled(AppBar, {
@ -123,16 +125,15 @@ interface HeaderProps {
const Header: React.FC<HeaderProps> = (props: HeaderProps) => { const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
const { user, logout } = useAuth(); const { user, logout } = useAuth();
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,
navigate, navigate,
navigationItems, navigationItems,
sessionId, sessionId,
setSnack,
} = props; } = props;
const { setSnack } = useAppState();
const theme = useTheme(); const theme = useTheme();
const location = useLocation(); const location = useLocation();
@ -186,6 +187,16 @@ const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
} }
]; ];
if (user?.isAdmin) {
const divider = userMenuItems.findIndex(p => p.id === 'divider');
if (divider !== -1) {
userMenuItems.splice(divider, 0, ...[
{ id: 'divider', label: '', icon: null, action: () => { } },
{ id: 'generate', label: 'Generate Candidate', icon: <FaceRetouchingNaturalIcon fontSize="small" />, action: () => { navigate('/admin/generate-candidate'); } }
]);
}
}
// 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 => {
if (!item.path) return false; if (!item.path) return false;

View File

@ -11,13 +11,19 @@ import {
Info as InfoIcon, 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,
History as HistoryIcon, History as HistoryIcon,
QuestionAnswer as QuestionAnswerIcon, QuestionAnswer as QuestionAnswerIcon,
AttachMoney as AttachMoneyIcon, AttachMoney as AttachMoneyIcon,
BubbleChart,
InsertEmoticon,
} from '@mui/icons-material'; } from '@mui/icons-material';
import FaceRetouchingNaturalIcon from '@mui/icons-material/FaceRetouchingNatural';
import LibraryBooksIcon from '@mui/icons-material/LibraryBooks';
import PersonSearchIcon from '@mui/icons-material/PersonSearch';
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';
@ -29,17 +35,19 @@ import { BetaPage } from 'pages/BetaPage';
import { CandidateListingPage } from 'pages/FindCandidatePage'; 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 { ControlsPage } from 'pages/ControlsPage'; import { Settings } from 'pages/candidate/Settings';
import { LoginPage } from 'pages/LoginPage'; import { LoginPage } from 'pages/LoginPage';
import { CandidateDashboardPage } from 'pages/candidate/Dashboard'; import { CandidateDashboard } from 'pages/candidate/Dashboard';
import { EmailVerificationPage } from 'components/EmailVerificationComponents'; import { EmailVerificationPage } from 'components/EmailVerificationComponents';
import { Typography } from '@mui/material'; import { Box, Typography } from '@mui/material';
import { NavigationConfig, NavigationItem } from 'types/navigation'; import { NavigationConfig, NavigationItem } from 'types/navigation';
import { CandidateProfile } from 'pages/candidate/Profile';
import { JobPicker } from 'components/ui/JobPicker';
import { DocumentManager } from 'components/DocumentManager';
import { VectorVisualizer } from 'components/VectorVisualizer';
import { ComingSoon } from 'components/ui/ComingSoon';
// 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>);
@ -62,7 +70,7 @@ export const navigationConfig: NavigationConfig = {
id: 'find-candidate', id: 'find-candidate',
label: 'Find a Candidate', label: 'Find a Candidate',
path: '/find-a-candidate', path: '/find-a-candidate',
icon: <InfoIcon />, icon: <PersonSearchIcon />,
component: <CandidateListingPage />, component: <CandidateListingPage />,
userTypes: ['guest', 'candidate', 'employer'], userTypes: ['guest', 'candidate', 'employer'],
}, },
@ -70,18 +78,9 @@ export const navigationConfig: NavigationConfig = {
id: 'docs', id: 'docs',
label: 'Docs', label: 'Docs',
path: '/docs/*', path: '/docs/*',
icon: <InfoIcon />, icon: <LibraryBooksIcon />,
component: <DocsPage />, component: <DocsPage />,
userTypes: ['guest', 'candidate', 'employer'], userTypes: ['guest', 'candidate', 'employer'],
// children: [
// {
// id: 'docs-subpage',
// label: 'Docs Subpage',
// path: '/docs/:subPage',
// component: <DocsPage />,
// userTypes: ['guest', 'candidate', 'employer'],
// },
// ],
}, },
{ {
id: 'chat', id: 'chat',
@ -93,83 +92,20 @@ export const navigationConfig: NavigationConfig = {
}, },
{ {
id: 'candidate-menu', id: 'candidate-menu',
label: 'Candidate Tools', label: 'Tools',
icon: <PersonIcon />, icon: <PersonIcon />,
userTypes: ['candidate'], userTypes: ['candidate'],
children: [ children: [
{ { id: 'candidate-dashboard', label: 'Dashboard', path: '/candidate/dashboard', icon: <DashboardIcon />, component: <CandidateDashboard />, userTypes: ['candidate'] },
id: 'candidate-dashboard', { id: 'candidate-profile', label: 'Profile', icon: <PersonIcon />, path: '/candidate/profile', component: <CandidateProfile />, userTypes: ['candidate'] },
label: 'Dashboard', { 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'] },
path: '/candidate/dashboard', { id: 'candidate-analytics', label: 'Analytics', icon: <AnalyticsIcon />, path: '/candidate/analytics', component: <BetaPage><Box>Candidate analytics page</Box></BetaPage>, userTypes: ['candidate'] },
icon: <DashboardIcon />, { id: 'candidate-jobs', label: 'Jobs', icon: <WorkIcon />, path: '/candidate/jobs', component: <JobPicker />, userTypes: ['candidate'] },
component: <CandidateDashboardPage />, { id: 'candidate-job-analysis', label: 'Job Analysis', path: '/candidate/job-analysis', icon: <WorkIcon />, component: <JobAnalysisPage />, userTypes: ['candidate'], },
userTypes: ['candidate'], { id: 'candidate-resumes', label: 'Resumes', icon: <DescriptionIcon />, path: '/candidate/resumes', component: <BetaPage><Box>Candidate resumes page</Box></BetaPage>, userTypes: ['candidate'] },
children: [ { id: 'candidate-resume-builder', label: 'Resume Builder', path: '/candidate/resume-builder', icon: <DescriptionIcon />, component: <ResumeBuilderPage />, userTypes: ['candidate'], },
{ { id: 'candidate-content', label: 'Content', icon: <BubbleChart />, path: '/candidate/content', component: <Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}><VectorVisualizer /><DocumentManager /></Box>, userTypes: ['candidate'] },
id: 'candidate-dashboard-subpage', { id: 'candidate-settings', label: 'Settings', path: '/candidate/settings', icon: <SettingsIcon />, component: <ComingSoon><Settings /></ComingSoon>, userTypes: ['candidate'], },
label: 'Dashboard Subpage',
path: '/candidate/dashboard/:subPage',
component: <CandidateDashboardPage />,
userTypes: ['candidate'],
},
],
},
{
id: 'candidate-job-analysis',
label: 'Job Analysis',
path: '/candidate/job-analysis',
icon: <WorkIcon />,
component: <JobAnalysisPage />,
userTypes: ['candidate'],
},
{
id: 'candidate-resume-builder',
label: 'Resume Builder',
path: '/candidate/resume-builder',
icon: <DescriptionIcon />,
component: <ResumeBuilderPage />,
userTypes: ['candidate'],
},
{
id: 'candidate-backstory',
label: 'Backstory',
path: '/candidate/backstory',
icon: <HistoryIcon />,
component: <BackstoryPage />,
userTypes: ['candidate'],
},
{
id: 'candidate-resumes',
label: 'Resumes',
path: '/candidate/resumes',
icon: <DescriptionIcon />,
component: <ResumesPage />,
userTypes: ['candidate'],
},
{
id: 'candidate-qa-setup',
label: 'Q&A Setup',
path: '/candidate/qa-setup',
icon: <QuestionAnswerIcon />,
component: <QASetupPage />,
userTypes: ['candidate'],
},
{
id: 'candidate-analytics',
label: 'Analytics',
path: '/candidate/analytics',
icon: <BarChartIcon />,
component: <AnalyticsPage />,
userTypes: ['candidate'],
},
{
id: 'candidate-settings',
label: 'Settings',
path: '/candidate/settings',
icon: <SettingsIcon />,
component: <SettingsPage />,
userTypes: ['candidate'],
},
], ],
}, },
{ {
@ -253,51 +189,12 @@ export const navigationConfig: NavigationConfig = {
], ],
}, },
{ {
id: 'global-tools', id: 'admin-menu',
label: 'Tools', label: 'Admin',
icon: <SettingsIcon />,
userTypes: ['candidate', 'employer'],
children: [
{
id: 'resume-builder',
label: 'Resume Builder',
path: '/resume-builder',
icon: <DescriptionIcon />,
component: <ResumeBuilderPage />,
userTypes: ['candidate', 'employer'],
},
{
id: 'knowledge-explorer',
label: 'Knowledge Explorer',
path: '/knowledge-explorer',
icon: <WorkIcon />,
component: <VectorVisualizerPage />,
userTypes: ['candidate', 'employer'],
},
{
id: 'job-analysis',
label: 'Job Analysis',
path: '/job-analysis',
icon: <WorkIcon />,
component: <JobAnalysisPage />,
userTypes: ['candidate', 'employer'],
},
{
id: 'generate-candidate',
label: 'Generate Candidate',
path: '/generate-candidate',
icon: <PersonIcon />, icon: <PersonIcon />,
component: <GenerateCandidate />, userTypes: ['admin'],
userTypes: ['candidate', 'employer'], children: [
}, { id: 'generate-candidate', label: 'Generate Candidate', path: '/admin/generate-candidate', icon: <FaceRetouchingNaturalIcon />, component: <GenerateCandidate />, userTypes: ['admin'] },
{
id: 'settings',
label: 'Settings',
path: '/settings',
icon: <SettingsIcon />,
component: <ControlsPage />,
userTypes: ['candidate', 'employer'],
},
], ],
}, },
// Auth routes (special handling) // Auth routes (special handling)
@ -348,12 +245,11 @@ export const navigationConfig: NavigationConfig = {
}; };
// Utility functions for working with navigation config // Utility functions for working with navigation config
export const getNavigationItemsForUser = (userType: 'guest' | 'candidate' | 'employer' | null): NavigationItem[] => { export const getNavigationItemsForUser = (userType: 'guest' | 'candidate' | 'employer' | null, isAdmin: boolean = false): 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)) .filter(item => !item.userTypes || item.userTypes.includes(currentUserType) || (item.userTypes.includes('admin') && isAdmin))
.map(item => ({ .map(item => ({
...item, ...item,
children: item.children ? filterItems(item.children) : undefined, children: item.children ? filterItems(item.children) : undefined,
@ -364,14 +260,14 @@ export const getNavigationItemsForUser = (userType: 'guest' | 'candidate' | 'emp
return filterItems(navigationConfig.items); return filterItems(navigationConfig.items);
}; };
export const getAllRoutes = (userType: 'guest' | 'candidate' | 'employer' | null): NavigationItem[] => { export const getAllRoutes = (userType: 'guest' | 'candidate' | 'employer' | null, isAdmin: boolean = false): 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)) { if (!item.userTypes || item.userTypes.includes(currentUserType) || (item.userTypes.includes('admin') && isAdmin)) {
if (item.path && item.component) { if (item.path && item.component) {
routes.push(item); routes.push(item);
} }
@ -387,8 +283,8 @@ export const getAllRoutes = (userType: 'guest' | 'candidate' | 'employer' | null
return extractRoutes(navigationConfig.items); return extractRoutes(navigationConfig.items);
}; };
export const getMainNavigationItems = (userType: 'guest' | 'candidate' | 'employer' | null): NavigationItem[] => { export const getMainNavigationItems = (userType: 'guest' | 'candidate' | 'employer' | null, isAdmin: boolean = false): NavigationItem[] => {
return getNavigationItemsForUser(userType) return getNavigationItemsForUser(userType, isAdmin)
.filter(item => .filter(item =>
item.id !== 'auth' && item.id !== 'auth' &&
item.id !== 'catch-all' && item.id !== 'catch-all' &&

View File

@ -38,9 +38,8 @@ const BetaPage: React.FC<BetaPageProps> = ({
const location = useLocation(); const location = useLocation();
if (!children) { if (!children) {
children = (<Box sx={{ width: "100%", display: "flex", justifyContent: "center" }}>The page you requested (<b>{location.pathname.replace(/^\//, '')}</b>) is not yet ready.</Box>); children = (<Box sx={{ width: "100%", display: "flex", justifyContent: "center" }}><Typography>The page you requested (<b>{location.pathname.replace(/^\//, '')}</b>) is not yet ready.</Typography></Box>);
} }
console.log("BetaPage", children);
// Enhanced sparkle effect for background elements // Enhanced sparkle effect for background elements
const [sparkles, setSparkles] = useState<Array<{ const [sparkles, setSparkles] = useState<Array<{

View File

@ -1,4 +1,4 @@
import React, {useState} from 'react'; import React from 'react';
import { import {
Box, Box,
Card, Card,
@ -6,133 +6,205 @@ import {
Typography, Typography,
Button, Button,
LinearProgress, LinearProgress,
List,
ListItem,
ListItemIcon,
ListItemText,
ListItemButton,
Divider,
Chip,
Stack Stack
} from '@mui/material'; } from '@mui/material';
import { import {
Dashboard as DashboardIcon,
Person as PersonIcon,
Work as WorkIcon,
Article as ArticleIcon,
Description as DescriptionIcon,
Quiz as QuizIcon,
Analytics as AnalyticsIcon,
Settings as SettingsIcon,
Add as AddIcon, Add as AddIcon,
Visibility as VisibilityIcon, Visibility as VisibilityIcon,
Download as DownloadIcon, Download as DownloadIcon,
ContactMail as ContactMailIcon, ContactMail as ContactMailIcon,
Edit as EditIcon, Edit as EditIcon,
TipsAndUpdates as TipsIcon, TipsAndUpdates as TipsIcon,
SettingsBackupRestore,
BubbleChart
} from '@mui/icons-material'; } from '@mui/icons-material';
import { useAuth } from 'hooks/AuthContext'; import { useAuth } from 'hooks/AuthContext';
import { LoadingPage } from 'pages/LoadingPage';
import { LoginRequired } from 'pages/LoginRequired'; import { LoginRequired } from 'pages/LoginRequired';
import { BackstoryPageProps } from 'components/BackstoryTab'; import { BackstoryElementProps } from 'components/BackstoryTab';
import { Navigate, useNavigate, useParams } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { BetaPage } from 'pages/BetaPage'; import { ComingSoon } from 'components/ui/ComingSoon';
import { map } from 'lodash';
import { CandidateDashboard } from 'pages/candidate/dashboard/Dashboard';
import { CandidateProfile } from 'pages/candidate/dashboard/Profile';
import { VectorVisualizer } from 'components/VectorVisualizer';
import { DocumentManager } from 'components/DocumentManager';
import { JobPicker } from 'components/ui/JobPicker';
import { useAppState } from 'hooks/GlobalContext'; import { useAppState } from 'hooks/GlobalContext';
interface DashboardProps extends BackstoryPageProps { interface CandidateDashboardProps extends BackstoryElementProps {
userName?: string; };
profileCompletion?: number;
}
const CandidateDashboardPage: React.FC<DashboardProps> = (props: DashboardProps) => { const CandidateDashboard = (props: CandidateDashboardProps) => {
const navigate = useNavigate();
const { subPage = 'dashboard' } = useParams();
const [activeTab, setActiveTab] = useState<string>(subPage);
const { user, isLoading, isInitializing, isAuthenticated } = useAuth();
const profileCompletion = 75;
const { setSnack } = useAppState(); const { setSnack } = useAppState();
const navigate = useNavigate();
const { user } = useAuth();
const profileCompletion = 75;
const sidebarItems = [ if (!user) {
{ text: 'Dashboard', icon: <DashboardIcon />, path: '/', element: <CandidateDashboard /> }, return <LoginRequired />;
{ text: 'Profile', icon: <PersonIcon />, path: '/profile', element: <CandidateProfile /> }, }
{ text: 'Jobs', icon: <WorkIcon />, path: '/jobs', element: <JobPicker /> },
{ text: 'Resumes', icon: <DescriptionIcon />,path: '/resumes', element: <BetaPage><Box>Candidate resumes page</Box></BetaPage> },
{ text: 'Content', icon: <BubbleChart />, path: '/rag', element: <Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}><VectorVisualizer /><DocumentManager /></Box> },
{ text: 'Q&A Setup', icon: <QuizIcon />,path: '/q-a-setup', element: <BetaPage><Box>Candidate q&a setup page</Box></BetaPage> },
{ text: 'Analytics', icon: <AnalyticsIcon />,path: '/analytics', element: <BetaPage><Box>Candidate analytics page</Box></BetaPage> },
{ text: 'Settings', icon: <SettingsIcon />,path: '/settings', element: <BetaPage><Box>Candidate settings page</Box></BetaPage> },
]
if (isLoading || isInitializing) { if (user?.userType !== 'candidate') {
return (<LoadingPage {...props}/>);
}
if (!user || !isAuthenticated) {
return (<LoginRequired {...props}/>);
}
if (user.userType !== 'candidate') {
setSnack(`The page you were on is only available for candidates (you are a ${user.userType}`, 'warning'); setSnack(`The page you were on is only available for candidates (you are a ${user.userType}`, 'warning');
navigate('/'); navigate('/');
return (<></>); return (<></>);
} }
return ( return (<>
<Box sx={{ display: 'flex', minHeight: '100vh', backgroundColor: '#f5f5f5' }}> {/* Main Content */}
{/* Sidebar */} <ComingSoon>
<Box <Box sx={{ flex: 1, p: 3 }}>
{/* Welcome Section */}
<Box sx={{ mb: 4 }}>
<Typography variant="h4" sx={{ mb: 2, fontWeight: 'bold' }}>
Welcome back, {user.firstName}!
</Typography>
<Box sx={{ mb: 2 }}>
<Typography variant="body1" sx={{ mb: 1 }}>
Your profile is {profileCompletion}% complete
</Typography>
<LinearProgress
variant="determinate"
value={profileCompletion}
sx={{ sx={{
width: 250, height: 8,
backgroundColor: 'white', borderRadius: 4,
borderRight: '1px solid #e0e0e0', backgroundColor: '#e0e0e0',
p: 2, '& .MuiLinearProgress-bar': {
}} backgroundColor: '#4caf50',
>
<List>
{sidebarItems.map((item, index) => (
<ListItem key={index} disablePadding sx={{ mb: 0.5 }}>
<ListItemButton
sx={{
borderRadius: 1,
backgroundColor: (item.text.toLowerCase() === activeTab )? '#e3f2fd' : 'transparent',
color: (item.text.toLowerCase() === activeTab ) ? '#1976d2' : '#666',
'&:hover': {
backgroundColor: (item.text.toLowerCase() === activeTab ) ? '#e3f2fd' : '#f5f5f5',
}, },
}} }}
onClick={()=>{setActiveTab(item.text.toLowerCase()); navigate(`/candidate/dashboard/${item.text.toLowerCase()}`);}}
>
<ListItemIcon sx={{ color: 'inherit', minWidth: 40 }}>
{item.icon}
</ListItemIcon>
<ListItemText
primary={item.text}
primaryTypographyProps={{
fontSize: '0.9rem',
fontWeight: (item.text === activeTab ) ? 600 : 400,
}}
/> />
</ListItemButton>
</ListItem>
))}
</List>
</Box> </Box>
{/* Main Content */} <Button
<Box sx={{ flex: 1, p: 3 }}> variant="contained"
{ sidebarItems.map(item => color="primary"
<Box sx={{display: (item.text.toLowerCase() === activeTab) ? "flex": "none", width: "100%"}}>{item.element}</Box> sx={{ mt: 1 }}
)} onClick={(e) => {e.stopPropagation(); navigate('/candidate/dashboard/profile'); }}
>
Complete Your Profile
</Button>
</Box>
{/* Cards Grid */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
{/* Top Row */}
<Box sx={{ display: 'flex', gap: 3 }}>
{/* Resume Builder Card */}
<Card sx={{ flex: 1, minHeight: 200 }}>
<CardContent>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 'bold' }}>
Resume Builder
</Typography>
<Typography variant="body2" sx={{ mb: 1, color: '#666' }}>
3 custom resumes
</Typography>
<Typography variant="body2" sx={{ mb: 3, color: '#666' }}>
Last created: May 15, 2025
</Typography>
<Button
variant="outlined"
startIcon={<AddIcon />}
fullWidth
>
Create New
</Button>
</CardContent>
</Card>
{/* Recent Activity Card */}
<Card sx={{ flex: 1, minHeight: 200 }}>
<CardContent>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 'bold' }}>
Recent Activity
</Typography>
<Stack spacing={1} sx={{ mb: 3 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<VisibilityIcon sx={{ fontSize: 16, color: '#666' }} />
<Typography variant="body2">5 profile views</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<DownloadIcon sx={{ fontSize: 16, color: '#666' }} />
<Typography variant="body2">2 resume downloads</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<ContactMailIcon sx={{ fontSize: 16, color: '#666' }} />
<Typography variant="body2">1 direct contact</Typography>
</Box>
</Stack>
<Button
variant="outlined"
fullWidth
>
View All Activity
</Button>
</CardContent>
</Card>
</Box>
{/* Bottom Row */}
<Box sx={{ display: 'flex', gap: 3 }}>
{/* Complete Your Backstory Card */}
<Card sx={{ flex: 1, minHeight: 200 }}>
<CardContent>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 'bold' }}>
Complete Your Backstory
</Typography>
<Stack spacing={1} sx={{ mb: 3 }}>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Add projects
</Typography>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Detail skills
</Typography>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Work history
</Typography>
</Stack>
<Button
variant="outlined"
startIcon={<EditIcon />}
fullWidth
>
Edit Backstory
</Button>
</CardContent>
</Card>
{/* Improvement Suggestions Card */}
<Card sx={{ flex: 1, minHeight: 200 }}>
<CardContent>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 'bold' }}>
Improvement Suggestions
</Typography>
<Stack spacing={1} sx={{ mb: 3 }}>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Add certifications
</Typography>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Enhance your project details
</Typography>
</Stack>
<Button
variant="outlined"
startIcon={<TipsIcon />}
fullWidth
>
View All Tips
</Button>
</CardContent>
</Card>
</Box> </Box>
</Box> </Box>
</Box>
</ComingSoon>
</>
); );
}; };
export { CandidateDashboardPage }; export { CandidateDashboard };

View File

@ -14,8 +14,8 @@ import Typography from '@mui/material/Typography';
// import ResetIcon from '@mui/icons-material/History'; // import ResetIcon from '@mui/icons-material/History';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { connectionBase } from '../utils/Global'; import { connectionBase } from '../../utils/Global';
import { BackstoryPageProps } from '../components/BackstoryTab'; import { BackstoryPageProps } from '../../components/BackstoryTab';
import { useAppState } from 'hooks/GlobalContext'; import { useAppState } from 'hooks/GlobalContext';
interface ServerTunables { interface ServerTunables {
@ -85,7 +85,7 @@ const SystemInfoComponent: React.FC<{ systemInfo: SystemInfo | undefined }> = ({
return <div className="SystemInfo">{systemElements}</div>; return <div className="SystemInfo">{systemElements}</div>;
}; };
const ControlsPage = (props: BackstoryPageProps) => { const Settings = (props: BackstoryPageProps) => {
const { setSnack } = useAppState(); const { setSnack } = useAppState();
const [editSystemPrompt, setEditSystemPrompt] = useState<string>(""); const [editSystemPrompt, setEditSystemPrompt] = useState<string>("");
const [systemInfo, setSystemInfo] = useState<SystemInfo | undefined>(undefined); const [systemInfo, setSystemInfo] = useState<SystemInfo | undefined>(undefined);
@ -429,5 +429,5 @@ const ControlsPage = (props: BackstoryPageProps) => {
} }
export { export {
ControlsPage Settings
}; };

View File

@ -1,210 +0,0 @@
import React from 'react';
import {
Box,
Card,
CardContent,
Typography,
Button,
LinearProgress,
Stack
} from '@mui/material';
import {
Add as AddIcon,
Visibility as VisibilityIcon,
Download as DownloadIcon,
ContactMail as ContactMailIcon,
Edit as EditIcon,
TipsAndUpdates as TipsIcon,
} from '@mui/icons-material';
import { useAuth } from 'hooks/AuthContext';
import { LoginRequired } from 'pages/LoginRequired';
import { BackstoryElementProps } from 'components/BackstoryTab';
import { useNavigate } from 'react-router-dom';
import { ComingSoon } from 'components/ui/ComingSoon';
import { useAppState } from 'hooks/GlobalContext';
interface CandidateDashboardProps extends BackstoryElementProps {
};
const CandidateDashboard = (props: CandidateDashboardProps) => {
const { setSnack } = useAppState();
const navigate = useNavigate();
const { user } = useAuth();
const profileCompletion = 75;
if (!user) {
return <LoginRequired />;
}
if (user?.userType !== 'candidate') {
setSnack(`The page you were on is only available for candidates (you are a ${user.userType}`, 'warning');
navigate('/');
return (<></>);
}
return (<>
{/* Main Content */}
<ComingSoon>
<Box sx={{ flex: 1, p: 3 }}>
{/* Welcome Section */}
<Box sx={{ mb: 4 }}>
<Typography variant="h4" sx={{ mb: 2, fontWeight: 'bold' }}>
Welcome back, {user.firstName}!
</Typography>
<Box sx={{ mb: 2 }}>
<Typography variant="body1" sx={{ mb: 1 }}>
Your profile is {profileCompletion}% complete
</Typography>
<LinearProgress
variant="determinate"
value={profileCompletion}
sx={{
height: 8,
borderRadius: 4,
backgroundColor: '#e0e0e0',
'& .MuiLinearProgress-bar': {
backgroundColor: '#4caf50',
},
}}
/>
</Box>
<Button
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={(e) => {e.stopPropagation(); navigate('/candidate/dashboard/profile'); }}
>
Complete Your Profile
</Button>
</Box>
{/* Cards Grid */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
{/* Top Row */}
<Box sx={{ display: 'flex', gap: 3 }}>
{/* Resume Builder Card */}
<Card sx={{ flex: 1, minHeight: 200 }}>
<CardContent>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 'bold' }}>
Resume Builder
</Typography>
<Typography variant="body2" sx={{ mb: 1, color: '#666' }}>
3 custom resumes
</Typography>
<Typography variant="body2" sx={{ mb: 3, color: '#666' }}>
Last created: May 15, 2025
</Typography>
<Button
variant="outlined"
startIcon={<AddIcon />}
fullWidth
>
Create New
</Button>
</CardContent>
</Card>
{/* Recent Activity Card */}
<Card sx={{ flex: 1, minHeight: 200 }}>
<CardContent>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 'bold' }}>
Recent Activity
</Typography>
<Stack spacing={1} sx={{ mb: 3 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<VisibilityIcon sx={{ fontSize: 16, color: '#666' }} />
<Typography variant="body2">5 profile views</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<DownloadIcon sx={{ fontSize: 16, color: '#666' }} />
<Typography variant="body2">2 resume downloads</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<ContactMailIcon sx={{ fontSize: 16, color: '#666' }} />
<Typography variant="body2">1 direct contact</Typography>
</Box>
</Stack>
<Button
variant="outlined"
fullWidth
>
View All Activity
</Button>
</CardContent>
</Card>
</Box>
{/* Bottom Row */}
<Box sx={{ display: 'flex', gap: 3 }}>
{/* Complete Your Backstory Card */}
<Card sx={{ flex: 1, minHeight: 200 }}>
<CardContent>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 'bold' }}>
Complete Your Backstory
</Typography>
<Stack spacing={1} sx={{ mb: 3 }}>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Add projects
</Typography>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Detail skills
</Typography>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Work history
</Typography>
</Stack>
<Button
variant="outlined"
startIcon={<EditIcon />}
fullWidth
>
Edit Backstory
</Button>
</CardContent>
</Card>
{/* Improvement Suggestions Card */}
<Card sx={{ flex: 1, minHeight: 200 }}>
<CardContent>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 'bold' }}>
Improvement Suggestions
</Typography>
<Stack spacing={1} sx={{ mb: 3 }}>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Add certifications
</Typography>
<Typography variant="body2" sx={{ display: 'flex', alignItems: 'center' }}>
Enhance your project details
</Typography>
</Stack>
<Button
variant="outlined"
startIcon={<TipsIcon />}
fullWidth
>
View All Tips
</Button>
</CardContent>
</Card>
</Box>
</Box>
</Box>
</ComingSoon>
</>
);
};
export { CandidateDashboard };

View File

@ -8,7 +8,7 @@ export interface NavigationItem {
icon?: ReactElement; icon?: ReactElement;
children?: NavigationItem[]; children?: NavigationItem[];
component?: ReactElement; component?: ReactElement;
userTypes?: ('candidate' | 'employer' | 'guest')[]; userTypes?: ('candidate' | 'employer' | 'guest' | 'admin')[];
exact?: boolean; exact?: boolean;
divider?: boolean; divider?: boolean;
} }