import { FormEvent, useCallback, useEffect, useRef, useState } from 'react' import { FaComment, FaPaperPlane, FaPlus, FaRobot } from 'react-icons/fa' import { Helmet } from 'react-helmet' import { useStoreState } from '@/store' import { Button, Drawer, Dropdown, ScrollBar, Spinner } from '@/components/ui' import { Container } from '@/components/shared' import { useLocalization } from '@/utils/hooks/useLocalization' import { aiService } from '@/services/ai.service' import { APP_NAME } from '@/constants/app.constant' import { AI_ASSISTANT } from '@/constants/permission.constant' import ConversationList from './components/ConversationList' import MessageBubble from './components/MessageBubble' import { useAiBots } from './hooks/useAiBots' import { useConversations } from './hooks/useConversations' const Assistant = () => { const { translate } = useLocalization() const { user, tenant } = useStoreState((state) => state.auth) const { bots, selectedBot, selectedBotId, setSelectedBotId } = useAiBots() const { conversations, activeConversationId, activeConversation, messages, appendMessage, startNewConversation, selectConversation, deleteConversation, } = useConversations() const [isConversationDrawerOpen, setIsConversationDrawerOpen] = useState(false) const [input, setInput] = useState('') const [loading, setLoading] = useState(false) const inputRef = useRef(null) const bottomRef = useRef(null) const requestRef = useRef(null) // Açık istek varken sayfadan çıkılırsa iptal et. useEffect(() => () => requestRef.current?.abort(), []) useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' }) }, [messages, loading]) useEffect(() => { if (!loading) inputRef.current?.focus() }, [loading]) const focusInput = () => requestAnimationFrame(() => inputRef.current?.focus()) const handleNewConversation = useCallback(() => { if (loading) return startNewConversation() setInput('') setIsConversationDrawerOpen(false) focusInput() }, [loading, startNewConversation]) const handleSelectConversation = useCallback( (conversationId: string) => { if (loading) return selectConversation(conversationId) setIsConversationDrawerOpen(false) focusInput() }, [loading, selectConversation], ) const handleDeleteConversation = useCallback( (conversationId: string) => { if (loading) return deleteConversation(conversationId) if (conversationId === activeConversationId) setInput('') }, [loading, deleteConversation, activeConversationId], ) const handleSubmit = async (event: FormEvent) => { event.preventDefault() const question = input.trim() if (loading || !question || !activeConversation) return const conversationId = activeConversation.id setInput('') setLoading(true) appendMessage(conversationId, { role: 'user', content: question, createdAt: new Date().toISOString(), }) const controller = new AbortController() requestRef.current = controller try { if (!selectedBot?.apiUrl?.trim()) { throw new Error('AI bot url is not configured.') } const content = await aiService.askBot( selectedBot.apiUrl, { biletId: conversationId, question, sessionId: conversationId, tenantName: tenant?.tenantName ?? null, }, controller.signal, ) appendMessage(conversationId, { role: 'assistant', content, createdAt: new Date().toISOString(), }) } catch (error) { if (controller.signal.aborted) return console.error('AI bot request failed:', error) appendMessage(conversationId, { role: 'assistant', content: selectedBot ? translate('::AI.RequestFailed') : translate('::AI.SelectModel'), createdAt: new Date().toISOString(), }) } finally { if (requestRef.current === controller) { requestRef.current = null setLoading(false) } } } const conversationListProps = { conversations, activeConversationId, disabled: loading, onSelect: handleSelectConversation, onDelete: handleDeleteConversation, } return ( setIsConversationDrawerOpen(false)} onRequestClose={() => setIsConversationDrawerOpen(false)} >
(
)} renderThumbVertical={({ style, ...props }) => (
)} >
{messages.length === 0 && (

{translate('::AI.Welcome')}

{translate('::AI.Name')}

)} {messages.map((message, index) => ( ))} {loading && (
{translate('::AI.WaitAnswer')}
)}
setInput(event.target.value)} />
{bots.map((item) => ( setSelectedBotId(eventKey)} > {item.name} ))}
) } export default Assistant