- Scan public website for cancellation button, imprint, privacy link, cookie consent - Generate follow-up questions when checks can't be verified without login - User answers "no" → finding with legal basis is added to results - Frontend: FollowUpQuestions component with Ja/Nein buttons - Sidebar: "Compliance Agent" entry added under KI-Compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
92 lines
3.8 KiB
TypeScript
92 lines
3.8 KiB
TypeScript
'use client'
|
|
|
|
import React from 'react'
|
|
import type { FollowUpQuestion } from '../_hooks/useAgentAnalysis'
|
|
|
|
const SEVERITY_STYLE: Record<string, { border: string; bg: string; icon: string }> = {
|
|
high: { border: 'border-red-300', bg: 'bg-red-50', icon: '!!' },
|
|
medium: { border: 'border-yellow-300', bg: 'bg-yellow-50', icon: '!' },
|
|
low: { border: 'border-blue-300', bg: 'bg-blue-50', icon: 'i' },
|
|
}
|
|
|
|
interface Props {
|
|
questions: FollowUpQuestion[]
|
|
answers: Record<string, boolean>
|
|
onAnswer: (questionId: string, answer: boolean) => void
|
|
}
|
|
|
|
export function FollowUpQuestions({ questions, answers, onAnswer }: Props) {
|
|
const unanswered = questions.filter(q => answers[q.id] === undefined)
|
|
const answered = questions.filter(q => answers[q.id] !== undefined)
|
|
|
|
if (questions.length === 0) return null
|
|
|
|
return (
|
|
<div className="space-y-3">
|
|
<h4 className="text-sm font-medium text-gray-700 flex items-center gap-2">
|
|
<svg className="w-4 h-4 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
Rueckfragen zur manuellen Pruefung ({unanswered.length} offen)
|
|
</h4>
|
|
|
|
{/* Unanswered questions */}
|
|
{unanswered.map(q => {
|
|
const style = SEVERITY_STYLE[q.severity] || SEVERITY_STYLE.medium
|
|
return (
|
|
<div key={q.id} className={`border ${style.border} ${style.bg} rounded-lg p-4`}>
|
|
<div className="flex items-start gap-3">
|
|
<span className={`mt-0.5 w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold ${
|
|
q.severity === 'high' ? 'bg-red-200 text-red-800' :
|
|
q.severity === 'medium' ? 'bg-yellow-200 text-yellow-800' :
|
|
'bg-blue-200 text-blue-800'
|
|
}`}>
|
|
{SEVERITY_STYLE[q.severity]?.icon || '?'}
|
|
</span>
|
|
<div className="flex-1">
|
|
<p className="text-sm font-medium text-gray-900">{q.question}</p>
|
|
<p className="text-xs text-gray-500 mt-1">Rechtsgrundlage: {q.legal_basis}</p>
|
|
<div className="flex gap-2 mt-3">
|
|
<button
|
|
onClick={() => onAnswer(q.id, true)}
|
|
className="px-4 py-1.5 text-sm bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors"
|
|
>
|
|
Ja
|
|
</button>
|
|
<button
|
|
onClick={() => onAnswer(q.id, false)}
|
|
className="px-4 py-1.5 text-sm bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors"
|
|
>
|
|
Nein
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
|
|
{/* Answered questions */}
|
|
{answered.map(q => {
|
|
const isYes = answers[q.id]
|
|
return (
|
|
<div key={q.id} className={`border rounded-lg p-3 ${isYes ? 'border-green-200 bg-green-50' : 'border-red-200 bg-red-50'}`}>
|
|
<div className="flex items-center gap-2">
|
|
<span className={`text-sm ${isYes ? 'text-green-700' : 'text-red-700'}`}>
|
|
{isYes ? '✓' : '✗'}
|
|
</span>
|
|
<span className="text-sm text-gray-700">{q.question}</span>
|
|
<span className={`ml-auto text-xs font-medium ${isYes ? 'text-green-600' : 'text-red-600'}`}>
|
|
{isYes ? 'Ja — OK' : 'Nein — Finding erstellt'}
|
|
</span>
|
|
</div>
|
|
{!isYes && (
|
|
<p className="text-xs text-red-600 mt-1 ml-6">{q.finding_if_no}</p>
|
|
)}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|