'use client'; import React, { useState, useRef, useEffect } from 'react'; import { User, ChatMessage as ChatMessageType } from '@/lib/types'; import { api } from '@/lib/api'; import ChatMessage from './ChatMessage'; import { Send, Loader2, LogOut } from 'lucide-react'; import { ROLE_COLORS, COLLECTION_ICONS } from '@/lib/constants'; interface ChatInterfaceProps { user: User; onLogout: () => void; onAdminPanel: () => void; } export default function ChatInterface({ user, onLogout, onAdminPanel }: ChatInterfaceProps) { const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); const [collections, setCollections] = useState([]); const messagesEndRef = useRef(null); useEffect(() => { scrollToBottom(); loadCollections(); }, [messages]); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; const loadCollections = async () => { try { const cols = await api.getCollections(); setCollections(cols.map((c) => c.name)); } catch (err) { console.error('Failed to load collections:', err); } }; const handleSendMessage = async (e: React.FormEvent) => { e.preventDefault(); if (!input.trim()) return; // Add user message const userMessage: ChatMessageType = { id: Date.now().toString(), type: 'user', content: input, timestamp: new Date(), }; setMessages((prev) => [...prev, userMessage]); setInput(''); setLoading(true); try { const response = await api.chat({ user_role: user.role, query: input, user_id: user.username, }); // Guard against empty/blank responses from the backend const answerText = response.answer?.trim() ? response.answer : "I wasn't able to generate a response for your question. Please try rephrasing or ask a different question."; const assistantMessage: ChatMessageType = { id: (Date.now() + 1).toString(), type: 'assistant', content: answerText, timestamp: new Date(), response: { ...response, answer: answerText }, }; setMessages((prev) => [...prev, assistantMessage]); } catch (error: unknown) { // Extract a helpful message from the error if possible let errorText = 'Sorry, I encountered an error processing your query. Please try again in a moment.'; if (error && typeof error === 'object' && 'response' in error) { const axiosError = error as { response?: { data?: { error?: string; detail?: string } } }; const serverMsg = axiosError.response?.data?.detail || axiosError.response?.data?.error; if (serverMsg) { errorText = `Sorry, something went wrong: ${serverMsg}`; } } const errorMessage: ChatMessageType = { id: (Date.now() + 1).toString(), type: 'assistant', content: errorText, timestamp: new Date(), }; setMessages((prev) => [...prev, errorMessage]); console.error('Chat error:', error); } finally { setLoading(false); } }; return (
{/* Header */}

FinBot

Advanced RAG with RBAC Enforcement

{/* Sidebar */}
{/* User Profile */}

Your Profile

Name

{user.name}

Username

@{user.username}

Department

{user.department}

{user.role}
{/* Access Control */}

🔐 Your Access

{(user.accessible_collections ?? []).map((collection) => (
{COLLECTION_ICONS[collection] || '📁'} {collection}
))}

Restricted Collections:

    {collections .filter((c) => !(user.accessible_collections ?? []).includes(c)) .map((c) => (
  • 🚫 {c}
  • ))}
{/* System Info */}

System Info

  • Backend: Running
  • Collections: {collections.length}
  • RBAC: Enforced
{/* Chat Area */}
{/* Messages */}
{messages.length === 0 && (
💬

Start a Conversation

Ask FinBot any questions about your company's business data. RBAC ensures you only see information you're authorized to access.

)} {messages.map((msg) => ( ))} {loading && (
FinBot is thinking...
)}
{/* Input */}
setInput(e.target.value)} placeholder="Ask a question about your company..." disabled={loading} className="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 disabled:opacity-50" />

💡 Tip: Try asking about different collections or testing RBAC by asking about restricted content.

); }