/* "Calm" Airtm-native dashboard. * Hero · pipeline funnel · filterable KPI grid (by req + time period) · open reqs · AI shortlist · updates feed. */ function DashV1({ onNav }) { const { role } = React.useContext(RoleContext); const [searchOpen, setSearchOpen] = React.useState(false); const [settingsOpen, setSettingsOpen] = React.useState(false); const [candidate, setCandidate] = React.useState(null); /* filters */ const [reqFilter, setReqFilter] = React.useState('all'); const [period, setPeriod] = React.useState('month'); const [updateFilter, setUpdateFilter] = React.useState('all'); const [showAllReqs, setShowAllReqs] = React.useState(false); const periodObj = TIME_PERIODS.find(p => p.id === period); React.useEffect(() => { const fn = (e) => { if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); setSearchOpen(true); } if (e.key === 'Escape') { setSearchOpen(false); setSettingsOpen(false); setCandidate(null); } }; window.addEventListener('keydown', fn); return () => window.removeEventListener('keydown', fn); }, []); /* hero copy is role-driven */ const hero = { exec: { eyebrow: `Hiring at a glance · ${periodObj.short}`, headline: 'On track on 4 of 6 reqs.', sub: 'Two reqs at risk of missing the 30-day target. AI flagged 3 strong candidates that need a decision today.', }, manager: { eyebrow: `Your team · ${periodObj.short}`, headline: '3 candidates need your review.', sub: 'Senior Backend & Product Designer are still open. AI ranked 12 new applicants since yesterday.', }, recruiter: { eyebrow: `Pipeline · ${periodObj.short}`, headline: '84 candidates in screening.', sub: '12 new applicants overnight. 4 interviews scheduled today, 2 offers waiting on signature.', }, }[role]; /* requisitions scoped to filter + role */ const baseReqs = role === 'manager' ? REQS.filter(r => r.owner === 'Carla R.') : REQS; const filteredReqs = baseReqs.filter(r => reqFilter === 'all' || r.id === reqFilter); const visibleReqs = showAllReqs ? baseReqs : filteredReqs; const topCandidates = (() => { let pool = CANDIDATES; if (reqFilter !== 'all') { const role = reqById(reqFilter)?.role; pool = pool.filter(c => c.role === role); } return [...pool].sort((a,b) => b.score - a.score).slice(0, 4); })(); /* KPIs — prefer live values from /api/dashboard (window.KPIS); fall back to metricSeries() pseudo-data only when no live data is available. */ const live = window.KPIS || null; const kpiDefs = [ { id: 'ttf', label: 'Time to fill', unit: 'd', fmt: (v) => `${Math.round(v)}d`, lowerBetter: true }, { id: 'offer', label: 'Offer acceptance', unit: '%', fmt: (v) => `${Math.round(v)}%`, lowerBetter: false }, { id: 'screen', label: 'Screening approval', unit: '%', fmt: (v) => `${Math.round(v)}%`, lowerBetter: false }, { id: 'reqs', label: 'Open jobs', unit: '', fmt: (v) => `${Math.round(v)}`, lowerBetter: false }, { id: 'applied', label: 'In applied stage', unit: '', fmt: (v) => `${Math.round(v)}`, lowerBetter: false }, ]; const kpis = kpiDefs.map(k => { if (live && live[k.id]) { const lk = live[k.id]; const current = Number(lk.current) || 0; const target = Number(lk.target) || 0; const onTrack = k.lowerBetter ? current <= target : current >= target; const tone = k.id === 'reqs' || k.id === 'applied' ? 'neutral' : onTrack ? 'prog' : 'warn'; return { ...k, current, target, lowerBetter: k.lowerBetter, tone, displayValue: k.fmt(current), targetDisplay: k.fmt(target), // Real-data KPIs don't have trend history yet — flat placeholder. values: Array(8).fill(current), trendPct: 0, binLabel: 'wk', bins: 8 }; } const s = metricSeries(k.id, period, reqFilter); const onTrack = s.lowerBetter ? s.current <= s.target : s.current >= s.target; const tone = k.id === 'reqs' ? 'neutral' : onTrack ? 'prog' : 'warn'; return { ...k, ...s, tone, displayValue: k.fmt(s.current), targetDisplay: k.fmt(s.target) }; }); /* pipeline counts, scoped */ const pipelineCounts = PIPELINE.map(s => { if (reqFilter === 'all') return s; const role = reqById(reqFilter)?.role; return { ...s, count: Math.max(2, Math.round(s.count * (CANDIDATES.filter(c => c.role === role).length / CANDIDATES.length) * 1.4)) }; }); const periodLabel = periodObj.label.toLowerCase(); const reqLabel = reqFilter === 'all' ? 'all jobs' : `the ${reqById(reqFilter)?.title}`; /* updates filter */ const UPDATE_FILTERS = [ { id: 'all', label: 'All', match: () => true }, { id: 'offers', label: 'Offers', match: (u) => u.t.startsWith('offer') }, { id: 'interviews', label: 'Interviews', match: (u) => u.t === 'interview' }, { id: 'stage', label: 'Stage changes', match: (u) => u.t === 'stage' }, ]; const visibleUpdates = UPDATES.filter( UPDATE_FILTERS.find(f => f.id === updateFilter)?.match || (() => true) ); return (
setSearchOpen(true)} onSettings={() => setSettingsOpen(true)} />
{/* page header: title + filters */}

Dashboard

Showing {reqLabel} · {periodLabel}
{/* hero */}
{hero.eyebrow}

{hero.headline}

{hero.sub}

{/* pipeline funnel mini */}
Pipeline
{pipelineCounts.map((s,i) => { const max = pipelineCounts[0].count; return (
{s.stage}
{s.count}
); })}
{/* KPIs */}

Key metrics

{periodObj.label} · {reqFilter === 'all' ? 'all reqs' : reqById(reqFilter)?.title}
{kpis.map(k => (
{k.label}
{k.trendPct > 0 ? : k.trendPct < 0 ? : null} {Math.abs(k.trendPct)}%
{k.displayValue}
Target {k.targetDisplay}
))}
{/* requisitions + AI shortlist */}

{role === 'manager' ? 'Your jobs' : reqFilter === 'all' ? 'Open jobs' : 'Job'}

{(reqFilter !== 'all' || (role === 'manager' && baseReqs.length < REQS.length)) && ( )}
{visibleReqs.map(r => (
e.currentTarget.style.background = 'var(--bg-secondary)'} onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}>
{r.title}
{r.applicants} applicants · {r.days}d open
AI fill
r.target ? 'var(--yellow-70)' : 'var(--green-70)' }}> ~{r.predicted}d
{r.status}
))} {visibleReqs.length === 0 && (
No jobs match this filter.
)}

AI shortlist

{reqFilter === 'all' ? 'Top-ranked across all open reqs.' : `Top-ranked for ${reqById(reqFilter)?.title}.`}

{topCandidates.map(c => ( ))} {topCandidates.length === 0 && (
No candidates yet.
)}
{/* updates feed */}

Latest updates

{UPDATE_FILTERS.map(f => ( setUpdateFilter(f.id)}> {f.label} ))}
{visibleUpdates.length === 0 && (
No updates in this category.
)} {visibleUpdates.map((u,i) => (
0 ? '1px solid var(--stroke-secondary)' : 0 }}>
{u.who} {u.what}
{u.when} ago
))}
setSearchOpen(false)} onOpenCandidate={setCandidate} /> setSettingsOpen(false)} /> setCandidate(null)} />
); } function UpdateIcon({ type, tone }) { const tones = { brand: { bg: 'var(--indigo-20)', fg: 'var(--indigo-90)' }, prog: { bg: 'var(--green-20)', fg: 'var(--green-90)' }, dest: { bg: 'var(--red-20)', fg: 'var(--red-90)' }, neutral: { bg: 'var(--neutral-20)',fg: 'var(--neutral-80)' }, }; const t = tones[tone] || tones.neutral; const ic = type === 'offer-accept' ? : type === 'offer-sent' ? : type === 'offer-decline'? : type === 'interview' ? : ; return (
{ic}
); } window.DashV1 = DashV1;