feat: Improve dashboard UI and interactivity

- Change 'Your Stats' section to row layout for better visual display
- Add dynamic event filtering without page reload
- Create AJAX endpoint for event filtering
- Add loading animation and UI feedback
- Improve mobile responsiveness

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
bengizmo 2025-05-19 18:54:52 -03:00
parent f5ccb9085c
commit 5bcd8a48a8
4 changed files with 310 additions and 31 deletions

View file

@ -24,7 +24,23 @@
margin-bottom: 2em;
}
/* Target column padding specifically within the stats section */
/* Row layout for stats */
.hvac-stats-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: -10px; /* Counteract the padding on columns */
justify-content: space-between;
align-items: stretch;
}
.hvac-stat-col {
flex: 1;
min-width: 160px; /* Ensure minimum width for readability */
padding: 10px;
}
/* Target column padding specifically within the stats section - maintain compatibility */
.hvac-dashboard-stats .ast-row > [class*="ast-col-"] {
padding: 10px; /* Replicates removed inline style */
display: flex; /* Ensure cards fill height if needed */
@ -38,6 +54,7 @@
text-align: center;
width: 100%; /* Make card fill column */
flex-grow: 1; /* Allow card to grow if needed */
height: 100%; /* Ensure equal height */
/* Add box-shadow or other theme-consistent styling if desired */
}
@ -79,6 +96,8 @@
/* Events Table */
.hvac-events-table-wrapper {
overflow-x: auto; /* Add horizontal scroll for smaller screens */
position: relative; /* For loading indicator positioning */
min-height: 100px; /* Ensure space for loading indicator */
}
/* Ensure table uses standard WP/Theme styling */
@ -88,4 +107,36 @@
.events-table .column-actions a {
margin-right: 0.5em;
}
/* Loading indicator */
.hvac-loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
padding: 20px;
z-index: 10;
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* Error message */
.hvac-error {
color: #d63638;
padding: 10px;
border: 1px solid #ffb8bb;
background-color: #ffebe9;
margin: 10px 0;
border-radius: 4px;
}

View file

@ -0,0 +1,111 @@
/**
* HVAC Trainer Dashboard JavaScript
*
* Handles dynamic filtering of events table and other interactive features.
*/
(function($) {
'use strict';
// Initialize the dashboard when DOM is ready
$(document).ready(function() {
initEventFilters();
});
/**
* Initialize event filters to work without page reload
*/
function initEventFilters() {
// Get the events table wrapper element
const $eventsTableWrapper = $('.hvac-events-table-wrapper');
// Add click handler to filter links
$('.hvac-event-filters a').on('click', function(e) {
e.preventDefault(); // Prevent default link behavior (page reload)
// Get the status filter from the link URL
const url = new URL($(this).attr('href'), window.location.origin);
const status = url.searchParams.get('event_status') || 'all';
// 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');
// Show loading indicator
$eventsTableWrapper.append('<div class="hvac-loading">Filtering events...</div>');
// Make AJAX request to get filtered events
$.ajax({
url: hvac_dashboard.ajax_url,
type: 'POST',
data: {
action: 'hvac_filter_events',
status: status,
nonce: hvac_dashboard.nonce
},
success: function(response) {
if (response.success) {
// Replace the table HTML with the filtered results
$eventsTableWrapper.html(response.data.html);
// Update the URL without reloading the page
if (history.pushState) {
const newUrl = status === 'all'
? removeURLParameter(window.location.href, 'event_status')
: addURLParameter(window.location.href, 'event_status', status);
window.history.pushState({ path: newUrl }, '', newUrl);
}
} else {
// Show error message
$eventsTableWrapper.find('.hvac-loading').remove();
$eventsTableWrapper.append('<div class="hvac-error">Error loading events: ' + response.data.message + '</div>');
}
},
error: function() {
// Show error message
$eventsTableWrapper.find('.hvac-loading').remove();
$eventsTableWrapper.append('<div class="hvac-error">Error communicating with server.</div>');
}
});
});
}
/**
* Helper function to add a URL parameter
*/
function addURLParameter(url, key, value) {
const re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
const separator = url.indexOf('?') !== -1 ? "&" : "?";
if (url.match(re)) {
return url.replace(re, '$1' + key + "=" + value + '$2');
} else {
return url + separator + key + "=" + value;
}
}
/**
* Helper function to remove a URL parameter
*/
function removeURLParameter(url, parameter) {
const urlParts = url.split('?');
if (urlParts.length < 2) {
return url;
}
const urlBase = urlParts[0];
const queryString = urlParts[1];
const prefix = encodeURIComponent(parameter) + '=';
const parts = queryString.split(/[&;]/g);
// Reverse iteration to safely remove items
for (let i = parts.length; i-- > 0;) {
if (parts[i].lastIndexOf(prefix, 0) !== -1) {
parts.splice(i, 1);
}
}
const newQueryString = parts.join('&');
return newQueryString.length > 0 ? urlBase + '?' + newQueryString : urlBase;
}
})(jQuery);

View file

@ -21,7 +21,10 @@ class HVAC_Dashboard {
add_action('init', array($this, 'register_shortcode'));
// Use higher priority to run after shortcode processing
add_filter('the_content', array($this, 'render_dashboard_content'), 99);
add_action('wp_enqueue_scripts', array($this, 'enqueue_dashboard_styles'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_dashboard_assets'));
// AJAX handler for events filtering
add_action('wp_ajax_hvac_filter_events', array($this, 'ajax_filter_events'));
}
/**
@ -104,39 +107,39 @@ class HVAC_Dashboard {
<!-- Statistics Section -->
<section class="hvac-dashboard-stats">
<h2>Your Stats</h2>
<div class="hvac-stats-grid">
<div class="hvac-stats-row">
<!-- Total Events -->
<div class="hvac-stat-card">
<div class="hvac-stat-col"><div class="hvac-stat-card">
<h3>Total Events</h3>
<p class="metric-value"><?php echo esc_html($data['total_events']); ?></p>
</div>
</div></div>
<!-- Upcoming Events -->
<div class="hvac-stat-card">
<div class="hvac-stat-col"><div class="hvac-stat-card">
<h3>Upcoming Events</h3>
<p class="metric-value"><?php echo esc_html($data['upcoming_events']); ?></p>
</div>
</div></div>
<!-- Past Events -->
<div class="hvac-stat-card">
<div class="hvac-stat-col"><div class="hvac-stat-card">
<h3>Past Events</h3>
<p class="metric-value"><?php echo esc_html($data['past_events']); ?></p>
</div>
</div></div>
<!-- Total Tickets Sold -->
<div class="hvac-stat-card">
<div class="hvac-stat-col"><div class="hvac-stat-card">
<h3>Tickets Sold</h3>
<p class="metric-value"><?php echo esc_html($data['total_sold']); ?></p>
</div>
</div></div>
<!-- Total Revenue -->
<div class="hvac-stat-card">
<div class="hvac-stat-col"><div class="hvac-stat-card">
<h3>Total Revenue</h3>
<p class="metric-value">$<?php echo esc_html(number_format($data['total_revenue'], 2)); ?></p>
<?php if ($data['revenue_target']) : ?>
<small>Target: $<?php echo esc_html(number_format($data['revenue_target'], 2)); ?></small>
<?php endif; ?>
</div>
</div></div>
</div>
</section>
@ -152,9 +155,9 @@ class HVAC_Dashboard {
$filter_statuses = array('all', 'publish', 'draft', 'pending', 'private');
foreach ($filter_statuses as $status) :
$url = ($status === 'all') ? remove_query_arg('event_status', $dashboard_url) : add_query_arg('event_status', $status, $dashboard_url);
$class = ($status === $data['current_filter']) ? 'hvac-filter-active' : '';
$class = ($status === $data['current_filter']) ? 'hvac-filter hvac-filter-active ast-button-primary' : 'hvac-filter ast-button-secondary';
?>
<a href="<?php echo esc_url($url); ?>" class="hvac-filter <?php echo esc_attr($class); ?>"><?php echo esc_html(ucfirst($status)); ?></a>
<a href="<?php echo esc_url($url); ?>" class="ast-button <?php echo esc_attr($class); ?>" data-status="<?php echo esc_attr($status); ?>"><?php echo esc_html(ucfirst($status)); ?></a>
<?php endforeach; ?>
</div>
@ -215,13 +218,118 @@ class HVAC_Dashboard {
}
/**
* Enqueue dashboard styles
* Handle AJAX request for filtered events table
*/
public function enqueue_dashboard_styles() {
if (!is_page('hvac-dashboard')) {
public function ajax_filter_events() {
// Check nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_dashboard_nonce')) {
wp_send_json_error(array('message' => 'Security check failed.'));
return;
}
// Get current user ID
$user_id = get_current_user_id();
if (!$user_id || !current_user_can('view_hvac_dashboard')) {
wp_send_json_error(array('message' => 'Access denied.'));
return;
}
// Get status filter
$status = isset($_POST['status']) ? sanitize_key($_POST['status']) : 'all';
// 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);
// Build HTML for events table
ob_start();
if (!empty($events)) : ?>
<table class="hvac-events-table">
<thead>
<tr>
<th>Status</th>
<th>Event Name</th>
<th>Date</th>
<th>Organizer</th>
<th>Capacity</th>
<th>Sold</th>
<th>Revenue</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($events as $event) : ?>
<tr>
<td><?php echo esc_html(ucfirst($event['status'])); ?></td>
<td>
<strong><a href="<?php echo esc_url($event['link']); ?>" target="_blank"><?php echo esc_html($event['name']); ?></a></strong>
</td>
<td><?php echo esc_html(date('Y-m-d H:i', $event['start_date_ts'])); ?></td>
<td><?php
if (function_exists('tribe_get_organizer')) {
echo esc_html(tribe_get_organizer($event['organizer_id']));
} else {
echo 'Organizer ID: ' . esc_html($event['organizer_id']);
}
?></td>
<td><?php echo esc_html($event['capacity']); ?></td>
<td><?php echo esc_html($event['sold']); ?></td>
<td>$<?php echo esc_html(number_format($event['revenue'], 2)); ?></td>
<td>
<?php
$edit_url = add_query_arg('event_id', $event['id'], home_url('/manage-event/'));
$summary_url = get_permalink($event['id']);
?>
<a href="<?php echo esc_url($edit_url); ?>">Edit</a> |
<a href="<?php echo esc_url($summary_url); ?>">Summary</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else : ?>
<p>No events found.</p>
<?php endif;
$html = ob_get_clean();
// Send JSON response
wp_send_json_success(array(
'html' => $html,
'count' => count($events),
'status' => $status
));
}
/**
* Enqueue dashboard assets (CSS and JavaScript)
*/
public function enqueue_dashboard_assets() {
// Check if we're on the dashboard page
global $post;
if (!is_a($post, 'WP_Post') || !has_shortcode($post->post_content, 'hvac_trainer_dashboard')) {
return;
}
// Enqueue dashboard JavaScript
wp_enqueue_script(
'hvac-dashboard-js',
HVAC_CE_PLUGIN_URL . 'assets/js/hvac-dashboard.js',
array('jquery'),
HVAC_CE_VERSION,
true
);
// Localize script with AJAX URL and nonce
wp_localize_script('hvac-dashboard-js', 'hvac_dashboard', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('hvac_dashboard_nonce')
));
// Inline CSS for now - can be moved to external file later
$css = '
.hvac-dashboard-wrapper {
@ -274,13 +382,22 @@ class HVAC_Dashboard {
margin-bottom: 40px;
}
.hvac-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
.hvac-stats-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: -10px; /* Counteract the padding on columns */
justify-content: space-between;
align-items: stretch;
margin-top: 20px;
}
.hvac-stat-col {
flex: 1;
min-width: 160px; /* Ensure minimum width for readability */
padding: 10px;
}
.hvac-stat-card {
background: #f8f9fa;
border: 1px solid #e9ecef;

View file

@ -64,10 +64,10 @@ get_header(); // Use theme's header
<!-- Statistics Section -->
<section class="hvac-dashboard-stats">
<h2>Your Stats</h2>
<div class="ast-row"> <!-- Use Astra grid row -->
<div class="hvac-stats-row"> <!-- Use flexbox row layout -->
<!-- Stat Card: Total Events -->
<div class="ast-col-md-6 ast-col-lg-4"> <!-- Adjust columns as needed -->
<div class="hvac-stat-col"> <!-- Equal width flexbox column -->
<div class="hvac-stat-card">
<h3>Total Events</h3>
<p><?php echo esc_html( $total_events ); ?></p>
@ -75,7 +75,7 @@ get_header(); // Use theme's header
</div>
<!-- Stat Card: Upcoming Events -->
<div class="ast-col-md-6 ast-col-lg-4">
<div class="hvac-stat-col">
<div class="hvac-stat-card">
<h3>Upcoming Events</h3>
<p><?php echo esc_html( $upcoming_events ); ?></p>
@ -83,7 +83,7 @@ get_header(); // Use theme's header
</div>
<!-- Stat Card: Past Events -->
<div class="ast-col-md-6 ast-col-lg-4">
<div class="hvac-stat-col">
<div class="hvac-stat-card">
<h3>Past Events</h3>
<p><?php echo esc_html( $past_events ); ?></p>
@ -91,7 +91,7 @@ get_header(); // Use theme's header
</div>
<!-- Stat Card: Total Tickets Sold -->
<div class="ast-col-md-6 ast-col-lg-4">
<div class="hvac-stat-col">
<div class="hvac-stat-card">
<h3>Tickets Sold</h3>
<p><?php echo esc_html( $total_sold ); ?></p>
@ -99,7 +99,7 @@ get_header(); // Use theme's header
</div>
<!-- Stat Card: Total Revenue -->
<div class="ast-col-md-6 ast-col-lg-4">
<div class="hvac-stat-col">
<div class="hvac-stat-card">
<h3>Total Revenue</h3>
<p>$<?php echo esc_html( number_format( $total_revenue, 2 ) ); ?></p>
@ -126,18 +126,18 @@ get_header(); // Use theme's header
<span>Filter: </span>
<?php foreach ($filter_statuses as $status) :
$url = add_query_arg( 'event_status', $status, $dashboard_url );
$class = 'ast-button ast-button-secondary';
$class = 'ast-button ast-button-secondary hvac-filter';
if ($status === $current_filter) {
// Add a class or style for the active filter
// Using primary button style for active state as an example
$class = 'ast-button ast-button-primary';
$class = 'ast-button ast-button-primary hvac-filter hvac-filter-active';
}
// Special case for 'all' filter link
if ($status === 'all') {
$url = remove_query_arg( 'event_status', $dashboard_url );
}
?>
<a href="<?php echo esc_url( $url ); ?>" class="<?php echo esc_attr( $class ); ?>"><?php echo esc_html( ucfirst( $status ) ); ?></a>
<a href="<?php echo esc_url( $url ); ?>" class="<?php echo esc_attr( $class ); ?>" data-status="<?php echo esc_attr( $status ); ?>"><?php echo esc_html( ucfirst( $status ) ); ?></a>
<?php endforeach; ?>
</div>