@merchi/cart 1.4.5 → 1.4.7

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.
@@ -19,6 +19,7 @@ var _product = require("./utilities/product");
19
19
  var _local_storage = require("./utilities/local_storage");
20
20
  var _user = require("./utilities/user");
21
21
  var _test_checkout = require("./utilities/test_checkout");
22
+ var _job_source = require("./utilities/job_source");
22
23
  var _jsxRuntime = require("react/jsx-runtime");
23
24
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t9 in e) "default" !== _t9 && {}.hasOwnProperty.call(e, _t9) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t9)) && (i.get || i.set) ? o(f, _t9, i) : f[_t9] = e[_t9]); return f; })(e, t); }
24
25
  function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
@@ -504,65 +505,56 @@ var CartProvider = function CartProvider(_ref) {
504
505
  }
505
506
  function _createCartAndCookie() {
506
507
  _createCartAndCookie = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(callback) {
507
- var isTest, _cart, created, cartJson, _t3;
508
+ var isTest, jobSource, _cart, query, created, cartJson, _t3;
508
509
  return _regenerator().w(function (_context2) {
509
510
  while (1) switch (_context2.p = _context2.n) {
510
511
  case 0:
511
512
  setFetchingCart(true);
512
513
  _context2.p = 1;
513
514
  isTest = (0, _test_checkout.readTestCheckoutFlag)();
515
+ jobSource = (0, _job_source.jobSourceFieldsForApi)((0, _job_source.captureAndReadJobSource)());
514
516
  _cart = (0, _cart3.makeCart)({
515
517
  domain: {
516
518
  id: domainId
517
519
  },
518
520
  isTest: isTest
519
521
  }, true);
520
- if (!isTest) {
521
- _context2.n = 3;
522
- break;
523
- }
522
+ query = [['embed', JSON.stringify(_helpers.cartEmbed)], ['skip_rights', 'y']];
523
+ if (isTest) query.push(['is_test', 'true']);
524
524
  _context2.n = 2;
525
525
  return merchi.authenticatedFetch('/carts/', {
526
526
  method: 'POST',
527
- body: _cart.toFormData(),
528
- query: [['embed', JSON.stringify(_helpers.cartEmbed)], ['skip_rights', 'y'], ['is_test', 'true']]
527
+ body: (0, _job_source.appendJobSourceToFormData)(_cart.toFormData(), jobSource),
528
+ query: query
529
529
  });
530
530
  case 2:
531
531
  created = _context2.v;
532
532
  _cart.fromJson(created.cart);
533
- _context2.n = 4;
534
- break;
535
- case 3:
536
- _context2.n = 4;
537
- return _cart.create({
538
- embed: _helpers.cartEmbed
539
- });
540
- case 4:
541
- _context2.n = 5;
533
+ _context2.n = 3;
542
534
  return _cart.toJson();
543
- case 5:
535
+ case 3:
544
536
  cartJson = _context2.v;
545
537
  if (domainId) (0, _cookie.setCartCookie)(Number(domainId), cartJson, undefined, isTest);
546
- _context2.n = 6;
538
+ _context2.n = 4;
547
539
  return attachSavedClientIfNeeded(cartJson);
548
- case 6:
540
+ case 4:
549
541
  cartJson = _context2.v;
550
542
  setCart(_objectSpread({}, cartJson));
551
543
  if (callback) callback();
552
- _context2.n = 8;
544
+ _context2.n = 6;
553
545
  break;
554
- case 7:
555
- _context2.p = 7;
546
+ case 5:
547
+ _context2.p = 5;
556
548
  _t3 = _context2.v;
557
549
  alertError(_t3.errorMessage || _t3.message || 'Unable to fetch cart.');
558
- case 8:
559
- _context2.p = 8;
550
+ case 6:
551
+ _context2.p = 6;
560
552
  setFetchingCart(false);
561
- return _context2.f(8);
562
- case 9:
553
+ return _context2.f(6);
554
+ case 7:
563
555
  return _context2.a(2);
564
556
  }
565
- }, _callee2, null, [[1, 7, 8, 9]]);
557
+ }, _callee2, null, [[1, 5, 6, 7]]);
566
558
  }));
567
559
  return _createCartAndCookie.apply(this, arguments);
568
560
  }
@@ -571,7 +563,7 @@ var CartProvider = function CartProvider(_ref) {
571
563
  }
572
564
  function _getCart() {
573
565
  _getCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(cartIdAndToken) {
574
- var id, _cart2, cartJson, isTest, _t4;
566
+ var id, _cart2, cartJson, isTest, jobSource, saved, _t4;
575
567
  return _regenerator().w(function (_context3) {
576
568
  while (1) switch (_context3.p = _context3.n) {
577
569
  case 0:
@@ -599,36 +591,52 @@ var CartProvider = function CartProvider(_ref) {
599
591
  case 4:
600
592
  return _context3.a(2);
601
593
  case 5:
602
- if (!((0, _cart3.stripeIsValidAndActive)(cartJson) || isTest)) {
594
+ jobSource = (0, _job_source.jobSourceFieldsForApi)((0, _job_source.captureAndReadJobSource)());
595
+ if (!(0, _job_source.cartNeedsSourceUpdate)(cartJson, jobSource)) {
603
596
  _context3.n = 7;
604
597
  break;
605
598
  }
606
599
  _context3.n = 6;
607
- return attachSavedClientIfNeeded(cartJson);
600
+ return merchi.authenticatedFetch("/carts/".concat(_cart2.id, "/"), {
601
+ method: 'PATCH',
602
+ body: (0, _job_source.appendJobSourceToFormData)(_cart2.toFormData(), jobSource),
603
+ query: [['embed', JSON.stringify(_helpers.cartEmbed)], ['skip_rights', 'y']]
604
+ });
608
605
  case 6:
606
+ saved = _context3.v;
607
+ _cart2.fromJson(saved.cart);
608
+ cartJson = _cart2.toJson();
609
+ case 7:
610
+ if (!((0, _cart3.stripeIsValidAndActive)(cartJson) || isTest)) {
611
+ _context3.n = 9;
612
+ break;
613
+ }
614
+ _context3.n = 8;
615
+ return attachSavedClientIfNeeded(cartJson);
616
+ case 8:
609
617
  cartJson = _context3.v;
610
618
  setCart(_objectSpread({}, cartJson));
611
- _context3.n = 8;
619
+ _context3.n = 10;
612
620
  break;
613
- case 7:
621
+ case 9:
614
622
  console.error("MErhci cart error: Stripe payment " + "options have not been correctly set up. " + "Check the company profile payment options tab " + "to set and edit stripe payment options.");
615
623
  setCartSettingsInvalid(true);
616
- case 8:
617
- _context3.n = 10;
624
+ case 10:
625
+ _context3.n = 12;
618
626
  break;
619
- case 9:
620
- _context3.p = 9;
627
+ case 11:
628
+ _context3.p = 11;
621
629
  _t4 = _context3.v;
622
- _context3.n = 10;
630
+ _context3.n = 12;
623
631
  return createCartAndCookie();
624
- case 10:
625
- _context3.p = 10;
632
+ case 12:
633
+ _context3.p = 12;
626
634
  setFetchingCart(false);
627
- return _context3.f(10);
628
- case 11:
635
+ return _context3.f(12);
636
+ case 13:
629
637
  return _context3.a(2);
630
638
  }
631
- }, _callee3, null, [[1, 9, 10, 11]]);
639
+ }, _callee3, null, [[1, 11, 12, 13]]);
632
640
  }));
633
641
  return _getCart.apply(this, arguments);
634
642
  }
@@ -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
  }
@@ -6,6 +6,7 @@ export * from './currency';
6
6
  export * from './helpers';
7
7
  export * from './invoice';
8
8
  export * from './job';
9
+ export * from './job_source';
9
10
  export * from './local_storage';
10
11
  export * from './product';
11
12
  export * from './shipment';
@@ -91,6 +91,17 @@ Object.keys(_job).forEach(function (key) {
91
91
  }
92
92
  });
93
93
  });
94
+ var _job_source = require("./job_source");
95
+ Object.keys(_job_source).forEach(function (key) {
96
+ if (key === "default" || key === "__esModule") return;
97
+ if (key in exports && exports[key] === _job_source[key]) return;
98
+ Object.defineProperty(exports, key, {
99
+ enumerable: true,
100
+ get: function get() {
101
+ return _job_source[key];
102
+ }
103
+ });
104
+ });
94
105
  var _local_storage = require("./local_storage");
95
106
  Object.keys(_local_storage).forEach(function (key) {
96
107
  if (key === "default" || key === "__esModule") return;
@@ -0,0 +1,23 @@
1
+ export declare const JOB_SOURCE_STORAGE_KEY = "merchi_job_source";
2
+ export interface JobSourceFields {
3
+ utmSource?: string;
4
+ utmMedium?: string;
5
+ utmCampaign?: string;
6
+ utmContent?: string;
7
+ utmTerm?: string;
8
+ sourceClickId?: string;
9
+ sourceLanding?: string;
10
+ sourceReferrer?: string;
11
+ }
12
+ export declare function sourceFromLocation(): JobSourceFields;
13
+ export declare function hasPaidClick(source: JobSourceFields | null | undefined): boolean;
14
+ export declare function hasSourceData(source: JobSourceFields | null | undefined): boolean;
15
+ export declare function captureAndReadJobSource(): JobSourceFields;
16
+ export declare function jobSourceFieldsForApi(source?: JobSourceFields | null): JobSourceFields;
17
+ export declare function cartNeedsSourceUpdate(cart: {
18
+ utmSource?: string;
19
+ sourceClickId?: string;
20
+ sourceChannel?: string;
21
+ } | null | undefined, source: JobSourceFields): boolean;
22
+ export declare const JOB_SOURCE_URL_KEYS: ("gclid" | "gbraid" | "wbraid" | "fbclid" | "msclkid" | "utm_source" | "utm_medium" | "utm_campaign" | "utm_content" | "utm_term")[];
23
+ export declare function appendJobSourceToFormData(data: FormData, source?: JobSourceFields | null): FormData;
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.JOB_SOURCE_URL_KEYS = exports.JOB_SOURCE_STORAGE_KEY = void 0;
7
+ exports.appendJobSourceToFormData = appendJobSourceToFormData;
8
+ exports.captureAndReadJobSource = captureAndReadJobSource;
9
+ exports.cartNeedsSourceUpdate = cartNeedsSourceUpdate;
10
+ exports.hasPaidClick = hasPaidClick;
11
+ exports.hasSourceData = hasSourceData;
12
+ exports.jobSourceFieldsForApi = jobSourceFieldsForApi;
13
+ exports.sourceFromLocation = sourceFromLocation;
14
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
15
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
16
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
17
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
18
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
19
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
20
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
21
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
22
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
23
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
24
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
25
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
26
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
27
+ var JOB_SOURCE_STORAGE_KEY = exports.JOB_SOURCE_STORAGE_KEY = 'merchi_job_source';
28
+ var CLICK_ID_PARAMS = ['gclid', 'gbraid', 'wbraid', 'fbclid', 'msclkid'];
29
+ var UTM_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
30
+ function trimTo(value, limit) {
31
+ if (!value) return undefined;
32
+ var text = value.trim().slice(0, limit);
33
+ return text || undefined;
34
+ }
35
+ function readSearchParams() {
36
+ if (typeof window === 'undefined') return new URLSearchParams();
37
+ try {
38
+ return new URLSearchParams(window.location.search);
39
+ } catch (_unused) {
40
+ return new URLSearchParams();
41
+ }
42
+ }
43
+ function readStoredSource() {
44
+ if (typeof window === 'undefined') return null;
45
+ try {
46
+ var raw = window.localStorage.getItem(JOB_SOURCE_STORAGE_KEY);
47
+ if (!raw) return null;
48
+ var parsed = JSON.parse(raw);
49
+ if (!parsed || _typeof(parsed) !== 'object') return null;
50
+ return parsed;
51
+ } catch (_unused2) {
52
+ return null;
53
+ }
54
+ }
55
+ function writeStoredSource(source) {
56
+ if (typeof window === 'undefined') return;
57
+ try {
58
+ window.localStorage.setItem(JOB_SOURCE_STORAGE_KEY, JSON.stringify(source));
59
+ } catch (_unused3) {
60
+ // Ignore quota / private-mode failures.
61
+ }
62
+ }
63
+ function currentLanding() {
64
+ if (typeof window === 'undefined') return undefined;
65
+ try {
66
+ var path = "".concat(window.location.pathname || '/').concat(window.location.search || '');
67
+ return trimTo(path, 512);
68
+ } catch (_unused4) {
69
+ return undefined;
70
+ }
71
+ }
72
+ function currentReferrer() {
73
+ if (typeof document === 'undefined') return undefined;
74
+ try {
75
+ return trimTo(document.referrer, 512);
76
+ } catch (_unused5) {
77
+ return undefined;
78
+ }
79
+ }
80
+ function sourceFromLocation() {
81
+ var params = readSearchParams();
82
+ var source = {
83
+ utmSource: trimTo(params.get('utm_source'), 256),
84
+ utmMedium: trimTo(params.get('utm_medium'), 256),
85
+ utmCampaign: trimTo(params.get('utm_campaign'), 256),
86
+ utmContent: trimTo(params.get('utm_content'), 256),
87
+ utmTerm: trimTo(params.get('utm_term'), 256),
88
+ sourceLanding: currentLanding(),
89
+ sourceReferrer: currentReferrer()
90
+ };
91
+ var _iterator = _createForOfIteratorHelper(CLICK_ID_PARAMS),
92
+ _step;
93
+ try {
94
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
95
+ var key = _step.value;
96
+ var value = trimTo(params.get(key), 256);
97
+ if (value) {
98
+ source.sourceClickId = value;
99
+ break;
100
+ }
101
+ }
102
+ } catch (err) {
103
+ _iterator.e(err);
104
+ } finally {
105
+ _iterator.f();
106
+ }
107
+ return source;
108
+ }
109
+ function hasPaidClick(source) {
110
+ return Boolean(source === null || source === void 0 ? void 0 : source.sourceClickId);
111
+ }
112
+ function hasSourceData(source) {
113
+ if (!source) return false;
114
+ return Boolean(source.utmSource || source.utmMedium || source.utmCampaign || source.utmContent || source.utmTerm || source.sourceClickId || source.sourceLanding || source.sourceReferrer);
115
+ }
116
+ function captureAndReadJobSource() {
117
+ var incoming = sourceFromLocation();
118
+ var stored = readStoredSource();
119
+ if (hasPaidClick(incoming) || !hasSourceData(stored)) {
120
+ var next = _objectSpread(_objectSpread({}, stored || {}), Object.fromEntries(Object.entries(incoming).filter(function (_ref) {
121
+ var _ref2 = _slicedToArray(_ref, 2),
122
+ value = _ref2[1];
123
+ return Boolean(value);
124
+ })));
125
+ if (hasSourceData(next)) writeStoredSource(next);
126
+ return next;
127
+ }
128
+ return stored || incoming;
129
+ }
130
+ function jobSourceFieldsForApi(source) {
131
+ var resolved = source || captureAndReadJobSource();
132
+ var out = {};
133
+ Object.keys(resolved).forEach(function (key) {
134
+ var value = resolved[key];
135
+ if (value) out[key] = value;
136
+ });
137
+ return out;
138
+ }
139
+ function cartNeedsSourceUpdate(cart, source) {
140
+ if (!hasSourceData(source)) return false;
141
+ if (!(cart !== null && cart !== void 0 && cart.utmSource) && !(cart !== null && cart !== void 0 && cart.sourceClickId) && !(cart !== null && cart !== void 0 && cart.sourceChannel)) return true;
142
+ return Boolean(source.sourceClickId && !(cart !== null && cart !== void 0 && cart.sourceClickId));
143
+ }
144
+ var JOB_SOURCE_URL_KEYS = exports.JOB_SOURCE_URL_KEYS = [].concat(UTM_PARAMS, CLICK_ID_PARAMS);
145
+ function appendJobSourceToFormData(data, source) {
146
+ var fields = jobSourceFieldsForApi(source);
147
+ Object.keys(fields).forEach(function (key) {
148
+ var value = fields[key];
149
+ if (value) data.append(key, value);
150
+ });
151
+ return data;
152
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -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.5",
3
+ "version": "1.4.7",
4
4
  "description": "Merchi's cart",
5
5
  "source": "src/index.ts",
6
6
  "main": "lib/index.js",
@@ -98,9 +98,9 @@
98
98
  "browser-or-node": "^3.0.0-pre.0",
99
99
  "js-cookie": "^3.0.5",
100
100
  "lodash": "^4.17.21",
101
- "@merchi/product-form": "^1.19.13",
102
- "@merchi/product-form-sdk": "^1.0.10",
103
- "@merchi/sdk": "^1.42.0",
101
+ "@merchi/product-form": "^1.19.15",
102
+ "@merchi/product-form-sdk": "^1.0.12",
103
+ "@merchi/sdk": "^1.42.2",
104
104
  "react-country-region-selector": "^3.6.1",
105
105
  "react-hook-form": "7.45.4",
106
106
  "react-icons": "^4.10.1",
@@ -21,6 +21,12 @@ import {
21
21
  } from './utilities/local_storage';
22
22
  import { makeUser } from './utilities/user';
23
23
  import { persistTestCheckoutFlag, readTestCheckoutFlag } from './utilities/test_checkout';
24
+ import {
25
+ appendJobSourceToFormData,
26
+ captureAndReadJobSource,
27
+ cartNeedsSourceUpdate,
28
+ jobSourceFieldsForApi,
29
+ } from './utilities/job_source';
24
30
 
25
31
  export interface PropsCart {
26
32
  cart: any;
@@ -622,21 +628,19 @@ const CartProvider = ({
622
628
  setFetchingCart(true);
623
629
  try {
624
630
  const isTest = readTestCheckoutFlag();
631
+ const jobSource = jobSourceFieldsForApi(captureAndReadJobSource());
625
632
  const cart = makeCart({domain: {id: domainId}, isTest}, true);
626
- if (isTest) {
627
- const created = await merchi.authenticatedFetch('/carts/', {
628
- method: 'POST',
629
- body: cart.toFormData(),
630
- query: [
631
- ['embed', JSON.stringify(cartEmbed)],
632
- ['skip_rights', 'y'],
633
- ['is_test', 'true'],
634
- ],
635
- });
636
- cart.fromJson(created.cart);
637
- } else {
638
- await cart.create({embed: cartEmbed});
639
- }
633
+ const query: Array<[string, string]> = [
634
+ ['embed', JSON.stringify(cartEmbed)],
635
+ ['skip_rights', 'y'],
636
+ ];
637
+ if (isTest) query.push(['is_test', 'true']);
638
+ const created = await merchi.authenticatedFetch('/carts/', {
639
+ method: 'POST',
640
+ body: appendJobSourceToFormData(cart.toFormData() as FormData, jobSource),
641
+ query,
642
+ });
643
+ cart.fromJson(created.cart);
640
644
  let cartJson = await cart.toJson();
641
645
  if (domainId) setCartCookie(Number(domainId), cartJson, undefined, isTest);
642
646
  cartJson = await attachSavedClientIfNeeded(cartJson);
@@ -663,6 +667,19 @@ const CartProvider = ({
663
667
  await createCartAndCookie();
664
668
  return;
665
669
  }
670
+ const jobSource = jobSourceFieldsForApi(captureAndReadJobSource());
671
+ if (cartNeedsSourceUpdate(cartJson, jobSource)) {
672
+ const saved = await merchi.authenticatedFetch(`/carts/${cart.id}/`, {
673
+ method: 'PATCH',
674
+ body: appendJobSourceToFormData(cart.toFormData() as FormData, jobSource),
675
+ query: [
676
+ ['embed', JSON.stringify(cartEmbed)],
677
+ ['skip_rights', 'y'],
678
+ ],
679
+ });
680
+ cart.fromJson(saved.cart);
681
+ cartJson = cart.toJson();
682
+ }
666
683
  if (stripeIsValidAndActive(cartJson) || isTest) {
667
684
  cartJson = await attachSavedClientIfNeeded(cartJson);
668
685
  setCart({...cartJson});
@@ -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;
@@ -6,6 +6,7 @@ export * from './currency';
6
6
  export * from './helpers';
7
7
  export * from './invoice';
8
8
  export * from './job';
9
+ export * from './job_source';
9
10
  export * from './local_storage';
10
11
  export * from './product';
11
12
  export * from './shipment';