// 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 = 'Unassigned'; staffList.forEach(s => { assigneeSelect.innerHTML += `${s.name} (${s.role})`; }); // 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 += `
${initial}
${chat.name} ${timeStr}
${lastMsg.body} ${unreadCount > 0 ? `${unreadCount}` : ''}
`; }); } 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 += `
${bodyHtml}
${timeStr}
`; }); // 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 += `${n.phone} (${n.name})`; }); 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;