🚨 CRITICAL: Fixed deployment blockers by adding missing core directories: **Community System (CRITICAL)** - includes/community/ - Login_Handler and all community classes - templates/community/ - Community login forms **Certificate System (CRITICAL)** - includes/certificates/ - 8+ certificate classes and handlers - templates/certificates/ - Certificate reports and generation templates **Core Individual Classes (CRITICAL)** - includes/class-hvac-event-summary.php - includes/class-hvac-trainer-profile-manager.php - includes/class-hvac-master-dashboard-data.php - Plus 40+ other individual HVAC classes **Major Feature Systems (HIGH)** - includes/database/ - Training leads database tables - includes/find-trainer/ - Find trainer directory and MapGeo integration - includes/google-sheets/ - Google Sheets integration system - includes/zoho/ - Complete Zoho CRM integration - includes/communication/ - Communication templates system **Template Infrastructure** - templates/attendee/, templates/email-attendees/ - templates/event-summary/, templates/status/ - templates/template-parts/ - Shared template components **Impact:** - 70+ files added covering 10+ missing directories - Resolves ALL deployment blockers and feature breakdowns - Plugin activation should now work correctly - Multi-machine deployment fully supported 🔧 Generated with Claude Code Co-Authored-By: Ben Reed <ben@tealmaker.com>
858 lines
No EOL
32 KiB
PHP
858 lines
No EOL
32 KiB
PHP
<?php
|
|
/**
|
|
* Admin Dashboard for HVAC Community Events
|
|
*
|
|
* @package HVAC_Community_Events
|
|
* @subpackage Admin
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Admin Dashboard class
|
|
*/
|
|
class HVAC_Admin_Dashboard {
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct() {
|
|
add_action('wp_ajax_hvac_refresh_dashboard_metrics', array($this, 'ajax_refresh_metrics'));
|
|
add_action('wp_ajax_hvac_export_metrics', array($this, 'ajax_export_metrics'));
|
|
add_action('wp_ajax_hvac_run_maintenance', array($this, 'ajax_run_maintenance'));
|
|
}
|
|
|
|
/**
|
|
* Render the dashboard page
|
|
*/
|
|
public function render_page() {
|
|
?>
|
|
<div class="wrap hvac-admin-dashboard">
|
|
<h1><?php _e('HVAC Community Events Dashboard', 'hvac-ce'); ?></h1>
|
|
|
|
<?php $this->render_health_check(); ?>
|
|
|
|
<div class="hvac-dashboard-grid">
|
|
<?php $this->render_trainer_metrics(); ?>
|
|
<?php $this->render_event_statistics(); ?>
|
|
<?php $this->render_revenue_statistics(); ?>
|
|
<?php $this->render_maintenance_controls(); ?>
|
|
</div>
|
|
|
|
<div class="hvac-dashboard-actions">
|
|
<button class="button button-primary" id="refresh-metrics">
|
|
<?php _e('Refresh All Metrics', 'hvac-ce'); ?>
|
|
</button>
|
|
<button class="button" id="export-metrics">
|
|
<?php _e('Export Metrics (CSV)', 'hvac-ce'); ?>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render health check section
|
|
*/
|
|
private function render_health_check() {
|
|
$health_status = $this->get_health_status();
|
|
?>
|
|
<div class="hvac-health-check">
|
|
<h2><?php _e('System Health', 'hvac-ce'); ?></h2>
|
|
<div class="health-status <?php echo esc_attr($health_status['overall_status']); ?>">
|
|
<span class="status-indicator"></span>
|
|
<?php _e('Overall Status:', 'hvac-ce'); ?>
|
|
<strong><?php echo esc_html($health_status['status_text']); ?></strong>
|
|
</div>
|
|
|
|
<table class="wp-list-table widefat striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php _e('Component', 'hvac-ce'); ?></th>
|
|
<th><?php _e('Status', 'hvac-ce'); ?></th>
|
|
<th><?php _e('Details', 'hvac-ce'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($health_status['checks'] as $check): ?>
|
|
<tr>
|
|
<td><?php echo esc_html($check['component']); ?></td>
|
|
<td>
|
|
<span class="status-badge status-<?php echo esc_attr($check['status']); ?>">
|
|
<?php echo esc_html($check['status_text']); ?>
|
|
</span>
|
|
</td>
|
|
<td><?php echo esc_html($check['details']); ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render trainer metrics widget
|
|
*/
|
|
private function render_trainer_metrics() {
|
|
$metrics = $this->get_trainer_metrics();
|
|
?>
|
|
<div class="hvac-dashboard-widget">
|
|
<h3><?php _e('Trainer Metrics', 'hvac-ce'); ?></h3>
|
|
<div class="metrics-grid">
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($metrics['total_trainers']); ?></div>
|
|
<div class="metric-label"><?php _e('Total Trainers', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($metrics['new_trainers_week']); ?></div>
|
|
<div class="metric-label"><?php _e('New This Week', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($metrics['total_logins']); ?></div>
|
|
<div class="metric-label"><?php _e('Total Logins', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($metrics['logins_week']); ?></div>
|
|
<div class="metric-label"><?php _e('Logins This Week', 'hvac-ce'); ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render event statistics widget
|
|
*/
|
|
private function render_event_statistics() {
|
|
$stats = $this->get_event_statistics();
|
|
?>
|
|
<div class="hvac-dashboard-widget">
|
|
<h3><?php _e('Event Statistics', 'hvac-ce'); ?></h3>
|
|
<div class="metrics-grid">
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($stats['total_events']); ?></div>
|
|
<div class="metric-label"><?php _e('Total Events', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($stats['past_events']); ?></div>
|
|
<div class="metric-label"><?php _e('Past Events', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($stats['future_events']); ?></div>
|
|
<div class="metric-label"><?php _e('Future Events', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($stats['draft_events']); ?></div>
|
|
<div class="metric-label"><?php _e('Draft Events', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($stats['cancelled_events']); ?></div>
|
|
<div class="metric-label"><?php _e('Cancelled Events', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($stats['total_attendees']); ?></div>
|
|
<div class="metric-label"><?php _e('Total Attendees', 'hvac-ce'); ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render revenue statistics widget
|
|
*/
|
|
private function render_revenue_statistics() {
|
|
$revenue = $this->get_revenue_statistics();
|
|
?>
|
|
<div class="hvac-dashboard-widget">
|
|
<h3><?php _e('Revenue Statistics', 'hvac-ce'); ?></h3>
|
|
<div class="metrics-grid">
|
|
<div class="metric">
|
|
<div class="metric-value">$<?php echo number_format($revenue['total_revenue'], 2); ?></div>
|
|
<div class="metric-label"><?php _e('Total Revenue', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value">$<?php echo number_format($revenue['revenue_week'], 2); ?></div>
|
|
<div class="metric-label"><?php _e('Revenue This Week', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($revenue['total_purchases']); ?></div>
|
|
<div class="metric-label"><?php _e('Total Purchases', 'hvac-ce'); ?></div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value"><?php echo esc_html($revenue['purchases_week']); ?></div>
|
|
<div class="metric-label"><?php _e('Purchases This Week', 'hvac-ce'); ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render maintenance controls
|
|
*/
|
|
private function render_maintenance_controls() {
|
|
?>
|
|
<div class="hvac-dashboard-widget maintenance-controls">
|
|
<h3><?php _e('Maintenance Controls', 'hvac-ce'); ?></h3>
|
|
<div class="maintenance-actions">
|
|
<button class="button" data-action="clear_transients">
|
|
<?php _e('Clear Cache', 'hvac-ce'); ?>
|
|
</button>
|
|
<button class="button" data-action="optimize_tables">
|
|
<?php _e('Optimize Database Tables', 'hvac-ce'); ?>
|
|
</button>
|
|
<button class="button" data-action="regenerate_roles">
|
|
<?php _e('Regenerate User Roles', 'hvac-ce'); ?>
|
|
</button>
|
|
<button class="button" data-action="sync_event_meta">
|
|
<?php _e('Sync Event Metadata', 'hvac-ce'); ?>
|
|
</button>
|
|
</div>
|
|
<div class="maintenance-log" style="display:none;">
|
|
<h4><?php _e('Maintenance Log', 'hvac-ce'); ?></h4>
|
|
<pre id="maintenance-output"></pre>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Get health status
|
|
*/
|
|
private function get_health_status() {
|
|
$checks = array();
|
|
$overall_status = 'healthy';
|
|
|
|
// Check plugin dependencies
|
|
$tec_active = class_exists('Tribe__Events__Main');
|
|
$checks[] = array(
|
|
'component' => 'The Events Calendar',
|
|
'status' => $tec_active ? 'ok' : 'error',
|
|
'status_text' => $tec_active ? __('Active', 'hvac-ce') : __('Inactive', 'hvac-ce'),
|
|
'details' => $tec_active ? __('Plugin is active and functioning', 'hvac-ce') : __('Required plugin is not active', 'hvac-ce')
|
|
);
|
|
|
|
if (!$tec_active) {
|
|
$overall_status = 'critical';
|
|
}
|
|
|
|
// Check Community Events
|
|
$ce_active = class_exists('Tribe__Events__Community__Main');
|
|
$checks[] = array(
|
|
'component' => 'Community Events',
|
|
'status' => $ce_active ? 'ok' : 'warning',
|
|
'status_text' => $ce_active ? __('Active', 'hvac-ce') : __('Inactive', 'hvac-ce'),
|
|
'details' => $ce_active ? __('Plugin is active', 'hvac-ce') : __('Recommended plugin is not active', 'hvac-ce')
|
|
);
|
|
|
|
if (!$ce_active && $overall_status !== 'critical') {
|
|
$overall_status = 'warning';
|
|
}
|
|
|
|
// Check database tables
|
|
global $wpdb;
|
|
$tables_exist = $wpdb->get_var("SHOW TABLES LIKE '{$wpdb->prefix}posts'") === "{$wpdb->prefix}posts";
|
|
$checks[] = array(
|
|
'component' => 'Database Tables',
|
|
'status' => $tables_exist ? 'ok' : 'error',
|
|
'status_text' => $tables_exist ? __('OK', 'hvac-ce') : __('Error', 'hvac-ce'),
|
|
'details' => $tables_exist ? __('All required tables exist', 'hvac-ce') : __('Missing required database tables', 'hvac-ce')
|
|
);
|
|
|
|
// Check file permissions
|
|
$upload_dir = wp_upload_dir();
|
|
$uploads_writable = wp_is_writable($upload_dir['basedir']);
|
|
$checks[] = array(
|
|
'component' => 'File Permissions',
|
|
'status' => $uploads_writable ? 'ok' : 'warning',
|
|
'status_text' => $uploads_writable ? __('OK', 'hvac-ce') : __('Warning', 'hvac-ce'),
|
|
'details' => $uploads_writable ? __('Upload directory is writable', 'hvac-ce') : __('Upload directory is not writable', 'hvac-ce')
|
|
);
|
|
|
|
// Memory limit check
|
|
$memory_limit = wp_convert_hr_to_bytes(ini_get('memory_limit'));
|
|
$recommended_limit = 256 * MB_IN_BYTES;
|
|
$checks[] = array(
|
|
'component' => 'Memory Limit',
|
|
'status' => $memory_limit >= $recommended_limit ? 'ok' : 'warning',
|
|
'status_text' => size_format($memory_limit),
|
|
'details' => $memory_limit >= $recommended_limit
|
|
? __('Memory limit is sufficient', 'hvac-ce')
|
|
: sprintf(__('Recommended: %s or higher', 'hvac-ce'), size_format($recommended_limit))
|
|
);
|
|
|
|
return array(
|
|
'overall_status' => $overall_status,
|
|
'status_text' => $this->get_status_text($overall_status),
|
|
'checks' => $checks
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get status text
|
|
*/
|
|
private function get_status_text($status) {
|
|
switch ($status) {
|
|
case 'healthy':
|
|
return __('All Systems Operational', 'hvac-ce');
|
|
case 'warning':
|
|
return __('Minor Issues Detected', 'hvac-ce');
|
|
case 'critical':
|
|
return __('Critical Issues Found', 'hvac-ce');
|
|
default:
|
|
return __('Unknown Status', 'hvac-ce');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get trainer metrics
|
|
*/
|
|
private function get_trainer_metrics() {
|
|
global $wpdb;
|
|
|
|
// Total trainers
|
|
$total_trainers = count(get_users(array(
|
|
'role' => 'trainer',
|
|
'fields' => 'ID'
|
|
)));
|
|
|
|
// New trainers this week
|
|
$week_ago = date('Y-m-d H:i:s', strtotime('-1 week'));
|
|
$new_trainers_week = count(get_users(array(
|
|
'role' => 'trainer',
|
|
'date_query' => array(
|
|
array(
|
|
'after' => $week_ago,
|
|
'inclusive' => true
|
|
)
|
|
),
|
|
'fields' => 'ID'
|
|
)));
|
|
|
|
// Login statistics (would require custom tracking)
|
|
$total_logins = get_option('hvac_total_logins', 0);
|
|
$logins_week = get_option('hvac_logins_week', 0);
|
|
|
|
return array(
|
|
'total_trainers' => $total_trainers,
|
|
'new_trainers_week' => $new_trainers_week,
|
|
'total_logins' => $total_logins,
|
|
'logins_week' => $logins_week
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get event statistics
|
|
*/
|
|
private function get_event_statistics() {
|
|
global $wpdb;
|
|
|
|
$now = current_time('mysql');
|
|
|
|
// Total events
|
|
$total_events = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(*) FROM {$wpdb->posts} p
|
|
WHERE p.post_type = %s
|
|
AND p.post_status IN ('publish', 'draft', 'private')
|
|
", 'tribe_events'));
|
|
|
|
// Past events
|
|
$past_events = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p
|
|
JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
|
|
WHERE p.post_type = %s
|
|
AND p.post_status = 'publish'
|
|
AND pm.meta_key = '_EventEndDate'
|
|
AND pm.meta_value < %s
|
|
", 'tribe_events', $now));
|
|
|
|
// Future events
|
|
$future_events = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p
|
|
JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
|
|
WHERE p.post_type = %s
|
|
AND p.post_status = 'publish'
|
|
AND pm.meta_key = '_EventStartDate'
|
|
AND pm.meta_value > %s
|
|
", 'tribe_events', $now));
|
|
|
|
// Draft events
|
|
$draft_events = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(*) FROM {$wpdb->posts}
|
|
WHERE post_type = %s
|
|
AND post_status = 'draft'
|
|
", 'tribe_events'));
|
|
|
|
// Cancelled events (assuming there's a meta field for cancelled status)
|
|
$cancelled_events = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p
|
|
JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
|
|
WHERE p.post_type = %s
|
|
AND pm.meta_key = '_event_cancelled'
|
|
AND pm.meta_value = '1'
|
|
", 'tribe_events'));
|
|
|
|
// Total attendees using Event Tickets data
|
|
$total_attendees = 0;
|
|
|
|
// Check if Event Tickets is active
|
|
if (class_exists('Tribe__Tickets__Main')) {
|
|
// Get all attendee post types from Event Tickets
|
|
$attendee_types = [
|
|
'tribe_rsvp_attendees', // RSVP attendees
|
|
'tribe_tpp_attendees', // PayPal attendees
|
|
'tec_tc_attendee' // Tickets Commerce attendees
|
|
];
|
|
|
|
// Preparing for the SQL query
|
|
$types_placeholder = implode(', ', array_fill(0, count($attendee_types), '%s'));
|
|
$query_args = $attendee_types;
|
|
|
|
// Add status condition - Public order statuses
|
|
// (based on Tribe__Tickets__Attendee_Repository class)
|
|
$public_order_statuses = [
|
|
'yes', // RSVP
|
|
'completed', // PayPal Legacy
|
|
'wc-completed', // WooCommerce
|
|
'publish', // Easy Digital Downloads, Legacy
|
|
'complete', // Easy Digital Downloads
|
|
];
|
|
|
|
// Count attendees with proper status
|
|
foreach ($attendee_types as $post_type) {
|
|
$count = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(*) FROM {$wpdb->posts}
|
|
WHERE post_type = %s
|
|
AND post_status = 'publish'
|
|
", $post_type));
|
|
|
|
$total_attendees += (int)$count;
|
|
}
|
|
|
|
// If WooCommerce Tickets is active, count WooCommerce ticket attendees too
|
|
if (class_exists('Tribe__Tickets_Plus__Commerce__WooCommerce__Main')) {
|
|
$wc_attendees = $wpdb->get_var("
|
|
SELECT COUNT(*) FROM {$wpdb->postmeta}
|
|
WHERE meta_key = '_tribe_wooticket_attendance'
|
|
");
|
|
$total_attendees += (int)$wc_attendees;
|
|
}
|
|
}
|
|
|
|
return array(
|
|
'total_events' => $total_events,
|
|
'past_events' => $past_events,
|
|
'future_events' => $future_events,
|
|
'draft_events' => $draft_events,
|
|
'cancelled_events' => $cancelled_events,
|
|
'total_attendees' => $total_attendees
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get revenue statistics
|
|
*/
|
|
private function get_revenue_statistics() {
|
|
global $wpdb;
|
|
|
|
$week_ago = date('Y-m-d H:i:s', strtotime('-1 week'));
|
|
|
|
$total_revenue = 0;
|
|
$revenue_week = 0;
|
|
$total_purchases = 0;
|
|
$purchases_week = 0;
|
|
|
|
// If using Event Tickets Plus with WooCommerce
|
|
if (class_exists('Tribe__Tickets_Plus__Commerce__WooCommerce__Main')) {
|
|
// Gather data from WooCommerce orders that contain tickets
|
|
// First, find all orders with ticket items
|
|
$ticket_product_ids = $wpdb->get_col("
|
|
SELECT ID FROM {$wpdb->posts}
|
|
WHERE post_type = 'tribe_wooticket'
|
|
");
|
|
|
|
if (!empty($ticket_product_ids)) {
|
|
$ticket_product_ids_str = implode(',', array_map('intval', $ticket_product_ids));
|
|
|
|
// Find orders that contain ticket products
|
|
$ticket_order_ids = $wpdb->get_col("
|
|
SELECT order_id
|
|
FROM {$wpdb->prefix}woocommerce_order_items oi
|
|
JOIN {$wpdb->prefix}woocommerce_order_itemmeta oim ON oi.order_item_id = oim.order_item_id
|
|
WHERE oim.meta_key = '_product_id'
|
|
AND oim.meta_value IN ({$ticket_product_ids_str})
|
|
");
|
|
|
|
if (!empty($ticket_order_ids)) {
|
|
$ticket_order_ids_str = implode(',', array_map('intval', $ticket_order_ids));
|
|
|
|
// Total revenue from these orders
|
|
$total_revenue = $wpdb->get_var("
|
|
SELECT SUM(meta.meta_value)
|
|
FROM {$wpdb->postmeta} meta
|
|
JOIN {$wpdb->posts} posts ON meta.post_id = posts.ID
|
|
WHERE meta.meta_key = '_order_total'
|
|
AND posts.ID IN ({$ticket_order_ids_str})
|
|
AND posts.post_status IN ('wc-completed', 'wc-processing')
|
|
");
|
|
|
|
// Revenue this week
|
|
$revenue_week = $wpdb->get_var($wpdb->prepare("
|
|
SELECT SUM(meta.meta_value)
|
|
FROM {$wpdb->postmeta} meta
|
|
JOIN {$wpdb->posts} posts ON meta.post_id = posts.ID
|
|
WHERE meta.meta_key = '_order_total'
|
|
AND posts.ID IN ({$ticket_order_ids_str})
|
|
AND posts.post_status IN ('wc-completed', 'wc-processing')
|
|
AND posts.post_date >= %s
|
|
", $week_ago));
|
|
|
|
// Total purchases (count of orders)
|
|
$total_purchases = count($ticket_order_ids);
|
|
|
|
// Purchases this week
|
|
$purchases_week = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(*)
|
|
FROM {$wpdb->posts}
|
|
WHERE ID IN ({$ticket_order_ids_str})
|
|
AND post_status IN ('wc-completed', 'wc-processing')
|
|
AND post_date >= %s
|
|
", $week_ago));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for Tickets Commerce data (modern Event Tickets)
|
|
if (class_exists('TEC\\Tickets\\Commerce\\Order')) {
|
|
// Get orders through Tickets Commerce
|
|
$tc_order_post_type = \TEC\Tickets\Commerce\Order::POSTTYPE;
|
|
|
|
$tc_completed_statuses = [
|
|
'completed', 'pfc-completed', 'tpay-completed', 'paid'
|
|
];
|
|
|
|
$placeholders = implode(', ', array_fill(0, count($tc_completed_statuses), '%s'));
|
|
$query_args = $tc_completed_statuses;
|
|
|
|
// Calculate total revenue
|
|
$tc_total_revenue = $wpdb->get_var($wpdb->prepare("
|
|
SELECT SUM(meta.meta_value)
|
|
FROM {$wpdb->postmeta} meta
|
|
JOIN {$wpdb->posts} posts ON meta.post_id = posts.ID
|
|
WHERE meta.meta_key = '_tec_tc_order_total'
|
|
AND posts.post_type = %s
|
|
AND posts.post_status IN ($placeholders)
|
|
", array_merge([$tc_order_post_type], $query_args)));
|
|
|
|
// Calculate this week's revenue
|
|
$query_args[] = $week_ago;
|
|
$tc_revenue_week = $wpdb->get_var($wpdb->prepare("
|
|
SELECT SUM(meta.meta_value)
|
|
FROM {$wpdb->postmeta} meta
|
|
JOIN {$wpdb->posts} posts ON meta.post_id = posts.ID
|
|
WHERE meta.meta_key = '_tec_tc_order_total'
|
|
AND posts.post_type = %s
|
|
AND posts.post_status IN ($placeholders)
|
|
AND posts.post_date >= %s
|
|
", array_merge([$tc_order_post_type], $query_args)));
|
|
|
|
// Count total purchases
|
|
$tc_total_purchases = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(*)
|
|
FROM {$wpdb->posts}
|
|
WHERE post_type = %s
|
|
AND post_status IN ($placeholders)
|
|
", array_merge([$tc_order_post_type], $tc_completed_statuses)));
|
|
|
|
// Count purchases this week
|
|
$tc_purchases_week = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(*)
|
|
FROM {$wpdb->posts}
|
|
WHERE post_type = %s
|
|
AND post_status IN ($placeholders)
|
|
AND post_date >= %s
|
|
", array_merge([$tc_order_post_type], $tc_completed_statuses, [$week_ago])));
|
|
|
|
// Add TC values to totals
|
|
$total_revenue += (float)$tc_total_revenue;
|
|
$revenue_week += (float)$tc_revenue_week;
|
|
$total_purchases += (int)$tc_total_purchases;
|
|
$purchases_week += (int)$tc_purchases_week;
|
|
}
|
|
|
|
// Tribe Commerce PayPal (legacy from Event Tickets)
|
|
if (class_exists('Tribe__Tickets__Commerce__PayPal__Main')) {
|
|
// PayPal orders are stored as posts with meta data
|
|
$pp_completed_status = 'completed';
|
|
|
|
// Calculate total revenue
|
|
$pp_total_revenue = $wpdb->get_var($wpdb->prepare("
|
|
SELECT SUM(meta.meta_value)
|
|
FROM {$wpdb->postmeta} meta
|
|
JOIN {$wpdb->postmeta} status ON status.post_id = meta.post_id
|
|
WHERE meta.meta_key = '_tribe_tpp_gross'
|
|
AND status.meta_key = '_tribe_tpp_status'
|
|
AND status.meta_value = %s
|
|
", $pp_completed_status));
|
|
|
|
// Calculate this week's revenue
|
|
$pp_revenue_week = $wpdb->get_var($wpdb->prepare("
|
|
SELECT SUM(meta.meta_value)
|
|
FROM {$wpdb->postmeta} meta
|
|
JOIN {$wpdb->postmeta} status ON status.post_id = meta.post_id
|
|
JOIN {$wpdb->posts} posts ON meta.post_id = posts.ID
|
|
WHERE meta.meta_key = '_tribe_tpp_gross'
|
|
AND status.meta_key = '_tribe_tpp_status'
|
|
AND status.meta_value = %s
|
|
AND posts.post_date >= %s
|
|
", $pp_completed_status, $week_ago));
|
|
|
|
// Count total PayPal orders
|
|
$pp_total_purchases = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(DISTINCT post_id)
|
|
FROM {$wpdb->postmeta}
|
|
WHERE meta_key = '_tribe_tpp_status'
|
|
AND meta_value = %s
|
|
", $pp_completed_status));
|
|
|
|
// Count purchases this week
|
|
$pp_purchases_week = $wpdb->get_var($wpdb->prepare("
|
|
SELECT COUNT(DISTINCT meta.post_id)
|
|
FROM {$wpdb->postmeta} meta
|
|
JOIN {$wpdb->posts} posts ON meta.post_id = posts.ID
|
|
WHERE meta.meta_key = '_tribe_tpp_status'
|
|
AND meta.meta_value = %s
|
|
AND posts.post_date >= %s
|
|
", $pp_completed_status, $week_ago));
|
|
|
|
// Add PayPal values to totals
|
|
$total_revenue += (float)$pp_total_revenue;
|
|
$revenue_week += (float)$pp_revenue_week;
|
|
$total_purchases += (int)$pp_total_purchases;
|
|
$purchases_week += (int)$pp_purchases_week;
|
|
}
|
|
|
|
return array(
|
|
'total_revenue' => $total_revenue ?: 0,
|
|
'revenue_week' => $revenue_week ?: 0,
|
|
'total_purchases' => $total_purchases ?: 0,
|
|
'purchases_week' => $purchases_week ?: 0
|
|
);
|
|
}
|
|
|
|
/**
|
|
* AJAX handler for refreshing metrics
|
|
*/
|
|
public function ajax_refresh_metrics() {
|
|
check_ajax_referer('hvac_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_die('Unauthorized');
|
|
}
|
|
|
|
$metrics = array(
|
|
'trainer' => $this->get_trainer_metrics(),
|
|
'events' => $this->get_event_statistics(),
|
|
'revenue' => $this->get_revenue_statistics()
|
|
);
|
|
|
|
wp_send_json_success($metrics);
|
|
}
|
|
|
|
/**
|
|
* AJAX handler for exporting metrics
|
|
*/
|
|
public function ajax_export_metrics() {
|
|
check_ajax_referer('hvac_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_die('Unauthorized');
|
|
}
|
|
|
|
$metrics = array(
|
|
'trainer' => $this->get_trainer_metrics(),
|
|
'events' => $this->get_event_statistics(),
|
|
'revenue' => $this->get_revenue_statistics()
|
|
);
|
|
|
|
// Generate CSV
|
|
$csv_data = array();
|
|
|
|
// Headers
|
|
$csv_data[] = array('Metric Category', 'Metric', 'Value');
|
|
|
|
// Trainer metrics
|
|
foreach ($metrics['trainer'] as $key => $value) {
|
|
$csv_data[] = array('Trainer Metrics', $this->humanize_key($key), $value);
|
|
}
|
|
|
|
// Event statistics
|
|
foreach ($metrics['events'] as $key => $value) {
|
|
$csv_data[] = array('Event Statistics', $this->humanize_key($key), $value);
|
|
}
|
|
|
|
// Revenue statistics
|
|
foreach ($metrics['revenue'] as $key => $value) {
|
|
if (strpos($key, 'revenue') !== false) {
|
|
$value = '$' . number_format($value, 2);
|
|
}
|
|
$csv_data[] = array('Revenue Statistics', $this->humanize_key($key), $value);
|
|
}
|
|
|
|
// Add timestamp
|
|
$csv_data[] = array('Export Date', date('Y-m-d H:i:s'), '');
|
|
|
|
wp_send_json_success(array(
|
|
'csv' => $csv_data,
|
|
'filename' => 'hvac-metrics-' . date('Y-m-d') . '.csv'
|
|
));
|
|
}
|
|
|
|
/**
|
|
* AJAX handler for maintenance actions
|
|
*/
|
|
public function ajax_run_maintenance() {
|
|
check_ajax_referer('hvac_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_die('Unauthorized');
|
|
}
|
|
|
|
$action = sanitize_text_field($_POST['action_type']);
|
|
$result = array();
|
|
|
|
switch ($action) {
|
|
case 'clear_transients':
|
|
$result = $this->clear_transients();
|
|
break;
|
|
|
|
case 'optimize_tables':
|
|
$result = $this->optimize_tables();
|
|
break;
|
|
|
|
case 'regenerate_roles':
|
|
$result = $this->regenerate_roles();
|
|
break;
|
|
|
|
case 'sync_event_meta':
|
|
$result = $this->sync_event_metadata();
|
|
break;
|
|
|
|
default:
|
|
$result = array(
|
|
'success' => false,
|
|
'message' => __('Invalid maintenance action', 'hvac-ce')
|
|
);
|
|
}
|
|
|
|
if ($result['success']) {
|
|
wp_send_json_success($result);
|
|
} else {
|
|
wp_send_json_error($result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear transients
|
|
*/
|
|
private function clear_transients() {
|
|
global $wpdb;
|
|
|
|
// Clear all transients
|
|
$query = "DELETE FROM {$wpdb->options}
|
|
WHERE option_name LIKE '_transient_%'
|
|
OR option_name LIKE '_site_transient_%'";
|
|
|
|
$deleted = $wpdb->query($query);
|
|
|
|
// Clear object cache
|
|
wp_cache_flush();
|
|
|
|
return array(
|
|
'success' => true,
|
|
'message' => sprintf(__('Cleared %d transients and flushed object cache', 'hvac-ce'), $deleted)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Optimize database tables
|
|
*/
|
|
private function optimize_tables() {
|
|
global $wpdb;
|
|
|
|
$tables = array(
|
|
$wpdb->posts,
|
|
$wpdb->postmeta,
|
|
$wpdb->users,
|
|
$wpdb->usermeta,
|
|
$wpdb->options
|
|
);
|
|
|
|
$optimized = 0;
|
|
foreach ($tables as $table) {
|
|
if ($wpdb->query("OPTIMIZE TABLE $table")) {
|
|
$optimized++;
|
|
}
|
|
}
|
|
|
|
return array(
|
|
'success' => true,
|
|
'message' => sprintf(__('Optimized %d database tables', 'hvac-ce'), $optimized)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Regenerate user roles
|
|
*/
|
|
private function regenerate_roles() {
|
|
// Re-add custom roles
|
|
$role_manager = new HVAC_Role_Manager();
|
|
$role_manager->add_roles();
|
|
|
|
return array(
|
|
'success' => true,
|
|
'message' => __('User roles regenerated successfully', 'hvac-ce')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Sync event metadata
|
|
*/
|
|
private function sync_event_metadata() {
|
|
global $wpdb;
|
|
|
|
// Example: Ensure all events have required metadata
|
|
$events = get_posts(array(
|
|
'post_type' => 'tribe_events',
|
|
'posts_per_page' => -1,
|
|
'post_status' => array('publish', 'draft', 'private')
|
|
));
|
|
|
|
$synced = 0;
|
|
foreach ($events as $event) {
|
|
// Check for required meta fields
|
|
if (!get_post_meta($event->ID, '_EventStartDate', true)) {
|
|
// Set default start date if missing
|
|
update_post_meta($event->ID, '_EventStartDate', current_time('mysql'));
|
|
$synced++;
|
|
}
|
|
}
|
|
|
|
return array(
|
|
'success' => true,
|
|
'message' => sprintf(__('Synced metadata for %d events', 'hvac-ce'), $synced)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Humanize key for display
|
|
*/
|
|
private function humanize_key($key) {
|
|
$key = str_replace('_', ' ', $key);
|
|
return ucwords($key);
|
|
}
|
|
}
|