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:
parent
057b0e8212
commit
1bd871cc7b
5 changed files with 926 additions and 530 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -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 -->
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue