- Add proper AJAX nonce distribution to page-master-trainers.php - Implement security authentication for both dashboard and trainers pages - Fix template-level nonce initialization for HVAC AJAX system - Maintain WordPress security best practices throughout implementation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			766 lines
		
	
	
		
			No EOL
		
	
	
		
			22 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			766 lines
		
	
	
		
			No EOL
		
	
	
		
			22 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Template Name: HVAC Master Trainer Dashboard
 | |
|  *
 | |
|  * This template handles the display of the HVAC Master Trainer Dashboard.
 | |
|  * It shows aggregate data across ALL trainers and events.
 | |
|  *
 | |
|  * @package    HVAC Community Events
 | |
|  * @subpackage Templates
 | |
|  * @author     Ben Reed
 | |
|  * @version    1.0.0
 | |
|  */
 | |
| 
 | |
| // Exit if accessed directly.
 | |
| if ( ! defined( 'ABSPATH' ) ) {
 | |
| 	exit;
 | |
| }
 | |
| 
 | |
| // --- Security Check & Data Loading ---
 | |
| echo '<!-- DEBUG: template-hvac-master-dashboard.php loaded -->';
 | |
| 
 | |
| // Ensure user is logged in and has access to the master dashboard
 | |
| if ( ! is_user_logged_in() ) {
 | |
| 	// Redirect to login page if not logged in
 | |
| 	wp_safe_redirect( home_url( '/training-login/' ) );
 | |
| 	exit;
 | |
| }
 | |
| 
 | |
| // Authentication handled by centralized HVAC_Access_Control system
 | |
| // Redundant template-level auth check removed to prevent content blocking
 | |
| 
 | |
| // Get current user info
 | |
| $current_user = wp_get_current_user();
 | |
| $user_id = $current_user->ID;
 | |
| 
 | |
| // Check for approval message
 | |
| $approval_message = get_transient( 'hvac_approval_message' );
 | |
| if ( $approval_message ) {
 | |
| 	delete_transient( 'hvac_approval_message' );
 | |
| }
 | |
| 
 | |
| // Classes are loaded during plugin initialization
 | |
| $master_data = new HVAC_Master_Dashboard_Data();
 | |
| 
 | |
| // Get statistics
 | |
| $total_events = $master_data->get_total_events_count();
 | |
| $upcoming_events = $master_data->get_upcoming_events_count();
 | |
| $past_events = $master_data->get_past_events_count();
 | |
| $total_tickets_sold = $master_data->get_total_tickets_sold();
 | |
| $total_revenue = $master_data->get_total_revenue();
 | |
| $trainer_stats = $master_data->get_trainer_statistics();
 | |
| 
 | |
| // Get events table data (default view)
 | |
| $default_args = array(
 | |
| 	'status' => 'all',
 | |
| 	'orderby' => 'date',
 | |
| 	'order' => 'DESC',
 | |
| 	'page' => 1,
 | |
| 	'per_page' => 10
 | |
| );
 | |
| $events_table_data = $master_data->get_events_table_data( $default_args );
 | |
| 
 | |
| // Get list of all trainers for filter dropdown
 | |
| $all_trainers = get_users(array(
 | |
| 	'role__in' => array('hvac_trainer', 'hvac_master_trainer'),
 | |
| 	'fields' => array('ID', 'display_name')
 | |
| ));
 | |
| 
 | |
| // Error handling for access denied
 | |
| $error_message = '';
 | |
| if ( isset( $_GET['error'] ) && $_GET['error'] === 'access_denied' ) {
 | |
| 	$error_message = 'You were redirected here because you do not have permission to access the Master Dashboard.';
 | |
| }
 | |
| 
 | |
| // Note: get_header() is called by the main page template
 | |
| 
 | |
| ?>
 | |
| 
 | |
| <style>
 | |
| /* Status badges */
 | |
| .status-badge {
 | |
| 	display: inline-block;
 | |
| 	padding: 4px 8px;
 | |
| 	font-size: 12px;
 | |
| 	font-weight: 600;
 | |
| 	border-radius: 4px;
 | |
| 	text-transform: capitalize;
 | |
| }
 | |
| .status-badge.status-pending {
 | |
| 	background: #f0ad4e;
 | |
| 	color: white;
 | |
| }
 | |
| .status-badge.status-approved {
 | |
| 	background: #5bc0de;
 | |
| 	color: white;
 | |
| }
 | |
| .status-badge.status-active {
 | |
| 	background: #5cb85c;
 | |
| 	color: white;
 | |
| }
 | |
| .status-badge.status-inactive {
 | |
| 	background: #777;
 | |
| 	color: white;
 | |
| }
 | |
| .status-badge.status-disabled {
 | |
| 	background: #d9534f;
 | |
| 	color: white;
 | |
| }
 | |
| 
 | |
| /* Filter controls */
 | |
| .trainer-filters,
 | |
| .bulk-update-controls {
 | |
| 	background: #f5f5f5;
 | |
| 	padding: 15px;
 | |
| 	border-radius: 4px;
 | |
| }
 | |
| .filter-group {
 | |
| 	display: inline-block;
 | |
| }
 | |
| .filter-group label {
 | |
| 	font-weight: 600;
 | |
| 	margin-right: 5px;
 | |
| }
 | |
| .filter-group input,
 | |
| .filter-group select {
 | |
| 	margin-right: 10px;
 | |
| }
 | |
| 
 | |
| /* Table styling */
 | |
| .trainers-table {
 | |
| 	width: 100%;
 | |
| 	border-collapse: collapse;
 | |
| 	margin-top: 20px;
 | |
| }
 | |
| .trainers-table th,
 | |
| .trainers-table td {
 | |
| 	padding: 10px;
 | |
| 	text-align: left;
 | |
| 	border-bottom: 1px solid #ddd;
 | |
| }
 | |
| .trainers-table th {
 | |
| 	background: #f5f5f5;
 | |
| 	font-weight: 600;
 | |
| }
 | |
| .trainers-table tbody tr:hover {
 | |
| 	background: #f9f9f9;
 | |
| }
 | |
| .trainers-table .number,
 | |
| .trainers-table .revenue {
 | |
| 	text-align: right;
 | |
| }
 | |
| 
 | |
| /* Pagination */
 | |
| .pagination-container {
 | |
| 	margin-top: 20px;
 | |
| 	display: flex;
 | |
| 	justify-content: space-between;
 | |
| 	align-items: center;
 | |
| }
 | |
| .pagination-btn {
 | |
| 	padding: 6px 12px;
 | |
| 	margin: 0 2px;
 | |
| 	background: #fff;
 | |
| 	border: 1px solid #ddd;
 | |
| 	cursor: pointer;
 | |
| 	border-radius: 3px;
 | |
| }
 | |
| .pagination-btn:hover {
 | |
| 	background: #f5f5f5;
 | |
| }
 | |
| .pagination-btn.active {
 | |
| 	background: #0073aa;
 | |
| 	color: white;
 | |
| 	border-color: #0073aa;
 | |
| }
 | |
| .pagination-btn:disabled {
 | |
| 	opacity: 0.5;
 | |
| 	cursor: not-allowed;
 | |
| }
 | |
| 
 | |
| /* Clickable trainer names */
 | |
| .trainers-table .trainer-name-link {
 | |
| 	text-decoration: none;
 | |
| 	color: inherit;
 | |
| 	transition: color 0.2s ease;
 | |
| }
 | |
| .trainers-table .trainer-name-link:hover {
 | |
| 	color: #0073aa;
 | |
| 	text-decoration: underline;
 | |
| }
 | |
| </style>
 | |
| 
 | |
| <div id="primary" class="content-area primary ast-container">
 | |
| 	<main id="main" class="site-main">
 | |
| 		
 | |
| 		<?php if ( $error_message ): ?>
 | |
| 		<div class="hvac-dashboard-header">
 | |
| 			<div class="hvac-stat-card" style="background: #fff3cd; border-left: 4px solid #856404; padding: 15px; margin-bottom: 20px;">
 | |
| 				<p style="color: #856404; margin: 0;"><?php echo esc_html( $error_message ); ?></p>
 | |
| 			</div>
 | |
| 		</div>
 | |
| 		<?php endif; ?>
 | |
| 		
 | |
| 		<?php if ( $approval_message ): ?>
 | |
| 		<div class="hvac-dashboard-header">
 | |
| 			<div class="hvac-stat-card" style="background: #d4edda; border-left: 4px solid #28a745; padding: 15px; margin-bottom: 20px;">
 | |
| 				<button type="button" class="close" style="float: right; background: none; border: none; font-size: 20px; cursor: pointer;" onclick="this.parentElement.style.display='none'">×</button>
 | |
| 				<p style="color: #155724; margin: 0;"><?php echo esc_html( $approval_message ); ?></p>
 | |
| 			</div>
 | |
| 		</div>
 | |
| 		<?php endif; ?>
 | |
| 
 | |
| 		<!-- Dashboard Header -->
 | |
| 		<div class="hvac-dashboard-header">
 | |
| 			<h1 class="entry-title">Master Dashboard</h1>
 | |
| 		</div>
 | |
| 
 | |
| 		<!-- System Overview Statistics -->
 | |
| 		<section class="hvac-dashboard-stats">
 | |
| 			<h2>System Overview</h2>
 | |
| 			<div class="hvac-stats-row">
 | |
| 				
 | |
| 				<!-- Stat Card: Total Events -->
 | |
| 				<div class="hvac-stat-col">
 | |
| 					<div class="hvac-stat-card">
 | |
| 						<h3>Total Events</h3>
 | |
| 						<p><?php echo number_format( $total_events ); ?></p>
 | |
| 					</div>
 | |
| 				</div>
 | |
| 				
 | |
| 				<!-- Stat Card: Upcoming Events -->
 | |
| 				<div class="hvac-stat-col">
 | |
| 					<div class="hvac-stat-card">
 | |
| 						<h3>Upcoming Events</h3>
 | |
| 						<p><?php echo number_format( $upcoming_events ); ?></p>
 | |
| 					</div>
 | |
| 				</div>
 | |
| 				
 | |
| 				<!-- Stat Card: Completed Events -->
 | |
| 				<div class="hvac-stat-col">
 | |
| 					<div class="hvac-stat-card">
 | |
| 						<h3>Completed Events</h3>
 | |
| 						<p><?php echo number_format( $past_events ); ?></p>
 | |
| 					</div>
 | |
| 				</div>
 | |
| 				
 | |
| 				<!-- Stat Card: Active Trainers -->
 | |
| 				<div class="hvac-stat-col">
 | |
| 					<div class="hvac-stat-card">
 | |
| 						<h3>Active Trainers</h3>
 | |
| 						<p><?php echo number_format( $trainer_stats['total_trainers'] ); ?></p>
 | |
| 					</div>
 | |
| 				</div>
 | |
| 				
 | |
| 				<!-- Stat Card: Tickets Sold -->
 | |
| 				<div class="hvac-stat-col">
 | |
| 					<div class="hvac-stat-card">
 | |
| 						<h3>Tickets Sold</h3>
 | |
| 						<p><?php echo number_format( $total_tickets_sold ); ?></p>
 | |
| 					</div>
 | |
| 				</div>
 | |
| 				
 | |
| 				<!-- Stat Card: Total Revenue -->
 | |
| 				<div class="hvac-stat-col">
 | |
| 					<div class="hvac-stat-card">
 | |
| 						<h3>Total Revenue</h3>
 | |
| 						<p>$<?php echo number_format( $total_revenue, 2 ); ?></p>
 | |
| 					</div>
 | |
| 				</div>
 | |
| 				
 | |
| 			</div>
 | |
| 		</section>
 | |
| 
 | |
| 	<!-- Trainer Analytics Section -->
 | |
| 	<section id="trainers" class="dashboard-section">
 | |
| 		<h2 class="section-title">Trainer Performance Analytics</h2>
 | |
| 		
 | |
| 		<!-- Bulk Update Controls -->
 | |
| 		<div class="bulk-update-controls" style="margin-bottom: 20px; display: flex; align-items: center; gap: 10px;">
 | |
| 			<select id="bulk-status-update" name="bulk_status" class="hvac-select">
 | |
| 				<option value="">-- Select Status --</option>
 | |
| 				<option value="pending">Pending</option>
 | |
| 				<option value="approved">Approved</option>
 | |
| 				<option value="disabled">Disabled</option>
 | |
| 			</select>
 | |
| 			<button type="button" id="bulk-update-btn" class="ast-button ast-button-primary">Update Selected</button>
 | |
| 			<span id="bulk-update-message" style="margin-left: 10px; color: #28a745; display: none;"></span>
 | |
| 		</div>
 | |
| 		
 | |
| 		<!-- Trainer Table Filters -->
 | |
| 		<div class="trainer-filters" style="margin-bottom: 20px; display: flex; gap: 15px; flex-wrap: wrap;">
 | |
| 			<div class="filter-group">
 | |
| 				<label for="trainer-status-filter">Status:</label>
 | |
| 				<select id="trainer-status-filter" name="status">
 | |
| 					<option value="all">All Trainers</option>
 | |
| 					<option value="pending">Pending</option>
 | |
| 					<option value="approved">Approved</option>
 | |
| 					<option value="active">Active</option>
 | |
| 					<option value="inactive">Inactive</option>
 | |
| 					<option value="disabled">Disabled</option>
 | |
| 				</select>
 | |
| 			</div>
 | |
| 			
 | |
| 			<div class="filter-group">
 | |
| 				<label for="trainer-search">Search:</label>
 | |
| 				<input type="text" id="trainer-search" name="search" placeholder="Name or email...">
 | |
| 			</div>
 | |
| 			
 | |
| 			<button type="button" id="apply-trainer-filters" class="btn btn-primary">Apply Filters</button>
 | |
| 			<button type="button" id="reset-trainer-filters" class="btn btn-secondary">Reset</button>
 | |
| 		</div>
 | |
| 		
 | |
| 		<!-- Trainer Table Container -->
 | |
| 		<div id="trainers-table-container" class="trainers-table-container">
 | |
| 			<div class="loading-placeholder">Loading trainers...</div>
 | |
| 		</div>
 | |
| 	</section>
 | |
| 
 | |
| 	<!-- All Events Section -->
 | |
| 	<section id="events" class="dashboard-section">
 | |
| 		<h2 class="section-title">All Events Management</h2>
 | |
| 		
 | |
| 		<!-- Events Table Filters -->
 | |
| 		<div class="events-filters">
 | |
| 			<div class="filter-group">
 | |
| 				<label for="events-status-filter">Status:</label>
 | |
| 				<select id="events-status-filter" name="status">
 | |
| 					<option value="all">All Events</option>
 | |
| 					<option value="publish">Published</option>
 | |
| 					<option value="future">Upcoming</option>
 | |
| 					<option value="draft">Draft</option>
 | |
| 					<option value="pending">Pending</option>
 | |
| 					<option value="private">Private</option>
 | |
| 				</select>
 | |
| 			</div>
 | |
| 			
 | |
| 			<div class="filter-group">
 | |
| 				<label for="events-trainer-filter">Trainer:</label>
 | |
| 				<select id="events-trainer-filter" name="trainer_id">
 | |
| 					<option value="">All Trainers</option>
 | |
| 					<?php foreach ( $all_trainers as $trainer ): ?>
 | |
| 					<option value="<?php echo esc_attr( $trainer->ID ); ?>">
 | |
| 						<?php echo esc_html( $trainer->display_name ); ?>
 | |
| 					</option>
 | |
| 					<?php endforeach; ?>
 | |
| 				</select>
 | |
| 			</div>
 | |
| 			
 | |
| 			<div class="filter-group">
 | |
| 				<label for="events-search">Search:</label>
 | |
| 				<input type="text" id="events-search" name="search" placeholder="Event name...">
 | |
| 			</div>
 | |
| 			
 | |
| 			<div class="filter-group">
 | |
| 				<label for="events-date-from">From:</label>
 | |
| 				<input type="date" id="events-date-from" name="date_from">
 | |
| 			</div>
 | |
| 			
 | |
| 			<div class="filter-group">
 | |
| 				<label for="events-date-to">To:</label>
 | |
| 				<input type="date" id="events-date-to" name="date_to">
 | |
| 			</div>
 | |
| 			
 | |
| 			<button type="button" id="apply-events-filters" class="btn btn-primary">Apply Filters</button>
 | |
| 			<button type="button" id="reset-events-filters" class="btn btn-secondary">Reset</button>
 | |
| 		</div>
 | |
| 
 | |
| 		<!-- Events Table -->
 | |
| 		<div id="events-table-container" class="events-table-container">
 | |
| 			<!-- Table will be loaded here via AJAX -->
 | |
| 			<div class="loading-placeholder">Loading events...</div>
 | |
| 		</div>
 | |
| 	</section>
 | |
| </div>
 | |
| 
 | |
| <!-- Master Dashboard JavaScript -->
 | |
| <script>
 | |
| jQuery(document).ready(function($) {
 | |
| 	// Trainer table functionality
 | |
| 	var trainerTable = {
 | |
| 		currentPage: 1,
 | |
| 		perPage: 10,
 | |
| 		orderBy: 'display_name',
 | |
| 		order: 'ASC',
 | |
| 		
 | |
| 		init: function() {
 | |
| 			this.bindEvents();
 | |
| 			this.loadTrainerTable();
 | |
| 		},
 | |
| 		
 | |
| 		bindEvents: function() {
 | |
| 			var self = this;
 | |
| 			
 | |
| 			// Filter controls
 | |
| 			$('#apply-trainer-filters').on('click', function() {
 | |
| 				self.currentPage = 1;
 | |
| 				self.loadTrainerTable();
 | |
| 			});
 | |
| 			
 | |
| 			$('#reset-trainer-filters').on('click', function() {
 | |
| 				$('#trainer-status-filter').val('all');
 | |
| 				$('#trainer-search').val('');
 | |
| 				self.currentPage = 1;
 | |
| 				self.loadTrainerTable();
 | |
| 			});
 | |
| 			
 | |
| 			// Bulk update
 | |
| 			$('#bulk-update-btn').on('click', function() {
 | |
| 				self.bulkUpdateStatus();
 | |
| 			});
 | |
| 		},
 | |
| 		
 | |
| 		loadTrainerTable: function() {
 | |
| 			var self = this;
 | |
| 			var container = $('#trainers-table-container');
 | |
| 			
 | |
| 			container.html('<div class="loading-placeholder">Loading trainers...</div>');
 | |
| 			
 | |
| 			var data = {
 | |
| 				action: 'hvac_master_dashboard_trainers',
 | |
| 				nonce: hvac_ajax.nonce,
 | |
| 				status: $('#trainer-status-filter').val(),
 | |
| 				search: $('#trainer-search').val(),
 | |
| 				page: self.currentPage,
 | |
| 				per_page: self.perPage,
 | |
| 				orderby: self.orderBy,
 | |
| 				order: self.order
 | |
| 			};
 | |
| 			
 | |
| 			$.post(ajaxurl, data, function(response) {
 | |
| 				if (response.success) {
 | |
| 					self.renderTrainerTable(response.data);
 | |
| 				} else {
 | |
| 					container.html('<div class="error-message">Error loading trainers table.</div>');
 | |
| 				}
 | |
| 			});
 | |
| 		},
 | |
| 		
 | |
| 		renderTrainerTable: function(data) {
 | |
| 			var container = $('#trainers-table-container');
 | |
| 			var html = '';
 | |
| 			
 | |
| 			if (data.trainers && data.trainers.length > 0) {
 | |
| 				html += '<table class="trainers-table">';
 | |
| 				html += '<thead><tr>';
 | |
| 				html += '<th><input type="checkbox" id="select-all-trainers"></th>';
 | |
| 				html += '<th>Name</th>';
 | |
| 				html += '<th>Status</th>';
 | |
| 				html += '<th>Registration Date</th>';
 | |
| 				html += '<th>Last Event Date</th>';
 | |
| 				html += '<th>Total Events</th>';
 | |
| 				html += '<th>Revenue</th>';
 | |
| 				html += '</tr></thead>';
 | |
| 				html += '<tbody>';
 | |
| 				
 | |
| 				data.trainers.forEach(function(trainer) {
 | |
| 					var statusClass = 'status-' + trainer.status.toLowerCase();
 | |
| 					html += '<tr>';
 | |
| 					html += '<td><input type="checkbox" class="trainer-checkbox" value="' + trainer.id + '"></td>';
 | |
| 					html += '<td><strong><a href="/master-trainer/edit-trainer-profile?user_id=' + trainer.id + '" class="trainer-name-link">' + trainer.name + '</a></strong><br><small>' + trainer.email + '</small></td>';
 | |
| 					html += '<td><span class="status-badge ' + statusClass + '">' + trainer.status_label + '</span></td>';
 | |
| 					html += '<td>' + trainer.registration_date + '</td>';
 | |
| 					html += '<td>' + (trainer.last_event_date || 'Never') + '</td>';
 | |
| 					html += '<td class="number">' + trainer.total_events + '</td>';
 | |
| 					html += '<td class="revenue">$' + parseFloat(trainer.revenue).toFixed(2) + '</td>';
 | |
| 					html += '</tr>';
 | |
| 				});
 | |
| 				
 | |
| 				html += '</tbody></table>';
 | |
| 				
 | |
| 				// Add pagination
 | |
| 				if (data.pagination.total_pages > 1) {
 | |
| 					html += this.renderPagination(data.pagination);
 | |
| 				}
 | |
| 			} else {
 | |
| 				html = '<div class="no-data-message"><p>No trainers found matching your criteria.</p></div>';
 | |
| 			}
 | |
| 			
 | |
| 			container.html(html);
 | |
| 			this.bindTableEvents();
 | |
| 		},
 | |
| 		
 | |
| 		renderPagination: function(pagination) {
 | |
| 			var html = '<div class="pagination-container">';
 | |
| 			html += '<div class="pagination-info">';
 | |
| 			html += 'Showing page ' + pagination.current_page + ' of ' + pagination.total_pages;
 | |
| 			html += ' (' + pagination.total_items + ' total trainers)';
 | |
| 			html += '</div>';
 | |
| 			html += '<div class="pagination-controls">';
 | |
| 			
 | |
| 			if (pagination.has_prev) {
 | |
| 				html += '<button class="pagination-btn" data-page="' + (pagination.current_page - 1) + '">← Previous</button>';
 | |
| 			}
 | |
| 			
 | |
| 			var startPage = Math.max(1, pagination.current_page - 2);
 | |
| 			var endPage = Math.min(pagination.total_pages, pagination.current_page + 2);
 | |
| 			
 | |
| 			for (var i = startPage; i <= endPage; i++) {
 | |
| 				var activeClass = (i === pagination.current_page) ? ' active' : '';
 | |
| 				html += '<button class="pagination-btn page-btn' + activeClass + '" data-page="' + i + '">' + i + '</button>';
 | |
| 			}
 | |
| 			
 | |
| 			if (pagination.has_next) {
 | |
| 				html += '<button class="pagination-btn" data-page="' + (pagination.current_page + 1) + '">Next →</button>';
 | |
| 			}
 | |
| 			
 | |
| 			html += '</div>';
 | |
| 			html += '</div>';
 | |
| 			
 | |
| 			return html;
 | |
| 		},
 | |
| 		
 | |
| 		bindTableEvents: function() {
 | |
| 			var self = this;
 | |
| 			
 | |
| 			// Pagination
 | |
| 			$('.pagination-btn').on('click', function() {
 | |
| 				var page = parseInt($(this).data('page'));
 | |
| 				if (page && page !== self.currentPage) {
 | |
| 					self.currentPage = page;
 | |
| 					self.loadTrainerTable();
 | |
| 				}
 | |
| 			});
 | |
| 			
 | |
| 			// Select all checkbox
 | |
| 			$('#select-all-trainers').on('change', function() {
 | |
| 				$('.trainer-checkbox').prop('checked', $(this).prop('checked'));
 | |
| 			});
 | |
| 		},
 | |
| 		
 | |
| 		bulkUpdateStatus: function() {
 | |
| 			var selectedIds = [];
 | |
| 			$('.trainer-checkbox:checked').each(function() {
 | |
| 				selectedIds.push($(this).val());
 | |
| 			});
 | |
| 			
 | |
| 			var newStatus = $('#bulk-status-update').val();
 | |
| 			
 | |
| 			if (selectedIds.length === 0) {
 | |
| 				alert('Please select at least one trainer.');
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			if (!newStatus) {
 | |
| 				alert('Please select a status to update to.');
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			if (!confirm('Are you sure you want to update the status of ' + selectedIds.length + ' trainer(s)?')) {
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			var data = {
 | |
| 				action: 'hvac_bulk_update_trainer_status',
 | |
| 				nonce: '<?php echo wp_create_nonce("hvac_master_dashboard_nonce"); ?>',
 | |
| 				user_ids: selectedIds,
 | |
| 				status: newStatus
 | |
| 			};
 | |
| 			
 | |
| 			$.post(ajaxurl, data, function(response) {
 | |
| 				if (response.success) {
 | |
| 					$('#bulk-update-message').text(response.data.message).show().delay(5000).fadeOut();
 | |
| 					trainerTable.loadTrainerTable();
 | |
| 				} else {
 | |
| 					alert('Error: ' + response.data.message);
 | |
| 				}
 | |
| 			});
 | |
| 		}
 | |
| 	};
 | |
| 	
 | |
| 	// Initialize trainer table
 | |
| 	trainerTable.init();
 | |
| 	
 | |
| 	// Events table
 | |
| 	var eventsTable = {
 | |
| 		currentPage: 1,
 | |
| 		perPage: 10,
 | |
| 		orderBy: 'date',
 | |
| 		order: 'DESC',
 | |
| 		
 | |
| 		init: function() {
 | |
| 			this.bindEvents();
 | |
| 			this.loadEventsTable();
 | |
| 		},
 | |
| 		
 | |
| 		bindEvents: function() {
 | |
| 			var self = this;
 | |
| 			
 | |
| 			// Filter controls
 | |
| 			$('#apply-events-filters').on('click', function() {
 | |
| 				self.currentPage = 1;
 | |
| 				self.loadEventsTable();
 | |
| 			});
 | |
| 			
 | |
| 			$('#reset-events-filters').on('click', function() {
 | |
| 				$('#events-status-filter').val('all');
 | |
| 				$('#events-trainer-filter').val('');
 | |
| 				$('#events-search').val('');
 | |
| 				$('#events-date-from').val('');
 | |
| 				$('#events-date-to').val('');
 | |
| 				self.currentPage = 1;
 | |
| 				self.loadEventsTable();
 | |
| 			});
 | |
| 			
 | |
| 			// Pagination will be bound dynamically when table loads
 | |
| 		},
 | |
| 		
 | |
| 		loadEventsTable: function() {
 | |
| 			var self = this;
 | |
| 			var container = $('#events-table-container');
 | |
| 			
 | |
| 			container.html('<div class="loading-placeholder">Loading events...</div>');
 | |
| 			
 | |
| 			var data = {
 | |
| 				action: 'hvac_master_dashboard_events',
 | |
| 				nonce: '<?php echo wp_create_nonce("hvac_master_dashboard_nonce"); ?>',
 | |
| 				status: $('#events-status-filter').val(),
 | |
| 				trainer_id: $('#events-trainer-filter').val(),
 | |
| 				search: $('#events-search').val(),
 | |
| 				date_from: $('#events-date-from').val(),
 | |
| 				date_to: $('#events-date-to').val(),
 | |
| 				page: self.currentPage,
 | |
| 				per_page: self.perPage,
 | |
| 				orderby: self.orderBy,
 | |
| 				order: self.order
 | |
| 			};
 | |
| 			
 | |
| 			$.post(ajaxurl, data, function(response) {
 | |
| 				if (response.success) {
 | |
| 					self.renderEventsTable(response.data);
 | |
| 				} else {
 | |
| 					container.html('<div class="error-message">Error loading events table.</div>');
 | |
| 				}
 | |
| 			});
 | |
| 		},
 | |
| 		
 | |
| 		renderEventsTable: function(data) {
 | |
| 			var container = $('#events-table-container');
 | |
| 			var html = '';
 | |
| 			
 | |
| 			if (data.events && data.events.length > 0) {
 | |
| 				html += '<table class="events-table">';
 | |
| 				html += '<thead><tr>';
 | |
| 				html += '<th>Event Name</th>';
 | |
| 				html += '<th>Trainer</th>';
 | |
| 				html += '<th>Status</th>';
 | |
| 				html += '<th>Date</th>';
 | |
| 				html += '<th>Attendees</th>';
 | |
| 				html += '<th>Revenue</th>';
 | |
| 				html += '<th>Actions</th>';
 | |
| 				html += '</tr></thead>';
 | |
| 				html += '<tbody>';
 | |
| 				
 | |
| 				data.events.forEach(function(event) {
 | |
| 					html += '<tr>';
 | |
| 					html += '<td><a href="' + event.link + '" target="_blank">' + event.name + '</a></td>';
 | |
| 					html += '<td>' + event.trainer_name + '</td>';
 | |
| 					html += '<td><span class="status-badge status-' + event.status + '">' + event.status + '</span></td>';
 | |
| 					html += '<td>' + new Date(event.start_date_ts * 1000).toLocaleDateString() + '</td>';
 | |
| 					html += '<td class="number">' + event.sold + ' / ' + event.capacity + '</td>';
 | |
| 					html += '<td class="revenue">$' + parseFloat(event.revenue).toFixed(2) + '</td>';
 | |
| 					html += '<td><a href="' + event.link + '" class="btn btn-small" target="_blank">View</a></td>';
 | |
| 					html += '</tr>';
 | |
| 				});
 | |
| 				
 | |
| 				html += '</tbody></table>';
 | |
| 				
 | |
| 				// Add pagination
 | |
| 				if (data.pagination.total_pages > 1) {
 | |
| 					html += this.renderPagination(data.pagination);
 | |
| 				}
 | |
| 			} else {
 | |
| 				html = '<div class="no-data-message"><p>No events found matching your criteria.</p></div>';
 | |
| 			}
 | |
| 			
 | |
| 			container.html(html);
 | |
| 			this.bindPaginationEvents();
 | |
| 		},
 | |
| 		
 | |
| 		renderPagination: function(pagination) {
 | |
| 			var html = '<div class="pagination-container">';
 | |
| 			html += '<div class="pagination-info">';
 | |
| 			html += 'Showing page ' + pagination.current_page + ' of ' + pagination.total_pages;
 | |
| 			html += ' (' + pagination.total_items + ' total events)';
 | |
| 			html += '</div>';
 | |
| 			html += '<div class="pagination-controls">';
 | |
| 			
 | |
| 			if (pagination.has_prev) {
 | |
| 				html += '<button class="pagination-btn" data-page="' + (pagination.current_page - 1) + '">← Previous</button>';
 | |
| 			}
 | |
| 			
 | |
| 			// Show page numbers (simplified - just current page context)
 | |
| 			var startPage = Math.max(1, pagination.current_page - 2);
 | |
| 			var endPage = Math.min(pagination.total_pages, pagination.current_page + 2);
 | |
| 			
 | |
| 			for (var i = startPage; i <= endPage; i++) {
 | |
| 				var activeClass = (i === pagination.current_page) ? ' active' : '';
 | |
| 				html += '<button class="pagination-btn page-btn' + activeClass + '" data-page="' + i + '">' + i + '</button>';
 | |
| 			}
 | |
| 			
 | |
| 			if (pagination.has_next) {
 | |
| 				html += '<button class="pagination-btn" data-page="' + (pagination.current_page + 1) + '">Next →</button>';
 | |
| 			}
 | |
| 			
 | |
| 			html += '</div>';
 | |
| 			html += '</div>';
 | |
| 			
 | |
| 			return html;
 | |
| 		},
 | |
| 		
 | |
| 		bindPaginationEvents: function() {
 | |
| 			var self = this;
 | |
| 			$('.pagination-btn').on('click', function() {
 | |
| 				var page = parseInt($(this).data('page'));
 | |
| 				if (page && page !== self.currentPage) {
 | |
| 					self.currentPage = page;
 | |
| 					self.loadEventsTable();
 | |
| 				}
 | |
| 			});
 | |
| 		}
 | |
| 	};
 | |
| 	
 | |
| 	// Initialize events table
 | |
| 	eventsTable.init();
 | |
| 	
 | |
| 	// Navigation smooth scrolling
 | |
| 	$('.nav-link').on('click', function(e) {
 | |
| 		var href = $(this).attr('href');
 | |
| 		if (href.startsWith('#')) {
 | |
| 			e.preventDefault();
 | |
| 			var target = $(href);
 | |
| 			if (target.length) {
 | |
| 				$('html, body').animate({
 | |
| 					scrollTop: target.offset().top - 100
 | |
| 				}, 500);
 | |
| 			}
 | |
| 			
 | |
| 			// Update active navigation
 | |
| 			$('.nav-link').removeClass('active');
 | |
| 			$(this).addClass('active');
 | |
| 		}
 | |
| 	});
 | |
| });
 | |
| </script>
 | |
| 
 | |
| <!-- AJAX URL and Security Nonces for JavaScript -->
 | |
| <script>
 | |
| var ajaxurl = '<?php echo admin_url("admin-ajax.php"); ?>';
 | |
| var hvac_ajax = {
 | |
| 	nonce: '<?php echo wp_create_nonce("hvac_ajax_nonce"); ?>',
 | |
| 	url: ajaxurl,
 | |
| 	actions: {
 | |
| 		get_trainer_stats: 'hvac_get_trainer_stats',
 | |
| 		manage_announcement: 'hvac_manage_announcement',
 | |
| 		master_dashboard_trainers: 'hvac_master_dashboard_trainers'
 | |
| 	}
 | |
| };
 | |
| console.log('[HVAC] AJAX nonces initialized for master dashboard');
 | |
| </script>
 | |
| 
 | |
| 	</main><!-- #main -->
 | |
| </div><!-- #primary -->
 | |
| 
 | |
| <?php
 | |
| // Note: get_footer() is called by the main page template
 | |
| ?>
 |