// DOM Elements const messagesContainer = document.getElementById('messages'); const messageInput = document.getElementById('message-input'); const sendButton = document.getElementById('send-button'); const emptyChat = document.getElementById('empty-chat'); const chatContainer = document.getElementById('chat-container'); const sourcesContainer = document.getElementById('sources-container'); const noSources = document.getElementById('no-sources'); const scrapeBtn = document.getElementById('scrape-btn'); const refreshBtn = document.getElementById('refresh-btn'); // State let messages = []; let sources = []; // Initialize document.addEventListener('DOMContentLoaded', () => { // Enable/disable send button based on input messageInput.addEventListener('input', () => { sendButton.disabled = !messageInput.value.trim(); }); // Send message on button click sendButton.addEventListener('click', sendMessage); // Send message on Enter key messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendMessage(); } }); // Admin buttons scrapeBtn.addEventListener('click', triggerScrape); refreshBtn.addEventListener('click', triggerRefreshIndex); }); // Format date function formatDate() { return new Date().toLocaleString(); } // Create message element function createMessageElement(message) { const messageEl = document.createElement('div'); messageEl.className = `message ${message.role}`; const bubble = document.createElement('div'); bubble.className = 'bubble'; bubble.innerHTML = marked.parse(message.content); // parse Markdown to HTML const meta = document.createElement('div'); meta.className = 'meta'; meta.textContent = formatDate(); messageEl.appendChild(bubble); messageEl.appendChild(meta); return messageEl; } // Update sources panel function updateSources(sources) { sourcesContainer.innerHTML = ''; // Filter sources with score > -8 const filteredSources = sources.filter(source => source.score > -8); if (filteredSources.length === 0) { sourcesContainer.appendChild(noSources); return; } filteredSources.forEach(source => { const card = document.createElement('div'); card.className = 'source-card'; const title = document.createElement('h4'); title.textContent = source.title; const link = document.createElement('a'); link.className = 'link'; link.href = source.url; link.target = '_blank'; link.innerHTML = `View source `; const score = document.createElement('div'); score.className = 'score'; score.textContent = `Relevance: ${source.score.toFixed(2)}`; card.appendChild(title); card.appendChild(link); card.appendChild(score); sourcesContainer.appendChild(card); }); } // Send message async function sendMessage() { const content = messageInput.value.trim(); if (!content) return; // Hide empty chat if visible if (emptyChat.style.display !== 'none') { emptyChat.style.display = 'none'; } // Add user message const userMessage = { role: 'user', content }; messages.push(userMessage); messagesContainer.appendChild(createMessageElement(userMessage)); // Clear input messageInput.value = ''; sendButton.disabled = true; // Show thinking message const thinkingEl = document.createElement('div'); thinkingEl.className = 'message assistant'; const thinkingBubble = document.createElement('div'); thinkingBubble.className = 'bubble'; thinkingBubble.textContent = 'Thinking...'; thinkingEl.appendChild(thinkingBubble); messagesContainer.appendChild(thinkingEl); // Scroll to bottom chatContainer.scrollTop = chatContainer.scrollHeight; try { // Call API const response = await fetch('/api/ask', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ query: content, k: 5 }), }); if (!response.ok) { throw new Error('API request failed'); } const data = await response.json(); // Remove thinking message messagesContainer.removeChild(thinkingEl); // Add assistant message const assistantMessage = { role: 'assistant', content: data.response }; messages.push(assistantMessage); messagesContainer.appendChild(createMessageElement(assistantMessage)); // Update sources sources = data.sources; updateSources(sources); // Scroll to bottom chatContainer.scrollTop = chatContainer.scrollHeight; } catch (error) { console.error('Error:', error); // Remove thinking message messagesContainer.removeChild(thinkingEl); // Add error message const errorMessage = { role: 'assistant', content: "I'm sorry, I encountered an error. Please try again later or contact UB International Student Services directly." }; messages.push(errorMessage); messagesContainer.appendChild(createMessageElement(errorMessage)); // Scroll to bottom chatContainer.scrollTop = chatContainer.scrollHeight; } } // Quick questions async function askQuickQuestion(question) { // Set the question in the input field messageInput.value = question; // Send the message await sendMessage(); } // Admin functions async function triggerScrape() { scrapeBtn.disabled = true; scrapeBtn.textContent = 'Scraping...'; try { const response = await fetch('/api/scrape', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ seed_url: 'https://www.buffalo.edu/international-student-services.html', max_pages: 100 }), }); if (!response.ok) { throw new Error('API request failed'); } const data = await response.json(); alert(`${data.message}`); } catch (error) { console.error('Error:', error); alert('Error starting scraper. Please check console for details.'); } finally { scrapeBtn.disabled = false; scrapeBtn.textContent = 'Scrape New Content'; } } async function triggerRefreshIndex() { refreshBtn.disabled = true; refreshBtn.textContent = 'Refreshing...'; try { const response = await fetch('/api/refresh-index', { method: 'POST', headers: { 'Content-Type': 'application/json', }, }); if (!response.ok) { throw new Error('API request failed'); } const data = await response.json(); alert(`${data.message}`); } catch (error) { console.error('Error:', error); alert('Error refreshing index. Please check console for details.'); } finally { refreshBtn.disabled = false; refreshBtn.textContent = 'Refresh Index'; } }