New UI
This commit is contained in:
parent
b1faac7020
commit
a73d083c2b
4327
frontend/package-lock.json
generated
4327
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@
|
||||
"@uiw/react-json-view": "^2.0.0-alpha.31",
|
||||
"@uiw/react-markdown-editor": "^6.1.4",
|
||||
"jsonrepair": "^3.12.0",
|
||||
"lucide-react": "^0.511.0",
|
||||
"markdown-it": "^14.1.0",
|
||||
"mermaid": "^11.6.0",
|
||||
"mui-markdown": "^2.0.1",
|
||||
|
15
frontend/public/docs/beta.md
Normal file
15
frontend/public/docs/beta.md
Normal file
@ -0,0 +1,15 @@
|
||||
I am happy to announce the first public BETA release of Backstory!
|
||||
|
||||
First, what works:
|
||||
|
||||
1. There are two personas populated:
|
||||
1. One is me [jketreno](/u/jketreno)
|
||||
2. The other is a ficticious AI generated persona named [Eliz](/u/eliza).
|
||||
2. **Candidate Skill Chat** You can go to the Chat tab to ask questions about the active candaite.
|
||||
3. **Resume Builder** You can build a resume for a person given a Job Description
|
||||
|
||||
What doesn't work:
|
||||
|
||||
1. User login, registration, etc.
|
||||
2. Lots of the links on the site.
|
||||
3. Anything that isn't "Chat", "Resume Builder", or "About".
|
@ -2,9 +2,10 @@ import React, { useRef, useCallback } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, useLocation } from "react-router-dom";
|
||||
import { SessionWrapper } from "./App/SessionWrapper";
|
||||
import { Main } from "./App/Main";
|
||||
import { BackstoryApp } from './NewApp/BackstoryApp';
|
||||
import { Snack, SeverityType } from './Components/Snack';
|
||||
|
||||
export function PathRouter({ setSnack }: { setSnack: any }) {
|
||||
const PathRouter = ({ setSnack }: { setSnack: any }) => {
|
||||
const location = useLocation();
|
||||
const segments = location.pathname.split("/").filter(Boolean);
|
||||
const sessionId = segments[segments.length - 1];
|
||||
@ -16,7 +17,7 @@ export function PathRouter({ setSnack }: { setSnack: any }) {
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
function App2() {
|
||||
const snackRef = useRef<any>(null);
|
||||
|
||||
const setSnack = useCallback((message: string, severity?: SeverityType) => {
|
||||
@ -37,4 +38,13 @@ function App() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<Router>
|
||||
<BackstoryApp />
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
@ -500,17 +500,18 @@ const Conversation = forwardRef<ConversationHandle, ConversationProps>((props: C
|
||||
};
|
||||
|
||||
return (
|
||||
<Scrollable
|
||||
className={`${className || ""} Conversation`}
|
||||
autoscroll
|
||||
textFieldRef={viewableElementRef}
|
||||
fallbackThreshold={0.5}
|
||||
sx={{
|
||||
p: 1,
|
||||
mt: 0,
|
||||
...sx
|
||||
}}
|
||||
>
|
||||
// <Scrollable
|
||||
// className={`${className || ""} Conversation`}
|
||||
// autoscroll
|
||||
// textFieldRef={viewableElementRef}
|
||||
// fallbackThreshold={0.5}
|
||||
// sx={{
|
||||
// p: 1,
|
||||
// mt: 0,
|
||||
// ...sx
|
||||
// }}
|
||||
// >
|
||||
<Box sx={{ p: 1, mt: 0, overflow: "hidden", ...sx }}>
|
||||
{
|
||||
filteredConversation.map((message, index) =>
|
||||
<Message key={index} expanded={message.expanded === undefined ? true : message.expanded} {...{ sendQuery, message, connectionBase, sessionId, setSnack, submitQuery }} />
|
||||
@ -610,7 +611,7 @@ const Conversation = forwardRef<ConversationHandle, ConversationProps>((props: C
|
||||
}
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", flexGrow: 1 }}></Box>
|
||||
</Scrollable>
|
||||
</Box >
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,18 +1,22 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
|
||||
import { Message } from './Message';
|
||||
import { ChatBubble } from '../Components/ChatBubble';
|
||||
import { BackstoryElementProps } from './BackstoryTab';
|
||||
|
||||
interface DocumentProps extends BackstoryElementProps {
|
||||
title: string;
|
||||
expanded?: boolean;
|
||||
filepath: string;
|
||||
filepath?: string;
|
||||
content?: string;
|
||||
disableCopy?: boolean;
|
||||
children?: React.ReactNode;
|
||||
onExpand?: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const Document = (props: DocumentProps) => {
|
||||
const { sessionId, setSnack, submitQuery, filepath, content, title, expanded, disableCopy, onExpand } = props;
|
||||
const { sessionId, setSnack, submitQuery, filepath, content, title, expanded, disableCopy, onExpand, children } = props;
|
||||
const backstoryProps = {
|
||||
submitQuery,
|
||||
setSnack,
|
||||
@ -49,21 +53,44 @@ const Document = (props: DocumentProps) => {
|
||||
}, [document, setDocument, filepath])
|
||||
|
||||
return (
|
||||
<Message
|
||||
{...{
|
||||
sx: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
p: 1,
|
||||
m: 0,
|
||||
flexGrow: 0,
|
||||
},
|
||||
message: { role: 'content', title: title, content: document || content || "" },
|
||||
expanded,
|
||||
disableCopy,
|
||||
onExpand,
|
||||
}}
|
||||
{...backstoryProps} />
|
||||
<Box>
|
||||
{children !== undefined && <ChatBubble
|
||||
{...{
|
||||
sx: {
|
||||
mt: 1,
|
||||
p: 1,
|
||||
flexGrow: 0,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
pt: "8px",
|
||||
pb: "8px",
|
||||
marginTop: "8px !important", // Remove whitespace from expanded Accordion
|
||||
marginBottom: "0px !important", // Remove whitespace from expanded Accordion
|
||||
},
|
||||
role: "content",
|
||||
title,
|
||||
expanded,
|
||||
disableCopy,
|
||||
onExpand
|
||||
}}>{children}</ChatBubble>}
|
||||
{children === undefined && <Message
|
||||
{...{
|
||||
sx: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
p: 1,
|
||||
m: 0,
|
||||
flexGrow: 0,
|
||||
marginTop: "8px !important", // Remove whitespace from expanded Accordion
|
||||
},
|
||||
message: { role: 'content', title: title, content: document || content || "" },
|
||||
expanded,
|
||||
disableCopy,
|
||||
onExpand,
|
||||
}}
|
||||
{...backstoryProps} />
|
||||
}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
247
frontend/src/NewApp/BackstoryApp.tsx
Normal file
247
frontend/src/NewApp/BackstoryApp.tsx
Normal file
@ -0,0 +1,247 @@
|
||||
import React, { ReactElement, useEffect, useState, useRef, useCallback} from 'react';
|
||||
import { Box, Typography, Container, Paper } from '@mui/material';
|
||||
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
||||
import DashboardIcon from '@mui/icons-material/Dashboard';
|
||||
import PersonIcon from '@mui/icons-material/Person';
|
||||
import ChatIcon from '@mui/icons-material/Chat';
|
||||
import HistoryIcon from '@mui/icons-material/History';
|
||||
import DescriptionIcon from '@mui/icons-material/Description';
|
||||
import QuestionAnswerIcon from '@mui/icons-material/QuestionAnswer';
|
||||
import BarChartIcon from '@mui/icons-material/BarChart';
|
||||
import SettingsIcon from '@mui/icons-material/Settings';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import BookmarkIcon from '@mui/icons-material/Bookmark';
|
||||
import WorkIcon from '@mui/icons-material/Work';
|
||||
import InfoIcon from '@mui/icons-material/Info';
|
||||
import BusinessIcon from '@mui/icons-material/Business';
|
||||
import { SxProps, Theme } from '@mui/material';
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
|
||||
import {Header} from './Components/Header';
|
||||
import { Scrollable } from '../Components/Scrollable';
|
||||
import { Footer } from './Components/Footer';
|
||||
import { HomePage } from './Pages/HomePage';
|
||||
// import { BackstoryThemeVisualizer } from './BackstoryThemeVisualizer';
|
||||
import { HomePage as ChatPage } from '../Pages/HomePage';
|
||||
import { ResumeBuilderPage } from '../Pages/ResumeBuilderPage';
|
||||
import { Snack, SeverityType } from '../Components/Snack';
|
||||
import { Query } from '../Components/ChatQuery';
|
||||
import { ConversationHandle } from '../Components/Conversation';
|
||||
import { AboutPage } from './Pages/AboutPage';
|
||||
import { backstoryTheme } from '../BackstoryTheme';
|
||||
|
||||
import '@fontsource/roboto/300.css';
|
||||
import '@fontsource/roboto/400.css';
|
||||
import '@fontsource/roboto/500.css';
|
||||
import '@fontsource/roboto/700.css';
|
||||
|
||||
type NavigationLinkType = {
|
||||
name: string;
|
||||
path: string;
|
||||
icon?: ReactElement<any>;
|
||||
label?: ReactElement<any>;
|
||||
};
|
||||
|
||||
const DefaultNavItems : NavigationLinkType[] = [
|
||||
{ name: 'Chat', path: '/chat', icon: <ChatIcon /> },
|
||||
{ name: 'Resume Builder', path: '/resume-builder', icon: <WorkIcon /> },
|
||||
{ name: 'About', path: '/about', icon: <InfoIcon /> },
|
||||
// { name: 'How It Works', path: '/how-it-works', icon: <InfoIcon/> },
|
||||
// { name: 'For Candidates', path: '/for-candidates', icon: <PersonIcon/> },
|
||||
// { name: 'For Employers', path: '/for-employers', icon: <BusinessIcon/> },
|
||||
// { name: 'Pricing', path: '/pricing', icon: <AttachMoneyIcon/> },
|
||||
];
|
||||
|
||||
const CandidateNavItems : NavigationLinkType[]= [
|
||||
{ name: 'Dashboard', icon: <DashboardIcon />, path: '/dashboard' },
|
||||
{ name: 'Profile', icon: <PersonIcon />, path: '/profile' },
|
||||
{ name: 'Backstory', icon: <HistoryIcon />, path: '/backstory' },
|
||||
{ name: 'Resumes', icon: <DescriptionIcon />, path: '/resumes' },
|
||||
{ name: 'Q&A Setup', icon: <QuestionAnswerIcon />, path: '/qa-setup' },
|
||||
{ name: 'Analytics', icon: <BarChartIcon />, path: '/analytics' },
|
||||
{ name: 'Settings', icon: <SettingsIcon />, path: '/settings' },
|
||||
];
|
||||
|
||||
const EmployerNavItems: NavigationLinkType[] = [
|
||||
{ name: 'Dashboard', icon: <DashboardIcon />, path: '/dashboard' },
|
||||
{ name: 'Search', icon: <SearchIcon />, path: '/search' },
|
||||
{ name: 'Saved', icon: <BookmarkIcon />, path: '/saved' },
|
||||
{ name: 'Jobs', icon: <WorkIcon />, path: '/jobs' },
|
||||
{ name: 'Company', icon: <BusinessIcon />, path: '/company' },
|
||||
{ name: 'Analytics', icon: <BarChartIcon />, path: '/analytics' },
|
||||
{ name: 'Settings', icon: <SettingsIcon />, path: '/settings' },
|
||||
];
|
||||
|
||||
// Navigation links based on user type
|
||||
const getNavigationLinks = (userType: string, isLoggedIn: boolean) : NavigationLinkType[] => {
|
||||
if (!isLoggedIn) {
|
||||
return DefaultNavItems;
|
||||
}
|
||||
|
||||
if (userType === 'candidate') {
|
||||
return CandidateNavItems;
|
||||
}
|
||||
|
||||
// Employer navigation
|
||||
return EmployerNavItems;
|
||||
};
|
||||
|
||||
// Placeholder for page components
|
||||
const DashboardPage = () => <Box p={3}><Typography variant="h4">Dashboard</Typography></Box>;
|
||||
const ProfilePage = () => <Box p={3}><Typography variant="h4">Profile</Typography></Box>;
|
||||
const BackstoryPage = () => <Box p={3}><Typography variant="h4">Backstory</Typography></Box>;
|
||||
const ResumesPage = () => <Box p={3}><Typography variant="h4">Resumes</Typography></Box>;
|
||||
const QASetupPage = () => <Box p={3}><Typography variant="h4">Q&A Setup</Typography></Box>;
|
||||
const AnalyticsPage = () => <Box p={3}><Typography variant="h4">Analytics</Typography></Box>;
|
||||
const SettingsPage = () => <Box p={3}><Typography variant="h4">Settings</Typography></Box>;
|
||||
const SearchPage = () => <Box p={3}><Typography variant="h4">Search</Typography></Box>;
|
||||
const SavedPage = () => <Box p={3}><Typography variant="h4">Saved</Typography></Box>;
|
||||
const JobsPage = () => <Box p={3}><Typography variant="h4">Jobs</Typography></Box>;
|
||||
const CompanyPage = () => <Box p={3}><Typography variant="h4">Company</Typography></Box>;
|
||||
|
||||
// This is a placeholder for your actual user context
|
||||
type UserContext = {
|
||||
user: {
|
||||
type: string;
|
||||
name: string;
|
||||
avatar?: any;
|
||||
};
|
||||
isAuthenticated: boolean;
|
||||
logout: () => void;
|
||||
};
|
||||
|
||||
const useUserContext = () : UserContext => {
|
||||
return {
|
||||
user: {
|
||||
type: 'candidate', // or 'employer'
|
||||
name: 'John Doe',
|
||||
avatar: null,
|
||||
},
|
||||
isAuthenticated: false,
|
||||
logout: () => console.log('Logging out'),
|
||||
};
|
||||
};
|
||||
|
||||
interface BackstoryPageContainerProps {
|
||||
children?: React.ReactNode;
|
||||
sx?: SxProps<Theme>;
|
||||
userContext: UserContext;
|
||||
};
|
||||
|
||||
const BackstoryPageContainer = (props : BackstoryPageContainerProps) => {
|
||||
const { children, sx } = props;
|
||||
return (
|
||||
<Container maxWidth="xl" sx={{ mt: 2, mb: 2, ...sx }}>
|
||||
<Paper
|
||||
elevation={2}
|
||||
sx={{
|
||||
p: 3,
|
||||
backgroundColor: 'background.paper',
|
||||
borderRadius: 2,
|
||||
minHeight: '80vh',
|
||||
}}
|
||||
>
|
||||
<Scrollable>
|
||||
{children}
|
||||
</Scrollable>
|
||||
</Paper>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
const BackstoryApp = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const userContext : UserContext = useUserContext();
|
||||
const { user, isAuthenticated } = userContext;
|
||||
const [navigationLinks, setNavigationLinks] = useState<NavigationLinkType[]>([]);
|
||||
const snackRef = useRef<any>(null);
|
||||
const chatRef = useRef<ConversationHandle>(null);
|
||||
const setSnack = useCallback((message: string, severity?: SeverityType) => {
|
||||
snackRef.current?.setSnack(message, severity);
|
||||
}, [snackRef]);
|
||||
const submitQuery = (query: Query) => {
|
||||
console.log(`handleSubmitChatQuery:`, query, chatRef.current ? ' sending' : 'no handler');
|
||||
chatRef.current?.submitQuery(query);
|
||||
navigate('/chat');
|
||||
};
|
||||
const [page, setPage] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
const currentRoute = location.pathname.split("/")[1] ? `/${location.pathname.split("/")[1]}` : "/";
|
||||
setPage(currentRoute);
|
||||
}, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
setNavigationLinks(getNavigationLinks(user.type, isAuthenticated));
|
||||
}, [user.type, isAuthenticated]);
|
||||
|
||||
// Render appropriate routes based on user type
|
||||
return (
|
||||
<ThemeProvider theme={backstoryTheme}>
|
||||
<Header currentPath={page} navigate={navigate} navigationLinks={navigationLinks} showLogin={false}/>
|
||||
<Box sx={{ display: "flex", minHeight: "72px", height: "72px" }}/>
|
||||
<Scrollable sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: 'background.default',
|
||||
maxHeight: "calc(100vh - 72px)",
|
||||
}}>
|
||||
<BackstoryPageContainer userContext={userContext}>
|
||||
<Routes>
|
||||
<Route path="/chat" element={<ChatPage ref={chatRef} setSnack={setSnack} sessionId={"684a0c7e-e638-4db7-b00d-0558bfefb710"} submitQuery={submitQuery}/>} />
|
||||
<Route path="/about" element={<AboutPage setSnack={setSnack} sessionId={"684a0c7e-e638-4db7-b00d-0558bfefb710"} submitQuery={submitQuery}/>} />
|
||||
<Route path="/about/:subPage" element={<AboutPage setSnack={setSnack} sessionId={"684a0c7e-e638-4db7-b00d-0558bfefb710"} submitQuery={submitQuery}/>} />
|
||||
<Route path="/resume-builder" element={<ResumeBuilderPage setSnack={setSnack} sessionId={"684a0c7e-e638-4db7-b00d-0558bfefb710"} submitQuery={submitQuery}/>} />
|
||||
<Route path="/dashboard" element={<DashboardPage />} />
|
||||
<Route path="/" element={<HomePage/>}/>
|
||||
{/* Candidate-specific routes */}
|
||||
{user.type === 'candidate' && (
|
||||
<>
|
||||
<Route path="/profile" element={<ProfilePage />} />
|
||||
<Route path="/backstory" element={<BackstoryPage />} />
|
||||
<Route path="/resumes" element={<ResumesPage />} />
|
||||
<Route path="/qa-setup" element={<QASetupPage />} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Employer-specific routes */}
|
||||
{user.type === 'employer' && (
|
||||
<>
|
||||
<Route path="/search" element={<SearchPage />} />
|
||||
<Route path="/saved" element={<SavedPage />} />
|
||||
<Route path="/jobs" element={<JobsPage />} />
|
||||
<Route path="/company" element={<CompanyPage />} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Common routes */}
|
||||
<Route path="/analytics" element={<AnalyticsPage />} />
|
||||
<Route path="/settings" element={<SettingsPage />} />
|
||||
|
||||
{/* Redirect to dashboard by default */}
|
||||
<Route path="*" element={<HomePage />} />
|
||||
</Routes>
|
||||
</BackstoryPageContainer>
|
||||
{ location.pathname === "/" && <Footer /> }
|
||||
</Scrollable>
|
||||
<Snack ref={snackRef}/>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<BackstoryApp />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export type {
|
||||
UserContext,
|
||||
NavigationLinkType
|
||||
};
|
||||
|
||||
export {
|
||||
BackstoryApp
|
||||
};
|
123
frontend/src/NewApp/BackstoryTheme.tsx
Normal file
123
frontend/src/NewApp/BackstoryTheme.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
|
||||
const backstoryTheme = createTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#1A2536', // Midnight Blue
|
||||
contrastText: '#D3CDBF', // Warm Gray
|
||||
},
|
||||
secondary: {
|
||||
main: '#4A7A7D', // Dusty Teal
|
||||
contrastText: '#FFFFFF', // White
|
||||
},
|
||||
text: {
|
||||
primary: '#2E2E2E', // Charcoal Black
|
||||
secondary: '#1A2536', // Midnight Blue
|
||||
},
|
||||
background: {
|
||||
default: '#D3CDBF', // Warm Gray
|
||||
paper: '#FFFFFF', // White
|
||||
},
|
||||
action: {
|
||||
active: '#D4A017', // Golden Ochre
|
||||
hover: 'rgba(212, 160, 23, 0.1)', // Golden Ochre with opacity
|
||||
},
|
||||
custom: {
|
||||
highlight: '#D4A017', // Golden Ochre
|
||||
contrast: '#2E2E2E', // Charcoal Black
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: "'Roboto', sans-serif",
|
||||
h1: {
|
||||
fontSize: '2rem',
|
||||
fontWeight: 500,
|
||||
color: '#2E2E2E', // Charcoal Black
|
||||
},
|
||||
h2: {
|
||||
fontSize: '1.75rem',
|
||||
fontWeight: 500,
|
||||
color: '#2E2E2E', // Charcoal Black
|
||||
marginBottom: '1rem',
|
||||
},
|
||||
h3: {
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 500,
|
||||
color: '#2E2E2E', // Charcoal Black
|
||||
marginBottom: '0.75rem',
|
||||
},
|
||||
h4: {
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 500,
|
||||
color: '#2E2E2E', // Charcoal Black
|
||||
marginBottom: '0.5rem',
|
||||
},
|
||||
body1: {
|
||||
fontSize: '1rem',
|
||||
color: '#2E2E2E', // Charcoal Black
|
||||
marginBottom: '0.5rem',
|
||||
},
|
||||
body2: {
|
||||
fontSize: '0.875rem',
|
||||
color: '#2E2E2E', // Charcoal Black
|
||||
},
|
||||
},
|
||||
components: {
|
||||
MuiLink: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: '#4A7A7D', // Dusty Teal (your secondary color)
|
||||
textDecoration: 'none',
|
||||
'&:hover': {
|
||||
color: '#D4A017', // Golden Ochre on hover
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
textTransform: 'none',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(212, 160, 23, 0.2)', // Golden Ochre hover
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAppBar: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: '#1A2536', // Midnight Blue
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: '2rem',
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiList: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: '0.5rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiListItem: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: '4px',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(212, 160, 23, 0.1)', // Golden Ochre with opacity
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { backstoryTheme };
|
151
frontend/src/NewApp/Components/Beta.css
Normal file
151
frontend/src/NewApp/Components/Beta.css
Normal file
@ -0,0 +1,151 @@
|
||||
.beta-clipper.mobile {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.beta-clipper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -5px;
|
||||
width: 100%;
|
||||
height: 72px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
z-index: 1101;
|
||||
cursor: pointer;
|
||||
font-family: 'Roboto';
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.beta-label.mobile::before {
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
.beta-label::before {
|
||||
padding-left: 10px;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.beta-label.mobile {
|
||||
transform: rotate(45deg);
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.beta-label {
|
||||
width: 200px;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: bottom center;
|
||||
font-size: 28px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #d8d8d8;
|
||||
background: rgba(255, 215, 0, 0.5);
|
||||
border: 1px solid gold;
|
||||
padding: 8px;
|
||||
z-index: 11;
|
||||
pointer-events: auto;
|
||||
box-shadow: 0 0 4px rgba(255, 215, 0, 0.6), 0 0 10px rgba(255, 215, 0, 0.4), 0 0 16px rgba(255, 215, 0, 0.2);
|
||||
}
|
||||
|
||||
.beta-label:hover {
|
||||
color: white;
|
||||
animation: glow-pulse 1.2s infinite ease-in-out;
|
||||
}
|
||||
.beta-label.animate {
|
||||
animation: text-pulse 1.5s ease-in-out;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
.particles {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
.particles::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
left: -50%;
|
||||
top: -50%;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.95) 0%, transparent 70%) -15% 110%,
|
||||
radial-gradient(circle, rgba(173, 216, 255, 0.95) 0%, transparent 70%) 83% 120%,
|
||||
radial-gradient(circle, rgba(255, 255, 200, 0.9) 0%, transparent 70%) -8% 95%,
|
||||
radial-gradient(circle, rgba(200, 240, 255, 0.9) 0%, transparent 70%) 115% 105%,
|
||||
radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, transparent 70%) 42% 130%,
|
||||
radial-gradient(circle, rgba(255, 255, 255, 0.95) 0%, transparent 50%) -20% 80%,
|
||||
radial-gradient(circle, rgba(200, 230, 255, 0.9) 0%, transparent 40%) 125% 90%,
|
||||
radial-gradient(circle, rgba(255, 240, 200, 0.85) 0%, transparent 60%) 35% 135%,
|
||||
radial-gradient(circle, rgba(220, 240, 255, 0.9) 0%, transparent 55%) -25% 110%,
|
||||
radial-gradient(circle, rgba(255, 255, 230, 0.9) 0%, transparent 65%) 110% 70%,
|
||||
radial-gradient(circle, rgba(170, 220, 255, 0.85) 0%, transparent 65%) 20% 140%,
|
||||
radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, transparent 60%) -10% 75%,
|
||||
radial-gradient(circle, rgba(255, 250, 220, 0.85) 0%, transparent 75%) 130% 115%,
|
||||
radial-gradient(circle, rgba(210, 235, 255, 0.95) 0%, transparent 80%) 5% 125%,
|
||||
radial-gradient(circle, rgba(255, 255, 255, 0.8) 0%, transparent 75%) -30% 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 10px 10px, 10px 10px, 10px 10px, 10px 10px, 10px 10px, 5px 5px, 6px 6px, 8px 8px, 7px 7px, 11px 11px,
|
||||
10px 10px, 9px 9px, 14px 14px, 15px 15px, 12px 12px;
|
||||
filter: blur(0.5px);
|
||||
opacity: 0;
|
||||
animation-name: none;
|
||||
}
|
||||
.beta-label.animate .particles::before {
|
||||
animation-name: beam-sparkles;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes glow-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 6px rgba(255, 215, 0, 0.5);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 12px rgba(255, 215, 0, 0.8);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 6px rgba(255, 215, 0, 0.5);
|
||||
}
|
||||
}
|
||||
@keyframes text-pulse {
|
||||
0% {
|
||||
color: #d8d8d8;
|
||||
}
|
||||
50% {
|
||||
color: gold;
|
||||
}
|
||||
100% {
|
||||
color: #d8d8d8;
|
||||
}
|
||||
}
|
||||
@keyframes beam-sparkles {
|
||||
0% {
|
||||
opacity: 1;
|
||||
background-position: -15% 110%, 83% 120%, -8% 95%, 115% 105%, 42% 130%, -20% 80%, 125% 90%, 35% 135%, -25% 110%,
|
||||
110% 70%, 20% 140%, -10% 75%, 130% 115%, 5% 125%, -30% 100%;
|
||||
}
|
||||
25% {
|
||||
background-position: 0% 90%, 75% 95%, 5% 85%, 100% 85%, 45% 100%, -5% 70%, 105% 75%, 40% 110%, -5% 90%, 95% 65%,
|
||||
25% 115%, 5% 70%, 110% 95%, 15% 100%, -10% 85%;
|
||||
}
|
||||
50% {
|
||||
background-position: 20% 70%, 65% 70%, 30% 70%, 80% 65%, 48% 75%, 20% 60%, 80% 60%, 45% 80%, 25% 70%, 75% 60%,
|
||||
40% 85%, 30% 65%, 85% 70%, 35% 75%, 15% 70%;
|
||||
}
|
||||
75% {
|
||||
background-position: 40% 55%, 55% 55%, 45% 60%, 65% 55%, 49% 60%, 40% 52%, 60% 52%, 48% 60%, 42% 55%, 58% 52%,
|
||||
45% 62%, 43% 53%, 62% 57%, 47% 58%, 38% 55%;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
background-position: 50% 40%, 50% 42%, 50% 38%, 50% 45%, 50% 41%, 50% 35%, 50% 43%, 50% 39%, 50% 44%, 50% 37%,
|
||||
50% 46%, 50% 36%, 50% 47%, 50% 40%, 50% 38%;
|
||||
}
|
||||
}
|
50
frontend/src/NewApp/Components/Beta.tsx
Normal file
50
frontend/src/NewApp/Components/Beta.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
|
||||
import './Beta.css';
|
||||
|
||||
type BetaProps = {
|
||||
onClick?: (event?: React.MouseEvent<HTMLElement>) => void;
|
||||
}
|
||||
|
||||
const Beta: React.FC<BetaProps> = (props : BetaProps) => {
|
||||
const { onClick } = props;
|
||||
const betaRef = useRef<HTMLElement | null>(null);
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const [animationKey, setAnimationKey] = useState<number>(0);
|
||||
const [firstPass, setFirstPass] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Initial animation trigger
|
||||
if (firstPass && betaRef.current) {
|
||||
triggerAnimation();
|
||||
setFirstPass(false);
|
||||
}
|
||||
}, [firstPass]);
|
||||
|
||||
const triggerAnimation = (): void => {
|
||||
if (!betaRef.current) return;
|
||||
|
||||
// Increment animation key to force React to recreate the element
|
||||
setAnimationKey(prevKey => prevKey + 1);
|
||||
|
||||
// Ensure the animate class is present
|
||||
betaRef.current.classList.add('animate');
|
||||
};
|
||||
|
||||
return (
|
||||
<Box className={`beta-clipper ${isMobile && "mobile"}`} onClick={(e) => { onClick && onClick(e);}}>
|
||||
<Box ref={betaRef} className={`beta-label ${isMobile && "mobile"}`}>
|
||||
<Box key={animationKey} className="particles"></Box>
|
||||
<Box>BETA</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
Beta
|
||||
};
|
253
frontend/src/NewApp/Components/Footer.tsx
Normal file
253
frontend/src/NewApp/Components/Footer.tsx
Normal file
@ -0,0 +1,253 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Paper,
|
||||
Box,
|
||||
Container,
|
||||
Grid,
|
||||
Typography,
|
||||
Link,
|
||||
Divider,
|
||||
IconButton,
|
||||
Stack,
|
||||
useMediaQuery,
|
||||
} from '@mui/material';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
Facebook,
|
||||
Twitter,
|
||||
LinkedIn,
|
||||
Instagram,
|
||||
YouTube,
|
||||
Email,
|
||||
LocationOn,
|
||||
Copyright,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
// Styled components
|
||||
const FooterContainer = styled(Paper)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
padding: theme.spacing(6, 0, 4),
|
||||
marginTop: theme.spacing(4),
|
||||
borderRadius: 0,
|
||||
}));
|
||||
|
||||
const FooterLink = styled(Link)(({ theme }) => ({
|
||||
color: theme.palette.primary.contrastText,
|
||||
textDecoration: 'none',
|
||||
'&:hover': {
|
||||
textDecoration: 'underline',
|
||||
color: theme.palette.action.active,
|
||||
},
|
||||
display: 'block',
|
||||
marginBottom: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const FooterHeading = styled(Typography)(({ theme }) => ({
|
||||
fontWeight: 600,
|
||||
marginBottom: theme.spacing(2),
|
||||
color: theme.palette.primary.contrastText,
|
||||
}));
|
||||
|
||||
const ContactItem = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing(1.5),
|
||||
}));
|
||||
|
||||
// Footer component
|
||||
const Footer = () => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<FooterContainer elevation={0}>
|
||||
<Container maxWidth="lg">
|
||||
<Grid container spacing={4} justifyContent="space-between">
|
||||
{/* About Company */}
|
||||
<Grid size={{ xs: 12, md: 3 }}>
|
||||
<Box mb={3}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
component="div"
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
letterSpacing: '.2rem',
|
||||
marginBottom: 2,
|
||||
}}
|
||||
>
|
||||
BACKSTORY
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ mb: 2 }}>
|
||||
Helping candidates share their professional journey and connect with the right employers through compelling backstories.
|
||||
</Typography>
|
||||
<Stack direction="row">
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="Facebook"
|
||||
sx={{
|
||||
color: theme.palette.primary.contrastText,
|
||||
mr: 1,
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(211, 205, 191, 0.1)',
|
||||
color: theme.palette.action.active,
|
||||
}
|
||||
}}
|
||||
onClick={() => window.open('https://facebook.com/', '_blank')}
|
||||
>
|
||||
<Facebook />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="Twitter"
|
||||
sx={{
|
||||
color: theme.palette.primary.contrastText,
|
||||
mr: 1,
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(211, 205, 191, 0.1)',
|
||||
color: theme.palette.action.active,
|
||||
}
|
||||
}}
|
||||
onClick={() => window.open('https://twitter.com/', '_blank')}
|
||||
>
|
||||
<Twitter />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="LinkedIn"
|
||||
sx={{
|
||||
color: theme.palette.primary.contrastText,
|
||||
mr: 1,
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(211, 205, 191, 0.1)',
|
||||
color: theme.palette.action.active,
|
||||
}
|
||||
}}
|
||||
onClick={() => window.open('https://linkedin.com/', '_blank')}
|
||||
>
|
||||
<LinkedIn />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="Instagram"
|
||||
sx={{
|
||||
color: theme.palette.primary.contrastText,
|
||||
mr: 1,
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(211, 205, 191, 0.1)',
|
||||
color: theme.palette.action.active,
|
||||
}
|
||||
}}
|
||||
onClick={() => window.open('https://instagram.com/', '_blank')}
|
||||
>
|
||||
<Instagram />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="YouTube"
|
||||
sx={{
|
||||
color: theme.palette.primary.contrastText,
|
||||
mr: 1,
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(211, 205, 191, 0.1)',
|
||||
color: theme.palette.action.active,
|
||||
}
|
||||
}}
|
||||
onClick={() => window.open('https://youtube.com/', '_blank')}
|
||||
>
|
||||
<YouTube />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* Quick Links */}
|
||||
<Grid size={{ xs: 12, sm: 6, md: 2 }}>
|
||||
<FooterHeading variant="subtitle1">
|
||||
For Candidates
|
||||
</FooterHeading>
|
||||
<FooterLink href="/create-profile">Create Profile</FooterLink>
|
||||
<FooterLink href="/backstory-editor">Backstory Editor</FooterLink>
|
||||
<FooterLink href="/resume-builder">Resume Builder</FooterLink>
|
||||
<FooterLink href="/career-resources">Career Resources</FooterLink>
|
||||
<FooterLink href="/interview-tips">Interview Tips</FooterLink>
|
||||
</Grid>
|
||||
|
||||
{/* Quick Links */}
|
||||
<Grid size={{ xs: 12, sm: 6, md: 2 }}>
|
||||
<FooterHeading variant="subtitle1">
|
||||
For Employers
|
||||
</FooterHeading>
|
||||
<FooterLink href="/post-job">Post a Job</FooterLink>
|
||||
<FooterLink href="/search-candidates">Search Candidates</FooterLink>
|
||||
<FooterLink href="/company-profile">Company Profile</FooterLink>
|
||||
<FooterLink href="/recruiting-tools">Recruiting Tools</FooterLink>
|
||||
<FooterLink href="/pricing-plans">Pricing Plans</FooterLink>
|
||||
</Grid>
|
||||
|
||||
{/* Contact */}
|
||||
<Grid size={{ xs: 12, sm: 6, md: 2 }}>
|
||||
<FooterHeading variant="subtitle1">
|
||||
Company
|
||||
</FooterHeading>
|
||||
<FooterLink href="/about-us">About Us</FooterLink>
|
||||
<FooterLink href="/our-team">Our Team</FooterLink>
|
||||
<FooterLink href="/blog">Blog</FooterLink>
|
||||
<FooterLink href="/press">Press</FooterLink>
|
||||
<FooterLink href="/careers">Careers</FooterLink>
|
||||
<FooterLink href="/contact-us">Contact Us</FooterLink>
|
||||
</Grid>
|
||||
|
||||
{/* Newsletter */}
|
||||
<Grid size={{ xs: 12, sm: 6, md: 3 }}>
|
||||
<ContactItem>
|
||||
<Email sx={{ mr: 1, fontSize: 20 }} />
|
||||
<FooterLink href="mailto:james_backstory@backstory.ketrenos.com">Email</FooterLink>
|
||||
</ContactItem>
|
||||
{/* <ContactItem>
|
||||
<Phone sx={{ mr: 1, fontSize: 20 }} />
|
||||
<FooterLink href="tel:+18005551234">+1 (800) 555-1234</FooterLink>
|
||||
</ContactItem> */}
|
||||
<ContactItem>
|
||||
<LocationOn sx={{ mr: 1, fontSize: 20 }} />
|
||||
<Typography variant="body2">
|
||||
Beaverton, OR 97003
|
||||
</Typography>
|
||||
</ContactItem>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Divider sx={{ my: 4, backgroundColor: 'rgba(211, 205, 191, 0.2)' }} />
|
||||
|
||||
{/* Bottom Footer */}
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Copyright sx={{ fontSize: 16, mr: 1 }} />
|
||||
<Typography variant="body2">
|
||||
{currentYear} James P. Ketrenos. All rights reserved.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Stack
|
||||
direction={isMobile ? 'column' : 'row'}
|
||||
spacing={isMobile ? 1 : 3}
|
||||
sx={{ textAlign: { xs: 'left', md: 'right' } }}
|
||||
>
|
||||
<FooterLink href="/terms" sx={{ mb: 0 }}>Terms of Service</FooterLink>
|
||||
<FooterLink href="/privacy" sx={{ mb: 0 }}>Privacy Policy</FooterLink>
|
||||
<FooterLink href="/accessibility" sx={{ mb: 0 }}>Accessibility</FooterLink>
|
||||
<FooterLink href="/sitemap" sx={{ mb: 0 }}>Sitemap</FooterLink>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</FooterContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
Footer
|
||||
};
|
3
frontend/src/NewApp/Components/Header.css
Normal file
3
frontend/src/NewApp/Components/Header.css
Normal file
@ -0,0 +1,3 @@
|
||||
.BackstoryLogo {
|
||||
margin: 0px !important;
|
||||
}
|
426
frontend/src/NewApp/Components/Header.tsx
Normal file
426
frontend/src/NewApp/Components/Header.tsx
Normal file
@ -0,0 +1,426 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { NavigateFunction, useLocation } from 'react-router-dom';
|
||||
import {
|
||||
AppBar,
|
||||
Toolbar,
|
||||
Typography,
|
||||
Button,
|
||||
IconButton,
|
||||
Box,
|
||||
Menu,
|
||||
MenuItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Drawer,
|
||||
Divider,
|
||||
Avatar,
|
||||
Tabs,
|
||||
Tab,
|
||||
Container,
|
||||
Fade,
|
||||
} from '@mui/material';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
Menu as MenuIcon,
|
||||
Dashboard,
|
||||
Person,
|
||||
Logout,
|
||||
Settings,
|
||||
ExpandMore,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import { NavigationLinkType } from '../BackstoryApp';
|
||||
import { Beta } from './Beta';
|
||||
import './Header.css';
|
||||
|
||||
// Interface for component props
|
||||
interface HeaderProps {
|
||||
isLoggedIn?: boolean;
|
||||
userType?: 'candidate' | 'employer' | null;
|
||||
userName?: string;
|
||||
transparent?: boolean;
|
||||
onLogout?: () => void;
|
||||
className?: string;
|
||||
navigate: NavigateFunction;
|
||||
navigationLinks: NavigationLinkType[];
|
||||
showLogin?: boolean;
|
||||
currentPath: string;
|
||||
}
|
||||
|
||||
// Styled components
|
||||
const StyledAppBar = styled(AppBar, {
|
||||
shouldForwardProp: (prop) => prop !== 'transparent',
|
||||
})<{ transparent?: boolean }>(({ theme, transparent }) => ({
|
||||
backgroundColor: transparent ? 'transparent' : theme.palette.primary.main,
|
||||
boxShadow: transparent ? 'none' : '',
|
||||
transition: 'background-color 0.3s ease',
|
||||
}));
|
||||
|
||||
const NavLinksContainer = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flex: 1,
|
||||
[theme.breakpoints.down('md')]: {
|
||||
display: 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
const UserActionsContainer = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const UserButton = styled(Button)(({ theme }) => ({
|
||||
color: theme.palette.primary.contrastText,
|
||||
textTransform: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
padding: theme.spacing(0.5, 1.5),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
},
|
||||
}));
|
||||
|
||||
const MobileDrawer = styled(Drawer)(({ theme }) => ({
|
||||
'& .MuiDrawer-paper': {
|
||||
width: 280,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
}));
|
||||
|
||||
const Header: React.FC<HeaderProps> = ({
|
||||
isLoggedIn = false,
|
||||
userName = '',
|
||||
transparent = false,
|
||||
onLogout,
|
||||
className,
|
||||
navigate,
|
||||
navigationLinks,
|
||||
showLogin,
|
||||
currentPath,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const location = useLocation();
|
||||
|
||||
const BackstoryLogo = () => {
|
||||
return <Typography
|
||||
variant="h6"
|
||||
className="BackstoryLogo"
|
||||
noWrap
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
fontWeight: 700,
|
||||
letterSpacing: '.2rem',
|
||||
color: theme.palette.primary.contrastText,
|
||||
textDecoration: 'none',
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 1,
|
||||
textTransform: "uppercase",
|
||||
}}
|
||||
>
|
||||
<Avatar sx={{ width: 24, height: 24 }}
|
||||
variant="rounded"
|
||||
alt="Backstory logo"
|
||||
src="/logo192.png" />
|
||||
Backstory
|
||||
</Typography>
|
||||
};
|
||||
|
||||
const navLinks : NavigationLinkType[] = [
|
||||
{name: "Home", path: "/", label: <BackstoryLogo/>},
|
||||
...navigationLinks
|
||||
];
|
||||
// State for page navigation
|
||||
const [ currentTab, setCurrentTab ] = useState<string>("/");
|
||||
|
||||
// State for mobile drawer
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
|
||||
// State for user menu
|
||||
const [userMenuAnchor, setUserMenuAnchor] = useState<null | HTMLElement>(null);
|
||||
const userMenuOpen = Boolean(userMenuAnchor);
|
||||
|
||||
useEffect(() => {
|
||||
const parts = location.pathname.split('/');
|
||||
console.log(location.pathname);
|
||||
let tab = '/';
|
||||
if (parts.length > 1) {
|
||||
tab = `/${parts[1]}`;
|
||||
}
|
||||
if (tab !== currentTab) {
|
||||
console.log(`Setting tab to ${tab}`);
|
||||
setCurrentTab(tab);
|
||||
} else {
|
||||
console.log(`Not setting tab to ${tab}`);
|
||||
}
|
||||
}, [location, currentTab]);
|
||||
|
||||
const handleUserMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setUserMenuAnchor(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleUserMenuClose = () => {
|
||||
setUserMenuAnchor(null);
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
handleUserMenuClose();
|
||||
if (onLogout) {
|
||||
onLogout();
|
||||
}
|
||||
};
|
||||
|
||||
const handleDrawerToggle = () => {
|
||||
setMobileOpen(!mobileOpen);
|
||||
};
|
||||
|
||||
// Render desktop navigation links
|
||||
const renderNavLinks = () => {
|
||||
return (
|
||||
<Tabs value={currentTab} onChange={(e, newValue) => setCurrentTab(newValue)}
|
||||
indicatorColor="secondary"
|
||||
textColor="inherit"
|
||||
variant="fullWidth"
|
||||
allowScrollButtonsMobile
|
||||
aria-label="Backstory navigation"
|
||||
>
|
||||
{navLinks.map((link) => (
|
||||
<Tab
|
||||
sx={{
|
||||
minWidth: link.path === '/' ? "max-content" : "auto",
|
||||
}}
|
||||
key={link.name}
|
||||
value={link.path}
|
||||
label={link.label ? link.label : link.name}
|
||||
onClick={() => {
|
||||
navigate(link.path);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
// Render mobile drawer content
|
||||
const renderDrawerContent = () => {
|
||||
return (
|
||||
<>
|
||||
<Tabs
|
||||
orientation="vertical"
|
||||
value={currentTab} >
|
||||
{navLinks.map((link) => (
|
||||
<Tab
|
||||
key={link.name}
|
||||
value={link.path}
|
||||
label={
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
{link.icon && <Box sx={{ mr: 1 }}>{link.icon}</Box>}
|
||||
{link.name}
|
||||
</Box>
|
||||
}
|
||||
onClick={(e) => { handleDrawerToggle() ; setCurrentTab(link.path); navigate(link.path);} }
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
<Divider />
|
||||
{!isLoggedIn && (showLogin === undefined || showLogin !== false) && (
|
||||
<Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
fullWidth
|
||||
onClick={() => navigate("/login") }
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
fullWidth
|
||||
onClick={() => navigate("/register") }
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// Render user account section
|
||||
const renderUserSection = () => {
|
||||
if (showLogin !== undefined && showLogin === false) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
color="info"
|
||||
variant="contained"
|
||||
onClick={() => navigate("/login") }
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'block' },
|
||||
color: theme.palette.primary.contrastText,
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
onClick={() => navigate("/navigate") }
|
||||
sx={{ display: { xs: 'none', sm: 'block' } }}
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<UserButton
|
||||
onClick={handleUserMenuOpen}
|
||||
aria-controls={userMenuOpen ? 'user-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={userMenuOpen ? 'true' : undefined}
|
||||
>
|
||||
<Avatar sx={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
bgcolor: theme.palette.secondary.main,
|
||||
}}>
|
||||
{userName.charAt(0).toUpperCase()}
|
||||
</Avatar>
|
||||
<Box sx={{ display: { xs: 'none', sm: 'block' } }}>
|
||||
{userName}
|
||||
</Box>
|
||||
<ExpandMore fontSize="small" />
|
||||
</UserButton>
|
||||
|
||||
<Menu
|
||||
id="user-menu"
|
||||
anchorEl={userMenuAnchor}
|
||||
open={userMenuOpen}
|
||||
onClose={handleUserMenuClose}
|
||||
slots={{
|
||||
transition: Fade,
|
||||
}}
|
||||
slotProps={{
|
||||
list: {
|
||||
'aria-labelledby': 'user-button',
|
||||
sx: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column', // Adjusted for menu items
|
||||
alignItems: 'center',
|
||||
gap: '1rem',
|
||||
textTransform: 'uppercase', // All caps as requested
|
||||
},
|
||||
},
|
||||
paper: {
|
||||
sx: {
|
||||
minWidth: 200, // Optional: ensures reasonable menu width
|
||||
},
|
||||
},
|
||||
}}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleUserMenuClose} component="a" href="/profile">
|
||||
<ListItemIcon>
|
||||
<Person fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>Profile</ListItemText>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleUserMenuClose} component="a" href="/dashboard">
|
||||
<ListItemIcon>
|
||||
<Dashboard fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>Dashboard</ListItemText>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleUserMenuClose} component="a" href="/settings">
|
||||
<ListItemIcon>
|
||||
<Settings fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>Settings</ListItemText>
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={handleLogout}>
|
||||
<ListItemIcon>
|
||||
<Logout fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>Logout</ListItemText>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<StyledAppBar
|
||||
position="fixed"
|
||||
transparent={transparent}
|
||||
className={className}
|
||||
>
|
||||
<Container maxWidth="xl">
|
||||
<Toolbar disableGutters>
|
||||
{/* Logo Section */}
|
||||
|
||||
{/* Navigation Links - Desktop */}
|
||||
<NavLinksContainer>
|
||||
{renderNavLinks()}
|
||||
</NavLinksContainer>
|
||||
|
||||
{/* User Actions Section */}
|
||||
<UserActionsContainer>
|
||||
{renderUserSection()}
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="open drawer"
|
||||
edge="end"
|
||||
onClick={handleDrawerToggle}
|
||||
sx={{ display: { md: 'none' } }}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
</UserActionsContainer>
|
||||
|
||||
{/* Mobile Navigation Drawer */}
|
||||
<MobileDrawer
|
||||
variant="temporary"
|
||||
anchor="right"
|
||||
open={mobileOpen}
|
||||
onClose={handleDrawerToggle}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile
|
||||
}}
|
||||
>
|
||||
{renderDrawerContent()}
|
||||
</MobileDrawer>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</StyledAppBar>
|
||||
<Beta onClick={()=>{navigate('/about/beta');}}/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
Header
|
||||
};
|
125
frontend/src/NewApp/Pages/AboutPage.tsx
Normal file
125
frontend/src/NewApp/Pages/AboutPage.tsx
Normal file
@ -0,0 +1,125 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {useNavigate, useLocation, useParams } from 'react-router-dom';
|
||||
|
||||
import { Box } from '@mui/material';
|
||||
|
||||
import { BackstoryPageProps } from '../../Components/BackstoryTab';
|
||||
import { Document } from '../../Components/Document';
|
||||
import { BackstoryUIOverviewPage} from './BackstoryUIOverviewPage';
|
||||
import { BackstoryAppAnalysisPage } from './BackstoryAppAnalysisPage';
|
||||
import { BackstoryThemeVisualizerPage } from './BackstoryThemeVisualizerPage';
|
||||
import { MockupPage } from './MockupPage';
|
||||
|
||||
const AboutPage = (props: BackstoryPageProps) => {
|
||||
const { sessionId, submitQuery, setSnack } = props;
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { paramPage = '' } = useParams();
|
||||
const [page, setPage] = useState<string>(paramPage);
|
||||
|
||||
/* If the location changes, set the page based on the
|
||||
* second part of the path, or clear if no path */
|
||||
useEffect(() => {
|
||||
const parts = location.pathname.split('/');
|
||||
if (parts.length > 2) {
|
||||
setPage(parts[2]);
|
||||
} else {
|
||||
setPage('');
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
const onDocumentExpand = (docName: string, open: boolean) => {
|
||||
console.log("Document expanded:", { docName, open, location });
|
||||
if (open) {
|
||||
const parts = location.pathname.split('/');
|
||||
if (parts.length > 2) {
|
||||
const basePath = parts.slice(0, -1).join('/');
|
||||
navigate(`${basePath}/${docName}`);
|
||||
} else {
|
||||
navigate(docName);
|
||||
}
|
||||
} else {
|
||||
const basePath = location.pathname.split('/').slice(0, -1).join('/');
|
||||
navigate(`${basePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
return (<Box sx={{gap: 1}}>
|
||||
<Document {...{
|
||||
title: "About",
|
||||
filepath: "/docs/about.md",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('about', open); },
|
||||
expanded: page === 'about',
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "BETA",
|
||||
filepath: "/docs/beta.md",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('beta', open); },
|
||||
expanded: page === 'beta',
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "Resume Generation Architecture",
|
||||
filepath: "/docs/resume-generation.md",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('resume-generation', open); },
|
||||
expanded: page === 'resume-generation',
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "Application Architecture",
|
||||
filepath: "/docs/about-app.md",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('about-app', open); },
|
||||
expanded: page === 'about-app',
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "UI Overview",
|
||||
children: <BackstoryUIOverviewPage/>,
|
||||
onExpand: (open: boolean) => { onDocumentExpand('ui-overview', open); },
|
||||
expanded: page === 'ui-overview',
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "Theme Visualizer",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('theme-visualizer', open); },
|
||||
expanded: page === 'theme-visualizer',
|
||||
children: <BackstoryThemeVisualizerPage/>,
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "App Analysis",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('app-analysis', open); },
|
||||
expanded: page === 'app-analysis',
|
||||
children: <BackstoryAppAnalysisPage/>,
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "UI Mockup",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('ui-mockup', open); },
|
||||
expanded: page === 'ui-mockup',
|
||||
children: <MockupPage />,
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
</Box>)
|
||||
};
|
||||
|
||||
export {
|
||||
AboutPage
|
||||
};
|
312
frontend/src/NewApp/Pages/BackstoryAppAnalysisPage.tsx
Normal file
312
frontend/src/NewApp/Pages/BackstoryAppAnalysisPage.tsx
Normal file
@ -0,0 +1,312 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { Box, Typography, Paper, Container } from '@mui/material';
|
||||
|
||||
// Import the backstoryTheme
|
||||
|
||||
// BackstoryAnalysisDisplay component
|
||||
const BackstoryAppAnalysisPage = () => {
|
||||
return (
|
||||
<Box sx={{ backgroundColor: 'background.default', minHeight: '100vh', py: 4 }}>
|
||||
<Container maxWidth="lg">
|
||||
<Paper sx={{ p: 4, boxShadow: 2 }}>
|
||||
<Typography variant="h1" component="h1" sx={{ mb: 3, color: 'primary.main' }}>
|
||||
Backstory Application Analysis
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h2" component="h2">
|
||||
Core Concept
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
Backstory is a dual-purpose platform designed to bridge the gap between job candidates and
|
||||
employers/recruiters with an AI-powered approach to professional profiles and resume generation.
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h3" component="h3" sx={{ mt: 3 }}>
|
||||
Primary User Types
|
||||
</Typography>
|
||||
<Box component="ol" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Job Candidates</strong> - Upload and manage comprehensive professional histories
|
||||
and generate tailored resumes for specific positions
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Employers/Recruiters</strong> - Search for candidates, directly interact with AI
|
||||
assistants about candidate experiences, and generate position-specific resumes
|
||||
</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h2" component="h2" sx={{ mt: 4 }}>
|
||||
Key Features
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h3" component="h3" sx={{ mt: 3 }}>
|
||||
For Candidates
|
||||
</Typography>
|
||||
<Box component="ul" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Complete Profile Management</strong> - Create detailed professional histories beyond typical resume constraints
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>AI-Assisted Q&A Setup</strong> - Configure an AI assistant to answer employer questions about your experience
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Smart Resume Generator</strong> - Create tailored resumes for specific positions using AI
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Analytics Dashboard</strong> - Track profile views, resume downloads, and employer engagement
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Privacy Controls</strong> - Manage visibility and access to your professional information
|
||||
</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h3" component="h3" sx={{ mt: 3 }}>
|
||||
For Employers
|
||||
</Typography>
|
||||
<Box component="ul" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Advanced Candidate Search</strong> - Find candidates with specific skills, experience levels, and qualifications
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Interactive Q&A</strong> - Ask questions directly to candidate AI assistants to learn more about their experience
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Resume Generation</strong> - Generate candidate resumes tailored to specific job requirements
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Talent Pool Management</strong> - Create and manage groups of candidates for different positions
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Job Posting Management</strong> - Create, manage, and track applications for job postings
|
||||
</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h2" component="h2" sx={{ mt: 4 }}>
|
||||
Design Analysis
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h3" component="h3" sx={{ mt: 3 }}>
|
||||
Navigation Structure
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
The application uses a role-based navigation system with:
|
||||
</Typography>
|
||||
<Box component="ul" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Public Navigation</strong> - Home, About, Pricing, Login/Register accessible to all users
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Candidate Dashboard Navigation</strong> - Profile, Backstory, Resumes, Q&A Setup, Analytics, Settings
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Employer Dashboard Navigation</strong> - Dashboard, Search, Saved, Jobs, Company, Analytics, Settings
|
||||
</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h3" component="h3" sx={{ mt: 3 }}>
|
||||
UI Patterns
|
||||
</Typography>
|
||||
<Box component="ol" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Dashboard Cards</strong> - Both user types have dashboards with card-based information displays
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Tab-Based Content Organization</strong> - Many screens use horizontal tabs to organize related content
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Form-Based Editors</strong> - Profile and content editors use structured forms with varied input types
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Three-Column Layouts</strong> - Many screens follow a left sidebar, main content, right sidebar pattern
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Preview/Edit Toggle</strong> - Resume and profile editing screens offer both editing and preview modes
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Filter-Based Search</strong> - Employer search uses multiple filter categories to refine candidate results
|
||||
</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h3" component="h3" sx={{ mt: 3 }}>
|
||||
Mobile Adaptations
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
The mobile designs show a simplified navigation structure with bottom tabs and a hamburger menu,
|
||||
maintaining the core functionality while adapting to smaller screens.
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h2" component="h2" sx={{ mt: 4 }}>
|
||||
Technology Integration
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h3" component="h3" sx={{ mt: 3 }}>
|
||||
AI Components
|
||||
</Typography>
|
||||
<Box component="ul" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>LLM Integration</strong> - Supports multiple AI models (Claude, GPT-4, self-hosted models)
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Candidate AI Assistant</strong> - Personalized AI chatbot that answers questions about candidate experience
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Resume Generation</strong> - AI-powered resume creation based on job requirements
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Skills Matching</strong> - Automated matching between candidate skills and job requirements
|
||||
</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h3" component="h3" sx={{ mt: 3 }}>
|
||||
External Integrations
|
||||
</Typography>
|
||||
<Box component="ul" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Authentication</strong> - OAuth with Google, LinkedIn, GitHub
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Data Import</strong> - LinkedIn profile import, resume parsing (PDF, DOCX), CSV/JSON import
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>ATS Compatibility</strong> - Integration with employer Applicant Tracking Systems
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Vector Databases</strong> - Semantic search capabilities for candidate matching
|
||||
</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h2" component="h2" sx={{ mt: 4 }}>
|
||||
Key Differentiators
|
||||
</Typography>
|
||||
<Box component="ol" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Beyond the Resume</strong> - Focuses on comprehensive professional stories rather than just resume highlights
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>AI-Mediated Communication</strong> - Uses AI to facilitate deeper understanding of candidate experiences
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Bidirectional Resume Generation</strong> - Both candidates and employers can generate tailored resumes
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Configurable AI Personalities</strong> - Candidates can customize how their AI assistant responds to questions
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1" component="div">
|
||||
<strong>Deep Analytics</strong> - Both candidates and employers receive insights about their engagement
|
||||
</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h2" component="h2" sx={{ mt: 4 }}>
|
||||
Deployment Options
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
The system supports multiple deployment architectures:
|
||||
</Typography>
|
||||
<Box component="ul" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1">Self-hosted (on-premises)</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1">Cloud-hosted (SaaS model)</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1">Hybrid deployment (mixed cloud/on-premises)</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h2" component="h2" sx={{ mt: 4 }}>
|
||||
Security and Privacy Considerations
|
||||
</Typography>
|
||||
<Box component="ul" sx={{ pl: 4 }}>
|
||||
<li>
|
||||
<Typography variant="body1">Granular candidate privacy controls</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1">Role-based access for employer teams</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body1">Data management options for compliance requirements</Typography>
|
||||
</li>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export {
|
||||
BackstoryAppAnalysisPage
|
||||
}
|
196
frontend/src/NewApp/Pages/BackstoryThemeVisualizerPage.tsx
Normal file
196
frontend/src/NewApp/Pages/BackstoryThemeVisualizerPage.tsx
Normal file
@ -0,0 +1,196 @@
|
||||
import React from 'react';
|
||||
import { backstoryTheme } from '../BackstoryTheme';
|
||||
|
||||
// This component provides a visual demonstration of the theme colors
|
||||
const BackstoryThemeVisualizerPage = () => {
|
||||
const colorSwatch = (color: string, name: string, textColor = '#fff') => (
|
||||
<div className="flex flex-col items-center">
|
||||
<div
|
||||
className="w-20 h-20 rounded-lg shadow-md flex items-center justify-center mb-2"
|
||||
style={{ backgroundColor: color, color: textColor }}>
|
||||
{name}
|
||||
</div>
|
||||
<span className="text-xs">{color}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="p-8">
|
||||
<h1 className="text-2xl font-bold mb-6" style={{ color: backstoryTheme.palette.text.primary }}>
|
||||
Backstory Theme Visualization
|
||||
</h1>
|
||||
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl mb-4" style={{ color: backstoryTheme.palette.text.primary }}>
|
||||
Primary Colors
|
||||
</h2>
|
||||
<div className="flex space-x-4">
|
||||
{colorSwatch(backstoryTheme.palette.primary.main, 'Primary', backstoryTheme.palette.primary.contrastText)}
|
||||
{colorSwatch(backstoryTheme.palette.secondary.main, 'Secondary', backstoryTheme.palette.secondary.contrastText)}
|
||||
{colorSwatch(backstoryTheme.palette.custom.highlight, 'Highlight', '#fff')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl mb-4" style={{ color: backstoryTheme.palette.text.primary }}>
|
||||
Background Colors
|
||||
</h2>
|
||||
<div className="flex space-x-4">
|
||||
{colorSwatch(backstoryTheme.palette.background.default, 'Default', '#000')}
|
||||
{colorSwatch(backstoryTheme.palette.background.paper, 'Paper', '#000')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl mb-4" style={{ color: backstoryTheme.palette.text.primary }}>
|
||||
Text Colors
|
||||
</h2>
|
||||
<div className="flex space-x-4">
|
||||
{colorSwatch(backstoryTheme.palette.text.primary, 'Primary', '#fff')}
|
||||
{colorSwatch(backstoryTheme.palette.text.secondary, 'Secondary', '#fff')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-8 border p-6 rounded-lg" style={{ backgroundColor: backstoryTheme.palette.background.paper }}>
|
||||
<h2 className="text-xl mb-4" style={{ color: backstoryTheme.palette.text.primary }}>
|
||||
Typography Examples
|
||||
</h2>
|
||||
|
||||
<div className="mb-4">
|
||||
<h1 style={{
|
||||
fontFamily: backstoryTheme.typography.fontFamily,
|
||||
fontSize: backstoryTheme.typography.h1.fontSize,
|
||||
fontWeight: backstoryTheme.typography.h1.fontWeight,
|
||||
color: backstoryTheme.typography.h1.color,
|
||||
}}>
|
||||
Heading 1 - Backstory Application
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<p style={{
|
||||
fontFamily: backstoryTheme.typography.fontFamily,
|
||||
fontSize: backstoryTheme.typography.body1.fontSize,
|
||||
color: backstoryTheme.typography.body1.color,
|
||||
}}>
|
||||
Body Text - This is how the regular text content will appear in the Backstory application.
|
||||
The application uses Roboto as its primary font family, with carefully selected sizing and colors.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* <div className="mt-6">
|
||||
<a href="#" style={{
|
||||
color: backstoryTheme.components?.MuiLink?.styleOverrides.root.color || "inherit",
|
||||
textDecoration: backstoryTheme.components.MuiLink.styleOverrides.root.textDecoration,
|
||||
}}>
|
||||
This is how links will appear by default
|
||||
</a>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl mb-4" style={{ color: backstoryTheme.palette.text.primary }}>
|
||||
UI Component Examples
|
||||
</h2>
|
||||
|
||||
<div className="p-4 mb-4 rounded-lg" style={{ backgroundColor: backstoryTheme.palette.background.paper }}>
|
||||
<div className="p-2 mb-4 rounded" style={{ backgroundColor: backstoryTheme.palette.primary.main }}>
|
||||
<span style={{ color: backstoryTheme.palette.primary.contrastText }}>
|
||||
AppBar Background
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
padding: '8px 16px',
|
||||
backgroundColor: backstoryTheme.palette.primary.main,
|
||||
color: backstoryTheme.palette.primary.contrastText,
|
||||
display: 'inline-block',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontFamily: backstoryTheme.typography.fontFamily,
|
||||
}}>
|
||||
Primary Button
|
||||
</div>
|
||||
|
||||
<div className="mt-4" style={{
|
||||
padding: '8px 16px',
|
||||
backgroundColor: backstoryTheme.palette.secondary.main,
|
||||
color: backstoryTheme.palette.secondary.contrastText,
|
||||
display: 'inline-block',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontFamily: backstoryTheme.typography.fontFamily,
|
||||
}}>
|
||||
Secondary Button
|
||||
</div>
|
||||
|
||||
<div className="mt-4" style={{
|
||||
padding: '8px 16px',
|
||||
backgroundColor: backstoryTheme.palette.action.active,
|
||||
color: '#fff',
|
||||
display: 'inline-block',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontFamily: backstoryTheme.typography.fontFamily,
|
||||
}}>
|
||||
Action Button
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-xl mb-4" style={{ color: backstoryTheme.palette.text.primary }}>
|
||||
Theme Color Breakdown
|
||||
</h2>
|
||||
<table className="border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border p-2 text-left"
|
||||
style={{ backgroundColor: backstoryTheme.palette.background.default, color: backstoryTheme.palette.text.primary }}>Color Name</th>
|
||||
<th className="border p-2 text-left"
|
||||
style={{ backgroundColor: backstoryTheme.palette.background.default, color: backstoryTheme.palette.text.primary }}>Hex Value</th>
|
||||
<th className="border p-2 text-left"
|
||||
style={{ backgroundColor: backstoryTheme.palette.background.default, color: backstoryTheme.palette.text.primary }}>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Primary Main</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>{backstoryTheme.palette.primary.main}</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Midnight Blue - Used for main headers and primary UI elements</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Primary Contrast</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>{backstoryTheme.palette.primary.contrastText}</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Warm Gray - Text that appears on primary color backgrounds</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Secondary Main</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>{backstoryTheme.palette.secondary.main}</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Dusty Teal - Used for secondary actions and accents</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Highlight</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>{backstoryTheme.palette.custom.highlight}</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Golden Ochre - Used for highlights, accents, and important actions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Background Default</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>{backstoryTheme.palette.background.default}</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Warm Gray - Main background color for the application</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Text Primary</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>{backstoryTheme.palette.text.primary}</td>
|
||||
<td className="border p-2" style={{ color: backstoryTheme.palette.text.primary }}>Charcoal Black - Primary text color throughout the app</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
BackstoryThemeVisualizerPage
|
||||
};
|
361
frontend/src/NewApp/Pages/BackstoryUIOverviewPage.tsx
Normal file
361
frontend/src/NewApp/Pages/BackstoryUIOverviewPage.tsx
Normal file
@ -0,0 +1,361 @@
|
||||
import React from 'react';
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||
import { Box, Container, Paper, Typography, Grid, Divider, CssBaseline } from '@mui/material';
|
||||
import { backstoryTheme } from 'BackstoryTheme';
|
||||
|
||||
const BackstoryUIOverviewPage: React.FC = () => {
|
||||
return (
|
||||
<ThemeProvider theme={backstoryTheme}>
|
||||
<CssBaseline />
|
||||
<Box sx={{ bgcolor: 'background.default', overflow: "hidden", py: 4 }}>
|
||||
<Container maxWidth="lg">
|
||||
<Paper sx={{ p: 4, borderRadius: 2, boxShadow: 2 }}>
|
||||
{/* Header */}
|
||||
<Box sx={{ p: 3, bgcolor: 'background.paper', borderRadius: 2, mb: 4, boxShadow: 1 }}>
|
||||
<Typography variant="h4" component="h1" sx={{ fontWeight: 'bold', color: 'primary.main', mb: 1 }}>
|
||||
Backstory UI Architecture
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
A visual overview of the dual-purpose application serving candidates and employers
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* User Types */}
|
||||
<Grid container spacing={3} sx={{ mb: 4 }}>
|
||||
<Grid size={{xs: 12, md: 6}}>
|
||||
<Box sx={{
|
||||
p: 3,
|
||||
bgcolor: 'rgba(74, 122, 125, 0.1)',
|
||||
borderRadius: 2,
|
||||
border: '1px solid',
|
||||
borderColor: 'rgba(74, 122, 125, 0.3)',
|
||||
height: '100%'
|
||||
}}>
|
||||
<Typography variant="h6" sx={{ color: 'secondary.main', mb: 2, fontWeight: 'bold' }}>
|
||||
Candidate Experience
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||
{[
|
||||
'Create comprehensive professional profiles',
|
||||
'Configure AI assistant for employer Q&A',
|
||||
'Generate tailored resumes for specific jobs',
|
||||
'Track profile engagement metrics'
|
||||
].map((item, index) => (
|
||||
<Box key={index} sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
<Box sx={{ width: 8, height: 8, borderRadius: '50%', bgcolor: 'secondary.main' }} />
|
||||
<Typography variant="body2">{item}</Typography>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
<Grid size={{ xs: 12, md: 6}}>
|
||||
<Box sx={{
|
||||
p: 3,
|
||||
bgcolor: 'rgba(26, 37, 54, 0.1)',
|
||||
borderRadius: 2,
|
||||
border: '1px solid',
|
||||
borderColor: 'rgba(26, 37, 54, 0.3)',
|
||||
height: '100%'
|
||||
}}>
|
||||
<Typography variant="h6" sx={{ color: 'primary.main', mb: 2, fontWeight: 'bold' }}>
|
||||
Employer Experience
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||
{[
|
||||
'Search for candidates with specific skills',
|
||||
'Interact with candidate AI assistants',
|
||||
'Generate position-specific candidate resumes',
|
||||
'Manage talent pools and job listings'
|
||||
].map((item, index) => (
|
||||
<Box key={index} sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
<Box sx={{ width: 8, height: 8, borderRadius: '50%', bgcolor: 'primary.main' }} />
|
||||
<Typography variant="body2">{item}</Typography>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* UI Components */}
|
||||
<Box sx={{ p: 3, bgcolor: 'background.paper', borderRadius: 2, mb: 4, boxShadow: 1 }}>
|
||||
<Typography variant="h5" sx={{ color: 'text.primary', mb: 3, fontWeight: 'bold' }}>
|
||||
Key UI Components
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
{[
|
||||
{ title: 'Dashboards', description: 'Role-specific dashboards with card-based metrics and action items' },
|
||||
{ title: 'Profile Editors', description: 'Comprehensive forms for managing professional information' },
|
||||
{ title: 'Resume Builder', description: 'AI-powered tools for creating tailored resumes' },
|
||||
{ title: 'Q&A Interface', description: 'Chat-like interface for employer-candidate AI interaction' },
|
||||
{ title: 'Search & Filters', description: 'Advanced search with multiple filter categories' },
|
||||
{ title: 'Analytics Dashboards', description: 'Visual metrics for tracking engagement and performance' }
|
||||
].map((component, index) => (
|
||||
<Grid size={{xs: 12, sm: 6, md: 4}} key={index}>
|
||||
<Box sx={{
|
||||
p: 2,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
borderRadius: 1,
|
||||
height: '100%',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
'&:hover': {
|
||||
bgcolor: 'rgba(212, 160, 23, 0.05)',
|
||||
borderColor: 'action.active',
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: 1
|
||||
}
|
||||
}}>
|
||||
<Typography variant="h6" sx={{ color: 'secondary.main', mb: 1, fontWeight: 'medium' }}>
|
||||
{component.title}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{component.description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{/* Navigation Structure */}
|
||||
<Grid container spacing={3} sx={{ mb: 4 }}>
|
||||
{[
|
||||
{
|
||||
title: 'Candidate Navigation',
|
||||
items: ['Dashboard', 'Profile', 'Backstory', 'Resumes', 'Q&A Setup', 'Analytics', 'Settings'],
|
||||
color: 'secondary.main',
|
||||
borderColor: 'secondary.main'
|
||||
},
|
||||
{
|
||||
title: 'Employer Navigation',
|
||||
items: ['Dashboard', 'Search', 'Saved', 'Jobs', 'Company', 'Analytics', 'Settings'],
|
||||
color: 'primary.main',
|
||||
borderColor: 'primary.main'
|
||||
},
|
||||
{
|
||||
title: 'Public Navigation',
|
||||
items: ['Home', 'About', 'Pricing', 'Login', 'Register'],
|
||||
color: 'custom.highlight',
|
||||
borderColor: 'custom.highlight'
|
||||
}
|
||||
].map((nav, index) => (
|
||||
<Grid size={{xs:12, md:4}} key={index}>
|
||||
<Box sx={{
|
||||
p: 3,
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: 2,
|
||||
boxShadow: 1,
|
||||
height: '100%'
|
||||
}}>
|
||||
<Typography variant="h6" sx={{ color: 'text.primary', mb: 2, fontWeight: 'bold' }}>
|
||||
{nav.title}
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
borderLeft: 3,
|
||||
borderColor: nav.borderColor,
|
||||
pl: 2,
|
||||
py: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1.5
|
||||
}}>
|
||||
{nav.items.map((item, idx) => (
|
||||
<Typography key={idx} sx={{ color: nav.color, fontWeight: 'medium' }}>
|
||||
{item}
|
||||
</Typography>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
{/* Connection Points */}
|
||||
<Box sx={{ p: 3, bgcolor: 'background.paper', borderRadius: 2, mb: 4, boxShadow: 1 }}>
|
||||
<Typography variant="h5" sx={{ color: 'text.primary', mb: 3, fontWeight: 'bold' }}>
|
||||
System Connection Points
|
||||
</Typography>
|
||||
<Box sx={{ position: 'relative', py: 2 }}>
|
||||
{/* Connection line */}
|
||||
<Box sx={{
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: 1,
|
||||
borderColor: 'divider',
|
||||
zIndex: 0,
|
||||
borderLeft: "1px solid",
|
||||
overflow: "hidden",
|
||||
}} />
|
||||
|
||||
{/* Connection points */}
|
||||
{[
|
||||
{ left: 'Candidate Profile', right: 'Employer Search' },
|
||||
{ left: 'Q&A Setup', right: 'Q&A Interface' },
|
||||
{ left: 'Resume Generator', right: 'Job Posts' }
|
||||
].map((connection, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
mb: index < 2 ? 5 : 0,
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
pr: 3
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'inline-block',
|
||||
bgcolor: 'rgba(74, 122, 125, 0.1)',
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
color: 'secondary.main',
|
||||
fontWeight: 'medium',
|
||||
border: '1px solid',
|
||||
borderColor: 'rgba(74, 122, 125, 0.3)'
|
||||
}}>
|
||||
{connection.left}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderRadius: '50%',
|
||||
bgcolor: 'custom.highlight',
|
||||
zIndex: 2,
|
||||
boxShadow: 2,
|
||||
}} />
|
||||
|
||||
<Box sx={{
|
||||
flex: 1,
|
||||
pl: 3,
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'inline-block',
|
||||
bgcolor: 'rgba(26, 37, 54, 0.1)',
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
color: 'primary.main',
|
||||
fontWeight: 'medium',
|
||||
border: '1px solid',
|
||||
borderColor: 'rgba(26, 37, 54, 0.3)',
|
||||
}}>
|
||||
{connection.right}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Mobile Adaptation */}
|
||||
<Box sx={{ p: 3, bgcolor: 'background.paper', borderRadius: 2, boxShadow: 1 }}>
|
||||
<Typography variant="h5" sx={{ color: 'text.primary', mb: 3, fontWeight: 'bold' }}>
|
||||
Mobile Adaptation
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Box sx={{
|
||||
width: 200,
|
||||
height: 400,
|
||||
border: '4px solid',
|
||||
borderColor: 'text.primary',
|
||||
borderRadius: 5,
|
||||
p: 1,
|
||||
bgcolor: 'background.default'
|
||||
}}>
|
||||
<Box sx={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
borderRadius: 4,
|
||||
overflow: 'hidden'
|
||||
}}>
|
||||
{/* Mobile header */}
|
||||
<Box sx={{
|
||||
bgcolor: 'primary.main',
|
||||
color: 'primary.contrastText',
|
||||
p: 1,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<Typography sx={{ fontWeight: 'bold', fontSize: '0.875rem' }}>BACKSTORY</Typography>
|
||||
<Box>☰</Box>
|
||||
</Box>
|
||||
|
||||
{/* Mobile content */}
|
||||
<Box sx={{
|
||||
flex: 1,
|
||||
p: 1.5,
|
||||
overflow: 'auto',
|
||||
fontSize: '0.75rem'
|
||||
}}>
|
||||
<Typography sx={{ mb: 1, fontWeight: 'medium' }}>Welcome back, [Name]!</Typography>
|
||||
<Typography sx={{ fontSize: '0.675rem', mb: 2 }}>Profile: 75% complete</Typography>
|
||||
|
||||
<Box sx={{
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
borderRadius: 1,
|
||||
p: 1.5,
|
||||
mb: 2,
|
||||
bgcolor: 'background.paper'
|
||||
}}>
|
||||
<Typography sx={{ fontWeight: 'bold', fontSize: '0.75rem', mb: 0.5 }}>Resume Builder</Typography>
|
||||
<Typography sx={{ fontSize: '0.675rem' }}>3 custom resumes</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
borderRadius: 1,
|
||||
p: 1.5,
|
||||
bgcolor: 'background.paper'
|
||||
}}>
|
||||
<Typography sx={{ fontWeight: 'bold', fontSize: '0.75rem', mb: 0.5 }}>Recent Activity</Typography>
|
||||
<Typography sx={{ fontSize: '0.675rem' }}>• 5 profile views</Typography>
|
||||
<Typography sx={{ fontSize: '0.675rem' }}>• 2 downloads</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Mobile footer */}
|
||||
<Box sx={{
|
||||
bgcolor: 'background.default',
|
||||
p: 1,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-around',
|
||||
borderTop: '1px solid',
|
||||
borderColor: 'divider'
|
||||
}}>
|
||||
<Typography sx={{ fontWeight: 'bold', fontSize: '0.75rem', color: 'secondary.main' }}>Home</Typography>
|
||||
<Typography sx={{ fontSize: '0.75rem', color: 'text.secondary' }}>Profile</Typography>
|
||||
<Typography sx={{ fontSize: '0.75rem', color: 'text.secondary' }}>More</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Container>
|
||||
</Box>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
BackstoryUIOverviewPage
|
||||
};
|
BIN
frontend/src/NewApp/Pages/Conversation.png
Executable file
BIN
frontend/src/NewApp/Pages/Conversation.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
110
frontend/src/NewApp/Pages/Conversation.svg
Normal file
110
frontend/src/NewApp/Pages/Conversation.svg
Normal file
@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background gradient -->
|
||||
<defs>
|
||||
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1a2b45;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#2a3b55;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<!-- Shadow filter -->
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="3" dy="3" stdDeviation="5" flood-opacity="0.3"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Base background -->
|
||||
<rect width="800" height="500" fill="url(#bgGradient)" rx="5" ry="5"/>
|
||||
|
||||
<!-- Abstract connection lines in background -->
|
||||
<path d="M100,100 C300,50 500,200 700,100" stroke="#ffffff" stroke-width="1" fill="none" stroke-opacity="0.1"/>
|
||||
<path d="M100,200 C300,150 500,300 700,200" stroke="#ffffff" stroke-width="1" fill="none" stroke-opacity="0.1"/>
|
||||
<path d="M100,300 C300,250 500,400 700,300" stroke="#ffffff" stroke-width="1" fill="none" stroke-opacity="0.1"/>
|
||||
<path d="M100,400 C300,350 500,450 700,400" stroke="#ffffff" stroke-width="1" fill="none" stroke-opacity="0.1"/>
|
||||
|
||||
<!-- Left person - more photorealistic style -->
|
||||
<g filter="url(#shadow)">
|
||||
<!-- Suit/blazer shape -->
|
||||
<path d="M190,180 L230,170 Q260,230 250,300 L200,320 Q190,250 170,230 Z" fill="#2c3e50"/>
|
||||
<!-- Shirt collar -->
|
||||
<path d="M200,175 L230,170 L235,190 L210,195 Z" fill="#f5f5f5"/>
|
||||
<!-- Head shape -->
|
||||
<circle cx="210" cy="130" r="50" fill="#e0c4a8"/>
|
||||
<!-- Hair -->
|
||||
<path d="M170,115 Q210,80 250,115 L240,135 Q215,110 180,135 Z" fill="#4a3520"/>
|
||||
<!-- Face features suggestion -->
|
||||
<ellipse cx="195" cy="120" rx="5" ry="3" fill="#333333"/>
|
||||
<ellipse cx="225" cy="120" rx="5" ry="3" fill="#333333"/>
|
||||
<path d="M195,145 Q210,155 225,145" fill="none" stroke="#333333" stroke-width="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Middle elements - digital content -->
|
||||
<g filter="url(#shadow)">
|
||||
<!-- Resume/CV element -->
|
||||
<rect x="310" y="150" width="180" height="240" rx="5" ry="5" fill="#f5f5f5"/>
|
||||
<!-- Resume content suggestion -->
|
||||
<line x1="330" y1="180" x2="470" y2="180" stroke="#333" stroke-width="3"/>
|
||||
<line x1="330" y1="200" x2="470" y2="200" stroke="#333" stroke-width="1"/>
|
||||
<line x1="330" y1="215" x2="470" y2="215" stroke="#333" stroke-width="1"/>
|
||||
<line x1="330" y1="230" x2="470" y2="230" stroke="#333" stroke-width="1"/>
|
||||
<line x1="330" y1="260" x2="390" y2="260" stroke="#333" stroke-width="2"/>
|
||||
<line x1="330" y1="280" x2="470" y2="280" stroke="#333" stroke-width="1"/>
|
||||
<line x1="330" y1="295" x2="470" y2="295" stroke="#333" stroke-width="1"/>
|
||||
<line x1="330" y1="310" x2="470" y2="310" stroke="#333" stroke-width="1"/>
|
||||
<line x1="330" y1="340" x2="390" y2="340" stroke="#333" stroke-width="2"/>
|
||||
<line x1="330" y1="360" x2="470" y2="360" stroke="#333" stroke-width="1"/>
|
||||
</g>
|
||||
|
||||
<!-- Digital connecting elements -->
|
||||
<g>
|
||||
<path d="M250,200 Q275,210 310,210" stroke="#4f97eb" stroke-width="2" fill="none" stroke-dasharray="3,3"/>
|
||||
<circle cx="250" cy="200" r="5" fill="#4f97eb"/>
|
||||
<circle cx="310" cy="210" r="5" fill="#4f97eb"/>
|
||||
|
||||
<path d="M250,250 Q285,240 310,260" stroke="#4f97eb" stroke-width="2" fill="none" stroke-dasharray="3,3"/>
|
||||
<circle cx="250" cy="250" r="5" fill="#4f97eb"/>
|
||||
<circle cx="310" cy="260" r="5" fill="#4f97eb"/>
|
||||
|
||||
<path d="M250,300 Q275,320 310,310" stroke="#4f97eb" stroke-width="2" fill="none" stroke-dasharray="3,3"/>
|
||||
<circle cx="250" cy="300" r="5" fill="#4f97eb"/>
|
||||
<circle cx="310" cy="310" r="5" fill="#4f97eb"/>
|
||||
|
||||
<path d="M490,200 Q515,210 550,190" stroke="#4f97eb" stroke-width="2" fill="none" stroke-dasharray="3,3"/>
|
||||
<circle cx="490" cy="200" r="5" fill="#4f97eb"/>
|
||||
<circle cx="550" cy="190" r="5" fill="#4f97eb"/>
|
||||
|
||||
<path d="M490,250 Q515,240 550,260" stroke="#4f97eb" stroke-width="2" fill="none" stroke-dasharray="3,3"/>
|
||||
<circle cx="490" cy="250" r="5" fill="#4f97eb"/>
|
||||
<circle cx="550" cy="260" r="5" fill="#4f97eb"/>
|
||||
|
||||
<path d="M490,300 Q515,320 550,310" stroke="#4f97eb" stroke-width="2" fill="none" stroke-dasharray="3,3"/>
|
||||
<circle cx="490" cy="300" r="5" fill="#4f97eb"/>
|
||||
<circle cx="550" cy="310" r="5" fill="#4f97eb"/>
|
||||
</g>
|
||||
|
||||
<!-- Right person - more photorealistic style -->
|
||||
<g filter="url(#shadow)">
|
||||
<!-- Suit/blazer shape -->
|
||||
<path d="M570,180 L610,170 Q630,230 620,300 L580,320 Q560,250 550,230 Z" fill="#2c3e50"/>
|
||||
<!-- Shirt collar -->
|
||||
<path d="M580,175 L610,170 L615,190 L590,195 Z" fill="#f5f5f5"/>
|
||||
<!-- Head shape -->
|
||||
<circle cx="590" cy="130" r="50" fill="#e0c4a8"/>
|
||||
<!-- Hair -->
|
||||
<path d="M550,110 Q590,80 625,110 L615,140 Q585,120 560,140 Z" fill="#774936"/>
|
||||
<!-- Face features suggestion -->
|
||||
<ellipse cx="575" cy="120" rx="5" ry="3" fill="#333333"/>
|
||||
<ellipse cx="605" cy="120" rx="5" ry="3" fill="#333333"/>
|
||||
<path d="M575,145 Q590,155 605,145" fill="none" stroke="#333333" stroke-width="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Top title and subtitle elements -->
|
||||
<g>
|
||||
<text x="400" y="60" font-family="Arial, sans-serif" font-size="24" text-anchor="middle" fill="#ffffff" font-weight="bold">Professional Conversations</text>
|
||||
<text x="400" y="90" font-family="Arial, sans-serif" font-size="16" text-anchor="middle" fill="#e1e8f0">Discover the depth of your career journey</text>
|
||||
</g>
|
||||
|
||||
<!-- Additional decorative elements -->
|
||||
<circle cx="150" cy="420" r="30" fill="#3b5998" opacity="0.2"/>
|
||||
<circle cx="650" cy="420" r="30" fill="#3b5998" opacity="0.2"/>
|
||||
<circle cx="400" cy="450" r="20" fill="#3b5998" opacity="0.2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
492
frontend/src/NewApp/Pages/HomePage.tsx
Normal file
492
frontend/src/NewApp/Pages/HomePage.tsx
Normal file
@ -0,0 +1,492 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Container,
|
||||
Paper,
|
||||
Typography,
|
||||
Stack,
|
||||
Card,
|
||||
CardContent,
|
||||
CardActions,
|
||||
} from '@mui/material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
|
||||
import PersonSearchIcon from '@mui/icons-material/PersonSearch';
|
||||
import WorkHistoryIcon from '@mui/icons-material/WorkHistory';
|
||||
import QuestionAnswerIcon from '@mui/icons-material/QuestionAnswer';
|
||||
import DescriptionIcon from '@mui/icons-material/Description';
|
||||
import professionalConversationPng from './Conversation.png';
|
||||
|
||||
// Placeholder for Testimonials component
|
||||
const Testimonials = () => {
|
||||
return (
|
||||
<Paper elevation={0} sx={{ p: 2, my: 4 }}>
|
||||
Testimonials Component (Quotes, Success Stories)
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
// Styled components
|
||||
const HeroSection = styled(Box)(({ theme }) => ({
|
||||
padding: theme.spacing(8, 0),
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
[theme.breakpoints.down('md')]: {
|
||||
padding: theme.spacing(6, 0),
|
||||
},
|
||||
}));
|
||||
|
||||
const HeroButton = styled(Button)(({ theme }) => ({
|
||||
marginTop: theme.spacing(2),
|
||||
padding: theme.spacing(1, 3),
|
||||
fontWeight: 500,
|
||||
backgroundColor: theme.palette.action.active,
|
||||
color: theme.palette.background.paper,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action.active,
|
||||
opacity: 0.9,
|
||||
},
|
||||
}));
|
||||
|
||||
const FeatureIcon = styled(Box)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.action.active,
|
||||
color: theme.palette.background.paper,
|
||||
borderRadius: '50%',
|
||||
padding: theme.spacing(2),
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing(2),
|
||||
width: 64,
|
||||
height: 64,
|
||||
}));
|
||||
|
||||
// Feature Card Component
|
||||
const FeatureCard = ({
|
||||
icon,
|
||||
title,
|
||||
description
|
||||
}: {
|
||||
icon: React.ReactNode;
|
||||
title: string;
|
||||
description: string;
|
||||
}) => {
|
||||
return (
|
||||
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||
<CardContent sx={{ flexGrow: 1 }}>
|
||||
<Box display="flex" justifyContent="center" mb={2}>
|
||||
{icon}
|
||||
</Box>
|
||||
<Typography variant="h5" component="h3" gutterBottom align="center">
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
{description}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<CardActions sx={{ justifyContent: 'center', pb: 2 }}>
|
||||
<Button size="small" endIcon={<ArrowForwardIcon />}>
|
||||
Learn more
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const HomePage = () => {
|
||||
return (<Box sx={{display: "flex", flexDirection: "column"}}>
|
||||
{/* Hero Section */}
|
||||
<HeroSection>
|
||||
<Container>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: { xs: 'column', md: 'row' },
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
flexGrow: 1,
|
||||
maxWidth: "1024px"
|
||||
}}>
|
||||
<Box sx={{ flex: 1, flexGrow: 1 }}>
|
||||
<Typography
|
||||
variant="h2"
|
||||
component="h1"
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
fontSize: { xs: '2rem', md: '3rem' },
|
||||
mb: 2
|
||||
}}
|
||||
>
|
||||
Your complete professional story, beyond a single page
|
||||
</Typography>
|
||||
<Typography variant="h5" sx={{ mb: 3, fontWeight: 400 }}>
|
||||
Let potential employers discover the depth of your experience through interactive Q&A and tailored resumes
|
||||
</Typography>
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
|
||||
<HeroButton
|
||||
variant="contained"
|
||||
size="large"
|
||||
>
|
||||
Get Started as Candidate
|
||||
</HeroButton>
|
||||
<HeroButton
|
||||
variant="outlined"
|
||||
size="large"
|
||||
sx={{
|
||||
backgroundColor: 'transparent',
|
||||
border: '2px solid',
|
||||
borderColor: 'action.active'
|
||||
}}
|
||||
>
|
||||
Recruit Talent
|
||||
</HeroButton>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box sx={{ justifyContent: "center", display: { xs: 'none', md: 'block' } }}>
|
||||
<Box
|
||||
component="img"
|
||||
src={professionalConversationPng}
|
||||
alt="Professional conversation"
|
||||
sx={{
|
||||
width: '100%',
|
||||
maxWidth: 200,
|
||||
height: 'auto',
|
||||
borderRadius: 2,
|
||||
boxShadow: 3,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
</HeroSection>
|
||||
|
||||
{/* How It Works Section */}
|
||||
<Container sx={{ py: 8 }}>
|
||||
<Typography
|
||||
variant="h3"
|
||||
component="h2"
|
||||
align="center"
|
||||
gutterBottom
|
||||
sx={{ mb: 6, fontWeight: 600 }}
|
||||
>
|
||||
How Backstory Works
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', flexDirection: { xs: 'column', md: 'row' }, gap: 4 }}>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="h4" component="h3" gutterBottom sx={{ color: 'primary.main' }}>
|
||||
For Job Seekers
|
||||
</Typography>
|
||||
<Box sx={{ my: 3 }}>
|
||||
<Typography variant="body1" paragraph>
|
||||
Backstory helps you tell your complete professional story, highlight your achievements, and showcase your skills beyond what fits on a traditional resume.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Stack spacing={3}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box sx={{
|
||||
backgroundColor: 'primary.main',
|
||||
color: 'primary.contrastText',
|
||||
borderRadius: '50%',
|
||||
width: 40,
|
||||
height: 40,
|
||||
minWidth: 40,
|
||||
minHeight: 40,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
mr: 2,
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
1
|
||||
</Box>
|
||||
<Typography variant="body1">
|
||||
Upload your complete work history, projects, and achievements
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box sx={{
|
||||
backgroundColor: 'primary.main',
|
||||
color: 'primary.contrastText',
|
||||
borderRadius: '50%',
|
||||
width: 40,
|
||||
height: 40,
|
||||
minWidth: 40,
|
||||
minHeight: 40,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
mr: 2,
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
2
|
||||
</Box>
|
||||
<Typography variant="body1">
|
||||
Configure your AI assistant to answer questions about your experience
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box sx={{
|
||||
backgroundColor: 'primary.main',
|
||||
color: 'primary.contrastText',
|
||||
borderRadius: '50%',
|
||||
width: 40,
|
||||
height: 40,
|
||||
minWidth: 40,
|
||||
minHeight: 40,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
mr: 2,
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
3
|
||||
</Box>
|
||||
<Typography variant="body1">
|
||||
Generate targeted resumes based on specific job descriptions
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
sx={{ mt: 4 }}
|
||||
endIcon={<ArrowForwardIcon />}
|
||||
>
|
||||
Create Your Profile
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="h4" component="h3" gutterBottom sx={{ color: 'primary.main' }}>
|
||||
For Employers
|
||||
</Typography>
|
||||
<Box sx={{ my: 3 }}>
|
||||
<Typography variant="body1" paragraph>
|
||||
Discover candidates with the perfect skills and experience for your positions by engaging in meaningful Q&A to learn more about their background.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Stack spacing={3}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box sx={{
|
||||
backgroundColor: 'secondary.main',
|
||||
color: 'secondary.contrastText',
|
||||
borderRadius: '50%',
|
||||
width: 40,
|
||||
height: 40,
|
||||
minWidth: 40,
|
||||
minHeight: 40,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
mr: 2,
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
1
|
||||
</Box>
|
||||
<Typography variant="body1">
|
||||
Search the candidate pool based on skills, experience, and location
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box sx={{
|
||||
backgroundColor: 'secondary.main',
|
||||
color: 'secondary.contrastText',
|
||||
borderRadius: '50%',
|
||||
width: 40,
|
||||
height: 40,
|
||||
minWidth: 40,
|
||||
minHeight: 40,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
mr: 2,
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
2
|
||||
</Box>
|
||||
<Typography variant="body1">
|
||||
Ask personalized questions about candidates' experience and skills
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box sx={{
|
||||
backgroundColor: 'secondary.main',
|
||||
color: 'secondary.contrastText',
|
||||
borderRadius: '50%',
|
||||
width: 40,
|
||||
height: 40,
|
||||
minWidth: 40,
|
||||
minHeight: 40,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
mr: 2,
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
3
|
||||
</Box>
|
||||
<Typography variant="body1">
|
||||
Generate targeted resumes that match your job requirements
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
sx={{ mt: 4 }}
|
||||
endIcon={<ArrowForwardIcon />}
|
||||
>
|
||||
Start Recruiting
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
|
||||
{/* Features Section */}
|
||||
<Box sx={{ backgroundColor: 'background.paper', py: 8 }}>
|
||||
<Container>
|
||||
<Typography
|
||||
variant="h3"
|
||||
component="h2"
|
||||
align="center"
|
||||
gutterBottom
|
||||
sx={{ mb: 6, fontWeight: 600 }}
|
||||
>
|
||||
Key Features
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
|
||||
<Box sx={{ flex: '1 1 250px', minWidth: { xs: '100%', sm: 'calc(50% - 16px)', md: 'calc(25% - 16px)' } }}>
|
||||
<FeatureCard
|
||||
icon={
|
||||
<FeatureIcon>
|
||||
<PersonSearchIcon fontSize="large" />
|
||||
</FeatureIcon>
|
||||
}
|
||||
title="AI-Powered Search"
|
||||
description="Find the perfect candidates based on skills, experience, and fit for your specific requirements."
|
||||
/>
|
||||
</Box>
|
||||
<Box sx={{ flex: '1 1 250px', minWidth: { xs: '100%', sm: 'calc(50% - 16px)', md: 'calc(25% - 16px)' } }}>
|
||||
<FeatureCard
|
||||
icon={
|
||||
<FeatureIcon>
|
||||
<WorkHistoryIcon fontSize="large" />
|
||||
</FeatureIcon>
|
||||
}
|
||||
title="Complete Backstory"
|
||||
description="Share your full professional journey beyond the limitations of a traditional resume."
|
||||
/>
|
||||
</Box>
|
||||
<Box sx={{ flex: '1 1 250px', minWidth: { xs: '100%', sm: 'calc(50% - 16px)', md: 'calc(25% - 16px)' } }}>
|
||||
<FeatureCard
|
||||
icon={
|
||||
<FeatureIcon>
|
||||
<QuestionAnswerIcon fontSize="large" />
|
||||
</FeatureIcon>
|
||||
}
|
||||
title="Interactive Q&A"
|
||||
description="Ask detailed questions about a candidate's experience and get immediate answers."
|
||||
/>
|
||||
</Box>
|
||||
<Box sx={{ flex: '1 1 250px', minWidth: { xs: '100%', sm: 'calc(50% - 16px)', md: 'calc(25% - 16px)' } }}>
|
||||
<FeatureCard
|
||||
icon={
|
||||
<FeatureIcon>
|
||||
<DescriptionIcon fontSize="large" />
|
||||
</FeatureIcon>
|
||||
}
|
||||
title="Custom Resumes"
|
||||
description="Generate tailored resumes optimized for specific job descriptions with one click."
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
|
||||
{/* Testimonials Section */}
|
||||
<Container sx={{ py: 8 }}>
|
||||
<Typography
|
||||
variant="h3"
|
||||
component="h2"
|
||||
align="center"
|
||||
gutterBottom
|
||||
sx={{ mb: 2, fontWeight: 600 }}
|
||||
>
|
||||
Success Stories
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body1"
|
||||
align="center"
|
||||
sx={{ mb: 6, maxWidth: 800, mx: 'auto' }}
|
||||
>
|
||||
See how Backstory has transformed the hiring process for both candidates and employers.
|
||||
</Typography>
|
||||
|
||||
<Testimonials />
|
||||
</Container>
|
||||
|
||||
{/* CTA Section */}
|
||||
<Box sx={{
|
||||
backgroundColor: 'primary.main',
|
||||
color: 'primary.contrastText',
|
||||
py: 8
|
||||
}}>
|
||||
<Container>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center',
|
||||
maxWidth: 800,
|
||||
mx: 'auto'
|
||||
}}>
|
||||
<Typography variant="h3" component="h2" gutterBottom>
|
||||
Ready to transform your hiring process?
|
||||
</Typography>
|
||||
<Typography variant="h6" sx={{ mb: 4 }}>
|
||||
Join Backstory today and discover a better way to connect talent with opportunity.
|
||||
</Typography>
|
||||
<Stack
|
||||
direction={{ xs: 'column', sm: 'row' }}
|
||||
spacing={2}
|
||||
justifyContent="center"
|
||||
>
|
||||
<HeroButton
|
||||
variant="contained"
|
||||
size="large"
|
||||
>
|
||||
Sign Up as Candidate
|
||||
</HeroButton>
|
||||
<HeroButton
|
||||
variant="outlined"
|
||||
size="large"
|
||||
sx={{
|
||||
backgroundColor: 'transparent',
|
||||
border: '2px solid',
|
||||
borderColor: 'action.active'
|
||||
}}
|
||||
>
|
||||
Sign Up as Employer
|
||||
</HeroButton>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
HomePage
|
||||
};
|
||||
|
518
frontend/src/NewApp/Pages/MockupPage.tsx
Normal file
518
frontend/src/NewApp/Pages/MockupPage.tsx
Normal file
@ -0,0 +1,518 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
AppBar, Avatar, Box, Button, Chip, Container, Divider, Drawer,
|
||||
IconButton, InputBase, List, ListItem, ListItemButton, ListItemIcon,
|
||||
ListItemText, Paper, Tab, Tabs, TextField, Typography,
|
||||
useMediaQuery, useTheme
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Menu as MenuIcon, Search as SearchIcon, Description as FileTextIcon,
|
||||
Person as UserIcon, Settings as SettingsIcon, Add as PlusIcon,
|
||||
Edit as EditIcon, Visibility as EyeIcon, Save as SaveIcon,
|
||||
Delete as TrashIcon, AccessTime as ClockIcon, ChevronRight as ChevronRightIcon
|
||||
} from '@mui/icons-material';
|
||||
|
||||
interface Resume {
|
||||
id: number;
|
||||
name: string;
|
||||
date: string;
|
||||
isRecent: boolean;
|
||||
}
|
||||
|
||||
const MockupPage = () => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const [activeTab, setActiveTab] = useState<string>("resume");
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState<boolean>(false);
|
||||
const [selectedResume, setSelectedResume] = useState<number | null>(null);
|
||||
|
||||
// Mock data
|
||||
const savedResumes: Resume[] = [
|
||||
{ id: 1, name: "Software Engineer - Tech Co", date: "May 15, 2025", isRecent: true },
|
||||
{ id: 2, name: "Product Manager - StartupX", date: "May 10, 2025", isRecent: false },
|
||||
{ id: 3, name: "Data Scientist - AI Corp", date: "May 5, 2025", isRecent: false },
|
||||
];
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100vh', bgcolor: 'background.default' }}>
|
||||
{/* Header */}
|
||||
<AppBar position="static" color="default" elevation={1} sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', px: 2, py: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="h6" component="h1" fontWeight="bold" color="text.primary">Backstory</Typography>
|
||||
{isMobile && (
|
||||
<IconButton edge="start" color="inherit" onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
{!isMobile && (
|
||||
<Button
|
||||
startIcon={<PlusIcon />}
|
||||
color="primary"
|
||||
size="small"
|
||||
>
|
||||
New Resume
|
||||
</Button>
|
||||
)}
|
||||
<IconButton sx={{ bgcolor: 'action.hover', borderRadius: '50%' }}>
|
||||
<UserIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</AppBar>
|
||||
|
||||
<Box sx={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
|
||||
{/* Sidebar - hidden on mobile */}
|
||||
{!isMobile && (
|
||||
<Drawer
|
||||
variant="permanent"
|
||||
sx={{
|
||||
width: 240,
|
||||
flexShrink: 0,
|
||||
[`& .MuiDrawer-paper`]: { width: 240, boxSizing: 'border-box', position: 'relative' },
|
||||
}}
|
||||
>
|
||||
<Box sx={{ p: 2, display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="overline" color="text.secondary" gutterBottom>Main</Typography>
|
||||
<List disablePadding>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton sx={{ borderRadius: 1 }}>
|
||||
<ListItemIcon sx={{ minWidth: 36 }}>
|
||||
<SearchIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Q&A" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton
|
||||
sx={{ borderRadius: 1, bgcolor: 'primary.lighter', color: 'primary.main' }}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 36, color: 'primary.main' }}>
|
||||
<FileTextIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Resume Builder" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="overline" color="text.secondary" gutterBottom>My Content</Typography>
|
||||
<List disablePadding>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton sx={{ borderRadius: 1 }}>
|
||||
<ListItemIcon sx={{ minWidth: 36 }}>
|
||||
<FileTextIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="My Resumes" />
|
||||
<Chip
|
||||
label={savedResumes.length}
|
||||
size="small"
|
||||
sx={{ height: 20, fontSize: 12 }}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton sx={{ borderRadius: 1 }}>
|
||||
<ListItemIcon sx={{ minWidth: 36 }}>
|
||||
<SettingsIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Settings" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mt: 'auto' }}>
|
||||
<Paper variant="outlined" sx={{ p: 2, bgcolor: 'background.default' }}>
|
||||
<Typography variant="subtitle2" color="text.primary" gutterBottom>Recent Activity</Typography>
|
||||
<List dense disablePadding>
|
||||
{savedResumes.filter(r => r.isRecent).map(resume => (
|
||||
<ListItem key={resume.id} disablePadding sx={{ mb: 0.5 }}>
|
||||
<ListItemIcon sx={{ minWidth: 24 }}>
|
||||
<ClockIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<Typography variant="body2" noWrap>{resume.name}</Typography>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Box>
|
||||
</Drawer>
|
||||
)}
|
||||
|
||||
{/* Mobile menu - drawer */}
|
||||
<Drawer
|
||||
anchor="left"
|
||||
open={isMobileMenuOpen}
|
||||
onClose={() => setIsMobileMenuOpen(false)}
|
||||
sx={{
|
||||
display: { xs: 'block', md: 'none' },
|
||||
'& .MuiDrawer-paper': { width: 240 }
|
||||
}}
|
||||
>
|
||||
<Box sx={{ p: 2, display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="overline" color="text.secondary" gutterBottom>Main</Typography>
|
||||
<List disablePadding>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton onClick={() => setIsMobileMenuOpen(false)}>
|
||||
<ListItemIcon>
|
||||
<SearchIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Q&A" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
sx={{ bgcolor: 'primary.lighter', color: 'primary.main' }}
|
||||
>
|
||||
<ListItemIcon sx={{ color: 'primary.main' }}>
|
||||
<FileTextIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Resume Builder" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="overline" color="text.secondary" gutterBottom>My Content</Typography>
|
||||
<List disablePadding>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton onClick={() => setIsMobileMenuOpen(false)}>
|
||||
<ListItemIcon>
|
||||
<FileTextIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="My Resumes" />
|
||||
<Chip
|
||||
label={savedResumes.length}
|
||||
size="small"
|
||||
sx={{ height: 20, fontSize: 12 }}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton onClick={() => setIsMobileMenuOpen(false)}>
|
||||
<ListItemIcon>
|
||||
<SettingsIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Settings" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Box>
|
||||
</Box>
|
||||
</Drawer>
|
||||
|
||||
{/* Main content */}
|
||||
<Box sx={{ flex: 1, overflow: 'auto', p: 3 }}>
|
||||
{/* Resume Builder content */}
|
||||
<Box sx={{ mb: 4 }}>
|
||||
<Typography variant="h5" component="h2" fontWeight="bold" gutterBottom>Resume Builder</Typography>
|
||||
<Typography variant="body2" color="text.secondary">Generate and customize resumes based on job descriptions</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Tabs */}
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={(_, newValue) => setActiveTab(newValue)}
|
||||
aria-label="Resume builder tabs"
|
||||
variant={isMobile ? "scrollable" : "standard"}
|
||||
scrollButtons={isMobile ? "auto" : undefined}
|
||||
>
|
||||
<Tab label="Job Description" value="job" />
|
||||
<Tab label="Resume" value="resume" />
|
||||
<Tab label="Fact Check" value="fact" />
|
||||
<Tab label="Saved Resumes" value="saved" />
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
{/* Tab content */}
|
||||
{activeTab === 'job' && (
|
||||
<Paper variant="outlined" sx={{ p: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>Job Description</Typography>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={10}
|
||||
placeholder="Paste the job description here..."
|
||||
variant="outlined"
|
||||
/>
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<Button variant="contained" color="primary">
|
||||
Generate Resume
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
{activeTab === 'resume' && (
|
||||
<Paper variant="outlined" sx={{ p: 3 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
||||
<Typography variant="h6">Resume Editor</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
startIcon={<SaveIcon />}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
startIcon={<EyeIcon />}
|
||||
>
|
||||
Preview
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Resume content editor with sections */}
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
{/* Contact information */}
|
||||
<Paper variant="outlined" sx={{ p: 2 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||
<Typography variant="subtitle1" fontWeight="medium">Contact Information</Typography>
|
||||
<IconButton size="small" color="default">
|
||||
<EditIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2 }}>
|
||||
<Box>
|
||||
<Typography variant="caption" color="text.secondary" display="block" gutterBottom>
|
||||
Full Name
|
||||
</Typography>
|
||||
<TextField size="small" fullWidth defaultValue="John Doe" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="caption" color="text.secondary" display="block" gutterBottom>
|
||||
Email
|
||||
</Typography>
|
||||
<TextField size="small" fullWidth defaultValue="john@example.com" />
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
{/* Professional Summary */}
|
||||
<Paper variant="outlined" sx={{ p: 2 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||
<Typography variant="subtitle1" fontWeight="medium">Professional Summary</Typography>
|
||||
<IconButton size="small" color="default">
|
||||
<EditIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={3}
|
||||
size="small"
|
||||
defaultValue="Experienced software developer with 8+ years in full-stack development, specializing in React, Node.js, and cloud infrastructure. Passionate about creating scalable, maintainable applications."
|
||||
/>
|
||||
</Paper>
|
||||
|
||||
{/* Work Experience */}
|
||||
<Paper variant="outlined" sx={{ p: 2 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||
<Typography variant="subtitle1" fontWeight="medium">Work Experience</Typography>
|
||||
<Button
|
||||
startIcon={<PlusIcon />}
|
||||
color="primary"
|
||||
size="small"
|
||||
>
|
||||
Add Position
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Job entry */}
|
||||
<Paper variant="outlined" sx={{ p: 2, mb: 2, bgcolor: 'background.default' }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography variant="subtitle2" fontWeight="medium">Senior Developer</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 0.5 }}>
|
||||
<IconButton size="small">
|
||||
<EditIcon fontSize="small" />
|
||||
</IconButton>
|
||||
<IconButton size="small" color="error">
|
||||
<TrashIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
<Typography variant="body2" color="text.secondary">Tech Company Inc. • 2020-Present</Typography>
|
||||
<Box component="ul" sx={{ pl: 2, mt: 1 }}>
|
||||
<Typography component="li" variant="body2">Led development of company's flagship product</Typography>
|
||||
<Typography component="li" variant="body2">Improved performance by 40% through code optimization</Typography>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Paper>
|
||||
|
||||
{/* Add more sections button */}
|
||||
<Button
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
sx={{
|
||||
borderStyle: 'dashed',
|
||||
p: 1.5,
|
||||
color: 'text.secondary',
|
||||
'&:hover': { bgcolor: 'background.default' }
|
||||
}}
|
||||
startIcon={<PlusIcon />}
|
||||
>
|
||||
Add Section
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
)} {activeTab === 'saved' && (
|
||||
<Paper variant="outlined" sx={{ p: 3 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
||||
<Typography variant="h6">Saved Resumes</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="small"
|
||||
startIcon={<PlusIcon />}
|
||||
>
|
||||
New Resume
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Resume list */}
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
{savedResumes.map(resume => (
|
||||
<Paper
|
||||
key={resume.id}
|
||||
variant="outlined"
|
||||
sx={{
|
||||
p: 2,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
bgcolor: selectedResume === resume.id ? 'primary.lighter' : 'background.paper',
|
||||
borderColor: selectedResume === resume.id ? 'primary.light' : 'divider',
|
||||
'&:hover': { bgcolor: selectedResume === resume.id ? 'primary.lighter' : 'action.hover' }
|
||||
}}
|
||||
onClick={() => setSelectedResume(resume.id)}
|
||||
>
|
||||
<Box>
|
||||
<Typography variant="subtitle2">{resume.name}</Typography>
|
||||
<Typography variant="caption" color="text.secondary">Last edited: {resume.date}</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
<IconButton size="small">
|
||||
<EditIcon fontSize="small" />
|
||||
</IconButton>
|
||||
<IconButton size="small" color="error">
|
||||
<TrashIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Paper>
|
||||
))}
|
||||
</Box>
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
{activeTab === 'fact' && (
|
||||
<Paper variant="outlined" sx={{ p: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>Fact Check</Typography>
|
||||
<Typography variant="body2" color="text.secondary" paragraph>
|
||||
This tab shows how your resume content compares to your employment history data.
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Paper variant="outlined" sx={{ p: 2, mb: 2, bgcolor: 'success.lighter' }}>
|
||||
<Typography variant="subtitle2" fontWeight="medium" color="success.dark">
|
||||
✓ Work History Verification
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
All employment dates match your documented history.
|
||||
</Typography>
|
||||
</Paper>
|
||||
|
||||
<Paper variant="outlined" sx={{ p: 2, mb: 2, bgcolor: 'warning.lighter' }}>
|
||||
<Typography variant="subtitle2" fontWeight="medium" color="warning.dark">
|
||||
⚠ Skills Verification
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
Some skills listed (React Native, Flutter) are not strongly supported by your experience documents.
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Paper>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Mobile bottom navigation */}
|
||||
{isMobile && (
|
||||
<Paper
|
||||
sx={{
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-around',
|
||||
borderTop: 1,
|
||||
borderColor: 'divider',
|
||||
zIndex: 1100
|
||||
}}
|
||||
elevation={3}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
py: 1,
|
||||
px: 2,
|
||||
color: 'text.secondary'
|
||||
}}
|
||||
component="button"
|
||||
>
|
||||
<SearchIcon />
|
||||
<Typography variant="caption">Q&A</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
py: 1,
|
||||
px: 2,
|
||||
color: 'primary.main'
|
||||
}}
|
||||
component="button"
|
||||
>
|
||||
<FileTextIcon />
|
||||
<Typography variant="caption">Resume</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
py: 1,
|
||||
px: 2,
|
||||
color: 'text.secondary'
|
||||
}}
|
||||
component="button"
|
||||
>
|
||||
<UserIcon />
|
||||
<Typography variant="caption">Profile</Typography>
|
||||
</Box>
|
||||
</Paper>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
MockupPage
|
||||
};
|
34
frontend/src/NewApp/Pages/RegisterPage.tsx
Normal file
34
frontend/src/NewApp/Pages/RegisterPage.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
|
||||
const RegisterPage = () => {
|
||||
return (
|
||||
<pre>
|
||||
+------------------------------------------------------+
|
||||
| BACKSTORY [Logo] Home |
|
||||
+------------------------------------------------------+
|
||||
| |
|
||||
| Create Your Candidate Account |
|
||||
| |
|
||||
| [ ] Email |
|
||||
| [ ] Password |
|
||||
| [ ] Confirm Password |
|
||||
| |
|
||||
| [ ] I agree to the Terms & Privacy Policy |
|
||||
| |
|
||||
| [Create Account] |
|
||||
| |
|
||||
| Already have an account? [Login] |
|
||||
| |
|
||||
| --- or --- |
|
||||
| |
|
||||
| [Continue with Google] |
|
||||
| [Continue with LinkedIn] |
|
||||
| |
|
||||
+------------------------------------------------------+
|
||||
</pre>
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
RegisterPage
|
||||
};
|
@ -76,6 +76,15 @@ const AboutPage = (props: BackstoryPageProps) => {
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "BETA",
|
||||
filepath: "/docs/beta.md",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('beta', open); },
|
||||
expanded: page === 'beta',
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "Resume Generation Architecture",
|
||||
filepath: "/docs/resume-generation.md",
|
||||
|
@ -93,10 +93,6 @@ What would you like to know about ${user.first_name}?
|
||||
}
|
||||
|
||||
return <Conversation
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
ref={ref}
|
||||
{...{
|
||||
multiline: true,
|
||||
|
14
lkml/init.sh
14
lkml/init.sh
@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
fail() {
|
||||
echo "FAIL: ${*}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
for i in {8..16}; do
|
||||
if [[ -d "${i}" ]]; then
|
||||
echo "Skipping lkml/git/$i -- already exists"
|
||||
else
|
||||
cmd="git clone https://erol.kernel.org/lkml/git/$i"
|
||||
${cmd} || fail "cmd"
|
||||
fi
|
||||
done
|
@ -991,14 +991,14 @@ class WebServer:
|
||||
matching_user = next((user for user in self.users if user.username == username), None)
|
||||
if matching_user:
|
||||
user = matching_user
|
||||
logger.info("Found matching user", user.model_dump(mode="json"))
|
||||
logger.info(f"Found matching user: {user.username}")
|
||||
else:
|
||||
user = User(username=username, llm=self.llm)
|
||||
user.initialize(prometheus_collector=self.prometheus_collector)
|
||||
logger.info("Created new instance of user", user.model_dump(mode="json"))
|
||||
logger.info(f"Created new instance of user: {user.username}")
|
||||
self.users.append(user)
|
||||
|
||||
logger.info(f"Creating context {context_id} with user", user.model_dump(mode='json'))
|
||||
logger.info(f"Creating context {context_id or "new"} for user: {user.username}")
|
||||
try:
|
||||
if context_id:
|
||||
context = Context(
|
||||
|
@ -239,8 +239,6 @@ class JobDescription(Agent):
|
||||
# Parse the result if it's a string
|
||||
if isinstance(result, str):
|
||||
try:
|
||||
import json
|
||||
|
||||
result = json.loads(result)
|
||||
assessment = result.get("skill_assessment", {})
|
||||
except:
|
||||
@ -468,8 +466,6 @@ Provide the resume in clean markdown format, ready for the candidate to use.
|
||||
# Parse if string
|
||||
if isinstance(result, str):
|
||||
try:
|
||||
import json
|
||||
|
||||
result = json.loads(result)
|
||||
except:
|
||||
continue
|
||||
|
@ -1,4 +1,8 @@
|
||||
{
|
||||
"first_name": "Eliza",
|
||||
"last_name": "Morgan"
|
||||
"first_name": "Eliza",
|
||||
"last_name": "Morgan",
|
||||
"questions": [
|
||||
"Is Eliza real?",
|
||||
"What are Eliza's skills?"
|
||||
]
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
Eliza Morgan
|
||||
Portland, Oregon | eliza.morgan@email.com | (555) 123-4567
|
||||
linkedin.com/in/elizamorgan | ORCID: 0000-0002-XXXX-XXXX
|
||||
Portland, Oregon | eliza.morgan@nomail.com | (555) 867-5309
|
||||
linkedin.com/in/elizamorgan18383
|
||||
|
||||
**Professional Summary**
|
||||
|
||||
I am a ficticious persona generated by AI to seed the Backstory system for
|
||||
testing, evaluation, and demo purposes.
|
||||
|
||||
Conservation botanist with over a decade of experience leading ecological restoration projects, advancing rare plant propagation methods, and managing native plant programs across the Pacific Northwest. Proven record of scientific innovation, collaborative project leadership, and effective stakeholder engagement. Passionate about preserving botanical diversity through applied research, restoration, and public education.
|
||||
|
||||
**Professional Experience**
|
||||
|
Loading…
x
Reference in New Issue
Block a user