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 ( ); } const CandidateProfile: React.FC = (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>({}); const [profileImage, setProfileImage] = useState(''); // Dialog states const [skillDialog, setSkillDialog] = useState(false); const [experienceDialog, setExperienceDialog] = useState(false); // New item states const [newSkill, setNewSkill] = useState>({ name: '', category: '', level: 'beginner', yearsOfExperience: 0 }); const [newExperience, setNewExperience] = useState>({ 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 ( Access denied. This page is only available for candidates. ); } // 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) => { 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 = () => ( {!profileImage && } {editMode.basic && ( <> Update profile photo )} {editMode.basic ? ( ) => 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}'.` })} {editMode.basic ? ( handleInputChange('firstName', e.target.value)} variant="outlined" /> ) : (<> First Name {candidate.firstName} )} {editMode.basic ? ( handleInputChange('lastName', e.target.value)} variant="outlined" /> ) : (<> Last Name {candidate.lastName} )} {(false && editMode.basic) ? ( handleInputChange('email', e.target.value)} variant="outlined" /> ) : (<> Email {candidate.email} )} {editMode.basic ? ( handleInputChange('phone', e.target.value)} variant="outlined" /> ) : (<> Phone {candidate.phone || 'Not provided'} )} {editMode.basic ? ( handleInputChange('description', e.target.value)} variant="outlined" /> ) : (<> Professional Summary {candidate.description || 'No summary provided'} )} {false && editMode.basic ? ( handleInputChange('location', { ...formData.location, city: e.target.value })} variant="outlined" placeholder="City, State, Country" /> ) : (<> Location {candidate.location?.city || 'Not specified'} {candidate.location?.country || ''} )} {editMode.basic ? ( <> ) : ( )} ); // Skills Tab const renderSkills = () => ( Skills & Expertise {(formData.skills || []).map((skill, index) => ( {skill.name} {skill.category} {skill.yearsOfExperience && ( {skill.yearsOfExperience} years experience )} handleRemoveSkill(index)} color="error" sx={{ ml: 1 }} > ))} {(!formData.skills || formData.skills.length === 0) && ( No skills added yet. Click "Add Skill" to get started. )} ); // Experience Tab const renderExperience = () => ( Work Experience {(formData.experience || []).map((exp, index) => ( {exp.position} {exp.companyName} {exp.startDate?.toLocaleDateString()} - {exp.isCurrent ? 'Present' : exp.endDate?.toLocaleDateString()} {exp.description} {exp.skills && exp.skills.length > 0 && ( {exp.skills.map((skill, skillIndex) => ( ))} )} handleRemoveExperience(index)} color="error" size="small" sx={{ alignSelf: { xs: 'flex-end', sm: 'flex-start' }, ml: { sm: 1 } }} > ))} {(!formData.experience || formData.experience.length === 0) && ( No work experience added yet. Click "Add Experience" to get started. )} ); const renderEducation = () => ( Education {(!formData.experience || formData.experience.length === 0) && ( No work experience added yet. Click "Add Experience" to get started. )} ); return ( } iconPosition={isMobile ? "top" : "start"} /> } iconPosition={isMobile ? "top" : "start"} /> } iconPosition={isMobile ? "top" : "start"} /> } iconPosition={isMobile ? "top" : "start"} /> {renderBasicInfo()} {renderSkills()} {renderExperience()} {renderEducation()} {/* Add Skill Dialog */} setSkillDialog(false)} maxWidth="sm" fullWidth fullScreen={isMobile} PaperProps={{ sx: { ...(isMobile && { margin: 0, width: '100%', height: '100%', maxHeight: '100%' }) } }} > Add New Skill setNewSkill({ ...newSkill, name: e.target.value })} size={isMobile ? "small" : "medium"} /> setNewSkill({ ...newSkill, category: e.target.value })} placeholder="e.g., Programming, Design, Marketing" size={isMobile ? "small" : "medium"} /> Proficiency Level setNewSkill({ ...newSkill, yearsOfExperience: parseInt(e.target.value) || 0 })} size={isMobile ? "small" : "medium"} /> {/* Add Experience Dialog */} setExperienceDialog(false)} maxWidth="md" fullWidth fullScreen={isMobile} PaperProps={{ sx: { ...(isMobile && { margin: 0, width: '100%', height: '100%', maxHeight: '100%' }) } }} > Add Work Experience setNewExperience({ ...newExperience, companyName: e.target.value })} size={isMobile ? "small" : "medium"} /> setNewExperience({ ...newExperience, position: e.target.value })} size={isMobile ? "small" : "medium"} /> setNewExperience({ ...newExperience, startDate: new Date(e.target.value) })} InputLabelProps={{ shrink: true }} size={isMobile ? "small" : "medium"} /> setNewExperience({ ...newExperience, isCurrent: e.target.checked })} size={isMobile ? "small" : "medium"} /> } label="Currently working here" sx={{ '& .MuiFormControlLabel-label': { fontSize: { xs: '0.875rem', sm: '1rem' } } }} /> setNewExperience({ ...newExperience, description: e.target.value })} placeholder="Describe your responsibilities and achievements..." size={isMobile ? "small" : "medium"} /> {/* Snackbar for notifications */} setSnackbar({ ...snackbar, open: false })} > setSnackbar({ ...snackbar, open: false })} severity={snackbar.severity} sx={{ width: '100%' }} > {snackbar.message} ); }; export { CandidateProfile };