Initial commit
This commit is contained in:
73
src/components/krow/KROWCandidateLeaderboard.jsx
Normal file
73
src/components/krow/KROWCandidateLeaderboard.jsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const TIERS = [
|
||||
{ label: 'Top Talent', range: '80-100', color: 'bg-[#1E3A8A]', textColor: 'text-[#1E3A8A]' },
|
||||
{ label: 'Strong Fit', range: '60-79', color: 'bg-[#60A5FA]', textColor: 'text-[#2563EB]' },
|
||||
{ label: 'Possible Fit', range: '40-59', color: 'bg-[#93C5FD]', textColor: 'text-[#3B82F6]' },
|
||||
{ label: 'Not a Fit', range: '0-39', color: 'bg-[#E5E7EB]', textColor: 'text-[#9CA3AF]' },
|
||||
];
|
||||
|
||||
function getTier(score) {
|
||||
if (score >= 80) return 0;
|
||||
if (score >= 60) return 1;
|
||||
if (score >= 40) return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
export default function KROWCandidateLeaderboard({ applications, maxBars = 4 }) {
|
||||
const screened = applications.filter(a => a.ai_score > 0 || a.status !== 'applied');
|
||||
const counts = TIERS.map((_, i) => screened.filter(a => getTier(a.ai_score || 0) === i).length);
|
||||
const total = counts.reduce((a, b) => a + b, 0) || 1;
|
||||
const sorted = [...screened].sort((a, b) => (b.ai_score || 0) - (a.ai_score || 0));
|
||||
|
||||
return (
|
||||
<div className="bg-white border border-[#E5E7EB] rounded-xl p-6">
|
||||
<h3 className="font-semibold text-[#111827] text-[15px] mb-5">Candidate Pipeline</h3>
|
||||
|
||||
{/* Bar chart */}
|
||||
<div className="space-y-3 mb-6">
|
||||
{TIERS.map((tier, i) => {
|
||||
const count = counts[i];
|
||||
const pct = (count / total) * 100;
|
||||
return (
|
||||
<div key={tier.label}>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-[12px] font-medium text-[#374151]">{tier.label} <span className="text-[#9CA3AF]">({tier.range})</span></span>
|
||||
<span className="text-[13px] font-bold text-[#111827]">{count}</span>
|
||||
</div>
|
||||
<div className="h-2.5 bg-[#F3F4F6] rounded-full overflow-hidden">
|
||||
<div
|
||||
className={cn('h-full rounded-full transition-all duration-500', tier.color)}
|
||||
style={{ width: `${Math.max(pct, count > 0 ? 4 : 0)}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Ranked list */}
|
||||
<div className="space-y-2">
|
||||
{sorted.slice(0, 5).map((app, idx) => (
|
||||
<div key={app.id} className="flex items-center gap-3 py-2">
|
||||
<span className="text-[12px] font-bold text-[#9CA3AF] w-6">#{idx + 1}</span>
|
||||
<div className="w-7 h-7 rounded-full bg-[#0A39DF] flex items-center justify-center text-white text-[10px] font-semibold shrink-0">
|
||||
{app.applicant_name?.split(' ').map(w => w[0]).slice(0, 2).join('').toUpperCase()}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-[13px] font-medium text-[#111827] truncate">{app.applicant_name}</div>
|
||||
<div className="text-[11px] text-[#6B7280] truncate">{app.job_title || 'Applicant'}</div>
|
||||
</div>
|
||||
<span className={cn('text-[13px] font-bold', TIERS[getTier(app.ai_score || 0)].textColor)}>
|
||||
{app.ai_score || 0}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
{sorted.length === 0 && (
|
||||
<div className="text-center py-8 text-[13px] text-[#9CA3AF]">No screened candidates yet</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user