984 lines
31 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import CheckIcon from '@mui/icons-material/Check';
import {
Box,
Button,
Container,
Grid,
Paper,
TextField,
Typography,
Avatar,
IconButton,
Tabs,
Tab,
useMediaQuery,
CircularProgress,
Snackbar,
Alert,
Card,
CardContent,
Chip,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
MenuItem,
Select,
FormControl,
InputLabel,
Switch,
FormControlLabel,
ToggleButton,
Checkbox
} from '@mui/material';
import { styled } from '@mui/material/styles';
import {
PhotoCamera,
Edit,
Save,
Cancel,
Add,
Delete,
Work,
School,
EmojiEvents,
LocationOn,
Phone,
Email,
AccountCircle,
} from '@mui/icons-material';
import { useTheme } from '@mui/material/styles';
import { useAuth } from "hooks/AuthContext";
import * as Types from 'types/types';
import { ComingSoon } from 'components/ui/ComingSoon';
import { BackstoryPageProps } from 'components/BackstoryTab';
import { useAppState } from 'hooks/GlobalContext';
// 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,
});
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
function TabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`profile-tabpanel-${index}`}
aria-labelledby={`profile-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{
p: { xs: 1, sm: 3 },
maxWidth: '100%',
overflow: 'hidden'
}}>
{children}
</Box>
)}
</div>
);
}
const CandidateProfile: React.FC<BackstoryPageProps> = (props: BackstoryPageProps) => {
const { setSnack } = useAppState();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const { user, updateUserData, apiClient } = useAuth();
const [isPublic, setIsPublic] = React.useState(true);
// Check if user is a candidate
const candidate = user?.userType === 'candidate' ? user as Types.Candidate : null;
// State management
const [tabValue, setTabValue] = useState(0);
const [editMode, setEditMode] = useState<{ [key: string]: boolean }>({});
const [loading, setLoading] = useState(false);
const [snackbar, setSnackbar] = useState<{
open: boolean;
message: string;
severity: "success" | "error" | "info" | "warning";
}>({
open: false,
message: '',
severity: 'success'
});
// Form data state
const [formData, setFormData] = useState<Partial<Types.Candidate>>({});
const [profileImage, setProfileImage] = useState<string>('');
// Dialog states
const [skillDialog, setSkillDialog] = useState(false);
const [experienceDialog, setExperienceDialog] = useState(false);
// New item states
const [newSkill, setNewSkill] = useState<Partial<Types.Skill>>({
name: '',
category: '',
level: 'beginner',
yearsOfExperience: 0
});
const [newExperience, setNewExperience] = useState<Partial<Types.WorkExperience>>({
companyName: '',
position: '',
startDate: new Date(),
isCurrent: false,
description: '',
skills: [],
location: { city: '', country: '' }
});
useEffect(() => {
if (candidate) {
setFormData(candidate);
if (candidate.profileImage) {
setProfileImage(`/api/1.0/candidates/profile/${candidate.username}`);
} else {
setProfileImage('');
}
console.log({ isPublic: candidate.isPublic });
}
}, [candidate]);
if (!candidate) {
return (
<Container maxWidth="md" sx={{ mt: 4 }}>
<Alert severity="error">
Access denied. This page is only available for candidates.
</Alert>
</Container>
);
}
// Handle tab change
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
setTabValue(newValue);
};
// Handle form input changes
const handleInputChange = (field: string, value: any) => {
setFormData({
...formData,
[field]: value,
});
};
// Handle profile image upload
const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
if (await apiClient.uploadCandidateProfile(e.target.files[0])) {
setProfileImage(URL.createObjectURL(e.target.files[0]));
candidate.profileImage = 'profile.' + e.target.files[0].name.replace(/^.*\./, '');
console.log(`Set profile image to: ${candidate.profileImage}`);
updateUserData(candidate);
}
}
};
// Toggle edit mode for a section
const toggleEditMode = (section: string) => {
setEditMode({
...editMode,
[section]: !editMode[section]
});
};
// Save changes
const handleSave = async (section: string) => {
setLoading(true);
try {
if (candidate.id) {
const updatedCandidate = await apiClient.updateCandidate(candidate.id, formData);
updateUserData(updatedCandidate);
setSnack('Profile updated successfully!');
toggleEditMode(section);
}
} catch (error) {
setSnackbar({
open: true,
message: 'Failed to update profile. Please try again.',
severity: 'error'
});
} finally {
setLoading(false);
}
};
// Cancel edit
const handleCancel = (section: string) => {
setFormData(candidate);
toggleEditMode(section);
};
// Add new skill
const handleAddSkill = () => {
if (newSkill.name && newSkill.category) {
const updatedSkills = [...(formData.skills || []), newSkill as Types.Skill];
setFormData({ ...formData, skills: updatedSkills });
setNewSkill({ name: '', category: '', level: 'beginner', yearsOfExperience: 0 });
setSkillDialog(false);
}
};
// Remove skill
const handleRemoveSkill = (index: number) => {
const updatedSkills = (formData.skills || []).filter((_, i) => i !== index);
setFormData({ ...formData, skills: updatedSkills });
};
// Add new work experience
const handleAddExperience = () => {
if (newExperience.companyName && newExperience.position) {
const updatedExperience = [...(formData.experience || []), newExperience as Types.WorkExperience];
setFormData({ ...formData, experience: updatedExperience });
setNewExperience({
companyName: '',
position: '',
startDate: new Date(),
isCurrent: false,
description: '',
skills: [],
location: { city: '', country: '' }
});
setExperienceDialog(false);
}
};
// Remove work experience
const handleRemoveExperience = (index: number) => {
const updatedExperience = (formData.experience || []).filter((_, i) => i !== index);
setFormData({ ...formData, experience: updatedExperience });
};
// Basic Information Tab
const renderBasicInfo = () => (
<Box sx={{ display: "flex", flexDirection: "column", "& .entry": { flexDirection: "column", fontSize: "0.9rem", display: "flex", mt: 1 }, "& .title": { display: "flex", fontWeight: "bold" } }}>
<Box sx={{ textAlign: 'center', mb: { xs: 1, sm: 2 } }}>
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<Avatar
src={profileImage}
sx={{
width: { xs: 80, sm: 120 },
height: { xs: 80, sm: 120 },
mb: { xs: 1, sm: 2 },
border: `2px solid ${theme.palette.primary.main}`
}}
>
{!profileImage && <AccountCircle sx={{ fontSize: { xs: 50, sm: 80 } }} />}
</Avatar>
{editMode.basic && (
<>
<IconButton
color="primary"
aria-label="upload picture"
component="label"
size={isMobile ? "small" : "medium"}
>
<PhotoCamera />
<VisuallyHiddenInput
type="file"
accept="image/*"
onChange={handleImageUpload}
/>
</IconButton>
<Typography variant="caption" color="textSecondary" sx={{ textAlign: 'center', fontSize: { xs: '0.7rem', sm: '0.75rem' } }}>
Update profile photo
</Typography>
</>
)}
</Box>
</Box>
<Box className="entry" sx={{ display: 'flex', justifyContent: 'center', alignContent: 'center', gap: 1, "& span": { mb: 0 } }}>
{editMode.basic ? (
<FormControlLabel sx={{ display: 'flex' }} control={
<Switch
checked={formData.isPublic}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => handleInputChange('isPublic', event.target.checked)} />}
label={
formData.isPublic ?
'Your account will appear in candidate lists and searches on Backstory.' :
`Your account will not be listed as a candidate. You can still share your profile by users navigating to '/u/${candidate.username}'.`
} />
) : (<>{
candidate.isPublic ?
'Your account will appear in candidate lists and searches on Backstory.' :
`Your account will not be listed as a candidate. You can still share your profile by users navigating to '/u/${candidate.username}'.`
}</>)}
</Box>
<Box className="entry">
{editMode.basic ? (
<TextField
fullWidth
label="First Name"
value={formData.firstName || ''}
onChange={(e) => handleInputChange('firstName', e.target.value)}
variant="outlined"
/>
) : (<>
<Box className="title">First Name</Box>
<Box className="value">{candidate.firstName}</Box>
</>)}
</Box>
<Box className="entry">
{editMode.basic ? (
<TextField
fullWidth
label="Last Name"
value={formData.lastName || ''}
onChange={(e) => handleInputChange('lastName', e.target.value)}
variant="outlined"
/>
) : (<>
<Box className="title">Last Name</Box>
<Box className="value">{candidate.lastName}</Box>
</>)}
</Box>
<Box className="entry">
{(false && editMode.basic) ? (
<TextField
fullWidth
label="Email"
type="email"
value={formData.email || ''}
onChange={(e) => handleInputChange('email', e.target.value)}
variant="outlined"
/>
) : (<>
<Box className="title"><Email sx={{ mr: 1, verticalAlign: 'middle' }} />
Email</Box>
<Box className="value">{candidate.email}</Box>
</>
)}
</Box>
<Box className="entry">
{editMode.basic ? (
<TextField
fullWidth
label="Phone"
value={formData.phone || ''}
onChange={(e) => handleInputChange('phone', e.target.value)}
variant="outlined"
/>
) : (<>
<Box className="title"><Phone sx={{ mr: 1, verticalAlign: 'middle' }} />
Phone</Box>
<Box className="value">{candidate.phone || 'Not provided'}</Box>
</>
)}
</Box>
<Box className="entry">
{editMode.basic ? (
<TextField
fullWidth
multiline
rows={3}
label="Professional Summary"
value={formData.description || ''}
onChange={(e) => handleInputChange('description', e.target.value)}
variant="outlined"
/>
) : (<>
<Box className="title">Professional Summary</Box>
<Box className="value">{candidate.description || 'No summary provided'}</Box>
</>)}
</Box>
<Box className="entry">
{false && editMode.basic ? (
<TextField
fullWidth
label="Location"
value={formData.location?.city || ''}
onChange={(e) => handleInputChange('location', {
...formData.location,
city: e.target.value
})}
variant="outlined"
placeholder="City, State, Country"
/>
) : (<><Box className="title">
<LocationOn sx={{ mr: 1, verticalAlign: 'middle' }} />
Location</Box>
<Box className="value">{candidate.location?.city || 'Not specified'} {candidate.location?.country || ''}</Box>
</>
)}
</Box>
<Box className="entry">
<Box sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
justifyContent: 'flex-end',
gap: 2,
mt: { xs: 2, sm: 0 }
}}>
{editMode.basic ? (
<>
<Button
variant="outlined"
onClick={() => handleCancel('basic')}
startIcon={<Cancel />}
fullWidth={isMobile}
>
Cancel
</Button>
<Button
variant="contained"
onClick={() => handleSave('basic')}
startIcon={loading ? <CircularProgress size={20} color="inherit" /> : <Save />}
disabled={loading}
fullWidth={isMobile}
>
Save
</Button>
</>
) : (
<Button
variant="outlined"
onClick={() => toggleEditMode('basic')}
startIcon={<Edit />}
fullWidth={isMobile}
>
Edit Info
</Button>
)}
</Box>
</Box>
</Box >
);
// Skills Tab
const renderSkills = () => (
<Box>
<Box sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
justifyContent: 'space-between',
alignItems: { xs: 'stretch', sm: 'center' },
mb: { xs: 2, sm: 3 },
gap: { xs: 1, sm: 0 }
}}>
<Typography variant={isMobile ? "subtitle1" : "h6"}>Skills & Expertise</Typography>
<Button
variant="outlined"
startIcon={<Add />}
onClick={() => setSkillDialog(true)}
fullWidth={isMobile}
size={isMobile ? "small" : "medium"}
>
Add Skill
</Button>
</Box>
<Grid container spacing={{ xs: 1, sm: 2 }} sx={{ maxWidth: '100%' }}>
{(formData.skills || []).map((skill, index) => (
<Grid size={{ xs: 12, sm: 6, md: 4 }} key={index}>
<Card variant="outlined" sx={{ height: '100%' }}>
<CardContent sx={{ p: { xs: 1.5, sm: 3 } }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<Box sx={{ flex: 1, minWidth: 0 }}>
<Typography variant={isMobile ? "subtitle2" : "h6"} component="div" sx={{
fontSize: { xs: '0.9rem', sm: '1.25rem' },
wordBreak: 'break-word'
}}>
{skill.name}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{
wordBreak: 'break-word',
fontSize: { xs: '0.75rem', sm: '0.875rem' }
}}>
{skill.category}
</Typography>
<Chip
size="small"
label={skill.level}
color="primary"
variant="outlined"
sx={{
mt: 1,
fontSize: { xs: '0.65rem', sm: '0.75rem' },
height: { xs: 20, sm: 24 }
}}
/>
{skill.yearsOfExperience && (
<Typography variant="caption" display="block" sx={{ fontSize: { xs: '0.65rem', sm: '0.75rem' } }}>
{skill.yearsOfExperience} years experience
</Typography>
)}
</Box>
<IconButton
size="small"
onClick={() => handleRemoveSkill(index)}
color="error"
sx={{ ml: 1 }}
>
<Delete sx={{ fontSize: { xs: 16, sm: 20 } }} />
</IconButton>
</Box>
</CardContent>
</Card>
</Grid>
))}
</Grid>
{(!formData.skills || formData.skills.length === 0) && (
<Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', py: 4 }}>
No skills added yet. Click "Add Skill" to get started.
</Typography>
)}
</Box>
);
// Experience Tab
const renderExperience = () => (
<Box>
<Box sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
justifyContent: 'space-between',
alignItems: { xs: 'stretch', sm: 'center' },
mb: { xs: 2, sm: 3 },
gap: { xs: 1, sm: 0 }
}}>
<Typography variant={isMobile ? "subtitle1" : "h6"}>Work Experience</Typography>
<Button
variant="outlined"
startIcon={<Add />}
onClick={() => setExperienceDialog(true)}
fullWidth={isMobile}
size={isMobile ? "small" : "medium"}
>
Add Experience
</Button>
</Box>
{(formData.experience || []).map((exp, index) => (
<Card key={index} sx={{ mb: { xs: 1.5, sm: 2 }, overflow: 'hidden' }}>
<CardContent sx={{ p: { xs: 1.5, sm: 3 } }}>
<Box sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
justifyContent: 'space-between',
alignItems: 'flex-start',
gap: { xs: 1, sm: 0 }
}}>
<Box sx={{ flex: 1, minWidth: 0 }}>
<Typography variant={isMobile ? "subtitle1" : "h6"} component="div" sx={{
fontSize: { xs: '1rem', sm: '1.25rem' },
wordBreak: 'break-word'
}}>
{exp.position}
</Typography>
<Typography variant="subtitle1" color="primary" sx={{
wordBreak: 'break-word',
fontSize: { xs: '0.9rem', sm: '1rem' }
}}>
{exp.companyName}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ fontSize: { xs: '0.8rem', sm: '0.875rem' } }}>
{exp.startDate?.toLocaleDateString()} - {exp.isCurrent ? 'Present' : exp.endDate?.toLocaleDateString()}
</Typography>
<Typography variant="body2" sx={{
mt: 1,
wordBreak: 'break-word',
fontSize: { xs: '0.8rem', sm: '0.875rem' }
}}>
{exp.description}
</Typography>
{exp.skills && exp.skills.length > 0 && (
<Box sx={{ mt: { xs: 1, sm: 2 } }}>
{exp.skills.map((skill, skillIndex) => (
<Chip
key={skillIndex}
label={skill}
size="small"
sx={{
mr: 0.5,
mb: 0.5,
fontSize: { xs: '0.65rem', sm: '0.75rem' },
height: { xs: 20, sm: 24 }
}}
/>
))}
</Box>
)}
</Box>
<IconButton
onClick={() => handleRemoveExperience(index)}
color="error"
size="small"
sx={{
alignSelf: { xs: 'flex-end', sm: 'flex-start' },
ml: { sm: 1 }
}}
>
<Delete sx={{ fontSize: { xs: 16, sm: 20 } }} />
</IconButton>
</Box>
</CardContent>
</Card>
))}
{(!formData.experience || formData.experience.length === 0) && (
<Typography variant="body2" color="text.secondary" sx={{
textAlign: 'center',
py: { xs: 2, sm: 4 },
fontSize: { xs: '0.8rem', sm: '0.875rem' }
}}>
No work experience added yet. Click "Add Experience" to get started.
</Typography>
)}
</Box>
);
const renderEducation = () => (
<Box>
<Box sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
justifyContent: 'space-between',
alignItems: { xs: 'stretch', sm: 'center' },
mb: { xs: 2, sm: 3 },
gap: { xs: 1, sm: 0 }
}}>
<Typography variant={isMobile ? "subtitle1" : "h6"}>Education</Typography>
<Button
variant="outlined"
startIcon={<Add />}
fullWidth={isMobile}
size={isMobile ? "small" : "medium"}
>
Add Education
</Button>
</Box>
{(!formData.experience || formData.experience.length === 0) && (
<Typography variant="body2" color="text.secondary" sx={{
textAlign: 'center',
py: { xs: 2, sm: 4 },
fontSize: { xs: '0.8rem', sm: '0.875rem' }
}}>
No work experience added yet. Click "Add Experience" to get started.
</Typography>
)}
</Box>
);
return (
<Container maxWidth="lg" sx={{
mt: { xs: 1, sm: 4 },
mb: { xs: 1, sm: 4 },
px: { xs: 0.5, sm: 3 }
}}>
<Paper elevation={3} sx={{
overflow: 'hidden',
mx: { xs: 0, sm: 0 }
}}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs
value={tabValue}
onChange={handleTabChange}
variant="scrollable"
scrollButtons="auto"
allowScrollButtonsMobile
sx={{
'& .MuiTabs-flexContainer': {
justifyContent: isMobile ? 'flex-start' : 'center'
},
'& .MuiTab-root': {
fontSize: { xs: '0.75rem', sm: '0.875rem' },
minWidth: { xs: 60, sm: 120 },
padding: { xs: '6px 8px', sm: '12px 16px' }
}
}}
>
<Tab
label={isMobile ? "Info" : "Basic Info"}
icon={<AccountCircle sx={{ fontSize: { xs: 18, sm: 24 } }} />}
iconPosition={isMobile ? "top" : "start"}
/>
<Tab
label="Skills"
icon={<EmojiEvents sx={{ fontSize: { xs: 18, sm: 24 } }} />}
iconPosition={isMobile ? "top" : "start"}
/>
<Tab
label={isMobile ? "Work" : "Experience"}
icon={<Work sx={{ fontSize: { xs: 18, sm: 24 } }} />}
iconPosition={isMobile ? "top" : "start"}
/>
<Tab
label={isMobile ? "Edu" : "Education"}
icon={<School sx={{ fontSize: { xs: 18, sm: 24 } }} />}
iconPosition={isMobile ? "top" : "start"}
/>
</Tabs>
</Box>
<TabPanel value={tabValue} index={0}>
{renderBasicInfo()}
</TabPanel>
<TabPanel value={tabValue} index={1}>
<ComingSoon>{renderSkills()}</ComingSoon>
</TabPanel>
<TabPanel value={tabValue} index={2}>
<ComingSoon>{renderExperience()}</ComingSoon>
</TabPanel>
<TabPanel value={tabValue} index={3}>
<ComingSoon>{renderEducation()}</ComingSoon>
</TabPanel>
</Paper>
{/* Add Skill Dialog */}
<Dialog
open={skillDialog}
onClose={() => setSkillDialog(false)}
maxWidth="sm"
fullWidth
fullScreen={isMobile}
PaperProps={{
sx: {
...(isMobile && {
margin: 0,
width: '100%',
height: '100%',
maxHeight: '100%'
})
}
}}
>
<DialogTitle sx={{ pb: { xs: 1, sm: 2 } }}>Add New Skill</DialogTitle>
<DialogContent
sx={{
overflow: 'auto',
pt: { xs: 1, sm: 2 }
}}
>
<Grid container spacing={{ xs: 1.5, sm: 2 }} sx={{ mt: 0.5, maxWidth: '100%' }}>
<Grid size={{ xs: 12 }}>
<TextField
fullWidth
label="Skill Name"
value={newSkill.name || ''}
onChange={(e) => setNewSkill({ ...newSkill, name: e.target.value })}
size={isMobile ? "small" : "medium"}
/>
</Grid>
<Grid size={{ xs: 12 }}>
<TextField
fullWidth
label="Category"
value={newSkill.category || ''}
onChange={(e) => setNewSkill({ ...newSkill, category: e.target.value })}
placeholder="e.g., Programming, Design, Marketing"
size={isMobile ? "small" : "medium"}
/>
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<FormControl fullWidth size={isMobile ? "small" : "medium"}>
<InputLabel>Proficiency Level</InputLabel>
<Select
value={newSkill.level || 'beginner'}
onChange={(e) => setNewSkill({ ...newSkill, level: e.target.value as Types.SkillLevel })}
label="Proficiency Level"
>
<MenuItem value="beginner">Beginner</MenuItem>
<MenuItem value="intermediate">Intermediate</MenuItem>
<MenuItem value="advanced">Advanced</MenuItem>
<MenuItem value="expert">Expert</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<TextField
fullWidth
type="number"
label="Years of Experience"
value={newSkill.yearsOfExperience || 0}
onChange={(e) => setNewSkill({ ...newSkill, yearsOfExperience: parseInt(e.target.value) || 0 })}
size={isMobile ? "small" : "medium"}
/>
</Grid>
</Grid>
</DialogContent>
<DialogActions sx={{
p: { xs: 1.5, sm: 3 },
flexDirection: { xs: 'column', sm: 'row' },
gap: { xs: 1, sm: 0 }
}}>
<Button
onClick={() => setSkillDialog(false)}
fullWidth={isMobile}
size={isMobile ? "small" : "medium"}
>
Cancel
</Button>
<Button
onClick={handleAddSkill}
variant="contained"
fullWidth={isMobile}
size={isMobile ? "small" : "medium"}
>
Add Skill
</Button>
</DialogActions>
</Dialog>
{/* Add Experience Dialog */}
<Dialog
open={experienceDialog}
onClose={() => setExperienceDialog(false)}
maxWidth="md"
fullWidth
fullScreen={isMobile}
PaperProps={{
sx: {
...(isMobile && {
margin: 0,
width: '100%',
height: '100%',
maxHeight: '100%'
})
}
}}
>
<DialogTitle sx={{ pb: { xs: 1, sm: 2 } }}>Add Work Experience</DialogTitle>
<DialogContent
sx={{
overflow: 'auto',
pt: { xs: 1, sm: 2 }
}}
>
<Grid container spacing={{ xs: 1.5, sm: 2 }} sx={{ mt: 0.5, maxWidth: '100%' }}>
<Grid size={{ xs: 12, sm: 6 }}>
<TextField
fullWidth
label="Company Name"
value={newExperience.companyName || ''}
onChange={(e) => setNewExperience({ ...newExperience, companyName: e.target.value })}
size={isMobile ? "small" : "medium"}
/>
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<TextField
fullWidth
label="Position/Title"
value={newExperience.position || ''}
onChange={(e) => setNewExperience({ ...newExperience, position: e.target.value })}
size={isMobile ? "small" : "medium"}
/>
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<TextField
fullWidth
type="date"
label="Start Date"
value={newExperience.startDate?.toISOString().split('T')[0] || ''}
onChange={(e) => setNewExperience({ ...newExperience, startDate: new Date(e.target.value) })}
InputLabelProps={{ shrink: true }}
size={isMobile ? "small" : "medium"}
/>
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<FormControlLabel
control={
<Switch
checked={newExperience.isCurrent || false}
onChange={(e) => setNewExperience({ ...newExperience, isCurrent: e.target.checked })}
size={isMobile ? "small" : "medium"}
/>
}
label="Currently working here"
sx={{
'& .MuiFormControlLabel-label': {
fontSize: { xs: '0.875rem', sm: '1rem' }
}
}}
/>
</Grid>
<Grid size={{ xs: 12 }}>
<TextField
fullWidth
multiline
rows={isMobile ? 3 : 4}
label="Job Description"
value={newExperience.description || ''}
onChange={(e) => setNewExperience({ ...newExperience, description: e.target.value })}
placeholder="Describe your responsibilities and achievements..."
size={isMobile ? "small" : "medium"}
/>
</Grid>
</Grid>
</DialogContent>
<DialogActions sx={{
p: { xs: 1.5, sm: 3 },
flexDirection: { xs: 'column', sm: 'row' },
gap: { xs: 1, sm: 0 }
}}>
<Button
onClick={() => setExperienceDialog(false)}
fullWidth={isMobile}
size={isMobile ? "small" : "medium"}
>
Cancel
</Button>
<Button
onClick={handleAddExperience}
variant="contained"
fullWidth={isMobile}
size={isMobile ? "small" : "medium"}
>
Add Experience
</Button>
</DialogActions>
</Dialog>
{/* Snackbar for notifications */}
<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 { CandidateProfile };