// Pre-seeded chat messages store
if (!state.whatsappChatHistories) {
state.whatsappChatHistories = {};
}
async function loadWhatsAppChatView() {
showLoader(true, 'Loading WhatsApp Chat Inbox...');
try {
// 1. Fetch guests from database
const guestsRes = await fetch('/api/guests');
const guests = await guestsRes.json();
state.guestsList = guests;
// 2. Fetch staff members for assignment dropdown
const staffRes = await fetch('/api/staff');
const staffList = await staffRes.json();
// Populate Assignee Select
const assigneeSelect = document.getElementById('wa-chat-assignee');
assigneeSelect.innerHTML = '';
staffList.forEach(s => {
assigneeSelect.innerHTML += ``;
});
// 3. Render chat roster list
renderWhatsAppChatRoster();
showLoader(false);
} catch (err) {
showLoader(false);
alert(`Failed to load chat client: ${err.message}`);
}
}
function renderWhatsAppChatRoster() {
const searchVal = document.getElementById('wa-chat-search-input').value.toLowerCase();
const rosterContainer = document.getElementById('wa-chat-roster-list');
rosterContainer.innerHTML = '';
const chats = state.guestsList.filter(g => {
const matchesSearch = g.name.toLowerCase().includes(searchVal) || g.phone.includes(searchVal);
return matchesSearch;
});
if (chats.length === 0) {
rosterContainer.innerHTML = '
No active guests found
';
return;
}
chats.forEach(chat => {
const history = state.whatsappChatHistories[chat.id] || [];
const lastMsg = history[history.length - 1] || { body: 'No messages yet', timestamp: '' };
const unreadCount = history.filter(m => m.status === 'unread' && m.direction === 'incoming').length;
const timeStr = lastMsg.timestamp ? new Date(lastMsg.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : '';
const activeClass = state.activeWhatsAppGuestId === chat.id ? 'active' : '';
const initial = chat.name.charAt(0).toUpperCase();
rosterContainer.innerHTML += `
`;
});
}
function filterWhatsAppChats(tab) {
document.querySelectorAll('.wa-chat-tab').forEach(b => b.classList.remove('active'));
document.getElementById(`btn-wa-tab-${tab}`).classList.add('active');
state.activeWhatsAppChatTab = tab;
renderWhatsAppChatRoster();
}
function selectWhatsAppChat(guestId) {
state.activeWhatsAppGuestId = guestId;
const guest = state.guestsList.find(g => g.id === guestId);
if (!guest) return;
// Mark incoming messages as read
const history = state.whatsappChatHistories[guestId] || [];
history.forEach(m => {
if (m.direction === 'incoming') m.status = 'read';
});
// Update Left roster badges
renderWhatsAppChatRoster();
// Show Middle View panels
document.getElementById('wa-chat-empty-state').style.display = 'none';
document.getElementById('wa-chat-active-header').style.display = 'flex';
document.getElementById('wa-chat-message-window').style.display = 'flex';
document.getElementById('wa-chat-input-bar').style.display = 'flex';
// Populates active header labels
document.getElementById('wa-chat-active-name').textContent = guest.name;
document.getElementById('wa-chat-active-phone').textContent = guest.phone;
document.getElementById('wa-chat-active-avatar').textContent = guest.name.charAt(0).toUpperCase();
// Set checkbox toggle status
document.getElementById('wa-chat-bot-toggle').checked = guest.wa_bot_enabled === 1 || guest.wa_bot_enabled === undefined;
// Build messages list
renderWhatsAppChatBubbles();
// Show Right sidebar details
document.getElementById('wa-chat-detail-drawer').style.display = 'flex';
document.getElementById('wa-profile-avatar-char').textContent = guest.name.charAt(0).toUpperCase();
document.getElementById('wa-profile-name').textContent = guest.name;
document.getElementById('wa-profile-phone').textContent = guest.phone;
document.getElementById('wa-profile-email').textContent = guest.email || 'N/A';
document.getElementById('wa-profile-doc').textContent = guest.document_id || 'N/A';
document.getElementById('wa-profile-lang').textContent = guest.familiar_language || 'English (US)';
// Set details active bookings label
const activeBookings = state.bookingsList ? state.bookingsList.filter(b => b.guest_id === guestId && b.status === 'Checked-In') : [];
if (activeBookings.length > 0) {
document.getElementById('wa-profile-bookings').textContent = activeBookings.map(b => b.room_number).join(', ');
} else {
document.getElementById('wa-profile-bookings').textContent = 'No active bookings';
}
}
function renderWhatsAppChatBubbles() {
const container = document.getElementById('wa-chat-message-window');
container.innerHTML = '';
const history = state.whatsappChatHistories[state.activeWhatsAppGuestId] || [];
if (history.length === 0) {
container.innerHTML = `
No message histories found. Type a message below to dispatch notifications.
`;
return;
}
history.forEach(m => {
const isOut = m.direction === 'outgoing';
const directionClass = isOut ? 'wa-bubble-outgoing' : 'wa-bubble-incoming';
const timeStr = new Date(m.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
// Auto detect links in body
let bodyHtml = m.body;
if (m.body.startsWith('http://') || m.body.startsWith('https://')) {
bodyHtml = `${m.body}`;
}
container.innerHTML += `
`;
});
// Scroll to bottom
container.scrollTop = container.scrollHeight;
}
async function assignWhatsAppChat() {
const assigneeId = document.getElementById('wa-chat-assignee').value;
const guest = state.guestsList.find(g => g.id === state.activeWhatsAppGuestId);
if (!guest) return;
try {
const res = await fetch('/api/guests/edit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: guest.id,
name: guest.name,
phone: guest.phone,
email: guest.email,
document_id: guest.document_id,
notes: guest.notes,
status: guest.status,
familiar_language: guest.familiar_language,
assignee_id: assigneeId ? parseInt(assigneeId) : null,
userId: state.currentUser.id
})
});
if (res.ok) {
guest.assignee_id = assigneeId ? parseInt(assigneeId) : null;
alert('Conversation assignment updated successfully.');
}
} catch (err) {
alert(`Assignment update failed: ${err.message}`);
}
}
async function toggleWhatsAppChatBot() {
const botEnabled = document.getElementById('wa-chat-bot-toggle').checked ? 1 : 0;
const guest = state.guestsList.find(g => g.id === state.activeWhatsAppGuestId);
if (!guest) return;
try {
const res = await fetch('/api/guests/edit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: guest.id,
name: guest.name,
phone: guest.phone,
email: guest.email,
document_id: guest.document_id,
notes: guest.notes,
status: guest.status,
familiar_language: guest.familiar_language,
wa_bot_enabled: botEnabled,
userId: state.currentUser.id
})
});
if (res.ok) {
guest.wa_bot_enabled = botEnabled;
}
} catch (err) {
alert(`AI Bot setting failed: ${err.message}`);
}
}
function triggerWhatsAppChatAttachment() {
const url = prompt('Enter the link/URL of the invoice or document you want to send:');
if (url) {
document.getElementById('wa-chat-text-input').value = url;
}
}
async function sendWhatsAppChatMessage(e) {
if (e) e.preventDefault();
const inputEl = document.getElementById('wa-chat-text-input');
const body = inputEl.value.trim();
if (!body) return;
const guest = state.guestsList.find(g => g.id === state.activeWhatsAppGuestId);
if (!guest) return;
const payload = {
phone: guest.phone,
message: body,
userId: state.currentUser.id
};
showLoader(true, 'Sending WhatsApp message...');
try {
const res = await fetch('/api/whatsapp/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
showLoader(false);
if (res.ok) {
const data = await res.json();
inputEl.value = '';
// Save outgoing message locally
if (!state.whatsappChatHistories[guest.id]) {
state.whatsappChatHistories[guest.id] = [];
}
state.whatsappChatHistories[guest.id].push({
id: data.messageId,
direction: 'outgoing',
body,
status: 'sent',
timestamp: new Date().toISOString()
});
renderWhatsAppChatBubbles();
} else {
const err = await res.json();
alert(`Message dispatch failed: ${err.error || 'Server error'}`);
}
} catch (err) {
showLoader(false);
alert(`Connection error: ${err.message}`);
}
}
function viewWhatsAppChatGuestDetails() {
if (state.activeWhatsAppGuestId) {
closeAllModals();
viewOccupiedBookingDetails(state.activeWhatsAppGuestId);
}
}
function openEmbeddedSignupWizard() {
gotoSignupStep(1);
openModal('modal-embedded-signup');
}
function gotoSignupStep(step) {
document.querySelectorAll('.signup-step').forEach(div => div.style.display = 'none');
document.getElementById(`signup-step-${step}`).style.display = 'block';
}
async function completeEmbeddedSignup() {
const phone = document.getElementById('signup-phone').value;
showLoader(true, 'Registering Meta credentials...');
setTimeout(async () => {
try {
const payload = {
whatsapp_provider: 'WhatsAppCloudAPI',
whatsapp_app_id: '586236783741846',
whatsapp_cloud_token: 'EAAGb3fHZA1...verifiedPermanentToken',
whatsapp_cloud_phone_id: '476519812212157',
whatsapp_cloud_waba_id: '507009069153384',
whatsapp_default_phone: phone,
whatsapp_webhook_verify_token: 'shaima_hotel_secret'
};
const res = await fetch('/api/settings/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
settings: payload,
userId: state.currentUser.id
})
});
showLoader(false);
if (res.ok) {
closeAllModals();
alert('Meta Embedded Onboarding complete! Gateway setup automatically updated.');
loadSettingsView();
} else {
alert('Server onboarding register error.');
}
} catch (e) {
showLoader(false);
alert('Onboarding connection error.');
}
}, 1200);
}
function openAddPhoneNumberModal() {
document.getElementById('phone-number-modal-title').textContent = 'Add Official Phone Number';
document.getElementById('phone-number-edit-id').value = '';
document.getElementById('phone-number-val').value = '';
document.getElementById('phone-number-name').value = '';
document.getElementById('phone-number-id-val').value = '';
document.getElementById('phone-number-status').value = 'CONNECTED';
document.getElementById('phone-number-quality').value = 'Green - High quality';
openModal('modal-phone-number');
}
async function savePhoneNumberSetting(e) {
if (e) e.preventDefault();
const phone = document.getElementById('phone-number-val').value.trim();
const name = document.getElementById('phone-number-name').value.trim();
const phoneId = document.getElementById('phone-number-id-val').value.trim();
const status = document.getElementById('phone-number-status').value;
const quality = document.getElementById('phone-number-quality').value;
const currentNumbers = JSON.parse(state.settings.whatsapp_phone_numbers || '[]');
currentNumbers.push({ phone, name, phoneId, status, quality });
showLoader(true, 'Saving phone number...');
try {
const res = await fetch('/api/settings/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
settings: { whatsapp_phone_numbers: JSON.stringify(currentNumbers) },
userId: state.currentUser.id
})
});
showLoader(false);
if (res.ok) {
closeAllModals();
state.settings.whatsapp_phone_numbers = JSON.stringify(currentNumbers);
renderPhoneNumbersTable();
alert('Phone number registered successfully.');
}
} catch (err) {
showLoader(false);
alert('Failed to save phone number settings.');
}
}
function renderPhoneNumbersTable() {
const container = document.getElementById('settings-phone-numbers-tbody');
if (!container) return;
container.innerHTML = '';
const numbers = JSON.parse(state.settings.whatsapp_phone_numbers || '[]');
if (numbers.length === 0) {
container.innerHTML = '| No custom phone numbers added yet. |
';
return;
}
numbers.forEach(n => {
const isGreen = n.quality.includes('Green');
const isYellow = n.quality.includes('Yellow');
container.innerHTML += `
${n.phoneId} |
${n.phone}
${n.name}
|
${n.status} |
${isGreen ? '🟢' : isYellow ? '🟡' : '🔴'} ${n.quality} |
`;
});
// Dynamically populate default selection dropdown in Settings
const defaultSelect = document.getElementById('set-meta-default-phone');
if (defaultSelect) {
const currentVal = defaultSelect.value;
defaultSelect.innerHTML = '';
numbers.forEach(n => {
defaultSelect.innerHTML += ``;
});
if (currentVal) {
defaultSelect.value = currentVal;
}
}
}
function toggleTemplateEditorFields() {
const headerType = document.getElementById('tpl-header-type').value;
const btnType = document.getElementById('tpl-btn-type').value;
const grpText = document.getElementById('grp-header-text');
const grpMedia = document.getElementById('grp-header-media');
if (headerType === 'Text') {
if (grpText) grpText.style.display = 'block';
if (grpMedia) grpMedia.style.display = 'none';
} else if (headerType === 'Image' || headerType === 'Document') {
if (grpText) grpText.style.display = 'none';
if (grpMedia) grpMedia.style.display = 'block';
} else {
if (grpText) grpText.style.display = 'none';
if (grpMedia) grpMedia.style.display = 'none';
}
const grpBtnText = document.getElementById('grp-btn-text');
const grpBtnUrl = document.getElementById('grp-btn-url');
if (btnType === 'None') {
if (grpBtnText) grpBtnText.style.display = 'none';
if (grpBtnUrl) grpBtnUrl.style.display = 'none';
} else if (btnType === 'Quick Reply') {
if (grpBtnText) grpBtnText.style.display = 'block';
if (grpBtnUrl) grpBtnUrl.style.display = 'none';
} else {
if (grpBtnText) grpBtnText.style.display = 'block';
if (grpBtnUrl) grpBtnUrl.style.display = 'block';
}
}
function regenerateAPIToken() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let token = 'shaima_apikey_live_';
for (let i = 0; i < 20; i++) {
token += chars.charAt(Math.floor(Math.random() * chars.length));
}
document.getElementById('set-api-token-val').value = token;
alert('API Key regenerated successfully! Click "Save Integration Gateways" to save updates.');
}
window.toggleTemplateEditorFields = toggleTemplateEditorFields;
window.saveSocialSettings = saveSocialSettings;
window.handleSocialPublish = handleSocialPublish;
window.updateSocialPreview = updateSocialPreview;
window.shareReportViaEmail = shareReportViaEmail;
window.shareInvoiceViaEmail = shareInvoiceViaEmail;
window.saveSMTPSettings = saveSMTPSettings;
window.testSMTPConnection = testSMTPConnection;
window.loadWhatsAppChatView = loadWhatsAppChatView;
window.filterWhatsAppChats = filterWhatsAppChats;
window.selectWhatsAppChat = selectWhatsAppChat;
window.assignWhatsAppChat = assignWhatsAppChat;
window.toggleWhatsAppChatBot = toggleWhatsAppChatBot;
window.triggerWhatsAppChatAttachment = triggerWhatsAppChatAttachment;
window.sendWhatsAppChatMessage = sendWhatsAppChatMessage;
window.viewWhatsAppChatGuestDetails = viewWhatsAppChatGuestDetails;
window.openEmbeddedSignupWizard = openEmbeddedSignupWizard;
window.gotoSignupStep = gotoSignupStep;
window.completeEmbeddedSignup = completeEmbeddedSignup;
window.openAddPhoneNumberModal = openAddPhoneNumberModal;
window.savePhoneNumberSetting = savePhoneNumberSetting;
window.renderPhoneNumbersTable = renderPhoneNumbersTable;
window.regenerateAPIToken = regenerateAPIToken;