From f26cb24dfd3f4be9cd5d7427c41f61bc488b2f62 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Fri, 15 Aug 2025 09:29:35 -0700 Subject: [PATCH] Add comprehensive voicemail integration to browser phone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add AJAX endpoint for fetching user voicemails with stats and recent list - Create voicemail display section with counts, transcriptions, and playback - Implement click-to-play functionality for voicemail audio - Add refresh and view-all buttons with admin page integration - Style voicemail section with consistent design and dark mode support - Include visual indicators for recordings and formatted durations - Add mobile responsive design and hover effects 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- admin/class-twp-admin.php | 50 ++++++++ assets/css/browser-phone-frontend.css | 173 +++++++++++++++++++++++++- assets/js/browser-phone-frontend.js | 131 +++++++++++++++++++ includes/class-twp-core.php | 1 + includes/class-twp-shortcodes.php | 29 +++++ 5 files changed, 380 insertions(+), 4 deletions(-) diff --git a/admin/class-twp-admin.php b/admin/class-twp-admin.php index 9833a2e..056d727 100644 --- a/admin/class-twp-admin.php +++ b/admin/class-twp-admin.php @@ -3770,6 +3770,56 @@ class TWP_Admin { } } + /** + * AJAX handler for getting user's recent voicemails + */ + public function ajax_get_user_voicemails() { + check_ajax_referer('twp_ajax_nonce', 'nonce'); + + if (!current_user_can('manage_options') && !current_user_can('twp_access_voicemails')) { + wp_send_json_error('Unauthorized'); + return; + } + + global $wpdb; + $table_name = $wpdb->prefix . 'twp_voicemails'; + + // Get recent voicemails (last 10) + $voicemails = $wpdb->get_results($wpdb->prepare(" + SELECT id, from_number, duration, transcription, created_at, recording_url + FROM $table_name + ORDER BY created_at DESC + LIMIT %d + ", 10)); + + // Format data for frontend + $formatted_voicemails = array(); + foreach ($voicemails as $vm) { + $formatted_voicemails[] = array( + 'id' => $vm->id, + 'from_number' => $vm->from_number, + 'duration' => $vm->duration, + 'transcription' => $vm->transcription ? substr($vm->transcription, 0, 100) . '...' : 'No transcription', + 'created_at' => $vm->created_at, + 'time_ago' => human_time_diff(strtotime($vm->created_at), current_time('timestamp')) . ' ago', + 'has_recording' => !empty($vm->recording_url) + ); + } + + // Get voicemail counts + $total_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name"); + $today_count = $wpdb->get_var($wpdb->prepare(" + SELECT COUNT(*) FROM $table_name + WHERE DATE(created_at) = %s + ", current_time('Y-m-d'))); + + wp_send_json_success(array( + 'voicemails' => $formatted_voicemails, + 'total_count' => $total_count, + 'today_count' => $today_count + )); + } + /** * AJAX handler for getting all groups */ diff --git a/assets/css/browser-phone-frontend.css b/assets/css/browser-phone-frontend.css index 474a3db..681eedd 100644 --- a/assets/css/browser-phone-frontend.css +++ b/assets/css/browser-phone-frontend.css @@ -463,6 +463,148 @@ opacity: 0.8; } +/* Voicemail Section */ +.twp-voicemail-section { + background: #fff; + padding: 16px; + border-radius: 8px; + border: 2px solid #e9ecef; + margin-bottom: 20px; +} + +.twp-voicemail-section h4 { + margin: 0 0 16px 0; + color: #212529; + font-size: 1.1rem; + text-align: center; +} + +.voicemail-stats { + display: flex; + justify-content: space-around; + margin-bottom: 16px; + padding: 12px; + background: #f8f9fa; + border-radius: 6px; +} + +.stat-item { + text-align: center; +} + +.stat-label { + display: block; + font-size: 0.9rem; + color: #6c757d; + margin-bottom: 4px; +} + +.stat-value { + display: block; + font-size: 1.2rem; + font-weight: 600; + color: #495057; +} + +.twp-voicemail-list { + margin-bottom: 16px; + max-height: 200px; + overflow-y: auto; +} + +.voicemail-item { + padding: 12px; + border: 1px solid #dee2e6; + border-radius: 6px; + margin-bottom: 8px; + cursor: pointer; + transition: all 0.2s ease; +} + +.voicemail-item:hover { + background: #f8f9fa; + border-color: #007bff; +} + +.voicemail-item.has-recording { + border-left: 4px solid #28a745; +} + +.voicemail-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} + +.voicemail-from { + display: flex; + align-items: center; + gap: 6px; +} + +.from-number { + font-weight: 600; + color: #495057; +} + +.voicemail-time { + font-size: 0.85rem; + color: #6c757d; +} + +.voicemail-details { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 8px; +} + +.voicemail-duration { + display: flex; + align-items: center; + gap: 4px; + font-size: 0.9rem; + color: #6c757d; +} + +.recording-indicator { + background: #28a745; + color: white; + padding: 2px 8px; + border-radius: 12px; + font-size: 0.8rem; +} + +.voicemail-transcription { + font-size: 0.9rem; + color: #495057; + font-style: italic; + padding: 8px; + background: #f8f9fa; + border-radius: 4px; + border-left: 3px solid #007bff; +} + +.voicemail-actions { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.voicemail-actions .twp-btn { + flex: 1; + min-width: 80px; +} + +.no-voicemails, +.voicemail-loading { + text-align: center; + padding: 20px; + color: #6c757d; + font-style: italic; +} + .twp-btn-secondary { background: #6c757d; color: white; @@ -588,7 +730,8 @@ .twp-connection-status, .twp-call-info, .twp-queue-controls, - .twp-queue-section { + .twp-queue-section, + .twp-voicemail-section { background: #2d3748; border-color: #4a5568; } @@ -623,8 +766,11 @@ .twp-phone-selection label, .twp-browser-phone-title, .twp-queue-section h4, + .twp-voicemail-section h4, .selected-queue-info h5, - .timer-value { + .timer-value, + .from-number, + .stat-value { color: #f7fafc; } @@ -635,14 +781,33 @@ .queue-info, .queue-stats, .queue-loading, - .no-queues { + .no-queues, + .voicemail-stats, + .voicemail-time, + .voicemail-duration, + .stat-label, + .voicemail-loading, + .no-voicemails { color: #cbd5e0; } - .no-queues { + .no-queues, + .voicemail-stats, + .voicemail-transcription { background: #2d3748; border-color: #4a5568; } + + .voicemail-item { + background: #2d3748; + border-color: #4a5568; + color: #f7fafc; + } + + .voicemail-item:hover { + background: #4a5568; + border-color: #63b3ed; + } } /* Animation for incoming calls */ diff --git a/assets/js/browser-phone-frontend.js b/assets/js/browser-phone-frontend.js index 1fecf22..f353f77 100644 --- a/assets/js/browser-phone-frontend.js +++ b/assets/js/browser-phone-frontend.js @@ -32,6 +32,7 @@ bindEvents(); loadPhoneNumbers(); loadUserQueues(); + loadUserVoicemails(); }); /** @@ -281,6 +282,23 @@ toggleAlert(); }); + // Voicemail refresh button + $('#twp-refresh-voicemails').on('click', function() { + loadUserVoicemails(); + }); + + // View all voicemails button + $('#twp-view-all-voicemails').on('click', function() { + // Open admin voicemails page in new tab + window.open(twp_frontend_ajax.admin_url + 'admin.php?page=twilio-wp-voicemails', '_blank'); + }); + + // Voicemail item click handler + $(document).on('click', '.voicemail-item', function() { + const voicemailId = $(this).data('voicemail-id'); + playVoicemail(voicemailId); + }); + // Queue item selection $(document).on('click', '.queue-item', function() { const queueId = $(this).data('queue-id'); @@ -921,6 +939,119 @@ } }); + /** + * Load user's voicemails + */ + function loadUserVoicemails(silent = false) { + $.ajax({ + url: twp_frontend_ajax.ajax_url, + method: 'POST', + data: { + action: 'twp_get_user_voicemails', + nonce: twp_frontend_ajax.nonce + }, + success: function(response) { + if (response.success) { + displayVoicemails(response.data); + } else if (!silent) { + showMessage('Failed to load voicemails: ' + (response.data || 'Unknown error'), 'error'); + } + }, + error: function() { + if (!silent) { + showMessage('Failed to load voicemails', 'error'); + } + } + }); + } + + /** + * Display voicemails in the UI + */ + function displayVoicemails(data) { + const $voicemailList = $('#twp-voicemail-list'); + + // Update stats + $('#twp-total-voicemails').text(data.total_count || 0); + $('#twp-today-voicemails').text(data.today_count || 0); + + if (!data.voicemails || data.voicemails.length === 0) { + $voicemailList.html('
No voicemails found.
'); + return; + } + + let html = ''; + data.voicemails.forEach(function(voicemail) { + const hasTranscription = voicemail.transcription && voicemail.transcription !== 'No transcription'; + const hasRecording = voicemail.has_recording; + + html += ` +
+
+
+ 📞 + ${voicemail.from_number} +
+
${voicemail.time_ago}
+
+
+
+ ⏱️ + ${formatDuration(voicemail.duration)} +
+ ${hasRecording ? '🎵 Recording' : ''} +
+ ${hasTranscription ? `
${voicemail.transcription}
` : ''} +
+ `; + }); + + $voicemailList.html(html); + } + + /** + * Format duration in seconds to mm:ss + */ + function formatDuration(seconds) { + if (!seconds || seconds === 0) return '0:00'; + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + return minutes + ':' + String(remainingSeconds).padStart(2, '0'); + } + + /** + * Play voicemail audio + */ + function playVoicemail(voicemailId) { + if (!voicemailId) return; + + // Get voicemail audio URL and play it + $.ajax({ + url: twp_frontend_ajax.ajax_url, + method: 'POST', + data: { + action: 'twp_get_voicemail_audio', + voicemail_id: voicemailId, + nonce: twp_frontend_ajax.nonce + }, + success: function(response) { + if (response.success && response.data.audio_url) { + // Create and play audio element + const audio = new Audio(response.data.audio_url); + audio.play().catch(function(error) { + showMessage('Failed to play voicemail: ' + error.message, 'error'); + }); + showMessage('Playing voicemail...', 'info'); + } else { + showMessage('No audio available for this voicemail', 'error'); + } + }, + error: function() { + showMessage('Failed to load voicemail audio', 'error'); + } + }); + } + // Load alert preference on init loadAlertPreference(); diff --git a/includes/class-twp-core.php b/includes/class-twp-core.php index 52b108f..8f626b2 100644 --- a/includes/class-twp-core.php +++ b/includes/class-twp-core.php @@ -156,6 +156,7 @@ class TWP_Core { $this->loader->add_action('wp_ajax_twp_delete_voicemail', $plugin_admin, 'ajax_delete_voicemail'); $this->loader->add_action('wp_ajax_twp_transcribe_voicemail', $plugin_admin, 'ajax_transcribe_voicemail'); $this->loader->add_action('wp_ajax_twp_get_voicemail_audio', $plugin_admin, 'ajax_get_voicemail_audio'); + $this->loader->add_action('wp_ajax_twp_get_user_voicemails', $plugin_admin, 'ajax_get_user_voicemails'); // Agent group management AJAX $this->loader->add_action('wp_ajax_twp_get_all_groups', $plugin_admin, 'ajax_get_all_groups'); diff --git a/includes/class-twp-shortcodes.php b/includes/class-twp-shortcodes.php index b92f09a..fba849c 100644 --- a/includes/class-twp-shortcodes.php +++ b/includes/class-twp-shortcodes.php @@ -72,6 +72,7 @@ class TWP_Shortcodes { // Localize script with AJAX data wp_localize_script('twp-browser-phone-frontend', 'twp_frontend_ajax', array( 'ajax_url' => admin_url('admin-ajax.php'), + 'admin_url' => admin_url(), 'nonce' => wp_create_nonce('twp_frontend_nonce'), 'user_id' => get_current_user_id(), 'is_logged_in' => is_user_logged_in() @@ -200,6 +201,34 @@ class TWP_Shortcodes { + +
+

Recent Voicemails

+
+
+ Total: + 0 +
+
+ Today: + 0 +
+
+ +
+
Loading voicemails...
+
+ +
+ + +
+
+