Skip to content

Commit cf01abf

Browse files
authored
Merge pull request #267 from moderntribe/release/4.11.0
packaged version 4.11.0
2 parents dd5249f + 06cb50d commit cf01abf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1417
-375
lines changed

CHANGELOG.md

+16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
## [4.11.0]
4+
5+
### Added
6+
- Added support for a generic Segment tracking event on order completion via Embedded Checkout.
7+
- Added Customizer option and cart functionality to process coupon/promo codes.
8+
9+
### Changed
10+
- Optimized Brand and Category import. (Less time to import by skipping terms that did not change)
11+
12+
### Fixed
13+
- Addressed issues with the Shipping Calculator not properly accommodating
14+
Free and Ship by Weight/Total options.
15+
- Fixed CLI importer warnings
16+
17+
318
## [4.10.0]
419

520
### Added
@@ -1382,6 +1397,7 @@
13821397
in fact, reset postdata, so far as Gutenberg 3.2.0 is concerned.
13831398

13841399

1400+
[4.11.0]: https://github.com/bigcommerce/bigcommerce-for-wordpress/compare/4.10.0...4.11.0
13851401
[4.10.0]: https://github.com/bigcommerce/bigcommerce-for-wordpress/compare/4.9.0...4.10.0
13861402
[4.9.0]: https://github.com/bigcommerce/bigcommerce-for-wordpress/compare/4.8.0...4.9.0
13871403
[4.8.0]: https://github.com/bigcommerce/bigcommerce-for-wordpress/compare/4.7.0...4.8.0

assets/css/cart-amp.css

+28-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/css/cart-amp.min.css

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/css/master.css

+78-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/css/master.min.css

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/dist/admin/gutenberg/scripts.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/dist/admin/gutenberg/scripts.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/dist/admin/scripts.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/dist/admin/scripts.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/dist/scripts.js

+187-180
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/dist/scripts.min.js

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/dist/vendor.js

+116-115
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/dist/vendor.min.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/src/constants/events.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
// custom events
22
export const AJAX_CART_UPDATE = 'bigcommerce/ajax_cart_update';
33
export const HANDLE_CART_STATE = 'bigcommerce/handle_cart_state';
4+
export const HANDLE_COUPON_CODE = 'bigcommerce/handle_coupon_code';

assets/js/src/public/analytics/segment.js

+35
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ const el = {
1212
segment: tools.getNodes('bc-segment-tracker')[0],
1313
};
1414

15+
/**
16+
* @function handleAddToCartTracker
17+
* @description Event handler for tracking products added to the cart.
18+
* @param e
19+
*/
1520
const handleAddToCartTracker = (e) => {
1621
const cartTrigger = e ? e.detail.cartButton : tools.getNodes('[data-tracking-event="add_to_cart_message"]', false, document, true)[0];
1722

@@ -30,6 +35,11 @@ const handleAddToCartTracker = (e) => {
3035
console.info(`Segment has sent the following cart tracking data to your analytics account(s): ${analyticsData}`);
3136
};
3237

38+
/**
39+
* @function handleClickTracker
40+
* @description Event handler for clicking on products to view PDP or Quick View.
41+
* @param e
42+
*/
3343
const handleClickTracker = (e) => {
3444
const target = e.delegateTarget;
3545
const analyticsData = target.dataset.trackingData;
@@ -46,6 +56,30 @@ const handleClickTracker = (e) => {
4656
console.info(`Segment has sent the following tracking data to your analytics account(s): ${analyticsData}`);
4757
};
4858

59+
/**
60+
* @function handleOrderCompleteTracker
61+
* @description Event handler for embedded checkout order completion.
62+
* @param e
63+
* TODO: This needs to be overhauled once BC can provide proper order data in the ECO response.
64+
*/
65+
const handleOrderCompleteTracker = (e) => {
66+
if (!e.detail) {
67+
return;
68+
}
69+
70+
const cartID = e.detail.cart_id;
71+
analytics.track('BigCommerce Order Completed', {
72+
cart_id: cartID,
73+
});
74+
75+
console.info(`Segment has sent the following tracking data to your analytics account(s): Order Completed. Cart ID: ${cartID}`);
76+
};
77+
78+
/**
79+
* @function gaCrossDomainInit
80+
* @description Enable GA x-domain tracking by default.
81+
* @return {Promise<void>}
82+
*/
4983
const gaCrossDomainInit = async () => {
5084
await analytics.ready(() => {
5185
ga('require', 'linker');
@@ -64,6 +98,7 @@ const bindEvents = () => {
6498
});
6599

66100
on(document, 'bigcommerce/analytics_trigger', handleAddToCartTracker);
101+
on(document, 'bigcommerce/order_complete', handleOrderCompleteTracker);
67102
};
68103

69104
const init = () => {

assets/js/src/public/cart/ajax-items.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { on, trigger } from 'utils/events';
1212
import cartState from 'publicConfig/cart-state';
1313
import { CART_API_BASE } from 'publicConfig/wp-settings';
1414
import { CART_ID_COOKIE_NAME, CART_ITEM_COUNT_COOKIE } from 'bcConstants/cookies';
15-
import { AJAX_CART_UPDATE, HANDLE_CART_STATE } from 'bcConstants/events';
15+
import { AJAX_CART_UPDATE, HANDLE_CART_STATE, HANDLE_COUPON_CODE } from 'bcConstants/events';
1616
import { NLS } from 'publicConfig/i18n';
1717
import { cartEmpty } from './cart-templates';
1818
import { updateMenuQtyTotal, updateCartMenuItem, updateFlatsomeCartMenuQty, updateFlatsomeCartMenuPrice } from './cart-menu-item';
@@ -132,10 +132,12 @@ const updateCartItems = (data = {}) => {
132132
* @param data
133133
*/
134134
const updatedCartTotals = (data = {}) => {
135+
const cartData = data.detail ? data.detail.data : data;
136+
135137
tools.getNodes('bc-cart', true).forEach((cart) => {
136-
const baseAmount = data.subtotal.formatted;
138+
const baseAmount = cartData.subtotal.formatted;
137139
const subTotal = tools.getNodes('.bc-cart-subtotal__amount', false, cart, true)[0];
138-
const taxAmount = data.tax_amount.formatted;
140+
const taxAmount = cartData.tax_amount.formatted;
139141
const taxTotal = tools.getNodes('.bc-cart-tax__amount', false, cart, true)[0];
140142

141143
subTotal.textContent = baseAmount;
@@ -316,6 +318,8 @@ const bindEvents = () => {
316318
delegate(document, '[data-js="bc-cart-item__quantity"]', 'input', handleQtyUpdate);
317319
delegate(document, '[data-js="remove-cart-item"]', 'click', handleCartItemRemoval);
318320
on(document, HANDLE_CART_STATE, handleCartState);
321+
on(document, HANDLE_COUPON_CODE, handleCartState);
322+
on(document, HANDLE_COUPON_CODE, updatedCartTotals);
319323
};
320324

321325
const init = () => {
+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/**
2+
* @module Coupon Codes
3+
* @description Scripts to handle cart submission of coupon codes.
4+
*/
5+
6+
import * as tools from 'utils/tools';
7+
import delegate from 'delegate';
8+
import { wpAPICouponCodes } from 'utils/ajax';
9+
import { on, trigger } from 'utils/events';
10+
import { COUPON_CODE_ADD, COUPON_CODE_REMOVE, AJAX_CART_NONCE } from 'publicConfig/wp-settings';
11+
import { AJAX_CART_UPDATE, HANDLE_CART_STATE, HANDLE_COUPON_CODE } from 'bcConstants/events';
12+
import { NLS } from 'publicConfig/i18n';
13+
import cartState from 'publicConfig/cart-state';
14+
15+
const el = {
16+
container: tools.getNodes('bc-coupon-code')[0],
17+
};
18+
19+
/**
20+
* @function updateCouponDiscount
21+
* @description Update discount amount on qty update or removal. Remove coupon section on empty cart.
22+
* @param e
23+
*/
24+
const updateCouponDiscount = (e) => {
25+
if (!e.detail.cartData) {
26+
el.container.parentNode.removeChild(el.container);
27+
return;
28+
}
29+
30+
el.couponDetails.innerText = `${NLS.cart.coupon_discount}: -${e.detail.cartData.coupons[0].discounted_amount.formatted}`;
31+
};
32+
33+
/**
34+
* @function handleCouponSuccess
35+
* @description Update cart data when a coupon has been applied.
36+
* @param cartObject
37+
*/
38+
const handleCouponSuccess = (cartObject = {}) => {
39+
if (!cartObject) {
40+
return;
41+
}
42+
43+
const couponCode = cartObject.coupons[0].code;
44+
45+
el.container.classList.add('bc-hide-add-form');
46+
el.container.classList.remove('bc-hide-remove-form');
47+
el.addCouponForm.setAttribute('aria-hidden', true);
48+
el.removeCouponForm.setAttribute('aria-hidden', false);
49+
el.cartErrorWrapper.classList.remove('message-active');
50+
el.cartError.innerText = NLS.cart.coupon_success;
51+
el.removeCouponButton.dataset.couponCode = couponCode;
52+
el.removeCouponTitle.innerText = couponCode;
53+
el.couponField.value = '';
54+
el.couponDetails.innerText = `${NLS.cart.coupon_discount}: -${cartObject.coupons[0].discounted_amount.formatted}`;
55+
};
56+
57+
/**
58+
* @function handleCouponRemoval
59+
* @description Update cart data when a coupon has been removed.
60+
*/
61+
const handleCouponRemoval = () => {
62+
el.container.classList.add('bc-hide-remove-form');
63+
el.container.classList.remove('bc-hide-add-form');
64+
el.addCouponForm.setAttribute('aria-hidden', false);
65+
el.removeCouponForm.setAttribute('aria-hidden', true);
66+
el.cartErrorWrapper.classList.remove('message-active');
67+
el.cartError.innerText = NLS.cart.coupon_success;
68+
el.removeCouponButton.dataset.couponCode = '';
69+
el.removeCouponTitle.innerText = '';
70+
el.couponDetails.innerText = '';
71+
};
72+
73+
/**
74+
* @function handleCouponAddError
75+
* @description Handle coupon errors when adding.
76+
*/
77+
const handleCouponAddError = () => {
78+
el.couponField.focus();
79+
el.container.classList.add('bc-hide-remove-form');
80+
el.container.classList.remove('bc-hide-add-form');
81+
el.cartErrorWrapper.classList.add('message-active');
82+
el.cartError.innerText = NLS.cart.coupon_error;
83+
};
84+
85+
/**
86+
* @function handleCouponRemoveError
87+
* @description Handle coupon errors when removing.
88+
*/
89+
const handleCouponRemoveError = () => {
90+
el.cartErrorWrapper.classList.remove('message-active');
91+
el.cartError.innerText = NLS.cart.coupon_removal_error;
92+
el.container.classList.add('bc-hide-remove-form');
93+
el.container.classList.remove('bc-hide-add-form');
94+
};
95+
96+
/**
97+
* @function handleCouponCodeAdd
98+
* @description Main coupon function to apply a coupon to the cart.
99+
*/
100+
const handleCouponCodeAdd = () => {
101+
if (!COUPON_CODE_ADD) {
102+
return;
103+
}
104+
105+
const queryObject = {
106+
coupon_code: el.couponField.value,
107+
};
108+
109+
cartState.isFetching = true;
110+
trigger({ event: HANDLE_CART_STATE, native: false });
111+
112+
wpAPICouponCodes(COUPON_CODE_ADD, queryObject, AJAX_CART_NONCE)
113+
.end((err, res) => {
114+
cartState.isFetching = false;
115+
trigger({ event: HANDLE_CART_STATE, native: false });
116+
117+
if (err || res.body.error) {
118+
console.error(err, res.body ? res.body.error : '');
119+
handleCouponAddError();
120+
return;
121+
}
122+
123+
trigger({ event: HANDLE_COUPON_CODE, data: { data: res.body }, native: false });
124+
handleCouponSuccess(res.body);
125+
});
126+
};
127+
128+
/**
129+
* @function handleCouponCodeRemove
130+
* @description Main coupon function to remove a coupon from the cart.
131+
* @param e
132+
*/
133+
const handleCouponCodeRemove = (e) => {
134+
if (!COUPON_CODE_REMOVE) {
135+
return;
136+
}
137+
138+
const queryObject = {
139+
coupon_code: e.delegateTarget.dataset.couponCode,
140+
};
141+
142+
cartState.isFetching = true;
143+
trigger({ event: HANDLE_CART_STATE, native: false });
144+
145+
wpAPICouponCodes(COUPON_CODE_REMOVE, queryObject, AJAX_CART_NONCE)
146+
.end((err, res) => {
147+
cartState.isFetching = false;
148+
trigger({ event: HANDLE_CART_STATE, native: false });
149+
150+
if (err || res.body.error) {
151+
console.error(err, res.body ? res.body.error : '');
152+
handleCouponRemoveError();
153+
return;
154+
}
155+
156+
handleCouponRemoval();
157+
trigger({ event: HANDLE_COUPON_CODE, data: { data: res.body }, native: false });
158+
});
159+
};
160+
161+
const cacheElements = () => {
162+
el.addCouponForm = tools.getNodes('bc-add-coupon-form', false, el.container)[0];
163+
el.couponField = tools.getNodes('bc-coupon-code-field', false, el.container)[0];
164+
el.removeCouponForm = tools.getNodes('bc-remove-coupon-form', false, el.container)[0];
165+
el.removeCouponButton = tools.getNodes('bc-coupon-code-remove', false, el.container)[0];
166+
el.removeCouponTitle = tools.getNodes('.bc-coupon-name', false, el.removeCouponButton, true)[0];
167+
el.couponDetails = tools.getNodes('bc-coupon-details', false, el.container)[0];
168+
el.cartErrorWrapper = tools.getNodes('.bc-cart-error', false, document, true)[0];
169+
el.cartError = tools.getNodes('bc-cart-error-message', false, el.cartErrorWrapper)[0];
170+
};
171+
172+
const bindEvents = () => {
173+
delegate(el.container, '[data-js="bc-coupon-code-submit"]', 'click', handleCouponCodeAdd);
174+
delegate(el.container, '[data-js="bc-coupon-code-remove"]', 'click', handleCouponCodeRemove);
175+
on(document, AJAX_CART_UPDATE, updateCouponDiscount);
176+
};
177+
178+
const init = () => {
179+
if (!el.container) {
180+
return;
181+
}
182+
183+
cacheElements();
184+
bindEvents();
185+
};
186+
187+
export default init;

assets/js/src/public/cart/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import addToCart from './add-to-cart';
1010
import miniCartWidget from './mini-cart-widget';
1111
import miniCartNav from './mini-cart-nav';
1212
import shippingCalc from './shipping-calculator';
13+
import couponCode from './coupon-code';
1314

1415
const init = () => {
1516
cartItemsAjax();
@@ -19,6 +20,7 @@ const init = () => {
1920
miniCartWidget();
2021
miniCartNav();
2122
shippingCalc();
23+
couponCode();
2224
};
2325

2426
export default init;

assets/js/src/public/cart/shipping-calculator.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as tools from 'utils/tools';
77
import delegate from 'delegate';
88
import cartState from 'publicConfig/cart-state';
99
import { Spinner } from 'spin.js/spin';
10-
import { AJAX_CART_UPDATE, HANDLE_CART_STATE } from 'bcConstants/events';
10+
import { AJAX_CART_UPDATE, HANDLE_CART_STATE, HANDLE_COUPON_CODE } from 'bcConstants/events';
1111
import { SHIPPING_API_ZONES, SHIPPING_API_METHODS } from 'publicConfig/wp-settings';
1212
import { NLS } from 'publicConfig/i18n';
1313
import { wpAPIGetShippingZones, wpAPIGetShippingMethods } from 'utils/ajax';
@@ -80,11 +80,12 @@ const resetShippingCalculator = (e) => {
8080
handleShippingError();
8181

8282
// On a cart ajax refresh event
83-
if (e.detail.cartData) {
83+
const cartData = e.detail.cartData ? e.detail.cartData : e.detail.data;
84+
if (cartData) {
8485
// First set the subtotal to the cart state.
85-
state.subtotal = e.detail.cartData.subtotal.formatted;
86+
state.subtotal = cartData.subtotal.formatted;
8687
// If the only remaining items in the card are digital goods, remove the shipping calculator all together.
87-
setValidCartCount(e.detail.cartData);
88+
setValidCartCount(cartData);
8889
if (state.shippingItemCount === 0) {
8990
el.calculator.parentNode.removeChild(el.calculator);
9091
return;
@@ -260,6 +261,7 @@ const bindEvents = () => {
260261
delegate(el.calculator, '[data-js="bc-shipping-zones"]', 'change', getMethods);
261262
delegate(el.calculator, '[data-js="shipping-calculator-update"]', 'click', updateCartPrice);
262263
on(document, AJAX_CART_UPDATE, resetShippingCalculator);
264+
on(document, HANDLE_COUPON_CODE, resetShippingCalculator);
263265
};
264266

265267
const init = () => {

assets/js/src/public/checkout/embedded-checkout.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Cookie from 'js-cookie';
77
import _ from 'lodash';
88
import * as tools from 'utils/tools';
99
import scrollTo from 'utils/dom/scroll-to';
10+
import { trigger } from 'utils/events';
1011
import { CART_ID_COOKIE_NAME, CART_ITEM_COUNT_COOKIE } from 'bcConstants/cookies';
1112
import { cartEmpty } from '../cart/cart-templates';
1213

@@ -51,6 +52,12 @@ const scrollIframe = () => {
5152
_.delay(() => scrollTo(options), 1000);
5253
};
5354

55+
const handleOrderCompleteEvents = () => {
56+
trigger({ event: 'bigcommerce/order_complete', data: { cart_id: Cookie.get(CART_ID_COOKIE_NAME) }, native: false });
57+
clearCartData();
58+
scrollIframe();
59+
};
60+
5461
/**
5562
* @function loadEmbeddedCheckout
5663
* @description Create an instance of the BC embedded checkout.
@@ -67,8 +74,7 @@ const loadEmbeddedCheckout = async () => {
6774
}
6875

6976
// Set the onComplete callback to use the clearCartData function.
70-
config.onComplete = clearCartData;
71-
config.onComplete = scrollIframe;
77+
config.onComplete = handleOrderCompleteEvents;
7278

7379
// Embed the checkout.
7480
checkoutCDN.embedCheckout(config);

0 commit comments

Comments
 (0)