181 lines
5.8 KiB
JavaScript
181 lines
5.8 KiB
JavaScript
// Aria Chat Widget - Dealspace Product Assistant
|
|
(function() {
|
|
'use strict';
|
|
|
|
// Generate or retrieve session ID
|
|
function getSessionId() {
|
|
let sessionId = sessionStorage.getItem('aria_session_id');
|
|
if (!sessionId) {
|
|
sessionId = 'aria_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
sessionStorage.setItem('aria_session_id', sessionId);
|
|
}
|
|
return sessionId;
|
|
}
|
|
|
|
// Chat state
|
|
const state = {
|
|
isOpen: false,
|
|
isLoading: false,
|
|
history: [],
|
|
sessionId: getSessionId()
|
|
};
|
|
|
|
// Create chat widget HTML
|
|
function createWidget() {
|
|
// Chat button
|
|
const button = document.createElement('button');
|
|
button.id = 'aria-chat-button';
|
|
button.setAttribute('aria-label', 'Open chat with Aria');
|
|
button.innerHTML = `
|
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/>
|
|
</svg>
|
|
`;
|
|
|
|
// Chat panel
|
|
const panel = document.createElement('div');
|
|
panel.id = 'aria-chat-panel';
|
|
panel.innerHTML = `
|
|
<div id="aria-chat-header">
|
|
<div id="aria-avatar"><span>A</span></div>
|
|
<div id="aria-header-text">
|
|
<h3>Aria</h3>
|
|
<p>Dealspace Assistant</p>
|
|
</div>
|
|
<button id="aria-close-btn" aria-label="Close chat">×</button>
|
|
</div>
|
|
<div id="aria-chat-messages"></div>
|
|
<div id="aria-chat-input">
|
|
<input type="text" id="aria-message-input" placeholder="Ask about Dealspace..." autocomplete="off">
|
|
<button id="aria-send-btn" aria-label="Send message">
|
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(button);
|
|
document.body.appendChild(panel);
|
|
|
|
// Event listeners
|
|
button.addEventListener('click', toggleChat);
|
|
document.getElementById('aria-close-btn').addEventListener('click', toggleChat);
|
|
document.getElementById('aria-send-btn').addEventListener('click', sendMessage);
|
|
document.getElementById('aria-message-input').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
sendMessage();
|
|
}
|
|
});
|
|
}
|
|
|
|
function toggleChat() {
|
|
const panel = document.getElementById('aria-chat-panel');
|
|
state.isOpen = !state.isOpen;
|
|
|
|
if (state.isOpen) {
|
|
panel.classList.add('open');
|
|
// Show welcome message if no history
|
|
if (state.history.length === 0) {
|
|
addMessage("Hi, I'm Aria! I can answer questions about Dealspace — features, pricing, security, or how it works. What would you like to know?", 'assistant');
|
|
}
|
|
document.getElementById('aria-message-input').focus();
|
|
} else {
|
|
panel.classList.remove('open');
|
|
}
|
|
}
|
|
|
|
function addMessage(content, role) {
|
|
const messagesContainer = document.getElementById('aria-chat-messages');
|
|
const messageDiv = document.createElement('div');
|
|
messageDiv.className = 'aria-message ' + role;
|
|
messageDiv.textContent = content;
|
|
messagesContainer.appendChild(messageDiv);
|
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
|
|
// Store in history (exclude welcome message)
|
|
if (role !== 'assistant' || state.history.length > 0 || content !== "Hi, I'm Aria! I can answer questions about Dealspace — features, pricing, security, or how it works. What would you like to know?") {
|
|
state.history.push({ role: role, content: content });
|
|
// Keep only last 6 messages
|
|
if (state.history.length > 6) {
|
|
state.history = state.history.slice(-6);
|
|
}
|
|
}
|
|
}
|
|
|
|
function showTyping() {
|
|
const messagesContainer = document.getElementById('aria-chat-messages');
|
|
const typingDiv = document.createElement('div');
|
|
typingDiv.id = 'aria-typing-indicator';
|
|
typingDiv.className = 'aria-typing';
|
|
typingDiv.innerHTML = '<span></span><span></span><span></span>';
|
|
messagesContainer.appendChild(typingDiv);
|
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
}
|
|
|
|
function hideTyping() {
|
|
const typingIndicator = document.getElementById('aria-typing-indicator');
|
|
if (typingIndicator) {
|
|
typingIndicator.remove();
|
|
}
|
|
}
|
|
|
|
async function sendMessage() {
|
|
const input = document.getElementById('aria-message-input');
|
|
const sendBtn = document.getElementById('aria-send-btn');
|
|
const message = input.value.trim();
|
|
|
|
if (!message || state.isLoading) return;
|
|
|
|
// Add user message
|
|
addMessage(message, 'user');
|
|
input.value = '';
|
|
|
|
// Show loading state
|
|
state.isLoading = true;
|
|
sendBtn.disabled = true;
|
|
showTyping();
|
|
|
|
try {
|
|
const response = await fetch('/api/chat', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
session_id: state.sessionId,
|
|
message: message,
|
|
history: state.history.slice(0, -1) // Exclude the message we just added
|
|
})
|
|
});
|
|
|
|
hideTyping();
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json();
|
|
throw new Error(error.error || 'Something went wrong');
|
|
}
|
|
|
|
const data = await response.json();
|
|
addMessage(data.reply, 'assistant');
|
|
|
|
} catch (error) {
|
|
hideTyping();
|
|
console.error('Chat error:', error);
|
|
addMessage("Sorry, I'm having trouble connecting. Please try again in a moment.", 'assistant');
|
|
} finally {
|
|
state.isLoading = false;
|
|
sendBtn.disabled = false;
|
|
input.focus();
|
|
}
|
|
}
|
|
|
|
// Initialize when DOM is ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', createWidget);
|
|
} else {
|
|
createWidget();
|
|
}
|
|
})();
|