// FinBot Frontend Application JavaScript const API_BASE = "http://localhost:8000/api"; // State let currentUser = null; let users = []; let userCollections = []; // Initialize app document.addEventListener("DOMContentLoaded", async () => { console.log("FinBot Frontend Initializing..."); // Check if already logged in const savedUser = localStorage.getItem("currentUser"); if (savedUser) { const user = JSON.parse(savedUser); await loginUser(user.username); } else { // Show login screen await loadUsers(); showLoginScreen(); } }); // Load users for login dropdown async function loadUsers() { try { const response = await fetch(`${API_BASE}/users`); if (!response.ok) throw new Error("Failed to load users"); users = await response.json(); // Populate dropdown const select = document.getElementById("userSelect"); select.innerHTML = ''; users.forEach(user => { const option = document.createElement("option"); option.value = user.username; option.textContent = `${user.name} (${user.role})`; select.appendChild(option); }); // Populate login buttons const usersList = document.getElementById("usersList"); usersList.innerHTML = ""; users.forEach(user => { const btn = document.createElement("button"); btn.className = "user-btn"; btn.onclick = () => loginUser(user.username); btn.innerHTML = ` ${user.name} ${user.role} • ${user.department} `; usersList.appendChild(btn); }); } catch (error) { console.error("Error loading users:", error); showError("Failed to load user list. Check that the backend is running."); } } // Show login screen function showLoginScreen() { document.getElementById("loginScreen").style.display = "flex"; document.getElementById("chatInterface").style.display = "none"; } // Show chat interface function showChatInterface() { document.getElementById("loginScreen").style.display = "none"; document.getElementById("chatInterface").style.display = "flex"; } // Login user async function loginUser(username) { try { const response = await fetch(`${API_BASE}/users/${username}`); if (!response.ok) throw new Error("User not found"); currentUser = await response.json(); userCollections = currentUser.accessible_collections; // Save to localStorage localStorage.setItem("currentUser", JSON.stringify(currentUser)); // Update UI document.getElementById("userName").textContent = currentUser.name; document.getElementById("userRole").textContent = currentUser.role; document.getElementById("userDept").textContent = currentUser.department; // Update collections display updateCollectionsDisplay(); // Clear chat and show interface document.getElementById("chatMessages").innerHTML = `

Welcome, ${currentUser.name}!

You are logged in as: ${currentUser.role}

You have access to: ${userCollections.join(", ")}

You can now ask questions about FinSolve. Try:

`; showChatInterface(); document.getElementById("queryInput").focus(); } catch (error) { console.error("Error logging in:", error); showError(`Failed to login user: ${error.message}`); } } // Logout user function logout() { currentUser = null; userCollections = []; localStorage.removeItem("currentUser"); document.getElementById("userSelect").value = ""; showLoginScreen(); } // Switch user via dropdown async function switchUser() { const username = document.getElementById("userSelect").value; if (username) { await loginUser(username); } } // Update collections display function updateCollectionsDisplay() { const collectionsDiv = document.getElementById("collections"); collectionsDiv.innerHTML = ""; userCollections.forEach(collection => { const tag = document.createElement("div"); tag.className = "collection-tag"; tag.textContent = collection.toUpperCase(); collectionsDiv.appendChild(tag); }); } // Send query async function sendQuery() { const queryInput = document.getElementById("queryInput"); const query = queryInput.value.trim(); if (!query) return; if (!currentUser) { showError("Please login first"); return; } // Add user message to chat addMessageToChat(query, "user"); queryInput.value = ""; // Show loading indicator showLoadingIndicator(true); try { // Call API const response = await fetch(`${API_BASE}/chat`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_role: currentUser.role, query: query, user_id: currentUser.username, }), }); if (!response.ok) { throw new Error(`API Error: ${response.statusText}`); } const result = await response.json(); // Hide loading showLoadingIndicator(false); // Display guardrail warnings if any if (result.guardrail_warnings && result.guardrail_warnings.length > 0) { showGuardrailWarnings(result.guardrail_warnings); } else { hideGuardrailWarnings(); } // Handle RBAC denial if (result.rbac_denied) { addMessageToChat( `🔒 Access Denied
${result.answer}
(Reason: ${result.rbac_reason})`, "assistant" ); return; } // Build message with metadata let message = result.answer; if (result.sources && result.sources.length > 0) { message += `
📄 Sources:`; result.sources.forEach(source => { message += `
${source.document} (Page ${source.page_number})`; if (source.section_title) { message += ` - ${source.section_title}`; } message += `
`; }); message += `
`; } // Add metadata message += `
`; message += `${result.route}`; if (result.guardrail_flags && result.guardrail_flags.length > 0) { message += `⚠️ Flags: ${result.guardrail_flags.join(", ")}`; } message += `
`; // Add message addMessageToChat(message, "assistant"); } catch (error) { console.error("Error:", error); showLoadingIndicator(false); addMessageToChat( `❌ Error: ${error.message}`, "assistant" ); showError("Failed to process query. Is the backend running on localhost:8000?"); } } // Add message to chat function addMessageToChat(content, role) { const messagesDiv = document.getElementById("chatMessages"); const messageDiv = document.createElement("div"); messageDiv.className = `message ${role}`; const contentDiv = document.createElement("div"); contentDiv.className = "message-content"; contentDiv.innerHTML = content; messageDiv.appendChild(contentDiv); messagesDiv.appendChild(messageDiv); // Scroll to bottom messagesDiv.scrollTop = messagesDiv.scrollHeight; } // Show/hide loading indicator function showLoadingIndicator(show) { document.getElementById("loadingIndicator").style.display = show ? "flex" : "none"; document.getElementById("sendBtn").disabled = show; document.getElementById("queryInput").disabled = show; } // Show guardrail warnings function showGuardrailWarnings(warnings) { const warningsDiv = document.getElementById("guardrailWarnings"); warningsDiv.innerHTML = ""; warnings.forEach(warning => { const p = document.createElement("p"); p.textContent = warning; warningsDiv.appendChild(p); }); warningsDiv.style.display = "block"; } // Hide guardrail warnings function hideGuardrailWarnings() { document.getElementById("guardrailWarnings").style.display = "none"; } // Handle Enter key in input function handleKeyPress(event) { if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); sendQuery(); } } // Close modal function closeModal() { document.getElementById("messageModal").style.display = "none"; } // Show error message function showError(message) { const errorDiv = document.createElement("div"); errorDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 1rem; background: #f44336; color: white; border-radius: 4px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); z-index: 10000; max-width: 400px; `; errorDiv.textContent = message; document.body.appendChild(errorDiv); setTimeout(() => { errorDiv.remove(); }, 5000); } // System health check async function checkSystemHealth() { try { const response = await fetch(`${API_BASE}/health`); const data = await response.json(); console.log("System Health:", data); if (!data.collections_available) { showError("Warning: No document collections available. Please run ingestion first."); } } catch (error) { console.warn("Health check failed:", error); } } // Run health check on startup setTimeout(checkSystemHealth, 1000); console.log("FinBot Frontend Ready");