382 lines
11 KiB
TypeScript
382 lines
11 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
Box,
|
|
Button,
|
|
Container,
|
|
Grid,
|
|
Paper,
|
|
TextField,
|
|
Typography,
|
|
Avatar,
|
|
IconButton,
|
|
Stepper,
|
|
Step,
|
|
StepLabel,
|
|
useMediaQuery,
|
|
CircularProgress,
|
|
Snackbar,
|
|
Alert
|
|
} from '@mui/material';
|
|
import { styled } from '@mui/material/styles';
|
|
import { CloudUpload, PhotoCamera } from '@mui/icons-material';
|
|
import { useTheme } from '@mui/material/styles';
|
|
// import { Beta } from '../components/Beta';
|
|
|
|
// Interfaces
|
|
interface ProfileFormData {
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
phoneNumber: string;
|
|
jobTitle: string;
|
|
location: string;
|
|
bio: string;
|
|
}
|
|
|
|
// Styled components
|
|
const VisuallyHiddenInput = styled('input')({
|
|
clip: 'rect(0 0 0 0)',
|
|
clipPath: 'inset(50%)',
|
|
height: 1,
|
|
overflow: 'hidden',
|
|
position: 'absolute',
|
|
bottom: 0,
|
|
left: 0,
|
|
whiteSpace: 'nowrap',
|
|
width: 1,
|
|
});
|
|
|
|
const CreateProfilePage: React.FC = () => {
|
|
const theme = useTheme();
|
|
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
|
|
|
// State management
|
|
const [activeStep, setActiveStep] = useState<number>(0);
|
|
const [profileImage, setProfileImage] = useState<string | null>(null);
|
|
const [resumeFile, setResumeFile] = useState<File | null>(null);
|
|
const [loading, setLoading] = useState<boolean>(false);
|
|
const [snackbar, setSnackbar] = useState<{open: boolean, message: string, severity: "success" | "error"}>({
|
|
open: false,
|
|
message: '',
|
|
severity: 'success'
|
|
});
|
|
|
|
const [formData, setFormData] = useState<ProfileFormData>({
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
phoneNumber: '',
|
|
jobTitle: '',
|
|
location: '',
|
|
bio: '',
|
|
});
|
|
|
|
// Steps for the profile creation process
|
|
const steps = ['Personal Information', 'Professional Details', 'Resume Upload'];
|
|
|
|
// Handle form input changes
|
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const { name, value } = e.target;
|
|
setFormData({
|
|
...formData,
|
|
[name]: value,
|
|
});
|
|
};
|
|
|
|
// Handle profile image upload
|
|
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
if (e.target.files && e.target.files[0]) {
|
|
const reader = new FileReader();
|
|
reader.onload = (event) => {
|
|
if (event.target?.result) {
|
|
setProfileImage(event.target.result.toString());
|
|
}
|
|
};
|
|
reader.readAsDataURL(e.target.files[0]);
|
|
}
|
|
};
|
|
|
|
// Handle resume file upload
|
|
const handleResumeUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
if (e.target.files && e.target.files[0]) {
|
|
setResumeFile(e.target.files[0]);
|
|
setSnackbar({
|
|
open: true,
|
|
message: `Resume uploaded: ${e.target.files[0].name}`,
|
|
severity: 'success'
|
|
});
|
|
}
|
|
};
|
|
|
|
// Navigation functions
|
|
const handleNext = () => {
|
|
if (activeStep === steps.length - 1) {
|
|
handleSubmit();
|
|
} else {
|
|
setActiveStep((prevStep) => prevStep + 1);
|
|
}
|
|
};
|
|
|
|
const handleBack = () => {
|
|
setActiveStep((prevStep) => prevStep - 1);
|
|
};
|
|
|
|
// Form submission
|
|
const handleSubmit = async () => {
|
|
setLoading(true);
|
|
|
|
// Simulate API call with timeout
|
|
setTimeout(() => {
|
|
setLoading(false);
|
|
setSnackbar({
|
|
open: true,
|
|
message: 'Profile created successfully! Redirecting to dashboard...',
|
|
severity: 'success'
|
|
});
|
|
|
|
// Redirect would happen here in a real application
|
|
// history.push('/dashboard');
|
|
}, 2000);
|
|
};
|
|
|
|
// Form validation
|
|
const isStepValid = () => {
|
|
switch (activeStep) {
|
|
case 0:
|
|
return formData.firstName.trim() !== '' &&
|
|
formData.lastName.trim() !== '' &&
|
|
formData.email.trim() !== '';
|
|
case 1:
|
|
return formData.jobTitle.trim() !== '';
|
|
case 2:
|
|
return resumeFile !== null;
|
|
default:
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// Stepper content based on active step
|
|
const getStepContent = (step: number) => {
|
|
switch (step) {
|
|
case 0:
|
|
return (
|
|
<Grid container spacing={3}>
|
|
<Grid size={{xs: 12}} sx={{ textAlign: 'center', mb: 2 }}>
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
<Avatar
|
|
src={profileImage || ''}
|
|
sx={{
|
|
width: 120,
|
|
height: 120,
|
|
mb: 2,
|
|
border: `2px solid ${theme.palette.primary.main}`
|
|
}}
|
|
/>
|
|
<IconButton
|
|
color="primary"
|
|
aria-label="upload picture"
|
|
component="label"
|
|
>
|
|
<PhotoCamera />
|
|
<VisuallyHiddenInput
|
|
type="file"
|
|
accept="image/*"
|
|
onChange={handleImageUpload}
|
|
/>
|
|
</IconButton>
|
|
<Typography variant="caption" color="textSecondary">
|
|
Add profile photo
|
|
</Typography>
|
|
</Box>
|
|
</Grid>
|
|
<Grid size={{xs: 12, sm: 6}}>
|
|
<TextField
|
|
required
|
|
fullWidth
|
|
label="First Name"
|
|
name="firstName"
|
|
value={formData.firstName}
|
|
onChange={handleInputChange}
|
|
variant="outlined"
|
|
/>
|
|
</Grid>
|
|
<Grid size={{xs: 12, sm: 6}}>
|
|
<TextField
|
|
required
|
|
fullWidth
|
|
label="Last Name"
|
|
name="lastName"
|
|
value={formData.lastName}
|
|
onChange={handleInputChange}
|
|
variant="outlined"
|
|
/>
|
|
</Grid>
|
|
<Grid size={{xs: 12}}>
|
|
<TextField
|
|
required
|
|
fullWidth
|
|
label="Email Address"
|
|
name="email"
|
|
type="email"
|
|
value={formData.email}
|
|
onChange={handleInputChange}
|
|
variant="outlined"
|
|
/>
|
|
</Grid>
|
|
<Grid size={{xs:12}}>
|
|
<TextField
|
|
fullWidth
|
|
label="Phone Number"
|
|
name="phoneNumber"
|
|
value={formData.phoneNumber}
|
|
onChange={handleInputChange}
|
|
variant="outlined"
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
);
|
|
case 1:
|
|
return (
|
|
<Grid container spacing={3}>
|
|
<Grid size={{xs:12}}>
|
|
<TextField
|
|
required
|
|
fullWidth
|
|
label="Job Title"
|
|
name="jobTitle"
|
|
value={formData.jobTitle}
|
|
onChange={handleInputChange}
|
|
variant="outlined"
|
|
/>
|
|
</Grid>
|
|
<Grid size={{xs: 12}}>
|
|
<TextField
|
|
fullWidth
|
|
label="Location"
|
|
name="location"
|
|
placeholder="City, State, Country"
|
|
value={formData.location}
|
|
onChange={handleInputChange}
|
|
variant="outlined"
|
|
/>
|
|
</Grid>
|
|
<Grid size={{xs:12}}>
|
|
<TextField
|
|
fullWidth
|
|
multiline
|
|
rows={4}
|
|
label="Professional Bio"
|
|
name="bio"
|
|
placeholder="Tell us about yourself and your professional experience"
|
|
value={formData.bio}
|
|
onChange={handleInputChange}
|
|
variant="outlined"
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
);
|
|
case 2:
|
|
return (
|
|
<Grid container spacing={3}>
|
|
<Grid size={{xs: 12}}>
|
|
<Typography variant="body1" component="p">
|
|
Upload your resume to complete your profile. We'll analyze it to better understand your skills and experience.
|
|
(Supported formats: .pdf, .docx, .md, and .txt)
|
|
</Typography>
|
|
<Box sx={{ textAlign: 'center', mt: 2 }}>
|
|
<Button
|
|
component="label"
|
|
variant="contained"
|
|
startIcon={<CloudUpload />}
|
|
sx={{ mb: 2 }}
|
|
>
|
|
Upload Resume
|
|
<VisuallyHiddenInput
|
|
type="file"
|
|
accept=".pdf,.docx,.txt,.md"
|
|
onChange={handleResumeUpload}
|
|
/>
|
|
</Button>
|
|
|
|
{resumeFile && (
|
|
<Typography variant="body2" color="textSecondary" sx={{ mt: 1 }}>
|
|
File uploaded: {resumeFile.name}
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
</Grid>
|
|
</Grid>
|
|
);
|
|
default:
|
|
return 'Unknown step';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Container component="main">
|
|
<Paper
|
|
elevation={3}
|
|
sx={{
|
|
p: { xs: 2, sm: 4 },
|
|
mt: { xs: 2, sm: 4 },
|
|
mb: { xs: 2, sm: 4 },
|
|
}}
|
|
>
|
|
<Typography component="h1" variant="h4" align="center" gutterBottom>
|
|
Create Your Profile
|
|
</Typography>
|
|
|
|
<Stepper
|
|
activeStep={activeStep}
|
|
alternativeLabel={!isMobile}
|
|
orientation={isMobile ? 'vertical' : 'horizontal'}
|
|
sx={{ mt: 3, mb: 5 }}
|
|
>
|
|
{steps.map((label) => (
|
|
<Step key={label}>
|
|
<StepLabel>{label}</StepLabel>
|
|
</Step>
|
|
))}
|
|
</Stepper>
|
|
|
|
<Box sx={{ mt: 2, mb: 4 }}>
|
|
{getStepContent(activeStep)}
|
|
</Box>
|
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mt: 4 }}>
|
|
<Button
|
|
disabled={activeStep === 0}
|
|
onClick={handleBack}
|
|
variant="outlined"
|
|
>
|
|
Back
|
|
</Button>
|
|
<Button
|
|
variant="contained"
|
|
onClick={handleNext}
|
|
disabled={!isStepValid() || loading}
|
|
startIcon={loading ? <CircularProgress size={20} color="inherit" /> : null}
|
|
>
|
|
{activeStep === steps.length - 1 ? 'Create Profile' : 'Next'}
|
|
</Button>
|
|
</Box>
|
|
</Paper>
|
|
|
|
<Snackbar
|
|
open={snackbar.open}
|
|
autoHideDuration={6000}
|
|
onClose={() => setSnackbar({ ...snackbar, open: false })}
|
|
>
|
|
<Alert
|
|
onClose={() => setSnackbar({ ...snackbar, open: false })}
|
|
severity={snackbar.severity}
|
|
sx={{ width: '100%' }}
|
|
>
|
|
{snackbar.message}
|
|
</Alert>
|
|
</Snackbar>
|
|
</Container>
|
|
);
|
|
};
|
|
|
|
export { CreateProfilePage }; |