/**
* Live Chat Admin JavaScript
*
* Handles inbox functionality and conversation management.
*
* @package King_Addons
*/
(function($) {
'use strict';
/**
* Live Chat Admin Module
*/
const KingLiveChatAdmin = {
/**
* Current state
*/
state: {
currentFilter: 'all',
currentSearch: '',
currentConversation: null,
conversations: [],
refreshInterval: null,
lastMessageId: 0
},
/**
* DOM elements cache
*/
elements: {},
/**
* Initialize the module
*/
init: function() {
this.cacheElements();
this.bindEvents();
this.loadConversations();
this.initColorPickers();
this.startAutoRefresh();
this.checkUrlParams();
},
/**
* Cache DOM elements
*/
cacheElements: function() {
this.elements = {
conversationsList: $('#ka-inbox-conversations'),
conversationView: $('#ka-conversation-view'),
filters: $('.ka-inbox-filter'),
searchInput: $('.ka-inbox-search')
};
},
/**
* Bind event handlers
*/
bindEvents: function() {
const self = this;
// Filter clicks
this.elements.filters.on('click', function() {
self.elements.filters.removeClass('active');
$(this).addClass('active');
self.state.currentFilter = $(this).data('status');
self.loadConversations();
});
// Search input
let searchTimeout;
this.elements.searchInput.on('input', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(function() {
self.state.currentSearch = self.elements.searchInput.val();
self.loadConversations();
}, 300);
});
// Conversation item click (delegated)
this.elements.conversationsList.on('click', '.ka-inbox-item', function() {
const id = $(this).data('id');
self.loadConversation(id);
});
// Reply form submit (delegated)
$(document).on('submit', '.ka-reply-form', function(e) {
e.preventDefault();
self.sendReply();
});
// Status toggle (delegated)
$(document).on('click', '.ka-toggle-status', function() {
const newStatus = $(this).data('status');
self.updateStatus(newStatus);
});
// Delete conversation (delegated)
$(document).on('click', '.ka-delete-conversation', function() {
if (confirm(kingLiveChatAdmin.strings.confirmDelete)) {
self.deleteConversation();
}
});
// Textarea enter to send
$(document).on('keydown', '.ka-reply-textarea', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
self.sendReply();
}
});
},
/**
* Check URL parameters for direct conversation link
*/
checkUrlParams: function() {
const urlParams = new URLSearchParams(window.location.search);
const conversationId = urlParams.get('conversation');
if (conversationId) {
this.loadConversation(parseInt(conversationId, 10));
}
},
/**
* Initialize color pickers
*/
initColorPickers: function() {
if ($.fn.wpColorPicker) {
$('.ka-color-picker').wpColorPicker();
}
},
/**
* Start auto-refresh for inbox
*/
startAutoRefresh: function() {
const self = this;
this.state.refreshInterval = setInterval(function() {
self.loadConversations(true);
if (self.state.currentConversation) {
self.pollNewMessages();
}
}, 10000);
},
/**
* Load conversations list
*
* @param {boolean} silent Don't show loading state
*/
loadConversations: function(silent) {
const self = this;
if (!silent) {
this.elements.conversationsList.html('
');
}
$.ajax({
url: kingLiveChatAdmin.ajaxUrl,
type: 'POST',
data: {
action: 'king_live_chat_get_conversations',
nonce: kingLiveChatAdmin.nonce,
status: this.state.currentFilter,
search: this.state.currentSearch
},
success: function(response) {
if (response.success) {
self.state.conversations = response.data.conversations;
self.renderConversationsList();
}
},
error: function() {
self.elements.conversationsList.html(
'' + kingLiveChatAdmin.strings.error + '
'
);
}
});
},
/**
* Render conversations list
*/
renderConversationsList: function() {
const self = this;
const conversations = this.state.conversations;
if (!conversations.length) {
this.elements.conversationsList.html(
'No conversations found
'
);
return;
}
let html = '';
conversations.forEach(function(conv) {
const initials = self.getInitials(conv.name);
const isActive = self.state.currentConversation === conv.id;
const isUnread = conv.unread > 0;
const timeAgo = self.formatTimeAgo(conv.last_message);
html += `
${initials}
${self.escapeHtml(conv.email || 'No email')}
`;
});
this.elements.conversationsList.html(html);
},
/**
* Load single conversation
*
* @param {number} id Conversation ID
*/
loadConversation: function(id) {
const self = this;
this.state.currentConversation = id;
// Update active state in list
this.elements.conversationsList.find('.ka-inbox-item').removeClass('active');
this.elements.conversationsList.find('[data-id="' + id + '"]').addClass('active').removeClass('unread');
this.elements.conversationView.html('');
$.ajax({
url: kingLiveChatAdmin.ajaxUrl,
type: 'POST',
data: {
action: 'king_live_chat_get_conversation',
nonce: kingLiveChatAdmin.nonce,
conversation_id: id
},
success: function(response) {
if (response.success) {
self.renderConversation(response.data);
// Update last message ID for polling
const messages = response.data.messages;
if (messages.length) {
self.state.lastMessageId = messages[messages.length - 1].id;
}
} else {
self.elements.conversationView.html(
'' + kingLiveChatAdmin.strings.error + '
'
);
}
}
});
},
/**
* Render conversation view
*
* @param {Object} data Conversation data
*/
renderConversation: function(data) {
const self = this;
const conv = data.conversation;
const messages = data.messages;
const initials = this.getInitials(conv.name || 'Anonymous');
const isOpen = conv.status === 'open';
let messagesHtml = '';
messages.forEach(function(msg) {
const isAdmin = msg.type === 'admin';
const time = self.formatTime(msg.time);
messagesHtml += `
${self.escapeHtml(msg.text)}
${isAdmin && msg.admin_name ? msg.admin_name + ' · ' : ''}${time}
`;
});
if (!messages.length) {
messagesHtml = '' + kingLiveChatAdmin.strings.noMessages + '
';
}
const html = `
${messagesHtml}
Visitor Info
Status:
${conv.status}
${conv.page_url ? `
` : ''}
${conv.referrer ? `
Referrer:
${self.truncateUrl(conv.referrer)}
` : ''}
Started:
${self.formatTime(conv.created)}
`;
this.elements.conversationView.html(html);
// Scroll to bottom
const messagesEl = this.elements.conversationView.find('.ka-conversation-messages');
messagesEl.scrollTop(messagesEl[0].scrollHeight);
},
/**
* Send admin reply
*/
sendReply: function() {
const self = this;
const textarea = this.elements.conversationView.find('.ka-reply-textarea');
const sendBtn = this.elements.conversationView.find('.ka-reply-send');
const message = textarea.val().trim();
if (!message || !this.state.currentConversation) {
return;
}
sendBtn.prop('disabled', true).text(kingLiveChatAdmin.strings.sending);
$.ajax({
url: kingLiveChatAdmin.ajaxUrl,
type: 'POST',
data: {
action: 'king_live_chat_send_reply',
nonce: kingLiveChatAdmin.nonce,
conversation_id: this.state.currentConversation,
message: message
},
success: function(response) {
if (response.success) {
// Add message to view
const msg = response.data.message;
const messagesEl = self.elements.conversationView.find('.ka-conversation-messages');
const msgHtml = `
${self.escapeHtml(msg.text)}
${msg.admin_name ? msg.admin_name + ' · ' : ''}${self.formatTime(msg.time)}
`;
messagesEl.find('.ka-inbox-empty').remove();
messagesEl.append(msgHtml);
messagesEl.scrollTop(messagesEl[0].scrollHeight);
textarea.val('');
self.state.lastMessageId = msg.id;
} else {
alert(kingLiveChatAdmin.strings.error);
}
},
error: function() {
alert(kingLiveChatAdmin.strings.error);
},
complete: function() {
sendBtn.prop('disabled', false).text(kingLiveChatAdmin.strings.send);
}
});
},
/**
* Poll for new messages in current conversation
*/
pollNewMessages: function() {
const self = this;
if (!this.state.currentConversation) {
return;
}
$.ajax({
url: kingLiveChatAdmin.ajaxUrl,
type: 'POST',
data: {
action: 'king_live_chat_get_conversation',
nonce: kingLiveChatAdmin.nonce,
conversation_id: this.state.currentConversation
},
success: function(response) {
if (response.success) {
const messages = response.data.messages;
if (messages.length) {
const lastMsg = messages[messages.length - 1];
if (lastMsg.id > self.state.lastMessageId) {
// New messages, reload view
self.renderConversation(response.data);
self.state.lastMessageId = lastMsg.id;
}
}
}
}
});
},
/**
* Update conversation status
*
* @param {string} status New status
*/
updateStatus: function(status) {
const self = this;
$.ajax({
url: kingLiveChatAdmin.ajaxUrl,
type: 'POST',
data: {
action: 'king_live_chat_update_status',
nonce: kingLiveChatAdmin.nonce,
conversation_id: this.state.currentConversation,
status: status
},
success: function(response) {
if (response.success) {
self.loadConversation(self.state.currentConversation);
self.loadConversations(true);
}
}
});
},
/**
* Delete conversation
*/
deleteConversation: function() {
const self = this;
$.ajax({
url: kingLiveChatAdmin.ajaxUrl,
type: 'POST',
data: {
action: 'king_live_chat_delete_conversation',
nonce: kingLiveChatAdmin.nonce,
conversation_id: this.state.currentConversation
},
success: function(response) {
if (response.success) {
self.state.currentConversation = null;
self.elements.conversationView.html(`
Select a conversation to view messages
`);
self.loadConversations();
}
}
});
},
/**
* Get initials from name
*
* @param {string} name Full name
* @returns {string}
*/
getInitials: function(name) {
if (!name) return '?';
const parts = name.trim().split(' ');
if (parts.length >= 2) {
return (parts[0][0] + parts[1][0]).toUpperCase();
}
return name.substring(0, 2).toUpperCase();
},
/**
* Format time for display
*
* @param {string} dateStr Date string
* @returns {string}
*/
formatTime: function(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr.replace(' ', 'T'));
return date.toLocaleString();
},
/**
* Format time ago
*
* @param {string} dateStr Date string
* @returns {string}
*/
formatTimeAgo: function(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr.replace(' ', 'T'));
const now = new Date();
const diff = Math.floor((now - date) / 1000);
if (diff < 60) return 'Just now';
if (diff < 3600) return Math.floor(diff / 60) + 'm';
if (diff < 86400) return Math.floor(diff / 3600) + 'h';
if (diff < 604800) return Math.floor(diff / 86400) + 'd';
return date.toLocaleDateString();
},
/**
* Truncate URL for display
*
* @param {string} url Full URL
* @returns {string}
*/
truncateUrl: function(url) {
if (!url) return '';
try {
const parsed = new URL(url);
let path = parsed.pathname;
if (path.length > 30) {
path = path.substring(0, 30) + '...';
}
return parsed.host + path;
} catch (e) {
return url.substring(0, 40) + (url.length > 40 ? '...' : '');
}
},
/**
* Escape HTML entities
*
* @param {string} str Input string
* @returns {string}
*/
escapeHtml: function(str) {
if (!str) return '';
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
};
// Initialize on DOM ready
$(function() {
if ($('#ka-inbox-conversations').length) {
KingLiveChatAdmin.init();
} else {
// Settings page - just init color pickers
if ($.fn.wpColorPicker) {
$('.ka-color-picker').wpColorPicker();
}
}
});
})(jQuery);