PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.10.2
Subscriptions for WooCommerce with Stripe Recurring Payments v1.10.2
2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 All 61 releases
subscription / includes / Admin / views / subscription-list.php

subscription-list.php in Subscriptions for WooCommerce with Stripe Recurring Payments 1.10.2, at includes/Admin/views/subscription-list.php

615 lines 22.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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
16 $filters_active = ! empty( $status ) || ! empty( $date_filter ) || ! empty( $search );
17 $months = array();
18 for ( $i = 0; $i < 12; $i++ ) {
19 $month = strtotime( "-$i month" );
20 $months[ date( 'Y-m', $month ) ] = date( 'F Y', $month );
21 }
22 ?>
23 <div class="wp-subscription-admin-content list-page subscrpt-subs-list">
24
25 <?php
26 // Getting started card — hidden once a subscription product exists.
27 require __DIR__ . '/subscription-gsc.php';
28 ?>
29
30 <!-- Page header -->
31 <div style="margin-bottom:20px;">
32 <h1 style="font-size:1.375rem;font-weight:700;color:var(--wpsubs-text);margin:0 0 6px;line-height:1.2;"><?php esc_html_e( 'Subscriptions', 'subscription' ); ?></h1>
33 <p style="font-size:13px;color:var(--wpsubs-text-muted);margin:0 0 12px;line-height:1.5;"><?php esc_html_e( 'Manage all your WooCommerce subscriptions.', 'subscription' ); ?></p>
34 <div style="border-top:1px dashed #d0d3d7;"></div>
35 </div>
36
37 <form method="post" id="subscriptions-form">
38 <?php wp_nonce_field( 'subscrpt_list_action' ); ?>
39 <input type="hidden" name="page" value="wp-subscription" />
40
41 <!-- Toolbar -->
42 <div class="wpsubs-toolbar">
43
44 <?php
45 wpsubs_render_adv_select(
46 array(
47 'name' => 'subscrpt_status',
48 'placeholder' => __( 'All Status', 'subscription' ),
49 'value' => $status,
50 'options' => array(
51 array(
52 'value' => 'active',
53 'label' => __( 'Active', 'subscription' ),
54 ),
55 array(
56 'value' => 'pending',
57 'label' => __( 'Pending', 'subscription' ),
58 ),
59 array(
60 'value' => 'cancelled',
61 'label' => __( 'Cancelled', 'subscription' ),
62 ),
63 array(
64 'value' => 'expired',
65 'label' => __( 'Expired', 'subscription' ),
66 ),
67 array(
68 'value' => 'draft',
69 'label' => __( 'Draft', 'subscription' ),
70 ),
71 array(
72 'value' => 'trash',
73 'label' => __( 'Trash', 'subscription' ),
74 ),
75 ),
76 )
77 );
78 ?>
79
80 <?php
81 $date_options = array();
82 foreach ( $months as $val => $label ) {
83 $date_options[] = array(
84 'value' => $val,
85 'label' => $label,
86 );
87 }
88 wpsubs_render_adv_select(
89 array(
90 'name' => 'date_filter',
91 'placeholder' => __( 'All Dates', 'subscription' ),
92 'value' => $date_filter,
93 'options' => $date_options,
94 )
95 );
96 ?>
97
98 <div class="wpsubs-search">
99 <div class="wpsubs-input-wrap wpsubs-input-wrap--icon-l">
100 <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>
101 <input type="search" name="s" value="<?php echo esc_attr( $search ); ?>" placeholder="<?php esc_attr_e( 'Search subscriptions...', 'subscription' ); ?>" class="wpsubs-input" />
102 </div>
103 </div>
104
105 <button type="submit" name="filter_action" value="filter" class="wpsubs-btn wpsubs-btn--outline">
106 <?php esc_html_e( 'Filter', 'subscription' ); ?>
107 </button>
108
109 <?php if ( $filters_active ) : ?>
110 <a href="<?php echo esc_url( remove_query_arg( array( 'subscrpt_status', 'date_filter', 's', 'title', 'paged' ) ) ); ?>" class="wpsubs-btn wpsubs-btn--outline">
111 <?php esc_html_e( 'Clear', 'subscription' ); ?>
112 </a>
113 <?php endif; ?>
114
115 <div class="wpsubs-toolbar__spacer"></div>
116
117 <?php
118 $current_per_page = isset( $_GET['per_page'] ) ? intval( wp_unslash( $_GET['per_page'] ) ) : 20;
119 wpsubs_render_adv_select(
120 array(
121 'name' => 'per_page',
122 'value' => (string) $current_per_page,
123 'options' => array(
124 array(
125 'value' => '10',
126 'label' => __( '10 / page', 'subscription' ),
127 ),
128 array(
129 'value' => '20',
130 'label' => __( '20 / page', 'subscription' ),
131 ),
132 array(
133 'value' => '50',
134 'label' => __( '50 / page', 'subscription' ),
135 ),
136 array(
137 'value' => '100',
138 'label' => __( '100 / page', 'subscription' ),
139 ),
140 ),
141 'align' => 'right',
142 'id' => 'wpsubs-per-page-select',
143 )
144 );
145 ?>
146
147 <?php
148 // Bulk action advanced-select + hidden submit.
149 $bulk_options = 'trash' === $status
150 ? array(
151 array(
152 'value' => 'restore',
153 'label' => __( 'Restore selected', 'subscription' ),
154 ),
155 array(
156 'value' => 'delete',
157 'label' => __( 'Delete permanently', 'subscription' ),
158 'danger' => true,
159 'confirm' => __( 'Delete selected subscriptions permanently? This cannot be undone.', 'subscription' ),
160 ),
161 )
162 : array(
163 array(
164 'value' => 'trash',
165 'label' => __( 'Move to trash', 'subscription' ),
166 'danger' => true,
167 'confirm' => __( 'Move selected subscriptions to trash?', 'subscription' ),
168 ),
169 );
170
171 wpsubs_render_adv_select(
172 array(
173 'name' => 'action',
174 'placeholder' => __( 'Select Action', 'subscription' ),
175 'value' => '-1',
176 'options' => $bulk_options,
177 'id' => 'wpsubs-bulk-action-select',
178 'align' => 'right',
179 )
180 );
181 ?>
182 <button type="submit" name="bulk_action" value="1" id="wpsubs-bulk-action-submit" style="display:none;" aria-hidden="true"></button>
183
184 <?php
185 if ( 'trash' === $status && ! empty( $subscriptions ) ) :
186 $empty_trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=clean_trash&sub_id=all' ), 'wpsubs_action_clean_trash' );
187 ?>
188 <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' ); ?>')">
189 <?php esc_html_e( 'Empty Trash', 'subscription' ); ?>
190 </a>
191 <?php endif; ?>
192
193 </div><!-- /.wpsubs-toolbar -->
194
195 <!-- Table card -->
196 <h2 class="screen-reader-text"><?php esc_html_e( 'Subscriptions list', 'subscription' ); ?></h2>
197 <div class="wpsubs-table-card">
198 <table class="wpsubs-table">
199 <thead>
200 <tr>
201 <th class="wpsubs-col--check">
202 <input type="checkbox" id="cb-select-all" class="wpsubs-checkbox">
203 </th>
204 <th style="width:1%;white-space:nowrap;"><?php esc_html_e( 'Status', 'subscription' ); ?></th>
205 <th><?php esc_html_e( 'Subscription', 'subscription' ); ?></th>
206 <th><?php esc_html_e( 'Customer', 'subscription' ); ?></th>
207 <th><?php esc_html_e( 'Amount', 'subscription' ); ?></th>
208 <th><?php esc_html_e( 'Started', 'subscription' ); ?></th>
209 <th><?php esc_html_e( 'Next Renewal', 'subscription' ); ?></th>
210 <th class="wpsubs-col--actions"><?php esc_html_e( 'Actions', 'subscription' ); ?></th>
211 </tr>
212 </thead>
213 <tbody>
214
215 <?php if ( ! empty( $subscriptions ) ) : ?>
216 <?php
217 foreach ( $subscriptions as $subscription ) :
218 $subscription_id = $subscription->ID;
219 $subscription_data = SpringDevs\Subscription\Illuminate\Helper::get_subscription_data( $subscription_id );
220
221 $subscrpt_status = $subscription_data['status'] ?? '';
222 $subscrpt_status = empty( $subscrpt_status ) ? get_post_status( $subscription_id ) : $subscrpt_status;
223
224 $order_id = $subscription_data['order']['order_id'] ?? 0;
225 $order_item_id = $subscription_data['order']['order_item_id'] ?? 0;
226 $order = $order_id ? wc_get_order( $order_id ) : null;
227 $order_item = $order ? $order->get_item( $order_item_id ) : null;
228 $product_name = $order_item ? $order_item->get_name() : '-';
229
230 $product_id = $subscription_data['product']['variation_id'] ?: ( $subscription_data['product']['product_id'] ?? 0 );
231 $product_url = $product_id ? get_edit_post_link( $product_id ) : '';
232
233 $customer = $order ? $order->get_formatted_billing_full_name() : '-';
234 $customer_id = $order ? $order->get_customer_id() : 0;
235 $customer_url = $customer_id ? admin_url( 'user-edit.php?user_id=' . $customer_id ) : '';
236 $customer_email = $order ? $order->get_billing_email() : '';
237
238 $start_date = $subscription_data['start_date'] ? strtotime( $subscription_data['start_date'] ) : 0;
239 $renewal_date = $subscription_data['next_date'] ? strtotime( $subscription_data['next_date'] ) : 0;
240
241 $is_trash = 'trash' === $subscription->post_status;
242 $is_grace_period = isset( $subscription_data['grace_period'] );
243 $grace_remaining = $subscription_data['grace_period']['remaining_days'] ?? 0;
244
245 // Amount + timing
246 $quantity = $order_item ? (int) $order_item->get_quantity() : 1;
247 $price = '' !== ( $subscription_data['price'] ?? '' )
248 ? (float) $subscription_data['price'] * max( 1, $quantity )
249 : '';
250 $timing_per = $subscription_data['schedule']['timing_per'] ?? '';
251 $timing_option = $subscription_data['schedule']['timing_option'] ?? '';
252 $timing_label = '';
253 if ( $timing_per && $timing_option ) {
254 $timing_label = ( (int) $timing_per > 1 )
255 ? $timing_per . ' ' . $timing_option . 's'
256 : $timing_option;
257 }
258
259 // Avatar
260 $name_parts = array_values( array_filter( explode( ' ', trim( $customer ) ) ) );
261 $initials = '?';
262 if ( $name_parts ) {
263 $initials = '';
264 foreach ( array_slice( $name_parts, 0, 2 ) as $part ) {
265 $initials .= strtoupper( $part[0] );
266 }
267 }
268 $color_slot = ord( strtolower( $initials[0] ?? 'a' ) ) % 8;
269
270 // Action URLs
271 $nonce_action = 'wpsubs_action_' . $subscription->ID;
272 $view_url = get_edit_post_link( $subscription->ID );
273 $trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=trash&sub_id=' . $subscription->ID ), $nonce_action );
274 $delete_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=delete&sub_id=' . $subscription->ID ), $nonce_action );
275 $restore_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=restore&sub_id=' . $subscription->ID ), $nonce_action );
276
277 // Status badge
278 $badge_mod_map = array(
279 'active' => 'active',
280 'pending' => 'pending',
281 'pe_cancelled' => 'pending-cancel',
282 'cancelled' => 'cancelled',
283 'expired' => 'expired',
284 'draft' => 'draft',
285 'trash' => 'trash',
286 );
287 $badge_mod = $badge_mod_map[ $subscrpt_status ] ?? 'expired';
288 $verbose_status = SpringDevs\Subscription\Illuminate\Helper::get_verbose_status( $subscrpt_status );
289
290 if ( $is_grace_period && $grace_remaining > 0 ) {
291 $badge_mod = 'active';
292 $verbose_status = __( 'Active', 'subscription' );
293 }
294 ?>
295 <tr>
296 <!-- Checkbox -->
297 <td class="wpsubs-col--check">
298 <input type="checkbox" name="subscription_ids[]" value="<?php echo esc_attr( $subscription->ID ); ?>" class="wpsubs-checkbox wpsubs-row-check">
299 </td>
300
301 <!-- Status badge -->
302 <td style="white-space:nowrap;">
303 <span class="wpsubs-badge wpsubs-badge--<?php echo esc_attr( $badge_mod ); ?>">
304 <?php echo esc_html( $verbose_status ); ?>
305 <?php if ( $is_grace_period && $grace_remaining > 0 ) : ?>
306 <span class="dashicons dashicons-warning" style="font-size:11px;width:11px;height:11px;color:#d97706;" title="<?php echo esc_attr( sprintf( __( '%d days remaining in grace period', 'subscription' ), $grace_remaining ) ); ?>"></span>
307 <?php endif; ?>
308 </span>
309 </td>
310
311 <!-- Subscription: product name + subscription ID -->
312 <td>
313 <a href="<?php echo esc_url( $view_url ); ?>" class="wpsubs-cell-title" style="font-weight:600;"><?php echo esc_html( $product_name ); ?></a>
314 <span class="wpsubs-cell-id">#<?php echo (int) $subscription->ID; ?></span>
315 </td>
316
317 <!-- Customer avatar + name + email -->
318 <td>
319 <div class="wpsubs-customer">
320 <div class="wpsubs-avatar" data-color="<?php echo (int) $color_slot; ?>">
321 <?php echo esc_html( $initials ); ?>
322 </div>
323 <div class="wpsubs-customer__info">
324 <?php if ( $customer_url ) : ?>
325 <a href="<?php echo esc_url( $customer_url ); ?>" class="wpsubs-customer__name"><?php echo esc_html( $customer ); ?></a>
326 <?php else : ?>
327 <span class="wpsubs-customer__name"><?php echo esc_html( $customer ); ?></span>
328 <?php endif; ?>
329 <?php if ( $customer_email ) : ?>
330 <span class="wpsubs-customer__sub"><?php echo esc_html( $customer_email ); ?></span>
331 <?php endif; ?>
332 </div>
333 </div>
334 </td>
335
336 <!-- Subscription amount + timing -->
337 <td style="white-space:nowrap;">
338 <?php if ( '' !== $price ) : ?>
339 <span class="wpsubs-cell-title" style="font-variant-numeric:tabular-nums;"><?php echo wp_kses_post( wc_price( $price ) ); ?></span>
340 <?php if ( $quantity > 1 ) : ?>
341 <span class="wpsubs-cell-id"><?php echo wp_kses_post( wc_price( (float) $subscription_data['price'] ) ); ?> &times; <?php echo (int) $quantity; ?></span>
342 <?php endif; ?>
343 <?php if ( $timing_label ) : ?>
344 <span class="wpsubs-cell-id">/ <?php echo esc_html( $timing_label ); ?></span>
345 <?php endif; ?>
346 <?php else : ?>
347 <span class="wpsubs-cell--muted">&#8212;</span>
348 <?php endif; ?>
349 </td>
350
351 <!-- Start date -->
352 <td class="wpsubs-col--nowrap">
353 <?php if ( $start_date ) : ?>
354 <span class="wpsubs-cell-title"><?php echo esc_html( wp_date( get_option( 'date_format' ), $start_date ) ); ?></span>
355 <span class="wpsubs-cell-id"><?php echo esc_html( wp_date( get_option( 'time_format' ), $start_date ) ); ?></span>
356 <?php else : ?>
357 <span class="wpsubs-cell--muted">&#8212;</span>
358 <?php endif; ?>
359 </td>
360
361 <!-- Renewal date -->
362 <td class="wpsubs-col--nowrap">
363 <?php if ( $renewal_date ) : ?>
364 <span class="wpsubs-cell-title"><?php echo esc_html( wp_date( get_option( 'date_format' ), $renewal_date ) ); ?></span>
365 <span class="wpsubs-cell-id"><?php echo esc_html( wp_date( get_option( 'time_format' ), $renewal_date ) ); ?></span>
366 <?php else : ?>
367 <span class="wpsubs-cell--muted">&#8212;</span>
368 <?php endif; ?>
369 </td>
370
371 <!-- Actions -->
372 <td class="wpsubs-cell--actions">
373 <div class="wpsubs-row-actions">
374 <button type="button" class="wpsubs-row-actions__trigger" aria-label="<?php esc_attr_e( 'Row actions', 'subscription' ); ?>">···</button>
375 <div class="wpsubs-dropdown">
376 <a href="<?php echo esc_url( $view_url ); ?>" class="wpsubs-dropdown__item"><?php esc_html_e( 'View / Edit', 'subscription' ); ?></a>
377 <div class="wpsubs-dropdown__divider"></div>
378 <?php if ( ! $is_trash ) : ?>
379 <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>
380 <?php else : ?>
381 <a href="<?php echo esc_url( $restore_url ); ?>" class="wpsubs-dropdown__item"><?php esc_html_e( 'Restore', 'subscription' ); ?></a>
382 <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>
383 <?php endif; ?>
384 </div>
385 </div>
386 </td>
387 </tr>
388 <?php endforeach; ?>
389
390 <?php else : ?>
391 <tr>
392 <td colspan="8">
393 <div class="wpsubs-empty">
394 <div class="wpsubs-empty__icon">📋</div>
395 <div class="wpsubs-empty__title"><?php esc_html_e( 'No subscriptions found', 'subscription' ); ?></div>
396 <div class="wpsubs-empty__desc"><?php esc_html_e( 'Subscriptions will appear here once customers complete checkout.', 'subscription' ); ?></div>
397 </div>
398 </td>
399 </tr>
400 <?php endif; ?>
401
402 </tbody>
403 </table>
404
405 <!-- Pagination inside table card -->
406 <?php
407 $start_item = $total > 0 ? ( ( $paged - 1 ) * $per_page ) + 1 : 0;
408 $end_item = min( $paged * $per_page, $total );
409 $base_url = remove_query_arg( 'paged' );
410 $total_pages = max( 1, $max_num_pages );
411
412 // Build page range: first, last, current ± 1 neighbours, ellipsis for gaps.
413 $nearby = array();
414 for ( $i = 1; $i <= $total_pages; $i++ ) {
415 if ( $i === 1 || $i === $total_pages || abs( $i - $paged ) <= 1 ) {
416 $nearby[] = $i;
417 }
418 }
419 $page_range = array();
420 $prev_page = null;
421 foreach ( $nearby as $p ) {
422 if ( null !== $prev_page ) {
423 $gap = $p - $prev_page;
424 if ( $gap === 2 ) {
425 $page_range[] = $prev_page + 1; // single hidden page — show it directly
426 } elseif ( $gap > 2 ) {
427 $page_range[] = null; // ellipsis
428 }
429 }
430 $page_range[] = $p;
431 $prev_page = $p;
432 }
433 ?>
434 <div class="wpsubs-pagination">
435 <span class="wpsubs-pagination__info">
436 <?php
437 echo esc_html(
438 sprintf(
439 /* translators: 1: first item number, 2: last item number, 3: total count */
440 __( 'Showing %1$s–%2$s of %3$s subscriptions', 'subscription' ),
441 number_format_i18n( $start_item ),
442 number_format_i18n( $end_item ),
443 number_format_i18n( $total )
444 )
445 );
446 ?>
447 </span>
448 <div class="wpsubs-pagination__nav">
449
450 <?php // Previous button ?>
451 <?php if ( $paged > 1 ) : ?>
452 <a href="
453 <?php
454 echo esc_url(
455 add_query_arg(
456 array(
457 'paged' => $paged - 1,
458 'per_page' => $per_page,
459 ),
460 $base_url
461 )
462 );
463 ?>
464 " class="wpsubs-pagination__btn" aria-label="<?php esc_attr_e( 'Previous page', 'subscription' ); ?>">&#8249;</a>
465 <?php else : ?>
466 <span class="wpsubs-pagination__btn wpsubs-pagination__btn--disabled" aria-hidden="true">&#8249;</span>
467 <?php endif; ?>
468
469 <?php // Numbered pages + ellipsis ?>
470 <?php foreach ( $page_range as $p ) : ?>
471 <?php if ( null === $p ) : ?>
472 <span class="wpsubs-pagination__ellipsis" aria-hidden="true"></span>
473 <?php elseif ( $p === $paged ) : ?>
474 <span class="wpsubs-pagination__btn wpsubs-pagination__btn--active" aria-current="page"><?php echo (int) $p; ?></span>
475 <?php else : ?>
476 <a href="
477 <?php
478 echo esc_url(
479 add_query_arg(
480 array(
481 'paged' => $p,
482 'per_page' => $per_page,
483 ),
484 $base_url
485 )
486 );
487 ?>
488 " class="wpsubs-pagination__btn"><?php echo (int) $p; ?></a>
489 <?php endif; ?>
490 <?php endforeach; ?>
491
492 <?php // Next button ?>
493 <?php if ( $paged < $total_pages ) : ?>
494 <a href="
495 <?php
496 echo esc_url(
497 add_query_arg(
498 array(
499 'paged' => $paged + 1,
500 'per_page' => $per_page,
501 ),
502 $base_url
503 )
504 );
505 ?>
506 " class="wpsubs-pagination__btn" aria-label="<?php esc_attr_e( 'Next page', 'subscription' ); ?>">&#8250;</a>
507 <?php else : ?>
508 <span class="wpsubs-pagination__btn wpsubs-pagination__btn--disabled" aria-hidden="true">&#8250;</span>
509 <?php endif; ?>
510
511 </div>
512 </div>
513
514 </div><!-- /.wpsubs-table-card -->
515
516 </form>
517
518 </div>
519
520 <script>
521 ( function () {
522 var selectAll = document.getElementById( 'cb-select-all' );
523 var form = document.getElementById( 'subscriptions-form' );
524
525 function rowChecks() {
526 return form ? Array.from( form.querySelectorAll( '.wpsubs-row-check' ) ) : [];
527 }
528
529 function syncSelectAll() {
530 if ( ! selectAll ) return;
531 var checks = rowChecks();
532 var checked = checks.filter( function ( cb ) { return cb.checked; } );
533 checks.forEach( function ( cb ) {
534 var row = cb.closest( 'tr' );
535 if ( row ) row.classList.toggle( 'wpsubs-row--selected', cb.checked );
536 } );
537 selectAll.indeterminate = checked.length > 0 && checked.length < checks.length;
538 selectAll.checked = checks.length > 0 && checked.length === checks.length;
539 }
540
541 if ( selectAll ) {
542 selectAll.addEventListener( 'change', function () {
543 rowChecks().forEach( function ( cb ) { cb.checked = selectAll.checked; } );
544 syncSelectAll();
545 } );
546 }
547
548 if ( form ) {
549 form.addEventListener( 'change', function ( e ) {
550 if ( e.target.classList.contains( 'wpsubs-row-check' ) ) syncSelectAll();
551 } );
552 }
553
554 // ── Advanced-select event handlers ──────────────────────────
555 var bulkSubmit = document.getElementById( 'wpsubs-bulk-action-submit' );
556 document.addEventListener( 'wpsubs:select', function ( e ) {
557 var id = e.target && e.target.id;
558
559 // Bulk action: submit via hidden button
560 if ( id === 'wpsubs-bulk-action-select' && bulkSubmit ) {
561 bulkSubmit.click();
562 return;
563 }
564
565 // Per-page: auto-submit as a filter action
566 if ( id === 'wpsubs-per-page-select' && form ) {
567 var fi = document.createElement( 'input' );
568 fi.type = 'hidden';
569 fi.name = 'filter_action';
570 fi.value = 'filter';
571 form.appendChild( fi );
572 form.submit();
573 }
574 } );
575
576 // ── Row action dropdowns ─────────────────────────────────────
577 function closeAllRowMenus() {
578 document.querySelectorAll( '.wpsubs-row-actions--open' ).forEach( function ( el ) {
579 el.classList.remove( 'wpsubs-row-actions--open' );
580 var d = el.querySelector( '.wpsubs-dropdown' );
581 if ( d ) d.classList.remove( 'wpsubs-dropdown--open' );
582 } );
583 }
584
585 document.addEventListener( 'click', function ( e ) {
586 var trigger = e.target.closest( '.wpsubs-row-actions__trigger' );
587
588 document.querySelectorAll( '.wpsubs-row-actions--open' ).forEach( function ( el ) {
589 if ( el !== ( trigger && trigger.closest( '.wpsubs-row-actions' ) ) ) {
590 el.classList.remove( 'wpsubs-row-actions--open' );
591 var d = el.querySelector( '.wpsubs-dropdown' );
592 if ( d ) d.classList.remove( 'wpsubs-dropdown--open' );
593 }
594 } );
595
596 if ( ! trigger ) return;
597
598 e.stopPropagation();
599 var wrapper = trigger.closest( '.wpsubs-row-actions' );
600 var dropdown = wrapper && wrapper.querySelector( '.wpsubs-dropdown' );
601 if ( ! wrapper || ! dropdown ) return;
602
603 var isOpen = wrapper.classList.contains( 'wpsubs-row-actions--open' );
604 wrapper.classList.toggle( 'wpsubs-row-actions--open', ! isOpen );
605 dropdown.classList.toggle( 'wpsubs-dropdown--open', ! isOpen );
606 } );
607
608 document.addEventListener( 'keydown', function ( e ) {
609 if ( e.key === 'Escape' ) closeAllRowMenus();
610 } );
611
612 syncSelectAll();
613 }() );
614 </script>
615