hero( $label ); ?> kpi_cards( $abpet_info, $kpi ); ?>
today_events( $kpi['upcoming'] ?? array() ); ?> orders_summary(); ?>
display_name ) ? $user->display_name : __( 'Admin', 'abp-event-ticket' ); ?>

$post_id ) ); ?>
0 ) { ?>

'on_off' ] ), 'warning' ), array( 'fas fa-calendar-days', __( 'Global Date', 'abp-event-ticket' ), __( 'Manage global dates', 'abp-event-ticket' ), ABPET_Function::build_url( 'global', [ 'global' => 'dates' ] ), 'theme' ), ); if ( ABPET_Function::on_off( 'additional_info' ) ) { $actions[] = array( 'fas fa-list-check', __( 'Additional Services', 'abp-event-ticket' ), __( 'Manage additional services', 'abp-event-ticket' ), ABPET_Function::build_url( 'global', [ 'global' => 'additional' ] ), 'success' ); } if ( ABPET_Function::on_off( 'client_info' ) ) { $actions[] = array( 'fas fa-user', __( 'Client Form', 'abp-event-ticket' ), __( 'Manage client form fields', 'abp-event-ticket' ), ABPET_Function::build_url( 'global', [ 'global' => 'client_form' ] ), 'purple' ); } $actions[] = array( 'fas fa-route', __( 'Stops Configuration', 'abp-event-ticket' ), __( 'Manage stops / locations', 'abp-event-ticket' ), ABPET_Function::build_url( 'global', [ 'global' => 'location' ] ), 'navy' ); if ( defined( 'ABPET_DIR_PRO' ) ) { if ( ABPET_Function::on_off( 'partial_payment' ) ) { $actions[] = array( 'fas fa-hand-holding-dollar', __( 'Partial Payment', 'abp-event-ticket' ), __( 'Configure partial payment', 'abp-event-ticket' ), ABPET_Function::build_url( 'global', [ 'global' => 'partial_payment' ] ), 'success' ); } if ( ( ABPET_Function::on_off( 'seasonal' ) && ABPET_Function::on_off( 'seasonal_global' ) ) || ( ABPET_Function::on_off( 'early_bird' ) && ABPET_Function::on_off( 'early_bird_global' ) ) ) { $actions[] = array( 'fas fa-percent', __( 'Global Discount', 'abp-event-ticket' ), __( 'Configure global discounts', 'abp-event-ticket' ), ABPET_Function::build_url( 'global', [ 'global' => 'discount' ] ), 'info' ); } if ( ABPET_Function::on_off( 'cancel_request' ) ) { $actions[] = array( 'fas fa-ban', __( 'Cancel Request', 'abp-event-ticket' ), __( 'Manage cancellation requests', 'abp-event-ticket' ), ABPET_Function::build_url( 'cancel_requests' ), 'warning' ); } } ?>

current_time( 'Y-m-d' ) ) ); ?>

order_table( $last, false ); ?>
today_orders_pane( $today ); ?>
order_table( $items, false, $step ); ?> $step ) { ?>
0 ); ?> $item ) { $order_id = (int) ( $item['order_id'] ?? 0 ); $event_date = $item['event_date'] ?? ''; $session_time = $item['session_time'] ?? ''; $total = (float) ( $item['total'] ?? 0 ); $totals['total'] += $total; $row_class = ( $limit > 0 && $index >= $limit ) ? 'dash_row_hidden' : ''; ?>
# (int) ( $item['post_id'] ?? 0 ) ) ); ?> 0 ) { echo function_exists( 'wc_price' ) ? wp_kses_post( wc_price( $total ) ) : esc_html( number_format_i18n( $total, 2 ) ); } else { esc_html_e( 'FREE', 'abp-event-ticket' ); } ?>
__( 'Invalid security token or Insufficient permissions.', 'abp-event-ticket' ), 'type' => 'warn' ], 403 ); } $post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0; $start_time = isset( $_POST['start_time'] ) ? sanitize_text_field( wp_unslash( $_POST['start_time'] ) ) : ''; if ( $post_id <= 0 || empty( $start_time ) ) { wp_send_json_error( [ 'msg' => __( 'Invalid session.', 'abp-event-ticket' ), 'type' => 'warn' ], 400 ); } $meta = self::journey_meta( $post_id, $start_time ); $seat_type = $meta['seat_type'] ?? 'ticket'; ob_start(); ?>

0 ) { ?>
journey_seat_map( $post_id, $start_time ); ?>
journey_ticket_table( $post_id, $start_time ); ?>
()
today_bookings( $post_id, $start_time ); ?>
$html, 'msg' => esc_html__( 'Session loaded.', 'abp-event-ticket' ), 'type' => 'success' ] ); } //=============================// private function journey_ticket_table( $post_id, $start_time ): void { $post_infos = ABPET_Function::get_all_meta( $post_id ); $ticket_infos = $post_infos['ticket_infos'] ?? array(); $filters = array( 'post_id' => $post_id, 'start_time' => $start_time, 'cache' => false ); $sold = ABPET_Query::get_sold_ticket( $filters, true ); if ( empty( $ticket_infos ) || ! is_array( $ticket_infos ) ) { echo '

' . esc_html__( 'No ticket configuration found.', 'abp-event-ticket' ) . '

'; return; } ?> $ticket_info ) { $qty = (int) ( $ticket_info['qty'] ?? 0 ); $reserve = (int) ( $ticket_info['reserve'] ?? 0 ); $sold_qty = (int) ( $sold[ $tic_id ] ?? 0 ); $avail = max( 0, $qty - $sold_qty - $reserve ); ?>

' . esc_html__( 'No seat plan assigned.', 'abp-event-ticket' ) . '

'; return; } $filters = array( 'post_id' => $post_id, 'start_time' => $start_time, 'cache' => false ); $sold_seat = ABPET_Query::get_sold_seat( $filters ); foreach ( $sp_infos as $sp_item ) { $sp_id = (int) ( $sp_item['id'] ?? 0 ); if ( $sp_id <= 0 ) { continue; } $this->render_seat_plan( $sp_id, $sold_seat ); } } //=============================// private function render_seat_plan( $sp_id, $sold_seat ): void { $row = ABPET_Query::get_sp( $sp_id ); $sp_info = ! empty( $row ) ? current( $row ) : array(); if ( empty( $sp_info ) ) { return; } $others = json_decode( $sp_info['others'] ?? '', true ) ?: array(); $cell_width = $others['width'] ?? 40; $cell_height = $others['height'] ?? 40; $gap = $others['gap'] ?? 0; $radius = $others['radius'] ?? 0; $cols = intval( $others['column'] ?? 10 ); $bg_image = $others['bg_image'] ?? ''; $img_url = ! empty( $bg_image ) && $bg_image > 0 ? ABPET_Function::get_image_url( '', $bg_image ) : ''; $bg_color = $others['bg_color'] ?? '#fff'; $layout = json_decode( $sp_info['layout_data'] ?? '', true ) ?: array(); $sp_name = $sp_info['name'] ?? ''; // Multi-row/column cells must hide their "occupied" grid slots. $hidden_cells = array(); foreach ( $layout as $index => $cell ) { $c_span = intval( $cell['width_ratio'] ?? 1 ); $r_span = intval( $cell['height_ratio'] ?? 1 ); if ( $c_span > 1 || $r_span > 1 ) { for ( $r = 0; $r < $r_span; $r ++ ) { for ( $c = 0; $c < $c_span; $c ++ ) { if ( $r === 0 && $c === 0 ) { continue; } $target_idx = $index + ( $r * $cols ) + $c; $hidden_cells[ $target_idx ] = true; } } } } ?>
$cell ) { if ( isset( $hidden_cells[ $index ] ) ) { continue; } $type_id = $cell['id'] ?? ''; $name = $cell['name'] ?? ''; $c_span = intval( $cell['width_ratio'] ?? 1 ); $r_span = intval( $cell['height_ratio'] ?? 1 ); $rotate = intval( $cell['rotate'] ?? 0 ); $fs = $cell['fs'] ?? 12; $is_seat = ( 'seat' === ( $cell['type'] ?? '' ) ); $seat_type_class = $is_seat ? 'available' : ''; $seat_type_class = $is_seat && in_array( $name, $sold_seat, true ) ? 'sold' : $seat_type_class; $class = $is_seat ? 'sp_cell ' . $seat_type_class : 'sp_decor'; $color = $is_seat ? ABPET_Function::ticket_color( $type_id ) : ABPET_Function::decor_color( $type_id ); $icon_image = $is_seat ? ABPET_Function::ticket_icon( $type_id ) : ABPET_Function::decor_icon( $type_id ); $width = $cell_width * $c_span; $height = $cell_height * $r_span; if ( $gap > 0 ) { $width = $c_span > 1 ? $width + ( $c_span - 1 ) * $gap : $width; $height = $r_span > 1 ? $height + ( $r_span - 1 ) * $gap : $height; } $style = "color: {$color}; grid-column: span {$c_span}; grid-row: span {$r_span}; width:{$width}px;height:{$height}px; border:1px solid {$color};font-size:{$fs}px;border-radius:{$radius}px;"; $image = ''; if ( ! empty( $icon_image ) && is_numeric( $icon_image ) ) { $image = ABPET_Function::get_image_url( '', $icon_image ); } ?>
" style="background-image: url('');">
$post_id, 'start_time' => $start_time, 'status' => ABPET_Function::booking_status_sold(), 'cache' => false, ) ); if ( empty( $bookings ) ) { echo '

' . esc_html__( 'No bookings for this journey yet.', 'abp-event-ticket' ) . '

'; return; } ?>

= 2 && defined( 'WC_VERSION' ) ) { ?>
= 2 ) { $wc_name = get_option( 'woocommerce_email_from_name' ); $wc_email = get_option( 'woocommerce_email_from_address' ); if ( ! empty( $wc_name ) ) { ?>
checklist_item( __( 'WooCommerce Plugin', 'abp-event-ticket' ), ABPET_WC >= 2, ABPET_WC == 1 ? 'wc_active' : 'wc_install_active' ); ?> checklist_item( __( 'Booking Page', 'abp-event-ticket' ), (bool) ABPET_Function::get_page_by_slug( 'tf_booking' ), 'tf_booking' ); ?> checklist_item( /* translators: %s: transport label. */ sprintf( esc_html__( '%s List Page', 'abp-event-ticket' ), esc_html( $label ) ), (bool) ABPET_Function::get_page_by_slug( 'tf_post' ), 'tf_post' ); ?> checklist_item( __( 'Gallery Page', 'abp-event-ticket' ), (bool) ABPET_Function::get_page_by_slug( 'tf_gallery' ), 'tf_gallery' ); ?> 1 ) { do_action( 'abpet_add_page' ); do_action( 'abpet_add_tools' ); } ?> checklist_item( __( 'First Transport Added', 'abp-event-ticket' ), $total > 0, 'add_new' ); ?>
0 ) { ?>

0 ? (int) round( ( $row[2] / $sum ) * 100 ) : 0; if ( $row[2] > 0 && $width < 1 ) { $width = 1; } ?>
prefix . 'abpet_orders'; $data = array( 'revenue' => 0, 'tickets' => 0, 'today' => 0, 'upcoming' => array() ); $statuses = array_filter( array_map( 'sanitize_text_field', explode( ',', (string) ABPET_Function::booking_status() ) ) ); if ( ! empty( $statuses ) && current( $statuses ) !== 'all' ) { $placeholders = implode( ',', array_fill( 0, count( $statuses ), '%s' ) ); $sql = "SELECT COALESCE(SUM(total), 0) AS revenue, COALESCE(SUM(qty), 0) AS tickets FROM %i WHERE order_status IN ($placeholders)"; $params = array_merge( array( $table_name ), $statuses ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $row = $wpdb->get_row( $wpdb->prepare( $sql, $params ), ARRAY_A ); if ( ! empty( $row ) ) { $data['revenue'] = (float) $row['revenue']; $data['tickets'] = (int) $row['tickets']; } } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $data['today'] = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i WHERE DATE(created_at) = %s', $table_name, current_time( 'Y-m-d' ) ) ); $data['upcoming'] = self::journey_list(); wp_cache_set( $cache_key, $data, 'abpet_dashboard' ); return $data; } //=============================// /** * Build today's session list (a session = an event on a specific start time). * Only today's date is considered; every session scheduled today is shown, * including sessions whose start time has already passed. */ public static function journey_list(): array { $post_ids = defined( 'ABPET_ids' ) && ! empty( ABPET_ids ) ? ABPET_ids : ABPET_Query::get_post_id(); if ( empty( $post_ids ) || ! is_array( $post_ids ) ) { return array(); } $today = current_time( 'Y-m-d' ); $journeys = array(); foreach ( $post_ids as $post_id ) { $post_id = (int) $post_id; if ( $post_id <= 0 ) { continue; } $journeys = array_merge( $journeys, self::journey_today( $post_id, $today ) ); } if ( count( $journeys ) > 1 ) { usort( $journeys, function ( $a, $b ) { return strtotime( $a['start_time'] ?? '' ) <=> strtotime( $b['start_time'] ?? '' ); } ); } return $journeys; } //=============================// private static function journey_today( $post_id, $today ): array { $post_infos = ABPET_Function::get_all_meta( $post_id ); if ( empty( $post_infos ) ) { return array(); } // Is today among the post's scheduled dates? $dates = ABPET_Function::date( $post_id, array(), $today ); $today_is_schedule = is_array( $dates ) && in_array( $today, $dates, true ); // Keep listing transports whose trips have already started today. if ( ! $today_is_schedule && ! self::is_schedule_day( $post_infos, $today ) ) { return array(); } $journeys = array(); // Outbound trips: every scheduled departure time today (multi schedule = several times). $time_infos = $post_infos['time_infos'] ?? []; $times = ABPET_Function::time( $time_infos, $today ); if ( ! empty( $times ) && is_array( $times ) ) { foreach ( $times as $_time ) { if ( '' === (string) $_time ) { continue; } $journeys[] = self::journey_meta( $post_id, $today . ' ' . $_time, - 1, - 1 ); } } return $journeys; } //=============================// private static function is_schedule_day( $post_infos, $today ): bool { $date_infos = $post_infos['abpet_dates'] ?? array(); if ( empty( $date_infos ) || ! is_array( $date_infos ) ) { return false; } $date_type = $date_infos['date_type'] ?? 'periodic_date'; if ( 'specific_date' === $date_type ) { $specific_dates = $date_infos['specific_dates'] ?? array(); if ( is_array( $specific_dates ) ) { foreach ( $specific_dates as $date_item ) { if ( ! empty( $date_item ) && gmdate( 'Y-m-d', strtotime( $date_item ) ) === $today ) { return true; } } } return false; } $start_date = $date_infos['periodic_start_date'] ?? ''; if ( empty( $start_date ) || strtotime( $today ) < strtotime( $start_date ) ) { return false; } $end_date = $date_infos['periodic_end_date'] ?? ''; $end_date = ! empty( $end_date ) ? gmdate( 'Y-m-d', strtotime( $end_date ) ) : ''; $advance_days = ( ABPET_Date_Config['advance_date_number'] ?? null ) ?: 28; $calc_end = gmdate( 'Y-m-d', strtotime( $start_date . ' +' . $advance_days . ' day' ) ); if ( ! empty( $end_date ) && strtotime( $end_date ) < strtotime( $calc_end ) ) { $calc_end = $end_date; } return in_array( $today, ABPET_Function::date_list_modify( $start_date, $calc_end, $date_infos ), true ); } //=============================// /** * Compute the stats (total, sold, available, reserve, seat_type) for a single session. * * @param int $post_id Event post ID. * @param string $start_time Exact session datetime. * @param int $sold_qty Pre-computed sold quantity (optional). * @param int $order_count Number of distinct orders (optional). */ public static function journey_meta( $post_id, $start_time, $sold_qty = - 1, $order_count = - 1 ): array { $post_id = (int) $post_id; $post_infos = ABPET_Function::get_all_meta( $post_id ); $seat_type = $post_infos['seat_type'] ?? 'sp'; $seat_type = ABPET_Function::on_off( 'sp' ) ? $seat_type : 'ticket'; $total = (int) ( ABPET_Function::get_total_qty( $post_id, $post_infos ) ); $filters = array( 'post_id' => $post_id, 'start_time' => $start_time, 'cache' => false ); // Sold quantity. if ( $sold_qty < 0 ) { $sold = ABPET_Query::get_sold_ticket( $filters ); $sold_qty = (int) ( $sold['total'] ?? 0 ); } if ( $order_count < 0 ) { $order_count = count( ABPET_Query::get_booking_query( array( 'post_id' => $post_id, 'start_time' => $start_time, 'status' => ABPET_Function::booking_status_sold(), 'cache' => false, ) ) ); } // Reserve quantity (only meaningful for ticket-type seating). $reserve = 0; if ( 'ticket' === $seat_type ) { $ticket_infos = $post_infos['ticket_infos'] ?? array(); if ( is_array( $ticket_infos ) ) { foreach ( $ticket_infos as $ticket_info ) { $reserve += (int) ( $ticket_info['reserve'] ?? 0 ); } } } $available = $total - $sold_qty - $reserve; if ( $available < 0 ) { $available = 0; } return array( 'post_id' => $post_id, 'start_time' => $start_time, 'title' => get_the_title( $post_id ), 'seat_type' => $seat_type, 'started' => strtotime( $start_time ) <= current_time( 'timestamp' ), 'sold' => (int) $sold_qty, 'available' => $available, 'total' => $total, 'reserve' => $reserve, 'order_count' => (int) $order_count, ); } } new ABPET_Dashboard(); }