import { useState, useEffect, useRef, useCallback } from 'react' import { Sparkles, Send, Loader2, Flag } from 'lucide-react' import { assistantChatApi } from '@/api/assistantChat' import { toast } from '@/lib/toast' import { ChatSidebar } from '@/components/assistant/ChatSidebar' import { ChatMessage } from '@/components/assistant/ChatMessage' import { ConcludeSessionModal } from '@/components/assistant/ConcludeSessionModal' import type { ChatListItem, AssistantChatMessage as ChatMessageType, ConclusionOutcome } from '@/types/assistant-chat' import type { SuggestedFlow } from '@/types/copilot' interface MessageWithMeta extends ChatMessageType { suggestedFlows?: SuggestedFlow[] } export default function AssistantChatPage() { const [chats, setChats] = useState([]) const [activeChatId, setActiveChatId] = useState(null) const [messages, setMessages] = useState([]) const [input, setInput] = useState('') const [loading, setLoading] = useState(false) const [showConclude, setShowConclude] = useState(false) const messagesEndRef = useRef(null) const inputRef = useRef(null) // Load chat list useEffect(() => { loadChats() }, []) // Auto-scroll useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [messages]) const loadChats = async () => { try { const list = await assistantChatApi.listChats(1, 100) setChats(list) } catch { // silently handle } } const selectChat = useCallback(async (chatId: string) => { setActiveChatId(chatId) try { const chat = await assistantChatApi.getChat(chatId) setMessages(chat.messages.map(m => ({ ...m }))) } catch { setMessages([]) } }, []) const handleNewChat = async () => { try { const chat = await assistantChatApi.createChat() setChats(prev => [ { id: chat.id, title: chat.title, message_count: 0, pinned: false, created_at: chat.created_at, updated_at: chat.updated_at }, ...prev, ]) setActiveChatId(chat.id) setMessages([]) } catch { toast.error('Failed to create chat') } } const handleDeleteChat = async (chatId: string) => { try { await assistantChatApi.deleteChat(chatId) setChats(prev => prev.filter(c => c.id !== chatId)) if (activeChatId === chatId) { setActiveChatId(null) setMessages([]) } } catch { toast.error('Failed to delete chat') } } const handleTogglePin = async (chatId: string, pinned: boolean) => { try { await assistantChatApi.updateChat(chatId, { pinned }) setChats(prev => prev.map(c => c.id === chatId ? { ...c, pinned } : c) ) } catch { toast.error('Failed to update chat') } } const handleSend = async () => { if (!input.trim() || !activeChatId || loading) return const userMessage = input.trim() setInput('') setMessages(prev => [...prev, { role: 'user', content: userMessage }]) setLoading(true) try { const response = await assistantChatApi.sendMessage(activeChatId, userMessage) setMessages(prev => [ ...prev, { role: 'assistant', content: response.content, suggestedFlows: response.suggested_flows }, ]) // Update chat list title if it was the first message setChats(prev => prev.map(c => c.id === activeChatId ? { ...c, message_count: c.message_count + 2, title: c.message_count === 0 ? userMessage.slice(0, 100) : c.title, updated_at: new Date().toISOString() } : c ) ) } catch { setMessages(prev => [ ...prev, { role: 'assistant', content: 'Sorry, something went wrong. Please try again.' }, ]) } finally { setLoading(false) requestAnimationFrame(() => inputRef.current?.focus()) } } const handleConclude = async (outcome: ConclusionOutcome, notes: string): Promise => { if (!activeChatId) throw new Error('No active chat') const response = await assistantChatApi.concludeChat(activeChatId, { outcome, notes: notes || undefined }) // Update chat in sidebar to show concluded status setChats(prev => prev.map(c => c.id === activeChatId ? { ...c, concluded_at: response.concluded_at, conclusion_outcome: outcome } : c ) ) return response.summary } const handleResumeNew = async (summary: string) => { try { const chat = await assistantChatApi.createChat() setChats(prev => [ { id: chat.id, title: chat.title, message_count: 0, pinned: false, created_at: chat.created_at, updated_at: chat.updated_at }, ...prev, ]) setActiveChatId(chat.id) setMessages([]) // Send the summary as the first message to prime the new chat const resumePrompt = `I'm continuing a previous troubleshooting session. Here's where we left off:\n\n${summary}\n\nPlease review this context and help me continue from where we stopped.` setInput('') setMessages([{ role: 'user', content: resumePrompt }]) setLoading(true) const response = await assistantChatApi.sendMessage(chat.id, resumePrompt) setMessages(prev => [ ...prev, { role: 'assistant', content: response.content, suggestedFlows: response.suggested_flows }, ]) setChats(prev => prev.map(c => c.id === chat.id ? { ...c, message_count: 2, title: resumePrompt.slice(0, 100), updated_at: new Date().toISOString() } : c ) ) } catch { toast.error('Failed to create resume chat') } finally { setLoading(false) } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleSend() } } return (
{/* Sidebar */} {/* Main chat area */}
{activeChatId ? ( <> {/* Messages */}
{messages.length === 0 && !loading && (

AI Assistant

Ask me anything about IT infrastructure, networking, Active Directory, cloud platforms, or troubleshooting. I'll also suggest relevant flows from your team's library.

)} {messages.map((msg, i) => ( ))} {loading && (
)}
{/* Input */}