"use client" import Link from "next/link" import { usePathname } from "next/navigation" import { Library, MessageSquare, Settings2, Plus, FileText } from "lucide-react" import { cn } from "@/lib/utils" import { useState, useEffect, useRef, useCallback } from "react" import { useChat } from "@/contexts/chat-context" import { EndpointType } from "@/contexts/chat-context" interface RawConversation { response_id: string title: string endpoint: string messages: Array<{ role: string content: string timestamp?: string response_id?: string }> created_at?: string last_activity?: string previous_response_id?: string total_messages: number [key: string]: unknown } interface ChatConversation { response_id: string title: string endpoint: EndpointType messages: Array<{ role: string content: string timestamp?: string response_id?: string }> created_at?: string last_activity?: string previous_response_id?: string total_messages: number [key: string]: unknown } export function Navigation() { const pathname = usePathname() const { endpoint, refreshTrigger, loadConversation, currentConversationId, setCurrentConversationId, conversationDocs, addConversationDoc } = useChat() const [conversations, setConversations] = useState([]) const [loadingConversations, setLoadingConversations] = useState(false) const fileInputRef = useRef(null) const handleNewConversation = () => { setCurrentConversationId(null) // The chat page will handle resetting messages when it detects a new conversation request window.dispatchEvent(new CustomEvent('newConversation')) } const handleFileUpload = async (file: File) => { console.log("Navigation file upload:", file.name) // Trigger loading start event for chat page window.dispatchEvent(new CustomEvent('fileUploadStart', { detail: { filename: file.name } })) try { const formData = new FormData() formData.append('file', file) formData.append('endpoint', endpoint) const response = await fetch('/api/upload_context', { method: 'POST', body: formData, }) if (!response.ok) { const errorText = await response.text() console.error("Upload failed:", errorText) return } const result = await response.json() console.log("Upload result:", result) // Add the file to conversation docs if (result.filename) { addConversationDoc(result.filename) } // Trigger file upload event for chat page to handle window.dispatchEvent(new CustomEvent('fileUploaded', { detail: { file, result } })) // Trigger loading end event window.dispatchEvent(new CustomEvent('fileUploadComplete')) } catch (error) { console.error('Upload failed:', error) // Trigger loading end event even on error window.dispatchEvent(new CustomEvent('fileUploadComplete')) // Trigger error event for chat page to handle window.dispatchEvent(new CustomEvent('fileUploadError', { detail: { filename: file.name, error: error instanceof Error ? error.message : 'Unknown error' } })) } } const handleFilePickerClick = () => { fileInputRef.current?.click() } const handleFilePickerChange = (e: React.ChangeEvent) => { const files = e.target.files if (files && files.length > 0) { handleFileUpload(files[0]) } // Reset the input so the same file can be selected again if (fileInputRef.current) { fileInputRef.current.value = '' } } const routes = [ { label: "Chat", icon: MessageSquare, href: "/chat", active: pathname === "/" || pathname === "/chat", }, { label: "Knowledge", icon: Library, href: "/knowledge", active: pathname === "/knowledge", }, { label: "Settings", icon: Settings2, href: "/settings", active: pathname === "/settings", }, ] const isOnChatPage = pathname === "/" || pathname === "/chat" const fetchConversations = useCallback(async () => { setLoadingConversations(true) try { // Fetch from the selected endpoint only const apiEndpoint = endpoint === 'chat' ? '/api/chat/history' : '/api/langflow/history' const response = await fetch(apiEndpoint) if (response.ok) { const history = await response.json() const rawConversations = history.conversations || [] // Cast conversations to proper type and ensure endpoint is correct const conversations: ChatConversation[] = rawConversations.map((conv: RawConversation) => ({ ...conv, endpoint: conv.endpoint as EndpointType })) // Sort conversations by last activity (most recent first) conversations.sort((a: ChatConversation, b: ChatConversation) => { const aTime = new Date(a.last_activity || a.created_at || 0).getTime() const bTime = new Date(b.last_activity || b.created_at || 0).getTime() return bTime - aTime }) setConversations(conversations) } else { setConversations([]) } // Conversation documents are now managed in chat context } catch (error) { console.error(`Failed to fetch ${endpoint} conversations:`, error) setConversations([]) } finally { setLoadingConversations(false) } }, [endpoint]) // Fetch chat conversations when on chat page, endpoint changes, or refresh is triggered useEffect(() => { if (isOnChatPage) { fetchConversations() } }, [isOnChatPage, endpoint, refreshTrigger, fetchConversations]) return (
{routes.map((route) => (
{route.label}
{route.label === "Settings" && (
)}
))}
{/* Chat Page Specific Sections */} {isOnChatPage && (
{/* Conversations Section */}

Conversations

{/* Conversations List - grows naturally, doesn't fill all space */}
{loadingConversations ? (
Loading...
) : conversations.length === 0 ? (
No conversations yet
) : ( conversations.map((conversation) => (
{ loadConversation(conversation) }} >
{conversation.title}
{conversation.total_messages} messages
{conversation.last_activity && (
{new Date(conversation.last_activity).toLocaleDateString()}
)}
)) )}
{/* Conversation Knowledge Section - appears right after last conversation */}

Conversation knowledge

{conversationDocs.length === 0 ? (
No documents yet
) : ( conversationDocs.map((doc, index) => (
{doc.filename}
)) )}
)}
) }