diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/assets/css/hvac-dashboard-enhanced.css b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/assets/css/hvac-dashboard-enhanced.css index 609c05e2..4d00447b 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/assets/css/hvac-dashboard-enhanced.css +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/assets/css/hvac-dashboard-enhanced.css @@ -1,451 +1,358 @@ /** - * HVAC Dashboard - Enhanced Styling + * HVAC Dashboard Enhanced Styles * - * Updated dashboard styles using the harmonized framework - * to integrate seamlessly with the Astra theme. - * - * @version 3.0.0 + * Styles for the enhanced dashboard with filters, search, and pagination */ -/* Dashboard page wrapper */ -.hvac-dashboard-page { - 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 { +/* Table Controls Container */ +.hvac-table-controls { display: flex; - gap: var(--hvac-spacing-3); 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 { - background-color: rgba(255, 255, 255, 0.15); - border-color: rgba(255, 255, 255, 0.3); - color: white; - backdrop-filter: blur(10px); +/* Search Box */ +.hvac-search-box { + flex: 1; + min-width: 250px; } -.hvac-dashboard-nav .hvac-btn:hover { - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(-2px); +.hvac-search-box input[type="search"] { + width: 100%; + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 14px; } -/* Stats grid */ -.hvac-dashboard-stats { - margin-bottom: var(--hvac-spacing-8); +.hvac-search-box input[type="search"]:focus { + outline: none; + border-color: #E9AF28; + box-shadow: 0 0 0 2px rgba(233, 175, 40, 0.2); } -.hvac-dashboard-stats h2 { - color: var(--hvac-theme-text-dark); - 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); +/* Date Filters */ +.hvac-date-filters { display: flex; align-items: center; - justify-content: space-between; - flex-wrap: wrap; - gap: var(--hvac-spacing-4); + gap: 10px; } -.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; - gap: var(--hvac-spacing-3); align-items: center; + gap: 8px; } -/* Events table container */ -.hvac-events-table-container { - background-color: var(--hvac-background-white); - border-radius: var(--hvac-radius-xl); - box-shadow: var(--hvac-shadow-lg); - overflow: hidden; - border: 1px solid var(--hvac-border); +.hvac-per-page label { + font-weight: 600; + color: #333; } -.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%; 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 { - background: linear-gradient(135deg, var(--hvac-secondary-light) 0%, var(--hvac-background-gray) 100%); - color: var(--hvac-theme-text-dark); - font-weight: var(--hvac-font-weight-semibold); - 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; + background-color: #f8f9fa; + font-weight: 600; + color: #333; } -.hvac-events-table td { - padding: var(--hvac-spacing-4) var(--hvac-spacing-5); - border-bottom: 1px solid var(--hvac-border-light); - vertical-align: middle; - font-size: var(--hvac-font-size-sm); +/* Sortable Column Headers */ +.hvac-events-table th.sortable { + cursor: pointer; } -.hvac-events-table tbody tr { - transition: background-color var(--hvac-transition-fast); +.hvac-events-table th.sortable a { + 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 { - background-color: var(--hvac-primary-subtle); + background-color: #f8f9fa; } -.hvac-events-table tbody tr:last-child td { - border-bottom: none; +.hvac-events-table tbody tr:nth-child(even) { + background-color: #fafafa; } -/* Event status badges */ -.hvac-event-status { - display: inline-flex; - 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; +/* Column Specific Styles */ +.column-status { + width: 80px; } -.hvac-event-status--published { - background-color: var(--hvac-success-light); - color: var(--hvac-success); - border: 1px solid var(--hvac-success); +.column-title { + min-width: 200px; } -.hvac-event-status--draft { - background-color: var(--hvac-warning-light); - color: var(--hvac-warning); - border: 1px solid var(--hvac-warning); +.column-date { + width: 150px; } -.hvac-event-status--upcoming { - background-color: var(--hvac-info-light); - color: var(--hvac-accent); - 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 { +.column-capacity, +.column-sold { + width: 80px; 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 { - font-size: 4rem; - color: var(--hvac-border-dark); - margin-bottom: var(--hvac-spacing-4); +.column-revenue { + width: 100px; + text-align: right; } -.hvac-empty-state h3 { - color: var(--hvac-theme-text); - margin-bottom: var(--hvac-spacing-3); +.column-actions { + width: 150px; } -.hvac-empty-state p { - color: var(--hvac-theme-text-light); - margin-bottom: var(--hvac-spacing-6); - max-width: 400px; - margin-left: auto; - margin-right: auto; +/* Pagination */ +.tablenav { + margin: 20px 0; + display: flex; + justify-content: space-between; + align-items: center; } -/* Quick actions panel */ -.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 { +.tablenav-pages { display: flex; align-items: center; - padding: var(--hvac-spacing-4); - 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); + gap: 10px; } -.hvac-quick-action-item:hover { - transform: translateY(-2px); - box-shadow: var(--hvac-shadow-md); - text-decoration: none; - color: var(--hvac-primary); +.displaying-num { + color: #666; + font-size: 13px; } -.hvac-quick-action-icon { - margin-right: var(--hvac-spacing-3); - 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; +.pagination-links { + display: flex; align-items: center; - gap: var(--hvac-spacing-2); + gap: 5px; } -.hvac-loading-spinner { - width: 16px; - height: 16px; - border: 2px solid var(--hvac-border); - border-top: 2px solid var(--hvac-primary); - border-radius: 50%; - animation: hvac-spin 1s linear infinite; +.pagination-links .button { + padding: 4px 8px; + border: 1px solid #ddd; + background: white; + color: #333; + text-decoration: none; + border-radius: 3px; + min-width: 30px; + text-align: center; + cursor: pointer; } -@keyframes hvac-spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } +.pagination-links .button:hover:not(.disabled) { + background: #f8f9fa; + border-color: #E9AF28; } -/* Focus styles for better accessibility */ -.hvac-events-table tbody tr:focus-within { - outline: 2px solid var(--hvac-primary); - outline-offset: -2px; +.pagination-links .button.disabled { + opacity: 0.5; + cursor: default; } -/* Dark mode support (if theme supports it) */ -@media (prefers-color-scheme: dark) { - .hvac-dashboard-page { - background-color: #1a202c; +.paging-input { + display: flex; + align-items: center; + 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-events-table-container, - .hvac-quick-actions { - background-color: #2d3748; - border-color: #4a5568; + .hvac-search-box { + min-width: 100%; + } + + .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; } } \ No newline at end of file diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/assets/js/hvac-dashboard-enhanced.js b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/assets/js/hvac-dashboard-enhanced.js new file mode 100644 index 00000000..ba1da608 --- /dev/null +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/assets/js/hvac-dashboard-enhanced.js @@ -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('
Loading events...
'); + + // 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('

' + message + '

'); + } + +})(jQuery); \ No newline at end of file diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-dashboard-data.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-dashboard-data.php index 5e551044..a45d077e 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-dashboard-data.php +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-dashboard-data.php @@ -175,153 +175,179 @@ class HVAC_Dashboard_Data { /** * 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'. - * @return array An array of event data arrays/objects, each containing keys like: id, status, name, link, date, organizer, capacity, sold, revenue. + * @param array $args Query arguments including: + * - '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 - 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) */ - private function get_events_table_data_direct( $filter_status = 'all' ) { + private function get_events_table_data_direct( $args ) { global $wpdb; $events_data = []; $valid_statuses = array( 'publish', 'future', 'draft', 'pending', 'private' ); - // Build status filter for SQL - if ( 'all' === $filter_status || ! in_array( $filter_status, $valid_statuses, true ) ) { + // Build WHERE clauses + $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_values = $valid_statuses; + $where_clauses[] = "p.post_status IN ($status_placeholders)"; + $where_values = array_merge( $where_values, $valid_statuses ); } else { - $status_placeholders = '%s'; - $status_values = array( $filter_status ); + $where_clauses[] = 'p.post_status = %s'; + $where_values[] = $args['status']; } - // Use direct database query to get events (bypassing TEC query modifications) - $sql = "SELECT ID, post_title, post_status, post_date - FROM {$wpdb->posts} - WHERE post_type = %s - AND post_author = %d - AND post_status IN ($status_placeholders) - ORDER BY post_date DESC"; + // Search filter + if ( ! empty( $args['search'] ) ) { + $where_clauses[] = 'p.post_title LIKE %s'; + $where_values[] = '%' . $wpdb->esc_like( $args['search'] ) . '%'; + } - $query_params = array_merge( - array( 'tribe_events', $this->user_id ), - $status_values - ); + // Date range filters + if ( ! empty( $args['date_from'] ) ) { + $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 ) ) { foreach ( $events as $event ) { $event_id = $event->ID; - - // 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 - ) ); + $start_date_ts = $event->event_date ? strtotime( $event->event_date ) : strtotime( $event->post_date ); // Build event data array (matching template expectations) $events_data[] = array( 'id' => $event_id, - 'name' => $event->post_title, // Template expects 'name' + 'name' => $event->post_title, 'status' => $event->post_status, - 'start_date_ts' => $start_date_ts, // Template expects this + 'start_date_ts' => $start_date_ts, 'link' => get_permalink( $event_id ), - 'organizer_id' => $this->user_id, // Template expects this - 'capacity' => 50, // Default capacity - 'sold' => (int) ($sold ?: 0), - 'revenue' => (float) ($revenue ?: 0.00), + 'organizer_id' => $this->user_id, + 'capacity' => (int) $event->capacity, + 'sold' => (int) $event->sold, + 'revenue' => (float) $event->revenue, ); } } - return $events_data; - - if ( ! empty( $events ) ) { - foreach ( $events as $event ) { - $event_id = $event->ID; - $event_id = get_the_ID(); - - // Get Capacity - Sum capacity of all tickets for this event - $total_capacity = 0; - if ( function_exists( 'tribe_get_tickets' ) ) { - $tickets = tribe_get_tickets( $event_id ); - if ( $tickets ) { - 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; + // Calculate pagination data + $total_pages = ceil( $total_items / $args['per_page'] ); + + return array( + 'events' => $events_data, + 'pagination' => array( + 'total_items' => $total_items, + 'total_pages' => $total_pages, + 'current_page' => $args['page'], + 'per_page' => $args['per_page'], + 'has_prev' => $args['page'] > 1, + 'has_next' => $args['page'] < $total_pages + ) + ); } /** diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-dashboard.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-dashboard.php index 28db0ce3..97774275 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-dashboard.php +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-dashboard.php @@ -234,65 +234,172 @@ class HVAC_Dashboard { return; } - // Get status filter - $status = isset($_POST['status']) ? sanitize_key($_POST['status']) : 'all'; + // Get all filters and parameters + $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 require_once HVAC_CE_PLUGIN_DIR . 'includes/class-hvac-dashboard-data.php'; $dashboard_data = new HVAC_Dashboard_Data($user_id); // 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 ob_start(); - if (!empty($events)) : ?> - - - - - - - - - - - - - - + ?> +
StatusEvent NameDateOrganizerCapacitySoldRevenueActions
+ + + + + + + + + + + + + + - - + - - + - - - - + + + - -
+ + Status + + + + + + + + Event Name + + + + + + + + Date + + + + + + Organizer + + Capacity + + + + + + + + Sold + + + + + + + + Revenue + + + + + + Actions
+ $ + $ Edit | - Summary + Summary | + View
- -

No events found.

+ + + No events found. + + + + + + 1) : ?> +
+
+ items + + + + First page + + + + Previous page + + + + + + + + + + + of + + + + + Next page + + + + Last page + + + + + + + +
+
$html, 'count' => count($events), - 'status' => $status + 'pagination' => $pagination, + 'args' => $args )); } @@ -331,17 +439,25 @@ class HVAC_Dashboard { 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( - 'hvac-dashboard-js', - HVAC_CE_PLUGIN_URL . 'assets/js/hvac-dashboard.js', + 'hvac-dashboard-enhanced-js', + HVAC_CE_PLUGIN_URL . 'assets/js/hvac-dashboard-enhanced.js', array('jquery', 'hvac-ux-enhancements-js'), HVAC_CE_VERSION, true ); // 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'), 'nonce' => wp_create_nonce('hvac_dashboard_nonce') )); diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/template-hvac-dashboard.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/template-hvac-dashboard.php index a4f00806..f91186f4 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/template-hvac-dashboard.php +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/template-hvac-dashboard.php @@ -206,6 +206,37 @@ get_header(); // Use theme's header 'bottom' ); ?> + +
+ + + + +
+ + + + +
+ + +
+ + + per page +
+
+ 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']; ?>
@@ -299,6 +344,49 @@ get_header(); // Use theme's header
+ 1) : ?> +
+
+ items + + + + First page + + + + Previous page + + + + + + + + + + + of + + + + + Next page + + + + Last page + + + + + + + +
+
+ +