'use client' import { useState, useEffect, useRef, useCallback } from 'react' import { EXAMPLE_QUESTIONS, AdvisorEmptyState, AdvisorMessageList, type Message } from './ComplianceAdvisorParts' // ============================================================================= // TYPES // ============================================================================= interface ComplianceAdvisorWidgetProps { currentStep?: string } type Country = 'DE' | 'AT' | 'CH' | 'EU' const COUNTRIES: { code: Country; label: string }[] = [ { code: 'DE', label: 'DE' }, { code: 'AT', label: 'AT' }, { code: 'CH', label: 'CH' }, { code: 'EU', label: 'EU' }, ] // ============================================================================= // COMPONENT // ============================================================================= export function ComplianceAdvisorWidget({ currentStep = 'default' }: ComplianceAdvisorWidgetProps) { const [isOpen, setIsOpen] = useState(false) const [isExpanded, setIsExpanded] = useState(false) const [messages, setMessages] = useState([]) const [inputValue, setInputValue] = useState('') const [isTyping, setIsTyping] = useState(false) const [selectedCountry, setSelectedCountry] = useState('DE') const messagesEndRef = useRef(null) const abortControllerRef = useRef(null) const exampleQuestions = EXAMPLE_QUESTIONS[currentStep] || EXAMPLE_QUESTIONS.default useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [messages]) useEffect(() => { return () => { abortControllerRef.current?.abort() } }, []) const handleSendMessage = useCallback( async (content: string) => { if (!content.trim() || isTyping) return const userMessage: Message = { id: `msg-${Date.now()}`, role: 'user', content: content.trim(), timestamp: new Date(), } setMessages((prev) => [...prev, userMessage]) setInputValue('') setIsTyping(true) const agentMessageId = `msg-${Date.now()}-agent` abortControllerRef.current = new AbortController() try { const history = messages.map((m) => ({ role: m.role === 'user' ? 'user' : 'assistant', content: m.content, })) const response = await fetch('/api/sdk/compliance-advisor/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: content.trim(), history, currentStep, country: selectedCountry, }), signal: abortControllerRef.current.signal, }) if (!response.ok) { const errorData = await response.json().catch(() => ({ error: 'Unbekannter Fehler' })) throw new Error(errorData.error || `Server-Fehler (${response.status})`) } setMessages((prev) => [ ...prev, { id: agentMessageId, role: 'agent', content: '', timestamp: new Date() }, ]) const reader = response.body!.getReader() const decoder = new TextDecoder() let accumulated = '' while (true) { const { done, value } = await reader.read() if (done) break accumulated += decoder.decode(value, { stream: true }) const currentText = accumulated setMessages((prev) => prev.map((m) => (m.id === agentMessageId ? { ...m, content: currentText } : m)) ) requestAnimationFrame(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) }) } setIsTyping(false) } catch (error) { if ((error as Error).name === 'AbortError') { setIsTyping(false) return } const errorMessage = error instanceof Error ? error.message : 'Verbindung fehlgeschlagen' setMessages((prev) => { const hasAgent = prev.some((m) => m.id === agentMessageId) if (hasAgent) { return prev.map((m) => m.id === agentMessageId ? { ...m, content: `Fehler: ${errorMessage}` } : m ) } return [ ...prev, { id: agentMessageId, role: 'agent' as const, content: `Fehler: ${errorMessage}`, timestamp: new Date() }, ] }) setIsTyping(false) } }, [isTyping, messages, currentStep, selectedCountry] ) const handleStopGeneration = useCallback(() => { abortControllerRef.current?.abort() setIsTyping(false) }, []) const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleSendMessage(inputValue) } } if (!isOpen) { return ( ) } return (
{/* Header */}
Compliance Advisor
{COUNTRIES.map(({ code, label }) => ( ))}
{/* Messages Area */}
{messages.length === 0 ? ( handleSendMessage(q)} /> ) : ( )}
{/* Input Area */}
setInputValue(e.target.value)} onKeyDown={handleKeyDown} placeholder="Frage eingeben..." disabled={isTyping} className="flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent disabled:opacity-50" /> {isTyping ? ( ) : ( )}
) }