97 lines
4.5 KiB
JavaScript
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>
|
|
);
|
|
} |