Systematic audit and implementation of missing Master Trainer functionality with comprehensive WordPress best practices and security implementation. ## Features Implemented - Master Events Overview (/master-trainer/events/) - KPI dashboard with filtering - Import/Export Data Management (/master-trainer/import-export/) - CSV operations - Communication Templates (/trainer/communication-templates/) - Professional templates - Enhanced Announcements (/master-trainer/announcements/) - Dynamic shortcode integration - Pending Approvals System (/master-trainer/pending-approvals/) - Workflow management ## Navigation & UX Improvements - Removed redundant Events link from top-level navigation menu - Reorganized administrative functions under Tools dropdown - Enhanced navigation clarity and professional appearance - Full responsive design with accessibility compliance ## Architecture & Security - 5 new singleton manager classes following WordPress patterns - Comprehensive role-based access control (hvac_master_trainer) - Complete security implementation (nonces, sanitization, escaping) - Performance optimizations with transient caching and conditional loading - Professional error handling and user feedback systems ## Files Added (16 new files) - 4 manager classes: Import/Export, Events Overview, Pending Approvals, Communication Templates - 4 CSS files with responsive design and accessibility features - 4 JavaScript files with AJAX functionality and error handling - 2 new templates: Import/Export, Pending Approvals - 2 enhanced templates: Events Overview, Communication Templates ## Files Modified (14 files) - Core system integration in Plugin, Page Manager, Scripts/Styles classes - Navigation system cleanup in Master Menu System - Enhanced access control and role management - Template updates for dynamic content integration ## Testing & Deployment - Comprehensive testing with Playwright automation - Successful staging deployment and verification - All 5 missing pages now fully functional - Navigation improvements verified working Resolves master trainer area audit requirements with production-ready implementation. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			428 lines
		
	
	
		
			No EOL
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			No EOL
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * HVAC Master Pending Approvals JavaScript
 | |
|  * 
 | |
|  * Handles interactive functionality for the pending approvals interface
 | |
|  */
 | |
| 
 | |
| (function($) {
 | |
|     'use strict';
 | |
|     
 | |
|     window.HVAC_PendingApprovals = {
 | |
|         
 | |
|         init: function() {
 | |
|             this.bindEvents();
 | |
|             this.initializeSelectors();
 | |
|         },
 | |
|         
 | |
|         bindEvents: function() {
 | |
|             // Individual approve/reject buttons
 | |
|             $(document).on('click', '.hvac-approve-btn', this.handleIndividualApprove);
 | |
|             $(document).on('click', '.hvac-reject-btn', this.handleIndividualReject);
 | |
|             
 | |
|             // Trainer name buttons (show details modal)
 | |
|             $(document).on('click', '.hvac-trainer-name-btn', this.handleShowDetails);
 | |
|             
 | |
|             // Select all checkboxes
 | |
|             $(document).on('change', '#hvac-select-all, #hvac-header-select-all', this.handleSelectAll);
 | |
|             
 | |
|             // Individual trainer checkboxes
 | |
|             $(document).on('change', '.hvac-trainer-select', this.handleTrainerSelect);
 | |
|             
 | |
|             // Bulk action buttons
 | |
|             $(document).on('click', '#hvac-bulk-approve', this.handleBulkApprove);
 | |
|             $(document).on('click', '#hvac-bulk-reject', this.handleBulkReject);
 | |
|             
 | |
|             // Modal controls
 | |
|             $(document).on('click', '.hvac-modal-close', this.hideModal);
 | |
|             $(document).on('click', '.hvac-modal', this.handleModalBackdropClick);
 | |
|             
 | |
|             // Reason modal confirm button
 | |
|             $(document).on('click', '#hvac-confirm-reason-action', this.handleConfirmReasonAction);
 | |
|             
 | |
|             // Bulk action modal confirm button
 | |
|             $(document).on('click', '#hvac-confirm-bulk-action', this.handleConfirmBulkAction);
 | |
|             
 | |
|             // Filter form auto-submit on select changes
 | |
|             $(document).on('change', '#status_filter, #region_filter', function() {
 | |
|                 $(this).closest('form').submit();
 | |
|             });
 | |
|             
 | |
|             // ESC key to close modals
 | |
|             $(document).on('keydown', this.handleKeydown);
 | |
|         },
 | |
|         
 | |
|         initializeSelectors: function() {
 | |
|             this.updateBulkActionButtons();
 | |
|         },
 | |
|         
 | |
|         handleIndividualApprove: function(e) {
 | |
|             e.preventDefault();
 | |
|             const userId = $(this).data('user-id');
 | |
|             const trainerName = $(this).closest('tr').find('.hvac-trainer-name-btn').text();
 | |
|             
 | |
|             HVAC_PendingApprovals.showReasonModal(userId, 'approve', 'Approve ' + trainerName);
 | |
|         },
 | |
|         
 | |
|         handleIndividualReject: function(e) {
 | |
|             e.preventDefault();
 | |
|             const userId = $(this).data('user-id');
 | |
|             const trainerName = $(this).closest('tr').find('.hvac-trainer-name-btn').text();
 | |
|             
 | |
|             HVAC_PendingApprovals.showReasonModal(userId, 'reject', 'Reject ' + trainerName);
 | |
|         },
 | |
|         
 | |
|         handleShowDetails: function(e) {
 | |
|             e.preventDefault();
 | |
|             const userId = $(this).data('user-id');
 | |
|             
 | |
|             HVAC_PendingApprovals.showTrainerDetails(userId);
 | |
|         },
 | |
|         
 | |
|         handleSelectAll: function(e) {
 | |
|             const isChecked = $(this).is(':checked');
 | |
|             $('.hvac-trainer-select').prop('checked', isChecked);
 | |
|             
 | |
|             // Sync both select-all checkboxes
 | |
|             $('#hvac-select-all, #hvac-header-select-all').prop('checked', isChecked);
 | |
|             
 | |
|             HVAC_PendingApprovals.updateBulkActionButtons();
 | |
|         },
 | |
|         
 | |
|         handleTrainerSelect: function(e) {
 | |
|             HVAC_PendingApprovals.updateBulkActionButtons();
 | |
|             
 | |
|             // Update select-all checkboxes
 | |
|             const totalCheckboxes = $('.hvac-trainer-select').length;
 | |
|             const checkedCheckboxes = $('.hvac-trainer-select:checked').length;
 | |
|             
 | |
|             $('#hvac-select-all, #hvac-header-select-all').prop('checked', totalCheckboxes === checkedCheckboxes);
 | |
|         },
 | |
|         
 | |
|         handleBulkApprove: function(e) {
 | |
|             e.preventDefault();
 | |
|             const selectedIds = HVAC_PendingApprovals.getSelectedTrainerIds();
 | |
|             
 | |
|             if (selectedIds.length === 0) {
 | |
|                 alert('Please select trainers to approve.');
 | |
|                 return;
 | |
|             }
 | |
|             
 | |
|             HVAC_PendingApprovals.showBulkActionModal(selectedIds, 'approve');
 | |
|         },
 | |
|         
 | |
|         handleBulkReject: function(e) {
 | |
|             e.preventDefault();
 | |
|             const selectedIds = HVAC_PendingApprovals.getSelectedTrainerIds();
 | |
|             
 | |
|             if (selectedIds.length === 0) {
 | |
|                 alert('Please select trainers to reject.');
 | |
|                 return;
 | |
|             }
 | |
|             
 | |
|             HVAC_PendingApprovals.showBulkActionModal(selectedIds, 'reject');
 | |
|         },
 | |
|         
 | |
|         handleModalBackdropClick: function(e) {
 | |
|             if (e.target === this) {
 | |
|                 HVAC_PendingApprovals.hideModal();
 | |
|             }
 | |
|         },
 | |
|         
 | |
|         handleKeydown: function(e) {
 | |
|             if (e.key === 'Escape') {
 | |
|                 HVAC_PendingApprovals.hideModal();
 | |
|             }
 | |
|         },
 | |
|         
 | |
|         handleConfirmReasonAction: function(e) {
 | |
|             e.preventDefault();
 | |
|             
 | |
|             const userId = $('#hvac-reason-user-id').val();
 | |
|             const action = $('#hvac-reason-action').val();
 | |
|             const reason = $('#hvac-approval-reason').val();
 | |
|             
 | |
|             if (action === 'approve') {
 | |
|                 HVAC_PendingApprovals.approveTrainer(userId, reason);
 | |
|             } else if (action === 'reject') {
 | |
|                 HVAC_PendingApprovals.rejectTrainer(userId, reason);
 | |
|             }
 | |
|             
 | |
|             HVAC_PendingApprovals.hideModal();
 | |
|         },
 | |
|         
 | |
|         handleConfirmBulkAction: function(e) {
 | |
|             e.preventDefault();
 | |
|             
 | |
|             const action = $('#hvac-bulk-action-type').val();
 | |
|             const reason = $('#hvac-bulk-reason').val();
 | |
|             const selectedIds = HVAC_PendingApprovals.getSelectedTrainerIds();
 | |
|             
 | |
|             HVAC_PendingApprovals.performBulkAction(selectedIds, action, reason);
 | |
|             HVAC_PendingApprovals.hideModal();
 | |
|         },
 | |
|         
 | |
|         showReasonModal: function(userId, action, title) {
 | |
|             $('#hvac-reason-modal-title').text(title);
 | |
|             $('#hvac-reason-user-id').val(userId);
 | |
|             $('#hvac-reason-action').val(action);
 | |
|             $('#hvac-approval-reason').val('');
 | |
|             
 | |
|             // Update button text and color
 | |
|             const confirmBtn = $('#hvac-confirm-reason-action');
 | |
|             if (action === 'approve') {
 | |
|                 confirmBtn.text('Approve').removeClass('hvac-btn-danger').addClass('hvac-btn-success');
 | |
|             } else {
 | |
|                 confirmBtn.text('Reject').removeClass('hvac-btn-success').addClass('hvac-btn-danger');
 | |
|             }
 | |
|             
 | |
|             this.showModal('#hvac-approval-reason-modal');
 | |
|         },
 | |
|         
 | |
|         showBulkActionModal: function(userIds, action) {
 | |
|             const actionText = action === 'approve' ? 'approve' : 'reject';
 | |
|             const count = userIds.length;
 | |
|             
 | |
|             $('#hvac-bulk-modal-title').text('Bulk ' + actionText.charAt(0).toUpperCase() + actionText.slice(1));
 | |
|             $('#hvac-bulk-action-type').val(action);
 | |
|             $('#hvac-bulk-reason').val('');
 | |
|             $('#hvac-bulk-action-message').text(`Are you sure you want to ${actionText} ${count} trainer(s)?`);
 | |
|             
 | |
|             // Update button text and color
 | |
|             const confirmBtn = $('#hvac-confirm-bulk-action');
 | |
|             if (action === 'approve') {
 | |
|                 confirmBtn.text('Approve All').removeClass('hvac-btn-danger').addClass('hvac-btn-success');
 | |
|             } else {
 | |
|                 confirmBtn.text('Reject All').removeClass('hvac-btn-success').addClass('hvac-btn-danger');
 | |
|             }
 | |
|             
 | |
|             this.showModal('#hvac-bulk-action-modal');
 | |
|         },
 | |
|         
 | |
|         showTrainerDetails: function(userId) {
 | |
|             $('#hvac-trainer-details-content').html('Loading...');
 | |
|             this.showModal('#hvac-trainer-details-modal');
 | |
|             
 | |
|             $.ajax({
 | |
|                 url: hvac_ajax.ajax_url,
 | |
|                 type: 'POST',
 | |
|                 data: {
 | |
|                     action: 'hvac_get_trainer_details',
 | |
|                     user_id: userId,
 | |
|                     nonce: hvac_ajax.nonce
 | |
|                 },
 | |
|                 success: function(response) {
 | |
|                     if (response.success) {
 | |
|                         $('#hvac-trainer-details-content').html(response.data.html);
 | |
|                     } else {
 | |
|                         $('#hvac-trainer-details-content').html('<p class="error">Failed to load trainer details: ' + response.data.message + '</p>');
 | |
|                     }
 | |
|                 },
 | |
|                 error: function() {
 | |
|                     $('#hvac-trainer-details-content').html('<p class="error">Failed to load trainer details. Please try again.</p>');
 | |
|                 }
 | |
|             });
 | |
|         },
 | |
|         
 | |
|         approveTrainer: function(userId, reason) {
 | |
|             this.showLoading();
 | |
|             
 | |
|             $.ajax({
 | |
|                 url: hvac_ajax.ajax_url,
 | |
|                 type: 'POST',
 | |
|                 data: {
 | |
|                     action: 'hvac_approve_trainer',
 | |
|                     user_id: userId,
 | |
|                     reason: reason,
 | |
|                     nonce: hvac_ajax.nonce
 | |
|                 },
 | |
|                 success: function(response) {
 | |
|                     HVAC_PendingApprovals.hideLoading();
 | |
|                     
 | |
|                     if (response.success) {
 | |
|                         HVAC_PendingApprovals.showSuccessMessage(response.data.message);
 | |
|                         HVAC_PendingApprovals.updateTrainerRow(userId, 'approved');
 | |
|                     } else {
 | |
|                         HVAC_PendingApprovals.showErrorMessage(response.data.message);
 | |
|                     }
 | |
|                 },
 | |
|                 error: function() {
 | |
|                     HVAC_PendingApprovals.hideLoading();
 | |
|                     HVAC_PendingApprovals.showErrorMessage('Failed to approve trainer. Please try again.');
 | |
|                 }
 | |
|             });
 | |
|         },
 | |
|         
 | |
|         rejectTrainer: function(userId, reason) {
 | |
|             this.showLoading();
 | |
|             
 | |
|             $.ajax({
 | |
|                 url: hvac_ajax.ajax_url,
 | |
|                 type: 'POST',
 | |
|                 data: {
 | |
|                     action: 'hvac_reject_trainer',
 | |
|                     user_id: userId,
 | |
|                     reason: reason,
 | |
|                     nonce: hvac_ajax.nonce
 | |
|                 },
 | |
|                 success: function(response) {
 | |
|                     HVAC_PendingApprovals.hideLoading();
 | |
|                     
 | |
|                     if (response.success) {
 | |
|                         HVAC_PendingApprovals.showSuccessMessage(response.data.message);
 | |
|                         HVAC_PendingApprovals.updateTrainerRow(userId, 'rejected');
 | |
|                     } else {
 | |
|                         HVAC_PendingApprovals.showErrorMessage(response.data.message);
 | |
|                     }
 | |
|                 },
 | |
|                 error: function() {
 | |
|                     HVAC_PendingApprovals.hideLoading();
 | |
|                     HVAC_PendingApprovals.showErrorMessage('Failed to reject trainer. Please try again.');
 | |
|                 }
 | |
|             });
 | |
|         },
 | |
|         
 | |
|         performBulkAction: function(userIds, action, reason) {
 | |
|             this.showLoading();
 | |
|             
 | |
|             $.ajax({
 | |
|                 url: hvac_ajax.ajax_url,
 | |
|                 type: 'POST',
 | |
|                 data: {
 | |
|                     action: 'hvac_bulk_trainer_action',
 | |
|                     user_ids: userIds,
 | |
|                     action: action,
 | |
|                     reason: reason,
 | |
|                     nonce: hvac_ajax.nonce
 | |
|                 },
 | |
|                 success: function(response) {
 | |
|                     HVAC_PendingApprovals.hideLoading();
 | |
|                     
 | |
|                     if (response.success) {
 | |
|                         HVAC_PendingApprovals.showSuccessMessage(response.data.message);
 | |
|                         
 | |
|                         // Update each trainer row
 | |
|                         const newStatus = action === 'approve' ? 'approved' : 'rejected';
 | |
|                         userIds.forEach(function(userId) {
 | |
|                             if (response.data.results[userId] === 'success') {
 | |
|                                 HVAC_PendingApprovals.updateTrainerRow(userId, newStatus);
 | |
|                             }
 | |
|                         });
 | |
|                         
 | |
|                         // Clear selections
 | |
|                         $('.hvac-trainer-select, #hvac-select-all, #hvac-header-select-all').prop('checked', false);
 | |
|                         HVAC_PendingApprovals.updateBulkActionButtons();
 | |
|                         
 | |
|                     } else {
 | |
|                         HVAC_PendingApprovals.showErrorMessage(response.data.message);
 | |
|                     }
 | |
|                 },
 | |
|                 error: function() {
 | |
|                     HVAC_PendingApprovals.hideLoading();
 | |
|                     HVAC_PendingApprovals.showErrorMessage('Failed to perform bulk action. Please try again.');
 | |
|                 }
 | |
|             });
 | |
|         },
 | |
|         
 | |
|         updateTrainerRow: function(userId, newStatus) {
 | |
|             const row = $('tr[data-user-id="' + userId + '"]');
 | |
|             
 | |
|             if (row.length) {
 | |
|                 // Update status badge
 | |
|                 const statusCell = row.find('.hvac-col-status');
 | |
|                 const statusBadges = {
 | |
|                     'approved': '<span class="hvac-status-badge hvac-status-approved">Approved</span>',
 | |
|                     'rejected': '<span class="hvac-status-badge hvac-status-rejected">Rejected</span>'
 | |
|                 };
 | |
|                 statusCell.html(statusBadges[newStatus] || newStatus);
 | |
|                 
 | |
|                 // Update actions cell
 | |
|                 const actionsCell = row.find('.hvac-col-actions');
 | |
|                 actionsCell.html('<span class="hvac-status-text">' + newStatus.charAt(0).toUpperCase() + newStatus.slice(1) + '</span>');
 | |
|                 
 | |
|                 // Remove checkbox column if it exists
 | |
|                 row.find('.hvac-col-select').remove();
 | |
|                 
 | |
|                 // Add visual feedback
 | |
|                 row.addClass('hvac-row-updated').delay(3000).queue(function() {
 | |
|                     $(this).removeClass('hvac-row-updated').dequeue();
 | |
|                 });
 | |
|             }
 | |
|         },
 | |
|         
 | |
|         getSelectedTrainerIds: function() {
 | |
|             const ids = [];
 | |
|             $('.hvac-trainer-select:checked').each(function() {
 | |
|                 ids.push($(this).val());
 | |
|             });
 | |
|             return ids;
 | |
|         },
 | |
|         
 | |
|         updateBulkActionButtons: function() {
 | |
|             const selectedCount = $('.hvac-trainer-select:checked').length;
 | |
|             const bulkButtons = $('#hvac-bulk-approve, #hvac-bulk-reject');
 | |
|             
 | |
|             if (selectedCount > 0) {
 | |
|                 bulkButtons.prop('disabled', false);
 | |
|             } else {
 | |
|                 bulkButtons.prop('disabled', true);
 | |
|             }
 | |
|         },
 | |
|         
 | |
|         showModal: function(modalSelector) {
 | |
|             $(modalSelector).fadeIn(300);
 | |
|             $('body').addClass('hvac-modal-open');
 | |
|         },
 | |
|         
 | |
|         hideModal: function() {
 | |
|             $('.hvac-modal').fadeOut(300);
 | |
|             $('body').removeClass('hvac-modal-open');
 | |
|         },
 | |
|         
 | |
|         showLoading: function() {
 | |
|             if ($('#hvac-loading-overlay').length === 0) {
 | |
|                 $('body').append('<div id="hvac-loading-overlay"><div class="hvac-loading-spinner"></div></div>');
 | |
|             }
 | |
|             $('#hvac-loading-overlay').show();
 | |
|         },
 | |
|         
 | |
|         hideLoading: function() {
 | |
|             $('#hvac-loading-overlay').hide();
 | |
|         },
 | |
|         
 | |
|         showSuccessMessage: function(message) {
 | |
|             this.showMessage(message, 'success');
 | |
|         },
 | |
|         
 | |
|         showErrorMessage: function(message) {
 | |
|             this.showMessage(message, 'error');
 | |
|         },
 | |
|         
 | |
|         showMessage: function(message, type) {
 | |
|             // Remove existing messages
 | |
|             $('.hvac-flash-message').remove();
 | |
|             
 | |
|             // Add new message
 | |
|             const messageHtml = '<div class="hvac-flash-message hvac-flash-' + type + '">' + message + '</div>';
 | |
|             $('.hvac-pending-approvals-wrapper').prepend(messageHtml);
 | |
|             
 | |
|             // Auto-remove after 5 seconds
 | |
|             setTimeout(function() {
 | |
|                 $('.hvac-flash-message').fadeOut(300, function() {
 | |
|                     $(this).remove();
 | |
|                 });
 | |
|             }, 5000);
 | |
|             
 | |
|             // Scroll to top to show message
 | |
|             $('html, body').animate({ scrollTop: 0 }, 300);
 | |
|         }
 | |
|     };
 | |
|     
 | |
|     // Initialize when document is ready
 | |
|     $(document).ready(function() {
 | |
|         // Only initialize if we're on the pending approvals page
 | |
|         if ($('.hvac-pending-approvals-wrapper').length > 0) {
 | |
|             HVAC_PendingApprovals.init();
 | |
|         }
 | |
|     });
 | |
|     
 | |
| })(jQuery); |