update hired page

This commit is contained in:
2026-08-11 19:17:40 +05:30
parent 8a256a4de7
commit 25c4ee4a6a
23 changed files with 2921 additions and 380 deletions

View File

@@ -9,6 +9,7 @@ import {
} from '@/components/ds';
import { useApplications, useInterviews, useJobPostings, useStaff, useWorkerProfiles } from '@/lib/krowHooks';
import { buildFacts } from '@/components/ai-assistant/insights';
import { HiringFlow } from '@/components/charts/HiringFlow';
import { AdminPage, SectionTitle } from '@/pages/admin/_shell';
/**
@@ -185,50 +186,32 @@ export default function AdminAnalytics() {
</ResponsiveContainer>
</section>
<div className="grid gap-6 lg:grid-cols-2">
{/* Pipeline conversion — stage-to-stage rates are the point, so each
step shows its own pass-through rather than only its count. */}
<section aria-labelledby="conversion" className="space-y-3">
<SectionTitle id="conversion" title="Pipeline conversion" meta={`${f.total} entered`} />
<div className="space-y-3">
{f.funnel.map((stage, i) => {
const rate = i === 0 ? 100 : f.transitions[i - 1]?.rate ?? 0;
const lost = i === 0 ? 0 : f.transitions[i - 1]?.lost ?? 0;
return (
<div key={stage.key}>
<div className="mb-1 flex items-baseline justify-between gap-2">
<span className="text-body-sm text-ink-2">{stage.label}</span>
<span className="flex items-baseline gap-2.5">
{lost > 0 && <span className="text-[10px] text-destructive">−{lost}</span>}
<span className={rate < 60 && i > 0 ? 'text-[11px] font-semibold text-warning' : 'text-[11px] text-ink-4'}>
{rate}%
</span>
<span className="w-8 text-right text-body-sm font-bold tabular-nums text-ink-1">{stage.count}</span>
</span>
</div>
<ProgressBar value={stage.count} max={f.total || 1} size="sm" tone={rate < 60 && i > 0 ? 'warning' : 'brand'} />
</div>
);
})}
</div>
</section>
{/* The funnel takes the full width: it is a horizontal sequence, and in a
half-width column the stage strip truncates its own labels. */}
<section aria-labelledby="conversion" className="space-y-3">
<SectionTitle id="conversion" title="Pipeline conversion" meta={`${f.total} entered`} />
<HiringFlow
stages={f.funnel}
transitions={f.transitions}
weakestKey={f.bottleneck?.to}
/>
</section>
{/* Score distribution — where the hireable pool actually sits. */}
<section aria-labelledby="quality" className="space-y-3">
<SectionTitle id="quality" title="Hiring quality" meta={`${f.scored.length} scored · avg ${f.avgScore}`} />
<ResponsiveContainer width="100%" height={196}>
<BarChart data={scoreDistribution}>
<CartesianGrid strokeDasharray="3 3" stroke={CHART_TONES.grid} vertical={false} />
<XAxis dataKey="label" {...AXIS_PROPS} />
<YAxis {...AXIS_PROPS} allowDecimals={false} width={28} />
<Tooltip content={<ChartTooltip />} />
<Bar dataKey="count" name="Candidates" radius={[4, 4, 0, 0]} maxBarSize={56}>
{scoreDistribution.map((d) => <Cell key={d.label} fill={bandColor(d.label)} />)}
</Bar>
</BarChart>
</ResponsiveContainer>
</section>
</div>
{/* Score distribution — where the hireable pool actually sits. */}
<section aria-labelledby="quality" className="space-y-3">
<SectionTitle id="quality" title="Hiring quality" meta={`${f.scored.length} scored · avg ${f.avgScore}`} />
<ResponsiveContainer width="100%" height={196}>
<BarChart data={scoreDistribution}>
<CartesianGrid strokeDasharray="3 3" stroke={CHART_TONES.grid} vertical={false} />
<XAxis dataKey="label" {...AXIS_PROPS} />
<YAxis {...AXIS_PROPS} allowDecimals={false} width={28} />
<Tooltip content={<ChartTooltip />} />
<Bar dataKey="count" name="Candidates" radius={[4, 4, 0, 0]} maxBarSize={56}>
{scoreDistribution.map((d) => <Cell key={d.label} fill={bandColor(d.label)} />)}
</Bar>
</BarChart>
</ResponsiveContainer>
</section>
{/* Velocity and screening efficiency. Both are ratios rather than shapes,
so they read better as figures with an explanation than as another