- Backend: mode field in request, adapts summary tone and email subject - Pre-launch: "Implementieren Sie X vor Veroeffentlichung" - Post-launch: "ACHTUNG: Maengel sind oeffentlich sichtbar, sofortige Nachbesserung" - Frontend: Mode toggle (internes Dokument vs. Live-Website) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
107 lines
3.1 KiB
TypeScript
107 lines
3.1 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
|
|
export interface FollowUpQuestion {
|
|
id: string
|
|
question: string
|
|
legal_basis: string
|
|
severity: 'high' | 'medium' | 'low'
|
|
finding_if_no: string
|
|
}
|
|
|
|
export interface AnalysisResult {
|
|
url: string
|
|
classification: string
|
|
risk_level: string
|
|
risk_score: number
|
|
escalation_level: string
|
|
responsible_role: string
|
|
findings: string[]
|
|
required_controls: string[]
|
|
summary: string
|
|
email_status: string
|
|
analyzed_at: string
|
|
follow_up_questions: FollowUpQuestion[]
|
|
follow_up_answers: Record<string, boolean>
|
|
}
|
|
|
|
const ESCALATION_ROLES: Record<string, string> = {
|
|
E0: 'Kein Handlungsbedarf',
|
|
E1: 'Teamleitung Datenschutz',
|
|
E2: 'Datenschutzbeauftragter (DSB)',
|
|
E3: 'DSB + Rechtsabteilung',
|
|
}
|
|
|
|
export function useAgentAnalysis() {
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const [result, setResult] = useState<AnalysisResult | null>(null)
|
|
const [history, setHistory] = useState<AnalysisResult[]>([])
|
|
|
|
async function analyze(url: string, mode: string = 'post_launch') {
|
|
setLoading(true)
|
|
setError(null)
|
|
setResult(null)
|
|
|
|
try {
|
|
const fetchRes = await fetch('/api/sdk/v1/agent/analyze', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ url, mode }),
|
|
})
|
|
|
|
if (!fetchRes.ok) {
|
|
throw new Error(`Analyse fehlgeschlagen: ${fetchRes.status}`)
|
|
}
|
|
|
|
const data = await fetchRes.json()
|
|
const analysisResult: AnalysisResult = {
|
|
url,
|
|
classification: data.classification || 'unknown',
|
|
risk_level: data.risk_level || 'unknown',
|
|
risk_score: data.risk_score || 0,
|
|
escalation_level: data.escalation_level || 'E0',
|
|
responsible_role: ESCALATION_ROLES[data.escalation_level] || ESCALATION_ROLES.E0,
|
|
findings: data.findings || [],
|
|
required_controls: data.required_controls || [],
|
|
summary: data.summary || '',
|
|
email_status: data.email_status || 'pending',
|
|
analyzed_at: new Date().toISOString(),
|
|
follow_up_questions: data.follow_up_questions || [],
|
|
follow_up_answers: {},
|
|
}
|
|
|
|
setResult(analysisResult)
|
|
setHistory(prev => [analysisResult, ...prev].slice(0, 20))
|
|
} catch (e) {
|
|
setError(e instanceof Error ? e.message : 'Unbekannter Fehler')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
function answerFollowUp(questionId: string, answer: boolean) {
|
|
if (!result) return
|
|
const question = result.follow_up_questions.find(q => q.id === questionId)
|
|
const newAnswers = { ...result.follow_up_answers, [questionId]: answer }
|
|
const newFindings = [...result.findings]
|
|
|
|
// If user answered "no" → add the finding
|
|
if (!answer && question) {
|
|
newFindings.push(question.finding_if_no)
|
|
}
|
|
|
|
const updated = {
|
|
...result,
|
|
findings: newFindings,
|
|
follow_up_answers: newAnswers,
|
|
}
|
|
setResult(updated)
|
|
// Update history too
|
|
setHistory(prev => prev.map(h => h.analyzed_at === result.analyzed_at ? updated : h))
|
|
}
|
|
|
|
return { analyze, answerFollowUp, loading, error, result, history }
|
|
}
|