'use client' import React, { useState } from 'react' import { ChecklistView } from './ChecklistView' import { ResultsTabsView } from './ResultsTabsView' import { PreScanWizard, useScanContext, isContextComplete } from './PreScanWizard' import { safeSetItem } from './storageHelpers' interface DocEntry { id: string type: string label: string url: string text: string // P-Paste: User kopiert Doc-Text direkt rein mode: 'url' | 'text' // welcher Input wird aktiv genutzt } const DOC_TYPES = [ { id: 'dse', label: 'Datenschutzerklärung / DSI' }, { id: 'cookie', label: 'Cookie-Richtlinie' }, { id: 'impressum', label: 'Impressum' }, { id: 'agb', label: 'AGB' }, { id: 'nutzungsbedingungen', label: 'Nutzungsbedingungen' }, { id: 'widerruf', label: 'Widerrufsbelehrung' }, { id: 'social_media', label: 'DSE Social Media (Art. 26)' }, { id: 'dsfa', label: 'DSFA (Art. 35)' }, { id: 'dsa', label: 'DSA / Digital Services Act' }, { id: 'legal_notice', label: 'Rechtliche Hinweise (IP, Forward-Looking)' }, { id: 'lizenzhinweise', label: 'Lizenzhinweise Dritter (OSS)' }, { id: 'other', label: 'Sonstiges' }, ] function newEntry(): DocEntry { return { id: crypto.randomUUID().slice(0, 8), type: 'dse', label: '', url: '', text: '', mode: 'url' } } export function DocCheckTab() { const [scanContext, setScanContext] = useScanContext() const [entries, setEntries] = useState(() => { if (typeof window === 'undefined') return [newEntry()] try { const s = localStorage.getItem('doc-check-entries'); return s ? JSON.parse(s) : [newEntry()] } catch { return [newEntry()] } }) const [checkCookieBanner, setCheckCookieBanner] = useState(false) const [useAgent, setUseAgent] = useState(false) const [loading, setLoading] = useState(false) const [progress, setProgress] = useState('') const [results, setResults] = useState(() => { if (typeof window === 'undefined') return null try { const s = localStorage.getItem('doc-check-results'); return s ? JSON.parse(s) : null } catch { return null } }) const [error, setError] = useState(null) const [history, setHistory] = useState<{ date: string; urls: number; findings: number; resultKey: string }[]>(() => { if (typeof window === 'undefined') return [] try { return JSON.parse(localStorage.getItem('doc-check-history') || '[]') } catch { return [] } }) // Persist entries React.useEffect(() => { localStorage.setItem('doc-check-entries', JSON.stringify(entries)) }, [entries]) const updateEntry = (id: string, field: keyof DocEntry, value: string) => { setEntries(prev => prev.map(e => e.id === id ? { ...e, [field]: value } : e)) } const removeEntry = (id: string) => { setEntries(prev => prev.filter(e => e.id !== id)) } const addEntry = () => { setEntries(prev => [...prev, newEntry()]) } // Auto-detect label from URL const autoLabel = (entry: DocEntry) => { if (entry.label) return try { const path = new URL(entry.url).pathname const last = path.split('/').filter(Boolean).pop() || '' const label = last.replace(/-\d+$/, '').replace(/-/g, ' ') .replace(/\b\w/g, c => c.toUpperCase()) if (label.length > 3) { updateEntry(entry.id, 'label', label) } } catch { /* invalid URL */ } } const handleSubmit = async () => { const validEntries = entries.filter(e => e.url.trim() || e.text.trim()) if (validEntries.length === 0) return setLoading(true) setError(null) setResults(null) setProgress('Pruefung wird gestartet...') try { const startRes = await fetch('/api/sdk/v1/agent/doc-check', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ entries: validEntries.map(e => ({ doc_type: e.type, label: e.label || (e.url ? e.url.split('/').pop() : '') || `${e.type}-paste`, url: e.mode === 'text' ? '' : e.url.trim(), // Backend nimmt text > url. Wenn beide gefuellt sind und // mode='url', schicken wir den text NICHT mit. text: e.mode === 'text' ? e.text.trim() : '', })), check_cookie_banner: checkCookieBanner, use_agent: useAgent, scan_context: scanContext, }), }) if (!startRes.ok) throw new Error(`Pruefung konnte nicht gestartet werden: ${startRes.status}`) const { check_id } = await startRes.json() if (!check_id) throw new Error('Keine Check-ID erhalten') // Poll for results let attempts = 0 while (attempts < 120) { await new Promise(r => setTimeout(r, 3000)) const pollRes = await fetch(`/api/sdk/v1/agent/doc-check?check_id=${check_id}`) if (!pollRes.ok) { attempts++; continue } const pollData = await pollRes.json() if (pollData.progress) setProgress(pollData.progress) if (pollData.status === 'completed' && pollData.result) { setResults(pollData.result) setProgress('') safeSetItem('doc-check-results', JSON.stringify(pollData.result)) const resultKey = `doc-check-result-${Date.now()}` safeSetItem(resultKey, JSON.stringify(pollData.result)) const entry = { date: new Date().toISOString(), urls: validEntries.length, findings: pollData.result.total_findings || 0, resultKey } const updated = [entry, ...history].slice(0, 30) setHistory(updated) safeSetItem('doc-check-history', JSON.stringify(updated)) break } if (pollData.status === 'failed') { throw new Error(pollData.error || 'Pruefung fehlgeschlagen') } attempts++ } } catch (e) { setError(e instanceof Error ? e.message : 'Unbekannter Fehler') setProgress('') } finally { setLoading(false) } } const contextReady = isContextComplete(scanContext) return (
{/* P79 Pre-Scan-Wizard — 8 Pflichtfelder */} {/* URL / Text Entries */}
{entries.map((entry, i) => (
updateEntry(entry.id, 'label', e.target.value)} placeholder={entry.type === 'other' ? 'Dokumentname' : 'Version / Stand (optional)'} className="w-40 px-3 py-2.5 border border-gray-300 rounded-lg text-sm shrink-0" /> {/* Mode-Toggle URL / Text */}
{entry.mode === 'url' && ( updateEntry(entry.id, 'url', e.target.value)} onBlur={() => autoLabel(entry)} placeholder="https://example.com/datenschutz" className="flex-1 px-3 py-2.5 border border-gray-300 rounded-lg text-sm" /> )} {entries.length > 1 && ( )}
{entry.mode === 'text' && (