PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.3.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.3.0
2.4.0 2.3.0 2.2.0 2.1.0 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 All 65 releases
subscription / includes / Admin / views / plans / tab-products.php

tab-products.php in Subscriptions for WooCommerce with Stripe Recurring Payments 2.3.0, at includes/Admin/views/plans/tab-products.php

392 lines 23.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plan detail - Products tab.
4 *
5 * Lists the products attached to this plan group. In free this is read-only:
6 * products are connected from their own editor. With Pro active, an "Add
7 * Products" bulk picker is available here.
8 *
9 * @var array $plan Plan (PlanPresenter shape).
10 *
11 * @package SpringDevs\Subscription\Admin
12 */
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 $pro_active = function_exists( 'subscrpt_pro_activated' ) && subscrpt_pro_activated();
19 // Products can only be attached once the plan group has at least one term.
20 $has_terms = ! empty( $plan['terms'] );
21 ?>
22 <div style="padding-top:8px;">
23
24 <?php if ( empty( $plan['products'] ) ) : ?>
25 <div class="wpsubs-empty">
26 <div class="wpsubs-empty__icon">📦</div>
27 <h3 class="wpsubs-empty__title"><?php esc_html_e( 'No products connected', 'subscription' ); ?></h3>
28 <?php if ( ! $has_terms ) : ?>
29 <p class="wpsubs-empty__desc"><?php esc_html_e( 'Add at least one duration on the Durations tab before attaching products.', 'subscription' ); ?></p>
30 <?php else : ?>
31 <p class="wpsubs-empty__desc"><?php esc_html_e( 'Add products to this plan and set their prices here, or connect a product from its own Subscription tab.', 'subscription' ); ?></p>
32 <button type="button" class="wpsubs-btn wpsubs-btn--primary" style="margin-top:20px;" data-wpsubs-modal-open="subscrpt-add-product">
33 <span class="dashicons dashicons-plus-alt2" style="font-size:16px;width:16px;height:16px;line-height:1;"></span>
34 <?php esc_html_e( 'Add Products', 'subscription' ); ?>
35 </button>
36 <?php endif; ?>
37 </div>
38 <?php else : ?>
39
40 <?php
41 /**
42 * Render the price table for a set of relation rows (Duration /
43 * Regular / Offer / Status). Each editable cell has a read view + a
44 * hidden input revealed by the card's Edit/Save buttons (JS).
45 *
46 * When $one_time is provided, a "One-time purchase" row is appended: its
47 * price is the product's/variation's native WooCommerce price and the
48 * card's single Save persists both the plan relations and the one-time
49 * native price. Pro-gated (read-only + Pro badge when Pro is inactive).
50 *
51 * @param array $rows Price rows (term / regular / offer / …).
52 * @param array|null $one_time One-time data: enabled, regular, offer.
53 */
54 $subscrpt_render_rows = function ( $rows, $one_time = null ) {
55 ?>
56 <table class="wpsubs-table">
57 <thead>
58 <tr>
59 <th><?php esc_html_e( 'Duration', 'subscription' ); ?></th>
60 <th>
61 <?php esc_html_e( 'Regular Price', 'subscription' ); ?>
62 <?php echo wp_kses_post( wpsubs_render_hint( __( 'The recurring price charged each billing cycle.', 'subscription' ) ) ); ?>
63 </th>
64 <th>
65 <?php esc_html_e( 'Offer Price', 'subscription' ); ?>
66 <?php echo wp_kses_post( wpsubs_render_hint( __( 'A discounted recurring price shown in place of the regular price. Leave empty for no discount.', 'subscription' ) ) ); ?>
67 </th>
68 <th><?php esc_html_e( 'Status', 'subscription' ); ?></th>
69 </tr>
70 </thead>
71 <tbody>
72 <?php foreach ( $rows as $row ) : ?>
73 <tr data-subscrpt-relation="<?php echo esc_attr( $row['relation_id'] ); ?>" data-plan-id="<?php echo esc_attr( isset( $row['plan_id'] ) ? $row['plan_id'] : '' ); ?>">
74
75 <td><?php echo esc_html( $row['term'] ); ?></td>
76 <td>
77 <span class="subscrpt-pe-view"><?php echo esc_html( $row['regular'] ); ?></span>
78 <input type="number" min="0" step="0.01" class="wpsubs-input subscrpt-pe-edit" data-field="regular_price" value="<?php echo esc_attr( $row['regular_raw'] ); ?>" placeholder="0.00" style="display:none;width:120px;max-width:100%;" />
79 </td>
80 <td>
81 <span class="subscrpt-pe-view"><?php echo ! empty( $row['has_offer'] ) ? esc_html( $row['offer'] ) : '<span style="color:var(--wpsubs-text-subtle);">&mdash;</span>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Static dash markup / escaped price. ?></span>
82 <input type="number" min="0" step="0.01" class="wpsubs-input subscrpt-pe-edit" data-field="sale_price" value="<?php echo esc_attr( $row['offer_raw'] ); ?>" placeholder="0.00" style="display:none;width:120px;max-width:100%;" />
83 </td>
84 <td>
85 <span class="subscrpt-pe-view">
86 <?php if ( ! empty( $row['exclude'] ) ) : ?>
87 <span class="wpsubs-badge wpsubs-badge--draft"><?php esc_html_e( 'Disabled', 'subscription' ); ?></span>
88 <?php else : ?>
89 <span class="wpsubs-badge wpsubs-badge--active"><?php esc_html_e( 'Enabled', 'subscription' ); ?></span>
90 <?php endif; ?>
91 </span>
92 <label class="wpsubs-settings-toggle-label subscrpt-pe-edit" style="display:none;align-items:center;" title="<?php esc_attr_e( 'Enable this plan for the product', 'subscription' ); ?>">
93 <input type="checkbox" class="wpsubs-toggle" data-field="enabled" <?php checked( empty( $row['exclude'] ) ); ?> />
94 <span class="wpsubs-toggle-ui" aria-hidden="true"></span>
95 </label>
96 </td>
97 </tr>
98 <?php endforeach; ?>
99
100 <?php
101 if ( is_array( $one_time ) ) :
102 $subscrpt_ot_on = ! empty( $one_time['enabled'] );
103 $subscrpt_ot_reg = (string) $one_time['regular'];
104 $subscrpt_ot_off = (string) $one_time['offer'];
105 $subscrpt_ot_dash = '<span style="color:var(--wpsubs-text-subtle);">&mdash;</span>';
106 $subscrpt_ot_rdsp = '' !== $subscrpt_ot_reg ? esc_html( \SpringDevs\Subscription\Admin\PlanPresenter::money( (float) $subscrpt_ot_reg ) ) : $subscrpt_ot_dash;
107 $subscrpt_ot_odsp = '' !== $subscrpt_ot_off ? esc_html( \SpringDevs\Subscription\Admin\PlanPresenter::money( (float) $subscrpt_ot_off ) ) : $subscrpt_ot_dash;
108 ?>
109 <tr data-subscrpt-onetime data-subscrpt-onetime-row style="border-top:2px solid var(--wpsubs-border,#e5e7eb);background:var(--wpsubs-surface-muted,#f6f7f7);">
110 <td>
111 <span style="display:inline-flex;align-items:center;gap:6px;">
112 <span class="dashicons dashicons-cart" style="flex:0 0 auto;font-size:15px;width:15px;height:15px;color:var(--wpsubs-text-subtle);"></span>
113 <?php esc_html_e( 'One-time purchase', 'subscription' ); ?>
114 <?php echo wp_kses_post( wpsubs_render_hint( __( 'A single, non-recurring purchase at the product’s regular WooCommerce price.', 'subscription' ) ) ); ?>
115 </span>
116 </td>
117 <?php
118 // Two independent conditions, so two wrappers: the outer is the card's
119 // read/edit cycle, the inner is the toggle. One element carrying both
120 // would have them fight over `display`.
121 $subscrpt_ot_hidden = $subscrpt_ot_on ? '' : ' style="display:none;"';
122 ?>
123 <td>
124 <span class="subscrpt-pe-view"><?php echo wp_kses_post( $subscrpt_ot_rdsp ); ?></span>
125 <span class="subscrpt-pe-edit" style="display:none;">
126 <span data-subscrpt-onetime-price<?php echo $subscrpt_ot_hidden; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fixed literal. ?>>
127 <input type="number" min="0" step="0.01" class="wpsubs-input" data-ot-field="price" value="<?php echo esc_attr( $subscrpt_ot_reg ); ?>" placeholder="0.00" style="max-width:110px;" />
128 </span>
129 </span>
130 </td>
131 <td>
132 <span class="subscrpt-pe-view"><?php echo wp_kses_post( $subscrpt_ot_odsp ); ?></span>
133 <span class="subscrpt-pe-edit" style="display:none;">
134 <span data-subscrpt-onetime-price<?php echo $subscrpt_ot_hidden; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fixed literal. ?>>
135 <input type="number" min="0" step="0.01" class="wpsubs-input" data-ot-field="offer" value="<?php echo esc_attr( $subscrpt_ot_off ); ?>" placeholder="<?php esc_attr_e( 'No offer', 'subscription' ); ?>" style="max-width:110px;" />
136 </span>
137 </span>
138 </td>
139 <td>
140 <span class="subscrpt-pe-view">
141 <?php if ( $subscrpt_ot_on ) : ?>
142 <span class="wpsubs-badge wpsubs-badge--active"><?php esc_html_e( 'Enabled', 'subscription' ); ?></span>
143 <?php else : ?>
144 <span class="wpsubs-badge wpsubs-badge--draft"><?php esc_html_e( 'Disabled', 'subscription' ); ?></span>
145 <?php endif; ?>
146 </span>
147 <label class="wpsubs-settings-toggle-label subscrpt-pe-edit" style="display:none;align-items:center;" title="<?php esc_attr_e( 'Offer this product for one-time purchase', 'subscription' ); ?>">
148 <input type="checkbox" class="wpsubs-toggle" data-subscrpt-onetime-enable <?php checked( $subscrpt_ot_on ); ?> />
149 <span class="wpsubs-toggle-ui" aria-hidden="true"></span>
150 </label>
151 </td>
152 </tr>
153 <?php endif; ?>
154 </tbody>
155 </table>
156 <?php
157 };
158
159 /**
160 * Render the price-card header controls: Edit / Cancel / Save.
161 * JS toggles the card into edit mode and saves the plan prices via REST.
162 */
163 $subscrpt_price_actions = function () {
164 ?>
165 <button type="button" class="wpsubs-btn wpsubs-btn--outline wpsubs-btn--sm" data-subscrpt-edit-prices>
166 <span class="dashicons dashicons-edit" style="font-size:15px;width:15px;height:15px;line-height:1;"></span>
167 <?php esc_html_e( 'Edit prices', 'subscription' ); ?>
168 </button>
169 <button type="button" class="wpsubs-btn wpsubs-btn--outline wpsubs-btn--sm" data-subscrpt-cancel-prices style="display:none;"><?php esc_html_e( 'Cancel', 'subscription' ); ?></button>
170 <button type="button" class="wpsubs-btn wpsubs-btn--primary wpsubs-btn--sm" data-subscrpt-save-prices style="display:none;"><?php esc_html_e( 'Save', 'subscription' ); ?></button>
171 <?php
172 };
173
174 /**
175 * Render a simple product's one-time purchase card (its own toggle + Save).
176 * One-time is product-specific: the price is the product’s native
177 * WooCommerce price (regular = one-time, sale = offer). Pro-gated: disabled
178 * with a Pro badge when Pro is inactive. Variable products use the
179 * per-variation one-time row inside render_rows().
180 *
181 * @param array $product Product entry (PlanPresenter shape).
182 */
183 $subscrpt_render_onetime = function ( $product ) {
184 $subscrpt_on = ! empty( $product['one_time_on'] );
185 $subscrpt_show_body = $subscrpt_on;
186 ?>
187 <div style="width:90%;border-top:1px dashed var(--wpsubs-border-strong,#d1d5db);margin:16px auto 0;"></div>
188 <div data-subscrpt-onetime data-subscrpt-onetime-card data-product-id="<?php echo esc_attr( $product['id'] ); ?>" style="border:1px solid var(--wpsubs-border,#e5e7eb);border-radius:8px;background:var(--wpsubs-surface,#fff);margin-top:14px;">
189 <div style="display:flex;align-items:center;gap:10px;padding:11px 14px;">
190 <span class="dashicons dashicons-cart" style="flex:0 0 auto;font-size:16px;width:16px;height:16px;color:var(--wpsubs-text-subtle);"></span>
191 <strong style="font-size:13px;color:var(--wpsubs-text);"><?php esc_html_e( 'One-time purchase', 'subscription' ); ?></strong>
192 <?php echo wp_kses_post( wpsubs_render_hint( __( 'This is the product’s regular WooCommerce price, charged when a customer buys it once instead of subscribing.', 'subscription' ) ) ); ?>
193 <span class="wpsubs-toolbar__spacer"></span>
194 <?php // The switch saves itself (plans.js): Save lives in the price row below, which hides while it is off. ?>
195 <label class="wpsubs-settings-toggle-label" style="display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--wpsubs-text-muted);cursor:pointer;">
196 <input type="checkbox" class="wpsubs-toggle" data-subscrpt-onetime-enable <?php checked( $subscrpt_on ); ?> />
197 <span class="wpsubs-toggle-ui" aria-hidden="true"></span>
198 <span><?php esc_html_e( 'Allow one-time purchase', 'subscription' ); ?></span>
199 </label>
200 </div>
201 <div data-subscrpt-onetime-body style="padding:12px 14px;border-top:1px solid var(--wpsubs-border,#e5e7eb);<?php echo $subscrpt_show_body ? '' : 'display:none;'; ?>">
202 <?php // Flex on an inner row: the body's own inline display is what the switch toggles. ?>
203 <div style="display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap;">
204 <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;max-width:460px;flex:1 1 360px;">
205 <label style="display:flex;flex-direction:column;gap:5px;font-size:12px;color:var(--wpsubs-text-muted);">
206 <?php esc_html_e( 'Regular Price', 'subscription' ); ?>
207 <input type="number" min="0" step="0.01" class="wpsubs-input" data-ot-field="price" value="<?php echo esc_attr( $product['ot_regular'] ); ?>" placeholder="0.00" style="width:100%;box-sizing:border-box;" />
208 </label>
209 <label style="display:flex;flex-direction:column;gap:5px;font-size:12px;color:var(--wpsubs-text-muted);">
210 <?php esc_html_e( 'Offer Price', 'subscription' ); ?>
211 <input type="number" min="0" step="0.01" class="wpsubs-input" data-ot-field="offer" value="<?php echo esc_attr( $product['ot_offer'] ); ?>" placeholder="<?php esc_attr_e( 'No offer', 'subscription' ); ?>" style="width:100%;box-sizing:border-box;" />
212 </label>
213 </div>
214 <button type="button" class="wpsubs-btn wpsubs-btn--primary wpsubs-btn--sm" data-subscrpt-onetime-save style="margin-left:auto;"><?php esc_html_e( 'Save', 'subscription' ); ?></button>
215 </div>
216 </div>
217 </div>
218 <?php
219 };
220
221 /**
222 * Render a simple product's "Allow User Cancellation" card. The switch
223 * saves itself (plans.js), as the one-time switch does. Variable products
224 * set it per variation in the product editor.
225 *
226 * @param array $product Product entry (PlanPresenter shape).
227 */
228 $subscrpt_render_user_cancel = function ( $product ) {
229 ?>
230 <div data-subscrpt-user-cancel-card data-product-id="<?php echo esc_attr( $product['id'] ); ?>" style="border:1px solid var(--wpsubs-border,#e5e7eb);border-radius:8px;background:var(--wpsubs-surface,#fff);margin-top:14px;">
231 <div style="display:flex;align-items:center;gap:10px;padding:11px 14px;">
232 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" width="16" height="16" fill="currentColor" aria-hidden="true" focusable="false" style="flex:0 0 auto;color:var(--wpsubs-text-subtle);"><path d="M431.2 476.5L163.5 208.8C141.1 240.2 128 278.6 128 320C128 426 214 512 320 512C361.5 512 399.9 498.9 431.2 476.5zM476.5 431.2C498.9 399.8 512 361.4 512 320C512 214 426 128 320 128C278.5 128 240.1 141.1 208.8 163.5L476.5 431.2zM64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576C178.6 576 64 461.4 64 320z"/></svg>
233 <strong style="font-size:13px;color:var(--wpsubs-text);"><?php esc_html_e( 'User cancellation', 'subscription' ); ?></strong>
234 <?php echo wp_kses_post( wpsubs_render_hint( __( 'Allow subscribers to cancel their subscription manually from their account dashboard. Applies to subscriptions bought after the change.', 'subscription' ) ) ); ?>
235 <span class="wpsubs-toolbar__spacer"></span>
236 <label class="wpsubs-settings-toggle-label" style="display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--wpsubs-text-muted);cursor:pointer;">
237 <input type="checkbox" class="wpsubs-toggle" data-subscrpt-user-cancel-enable <?php checked( ! empty( $product['user_cancel'] ) ); ?> />
238 <span class="wpsubs-toggle-ui" aria-hidden="true"></span>
239 <span><?php esc_html_e( 'Allow User Cancellation', 'subscription' ); ?></span>
240 </label>
241 </div>
242 </div>
243 <?php
244 };
245 ?>
246
247 <div data-subscrpt-browse data-per-page="10">
248 <div class="wpsubs-toolbar" style="margin-bottom:14px;">
249 <div class="wpsubs-search">
250 <div class="wpsubs-input-wrap wpsubs-input-wrap--icon-l">
251 <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>
252 <input type="search" class="wpsubs-input" placeholder="<?php esc_attr_e( 'Search products...', 'subscription' ); ?>" data-subscrpt-browse-search />
253 </div>
254 </div>
255
256 <?php
257 wpsubs_render_per_page_select(
258 array(
259 'name' => 'subscrpt_products_per_page',
260 'attrs' => array( 'data-subscrpt-browse-perpage' => '1' ),
261 )
262 );
263 ?>
264
265 <div class="wpsubs-toolbar__spacer"></div>
266
267 <button type="button" class="wpsubs-btn wpsubs-btn--outline" data-wpsubs-modal-open="subscrpt-bulk-price">
268 <span class="dashicons dashicons-money-alt" style="font-size:16px;width:16px;height:16px;line-height:1;"></span>
269 <?php esc_html_e( 'Quick price update', 'subscription' ); ?>
270 </button>
271 </div>
272
273 <div class="wpsubs-accordion" data-multi="1" data-subscrpt-product-list data-subscrpt-sortable="products">
274 <?php $subscrpt_is_first = true; ?>
275 <?php foreach ( $plan['products'] as $product ) : ?>
276 <?php $subscrpt_panel_id = 'subscrpt-prod-' . (int) $product['id']; ?>
277 <div class="wpsubs-accordion__item" data-subscrpt-browse-item data-subscrpt-sort-item="<?php echo esc_attr( $product['id'] ); ?>" data-name="<?php echo esc_attr( strtolower( $product['name'] ) ); ?>" data-pid="<?php echo esc_attr( $product['id'] ); ?>">
278 <div style="display:flex;align-items:stretch;background:var(--wpsubs-surface-muted,#f9fafb);">
279 <span style="display:flex;align-items:center;padding-left:10px;">
280 <button type="button" class="wpsubs-sort-handle" data-subscrpt-sort-handle title="<?php esc_attr_e( 'Drag to reorder', 'subscription' ); ?>" aria-label="<?php esc_attr_e( 'Drag to reorder', 'subscription' ); ?>">
281 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="currentColor" aria-hidden="true"><circle cx="7" cy="5" r="1.5"/><circle cx="13" cy="5" r="1.5"/><circle cx="7" cy="10" r="1.5"/><circle cx="13" cy="10" r="1.5"/><circle cx="7" cy="15" r="1.5"/><circle cx="13" cy="15" r="1.5"/></svg>
282 </button>
283 </span>
284 <button type="button" class="wpsubs-accordion__header wpsubs-accordion__header--chevron-start" style="flex:1 1 auto;min-width:0;background:transparent;" aria-controls="<?php echo esc_attr( $subscrpt_panel_id ); ?>" aria-expanded="<?php echo $subscrpt_is_first ? 'true' : 'false'; ?>">
285 <span style="display:flex;align-items:center;gap:10px;min-width:0;">
286 <span style="flex:0 0 auto;width:44px;height:44px;border-radius:6px;background:var(--wpsubs-surface,#fff);border:1px solid var(--wpsubs-border,#e5e7eb);overflow:hidden;display:flex;align-items:center;justify-content:center;">
287 <?php if ( ! empty( $product['image'] ) ) : ?>
288 <img src="<?php echo esc_url( $product['image'] ); ?>" alt="" style="width:100%;height:100%;object-fit:cover;display:block;" />
289 <?php else : ?>
290 <span class="dashicons dashicons-format-image" style="font-size:22px;width:22px;height:22px;color:var(--wpsubs-text-subtle);"></span>
291 <?php endif; ?>
292 </span>
293 <span style="display:flex;flex-direction:column;min-width:0;line-height:1.35;">
294 <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"><?php echo esc_html( $product['name'] ); ?></span>
295 <span style="font-size:12px;font-weight:400;color:var(--wpsubs-text-muted);white-space:nowrap;">
296 <?php
297 /* translators: %d: product ID. */
298 $subscrpt_desc = sprintf( __( 'ID: %d', 'subscription' ), (int) $product['id'] );
299 if ( ! empty( $product['is_variable'] ) ) {
300 $subscrpt_var_count = count( $product['variations'] );
301 /* translators: %d: number of variations. */
302 $subscrpt_desc .= ' &middot; ' . sprintf( _n( '%d variation', '%d variations', $subscrpt_var_count, 'subscription' ), $subscrpt_var_count );
303 }
304 echo wp_kses_post( $subscrpt_desc );
305 ?>
306 </span>
307 </span>
308 </span>
309 </button>
310 <div style="display:flex;align-items:center;gap:8px;padding:0 12px 0 4px;">
311 <?php if ( ! empty( $product['edit_url'] ) ) : ?>
312 <a href="<?php echo esc_url( $product['edit_url'] ); ?>" class="wpsubs-btn wpsubs-btn--muted wpsubs-btn--sm" target="_blank" rel="noopener">
313 <span class="dashicons dashicons-external" aria-hidden="true"></span>
314 <?php esc_html_e( 'Edit on WooCommerce', 'subscription' ); ?>
315 </a>
316 <?php endif; ?>
317 <?php if ( ! empty( $product['view_url'] ) ) : ?>
318 <a href="<?php echo esc_url( $product['view_url'] ); ?>" class="wpsubs-btn wpsubs-btn--muted wpsubs-btn--sm" target="_blank" rel="noopener">
319 <span class="dashicons dashicons-visibility" aria-hidden="true"></span>
320 <?php esc_html_e( 'Preview product', 'subscription' ); ?>
321 </a>
322 <?php endif; ?>
323 <?php if ( $pro_active ) : ?>
324 <button type="button" class="wpsubs-btn wpsubs-btn--outline wpsubs-btn--sm wpsubs-btn--danger" data-subscrpt-remove-product="<?php echo esc_attr( $product['id'] ); ?>">
325 <?php esc_html_e( 'Remove', 'subscription' ); ?>
326 </button>
327 <?php endif; ?>
328 </div>
329 </div>
330
331 <div class="wpsubs-accordion__panel" id="<?php echo esc_attr( $subscrpt_panel_id ); ?>" style="padding:14px 16px;background:var(--wpsubs-surface-muted,#f9fafb);" <?php echo $subscrpt_is_first ? '' : 'hidden'; ?>>
332 <?php if ( ! empty( $product['is_variable'] ) ) : ?>
333 <div style="display:flex;flex-direction:column;gap:14px;">
334 <?php foreach ( $product['variations'] as $variation ) : ?>
335 <div data-subscrpt-price-card data-product-id="<?php echo esc_attr( $product['id'] ); ?>" data-variation-id="<?php echo esc_attr( $variation['vid'] ); ?>" style="border:1px solid var(--wpsubs-border,#e5e7eb);border-radius:8px;background:var(--wpsubs-surface,#fff);">
336 <div style="display:flex;align-items:center;gap:10px;padding:11px 14px;border-bottom:1px solid var(--wpsubs-border,#e5e7eb);">
337 <span class="dashicons dashicons-image-filter" style="flex:0 0 auto;font-size:16px;width:16px;height:16px;color:var(--wpsubs-text-subtle);"></span>
338 <strong style="font-size:13px;color:var(--wpsubs-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"><?php echo esc_html( $variation['name'] ); ?></strong>
339 <span class="wpsubs-toolbar__spacer"></span>
340 <?php $subscrpt_price_actions(); ?>
341 <?php if ( $pro_active ) : ?>
342 <button type="button" class="wpsubs-btn wpsubs-btn--outline wpsubs-btn--sm wpsubs-btn--danger" data-subscrpt-remove-variation data-oid="<?php echo esc_attr( $product['id'] ); ?>" data-vid="<?php echo esc_attr( $variation['vid'] ); ?>">
343 <?php esc_html_e( 'Remove', 'subscription' ); ?>
344 </button>
345 <?php endif; ?>
346 </div>
347 <?php
348 $subscrpt_render_rows(
349 $variation['rows'],
350 array(
351 'enabled' => ! empty( $variation['one_time_on'] ),
352 'regular' => $variation['ot_regular'],
353 'offer' => $variation['ot_offer'],
354 )
355 );
356 ?>
357 </div>
358 <?php endforeach; ?>
359 </div>
360 <?php else : ?>
361 <div data-subscrpt-price-card data-product-id="<?php echo esc_attr( $product['id'] ); ?>" style="border:1px solid var(--wpsubs-border,#e5e7eb);border-radius:8px;background:var(--wpsubs-surface,#fff);">
362 <div style="display:flex;align-items:center;gap:8px;padding:11px 14px;border-bottom:1px solid var(--wpsubs-border,#e5e7eb);">
363 <strong style="font-size:13px;color:var(--wpsubs-text);"><?php esc_html_e( 'Pricing', 'subscription' ); ?></strong>
364 <span class="wpsubs-toolbar__spacer"></span>
365 <?php $subscrpt_price_actions(); ?>
366 </div>
367 <?php $subscrpt_render_rows( $product['rows'] ); ?>
368 </div>
369 <?php $subscrpt_render_onetime( $product ); ?>
370 <?php $subscrpt_render_user_cancel( $product ); ?>
371 <?php endif; ?>
372 </div>
373 </div>
374 <?php $subscrpt_is_first = false; ?>
375 <?php endforeach; ?>
376 </div>
377
378 <p data-subscrpt-browse-empty style="display:none;padding:20px 4px;color:var(--wpsubs-text-subtle);font-size:13px;text-align:center;">
379 <?php esc_html_e( 'No products match your search.', 'subscription' ); ?>
380 </p>
381
382 <div data-subscrpt-browse-pager style="margin-top:14px;"></div>
383 </div>
384
385 <?php endif; ?>
386
387 </div>
388
389 <?php
390 require __DIR__ . '/modal-add-product.php';
391 require __DIR__ . '/modal-bulk-price.php';
392