Files
krow_talent_app/src/components/krow/HiringAnalytics.jsx
2026-08-28 11:02:02 +05:30

97 lines
4.5 KiB
JavaScript

import React, { useMemo } from 'react';
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Cell } from 'recharts';
const TIER_COLORS = { Skilled: '#0838E0', 'Cross-Trained': '#333F48', Beginner: '#D1E0D7' };
export default function HiringAnalytics({ applications, staff }) {
const byTier = useMemo(() => {
const tiers = ['Skilled', 'Cross-Trained', 'Beginner'];
return tiers.map(tier => ({
label: tier,
count: staff.filter(s => s.profile_tier === tier).length,
color: TIER_COLORS[tier],
}));
}, [staff]);
const byRole = useMemo(() => {
const counts = {};
staff.forEach(s => {
const role = s.role || 'Unassigned';
counts[role] = (counts[role] || 0) + 1;
});
return Object.entries(counts)
.map(([role, count]) => ({ role, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 6);
}, [staff]);
const hiredApps = applications.filter(a => a.status === 'hired');
const totalApps = applications.length;
const hireRate = totalApps ? Math.round((hiredApps.length / totalApps) * 100) : 0;
/* Averaged over the hires that carry a score. A hire can reach 'hired'
without ever being scored, and counting that as a 0 drags the figure down
by the number of people nobody scored rather than by anything about them. */
const scoredHires = hiredApps.filter(a => a.ai_score > 0);
const avgHireScore = scoredHires.length
? Math.round(scoredHires.reduce((s, a) => s + a.ai_score, 0) / scoredHires.length)
: 0;
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const recentHires = staff.filter(s => new Date(s.hire_date) >= sevenDaysAgo).length;
return (
<div className="glass-card border border-white/60 rounded-2xl p-6 shadow-sm">
<h3 className="font-heading font-semibold text-[#111827] text-[16px] mb-1">Hiring Analytics</h3>
<p className="text-[12px] text-[#6B7280] mb-5">{staff.length} total hires · {recentHires} in last 7 days</p>
<div className="grid grid-cols-3 gap-3 mb-6">
<div className="bg-[#F9FAFB] rounded-xl p-3 text-center border border-[#F3F4F6]">
<div className="text-2xl font-heading font-bold text-[#0838E0]">{hireRate}%</div>
<div className="text-[10px] text-[#9CA3AF] tracking-wide mt-0.5">HIRE RATE</div>
</div>
<div className="bg-[#F9FAFB] rounded-xl p-3 text-center border border-[#F3F4F6]">
<div className="text-2xl font-heading font-bold text-[#333F48]">{avgHireScore || '—'}</div>
<div className="text-[10px] text-[#9CA3AF] tracking-wide mt-0.5">AVG SCORE</div>
</div>
<div className="bg-[#F9FAFB] rounded-xl p-3 text-center border border-[#F3F4F6]">
<div className="text-2xl font-heading font-bold text-[#0838E0]">{recentHires}</div>
<div className="text-[10px] text-[#9CA3AF] tracking-wide mt-0.5">LAST 7 DAYS</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
<div>
<p className="text-[12px] font-medium text-[#374151] mb-3">Hires by Tier</p>
<ResponsiveContainer width="100%" height={180}>
<BarChart data={byTier}>
<CartesianGrid strokeDasharray="3 3" stroke="#F3F4F6" />
<XAxis dataKey="label" tick={{ fontSize: 10 }} stroke="#9CA3AF" />
<YAxis tick={{ fontSize: 11 }} stroke="#9CA3AF" allowDecimals={false} />
<Tooltip contentStyle={{ borderRadius: 8, border: '1px solid #E5E7EB', fontSize: 12 }} />
<Bar dataKey="count" radius={[4, 4, 0, 0]}>
{byTier.map((entry, i) => (
<Cell key={i} fill={entry.color} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
<div>
<p className="text-[12px] font-medium text-[#374151] mb-3">Hires by Role</p>
<ResponsiveContainer width="100%" height={180}>
<BarChart data={byRole} layout="vertical">
<CartesianGrid strokeDasharray="3 3" stroke="#F3F4F6" />
<XAxis type="number" tick={{ fontSize: 11 }} stroke="#9CA3AF" allowDecimals={false} />
<YAxis type="category" dataKey="role" tick={{ fontSize: 9 }} stroke="#9CA3AF" width={100} />
<Tooltip contentStyle={{ borderRadius: 8, border: '1px solid #E5E7EB', fontSize: 12 }} />
<Bar dataKey="count" fill="#0838E0" radius={[0, 4, 4, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
</div>
</div>
);
}