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

View File

@ -17,9 +17,10 @@ interface BackstoryDynamicRoutesProps extends BackstoryPageProps {
const getBackstoryDynamicRoutes = (props: BackstoryDynamicRoutesProps): ReactNode => {
const { user, chatRef } = props;
const userType = user?.userType || null;
const isAdmin = user?.isAdmin ? true : false;
// Get all routes from navigation config
const routes = getAllRoutes(userType);
const routes = getAllRoutes(userType, isAdmin);
return routes.map((route: NavigationItem, index: number) => {
if (!route.path || !route.component) return null;

View File

@ -38,6 +38,7 @@ import {
ExpandLess,
KeyboardArrowDown,
} from '@mui/icons-material';
import FaceRetouchingNaturalIcon from '@mui/icons-material/FaceRetouchingNatural';
import { NavigationItem } from 'types/navigation';
import { Beta } from 'components/ui/Beta';
@ -47,6 +48,7 @@ import { CopyBubble } from 'components/CopyBubble';
import 'components/layout/Header.css';
import { useAuth } from 'hooks/AuthContext';
import { useAppState } from 'hooks/GlobalContext';
// Styled components
const StyledAppBar = styled(AppBar, {
@ -123,16 +125,15 @@ interface HeaderProps {
const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
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 {
transparent = false,
className,
navigate,
navigationItems,
sessionId,
setSnack,
} = props;
const { setSnack } = useAppState();
const theme = useTheme();
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
const isCurrentPath = (item: NavigationItem): boolean => {
if (!item.path) return false;

View File

@ -11,13 +11,19 @@ import {
Info as InfoIcon,
Person as PersonIcon,
Business as BusinessIcon,
Quiz as QuizIcon,
Analytics as AnalyticsIcon,
Search as SearchIcon,
Bookmark as BookmarkIcon,
History as HistoryIcon,
QuestionAnswer as QuestionAnswerIcon,
AttachMoney as AttachMoneyIcon,
BubbleChart,
InsertEmoticon,
} 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 { HomePage } from 'pages/HomePage';
import { CandidateChatPage } from 'pages/CandidateChatPage';
@ -29,17 +35,19 @@ import { BetaPage } from 'pages/BetaPage';
import { CandidateListingPage } from 'pages/FindCandidatePage';
import { JobAnalysisPage } from 'pages/JobAnalysisPage';
import { GenerateCandidate } from 'pages/GenerateCandidate';
import { ControlsPage } from 'pages/ControlsPage';
import { Settings } from 'pages/candidate/Settings';
import { LoginPage } from 'pages/LoginPage';
import { CandidateDashboardPage } from 'pages/candidate/Dashboard';
import { CandidateDashboard } from 'pages/candidate/Dashboard';
import { EmailVerificationPage } from 'components/EmailVerificationComponents';
import { Typography } from '@mui/material';
import { Box, Typography } from '@mui/material';
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
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 SavedPage = () => (<BetaPage><Typography variant="h4">Saved</Typography></BetaPage>);
const JobsPage = () => (<BetaPage><Typography variant="h4">Jobs</Typography></BetaPage>);
@ -62,7 +70,7 @@ export const navigationConfig: NavigationConfig = {
id: 'find-candidate',
label: 'Find a Candidate',
path: '/find-a-candidate',
icon: <InfoIcon />,
icon: <PersonSearchIcon />,
component: <CandidateListingPage />,
userTypes: ['guest', 'candidate', 'employer'],
},
@ -70,18 +78,9 @@ export const navigationConfig: NavigationConfig = {
id: 'docs',
label: 'Docs',
path: '/docs/*',
icon: <InfoIcon />,
icon: <LibraryBooksIcon />,
component: <DocsPage />,
userTypes: ['guest', 'candidate', 'employer'],
// children: [
// {
// id: 'docs-subpage',
// label: 'Docs Subpage',
// path: '/docs/:subPage',
// component: <DocsPage />,
// userTypes: ['guest', 'candidate', 'employer'],
// },
// ],
userTypes: ['guest', 'candidate', 'employer'],
},
{
id: 'chat',
@ -93,83 +92,20 @@ export const navigationConfig: NavigationConfig = {
},
{
id: 'candidate-menu',
label: 'Candidate Tools',
label: 'Tools',
icon: <PersonIcon />,
userTypes: ['candidate'],
children: [
{
id: 'candidate-dashboard',
label: 'Dashboard',
path: '/candidate/dashboard',
icon: <DashboardIcon />,
component: <CandidateDashboardPage />,
userTypes: ['candidate'],
children: [
{
id: 'candidate-dashboard-subpage',
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'],
},
{ id: 'candidate-dashboard', label: 'Dashboard', path: '/candidate/dashboard', icon: <DashboardIcon />, component: <CandidateDashboard />, userTypes: ['candidate'] },
{ id: 'candidate-profile', label: 'Profile', icon: <PersonIcon />, path: '/candidate/profile', component: <CandidateProfile />, 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-jobs', label: 'Jobs', icon: <WorkIcon />, path: '/candidate/jobs', component: <JobPicker />, userTypes: ['candidate'] },
{ id: 'candidate-job-analysis', label: 'Job Analysis', path: '/candidate/job-analysis', icon: <WorkIcon />, component: <JobAnalysisPage />, userTypes: ['candidate'], },
{ id: 'candidate-resumes', label: 'Resumes', icon: <DescriptionIcon />, path: '/candidate/resumes', component: <BetaPage><Box>Candidate resumes page</Box></BetaPage>, userTypes: ['candidate'] },
{ 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-settings', label: 'Settings', path: '/candidate/settings', icon: <SettingsIcon />, component: <ComingSoon><Settings /></ComingSoon>, userTypes: ['candidate'], },
],
},
{
@ -250,55 +186,16 @@ export const navigationConfig: NavigationConfig = {
component: <SettingsPage />,
userTypes: ['employer'],
},
],
},
],
},
{
id: 'global-tools',
label: 'Tools',
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 />,
component: <GenerateCandidate />,
userTypes: ['candidate', 'employer'],
},
{
id: 'settings',
label: 'Settings',
path: '/settings',
icon: <SettingsIcon />,
component: <ControlsPage />,
userTypes: ['candidate', 'employer'],
},
],
id: 'admin-menu',
label: 'Admin',
icon: <PersonIcon />,
userTypes: ['admin'],
children: [
{ id: 'generate-candidate', label: 'Generate Candidate', path: '/admin/generate-candidate', icon: <FaceRetouchingNaturalIcon />, component: <GenerateCandidate />, userTypes: ['admin'] },
],
},
// Auth routes (special handling)
{
@ -348,12 +245,11 @@ export const navigationConfig: NavigationConfig = {
};
// Utility functions for working with navigation config
export const getNavigationItemsForUser = (userType: 'guest' | 'candidate' | 'employer' | null): NavigationItem[] => {
const currentUserType = userType || 'guest';
export const getNavigationItemsForUser = (userType: 'guest' | 'candidate' | 'employer' | null, isAdmin: boolean = false): NavigationItem[] => {
const currentUserType = userType || 'guest';
const filterItems = (items: NavigationItem[]): NavigationItem[] => {
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 => ({
...item,
children: item.children ? filterItems(item.children) : undefined,
@ -364,14 +260,14 @@ export const getNavigationItemsForUser = (userType: 'guest' | 'candidate' | 'emp
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 extractRoutes = (items: NavigationItem[]): NavigationItem[] => {
const routes: NavigationItem[] = [];
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) {
routes.push(item);
}
@ -387,8 +283,8 @@ export const getAllRoutes = (userType: 'guest' | 'candidate' | 'employer' | null
return extractRoutes(navigationConfig.items);
};
export const getMainNavigationItems = (userType: 'guest' | 'candidate' | 'employer' | null): NavigationItem[] => {
return getNavigationItemsForUser(userType)
export const getMainNavigationItems = (userType: 'guest' | 'candidate' | 'employer' | null, isAdmin: boolean = false): NavigationItem[] => {
return getNavigationItemsForUser(userType, isAdmin)
.filter(item =>
item.id !== 'auth' &&
item.id !== 'catch-all' &&

View File

@ -38,9 +38,8 @@ const BetaPage: React.FC<BetaPageProps> = ({
const location = useLocation();
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
const [sparkles, setSparkles] = useState<Array<{

View File

@ -1,4 +1,4 @@
import React, {useState} from 'react';
import React from 'react';
import {
Box,
Card,
@ -6,133 +6,205 @@ import {
Typography,
Button,
LinearProgress,
List,
ListItem,
ListItemIcon,
ListItemText,
ListItemButton,
Divider,
Chip,
Stack
} from '@mui/material';
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,
Visibility as VisibilityIcon,
Download as DownloadIcon,
ContactMail as ContactMailIcon,
Edit as EditIcon,
TipsAndUpdates as TipsIcon,
SettingsBackupRestore,
BubbleChart
} from '@mui/icons-material';
import { useAuth } from 'hooks/AuthContext';
import { LoadingPage } from 'pages/LoadingPage';
import { LoginRequired } from 'pages/LoginRequired';
import { BackstoryPageProps } from 'components/BackstoryTab';
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import { BetaPage } from 'pages/BetaPage';
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 { BackstoryElementProps } from 'components/BackstoryTab';
import { useNavigate } from 'react-router-dom';
import { ComingSoon } from 'components/ui/ComingSoon';
import { useAppState } from 'hooks/GlobalContext';
interface DashboardProps extends BackstoryPageProps {
userName?: string;
profileCompletion?: number;
}
interface CandidateDashboardProps extends BackstoryElementProps {
};
const CandidateDashboardPage: React.FC<DashboardProps> = (props: DashboardProps) => {
const navigate = useNavigate();
const { subPage = 'dashboard' } = useParams();
const [activeTab, setActiveTab] = useState<string>(subPage);
const { user, isLoading, isInitializing, isAuthenticated } = useAuth();
const profileCompletion = 75;
const CandidateDashboard = (props: CandidateDashboardProps) => {
const { setSnack } = useAppState();
const navigate = useNavigate();
const { user } = useAuth();
const profileCompletion = 75;
const sidebarItems = [
{ text: 'Dashboard', icon: <DashboardIcon />, path: '/', element: <CandidateDashboard /> },
{ 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 (!user) {
return <LoginRequired />;
}
if (isLoading || isInitializing) {
return (<LoadingPage {...props}/>);
}
if (!user || !isAuthenticated) {
return (<LoginRequired {...props}/>);
}
if (user.userType !== 'candidate') {
if (user?.userType !== 'candidate') {
setSnack(`The page you were on is only available for candidates (you are a ${user.userType}`, 'warning');
navigate('/');
return (<></>);
}
return (
<Box sx={{ display: 'flex', minHeight: '100vh', backgroundColor: '#f5f5f5' }}>
{/* Sidebar */}
<Box
sx={{
width: 250,
backgroundColor: 'white',
borderRight: '1px solid #e0e0e0',
p: 2,
}}
>
<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>
return (<>
{/* Main Content */}
<ComingSoon>
<Box sx={{ flex: 1, p: 3 }}>
{ sidebarItems.map(item =>
<Box sx={{display: (item.text.toLowerCase() === activeTab) ? "flex": "none", width: "100%"}}>{item.element}</Box>
)}
{/* 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>
</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 ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { connectionBase } from '../utils/Global';
import { BackstoryPageProps } from '../components/BackstoryTab';
import { connectionBase } from '../../utils/Global';
import { BackstoryPageProps } from '../../components/BackstoryTab';
import { useAppState } from 'hooks/GlobalContext';
interface ServerTunables {
@ -85,7 +85,7 @@ const SystemInfoComponent: React.FC<{ systemInfo: SystemInfo | undefined }> = ({
return <div className="SystemInfo">{systemElements}</div>;
};
const ControlsPage = (props: BackstoryPageProps) => {
const Settings = (props: BackstoryPageProps) => {
const { setSnack } = useAppState();
const [editSystemPrompt, setEditSystemPrompt] = useState<string>("");
const [systemInfo, setSystemInfo] = useState<SystemInfo | undefined>(undefined);
@ -429,5 +429,5 @@ const ControlsPage = (props: BackstoryPageProps) => {
}
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;
children?: NavigationItem[];
component?: ReactElement;
userTypes?: ('candidate' | 'employer' | 'guest')[];
userTypes?: ('candidate' | 'employer' | 'guest' | 'admin')[];
exact?: boolean;
divider?: boolean;
}