feat: Implement Order Summary page functionality

- Enhanced Order Summary template with detailed information display
- Added comprehensive order details including events, tickets, and notes
- Improved Order Summary Data class with additional functionality
- Added access control to ensure only authorized users can view orders
- Created links from Event Summary page to Order Summary page
- Added E2E test for the Order Summary feature
- Created helper script for Order Summary page creation

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
bengizmo 2025-05-20 09:56:28 -03:00
parent 20a5be3e65
commit 11bad93a65
4 changed files with 854 additions and 93 deletions

View file

@ -0,0 +1,148 @@
/**
* @fileoverview E2E Tests for Order Summary page.
* This test suite verifies that trainers can view order details for their events.
*/
import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
import { DashboardPage } from './pages/DashboardPage';
// Test data
const TEST_USER = {
username: process.env.TEST_TRAINER_USERNAME || 'test_trainer',
password: process.env.TEST_TRAINER_PASSWORD || 'Test_password123'
};
test.describe('Order Summary Page @order-summary', () => {
test.beforeEach(async ({ page }) => {
// Login before each test
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login(TEST_USER.username, TEST_USER.password);
// Verify we're logged in by checking if we're on the dashboard
const dashboardPage = new DashboardPage(page);
await expect(page).toHaveURL(/.*hvac-dashboard.*/);
await expect(dashboardPage.welcomeMessage).toBeVisible();
});
test('should navigate from dashboard to event summary to order summary', async ({ page }) => {
// Navigate to dashboard
await page.goto('/hvac-dashboard/');
await expect(page).toHaveURL(/.*hvac-dashboard.*/);
// Find an event with tickets and navigate to its event summary
// This depends on having test events with ticket sales
const eventLinks = await page.$$('a[href*="event-summary"]');
if (eventLinks.length === 0) {
test.skip('No events with ticket sales found');
}
// Click the first event link
await eventLinks[0].click();
await expect(page).toHaveURL(/.*event-summary.*/);
await expect(page.locator('h1')).toContainText('Summary');
// Look for order links in the transactions table
const orderLinks = await page.$$('a[href*="order-summary"]');
if (orderLinks.length === 0) {
test.skip('No orders found for this event');
}
// Click the first order link
await orderLinks[0].click();
await expect(page).toHaveURL(/.*order-summary.*/);
await expect(page.locator('h1')).toContainText('Order Summary');
});
test('should display correct order information', async ({ page }) => {
// Navigate directly to an order summary page
// This depends on having an order ID for testing
// For this test, we'll need to get an order ID from an event first
await page.goto('/hvac-dashboard/');
// Find an event and navigate to its summary
const eventLinks = await page.$$('a[href*="event-summary"]');
if (eventLinks.length === 0) {
test.skip('No events with ticket sales found');
}
await eventLinks[0].click();
await expect(page).toHaveURL(/.*event-summary.*/);
// Look for order links and get the first order's ID
const orderLink = await page.$('a[href*="order-summary"]');
if (!orderLink) {
test.skip('No orders found for this event');
}
const href = await orderLink.getAttribute('href');
const orderId = href.match(/order_id=(\d+)/)[1];
// Navigate to order summary page directly
await page.goto(`/order-summary/?order_id=${orderId}`);
await expect(page).toHaveURL(/.*order-summary.*/);
// Check that order details are displayed correctly
await expect(page.locator('h1')).toContainText('Order Summary');
await expect(page.locator('.hvac-details-table')).toBeVisible();
// Check for order number
const orderNumberCell = await page.locator('.hvac-details-table tr', { has: page.locator('th', { hasText: 'Order Number' }) }).locator('td');
await expect(orderNumberCell).toBeVisible();
// Check for tickets table
await expect(page.locator('.hvac-tickets-table')).toBeVisible();
await expect(page.locator('.hvac-tickets-table th', { hasText: 'Attendee' })).toBeVisible();
await expect(page.locator('.hvac-tickets-table th', { hasText: 'Email' })).toBeVisible();
await expect(page.locator('.hvac-tickets-table th', { hasText: 'Ticket Type' })).toBeVisible();
await expect(page.locator('.hvac-tickets-table th', { hasText: 'Event' })).toBeVisible();
});
test('should redirect to login page when not logged in', async ({ page }) => {
// Logout first
await page.goto('/wp-login.php?action=logout');
await page.click('text=log out');
// Attempt to access order summary page directly
await page.goto('/order-summary/?order_id=1');
// Should redirect to login page
await expect(page.locator('text=Authentication Required')).toBeVisible();
await expect(page.locator('text=Please log in to view the order summary')).toBeVisible();
await expect(page.locator('a', { hasText: 'Log In' })).toBeVisible();
});
test('should provide navigation back to event summary', async ({ page }) => {
// Find an event and navigate to its summary
await page.goto('/hvac-dashboard/');
const eventLinks = await page.$$('a[href*="event-summary"]');
if (eventLinks.length === 0) {
test.skip('No events with ticket sales found');
}
await eventLinks[0].click();
await expect(page).toHaveURL(/.*event-summary.*/);
// Get the event ID from the URL
const url = page.url();
const eventId = url.match(/event_id=(\d+)/)[1];
// Look for order links and navigate to an order
const orderLink = await page.$('a[href*="order-summary"]');
if (!orderLink) {
test.skip('No orders found for this event');
}
await orderLink.click();
await expect(page).toHaveURL(/.*order-summary.*/);
// Check for "Back to Event Summary" link
const backLink = page.locator('a', { hasText: 'Back to Event Summary' });
await expect(backLink).toBeVisible();
// Click the link and verify it goes back to the event summary
await backLink.click();
await expect(page).toHaveURL(new RegExp(`.*event-summary.*event_id=${eventId}`));
});
});

View file

@ -24,6 +24,13 @@ class HVAC_Order_Summary_Data {
*/
private $order_object = null;
/**
* Array of event IDs associated with this order
*
* @var array
*/
private $event_ids = [];
/**
* Constructor.
*
@ -32,6 +39,11 @@ class HVAC_Order_Summary_Data {
public function __construct( $order_id ) {
$this->order_id = absint( $order_id );
$this->order_object = $this->load_order_object( $this->order_id );
// Load associated events
if ($this->is_valid_order()) {
$this->event_ids = $this->get_associated_events();
}
}
/**
@ -48,7 +60,13 @@ class HVAC_Order_Summary_Data {
return $order;
}
}
// Event Tickets RSVP/Tribe order (fallback)
if ( class_exists( 'Tribe__Tickets__RSVP' ) ) {
// Implementation depends on how RSVP orders are stored
// This is a placeholder for potential RSVP orders
}
// Add additional logic for other ticket providers if needed
return null;
}
@ -62,6 +80,66 @@ class HVAC_Order_Summary_Data {
return ! is_null( $this->order_object );
}
/**
* Check if the current user has permission to view this order.
* Users can only view orders for events they created.
*
* @return bool
*/
public function user_can_view_order() {
// User must be logged in
if (!is_user_logged_in()) {
return false;
}
// Order must be valid
if (!$this->is_valid_order()) {
return false;
}
// Admin users can view all orders
if (current_user_can('manage_options')) {
return true;
}
// Get the current user ID
$current_user_id = get_current_user_id();
// Check if the user is the author of any of the events in this order
foreach ($this->event_ids as $event_id) {
$event = get_post($event_id);
if ($event && $event->post_author == $current_user_id) {
return true;
}
}
return false;
}
/**
* Get event IDs associated with this order.
*
* @return array Array of event IDs
*/
public function get_associated_events() {
$event_ids = [];
// Get attendees for this order
$attendees = [];
if (function_exists('tribe_tickets_get_order_attendees')) {
$attendees = tribe_tickets_get_order_attendees($this->order_id);
}
// Extract event IDs from attendees
foreach ($attendees as $attendee) {
if (isset($attendee['event_id'])) {
$event_ids[] = absint($attendee['event_id']);
}
}
return array_unique($event_ids);
}
/**
* Get basic order details.
*
@ -81,6 +159,10 @@ class HVAC_Order_Summary_Data {
'total_price' => null,
'status' => null,
'tickets' => [],
'events' => [],
'billing_address' => null,
'payment_method' => null,
'organization' => null,
];
// WooCommerce order details
@ -89,9 +171,30 @@ class HVAC_Order_Summary_Data {
$details['purchaser_name'] = $this->order_object->get_billing_first_name() . ' ' . $this->order_object->get_billing_last_name();
$details['purchaser_email']= $this->order_object->get_billing_email();
$details['purchase_date'] = $this->order_object->get_date_created() ? $this->order_object->get_date_created()->date( 'Y-m-d H:i:s' ) : null;
$details['total_price'] = $this->order_object->get_total();
$details['total_price'] = $this->order_object->get_formatted_order_total();
$details['status'] = $this->order_object->get_status();
$details['tickets'] = $this->get_order_tickets();
$details['events'] = $this->get_event_details();
// Get billing address
$address_parts = [
$this->order_object->get_billing_address_1(),
$this->order_object->get_billing_address_2(),
$this->order_object->get_billing_city(),
$this->order_object->get_billing_state(),
$this->order_object->get_billing_postcode(),
$this->order_object->get_billing_country()
];
// Filter out empty address parts and join
$address_parts = array_filter($address_parts);
$details['billing_address'] = implode(', ', $address_parts);
// Get payment method
$details['payment_method'] = $this->order_object->get_payment_method_title();
// Get organization (company name)
$details['organization'] = $this->order_object->get_billing_company();
}
// Add additional providers here if needed
@ -108,13 +211,18 @@ class HVAC_Order_Summary_Data {
$tickets = [];
// WooCommerce + Event Tickets Plus
if ( $this->order_object instanceof WC_Order && function_exists( 'Tribe__Tickets_Plus__Commerce__WooCommerce__Main' ) ) {
if ( $this->order_object instanceof WC_Order && function_exists( 'tribe_tickets_get_order_attendees' ) ) {
$order_id = $this->order_id;
$attendees = function_exists( 'tribe_tickets_get_order_attendees' )
? tribe_tickets_get_order_attendees( $order_id )
: [];
$attendees = tribe_tickets_get_order_attendees( $order_id );
foreach ( $attendees as $attendee ) {
$event_id = $attendee['event_id'] ?? null;
$event_title = '';
if ($event_id) {
$event_title = get_the_title($event_id);
}
$tickets[] = [
'attendee_id' => $attendee['attendee_id'] ?? null,
'ticket_type' => $attendee['ticket_name'] ?? null,
@ -123,7 +231,10 @@ class HVAC_Order_Summary_Data {
'attendee_email' => $attendee['holder_email'] ?? null,
'security_code' => $attendee['security_code'] ?? null,
'checked_in' => isset( $attendee['check_in'] ) ? (bool) $attendee['check_in'] : false,
// Add other fields as needed
'event_id' => $event_id,
'event_title' => $event_title,
'price' => $attendee['price'] ?? $attendee['price_paid'] ?? null,
'additional_fields' => $this->get_attendee_additional_fields($attendee),
];
}
}
@ -132,4 +243,101 @@ class HVAC_Order_Summary_Data {
return $tickets;
}
/**
* Get details of events associated with this order.
*
* @return array
*/
public function get_event_details() {
$events = [];
foreach ($this->event_ids as $event_id) {
$event = get_post($event_id);
if (!$event) {
continue;
}
$event_data = [
'id' => $event_id,
'title' => $event->post_title,
'permalink' => get_permalink($event_id),
'start_date' => null,
'end_date' => null,
'venue' => null,
];
// Add Event Calendar specific data if available
if (function_exists('tribe_get_start_date')) {
$event_data['start_date'] = tribe_get_start_date($event_id, false);
$event_data['end_date'] = tribe_get_end_date($event_id, false);
if (function_exists('tribe_get_venue')) {
$event_data['venue'] = tribe_get_venue($event_id);
}
}
$events[] = $event_data;
}
return $events;
}
/**
* Get additional fields for an attendee.
* These could be custom fields collected during checkout.
*
* @param array $attendee The attendee data
* @return array
*/
private function get_attendee_additional_fields($attendee) {
$additional_fields = [];
// Check for meta data stored with the attendee
if (isset($attendee['attendee_meta']) && is_array($attendee['attendee_meta'])) {
foreach ($attendee['attendee_meta'] as $key => $value) {
// Skip internal or empty fields
if (strpos($key, '_') === 0 || empty($value)) {
continue;
}
// Format field name for display
$field_name = ucwords(str_replace(['_', '-'], ' ', $key));
$additional_fields[$key] = [
'label' => $field_name,
'value' => $value
];
}
}
return $additional_fields;
}
/**
* Get order notes.
*
* @return array
*/
public function get_order_notes() {
$notes = [];
if ($this->order_object instanceof WC_Order && function_exists('wc_get_order_notes')) {
$raw_notes = wc_get_order_notes([
'order_id' => $this->order_id,
'type' => 'customer',
]);
foreach ($raw_notes as $note) {
$notes[] = [
'id' => $note->id,
'content' => $note->content,
'date' => $note->date_created->date('Y-m-d H:i:s'),
'author' => $note->added_by,
];
}
}
return $notes;
}
}

View file

@ -263,11 +263,27 @@ get_header();
<tbody>
<?php foreach ( $transactions as $txn ) : ?>
<tr>
<td><?php echo esc_html( $txn['purchaser_name'] ?? 'N/A' ); ?></td>
<td>
<?php if ( ! empty( $txn['order_id'] ) ) : ?>
<a href="<?php echo esc_url( add_query_arg( 'order_id', $txn['order_id'], home_url( '/order-summary/' ) ) ); ?>" title="View order details">
<?php echo esc_html( $txn['purchaser_name'] ?? 'N/A' ); ?>
</a>
<?php else : ?>
<?php echo esc_html( $txn['purchaser_name'] ?? 'N/A' ); ?>
<?php endif; ?>
</td>
<td><?php echo esc_html( $txn['purchaser_email'] ?? 'N/A' ); ?></td>
<td><?php echo esc_html( $txn['ticket_type_name'] ?? 'N/A' ); ?></td>
<td>$<?php echo esc_html( number_format( $txn['price'] ?? 0, 2 ) ); ?></td>
<td><?php echo esc_html( $txn['order_id'] ?? 'N/A' ); ?></td>
<td>
<?php if ( ! empty( $txn['order_id'] ) ) : ?>
<a href="<?php echo esc_url( add_query_arg( 'order_id', $txn['order_id'], home_url( '/order-summary/' ) ) ); ?>" title="View order details">
<?php echo esc_html( $txn['order_id'] ?? 'N/A' ); ?>
</a>
<?php else : ?>
<?php echo esc_html( $txn['order_id'] ?? 'N/A' ); ?>
<?php endif; ?>
</td>
<td><?php echo $txn['checked_in'] ? 'Yes' : 'No'; ?></td>
</tr>
<?php endforeach; ?>

View file

@ -22,93 +22,482 @@ if ( ! class_exists( 'HVAC_Order_Summary_Data' ) ) {
}
}
// Check if user is logged in
if ( ! is_user_logged_in() ) {
get_header();
echo '<div id="primary" class="content-area primary ast-container">';
echo '<main id="main" class="site-main">';
echo '<div class="hvac-login-required" style="padding: 30px; background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; text-align: center; margin: 30px 0;">';
echo '<h2>Authentication Required</h2>';
echo '<p>Please log in to view the order summary.</p>';
echo '<p><a href="' . esc_url(home_url('/community-login/')) . '" class="ast-button ast-button-primary">Log In</a></p>';
echo '</div>';
echo '</main></div>';
get_footer();
exit;
}
// Get order ID from query var or context
$order_id = isset( $_GET['order_id'] ) ? absint( $_GET['order_id'] ) : 0;
if ( $order_id <= 0 ) {
get_header();
echo '<div id="primary" class="content-area primary ast-container">';
echo '<main id="main" class="site-main">';
echo '<div class="hvac-error">No order specified. Please return to your dashboard.</div>';
echo '<p><a href="' . esc_url(home_url('/hvac-dashboard/')) . '" class="ast-button ast-button-primary">Return to Dashboard</a></p>';
echo '</main></div>';
get_footer();
exit;
}
// Initialize order data handler
$order_summary = new HVAC_Order_Summary_Data( $order_id );
// Check if order is valid and user has permission to view it
if ( ! $order_summary->is_valid_order() || ! $order_summary->user_can_view_order() ) {
get_header();
echo '<div id="primary" class="content-area primary ast-container">';
echo '<main id="main" class="site-main">';
echo '<div class="hvac-error">Order not found or you do not have permission to view this order.</div>';
echo '<p><a href="' . esc_url(home_url('/hvac-dashboard/')) . '" class="ast-button ast-button-primary">Return to Dashboard</a></p>';
echo '</main></div>';
get_footer();
exit;
}
// Get order data
$order_details = $order_summary->get_order_details();
$tickets = $order_details['tickets'];
$events = $order_details['events'];
$notes = $order_summary->get_order_notes();
get_header();
?>
<div id="primary" <?php astra_primary_class(); ?>>
<div id="primary" class="content-area primary ast-container">
<main id="main" class="site-main">
<?php astra_primary_content_top(); ?>
<!-- Header & Navigation -->
<div class="hvac-dashboard-header">
<h1 class="entry-title">Order Summary: #<?php echo esc_html( $order_details['order_number'] ); ?></h1>
<div class="hvac-dashboard-nav">
<?php if(!empty($events) && isset($events[0]['id'])): ?>
<a href="<?php echo esc_url(add_query_arg('event_id', $events[0]['id'], home_url('/event-summary/'))); ?>" class="ast-button ast-button-secondary">Back to Event Summary</a>
<?php endif; ?>
<a href="<?php echo esc_url(home_url('/hvac-dashboard/')); ?>" class="ast-button ast-button-primary">Dashboard</a>
<?php
// Email attendees link (Phase 2 feature)
if (count($tickets) > 0) {
echo '<a href="#" class="ast-button ast-button-secondary">Email Attendees</a>';
}
?>
</div>
</div>
<?php
// Get order ID from query var or context
$order_id = isset( $_GET['order_id'] ) ? absint( $_GET['order_id'] ) : 0;
<!-- Order Overview Section -->
<section class="hvac-summary-section">
<h2>Order Overview</h2>
<div class="hvac-summary-content">
<!-- Order Details -->
<div class="hvac-details-card">
<table class="hvac-details-table">
<tr>
<th>Order Number:</th>
<td><?php echo esc_html( $order_details['order_number'] ); ?></td>
</tr>
<tr>
<th>Purchaser:</th>
<td>
<?php echo esc_html( $order_details['purchaser_name'] ); ?>
<?php if (!empty($order_details['purchaser_email'])) : ?>
<div class="hvac-detail-subtext"><?php echo esc_html( $order_details['purchaser_email'] ); ?></div>
<?php endif; ?>
</td>
</tr>
<?php if (!empty($order_details['organization'])) : ?>
<tr>
<th>Organization:</th>
<td><?php echo esc_html( $order_details['organization'] ); ?></td>
</tr>
<?php endif; ?>
<tr>
<th>Purchase Date:</th>
<td><?php echo esc_html( $order_details['purchase_date'] ); ?></td>
</tr>
<tr>
<th>Status:</th>
<td><?php echo esc_html( ucfirst( $order_details['status'] ) ); ?></td>
</tr>
<tr>
<th>Total Price:</th>
<td><?php echo esc_html( $order_details['total_price'] ); ?></td>
</tr>
<?php if (!empty($order_details['payment_method'])) : ?>
<tr>
<th>Payment Method:</th>
<td><?php echo esc_html( $order_details['payment_method'] ); ?></td>
</tr>
<?php endif; ?>
<?php if (!empty($order_details['billing_address'])) : ?>
<tr>
<th>Billing Address:</th>
<td><?php echo esc_html( $order_details['billing_address'] ); ?></td>
</tr>
<?php endif; ?>
</table>
</div>
</div>
</section>
// TODO: Add access control: Only allow trainers to view their own orders
<!-- Events Section -->
<?php if (!empty($events)) : ?>
<section class="hvac-summary-section">
<h2>Events</h2>
<div class="hvac-summary-content">
<div class="hvac-events-list">
<?php foreach ($events as $event) : ?>
<div class="hvac-event-card">
<h3>
<a href="<?php echo esc_url(add_query_arg('event_id', $event['id'], home_url('/event-summary/'))); ?>">
<?php echo esc_html($event['title']); ?>
</a>
</h3>
<?php if (!empty($event['start_date'])) : ?>
<div class="hvac-event-date">
<?php echo esc_html($event['start_date']); ?>
<?php if (!empty($event['end_date']) && $event['start_date'] !== $event['end_date']) : ?>
- <?php echo esc_html($event['end_date']); ?>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (!empty($event['venue'])) : ?>
<div class="hvac-event-venue">
<i class="fas fa-map-marker-alt"></i> <?php echo esc_html($event['venue']); ?>
</div>
<?php endif; ?>
<div class="hvac-event-links">
<a href="<?php echo esc_url($event['permalink']); ?>" target="_blank" class="hvac-view-link">View Public Page</a>
<a href="<?php echo esc_url(add_query_arg('event_id', $event['id'], home_url('/event-summary/'))); ?>" class="hvac-summary-link">Event Summary</a>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>
if ( $order_id > 0 ) {
$order_summary = new HVAC_Order_Summary_Data( $order_id );
<!-- Tickets & Attendees Section -->
<section class="hvac-summary-section">
<h2>Tickets & Attendees</h2>
<div class="hvac-summary-content">
<?php if (!empty($tickets)) : ?>
<table class="hvac-tickets-table">
<thead>
<tr>
<th>Attendee</th>
<th>Email</th>
<th>Ticket Type</th>
<th>Event</th>
<th>Price</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($tickets as $ticket) : ?>
<tr>
<td><?php echo esc_html($ticket['attendee_name']); ?></td>
<td><?php echo esc_html($ticket['attendee_email']); ?></td>
<td><?php echo esc_html($ticket['ticket_type']); ?></td>
<td>
<?php if (!empty($ticket['event_id'])) : ?>
<a href="<?php echo esc_url(add_query_arg('event_id', $ticket['event_id'], home_url('/event-summary/'))); ?>">
<?php echo esc_html($ticket['event_title']); ?>
</a>
<?php else : ?>
<?php echo esc_html($ticket['event_title'] ?? 'N/A'); ?>
<?php endif; ?>
</td>
<td><?php echo !empty($ticket['price']) ? '$' . number_format((float)$ticket['price'], 2) : 'N/A'; ?></td>
<td><?php echo $ticket['checked_in'] ? 'Checked In' : 'Not Checked In'; ?></td>
</tr>
<?php if (!empty($ticket['additional_fields'])) : ?>
<tr class="attendee-additional-fields">
<td colspan="6">
<details>
<summary>Additional Information</summary>
<div class="additional-fields-content">
<table class="additional-fields-table">
<?php foreach ($ticket['additional_fields'] as $field) : ?>
<tr>
<th><?php echo esc_html($field['label']); ?>:</th>
<td><?php echo esc_html($field['value']); ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
</details>
</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
<?php else : ?>
<p>No tickets or attendees found for this order.</p>
<?php endif; ?>
</div>
</section>
if ( $order_summary->is_valid_order() ) {
$order_details = $order_summary->get_order_details();
$tickets = $order_details['tickets'];
?>
<article id="order-<?php echo esc_attr( $order_id ); ?>" <?php post_class(); ?>>
<!-- Order Notes Section -->
<?php if (!empty($notes)) : ?>
<section class="hvac-summary-section">
<h2>Order Notes</h2>
<div class="hvac-summary-content">
<div class="hvac-notes-list">
<?php foreach ($notes as $note) : ?>
<div class="hvac-note-item">
<div class="hvac-note-content"><?php echo wp_kses_post($note['content']); ?></div>
<div class="hvac-note-meta">
<span class="hvac-note-date"><?php echo esc_html($note['date']); ?></span>
<?php if (!empty($note['author'])) : ?>
<span class="hvac-note-author">by <?php echo esc_html($note['author']); ?></span>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>
<header class="entry-header <?php astra_entry_header_class(); ?>">
<h1 class="entry-title">Order Summary: #<?php echo esc_html( $order_details['order_number'] ); ?></h1>
<?php
// Breadcrumb navigation (Astra)
if ( function_exists( 'astra_breadcrumb' ) ) {
astra_breadcrumb();
}
?>
</header>
</main>
</div>
<div class="entry-content">
<section class="order-details">
<h2>Order Details</h2>
<ul>
<li><strong>Order Number:</strong> <?php echo esc_html( $order_details['order_number'] ); ?></li>
<li><strong>Purchaser Name:</strong> <?php echo esc_html( $order_details['purchaser_name'] ); ?></li>
<li><strong>Purchaser Email:</strong> <?php echo esc_html( $order_details['purchaser_email'] ); ?></li>
<li><strong>Purchase Date:</strong> <?php echo esc_html( $order_details['purchase_date'] ); ?></li>
<li><strong>Total Price:</strong> <?php echo esc_html( $order_details['total_price'] ); ?></li>
<li><strong>Status:</strong> <?php echo esc_html( ucfirst( $order_details['status'] ) ); ?></li>
</ul>
</section>
<style>
/* Order Summary Styles */
.hvac-dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
<section class="order-tickets">
<h2>Tickets / Attendees</h2>
<?php if ( ! empty( $tickets ) ) : ?>
<table class="ast-table">
<thead>
<tr>
<th>Attendee Name</th>
<th>Attendee Email</th>
<th>Ticket Type</th>
<th>Checked In</th>
<th>Security Code</th>
</tr>
</thead>
<tbody>
<?php foreach ( $tickets as $ticket ) : ?>
<tr>
<td><?php echo esc_html( $ticket['attendee_name'] ); ?></td>
<td><?php echo esc_html( $ticket['attendee_email'] ); ?></td>
<td><?php echo esc_html( $ticket['ticket_type'] ); ?></td>
<td><?php echo $ticket['checked_in'] ? 'Yes' : 'No'; ?></td>
<td><?php echo esc_html( $ticket['security_code'] ); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else : ?>
<p>No tickets or attendees found for this order.</p>
<?php endif; ?>
</section>
</div>
</article>
<?php
} else {
echo "<p>Order not found or you do not have permission to view this order.</p>";
}
} else {
echo "<p>No order specified.</p>";
}
?>
.hvac-dashboard-nav {
display: flex;
gap: 10px;
}
<?php astra_primary_content_bottom(); ?>
.hvac-summary-section {
margin-bottom: 40px;
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
border: 1px solid #e9ecef;
}
</div><!-- #primary -->
.hvac-summary-section h2 {
margin-top: 0;
margin-bottom: 20px;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.hvac-summary-content {
margin-top: 10px;
}
/* Order Details Table */
.hvac-details-table {
width: 100%;
border-collapse: collapse;
}
.hvac-details-table th,
.hvac-details-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #eee;
}
.hvac-details-table th {
width: 150px;
font-weight: bold;
vertical-align: top;
}
.hvac-detail-subtext {
font-size: 0.9em;
color: #666;
margin-top: 5px;
}
/* Events List */
.hvac-events-list {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.hvac-event-card {
flex: 1;
min-width: 250px;
background: #fff;
border: 1px solid #e9ecef;
border-radius: 5px;
padding: 15px;
}
.hvac-event-card h3 {
margin-top: 0;
margin-bottom: 10px;
}
.hvac-event-date,
.hvac-event-venue {
margin-bottom: 10px;
color: #666;
font-size: 0.9em;
}
.hvac-event-links {
display: flex;
justify-content: space-between;
margin-top: 15px;
}
.hvac-event-links a {
display: inline-block;
font-size: 0.9em;
text-decoration: none;
}
/* Tickets Table */
.hvac-tickets-table {
width: 100%;
border-collapse: collapse;
}
.hvac-tickets-table th,
.hvac-tickets-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #eee;
}
.hvac-tickets-table th {
background-color: #f1f1f1;
font-weight: bold;
}
.hvac-tickets-table tr:nth-child(even) {
background-color: #f8f8f8;
}
.hvac-tickets-table tr:hover {
background-color: #f0f0f0;
}
.attendee-additional-fields {
background-color: #f5f5f5;
}
.attendee-additional-fields td {
padding: 0;
}
.attendee-additional-fields details {
padding: 10px;
}
.attendee-additional-fields summary {
cursor: pointer;
padding: 5px;
color: #0073aa;
}
.additional-fields-content {
padding: 10px;
background: #fff;
border-radius: 5px;
margin-top: 10px;
}
.additional-fields-table {
width: 100%;
border-collapse: collapse;
}
.additional-fields-table th,
.additional-fields-table td {
padding: 5px;
text-align: left;
border-bottom: 1px solid #eee;
}
.additional-fields-table th {
width: 150px;
font-weight: normal;
color: #666;
}
/* Order Notes */
.hvac-notes-list {
border: 1px solid #e0e0e0;
border-radius: 5px;
background: #fff;
}
.hvac-note-item {
padding: 15px;
border-bottom: 1px solid #e0e0e0;
}
.hvac-note-item:last-child {
border-bottom: none;
}
.hvac-note-content {
margin-bottom: 5px;
}
.hvac-note-meta {
font-size: 0.85em;
color: #777;
}
.hvac-note-date {
margin-right: 10px;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.hvac-dashboard-header {
flex-direction: column;
align-items: flex-start;
}
.hvac-dashboard-nav {
margin-top: 15px;
flex-wrap: wrap;
}
.hvac-dashboard-nav a {
margin-bottom: 5px;
}
.hvac-details-table th {
width: 120px;
}
.hvac-tickets-table {
display: block;
overflow-x: auto;
}
.hvac-event-card {
flex: 1 0 100%;
}
}
</style>
<?php get_footer(); ?>