|
|
| |
| 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'); |
|
|
| |
| let messages = []; |
| let sources = []; |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| |
| messageInput.addEventListener('input', () => { |
| sendButton.disabled = !messageInput.value.trim(); |
| }); |
| |
| |
| sendButton.addEventListener('click', sendMessage); |
| |
| |
| messageInput.addEventListener('keypress', (e) => { |
| if (e.key === 'Enter') { |
| sendMessage(); |
| } |
| }); |
| |
| |
| scrapeBtn.addEventListener('click', triggerScrape); |
| refreshBtn.addEventListener('click', triggerRefreshIndex); |
| }); |
|
|
| |
| function formatDate() { |
| return new Date().toLocaleString(); |
| } |
|
|
| |
| 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); |
| |
| const meta = document.createElement('div'); |
| meta.className = 'meta'; |
| meta.textContent = formatDate(); |
| |
| messageEl.appendChild(bubble); |
| messageEl.appendChild(meta); |
| |
| return messageEl; |
| } |
|
|
| |
| function updateSources(sources) { |
| sourcesContainer.innerHTML = ''; |
| |
| |
| 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 <i class="fas fa-external-link-alt"></i>`; |
| |
| 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); |
| }); |
| } |
|
|
| |
| async function sendMessage() { |
| const content = messageInput.value.trim(); |
| if (!content) return; |
| |
| |
| if (emptyChat.style.display !== 'none') { |
| emptyChat.style.display = 'none'; |
| } |
| |
| |
| const userMessage = { role: 'user', content }; |
| messages.push(userMessage); |
| messagesContainer.appendChild(createMessageElement(userMessage)); |
| |
| |
| messageInput.value = ''; |
| sendButton.disabled = true; |
| |
| |
| 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); |
| |
| |
| chatContainer.scrollTop = chatContainer.scrollHeight; |
| |
| try { |
| |
| 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(); |
| |
| |
| messagesContainer.removeChild(thinkingEl); |
| |
| |
| const assistantMessage = { role: 'assistant', content: data.response }; |
| messages.push(assistantMessage); |
| messagesContainer.appendChild(createMessageElement(assistantMessage)); |
| |
| |
| sources = data.sources; |
| updateSources(sources); |
| |
| |
| chatContainer.scrollTop = chatContainer.scrollHeight; |
| |
| } catch (error) { |
| console.error('Error:', error); |
| |
| |
| messagesContainer.removeChild(thinkingEl); |
| |
| |
| 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)); |
| |
| |
| chatContainer.scrollTop = chatContainer.scrollHeight; |
| } |
| } |
|
|
| |
| async function askQuickQuestion(question) { |
| |
| messageInput.value = question; |
| |
| |
| await sendMessage(); |
| } |
|
|
| |
| 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'; |
| } |
| } |
| |