backstory/frontend/src/pages/HowItWorks.tsx

531 lines
17 KiB
TypeScript

import React, { JSX } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Box,
Button,
Container,
Paper,
Typography,
Grid,
Step,
StepLabel,
Stepper,
Stack,
ButtonProps,
} from '@mui/material';
import { styled } from '@mui/material/styles';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import AssessmentIcon from '@mui/icons-material/Assessment';
import PersonIcon from '@mui/icons-material/Person';
import WorkIcon from '@mui/icons-material/Work';
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
import DescriptionIcon from '@mui/icons-material/Description';
import professionalConversationPng from 'assets/Conversation.png';
import selectAJobPng from 'assets/select-a-job.png';
import selectJobAnalysisPng from 'assets/select-job-analysis.png';
import selectACandidatePng from 'assets/select-a-candidate.png';
import selectStartAnalysisPng from 'assets/select-start-analysis.png';
import waitPng from 'assets/wait.png';
import finalResumePng from 'assets/final-resume.png';
import { Beta } from 'components/ui/Beta';
import { Quote } from '@uiw/react-json-view';
// Styled components matching HomePage patterns
const HeroSection = styled(Box)(({ theme }) => ({
padding: theme.spacing(3, 0),
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
[theme.breakpoints.down('md')]: {
padding: theme.spacing(2, 0),
},
}));
const StepSection = styled(Box)(({ theme }) => ({
padding: theme.spacing(6, 0),
'&:nth-of-type(even)': {
backgroundColor: theme.palette.background.default,
},
}));
const StepNumber = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.action.active,
color: theme.palette.background.paper,
borderRadius: '50%',
width: 60,
height: 60,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: '1.5rem',
fontWeight: 'bold',
margin: '0 auto 1rem auto',
[theme.breakpoints.up('md')]: {
margin: 0,
},
}));
const ImageContainer = styled(Box)(({ theme }) => ({
textAlign: 'center',
'& img': {
maxWidth: '100%',
height: 'auto',
borderRadius: theme.spacing(1),
boxShadow: theme.shadows[3],
border: `2px solid ${theme.palette.action.active}`,
},
}));
const steps = [
'Select Job Analysis',
'Select a Candidate',
'Choose a Job',
'Start Assessment',
'Review Results',
'Generate Resume',
];
interface StepContentProps {
stepNumber: number;
title: string;
subtitle: string;
icon: React.ReactNode;
description: string[];
imageSrc: string;
imageAlt: string;
note?: string;
success?: string;
reversed?: boolean;
}
const StepContent: React.FC<StepContentProps> = ({
stepNumber,
title,
subtitle,
icon,
description,
imageSrc,
imageAlt,
note,
success,
reversed = false,
}) => {
const textContent = (
<Grid size={{ xs: 12, md: 6 }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3 }}>
<StepNumber>{stepNumber}</StepNumber>
<Box sx={{ ml: { xs: 0, md: 3 }, textAlign: { xs: 'center', md: 'left' } }}>
<Typography variant="h3" component="h2" sx={{ color: 'primary.main', mb: 1 }}>
{title}
</Typography>
<Box
sx={{
display: 'flex',
gap: 1,
alignItems: 'center',
justifyContent: { xs: 'center', md: 'flex-start' },
}}
>
{icon}
<Typography variant="body2" color="text.secondary">
{subtitle}
</Typography>
</Box>
</Box>
</Box>
{description.map((paragraph, index) => (
<Typography key={index} variant="body1" paragraph>
{paragraph}
</Typography>
))}
{note && (
<Paper
sx={{
p: 2,
backgroundColor: 'action.hover',
border: '1px solid',
borderColor: 'action.active',
mt: 2,
}}
>
<Typography variant="body2" sx={{ fontStyle: 'italic' }}>
<strong>Note:</strong> {note}
</Typography>
</Paper>
)}
{success && (
<Paper
sx={{
p: 2,
backgroundColor: 'secondary.main',
color: 'secondary.contrastText',
mt: 2,
}}
>
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
🎉 {success}
</Typography>
</Paper>
)}
</Grid>
);
const imageContent = (
<Grid size={{ xs: 12, md: 6 }}>
<ImageContainer>
<img src={imageSrc} alt={imageAlt} />
</ImageContainer>
</Grid>
);
return (
<Grid container spacing={4} alignItems="center">
{reversed ? (
<>
{imageContent}
{textContent}
</>
) : (
<>
{textContent}
{imageContent}
</>
)}
</Grid>
);
};
interface HeroButtonProps extends ButtonProps {
children?: string;
path: string;
}
const HeroButton = (props: HeroButtonProps): JSX.Element => {
const { children, onClick, path, ...rest } = props;
const navigate = useNavigate();
const handleClick = (): void => {
navigate(path);
};
const HeroStyledButton = 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,
},
}));
return (
<HeroStyledButton onClick={onClick ? onClick : handleClick} {...rest}>
{children}
</HeroStyledButton>
);
};
const HowItWorks: React.FC = () => {
const navigate = useNavigate();
const handleGetStarted = (): void => {
navigate('/job-analysis');
};
return (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
{/* Hero Section */}
{/* 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: '1.25rem', md: '1.75rem' },
mb: 2,
color: 'white',
}}
>
Your complete professional story, beyond a single page
</Typography>
<Typography
variant="h5"
sx={{ mb: 3, fontWeight: 400, fontSize: { xs: '1rem', md: '1.25rem' } }}
>
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" path="/login/register">
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>
<HeroSection
sx={{
display: 'flex',
position: 'relative',
overflow: 'hidden',
border: '2px solid orange',
}}
>
<Beta adaptive={false} sx={{ left: '-90px' }} />
<Container sx={{ display: 'flex', position: 'relative' }}>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
textAlign: 'center',
maxWidth: 800,
mx: 'auto',
position: 'relative',
}}
>
<Typography
variant="h2"
component="h1"
sx={{
fontWeight: 700,
fontSize: { xs: '2rem', md: '2.5rem' },
mb: 2,
color: 'white',
}}
>
Welcome to the Backstory Beta!
</Typography>
<Typography variant="h5" sx={{ mb: 3, fontWeight: 400 }}>
Here are your steps from zero-to-hero to see Backstory in action
</Typography>
</Box>
</Container>
</HeroSection>
{/* Progress Overview */}
<Container sx={{ py: 4 }}>
<Box sx={{ display: { xs: 'none', md: 'block' } }}>
<Stepper alternativeLabel sx={{ mb: 4 }}>
{steps.map(label => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
</Container>
{/* Step 1: Select Job Analysis */}
<StepSection>
<Container>
<StepContent
stepNumber={1}
title="Select Job Analysis"
subtitle="Navigate to the main feature"
icon={<AssessmentIcon sx={{ color: 'action.active' }} />}
description={[
"Select 'Job Analysis' from the menu. This takes you to the interactive Job Analysis page, where you will get to evaluate the requirements for a job and perform a Backstory assisted evaluation of a candidate, or yourself!",
]}
imageSrc={selectJobAnalysisPng}
imageAlt="Select Job Analysis from menu"
/>
</Container>
</StepSection>
{/* Step 2: Select a Candidate */}
<StepSection>
<Container>
<StepContent
stepNumber={2}
title="Select a Candidate"
subtitle="Choose from available candidate profiles"
icon={<PersonIcon sx={{ color: 'action.active' }} />}
description={[
'First, select a candidate. If you create an account and are logged in, it will default to selecting you. In addition to myself (James), there are several candidates which AI has generated. Each has a unique skillset and can be used to test out the system.',
'Once you\'ve selected a candidate, click "Next".',
]}
imageSrc={selectACandidatePng}
imageAlt="Select a candidate from the available profiles"
note="If you create an account, you can opt-in to have your account show up for others to view as well, or keep it private for just your own resume generation and job research."
reversed={true}
/>
</Container>
</StepSection>
{/* Step 3: Select a Job */}
<StepSection>
<Container>
<StepContent
stepNumber={3}
title="Choose a Job"
subtitle="Pick from existing job postings"
icon={<WorkIcon sx={{ color: 'action.active' }} />}
description={[
'After selecting a candidate, explore a little bit and then select one of the jobs. The requirements and information provided on Backstory are extracted from job postings that users have pasted as a job description or uploaded from a PDF.',
'Clicking a job will give you more details about it.',
'When you\'re happy with your selection, click "Next".',
]}
imageSrc={selectAJobPng}
imageAlt="Select a job from the available options"
note="You can create your own job postings once you create an account. Until then, you need to select one that already exists."
/>
</Container>
</StepSection>
{/* Step 4: Start Assessment */}
<StepSection>
<Container>
<StepContent
stepNumber={4}
title="Start Assessment"
subtitle="Begin the AI analysis"
icon={<PlayArrowIcon sx={{ color: 'action.active' }} />}
description={[
'After selecting a candidate, you are ready to have Backstory perform the Job Analysis. During this phase, Backstory will take each of requirements extracted from the Job and match it against information about the selected candidate.',
'This could be as little as a simple resume, or as complete as a full work history. Backstory performs similarity searches to identify key elements from the candidate that pertain to a given skill and provides a graded response.',
'To see that in action, click the "Start Skill Assessment" button. Backstory will save the results, only regenerating the information if the candidate updates their uploaded content.',
]}
imageSrc={selectStartAnalysisPng}
imageAlt="Start the skill assessment process"
reversed={true}
/>
</Container>
</StepSection>
{/* Step 5: Wait and Review */}
<StepSection>
<Container>
<StepContent
stepNumber={5}
title="Review Results"
subtitle="Watch the magic happen"
icon={<AutoAwesomeIcon sx={{ color: 'action.active' }} />}
description={[
'Once you begin that action, the Start Skill Assessment button will grey out and the page will begin updating as it collates information about the candidate. As Backstory performs its magic, you can monitor the progress and explore the different identified skills to see how or why a candidate does or does not have that skill.',
'Once it is done, you can see the final Overall Match. This is a weighted score based on amount of evidence a skill had, whether the skill was required or preferred, and other metrics.',
'After looking at the results, you can click "Next" to proceed to the final step--resume generation.',
]}
imageSrc={waitPng}
imageAlt="Wait for the analysis to complete and review results"
/>
</Container>
</StepSection>
{/* Step 6: Generate Resume */}
<StepSection>
<Container>
<StepContent
stepNumber={6}
title="Generate Resume"
subtitle="Create your tailored resume"
icon={<DescriptionIcon sx={{ color: 'action.active' }} />}
description={[
'The final step is creating the custom resume for the Candidate tailored to the particular Job. On the bottom right you can click "Next" to have Backstory generate the custom resume.',
"Note that the resume focuses on identifying key areas from the Candidate's work history that align with skills which were extracted from the original job posting.",
'After the initial resume is generated, if you are logged in, you can select "Save Resume and Edit" to take you to the resume editor.',
]}
imageSrc={finalResumePng}
imageAlt="Generated custom resume tailored to the job"
success="Success! You can then click the Copy button to copy the resume into your editor, adjust, and apply for your dream job!"
reversed={true}
/>
</Container>
</StepSection>
{/* CTA Section */}
<Box
sx={{
backgroundColor: 'primary.main',
color: 'primary.contrastText',
py: 6,
}}
>
<Container>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
textAlign: 'center',
maxWidth: 600,
mx: 'auto',
}}
>
<Typography variant="h3" component="h2" gutterBottom sx={{ color: 'white' }}>
Ready to try Backstory?
</Typography>
<Typography variant="h6" sx={{ mb: 4 }}>
Experience the future of job matching and resume generation today.
</Typography>
<Button
variant="contained"
size="large"
startIcon={<PlayArrowIcon />}
onClick={handleGetStarted}
sx={{
backgroundColor: 'action.active',
color: 'background.paper',
fontWeight: 'bold',
px: 4,
py: 1.5,
'&:hover': {
backgroundColor: 'action.active',
opacity: 0.9,
},
}}
>
Get Started Now
</Button>
</Box>
</Container>
</Box>
</Box>
);
};
export { HowItWorks };