import { useCallback, useEffect, useRef, useState } from 'react' import { Box, Divider, Drawer, IconButton, TextField, Tooltip, Typography } from '@mui/material' import { RotateCcw, Send, Sparkles, X } from 'lucide-react' import { useLocation } from 'react-router' import { useShallow } from 'zustand/react/shallow' import { useAssistantStore } from '../../stores/assistantStore' import { useSessionStore } from '../../stores/sessionStore' import { useAssistantSuggestions, useAssistantAnswer } from '../../hooks/useAssistant' import { AssistantContextSummary } from './AssistantContextSummary' import { AssistantMessageList } from './AssistantMessageList' import { AssistantPromptSuggestions } from './AssistantPromptSuggestions' import { AssistantLoadingState } from './AssistantLoadingState' import { AssistantErrorState } from './AssistantErrorState' import type { AssistantContext } from '../../domain/assistant' import type { WorkspaceType } from '../../domain/enums' function resolveWorkspace(pathname: string): WorkspaceType | null { if (pathname.startsWith('/supply')) return 'SUPPLY' as WorkspaceType if (pathname.startsWith('/demand')) return 'DEMAND' as WorkspaceType return null } export function GlobalAIAssistantDrawer() { const { isOpen, close, context, setContext, messages, isLoading, error, addMessage, setLoading, setError, clearConversation } = useAssistantStore(useShallow(s => s)) const { currentUser } = useSessionStore() const location = useLocation() const [inputText, setInputText] = useState('') const scrollRef = useRef(null) const answerQuestion = useAssistantAnswer() const { data: suggestions = [] } = useAssistantSuggestions(isOpen ? context : null) // Build context from route when drawer opens or route changes while open useEffect(() => { if (!isOpen) return const ctx: AssistantContext = { currentRoute: location.pathname, workspace: resolveWorkspace(location.pathname), userRole: currentUser?.role ?? 'VIEWER', organizationId: currentUser?.organizationId ?? '', } setContext(ctx) }, [isOpen, location.pathname]) // Auto-scroll on new messages useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight } }, [messages, isLoading]) const handleQuestion = useCallback((question: string) => { if (!question.trim() || isLoading) return setInputText('') setError(null) const userMsg = { id: crypto.randomUUID(), role: 'user' as const, content: question.trim(), createdAt: new Date().toISOString(), } addMessage(userMsg) setLoading(true) const ctx = context ?? { currentRoute: location.pathname, workspace: resolveWorkspace(location.pathname), userRole: currentUser?.role ?? 'VIEWER', organizationId: currentUser?.organizationId ?? '', } answerQuestion.mutate( { context: ctx, question }, { onSuccess: (answer) => { addMessage({ id: crypto.randomUUID(), role: 'assistant', createdAt: new Date().toISOString(), ...answer, }) setLoading(false) }, onError: () => { setError('Antwort konnte nicht generiert werden. Bitte erneut versuchen.') setLoading(false) }, }, ) }, [context, isLoading, location.pathname, currentUser, addMessage, setLoading, setError, answerQuestion]) const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleQuestion(inputText) } } const handleClear = () => { clearConversation() } const showSuggestions = suggestions.length > 0 && messages.length === 0 return ( {/* Header */} AI Assistent Kontextbasierte Entscheidungsunterstützung {/* Context summary */} {context && } {/* Scrollable body */} {/* Welcome message */} {messages.length === 0 && !isLoading && ( Ich helfe Ihnen mit kontextbezogenen Fragen zu dieser Seite. Meine Antworten basieren auf strukturierten Daten — keine erfundenen Fakten. )} {/* Suggestions */} {showSuggestions && ( <> )} {/* Messages */} {messages.length > 0 && ( )} {/* Inline suggestions after messages */} {messages.length > 0 && suggestions.length > 0 && !isLoading && ( <> )} {/* Loading */} {isLoading && } {/* Error */} {error && setError(null)} />} {/* Input area */} setInputText(e.target.value)} onKeyDown={handleKeyDown} disabled={isLoading} sx={{ '& .MuiOutlinedInput-root': { fontSize: '0.8125rem', borderRadius: 2 }, }} /> handleQuestion(inputText)} disabled={!inputText.trim() || isLoading} sx={{ bgcolor: '#4f46e5', color: 'white', flexShrink: 0, '&:hover': { bgcolor: '#4338ca' }, '&:disabled': { bgcolor: '#e8e7e4', color: '#94a3b8' }, }} > Antworten sind datenbasiert — Aktionen erfordern manuelle Bestätigung ) }