diff --git a/frontend/src/components/layout/BackstoryLayout.tsx b/frontend/src/components/layout/BackstoryLayout.tsx index 70eb72e..5d84e67 100644 --- a/frontend/src/components/layout/BackstoryLayout.tsx +++ b/frontend/src/components/layout/BackstoryLayout.tsx @@ -89,7 +89,7 @@ const BackstoryLayout: React.FC = (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 = (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; diff --git a/frontend/src/components/layout/BackstoryRoutes.tsx b/frontend/src/components/layout/BackstoryRoutes.tsx index f0119ca..07958cf 100644 --- a/frontend/src/components/layout/BackstoryRoutes.tsx +++ b/frontend/src/components/layout/BackstoryRoutes.tsx @@ -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; diff --git a/frontend/src/components/layout/Header.tsx b/frontend/src/components/layout/Header.tsx index b16fe07..b464faf 100644 --- a/frontend/src/components/layout/Header.tsx +++ b/frontend/src/components/layout/Header.tsx @@ -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 = (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 = (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: , 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; diff --git a/frontend/src/config/navigationConfig.tsx b/frontend/src/config/navigationConfig.tsx index 31585d9..0c1086e 100644 --- a/frontend/src/config/navigationConfig.tsx +++ b/frontend/src/config/navigationConfig.tsx @@ -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 = () => (Backstory); -const ResumesPage = () => (Resumes); -const QASetupPage = () => (Q&A Setup); const SearchPage = () => (Search); const SavedPage = () => (Saved); const JobsPage = () => (Jobs); @@ -62,7 +70,7 @@ export const navigationConfig: NavigationConfig = { id: 'find-candidate', label: 'Find a Candidate', path: '/find-a-candidate', - icon: , + icon: , component: , userTypes: ['guest', 'candidate', 'employer'], }, @@ -70,18 +78,9 @@ export const navigationConfig: NavigationConfig = { id: 'docs', label: 'Docs', path: '/docs/*', - icon: , + icon: , component: , - userTypes: ['guest', 'candidate', 'employer'], - // children: [ - // { - // id: 'docs-subpage', - // label: 'Docs Subpage', - // path: '/docs/:subPage', - // component: , - // 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: , userTypes: ['candidate'], children: [ - { - id: 'candidate-dashboard', - label: 'Dashboard', - path: '/candidate/dashboard', - icon: , - component: , - userTypes: ['candidate'], - children: [ - { - id: 'candidate-dashboard-subpage', - label: 'Dashboard Subpage', - path: '/candidate/dashboard/:subPage', - component: , - userTypes: ['candidate'], - }, - ], - }, - { - id: 'candidate-job-analysis', - label: 'Job Analysis', - path: '/candidate/job-analysis', - icon: , - component: , - userTypes: ['candidate'], - }, - { - id: 'candidate-resume-builder', - label: 'Resume Builder', - path: '/candidate/resume-builder', - icon: , - component: , - userTypes: ['candidate'], - }, - { - id: 'candidate-backstory', - label: 'Backstory', - path: '/candidate/backstory', - icon: , - component: , - userTypes: ['candidate'], - }, - { - id: 'candidate-resumes', - label: 'Resumes', - path: '/candidate/resumes', - icon: , - component: , - userTypes: ['candidate'], - }, - { - id: 'candidate-qa-setup', - label: 'Q&A Setup', - path: '/candidate/qa-setup', - icon: , - component: , - userTypes: ['candidate'], - }, - { - id: 'candidate-analytics', - label: 'Analytics', - path: '/candidate/analytics', - icon: , - component: , - userTypes: ['candidate'], - }, - { - id: 'candidate-settings', - label: 'Settings', - path: '/candidate/settings', - icon: , - component: , - userTypes: ['candidate'], - }, + { id: 'candidate-dashboard', label: 'Dashboard', path: '/candidate/dashboard', icon: , component: , userTypes: ['candidate'] }, + { id: 'candidate-profile', label: 'Profile', icon: , path: '/candidate/profile', component: , userTypes: ['candidate'] }, + { id: 'candidate-qa-setup', label: 'Q&A Setup', icon: , path: '/candidate/qa-setup', component: Candidate q&a setup page, userTypes: ['candidate'] }, + { id: 'candidate-analytics', label: 'Analytics', icon: , path: '/candidate/analytics', component: Candidate analytics page, userTypes: ['candidate'] }, + { id: 'candidate-jobs', label: 'Jobs', icon: , path: '/candidate/jobs', component: , userTypes: ['candidate'] }, + { id: 'candidate-job-analysis', label: 'Job Analysis', path: '/candidate/job-analysis', icon: , component: , userTypes: ['candidate'], }, + { id: 'candidate-resumes', label: 'Resumes', icon: , path: '/candidate/resumes', component: Candidate resumes page, userTypes: ['candidate'] }, + { id: 'candidate-resume-builder', label: 'Resume Builder', path: '/candidate/resume-builder', icon: , component: , userTypes: ['candidate'], }, + { id: 'candidate-content', label: 'Content', icon: , path: '/candidate/content', component: , userTypes: ['candidate'] }, + { id: 'candidate-settings', label: 'Settings', path: '/candidate/settings', icon: , component: , userTypes: ['candidate'], }, ], }, { @@ -250,55 +186,16 @@ export const navigationConfig: NavigationConfig = { component: , userTypes: ['employer'], }, - ], - }, + ], + }, { - id: 'global-tools', - label: 'Tools', - icon: , - userTypes: ['candidate', 'employer'], - children: [ - { - id: 'resume-builder', - label: 'Resume Builder', - path: '/resume-builder', - icon: , - component: , - userTypes: ['candidate', 'employer'], - }, - { - id: 'knowledge-explorer', - label: 'Knowledge Explorer', - path: '/knowledge-explorer', - icon: , - component: , - userTypes: ['candidate', 'employer'], - }, - { - id: 'job-analysis', - label: 'Job Analysis', - path: '/job-analysis', - icon: , - component: , - userTypes: ['candidate', 'employer'], - }, - { - id: 'generate-candidate', - label: 'Generate Candidate', - path: '/generate-candidate', - icon: , - component: , - userTypes: ['candidate', 'employer'], - }, - { - id: 'settings', - label: 'Settings', - path: '/settings', - icon: , - component: , - userTypes: ['candidate', 'employer'], - }, - ], + id: 'admin-menu', + label: 'Admin', + icon: , + userTypes: ['admin'], + children: [ + { id: 'generate-candidate', label: 'Generate Candidate', path: '/admin/generate-candidate', icon: , component: , 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' && diff --git a/frontend/src/pages/BetaPage.tsx b/frontend/src/pages/BetaPage.tsx index db82c84..fcfdb5d 100644 --- a/frontend/src/pages/BetaPage.tsx +++ b/frontend/src/pages/BetaPage.tsx @@ -38,9 +38,8 @@ const BetaPage: React.FC = ({ const location = useLocation(); if (!children) { - children = (The page you requested ({location.pathname.replace(/^\//, '')}) is not yet ready.); + children = (The page you requested ({location.pathname.replace(/^\//, '')}) is not yet ready.); } - console.log("BetaPage", children); // Enhanced sparkle effect for background elements const [sparkles, setSparkles] = useState = (props: DashboardProps) => { - const navigate = useNavigate(); - const { subPage = 'dashboard' } = useParams(); - const [activeTab, setActiveTab] = useState(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: , path: '/', element: }, - { text: 'Profile', icon: , path: '/profile', element: }, - { text: 'Jobs', icon: , path: '/jobs', element: }, - { text: 'Resumes', icon: ,path: '/resumes', element: Candidate resumes page }, - { text: 'Content', icon: , path: '/rag', element: }, - { text: 'Q&A Setup', icon: ,path: '/q-a-setup', element: Candidate q&a setup page }, - { text: 'Analytics', icon: ,path: '/analytics', element: Candidate analytics page }, - { text: 'Settings', icon: ,path: '/settings', element: Candidate settings page }, -] + if (!user) { + return ; + } - if (isLoading || isInitializing) { - return (); - } - if (!user || !isAuthenticated) { - return (); - } - 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 ( - - {/* Sidebar */} - - - {sidebarItems.map((item, index) => ( - - {setActiveTab(item.text.toLowerCase()); navigate(`/candidate/dashboard/${item.text.toLowerCase()}`);}} - > - - {item.icon} - - - - - ))} - - - + return (<> {/* Main Content */} + - { sidebarItems.map(item => - {item.element} - )} + {/* Welcome Section */} + + + Welcome back, {user.firstName}! + + + + + Your profile is {profileCompletion}% complete + + + + + + + + {/* Cards Grid */} + + {/* Top Row */} + + {/* Resume Builder Card */} + + + + Resume Builder + + + + 3 custom resumes + + + + Last created: May 15, 2025 + + + + + + + {/* Recent Activity Card */} + + + + Recent Activity + + + + + + 5 profile views + + + + + 2 resume downloads + + + + + 1 direct contact + + + + + + + + + {/* Bottom Row */} + + {/* Complete Your Backstory Card */} + + + + Complete Your Backstory + + + + + • Add projects + + + • Detail skills + + + • Work history + + + + + + + + {/* Improvement Suggestions Card */} + + + + Improvement Suggestions + + + + + • Add certifications + + + • Enhance your project details + + + + + + + + - + + ); }; -export { CandidateDashboardPage }; \ No newline at end of file +export { CandidateDashboard }; \ No newline at end of file diff --git a/frontend/src/pages/candidate/dashboard/Profile.tsx b/frontend/src/pages/candidate/Profile.tsx similarity index 100% rename from frontend/src/pages/candidate/dashboard/Profile.tsx rename to frontend/src/pages/candidate/Profile.tsx diff --git a/frontend/src/pages/ControlsPage.tsx b/frontend/src/pages/candidate/Settings.tsx similarity index 98% rename from frontend/src/pages/ControlsPage.tsx rename to frontend/src/pages/candidate/Settings.tsx index a705fcb..71c7a0b 100644 --- a/frontend/src/pages/ControlsPage.tsx +++ b/frontend/src/pages/candidate/Settings.tsx @@ -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
{systemElements}
; }; -const ControlsPage = (props: BackstoryPageProps) => { +const Settings = (props: BackstoryPageProps) => { const { setSnack } = useAppState(); const [editSystemPrompt, setEditSystemPrompt] = useState(""); const [systemInfo, setSystemInfo] = useState(undefined); @@ -429,5 +429,5 @@ const ControlsPage = (props: BackstoryPageProps) => { } export { - ControlsPage + Settings }; \ No newline at end of file diff --git a/frontend/src/pages/candidate/dashboard/Dashboard.tsx b/frontend/src/pages/candidate/dashboard/Dashboard.tsx deleted file mode 100644 index 85c0472..0000000 --- a/frontend/src/pages/candidate/dashboard/Dashboard.tsx +++ /dev/null @@ -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 ; - } - - 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 */} - - - {/* Welcome Section */} - - - Welcome back, {user.firstName}! - - - - - Your profile is {profileCompletion}% complete - - - - - - - - {/* Cards Grid */} - - {/* Top Row */} - - {/* Resume Builder Card */} - - - - Resume Builder - - - - 3 custom resumes - - - - Last created: May 15, 2025 - - - - - - - {/* Recent Activity Card */} - - - - Recent Activity - - - - - - 5 profile views - - - - - 2 resume downloads - - - - - 1 direct contact - - - - - - - - - {/* Bottom Row */} - - {/* Complete Your Backstory Card */} - - - - Complete Your Backstory - - - - - • Add projects - - - • Detail skills - - - • Work history - - - - - - - - {/* Improvement Suggestions Card */} - - - - Improvement Suggestions - - - - - • Add certifications - - - • Enhance your project details - - - - - - - - - - - - ); -}; - -export { CandidateDashboard }; \ No newline at end of file diff --git a/frontend/src/types/navigation.ts b/frontend/src/types/navigation.ts index 7b58a62..fb57cbb 100644 --- a/frontend/src/types/navigation.ts +++ b/frontend/src/types/navigation.ts @@ -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; }