import React, { useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { X, ExternalLink, ThumbsUp, ThumbsDown, BrainCircuit } from 'lucide-react'; import FullProfileOverlay from './FullProfileOverlay'; import { saveAs } from 'file-saver'; import { supabase } from '../supabaseClient'; const CandidateDrawer = ({ isOpen, onClose, candidate }) => { const [showFullProfile, setShowFullProfile] = useState(false); const [analysis, setAnalysis] = useState(null); const [loadingAnalysis, setLoadingAnalysis] = useState(false); React.useEffect(() => { if (isOpen && candidate?.userId && !analysis) { fetchAnalysis(); } }, [isOpen, candidate, analysis]); // Reset analysis when candidate changes React.useEffect(() => { setAnalysis(null); }, [candidate?.id]); const fetchAnalysis = async () => { setLoadingAnalysis(true); try { const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; const response = await fetch(`${API_URL}/analyze-candidate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ candidate_id: candidate.userId, job_id: candidate.jobId }) }); const result = await response.json(); if (result.status === 'success') { setAnalysis(result.data); } } catch (error) { console.error("Failed to fetch analysis:", error); } finally { setLoadingAnalysis(false); } }; const handleDownloadCV = async () => { let url = candidate.resumeUrl; if (Array.isArray(url)) url = url[0]; url = (url || '').trim(); if (!url) { alert("No resume available for this candidate."); return; } try { if (url.startsWith('http')) { const response = await fetch(url); const blob = await response.blob(); saveAs(blob, `${candidate.name.replace(/\s+/g, '_')}_Resume.pdf`); return; } // 1. Try common bucket and path combinations const tryDownload = async (path) => { const { data, error } = await supabase.storage.from('resume').createSignedUrl(path, 60); if (!error && data?.signedUrl) return data.signedUrl; // Try 'resumes' plural bucket too const fallback = await supabase.storage.from('resumes').createSignedUrl(path, 60); if (!fallback.error && fallback.data?.signedUrl) return fallback.data.signedUrl; return null; }; // Path strategy 1: Use as stored let signedUrl = await tryDownload(url); // Path strategy 2: If no folder in path, try prepending userID (as per user clarification) if (!signedUrl && !url.includes('/') && candidate.userId) { const prefixedPath = `${candidate.userId}/${url}`; console.log(`Retrying with userID prefix: ${prefixedPath}`); signedUrl = await tryDownload(prefixedPath); } if (signedUrl) { window.open(signedUrl, '_blank'); } else { throw new Error("File not found in storage"); } } catch (error) { console.error("Download failed:", error); // 2. Final Fallback: Attempt Public URL (best guess) try { const { data: { publicUrl } } = supabase.storage.from('resume').getPublicUrl(url); window.open(publicUrl, '_blank'); } catch (finalError) { alert(`Could not access the file. Please ensure the resume is in the 'resume' bucket inside a folder named '${candidate.userId}'.`); } } }; if (!candidate) return null; const summary = analysis?.summary || candidate.summary || "No summary available."; const strengths = analysis?.strengths || candidate.insights?.strengths || []; const weaknesses = analysis?.weaknesses || candidate.insights?.weaknesses || []; const missingSkills = analysis?.missing_skills || []; return ( {isOpen && ( <>
{/* Header */}

{candidate.name}

{candidate.jobTitle || candidate.role} • {candidate.experience}

{analysis?.score !== undefined ? 'Gemini AI Score' : 'Semantic Match Score'} {analysis?.score ?? (candidate.score || candidate.matchScore || 0)}%
{loadingAnalysis && (
Analyzing candidate with AI...
)} {/* AI Insights Section */}

AI Insights

{strengths.map((str, i) => (
{str}
))} {weaknesses.map((wk, i) => (
{wk}
))}
{/* Missing Skills Section */} {missingSkills.length > 0 && (

Missing Skills (Gap Analysis)

{missingSkills.map((skill, i) => ( {skill} ))}
)} {/* Resume Summary */}

AI Professional Summary

{summary}

{/* Portfolio & Projects */}

Portfolio & Projects

{candidate.projects?.map((project, i) => (
{typeof project === 'object' ? (project.title || project.name) : project}
))}
{/* Footer Actions */}
{showFullProfile && ( setShowFullProfile(false)} /> )} )}
); }; export default CandidateDrawer;