feat: Add enhanced dashboard features with search, filters, pagination, and sorting

- Add search box for real-time event name filtering with 500ms debounce
- Add date range filters (from/to) for filtering events by start date
- Add pagination with customizable items per page (10, 25, 50, 100)
- Add sortable table columns for status, name, date, capacity, sold, revenue
- Update dashboard data handler to support all new query parameters
- Create new JavaScript file for handling interactive features
- Create new CSS file for enhanced dashboard styling
- Maintain backward compatibility with existing status filters
- Add URL state management for browser navigation
- Add loading indicators and responsive design

The dashboard now provides a comprehensive interface for trainers to efficiently
manage and navigate their events with powerful filtering and sorting capabilities.

Co-Authored-By: Ben Reed <ben@tealmaker.com>
This commit is contained in:
bengizmo 2025-05-30 10:16:45 -06:00
parent 057b0e8212
commit 1bd871cc7b
5 changed files with 926 additions and 530 deletions

View file

@ -1,451 +1,358 @@
/** /**
* HVAC Dashboard - Enhanced Styling * HVAC Dashboard Enhanced Styles
* *
* Updated dashboard styles using the harmonized framework * Styles for the enhanced dashboard with filters, search, and pagination
* to integrate seamlessly with the Astra theme.
*
* @version 3.0.0
*/ */
/* Dashboard page wrapper */ /* Table Controls Container */
.hvac-dashboard-page { .hvac-table-controls {
background-color: var(--hvac-theme-background);
min-height: 70vh;
padding: var(--hvac-spacing-6) 0;
}
/* Main dashboard container */
.hvac-dashboard {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--hvac-spacing-4);
}
/* Dashboard header */
.hvac-dashboard-header {
background: linear-gradient(135deg, var(--hvac-primary) 0%, var(--hvac-primary-dark) 100%);
color: white;
padding: var(--hvac-spacing-8) var(--hvac-spacing-6);
border-radius: var(--hvac-radius-xl);
margin-bottom: var(--hvac-spacing-8);
box-shadow: var(--hvac-shadow-lg);
position: relative;
overflow: hidden;
}
.hvac-dashboard-header::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 100px;
height: 100px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
transform: translate(30px, -30px);
}
.hvac-dashboard-header h1 {
font-size: var(--hvac-font-size-3xl);
font-weight: var(--hvac-font-weight-bold);
margin: 0 0 var(--hvac-spacing-2) 0;
color: white;
}
.hvac-dashboard-header p {
font-size: var(--hvac-font-size-lg);
margin: 0;
opacity: 0.9;
color: white;
}
/* Navigation */
.hvac-dashboard-nav {
display: flex; display: flex;
gap: var(--hvac-spacing-3);
flex-wrap: wrap; flex-wrap: wrap;
margin-top: var(--hvac-spacing-6); gap: 20px;
align-items: center;
margin: 20px 0;
padding: 15px;
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
} }
.hvac-dashboard-nav .hvac-btn { /* Search Box */
background-color: rgba(255, 255, 255, 0.15); .hvac-search-box {
border-color: rgba(255, 255, 255, 0.3); flex: 1;
color: white; min-width: 250px;
backdrop-filter: blur(10px);
} }
.hvac-dashboard-nav .hvac-btn:hover { .hvac-search-box input[type="search"] {
background-color: rgba(255, 255, 255, 0.25); width: 100%;
transform: translateY(-2px); padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
} }
/* Stats grid */ .hvac-search-box input[type="search"]:focus {
.hvac-dashboard-stats { outline: none;
margin-bottom: var(--hvac-spacing-8); border-color: #E9AF28;
box-shadow: 0 0 0 2px rgba(233, 175, 40, 0.2);
} }
.hvac-dashboard-stats h2 { /* Date Filters */
color: var(--hvac-theme-text-dark); .hvac-date-filters {
margin-bottom: var(--hvac-spacing-6);
text-align: center;
}
.hvac-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--hvac-spacing-6);
}
.hvac-stat-card {
background: linear-gradient(135deg, var(--hvac-background-white) 0%, var(--hvac-primary-subtle) 100%);
border: 1px solid var(--hvac-border);
border-radius: var(--hvac-radius-xl);
padding: var(--hvac-spacing-6);
text-align: center;
box-shadow: var(--hvac-shadow-md);
transition: all var(--hvac-transition-normal);
position: relative;
overflow: hidden;
}
.hvac-stat-card:hover {
transform: translateY(-4px);
box-shadow: var(--hvac-shadow-xl);
}
.hvac-stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--hvac-primary), var(--hvac-accent));
}
.hvac-stat-number {
font-size: var(--hvac-font-size-4xl);
font-weight: var(--hvac-font-weight-bold);
color: var(--hvac-primary);
margin-bottom: var(--hvac-spacing-2);
line-height: 1;
}
.hvac-stat-label {
font-size: var(--hvac-font-size-md);
color: var(--hvac-theme-text);
font-weight: var(--hvac-font-weight-medium);
margin: 0;
}
/* Events section */
.hvac-dashboard-events {
margin-bottom: var(--hvac-spacing-8);
}
.hvac-dashboard-events h2 {
color: var(--hvac-theme-text-dark);
margin-bottom: var(--hvac-spacing-6);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; gap: 10px;
flex-wrap: wrap;
gap: var(--hvac-spacing-4);
} }
.hvac-events-header-actions { .hvac-date-filters label {
font-weight: 600;
color: #333;
}
.hvac-date-input {
padding: 6px 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.hvac-date-input:focus {
outline: none;
border-color: #E9AF28;
}
/* Per Page Selector */
.hvac-per-page {
display: flex; display: flex;
gap: var(--hvac-spacing-3);
align-items: center; align-items: center;
gap: 8px;
} }
/* Events table container */ .hvac-per-page label {
.hvac-events-table-container { font-weight: 600;
background-color: var(--hvac-background-white); color: #333;
border-radius: var(--hvac-radius-xl);
box-shadow: var(--hvac-shadow-lg);
overflow: hidden;
border: 1px solid var(--hvac-border);
} }
.hvac-events-table { .hvac-per-page-select {
padding: 6px 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
background: white;
}
.hvac-per-page-select:focus {
outline: none;
border-color: #E9AF28;
}
/* Event Filters (Status Tabs) */
.hvac-event-filters {
margin: 20px 0;
display: flex;
gap: 10px;
align-items: center;
padding: 10px 0;
border-bottom: 2px solid #e9ecef;
}
.hvac-event-filters span {
font-weight: 600;
color: #333;
}
.hvac-filter {
padding: 6px 16px;
border: 1px solid #ddd;
border-radius: 4px;
text-decoration: none;
color: #333;
transition: all 0.3s ease;
background: white;
}
.hvac-filter:hover {
background-color: #f8f9fa;
border-color: #E9AF28;
}
.hvac-filter-active,
.hvac-filter.ast-button-primary {
background-color: #E9AF28 !important;
color: #000 !important;
border-color: #E9AF28 !important;
}
/* Events Table Wrapper */
.hvac-events-table-wrapper {
position: relative;
overflow-x: auto;
margin-top: 20px;
}
.hvac-events-table-wrapper.loading {
opacity: 0.6;
}
.hvac-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(255, 255, 255, 0.95);
padding: 20px 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
z-index: 10;
}
/* Enhanced Table Styles */
.hvac-events-table,
.wp-list-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
margin: 0; margin-top: 20px;
}
.hvac-events-table th,
.hvac-events-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
} }
.hvac-events-table th { .hvac-events-table th {
background: linear-gradient(135deg, var(--hvac-secondary-light) 0%, var(--hvac-background-gray) 100%); background-color: #f8f9fa;
color: var(--hvac-theme-text-dark); font-weight: 600;
font-weight: var(--hvac-font-weight-semibold); color: #333;
text-align: left;
padding: var(--hvac-spacing-4) var(--hvac-spacing-5);
border-bottom: 2px solid var(--hvac-border);
font-size: var(--hvac-font-size-sm);
text-transform: uppercase;
letter-spacing: 0.05em;
} }
.hvac-events-table td { /* Sortable Column Headers */
padding: var(--hvac-spacing-4) var(--hvac-spacing-5); .hvac-events-table th.sortable {
border-bottom: 1px solid var(--hvac-border-light); cursor: pointer;
vertical-align: middle;
font-size: var(--hvac-font-size-sm);
} }
.hvac-events-table tbody tr { .hvac-events-table th.sortable a {
transition: background-color var(--hvac-transition-fast); color: #333;
text-decoration: none;
display: flex;
align-items: center;
justify-content: space-between;
} }
.hvac-events-table th.sortable:hover {
background-color: #e9ecef;
}
/* Sorting Indicators */
.sorting-indicators {
display: inline-flex;
flex-direction: column;
margin-left: 5px;
}
.sorting-indicator {
width: 0;
height: 0;
border-style: solid;
margin: 1px 0;
}
.sorting-indicator.asc {
border-width: 0 4px 6px 4px;
border-color: transparent transparent #ccc transparent;
}
.sorting-indicator.desc {
border-width: 6px 4px 0 4px;
border-color: #ccc transparent transparent transparent;
}
.sorted.asc .sorting-indicator.asc,
.sorted.desc .sorting-indicator.desc {
border-bottom-color: #E9AF28;
border-top-color: #E9AF28;
}
/* Table Rows */
.hvac-events-table tbody tr:hover { .hvac-events-table tbody tr:hover {
background-color: var(--hvac-primary-subtle); background-color: #f8f9fa;
} }
.hvac-events-table tbody tr:last-child td { .hvac-events-table tbody tr:nth-child(even) {
border-bottom: none; background-color: #fafafa;
} }
/* Event status badges */ /* Column Specific Styles */
.hvac-event-status { .column-status {
display: inline-flex; width: 80px;
align-items: center;
padding: var(--hvac-spacing-1) var(--hvac-spacing-3);
border-radius: var(--hvac-radius-lg);
font-size: var(--hvac-font-size-xs);
font-weight: var(--hvac-font-weight-medium);
text-transform: uppercase;
letter-spacing: 0.05em;
} }
.hvac-event-status--published { .column-title {
background-color: var(--hvac-success-light); min-width: 200px;
color: var(--hvac-success);
border: 1px solid var(--hvac-success);
} }
.hvac-event-status--draft { .column-date {
background-color: var(--hvac-warning-light); width: 150px;
color: var(--hvac-warning);
border: 1px solid var(--hvac-warning);
} }
.hvac-event-status--upcoming { .column-capacity,
background-color: var(--hvac-info-light); .column-sold {
color: var(--hvac-accent); width: 80px;
border: 1px solid var(--hvac-accent);
}
/* Event actions */
.hvac-event-actions {
display: flex;
gap: var(--hvac-spacing-2);
align-items: center;
}
.hvac-event-actions .hvac-btn {
padding: var(--hvac-spacing-1) var(--hvac-spacing-3);
font-size: var(--hvac-font-size-xs);
min-height: 32px;
}
/* Empty state */
.hvac-empty-state {
text-align: center; text-align: center;
padding: var(--hvac-spacing-12) var(--hvac-spacing-6);
background-color: var(--hvac-background-white);
border-radius: var(--hvac-radius-xl);
border: 2px dashed var(--hvac-border);
margin: var(--hvac-spacing-6) 0;
} }
.hvac-empty-state-icon { .column-revenue {
font-size: 4rem; width: 100px;
color: var(--hvac-border-dark); text-align: right;
margin-bottom: var(--hvac-spacing-4);
} }
.hvac-empty-state h3 { .column-actions {
color: var(--hvac-theme-text); width: 150px;
margin-bottom: var(--hvac-spacing-3);
} }
.hvac-empty-state p { /* Pagination */
color: var(--hvac-theme-text-light); .tablenav {
margin-bottom: var(--hvac-spacing-6); margin: 20px 0;
max-width: 400px; display: flex;
margin-left: auto; justify-content: space-between;
margin-right: auto; align-items: center;
} }
/* Quick actions panel */ .tablenav-pages {
.hvac-quick-actions {
background: linear-gradient(135deg, var(--hvac-background-white) 0%, var(--hvac-accent-light) 100%);
border: 1px solid var(--hvac-border);
border-radius: var(--hvac-radius-xl);
padding: var(--hvac-spacing-6);
box-shadow: var(--hvac-shadow-md);
}
.hvac-quick-actions h3 {
margin-top: 0;
margin-bottom: var(--hvac-spacing-4);
color: var(--hvac-theme-text-dark);
}
.hvac-quick-actions-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--hvac-spacing-4);
}
.hvac-quick-action-item {
display: flex; display: flex;
align-items: center; align-items: center;
padding: var(--hvac-spacing-4); gap: 10px;
background-color: var(--hvac-background-white);
border: 1px solid var(--hvac-border);
border-radius: var(--hvac-radius-lg);
text-decoration: none;
color: var(--hvac-theme-text);
transition: all var(--hvac-transition-fast);
} }
.hvac-quick-action-item:hover { .displaying-num {
transform: translateY(-2px); color: #666;
box-shadow: var(--hvac-shadow-md); font-size: 13px;
text-decoration: none;
color: var(--hvac-primary);
} }
.hvac-quick-action-icon { .pagination-links {
margin-right: var(--hvac-spacing-3); display: flex;
font-size: var(--hvac-font-size-xl);
color: var(--hvac-primary);
}
.hvac-quick-action-text {
font-weight: var(--hvac-font-weight-medium);
}
/* Responsive design */
@media (max-width: 768px) {
.hvac-dashboard {
padding: 0 var(--hvac-spacing-3);
}
.hvac-dashboard-header {
padding: var(--hvac-spacing-6) var(--hvac-spacing-4);
text-align: center;
}
.hvac-dashboard-header h1 {
font-size: var(--hvac-font-size-2xl);
}
.hvac-dashboard-nav {
justify-content: center;
}
.hvac-stats-grid {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--hvac-spacing-4);
}
.hvac-stat-card {
padding: var(--hvac-spacing-4);
}
.hvac-stat-number {
font-size: var(--hvac-font-size-3xl);
}
.hvac-events-table-container {
overflow-x: auto;
}
.hvac-events-table th,
.hvac-events-table td {
padding: var(--hvac-spacing-2) var(--hvac-spacing-3);
font-size: var(--hvac-font-size-xs);
}
.hvac-quick-actions-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.hvac-dashboard-header {
padding: var(--hvac-spacing-4);
}
.hvac-dashboard-header h1 {
font-size: var(--hvac-font-size-xl);
}
.hvac-dashboard-nav .hvac-btn {
width: 100%;
margin-bottom: var(--hvac-spacing-2);
}
.hvac-stats-grid {
grid-template-columns: 1fr;
}
.hvac-events-table th,
.hvac-events-table td {
padding: var(--hvac-spacing-1) var(--hvac-spacing-2);
}
.hvac-event-actions {
flex-direction: column;
gap: var(--hvac-spacing-1);
}
.hvac-event-actions .hvac-btn {
width: 100%;
padding: var(--hvac-spacing-2);
font-size: var(--hvac-font-size-xs);
}
}
/* Loading states */
.hvac-loading {
display: inline-flex;
align-items: center; align-items: center;
gap: var(--hvac-spacing-2); gap: 5px;
} }
.hvac-loading-spinner { .pagination-links .button {
width: 16px; padding: 4px 8px;
height: 16px; border: 1px solid #ddd;
border: 2px solid var(--hvac-border); background: white;
border-top: 2px solid var(--hvac-primary); color: #333;
border-radius: 50%; text-decoration: none;
animation: hvac-spin 1s linear infinite; border-radius: 3px;
min-width: 30px;
text-align: center;
cursor: pointer;
} }
@keyframes hvac-spin { .pagination-links .button:hover:not(.disabled) {
0% { transform: rotate(0deg); } background: #f8f9fa;
100% { transform: rotate(360deg); } border-color: #E9AF28;
} }
/* Focus styles for better accessibility */ .pagination-links .button.disabled {
.hvac-events-table tbody tr:focus-within { opacity: 0.5;
outline: 2px solid var(--hvac-primary); cursor: default;
outline-offset: -2px;
} }
/* Dark mode support (if theme supports it) */ .paging-input {
@media (prefers-color-scheme: dark) { display: flex;
.hvac-dashboard-page { align-items: center;
background-color: #1a202c; gap: 5px;
}
.current-page {
width: 50px;
padding: 4px;
text-align: center;
border: 1px solid #ddd;
border-radius: 3px;
}
/* Error Messages */
.hvac-error {
background: #fee;
border: 1px solid #fcc;
color: #c00;
padding: 15px;
margin: 20px 0;
border-radius: 4px;
}
/* Responsive Design */
@media (max-width: 768px) {
.hvac-table-controls {
flex-direction: column;
align-items: stretch;
} }
.hvac-stat-card, .hvac-search-box {
.hvac-events-table-container, min-width: 100%;
.hvac-quick-actions { }
background-color: #2d3748;
border-color: #4a5568; .hvac-date-filters {
flex-wrap: wrap;
}
.hvac-events-table {
font-size: 14px;
}
.hvac-events-table th,
.hvac-events-table td {
padding: 8px 5px;
}
.column-organizer,
.column-capacity {
display: none;
}
.tablenav {
flex-direction: column;
gap: 15px;
} }
} }

View file

@ -0,0 +1,259 @@
/**
* HVAC Trainer Dashboard Enhanced JavaScript
*
* Handles dynamic filtering, sorting, pagination, and search for the events table.
*/
(function($) {
'use strict';
// Store current state
let currentState = {
status: 'all',
search: '',
orderby: 'date',
order: 'DESC',
page: 1,
per_page: 10,
date_from: '',
date_to: ''
};
// Debounce timer for search
let searchTimer = null;
// Initialize the dashboard when DOM is ready
$(document).ready(function() {
initializeEnhancedDashboard();
});
/**
* Initialize all enhanced dashboard features
*/
function initializeEnhancedDashboard() {
// Initialize state from URL params
initializeStateFromURL();
// Set up event handlers
initEventFilters();
initSearchBox();
initDateFilters();
initPerPageSelector();
initSortableColumns();
initPagination();
}
/**
* Initialize state from URL parameters
*/
function initializeStateFromURL() {
const urlParams = new URLSearchParams(window.location.search);
currentState.status = urlParams.get('event_status') || 'all';
currentState.search = urlParams.get('search') || '';
currentState.orderby = urlParams.get('orderby') || 'date';
currentState.order = urlParams.get('order') || 'DESC';
currentState.page = parseInt(urlParams.get('paged')) || 1;
currentState.per_page = parseInt(urlParams.get('per_page')) || 10;
currentState.date_from = urlParams.get('date_from') || '';
currentState.date_to = urlParams.get('date_to') || '';
// Update UI elements to match state
$('#hvac-event-search').val(currentState.search);
$('#hvac-date-from').val(currentState.date_from);
$('#hvac-date-to').val(currentState.date_to);
$('#hvac-per-page').val(currentState.per_page);
}
/**
* Initialize status filter tabs
*/
function initEventFilters() {
$('.hvac-event-filters a').on('click', function(e) {
e.preventDefault();
const status = $(this).data('status');
currentState.status = status;
currentState.page = 1; // Reset to first page when filtering
// Update active class
$('.hvac-event-filters a').removeClass('hvac-filter-active ast-button-primary').addClass('ast-button-secondary');
$(this).addClass('hvac-filter-active ast-button-primary').removeClass('ast-button-secondary');
// Refresh table
refreshEventsTable();
});
}
/**
* Initialize search box
*/
function initSearchBox() {
$('#hvac-event-search').on('keyup', function() {
const searchTerm = $(this).val();
// Clear existing timer
if (searchTimer) {
clearTimeout(searchTimer);
}
// Set new timer to debounce search
searchTimer = setTimeout(function() {
currentState.search = searchTerm;
currentState.page = 1; // Reset to first page when searching
refreshEventsTable();
}, 500); // 500ms delay
});
}
/**
* Initialize date filters
*/
function initDateFilters() {
$('#hvac-date-from, #hvac-date-to').on('change', function() {
currentState.date_from = $('#hvac-date-from').val();
currentState.date_to = $('#hvac-date-to').val();
currentState.page = 1; // Reset to first page when filtering
refreshEventsTable();
});
}
/**
* Initialize per page selector
*/
function initPerPageSelector() {
$('#hvac-per-page').on('change', function() {
currentState.per_page = parseInt($(this).val());
currentState.page = 1; // Reset to first page when changing per page
refreshEventsTable();
});
}
/**
* Initialize sortable column headers
*/
function initSortableColumns() {
$(document).on('click', '.hvac-events-table-wrapper th.sortable a', function(e) {
e.preventDefault();
const orderby = $(this).data('orderby');
const order = $(this).data('order');
currentState.orderby = orderby;
currentState.order = order;
currentState.page = 1; // Reset to first page when sorting
refreshEventsTable();
});
}
/**
* Initialize pagination controls
*/
function initPagination() {
// Pagination links
$(document).on('click', '.pagination-links a:not(.disabled)', function(e) {
e.preventDefault();
const page = parseInt($(this).data('page'));
if (page) {
currentState.page = page;
refreshEventsTable();
}
});
// Page input field
$(document).on('keypress', '.current-page', function(e) {
if (e.which === 13) { // Enter key
e.preventDefault();
const page = parseInt($(this).val());
const totalPages = parseInt($('.total-pages').text());
if (page >= 1 && page <= totalPages) {
currentState.page = page;
refreshEventsTable();
}
}
});
}
/**
* Refresh the events table via AJAX
*/
function refreshEventsTable() {
const $eventsTableWrapper = $('.hvac-events-table-wrapper');
// Show loading indicator
$eventsTableWrapper.addClass('loading').append('<div class="hvac-loading">Loading events...</div>');
// Make AJAX request
$.ajax({
url: hvac_dashboard.ajax_url,
type: 'POST',
data: {
action: 'hvac_filter_events',
status: currentState.status,
search: currentState.search,
orderby: currentState.orderby,
order: currentState.order,
page: currentState.page,
per_page: currentState.per_page,
date_from: currentState.date_from,
date_to: currentState.date_to,
nonce: hvac_dashboard.nonce
},
success: function(response) {
if (response.success) {
// Replace the table HTML
$eventsTableWrapper.html(response.data.html);
// Update URL without reloading the page
updateURL();
// Scroll to top of table
$('html, body').animate({
scrollTop: $('.hvac-dashboard-events').offset().top - 50
}, 300);
} else {
showError('Error loading events: ' + response.data.message);
}
},
error: function() {
showError('Error communicating with server.');
},
complete: function() {
$eventsTableWrapper.removeClass('loading');
}
});
}
/**
* Update URL parameters without reloading
*/
function updateURL() {
if (!history.pushState) return;
const params = new URLSearchParams();
// Only add non-default values to URL
if (currentState.status !== 'all') params.set('event_status', currentState.status);
if (currentState.search) params.set('search', currentState.search);
if (currentState.orderby !== 'date') params.set('orderby', currentState.orderby);
if (currentState.order !== 'DESC') params.set('order', currentState.order);
if (currentState.page > 1) params.set('paged', currentState.page);
if (currentState.per_page !== 10) params.set('per_page', currentState.per_page);
if (currentState.date_from) params.set('date_from', currentState.date_from);
if (currentState.date_to) params.set('date_to', currentState.date_to);
const newUrl = window.location.pathname + (params.toString() ? '?' + params.toString() : '');
window.history.pushState({ path: newUrl }, '', newUrl);
}
/**
* Show error message
*/
function showError(message) {
$('.hvac-events-table-wrapper')
.html('<div class="hvac-error notice notice-error"><p>' + message + '</p></div>');
}
})(jQuery);

View file

@ -175,153 +175,179 @@ class HVAC_Dashboard_Data {
/** /**
* Get the data needed for the events table on the dashboard. * Get the data needed for the events table on the dashboard.
* *
* @param string $filter_status The status to filter events by ('all', 'publish', 'future', 'draft', 'pending', 'private'). Defaults to 'all'. * @param array $args Query arguments including:
* @return array An array of event data arrays/objects, each containing keys like: id, status, name, link, date, organizer, capacity, sold, revenue. * - 'status' (string): Event status filter ('all', 'publish', 'future', 'draft', 'pending', 'private')
* - 'search' (string): Search term for event names
* - 'orderby' (string): Column to sort by ('date', 'name', 'status', 'capacity', 'sold', 'revenue')
* - 'order' (string): Sort order ('ASC' or 'DESC')
* - 'page' (int): Current page number
* - 'per_page' (int): Number of events per page
* - 'date_from' (string): Start date filter (Y-m-d format)
* - 'date_to' (string): End date filter (Y-m-d format)
* @return array Contains 'events' array and 'pagination' data
*/ */
public function get_events_table_data( $filter_status = 'all' ) { public function get_events_table_data( $args = array() ) {
// Default arguments
$defaults = array(
'status' => 'all',
'search' => '',
'orderby' => 'date',
'order' => 'DESC',
'page' => 1,
'per_page' => 10,
'date_from' => '',
'date_to' => ''
);
$args = wp_parse_args( $args, $defaults );
// Use direct database approach since TEC interferes with WP_Query // Use direct database approach since TEC interferes with WP_Query
return $this->get_events_table_data_direct( $filter_status ); return $this->get_events_table_data_direct( $args );
} }
/** /**
* Get events table data using direct database queries (bypassing TEC query interference) * Get events table data using direct database queries (bypassing TEC query interference)
*/ */
private function get_events_table_data_direct( $filter_status = 'all' ) { private function get_events_table_data_direct( $args ) {
global $wpdb; global $wpdb;
$events_data = []; $events_data = [];
$valid_statuses = array( 'publish', 'future', 'draft', 'pending', 'private' ); $valid_statuses = array( 'publish', 'future', 'draft', 'pending', 'private' );
// Build status filter for SQL // Build WHERE clauses
if ( 'all' === $filter_status || ! in_array( $filter_status, $valid_statuses, true ) ) { $where_clauses = array(
'p.post_type = %s',
'p.post_author = %d'
);
$where_values = array( 'tribe_events', $this->user_id );
// Status filter
if ( 'all' === $args['status'] || ! in_array( $args['status'], $valid_statuses, true ) ) {
$status_placeholders = implode( ',', array_fill( 0, count( $valid_statuses ), '%s' ) ); $status_placeholders = implode( ',', array_fill( 0, count( $valid_statuses ), '%s' ) );
$status_values = $valid_statuses; $where_clauses[] = "p.post_status IN ($status_placeholders)";
$where_values = array_merge( $where_values, $valid_statuses );
} else { } else {
$status_placeholders = '%s'; $where_clauses[] = 'p.post_status = %s';
$status_values = array( $filter_status ); $where_values[] = $args['status'];
} }
// Use direct database query to get events (bypassing TEC query modifications) // Search filter
$sql = "SELECT ID, post_title, post_status, post_date if ( ! empty( $args['search'] ) ) {
FROM {$wpdb->posts} $where_clauses[] = 'p.post_title LIKE %s';
WHERE post_type = %s $where_values[] = '%' . $wpdb->esc_like( $args['search'] ) . '%';
AND post_author = %d }
AND post_status IN ($status_placeholders)
ORDER BY post_date DESC";
$query_params = array_merge( // Date range filters
array( 'tribe_events', $this->user_id ), if ( ! empty( $args['date_from'] ) ) {
$status_values $where_clauses[] = "pm_start.meta_value >= %s";
); $where_values[] = $args['date_from'] . ' 00:00:00';
}
$events = $wpdb->get_results( $wpdb->prepare( $sql, $query_params ) ); if ( ! empty( $args['date_to'] ) ) {
$where_clauses[] = "pm_start.meta_value <= %s";
$where_values[] = $args['date_to'] . ' 23:59:59';
}
// Build ORDER BY clause
$order_column = 'p.post_date';
switch ( $args['orderby'] ) {
case 'name':
$order_column = 'p.post_title';
break;
case 'status':
$order_column = 'p.post_status';
break;
case 'date':
$order_column = 'COALESCE(pm_start.meta_value, p.post_date)';
break;
case 'capacity':
$order_column = 'capacity';
break;
case 'sold':
$order_column = 'sold';
break;
case 'revenue':
$order_column = 'revenue';
break;
}
$order_dir = ( strtoupper( $args['order'] ) === 'ASC' ) ? 'ASC' : 'DESC';
// Calculate offset for pagination
$offset = ( $args['page'] - 1 ) * $args['per_page'];
// Build the complete SQL query
$where_sql = implode( ' AND ', $where_clauses );
// First, get total count for pagination
$count_sql = "SELECT COUNT(DISTINCT p.ID)
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} pm_start ON p.ID = pm_start.post_id AND pm_start.meta_key = '_EventStartDate'
WHERE $where_sql";
$total_items = $wpdb->get_var( $wpdb->prepare( $count_sql, $where_values ) );
// Main query with joins for all needed data
$sql = "SELECT
p.ID,
p.post_title,
p.post_status,
p.post_date,
COALESCE(pm_start.meta_value, p.post_date) as event_date,
COALESCE(
(SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'tribe_tpp_attendees' AND post_parent = p.ID),
0
) as sold,
COALESCE(
(SELECT SUM(CAST(pm_price.meta_value AS DECIMAL(10,2)))
FROM {$wpdb->posts} attendees
LEFT JOIN {$wpdb->postmeta} pm_price ON attendees.ID = pm_price.post_id AND pm_price.meta_key = '_tribe_tpp_ticket_price'
WHERE attendees.post_type = 'tribe_tpp_attendees' AND attendees.post_parent = p.ID),
0
) as revenue,
50 as capacity
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} pm_start ON p.ID = pm_start.post_id AND pm_start.meta_key = '_EventStartDate'
WHERE $where_sql
ORDER BY $order_column $order_dir
LIMIT %d OFFSET %d";
$query_values = array_merge( $where_values, array( $args['per_page'], $offset ) );
$events = $wpdb->get_results( $wpdb->prepare( $sql, $query_values ) );
if ( ! empty( $events ) ) { if ( ! empty( $events ) ) {
foreach ( $events as $event ) { foreach ( $events as $event ) {
$event_id = $event->ID; $event_id = $event->ID;
$start_date_ts = $event->event_date ? strtotime( $event->event_date ) : strtotime( $event->post_date );
// Get event start date
$start_date = get_post_meta( $event_id, '_EventStartDate', true );
$start_date_ts = $start_date ? strtotime( $start_date ) : time();
// Get sold count from attendees
$sold = $wpdb->get_var( $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->posts}
WHERE post_type = 'tribe_tpp_attendees'
AND post_parent = %d",
$event_id
) );
// Get revenue from attendees
$revenue = $wpdb->get_var( $wpdb->prepare(
"SELECT SUM(CAST(pm.meta_value AS DECIMAL(10,2)))
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_tribe_tpp_ticket_price'
WHERE p.post_type = 'tribe_tpp_attendees'
AND p.post_parent = %d",
$event_id
) );
// Build event data array (matching template expectations) // Build event data array (matching template expectations)
$events_data[] = array( $events_data[] = array(
'id' => $event_id, 'id' => $event_id,
'name' => $event->post_title, // Template expects 'name' 'name' => $event->post_title,
'status' => $event->post_status, 'status' => $event->post_status,
'start_date_ts' => $start_date_ts, // Template expects this 'start_date_ts' => $start_date_ts,
'link' => get_permalink( $event_id ), 'link' => get_permalink( $event_id ),
'organizer_id' => $this->user_id, // Template expects this 'organizer_id' => $this->user_id,
'capacity' => 50, // Default capacity 'capacity' => (int) $event->capacity,
'sold' => (int) ($sold ?: 0), 'sold' => (int) $event->sold,
'revenue' => (float) ($revenue ?: 0.00), 'revenue' => (float) $event->revenue,
); );
} }
} }
return $events_data; // Calculate pagination data
$total_pages = ceil( $total_items / $args['per_page'] );
if ( ! empty( $events ) ) {
foreach ( $events as $event ) { return array(
$event_id = $event->ID; 'events' => $events_data,
$event_id = get_the_ID(); 'pagination' => array(
'total_items' => $total_items,
// Get Capacity - Sum capacity of all tickets for this event 'total_pages' => $total_pages,
$total_capacity = 0; 'current_page' => $args['page'],
if ( function_exists( 'tribe_get_tickets' ) ) { 'per_page' => $args['per_page'],
$tickets = tribe_get_tickets( $event_id ); 'has_prev' => $args['page'] > 1,
if ( $tickets ) { 'has_next' => $args['page'] < $total_pages
foreach ( $tickets as $ticket ) { )
$capacity = $ticket->capacity(); );
// -1 often means unlimited capacity for Tribe Tickets
if ( $capacity === -1 ) {
$total_capacity = -1; // Mark as unlimited
break; // No need to sum further if one is unlimited
}
if ( is_numeric( $capacity ) ) {
$total_capacity += $capacity;
}
}
}
}
// Get sold and revenue counts, checking for both standard and alternative meta fields
$sold = get_post_meta( $event_id, '_tribe_tickets_sold', true );
if (!is_numeric($sold)) {
$sold = get_post_meta( $event_id, '_tribe_ticket_sold_count', true );
// If still no valid count, calculate from attendees
if (!is_numeric($sold)) {
$sold = $this->count_event_attendees($event_id);
if ($sold > 0) {
update_post_meta($event_id, '_tribe_tickets_sold', $sold);
}
}
}
$revenue = get_post_meta( $event_id, '_tribe_revenue_total', true );
if (!is_numeric($revenue)) {
$revenue = $this->calculate_event_revenue($event_id);
if ($revenue > 0) {
update_post_meta($event_id, '_tribe_revenue_total', $revenue);
}
}
$events_data[] = array(
'id' => $event_id,
'status' => get_post_status( $event_id ),
'name' => get_the_title(),
// Return raw data instead of calling TEC functions here
'link' => get_permalink( $event_id ), // Use standard WP permalink
'start_date_ts' => strtotime( get_post_meta( $event_id, '_EventStartDate', true ) ), // Return timestamp
'organizer_id' => (int) get_post_meta( $event_id, '_EventOrganizerID', true ), // Return organizer ID
'capacity' => ( $total_capacity === -1 ) ? 'Unlimited' : (int) $total_capacity,
'sold' => is_numeric( $sold ) ? (int) $sold : 0,
'revenue' => is_numeric( $revenue ) ? (float) $revenue : 0.0,
);
}
wp_reset_postdata(); // Restore original Post Data
}
return $events_data;
} }
/** /**

View file

@ -234,65 +234,172 @@ class HVAC_Dashboard {
return; return;
} }
// Get status filter // Get all filters and parameters
$status = isset($_POST['status']) ? sanitize_key($_POST['status']) : 'all'; $args = array(
'status' => isset($_POST['status']) ? sanitize_key($_POST['status']) : 'all',
'search' => isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '',
'orderby' => isset($_POST['orderby']) ? sanitize_key($_POST['orderby']) : 'date',
'order' => isset($_POST['order']) ? sanitize_key($_POST['order']) : 'DESC',
'page' => isset($_POST['page']) ? absint($_POST['page']) : 1,
'per_page' => isset($_POST['per_page']) ? absint($_POST['per_page']) : 10,
'date_from' => isset($_POST['date_from']) ? sanitize_text_field($_POST['date_from']) : '',
'date_to' => isset($_POST['date_to']) ? sanitize_text_field($_POST['date_to']) : '',
);
// Include dashboard data class // Include dashboard data class
require_once HVAC_CE_PLUGIN_DIR . 'includes/class-hvac-dashboard-data.php'; require_once HVAC_CE_PLUGIN_DIR . 'includes/class-hvac-dashboard-data.php';
$dashboard_data = new HVAC_Dashboard_Data($user_id); $dashboard_data = new HVAC_Dashboard_Data($user_id);
// Get filtered events data // Get filtered events data
$events = $dashboard_data->get_events_table_data($status); $result = $dashboard_data->get_events_table_data($args);
$events = $result['events'];
$pagination = $result['pagination'];
// Build HTML for events table // Build HTML for events table
ob_start(); ob_start();
if (!empty($events)) : ?> ?>
<table class="hvac-events-table"> <table class="hvac-events-table wp-list-table widefat fixed striped">
<thead> <thead>
<tr> <tr>
<th>Status</th> <th scope="col" class="manage-column column-status sortable <?php echo ($args['orderby'] === 'status') ? 'sorted ' . strtolower($args['order']) : ''; ?>">
<th>Event Name</th> <a href="#" data-orderby="status" data-order="<?php echo ($args['orderby'] === 'status' && $args['order'] === 'ASC') ? 'DESC' : 'ASC'; ?>">
<th>Date</th> <span>Status</span>
<th>Organizer</th> <span class="sorting-indicators">
<th>Capacity</th> <span class="sorting-indicator asc" aria-hidden="true"></span>
<th>Sold</th> <span class="sorting-indicator desc" aria-hidden="true"></span>
<th>Revenue</th> </span>
<th>Actions</th> </a>
</tr> </th>
</thead> <th scope="col" class="manage-column column-title sortable <?php echo ($args['orderby'] === 'name') ? 'sorted ' . strtolower($args['order']) : ''; ?>">
<tbody> <a href="#" data-orderby="name" data-order="<?php echo ($args['orderby'] === 'name' && $args['order'] === 'ASC') ? 'DESC' : 'ASC'; ?>">
<span>Event Name</span>
<span class="sorting-indicators">
<span class="sorting-indicator asc" aria-hidden="true"></span>
<span class="sorting-indicator desc" aria-hidden="true"></span>
</span>
</a>
</th>
<th scope="col" class="manage-column column-date sortable <?php echo ($args['orderby'] === 'date') ? 'sorted ' . strtolower($args['order']) : ''; ?>">
<a href="#" data-orderby="date" data-order="<?php echo ($args['orderby'] === 'date' && $args['order'] === 'ASC') ? 'DESC' : 'ASC'; ?>">
<span>Date</span>
<span class="sorting-indicators">
<span class="sorting-indicator asc" aria-hidden="true"></span>
<span class="sorting-indicator desc" aria-hidden="true"></span>
</span>
</a>
</th>
<th scope="col" class="manage-column column-organizer">Organizer</th>
<th scope="col" class="manage-column column-capacity sortable <?php echo ($args['orderby'] === 'capacity') ? 'sorted ' . strtolower($args['order']) : ''; ?>">
<a href="#" data-orderby="capacity" data-order="<?php echo ($args['orderby'] === 'capacity' && $args['order'] === 'ASC') ? 'DESC' : 'ASC'; ?>">
<span>Capacity</span>
<span class="sorting-indicators">
<span class="sorting-indicator asc" aria-hidden="true"></span>
<span class="sorting-indicator desc" aria-hidden="true"></span>
</span>
</a>
</th>
<th scope="col" class="manage-column column-sold sortable <?php echo ($args['orderby'] === 'sold') ? 'sorted ' . strtolower($args['order']) : ''; ?>">
<a href="#" data-orderby="sold" data-order="<?php echo ($args['orderby'] === 'sold' && $args['order'] === 'ASC') ? 'DESC' : 'ASC'; ?>">
<span>Sold</span>
<span class="sorting-indicators">
<span class="sorting-indicator asc" aria-hidden="true"></span>
<span class="sorting-indicator desc" aria-hidden="true"></span>
</span>
</a>
</th>
<th scope="col" class="manage-column column-revenue sortable <?php echo ($args['orderby'] === 'revenue') ? 'sorted ' . strtolower($args['order']) : ''; ?>">
<a href="#" data-orderby="revenue" data-order="<?php echo ($args['orderby'] === 'revenue' && $args['order'] === 'ASC') ? 'DESC' : 'ASC'; ?>">
<span>Revenue</span>
<span class="sorting-indicators">
<span class="sorting-indicator asc" aria-hidden="true"></span>
<span class="sorting-indicator desc" aria-hidden="true"></span>
</span>
</a>
</th>
<th scope="col" class="manage-column column-actions">Actions</th>
</tr>
</thead>
<tbody id="the-list">
<?php if (!empty($events)) : ?>
<?php foreach ($events as $event) : ?> <?php foreach ($events as $event) : ?>
<tr> <tr>
<td><?php echo esc_html(ucfirst($event['status'])); ?></td> <td class="column-status"><?php echo esc_html(ucfirst($event['status'])); ?></td>
<td> <td class="column-title">
<strong><a href="<?php echo esc_url($event['link']); ?>" target="_blank"><?php echo esc_html($event['name']); ?></a></strong> <strong><a href="<?php echo esc_url($event['link']); ?>" target="_blank"><?php echo esc_html($event['name']); ?></a></strong>
</td> </td>
<td><?php echo esc_html(date('Y-m-d H:i', $event['start_date_ts'])); ?></td> <td class="column-date"><?php echo esc_html(date('Y-m-d H:i', $event['start_date_ts'])); ?></td>
<td><?php <td class="column-organizer"><?php
if (function_exists('tribe_get_organizer')) { if (function_exists('tribe_get_organizer')) {
echo esc_html(tribe_get_organizer($event['organizer_id'])); echo esc_html(tribe_get_organizer($event['organizer_id']));
} else { } else {
echo 'Organizer ID: ' . esc_html($event['organizer_id']); echo 'Organizer ID: ' . esc_html($event['organizer_id']);
} }
?></td> ?></td>
<td><?php echo esc_html($event['capacity']); ?></td> <td class="column-capacity"><?php echo esc_html($event['capacity']); ?></td>
<td><?php echo esc_html($event['sold']); ?></td> <td class="column-sold"><?php echo esc_html($event['sold']); ?></td>
<td>$<?php echo esc_html(number_format($event['revenue'], 2)); ?></td> <td class="column-revenue">$<?php echo esc_html(number_format($event['revenue'], 2)); ?></td>
<td> <td class="column-actions">
<?php <?php
$edit_url = add_query_arg('event_id', $event['id'], home_url('/manage-event/')); $edit_url = add_query_arg('event_id', $event['id'], home_url('/manage-event/'));
$summary_url = get_permalink($event['id']); $summary_url = add_query_arg('event_id', $event['id'], home_url('/event-summary/'));
$view_url = get_permalink($event['id']);
?> ?>
<a href="<?php echo esc_url($edit_url); ?>">Edit</a> | <a href="<?php echo esc_url($edit_url); ?>">Edit</a> |
<a href="<?php echo esc_url($summary_url); ?>">Summary</a> <a href="<?php echo esc_url($summary_url); ?>">Summary</a> |
<a href="<?php echo esc_url($view_url); ?>" target="_blank">View</a>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> <?php else : ?>
</table> <tr>
<?php else : ?> <td colspan="8">No events found.</td>
<p>No events found.</p> </tr>
<?php endif; ?>
</tbody>
</table>
<?php if ($pagination['total_pages'] > 1) : ?>
<div class="tablenav bottom">
<div class="tablenav-pages">
<span class="displaying-num"><?php echo esc_html($pagination['total_items']); ?> items</span>
<span class="pagination-links">
<?php if ($pagination['has_prev']) : ?>
<a class="prev-page button" href="#" data-page="1">
<span class="screen-reader-text">First page</span>
<span aria-hidden="true">«</span>
</a>
<a class="prev-page button" href="#" data-page="<?php echo esc_attr($pagination['current_page'] - 1); ?>">
<span class="screen-reader-text">Previous page</span>
<span aria-hidden="true"></span>
</a>
<?php else : ?>
<span class="tablenav-pages-navspan button disabled" aria-hidden="true">«</span>
<span class="tablenav-pages-navspan button disabled" aria-hidden="true"></span>
<?php endif; ?>
<span class="paging-input">
<label for="current-page-selector" class="screen-reader-text">Current Page</label>
<input class="current-page" id="current-page-selector" type="text" name="paged" value="<?php echo esc_attr($pagination['current_page']); ?>" size="1" aria-describedby="table-paging">
<span class="tablenav-paging-text"> of <span class="total-pages"><?php echo esc_html($pagination['total_pages']); ?></span></span>
</span>
<?php if ($pagination['has_next']) : ?>
<a class="next-page button" href="#" data-page="<?php echo esc_attr($pagination['current_page'] + 1); ?>">
<span class="screen-reader-text">Next page</span>
<span aria-hidden="true"></span>
</a>
<a class="next-page button" href="#" data-page="<?php echo esc_attr($pagination['total_pages']); ?>">
<span class="screen-reader-text">Last page</span>
<span aria-hidden="true">»</span>
</a>
<?php else : ?>
<span class="tablenav-pages-navspan button disabled" aria-hidden="true"></span>
<span class="tablenav-pages-navspan button disabled" aria-hidden="true">»</span>
<?php endif; ?>
</span>
</div>
</div>
<?php endif; <?php endif;
$html = ob_get_clean(); $html = ob_get_clean();
@ -301,7 +408,8 @@ class HVAC_Dashboard {
wp_send_json_success(array( wp_send_json_success(array(
'html' => $html, 'html' => $html,
'count' => count($events), 'count' => count($events),
'status' => $status 'pagination' => $pagination,
'args' => $args
)); ));
} }
@ -331,17 +439,25 @@ class HVAC_Dashboard {
true true
); );
// Enqueue dashboard JavaScript // Enqueue enhanced dashboard CSS
wp_enqueue_style(
'hvac-dashboard-enhanced-css',
HVAC_CE_PLUGIN_URL . 'assets/css/hvac-dashboard-enhanced.css',
array('hvac-ux-enhancements-css'),
HVAC_CE_VERSION
);
// Enqueue enhanced dashboard JavaScript
wp_enqueue_script( wp_enqueue_script(
'hvac-dashboard-js', 'hvac-dashboard-enhanced-js',
HVAC_CE_PLUGIN_URL . 'assets/js/hvac-dashboard.js', HVAC_CE_PLUGIN_URL . 'assets/js/hvac-dashboard-enhanced.js',
array('jquery', 'hvac-ux-enhancements-js'), array('jquery', 'hvac-ux-enhancements-js'),
HVAC_CE_VERSION, HVAC_CE_VERSION,
true true
); );
// Localize script with AJAX URL and nonce // Localize script with AJAX URL and nonce
wp_localize_script('hvac-dashboard-js', 'hvac_dashboard', array( wp_localize_script('hvac-dashboard-enhanced-js', 'hvac_dashboard', array(
'ajax_url' => admin_url('admin-ajax.php'), 'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('hvac_dashboard_nonce') 'nonce' => wp_create_nonce('hvac_dashboard_nonce')
)); ));

View file

@ -206,6 +206,37 @@ get_header(); // Use theme's header
'bottom' 'bottom'
); ?> ); ?>
<!-- Enhanced Filters and Controls -->
<div class="hvac-table-controls">
<!-- Search Box -->
<div class="hvac-search-box">
<?php echo HVAC_Help_System::add_tooltip(
'<input type="search" id="hvac-event-search" placeholder="Search events..." class="regular-text">',
'Search events by name'
); ?>
</div>
<!-- Date Range Filters -->
<div class="hvac-date-filters">
<label for="hvac-date-from">From:</label>
<input type="date" id="hvac-date-from" class="hvac-date-input">
<label for="hvac-date-to">To:</label>
<input type="date" id="hvac-date-to" class="hvac-date-input">
</div>
<!-- Per Page Selector -->
<div class="hvac-per-page">
<label for="hvac-per-page">Show:</label>
<select id="hvac-per-page" class="hvac-per-page-select">
<option value="10" selected>10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
<span>per page</span>
</div>
</div>
<!-- Tab Filters --> <!-- Tab Filters -->
<?php <?php
$dashboard_url = get_permalink(); // Get the current page URL $dashboard_url = get_permalink(); // Get the current page URL
@ -237,7 +268,21 @@ get_header(); // Use theme's header
<!-- Events Table --> <!-- Events Table -->
<?php <?php
// $current_filter is already defined above // $current_filter is already defined above
$events = $dashboard_data->get_events_table_data( $current_filter ); // Get events with new parameters
$args = array(
'status' => $current_filter,
'search' => isset($_GET['search']) ? sanitize_text_field($_GET['search']) : '',
'orderby' => isset($_GET['orderby']) ? sanitize_key($_GET['orderby']) : 'date',
'order' => isset($_GET['order']) ? sanitize_key($_GET['order']) : 'DESC',
'page' => isset($_GET['paged']) ? absint($_GET['paged']) : 1,
'per_page' => isset($_GET['per_page']) ? absint($_GET['per_page']) : 10,
'date_from' => isset($_GET['date_from']) ? sanitize_text_field($_GET['date_from']) : '',
'date_to' => isset($_GET['date_to']) ? sanitize_text_field($_GET['date_to']) : ''
);
$result = $dashboard_data->get_events_table_data( $args );
$events = $result['events'];
$pagination = $result['pagination'];
?> ?>
<div class="hvac-events-table-wrapper"> <div class="hvac-events-table-wrapper">
@ -299,6 +344,49 @@ get_header(); // Use theme's header
</table> </table>
</div> </div>
<?php if ($pagination['total_pages'] > 1) : ?>
<div class="tablenav bottom">
<div class="tablenav-pages">
<span class="displaying-num"><?php echo esc_html($pagination['total_items']); ?> items</span>
<span class="pagination-links">
<?php if ($pagination['has_prev']) : ?>
<a class="first-page button" href="#" data-page="1">
<span class="screen-reader-text">First page</span>
<span aria-hidden="true">«</span>
</a>
<a class="prev-page button" href="#" data-page="<?php echo esc_attr($pagination['current_page'] - 1); ?>">
<span class="screen-reader-text">Previous page</span>
<span aria-hidden="true"></span>
</a>
<?php else : ?>
<span class="tablenav-pages-navspan button disabled" aria-hidden="true">«</span>
<span class="tablenav-pages-navspan button disabled" aria-hidden="true"></span>
<?php endif; ?>
<span class="paging-input">
<label for="current-page-selector" class="screen-reader-text">Current Page</label>
<input class="current-page" id="current-page-selector" type="text" name="paged" value="<?php echo esc_attr($pagination['current_page']); ?>" size="1" aria-describedby="table-paging">
<span class="tablenav-paging-text"> of <span class="total-pages"><?php echo esc_html($pagination['total_pages']); ?></span></span>
</span>
<?php if ($pagination['has_next']) : ?>
<a class="next-page button" href="#" data-page="<?php echo esc_attr($pagination['current_page'] + 1); ?>">
<span class="screen-reader-text">Next page</span>
<span aria-hidden="true"></span>
</a>
<a class="last-page button" href="#" data-page="<?php echo esc_attr($pagination['total_pages']); ?>">
<span class="screen-reader-text">Last page</span>
<span aria-hidden="true">»</span>
</a>
<?php else : ?>
<span class="tablenav-pages-navspan button disabled" aria-hidden="true"></span>
<span class="tablenav-pages-navspan button disabled" aria-hidden="true">»</span>
<?php endif; ?>
</span>
</div>
</div>
<?php endif; ?>
</section> </section>
</main> <!-- #main --> </main> <!-- #main -->