Added question editing

This commit is contained in:
James Ketr 2025-06-27 11:00:10 -07:00
parent 4a95c72a6f
commit 1e04f2e070
2 changed files with 166 additions and 64 deletions

View File

@ -104,7 +104,7 @@ const capitalize = (str: string): string => {
}; };
// Main component // Main component
const JobAnalysisPage: React.FC<BackstoryPageProps> = (_props: BackstoryPageProps) => { const JobAnalysisPage: React.FC<BackstoryPageProps> = () => {
const theme = useTheme(); const theme = useTheme();
const { user, guest } = useAuth(); const { user, guest } = useAuth();
const { selectedCandidate, setSelectedCandidate } = useSelectedCandidate(); const { selectedCandidate, setSelectedCandidate } = useSelectedCandidate();

View File

@ -13,7 +13,6 @@ import {
Tab, Tab,
useMediaQuery, useMediaQuery,
CircularProgress, CircularProgress,
Snackbar,
Alert, Alert,
Card, Card,
CardContent, CardContent,
@ -49,7 +48,6 @@ import {
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import { useAuth } from 'hooks/AuthContext'; import { useAuth } from 'hooks/AuthContext';
import * as Types from 'types/types'; import * as Types from 'types/types';
import { ComingSoon } from 'components/ui/ComingSoon';
import { BackstoryPageProps } from 'components/BackstoryTab'; import { BackstoryPageProps } from 'components/BackstoryTab';
import { useAppState } from 'hooks/GlobalContext'; import { useAppState } from 'hooks/GlobalContext';
@ -98,9 +96,9 @@ function TabPanel(props: TabPanelProps): JSX.Element {
); );
} }
const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPageProps) => { const CandidateProfile: React.FC = () => {
const { setSnack } = useAppState();
const theme = useTheme(); const theme = useTheme();
const { setSnack } = useAppState();
const isMobile = useMediaQuery(theme.breakpoints.down('sm')); const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const { user, updateUserData, apiClient } = useAuth(); const { user, updateUserData, apiClient } = useAuth();
@ -111,15 +109,6 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
const [tabValue, setTabValue] = useState<string>('info'); const [tabValue, setTabValue] = useState<string>('info');
const [editMode, setEditMode] = useState<{ [key: string]: boolean }>({}); const [editMode, setEditMode] = useState<{ [key: string]: boolean }>({});
const [loading, setLoading] = useState(false); 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 // Form data state
const [formData, setFormData] = useState<Partial<Types.Candidate>>({}); const [formData, setFormData] = useState<Partial<Types.Candidate>>({});
@ -137,6 +126,11 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
level: 'beginner', level: 'beginner',
yearsOfExperience: 0, yearsOfExperience: 0,
}); });
const [newQuestion, setNewQuestion] = useState<Partial<Types.CandidateQuestion>>({
question: '',
});
const [newExperience, setNewExperience] = useState<Partial<Types.WorkExperience>>({ const [newExperience, setNewExperience] = useState<Partial<Types.WorkExperience>>({
companyName: '', companyName: '',
position: '', position: '',
@ -155,7 +149,6 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
} else { } else {
setProfileImage(''); setProfileImage('');
} }
console.log({ isPublic: candidate.isPublic });
} }
}, [candidate]); }, [candidate]);
@ -195,19 +188,11 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
console.log(`Set profile image to: ${candidate.profileImage}`); console.log(`Set profile image to: ${candidate.profileImage}`);
updateUserData(candidate); updateUserData(candidate);
} else { } else {
setSnackbar({ setSnack('Failed to upload profile image. Please try again.', 'error');
open: true,
message: 'Failed to upload profile image. Please try again.',
severity: 'error',
});
} }
} catch (error) { } catch (error) {
console.error('Error uploading profile image:', error); console.error('Error uploading profile image:', error);
setSnackbar({ setSnack('Failed to upload profile image. Please try again.', 'error');
open: true,
message: 'Failed to upload profile image. Please try again.',
severity: 'error',
});
} }
}; };
@ -230,11 +215,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
toggleEditMode(section); toggleEditMode(section);
} }
} catch (error) { } catch (error) {
setSnackbar({ setSnack('Failed to update profile. Please try again.', 'error');
open: true,
message: 'Failed to update profile. Please try again.',
severity: 'error',
});
} finally { } finally {
setLoading(false); setLoading(false);
} }
@ -258,6 +239,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
yearsOfExperience: 0, yearsOfExperience: 0,
}); });
setSkillDialog(false); setSkillDialog(false);
setSnack('Skill added successfully!');
} }
}; };
@ -265,6 +247,30 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
const handleRemoveSkill = (index: number): void => { const handleRemoveSkill = (index: number): void => {
const updatedSkills = (formData.skills || []).filter((_, i) => i !== index); const updatedSkills = (formData.skills || []).filter((_, i) => i !== index);
setFormData({ ...formData, skills: updatedSkills }); setFormData({ ...formData, skills: updatedSkills });
setSnack('Skill removed successfully!');
};
// Add new question
const handleAddQuestion = (): void => {
if (newQuestion.question?.trim()) {
const questionToAdd: Types.CandidateQuestion = {
question: newQuestion.question.trim(),
};
const updatedQuestions = [...(formData.questions || []), questionToAdd];
setFormData({ ...formData, questions: updatedQuestions });
setNewQuestion({
question: '',
});
setQuestionDialog(false);
setSnack('Question added successfully!');
}
};
// Remove question
const handleRemoveQuestion = (index: number): void => {
const updatedQuestions = (formData.questions || []).filter((_, i) => i !== index);
setFormData({ ...formData, questions: updatedQuestions });
setSnack('Question removed successfully!');
}; };
// Add new work experience // Add new work experience
@ -285,6 +291,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
location: { city: '', country: '' }, location: { city: '', country: '' },
}); });
setExperienceDialog(false); setExperienceDialog(false);
setSnack('Experience added successfully!');
} }
}; };
@ -292,6 +299,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
const handleRemoveExperience = (index: number): void => { const handleRemoveExperience = (index: number): void => {
const updatedExperience = (formData.experience || []).filter((_, i) => i !== index); const updatedExperience = (formData.experience || []).filter((_, i) => i !== index);
setFormData({ ...formData, experience: updatedExperience }); setFormData({ ...formData, experience: updatedExperience });
setSnack('Experience removed successfully!');
}; };
// Basic Information Tab // Basic Information Tab
@ -676,14 +684,27 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
sx={{ sx={{
fontSize: { xs: '0.9rem', sm: '1.25rem' }, fontSize: { xs: '0.9rem', sm: '1.25rem' },
wordBreak: 'break-word', wordBreak: 'break-word',
mb: 1,
}} }}
> >
{question.question} {question.question}
</Typography> </Typography>
{/* {question.category && (
<Chip
size="small"
label={question.category}
color="secondary"
variant="outlined"
sx={{
fontSize: { xs: '0.65rem', sm: '0.75rem' },
height: { xs: 20, sm: 24 },
}}
/>
)} */}
</Box> </Box>
<IconButton <IconButton
size="small" size="small"
onClick={(): void => handleRemoveSkill(index)} onClick={(): void => handleRemoveQuestion(index)}
color="error" color="error"
sx={{ ml: 1 }} sx={{ ml: 1 }}
> >
@ -696,7 +717,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
))} ))}
</Grid> </Grid>
{(!formData.skills || formData.skills.length === 0) && ( {(!formData.questions || formData.questions.length === 0) && (
<Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', py: 4 }}> <Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', py: 4 }}>
No questions added yet. Click &quot;Add Question&quot; to get started. No questions added yet. Click &quot;Add Question&quot; to get started.
</Typography> </Typography>
@ -855,19 +876,17 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
</Button> </Button>
</Box> </Box>
{(!formData.experience || formData.experience.length === 0) && ( <Typography
<Typography variant="body2"
variant="body2" color="text.secondary"
color="text.secondary" sx={{
sx={{ textAlign: 'center',
textAlign: 'center', py: { xs: 2, sm: 4 },
py: { xs: 2, sm: 4 }, fontSize: { xs: '0.8rem', sm: '0.875rem' },
fontSize: { xs: '0.8rem', sm: '0.875rem' }, }}
}} >
> No education added yet. Click &quot;Add Education&quot; to get started.
No work experience added yet. Click &quot;Add Experience&quot; to get started. </Typography>
</Typography>
)}
</Box> </Box>
); );
@ -947,15 +966,15 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
</TabPanel> </TabPanel>
<TabPanel value="skills" active={tabValue}> <TabPanel value="skills" active={tabValue}>
<ComingSoon>{renderSkills()}</ComingSoon> {renderSkills()}
</TabPanel> </TabPanel>
<TabPanel value="experience" active={tabValue}> <TabPanel value="experience" active={tabValue}>
<ComingSoon>{renderExperience()}</ComingSoon> {renderExperience()}
</TabPanel> </TabPanel>
<TabPanel value="education" active={tabValue}> <TabPanel value="education" active={tabValue}>
<ComingSoon>{renderEducation()}</ComingSoon> {renderEducation()}
</TabPanel> </TabPanel>
</Paper> </Paper>
@ -1012,7 +1031,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
onChange={(e): void => onChange={(e): void =>
setNewSkill({ setNewSkill({
...newSkill, ...newSkill,
level: e.target.value as Types.SkillLevel, level: e.target.value as 'beginner' | 'intermediate' | 'advanced' | 'expert',
}) })
} }
label="Proficiency Level" label="Proficiency Level"
@ -1066,6 +1085,104 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
</DialogActions> </DialogActions>
</Dialog> </Dialog>
{/* Add Question Dialog */}
<Dialog
open={questionDialog}
onClose={(): void => setQuestionDialog(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 Question</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
multiline
rows={isMobile ? 3 : 4}
label="Question"
value={newQuestion.question || ''}
onChange={(e): void => setNewQuestion({ ...newQuestion, question: e.target.value })}
placeholder="What would you potential employers to ask about you?"
size={isMobile ? 'small' : 'medium'}
/>
</Grid>
{/* <Grid size={{ xs: 12, sm: 8 }}>
<TextField
fullWidth
label="Category (Optional)"
value={newQuestion.category || ''}
onChange={(e): void => setNewQuestion({ ...newQuestion, category: e.target.value })}
placeholder="e.g., General, Technical, Behavioral"
size={isMobile ? 'small' : 'medium'}
/>
</Grid> */}
{/* <Grid size={{ xs: 12, sm: 4 }}>
<FormControlLabel
control={
<Switch
checked={newQuestion.isActive !== false}
onChange={(e): void =>
setNewQuestion({
...newQuestion,
isActive: e.target.checked,
})
}
size={isMobile ? 'small' : 'medium'}
/>
}
label="Active"
sx={{
'& .MuiFormControlLabel-label': {
fontSize: { xs: '0.875rem', sm: '1rem' },
},
}}
/>
</Grid> */}
</Grid>
</DialogContent>
<DialogActions
sx={{
p: { xs: 1.5, sm: 3 },
flexDirection: { xs: 'column', sm: 'row' },
gap: { xs: 1, sm: 0 },
}}
>
<Button
onClick={(): void => setQuestionDialog(false)}
fullWidth={isMobile}
size={isMobile ? 'small' : 'medium'}
>
Cancel
</Button>
<Button
onClick={handleAddQuestion}
variant="contained"
fullWidth={isMobile}
size={isMobile ? 'small' : 'medium'}
disabled={!newQuestion.question?.trim()}
>
Add Question
</Button>
</DialogActions>
</Dialog>
{/* Add Experience Dialog */} {/* Add Experience Dialog */}
<Dialog <Dialog
open={experienceDialog} open={experienceDialog}
@ -1201,21 +1318,6 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (_props: BackstoryPagePro
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
{/* Snackbar for notifications */}
<Snackbar
open={snackbar.open}
autoHideDuration={6000}
onClose={(): void => setSnackbar({ ...snackbar, open: false })}
>
<Alert
onClose={(): void => setSnackbar({ ...snackbar, open: false })}
severity={snackbar.severity}
sx={{ width: '100%' }}
>
{snackbar.message}
</Alert>
</Snackbar>
</Container> </Container>
); );
}; };