}
// ================= WHATSAPP CHAT INBOX CLIENT CLIENT SYSTEM ================= //
let activeChatGuestId = null;
let waChatFilterTab = 'all'; // all, mine, unassigned
// 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 public URL link of invoice receipt / document to attach:');
if (url) {
document.getElementById('wa-chat-text-input').value = url;
}
}
async function sendWhatsAppChatMessage(e) {
if (e) e.preventDefault();
if (!activeChatGuestId) return;
const textInput = document.getElementById('wa-chat-text-input');
const messageText = textInput.value.trim();
if (!messageText) return;
const guest = state.guestsList.find(g => g.id === activeChatGuestId);
if (!guest) return;
const timestamp = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
// 1. Append message to UI locally
state.whatsappChatHistories[activeChatGuestId].push({
text: messageText,
sender: 'staff',
time: timestamp
});
renderWhatsAppChatBubbles();
textInput.value = '';
// 2. Mock REST API hit to WhatsApp Cloud Gateway
try {
await fetch('/api/settings/smtp-test', { // Mocking network API connectivity test
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ simulateWhatsAppDispatch: true, recipient: guest.mobile, body: messageText })
});
} catch (err) {}
// 3. AI FAQ bot check
if (guest.reply_bot_enabled === 1) {
setTimeout(() => {
let botResponse = 'Thank you for reaching out! Our reception desk team has been notified of your message. (Concierge AI Bot)';
const cleanMsg = messageText.toLowerCase();
if (cleanMsg.includes('wifi') || cleanMsg.includes('internet')) {
botResponse = 'The Shaima Hotel WiFi SSID is "SHAIMA_GUEST" and the password is: Shaima@2026. (Concierge AI Bot)';
} else if (cleanMsg.includes('checkout') || cleanMsg.includes('check out') || cleanMsg.includes('settle')) {
botResponse = 'Standard checkout time is 11:00 AM. If you would like to settle bills early, please click "Billing" in your app to review outstanding room services. (Concierge AI Bot)';
} else if (cleanMsg.includes('food') || cleanMsg.includes('breakfast') || cleanMsg.includes('menu')) {
botResponse = 'Breakfast is served daily from 7:30 AM to 10:30 AM at the Sea View Restaurant. Room service menu can be viewed on your desk QR code. (Concierge AI Bot)';
}
state.whatsappChatHistories[activeChatGuestId].push({
text: botResponse,
sender: 'bot',
time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
});
if (activeChatGuestId === guest.id) {
renderWhatsAppChatBubbles();
}
renderWhatsAppChatRoster();
}, 1200);
}
}
function viewWhatsAppChatGuestDetails() {
if (!activeChatGuestId) return;
// Switch to Guests CRM view and search for guest
navigateTo('guests');
const searchInput = document.getElementById('search-guest');
if (searchInput) {
const guest = state.guestsList.find(g => g.id === activeChatGuestId);
if (guest) {
searchInput.value = guest.name;
loadGuestsList();
}
}
}
// ================= WHATSAPP EMBEDDED SIGNUP FLOW ================= //
function openEmbeddedSignupWizard() {
gotoSignupStep(1);
openModal('modal-embedded-signup');
}
function gotoSignupStep(step) {
document.querySelectorAll('.signup-step').forEach(el => el.style.display = 'none');
document.getElementById(`signup-step-${step}`).style.display = 'block';
}
async function completeEmbeddedSignup() {
showLoader(true, 'Creating Meta Graph API connection bindings...');
setTimeout(async () => {
showLoader(false);
closeAllModals();
// Populate official Meta keys automatically
document.getElementById('set-meta-app-id').value = '586236783741846';
document.getElementById('set-meta-phone-id').value = '476519812212157';
document.getElementById('set-meta-waba-id').value = '507009069153384';
document.getElementById('set-meta-token').value = 'EAAGb3OnboardedTokenLive72bb83df293a90bc1e92';
document.getElementById('set-meta-default-phone').value = '+91 4567 299 438';
const data = {
whatsapp_provider: 'WhatsAppCloudAPI',
whatsapp_app_id: '586236783741846',
whatsapp_cloud_token: 'EAAGb3OnboardedTokenLive72bb83df293a90bc1e92',
whatsapp_cloud_phone_id: '476519812212157',
whatsapp_cloud_waba_id: '507009069153384',
whatsapp_default_phone: '+91 4567 299 438',
whatsapp_webhook_verify_token: 'shaima_hotel_secret'
};
await saveSettingsAPI(data, 'Embedded onboarding completed! WhatsApp WABA linked and credentials updated.');
loadSettingsView();
}, 2000);
}
// ================= MULTIPLE PHONE NUMBERS GATEWAY MANAGEMENT ================= //
function openAddPhoneNumberModal() {
document.getElementById('phone-number-form').reset();
document.getElementById('phone-number-edit-id').value = '';
document.getElementById('phone-number-modal-title').textContent = 'Add Official Phone Number';
openModal('modal-phone-number');
}
async function savePhoneNumberSetting(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;
let currentNumbers = [];
try {
currentNumbers = JSON.parse(state.settings.whatsapp_phone_numbers || '[]');
} catch (err) {}
// Check if adding or editing
const existingIdx = currentNumbers.findIndex(x => x.phoneId === phoneId);
if (existingIdx !== -1) {
currentNumbers[existingIdx] = { phone, name, phoneId, status, quality };
} else {
currentNumbers.push({ phone, name, phoneId, status, quality });
}
showLoader(true, 'Saving phone numbers metadata...');
const payload = {
whatsapp_phone_numbers: JSON.stringify(currentNumbers)
};
await saveSettingsAPI(payload, 'Phone number settings successfully synchronized.');
closeAllModals();
loadSettingsView();
}
function renderPhoneNumbersTable() {
const container = document.getElementById('settings-phone-numbers-tbody');
if (!container) return;
let numbers = [];
try {
numbers = JSON.parse(state.settings.whatsapp_phone_numbers || '[]');
} catch (err) {}
// Defaults if empty
if (numbers.length === 0) {
numbers = [
{ phoneId: '476519812212157', phone: '+91 4567 299 438', name: 'AL QALAM INTERNATIONAL SCHOOL', status: 'CONNECTED', quality: 'Green - High quality' },
{ phoneId: '1038304142688601', phone: '+1 555-728-0981', name: 'AL QALAM INTERNATIONAL SCHOOL', status: 'CONNECTED', quality: 'Green - High quality' }
];
}
container.innerHTML = '';
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;