Fix browser phone call control buttons not appearing

## Frontend Issues Fixed
- Call control panel visibility during connected calls
- Button event handlers properly bound
- JavaScript localization includes user_id for personal queues

## Backend Browser Phone Enhanced
- Added complete call control panel with Hold, Transfer, Requeue, Record buttons
- Integrated admin-specific JavaScript functions for all call controls
- Added agent selection dialogs with status indicators
- Call control panel shows/hides based on call state
- Button state management (hold/unhold, record/stop recording)
- WordPress admin styling with dashicons

## Technical Improvements
- Separate admin button IDs to avoid conflicts with frontend
- Enhanced call state management in setupCallHandlers()
- Admin notification system for user feedback
- Proper cleanup when calls end

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-30 15:35:08 -07:00
parent dc3c12e006
commit 060b95c710

View File

@@ -5598,13 +5598,22 @@ class TWP_Admin {
</button>
</div>
<div class="phone-controls-extra" style="display: none;">
<button id="mute-btn" class="button">
<span class="dashicons dashicons-microphone"></span> Mute
</button>
<button id="hold-btn" class="button">
<span class="dashicons dashicons-controls-pause"></span> Hold
</button>
<!-- Call Control Panel (shown during active calls) -->
<div class="phone-controls-extra" id="admin-call-controls-panel" style="display: none;">
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin: 15px 0;">
<button id="admin-hold-btn" class="button" title="Put call on hold">
<span class="dashicons dashicons-controls-pause"></span> Hold
</button>
<button id="admin-transfer-btn" class="button" title="Transfer to another agent">
<span class="dashicons dashicons-share-alt"></span> Transfer
</button>
<button id="admin-requeue-btn" class="button" title="Put call back in queue">
<span class="dashicons dashicons-backup"></span> Requeue
</button>
<button id="admin-record-btn" class="button" title="Start/stop recording">
<span class="dashicons dashicons-controls-volumeon"></span> Record
</button>
</div>
</div>
</div>
</div>
@@ -6064,6 +6073,7 @@ class TWP_Admin {
$('#answer-btn').hide();
$('#hangup-btn').show();
$('#phone-controls-extra').show();
$('#admin-call-controls-panel').show();
startCallTimer();
});
@@ -6075,8 +6085,13 @@ class TWP_Admin {
$('#answer-btn').hide();
$('#call-btn').show();
$('#phone-controls-extra').hide();
$('#admin-call-controls-panel').hide();
$('#call-timer').hide();
stopCallTimer();
// Reset button states
$('#admin-hold-btn').text('Hold').removeClass('btn-active');
$('#admin-record-btn').text('Record').removeClass('btn-active');
});
// Call rejected
@@ -6287,6 +6302,31 @@ class TWP_Admin {
}
});
// Admin call control buttons
$('#admin-hold-btn').on('click', function() {
if (currentCall) {
adminToggleHold();
}
});
$('#admin-transfer-btn').on('click', function() {
if (currentCall) {
adminShowTransferDialog();
}
});
$('#admin-requeue-btn').on('click', function() {
if (currentCall) {
adminShowRequeueDialog();
}
});
$('#admin-record-btn').on('click', function() {
if (currentCall) {
adminToggleRecording();
}
});
// Check if SDK loaded and initialize
$(window).on('load', function() {
setTimeout(function() {
@@ -6431,6 +6471,345 @@ class TWP_Admin {
button.prop('disabled', false).text('Save Changes');
});
});
// Admin call control functions
var adminIsOnHold = false;
var adminIsRecording = false;
var adminRecordingSid = null;
function adminToggleHold() {
if (!currentCall) return;
var callSid = currentCall.parameters.CallSid || currentCall.customParameters.CallSid;
var $holdBtn = $('#admin-hold-btn');
$.post(ajaxurl, {
action: 'twp_toggle_hold',
call_sid: callSid,
hold: !adminIsOnHold,
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success) {
adminIsOnHold = !adminIsOnHold;
if (adminIsOnHold) {
$holdBtn.html('<span class="dashicons dashicons-controls-play"></span> Unhold').addClass('btn-active');
showNotice('Call placed on hold', 'info');
} else {
$holdBtn.html('<span class="dashicons dashicons-controls-pause"></span> Hold').removeClass('btn-active');
showNotice('Call resumed', 'info');
}
} else {
showNotice('Failed to toggle hold: ' + (response.data || 'Unknown error'), 'error');
}
}).fail(function() {
showNotice('Failed to toggle hold', 'error');
});
}
function adminShowTransferDialog() {
if (!currentCall) return;
// Load available agents
$.post(ajaxurl, {
action: 'twp_get_online_agents',
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success && response.data.length > 0) {
adminShowAgentTransferDialog(response.data);
} else {
adminShowManualTransferDialog();
}
}).fail(function() {
adminShowManualTransferDialog();
});
}
function adminShowAgentTransferDialog(agents) {
var agentOptions = '<div class="agent-list" style="max-height: 200px; overflow-y: auto; border: 1px solid #ccc; margin: 10px 0;">';
agents.forEach(function(agent) {
var statusClass = agent.is_available ? 'available' : 'busy';
var statusText = agent.is_available ? '🟢 Available' : '🔴 Busy';
var methodIcon = agent.has_phone ? '📱' : '💻';
agentOptions += '<div class="agent-option" style="padding: 10px; border-bottom: 1px solid #eee; cursor: pointer; display: flex; justify-content: space-between;" data-agent-id="' + agent.id + '" data-transfer-method="' + agent.transfer_method + '" data-transfer-value="' + agent.transfer_value + '">';
agentOptions += '<div><strong>' + agent.name + '</strong> ' + methodIcon + '</div>';
agentOptions += '<div>' + statusText + '</div>';
agentOptions += '</div>';
});
agentOptions += '</div>';
var dialogHtml = '<div id="admin-transfer-dialog" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border: 1px solid #ccc; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 10000; width: 400px;">';
dialogHtml += '<h3>Transfer Call to Agent</h3>';
dialogHtml += '<p>Select an agent to transfer this call to:</p>';
dialogHtml += agentOptions;
dialogHtml += '<p>Or enter phone number manually:</p>';
dialogHtml += '<input type="tel" id="admin-transfer-manual" placeholder="+1234567890" style="width: 100%; margin: 10px 0; padding: 8px;" />';
dialogHtml += '<div style="text-align: right; margin-top: 15px;">';
dialogHtml += '<button id="admin-confirm-transfer" class="button button-primary" style="margin-right: 10px;" disabled>Transfer</button>';
dialogHtml += '<button id="admin-cancel-transfer" class="button">Cancel</button>';
dialogHtml += '</div>';
dialogHtml += '</div>';
dialogHtml += '<div id="admin-transfer-overlay" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9999;"></div>';
$('body').append(dialogHtml);
var selectedAgent = null;
$('.agent-option').on('click', function() {
$('.agent-option').css('background', '');
$(this).css('background', '#e7f3ff');
selectedAgent = {
id: $(this).data('agent-id'),
method: $(this).data('transfer-method'),
value: $(this).data('transfer-value')
};
$('#admin-transfer-manual').val('');
$('#admin-confirm-transfer').prop('disabled', false);
});
$('#admin-transfer-manual').on('input', function() {
var number = $(this).val().trim();
if (number) {
$('.agent-option').css('background', '');
selectedAgent = null;
$('#admin-confirm-transfer').prop('disabled', false);
} else {
$('#admin-confirm-transfer').prop('disabled', !selectedAgent);
}
});
$('#admin-confirm-transfer').on('click', function() {
var manualNumber = $('#admin-transfer-manual').val().trim();
if (manualNumber) {
adminTransferCall(manualNumber);
} else if (selectedAgent) {
adminTransferToAgent(selectedAgent);
}
});
$('#admin-cancel-transfer, #admin-transfer-overlay').on('click', function() {
adminHideTransferDialog();
});
}
function adminShowManualTransferDialog() {
var dialogHtml = '<div id="admin-transfer-dialog" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border: 1px solid #ccc; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 10000;">';
dialogHtml += '<h3>Transfer Call</h3>';
dialogHtml += '<p>Enter the phone number to transfer this call:</p>';
dialogHtml += '<input type="tel" id="admin-transfer-number" placeholder="+1234567890" style="width: 100%; margin: 10px 0; padding: 8px;" />';
dialogHtml += '<div style="text-align: right; margin-top: 15px;">';
dialogHtml += '<button id="admin-confirm-transfer" class="button button-primary" style="margin-right: 10px;">Transfer</button>';
dialogHtml += '<button id="admin-cancel-transfer" class="button">Cancel</button>';
dialogHtml += '</div>';
dialogHtml += '</div>';
dialogHtml += '<div id="admin-transfer-overlay" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9999;"></div>';
$('body').append(dialogHtml);
$('#admin-confirm-transfer').on('click', function() {
var number = $('#admin-transfer-number').val().trim();
if (number) {
adminTransferCall(number);
}
});
$('#admin-cancel-transfer, #admin-transfer-overlay').on('click', function() {
adminHideTransferDialog();
});
}
function adminTransferCall(phoneNumber) {
if (!currentCall) return;
var callSid = currentCall.parameters.CallSid || currentCall.customParameters.CallSid;
$.post(ajaxurl, {
action: 'twp_transfer_call',
call_sid: callSid,
agent_number: phoneNumber,
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success) {
showNotice('Call transferred successfully', 'success');
adminHideTransferDialog();
if (currentCall) {
currentCall.disconnect();
}
} else {
showNotice('Failed to transfer call: ' + (response.data || 'Unknown error'), 'error');
}
}).fail(function() {
showNotice('Failed to transfer call', 'error');
});
}
function adminTransferToAgent(agent) {
if (!currentCall) return;
var callSid = currentCall.parameters.CallSid || currentCall.customParameters.CallSid;
$.post(ajaxurl, {
action: 'twp_transfer_to_agent_queue',
call_sid: callSid,
agent_id: agent.id,
transfer_method: agent.method,
transfer_value: agent.value,
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success) {
showNotice('Call transferred successfully', 'success');
adminHideTransferDialog();
if (currentCall) {
currentCall.disconnect();
}
} else {
showNotice('Failed to transfer call: ' + (response.data || 'Unknown error'), 'error');
}
}).fail(function() {
showNotice('Failed to transfer call', 'error');
});
}
function adminHideTransferDialog() {
$('#admin-transfer-dialog, #admin-transfer-overlay').remove();
}
function adminShowRequeueDialog() {
if (!currentCall) return;
$.post(ajaxurl, {
action: 'twp_get_all_queues',
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success && response.data.length > 0) {
var options = '';
response.data.forEach(function(queue) {
options += '<option value="' + queue.id + '">' + queue.queue_name + '</option>';
});
var dialogHtml = '<div id="admin-requeue-dialog" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border: 1px solid #ccc; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 10000;">';
dialogHtml += '<h3>Requeue Call</h3>';
dialogHtml += '<p>Select a queue to transfer this call to:</p>';
dialogHtml += '<select id="admin-requeue-select" style="width: 100%; margin: 10px 0; padding: 8px;">' + options + '</select>';
dialogHtml += '<div style="text-align: right; margin-top: 15px;">';
dialogHtml += '<button id="admin-confirm-requeue" class="button button-primary" style="margin-right: 10px;">Requeue</button>';
dialogHtml += '<button id="admin-cancel-requeue" class="button">Cancel</button>';
dialogHtml += '</div>';
dialogHtml += '</div>';
dialogHtml += '<div id="admin-requeue-overlay" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9999;"></div>';
$('body').append(dialogHtml);
$('#admin-confirm-requeue').on('click', function() {
var queueId = $('#admin-requeue-select').val();
if (queueId) {
adminRequeueCall(queueId);
}
});
$('#admin-cancel-requeue, #admin-requeue-overlay').on('click', function() {
$('#admin-requeue-dialog, #admin-requeue-overlay').remove();
});
} else {
showNotice('No queues available', 'error');
}
}).fail(function() {
showNotice('Failed to load queues', 'error');
});
}
function adminRequeueCall(queueId) {
if (!currentCall) return;
var callSid = currentCall.parameters.CallSid || currentCall.customParameters.CallSid;
$.post(ajaxurl, {
action: 'twp_requeue_call',
call_sid: callSid,
queue_id: queueId,
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success) {
showNotice('Call requeued successfully', 'success');
$('#admin-requeue-dialog, #admin-requeue-overlay').remove();
if (currentCall) {
currentCall.disconnect();
}
} else {
showNotice('Failed to requeue call: ' + (response.data || 'Unknown error'), 'error');
}
}).fail(function() {
showNotice('Failed to requeue call', 'error');
});
}
function adminToggleRecording() {
if (!currentCall) return;
if (adminIsRecording) {
adminStopRecording();
} else {
adminStartRecording();
}
}
function adminStartRecording() {
var callSid = currentCall.parameters.CallSid || currentCall.customParameters.CallSid;
var $recordBtn = $('#admin-record-btn');
$.post(ajaxurl, {
action: 'twp_start_recording',
call_sid: callSid,
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success) {
adminIsRecording = true;
adminRecordingSid = response.data.recording_sid;
$recordBtn.html('<span class="dashicons dashicons-controls-volumeoff"></span> Stop Recording').addClass('btn-active');
showNotice('Recording started', 'success');
} else {
showNotice('Failed to start recording: ' + (response.data || 'Unknown error'), 'error');
}
}).fail(function() {
showNotice('Failed to start recording', 'error');
});
}
function adminStopRecording() {
if (!adminRecordingSid) return;
var callSid = currentCall ? (currentCall.parameters.CallSid || currentCall.customParameters.CallSid) : '';
var $recordBtn = $('#admin-record-btn');
$.post(ajaxurl, {
action: 'twp_stop_recording',
call_sid: callSid,
recording_sid: adminRecordingSid,
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success) {
adminIsRecording = false;
adminRecordingSid = null;
$recordBtn.html('<span class="dashicons dashicons-controls-volumeon"></span> Record').removeClass('btn-active');
showNotice('Recording stopped', 'info');
} else {
showNotice('Failed to stop recording: ' + (response.data || 'Unknown error'), 'error');
}
}).fail(function() {
showNotice('Failed to stop recording', 'error');
});
}
function showNotice(message, type) {
var noticeClass = type === 'error' ? 'notice-error' : (type === 'success' ? 'notice-success' : 'notice-info');
var notice = $('<div class="notice ' + noticeClass + ' is-dismissible" style="margin: 10px 0;"><p>' + message + '</p></div>');
$('.browser-phone-container').prepend(notice);
setTimeout(function() {
notice.fadeOut();
}, 4000);
}
});
</script>
</div>