@occluze/tether 0.6.7 → 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 +773 -442
- package/dist/tether.account-pages.min.js +1 -1
- package/dist/tether.account.js +648 -318
- 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 +331 -242
- 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 +3176 -2015
- 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 +69 -298
- package/src/account.js +280 -323
- 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 +320 -488
- 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 -287
- 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-Q6E2TFEV.js +0 -1
- package/dist/esm/chunks/account-pages-UZXR4R5F.js +0 -1
- package/dist/esm/chunks/analytics-IJBKATUP.js +0 -1
- package/dist/esm/chunks/chunk-4QJKLPYR.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-KIDH76B7.js +0 -1
- package/dist/esm/chunks/chunk-PAYT5ZD7.js +0 -1
- package/dist/esm/chunks/chunk-RREHGH4A.js +0 -1
- package/dist/esm/chunks/chunk-SSJT43CJ.js +0 -1
- package/dist/esm/chunks/chunk-VC46IEJQ.js +0 -1
- package/dist/esm/chunks/diagnose-3WVAWNTX.js +0 -14
- package/dist/esm/chunks/extras-7I7WJX35.js +0 -1
- package/dist/esm/chunks/markets-ui-545XLFTE.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,174 +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
|
-
else if (clear &&
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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;
|
|
91
150
|
}
|
|
92
151
|
return out;
|
|
93
152
|
}
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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 });
|
|
100
162
|
}
|
|
101
163
|
return payload;
|
|
102
|
-
}
|
|
164
|
+
}
|
|
103
165
|
var ops = {
|
|
104
166
|
async order(ask, { id }) {
|
|
105
|
-
const data = await ask(
|
|
106
|
-
return { order: data
|
|
167
|
+
const data = await ask(QUERIES.order, { id: idOf("Order", id) });
|
|
168
|
+
return { order: field(data, "order") ?? null };
|
|
107
169
|
},
|
|
108
170
|
async profile(ask, { firstName, lastName }) {
|
|
109
171
|
const input = {};
|
|
110
|
-
if (firstName !== void 0) input
|
|
111
|
-
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) ?? "";
|
|
112
174
|
if (!Object.keys(input).length) throw new InputError("nothing to change");
|
|
113
|
-
const
|
|
114
|
-
return { customer:
|
|
175
|
+
const payload = payloadOf(await ask(MUTATIONS.profile, { input }), "customerUpdate");
|
|
176
|
+
return { customer: field(payload, "customer") };
|
|
115
177
|
},
|
|
116
178
|
async addresses(ask) {
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
|
|
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 };
|
|
120
187
|
},
|
|
121
188
|
async saveAddress(ask, { id, address, isDefault }) {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
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") };
|
|
126
201
|
},
|
|
127
202
|
async deleteAddress(ask, { id }) {
|
|
128
|
-
const
|
|
129
|
-
return { deleted:
|
|
203
|
+
const data = await ask(MUTATIONS.addressDelete, { id: idOf("CustomerAddress", id) });
|
|
204
|
+
return { deleted: field(payloadOf(data, "customerAddressDelete"), "deletedAddressId") };
|
|
130
205
|
},
|
|
131
206
|
async subscriptions(ask) {
|
|
132
|
-
|
|
207
|
+
const contracts = field(field(await ask(QUERIES.subscriptions, {}), "customer"), "subscriptionContracts");
|
|
208
|
+
return { subscriptions: nodes(contracts) };
|
|
133
209
|
},
|
|
134
210
|
async subscription(ask, { id, action }) {
|
|
135
|
-
if (!
|
|
211
|
+
if (!isSubscriptionAction(action)) throw new InputError("action");
|
|
136
212
|
const contractId = idOf("SubscriptionContract", id);
|
|
137
|
-
const contract = (await ask(
|
|
138
|
-
if (!contract) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
if (action === "skip" || action === "unskip") {
|
|
144
|
-
const cycles = contract.upcomingBillingCycles?.nodes || [];
|
|
145
|
-
const cycle = action === "skip" ? cycles.find((c) => !c.skipped) : cycles.find((c) => c.skipped);
|
|
146
|
-
if (!cycle) throw new InputError(action === "skip" ? "no upcoming delivery to skip" : "nothing skipped");
|
|
147
|
-
const p2 = userErrors((await ask(M[action], { input: { contractId, selector: { index: cycle.cycleIndex } } }))?.[action === "skip" ? "subscriptionBillingCycleSkip" : "subscriptionBillingCycleUnskip"]);
|
|
148
|
-
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") };
|
|
149
218
|
}
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
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") };
|
|
153
225
|
},
|
|
154
|
-
async requestReturn(ask, { orderId, lines
|
|
155
|
-
const
|
|
156
|
-
if (!Array.isArray(
|
|
157
|
-
const requested =
|
|
158
|
-
const
|
|
159
|
-
if (!Number.isInteger(
|
|
160
|
-
const out = { lineItemId: idOf("LineItem",
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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;
|
|
164
237
|
return out;
|
|
165
238
|
});
|
|
166
|
-
const
|
|
167
|
-
return { return:
|
|
239
|
+
const data = await ask(MUTATIONS.returnRequest, { orderId: order2, lines: requested });
|
|
240
|
+
return { return: field(payloadOf(data, "orderRequestReturn"), "return") };
|
|
168
241
|
}
|
|
169
242
|
};
|
|
170
243
|
|
|
171
|
-
// src/account
|
|
172
|
-
var
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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;
|
|
176
252
|
}
|
|
177
|
-
var https = (u) => {
|
|
178
|
-
try {
|
|
179
|
-
return new URL(u).protocol === "https:";
|
|
180
|
-
} catch {
|
|
181
|
-
return false;
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
var date = (s) => {
|
|
185
|
-
try {
|
|
186
|
-
return new Date(s).toLocaleDateString(cfg.locale || void 0);
|
|
187
|
-
} catch {
|
|
188
|
-
return s;
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
253
|
var ROUTE = {
|
|
192
|
-
order: (
|
|
254
|
+
order: (input) => ["GET", `/order?id=${encodeURIComponent(String(input["id"]))}`],
|
|
193
255
|
addresses: () => ["GET", "/addresses"],
|
|
194
256
|
subscriptions: () => ["GET", "/subscriptions"],
|
|
195
257
|
profile: () => ["POST", "/profile"],
|
|
@@ -199,394 +261,663 @@
|
|
|
199
261
|
requestReturn: () => ["POST", "/return"]
|
|
200
262
|
};
|
|
201
263
|
async function run(name, input = {}) {
|
|
202
|
-
const {
|
|
203
|
-
if (
|
|
264
|
+
const { serverBase, serverFetch, query } = pagesContext();
|
|
265
|
+
if (serverBase()) {
|
|
204
266
|
const [method, path] = ROUTE[name](input);
|
|
205
|
-
const
|
|
206
|
-
if (
|
|
207
|
-
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;
|
|
208
270
|
}
|
|
271
|
+
const operation = ops[name];
|
|
272
|
+
if (!operation) throw new TetherError(`Customer accounts: no operation ${name}`);
|
|
209
273
|
try {
|
|
210
|
-
return await
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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 });
|
|
214
284
|
}
|
|
215
285
|
}
|
|
216
|
-
|
|
217
|
-
|
|
286
|
+
function changed(what) {
|
|
287
|
+
pagesContext().reset();
|
|
218
288
|
emit("account-updated", { what });
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
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 });
|
|
222
299
|
changed("profile");
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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 });
|
|
227
314
|
changed("addresses");
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
|
|
315
|
+
return (
|
|
316
|
+
/** @type {CustomerAddress} */
|
|
317
|
+
result["address"]
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
async function deleteAddress(id) {
|
|
321
|
+
const result = await run("deleteAddress", { id });
|
|
231
322
|
changed("addresses");
|
|
232
|
-
return
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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 });
|
|
236
337
|
changed("subscriptions");
|
|
237
|
-
return
|
|
238
|
-
}
|
|
239
|
-
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
async function requestReturn(orderId, lines) {
|
|
341
|
+
const result = await run("requestReturn", { orderId, lines });
|
|
240
342
|
changed("orders");
|
|
241
|
-
return
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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)
|
|
248
384
|
});
|
|
249
|
-
var
|
|
250
|
-
function
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
qsa(row, sel("return-quantity")).forEach((input) => {
|
|
265
|
-
input.max = String(can);
|
|
266
|
-
input.min = "0";
|
|
267
|
-
if (!input.value) input.value = "0";
|
|
268
|
-
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));
|
|
269
400
|
});
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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;
|
|
275
414
|
}
|
|
415
|
+
const text2 = ownValue(texts, key);
|
|
416
|
+
if (text2 !== void 0) setText(el, text2);
|
|
276
417
|
});
|
|
418
|
+
if (id && returnable !== null) fillReturnControls(row, { ...line, id }, returnable);
|
|
277
419
|
});
|
|
278
420
|
}
|
|
279
|
-
|
|
280
|
-
|
|
421
|
+
|
|
422
|
+
// src/account/order-page.js
|
|
423
|
+
function orderKey(el) {
|
|
281
424
|
const raw = get(el, "order") || "";
|
|
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
|
-
|
|
311
|
-
|
|
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);
|
|
312
467
|
return;
|
|
313
468
|
}
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
date: date(o.processedAt),
|
|
317
|
-
total: formatMoney(o.totalPrice),
|
|
318
|
-
subtotal: o.subtotal ? formatMoney(o.subtotal) : "",
|
|
319
|
-
shipping: o.totalShipping ? formatMoney(o.totalShipping) : "",
|
|
320
|
-
tax: o.totalTax ? formatMoney(o.totalTax) : "",
|
|
321
|
-
status: lower(o.financialStatus),
|
|
322
|
-
fulfillment: lower(o.fulfillmentStatus),
|
|
323
|
-
"shipping-address": lines(o.shippingAddress),
|
|
324
|
-
"billing-address": lines(o.billingAddress),
|
|
325
|
-
items: (o.lineItems?.nodes || []).map((l) => `${l.quantity}\xD7 ${l.title || l.name}`).join(", "),
|
|
326
|
-
cancelled: o.cancelledAt ? date(o.cancelledAt) : ""
|
|
327
|
-
}[k];
|
|
328
|
-
if (v != null) setText(b, v);
|
|
469
|
+
const text2 = ownValue(texts, key);
|
|
470
|
+
if (text2 !== void 0) setText(el, text2);
|
|
329
471
|
});
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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";
|
|
345
498
|
showIn(el, "order-show", {
|
|
346
499
|
loading: false,
|
|
347
500
|
ready: true,
|
|
348
501
|
error: false,
|
|
349
502
|
"not-found": false,
|
|
350
|
-
cancelled: Boolean(
|
|
351
|
-
shipped:
|
|
352
|
-
returnable: [...returnable.values()].some((
|
|
353
|
-
"has-returns":
|
|
354
|
-
"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
|
|
355
508
|
});
|
|
356
|
-
|
|
509
|
+
el.setAttribute(attr("order-state"), "ready");
|
|
357
510
|
}
|
|
358
|
-
async function
|
|
359
|
-
const
|
|
360
|
-
if (!
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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);
|
|
364
556
|
});
|
|
557
|
+
showIn(row, "address-show", { default: address.isDefault, "not-default": !address.isDefault });
|
|
365
558
|
}
|
|
366
|
-
var addressCache = null;
|
|
367
559
|
async function renderAddresses(root) {
|
|
368
560
|
const boxes = qsa(root, sel("addresses"));
|
|
369
561
|
if (!boxes.length) return;
|
|
370
|
-
|
|
562
|
+
showIn(root, "show", { "addresses-loading": true, "addresses-empty": false });
|
|
371
563
|
let list;
|
|
372
564
|
try {
|
|
373
|
-
list =
|
|
374
|
-
} catch (
|
|
375
|
-
reportError(
|
|
376
|
-
|
|
565
|
+
list = await addresses();
|
|
566
|
+
} catch (error) {
|
|
567
|
+
reportError(error, "addresses");
|
|
568
|
+
showIn(root, "show", { "addresses-loading": false, "addresses-error": true });
|
|
377
569
|
return;
|
|
378
570
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (v != null) setText(b, v);
|
|
385
|
-
});
|
|
386
|
-
showIn(row, "address-show", { default: a.isDefault, "not-default": !a.isDefault });
|
|
387
|
-
}));
|
|
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
|
+
});
|
|
388
576
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
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";
|
|
393
583
|
return {
|
|
394
|
-
active
|
|
395
|
-
paused:
|
|
396
|
-
cancelled:
|
|
397
|
-
expired:
|
|
398
|
-
failed:
|
|
584
|
+
active,
|
|
585
|
+
paused: status === "PAUSED",
|
|
586
|
+
cancelled: status === "CANCELLED",
|
|
587
|
+
expired: status === "EXPIRED",
|
|
588
|
+
failed: status === "FAILED",
|
|
399
589
|
skipped: Boolean(next?.skipped),
|
|
400
|
-
"can-pause":
|
|
401
|
-
"can-resume":
|
|
402
|
-
"can-skip":
|
|
403
|
-
"can-unskip":
|
|
404
|
-
"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"
|
|
405
595
|
};
|
|
406
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)
|
|
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
|
+
});
|
|
650
|
+
}
|
|
407
651
|
async function renderSubscriptions(root) {
|
|
408
652
|
const boxes = qsa(root, sel("subscriptions"));
|
|
409
653
|
if (!boxes.length) return;
|
|
410
|
-
|
|
654
|
+
showIn(root, "show", { "subscriptions-loading": true, "subscriptions-empty": false });
|
|
411
655
|
let list;
|
|
412
656
|
try {
|
|
413
657
|
list = await subscriptions();
|
|
414
|
-
} catch (
|
|
415
|
-
reportError(
|
|
416
|
-
|
|
658
|
+
} catch (error) {
|
|
659
|
+
reportError(error, "subscriptions");
|
|
660
|
+
showIn(root, "show", { "subscriptions-loading": false, "subscriptions-error": true });
|
|
417
661
|
return;
|
|
418
662
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
qsa(row, sel("subscription-bind")).forEach((b) => {
|
|
428
|
-
const k = get(b, "subscription-bind");
|
|
429
|
-
const v = {
|
|
430
|
-
status: lower(c.status),
|
|
431
|
-
next: c.nextBillingDate ? date(c.nextBillingDate) : "",
|
|
432
|
-
interval: unit ? n === 1 ? unit : `${n} ${unit}s` : "",
|
|
433
|
-
products: ls.map((l) => `${l.quantity}\xD7 ${l.title}`).join(", "),
|
|
434
|
-
total: ls.length ? formatMoney({ amount: String(total), currencyCode: c.currencyCode }) : "",
|
|
435
|
-
delivery: c.deliveryPrice ? formatMoney(c.deliveryPrice) : ""
|
|
436
|
-
}[k];
|
|
437
|
-
if (v != null) setText(b, v);
|
|
438
|
-
});
|
|
439
|
-
qsa(row, sel("subscription-lines")).forEach((lb) => repeat(lb, ls, (lr, l) => {
|
|
440
|
-
qsa(lr, sel("subscription-line")).concat(lr.matches(sel("subscription-line")) ? [lr] : []).forEach((b) => {
|
|
441
|
-
const k = get(b, "subscription-line");
|
|
442
|
-
if (k === "image") {
|
|
443
|
-
if (l.image) setImage(b, l.image.url, l.image.altText || l.title);
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
const v = { title: l.title, "variant-title": l.variantTitle || "", quantity: String(l.quantity), price: formatMoney(l.currentPrice) }[k];
|
|
447
|
-
if (v != null) setText(b, v);
|
|
448
|
-
});
|
|
449
|
-
}));
|
|
450
|
-
const conds = subConds(c);
|
|
451
|
-
showIn(row, "subscription-show", conds);
|
|
452
|
-
qsa(row, sel("subscription-action")).forEach((btn) => {
|
|
453
|
-
const a = get(btn, "subscription-action");
|
|
454
|
-
const ok = conds[`can-${a}`];
|
|
455
|
-
btn.disabled = !ok;
|
|
456
|
-
btn.classList.toggle("is-disabled", !ok);
|
|
457
|
-
});
|
|
458
|
-
}));
|
|
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
|
+
});
|
|
459
671
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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;
|
|
464
688
|
}
|
|
465
|
-
|
|
466
|
-
...qsa(root, sel("order")).map((el) => renderOrder(el)),
|
|
467
|
-
...qsa(root, sel("profile-form")).map((f) => renderProfile(f)),
|
|
468
|
-
renderAddresses(root),
|
|
469
|
-
renderSubscriptions(root)
|
|
470
|
-
]);
|
|
689
|
+
return fields;
|
|
471
690
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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: /, "");
|
|
697
|
+
}
|
|
698
|
+
async function act(el, change) {
|
|
699
|
+
if (el.getAttribute("aria-busy") === "true") return "busy";
|
|
475
700
|
el.classList.add("is-busy");
|
|
476
701
|
el.setAttribute("aria-busy", "true");
|
|
477
702
|
el.setAttribute(attr("form-state"), "pending");
|
|
478
|
-
qsa(el, sel("form-error"))
|
|
703
|
+
const errorBoxes = qsa(el, sel("form-error"));
|
|
704
|
+
errorBoxes.forEach((box) => {
|
|
705
|
+
setText(box, "");
|
|
706
|
+
});
|
|
479
707
|
try {
|
|
480
|
-
|
|
708
|
+
await change();
|
|
481
709
|
el.setAttribute(attr("form-state"), "saved");
|
|
482
|
-
return
|
|
483
|
-
} catch (
|
|
710
|
+
return "saved";
|
|
711
|
+
} catch (error) {
|
|
484
712
|
el.setAttribute(attr("form-state"), "error");
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
return
|
|
713
|
+
errorBoxes.forEach((box) => {
|
|
714
|
+
setText(box, messageOf(error));
|
|
715
|
+
});
|
|
716
|
+
return "error";
|
|
489
717
|
} finally {
|
|
490
718
|
el.classList.remove("is-busy");
|
|
491
719
|
el.removeAttribute("aria-busy");
|
|
492
720
|
}
|
|
493
721
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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 : "";
|
|
513
766
|
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
|
|
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
|
+
});
|
|
517
816
|
});
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}).then(() => {
|
|
567
|
-
const state = sub.getAttribute(attr("form-state")) === "error" ? "error" : "done";
|
|
568
|
-
qsa(document, "[data-tether-subscription]").filter((r) => r === sub || r.dataset.tetherSubscription === id).forEach((r) => r.setAttribute(attr("subscription-state"), state));
|
|
569
|
-
});
|
|
570
|
-
} else if (btn = t.closest?.(sel("order-return"))) {
|
|
571
|
-
e.preventDefault();
|
|
572
|
-
const scope = btn.closest(SCOPE.order);
|
|
573
|
-
if (!scope?.dataset.tetherOrderId) return;
|
|
574
|
-
const req = qsa(scope, "[data-tether-line]").map((r) => ({
|
|
575
|
-
lineItemId: r.dataset.tetherLine,
|
|
576
|
-
quantity: parseInt(r.querySelector(sel("return-quantity"))?.value || "0", 10),
|
|
577
|
-
reasonId: r.querySelector(sel("return-reason"))?.value || void 0,
|
|
578
|
-
note: r.querySelector(sel("return-note"))?.value || void 0
|
|
579
|
-
})).filter((l) => l.quantity > 0);
|
|
580
|
-
if (!req.length || !confirmOk(btn)) return;
|
|
581
|
-
act(scope, async () => {
|
|
582
|
-
await requestReturn(scope.dataset.tetherOrderId, req);
|
|
583
|
-
scope.dataset.tetherReturned = "1";
|
|
584
|
-
await renderOrder(scope);
|
|
585
|
-
});
|
|
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);
|
|
841
|
+
});
|
|
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";
|
|
586
865
|
}
|
|
587
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
|
+
});
|
|
588
914
|
}
|
|
589
915
|
|
|
590
916
|
// src/parts/account-pages.js
|
|
591
|
-
|
|
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;
|
|
592
923
|
})();
|