+
+
+
+ {statusIcon(pack.status)} Evidence Pack
+
+
+ {pack.status}
+ {pack.status !== 'COMPLETED' && pack.status !== 'FAILED' && (
+
+ )}
+
+
+
+ {/* Status bar for in-progress */}
+ {pack.status !== 'COMPLETED' && pack.status !== 'FAILED' && (
+
+
+ Agent is {pack.status.toLowerCase()}...
+ {pack.iterations.length > 0 && (
+ (Iteration {pack.iterations.length})
+ )}
+
+
+
+ )}
+
+ {/* Tabs */}
+
+
+
+
+
+
+
+
+ {/* Overview Tab */}
+ {tab === 'overview' && (
+
+
+
+ {pack.evidence.length}
+ Evidence Items
+
+
+ {pack.gaps.length}
+ Remaining Gaps
+
+
+ {pack.campaign_angles.length}
+ Campaign Angles
+
+
+ {coverageScore()}%
+ Coverage Score
+
+
+
+
+
Competitors
+
+ {pack.competitors.map((c, i) => (
+
+ {c.name}
+ {c.url && ↗}
+
+ ))}
+
+
+
+ {pack.iterations.length > 0 && (
+
+
Agent Iterations
+
+ {pack.iterations.map(it => (
+
+ #{it.iteration}
+
+ Found {it.gaps_found.length} gaps,
+ added {it.evidence_added} evidence,
+ resolved {it.gaps_resolved}
+
+
+ ))}
+
+
+ )}
+
+ )}
+
+ {/* Evidence Tab */}
+ {tab === 'evidence' && (
+
+ {Object.entries(groupByCompetitor()).map(([competitor, items]) => (
+
+
{competitor}
+
{items.length} evidence items
+ {items.map(item => (
+
+
+ {item.category}
+ {confidenceIcon(item.confidence)} {item.confidence}
+
+
{item.claim}
+
{item.supporting_text.slice(0, 300)}
+ {item.citations.length > 0 && (
+
+ )}
+ {item.tags.length > 0 && (
+
+ {item.tags.map(t => {t})}
+
+ )}
+
+ ))}
+
+ ))}
+ {pack.evidence.length === 0 && (
+
No evidence collected yet.
+ )}
+
+ )}
+
+ {/* Gaps Tab */}
+ {tab === 'gaps' && (
+
+ {pack.gaps.length === 0 ? (
+
+
+ No remaining gaps — full coverage achieved!
+
+
+ ) : (
+
+
Evidence Gaps to Resolve
+
+ These areas need further research or manual verification.
+
+ {pack.gaps.map((gap, i) => (
+
+
+ {gap.category}
+ {gap.competitor}
+
+
{gap.description}
+ {gap.suggested_sources.length > 0 && (
+
+
Suggested:
+ {gap.suggested_sources.slice(0, 2).map((s, j) => (
+
{s}
+ ))}
+
+ )}
+
+ ))}
+
+ )}
+
+ )}
+
+ {/* Campaign Angles Tab */}
+ {tab === 'angles' && (
+
+ {pack.campaign_angles.length === 0 ? (
+
+
+ Campaign angles will be generated when research completes.
+
+
+ ) : (
+ pack.campaign_angles.map((angle, i) => (
+
+
+ #{i + 1}
+
{confidenceIcon(angle.confidence)} {angle.title}
+
+
+
Target Weakness: {angle.target_weakness}
+
{angle.description}
+
+ Channel Fit:
+ {angle.channel_fit.map(ch => (
+ {ch}
+ ))}
+
+
+
+ ))
+ )}
+
+ )}
+
+ {/* Report Tab */}
+ {tab === 'report' && (
+
+
+
+
+
+
{report || 'Loading report...'}
+
+
+ )}
+
+ );
+}
diff --git a/intel-agent/frontend/src/components/ResearchForm.tsx b/intel-agent/frontend/src/components/ResearchForm.tsx
new file mode 100644
index 000000000..87c3e6336
--- /dev/null
+++ b/intel-agent/frontend/src/components/ResearchForm.tsx
@@ -0,0 +1,163 @@
+import { useState } from 'react';
+import { startResearch, EVIDENCE_CATEGORIES } from '../api';
+
+interface Props {
+ onCreated: (id: string) => void;
+}
+
+export default function ResearchForm({ onCreated }: Props) {
+ const [competitors, setCompetitors] = useState([{ name: '', url: '' }]);
+ const [focusAreas, setFocusAreas] = useState