/* "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 (
{hero.sub}
{reqFilter === 'all' ? 'Top-ranked across all open reqs.' : `Top-ranked for ${reqById(reqFilter)?.title}.`}