Files
breakpilot-compliance/admin-compliance/app/sdk/agent/snapshots/[snapshotId]/page.tsx
T
Benjamin Admin 7258744107 refactor+feat: Snapshot-Router-Split + generischer ChecklistAgent + AGB-Modul
- Item 2: Snapshot-Doc-Checks (cookie/impressum/dse/agb) in snapshot_check_routes.py
  (agent_compliance_check_routes.py 464→365 Z.); gleiche Pfade, in main.py registriert.
- ChecklistAgent-Basis: DSE-Logik generalisiert (L1/L2, kurze Titel, _severity_
  override-Hook). DSEAgent + AGBAgent sind jetzt Thin-Subclasses → künftige
  Doc-Agenten (widerruf/avv/…) trivial.
- Item 4: AGBAgent (§§ 305 ff. BGB, AGB_CHECKLIST) + agb-check + AGB-Tab via
  AgentModuleTab. Kein Library-Firehose.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 14:23:29 +02:00

117 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
/**
* Snapshot-Detail — öffnet einen gespeicherten Check aus der Historie und
* zeigt die Ergebnis-Views aus den Rohdaten (kein Re-Crawl), als Modul-Tabs:
* Cookies & Tracking + Impressum + Datenschutzerklärung (AGB folgen).
* Doc-Agenten (Impressum/DSE) laufen beim Öffnen des Tabs auf dem gespeicherten
* Text — generisch via AgentModuleTab.
*/
import React, { use as useUnwrap, useEffect, useMemo, useState } from 'react'
import Link from 'next/link'
import { CookieLibraryPanel } from '../../_components/CookieLibraryPanel'
import { CookieResultView } from '../../_components/CookieResultView'
import { AgentModuleTab } from '../../_components/AgentModuleTab'
export default function SnapshotDetail(
{ params }: { params: Promise<{ snapshotId: string }> },
) {
const { snapshotId } = useUnwrap(params)
const [snap, setSnap] = useState<any>(null)
const [check, setCheck] = useState<any>(null) // cookie-check
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [tab, setTab] = useState<string>('')
useEffect(() => {
let cancelled = false
fetch(`/api/sdk/v1/agent/snapshots/${snapshotId}`)
.then(r => r.json())
.then(d => {
if (cancelled) return
if (d?.error) setError(d.error); else setSnap(d)
})
.catch(e => { if (!cancelled) setError(String(e)) })
.finally(() => { if (!cancelled) setLoading(false) })
return () => { cancelled = true }
}, [snapshotId])
// Cookie-Abgleich einmal laden (Findings + cookie_categories für beide Views).
useEffect(() => {
let cancelled = false
fetch(`/api/sdk/v1/agent/snapshots/${snapshotId}/cookie-check`)
.then(r => r.json())
.then(d => { if (!cancelled) setCheck(d) })
.catch(() => { if (!cancelled) setCheck(null) })
return () => { cancelled = true }
}, [snapshotId])
const docs = snap?.doc_entries || []
const hasCookies = (snap?.cmp_vendors?.length ?? 0) > 0
const hasDoc = (dt: string) => docs.some(
(e: any) => e.doc_type === dt && (e.text || e.content || '').length > 100)
const modules = useMemo(() => [
...(hasCookies ? [{ key: 'cookie', label: 'Cookies & Tracking' }] : []),
...(hasDoc('impressum') ? [{ key: 'impressum', label: 'Impressum' }] : []),
...(hasDoc('dse') ? [{ key: 'dse', label: 'Datenschutzerklärung' }] : []),
...(hasDoc('agb') ? [{ key: 'agb', label: 'AGB' }] : []),
// eslint-disable-next-line react-hooks/exhaustive-deps
], [snap])
useEffect(() => {
if (!tab && modules.length) setTab(modules[0].key)
}, [modules, tab])
const tabBtn = (key: string, label: string) => (
<button key={key} onClick={() => setTab(key)}
className={`px-3 py-1.5 text-sm border-b-2 -mb-px ${tab === key ? 'border-blue-600 text-blue-700 font-medium' : 'border-transparent text-gray-500 hover:text-gray-700'}`}>
{label}
</button>
)
return (
<div className="p-6 max-w-6xl space-y-4">
<Link href="/sdk/agent/snapshots" className="text-xs text-blue-700 hover:underline">
Zurück zur Historie
</Link>
{loading ? (
<div className="text-sm text-gray-500">Lade Snapshot</div>
) : error || !snap ? (
<div className="text-sm text-red-600">Snapshot nicht gefunden.</div>
) : modules.length === 0 ? (
<div className="text-sm text-gray-500">
Dieser Snapshot enthält keine auswertbaren Daten.
</div>
) : (
<>
<div className="flex gap-1 border-b border-gray-200">
{modules.map(m => tabBtn(m.key, m.label))}
</div>
{tab === 'cookie' && hasCookies && (
<div className="space-y-4">
<CookieLibraryPanel snapshotId={snapshotId} data={check ?? undefined} />
<CookieResultView snapshot={snap} cookieCategories={check?.cookie_categories} />
</div>
)}
{tab === 'impressum' && (
<AgentModuleTab snapshotId={snapshotId} docType="impressum" label="Impressum" />
)}
{tab === 'dse' && (
<AgentModuleTab snapshotId={snapshotId} docType="dse" label="Datenschutzerklärung" />
)}
{tab === 'agb' && (
<AgentModuleTab snapshotId={snapshotId} docType="agb" label="AGB" />
)}
</>
)}
</div>
)
}