"use client"; import { useState } from "react"; import { searchTrials, getTrialIntelligence } from "@/lib/api"; import { Search, MapPin, Calendar, Users, ChevronDown, ChevronUp, FlaskConical, Brain, ExternalLink, Clock, CheckCircle } from "lucide-react"; import { clsx } from "clsx"; const PHASES = ["", "1", "2", "3", "4"]; const PHASE_LABELS: Record = { "": "All Phases", "1": "Phase I", "2": "Phase II", "3": "Phase III", "4": "Phase IV" }; const PHASE_COLORS: Record = { PHASE1: "bg-blue-100 text-blue-700", PHASE2: "bg-violet-100 text-violet-700", PHASE3: "bg-emerald-100 text-emerald-700", PHASE4: "bg-amber-100 text-amber-700", "N/A": "bg-slate-100 text-slate-600", }; function daysAgo(dateStr: string): string { if (!dateStr) return ""; const d = new Date(dateStr); if (isNaN(d.getTime())) return ""; const days = Math.floor((Date.now() - d.getTime()) / 86400000); if (days === 0) return "Updated today"; if (days === 1) return "Updated yesterday"; if (days < 30) return `Updated ${days}d ago`; if (days < 365) return `Updated ${Math.floor(days / 30)}mo ago`; return `Updated ${Math.floor(days / 365)}y ago`; } export default function TrialFinder() { const [condition, setCondition] = useState(""); const [phase, setPhase] = useState(""); const [trials, setTrials] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [expanded, setExpanded] = useState(null); const [searched, setSearched] = useState(false); const [intelligence, setIntelligence] = useState>({}); const [loadingIntel, setLoadingIntel] = useState(null); const handleSearch = async () => { if (!condition.trim()) return; setLoading(true); setError(""); setSearched(true); setExpanded(null); setIntelligence({}); try { const data = await searchTrials(condition, phase || undefined, 20); setTrials(data.trials); } catch (e: any) { setError(e.message); setTrials([]); } setLoading(false); }; const handleExpand = async (nctId: string) => { const next = expanded === nctId ? null : nctId; setExpanded(next); if (next && !intelligence[next]) { setLoadingIntel(next); try { const intel = await getTrialIntelligence(next); setIntelligence((prev) => ({ ...prev, [next]: intel })); } catch {} setLoadingIntel(null); } }; const handleKey = (e: React.KeyboardEvent) => { if (e.key === "Enter") handleSearch(); }; return (

Clinical Trial Finder

Search ClinicalTrials.gov for recruiting studies — powered by real-time data

{/* Search bar */}
setCondition(e.target.value)} onKeyDown={handleKey} placeholder="e.g. breast cancer, NSCLC, Alzheimer's disease..." className="w-full pl-9 pr-4 py-2.5 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-white" />
{error && (
{error}
)} {searched && !loading && trials.length === 0 && !error && (

No trials found. Try a different condition or phase.

)} {trials.length > 0 && (

{trials.length} trials found for {condition}

Sorted by most recently updated
{trials.map((trial) => { const isExpanded = expanded === trial.nct_id; const phaseKey = trial.phase?.replace("PHASE", "Phase ") || "N/A"; const recency = daysAgo(trial.last_updated); const intel = intelligence[trial.nct_id]; const isLoadingIntel = loadingIntel === trial.nct_id; return (
{isExpanded && (
{trial.brief_summary && (

{trial.brief_summary.slice(0, 500)}{trial.brief_summary.length > 500 ? "…" : ""}

)}
Age Range
{trial.min_age || "18 years"} – {trial.max_age || "No max"}
Sex
{trial.sex?.toLowerCase() || "All"}
Start Date
{trial.start_date || "N/A"}
Completion
{trial.completion_date || "N/A"}
{trial.locations?.length > 0 && (
Sites
{trial.locations.slice(0, 4).map((loc: any, i: number) => ( {loc.facility || `${loc.city}, ${loc.state}`} ))} {trial.location_count > 4 && +{trial.location_count - 4} more}
)} {trial.primary_outcomes?.length > 0 && (
Primary Outcomes
    {trial.primary_outcomes.map((o: string, i: number) =>
  • · {o}
  • )}
)} {/* Graph intelligence panel */}
Graph Intelligence {trial.eligible_patients_in_graph === 0 && !intel && ( Enriching graph… )}
{isLoadingIntel ? (

Loading graph data…

) : intel ? (
{intel.eligible_patients} patients in graph eligible for this trial
{intel.top_biomarkers?.length > 0 && (
Top biomarkers among eligible patients:
{intel.top_biomarkers.map((b: any, i: number) => ( {b.biomarker} ({b.patient_count}) ))}
)} {intel.similar_trials?.length > 0 && (
Similar trials (by shared eligible patients):
{intel.similar_trials.map((t: any, i: number) => (
{t.nct_id} {t.title?.slice(0, 50)}… {t.shared_patients} shared
))}
)}
) : (

Trial not yet in graph — being ingested now.

)}
Ingested to graph {trial.ctgov_url && ( View on ClinicalTrials.gov )}
)}
); })}
)}
); }