| 1 |
<?php |
| 2 |
/** |
| 3 |
* Subscription admin list view. |
| 4 |
* |
| 5 |
* @package SpringDevs\Subscription\Admin |
| 6 |
*/ |
| 7 |
|
| 8 |
if ( ! defined( 'ABSPATH' ) ) { |
| 9 |
exit; |
| 10 |
} |
| 11 |
|
| 12 |
if ( ! isset( $date_filter ) ) { |
| 13 |
$date_filter = ''; |
| 14 |
} |
| 15 |
if ( ! isset( $renewal_due ) ) { |
| 16 |
$renewal_due = 0; |
| 17 |
} |
| 18 |
|
| 19 |
$filters_active = ! empty( $status ) || ! empty( $date_filter ) || ! empty( $search ) || ! empty( $renewal_due ); |
| 20 |
|
| 21 |
// The last twelve of the store's months, counted back from the 1st: stepping |
| 22 |
// back from today skips a month on the 29th–31st (31 Oct − 1 month is 1 Oct). |
| 23 |
// The store's timezone, because the list filters on the local post date. |
| 24 |
$months = array(); |
| 25 |
$this_month = ( new DateTimeImmutable( 'now', wp_timezone() ) )->modify( 'first day of this month' )->setTime( 0, 0 ); |
| 26 |
for ( $i = 0; $i < 12; $i++ ) { |
| 27 |
$month = strtotime( "-$i month" ); |
| 28 |
$months[ gmdate( 'Y-m', $month ) ] = gmdate( 'F Y', $month ); |
| 29 |
} |
| 30 |
?> |
| 31 |
<div class="wp-subscription-admin-content list-page"> |
| 32 |
|
| 33 |
<?php |
| 34 |
// Getting started ("Welcome to WPSubscription") card hidden for now — re-enable later. |
| 35 |
// require __DIR__ . '/subscription-gsc.php'; |
| 36 |
?> |
| 37 |
|
| 38 |
<?php |
| 39 |
wpsubs_render_page_header( |
| 40 |
array( |
| 41 |
'title' => __( 'Subscriptions', 'subscription' ), |
| 42 |
'description' => __( 'Manage all your WooCommerce subscriptions.', 'subscription' ), |
| 43 |
) |
| 44 |
); |
| 45 |
?> |
| 46 |
|
| 47 |
<form method="post" id="subscriptions-form"> |
| 48 |
<?php wp_nonce_field( 'subscrpt_list_action' ); ?> |
| 49 |
<input type="hidden" name="page" value="wp-subscription-list" /> |
| 50 |
|
| 51 |
<!-- Toolbar --> |
| 52 |
<div class="wpsubs-toolbar"> |
| 53 |
|
| 54 |
<?php |
| 55 |
wpsubs_render_adv_select( |
| 56 |
array( |
| 57 |
'name' => 'subscrpt_status', |
| 58 |
'placeholder' => __( 'All Status', 'subscription' ), |
| 59 |
'value' => $status, |
| 60 |
'options' => array( |
| 61 |
array( |
| 62 |
'value' => 'active', |
| 63 |
'label' => __( 'Active', 'subscription' ), |
| 64 |
), |
| 65 |
array( |
| 66 |
'value' => 'on_hold', |
| 67 |
'label' => __( 'On Hold', 'subscription' ), |
| 68 |
), |
| 69 |
array( |
| 70 |
'value' => 'pending', |
| 71 |
'label' => __( 'Pending', 'subscription' ), |
| 72 |
), |
| 73 |
array( |
| 74 |
'value' => 'pe_cancelled', |
| 75 |
'label' => __( 'Pending Cancellation', 'subscription' ), |
| 76 |
), |
| 77 |
array( |
| 78 |
'value' => 'cancelled', |
| 79 |
'label' => __( 'Cancelled', 'subscription' ), |
| 80 |
), |
| 81 |
array( |
| 82 |
'value' => 'expired', |
| 83 |
'label' => __( 'Expired', 'subscription' ), |
| 84 |
), |
| 85 |
array( |
| 86 |
'value' => 'completed', |
| 87 |
'label' => __( 'Completed', 'subscription' ), |
| 88 |
), |
| 89 |
array( |
| 90 |
'value' => 'trash', |
| 91 |
'label' => __( 'Trash', 'subscription' ), |
| 92 |
), |
| 93 |
), |
| 94 |
) |
| 95 |
); |
| 96 |
?> |
| 97 |
|
| 98 |
<?php |
| 99 |
$date_options = array(); |
| 100 |
foreach ( $months as $val => $label ) { |
| 101 |
$date_options[] = array( |
| 102 |
'value' => $val, |
| 103 |
'label' => $label, |
| 104 |
); |
| 105 |
} |
| 106 |
wpsubs_render_adv_select( |
| 107 |
array( |
| 108 |
'name' => 'date_filter', |
| 109 |
'placeholder' => __( 'All Dates', 'subscription' ), |
| 110 |
'value' => $date_filter, |
| 111 |
'options' => $date_options, |
| 112 |
) |
| 113 |
); |
| 114 |
?> |
| 115 |
|
| 116 |
<?php |
| 117 |
// Next-renewal window. The Overview's "Renewals due" figure opens the |
| 118 |
// list with 7 chosen; a window reached by URL is offered too, so the |
| 119 |
// dropdown always shows what is applied. |
| 120 |
$renewal_windows = array_unique( array_merge( array( 7, 14, 30 ), $renewal_due ? array( $renewal_due ) : array() ) ); |
| 121 |
sort( $renewal_windows ); |
| 122 |
$renewal_options = array(); |
| 123 |
foreach ( $renewal_windows as $days ) { |
| 124 |
$renewal_options[] = array( |
| 125 |
'value' => (string) $days, |
| 126 |
/* translators: %d: number of days ahead. */ |
| 127 |
'label' => sprintf( _n( 'Due in %d day', 'Due in %d days', $days, 'subscription' ), $days ), |
| 128 |
); |
| 129 |
} |
| 130 |
wpsubs_render_adv_select( |
| 131 |
array( |
| 132 |
'name' => 'renewal_due', |
| 133 |
'placeholder' => __( 'All Renewals', 'subscription' ), |
| 134 |
'value' => $renewal_due ? (string) $renewal_due : '', |
| 135 |
'options' => $renewal_options, |
| 136 |
) |
| 137 |
); |
| 138 |
?> |
| 139 |
|
| 140 |
<div class="wpsubs-search"> |
| 141 |
<div class="wpsubs-input-wrap wpsubs-input-wrap--icon-l"> |
| 142 |
<svg class="wpsubs-input-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-4.35-4.35M17 11A6 6 0 1 1 5 11a6 6 0 0 1 12 0z"/></svg> |
| 143 |
<input type="search" name="s" value="<?php echo esc_attr( $search ); ?>" placeholder="<?php esc_attr_e( 'Search subscriptions...', 'subscription' ); ?>" class="wpsubs-input" /> |
| 144 |
</div> |
| 145 |
</div> |
| 146 |
|
| 147 |
<button type="submit" name="filter_action" value="filter" class="wpsubs-btn wpsubs-btn--outline"> |
| 148 |
<?php esc_html_e( 'Filter', 'subscription' ); ?> |
| 149 |
</button> |
| 150 |
|
| 151 |
<?php if ( $filters_active ) : ?> |
| 152 |
<a href="<?php echo esc_url( remove_query_arg( array( 'subscrpt_status', 'date_filter', 's', 'title', 'paged', 'renewal_due' ) ) ); ?>" class="wpsubs-btn wpsubs-btn--outline"> |
| 153 |
<?php esc_html_e( 'Clear', 'subscription' ); ?> |
| 154 |
</a> |
| 155 |
<?php endif; ?> |
| 156 |
|
| 157 |
<div class="wpsubs-toolbar__spacer"></div> |
| 158 |
|
| 159 |
<?php // Page size and bulk actions wrap as one right-aligned group, never one without the other. ?> |
| 160 |
<div style="display:flex;flex-wrap:wrap;gap:8px;margin-inline-start:auto;"> |
| 161 |
<?php |
| 162 |
$current_per_page = isset( $_GET['per_page'] ) ? intval( wp_unslash( $_GET['per_page'] ) ) : 20; |
| 163 |
wpsubs_render_per_page_select( |
| 164 |
array( |
| 165 |
'name' => 'per_page', |
| 166 |
'value' => (string) $current_per_page, |
| 167 |
'align' => 'right', |
| 168 |
'id' => 'wpsubs-per-page-select', |
| 169 |
) |
| 170 |
); |
| 171 |
?> |
| 172 |
|
| 173 |
<?php |
| 174 |
// Bulk action advanced-select + hidden submit. |
| 175 |
$bulk_options = 'trash' === $status |
| 176 |
? array( |
| 177 |
array( |
| 178 |
'value' => 'restore', |
| 179 |
'label' => __( 'Restore selected', 'subscription' ), |
| 180 |
), |
| 181 |
array( |
| 182 |
'value' => 'delete', |
| 183 |
'label' => __( 'Delete permanently', 'subscription' ), |
| 184 |
'danger' => true, |
| 185 |
'confirm' => __( 'Delete selected subscriptions permanently? This cannot be undone.', 'subscription' ), |
| 186 |
), |
| 187 |
) |
| 188 |
: array( |
| 189 |
array( |
| 190 |
'value' => 'trash', |
| 191 |
'label' => __( 'Move to trash', 'subscription' ), |
| 192 |
'danger' => true, |
| 193 |
'confirm' => __( 'Move selected subscriptions to trash?', 'subscription' ), |
| 194 |
), |
| 195 |
); |
| 196 |
|
| 197 |
wpsubs_render_adv_select( |
| 198 |
array( |
| 199 |
'name' => 'action', |
| 200 |
'placeholder' => __( 'Select Action', 'subscription' ), |
| 201 |
'value' => '-1', |
| 202 |
'options' => $bulk_options, |
| 203 |
'id' => 'wpsubs-bulk-action-select', |
| 204 |
'align' => 'right', |
| 205 |
) |
| 206 |
); |
| 207 |
?> |
| 208 |
<button type="submit" name="bulk_action" value="1" id="wpsubs-bulk-action-submit" style="display:none;" aria-hidden="true"></button> |
| 209 |
|
| 210 |
<?php |
| 211 |
if ( 'trash' === $status && ! empty( $subscriptions ) ) : |
| 212 |
$empty_trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription-list&action=clean_trash&sub_id=all' ), 'wpsubs_action_clean_trash' ); |
| 213 |
?> |
| 214 |
<a href="<?php echo esc_url( $empty_trash_url ); ?>" class="wpsubs-btn wpsubs-btn--danger" onclick="return confirm('<?php esc_attr_e( 'Permanently delete all trash items? This cannot be undone.', 'subscription' ); ?>')"> |
| 215 |
<?php esc_html_e( 'Empty Trash', 'subscription' ); ?> |
| 216 |
</a> |
| 217 |
<?php endif; ?> |
| 218 |
</div> |
| 219 |
|
| 220 |
</div><!-- /.wpsubs-toolbar --> |
| 221 |
|
| 222 |
<!-- Table card --> |
| 223 |
<h2 class="screen-reader-text"><?php esc_html_e( 'Subscriptions list', 'subscription' ); ?></h2> |
| 224 |
<div class="wpsubs-table-card"> |
| 225 |
<table class="wpsubs-table"> |
| 226 |
<thead> |
| 227 |
<tr> |
| 228 |
<th class="wpsubs-col--check"> |
| 229 |
<input type="checkbox" id="cb-select-all" class="wpsubs-checkbox"> |
| 230 |
</th> |
| 231 |
<th style="width:1%;white-space:nowrap;"><?php esc_html_e( 'Status', 'subscription' ); ?></th> |
| 232 |
<th><?php esc_html_e( 'Subscription', 'subscription' ); ?></th> |
| 233 |
<th><?php esc_html_e( 'Customer', 'subscription' ); ?></th> |
| 234 |
<th><?php esc_html_e( 'Amount', 'subscription' ); ?></th> |
| 235 |
<th><?php esc_html_e( 'Started', 'subscription' ); ?></th> |
| 236 |
<th><?php esc_html_e( 'Next Renewal', 'subscription' ); ?></th> |
| 237 |
<th class="wpsubs-col--actions"><?php esc_html_e( 'Actions', 'subscription' ); ?></th> |
| 238 |
</tr> |
| 239 |
</thead> |
| 240 |
<tbody> |
| 241 |
|
| 242 |
<?php if ( ! empty( $subscriptions ) ) : ?> |
| 243 |
<?php |
| 244 |
foreach ( $subscriptions as $subscription ) : |
| 245 |
$subscription_id = $subscription->ID; |
| 246 |
$subscription_data = SpringDevs\Subscription\Illuminate\Helper::get_subscription_data( $subscription_id ); |
| 247 |
|
| 248 |
$subscrpt_status = $subscription_data['status'] ?? ''; |
| 249 |
$subscrpt_status = empty( $subscrpt_status ) ? get_post_status( $subscription_id ) : $subscrpt_status; |
| 250 |
|
| 251 |
$order_id = $subscription_data['order']['order_id'] ?? 0; |
| 252 |
$order_item_id = $subscription_data['order']['order_item_id'] ?? 0; |
| 253 |
$order = $order_id ? wc_get_order( $order_id ) : null; |
| 254 |
$order_item = $order ? $order->get_item( $order_item_id ) : null; |
| 255 |
$product_name = $order_item ? $order_item->get_name() : '-'; |
| 256 |
|
| 257 |
$product_id = $subscription_data['product']['variation_id'] ?: ( $subscription_data['product']['product_id'] ?? 0 ); |
| 258 |
$product_url = $product_id ? get_edit_post_link( $product_id ) : ''; |
| 259 |
|
| 260 |
$customer = $order ? $order->get_formatted_billing_full_name() : '-'; |
| 261 |
$customer_id = $order ? $order->get_customer_id() : 0; |
| 262 |
$customer_url = $customer_id ? admin_url( 'user-edit.php?user_id=' . $customer_id ) : ''; |
| 263 |
$customer_email = $order ? $order->get_billing_email() : ''; |
| 264 |
|
| 265 |
$start_date = $subscription_data['start_date'] ? strtotime( $subscription_data['start_date'] ) : 0; |
| 266 |
$renewal_date = $subscription_data['next_date'] ? strtotime( $subscription_data['next_date'] ) : 0; |
| 267 |
|
| 268 |
$is_trash = 'trash' === $subscription->post_status; |
| 269 |
$is_grace_period = isset( $subscription_data['grace_period'] ); |
| 270 |
$grace_remaining = $subscription_data['grace_period']['remaining_days'] ?? 0; |
| 271 |
|
| 272 |
// Amount + timing. Net of any discount that carries into renewals. |
| 273 |
$quantity = $order_item ? (int) $order_item->get_quantity() : 1; |
| 274 |
$amount_totals = SpringDevs\Subscription\Illuminate\Helper::get_subscription_display_totals( $subscription_id, $order_item ); |
| 275 |
$price = '' !== ( $subscription_data['price'] ?? '' ) ? (float) $amount_totals['total'] : ''; |
| 276 |
$price_full = (float) $amount_totals['full_excl'] + $amount_totals['tax'] + $amount_totals['discount_tax']; |
| 277 |
$has_discount = (bool) $amount_totals['has_discount']; |
| 278 |
$price_per_unit = '' !== $price ? $price / max( 1, $quantity ) : ''; |
| 279 |
$timing_per = $subscription_data['schedule']['timing_per'] ?? ''; |
| 280 |
$timing_option = $subscription_data['schedule']['timing_option'] ?? ''; |
| 281 |
$timing_label = ''; |
| 282 |
if ( $timing_per && $timing_option ) { |
| 283 |
$timing_unit = \SpringDevs\Subscription\Illuminate\Helper::get_typos( $timing_per, $timing_option ); |
| 284 |
$timing_label = ( (int) $timing_per > 1 ) |
| 285 |
? $timing_per . ' ' . $timing_unit |
| 286 |
: $timing_unit; |
| 287 |
} |
| 288 |
|
| 289 |
// Avatar |
| 290 |
$name_parts = array_values( array_filter( explode( ' ', trim( $customer ) ) ) ); |
| 291 |
$initials = '?'; |
| 292 |
if ( $name_parts ) { |
| 293 |
$initials = ''; |
| 294 |
foreach ( array_slice( $name_parts, 0, 2 ) as $part ) { |
| 295 |
$initials .= strtoupper( $part[0] ); |
| 296 |
} |
| 297 |
} |
| 298 |
$color_slot = ord( strtolower( $initials[0] ?? 'a' ) ) % 8; |
| 299 |
|
| 300 |
// Action URLs |
| 301 |
$nonce_action = 'wpsubs_action_' . $subscription->ID; |
| 302 |
$view_url = admin_url( 'admin.php?page=wp-subscription-details&id=' . $subscription->ID ); |
| 303 |
$trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription-list&action=trash&sub_id=' . $subscription->ID ), $nonce_action ); |
| 304 |
$delete_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription-list&action=delete&sub_id=' . $subscription->ID ), $nonce_action ); |
| 305 |
$restore_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription-list&action=restore&sub_id=' . $subscription->ID ), $nonce_action ); |
| 306 |
|
| 307 |
// Status badge |
| 308 |
$badge_mod_map = array( |
| 309 |
'active' => 'active', |
| 310 |
'pending' => 'pending', |
| 311 |
'pe_cancelled' => 'pending-cancel', |
| 312 |
'cancelled' => 'cancelled', |
| 313 |
'expired' => 'expired', |
| 314 |
'completed' => 'completed', |
| 315 |
'draft' => 'draft', |
| 316 |
'trash' => 'trash', |
| 317 |
); |
| 318 |
$badge_mod = $badge_mod_map[ $subscrpt_status ] ?? 'expired'; |
| 319 |
$verbose_status = SpringDevs\Subscription\Illuminate\Helper::get_verbose_status( $subscrpt_status ); |
| 320 |
|
| 321 |
if ( $is_grace_period && $grace_remaining > 0 ) { |
| 322 |
$badge_mod = 'active'; |
| 323 |
$verbose_status = __( 'Active', 'subscription' ); |
| 324 |
} |
| 325 |
?> |
| 326 |
<tr> |
| 327 |
<!-- Checkbox --> |
| 328 |
<td class="wpsubs-col--check"> |
| 329 |
<input type="checkbox" name="subscription_ids[]" value="<?php echo esc_attr( $subscription->ID ); ?>" class="wpsubs-checkbox wpsubs-row-check"> |
| 330 |
</td> |
| 331 |
|
| 332 |
<!-- Status badge --> |
| 333 |
<td style="white-space:nowrap;"> |
| 334 |
<span class="wpsubs-badge wpsubs-badge--<?php echo esc_attr( $badge_mod ); ?>"> |
| 335 |
<?php echo esc_html( $verbose_status ); ?> |
| 336 |
<?php if ( $is_grace_period && $grace_remaining > 0 ) : ?> |
| 337 |
<span class="dashicons dashicons-warning" style="font-size:11px;width:11px;height:11px;color:#d97706;" title="<?php echo esc_attr( sprintf( /* translators: %d: number of days left in the grace period. */ __( '%d days remaining in grace period', 'subscription' ), $grace_remaining ) ); ?>"></span> |
| 338 |
<?php endif; ?> |
| 339 |
</span> |
| 340 |
</td> |
| 341 |
|
| 342 |
<!-- Subscription: product name + subscription ID --> |
| 343 |
<td> |
| 344 |
<a href="<?php echo esc_url( $view_url ); ?>" class="wpsubs-cell-title" style="font-weight:600;"><?php echo esc_html( $product_name ); ?></a> |
| 345 |
<span class="wpsubs-cell-id">#<?php echo (int) $subscription->ID; ?></span> |
| 346 |
</td> |
| 347 |
|
| 348 |
<!-- Customer avatar + name + email --> |
| 349 |
<td> |
| 350 |
<div class="wpsubs-customer"> |
| 351 |
<div class="wpsubs-avatar" data-color="<?php echo (int) $color_slot; ?>"> |
| 352 |
<?php echo esc_html( $initials ); ?> |
| 353 |
</div> |
| 354 |
<div class="wpsubs-customer__info"> |
| 355 |
<?php if ( $customer_url ) : ?> |
| 356 |
<a href="<?php echo esc_url( $customer_url ); ?>" class="wpsubs-customer__name"><?php echo esc_html( $customer ); ?></a> |
| 357 |
<?php else : ?> |
| 358 |
<span class="wpsubs-customer__name"><?php echo esc_html( $customer ); ?></span> |
| 359 |
<?php endif; ?> |
| 360 |
<?php if ( $customer_email ) : ?> |
| 361 |
<span class="wpsubs-customer__sub"><?php echo esc_html( $customer_email ); ?></span> |
| 362 |
<?php endif; ?> |
| 363 |
</div> |
| 364 |
</div> |
| 365 |
</td> |
| 366 |
|
| 367 |
<!-- Subscription amount + timing --> |
| 368 |
<td style="white-space:nowrap;"> |
| 369 |
<?php if ( '' !== $price ) : ?> |
| 370 |
<span class="wpsubs-cell-title" style="font-variant-numeric:tabular-nums;"><?php echo wp_kses_post( wc_price( $price ) ); ?></span> |
| 371 |
<?php if ( $has_discount ) : ?> |
| 372 |
<span class="wpsubs-cell-id"><del aria-hidden="true"><?php echo wp_kses_post( wc_price( $price_full ) ); ?></del></span> |
| 373 |
<?php endif; ?> |
| 374 |
<?php if ( $quantity > 1 ) : ?> |
| 375 |
<span class="wpsubs-cell-id"><?php echo wp_kses_post( wc_price( (float) $price_per_unit ) ); ?> × <?php echo (int) $quantity; ?></span> |
| 376 |
<?php endif; ?> |
| 377 |
<?php if ( $timing_label ) : ?> |
| 378 |
<span class="wpsubs-cell-id">/ <?php echo esc_html( $timing_label ); ?></span> |
| 379 |
<?php endif; ?> |
| 380 |
<?php else : ?> |
| 381 |
<span class="wpsubs-cell--muted">—</span> |
| 382 |
<?php endif; ?> |
| 383 |
</td> |
| 384 |
|
| 385 |
<!-- Start date --> |
| 386 |
<td class="wpsubs-col--nowrap"> |
| 387 |
<?php if ( $start_date ) : ?> |
| 388 |
<span class="wpsubs-cell-title"><?php echo esc_html( wp_date( get_option( 'date_format' ), $start_date ) ); ?></span> |
| 389 |
<span class="wpsubs-cell-id"><?php echo esc_html( wp_date( get_option( 'time_format' ), $start_date ) ); ?></span> |
| 390 |
<?php else : ?> |
| 391 |
<span class="wpsubs-cell--muted">—</span> |
| 392 |
<?php endif; ?> |
| 393 |
</td> |
| 394 |
|
| 395 |
<!-- Renewal date --> |
| 396 |
<td class="wpsubs-col--nowrap"> |
| 397 |
<?php if ( $renewal_date ) : ?> |
| 398 |
<span class="wpsubs-cell-title"><?php echo esc_html( wp_date( get_option( 'date_format' ), $renewal_date ) ); ?></span> |
| 399 |
<span class="wpsubs-cell-id"><?php echo esc_html( wp_date( get_option( 'time_format' ), $renewal_date ) ); ?></span> |
| 400 |
<?php else : ?> |
| 401 |
<span class="wpsubs-cell--muted">—</span> |
| 402 |
<?php endif; ?> |
| 403 |
</td> |
| 404 |
|
| 405 |
<!-- Actions --> |
| 406 |
<td class="wpsubs-cell--actions"> |
| 407 |
<div class="wpsubs-row-actions"> |
| 408 |
<button type="button" class="wpsubs-row-actions__trigger" aria-label="<?php esc_attr_e( 'Row actions', 'subscription' ); ?>">···</button> |
| 409 |
<div class="wpsubs-dropdown"> |
| 410 |
<a href="<?php echo esc_url( $view_url ); ?>" class="wpsubs-dropdown__item"><?php esc_html_e( 'View / Edit', 'subscription' ); ?></a> |
| 411 |
<div class="wpsubs-dropdown__divider"></div> |
| 412 |
<?php if ( ! $is_trash ) : ?> |
| 413 |
<a href="<?php echo esc_url( $trash_url ); ?>" class="wpsubs-dropdown__item wpsubs-dropdown__item--danger" onclick="return confirm('<?php esc_attr_e( 'Move this subscription to trash?', 'subscription' ); ?>')"><?php esc_html_e( 'Trash', 'subscription' ); ?></a> |
| 414 |
<?php else : ?> |
| 415 |
<a href="<?php echo esc_url( $restore_url ); ?>" class="wpsubs-dropdown__item"><?php esc_html_e( 'Restore', 'subscription' ); ?></a> |
| 416 |
<a href="<?php echo esc_url( $delete_url ); ?>" class="wpsubs-dropdown__item wpsubs-dropdown__item--danger" onclick="return confirm('<?php esc_attr_e( 'Delete permanently? This cannot be undone.', 'subscription' ); ?>')"><?php esc_html_e( 'Delete Permanently', 'subscription' ); ?></a> |
| 417 |
<?php endif; ?> |
| 418 |
</div> |
| 419 |
</div> |
| 420 |
</td> |
| 421 |
</tr> |
| 422 |
<?php endforeach; ?> |
| 423 |
|
| 424 |
<?php else : ?> |
| 425 |
<tr> |
| 426 |
<td colspan="8"> |
| 427 |
<div class="wpsubs-empty"> |
| 428 |
<div class="wpsubs-empty__icon">📋</div> |
| 429 |
<div class="wpsubs-empty__title"><?php esc_html_e( 'No subscriptions found', 'subscription' ); ?></div> |
| 430 |
<div class="wpsubs-empty__desc"><?php esc_html_e( 'Subscriptions will appear here once customers complete checkout.', 'subscription' ); ?></div> |
| 431 |
</div> |
| 432 |
</td> |
| 433 |
</tr> |
| 434 |
<?php endif; ?> |
| 435 |
|
| 436 |
</tbody> |
| 437 |
</table> |
| 438 |
|
| 439 |
<!-- Pagination inside table card --> |
| 440 |
<?php |
| 441 |
wpsubs_render_pager( |
| 442 |
array( |
| 443 |
'current' => (int) $paged, |
| 444 |
'total' => max( 1, (int) $max_num_pages ), |
| 445 |
'info' => true, |
| 446 |
'per_page' => (int) $per_page, |
| 447 |
'item_count' => (int) $total, |
| 448 |
'base_url' => remove_query_arg( 'paged' ), |
| 449 |
'link_mode' => 'url', |
| 450 |
// translators: %1$s = first item number, %2$s = last item number, %3$s = total items. |
| 451 |
'info_format' => __( 'Showing %1$s–%2$s of %3$s subscriptions', 'subscription' ), |
| 452 |
'context' => 'subscription-list', |
| 453 |
) |
| 454 |
); |
| 455 |
?> |
| 456 |
|
| 457 |
</div><!-- /.wpsubs-table-card --> |
| 458 |
|
| 459 |
</form> |
| 460 |
|
| 461 |
</div> |
| 462 |
|
| 463 |
<script> |
| 464 |
( function () { |
| 465 |
var selectAll = document.getElementById( 'cb-select-all' ); |
| 466 |
var form = document.getElementById( 'subscriptions-form' ); |
| 467 |
|
| 468 |
function rowChecks() { |
| 469 |
return form ? Array.from( form.querySelectorAll( '.wpsubs-row-check' ) ) : []; |
| 470 |
} |
| 471 |
|
| 472 |
function syncSelectAll() { |
| 473 |
if ( ! selectAll ) return; |
| 474 |
var checks = rowChecks(); |
| 475 |
var checked = checks.filter( function ( cb ) { return cb.checked; } ); |
| 476 |
checks.forEach( function ( cb ) { |
| 477 |
var row = cb.closest( 'tr' ); |
| 478 |
if ( row ) row.classList.toggle( 'wpsubs-row--selected', cb.checked ); |
| 479 |
} ); |
| 480 |
selectAll.indeterminate = checked.length > 0 && checked.length < checks.length; |
| 481 |
selectAll.checked = checks.length > 0 && checked.length === checks.length; |
| 482 |
} |
| 483 |
|
| 484 |
if ( selectAll ) { |
| 485 |
selectAll.addEventListener( 'change', function () { |
| 486 |
rowChecks().forEach( function ( cb ) { cb.checked = selectAll.checked; } ); |
| 487 |
syncSelectAll(); |
| 488 |
} ); |
| 489 |
} |
| 490 |
|
| 491 |
if ( form ) { |
| 492 |
form.addEventListener( 'change', function ( e ) { |
| 493 |
if ( e.target.classList.contains( 'wpsubs-row-check' ) ) syncSelectAll(); |
| 494 |
} ); |
| 495 |
} |
| 496 |
|
| 497 |
// ── Advanced-select event handlers ────────────────────────── |
| 498 |
var bulkSubmit = document.getElementById( 'wpsubs-bulk-action-submit' ); |
| 499 |
document.addEventListener( 'wpsubs:select', function ( e ) { |
| 500 |
var id = e.target && e.target.id; |
| 501 |
|
| 502 |
// Bulk action: submit via hidden button |
| 503 |
if ( id === 'wpsubs-bulk-action-select' && bulkSubmit ) { |
| 504 |
bulkSubmit.click(); |
| 505 |
return; |
| 506 |
} |
| 507 |
|
| 508 |
// Per-page: auto-submit as a filter action |
| 509 |
if ( id === 'wpsubs-per-page-select' && form ) { |
| 510 |
var fi = document.createElement( 'input' ); |
| 511 |
fi.type = 'hidden'; |
| 512 |
fi.name = 'filter_action'; |
| 513 |
fi.value = 'filter'; |
| 514 |
form.appendChild( fi ); |
| 515 |
form.submit(); |
| 516 |
} |
| 517 |
} ); |
| 518 |
|
| 519 |
// ── Row action dropdowns ───────────────────────────────────── |
| 520 |
function closeAllRowMenus() { |
| 521 |
document.querySelectorAll( '.wpsubs-row-actions--open' ).forEach( function ( el ) { |
| 522 |
el.classList.remove( 'wpsubs-row-actions--open' ); |
| 523 |
var d = el.querySelector( '.wpsubs-dropdown' ); |
| 524 |
if ( d ) d.classList.remove( 'wpsubs-dropdown--open' ); |
| 525 |
} ); |
| 526 |
} |
| 527 |
|
| 528 |
document.addEventListener( 'click', function ( e ) { |
| 529 |
var trigger = e.target.closest( '.wpsubs-row-actions__trigger' ); |
| 530 |
|
| 531 |
document.querySelectorAll( '.wpsubs-row-actions--open' ).forEach( function ( el ) { |
| 532 |
if ( el !== ( trigger && trigger.closest( '.wpsubs-row-actions' ) ) ) { |
| 533 |
el.classList.remove( 'wpsubs-row-actions--open' ); |
| 534 |
var d = el.querySelector( '.wpsubs-dropdown' ); |
| 535 |
if ( d ) d.classList.remove( 'wpsubs-dropdown--open' ); |
| 536 |
} |
| 537 |
} ); |
| 538 |
|
| 539 |
if ( ! trigger ) return; |
| 540 |
|
| 541 |
e.stopPropagation(); |
| 542 |
var wrapper = trigger.closest( '.wpsubs-row-actions' ); |
| 543 |
var dropdown = wrapper && wrapper.querySelector( '.wpsubs-dropdown' ); |
| 544 |
if ( ! wrapper || ! dropdown ) return; |
| 545 |
|
| 546 |
var isOpen = wrapper.classList.contains( 'wpsubs-row-actions--open' ); |
| 547 |
wrapper.classList.toggle( 'wpsubs-row-actions--open', ! isOpen ); |
| 548 |
dropdown.classList.toggle( 'wpsubs-dropdown--open', ! isOpen ); |
| 549 |
} ); |
| 550 |
|
| 551 |
document.addEventListener( 'keydown', function ( e ) { |
| 552 |
if ( e.key === 'Escape' ) closeAllRowMenus(); |
| 553 |
} ); |
| 554 |
|
| 555 |
syncSelectAll(); |
| 556 |
}() ); |
| 557 |
</script> |
| 558 |
|