class ChatInterface { constructor() { this.messagesContainer = document.getElementById('chatMessages'); this.messageInput = document.getElementById('messageInput'); this.sendButton = document.getElementById('sendButton'); this.typingIndicator = document.getElementById('typingIndicator'); this.quickReplies = document.getElementById('quickReplies'); this.isTyping = false; this.conversationStarted = false; this.init(); } init() { this.setupEventListeners(); this.startConversation(); this.adjustTextareaHeight(); } setupEventListeners() { // Send button click this.sendButton.addEventListener('click', () => this.sendMessage()); // Enter key to send (Shift+Enter for new line) this.messageInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.sendMessage(); } }); // Auto-resize textarea this.messageInput.addEventListener('input', () => this.adjustTextareaHeight()); // Quick reply clicks this.quickReplies.addEventListener('click', (e) => { if (e.target.classList.contains('quick-reply')) { this.sendQuickReply(e.target.textContent); } }); } adjustTextareaHeight() { this.messageInput.style.height = 'auto'; this.messageInput.style.height = Math.min(this.messageInput.scrollHeight, 100) + 'px'; } async startConversation() { await this.delay(2000); const welcomeMessage = "Hello! 👋 I'm your AI assistant calling on behalf of a client who is interested in making a reservation to visit your apartment. Could you please tell me which apartment you'd like to schedule a viewing for?"; await this.addBotMessage(welcomeMessage); await this.delay(2000); this.showQuickReplies([ "Apartment A - 2BR/1BA", "Apartment B - 3BR/2BA", "Apartment C - 1BR/1BA", "I have multiple units" ]); this.conversationStarted = true; } async sendMessage() { const message = this.messageInput.value.trim(); if (!message || this.isTyping) return; this.addUserMessage(message); this.messageInput.value = ''; this.adjustTextareaHeight(); this.hideQuickReplies(); // Simulate AI processing await this.showTyping(); const response = await this.generateResponse(message); await this.addBotMessage(response.message); await this.delay(2000); if (response.quickReplies) { this.showQuickReplies(response.quickReplies); } } async sendQuickReply(reply) { this.addUserMessage(reply); this.hideQuickReplies(); await this.showTyping(); const response = await this.generateResponse(reply); await this.addBotMessage(response.message); await this.delay(2000); if (response.quickReplies) { this.showQuickReplies(response.quickReplies); } } async generateResponse(userMessage) { try { const response = await fetch('/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message: userMessage, conversation_history: this.getConversationHistory() }) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return { message: data.message, quickReplies: data.quick_replies || [] }; } catch (error) { console.error('Error calling chat API:', error); // Fallback response if API fails return { message: "I apologize, but I'm having trouble connecting right now. Could you please try again in a moment?", quickReplies: ["Try again", "Contact support"] }; } } getConversationHistory() { const messages = this.messagesContainer.querySelectorAll('.message'); const history = []; messages.forEach(messageEl => { const isBot = messageEl.classList.contains('message-bot'); const content = messageEl.querySelector('.message-content').textContent; history.push({ role: isBot ? 'assistant' : 'user', content: content }); }); return history; } addUserMessage(message) { const messageElement = this.createMessageElement('user', message); this.messagesContainer.appendChild(messageElement); this.scrollToBottom(); } async addBotMessage(message) { const messageElement = this.createMessageElement('bot', message); this.messagesContainer.appendChild(messageElement); this.scrollToBottom(); this.hideTyping(); } createMessageElement(sender, content) { const messageDiv = document.createElement('div'); messageDiv.className = `message message-${sender}`; const avatar = document.createElement('div'); avatar.className = `avatar avatar-${sender}`; avatar.innerHTML = sender === 'bot' ? '' : ''; const contentDiv = document.createElement('div'); contentDiv.className = 'message-content'; contentDiv.textContent = content; messageDiv.appendChild(avatar); messageDiv.appendChild(contentDiv); return messageDiv; } async showTyping() { this.isTyping = true; this.sendButton.disabled = true; this.typingIndicator.style.display = 'flex'; this.scrollToBottom(); } hideTyping() { this.isTyping = false; this.sendButton.disabled = false; this.typingIndicator.style.display = 'none'; } showQuickReplies(replies) { this.quickReplies.innerHTML = ''; replies.forEach(reply => { const button = document.createElement('button'); button.className = 'quick-reply'; button.textContent = reply; this.quickReplies.appendChild(button); }); this.quickReplies.style.display = 'flex'; } hideQuickReplies() { this.quickReplies.style.display = 'none'; } scrollToBottom() { setTimeout(() => { this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight; }, 100); } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } // Initialize the chat interface when the page loads document.addEventListener('DOMContentLoaded', () => { new ChatInterface(); }); // Add some smooth animations and interactions document.addEventListener('DOMContentLoaded', () => { // Add hover effects to messages document.addEventListener('mouseover', (e) => { if (e.target.closest('.message')) { e.target.closest('.message').style.transform = 'translateY(-1px)'; } }); document.addEventListener('mouseout', (e) => { if (e.target.closest('.message')) { e.target.closest('.message').style.transform = 'translateY(0)'; } }); // Add some visual feedback for the send button const sendButton = document.getElementById('sendButton'); sendButton.addEventListener('mousedown', () => { sendButton.style.transform = 'scale(0.95)'; }); sendButton.addEventListener('mouseup', () => { sendButton.style.transform = 'scale(1)'; }); });