feat: HTML email format, tab info hints, scan history
- Summary now renders as styled HTML (table layout, colored risk badge, warning banners) instead of plaintext in <div> - Tab info text explains scope: "Analysiert nur die eingegebene URL" vs "Scannt automatisch 5-10 Unterseiten" - Scan history with findings count badge and page count Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,9 +15,9 @@ const MODES: { id: AnalysisMode; label: string; desc: string; icon: string }[] =
|
||||
{ id: 'post_launch', label: 'Live-Website', desc: 'Bereits online analysieren', icon: '🌐' },
|
||||
]
|
||||
|
||||
const TABS: { id: AnalysisTab; label: string; desc: string }[] = [
|
||||
{ id: 'quick', label: 'Schnellanalyse', desc: 'Einzelne Seite klassifizieren + bewerten' },
|
||||
{ id: 'scan', label: 'Website-Scan', desc: 'Mehrere Seiten scannen + Dienstleister abgleichen' },
|
||||
const TABS: { id: AnalysisTab; label: string; info: string }[] = [
|
||||
{ id: 'quick', label: 'Schnellanalyse', info: 'Analysiert nur die eingegebene URL. Fuer einen umfassenden Check nutzen Sie den Website-Scan.' },
|
||||
{ id: 'scan', label: 'Website-Scan', info: 'Scannt automatisch 5-10 Unterseiten (Startseite, Datenschutz, Impressum, AGB, Cookies) und gleicht erkannte Dienste mit der Datenschutzerklaerung ab.' },
|
||||
]
|
||||
|
||||
export default function AgentPage() {
|
||||
@@ -27,6 +27,7 @@ export default function AgentPage() {
|
||||
const [scanLoading, setScanLoading] = useState(false)
|
||||
const [scanError, setScanError] = useState<string | null>(null)
|
||||
const [scanData, setScanData] = useState<any>(null)
|
||||
const [scanHistory, setScanHistory] = useState<any[]>([])
|
||||
const { analyze, answerFollowUp, loading, error, result, history } = useAgentAnalysis()
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
@@ -46,7 +47,9 @@ export default function AgentPage() {
|
||||
body: JSON.stringify({ url: url.trim(), mode }),
|
||||
})
|
||||
if (!res.ok) throw new Error(`Scan fehlgeschlagen: ${res.status}`)
|
||||
setScanData(await res.json())
|
||||
const data = await res.json()
|
||||
setScanData(data)
|
||||
setScanHistory(prev => [{ url: url.trim(), ...data, scanned_at: new Date().toISOString() }, ...prev].slice(0, 20))
|
||||
} catch (e) {
|
||||
setScanError(e instanceof Error ? e.message : 'Unbekannter Fehler')
|
||||
} finally {
|
||||
@@ -57,6 +60,7 @@ export default function AgentPage() {
|
||||
|
||||
const isLoading = tab === 'quick' ? loading : scanLoading
|
||||
const currentError = tab === 'quick' ? error : scanError
|
||||
const currentTab = TABS.find(t => t.id === tab)!
|
||||
|
||||
return (
|
||||
<div className="space-y-6 max-w-4xl">
|
||||
@@ -82,17 +86,20 @@ export default function AgentPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tab Selection */}
|
||||
<div className="flex border-b border-gray-200">
|
||||
{TABS.map(t => (
|
||||
<button key={t.id} onClick={() => setTab(t.id)}
|
||||
className={`px-4 py-2.5 text-sm font-medium border-b-2 transition-colors ${
|
||||
tab === t.id
|
||||
? 'border-purple-500 text-purple-700'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700'}`}>
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
{/* Tab Selection + Info */}
|
||||
<div>
|
||||
<div className="flex border-b border-gray-200">
|
||||
{TABS.map(t => (
|
||||
<button key={t.id} onClick={() => setTab(t.id)}
|
||||
className={`px-4 py-2.5 text-sm font-medium border-b-2 transition-colors ${
|
||||
tab === t.id
|
||||
? 'border-purple-500 text-purple-700'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700'}`}>
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xs text-gray-400 mt-2 px-1">{currentTab.info}</p>
|
||||
</div>
|
||||
|
||||
{/* URL Input */}
|
||||
@@ -136,10 +143,32 @@ export default function AgentPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* History (quick only) */}
|
||||
{/* History */}
|
||||
{tab === 'quick' && (
|
||||
<AnalysisHistory history={history} onSelect={r => { setUrl(r.url); analyze(r.url, mode) }} />
|
||||
)}
|
||||
{tab === 'scan' && scanHistory.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-700 mb-3">Letzte Scans</h3>
|
||||
<div className="space-y-2">
|
||||
{scanHistory.map((item, i) => (
|
||||
<button key={i} onClick={() => { setUrl(item.url); }}
|
||||
className="w-full text-left p-3 bg-white border border-gray-200 rounded-lg hover:border-purple-300 hover:bg-purple-50 transition-colors">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xs font-medium text-gray-500 w-8">{item.pages_scanned}p</span>
|
||||
<span className="text-sm text-gray-700 truncate flex-1">{item.url}</span>
|
||||
<span className={`text-xs px-2 py-0.5 rounded ${item.findings?.length > 0 ? 'bg-red-100 text-red-700' : 'bg-green-100 text-green-700'}`}>
|
||||
{item.findings?.length || 0} Findings
|
||||
</span>
|
||||
<span className="text-xs text-gray-400">
|
||||
{new Date(item.scanned_at).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user