import { useEffect, useMemo, useState } from 'react' import { Link, useNavigate } from 'react-router-dom' import { BookOpenText, ChevronRight, Columns3, ExternalLink, Info } from 'lucide-react' import { useSamOpportunities } from '../../lib/queries' import { useScreenRanked, useScreenRequirements } from './queries' import { screenBrand } from './brand' import type { OpportunityFilters, RequirementListView, SamOpportunity } from '../../lib/types' import { Badge, Panel } from '../../components/ui' import { ToolHeader } from '../../components/ToolHeader' 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 (
No cached opportunities to screen against. Run the SAM sync to populate the feed.
) : ranked.isLoading ? (Scoring candidates…
) : (