import { useEffect, useMemo, useState } from 'react' import { useNavigate } from 'react-router-dom' import { ChevronRight, ExternalLink, Info } from 'lucide-react' import { useRankedCandidates, useRequirements, useSamOpportunities } from '../lib/queries' import type { OpportunityFilters, RequirementListView, SamOpportunity } from '../lib/types' import { Badge, Panel } from '../components/ui' import { bandColor, clearanceLabel, fmtDate, GALAPAGOS, num } from '../lib/format' const selectCls = 'rounded-lg border border-line/70 bg-navy-900 px-3 py-2 text-sm text-ink outline-none focus:border-blue min-w-[24rem]' // The screen flow opens on the relevant, non empty Galapagos slice: their NAICS with Air Force and // Space Force as the customers. (The strict 8A plus HI, NM intersection is often empty in real data.) const pickerFilters: OpportunityFilters = { naics: [...GALAPAGOS.naics], sub_agency: ['AIR FORCE', 'SPACE FORCE'], active_only: true, limit: 100, } // Map an opportunity to a representative demo requirement so the engine can score synthetic candidates // with real, structured criteria. SAM notices don't carry structured clearance/skill specs, so this // is an explicit, labeled demo bridge, not derived clearance. const KEYWORDS: string[][] = [ ['cyber', 'security', 'defensive', 'soc'], ['intel', 'sigint', 'analyst', 'intelligence'], ['data', 'machine learning', ' ai ', 'scientist'], ['cloud', 'devops', 'aws', 'azure'], ['software', 'developer', 'engineer', 'programming'], ['network', 'systems', 'infrastructure', 'architect'], ] function pickRequirement(opp: SamOpportunity | undefined, reqs: RequirementListView[]): RequirementListView | null { if (!reqs.length) return null const t = (opp?.title ?? '').toLowerCase() for (const words of KEYWORDS) { if (words.some((w) => t.includes(w.trim()))) { const match = reqs.find((r) => words.some((w) => r.title.toLowerCase().includes(w.trim()))) if (match) return match } } return reqs[0] } function MiniRing({ value, color }: { value: number; color: string }) { const r = 22 const c = 2 * Math.PI * r return (
{value.toFixed(0)}
) } export function ScreeningQueue() { const opportunities = useSamOpportunities(pickerFilters) const requirements = useRequirements() const [noticeId, setNoticeId] = useState('') const nav = useNavigate() const opps = useMemo(() => opportunities.data ?? [], [opportunities.data]) useEffect(() => { // Re-anchor to the first opportunity on initial load OR when a refetch drops the selected notice // (otherwise the picker shows a blank selection) [review FE-2]. if (opps.length && (!noticeId || !opps.some((o) => o.notice_id === noticeId))) { setNoticeId(opps[0].notice_id) } }, [opps, noticeId]) const selectedOpp = opps.find((o) => o.notice_id === noticeId) const repReq = pickRequirement(selectedOpp, requirements.data ?? []) const ranked = useRankedCandidates(repReq?.id ?? null) return (
{selectedOpp && (
Sol# {selectedOpp.solicitation_number || 'n/a'} NAICS {selectedOpp.naics_code || 'n/a'} {selectedOpp.set_aside_code && ( {selectedOpp.set_aside_code} )} Response due {fmtDate(selectedOpp.response_deadline)} {selectedOpp.ui_link && ( View on SAM.gov )}
)} {repReq && (

Candidate scoring runs against a representative cleared role, {repReq.title}{' '} (demo requirement). Clearance shown is a self-reported / detected indicator, not verified. Candidates are Demo data; opportunities are live from SAM.gov.

)}
{opps.length === 0 ? (

No cached opportunities to screen against. Run the SAM sync to populate the feed.

) : ranked.isLoading ? (

Scoring candidates…

) : ( )}
) }