'use client'; import { useMemo, useState } from 'react'; import { Search, Filter, FileCode, ShieldCheck, Target } from 'lucide-react'; import { Avatar, Badge } from '@nextcraft/ui'; import type { Candidate } from '@nextcraft/types'; import { matchBadgeClasses } from '../../lib/format'; /* -------------------------------------------------------------------------- */ /* Filter config */ /* -------------------------------------------------------------------------- */ const STACK_OPTIONS = [ { value: 'all', label: 'All stacks' }, { value: 'stack-orchestration', label: 'AI Orchestration' }, { value: 'stack-safety', label: 'AI Safety' }, { value: 'stack-designer', label: 'Product Design' }, { value: 'stack-operator', label: 'Field Operator' }, { value: 'stack-science', label: 'Comp Sciences' }, ]; const DEFENSE_OPTIONS = [ { value: 0, label: 'Any defense score' }, { value: 70, label: 'Defense 70+' }, { value: 80, label: 'Defense 80+' }, { value: 90, label: 'Defense 90+' }, ]; const ARTIFACT_OPTIONS = [ { value: 0, label: 'Any artifacts' }, { value: 1, label: '1+ artifacts' }, { value: 3, label: '3+ artifacts' }, { value: 5, label: '5+ artifacts' }, { value: 10, label: '10+ artifacts' }, ]; /** Derive 3 short microcredential badges from a candidate's stack + count. */ function microcredentialBadges(cand: Candidate): string[] { const stackMap: Record = { 'stack-orchestration': ['Agent Arch', 'Tool Use', 'RAG', 'Eval', 'Routing'], 'stack-safety': ['Red Team', 'Model Cards', 'Bias Audit', 'Policy', 'Interp'], 'stack-designer': ['Agentic UX', 'Conv UX', 'Transparency', 'Trust', 'Persona'], 'stack-operator': ['Robotics', 'Vision', 'Calibration', 'AR', 'Field Data'], 'stack-science': ['Sci Python', 'HPC', 'PINNs', 'Active Learn', 'Bioinformatics'], }; const pool = stackMap[cand.competencyStackId] ?? ['MC-1', 'MC-2', 'MC-3']; // Spread selection deterministically based on id suffix. const offset = Number(cand.id.replace(/\D/g, '')) % pool.length; return [pool[offset % pool.length], pool[(offset + 1) % pool.length], pool[(offset + 2) % pool.length]]; } /* -------------------------------------------------------------------------- */ /* Component */ /* -------------------------------------------------------------------------- */ export interface TalentSearchProps { candidates: Candidate[]; } export function TalentSearch({ candidates }: TalentSearchProps) { const [query, setQuery] = useState(''); const [stack, setStack] = useState('all'); const [minDefense, setMinDefense] = useState(0); const [minArtifacts, setMinArtifacts] = useState(0); const filtered = useMemo(() => { const q = query.trim().toLowerCase(); return candidates.filter((c) => { if (q) { const hay = `${c.name} ${c.headline}`.toLowerCase(); if (!hay.includes(q)) return false; } if (stack !== 'all' && c.competencyStackId !== stack) return false; if (c.defenseScore < minDefense) return false; if (c.artifactCount < minArtifacts) return false; return true; }); }, [candidates, query, stack, minDefense, minArtifacts]); const selectClass = 'h-9 rounded-md border border-slate-300 bg-white px-2.5 text-sm text-slate-900 focus:border-primary-500 focus:ring-2 focus:ring-primary-500/30 focus:outline-none dark:border-slate-700 dark:bg-slate-800 dark:text-slate-100'; return (
{/* Filter bar */}
setQuery(e.target.value)} placeholder="Search candidates by name or headline..." aria-label="Search candidates" className="h-10 w-full rounded-md border border-slate-300 bg-white pl-9 pr-3 text-sm text-slate-900 placeholder:text-slate-400 focus:border-primary-500 focus:ring-2 focus:ring-primary-500/30 focus:outline-none dark:border-slate-700 dark:bg-slate-800 dark:text-slate-100 dark:placeholder:text-slate-500" />
Filters: Showing {filtered.length} of{' '} {candidates.length}
{/* Candidate grid */} {filtered.length === 0 ? (
No candidates match your filters.
) : (
{filtered.map((cand) => ( ))}
)}
); } function CandidateCard({ cand }: { cand: Candidate }) { const badges = microcredentialBadges(cand); return (

{cand.name}

{cand.headline}

{cand.matchScore}%
{badges.map((b) => ( {b} ))}
{cand.artifactCount} artifacts Defense: {cand.defenseScore} Match: {cand.matchScore}%
View Profile
); } // Re-export icon used in card stats so the import is not tree-shaken away.