|
|
|
|
|
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'; |
|
} |
|
} |
|
|