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