merchi_cart 1.4.6 → 1.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,10 +17,10 @@ import { useForm } from "react-hook-form";
17
17
  import InputsAddress from "./InputsAddress";
18
18
  import { CheckoutContainer, InnerContainer } from "../components/containers";
19
19
  import { Title } from "../components";
20
- import { shipmentFormId } from "../utilities/shipment";
20
+ import { checkoutShipmentHydrationAction, shipmentFormId } from "../utilities/shipment";
21
21
  import { faMapMarkerAlt } from "@fortawesome/free-solid-svg-icons";
22
22
  import { useCartContext } from "../CartProvider";
23
- import { useState } from "react";
23
+ import { useEffect, useRef, useState } from "react";
24
24
  import { getCartCookieToken } from "../utilities/cookie";
25
25
  import { makeAddress, sanitizeAddressFields } from "../utilities/address";
26
26
  import { makeCart } from "../utilities/cart";
@@ -122,7 +122,9 @@ export function ActiveFormShipmentAddressAndNotes() {
122
122
  cart = _useCartContext2.cart,
123
123
  setCart = _useCartContext2.setCart,
124
124
  setLoadingTotals = _useCartContext2.setLoadingTotals,
125
- setActiveTabAndEditDisabled = _useCartContext2.setActiveTabAndEditDisabled;
125
+ setActiveTabAndEditDisabled = _useCartContext2.setActiveTabAndEditDisabled,
126
+ updateCartShipmentAddress = _useCartContext2.updateCartShipmentAddress,
127
+ getCartShipmentOptions = _useCartContext2.getCartShipmentOptions;
126
128
  var saved = getSavedCheckoutAddress(domainId);
127
129
  var cartAddress = cart === null || cart === void 0 ? void 0 : cart.receiverAddress;
128
130
  var receiverAddress = addressHasContent(cartAddress) ? cartAddress : saved.address || {};
@@ -131,6 +133,25 @@ export function ActiveFormShipmentAddressAndNotes() {
131
133
  _useState2 = _slicedToArray(_useState, 2),
132
134
  addressFieldsOpen = _useState2[0],
133
135
  setAddressFieldsOpen = _useState2[1];
136
+ var hydratedShipmentOptionsRef = useRef(false);
137
+ useEffect(function () {
138
+ if (hydratedShipmentOptionsRef.current || !(cart !== null && cart !== void 0 && cart.id)) return;
139
+ var draft = getSavedCheckoutAddress(domainId);
140
+ var action = checkoutShipmentHydrationAction({
141
+ cartAddress: cart === null || cart === void 0 ? void 0 : cart.receiverAddress,
142
+ savedAddress: draft.address,
143
+ shipmentGroups: cart === null || cart === void 0 ? void 0 : cart.shipmentGroups
144
+ });
145
+ if (!action) return;
146
+ hydratedShipmentOptionsRef.current = true;
147
+ if (action === "apply-saved-address" && draft.address) {
148
+ updateCartShipmentAddress(draft.address);
149
+ return;
150
+ }
151
+ if (action === "refresh-quotes") {
152
+ getCartShipmentOptions === null || getCartShipmentOptions === void 0 || getCartShipmentOptions();
153
+ }
154
+ }, [cart === null || cart === void 0 ? void 0 : cart.id, cart === null || cart === void 0 ? void 0 : cart.receiverAddress, cart === null || cart === void 0 ? void 0 : cart.shipmentGroups, domainId, getCartShipmentOptions, updateCartShipmentAddress]);
134
155
  function saveCartShipmentAddressAndGoToNextTab(_x) {
135
156
  return _saveCartShipmentAddressAndGoToNextTab.apply(this, arguments);
136
157
  }
@@ -1,3 +1,4 @@
1
+ import { addressHasContent } from './local_storage';
1
2
  export var shipmentFormId = '_shipment-address-form-id';
2
3
  export function cartItemsNeedShipment(cart) {
3
4
  var _cart$cartItems = cart.cartItems,
@@ -8,4 +9,25 @@ export function cartItemsNeedShipment(cart) {
8
9
  }
9
10
  }
10
11
  return false;
12
+ }
13
+ export function shipmentGroupsHaveQuotes(groups) {
14
+ if (!(groups !== null && groups !== void 0 && groups.length)) return false;
15
+ return groups.some(function (group) {
16
+ return Array.isArray(group === null || group === void 0 ? void 0 : group.quotes) && group.quotes.length > 0;
17
+ });
18
+ }
19
+ /**
20
+ * When checkout hydrates a saved address (localStorage) the form shows it,
21
+ * but quotes are only fetched after the user edits the field. Returning
22
+ * `apply-saved-address` means the cart still needs that address applied so
23
+ * quotes can be generated; `refresh-quotes` means the cart already has an
24
+ * address but no usable shipment options.
25
+ */
26
+ export function checkoutShipmentHydrationAction(options) {
27
+ var hasCartAddress = addressHasContent(options.cartAddress);
28
+ var hasSavedAddress = addressHasContent(options.savedAddress);
29
+ var hasQuotes = shipmentGroupsHaveQuotes(options.shipmentGroups);
30
+ if (!hasCartAddress && hasSavedAddress) return 'apply-saved-address';
31
+ if (hasCartAddress && !hasQuotes) return 'refresh-quotes';
32
+ return null;
11
33
  }
@@ -0,0 +1,60 @@
1
+ import { checkoutShipmentHydrationAction, shipmentGroupsHaveQuotes } from './shipment';
2
+ var savedAddress = {
3
+ lineOne: '32 Bridge St',
4
+ city: 'Brighton',
5
+ state: 'VIC',
6
+ postcode: '3186',
7
+ country: 'AU'
8
+ };
9
+ describe('shipmentGroupsHaveQuotes', function () {
10
+ it('is false when groups are missing or empty', function () {
11
+ expect(shipmentGroupsHaveQuotes(undefined)).toBe(false);
12
+ expect(shipmentGroupsHaveQuotes([])).toBe(false);
13
+ expect(shipmentGroupsHaveQuotes([{
14
+ quotes: []
15
+ }])).toBe(false);
16
+ });
17
+ it('is true when any group has quotes', function () {
18
+ expect(shipmentGroupsHaveQuotes([{
19
+ quotes: []
20
+ }, {
21
+ quotes: [{
22
+ id: 1
23
+ }]
24
+ }])).toBe(true);
25
+ });
26
+ });
27
+ describe('checkoutShipmentHydrationAction', function () {
28
+ it('applies a localStorage address when the cart has none', function () {
29
+ expect(checkoutShipmentHydrationAction({
30
+ cartAddress: {},
31
+ savedAddress: savedAddress,
32
+ shipmentGroups: []
33
+ })).toBe('apply-saved-address');
34
+ });
35
+ it('refreshes quotes when the cart already has an address but no options', function () {
36
+ expect(checkoutShipmentHydrationAction({
37
+ cartAddress: savedAddress,
38
+ savedAddress: savedAddress,
39
+ shipmentGroups: []
40
+ })).toBe('refresh-quotes');
41
+ });
42
+ it('does nothing when quotes are already available', function () {
43
+ expect(checkoutShipmentHydrationAction({
44
+ cartAddress: savedAddress,
45
+ savedAddress: savedAddress,
46
+ shipmentGroups: [{
47
+ quotes: [{
48
+ id: 9
49
+ }]
50
+ }]
51
+ })).toBe(null);
52
+ });
53
+ it('does nothing when neither cart nor localStorage has an address', function () {
54
+ expect(checkoutShipmentHydrationAction({
55
+ cartAddress: {},
56
+ savedAddress: {},
57
+ shipmentGroups: []
58
+ })).toBe(null);
59
+ });
60
+ });
@@ -129,7 +129,9 @@ function ActiveFormShipmentAddressAndNotes() {
129
129
  cart = _useCartContext2.cart,
130
130
  setCart = _useCartContext2.setCart,
131
131
  setLoadingTotals = _useCartContext2.setLoadingTotals,
132
- setActiveTabAndEditDisabled = _useCartContext2.setActiveTabAndEditDisabled;
132
+ setActiveTabAndEditDisabled = _useCartContext2.setActiveTabAndEditDisabled,
133
+ updateCartShipmentAddress = _useCartContext2.updateCartShipmentAddress,
134
+ getCartShipmentOptions = _useCartContext2.getCartShipmentOptions;
133
135
  var saved = (0, _local_storage.getSavedCheckoutAddress)(domainId);
134
136
  var cartAddress = cart === null || cart === void 0 ? void 0 : cart.receiverAddress;
135
137
  var receiverAddress = (0, _local_storage.addressHasContent)(cartAddress) ? cartAddress : saved.address || {};
@@ -138,6 +140,25 @@ function ActiveFormShipmentAddressAndNotes() {
138
140
  _useState2 = _slicedToArray(_useState, 2),
139
141
  addressFieldsOpen = _useState2[0],
140
142
  setAddressFieldsOpen = _useState2[1];
143
+ var hydratedShipmentOptionsRef = (0, _react.useRef)(false);
144
+ (0, _react.useEffect)(function () {
145
+ if (hydratedShipmentOptionsRef.current || !(cart !== null && cart !== void 0 && cart.id)) return;
146
+ var draft = (0, _local_storage.getSavedCheckoutAddress)(domainId);
147
+ var action = (0, _shipment.checkoutShipmentHydrationAction)({
148
+ cartAddress: cart === null || cart === void 0 ? void 0 : cart.receiverAddress,
149
+ savedAddress: draft.address,
150
+ shipmentGroups: cart === null || cart === void 0 ? void 0 : cart.shipmentGroups
151
+ });
152
+ if (!action) return;
153
+ hydratedShipmentOptionsRef.current = true;
154
+ if (action === "apply-saved-address" && draft.address) {
155
+ updateCartShipmentAddress(draft.address);
156
+ return;
157
+ }
158
+ if (action === "refresh-quotes") {
159
+ getCartShipmentOptions === null || getCartShipmentOptions === void 0 || getCartShipmentOptions();
160
+ }
161
+ }, [cart === null || cart === void 0 ? void 0 : cart.id, cart === null || cart === void 0 ? void 0 : cart.receiverAddress, cart === null || cart === void 0 ? void 0 : cart.shipmentGroups, domainId, getCartShipmentOptions, updateCartShipmentAddress]);
141
162
  function saveCartShipmentAddressAndGoToNextTab(_x) {
142
163
  return _saveCartShipmentAddressAndGoToNextTab.apply(this, arguments);
143
164
  }
@@ -1,2 +1,17 @@
1
+ import { SavedCheckoutAddress } from './local_storage';
1
2
  export declare const shipmentFormId = "_shipment-address-form-id";
2
3
  export declare function cartItemsNeedShipment(cart: any): boolean;
4
+ export declare function shipmentGroupsHaveQuotes(groups?: any[] | null): boolean;
5
+ export type CheckoutShipmentHydrationAction = 'apply-saved-address' | 'refresh-quotes' | null;
6
+ /**
7
+ * When checkout hydrates a saved address (localStorage) the form shows it,
8
+ * but quotes are only fetched after the user edits the field. Returning
9
+ * `apply-saved-address` means the cart still needs that address applied so
10
+ * quotes can be generated; `refresh-quotes` means the cart already has an
11
+ * address but no usable shipment options.
12
+ */
13
+ export declare function checkoutShipmentHydrationAction(options: {
14
+ cartAddress?: SavedCheckoutAddress | null;
15
+ savedAddress?: SavedCheckoutAddress | null;
16
+ shipmentGroups?: any[] | null;
17
+ }): CheckoutShipmentHydrationAction;
@@ -4,7 +4,10 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.cartItemsNeedShipment = cartItemsNeedShipment;
7
+ exports.checkoutShipmentHydrationAction = checkoutShipmentHydrationAction;
7
8
  exports.shipmentFormId = void 0;
9
+ exports.shipmentGroupsHaveQuotes = shipmentGroupsHaveQuotes;
10
+ var _local_storage = require("./local_storage");
8
11
  var shipmentFormId = exports.shipmentFormId = '_shipment-address-form-id';
9
12
  function cartItemsNeedShipment(cart) {
10
13
  var _cart$cartItems = cart.cartItems,
@@ -15,4 +18,25 @@ function cartItemsNeedShipment(cart) {
15
18
  }
16
19
  }
17
20
  return false;
21
+ }
22
+ function shipmentGroupsHaveQuotes(groups) {
23
+ if (!(groups !== null && groups !== void 0 && groups.length)) return false;
24
+ return groups.some(function (group) {
25
+ return Array.isArray(group === null || group === void 0 ? void 0 : group.quotes) && group.quotes.length > 0;
26
+ });
27
+ }
28
+ /**
29
+ * When checkout hydrates a saved address (localStorage) the form shows it,
30
+ * but quotes are only fetched after the user edits the field. Returning
31
+ * `apply-saved-address` means the cart still needs that address applied so
32
+ * quotes can be generated; `refresh-quotes` means the cart already has an
33
+ * address but no usable shipment options.
34
+ */
35
+ function checkoutShipmentHydrationAction(options) {
36
+ var hasCartAddress = (0, _local_storage.addressHasContent)(options.cartAddress);
37
+ var hasSavedAddress = (0, _local_storage.addressHasContent)(options.savedAddress);
38
+ var hasQuotes = shipmentGroupsHaveQuotes(options.shipmentGroups);
39
+ if (!hasCartAddress && hasSavedAddress) return 'apply-saved-address';
40
+ if (hasCartAddress && !hasQuotes) return 'refresh-quotes';
41
+ return null;
18
42
  }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "merchi_cart",
3
- "version": "1.4.6",
3
+ "version": "1.4.8",
4
4
  "description": "Merchi's cart",
5
5
  "source": "src/index.ts",
6
6
  "main": "lib/index.js",
@@ -94,7 +94,7 @@
94
94
  "browser-or-node": "^3.0.0-pre.0",
95
95
  "js-cookie": "^3.0.5",
96
96
  "lodash": "^4.17.21",
97
- "merchi_product_form": "^1.19.4",
97
+ "merchi_product_form": "^1.19.14",
98
98
  "merchi_sdk_product_form": "^1.0.9",
99
99
  "merchi_sdk_ts": "^1.42.0",
100
100
  "react-country-region-selector": "^3.6.1",
@@ -2,10 +2,13 @@ import { useForm } from "react-hook-form";
2
2
  import InputsAddress from "./InputsAddress";
3
3
  import { CheckoutContainer, InnerContainer } from "../components/containers";
4
4
  import { Title } from "../components";
5
- import { shipmentFormId } from "../utilities/shipment";
5
+ import {
6
+ checkoutShipmentHydrationAction,
7
+ shipmentFormId,
8
+ } from "../utilities/shipment";
6
9
  import { faMapMarkerAlt } from "@fortawesome/free-solid-svg-icons";
7
10
  import { useCartContext } from "../CartProvider";
8
- import { useState } from "react";
11
+ import { useEffect, useRef, useState } from "react";
9
12
  import { getCartCookieToken } from "../utilities/cookie";
10
13
  import { makeAddress, sanitizeAddressFields } from "../utilities/address";
11
14
  import { makeCart } from "../utilities/cart";
@@ -114,6 +117,8 @@ export function ActiveFormShipmentAddressAndNotes() {
114
117
  setCart,
115
118
  setLoadingTotals,
116
119
  setActiveTabAndEditDisabled,
120
+ updateCartShipmentAddress,
121
+ getCartShipmentOptions,
117
122
  } = useCartContext();
118
123
  const saved = getSavedCheckoutAddress(domainId);
119
124
  const cartAddress = cart?.receiverAddress;
@@ -122,6 +127,33 @@ export function ActiveFormShipmentAddressAndNotes() {
122
127
  : saved.address || {};
123
128
  const receiverNotes = cart?.receiverNotes || saved.receiverNotes || "";
124
129
  const [addressFieldsOpen, setAddressFieldsOpen] = useState(false);
130
+ const hydratedShipmentOptionsRef = useRef(false);
131
+
132
+ useEffect(() => {
133
+ if (hydratedShipmentOptionsRef.current || !cart?.id) return;
134
+ const draft = getSavedCheckoutAddress(domainId);
135
+ const action = checkoutShipmentHydrationAction({
136
+ cartAddress: cart?.receiverAddress,
137
+ savedAddress: draft.address,
138
+ shipmentGroups: cart?.shipmentGroups,
139
+ });
140
+ if (!action) return;
141
+ hydratedShipmentOptionsRef.current = true;
142
+ if (action === "apply-saved-address" && draft.address) {
143
+ updateCartShipmentAddress(draft.address);
144
+ return;
145
+ }
146
+ if (action === "refresh-quotes") {
147
+ getCartShipmentOptions?.();
148
+ }
149
+ }, [
150
+ cart?.id,
151
+ cart?.receiverAddress,
152
+ cart?.shipmentGroups,
153
+ domainId,
154
+ getCartShipmentOptions,
155
+ updateCartShipmentAddress,
156
+ ]);
125
157
 
126
158
  async function saveCartShipmentAddressAndGoToNextTab(values: any) {
127
159
  const { receiverAddress: address, receiverNotes } = values;
@@ -0,0 +1,68 @@
1
+ import {
2
+ checkoutShipmentHydrationAction,
3
+ shipmentGroupsHaveQuotes,
4
+ } from './shipment';
5
+
6
+ const savedAddress = {
7
+ lineOne: '32 Bridge St',
8
+ city: 'Brighton',
9
+ state: 'VIC',
10
+ postcode: '3186',
11
+ country: 'AU',
12
+ };
13
+
14
+ describe('shipmentGroupsHaveQuotes', () => {
15
+ it('is false when groups are missing or empty', () => {
16
+ expect(shipmentGroupsHaveQuotes(undefined)).toBe(false);
17
+ expect(shipmentGroupsHaveQuotes([])).toBe(false);
18
+ expect(shipmentGroupsHaveQuotes([{ quotes: [] }])).toBe(false);
19
+ });
20
+
21
+ it('is true when any group has quotes', () => {
22
+ expect(
23
+ shipmentGroupsHaveQuotes([{ quotes: [] }, { quotes: [{ id: 1 }] }])
24
+ ).toBe(true);
25
+ });
26
+ });
27
+
28
+ describe('checkoutShipmentHydrationAction', () => {
29
+ it('applies a localStorage address when the cart has none', () => {
30
+ expect(
31
+ checkoutShipmentHydrationAction({
32
+ cartAddress: {},
33
+ savedAddress,
34
+ shipmentGroups: [],
35
+ })
36
+ ).toBe('apply-saved-address');
37
+ });
38
+
39
+ it('refreshes quotes when the cart already has an address but no options', () => {
40
+ expect(
41
+ checkoutShipmentHydrationAction({
42
+ cartAddress: savedAddress,
43
+ savedAddress,
44
+ shipmentGroups: [],
45
+ })
46
+ ).toBe('refresh-quotes');
47
+ });
48
+
49
+ it('does nothing when quotes are already available', () => {
50
+ expect(
51
+ checkoutShipmentHydrationAction({
52
+ cartAddress: savedAddress,
53
+ savedAddress,
54
+ shipmentGroups: [{ quotes: [{ id: 9 }] }],
55
+ })
56
+ ).toBe(null);
57
+ });
58
+
59
+ it('does nothing when neither cart nor localStorage has an address', () => {
60
+ expect(
61
+ checkoutShipmentHydrationAction({
62
+ cartAddress: {},
63
+ savedAddress: {},
64
+ shipmentGroups: [],
65
+ })
66
+ ).toBe(null);
67
+ });
68
+ });
@@ -1,3 +1,5 @@
1
+ import { addressHasContent, SavedCheckoutAddress } from './local_storage';
2
+
1
3
  export const shipmentFormId = '_shipment-address-form-id';
2
4
 
3
5
  export function cartItemsNeedShipment(cart: any) {
@@ -9,3 +11,35 @@ export function cartItemsNeedShipment(cart: any) {
9
11
  }
10
12
  return false;
11
13
  }
14
+
15
+ export function shipmentGroupsHaveQuotes(groups?: any[] | null) {
16
+ if (!groups?.length) return false;
17
+ return groups.some(
18
+ (group) => Array.isArray(group?.quotes) && group.quotes.length > 0
19
+ );
20
+ }
21
+
22
+ export type CheckoutShipmentHydrationAction =
23
+ | 'apply-saved-address'
24
+ | 'refresh-quotes'
25
+ | null;
26
+
27
+ /**
28
+ * When checkout hydrates a saved address (localStorage) the form shows it,
29
+ * but quotes are only fetched after the user edits the field. Returning
30
+ * `apply-saved-address` means the cart still needs that address applied so
31
+ * quotes can be generated; `refresh-quotes` means the cart already has an
32
+ * address but no usable shipment options.
33
+ */
34
+ export function checkoutShipmentHydrationAction(options: {
35
+ cartAddress?: SavedCheckoutAddress | null;
36
+ savedAddress?: SavedCheckoutAddress | null;
37
+ shipmentGroups?: any[] | null;
38
+ }): CheckoutShipmentHydrationAction {
39
+ const hasCartAddress = addressHasContent(options.cartAddress);
40
+ const hasSavedAddress = addressHasContent(options.savedAddress);
41
+ const hasQuotes = shipmentGroupsHaveQuotes(options.shipmentGroups);
42
+ if (!hasCartAddress && hasSavedAddress) return 'apply-saved-address';
43
+ if (hasCartAddress && !hasQuotes) return 'refresh-quotes';
44
+ return null;
45
+ }