manan77709's picture
feat: render sub_results breakdown and pair judge papers by pmid in ResultsPanel
495aff2
Raw History Blame Contribute Delete
8.23 kB
import {
ChevronDown,
ChevronUp,
ExternalLink,
Zap,
Layers,
FileText,
Lightbulb,
BookOpen,
} from "lucide-react"
import VerdictBadge from "./VerdictBadge"
function StanceDot({ stance }) {
const cls =
stance === "SUPPORTS"
? "stance-dot--supports"
: stance === "CONTRADICTS"
? "stance-dot--contradicts"
: "stance-dot--neutral"
return <span className={`stance-dot ${cls}`} aria-hidden="true" />
}
export default function ResultsPanel({ result, expandedPapers, onTogglePaper }) {
if (!result) return null
const papersByPmid = new Map(
(result.papers || []).map((p) => [String(p.pmid), p])
)
const hasSubResults =
Array.isArray(result.sub_results) &&
(result.sub_results.length > 1 || (result.sub_results.length === 1 && result.decomposition?.is_complex))
return (
<div className="results">
<div className="results-card">
<div className="results-card__header">
<VerdictBadge verdict={result.verdict} />
<div className="results-stats">
<div className="results-stat">
<span className="results-stat__value">{Math.round((result.confidence ?? 0) * 100)}%</span>
<span className="results-stat__label">Confidence</span>
</div>
<div className="results-stat">
<span className="results-stat__value">{result.evidence_strength || "—"}</span>
<span className="results-stat__label">Evidence</span>
</div>
</div>
</div>
{result.cached && (
<p className="results-cache">
<Zap size={13} />
Served from cache
</p>
)}
{hasSubResults ? (
<div className="results-block results-block--accent">
<div className="results-block__heading">
<Layers size={15} />
Complex claim — {result.sub_results.length} sub-claims analyzed
</div>
<div className="sub-results">
{result.sub_results.map((sr, i) => (
<div key={i} className="sub-result-item">
<span className="sub-result-item__claim">{sr.sub_claim}</span>
<div className="sub-result-item__meta">
<VerdictBadge verdict={sr.verdict} />
<span>{Math.round((sr.confidence ?? 0) * 100)}%</span>
<span>{sr.papers?.length ?? 0} studies</span>
</div>
</div>
))}
</div>
</div>
) : (
result.decomposition?.is_complex && Array.isArray(result.decomposition.sub_claims) && (
<div className="results-block results-block--accent">
<div className="results-block__heading">
<Layers size={15} />
Complex claim — {result.decomposition.sub_claims.length} sub-claims analyzed
</div>
<ul className="results-list">
{result.decomposition.sub_claims.map((sc, i) => (
<li key={i}>{sc}</li>
))}
</ul>
</div>
)
)}
{result.plain_english && (
<div className="results-block">
<div className="results-block__heading">
<FileText size={15} />
In plain English
</div>
<p className="results-block__text">{result.plain_english}</p>
</div>
)}
{result.takeaway && (
<div className="results-takeaway">
<Lightbulb size={15} />
<p><strong>Takeaway.</strong> {result.takeaway}</p>
</div>
)}
</div>
<details className="results-card results-details">
<summary className="results-details__summary">
<BookOpen size={15} />
Technical explanation
</summary>
<p className="results-block__text">{result.explanation}</p>
{result.citations?.length > 0 && (
<div className="citations">
<p className="citations__title">Citations</p>
{result.citations.map((c, i) => (
<div key={i} className="citation-row">
{c.pmid && /^\d+$/.test(c.pmid) ? (
<a
href={`https://pubmed.ncbi.nlm.nih.gov/${c.pmid}/`}
target="_blank"
rel="noopener noreferrer"
className="citation-link"
>
{c.title}
<ExternalLink size={12} />
</a>
) : (
<span className="citation-text">{c.title}</span>
)}
</div>
))}
</div>
)}
</details>
{result.judge && result.judge.overall_quality && result.judge.overall_quality !== "UNKNOWN" && (
<div className="results-card">
<div className="results-card__row">
<div className="results-block__heading">Evidence quality</div>
<span className={`quality-badge quality-badge--${result.judge.overall_quality.toLowerCase()}`}>
{result.judge.overall_quality}
</span>
</div>
<p className="results-block__text results-block__text--muted">
{result.judge.quality_explanation}
</p>
<div className="papers-list">
{(result.judge.papers || []).map((jp, i) => {
const paper = jp.pmid != null ? papersByPmid.get(String(jp.pmid)) : undefined
const key = jp.pmid != null ? String(jp.pmid) : i
return (
<div key={key} className="paper-item">
<button
type="button"
className="paper-item__toggle"
onClick={() => onTogglePaper(key)}
>
<div className="paper-item__title">
<StanceDot stance={jp.stance} />
<span>
{paper ? `${paper.title} (${paper.year})` : (jp.one_line_summary || `Study #${jp.paper_number || i + 1}`)}
</span>
</div>
{expandedPapers[key] ? (
<ChevronUp size={16} className="paper-item__chevron" />
) : (
<ChevronDown size={16} className="paper-item__chevron" />
)}
</button>
{expandedPapers[key] && (
<div className="paper-item__body">
<div className="paper-metrics">
<div className="paper-metric">
<span>Study type</span>
<strong>{jp.study_type || paper?.study_type || "Other"}</strong>
</div>
<div className="paper-metric">
<span>Quality</span>
<strong>{jp.quality_score}/5</strong>
</div>
<div className="paper-metric">
<span>Similarity</span>
<strong>{paper ? paper.similarity : "—"}</strong>
</div>
</div>
<p className="paper-item__summary">{jp.one_line_summary}</p>
{paper && (
<>
<p className="paper-item__journal">{paper.journal}</p>
<a
href={`https://pubmed.ncbi.nlm.nih.gov/${paper.pmid}/`}
target="_blank"
rel="noopener noreferrer"
className="citation-link citation-link--sm"
>
View on PubMed
<ExternalLink size={10} />
</a>
</>
)}
</div>
)}
</div>
)
})}
</div>
</div>
)}
</div>
)
}