Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 93 additions & 57 deletions apps/web/src/components/layout/EvidenceDetailModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export function EvidenceDetailModal({
onClose: () => void
}) {
const [showFullAnswer, setShowFullAnswer] = useState(false)
const [sidebarTab, setSidebarTab] = useState<'competitors' | 'sources'>('competitors')
const [selectedRunIdx, setSelectedRunIdx] = useState(-1) // -1 = latest (current)
const [historicalSnapshot, setHistoricalSnapshot] = useState<EvidenceDisplayData | null>(null)
const [loadingHistory, setLoadingHistory] = useState(false)
Expand Down Expand Up @@ -470,68 +471,103 @@ export function EvidenceDetailModal({

{/* Right: leaderboard + sources */}
<div className="evidence-modal-sidebar">
{/* Citation leaderboard */}
{display.citedDomains.length > 0 && (
<div>
<p className="drawer-section-label">Who was cited \u2014 in order</p>
<div className="citation-leaderboard">
{display.citedDomains.map((domain, i) => {
const norm = domain.toLowerCase().replace(/^www\./, '')
const isYou = myDomains.has(norm)
const isCompetitor = !isYou && display.competitorDomains.some(
c => c.toLowerCase().replace(/^www\./, '') === norm,
)
const variant = isYou ? 'you' : isCompetitor ? 'competitor' : 'other'
return (
<div key={domain} className={`citation-leaderboard-item citation-leaderboard-item--${variant}`}>
<span className="citation-leaderboard-rank">#{i + 1}</span>
<span className="citation-leaderboard-domain">{domain}</span>
{isYou && <span className="citation-leaderboard-tag">You</span>}
{isCompetitor && <span className="citation-leaderboard-tag">Competitor</span>}
</div>
)
})}
{!isCited && (
<div className="citation-leaderboard-item citation-leaderboard-item--not-cited border-dashed">
<span className="citation-leaderboard-rank text-zinc-600">{'\u2014'}</span>
<span className="citation-leaderboard-domain text-zinc-600">{project.project.canonicalDomain}</span>
<span className="citation-leaderboard-tag text-zinc-600">Not cited</span>
</div>
)}
</div>
{/* Tabbed sidebar navigation */}
{(display.citedDomains.length > 0 || display.groundingSources.length > 0 || display.evidenceUrls.length > 0) && (
<div className="sidebar-tabs">
<button
className={`sidebar-tab ${sidebarTab === 'competitors' ? 'sidebar-tab--active' : ''}`}
onClick={() => setSidebarTab('competitors')}
>
Competitors
</button>
<button
className={`sidebar-tab ${sidebarTab === 'sources' ? 'sidebar-tab--active' : ''}`}
onClick={() => setSidebarTab('sources')}
>
Sources
</button>
</div>
)}

{/* Grounding sources */}
{display.groundingSources.length > 0 && (
<div>
<p className="drawer-section-label">Grounding sources ({display.groundingSources.length})</p>
<ul className="grid gap-0.5">
{display.groundingSources.map((src, i) => (
<li key={i} className="truncate text-sm">
<a href={src.uri} target="_blank" rel="noreferrer" className="text-zinc-400 hover:text-zinc-200 transition-colors">
{src.title || src.uri}
</a>
</li>
))}
</ul>
</div>
{/* Competitors tab — domains cited instead */}
{sidebarTab === 'competitors' && (
<>
{display.citedDomains.length > 0 ? (
<div>
<p className="drawer-section-label">Domains cited instead</p>
<div className="citation-leaderboard">
{display.citedDomains.map((domain, i) => {
const norm = domain.toLowerCase().replace(/^www\./, '')
const isYou = myDomains.has(norm)
const isCompetitor = !isYou && display.competitorDomains.some(
c => c.toLowerCase().replace(/^www\./, '') === norm,
)
const variant = isYou ? 'you' : isCompetitor ? 'competitor' : 'other'
return (
<div key={domain} className={`citation-leaderboard-item citation-leaderboard-item--${variant}`}>
<span className="citation-leaderboard-rank">#{i + 1}</span>
<span className="citation-leaderboard-domain">{domain}</span>
{isYou && <span className="citation-leaderboard-tag">You</span>}
{isCompetitor && <span className="citation-leaderboard-tag">Competitor</span>}
</div>
)
})}
{!isCited && (
<div className="citation-leaderboard-item citation-leaderboard-item--not-cited border-dashed">
<span className="citation-leaderboard-rank text-zinc-600">{'\u2014'}</span>
<span className="citation-leaderboard-domain text-zinc-600">{project.project.canonicalDomain}</span>
<span className="citation-leaderboard-tag text-zinc-600">Not cited</span>
</div>
)}
</div>
</div>
) : (
<div className="flex items-center justify-center h-24 text-zinc-600 text-sm">
No competitor data {isViewingHistory ? 'for this run' : 'yet'}
</div>
)}
</>
)}

{/* Evidence URLs */}
{display.evidenceUrls.length > 0 && (
<div>
<p className="drawer-section-label">Evidence URLs</p>
<ul className="grid gap-1">
{display.evidenceUrls.map((url) => (
<li key={url} className="truncate text-sm">
<a href={url} target="_blank" rel="noreferrer" className="text-zinc-400 hover:text-zinc-200 transition-colors">
{url}
</a>
</li>
))}
</ul>
</div>
{/* Sources tab — grounding sources + evidence URLs */}
{sidebarTab === 'sources' && (
<>
{display.groundingSources.length > 0 && (
<div>
<p className="drawer-section-label">Grounding sources ({display.groundingSources.length})</p>
<ul className="grid gap-0.5">
{display.groundingSources.map((src, i) => (
<li key={i} className="truncate text-sm">
<a href={src.uri} target="_blank" rel="noreferrer" className="text-zinc-400 hover:text-zinc-200 transition-colors">
{src.title || src.uri}
</a>
</li>
))}
</ul>
</div>
)}

{display.evidenceUrls.length > 0 && (
<div>
<p className="drawer-section-label">Evidence URLs</p>
<ul className="grid gap-1">
{display.evidenceUrls.map((url) => (
<li key={url} className="truncate text-sm">
<a href={url} target="_blank" rel="noreferrer" className="text-zinc-400 hover:text-zinc-200 transition-colors">
{url}
</a>
</li>
))}
</ul>
</div>
)}

{display.groundingSources.length === 0 && display.evidenceUrls.length === 0 && (
<div className="flex items-center justify-center h-24 text-zinc-600 text-sm">
No source data {isViewingHistory ? 'for this run' : 'yet'}
</div>
)}
</>
)}

{/* No data state */}
Expand Down
13 changes: 13 additions & 0 deletions apps/web/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,19 @@
@apply md:border-l md:border-zinc-800/40 md:pl-6;
}

.sidebar-tabs {
@apply flex gap-1 p-0.5 rounded-lg bg-zinc-900/60 border border-zinc-800/40;
}

.sidebar-tab {
@apply flex-1 px-3 py-1.5 text-xs font-medium rounded-md transition-colors;
@apply text-zinc-500 hover:text-zinc-300;
}

.sidebar-tab--active {
@apply bg-zinc-800 text-zinc-200 shadow-sm;
}

.evidence-answer-collapsed {
max-height: 200px;
overflow: hidden;
Expand Down
Loading