Restructured for menus
This commit is contained in:
parent
38635f0fd4
commit
43b332bd47
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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' &&
|
||||
|
@ -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<{
|
||||
|
@ -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 };
|
@ -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
|
||||
};
|
@ -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 };
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user