PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.2.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.2.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.2.0, at includes/Admin/views/plans/tab-products.php

366 lines 21.8 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 <div data-subscrpt-browse data-per-page="10">
223 <div class="wpsubs-toolbar" style="margin-bottom:14px;">
224 <div class="wpsubs-search">
225 <div class="wpsubs-input-wrap wpsubs-input-wrap--icon-l">
226 <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>
227 <input type="search" class="wpsubs-input" placeholder="<?php esc_attr_e( 'Search products...', 'subscription' ); ?>" data-subscrpt-browse-search />
228 </div>
229 </div>
230
231 <?php
232 wpsubs_render_per_page_select(
233 array(
234 'name' => 'subscrpt_products_per_page',
235 'attrs' => array( 'data-subscrpt-browse-perpage' => '1' ),
236 )
237 );
238 ?>
239
240 <div class="wpsubs-toolbar__spacer"></div>
241
242 <button type="button" class="wpsubs-btn wpsubs-btn--outline" data-wpsubs-modal-open="subscrpt-bulk-price">
243 <span class="dashicons dashicons-money-alt" style="font-size:16px;width:16px;height:16px;line-height:1;"></span>
244 <?php esc_html_e( 'Quick price update', 'subscription' ); ?>
245 </button>
246 </div>
247
248 <div class="wpsubs-accordion" data-multi="1" data-subscrpt-product-list data-subscrpt-sortable="products">
249 <?php $subscrpt_is_first = true; ?>
250 <?php foreach ( $plan['products'] as $product ) : ?>
251 <?php $subscrpt_panel_id = 'subscrpt-prod-' . (int) $product['id']; ?>
252 <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'] ); ?>">
253 <div style="display:flex;align-items:stretch;background:var(--wpsubs-surface-muted,#f9fafb);">
254 <span style="display:flex;align-items:center;padding-left:10px;">
255 <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' ); ?>">
256 <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>
257 </button>
258 </span>
259 <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'; ?>">
260 <span style="display:flex;align-items:center;gap:10px;min-width:0;">
261 <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;">
262 <?php if ( ! empty( $product['image'] ) ) : ?>
263 <img src="<?php echo esc_url( $product['image'] ); ?>" alt="" style="width:100%;height:100%;object-fit:cover;display:block;" />
264 <?php else : ?>
265 <span class="dashicons dashicons-format-image" style="font-size:22px;width:22px;height:22px;color:var(--wpsubs-text-subtle);"></span>
266 <?php endif; ?>
267 </span>
268 <span style="display:flex;flex-direction:column;min-width:0;line-height:1.35;">
269 <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"><?php echo esc_html( $product['name'] ); ?></span>
270 <span style="font-size:12px;font-weight:400;color:var(--wpsubs-text-muted);white-space:nowrap;">
271 <?php
272 /* translators: %d: product ID. */
273 $subscrpt_desc = sprintf( __( 'ID: %d', 'subscription' ), (int) $product['id'] );
274 if ( ! empty( $product['is_variable'] ) ) {
275 $subscrpt_var_count = count( $product['variations'] );
276 /* translators: %d: number of variations. */
277 $subscrpt_desc .= ' &middot; ' . sprintf( _n( '%d variation', '%d variations', $subscrpt_var_count, 'subscription' ), $subscrpt_var_count );
278 }
279 echo wp_kses_post( $subscrpt_desc );
280 ?>
281 </span>
282 </span>
283 </span>
284 </button>
285 <div style="display:flex;align-items:center;gap:8px;padding:0 12px 0 4px;">
286 <?php if ( ! empty( $product['edit_url'] ) ) : ?>
287 <a href="<?php echo esc_url( $product['edit_url'] ); ?>" class="wpsubs-btn wpsubs-btn--muted wpsubs-btn--sm" target="_blank" rel="noopener">
288 <span class="dashicons dashicons-external" aria-hidden="true"></span>
289 <?php esc_html_e( 'Edit on WooCommerce', 'subscription' ); ?>
290 </a>
291 <?php endif; ?>
292 <?php if ( ! empty( $product['view_url'] ) ) : ?>
293 <a href="<?php echo esc_url( $product['view_url'] ); ?>" class="wpsubs-btn wpsubs-btn--muted wpsubs-btn--sm" target="_blank" rel="noopener">
294 <span class="dashicons dashicons-visibility" aria-hidden="true"></span>
295 <?php esc_html_e( 'Preview product', 'subscription' ); ?>
296 </a>
297 <?php endif; ?>
298 <?php if ( $pro_active ) : ?>
299 <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'] ); ?>">
300 <?php esc_html_e( 'Remove', 'subscription' ); ?>
301 </button>
302 <?php endif; ?>
303 </div>
304 </div>
305
306 <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'; ?>>
307 <?php if ( ! empty( $product['is_variable'] ) ) : ?>
308 <div style="display:flex;flex-direction:column;gap:14px;">
309 <?php foreach ( $product['variations'] as $variation ) : ?>
310 <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);">
311 <div style="display:flex;align-items:center;gap:10px;padding:11px 14px;border-bottom:1px solid var(--wpsubs-border,#e5e7eb);">
312 <span class="dashicons dashicons-image-filter" style="flex:0 0 auto;font-size:16px;width:16px;height:16px;color:var(--wpsubs-text-subtle);"></span>
313 <strong style="font-size:13px;color:var(--wpsubs-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"><?php echo esc_html( $variation['name'] ); ?></strong>
314 <span class="wpsubs-toolbar__spacer"></span>
315 <?php $subscrpt_price_actions(); ?>
316 <?php if ( $pro_active ) : ?>
317 <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'] ); ?>">
318 <?php esc_html_e( 'Remove', 'subscription' ); ?>
319 </button>
320 <?php endif; ?>
321 </div>
322 <?php
323 $subscrpt_render_rows(
324 $variation['rows'],
325 array(
326 'enabled' => ! empty( $variation['one_time_on'] ),
327 'regular' => $variation['ot_regular'],
328 'offer' => $variation['ot_offer'],
329 )
330 );
331 ?>
332 </div>
333 <?php endforeach; ?>
334 </div>
335 <?php else : ?>
336 <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);">
337 <div style="display:flex;align-items:center;gap:8px;padding:11px 14px;border-bottom:1px solid var(--wpsubs-border,#e5e7eb);">
338 <strong style="font-size:13px;color:var(--wpsubs-text);"><?php esc_html_e( 'Pricing', 'subscription' ); ?></strong>
339 <span class="wpsubs-toolbar__spacer"></span>
340 <?php $subscrpt_price_actions(); ?>
341 </div>
342 <?php $subscrpt_render_rows( $product['rows'] ); ?>
343 </div>
344 <?php $subscrpt_render_onetime( $product ); ?>
345 <?php endif; ?>
346 </div>
347 </div>
348 <?php $subscrpt_is_first = false; ?>
349 <?php endforeach; ?>
350 </div>
351
352 <p data-subscrpt-browse-empty style="display:none;padding:20px 4px;color:var(--wpsubs-text-subtle);font-size:13px;text-align:center;">
353 <?php esc_html_e( 'No products match your search.', 'subscription' ); ?>
354 </p>
355
356 <div data-subscrpt-browse-pager style="margin-top:14px;"></div>
357 </div>
358
359 <?php endif; ?>
360
361 </div>
362
363 <?php
364 require __DIR__ . '/modal-add-product.php';
365 require __DIR__ . '/modal-bulk-price.php';
366