File size: 7,437 Bytes
ab13a8a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | 'use client';
import React, { useState } from 'react';
import { motion } from 'framer-motion';
import { X, ShieldCheck, Zap, Briefcase, Cpu, TrendingUp, Info, Search } from 'lucide-react';
export default function CandidateDetail({ evaluation, onClose }) {
const [activeTab, setActiveTab] = useState('Synthesis');
const agents = [
{ name: 'Synthesis', icon: ShieldCheck },
{ name: 'Signal Extraction', icon: Zap },
{ name: 'Explanation', icon: Info },
{ name: 'Founder Eval', icon: TrendingUp },
{ name: 'HR Agent', icon: Search },
{ name: 'Tech Agent', icon: Cpu },
{ name: 'Business Agent', icon: Briefcase },
];
const getAgentContent = (name) => {
if (name === 'Synthesis') return evaluation.synthesis;
const agent = evaluation.agent_outputs.find(a => a.agent_name === name);
return agent ? agent.content : 'No output available for this agent.';
};
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-[100] bg-black/40 backdrop-blur-sm flex justify-end"
onClick={onClose}
>
<motion.div
initial={{ x: '100%' }}
animate={{ x: 0 }}
exit={{ x: '100%' }}
transition={{ type: 'spring', damping: 30, stiffness: 300 }}
className="w-full max-w-2xl bg-white dark:bg-slate-950 h-full shadow-2xl flex flex-col"
onClick={e => e.stopPropagation()}
>
{/* Header */}
<div className="p-8 border-b border-slate-100 dark:border-slate-800 flex justify-between items-start">
<div>
<span className="text-xs font-bold text-indigo-600 uppercase tracking-widest mb-1 block">Full Report</span>
<h2 className="text-3xl font-bold text-slate-900 dark:text-white">{evaluation.name}</h2>
<div className="flex items-center gap-4 mt-2">
<div className="flex items-center gap-1.5">
<div className="w-2 h-2 rounded-full bg-emerald-500"></div>
<span className="text-sm font-medium text-slate-500">{evaluation.decision}</span>
</div>
<div className="text-sm text-slate-400">Score: {Math.round(evaluation.final_score)}/100</div>
</div>
</div>
<button
onClick={onClose}
className="p-2 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-full transition-colors"
>
<X className="w-6 h-6 text-slate-400" />
</button>
</div>
{/* Content Area */}
<div className="flex flex-1 overflow-hidden">
{/* Sidebar Tabs */}
<div className="w-16 md:w-48 border-r border-slate-100 dark:border-slate-900 bg-slate-50/50 dark:bg-slate-900/20 py-6">
{agents.map((agent) => {
const Icon = agent.icon;
const isActive = activeTab === agent.name;
return (
<button
key={agent.name}
onClick={() => setActiveTab(agent.name)}
className={`w-full flex items-center gap-3 px-4 py-3 text-sm font-medium transition-all ${
isActive
? 'text-indigo-600 bg-white dark:bg-slate-900 shadow-sm border-r-2 border-indigo-600'
: 'text-slate-400 hover:text-slate-600 dark:hover:text-slate-300'
}`}
>
<Icon className="w-5 h-5 flex-shrink-0" />
<span className="hidden md:block truncate">{agent.name}</span>
</button>
);
})}
</div>
{/* Main Content */}
<div className="flex-1 overflow-y-auto p-8">
<AnimatePresence mode="wait">
<motion.div
key={activeTab}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="prose dark:prose-invert max-w-none"
>
<div className="mb-6 flex items-center justify-between">
<h3 className="text-xl font-bold m-0">{activeTab} Output</h3>
</div>
{activeTab === 'Synthesis' ? (
<div className="space-y-6">
<div className="p-5 bg-indigo-50 dark:bg-indigo-900/20 rounded-2xl border border-indigo-100 dark:border-indigo-800">
<h4 className="text-indigo-900 dark:text-indigo-300 font-bold mb-2">Final Summary</h4>
<p className="text-sm leading-relaxed text-indigo-800 dark:text-indigo-400">{evaluation.synthesis}</p>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="p-4 bg-emerald-50 dark:bg-emerald-900/10 rounded-xl border border-emerald-100 dark:border-emerald-800/50">
<h4 className="text-emerald-700 dark:text-emerald-400 font-bold text-sm mb-3">Key Strengths</h4>
<ul className="space-y-2">
{evaluation.strengths.map((s, i) => (
<li key={i} className="text-xs flex items-start gap-2 text-emerald-800/70 dark:text-emerald-500/70">
<span className="block w-1 h-1 rounded-full bg-emerald-400 mt-1.5 flex-shrink-0"></span>
{s}
</li>
))}
</ul>
</div>
<div className="p-4 bg-rose-50 dark:bg-rose-900/10 rounded-xl border border-rose-100 dark:border-rose-800/50">
<h4 className="text-rose-700 dark:text-rose-400 font-bold text-sm mb-3">Potential Risks</h4>
<ul className="space-y-2">
{evaluation.risks.map((r, i) => (
<li key={i} className="text-xs flex items-start gap-2 text-rose-800/70 dark:text-rose-500/70">
<span className="block w-1 h-1 rounded-full bg-rose-400 mt-1.5 flex-shrink-0"></span>
{r}
</li>
))}
</ul>
</div>
</div>
<div className="grid grid-cols-5 gap-2">
{Object.entries(evaluation.scores).map(([key, val]) => (
<div key={key} className="p-3 bg-slate-50 dark:bg-slate-900 rounded-lg text-center">
<div className="text-[10px] uppercase text-slate-400 font-bold mb-1">{key}</div>
<div className="text-sm font-bold text-slate-700 dark:text-slate-300">{val}</div>
</div>
))}
</div>
</div>
) : (
<div className="bg-slate-50 dark:bg-slate-900/50 p-6 rounded-2xl whitespace-pre-wrap text-sm leading-relaxed text-slate-600 dark:text-slate-400 border border-slate-100 dark:border-slate-800">
{getAgentContent(activeTab)}
</div>
)}
</motion.div>
</AnimatePresence>
</div>
</div>
</motion.div>
</motion.div>
);
}
|