@occluze/tether 0.6.6 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/esm/astro.js +2 -2
- package/dist/esm/chunks/account-C3NP6HFB.js +1 -0
- package/dist/esm/chunks/account-pages-WLK374FO.js +1 -0
- package/dist/esm/chunks/analytics-G3SAWMNL.js +1 -0
- package/dist/esm/chunks/chunk-27PJJZGC.js +1 -0
- package/dist/esm/chunks/chunk-2FSXS55U.js +1 -0
- package/dist/esm/chunks/chunk-FCSWQDVV.js +1 -0
- package/dist/esm/chunks/chunk-FOMSMIAM.js +1 -0
- package/dist/esm/chunks/chunk-GZJTCLLW.js +1 -0
- package/dist/esm/chunks/chunk-HEQILZVR.js +1 -0
- package/dist/esm/chunks/chunk-JOOEXYF3.js +4 -0
- package/dist/esm/chunks/chunk-MPTQYLSM.js +1 -0
- package/dist/esm/chunks/chunk-NEHZ37TU.js +3 -0
- package/dist/esm/chunks/chunk-V74KGEJF.js +1 -0
- package/dist/esm/chunks/chunk-VT4CDUJJ.js +1 -0
- package/dist/esm/chunks/chunk-YJLWX2HK.js +1 -0
- package/dist/esm/chunks/diagnose-6VVGZNTY.js +14 -0
- package/dist/esm/chunks/extras-GJVRDFM3.js +1 -0
- package/dist/esm/chunks/markets-ui-UNB4PTA7.js +1 -0
- package/dist/esm/chunks/search-OU75RNQL.js +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/server.js +2 -1
- package/dist/presets/barba.esm.js +56 -36
- package/dist/tether.account-pages.js +776 -432
- package/dist/tether.account-pages.min.js +1 -1
- package/dist/tether.account.js +655 -321
- package/dist/tether.account.min.js +1 -1
- package/dist/tether.analytics.js +318 -191
- package/dist/tether.analytics.min.js +1 -1
- package/dist/tether.diagnose.js +334 -243
- package/dist/tether.diagnose.min.js +2 -2
- package/dist/tether.extras.js +78 -33
- package/dist/tether.extras.min.js +1 -1
- package/dist/tether.js +3171 -1998
- package/dist/tether.markets.js +231 -126
- package/dist/tether.markets.min.js +1 -1
- package/dist/tether.min.js +0 -0
- package/dist/tether.min.js.map +4 -4
- package/dist/tether.search.js +221 -126
- package/dist/tether.search.min.js +1 -1
- package/package.json +23 -4
- package/src/account/addresses-view.js +92 -0
- package/src/account/browser-session.js +359 -0
- package/src/account/markup.js +48 -0
- package/src/account/operations.js +180 -0
- package/src/account/order-lines.js +96 -0
- package/src/account/order-list.js +91 -0
- package/src/account/order-page.js +192 -0
- package/src/account/page-events.js +288 -0
- package/src/account/server-client.js +172 -0
- package/src/account/shared.js +111 -0
- package/src/account/shopify-auth.js +150 -0
- package/src/account/subscriptions-view.js +158 -0
- package/src/account-api.js +118 -0
- package/src/account-links.js +35 -0
- package/src/account-pages.js +68 -284
- package/src/account.js +281 -317
- package/src/amounts.js +36 -0
- package/src/analytics-shopify.js +241 -0
- package/src/analytics.js +304 -184
- package/src/api-version.js +75 -25
- package/src/astro.js +233 -68
- package/src/cart/checkout.js +97 -0
- package/src/cart/drawer.js +138 -0
- package/src/cart/mutations.js +234 -0
- package/src/cart/render.js +294 -0
- package/src/cart/store.js +326 -0
- package/src/cart.js +322 -481
- package/src/client.js +214 -78
- package/src/collection.js +126 -57
- package/src/config.js +323 -59
- package/src/controls/cart-controls.js +155 -0
- package/src/controls/discount.js +96 -0
- package/src/controls/extras.js +63 -0
- package/src/controls/navigation.js +83 -0
- package/src/diagnose-markup.js +333 -0
- package/src/diagnose.js +251 -202
- package/src/dom.js +219 -220
- package/src/extras.js +119 -37
- package/src/fragments.js +115 -22
- package/src/images.js +104 -0
- package/src/index.js +245 -285
- package/src/initial.js +78 -66
- package/src/lazy-iife.js +92 -22
- package/src/lazy.js +25 -4
- package/src/loader.js +263 -124
- package/src/market-codes.js +35 -0
- package/src/markets-ui.js +169 -55
- package/src/markets.js +286 -85
- package/src/markup.js +64 -0
- package/src/mounting.js +214 -91
- package/src/page-market.js +116 -0
- package/src/parts/account-pages.js +4 -1
- package/src/parts/account.js +4 -1
- package/src/parts/analytics.js +4 -1
- package/src/parts/diagnose.js +4 -1
- package/src/parts/extras.js +4 -1
- package/src/parts/markets.js +4 -1
- package/src/parts/search.js +4 -1
- package/src/presets/barba.js +120 -30
- package/src/product-events.js +130 -0
- package/src/product-render.js +257 -0
- package/src/product-state.js +318 -0
- package/src/product.js +233 -459
- package/src/repeaters.js +173 -0
- package/src/search.js +277 -107
- package/src/selling-plans.js +107 -47
- package/src/server.js +168 -205
- package/src/shopify-values.js +89 -0
- package/src/status.js +99 -39
- package/src/store-html.js +0 -0
- package/src/storefront/catalog.js +199 -0
- package/src/storefront/json-ld.js +167 -0
- package/src/variant-render.js +165 -0
- package/types/astro.d.ts +13 -2
- package/types/index.d.ts +178 -27
- package/types/server.d.ts +89 -17
- package/dist/esm/chunks/account-QUBHQG3C.js +0 -1
- package/dist/esm/chunks/account-pages-VWDHF6WA.js +0 -1
- package/dist/esm/chunks/analytics-SLATWPLC.js +0 -1
- package/dist/esm/chunks/chunk-64TUD3QR.js +0 -1
- package/dist/esm/chunks/chunk-AJLPU2JC.js +0 -4
- package/dist/esm/chunks/chunk-FIOZPPC5.js +0 -1
- package/dist/esm/chunks/chunk-K2YEQAIH.js +0 -1
- package/dist/esm/chunks/chunk-L2TRRKYV.js +0 -1
- package/dist/esm/chunks/chunk-RREHGH4A.js +0 -1
- package/dist/esm/chunks/chunk-VC46IEJQ.js +0 -1
- package/dist/esm/chunks/chunk-VCPI7MJ2.js +0 -1
- package/dist/esm/chunks/chunk-VPVJPNB4.js +0 -1
- package/dist/esm/chunks/chunk-ZIUIWEON.js +0 -1
- package/dist/esm/chunks/diagnose-VXQBSD3A.js +0 -14
- package/dist/esm/chunks/extras-R4X34VQL.js +0 -1
- package/dist/esm/chunks/markets-ui-F3R5T5EL.js +0 -1
- package/dist/esm/chunks/search-NILMGYXG.js +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
(() => {
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __export = (target, all) => {
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
requestReturn: () => requestReturn,
|
|
16
17
|
saveAddress: () => saveAddress,
|
|
17
18
|
setup: () => setup,
|
|
19
|
+
showUnavailable: () => showUnavailable,
|
|
18
20
|
subscriptionAction: () => subscriptionAction,
|
|
19
21
|
subscriptions: () => subscriptions,
|
|
20
22
|
updateProfile: () => updateProfile
|
|
@@ -22,173 +24,234 @@
|
|
|
22
24
|
|
|
23
25
|
// tether-core:config.js
|
|
24
26
|
var m = window.Tether.__core["config.js"];
|
|
25
|
-
var { DEFAULTS, cfg, configure, isConfigured, log, reportError, warn } = m;
|
|
26
|
-
|
|
27
|
-
// tether-core:client.js
|
|
28
|
-
var m2 = window.Tether.__core["client.js"];
|
|
29
|
-
var { TetherError, endpoint, gql, request, unwrap, withContext } = m2;
|
|
27
|
+
var { DEFAULTS, cfg, configure, isConfigured, log, reportError, resolveConfig, toMetafieldIdentifier, warn } = m;
|
|
30
28
|
|
|
31
29
|
// tether-core:dom.js
|
|
32
|
-
var
|
|
33
|
-
var { attr, busy, cdnUrl,
|
|
30
|
+
var m2 = window.Tether.__core["dom.js"];
|
|
31
|
+
var { attr, busy, cdnUrl, emit, formatDate, formatMoney, formatPercent, get, has, hasTemplate, imageUrl, notifyLayoutChanged, parseJSONAttr, productUrl, qsa, repeat, repeatKeyed, sameImage, scoped, sel, setImage, setSelected, setStoreHtml, setText, setTextTracked, show, toAttributes, watchImages } = m2;
|
|
32
|
+
|
|
33
|
+
// tether-core:client.js
|
|
34
|
+
var m3 = window.Tether.__core["client.js"];
|
|
35
|
+
var { TetherError, endpoint, gql, request, unwrap, withContext } = m3;
|
|
34
36
|
|
|
35
|
-
// server/src/account-ops.js
|
|
37
|
+
// server/src/shared/account-ops.js
|
|
38
|
+
var InputError = class extends Error {
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} message
|
|
41
|
+
* @param {{ status?: number, userErrors?: UserError[] }} [details]
|
|
42
|
+
*/
|
|
43
|
+
constructor(message, { status = 400, userErrors = [] } = {}) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = "InputError";
|
|
46
|
+
this.status = status;
|
|
47
|
+
this.userErrors = userErrors;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
36
50
|
var MONEY = "amount currencyCode";
|
|
37
51
|
var ADDRESS = "id firstName lastName company address1 address2 city province zoneCode zip country countryCode phoneNumber formatted";
|
|
38
|
-
var
|
|
52
|
+
var USER_ERRORS = "userErrors { field message }";
|
|
53
|
+
var QUERIES = {
|
|
39
54
|
order: `query TetherOrder($id:ID!){order(id:$id){id name number processedAt cancelledAt financialStatus fulfillmentStatus statusPageUrl requiresShipping totalPrice{${MONEY}}subtotal{${MONEY}}totalShipping{${MONEY}}totalTax{${MONEY}}shippingAddress{${ADDRESS}}billingAddress{${ADDRESS}}lineItems(first:50){nodes{id name title variantTitle quantity sku image{url altText}price{${MONEY}}totalPrice{${MONEY}}suggestedReturnReasonDefinitions(first:10){nodes{id name handle}}}}fulfillments(first:10){nodes{id status createdAt estimatedDeliveryAt latestShipmentStatus trackingInformation{company number url}}}returnInformation{returnableLineItems(first:50){nodes{quantity lineItem{id}}}}returns(first:10){nodes{id status}}}}`,
|
|
40
55
|
addresses: `query TetherAddresses{customer{defaultAddress{id}addresses(first:20){nodes{${ADDRESS}}}}}`,
|
|
41
56
|
subscriptions: `query TetherSubscriptions{customer{subscriptionContracts(first:20,reverse:true){nodes{id status nextBillingDate currencyCode deliveryPrice{${MONEY}}deliveryPolicy{interval intervalCount{count}}lines(first:20){nodes{id name title variantTitle quantity sku currentPrice{${MONEY}}lineDiscountedPrice{${MONEY}}image{url altText}}}upcomingBillingCycles(first:3){nodes{cycleIndex skipped billingAttemptExpectedDate}}}}}}`,
|
|
42
57
|
contract: `query TetherContract($id:ID!){customer{subscriptionContract(id:$id){id status upcomingBillingCycles(first:3){nodes{cycleIndex skipped}}}}}`
|
|
43
58
|
};
|
|
44
|
-
var
|
|
45
|
-
profile:
|
|
46
|
-
addressCreate: `mutation TetherAddressCreate($address:CustomerAddressInput!,$default:Boolean){customerAddressCreate(address:$address,defaultAddress:$default){customerAddress{${ADDRESS}}
|
|
47
|
-
addressUpdate: `mutation TetherAddressUpdate($id:ID!,$address:CustomerAddressInput,$default:Boolean){customerAddressUpdate(addressId:$id,address:$address,defaultAddress:$default){customerAddress{${ADDRESS}}
|
|
48
|
-
addressDelete:
|
|
49
|
-
|
|
50
|
-
resume: "mutation TetherResume($id: ID!) { subscriptionContractActivate(subscriptionContractId: $id) { contract { id status } userErrors { field message } } }",
|
|
51
|
-
cancel: "mutation TetherCancel($id: ID!) { subscriptionContractCancel(subscriptionContractId: $id) { contract { id status } userErrors { field message } } }",
|
|
52
|
-
skip: "mutation TetherSkip($input: SubscriptionBillingCycleInput!) { subscriptionBillingCycleSkip(billingCycleInput: $input) { billingCycle { cycleIndex skipped } userErrors { field message } } }",
|
|
53
|
-
unskip: "mutation TetherUnskip($input: SubscriptionBillingCycleInput!) { subscriptionBillingCycleUnskip(billingCycleInput: $input) { billingCycle { cycleIndex skipped } userErrors { field message } } }",
|
|
54
|
-
returnRequest: "mutation TetherReturn($orderId: ID!, $lines: [RequestedLineItemInput!]!) { orderRequestReturn(orderId: $orderId, requestedLineItems: $lines) { return { id status } userErrors { field message } } }"
|
|
55
|
-
};
|
|
56
|
-
var InputError = class extends Error {
|
|
59
|
+
var MUTATIONS = {
|
|
60
|
+
profile: `mutation TetherProfile($input:CustomerUpdateInput!){customerUpdate(input:$input){customer{firstName lastName}${USER_ERRORS}}}`,
|
|
61
|
+
addressCreate: `mutation TetherAddressCreate($address:CustomerAddressInput!,$default:Boolean){customerAddressCreate(address:$address,defaultAddress:$default){customerAddress{${ADDRESS}}${USER_ERRORS}}}`,
|
|
62
|
+
addressUpdate: `mutation TetherAddressUpdate($id:ID!,$address:CustomerAddressInput,$default:Boolean){customerAddressUpdate(addressId:$id,address:$address,defaultAddress:$default){customerAddress{${ADDRESS}}${USER_ERRORS}}}`,
|
|
63
|
+
addressDelete: `mutation TetherAddressDelete($id:ID!){customerAddressDelete(addressId:$id){deletedAddressId ${USER_ERRORS}}}`,
|
|
64
|
+
returnRequest: `mutation TetherReturn($orderId:ID!,$lines:[RequestedLineItemInput!]!){orderRequestReturn(orderId:$orderId,requestedLineItems:$lines){return{id status}${USER_ERRORS}}}`
|
|
57
65
|
};
|
|
58
|
-
var
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
var SUBSCRIPTION_ACTIONS = {
|
|
67
|
+
pause: {
|
|
68
|
+
document: `mutation TetherPause($id:ID!){subscriptionContractPause(subscriptionContractId:$id){contract{id status}${USER_ERRORS}}}`,
|
|
69
|
+
payload: "subscriptionContractPause",
|
|
70
|
+
input: "contract"
|
|
71
|
+
},
|
|
72
|
+
resume: {
|
|
73
|
+
document: `mutation TetherResume($id:ID!){subscriptionContractActivate(subscriptionContractId:$id){contract{id status}${USER_ERRORS}}}`,
|
|
74
|
+
payload: "subscriptionContractActivate",
|
|
75
|
+
input: "contract"
|
|
76
|
+
},
|
|
77
|
+
cancel: {
|
|
78
|
+
document: `mutation TetherCancel($id:ID!){subscriptionContractCancel(subscriptionContractId:$id){contract{id status}${USER_ERRORS}}}`,
|
|
79
|
+
payload: "subscriptionContractCancel",
|
|
80
|
+
input: "contract"
|
|
81
|
+
},
|
|
82
|
+
skip: {
|
|
83
|
+
document: `mutation TetherSkip($input:SubscriptionBillingCycleInput!){subscriptionBillingCycleSkip(billingCycleInput:$input){billingCycle{cycleIndex skipped}${USER_ERRORS}}}`,
|
|
84
|
+
payload: "subscriptionBillingCycleSkip",
|
|
85
|
+
input: "billingCycle"
|
|
86
|
+
},
|
|
87
|
+
unskip: {
|
|
88
|
+
document: `mutation TetherUnskip($input:SubscriptionBillingCycleInput!){subscriptionBillingCycleUnskip(billingCycleInput:$input){billingCycle{cycleIndex skipped}${USER_ERRORS}}}`,
|
|
89
|
+
payload: "subscriptionBillingCycleUnskip",
|
|
90
|
+
input: "billingCycle"
|
|
91
|
+
}
|
|
65
92
|
};
|
|
93
|
+
function isSubscriptionAction(value) {
|
|
94
|
+
return typeof value === "string" && Object.hasOwn(SUBSCRIPTION_ACTIONS, value);
|
|
95
|
+
}
|
|
96
|
+
function field(value, key) {
|
|
97
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
98
|
+
return (
|
|
99
|
+
/** @type {Record<string, unknown>} */
|
|
100
|
+
value[key]
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
function nodes(value) {
|
|
104
|
+
const list = field(value, "nodes");
|
|
105
|
+
return Array.isArray(list) ? list : [];
|
|
106
|
+
}
|
|
107
|
+
var NUMERIC_ID = /^\d{1,20}$/;
|
|
66
108
|
function idOf(type, value) {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
109
|
+
const raw = typeof value === "string" || typeof value === "number" ? String(value) : "";
|
|
110
|
+
const id = NUMERIC_ID.test(raw) ? `gid://shopify/${type}/${raw}` : raw;
|
|
111
|
+
if (!new RegExp(`^gid://shopify/${type}/\\d+(\\?[\\w=&.-]{1,80})?$`).test(id)) throw new InputError(`${type} id`);
|
|
112
|
+
return id;
|
|
113
|
+
}
|
|
114
|
+
var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/g;
|
|
115
|
+
function text(value, maxLength = 255) {
|
|
116
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
117
|
+
if (typeof value !== "string" && typeof value !== "number") throw new InputError("text");
|
|
118
|
+
const clean = String(value).replace(CONTROL_CHARACTERS, "").trim();
|
|
119
|
+
if (clean.length > maxLength) throw new InputError("too long");
|
|
120
|
+
return clean;
|
|
121
|
+
}
|
|
122
|
+
var ADDRESS_FIELDS = [
|
|
123
|
+
"firstName",
|
|
124
|
+
"lastName",
|
|
125
|
+
"company",
|
|
126
|
+
"address1",
|
|
127
|
+
"address2",
|
|
128
|
+
"city",
|
|
129
|
+
"zip",
|
|
130
|
+
"zoneCode",
|
|
131
|
+
"phoneNumber"
|
|
132
|
+
];
|
|
133
|
+
function addressInput(address, { clear = false } = {}) {
|
|
134
|
+
if (address === null || typeof address !== "object" || Array.isArray(address)) throw new InputError("address");
|
|
135
|
+
const source = (
|
|
136
|
+
/** @type {Record<string, unknown>} */
|
|
137
|
+
address
|
|
138
|
+
);
|
|
81
139
|
const out = {};
|
|
82
|
-
ADDRESS_FIELDS
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
140
|
+
for (const key of ADDRESS_FIELDS) {
|
|
141
|
+
const value = text(source[key]);
|
|
142
|
+
if (value !== void 0) out[key] = value;
|
|
143
|
+
else if (clear && key in source && (source[key] === "" || source[key] === null)) out[key] = "";
|
|
144
|
+
}
|
|
145
|
+
const country = text(source["countryCode"]);
|
|
146
|
+
if (country !== void 0) {
|
|
147
|
+
const code = country.toUpperCase();
|
|
148
|
+
if (!/^[A-Z]{2}$/.test(code)) throw new InputError("countryCode");
|
|
149
|
+
out["countryCode"] = code;
|
|
90
150
|
}
|
|
91
151
|
return out;
|
|
92
152
|
}
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
153
|
+
function payloadOf(data, name) {
|
|
154
|
+
const payload = field(data, name);
|
|
155
|
+
const errors = field(payload, "userErrors");
|
|
156
|
+
if (Array.isArray(errors) && errors.length) {
|
|
157
|
+
const userErrors = (
|
|
158
|
+
/** @type {UserError[]} */
|
|
159
|
+
errors
|
|
160
|
+
);
|
|
161
|
+
throw new InputError(userErrors.map((error) => error.message).join(" | "), { userErrors });
|
|
99
162
|
}
|
|
100
163
|
return payload;
|
|
101
|
-
}
|
|
164
|
+
}
|
|
102
165
|
var ops = {
|
|
103
166
|
async order(ask, { id }) {
|
|
104
|
-
const data = await ask(
|
|
105
|
-
return { order: data
|
|
167
|
+
const data = await ask(QUERIES.order, { id: idOf("Order", id) });
|
|
168
|
+
return { order: field(data, "order") ?? null };
|
|
106
169
|
},
|
|
107
170
|
async profile(ask, { firstName, lastName }) {
|
|
108
171
|
const input = {};
|
|
109
|
-
if (firstName !== void 0) input
|
|
110
|
-
if (lastName !== void 0) input
|
|
172
|
+
if (firstName !== void 0) input["firstName"] = text(firstName, 100) ?? "";
|
|
173
|
+
if (lastName !== void 0) input["lastName"] = text(lastName, 100) ?? "";
|
|
111
174
|
if (!Object.keys(input).length) throw new InputError("nothing to change");
|
|
112
|
-
const
|
|
113
|
-
return { customer:
|
|
175
|
+
const payload = payloadOf(await ask(MUTATIONS.profile, { input }), "customerUpdate");
|
|
176
|
+
return { customer: field(payload, "customer") };
|
|
114
177
|
},
|
|
115
178
|
async addresses(ask) {
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
|
|
179
|
+
const customer = field(await ask(QUERIES.addresses, {}), "customer");
|
|
180
|
+
const defaultId = field(field(customer, "defaultAddress"), "id") ?? null;
|
|
181
|
+
const addresses2 = nodes(field(customer, "addresses")).map((address) => ({
|
|
182
|
+
.../** @type {object} */
|
|
183
|
+
address,
|
|
184
|
+
isDefault: field(address, "id") === defaultId
|
|
185
|
+
}));
|
|
186
|
+
return { addresses: addresses2 };
|
|
119
187
|
},
|
|
120
188
|
async saveAddress(ask, { id, address, isDefault }) {
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
189
|
+
const fields = addressInput(address ?? {}, { clear: Boolean(id) });
|
|
190
|
+
const makeDefault = isDefault === true;
|
|
191
|
+
if (!id) {
|
|
192
|
+
const created = await ask(MUTATIONS.addressCreate, { address: fields, default: makeDefault });
|
|
193
|
+
return { address: field(payloadOf(created, "customerAddressCreate"), "customerAddress") };
|
|
194
|
+
}
|
|
195
|
+
const updated = await ask(MUTATIONS.addressUpdate, {
|
|
196
|
+
id: idOf("CustomerAddress", id),
|
|
197
|
+
address: Object.keys(fields).length ? fields : null,
|
|
198
|
+
default: makeDefault ? true : null
|
|
199
|
+
});
|
|
200
|
+
return { address: field(payloadOf(updated, "customerAddressUpdate"), "customerAddress") };
|
|
125
201
|
},
|
|
126
202
|
async deleteAddress(ask, { id }) {
|
|
127
|
-
const
|
|
128
|
-
return { deleted:
|
|
203
|
+
const data = await ask(MUTATIONS.addressDelete, { id: idOf("CustomerAddress", id) });
|
|
204
|
+
return { deleted: field(payloadOf(data, "customerAddressDelete"), "deletedAddressId") };
|
|
129
205
|
},
|
|
130
206
|
async subscriptions(ask) {
|
|
131
|
-
|
|
207
|
+
const contracts = field(field(await ask(QUERIES.subscriptions, {}), "customer"), "subscriptionContracts");
|
|
208
|
+
return { subscriptions: nodes(contracts) };
|
|
132
209
|
},
|
|
133
210
|
async subscription(ask, { id, action }) {
|
|
134
|
-
if (!
|
|
211
|
+
if (!isSubscriptionAction(action)) throw new InputError("action");
|
|
135
212
|
const contractId = idOf("SubscriptionContract", id);
|
|
136
|
-
const contract = (await ask(
|
|
137
|
-
if (!contract) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
if (action === "skip" || action === "unskip") {
|
|
143
|
-
const cycles = contract.upcomingBillingCycles?.nodes || [];
|
|
144
|
-
const cycle = action === "skip" ? cycles.find((c) => !c.skipped) : cycles.find((c) => c.skipped);
|
|
145
|
-
if (!cycle) throw new InputError(action === "skip" ? "no upcoming delivery to skip" : "nothing skipped");
|
|
146
|
-
const p2 = userErrors((await ask(M[action], { input: { contractId, selector: { index: cycle.cycleIndex } } }))?.[action === "skip" ? "subscriptionBillingCycleSkip" : "subscriptionBillingCycleUnskip"]);
|
|
147
|
-
return { billingCycle: p2.billingCycle };
|
|
213
|
+
const contract = field(field(await ask(QUERIES.contract, { id: contractId }), "customer"), "subscriptionContract");
|
|
214
|
+
if (!contract) throw new InputError("not found", { status: 404 });
|
|
215
|
+
const { document: document2, payload, input } = SUBSCRIPTION_ACTIONS[action];
|
|
216
|
+
if (input === "contract") {
|
|
217
|
+
return { contract: field(payloadOf(await ask(document2, { id: contractId }), payload), "contract") };
|
|
148
218
|
}
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
219
|
+
const cycles = nodes(field(contract, "upcomingBillingCycles"));
|
|
220
|
+
const wantSkipped = action === "unskip";
|
|
221
|
+
const cycle = cycles.find((candidate) => Boolean(field(candidate, "skipped")) === wantSkipped);
|
|
222
|
+
if (!cycle) throw new InputError(wantSkipped ? "nothing skipped" : "no upcoming delivery to skip");
|
|
223
|
+
const variables = { input: { contractId, selector: { index: field(cycle, "cycleIndex") } } };
|
|
224
|
+
return { billingCycle: field(payloadOf(await ask(document2, variables), payload), "billingCycle") };
|
|
152
225
|
},
|
|
153
|
-
async requestReturn(ask, { orderId, lines
|
|
154
|
-
const
|
|
155
|
-
if (!Array.isArray(
|
|
156
|
-
const requested =
|
|
157
|
-
const
|
|
158
|
-
if (!Number.isInteger(
|
|
159
|
-
const out = { lineItemId: idOf("LineItem",
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
226
|
+
async requestReturn(ask, { orderId, lines }) {
|
|
227
|
+
const order2 = idOf("Order", orderId);
|
|
228
|
+
if (!Array.isArray(lines) || !lines.length || lines.length > 50) throw new InputError("lines");
|
|
229
|
+
const requested = lines.map((line) => {
|
|
230
|
+
const quantity = Number(field(line, "quantity"));
|
|
231
|
+
if (!Number.isInteger(quantity) || quantity < 1 || quantity > 1e3) throw new InputError("quantity");
|
|
232
|
+
const out = { lineItemId: idOf("LineItem", field(line, "lineItemId")), quantity };
|
|
233
|
+
const reason = field(line, "reasonId");
|
|
234
|
+
if (reason) out["returnReasonDefinitionId"] = idOf("ReturnReasonDefinition", reason);
|
|
235
|
+
const note = text(field(line, "note"), 300);
|
|
236
|
+
if (note) out["customerNote"] = note;
|
|
163
237
|
return out;
|
|
164
238
|
});
|
|
165
|
-
const
|
|
166
|
-
return { return:
|
|
239
|
+
const data = await ask(MUTATIONS.returnRequest, { orderId: order2, lines: requested });
|
|
240
|
+
return { return: field(payloadOf(data, "orderRequestReturn"), "return") };
|
|
167
241
|
}
|
|
168
242
|
};
|
|
169
243
|
|
|
170
|
-
// src/account
|
|
171
|
-
var
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
244
|
+
// src/account/operations.js
|
|
245
|
+
var context = null;
|
|
246
|
+
function setup(shared) {
|
|
247
|
+
context = shared;
|
|
248
|
+
}
|
|
249
|
+
function pagesContext() {
|
|
250
|
+
if (!context) throw new Error("account-pages: loaded without the account part");
|
|
251
|
+
return context;
|
|
175
252
|
}
|
|
176
|
-
var https = (u) => {
|
|
177
|
-
try {
|
|
178
|
-
return new URL(u).protocol === "https:";
|
|
179
|
-
} catch {
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
var date = (s) => {
|
|
184
|
-
try {
|
|
185
|
-
return new Date(s).toLocaleDateString(cfg.locale || void 0);
|
|
186
|
-
} catch {
|
|
187
|
-
return s;
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
253
|
var ROUTE = {
|
|
191
|
-
order: (
|
|
254
|
+
order: (input) => ["GET", `/order?id=${encodeURIComponent(String(input["id"]))}`],
|
|
192
255
|
addresses: () => ["GET", "/addresses"],
|
|
193
256
|
subscriptions: () => ["GET", "/subscriptions"],
|
|
194
257
|
profile: () => ["POST", "/profile"],
|
|
@@ -198,382 +261,663 @@
|
|
|
198
261
|
requestReturn: () => ["POST", "/return"]
|
|
199
262
|
};
|
|
200
263
|
async function run(name, input = {}) {
|
|
201
|
-
const {
|
|
202
|
-
if (
|
|
264
|
+
const { serverBase, serverFetch, query } = pagesContext();
|
|
265
|
+
if (serverBase()) {
|
|
203
266
|
const [method, path] = ROUTE[name](input);
|
|
204
|
-
const
|
|
205
|
-
if (
|
|
206
|
-
return data;
|
|
267
|
+
const answer = await serverFetch(path, method === "POST" ? { method, body: input } : {});
|
|
268
|
+
if (answer.kind === "logged-out") throw new TetherError("Not logged in", { code: "NOT_LOGGED_IN", status: 401 });
|
|
269
|
+
return answer.data;
|
|
207
270
|
}
|
|
271
|
+
const operation = ops[name];
|
|
272
|
+
if (!operation) throw new TetherError(`Customer accounts: no operation ${name}`);
|
|
208
273
|
try {
|
|
209
|
-
return await
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
274
|
+
return await operation(
|
|
275
|
+
async (document2, variables) => (
|
|
276
|
+
/** @type {Record<string, unknown> | null} */
|
|
277
|
+
await query(document2, variables)
|
|
278
|
+
),
|
|
279
|
+
input
|
|
280
|
+
);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (!(error instanceof InputError)) throw error;
|
|
283
|
+
throw new TetherError(error.message, { code: "INVALID", userErrors: error.userErrors });
|
|
213
284
|
}
|
|
214
285
|
}
|
|
215
|
-
|
|
216
|
-
|
|
286
|
+
function changed(what) {
|
|
287
|
+
pagesContext().reset();
|
|
217
288
|
emit("account-updated", { what });
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
289
|
+
}
|
|
290
|
+
async function order(id) {
|
|
291
|
+
const result = await run("order", { id });
|
|
292
|
+
return (
|
|
293
|
+
/** @type {OrderDetail | null} */
|
|
294
|
+
result["order"] ?? null
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
async function updateProfile({ firstName, lastName } = {}) {
|
|
298
|
+
const result = await run("profile", { firstName, lastName });
|
|
221
299
|
changed("profile");
|
|
222
|
-
return
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
300
|
+
return (
|
|
301
|
+
/** @type {{ firstName: string | null, lastName: string | null }} */
|
|
302
|
+
result["customer"]
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
async function addresses() {
|
|
306
|
+
const result = await run("addresses");
|
|
307
|
+
return (
|
|
308
|
+
/** @type {CustomerAddress[]} */
|
|
309
|
+
result["addresses"]
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
async function saveAddress(address, { id, isDefault } = {}) {
|
|
313
|
+
const result = await run("saveAddress", { id, address, isDefault });
|
|
226
314
|
changed("addresses");
|
|
227
|
-
return
|
|
228
|
-
|
|
229
|
-
|
|
315
|
+
return (
|
|
316
|
+
/** @type {CustomerAddress} */
|
|
317
|
+
result["address"]
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
async function deleteAddress(id) {
|
|
321
|
+
const result = await run("deleteAddress", { id });
|
|
230
322
|
changed("addresses");
|
|
231
|
-
return
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
323
|
+
return (
|
|
324
|
+
/** @type {string} */
|
|
325
|
+
result["deleted"]
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
async function subscriptions() {
|
|
329
|
+
const result = await run("subscriptions");
|
|
330
|
+
return (
|
|
331
|
+
/** @type {SubscriptionContract[]} */
|
|
332
|
+
result["subscriptions"]
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
async function subscriptionAction(id, action) {
|
|
336
|
+
const result = await run("subscription", { id, action });
|
|
235
337
|
changed("subscriptions");
|
|
236
|
-
return
|
|
237
|
-
}
|
|
238
|
-
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
async function requestReturn(orderId, lines) {
|
|
341
|
+
const result = await run("requestReturn", { orderId, lines });
|
|
239
342
|
changed("orders");
|
|
240
|
-
return
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
343
|
+
return (
|
|
344
|
+
/** @type {{ id: string, status: string }} */
|
|
345
|
+
result["return"]
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// tether-core:shopify-values.js
|
|
350
|
+
var m4 = window.Tether.__core["shopify-values.js"];
|
|
351
|
+
var { STORE_ENV_NAMES, formatMoney: formatMoney2, isHttpsUrl, isSecretShopifyToken, productKey, productUrl: productUrl2 } = m4;
|
|
352
|
+
|
|
353
|
+
// src/account-links.js
|
|
354
|
+
function localDate(iso) {
|
|
355
|
+
try {
|
|
356
|
+
return new Date(iso).toLocaleDateString(cfg.locale);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
if (error instanceof RangeError) return iso;
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/account/markup.js
|
|
364
|
+
function showIn(root, name, conditions) {
|
|
365
|
+
qsa(root, sel(name)).forEach((el) => {
|
|
366
|
+
const key = get(el, name) ?? "";
|
|
367
|
+
if (Object.hasOwn(conditions, key)) show(el, conditions[key] === true);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
var bindsIn = (row, name) => qsa(row, sel(name)).concat(row.matches(sel(name)) ? [row] : []);
|
|
371
|
+
var enumText = (value) => value ? value.toLowerCase().replace(/_/g, " ") : "";
|
|
372
|
+
var addressText = (address) => (address?.formatted ?? []).join(", ");
|
|
373
|
+
var ownValue = (values, key) => Object.hasOwn(values, key) ? values[key] : void 0;
|
|
374
|
+
|
|
375
|
+
// src/account/order-lines.js
|
|
376
|
+
var lineTexts = (line, returnable) => ({
|
|
377
|
+
title: line.title || line.name,
|
|
378
|
+
"variant-title": line.variantTitle || "",
|
|
379
|
+
quantity: String(line.quantity),
|
|
380
|
+
price: formatMoney(line.price),
|
|
381
|
+
total: formatMoney(line.totalPrice),
|
|
382
|
+
sku: line.sku || "",
|
|
383
|
+
returnable: returnable === null ? void 0 : String(returnable)
|
|
247
384
|
});
|
|
248
|
-
var
|
|
249
|
-
function
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
qsa(row, sel("return-quantity")).forEach((input) => {
|
|
264
|
-
input.max = String(can);
|
|
265
|
-
input.min = "0";
|
|
266
|
-
if (!input.value) input.value = "0";
|
|
267
|
-
input.disabled = can < 1;
|
|
385
|
+
var isInput = (el) => el.tagName === "INPUT";
|
|
386
|
+
function fillReturnControls(row, line, returnable) {
|
|
387
|
+
showIn(row, "order-line-show", { returnable: returnable > 0 });
|
|
388
|
+
qsa(row, sel("return-quantity")).forEach((input) => {
|
|
389
|
+
if (!isInput(input)) return;
|
|
390
|
+
input.max = String(returnable);
|
|
391
|
+
input.min = "0";
|
|
392
|
+
if (!input.value) input.value = "0";
|
|
393
|
+
input.disabled = returnable < 1;
|
|
394
|
+
});
|
|
395
|
+
qsa(row, `select${sel("return-reason")}`).forEach((select) => {
|
|
396
|
+
if (select.dataset["tetherFilled"] === line.id) return;
|
|
397
|
+
select.dataset["tetherFilled"] = line.id;
|
|
398
|
+
(line.suggestedReturnReasonDefinitions?.nodes ?? []).forEach((reason) => {
|
|
399
|
+
select.appendChild(new Option(reason.name, reason.id));
|
|
268
400
|
});
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
function fillLines(box, lines, returns) {
|
|
404
|
+
repeat(box, lines, (row, line) => {
|
|
405
|
+
const id = line.id;
|
|
406
|
+
const returnable = returns && id ? returns.get(id) ?? 0 : null;
|
|
407
|
+
if (id) row.dataset["tetherLine"] = id;
|
|
408
|
+
const texts = lineTexts(line, returnable);
|
|
409
|
+
bindsIn(row, "order-line").forEach((el) => {
|
|
410
|
+
const key = get(el, "order-line") ?? "";
|
|
411
|
+
if (key === "image") {
|
|
412
|
+
if (line.image) setImage(el, line.image.url, line.image.altText || line.title);
|
|
413
|
+
return;
|
|
274
414
|
}
|
|
415
|
+
const text2 = ownValue(texts, key);
|
|
416
|
+
if (text2 !== void 0) setText(el, text2);
|
|
275
417
|
});
|
|
418
|
+
if (id && returnable !== null) fillReturnControls(row, { ...line, id }, returnable);
|
|
276
419
|
});
|
|
277
420
|
}
|
|
278
|
-
|
|
279
|
-
|
|
421
|
+
|
|
422
|
+
// src/account/order-page.js
|
|
423
|
+
function orderKey(el) {
|
|
280
424
|
const raw = get(el, "order") || "";
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
425
|
+
if (!raw.startsWith("url:")) return raw;
|
|
426
|
+
return new URL(window.location.href).searchParams.get(raw.slice("url:".length) || "id");
|
|
427
|
+
}
|
|
428
|
+
function showOrderState(el, state) {
|
|
429
|
+
el.setAttribute(attr("order-state"), state);
|
|
430
|
+
showIn(el, "order-show", {
|
|
431
|
+
loading: state === "loading",
|
|
432
|
+
ready: false,
|
|
433
|
+
"not-found": state === "not-found",
|
|
434
|
+
error: state === "error"
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
var orderTexts = (detail) => ({
|
|
438
|
+
name: detail.name,
|
|
439
|
+
date: localDate(detail.processedAt),
|
|
440
|
+
total: formatMoney(detail.totalPrice),
|
|
441
|
+
subtotal: formatMoney(detail.subtotal),
|
|
442
|
+
shipping: formatMoney(detail.totalShipping),
|
|
443
|
+
tax: formatMoney(detail.totalTax),
|
|
444
|
+
status: enumText(detail.financialStatus),
|
|
445
|
+
fulfillment: enumText(detail.fulfillmentStatus),
|
|
446
|
+
"shipping-address": addressText(detail.shippingAddress),
|
|
447
|
+
"billing-address": addressText(detail.billingAddress),
|
|
448
|
+
items: detail.lineItems.nodes.map((line) => `${line.quantity}\xD7 ${line.title || line.name}`).join(", "),
|
|
449
|
+
cancelled: detail.cancelledAt ? localDate(detail.cancelledAt) : ""
|
|
450
|
+
});
|
|
451
|
+
function fillFulfillment(row, fulfillment) {
|
|
452
|
+
const tracking = fulfillment.trackingInformation[0];
|
|
453
|
+
const texts = {
|
|
454
|
+
status: enumText(fulfillment.latestShipmentStatus || fulfillment.status),
|
|
455
|
+
date: localDate(fulfillment.createdAt),
|
|
456
|
+
eta: fulfillment.estimatedDeliveryAt ? localDate(fulfillment.estimatedDeliveryAt) : "",
|
|
457
|
+
company: tracking?.company || "",
|
|
458
|
+
number: tracking?.number || ""
|
|
459
|
+
};
|
|
460
|
+
bindsIn(row, "fulfillment-bind").forEach((el) => {
|
|
461
|
+
const key = get(el, "fulfillment-bind") ?? "";
|
|
462
|
+
if (key === "url") {
|
|
463
|
+
const url = tracking?.url;
|
|
464
|
+
const linked = isHttpsUrl(url);
|
|
465
|
+
if (linked && url) el.setAttribute("href", url);
|
|
466
|
+
show(el, linked);
|
|
311
467
|
return;
|
|
312
468
|
}
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
date: date(o.processedAt),
|
|
316
|
-
total: formatMoney(o.totalPrice),
|
|
317
|
-
subtotal: o.subtotal ? formatMoney(o.subtotal) : "",
|
|
318
|
-
shipping: o.totalShipping ? formatMoney(o.totalShipping) : "",
|
|
319
|
-
tax: o.totalTax ? formatMoney(o.totalTax) : "",
|
|
320
|
-
status: lower(o.financialStatus),
|
|
321
|
-
fulfillment: lower(o.fulfillmentStatus),
|
|
322
|
-
"shipping-address": lines(o.shippingAddress),
|
|
323
|
-
"billing-address": lines(o.billingAddress),
|
|
324
|
-
items: (o.lineItems?.nodes || []).map((l) => `${l.quantity}\xD7 ${l.title || l.name}`).join(", "),
|
|
325
|
-
cancelled: o.cancelledAt ? date(o.cancelledAt) : ""
|
|
326
|
-
}[k];
|
|
327
|
-
if (v != null) setText(b, v);
|
|
469
|
+
const text2 = ownValue(texts, key);
|
|
470
|
+
if (text2 !== void 0) setText(el, text2);
|
|
328
471
|
});
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
472
|
+
}
|
|
473
|
+
function fillOrder(el, detail) {
|
|
474
|
+
const returnable = new Map(
|
|
475
|
+
(detail.returnInformation?.returnableLineItems.nodes ?? []).map((entry) => [
|
|
476
|
+
entry.lineItem?.id ?? "",
|
|
477
|
+
entry.quantity
|
|
478
|
+
])
|
|
479
|
+
);
|
|
480
|
+
const shipments = detail.fulfillments.nodes;
|
|
481
|
+
const texts = orderTexts(detail);
|
|
482
|
+
qsa(el, sel("order-bind")).forEach((bind) => {
|
|
483
|
+
const key = get(bind, "order-bind") ?? "";
|
|
484
|
+
if (key === "url") {
|
|
485
|
+
if (detail.statusPageUrl && isHttpsUrl(detail.statusPageUrl)) bind.setAttribute("href", detail.statusPageUrl);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const text2 = ownValue(texts, key);
|
|
489
|
+
if (text2 !== void 0) setText(bind, text2);
|
|
490
|
+
});
|
|
491
|
+
qsa(el, sel("order-lines")).forEach((box) => {
|
|
492
|
+
fillLines(box, detail.lineItems.nodes, returnable);
|
|
493
|
+
});
|
|
494
|
+
qsa(el, sel("order-fulfillments")).forEach((box) => {
|
|
495
|
+
repeat(box, shipments, fillFulfillment);
|
|
496
|
+
});
|
|
497
|
+
const returnRequested = el.dataset["tetherReturned"] === "1";
|
|
342
498
|
showIn(el, "order-show", {
|
|
343
499
|
loading: false,
|
|
344
500
|
ready: true,
|
|
345
501
|
error: false,
|
|
346
502
|
"not-found": false,
|
|
347
|
-
cancelled: Boolean(
|
|
348
|
-
shipped:
|
|
349
|
-
returnable: [...returnable.values()].some((
|
|
350
|
-
"has-returns":
|
|
351
|
-
"return-requested":
|
|
503
|
+
cancelled: Boolean(detail.cancelledAt),
|
|
504
|
+
shipped: shipments.length > 0,
|
|
505
|
+
returnable: [...returnable.values()].some((quantity) => quantity > 0),
|
|
506
|
+
"has-returns": !returnRequested && detail.returns.nodes.length > 0,
|
|
507
|
+
"return-requested": returnRequested
|
|
352
508
|
});
|
|
353
|
-
|
|
509
|
+
el.setAttribute(attr("order-state"), "ready");
|
|
354
510
|
}
|
|
355
|
-
async function
|
|
356
|
-
const
|
|
357
|
-
if (!
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
511
|
+
async function renderOrder(el) {
|
|
512
|
+
const key = orderKey(el);
|
|
513
|
+
if (!key) {
|
|
514
|
+
el.setAttribute(attr("order-state"), "not-found");
|
|
515
|
+
showIn(el, "order-show", { "not-found": true });
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
showOrderState(el, "loading");
|
|
519
|
+
let detail;
|
|
520
|
+
try {
|
|
521
|
+
detail = await order(key);
|
|
522
|
+
} catch (error) {
|
|
523
|
+
reportError(error, "order");
|
|
524
|
+
showOrderState(el, "error");
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
if (!detail) {
|
|
528
|
+
showOrderState(el, "not-found");
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
el.dataset["tetherOrderId"] = detail.id;
|
|
532
|
+
fillOrder(el, detail);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// src/account/addresses-view.js
|
|
536
|
+
var shown = [];
|
|
537
|
+
var shownAddress = (id) => shown.find((address) => address.id === id);
|
|
538
|
+
var addressTexts = (address) => ({
|
|
539
|
+
formatted: addressText(address),
|
|
540
|
+
name: [address.firstName, address.lastName].filter(Boolean).join(" "),
|
|
541
|
+
company: address.company || "",
|
|
542
|
+
address1: address.address1 || "",
|
|
543
|
+
address2: address.address2 || "",
|
|
544
|
+
city: address.city || "",
|
|
545
|
+
zip: address.zip || "",
|
|
546
|
+
province: address.province || "",
|
|
547
|
+
country: address.country || "",
|
|
548
|
+
phone: address.phoneNumber || ""
|
|
549
|
+
});
|
|
550
|
+
function fillAddress(row, address) {
|
|
551
|
+
row.dataset["tetherAddress"] = address.id;
|
|
552
|
+
const texts = addressTexts(address);
|
|
553
|
+
bindsIn(row, "address-bind").forEach((el) => {
|
|
554
|
+
const text2 = ownValue(texts, get(el, "address-bind") ?? "");
|
|
555
|
+
if (text2 !== void 0) setText(el, text2);
|
|
361
556
|
});
|
|
557
|
+
showIn(row, "address-show", { default: address.isDefault, "not-default": !address.isDefault });
|
|
362
558
|
}
|
|
363
|
-
var addressCache = null;
|
|
364
559
|
async function renderAddresses(root) {
|
|
365
560
|
const boxes = qsa(root, sel("addresses"));
|
|
366
561
|
if (!boxes.length) return;
|
|
367
|
-
|
|
368
|
-
let list
|
|
562
|
+
showIn(root, "show", { "addresses-loading": true, "addresses-empty": false });
|
|
563
|
+
let list;
|
|
369
564
|
try {
|
|
370
|
-
list =
|
|
371
|
-
} catch (
|
|
372
|
-
reportError(
|
|
565
|
+
list = await addresses();
|
|
566
|
+
} catch (error) {
|
|
567
|
+
reportError(error, "addresses");
|
|
568
|
+
showIn(root, "show", { "addresses-loading": false, "addresses-error": true });
|
|
569
|
+
return;
|
|
373
570
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (v != null) setText(b, v);
|
|
380
|
-
});
|
|
381
|
-
showIn(row, "address-show", { default: a.isDefault, "not-default": !a.isDefault });
|
|
382
|
-
}));
|
|
571
|
+
shown = list;
|
|
572
|
+
showIn(root, "show", { "addresses-loading": false, "addresses-empty": list.length === 0, "addresses-error": false });
|
|
573
|
+
boxes.forEach((box) => {
|
|
574
|
+
repeat(box, list, fillAddress);
|
|
575
|
+
});
|
|
383
576
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const
|
|
577
|
+
|
|
578
|
+
// src/account/subscriptions-view.js
|
|
579
|
+
function conditionsOf(contract) {
|
|
580
|
+
const next = contract.upcomingBillingCycles.nodes[0];
|
|
581
|
+
const status = contract.status;
|
|
582
|
+
const active = status === "ACTIVE";
|
|
388
583
|
return {
|
|
389
|
-
active
|
|
390
|
-
paused:
|
|
391
|
-
cancelled:
|
|
392
|
-
expired:
|
|
393
|
-
failed:
|
|
584
|
+
active,
|
|
585
|
+
paused: status === "PAUSED",
|
|
586
|
+
cancelled: status === "CANCELLED",
|
|
587
|
+
expired: status === "EXPIRED",
|
|
588
|
+
failed: status === "FAILED",
|
|
394
589
|
skipped: Boolean(next?.skipped),
|
|
395
|
-
"can-pause":
|
|
396
|
-
"can-resume":
|
|
397
|
-
"can-skip":
|
|
398
|
-
"can-unskip":
|
|
399
|
-
"can-cancel":
|
|
590
|
+
"can-pause": active,
|
|
591
|
+
"can-resume": status === "PAUSED" || status === "FAILED",
|
|
592
|
+
"can-skip": active && next !== void 0 && !next.skipped,
|
|
593
|
+
"can-unskip": active && Boolean(next?.skipped),
|
|
594
|
+
"can-cancel": active || status === "PAUSED" || status === "FAILED"
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function intervalText(contract) {
|
|
598
|
+
const unit = enumText(contract.deliveryPolicy?.interval);
|
|
599
|
+
if (!unit) return "";
|
|
600
|
+
const count = contract.deliveryPolicy?.intervalCount?.count ?? 1;
|
|
601
|
+
return count <= 1 ? unit : `${count} ${unit}s`;
|
|
602
|
+
}
|
|
603
|
+
function contractTexts(contract) {
|
|
604
|
+
const lines = contract.lines.nodes;
|
|
605
|
+
const total = lines.reduce((sum, line) => sum + Number.parseFloat(line.lineDiscountedPrice?.amount ?? "0"), 0);
|
|
606
|
+
return {
|
|
607
|
+
status: enumText(contract.status),
|
|
608
|
+
next: contract.nextBillingDate ? localDate(contract.nextBillingDate) : "",
|
|
609
|
+
interval: intervalText(contract),
|
|
610
|
+
products: lines.map((line) => `${line.quantity}\xD7 ${line.title}`).join(", "),
|
|
611
|
+
total: lines.length ? formatMoney({ amount: String(total), currencyCode: contract.currencyCode }) : "",
|
|
612
|
+
delivery: formatMoney(contract.deliveryPrice)
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
function fillLine(row, line) {
|
|
616
|
+
const texts = {
|
|
617
|
+
title: line.title,
|
|
618
|
+
"variant-title": line.variantTitle || "",
|
|
619
|
+
quantity: String(line.quantity),
|
|
620
|
+
price: formatMoney(line.currentPrice)
|
|
400
621
|
};
|
|
622
|
+
bindsIn(row, "subscription-line").forEach((el) => {
|
|
623
|
+
const key = get(el, "subscription-line") ?? "";
|
|
624
|
+
if (key === "image") {
|
|
625
|
+
if (line.image) setImage(el, line.image.url, line.image.altText || line.title);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
const text2 = ownValue(texts, key);
|
|
629
|
+
if (text2 !== void 0) setText(el, text2);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
function fillContract(row, contract) {
|
|
633
|
+
row.dataset["tetherSubscription"] = contract.id;
|
|
634
|
+
row.setAttribute(attr("subscription-status"), enumText(contract.status));
|
|
635
|
+
const texts = contractTexts(contract);
|
|
636
|
+
qsa(row, sel("subscription-bind")).forEach((el) => {
|
|
637
|
+
const text2 = ownValue(texts, get(el, "subscription-bind") ?? "");
|
|
638
|
+
if (text2 !== void 0) setText(el, text2);
|
|
639
|
+
});
|
|
640
|
+
qsa(row, sel("subscription-lines")).forEach((box) => {
|
|
641
|
+
repeat(box, contract.lines.nodes, fillLine);
|
|
642
|
+
});
|
|
643
|
+
const conditions = conditionsOf(contract);
|
|
644
|
+
showIn(row, "subscription-show", conditions);
|
|
645
|
+
qsa(row, sel("subscription-action")).forEach((button) => {
|
|
646
|
+
const allowed = ownValue(conditions, `can-${get(button, "subscription-action") ?? ""}`) === true;
|
|
647
|
+
if ("disabled" in button) button.disabled = !allowed;
|
|
648
|
+
button.classList.toggle("is-disabled", !allowed);
|
|
649
|
+
});
|
|
401
650
|
}
|
|
402
651
|
async function renderSubscriptions(root) {
|
|
403
652
|
const boxes = qsa(root, sel("subscriptions"));
|
|
404
653
|
if (!boxes.length) return;
|
|
405
|
-
|
|
406
|
-
let list
|
|
654
|
+
showIn(root, "show", { "subscriptions-loading": true, "subscriptions-empty": false });
|
|
655
|
+
let list;
|
|
407
656
|
try {
|
|
408
657
|
list = await subscriptions();
|
|
409
|
-
} catch (
|
|
410
|
-
reportError(
|
|
658
|
+
} catch (error) {
|
|
659
|
+
reportError(error, "subscriptions");
|
|
660
|
+
showIn(root, "show", { "subscriptions-loading": false, "subscriptions-error": true });
|
|
661
|
+
return;
|
|
411
662
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
qsa(row, sel("subscription-bind")).forEach((b) => {
|
|
421
|
-
const k = get(b, "subscription-bind");
|
|
422
|
-
const v = {
|
|
423
|
-
status: lower(c.status),
|
|
424
|
-
next: c.nextBillingDate ? date(c.nextBillingDate) : "",
|
|
425
|
-
interval: unit ? n === 1 ? unit : `${n} ${unit}s` : "",
|
|
426
|
-
products: ls.map((l) => `${l.quantity}\xD7 ${l.title}`).join(", "),
|
|
427
|
-
total: ls.length ? formatMoney({ amount: String(total), currencyCode: c.currencyCode }) : "",
|
|
428
|
-
delivery: c.deliveryPrice ? formatMoney(c.deliveryPrice) : ""
|
|
429
|
-
}[k];
|
|
430
|
-
if (v != null) setText(b, v);
|
|
431
|
-
});
|
|
432
|
-
qsa(row, sel("subscription-lines")).forEach((lb) => repeat(lb, ls, (lr, l) => {
|
|
433
|
-
qsa(lr, sel("subscription-line")).concat(lr.matches(sel("subscription-line")) ? [lr] : []).forEach((b) => {
|
|
434
|
-
const k = get(b, "subscription-line");
|
|
435
|
-
if (k === "image") {
|
|
436
|
-
if (l.image) setImage(b, l.image.url, l.image.altText || l.title);
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
const v = { title: l.title, "variant-title": l.variantTitle || "", quantity: String(l.quantity), price: formatMoney(l.currentPrice) }[k];
|
|
440
|
-
if (v != null) setText(b, v);
|
|
441
|
-
});
|
|
442
|
-
}));
|
|
443
|
-
const conds = subConds(c);
|
|
444
|
-
showIn(row, "subscription-show", conds);
|
|
445
|
-
qsa(row, sel("subscription-action")).forEach((btn) => {
|
|
446
|
-
const a = get(btn, "subscription-action");
|
|
447
|
-
const ok = conds[`can-${a}`];
|
|
448
|
-
btn.disabled = !ok;
|
|
449
|
-
btn.classList.toggle("is-disabled", !ok);
|
|
450
|
-
});
|
|
451
|
-
}));
|
|
663
|
+
showIn(root, "show", {
|
|
664
|
+
"subscriptions-loading": false,
|
|
665
|
+
"subscriptions-empty": list.length === 0,
|
|
666
|
+
"subscriptions-error": false
|
|
667
|
+
});
|
|
668
|
+
boxes.forEach((box) => {
|
|
669
|
+
repeat(box, list, fillContract);
|
|
670
|
+
});
|
|
452
671
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
672
|
+
|
|
673
|
+
// src/account/page-events.js
|
|
674
|
+
var SUBSCRIPTION_ACTIONS2 = ["pause", "resume", "skip", "unskip", "cancel"];
|
|
675
|
+
var isSubscriptionAction2 = (value) => SUBSCRIPTION_ACTIONS2.some((action) => action === value);
|
|
676
|
+
var isInput2 = (el) => el.tagName === "INPUT";
|
|
677
|
+
var isForm = (el) => el.tagName === "FORM";
|
|
678
|
+
function fieldsOf(form) {
|
|
679
|
+
const fields = {};
|
|
680
|
+
for (const field2 of form.elements) {
|
|
681
|
+
if (!isInput2(field2) && field2.tagName !== "SELECT" && field2.tagName !== "TEXTAREA") continue;
|
|
682
|
+
const input = (
|
|
683
|
+
/** @type {HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement} */
|
|
684
|
+
field2
|
|
685
|
+
);
|
|
686
|
+
if (!input.name || input.type === "submit" || input.type === "button" || input.type === "checkbox") continue;
|
|
687
|
+
fields[input.name] = input.value;
|
|
457
688
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
689
|
+
return fields;
|
|
690
|
+
}
|
|
691
|
+
function messageOf(error) {
|
|
692
|
+
const userErrors = error instanceof TetherError ? error.userErrors ?? [] : [];
|
|
693
|
+
const message = userErrors.map((userError) => userError.message).join(" ");
|
|
694
|
+
if (message) return message;
|
|
695
|
+
const text2 = error instanceof Error && error.message ? error.message : "Something went wrong";
|
|
696
|
+
return text2.replace(/^Customer accounts: /, "");
|
|
464
697
|
}
|
|
465
|
-
|
|
466
|
-
|
|
698
|
+
async function act(el, change) {
|
|
699
|
+
if (el.getAttribute("aria-busy") === "true") return "busy";
|
|
467
700
|
el.classList.add("is-busy");
|
|
468
701
|
el.setAttribute("aria-busy", "true");
|
|
469
702
|
el.setAttribute(attr("form-state"), "pending");
|
|
470
|
-
qsa(el, sel("form-error"))
|
|
703
|
+
const errorBoxes = qsa(el, sel("form-error"));
|
|
704
|
+
errorBoxes.forEach((box) => {
|
|
705
|
+
setText(box, "");
|
|
706
|
+
});
|
|
471
707
|
try {
|
|
472
|
-
|
|
708
|
+
await change();
|
|
473
709
|
el.setAttribute(attr("form-state"), "saved");
|
|
474
|
-
return
|
|
475
|
-
} catch (
|
|
710
|
+
return "saved";
|
|
711
|
+
} catch (error) {
|
|
476
712
|
el.setAttribute(attr("form-state"), "error");
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
return
|
|
713
|
+
errorBoxes.forEach((box) => {
|
|
714
|
+
setText(box, messageOf(error));
|
|
715
|
+
});
|
|
716
|
+
return "error";
|
|
481
717
|
} finally {
|
|
482
718
|
el.classList.remove("is-busy");
|
|
483
719
|
el.removeAttribute("aria-busy");
|
|
484
720
|
}
|
|
485
721
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
722
|
+
var confirmed = (button) => {
|
|
723
|
+
const question = get(button, "confirm");
|
|
724
|
+
return !question || window.confirm(question);
|
|
725
|
+
};
|
|
726
|
+
var addressFormReset = (form) => {
|
|
727
|
+
form.reset();
|
|
728
|
+
delete form.dataset["tetherAddressId"];
|
|
729
|
+
};
|
|
730
|
+
function onSubmit(event) {
|
|
731
|
+
const form = event.target;
|
|
732
|
+
if (!(form instanceof window.HTMLFormElement)) return;
|
|
733
|
+
if (form.matches(sel("profile-form"))) {
|
|
734
|
+
event.preventDefault();
|
|
735
|
+
void act(form, async () => {
|
|
736
|
+
await updateProfile(fieldsOf(form));
|
|
737
|
+
await pagesContext().bindCustomer(document);
|
|
738
|
+
});
|
|
739
|
+
} else if (form.matches(sel("address-form"))) {
|
|
740
|
+
event.preventDefault();
|
|
741
|
+
const id = form.dataset["tetherAddressId"];
|
|
742
|
+
const box = form.elements.namedItem("isDefault");
|
|
743
|
+
const isDefault = box instanceof window.HTMLInputElement && box.checked;
|
|
744
|
+
void act(form, async () => {
|
|
745
|
+
await saveAddress(fieldsOf(form), { id, isDefault });
|
|
746
|
+
addressFormReset(form);
|
|
747
|
+
await renderAddresses(document);
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function editAddress(id) {
|
|
752
|
+
const address = shownAddress(id);
|
|
753
|
+
const form = document.querySelector(sel("address-form"));
|
|
754
|
+
if (!address || !form || !isForm(form)) return;
|
|
755
|
+
form.dataset["tetherAddressId"] = address.id;
|
|
756
|
+
for (const field2 of form.elements) {
|
|
757
|
+
if (!isInput2(field2) && field2.tagName !== "SELECT" && field2.tagName !== "TEXTAREA") continue;
|
|
758
|
+
const input = (
|
|
759
|
+
/** @type {HTMLInputElement} */
|
|
760
|
+
field2
|
|
761
|
+
);
|
|
762
|
+
if (input.name === "isDefault") input.checked = address.isDefault;
|
|
763
|
+
else if (input.name && Object.hasOwn(address, input.name)) {
|
|
764
|
+
const value = Object.entries(address).find(([key]) => key === input.name)?.[1];
|
|
765
|
+
input.value = typeof value === "string" ? value : "";
|
|
502
766
|
}
|
|
767
|
+
}
|
|
768
|
+
form.scrollIntoView({ block: "nearest" });
|
|
769
|
+
}
|
|
770
|
+
function onAddressClick(event, target) {
|
|
771
|
+
const row = target.closest("[data-tether-address]");
|
|
772
|
+
const id = row instanceof window.HTMLElement ? row.dataset["tetherAddress"] : void 0;
|
|
773
|
+
if (target.closest(sel("address-new"))) {
|
|
774
|
+
const form = document.querySelector(sel("address-form"));
|
|
775
|
+
if (form && isForm(form)) addressFormReset(form);
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
if (!(row instanceof window.HTMLElement) || !id) return false;
|
|
779
|
+
const deleteButton = target.closest(sel("address-delete"));
|
|
780
|
+
if (deleteButton) {
|
|
781
|
+
event.preventDefault();
|
|
782
|
+
if (!confirmed(deleteButton)) return true;
|
|
783
|
+
void act(row, async () => {
|
|
784
|
+
await deleteAddress(id);
|
|
785
|
+
await renderAddresses(document);
|
|
786
|
+
});
|
|
787
|
+
} else if (target.closest(sel("address-default"))) {
|
|
788
|
+
event.preventDefault();
|
|
789
|
+
void act(row, async () => {
|
|
790
|
+
await saveAddress(null, { id, isDefault: true });
|
|
791
|
+
await renderAddresses(document);
|
|
792
|
+
});
|
|
793
|
+
} else if (target.closest(sel("address-edit"))) {
|
|
794
|
+
event.preventDefault();
|
|
795
|
+
editAddress(id);
|
|
796
|
+
} else {
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
return true;
|
|
800
|
+
}
|
|
801
|
+
function onSubscriptionAction(button) {
|
|
802
|
+
const row = button.closest("[data-tether-subscription]");
|
|
803
|
+
const action = get(button, "subscription-action");
|
|
804
|
+
const disabled = "disabled" in button && button.disabled === true || button.classList.contains("is-disabled");
|
|
805
|
+
if (!(row instanceof window.HTMLElement) || !isSubscriptionAction2(action) || disabled || !confirmed(button)) return;
|
|
806
|
+
const id = row.dataset["tetherSubscription"] ?? "";
|
|
807
|
+
row.setAttribute(attr("subscription-state"), "pending");
|
|
808
|
+
void act(row, async () => {
|
|
809
|
+
await subscriptionAction(id, action);
|
|
810
|
+
await renderSubscriptions(document);
|
|
811
|
+
}).then((outcome) => {
|
|
812
|
+
const state = outcome === "error" ? "error" : "done";
|
|
813
|
+
qsa(document, "[data-tether-subscription]").filter((candidate) => candidate === row || candidate.dataset["tetherSubscription"] === id).forEach((candidate) => {
|
|
814
|
+
candidate.setAttribute(attr("subscription-state"), state);
|
|
815
|
+
});
|
|
503
816
|
});
|
|
504
|
-
|
|
505
|
-
|
|
817
|
+
}
|
|
818
|
+
function returnLineOf(row) {
|
|
819
|
+
const quantity = row.querySelector(sel("return-quantity"));
|
|
820
|
+
const reason = row.querySelector(sel("return-reason"));
|
|
821
|
+
const note = row.querySelector(sel("return-note"));
|
|
822
|
+
const valueOf = (field2) => field2 && "value" in field2 && typeof field2.value === "string" ? field2.value : "";
|
|
823
|
+
const line = { lineItemId: row.dataset["tetherLine"] ?? "", quantity: Number.parseInt(valueOf(quantity) || "0", 10) };
|
|
824
|
+
const reasonId = valueOf(reason);
|
|
825
|
+
if (reasonId) line.reasonId = reasonId;
|
|
826
|
+
const noteText = valueOf(note);
|
|
827
|
+
if (noteText) line.note = noteText;
|
|
828
|
+
return line;
|
|
829
|
+
}
|
|
830
|
+
function onReturnRequest(button) {
|
|
831
|
+
const scope = button.closest(sel("order"));
|
|
832
|
+
if (!(scope instanceof window.HTMLElement)) return;
|
|
833
|
+
const orderId = scope.dataset["tetherOrderId"];
|
|
834
|
+
if (!orderId) return;
|
|
835
|
+
const lines = qsa(scope, "[data-tether-line]").map(returnLineOf).filter((line) => line.quantity > 0);
|
|
836
|
+
if (!lines.length || !confirmed(button)) return;
|
|
837
|
+
void act(scope, async () => {
|
|
838
|
+
await requestReturn(orderId, lines);
|
|
839
|
+
scope.dataset["tetherReturned"] = "1";
|
|
840
|
+
await renderOrder(scope);
|
|
506
841
|
});
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const f = document.querySelector(sel("address-form"));
|
|
531
|
-
if (a && f) {
|
|
532
|
-
f.dataset.tetherAddressId = a.id;
|
|
533
|
-
[...f.elements].forEach((i) => {
|
|
534
|
-
if (i.name && i.name in a) i.value = a[i.name] ?? "";
|
|
535
|
-
if (i.name === "isDefault") i.checked = a.isDefault;
|
|
536
|
-
});
|
|
537
|
-
f.scrollIntoView?.({ block: "nearest" });
|
|
538
|
-
}
|
|
539
|
-
} else if (btn = t.closest?.(sel("address-new"))) {
|
|
540
|
-
const f = document.querySelector(sel("address-form"));
|
|
541
|
-
if (f) {
|
|
542
|
-
f.reset();
|
|
543
|
-
delete f.dataset.tetherAddressId;
|
|
544
|
-
}
|
|
545
|
-
} else if (btn = t.closest?.(sel("subscription-action"))) {
|
|
546
|
-
e.preventDefault();
|
|
547
|
-
const sub = btn.closest("[data-tether-subscription]");
|
|
548
|
-
const a = get(btn, "subscription-action");
|
|
549
|
-
if (!sub || !SUB_ACTIONS.includes(a) || btn.disabled || !confirmOk(btn)) return;
|
|
550
|
-
sub.setAttribute(attr("subscription-state"), "pending");
|
|
551
|
-
act(sub, async () => {
|
|
552
|
-
await subscriptionAction(sub.dataset.tetherSubscription, a);
|
|
553
|
-
await renderSubscriptions(document);
|
|
554
|
-
}).then(() => {
|
|
555
|
-
sub.setAttribute(attr("subscription-state"), sub.getAttribute(attr("form-state")) === "error" ? "error" : "done");
|
|
556
|
-
});
|
|
557
|
-
} else if (btn = t.closest?.(sel("order-return"))) {
|
|
558
|
-
e.preventDefault();
|
|
559
|
-
const scope = btn.closest(SCOPE.order);
|
|
560
|
-
if (!scope?.dataset.tetherOrderId) return;
|
|
561
|
-
const req = qsa(scope, "[data-tether-line]").map((r) => ({
|
|
562
|
-
lineItemId: r.dataset.tetherLine,
|
|
563
|
-
quantity: parseInt(r.querySelector(sel("return-quantity"))?.value || "0", 10),
|
|
564
|
-
reasonId: r.querySelector(sel("return-reason"))?.value || void 0,
|
|
565
|
-
note: r.querySelector(sel("return-note"))?.value || void 0
|
|
566
|
-
})).filter((l) => l.quantity > 0);
|
|
567
|
-
if (!req.length || !confirmOk(btn)) return;
|
|
568
|
-
act(scope, async () => {
|
|
569
|
-
await requestReturn(scope.dataset.tetherOrderId, req);
|
|
570
|
-
scope.dataset.tetherReturned = "1";
|
|
571
|
-
await renderOrder(scope);
|
|
572
|
-
});
|
|
842
|
+
}
|
|
843
|
+
function onClick(event) {
|
|
844
|
+
const target = event.target;
|
|
845
|
+
if (!(target instanceof window.Element)) return;
|
|
846
|
+
if (onAddressClick(event, target)) return;
|
|
847
|
+
const subscriptionButton = target.closest(sel("subscription-action"));
|
|
848
|
+
if (subscriptionButton instanceof window.HTMLElement) {
|
|
849
|
+
event.preventDefault();
|
|
850
|
+
onSubscriptionAction(subscriptionButton);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const returnButton = target.closest(sel("order-return"));
|
|
854
|
+
if (returnButton instanceof window.HTMLElement) {
|
|
855
|
+
event.preventDefault();
|
|
856
|
+
onReturnRequest(returnButton);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
function listenToPages() {
|
|
860
|
+
document.addEventListener("submit", onSubmit, true);
|
|
861
|
+
document.addEventListener("input", (event) => {
|
|
862
|
+
const target = event.target;
|
|
863
|
+
if (target instanceof window.HTMLElement && target.closest(sel("profile-form"))) {
|
|
864
|
+
target.dataset["tetherTouched"] = "1";
|
|
573
865
|
}
|
|
574
866
|
});
|
|
867
|
+
document.addEventListener("click", onClick);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// src/account-pages.js
|
|
871
|
+
var PROFILE_FIELDS = (
|
|
872
|
+
/** @type {const} */
|
|
873
|
+
["firstName", "lastName"]
|
|
874
|
+
);
|
|
875
|
+
async function renderProfile(form) {
|
|
876
|
+
const customer = await pagesContext().customer().catch((error) => {
|
|
877
|
+
reportError(error, "account");
|
|
878
|
+
return null;
|
|
879
|
+
});
|
|
880
|
+
if (!customer || form.tagName !== "FORM") return;
|
|
881
|
+
const fields = (
|
|
882
|
+
/** @type {HTMLFormElement} */
|
|
883
|
+
form.elements
|
|
884
|
+
);
|
|
885
|
+
PROFILE_FIELDS.forEach((name) => {
|
|
886
|
+
const input = fields.namedItem(name);
|
|
887
|
+
const isTextField = input instanceof window.HTMLInputElement || input instanceof window.HTMLTextAreaElement;
|
|
888
|
+
if (isTextField && !input.dataset["tetherTouched"]) input.value = customer[name] ?? "";
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
var listening = false;
|
|
892
|
+
async function renderPages(root = document) {
|
|
893
|
+
if (!listening) {
|
|
894
|
+
listening = true;
|
|
895
|
+
listenToPages();
|
|
896
|
+
}
|
|
897
|
+
await Promise.all([
|
|
898
|
+
...qsa(root, sel("order")).map((el) => renderOrder(el)),
|
|
899
|
+
...qsa(root, sel("profile-form")).map((form) => renderProfile(form)),
|
|
900
|
+
renderAddresses(root),
|
|
901
|
+
renderSubscriptions(root)
|
|
902
|
+
]);
|
|
903
|
+
}
|
|
904
|
+
function showUnavailable(root) {
|
|
905
|
+
qsa(root, sel("order")).forEach((el) => {
|
|
906
|
+
showOrderState(el, "error");
|
|
907
|
+
});
|
|
908
|
+
showIn(root, "show", {
|
|
909
|
+
"addresses-loading": false,
|
|
910
|
+
"addresses-error": true,
|
|
911
|
+
"subscriptions-loading": false,
|
|
912
|
+
"subscriptions-error": true
|
|
913
|
+
});
|
|
575
914
|
}
|
|
576
915
|
|
|
577
916
|
// src/parts/account-pages.js
|
|
578
|
-
|
|
917
|
+
var { Tether } = (
|
|
918
|
+
/** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */
|
|
919
|
+
window
|
|
920
|
+
);
|
|
921
|
+
if (!Tether) throw new Error("tether.account-pages.js runs after the Tether script, which loads it");
|
|
922
|
+
Tether.__parts["account-pages"] = account_pages_exports;
|
|
579
923
|
})();
|