iris_backend / src /components /FullProfileOverlay.jsx
Saandraahh's picture
Added AI score and Insights
35709fb
import React from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { X, Mail, Phone, MapPin, Briefcase, GraduationCap, Award, User, Loader2 } from 'lucide-react';
import { supabase } from '../supabaseClient';
const FullProfileOverlay = ({ candidate, onClose }) => {
const [profile, setProfile] = React.useState(null);
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
const fetchFullProfile = async () => {
if (!candidate?.userId && !candidate?.id) return;
setLoading(true);
try {
const targetId = candidate.userId || candidate.id;
const { data, error } = await supabase
.from('profiles')
.select('*')
.eq('id', targetId)
.single();
if (error) throw error;
setProfile(data);
} catch (error) {
console.error("Error fetching full profile:", error);
setProfile(candidate);
} finally {
setLoading(false);
}
};
fetchFullProfile();
}, [candidate]);
if (!candidate) return null;
if (loading) {
return (
<div style={{ position: 'fixed', inset: 0, zIndex: 60, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0, 0, 0, 0.7)', backdropFilter: 'blur(8px)' }}>
<motion.div animate={{ rotate: 360 }} transition={{ repeat: Infinity, duration: 1, ease: 'linear' }}>
<Loader2 size={48} color="#EF4444" />
</motion.div>
</div>
);
}
const displayData = profile || candidate;
// Helper to safely parse skills (array or CSV string)
const parseSkills = (data) => {
if (Array.isArray(data)) return data;
if (typeof data === 'string') return data.split(',').map(s => s.trim()).filter(Boolean);
return [];
};
const allSkills = [...new Set([
...parseSkills(displayData.skills),
...parseSkills(displayData.technical_skills)
])];
const fullCandidate = {
...displayData,
name: displayData.full_name || displayData.name || 'No Name Provided',
role: displayData.current_position || displayData.role || 'Applicant',
email: displayData.email || 'No email available',
phone: displayData.phone || 'No phone provided',
location: displayData.location || 'Remote',
avatar: displayData.avatar_url || displayData.avatar || `https://ui-avatars.com/api/?name=${encodeURIComponent(displayData.full_name || 'User')}`,
about: displayData.summary || displayData.headline || "No summary available.",
skills: allSkills,
education: Array.isArray(displayData.education) ? displayData.education : [],
experience_details: Array.isArray(displayData.work_experience) ? displayData.work_experience : [],
projects: Array.isArray(displayData.projects) ? displayData.projects : []
};
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
style={{
position: 'fixed',
inset: 0,
zIndex: 60, // Higher than Drawer (50)
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
backdropFilter: 'blur(8px)',
padding: '2rem'
}}
>
<motion.div
initial={{ scale: 0.9, opacity: 0, y: 20 }}
animate={{ scale: 1, opacity: 1, y: 0 }}
exit={{ scale: 0.9, opacity: 0, y: 20 }}
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
style={{
width: '100%',
maxWidth: '900px',
maxHeight: '90vh',
overflowY: 'auto',
backgroundColor: '#0f172a',
backgroundImage: `
radial-gradient(at 0% 0%, rgba(56, 189, 248, 0.15) 0px, transparent 50%),
radial-gradient(at 100% 100%, rgba(239, 68, 68, 0.15) 0px, transparent 50%),
linear-gradient(135deg, rgba(255,255,255,0.03) 0%, transparent 100%)
`,
borderRadius: '1.5rem',
border: '1px solid rgba(255,255,255,0.1)',
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.5)',
color: 'white',
display: 'flex',
flexDirection: 'column'
}}
>
{/* === Header === */}
<div style={{
padding: '2rem',
borderBottom: '1px solid rgba(255,255,255,0.1)',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'start',
position: 'sticky',
top: 0,
backgroundColor: 'rgba(15, 23, 42, 0.95)',
backdropFilter: 'blur(4px)',
zIndex: 10
}}>
<div style={{ display: 'flex', gap: '1.5rem', alignItems: 'center' }}>
<div style={{
width: '80px',
height: '80px',
borderRadius: '50%',
overflow: 'hidden',
border: '3px solid rgba(255,255,255,0.1)',
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)'
}}>
<img
src={fullCandidate.avatar || `https://ui-avatars.com/api/?name=${encodeURIComponent(fullCandidate.name)}&background=random`}
alt={fullCandidate.name}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/>
</div>
<div>
<h2 style={{ fontSize: '2rem', fontWeight: 'bold', margin: 0, lineHeight: 1.2 }}>{fullCandidate.name}</h2>
<p style={{ color: '#94a3b8', fontSize: '1.1rem', margin: '0.25rem 0 0 0' }}>{fullCandidate.role}</p>
<div style={{ display: 'flex', gap: '1rem', marginTop: '0.75rem', fontSize: '0.9rem', color: '#cbd5e1' }}>
<span style={{ display: 'flex', alignItems: 'center', gap: '6px' }}><Mail size={14} /> {fullCandidate.email}</span>
<span style={{ display: 'flex', alignItems: 'center', gap: '6px' }}><Phone size={14} /> {fullCandidate.phone}</span>
<span style={{ display: 'flex', alignItems: 'center', gap: '6px' }}><MapPin size={14} /> {fullCandidate.location}</span>
</div>
</div>
</div>
<button
onClick={onClose}
style={{
padding: '0.75rem',
borderRadius: '0.75rem',
backgroundColor: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.1)',
color: '#94a3b8',
cursor: 'pointer',
transition: 'all 0.2s',
display: 'flex', alignItems: 'center', justifyContent: 'center'
}}
onMouseOver={(e) => { e.currentTarget.style.backgroundColor = 'rgba(239, 68, 68, 0.2)'; e.currentTarget.style.color = 'white'; }}
onMouseOut={(e) => { e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.05)'; e.currentTarget.style.color = '#94a3b8'; }}
>
<X size={24} />
</button>
</div>
{/* === Content Body === */}
<div style={{ padding: '2rem', display: 'grid', gridTemplateColumns: 'minmax(300px, 1fr) 2fr', gap: '3rem' }}>
{/* Left Column */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
{/* About */}
<section>
<h3 style={{ display: 'flex', alignItems: 'center', gap: '8px', fontSize: '1.1rem', fontWeight: '600', color: 'white', marginBottom: '1rem' }}>
<User size={18} color="#38bdf8" /> About
</h3>
<p style={{ color: '#94a3b8', lineHeight: '1.7', fontSize: '0.95rem' }}>
{fullCandidate.about}
</p>
</section>
{/* Skills */}
<section>
<h3 style={{ display: 'flex', alignItems: 'center', gap: '8px', fontSize: '1.1rem', fontWeight: '600', color: 'white', marginBottom: '1rem' }}>
<Award size={18} color="#c084fc" /> Skills
</h3>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
{fullCandidate.skills?.map((skill, i) => (
<span key={i} style={{
fontSize: '0.85rem',
backgroundColor: 'rgba(56, 189, 248, 0.1)',
color: '#38bdf8',
padding: '0.4rem 0.8rem',
borderRadius: '6px',
border: '1px solid rgba(56, 189, 248, 0.2)'
}}>
{skill}
</span>
))}
</div>
</section>
{/* Projects Pointers */}
<section>
<h3 style={{ display: 'flex', alignItems: 'center', gap: '8px', fontSize: '1.1rem', fontWeight: '600', color: 'white', marginBottom: '1rem' }}>
<Briefcase size={18} color="#34d399" /> Key Projects
</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
{fullCandidate.projects?.map((project, i) => (
<div key={i} style={{ padding: '0.75rem', backgroundColor: 'rgba(255,255,255,0.03)', borderRadius: '0.5rem', border: '1px solid rgba(255,255,255,0.05)' }}>
<div style={{ color: '#e2e8f0', fontSize: '0.95rem', fontWeight: '600' }}>
{typeof project === 'object' ? (project.title || project.name) : project}
</div>
{typeof project === 'object' && project.description && (
<div style={{ color: '#94a3b8', fontSize: '0.85rem', marginTop: '0.25rem' }}>
{project.description}
</div>
)}
</div>
))}
</div>
</section>
</div>
{/* Right Column */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
{/* Experience Timeline */}
<section>
<h3 style={{ display: 'flex', alignItems: 'center', gap: '8px', fontSize: '1.1rem', fontWeight: '600', color: 'white', marginBottom: '1.5rem' }}>
<Briefcase size={18} color="#fcd34d" /> Experience
</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem', position: 'relative', paddingLeft: '1rem' }}>
{/* Vertical Line */}
<div style={{ position: 'absolute', left: 0, top: '10px', bottom: '10px', width: '2px', backgroundColor: 'rgba(255,255,255,0.1)' }} />
{fullCandidate.experience_details.map((exp, i) => (
<div key={i} style={{ position: 'relative', paddingLeft: '1.5rem' }}>
{/* Dot */}
<div style={{ position: 'absolute', left: '-5px', top: '6px', width: '12px', height: '12px', borderRadius: '50%', backgroundColor: '#fcd34d', boxShadow: '0 0 10px rgba(252, 211, 77, 0.5)' }} />
<h4 style={{ color: 'white', fontWeight: '600', fontSize: '1.05rem', marginBottom: '0.25rem' }}>{exp.role || exp.job_title}</h4>
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.9rem', color: '#94a3b8', marginBottom: '0.5rem' }}>
<span>{exp.company}</span>
<span>{exp.duration || exp.years}</span>
</div>
<p style={{ color: '#cbd5e1', fontSize: '0.95rem', lineHeight: '1.6' }}>{exp.description}</p>
</div>
))}
</div>
</section>
{/* Education */}
<section>
<h3 style={{ display: 'flex', alignItems: 'center', gap: '8px', fontSize: '1.1rem', fontWeight: '600', color: 'white', marginBottom: '1rem' }}>
<GraduationCap size={18} color="#f472b6" /> Education
</h3>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
{fullCandidate.education.map((edu, i) => (
<div key={i} style={{ padding: '1rem', backgroundColor: 'rgba(255,255,255,0.03)', borderRadius: '0.75rem', border: '1px solid rgba(255,255,255,0.05)' }}>
<h4 style={{ color: 'white', fontWeight: '600', fontSize: '1rem', marginBottom: '0.25rem' }}>{edu.degree || edu.course}</h4>
<p style={{ color: '#94a3b8', fontSize: '0.9rem' }}>{edu.school || edu.institution}</p>
<p style={{ color: '#64748b', fontSize: '0.85rem', marginTop: '0.5rem' }}>{edu.year}</p>
</div>
))}
</div>
</section>
</div>
</div>
{/* === Footer === */}
<div style={{
padding: '1.5rem 2rem',
borderTop: '1px solid rgba(255,255,255,0.1)',
backgroundColor: 'rgba(15, 23, 42, 0.5)',
display: 'flex',
justifyContent: 'flex-end',
gap: '1rem'
}}>
<button
onClick={onClose}
style={{
padding: '0.75rem 1.5rem',
borderRadius: '0.5rem',
border: '1px solid rgba(255,255,255,0.2)',
backgroundColor: 'transparent',
color: 'white',
fontWeight: '600',
cursor: 'pointer'
}}
>
Close
</button>
</div>
</motion.div>
</motion.div>
);
};
export default FullProfileOverlay;