merchi_cart 1.3.2 → 1.3.3
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.
- package/esm/components/CustomCartProductForm.js +129 -0
- package/esm/components/product-form-sdk-externals.js +35 -0
- package/esm/panels/PanelEditCartItem.js +29 -14
- package/esm/utilities/customForm.js +57 -0
- package/esm/utilities/helpers.js +4 -0
- package/lib/components/CustomCartProductForm.js +137 -0
- package/lib/components/product-form-sdk-externals.js +42 -0
- package/lib/panels/PanelEditCartItem.js +30 -14
- package/lib/utilities/customForm.js +64 -0
- package/lib/utilities/helpers.js +4 -0
- package/package.json +2 -1
- package/src/components/CustomCartProductForm.tsx +123 -0
- package/src/components/product-form-sdk-externals.ts +65 -0
- package/src/panels/PanelEditCartItem.tsx +50 -27
- package/src/utilities/customForm.ts +71 -0
- package/src/utilities/helpers.ts +2 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
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); }
|
|
2
|
+
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; }
|
|
3
|
+
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; }
|
|
4
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
5
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
6
|
+
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); }
|
|
7
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
8
|
+
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."); }
|
|
9
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
10
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
11
|
+
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; } }
|
|
12
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
13
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
14
|
+
import { MerchiProductFormSdkExternals } from './product-form-sdk-externals';
|
|
15
|
+
import { loadFormBundle, publishedFormBundle } from '../utilities/customForm';
|
|
16
|
+
import { LoadingTemplateSm } from './LoadingTemplate';
|
|
17
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
18
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
19
|
+
/**
|
|
20
|
+
* Loads a product's published custom form for cart-item edit. On any failure
|
|
21
|
+
* renders `fallback` (the default merchi_product_form) so the cart stays usable.
|
|
22
|
+
*/
|
|
23
|
+
export function CustomCartProductForm(_ref) {
|
|
24
|
+
var _publishedFormBundle$, _publishedFormBundle;
|
|
25
|
+
var product = _ref.product,
|
|
26
|
+
initialJob = _ref.initialJob,
|
|
27
|
+
apiUrl = _ref.apiUrl,
|
|
28
|
+
onSave = _ref.onSave,
|
|
29
|
+
fallback = _ref.fallback,
|
|
30
|
+
onActiveChange = _ref.onActiveChange;
|
|
31
|
+
var _useState = useState(null),
|
|
32
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
33
|
+
Loaded = _useState2[0],
|
|
34
|
+
setLoaded = _useState2[1];
|
|
35
|
+
var _useState3 = useState(false),
|
|
36
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
37
|
+
failed = _useState4[0],
|
|
38
|
+
setFailed = _useState4[1];
|
|
39
|
+
var _useState5 = useState(true),
|
|
40
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
41
|
+
loading = _useState6[0],
|
|
42
|
+
setLoading = _useState6[1];
|
|
43
|
+
var onSaveRef = useRef(onSave);
|
|
44
|
+
onSaveRef.current = onSave;
|
|
45
|
+
var onActiveChangeRef = useRef(onActiveChange);
|
|
46
|
+
onActiveChangeRef.current = onActiveChange;
|
|
47
|
+
var bundleKey = (_publishedFormBundle$ = (_publishedFormBundle = publishedFormBundle(product)) === null || _publishedFormBundle === void 0 ? void 0 : _publishedFormBundle.url) !== null && _publishedFormBundle$ !== void 0 ? _publishedFormBundle$ : '';
|
|
48
|
+
useEffect(function () {
|
|
49
|
+
var active = true;
|
|
50
|
+
setLoaded(null);
|
|
51
|
+
setFailed(false);
|
|
52
|
+
setLoading(true);
|
|
53
|
+
var bundle = publishedFormBundle(product);
|
|
54
|
+
if (!bundle) {
|
|
55
|
+
setFailed(true);
|
|
56
|
+
setLoading(false);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
var w = window;
|
|
60
|
+
w.React = React;
|
|
61
|
+
w.MerchiProductFormSdk = MerchiProductFormSdkExternals;
|
|
62
|
+
loadFormBundle(bundle).then(function (Component) {
|
|
63
|
+
if (active) {
|
|
64
|
+
setLoaded(function () {
|
|
65
|
+
return Component;
|
|
66
|
+
});
|
|
67
|
+
setLoading(false);
|
|
68
|
+
}
|
|
69
|
+
})["catch"](function (e) {
|
|
70
|
+
console.error('[merchi_cart] custom form load failed', e);
|
|
71
|
+
if (active) {
|
|
72
|
+
setFailed(true);
|
|
73
|
+
setLoading(false);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return function () {
|
|
77
|
+
active = false;
|
|
78
|
+
};
|
|
79
|
+
}, [product, bundleKey]);
|
|
80
|
+
var props = useMemo(function () {
|
|
81
|
+
try {
|
|
82
|
+
var runtime = MerchiProductFormSdkExternals.createProductFormRuntime({
|
|
83
|
+
apiUrl: apiUrl || '',
|
|
84
|
+
product: product,
|
|
85
|
+
onAddToCart: function onAddToCart(job) {
|
|
86
|
+
var _onSaveRef$current;
|
|
87
|
+
return (_onSaveRef$current = onSaveRef.current) === null || _onSaveRef$current === void 0 ? void 0 : _onSaveRef$current.call(onSaveRef, job);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
product: product,
|
|
92
|
+
pricing: runtime.pricing,
|
|
93
|
+
actions: runtime.actions,
|
|
94
|
+
helpers: runtime.helpers
|
|
95
|
+
};
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.error('[merchi_cart] custom form props build failed', e);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}, [product, apiUrl]);
|
|
101
|
+
var isActive = !failed && !loading && !!Loaded && !!props;
|
|
102
|
+
useEffect(function () {
|
|
103
|
+
var _onActiveChangeRef$cu;
|
|
104
|
+
(_onActiveChangeRef$cu = onActiveChangeRef.current) === null || _onActiveChangeRef$cu === void 0 || _onActiveChangeRef$cu.call(onActiveChangeRef, isActive);
|
|
105
|
+
return function () {
|
|
106
|
+
var _onActiveChangeRef$cu2;
|
|
107
|
+
return (_onActiveChangeRef$cu2 = onActiveChangeRef.current) === null || _onActiveChangeRef$cu2 === void 0 ? void 0 : _onActiveChangeRef$cu2.call(onActiveChangeRef, false);
|
|
108
|
+
};
|
|
109
|
+
}, [isActive]);
|
|
110
|
+
if (loading) return /*#__PURE__*/_jsx(LoadingTemplateSm, {});
|
|
111
|
+
if (failed || !Loaded || !props) return /*#__PURE__*/_jsx(_Fragment, {
|
|
112
|
+
children: fallback
|
|
113
|
+
});
|
|
114
|
+
var Shell = MerchiProductFormSdkExternals.ProductFormShell;
|
|
115
|
+
return /*#__PURE__*/_jsx(_Fragment, {
|
|
116
|
+
children: Shell ? /*#__PURE__*/_jsx(Shell, {
|
|
117
|
+
product: props.product,
|
|
118
|
+
pricing: props.pricing,
|
|
119
|
+
actions: props.actions,
|
|
120
|
+
helpers: props.helpers,
|
|
121
|
+
initialJob: initialJob,
|
|
122
|
+
actionLabels: {
|
|
123
|
+
addToCart: 'Save'
|
|
124
|
+
},
|
|
125
|
+
children: /*#__PURE__*/_jsx(Loaded, _objectSpread({}, props))
|
|
126
|
+
}) : /*#__PURE__*/_jsx(Loaded, _objectSpread({}, props))
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
export default CustomCartProductForm;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full merchi_sdk_product_form runtime for window.MerchiProductFormSdk.
|
|
3
|
+
* Keep in sync with merchi_api/common/js/product_form_gate.cjs SDK_EXPORTS.
|
|
4
|
+
*/
|
|
5
|
+
import { SDK_VERSION, serializeJob, nonEmptyGroups, formatCurrency, urlFor, createPricing, createProductFormRuntime, productHasGroups, groupFieldsOf, independentFieldsOf, isOptionQuantityGridProduct, buildProductFormJob, jobToFormState, selectionsToVariations, Section, Stack, Card, Heading, Text, Divider, Field, GroupRows, OptionQuantityGrid, ProductFormProvider, ProductFormShell, useProductForm, OPTION_FIELD_TYPES, theme } from 'merchi_sdk_product_form';
|
|
6
|
+
export var MerchiProductFormSdkExternals = {
|
|
7
|
+
SDK_VERSION: SDK_VERSION,
|
|
8
|
+
serializeJob: serializeJob,
|
|
9
|
+
nonEmptyGroups: nonEmptyGroups,
|
|
10
|
+
formatCurrency: formatCurrency,
|
|
11
|
+
urlFor: urlFor,
|
|
12
|
+
createPricing: createPricing,
|
|
13
|
+
createProductFormRuntime: createProductFormRuntime,
|
|
14
|
+
productHasGroups: productHasGroups,
|
|
15
|
+
groupFieldsOf: groupFieldsOf,
|
|
16
|
+
independentFieldsOf: independentFieldsOf,
|
|
17
|
+
isOptionQuantityGridProduct: isOptionQuantityGridProduct,
|
|
18
|
+
buildProductFormJob: buildProductFormJob,
|
|
19
|
+
jobToFormState: jobToFormState,
|
|
20
|
+
selectionsToVariations: selectionsToVariations,
|
|
21
|
+
Section: Section,
|
|
22
|
+
Stack: Stack,
|
|
23
|
+
Card: Card,
|
|
24
|
+
Heading: Heading,
|
|
25
|
+
Text: Text,
|
|
26
|
+
Divider: Divider,
|
|
27
|
+
Field: Field,
|
|
28
|
+
GroupRows: GroupRows,
|
|
29
|
+
OptionQuantityGrid: OptionQuantityGrid,
|
|
30
|
+
ProductFormProvider: ProductFormProvider,
|
|
31
|
+
ProductFormShell: ProductFormShell,
|
|
32
|
+
useProductForm: useProductForm,
|
|
33
|
+
OPTION_FIELD_TYPES: OPTION_FIELD_TYPES,
|
|
34
|
+
theme: theme
|
|
35
|
+
};
|
|
@@ -23,6 +23,8 @@ import { LoadingTemplateSm } from '../components/LoadingTemplate';
|
|
|
23
23
|
import { useCartContext } from '../CartProvider';
|
|
24
24
|
import { makeCartItem } from '../utilities/cart';
|
|
25
25
|
import { getCartCookieToken } from '../utilities/cookie';
|
|
26
|
+
import { publishedFormBundle } from '../utilities/customForm';
|
|
27
|
+
import CustomCartProductForm from '../components/CustomCartProductForm';
|
|
26
28
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
27
29
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
28
30
|
var MerchiProductForm = /*#__PURE__*/lazy(function () {
|
|
@@ -76,6 +78,10 @@ function PanelEditCartItem(_ref) {
|
|
|
76
78
|
_useState2 = _slicedToArray(_useState, 2),
|
|
77
79
|
loading = _useState2[0],
|
|
78
80
|
setLoading = _useState2[1];
|
|
81
|
+
var _useState3 = useState(false),
|
|
82
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
83
|
+
customFormActive = _useState4[0],
|
|
84
|
+
setCustomFormActive = _useState4[1];
|
|
79
85
|
var formId = 'edit-cart-item-form';
|
|
80
86
|
|
|
81
87
|
// This action patches the cart item
|
|
@@ -149,27 +155,36 @@ function PanelEditCartItem(_ref) {
|
|
|
149
155
|
id: undefined
|
|
150
156
|
});
|
|
151
157
|
var showForm = activeTabIndex === tabIdItem && !!(cartItem !== null && cartItem !== void 0 && cartItem.id) && !!(cartItem !== null && cartItem !== void 0 && (_cartItem$product = cartItem.product) !== null && _cartItem$product !== void 0 && _cartItem$product.id);
|
|
158
|
+
var hasPublishedCustomForm = !!publishedFormBundle(cartItem === null || cartItem === void 0 ? void 0 : cartItem.product);
|
|
159
|
+
var defaultForm = /*#__PURE__*/_jsx(Suspense, {
|
|
160
|
+
fallback: /*#__PURE__*/_jsx(LoadingTemplateSm, {}),
|
|
161
|
+
children: /*#__PURE__*/_jsx(MerchiProductForm, _objectSpread({
|
|
162
|
+
apiUrl: apiUrl,
|
|
163
|
+
isCartItem: true,
|
|
164
|
+
initJob: _objectSpread({}, cleanCartItem),
|
|
165
|
+
initProduct: cartItem.product,
|
|
166
|
+
onSubmit: onSubmit,
|
|
167
|
+
productFormId: formId,
|
|
168
|
+
hideRequestQuotationButton: true,
|
|
169
|
+
hidePaymentUpfrontButton: true
|
|
170
|
+
}, productFormClassNames))
|
|
171
|
+
});
|
|
152
172
|
return /*#__PURE__*/_jsxs(CartTabPanel, {
|
|
153
173
|
tabId: tabIdItem,
|
|
154
174
|
children: [/*#__PURE__*/_jsx(CartBody, {
|
|
155
175
|
style: {
|
|
156
176
|
padding: '2rem'
|
|
157
177
|
},
|
|
158
|
-
children: showForm && /*#__PURE__*/_jsx(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
productFormId: formId,
|
|
167
|
-
hideRequestQuotationButton: true,
|
|
168
|
-
hidePaymentUpfrontButton: true
|
|
169
|
-
}, productFormClassNames))
|
|
170
|
-
})
|
|
178
|
+
children: showForm && (hasPublishedCustomForm ? /*#__PURE__*/_jsx(CustomCartProductForm, {
|
|
179
|
+
product: cartItem.product,
|
|
180
|
+
initialJob: cleanCartItem,
|
|
181
|
+
apiUrl: apiUrl,
|
|
182
|
+
onSave: onSubmit,
|
|
183
|
+
fallback: defaultForm,
|
|
184
|
+
onActiveChange: setCustomFormActive
|
|
185
|
+
}) : defaultForm)
|
|
171
186
|
}), /*#__PURE__*/_jsxs(CartFooter, {
|
|
172
|
-
children: [/*#__PURE__*/_jsx(ButtonBack, {}), /*#__PURE__*/_jsxs(Button, {
|
|
187
|
+
children: [/*#__PURE__*/_jsx(ButtonBack, {}), !customFormActive && /*#__PURE__*/_jsxs(Button, {
|
|
173
188
|
className: classNameBtnEditCartItem,
|
|
174
189
|
disabled: loading,
|
|
175
190
|
form: formId,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for loading a product's published custom form bundle in cart edit.
|
|
3
|
+
* Mirrors the product-embed loader: SRI script tag → window.MERCHI_PRODUCT_FORM_<id>.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** If the product has a published custom form with a compiled bundle, return
|
|
7
|
+
* the info needed to load it; otherwise null (caller falls back). */
|
|
8
|
+
export function publishedFormBundle(product) {
|
|
9
|
+
var form = product === null || product === void 0 ? void 0 : product.productForm;
|
|
10
|
+
if (!form || form.status !== 'published') return null;
|
|
11
|
+
var version = form.activeVersion;
|
|
12
|
+
if (!version || !version.compiledBundleUrl) return null;
|
|
13
|
+
return {
|
|
14
|
+
url: version.compiledBundleUrl,
|
|
15
|
+
integrity: version.bundleSha || undefined,
|
|
16
|
+
globalName: "MERCHI_PRODUCT_FORM_".concat(form.id)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Dynamically load a compiled form bundle and resolve its default export. */
|
|
21
|
+
export function loadFormBundle(bundle) {
|
|
22
|
+
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
23
|
+
var url = bundle.url,
|
|
24
|
+
integrity = bundle.integrity,
|
|
25
|
+
globalName = bundle.globalName;
|
|
26
|
+
var timeoutMs = opts.timeoutMs || 15000;
|
|
27
|
+
var w = window;
|
|
28
|
+
var existing = w[globalName] && w[globalName]["default"];
|
|
29
|
+
if (existing) return Promise.resolve(existing);
|
|
30
|
+
return new Promise(function (resolve, reject) {
|
|
31
|
+
var settled = false;
|
|
32
|
+
var finish = function finish(fn, arg) {
|
|
33
|
+
if (settled) return;
|
|
34
|
+
settled = true;
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
fn(arg);
|
|
37
|
+
};
|
|
38
|
+
var timer = setTimeout(function () {
|
|
39
|
+
return finish(reject, new Error("form bundle ".concat(globalName, " timed out")));
|
|
40
|
+
}, timeoutMs);
|
|
41
|
+
var script = document.createElement('script');
|
|
42
|
+
script.src = url;
|
|
43
|
+
if (integrity) script.integrity = integrity;
|
|
44
|
+
script.crossOrigin = 'anonymous';
|
|
45
|
+
script.async = true;
|
|
46
|
+
script.onload = function () {
|
|
47
|
+
var mod = w[globalName];
|
|
48
|
+
if (mod && mod["default"]) finish(resolve, mod["default"]);else {
|
|
49
|
+
finish(reject, new Error("form bundle ".concat(globalName, " did not register a default export")));
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
script.onerror = function () {
|
|
53
|
+
return finish(reject, new Error("failed to load form bundle ".concat(globalName)));
|
|
54
|
+
};
|
|
55
|
+
document.head.appendChild(script);
|
|
56
|
+
});
|
|
57
|
+
}
|
package/esm/utilities/helpers.js
CHANGED
|
@@ -184,6 +184,10 @@ var productWithImagesEmbed = {
|
|
|
184
184
|
linkedFile: {}
|
|
185
185
|
}
|
|
186
186
|
},
|
|
187
|
+
// Published custom form pointer for cart-item edit (same shape as product embed).
|
|
188
|
+
productForm: {
|
|
189
|
+
activeVersion: {}
|
|
190
|
+
},
|
|
187
191
|
taxType: {}
|
|
188
192
|
};
|
|
189
193
|
var cartShipmentQuote = {
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
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); }
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.CustomCartProductForm = CustomCartProductForm;
|
|
8
|
+
exports["default"] = void 0;
|
|
9
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
10
|
+
var _productFormSdkExternals = require("./product-form-sdk-externals");
|
|
11
|
+
var _customForm = require("../utilities/customForm");
|
|
12
|
+
var _LoadingTemplate = require("./LoadingTemplate");
|
|
13
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
15
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
|
|
16
|
+
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; }
|
|
17
|
+
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; }
|
|
18
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
19
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
20
|
+
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); }
|
|
21
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
22
|
+
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."); }
|
|
23
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
24
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
25
|
+
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; } }
|
|
26
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
27
|
+
/**
|
|
28
|
+
* Loads a product's published custom form for cart-item edit. On any failure
|
|
29
|
+
* renders `fallback` (the default merchi_product_form) so the cart stays usable.
|
|
30
|
+
*/
|
|
31
|
+
function CustomCartProductForm(_ref) {
|
|
32
|
+
var _publishedFormBundle$, _publishedFormBundle;
|
|
33
|
+
var product = _ref.product,
|
|
34
|
+
initialJob = _ref.initialJob,
|
|
35
|
+
apiUrl = _ref.apiUrl,
|
|
36
|
+
onSave = _ref.onSave,
|
|
37
|
+
fallback = _ref.fallback,
|
|
38
|
+
onActiveChange = _ref.onActiveChange;
|
|
39
|
+
var _useState = (0, _react.useState)(null),
|
|
40
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
41
|
+
Loaded = _useState2[0],
|
|
42
|
+
setLoaded = _useState2[1];
|
|
43
|
+
var _useState3 = (0, _react.useState)(false),
|
|
44
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
45
|
+
failed = _useState4[0],
|
|
46
|
+
setFailed = _useState4[1];
|
|
47
|
+
var _useState5 = (0, _react.useState)(true),
|
|
48
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
49
|
+
loading = _useState6[0],
|
|
50
|
+
setLoading = _useState6[1];
|
|
51
|
+
var onSaveRef = (0, _react.useRef)(onSave);
|
|
52
|
+
onSaveRef.current = onSave;
|
|
53
|
+
var onActiveChangeRef = (0, _react.useRef)(onActiveChange);
|
|
54
|
+
onActiveChangeRef.current = onActiveChange;
|
|
55
|
+
var bundleKey = (_publishedFormBundle$ = (_publishedFormBundle = (0, _customForm.publishedFormBundle)(product)) === null || _publishedFormBundle === void 0 ? void 0 : _publishedFormBundle.url) !== null && _publishedFormBundle$ !== void 0 ? _publishedFormBundle$ : '';
|
|
56
|
+
(0, _react.useEffect)(function () {
|
|
57
|
+
var active = true;
|
|
58
|
+
setLoaded(null);
|
|
59
|
+
setFailed(false);
|
|
60
|
+
setLoading(true);
|
|
61
|
+
var bundle = (0, _customForm.publishedFormBundle)(product);
|
|
62
|
+
if (!bundle) {
|
|
63
|
+
setFailed(true);
|
|
64
|
+
setLoading(false);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
var w = window;
|
|
68
|
+
w.React = _react["default"];
|
|
69
|
+
w.MerchiProductFormSdk = _productFormSdkExternals.MerchiProductFormSdkExternals;
|
|
70
|
+
(0, _customForm.loadFormBundle)(bundle).then(function (Component) {
|
|
71
|
+
if (active) {
|
|
72
|
+
setLoaded(function () {
|
|
73
|
+
return Component;
|
|
74
|
+
});
|
|
75
|
+
setLoading(false);
|
|
76
|
+
}
|
|
77
|
+
})["catch"](function (e) {
|
|
78
|
+
console.error('[merchi_cart] custom form load failed', e);
|
|
79
|
+
if (active) {
|
|
80
|
+
setFailed(true);
|
|
81
|
+
setLoading(false);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return function () {
|
|
85
|
+
active = false;
|
|
86
|
+
};
|
|
87
|
+
}, [product, bundleKey]);
|
|
88
|
+
var props = (0, _react.useMemo)(function () {
|
|
89
|
+
try {
|
|
90
|
+
var runtime = _productFormSdkExternals.MerchiProductFormSdkExternals.createProductFormRuntime({
|
|
91
|
+
apiUrl: apiUrl || '',
|
|
92
|
+
product: product,
|
|
93
|
+
onAddToCart: function onAddToCart(job) {
|
|
94
|
+
var _onSaveRef$current;
|
|
95
|
+
return (_onSaveRef$current = onSaveRef.current) === null || _onSaveRef$current === void 0 ? void 0 : _onSaveRef$current.call(onSaveRef, job);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
product: product,
|
|
100
|
+
pricing: runtime.pricing,
|
|
101
|
+
actions: runtime.actions,
|
|
102
|
+
helpers: runtime.helpers
|
|
103
|
+
};
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.error('[merchi_cart] custom form props build failed', e);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}, [product, apiUrl]);
|
|
109
|
+
var isActive = !failed && !loading && !!Loaded && !!props;
|
|
110
|
+
(0, _react.useEffect)(function () {
|
|
111
|
+
var _onActiveChangeRef$cu;
|
|
112
|
+
(_onActiveChangeRef$cu = onActiveChangeRef.current) === null || _onActiveChangeRef$cu === void 0 || _onActiveChangeRef$cu.call(onActiveChangeRef, isActive);
|
|
113
|
+
return function () {
|
|
114
|
+
var _onActiveChangeRef$cu2;
|
|
115
|
+
return (_onActiveChangeRef$cu2 = onActiveChangeRef.current) === null || _onActiveChangeRef$cu2 === void 0 ? void 0 : _onActiveChangeRef$cu2.call(onActiveChangeRef, false);
|
|
116
|
+
};
|
|
117
|
+
}, [isActive]);
|
|
118
|
+
if (loading) return /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingTemplate.LoadingTemplateSm, {});
|
|
119
|
+
if (failed || !Loaded || !props) return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
|
|
120
|
+
children: fallback
|
|
121
|
+
});
|
|
122
|
+
var Shell = _productFormSdkExternals.MerchiProductFormSdkExternals.ProductFormShell;
|
|
123
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
|
|
124
|
+
children: Shell ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Shell, {
|
|
125
|
+
product: props.product,
|
|
126
|
+
pricing: props.pricing,
|
|
127
|
+
actions: props.actions,
|
|
128
|
+
helpers: props.helpers,
|
|
129
|
+
initialJob: initialJob,
|
|
130
|
+
actionLabels: {
|
|
131
|
+
addToCart: 'Save'
|
|
132
|
+
},
|
|
133
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Loaded, _objectSpread({}, props))
|
|
134
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(Loaded, _objectSpread({}, props))
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
var _default = exports["default"] = CustomCartProductForm;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.MerchiProductFormSdkExternals = void 0;
|
|
7
|
+
var _merchi_sdk_product_form = require("merchi_sdk_product_form");
|
|
8
|
+
/**
|
|
9
|
+
* Full merchi_sdk_product_form runtime for window.MerchiProductFormSdk.
|
|
10
|
+
* Keep in sync with merchi_api/common/js/product_form_gate.cjs SDK_EXPORTS.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
var MerchiProductFormSdkExternals = exports.MerchiProductFormSdkExternals = {
|
|
14
|
+
SDK_VERSION: _merchi_sdk_product_form.SDK_VERSION,
|
|
15
|
+
serializeJob: _merchi_sdk_product_form.serializeJob,
|
|
16
|
+
nonEmptyGroups: _merchi_sdk_product_form.nonEmptyGroups,
|
|
17
|
+
formatCurrency: _merchi_sdk_product_form.formatCurrency,
|
|
18
|
+
urlFor: _merchi_sdk_product_form.urlFor,
|
|
19
|
+
createPricing: _merchi_sdk_product_form.createPricing,
|
|
20
|
+
createProductFormRuntime: _merchi_sdk_product_form.createProductFormRuntime,
|
|
21
|
+
productHasGroups: _merchi_sdk_product_form.productHasGroups,
|
|
22
|
+
groupFieldsOf: _merchi_sdk_product_form.groupFieldsOf,
|
|
23
|
+
independentFieldsOf: _merchi_sdk_product_form.independentFieldsOf,
|
|
24
|
+
isOptionQuantityGridProduct: _merchi_sdk_product_form.isOptionQuantityGridProduct,
|
|
25
|
+
buildProductFormJob: _merchi_sdk_product_form.buildProductFormJob,
|
|
26
|
+
jobToFormState: _merchi_sdk_product_form.jobToFormState,
|
|
27
|
+
selectionsToVariations: _merchi_sdk_product_form.selectionsToVariations,
|
|
28
|
+
Section: _merchi_sdk_product_form.Section,
|
|
29
|
+
Stack: _merchi_sdk_product_form.Stack,
|
|
30
|
+
Card: _merchi_sdk_product_form.Card,
|
|
31
|
+
Heading: _merchi_sdk_product_form.Heading,
|
|
32
|
+
Text: _merchi_sdk_product_form.Text,
|
|
33
|
+
Divider: _merchi_sdk_product_form.Divider,
|
|
34
|
+
Field: _merchi_sdk_product_form.Field,
|
|
35
|
+
GroupRows: _merchi_sdk_product_form.GroupRows,
|
|
36
|
+
OptionQuantityGrid: _merchi_sdk_product_form.OptionQuantityGrid,
|
|
37
|
+
ProductFormProvider: _merchi_sdk_product_form.ProductFormProvider,
|
|
38
|
+
ProductFormShell: _merchi_sdk_product_form.ProductFormShell,
|
|
39
|
+
useProductForm: _merchi_sdk_product_form.useProductForm,
|
|
40
|
+
OPTION_FIELD_TYPES: _merchi_sdk_product_form.OPTION_FIELD_TYPES,
|
|
41
|
+
theme: _merchi_sdk_product_form.theme
|
|
42
|
+
};
|
|
@@ -14,7 +14,10 @@ var _LoadingTemplate = require("../components/LoadingTemplate");
|
|
|
14
14
|
var _CartProvider = require("../CartProvider");
|
|
15
15
|
var _cart = require("../utilities/cart");
|
|
16
16
|
var _cookie = require("../utilities/cookie");
|
|
17
|
+
var _customForm = require("../utilities/customForm");
|
|
18
|
+
var _CustomCartProductForm = _interopRequireDefault(require("../components/CustomCartProductForm"));
|
|
17
19
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
20
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
18
21
|
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); }
|
|
19
22
|
function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, "catch": function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
|
|
20
23
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
@@ -81,6 +84,10 @@ function PanelEditCartItem(_ref) {
|
|
|
81
84
|
_useState2 = _slicedToArray(_useState, 2),
|
|
82
85
|
loading = _useState2[0],
|
|
83
86
|
setLoading = _useState2[1];
|
|
87
|
+
var _useState3 = (0, _react.useState)(false),
|
|
88
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
89
|
+
customFormActive = _useState4[0],
|
|
90
|
+
setCustomFormActive = _useState4[1];
|
|
84
91
|
var formId = 'edit-cart-item-form';
|
|
85
92
|
|
|
86
93
|
// This action patches the cart item
|
|
@@ -154,27 +161,36 @@ function PanelEditCartItem(_ref) {
|
|
|
154
161
|
id: undefined
|
|
155
162
|
});
|
|
156
163
|
var showForm = activeTabIndex === _tabs.tabIdItem && !!(cartItem !== null && cartItem !== void 0 && cartItem.id) && !!(cartItem !== null && cartItem !== void 0 && (_cartItem$product = cartItem.product) !== null && _cartItem$product !== void 0 && _cartItem$product.id);
|
|
164
|
+
var hasPublishedCustomForm = !!(0, _customForm.publishedFormBundle)(cartItem === null || cartItem === void 0 ? void 0 : cartItem.product);
|
|
165
|
+
var defaultForm = /*#__PURE__*/(0, _jsxRuntime.jsx)(_react.Suspense, {
|
|
166
|
+
fallback: /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingTemplate.LoadingTemplateSm, {}),
|
|
167
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(MerchiProductForm, _objectSpread({
|
|
168
|
+
apiUrl: apiUrl,
|
|
169
|
+
isCartItem: true,
|
|
170
|
+
initJob: _objectSpread({}, cleanCartItem),
|
|
171
|
+
initProduct: cartItem.product,
|
|
172
|
+
onSubmit: onSubmit,
|
|
173
|
+
productFormId: formId,
|
|
174
|
+
hideRequestQuotationButton: true,
|
|
175
|
+
hidePaymentUpfrontButton: true
|
|
176
|
+
}, productFormClassNames))
|
|
177
|
+
});
|
|
157
178
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.CartTabPanel, {
|
|
158
179
|
tabId: _tabs.tabIdItem,
|
|
159
180
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.CartBody, {
|
|
160
181
|
style: {
|
|
161
182
|
padding: '2rem'
|
|
162
183
|
},
|
|
163
|
-
children: showForm && /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
productFormId: formId,
|
|
172
|
-
hideRequestQuotationButton: true,
|
|
173
|
-
hidePaymentUpfrontButton: true
|
|
174
|
-
}, productFormClassNames))
|
|
175
|
-
})
|
|
184
|
+
children: showForm && (hasPublishedCustomForm ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_CustomCartProductForm["default"], {
|
|
185
|
+
product: cartItem.product,
|
|
186
|
+
initialJob: cleanCartItem,
|
|
187
|
+
apiUrl: apiUrl,
|
|
188
|
+
onSave: onSubmit,
|
|
189
|
+
fallback: defaultForm,
|
|
190
|
+
onActiveChange: setCustomFormActive
|
|
191
|
+
}) : defaultForm)
|
|
176
192
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.CartFooter, {
|
|
177
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_buttons.ButtonBack, {}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_buttons.Button, {
|
|
193
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_buttons.ButtonBack, {}), !customFormActive && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_buttons.Button, {
|
|
178
194
|
className: classNameBtnEditCartItem,
|
|
179
195
|
disabled: loading,
|
|
180
196
|
form: formId,
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.loadFormBundle = loadFormBundle;
|
|
7
|
+
exports.publishedFormBundle = publishedFormBundle;
|
|
8
|
+
/**
|
|
9
|
+
* Helpers for loading a product's published custom form bundle in cart edit.
|
|
10
|
+
* Mirrors the product-embed loader: SRI script tag → window.MERCHI_PRODUCT_FORM_<id>.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** If the product has a published custom form with a compiled bundle, return
|
|
14
|
+
* the info needed to load it; otherwise null (caller falls back). */
|
|
15
|
+
function publishedFormBundle(product) {
|
|
16
|
+
var form = product === null || product === void 0 ? void 0 : product.productForm;
|
|
17
|
+
if (!form || form.status !== 'published') return null;
|
|
18
|
+
var version = form.activeVersion;
|
|
19
|
+
if (!version || !version.compiledBundleUrl) return null;
|
|
20
|
+
return {
|
|
21
|
+
url: version.compiledBundleUrl,
|
|
22
|
+
integrity: version.bundleSha || undefined,
|
|
23
|
+
globalName: "MERCHI_PRODUCT_FORM_".concat(form.id)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Dynamically load a compiled form bundle and resolve its default export. */
|
|
28
|
+
function loadFormBundle(bundle) {
|
|
29
|
+
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
30
|
+
var url = bundle.url,
|
|
31
|
+
integrity = bundle.integrity,
|
|
32
|
+
globalName = bundle.globalName;
|
|
33
|
+
var timeoutMs = opts.timeoutMs || 15000;
|
|
34
|
+
var w = window;
|
|
35
|
+
var existing = w[globalName] && w[globalName]["default"];
|
|
36
|
+
if (existing) return Promise.resolve(existing);
|
|
37
|
+
return new Promise(function (resolve, reject) {
|
|
38
|
+
var settled = false;
|
|
39
|
+
var finish = function finish(fn, arg) {
|
|
40
|
+
if (settled) return;
|
|
41
|
+
settled = true;
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
fn(arg);
|
|
44
|
+
};
|
|
45
|
+
var timer = setTimeout(function () {
|
|
46
|
+
return finish(reject, new Error("form bundle ".concat(globalName, " timed out")));
|
|
47
|
+
}, timeoutMs);
|
|
48
|
+
var script = document.createElement('script');
|
|
49
|
+
script.src = url;
|
|
50
|
+
if (integrity) script.integrity = integrity;
|
|
51
|
+
script.crossOrigin = 'anonymous';
|
|
52
|
+
script.async = true;
|
|
53
|
+
script.onload = function () {
|
|
54
|
+
var mod = w[globalName];
|
|
55
|
+
if (mod && mod["default"]) finish(resolve, mod["default"]);else {
|
|
56
|
+
finish(reject, new Error("form bundle ".concat(globalName, " did not register a default export")));
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
script.onerror = function () {
|
|
60
|
+
return finish(reject, new Error("failed to load form bundle ".concat(globalName)));
|
|
61
|
+
};
|
|
62
|
+
document.head.appendChild(script);
|
|
63
|
+
});
|
|
64
|
+
}
|
package/lib/utilities/helpers.js
CHANGED
|
@@ -195,6 +195,10 @@ var productWithImagesEmbed = {
|
|
|
195
195
|
linkedFile: {}
|
|
196
196
|
}
|
|
197
197
|
},
|
|
198
|
+
// Published custom form pointer for cart-item edit (same shape as product embed).
|
|
199
|
+
productForm: {
|
|
200
|
+
activeVersion: {}
|
|
201
|
+
},
|
|
198
202
|
taxType: {}
|
|
199
203
|
};
|
|
200
204
|
var cartShipmentQuote = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "merchi_cart",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "Merchi's cart",
|
|
5
5
|
"source": "src/index.ts",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
"js-cookie": "^3.0.5",
|
|
92
92
|
"lodash": "^4.17.21",
|
|
93
93
|
"merchi_product_form": "^1.16.0",
|
|
94
|
+
"merchi_sdk_product_form": "^1.0.7",
|
|
94
95
|
"merchi_sdk_ts": "^1.23.0",
|
|
95
96
|
"react-country-region-selector": "^3.6.1",
|
|
96
97
|
"react-geosuggest": "^2.14.1",
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { MerchiProductFormSdkExternals } from './product-form-sdk-externals';
|
|
3
|
+
import { loadFormBundle, publishedFormBundle } from '../utilities/customForm';
|
|
4
|
+
import { LoadingTemplateSm } from './LoadingTemplate';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
product: any;
|
|
8
|
+
/** Current cart item used to hydrate quantity / selections. */
|
|
9
|
+
initialJob: any;
|
|
10
|
+
apiUrl?: string;
|
|
11
|
+
onSave: (job: any) => void;
|
|
12
|
+
/** Rendered when there is no published bundle or load/runtime fails. */
|
|
13
|
+
fallback: React.ReactNode;
|
|
14
|
+
/** Notifies the host when the custom form is actively rendering. */
|
|
15
|
+
onActiveChange?: (active: boolean) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Loads a product's published custom form for cart-item edit. On any failure
|
|
20
|
+
* renders `fallback` (the default merchi_product_form) so the cart stays usable.
|
|
21
|
+
*/
|
|
22
|
+
export function CustomCartProductForm({
|
|
23
|
+
product,
|
|
24
|
+
initialJob,
|
|
25
|
+
apiUrl,
|
|
26
|
+
onSave,
|
|
27
|
+
fallback,
|
|
28
|
+
onActiveChange,
|
|
29
|
+
}: Props) {
|
|
30
|
+
const [Loaded, setLoaded] = useState<React.ComponentType<any> | null>(null);
|
|
31
|
+
const [failed, setFailed] = useState(false);
|
|
32
|
+
const [loading, setLoading] = useState(true);
|
|
33
|
+
const onSaveRef = useRef(onSave);
|
|
34
|
+
onSaveRef.current = onSave;
|
|
35
|
+
const onActiveChangeRef = useRef(onActiveChange);
|
|
36
|
+
onActiveChangeRef.current = onActiveChange;
|
|
37
|
+
|
|
38
|
+
const bundleKey = publishedFormBundle(product)?.url ?? '';
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
let active = true;
|
|
42
|
+
setLoaded(null);
|
|
43
|
+
setFailed(false);
|
|
44
|
+
setLoading(true);
|
|
45
|
+
const bundle = publishedFormBundle(product);
|
|
46
|
+
if (!bundle) {
|
|
47
|
+
setFailed(true);
|
|
48
|
+
setLoading(false);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const w = window as any;
|
|
52
|
+
w.React = React;
|
|
53
|
+
w.MerchiProductFormSdk = MerchiProductFormSdkExternals;
|
|
54
|
+
loadFormBundle(bundle)
|
|
55
|
+
.then((Component) => {
|
|
56
|
+
if (active) {
|
|
57
|
+
setLoaded(() => Component);
|
|
58
|
+
setLoading(false);
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.catch((e) => {
|
|
62
|
+
console.error('[merchi_cart] custom form load failed', e);
|
|
63
|
+
if (active) {
|
|
64
|
+
setFailed(true);
|
|
65
|
+
setLoading(false);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return () => {
|
|
69
|
+
active = false;
|
|
70
|
+
};
|
|
71
|
+
}, [product, bundleKey]);
|
|
72
|
+
|
|
73
|
+
const props = useMemo(() => {
|
|
74
|
+
try {
|
|
75
|
+
const runtime = MerchiProductFormSdkExternals.createProductFormRuntime({
|
|
76
|
+
apiUrl: apiUrl || '',
|
|
77
|
+
product,
|
|
78
|
+
onAddToCart: (job: any) => onSaveRef.current?.(job),
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
product,
|
|
82
|
+
pricing: runtime.pricing,
|
|
83
|
+
actions: runtime.actions,
|
|
84
|
+
helpers: runtime.helpers,
|
|
85
|
+
};
|
|
86
|
+
} catch (e) {
|
|
87
|
+
console.error('[merchi_cart] custom form props build failed', e);
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}, [product, apiUrl]);
|
|
91
|
+
|
|
92
|
+
const isActive = !failed && !loading && !!Loaded && !!props;
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
onActiveChangeRef.current?.(isActive);
|
|
95
|
+
return () => onActiveChangeRef.current?.(false);
|
|
96
|
+
}, [isActive]);
|
|
97
|
+
|
|
98
|
+
if (loading) return <LoadingTemplateSm />;
|
|
99
|
+
if (failed || !Loaded || !props) return <>{fallback}</>;
|
|
100
|
+
|
|
101
|
+
const Shell = MerchiProductFormSdkExternals.ProductFormShell;
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<>
|
|
105
|
+
{Shell ? (
|
|
106
|
+
<Shell
|
|
107
|
+
product={props.product}
|
|
108
|
+
pricing={props.pricing}
|
|
109
|
+
actions={props.actions}
|
|
110
|
+
helpers={props.helpers}
|
|
111
|
+
initialJob={initialJob}
|
|
112
|
+
actionLabels={{ addToCart: 'Save' }}
|
|
113
|
+
>
|
|
114
|
+
<Loaded {...props} />
|
|
115
|
+
</Shell>
|
|
116
|
+
) : (
|
|
117
|
+
<Loaded {...props} />
|
|
118
|
+
)}
|
|
119
|
+
</>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default CustomCartProductForm;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full merchi_sdk_product_form runtime for window.MerchiProductFormSdk.
|
|
3
|
+
* Keep in sync with merchi_api/common/js/product_form_gate.cjs SDK_EXPORTS.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
SDK_VERSION,
|
|
7
|
+
serializeJob,
|
|
8
|
+
nonEmptyGroups,
|
|
9
|
+
formatCurrency,
|
|
10
|
+
urlFor,
|
|
11
|
+
createPricing,
|
|
12
|
+
createProductFormRuntime,
|
|
13
|
+
productHasGroups,
|
|
14
|
+
groupFieldsOf,
|
|
15
|
+
independentFieldsOf,
|
|
16
|
+
isOptionQuantityGridProduct,
|
|
17
|
+
buildProductFormJob,
|
|
18
|
+
jobToFormState,
|
|
19
|
+
selectionsToVariations,
|
|
20
|
+
Section,
|
|
21
|
+
Stack,
|
|
22
|
+
Card,
|
|
23
|
+
Heading,
|
|
24
|
+
Text,
|
|
25
|
+
Divider,
|
|
26
|
+
Field,
|
|
27
|
+
GroupRows,
|
|
28
|
+
OptionQuantityGrid,
|
|
29
|
+
ProductFormProvider,
|
|
30
|
+
ProductFormShell,
|
|
31
|
+
useProductForm,
|
|
32
|
+
OPTION_FIELD_TYPES,
|
|
33
|
+
theme,
|
|
34
|
+
} from 'merchi_sdk_product_form';
|
|
35
|
+
|
|
36
|
+
export const MerchiProductFormSdkExternals = {
|
|
37
|
+
SDK_VERSION,
|
|
38
|
+
serializeJob,
|
|
39
|
+
nonEmptyGroups,
|
|
40
|
+
formatCurrency,
|
|
41
|
+
urlFor,
|
|
42
|
+
createPricing,
|
|
43
|
+
createProductFormRuntime,
|
|
44
|
+
productHasGroups,
|
|
45
|
+
groupFieldsOf,
|
|
46
|
+
independentFieldsOf,
|
|
47
|
+
isOptionQuantityGridProduct,
|
|
48
|
+
buildProductFormJob,
|
|
49
|
+
jobToFormState,
|
|
50
|
+
selectionsToVariations,
|
|
51
|
+
Section,
|
|
52
|
+
Stack,
|
|
53
|
+
Card,
|
|
54
|
+
Heading,
|
|
55
|
+
Text,
|
|
56
|
+
Divider,
|
|
57
|
+
Field,
|
|
58
|
+
GroupRows,
|
|
59
|
+
OptionQuantityGrid,
|
|
60
|
+
ProductFormProvider,
|
|
61
|
+
ProductFormShell,
|
|
62
|
+
useProductForm,
|
|
63
|
+
OPTION_FIELD_TYPES,
|
|
64
|
+
theme,
|
|
65
|
+
};
|
|
@@ -12,6 +12,8 @@ import { LoadingTemplateSm } from '../components/LoadingTemplate';
|
|
|
12
12
|
import { useCartContext } from '../CartProvider';
|
|
13
13
|
import { makeCartItem } from '../utilities/cart';
|
|
14
14
|
import { getCartCookieToken } from '../utilities/cookie';
|
|
15
|
+
import { publishedFormBundle } from '../utilities/customForm';
|
|
16
|
+
import CustomCartProductForm from '../components/CustomCartProductForm';
|
|
15
17
|
|
|
16
18
|
const MerchiProductForm = lazy(() => import('merchi_product_form'));
|
|
17
19
|
|
|
@@ -54,8 +56,9 @@ function PanelEditCartItem({ cart }: Props) {
|
|
|
54
56
|
setCartItem,
|
|
55
57
|
} = useCartContext();
|
|
56
58
|
const [loading, setLoading] = useState(false);
|
|
59
|
+
const [customFormActive, setCustomFormActive] = useState(false);
|
|
57
60
|
const formId = 'edit-cart-item-form';
|
|
58
|
-
|
|
61
|
+
|
|
59
62
|
// This action patches the cart item
|
|
60
63
|
async function actionCartItemEdit(cartItemJson: any) {
|
|
61
64
|
setLoading(true);
|
|
@@ -68,7 +71,7 @@ function PanelEditCartItem({ cart }: Props) {
|
|
|
68
71
|
variations = [],
|
|
69
72
|
variationsGroups = [],
|
|
70
73
|
} = cartItemJson;
|
|
71
|
-
|
|
74
|
+
|
|
72
75
|
const cartItemEnt = makeCartItem({
|
|
73
76
|
...cartItem,
|
|
74
77
|
id: (cartItem as any).id,
|
|
@@ -79,19 +82,19 @@ function PanelEditCartItem({ cart }: Props) {
|
|
|
79
82
|
variations: variations.map(cleanVariation),
|
|
80
83
|
variationsGroups: variationsGroups.map(cleanVariationGroups),
|
|
81
84
|
}, true, cartToken);
|
|
82
|
-
|
|
85
|
+
|
|
83
86
|
// Save changes to the Cart Item
|
|
84
87
|
await cartItemEnt.save();
|
|
85
|
-
|
|
88
|
+
|
|
86
89
|
// Refetch the Cart and all it's relationships
|
|
87
90
|
await refetchCart();
|
|
88
|
-
|
|
91
|
+
|
|
89
92
|
// Show success alert
|
|
90
93
|
alertSuccess('Item updated.');
|
|
91
|
-
|
|
94
|
+
|
|
92
95
|
// Set active Cart tab
|
|
93
96
|
setActiveTabIndex(tabIdItems);
|
|
94
|
-
|
|
97
|
+
|
|
95
98
|
// Clear Cart Item from state
|
|
96
99
|
setCartItem({});
|
|
97
100
|
} catch (e: any) {
|
|
@@ -110,37 +113,57 @@ function PanelEditCartItem({ cart }: Props) {
|
|
|
110
113
|
};
|
|
111
114
|
const showForm =
|
|
112
115
|
activeTabIndex === tabIdItem && !!cartItem?.id && !!cartItem?.product?.id;
|
|
116
|
+
const hasPublishedCustomForm = !!publishedFormBundle(cartItem?.product);
|
|
117
|
+
|
|
118
|
+
const defaultForm = (
|
|
119
|
+
<Suspense fallback={<LoadingTemplateSm />}>
|
|
120
|
+
<MerchiProductForm
|
|
121
|
+
apiUrl={apiUrl}
|
|
122
|
+
isCartItem={true}
|
|
123
|
+
initJob={{...cleanCartItem}}
|
|
124
|
+
initProduct={cartItem.product}
|
|
125
|
+
onSubmit={onSubmit}
|
|
126
|
+
productFormId={formId}
|
|
127
|
+
hideRequestQuotationButton={true}
|
|
128
|
+
hidePaymentUpfrontButton={true}
|
|
129
|
+
{...productFormClassNames}
|
|
130
|
+
/>
|
|
131
|
+
</Suspense>
|
|
132
|
+
);
|
|
113
133
|
|
|
114
134
|
return (
|
|
115
135
|
<CartTabPanel tabId={tabIdItem}>
|
|
116
136
|
<CartBody style={{padding: '2rem'}}>
|
|
117
137
|
{showForm && (
|
|
118
|
-
|
|
119
|
-
<
|
|
138
|
+
hasPublishedCustomForm ? (
|
|
139
|
+
<CustomCartProductForm
|
|
140
|
+
product={cartItem.product}
|
|
141
|
+
initialJob={cleanCartItem}
|
|
120
142
|
apiUrl={apiUrl}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
onSubmit={onSubmit}
|
|
125
|
-
productFormId={formId}
|
|
126
|
-
hideRequestQuotationButton={true}
|
|
127
|
-
hidePaymentUpfrontButton={true}
|
|
128
|
-
{...productFormClassNames}
|
|
143
|
+
onSave={onSubmit}
|
|
144
|
+
fallback={defaultForm}
|
|
145
|
+
onActiveChange={setCustomFormActive}
|
|
129
146
|
/>
|
|
130
|
-
|
|
147
|
+
) : (
|
|
148
|
+
defaultForm
|
|
149
|
+
)
|
|
131
150
|
)}
|
|
132
151
|
</CartBody>
|
|
133
152
|
<CartFooter>
|
|
134
153
|
<ButtonBack />
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
154
|
+
{/* Custom forms render their own Save in ProductFormShell; keep the
|
|
155
|
+
HTML-form Save only for the default merchi_product_form path. */}
|
|
156
|
+
{!customFormActive && (
|
|
157
|
+
<Button
|
|
158
|
+
className={classNameBtnEditCartItem}
|
|
159
|
+
disabled={loading}
|
|
160
|
+
form={formId}
|
|
161
|
+
type='submit'
|
|
162
|
+
>
|
|
163
|
+
{loading && <FontAwesomeIcon icon={faCircleNotch} spin />}
|
|
164
|
+
{loading ? ' Loading...' : 'Save'}
|
|
165
|
+
</Button>
|
|
166
|
+
)}
|
|
144
167
|
</CartFooter>
|
|
145
168
|
</CartTabPanel>
|
|
146
169
|
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for loading a product's published custom form bundle in cart edit.
|
|
3
|
+
* Mirrors the product-embed loader: SRI script tag → window.MERCHI_PRODUCT_FORM_<id>.
|
|
4
|
+
*/
|
|
5
|
+
import type { ComponentType } from 'react';
|
|
6
|
+
|
|
7
|
+
export interface FormBundleRef {
|
|
8
|
+
url: string;
|
|
9
|
+
integrity?: string;
|
|
10
|
+
globalName: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** If the product has a published custom form with a compiled bundle, return
|
|
14
|
+
* the info needed to load it; otherwise null (caller falls back). */
|
|
15
|
+
export function publishedFormBundle(product: any): FormBundleRef | null {
|
|
16
|
+
const form = product?.productForm;
|
|
17
|
+
if (!form || form.status !== 'published') return null;
|
|
18
|
+
const version = form.activeVersion;
|
|
19
|
+
if (!version || !version.compiledBundleUrl) return null;
|
|
20
|
+
return {
|
|
21
|
+
url: version.compiledBundleUrl,
|
|
22
|
+
integrity: version.bundleSha || undefined,
|
|
23
|
+
globalName: `MERCHI_PRODUCT_FORM_${form.id}`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Dynamically load a compiled form bundle and resolve its default export. */
|
|
28
|
+
export function loadFormBundle(
|
|
29
|
+
bundle: FormBundleRef,
|
|
30
|
+
opts: { timeoutMs?: number } = {},
|
|
31
|
+
): Promise<ComponentType<any>> {
|
|
32
|
+
const { url, integrity, globalName } = bundle;
|
|
33
|
+
const timeoutMs = opts.timeoutMs || 15000;
|
|
34
|
+
const w = window as any;
|
|
35
|
+
|
|
36
|
+
const existing = w[globalName] && w[globalName].default;
|
|
37
|
+
if (existing) return Promise.resolve(existing);
|
|
38
|
+
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
let settled = false;
|
|
41
|
+
const finish = (fn: (arg: any) => void, arg: any) => {
|
|
42
|
+
if (settled) return;
|
|
43
|
+
settled = true;
|
|
44
|
+
clearTimeout(timer);
|
|
45
|
+
fn(arg);
|
|
46
|
+
};
|
|
47
|
+
const timer = setTimeout(
|
|
48
|
+
() => finish(reject, new Error(`form bundle ${globalName} timed out`)),
|
|
49
|
+
timeoutMs,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const script = document.createElement('script');
|
|
53
|
+
script.src = url;
|
|
54
|
+
if (integrity) script.integrity = integrity;
|
|
55
|
+
script.crossOrigin = 'anonymous';
|
|
56
|
+
script.async = true;
|
|
57
|
+
script.onload = () => {
|
|
58
|
+
const mod = w[globalName];
|
|
59
|
+
if (mod && mod.default) finish(resolve, mod.default);
|
|
60
|
+
else {
|
|
61
|
+
finish(
|
|
62
|
+
reject,
|
|
63
|
+
new Error(`form bundle ${globalName} did not register a default export`),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
script.onerror = () =>
|
|
68
|
+
finish(reject, new Error(`failed to load form bundle ${globalName}`));
|
|
69
|
+
document.head.appendChild(script);
|
|
70
|
+
});
|
|
71
|
+
}
|
package/src/utilities/helpers.ts
CHANGED
|
@@ -173,6 +173,8 @@ const productWithImagesEmbed: any = {
|
|
|
173
173
|
groupVariationFields: {options: {linkedFile: {}}},
|
|
174
174
|
images: {},
|
|
175
175
|
independentVariationFields: {options: {linkedFile: {}}},
|
|
176
|
+
// Published custom form pointer for cart-item edit (same shape as product embed).
|
|
177
|
+
productForm: {activeVersion: {}},
|
|
176
178
|
taxType: {},
|
|
177
179
|
};
|
|
178
180
|
|