PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.11.1
Subscriptions for WooCommerce with Stripe Recurring Payments v1.11.1
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 / assets / js / admin / onboarding-wizard.js

onboarding-wizard.js in Subscriptions for WooCommerce with Stripe Recurring Payments 1.11.1, at assets/js/admin/onboarding-wizard.js

580 lines 19.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Onboarding Wizard — SPA-style
3 * All pages rendered at once; JS controls section visibility
4 * PHP values passed via localized script data (see Menu::enqueue_admin_assets)
5 */
6 (function ($) {
7 "use strict";
8
9 var Wizard = {
10 currentPage: 1,
11 ajaxUrl: "",
12 subscriptionsUrl: "",
13
14 init: function () {
15 this.ajaxUrl = subscrpt_wizard.ajax_url;
16 this.subscriptionsUrl = subscrpt_wizard.subscriptions_url;
17 this.bindEvents();
18 this.showRelevantProductSection();
19 this.initPageIndicator();
20 this.initLivePreview();
21 this.initStepperState();
22 },
23
24 initPageIndicator: function () {
25 this.currentPage = parseInt($("#subscrpt-current-page").text(), 10) || 1;
26 },
27
28 initStepperState: function () {
29 var page = parseInt($("#subscrpt-wizard-page").val(), 10) || 1;
30 if (page > 1) {
31 $("#subscrpt-stepper").show();
32 $(".wpsubs-wizard-stepper__step").removeClass("active done");
33 $(".wpsubs-wizard-stepper__step").each(function () {
34 var step = parseInt($(this).data("step"), 10);
35 if (step < page) {
36 $(this).addClass("done");
37 } else if (step === page) {
38 $(this).addClass("active");
39 }
40 });
41 }
42 },
43
44 bindEvents: function () {
45 // Page 1
46 $(document).on("click", "#subscrpt-btn-start", $.proxy(this.goToPage2, this));
47 $(document).on("click", "#subscrpt-btn-skip", $.proxy(this.skip, this));
48
49 // Page 2: product toggle
50 $(document).on("click", "#subscrpt-btn-create-new", $.proxy(this.showNewProduct, this));
51 $(document).on("click", "#subscrpt-btn-use-existing", $.proxy(this.showExistingProduct, this));
52
53 // Page 2: product search
54 $(document).on("focus", "#subscrpt-product-search-input", $.proxy(this.openProductSearch, this));
55 $(document).on("input", "#subscrpt-product-search-input", $.proxy(this.filterProducts, this));
56 $(document).on("click", ".wpsubs-p2-product-search__item", $.proxy(this.onProductItemClick, this));
57 $(document).on("click", function (e) {
58 if (!$(e.target).closest(".wpsubs-p2-product-search").length) {
59 $("#subscrpt-product-search-dropdown").hide();
60 }
61 });
62 $(document).on("click", "#subscrpt-btn-clear-product", $.proxy(this.clearProductSelection, this));
63 $(document).on("click", ".wpsubs-p2-variation-item", $.proxy(this.onVariationSelect, this));
64
65 // Page 2: navigation
66 $(document).on("click", "#subscrpt-btn-back", $.proxy(this.goToPage1, this));
67 $(document).on("click", "#subscrpt-btn-save", $.proxy(this.savePage2, this));
68
69 // Page 3
70 $(document).on("click", "#subscrpt-btn-add-another", $.proxy(this.restart, this));
71 $(document).on("click", "#subscrpt-btn-start-over", $.proxy(this.restart, this));
72 },
73
74 // ----- Page transitions -----
75
76 goToPage1: function () {
77 this.switchSection(1);
78 },
79
80 goToPage2: function () {
81 this.switchSection(2);
82 },
83
84 goToPage3: function (productId) {
85 $("#subscrpt-product-id").val(productId || 0);
86 this.switchSection(3);
87 },
88
89 switchSection: function (pageNum) {
90 this.currentPage = pageNum;
91
92 $("#subscrpt-wizard-page").val(pageNum);
93
94 if (pageNum === 1) {
95 $("#subscrpt-stepper").hide();
96 } else {
97 $("#subscrpt-stepper").show();
98 $(".wpsubs-wizard-stepper__step").removeClass("active done");
99 $(".wpsubs-wizard-stepper__step").each(function () {
100 var step = parseInt($(this).data("step"), 10);
101 if (step < pageNum) {
102 $(this).addClass("done");
103 }
104 });
105 $('.wpsubs-wizard-stepper__step[data-step="' + pageNum + '"]').addClass("active");
106 }
107
108 $(".wpsubs-wizard-section").removeClass("active");
109 $("#subscrpt-section-" + pageNum).addClass("active");
110 },
111
112 // ----- Page 1 actions -----
113
114 skip: function (e) {
115 e.preventDefault();
116 window.location.href = this.subscriptionsUrl;
117 },
118
119 // ----- Page 2 actions -----
120
121 showNewProduct: function (e) {
122 e.preventDefault();
123 $(".wpsubs-p2-option-card, .product-toggle-btn").removeClass("active");
124 $(e.currentTarget).addClass("active");
125 $("#subscrpt-existing-product-fields").hide();
126 // Restore name field editability
127 $("#subscrpt_product_name").prop("readonly", false).val("");
128 $("#subscrpt_product_price").val("");
129 $("#subscrpt-btn-save").html("Create product &rsaquo;");
130 this.updatePreview();
131 },
132
133 showExistingProduct: function (e) {
134 e.preventDefault();
135 $(".wpsubs-p2-option-card, .product-toggle-btn").removeClass("active");
136 $(e.currentTarget).addClass("active");
137 $("#subscrpt-existing-product-fields").show();
138 $("#subscrpt-btn-save").html("Update product &rsaquo;");
139 // If already has a selection, re-show chip
140 var selectedVal = $("#subscrpt_existing_product").val();
141 if (selectedVal) {
142 this.showProductChip($("#subscrpt_existing_product option:selected"));
143 }
144 },
145
146 openProductSearch: function () {
147 $(".wpsubs-p2-product-search__item").show();
148 $(".wpsubs-p2-product-search__empty").hide();
149 $("#subscrpt-product-search-dropdown").show();
150 },
151
152 filterProducts: function (e) {
153 var q = $(e.target).val().toLowerCase().trim();
154 $("#subscrpt-product-search-dropdown").show();
155 var visible = 0;
156 $(".wpsubs-p2-product-search__item").each(function () {
157 var name = $(this).data("name") ? $(this).data("name").toLowerCase() : "";
158 var sku = $(this).data("sku") ? String($(this).data("sku")).toLowerCase() : "";
159 if (!q || name.indexOf(q) >= 0 || sku.indexOf(q) >= 0) {
160 $(this).show();
161 visible++;
162 } else {
163 $(this).hide();
164 }
165 });
166 $(".wpsubs-p2-product-search__empty").toggle(visible === 0);
167 },
168
169 onProductItemClick: function (e) {
170 var item = $(e.currentTarget);
171 if (item.hasClass("wpsubs-p2-product-search__item--locked")) {
172 return;
173 }
174 var id = String(item.data("id"));
175 var name = item.data("name") || "";
176 var price = item.data("price") != null ? String(item.data("price")) : "";
177 var type = item.data("type") || "";
178 var sku = item.data("sku") ? String(item.data("sku")) : "";
179 var productType = item.data("product-type") || "";
180
181 $("#subscrpt-existing-product-hidden").val(id);
182 $("#subscrpt-product-search-dropdown").hide();
183 this.showProductChip(name, price, type, sku);
184 $("#subscrpt_product_name").val(name);
185 $("#subscrpt_product_price").val(price);
186
187 // Reset variation picker state
188 $("#subscrpt-variation-id-hidden").val("");
189
190 if (productType === "variable") {
191 // Variation picker handles subscription field autofill
192 this.fetchAndShowVariations(id);
193 } else {
194 $("#subscrpt-variation-picker-wrap").hide();
195
196 // Autofill subscription fields from product meta
197 var billingPeriod = item.data("billing-period");
198 var billingPer = item.data("billing-per");
199 var trialPer = item.data("trial-per");
200 var signupFee = item.data("signup-fee");
201
202 if (billingPeriod) {
203 this.setAdvSelectValue("#subscrpt-billing-period-select", String(billingPeriod));
204 }
205 if (billingPer) {
206 $("#subscrpt_billing_per").val(billingPer);
207 $("#subscrpt_billing_per_visible").val(billingPer);
208 }
209 if (trialPer !== undefined && trialPer !== "") {
210 $("#subscrpt_trial_timing_per").val(trialPer);
211 }
212 if (signupFee !== undefined && signupFee !== "") {
213 $("#subscrpt_signup_fee").val(signupFee);
214 }
215 }
216
217 this.updatePreview();
218 },
219
220 fetchAndShowVariations: function (productId) {
221 var self = this;
222 var wrap = $("#subscrpt-variation-picker-wrap");
223 var list = $("#subscrpt-variation-picker-list");
224
225 wrap.show();
226 list.html(
227 '<p class="wpsubs-p2-variation-picker__loading">' +
228 (window.subscrpt_wizard_i18n ? subscrpt_wizard_i18n.loading : "Loading variations…") +
229 "</p>",
230 );
231
232 $.post(
233 this.ajaxUrl,
234 {
235 action: "subscrpt_get_product_variations",
236 nonce: $("#subscrpt_wizard_nonce").val(),
237 product_id: productId,
238 },
239 function (response) {
240 if (response.success && response.data && response.data.length) {
241 self.renderVariationPicker(response.data);
242 } else {
243 wrap.hide();
244 }
245 },
246 ).fail(function () {
247 wrap.hide();
248 });
249 },
250
251 renderVariationPicker: function (variations) {
252 var self = this;
253 var symbol = (window.subscrpt_wizard && subscrpt_wizard.currency_symbol) || "$";
254 var html = '<p class="wpsubs-p2-variation-picker__label">Select a variation</p>';
255 html += '<div class="wpsubs-p2-variation-picker__list">';
256
257 variations.forEach(function (v) {
258 var meta = v.sku ? "SKU " + v.sku : "";
259 var priceDisplay =
260 v.price !== "" && v.price !== null && v.price !== undefined ? symbol + parseFloat(v.price).toFixed(2) : "";
261
262 html +=
263 '<div class="wpsubs-p2-variation-item"' +
264 ' data-id="' +
265 self.escAttr(String(v.id)) +
266 '"' +
267 ' data-label="' +
268 self.escAttr(v.label) +
269 '"' +
270 ' data-price="' +
271 self.escAttr(String(v.price !== null && v.price !== undefined ? v.price : "")) +
272 '"' +
273 ' data-sku="' +
274 self.escAttr(v.sku || "") +
275 '"' +
276 ' data-billing-period="' +
277 self.escAttr(v.billing_period || "") +
278 '"' +
279 ' data-billing-per="' +
280 self.escAttr(String(v.billing_per || "1")) +
281 '"' +
282 ' data-trial-per="' +
283 self.escAttr(String(v.trial_per !== null && v.trial_per !== undefined ? v.trial_per : "")) +
284 '"' +
285 ' data-signup-fee="' +
286 self.escAttr(String(v.signup_fee || "")) +
287 '">' +
288 '<div class="wpsubs-p2-variation-item__check">&#10003;</div>' +
289 '<div class="wpsubs-p2-variation-item__info">' +
290 '<p class="wpsubs-p2-variation-item__name">' +
291 self.escHtml(v.label) +
292 "</p>" +
293 (meta ? '<p class="wpsubs-p2-variation-item__meta">' + self.escHtml(meta) + "</p>" : "") +
294 "</div>" +
295 (priceDisplay
296 ? '<span class="wpsubs-p2-variation-item__price">' + self.escHtml(priceDisplay) + "</span>"
297 : "") +
298 "</div>";
299 });
300
301 html += "</div>";
302 $("#subscrpt-variation-picker-list").html(html);
303 $("#subscrpt-variation-picker-wrap").show();
304 },
305
306 onVariationSelect: function (e) {
307 var item = $(e.currentTarget);
308 $(".wpsubs-p2-variation-item").removeClass("selected");
309 item.addClass("selected");
310
311 $("#subscrpt-variation-id-hidden").val(String(item.data("id")));
312
313 // Autofill subscription fields and price from variation meta
314 var price = item.data("price");
315 var billingPeriod = item.data("billing-period");
316 var billingPer = item.data("billing-per");
317 var trialPer = item.data("trial-per");
318 var signupFee = item.data("signup-fee");
319
320 if (price !== undefined && price !== "") {
321 $("#subscrpt_product_price").val(price);
322 }
323 if (billingPeriod) {
324 this.setAdvSelectValue("#subscrpt-billing-period-select", String(billingPeriod));
325 }
326 if (billingPer) {
327 $("#subscrpt_billing_per").val(billingPer);
328 $("#subscrpt_billing_per_visible").val(billingPer);
329 }
330 if (trialPer !== undefined && trialPer !== "") {
331 $("#subscrpt_trial_timing_per").val(trialPer);
332 }
333 if (signupFee !== undefined && signupFee !== "") {
334 $("#subscrpt_signup_fee").val(signupFee);
335 }
336
337 this.updatePreview();
338 },
339
340 escHtml: function (str) {
341 return String(str).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
342 },
343
344 escAttr: function (str) {
345 return String(str)
346 .replace(/&/g, "&amp;")
347 .replace(/"/g, "&quot;")
348 .replace(/'/g, "&#39;")
349 .replace(/</g, "&lt;")
350 .replace(/>/g, "&gt;");
351 },
352
353 setAdvSelectValue: function (selector, value) {
354 var el = $(selector);
355 if (!el.length) return;
356 var menuItem = el.find('.wpsubs-adv-select__item[data-value="' + value + '"]');
357 var label = menuItem.length ? menuItem.find(".wpsubs-adv-select__item-label").text().trim() : value;
358 el.find('input[type="hidden"]').val(value);
359 el.find(".wpsubs-adv-select__label").text(label);
360 },
361
362 showProductChip: function (name, price, type, sku) {
363 var initials = name
364 .split(" ")
365 .filter(Boolean)
366 .slice(0, 2)
367 .map(function (w) {
368 return w[0].toUpperCase();
369 })
370 .join("");
371
372 var meta = [sku ? "SKU " + sku : null, type, price ? "$" + parseFloat(price).toFixed(2) : null]
373 .filter(Boolean)
374 .join(" · ");
375
376 $("#p2-chip-avatar").text(initials);
377 $("#p2-chip-name").text(name);
378 $("#p2-chip-meta").text(meta);
379 $("#subscrpt-product-select-wrap").hide();
380 $("#subscrpt-selected-product-chip").show();
381 },
382
383 clearProductSelection: function () {
384 $("#subscrpt-existing-product-hidden").val("");
385 $("#subscrpt-product-search-input").val("");
386 $(".wpsubs-p2-product-search__item").show();
387 $(".wpsubs-p2-product-search__empty").hide();
388 $("#subscrpt-product-search-dropdown").hide();
389 $("#subscrpt-selected-product-chip").hide();
390 $("#subscrpt-product-select-wrap").show();
391 $("#subscrpt_product_name").val("");
392 $("#subscrpt_product_price").val("");
393 // Reset variation picker
394 $("#subscrpt-variation-picker-wrap").hide();
395 $("#subscrpt-variation-picker-list").empty();
396 $("#subscrpt-variation-id-hidden").val("");
397 this.updatePreview();
398 },
399
400 showRelevantProductSection: function () {
401 var activeBtn = $(".product-toggle-btn.active");
402 var mode = activeBtn.length ? activeBtn.data("mode") : "new";
403
404 if (mode === "existing") {
405 $("#subscrpt-existing-product-fields").show();
406 var selectedId = $("#subscrpt-existing-product-hidden").val();
407 if (selectedId) {
408 var item = $(".wpsubs-p2-product-search__item[data-id='" + selectedId + "']");
409 if (item.length) {
410 var name = item.data("name") || "";
411 var price = item.data("price") != null ? String(item.data("price")) : "";
412 var type = item.data("type") || "";
413 var sku = item.data("sku") ? String(item.data("sku")) : "";
414 this.showProductChip(name, price, type, sku);
415 }
416 }
417 } else {
418 $("#subscrpt-existing-product-fields").hide();
419 }
420 },
421
422 // ----- Live preview -----
423
424 initLivePreview: function () {
425 var self = this;
426 $(document).on("input", "#subscrpt_product_name", function () {
427 self.updatePreview();
428 });
429 $(document).on("input", "#subscrpt_product_price", function () {
430 self.updatePreview();
431 });
432 $(document).on("wpsubs:select", "#subscrpt-billing-period-select", function () {
433 self.updatePreview();
434 });
435 },
436
437 updatePreview: function () {
438 var name = $("#subscrpt_product_name").val() || "Your product";
439 var price = $("#subscrpt_product_price").val() || "0.00";
440 var period = $("input[name='subscrpt_billing_period']").val() || "months";
441
442 $("#p2-preview-name").text(name);
443 $("#p2-preview-price").text(parseFloat(price).toFixed(2));
444 $("#p2-preview-period").text(period);
445 },
446
447 // ----- Save & validate -----
448
449 savePage2: function (e) {
450 e.preventDefault();
451
452 if (!this.validatePage2()) {
453 return;
454 }
455
456 var productMode = $(".product-toggle-btn.active").data("mode") || "new";
457 var confirmMsg =
458 productMode === "new"
459 ? "Create this subscription product?"
460 : "Apply these subscription settings to the selected product?";
461
462 if (!confirm(confirmMsg)) {
463 return;
464 }
465
466 var data = {
467 action: "subscrpt_save_wizard_page2",
468 nonce: $("#subscrpt_wizard_nonce").val(),
469 product_mode: $(".product-toggle-btn.active").data("mode") || "new",
470 product_name: $("#subscrpt_product_name").val(),
471 product_price: $("#subscrpt_product_price").val(),
472 existing_product_id: $("#subscrpt-existing-product-hidden").val(),
473 variation_id: $("#subscrpt-variation-id-hidden").val(),
474 timing_option: $("#subscrpt_timing_option").val(),
475 billing_per: $("#subscrpt_billing_per").val(),
476 billing_period: $("input[name='subscrpt_billing_period']").val(),
477 trial_timing_per: $("#subscrpt_trial_timing_per").val(),
478 signup_fee: $("#subscrpt_signup_fee").val(),
479 // hidden compat fields
480 trial_enabled: 0,
481 trial_timing_option: $("input[name='subscrpt_trial_timing_option']").val() || "days",
482 length_enabled: 0,
483 length_per: "",
484 length_option: "months",
485 };
486
487 var self = this;
488
489 $.post(this.ajaxUrl, data, function (response) {
490 if (response.success) {
491 window.location.reload();
492 } else {
493 alert(response.data.message || "Something went wrong. Please try again.");
494 }
495 }).fail(function () {
496 alert("Server error. Please try again.");
497 });
498 },
499
500 validatePage2: function () {
501 var isValid = true;
502 var messages = [];
503 var productMode = $(".product-toggle-btn.active").data("mode") || "new";
504 var productName = $("#subscrpt_product_name").val().trim();
505 var price = $("#subscrpt_product_price").val().trim();
506 var existing = $("#subscrpt-existing-product-hidden").val();
507 var billing = $("input[name='subscrpt_billing_period']").val();
508
509 if (productMode === "new") {
510 if (!productName) {
511 messages.push("Product name is required.");
512 isValid = false;
513 }
514 } else {
515 if (!existing) {
516 messages.push("Please select an existing product.");
517 isValid = false;
518 }
519 if (
520 existing &&
521 $("#subscrpt-variation-picker-wrap").is(":visible") &&
522 !$("#subscrpt-variation-id-hidden").val()
523 ) {
524 messages.push("Please select a variation.");
525 isValid = false;
526 }
527 }
528
529 if (!price || isNaN(parseFloat(price)) || parseFloat(price) < 0) {
530 messages.push("Please enter a valid price.");
531 isValid = false;
532 }
533
534 if (!billing) {
535 messages.push("Please select a billing period.");
536 isValid = false;
537 }
538
539 if (!isValid) {
540 alert(messages.join("\n"));
541 }
542
543 return isValid;
544 },
545
546 // ----- Page 3 actions -----
547
548 restart: function (e) {
549 e.preventDefault();
550 var self = this;
551 $.post(
552 this.ajaxUrl,
553 {
554 action: "subscrpt_reset_wizard",
555 nonce: $("#subscrpt_wizard_nonce").val(),
556 },
557 function () {
558 self.switchSection(1);
559 $("#subscrpt-current-page").text("1");
560 $("#subscrpt_product_name").val("");
561 $("#subscrpt_product_price").val("");
562 $("#subscrpt_timing_option").val("never");
563 $("#subscrpt_billing_per").val("1");
564 $("input[name='subscrpt_billing_period']").val("months");
565 $("#subscrpt_trial_timing_per").val("0");
566 $("#subscrpt_signup_fee").val("");
567 // Reset chip
568 $("#subscrpt-selected-product-chip").hide();
569 $("#subscrpt-product-select-wrap").show();
570 $("#subscrpt_existing_product").val("");
571 },
572 );
573 },
574 };
575
576 $(document).ready(function () {
577 Wizard.init();
578 });
579 })(jQuery);
580