@juo/blocks 1.9.0 → 1.11.0-alpha.0
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/dist/{ExtensionRoot-BmfKKpcU.js → ExtensionRoot-C7tcvV-n.js} +3 -3
- package/dist/{block-C8lPBCF8.js → block-WoHZvF51.js} +2 -0
- package/dist/block.d.ts +8 -0
- package/dist/block.d.ts.map +1 -1
- package/dist/{bridge-BOcO9toS.js → bridge-w5KQ-czl.js} +1 -0
- package/dist/core.js +985 -274
- package/dist/editor/components.d.ts.map +1 -1
- package/dist/editor/main.d.ts.map +1 -1
- package/dist/editor/messages.d.ts +1 -0
- package/dist/editor/messages.d.ts.map +1 -1
- package/dist/editor.js +6 -5
- package/dist/flows/WorkflowService.d.ts.map +1 -1
- package/dist/{juo-workflow-B3OHYsp-.js → juo-workflow-DRauDEnU.js} +18 -3
- package/dist/preact.js +1 -1
- package/dist/react.js +1 -1
- package/dist/services/customer/adapter.d.ts +8 -0
- package/dist/services/customer/adapter.d.ts.map +1 -0
- package/dist/services/customer/customerService.d.ts +3 -2
- package/dist/services/customer/customerService.d.ts.map +1 -1
- package/dist/services/customer/index.d.ts +1 -0
- package/dist/services/customer/index.d.ts.map +1 -1
- package/dist/services/customer/types.d.ts +4 -1
- package/dist/services/customer/types.d.ts.map +1 -1
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/login/adapter.d.ts +32 -0
- package/dist/services/login/adapter.d.ts.map +1 -0
- package/dist/services/login/index.d.ts +3 -1
- package/dist/services/login/index.d.ts.map +1 -1
- package/dist/services/login/loginService.d.ts +6 -2
- package/dist/services/login/loginService.d.ts.map +1 -1
- package/dist/services/order/adapter.d.ts +9 -0
- package/dist/services/order/adapter.d.ts.map +1 -0
- package/dist/services/order/index.d.ts +5 -0
- package/dist/services/order/index.d.ts.map +1 -0
- package/dist/services/order/orderService.d.ts +7 -0
- package/dist/services/order/orderService.d.ts.map +1 -0
- package/dist/services/order/types.d.ts +21 -0
- package/dist/services/order/types.d.ts.map +1 -0
- package/dist/services/product/adapter.d.ts +4 -1
- package/dist/services/product/adapter.d.ts.map +1 -1
- package/dist/services/product/index.d.ts +1 -1
- package/dist/services/product/index.d.ts.map +1 -1
- package/dist/services/product/types.d.ts +2 -0
- package/dist/services/product/types.d.ts.map +1 -1
- package/dist/services/schedules/adapter.d.ts +22 -0
- package/dist/services/schedules/adapter.d.ts.map +1 -0
- package/dist/services/schedules/index.d.ts +5 -0
- package/dist/services/schedules/index.d.ts.map +1 -0
- package/dist/services/schedules/schedulesService.d.ts +10 -0
- package/dist/services/schedules/schedulesService.d.ts.map +1 -0
- package/dist/services/schedules/types.d.ts +47 -0
- package/dist/services/schedules/types.d.ts.map +1 -0
- package/dist/services/subscription/adapter.d.ts +12 -2
- package/dist/services/subscription/adapter.d.ts.map +1 -1
- package/dist/services/subscription/index.d.ts +1 -1
- package/dist/services/subscription/index.d.ts.map +1 -1
- package/dist/services/subscription/subscriptionService.d.ts +1 -6
- package/dist/services/subscription/subscriptionService.d.ts.map +1 -1
- package/dist/services/subscription/types.d.ts +25 -9
- package/dist/services/subscription/types.d.ts.map +1 -1
- package/dist/{theme-state-LZS5T-Xb.js → theme-state-CzXQGOTk.js} +1 -1
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/vue.js +3 -3
- package/dist/web-components/editor/InlineText.d.ts.map +1 -1
- package/dist/web-components-editor.js +5 -4
- package/dist/web-components-runtime.js +3 -3
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import { c as createContext, d, w, r as registerBlock, E, u } from "./block-
|
|
5
|
-
import { B, C, a, S, b, e, f, g, h, i, j, k, l, m, n, o, p, q, s, t, v, x, y } from "./block-
|
|
6
|
-
import { D, T, c, i as i2 } from "./theme-state-
|
|
7
|
-
import { T as T2, W, c as c2, a as a2, b as b2, g as g2, h as h2 } from "./juo-workflow-
|
|
4
|
+
import { c as createContext, d, w, r as registerBlock, E, u } from "./block-WoHZvF51.js";
|
|
5
|
+
import { B, C, a, S, b, e, f, g, h, i, j, k, l, m, n, o, p, q, s, t, v, x, y } from "./block-WoHZvF51.js";
|
|
6
|
+
import { D, T, c, i as i2 } from "./theme-state-CzXQGOTk.js";
|
|
7
|
+
import { T as T2, W, c as c2, a as a2, b as b2, g as g2, h as h2 } from "./juo-workflow-DRauDEnU.js";
|
|
8
8
|
import { reportBlockWarning, reportBlockError } from "./observability.js";
|
|
9
9
|
function createState() {
|
|
10
10
|
const discountCode = d(null);
|
|
@@ -158,13 +158,43 @@ async function loadBlockRegistry(options = {}) {
|
|
|
158
158
|
function createCustomerService(options) {
|
|
159
159
|
const current = d(null);
|
|
160
160
|
const accountOrdersUrl = d(options.getAccountOrdersUrl());
|
|
161
|
-
|
|
161
|
+
return {
|
|
162
162
|
current,
|
|
163
|
-
accountOrdersUrl
|
|
163
|
+
accountOrdersUrl,
|
|
164
|
+
async getCurrent() {
|
|
165
|
+
const result = await options.adapter.getCurrent();
|
|
166
|
+
if (result._tag === "Success") current.value = result.data;
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
164
169
|
};
|
|
165
|
-
return service;
|
|
166
170
|
}
|
|
167
171
|
const CustomerServiceContext = createContext("services/customer");
|
|
172
|
+
function createMockCustomerAdapter() {
|
|
173
|
+
const customer = { id: "customer-1", name: "Customer 1" };
|
|
174
|
+
return {
|
|
175
|
+
async getCurrent() {
|
|
176
|
+
return { _tag: "Success", data: customer };
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function createApiCustomerAdapter(fetcher) {
|
|
181
|
+
return {
|
|
182
|
+
async getCurrent() {
|
|
183
|
+
try {
|
|
184
|
+
const res = await fetcher("/customer/info");
|
|
185
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
186
|
+
const data = await res.json();
|
|
187
|
+
const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
|
|
188
|
+
return {
|
|
189
|
+
_tag: "Success",
|
|
190
|
+
data: { id: data.id, name, email: data.email }
|
|
191
|
+
};
|
|
192
|
+
} catch (e2) {
|
|
193
|
+
return { _tag: "Failure", error: e2 };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
168
198
|
const PARAMS = {
|
|
169
199
|
DELETGATED_TOKEN: "delegated-token",
|
|
170
200
|
REDIRECT_TO: "redirect-to",
|
|
@@ -181,6 +211,122 @@ function uuid() {
|
|
|
181
211
|
(c3) => (c3 ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c3 / 4).toString(16)
|
|
182
212
|
);
|
|
183
213
|
}
|
|
214
|
+
function createMockLoginAdapter() {
|
|
215
|
+
return {
|
|
216
|
+
async passwordlessStart() {
|
|
217
|
+
return { _tag: "Success", data: void 0 };
|
|
218
|
+
},
|
|
219
|
+
async authenticate() {
|
|
220
|
+
return {
|
|
221
|
+
_tag: "Success",
|
|
222
|
+
data: {
|
|
223
|
+
access_token: "mock-access-token",
|
|
224
|
+
refresh_token: "mock-refresh-token",
|
|
225
|
+
expires_in: 3600
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
getAuthorizeUrl({ connection, state, shop, appUrl }) {
|
|
230
|
+
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
231
|
+
url.searchParams.set("connection", connection);
|
|
232
|
+
url.searchParams.set("state", state);
|
|
233
|
+
url.searchParams.set("shop", shop);
|
|
234
|
+
return url.toString();
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function createApiLoginAdapter(fetcher, opts) {
|
|
239
|
+
const { shopDomain, appUrl } = opts;
|
|
240
|
+
async function callAuthEndpoint(path, body, lang) {
|
|
241
|
+
const url = lang ? `${path}?lang=${lang}` : path;
|
|
242
|
+
const res = await fetcher(url, {
|
|
243
|
+
method: "POST",
|
|
244
|
+
headers: {
|
|
245
|
+
Accept: "application/json",
|
|
246
|
+
"Content-Type": "application/json",
|
|
247
|
+
"X-Shopify-Shop-Domain": shopDomain
|
|
248
|
+
},
|
|
249
|
+
body: JSON.stringify(body)
|
|
250
|
+
});
|
|
251
|
+
if (res.status === 403) {
|
|
252
|
+
const data = await res.json();
|
|
253
|
+
if ("error_description" in data) {
|
|
254
|
+
if (data.error_description === "Wrong email or verification code.") {
|
|
255
|
+
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
256
|
+
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
257
|
+
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
258
|
+
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
259
|
+
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
260
|
+
}
|
|
261
|
+
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
262
|
+
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
263
|
+
connection: data.connection
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (!res.ok) {
|
|
268
|
+
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
269
|
+
}
|
|
270
|
+
return { _tag: "Success", data: void 0 };
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
async passwordlessStart({ email, lang }) {
|
|
274
|
+
try {
|
|
275
|
+
await callAuthEndpoint(
|
|
276
|
+
"/auth/customer/passwordless/start",
|
|
277
|
+
{ email },
|
|
278
|
+
lang
|
|
279
|
+
);
|
|
280
|
+
return { _tag: "Success", data: void 0 };
|
|
281
|
+
} catch (e2) {
|
|
282
|
+
return { _tag: "Failure", error: e2 };
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
async authenticate(params) {
|
|
286
|
+
try {
|
|
287
|
+
const res = await fetcher("/auth/customer/token", {
|
|
288
|
+
method: "POST",
|
|
289
|
+
headers: {
|
|
290
|
+
Accept: "application/json",
|
|
291
|
+
"Content-Type": "application/json",
|
|
292
|
+
"X-Shopify-Shop-Domain": shopDomain
|
|
293
|
+
},
|
|
294
|
+
body: JSON.stringify(params)
|
|
295
|
+
});
|
|
296
|
+
if (res.status === 403) {
|
|
297
|
+
const data = await res.json();
|
|
298
|
+
if ("error_description" in data) {
|
|
299
|
+
if (data.error_description === "Wrong email or verification code.") {
|
|
300
|
+
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
301
|
+
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
302
|
+
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
303
|
+
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
304
|
+
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
305
|
+
}
|
|
306
|
+
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
307
|
+
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
308
|
+
connection: data.connection
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (!res.ok) {
|
|
313
|
+
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
314
|
+
}
|
|
315
|
+
return { _tag: "Success", data: await res.json() };
|
|
316
|
+
} catch (e2) {
|
|
317
|
+
return { _tag: "Failure", error: e2 };
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
getAuthorizeUrl({ connection, state, shop }) {
|
|
321
|
+
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
322
|
+
url.searchParams.set("connection", connection);
|
|
323
|
+
url.searchParams.set("state", state);
|
|
324
|
+
url.searchParams.set("shop", shop);
|
|
325
|
+
return url.toString();
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
const __vite_import_meta_env__ = {};
|
|
184
330
|
var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
|
|
185
331
|
AuthenticationErrorReason2["Expired"] = "expired";
|
|
186
332
|
AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
|
|
@@ -199,6 +345,9 @@ class AuthenticationError extends Error {
|
|
|
199
345
|
this.data = data ?? {};
|
|
200
346
|
}
|
|
201
347
|
}
|
|
348
|
+
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
349
|
+
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
350
|
+
const STORAGE_STATE_KEY = "state";
|
|
202
351
|
function storage(key, value) {
|
|
203
352
|
const storageKey = `auth:${key}`;
|
|
204
353
|
try {
|
|
@@ -214,83 +363,33 @@ function storage(key, value) {
|
|
|
214
363
|
}
|
|
215
364
|
return null;
|
|
216
365
|
}
|
|
217
|
-
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
218
|
-
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
219
|
-
const STORAGE_STATE_KEY = "state";
|
|
220
|
-
function createAuthFunctions(basePath, shop) {
|
|
221
|
-
async function start(email, lang) {
|
|
222
|
-
const response = await fetch(
|
|
223
|
-
`${basePath}/auth/customer/passwordless/start?lang=${lang}`,
|
|
224
|
-
{
|
|
225
|
-
method: "POST",
|
|
226
|
-
headers: {
|
|
227
|
-
Accept: "application/json",
|
|
228
|
-
"Content-Type": "application/json",
|
|
229
|
-
"X-Shopify-Shop-Domain": shop
|
|
230
|
-
},
|
|
231
|
-
body: JSON.stringify({ email })
|
|
232
|
-
}
|
|
233
|
-
);
|
|
234
|
-
if (response.status !== 200) {
|
|
235
|
-
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
async function authenticate(data) {
|
|
239
|
-
const response = await fetch(`${basePath}/auth/customer/token`, {
|
|
240
|
-
method: "POST",
|
|
241
|
-
headers: {
|
|
242
|
-
Accept: "application/json",
|
|
243
|
-
"Content-Type": "application/json",
|
|
244
|
-
"X-Shopify-Shop-Domain": shop
|
|
245
|
-
},
|
|
246
|
-
body: JSON.stringify(data)
|
|
247
|
-
});
|
|
248
|
-
if (response.status === 403) {
|
|
249
|
-
const data2 = await response.json();
|
|
250
|
-
if ("error_description" in data2) {
|
|
251
|
-
if (data2.error_description === "Wrong email or verification code.") {
|
|
252
|
-
throw new AuthenticationError(
|
|
253
|
-
"wrong-code"
|
|
254
|
-
/* IncorrectCode */
|
|
255
|
-
);
|
|
256
|
-
} else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
257
|
-
throw new AuthenticationError(
|
|
258
|
-
"too-many-tries"
|
|
259
|
-
/* TooManyTries */
|
|
260
|
-
);
|
|
261
|
-
} else if (data2.error_description === "The verification code has expired. Please try to login again.") {
|
|
262
|
-
throw new AuthenticationError(
|
|
263
|
-
"expired"
|
|
264
|
-
/* Expired */
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
} else if ("code" in data2) {
|
|
268
|
-
if (data2.code === "AUTH_ERROR") {
|
|
269
|
-
throw new AuthenticationError("no-email", {
|
|
270
|
-
connection: data2.connection
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
if (response.status !== 200) {
|
|
276
|
-
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
277
|
-
}
|
|
278
|
-
return await response.json();
|
|
279
|
-
}
|
|
280
|
-
return { start, authenticate };
|
|
281
|
-
}
|
|
282
366
|
function nonce(len = 10) {
|
|
283
367
|
return uuid().replace(/-/g, "").slice(0, len);
|
|
284
368
|
}
|
|
285
|
-
function
|
|
286
|
-
const
|
|
369
|
+
function loginService(deps) {
|
|
370
|
+
const apiBase = deps.shop.rootUrl.replace(/\/$/, "") + "/api";
|
|
371
|
+
const appUrl = (typeof import.meta !== "undefined" ? __vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_APP_URL : void 0) ?? "https://admin.juo.io";
|
|
372
|
+
const fetcher = (path, init) => fetch(`${apiBase}${path}`, init);
|
|
373
|
+
const adapter = createApiLoginAdapter(fetcher, {
|
|
374
|
+
shopDomain: deps.shop.domain,
|
|
375
|
+
appUrl
|
|
376
|
+
});
|
|
377
|
+
return createLoginService({
|
|
378
|
+
adapter,
|
|
379
|
+
router: deps.router,
|
|
380
|
+
formatError: deps.formatError,
|
|
381
|
+
shop: {
|
|
382
|
+
domain: deps.shop.domain,
|
|
383
|
+
locale: w(() => deps.getLocale())
|
|
384
|
+
},
|
|
385
|
+
onLoginSuccess: deps.onLoginSuccess
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
function createLoginService(init) {
|
|
389
|
+
const { adapter, formatError, router, shop, onLoginSuccess } = init;
|
|
287
390
|
const accessToken = d(null);
|
|
288
|
-
const delegatedToken = d(
|
|
289
|
-
|
|
290
|
-
);
|
|
291
|
-
const refreshToken = d(
|
|
292
|
-
storage(STORAGE_REFRESH_TOKEN_KEY)
|
|
293
|
-
);
|
|
391
|
+
const delegatedToken = d(storage(STORAGE_DELEGATED_TOKEN));
|
|
392
|
+
const refreshToken = d(storage(STORAGE_REFRESH_TOKEN_KEY));
|
|
294
393
|
const expires = d(null);
|
|
295
394
|
const state = d(storage(STORAGE_STATE_KEY));
|
|
296
395
|
E(() => {
|
|
@@ -302,67 +401,28 @@ function createAuthService(apiUrl, shopDomain) {
|
|
|
302
401
|
E(() => {
|
|
303
402
|
storage(STORAGE_STATE_KEY, state.value);
|
|
304
403
|
});
|
|
305
|
-
function
|
|
404
|
+
function storeTokens(data) {
|
|
306
405
|
const { access_token, refresh_token, expires_in } = data;
|
|
307
406
|
accessToken.value = access_token;
|
|
308
407
|
refreshToken.value = refresh_token;
|
|
309
408
|
expires.value = new Date(Date.now() + expires_in);
|
|
310
409
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
},
|
|
319
|
-
getDelegatedToken() {
|
|
320
|
-
return delegatedToken.value;
|
|
321
|
-
},
|
|
322
|
-
setDelegatedToken(token) {
|
|
323
|
-
delegatedToken.value = token;
|
|
324
|
-
},
|
|
325
|
-
async getToken() {
|
|
326
|
-
if (refreshToken.value == null) {
|
|
327
|
-
return null;
|
|
328
|
-
}
|
|
329
|
-
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
330
|
-
accessToken.value = new Promise(
|
|
331
|
-
(resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
|
|
332
|
-
store(data);
|
|
333
|
-
resolve(data.access_token);
|
|
410
|
+
async function getToken() {
|
|
411
|
+
if (refreshToken.value == null) return null;
|
|
412
|
+
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
413
|
+
accessToken.value = new Promise(
|
|
414
|
+
(resolve, reject) => adapter.authenticate({ refreshToken: refreshToken.value }).then((result) => {
|
|
415
|
+
if (result._tag === "Failure") {
|
|
416
|
+
reject(result.error);
|
|
334
417
|
return;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
refreshToken.value = null;
|
|
344
|
-
expires.value = null;
|
|
345
|
-
},
|
|
346
|
-
isAuthenticated: w(
|
|
347
|
-
() => delegatedToken.value != null || refreshToken.value != null
|
|
348
|
-
)
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
function loginService(deps) {
|
|
352
|
-
return createLoginService({
|
|
353
|
-
apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
|
|
354
|
-
router: deps.router,
|
|
355
|
-
formatError: deps.formatError,
|
|
356
|
-
shop: {
|
|
357
|
-
domain: deps.shop.domain,
|
|
358
|
-
locale: w(() => deps.getLocale())
|
|
359
|
-
},
|
|
360
|
-
onLoginSuccess: deps.onLoginSuccess
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
function createLoginService(init) {
|
|
364
|
-
const { formatError, router, shop, onLoginSuccess } = init;
|
|
365
|
-
const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
|
|
418
|
+
}
|
|
419
|
+
storeTokens(result.data);
|
|
420
|
+
resolve(result.data.access_token);
|
|
421
|
+
}).catch(reject)
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
return Promise.resolve(accessToken.value);
|
|
425
|
+
}
|
|
366
426
|
const userEmail = d(null);
|
|
367
427
|
async function onSuccess(query) {
|
|
368
428
|
await (onLoginSuccess == null ? void 0 : onLoginSuccess());
|
|
@@ -375,13 +435,9 @@ function createLoginService(init) {
|
|
|
375
435
|
queryParams[key] = value;
|
|
376
436
|
}
|
|
377
437
|
});
|
|
378
|
-
router.push({
|
|
379
|
-
path: decodeURIComponent(redirect),
|
|
380
|
-
query: queryParams
|
|
381
|
-
});
|
|
438
|
+
router.push({ path: decodeURIComponent(redirect), query: queryParams });
|
|
382
439
|
} else {
|
|
383
|
-
|
|
384
|
-
router.replace(decodeURIComponent(redirect));
|
|
440
|
+
router.replace(decodeURIComponent(query.get(PARAMS.REDIRECT_TO)));
|
|
385
441
|
}
|
|
386
442
|
} else {
|
|
387
443
|
router.replace("/");
|
|
@@ -393,16 +449,17 @@ function createLoginService(init) {
|
|
|
393
449
|
async start(email) {
|
|
394
450
|
try {
|
|
395
451
|
userEmail.value = email;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
452
|
+
const lang = shop.locale instanceof u ? shop.locale.value : shop.locale;
|
|
453
|
+
const result = await adapter.passwordlessStart({
|
|
454
|
+
email: userEmail.value,
|
|
455
|
+
lang
|
|
456
|
+
});
|
|
457
|
+
if (result._tag === "Failure") {
|
|
458
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
459
|
+
}
|
|
400
460
|
return { _tag: "Success", data: void 0 };
|
|
401
461
|
} catch (error) {
|
|
402
|
-
return {
|
|
403
|
-
_tag: "Failure",
|
|
404
|
-
error: formatError(error)
|
|
405
|
-
};
|
|
462
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
406
463
|
}
|
|
407
464
|
},
|
|
408
465
|
async verify(email, code) {
|
|
@@ -413,63 +470,61 @@ function createLoginService(init) {
|
|
|
413
470
|
};
|
|
414
471
|
}
|
|
415
472
|
try {
|
|
416
|
-
const result = await
|
|
473
|
+
const result = await adapter.authenticate({
|
|
417
474
|
email: userEmail.value,
|
|
418
475
|
code
|
|
419
476
|
});
|
|
477
|
+
if (result._tag === "Failure") {
|
|
478
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
479
|
+
}
|
|
480
|
+
storeTokens(result.data);
|
|
420
481
|
await onSuccess();
|
|
421
|
-
return { _tag: "Success", data:
|
|
482
|
+
return { _tag: "Success", data: void 0 };
|
|
422
483
|
} catch (error) {
|
|
423
|
-
return {
|
|
424
|
-
_tag: "Failure",
|
|
425
|
-
error: formatError(error)
|
|
426
|
-
};
|
|
484
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
427
485
|
}
|
|
428
486
|
}
|
|
429
487
|
}
|
|
430
488
|
},
|
|
431
489
|
social: {
|
|
432
490
|
async start(provider) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
provider === "Facebook" ? "facebook" : "google-oauth2",
|
|
446
|
-
shop.domain
|
|
447
|
-
);
|
|
448
|
-
return {
|
|
449
|
-
_tag: "Success",
|
|
450
|
-
data: void 0
|
|
451
|
-
};
|
|
491
|
+
state.value = nonce();
|
|
492
|
+
const connection = provider === "Facebook" ? "facebook" : "google-oauth2";
|
|
493
|
+
window.location.href = adapter.getAuthorizeUrl({
|
|
494
|
+
connection,
|
|
495
|
+
state: state.value ?? "",
|
|
496
|
+
shop: shop.domain,
|
|
497
|
+
appUrl: (
|
|
498
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
499
|
+
(typeof import.meta !== "undefined" ? __vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_APP_URL : void 0) ?? "https://admin.juo.io"
|
|
500
|
+
)
|
|
501
|
+
});
|
|
502
|
+
return { _tag: "Success", data: void 0 };
|
|
452
503
|
},
|
|
453
504
|
async verify(code) {
|
|
454
505
|
try {
|
|
455
|
-
const result = await
|
|
506
|
+
const result = await adapter.authenticate({ code });
|
|
507
|
+
if (result._tag === "Failure") {
|
|
508
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
509
|
+
}
|
|
510
|
+
storeTokens(result.data);
|
|
456
511
|
await onSuccess();
|
|
457
|
-
return { _tag: "Success", data:
|
|
512
|
+
return { _tag: "Success", data: void 0 };
|
|
458
513
|
} catch (error) {
|
|
459
|
-
return {
|
|
460
|
-
_tag: "Failure",
|
|
461
|
-
error: formatError(error)
|
|
462
|
-
};
|
|
514
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
463
515
|
}
|
|
464
516
|
}
|
|
465
517
|
},
|
|
466
518
|
async logout() {
|
|
467
|
-
|
|
519
|
+
accessToken.value = null;
|
|
520
|
+
delegatedToken.value = null;
|
|
521
|
+
refreshToken.value = null;
|
|
522
|
+
expires.value = null;
|
|
468
523
|
return { _tag: "Success", data: void 0 };
|
|
469
524
|
},
|
|
470
525
|
async handleQueryParams(query) {
|
|
471
526
|
if (query.has(PARAMS.DELETGATED_TOKEN)) {
|
|
472
|
-
|
|
527
|
+
delegatedToken.value = query.get(PARAMS.DELETGATED_TOKEN);
|
|
473
528
|
await onSuccess(query);
|
|
474
529
|
return { _tag: "Success", data: void 0 };
|
|
475
530
|
}
|
|
@@ -481,16 +536,13 @@ function createLoginService(init) {
|
|
|
481
536
|
}
|
|
482
537
|
const code = query.get(PARAMS.CODE);
|
|
483
538
|
if (code != null) {
|
|
484
|
-
if (query.get(PARAMS.STATE) !==
|
|
485
|
-
return {
|
|
486
|
-
_tag: "Failure",
|
|
487
|
-
error: formatError("Invalid state")
|
|
488
|
-
};
|
|
539
|
+
if (query.get(PARAMS.STATE) !== state.value) {
|
|
540
|
+
return { _tag: "Failure", error: formatError("Invalid state") };
|
|
489
541
|
} else {
|
|
490
542
|
return service.social.verify(code);
|
|
491
543
|
}
|
|
492
544
|
}
|
|
493
|
-
if (query.get(PARAMS.REFRESH) === "1" && await
|
|
545
|
+
if (query.get(PARAMS.REFRESH) === "1" && await getToken() != null) {
|
|
494
546
|
await onSuccess();
|
|
495
547
|
return { _tag: "Success", data: void 0 };
|
|
496
548
|
}
|
|
@@ -507,8 +559,20 @@ function createLoginService(init) {
|
|
|
507
559
|
}
|
|
508
560
|
return { _tag: "Success", data: void 0 };
|
|
509
561
|
},
|
|
510
|
-
isAuthenticated:
|
|
511
|
-
|
|
562
|
+
isAuthenticated: w(
|
|
563
|
+
() => delegatedToken.value != null || refreshToken.value != null
|
|
564
|
+
),
|
|
565
|
+
getToken,
|
|
566
|
+
async getAuthHeaders() {
|
|
567
|
+
if (delegatedToken.value) {
|
|
568
|
+
return { "X-Delegated-Token": delegatedToken.value };
|
|
569
|
+
}
|
|
570
|
+
const token = await getToken();
|
|
571
|
+
if (token) {
|
|
572
|
+
return { Authorization: `Bearer ${token}` };
|
|
573
|
+
}
|
|
574
|
+
return {};
|
|
575
|
+
}
|
|
512
576
|
};
|
|
513
577
|
return service;
|
|
514
578
|
}
|
|
@@ -721,6 +785,106 @@ function createMockProductAdapter() {
|
|
|
721
785
|
}
|
|
722
786
|
};
|
|
723
787
|
}
|
|
788
|
+
function mapVariant(variant, currencyCode) {
|
|
789
|
+
var _a;
|
|
790
|
+
return {
|
|
791
|
+
id: variant.id,
|
|
792
|
+
title: variant.title,
|
|
793
|
+
image: variant.image == null ? null : { url: variant.image.url, altText: variant.image.altText ?? void 0 },
|
|
794
|
+
price: { amount: variant.price.toFixed(2), currencyCode },
|
|
795
|
+
...((_a = variant.purchaseOptions) == null ? void 0 : _a.length) ? {
|
|
796
|
+
purchaseOptions: variant.purchaseOptions.map((opt) => ({
|
|
797
|
+
name: opt.name,
|
|
798
|
+
price: { amount: opt.price.toFixed(2), currencyCode },
|
|
799
|
+
interval: opt.interval
|
|
800
|
+
}))
|
|
801
|
+
} : {}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
function mapProduct(product, currencyCode) {
|
|
805
|
+
return {
|
|
806
|
+
id: product.id,
|
|
807
|
+
title: product.title,
|
|
808
|
+
featuredImage: product.image == null ? null : { url: product.image.url, altText: product.image.altText ?? void 0 },
|
|
809
|
+
hasOnlyDefaultVariant: product.variants.length <= 1,
|
|
810
|
+
variants: {
|
|
811
|
+
items: product.variants.map((v2) => mapVariant(v2, currencyCode)),
|
|
812
|
+
pageInfo: {
|
|
813
|
+
hasNextPage: false,
|
|
814
|
+
hasPrevPage: false,
|
|
815
|
+
startCursor: null,
|
|
816
|
+
endCursor: null
|
|
817
|
+
}
|
|
818
|
+
},
|
|
819
|
+
collections: product.collections
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
function createApiProductAdapter(fetcher, opts = { currencyCode: "USD" }) {
|
|
823
|
+
const { currencyCode } = opts;
|
|
824
|
+
async function fetchProducts(params) {
|
|
825
|
+
const query = params.toString() ? `?${params.toString()}` : "";
|
|
826
|
+
const res = await fetcher(`/customer/v1/products${query}`);
|
|
827
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
828
|
+
return res.json();
|
|
829
|
+
}
|
|
830
|
+
return {
|
|
831
|
+
async getById(id) {
|
|
832
|
+
try {
|
|
833
|
+
const params = new URLSearchParams({ query: `id:"${id}"`, limit: "1" });
|
|
834
|
+
const body = await fetchProducts(params);
|
|
835
|
+
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
836
|
+
} catch {
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
async getByVariantId(variantId) {
|
|
841
|
+
try {
|
|
842
|
+
const params = new URLSearchParams({
|
|
843
|
+
query: `variants.id:"${variantId}"`,
|
|
844
|
+
limit: "1"
|
|
845
|
+
});
|
|
846
|
+
const body = await fetchProducts(params);
|
|
847
|
+
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
848
|
+
} catch {
|
|
849
|
+
return null;
|
|
850
|
+
}
|
|
851
|
+
},
|
|
852
|
+
async search(opts2) {
|
|
853
|
+
try {
|
|
854
|
+
const params = new URLSearchParams();
|
|
855
|
+
if (opts2 == null ? void 0 : opts2.query) {
|
|
856
|
+
params.set("query", `"${opts2.query.trim().replace(/"/g, '\\"')}"`);
|
|
857
|
+
}
|
|
858
|
+
if (opts2 == null ? void 0 : opts2.pagination) {
|
|
859
|
+
if ("after" in opts2.pagination)
|
|
860
|
+
params.set("after", opts2.pagination.after);
|
|
861
|
+
else if ("before" in opts2.pagination)
|
|
862
|
+
params.set("before", opts2.pagination.before);
|
|
863
|
+
}
|
|
864
|
+
const body = await fetchProducts(params);
|
|
865
|
+
return {
|
|
866
|
+
items: body.data.map((p2) => mapProduct(p2, currencyCode)),
|
|
867
|
+
pageInfo: {
|
|
868
|
+
hasNextPage: body.hasNextPage,
|
|
869
|
+
hasPrevPage: body.hasPrevPage,
|
|
870
|
+
startCursor: body.startCursor,
|
|
871
|
+
endCursor: body.endCursor
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
} catch {
|
|
875
|
+
return {
|
|
876
|
+
items: [],
|
|
877
|
+
pageInfo: {
|
|
878
|
+
hasNextPage: false,
|
|
879
|
+
hasPrevPage: false,
|
|
880
|
+
startCursor: null,
|
|
881
|
+
endCursor: null
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
}
|
|
724
888
|
const ProductServiceContext = createContext("services/product");
|
|
725
889
|
function createProductService(adapter) {
|
|
726
890
|
return {
|
|
@@ -786,6 +950,250 @@ function createMockRouterService() {
|
|
|
786
950
|
}
|
|
787
951
|
}).routerService;
|
|
788
952
|
}
|
|
953
|
+
function futureDateStr(daysFromNow) {
|
|
954
|
+
const d2 = /* @__PURE__ */ new Date();
|
|
955
|
+
d2.setDate(d2.getDate() + daysFromNow);
|
|
956
|
+
return d2.toISOString().split("T")[0] ?? "";
|
|
957
|
+
}
|
|
958
|
+
function createMockSchedulesAdapter() {
|
|
959
|
+
let orders = [
|
|
960
|
+
{
|
|
961
|
+
id: "schedule-order-1",
|
|
962
|
+
subscriptionId: "sub_1",
|
|
963
|
+
deliveryDate: futureDateStr(7),
|
|
964
|
+
items: [
|
|
965
|
+
{
|
|
966
|
+
id: "si-1",
|
|
967
|
+
productId: "p-beauty-1",
|
|
968
|
+
variantId: "v-beauty-1",
|
|
969
|
+
quantity: 1,
|
|
970
|
+
title: "Rose Face Serum",
|
|
971
|
+
imageUrl: "https://images.unsplash.com/photo-1611930022073-b7a4ba5fcccd?w=200"
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
id: "si-2",
|
|
975
|
+
productId: "p-beauty-2",
|
|
976
|
+
variantId: "v-beauty-2",
|
|
977
|
+
quantity: 1,
|
|
978
|
+
title: "Vitamin C Moisturizer",
|
|
979
|
+
imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200"
|
|
980
|
+
}
|
|
981
|
+
],
|
|
982
|
+
status: "scheduled"
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
id: "schedule-order-2",
|
|
986
|
+
subscriptionId: "sub_1",
|
|
987
|
+
deliveryDate: futureDateStr(35),
|
|
988
|
+
items: [
|
|
989
|
+
{
|
|
990
|
+
id: "si-3",
|
|
991
|
+
productId: "p-beauty-1",
|
|
992
|
+
variantId: "v-beauty-1",
|
|
993
|
+
quantity: 1,
|
|
994
|
+
title: "Rose Face Serum",
|
|
995
|
+
imageUrl: "https://images.unsplash.com/photo-1611930022073-b7a4ba5fcccd?w=200"
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
id: "si-4",
|
|
999
|
+
productId: "p-beauty-3",
|
|
1000
|
+
variantId: "v-beauty-3",
|
|
1001
|
+
quantity: 1,
|
|
1002
|
+
title: "Hyaluronic Acid Serum",
|
|
1003
|
+
imageUrl: "https://images.unsplash.com/photo-1621232201696-ba6c9db10a97?w=200"
|
|
1004
|
+
}
|
|
1005
|
+
],
|
|
1006
|
+
status: "scheduled"
|
|
1007
|
+
}
|
|
1008
|
+
];
|
|
1009
|
+
let adjustments = [];
|
|
1010
|
+
return {
|
|
1011
|
+
async getUpcomingOrders(count) {
|
|
1012
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1013
|
+
return { _tag: "Success", data: orders.slice(0, count) };
|
|
1014
|
+
},
|
|
1015
|
+
async getAdjustments() {
|
|
1016
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1017
|
+
return { _tag: "Success", data: [...adjustments] };
|
|
1018
|
+
},
|
|
1019
|
+
async skipOrder({ subscriptionId }) {
|
|
1020
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
1021
|
+
const order = orders.find(
|
|
1022
|
+
(o2) => o2.subscriptionId === subscriptionId && o2.status === "scheduled"
|
|
1023
|
+
);
|
|
1024
|
+
if (!order) return { _tag: "Failure", error: "No scheduled order found" };
|
|
1025
|
+
orders = orders.map(
|
|
1026
|
+
(o2) => o2.id === order.id ? { ...o2, status: "skipped" } : o2
|
|
1027
|
+
);
|
|
1028
|
+
const adj = {
|
|
1029
|
+
id: `adj-${Date.now()}`,
|
|
1030
|
+
subscriptionId,
|
|
1031
|
+
type: "skip"
|
|
1032
|
+
};
|
|
1033
|
+
adjustments = [...adjustments, adj];
|
|
1034
|
+
return { _tag: "Success", data: void 0 };
|
|
1035
|
+
},
|
|
1036
|
+
async changeDateAdjustment({ subscriptionId, newDate }) {
|
|
1037
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
1038
|
+
const order = orders.find(
|
|
1039
|
+
(o2) => o2.subscriptionId === subscriptionId && o2.status === "scheduled"
|
|
1040
|
+
);
|
|
1041
|
+
if (!order) return { _tag: "Failure", error: "No scheduled order found" };
|
|
1042
|
+
orders = orders.map(
|
|
1043
|
+
(o2) => o2.id === order.id ? { ...o2, deliveryDate: newDate } : o2
|
|
1044
|
+
);
|
|
1045
|
+
const adj = {
|
|
1046
|
+
id: `adj-${Date.now()}`,
|
|
1047
|
+
subscriptionId,
|
|
1048
|
+
type: "date_change",
|
|
1049
|
+
newDate
|
|
1050
|
+
};
|
|
1051
|
+
adjustments = [...adjustments, adj];
|
|
1052
|
+
return { _tag: "Success", data: void 0 };
|
|
1053
|
+
},
|
|
1054
|
+
async removeAdjustment({ adjustmentId }) {
|
|
1055
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1056
|
+
adjustments = adjustments.filter((a3) => a3.id !== adjustmentId);
|
|
1057
|
+
return { _tag: "Success", data: void 0 };
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
function createApiSchedulesAdapter(fetcher) {
|
|
1062
|
+
return {
|
|
1063
|
+
async getUpcomingOrders(count) {
|
|
1064
|
+
try {
|
|
1065
|
+
const res = await fetcher(`/customer/v1/schedules/?count=${count}`);
|
|
1066
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1067
|
+
const data = await res.json();
|
|
1068
|
+
return { _tag: "Success", data: data.orders };
|
|
1069
|
+
} catch (e2) {
|
|
1070
|
+
return { _tag: "Failure", error: e2 };
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
async getAdjustments() {
|
|
1074
|
+
try {
|
|
1075
|
+
const res = await fetcher(`/customer/v1/schedules/adjustments`);
|
|
1076
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1077
|
+
const data = await res.json();
|
|
1078
|
+
const adjustments = data.data.map((a3) => ({
|
|
1079
|
+
id: a3.id,
|
|
1080
|
+
subscriptionId: a3.subscriptionId ?? "",
|
|
1081
|
+
type: a3.type === "CHANGE_DATE" ? "date_change" : "skip",
|
|
1082
|
+
newDate: a3.newDate
|
|
1083
|
+
}));
|
|
1084
|
+
return { _tag: "Success", data: adjustments };
|
|
1085
|
+
} catch (e2) {
|
|
1086
|
+
return { _tag: "Failure", error: e2 };
|
|
1087
|
+
}
|
|
1088
|
+
},
|
|
1089
|
+
async skipOrder({ cycle, date }) {
|
|
1090
|
+
if (cycle == null && date == null) {
|
|
1091
|
+
return { _tag: "Failure", error: "Either cycle or date is required for skipOrder" };
|
|
1092
|
+
}
|
|
1093
|
+
try {
|
|
1094
|
+
const matcher = cycle != null ? { type: "CYCLE", input: { cycle } } : { type: "DATE", input: { date: new Date(date).toISOString() } };
|
|
1095
|
+
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1096
|
+
method: "POST",
|
|
1097
|
+
headers: { "Content-Type": "application/json" },
|
|
1098
|
+
body: JSON.stringify({
|
|
1099
|
+
matcher,
|
|
1100
|
+
action: { type: "SKIP_ORDER", input: { reason: "" } }
|
|
1101
|
+
})
|
|
1102
|
+
});
|
|
1103
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1104
|
+
return { _tag: "Success", data: void 0 };
|
|
1105
|
+
} catch (e2) {
|
|
1106
|
+
return { _tag: "Failure", error: e2 };
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
async changeDateAdjustment({ newDate, currentDate }) {
|
|
1110
|
+
if (currentDate == null) {
|
|
1111
|
+
return {
|
|
1112
|
+
_tag: "Failure",
|
|
1113
|
+
error: "currentDate is required for changeDateAdjustment"
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
try {
|
|
1117
|
+
const matcher = { type: "DATE", input: { date: new Date(currentDate).toISOString() } };
|
|
1118
|
+
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1119
|
+
method: "POST",
|
|
1120
|
+
headers: { "Content-Type": "application/json" },
|
|
1121
|
+
body: JSON.stringify({
|
|
1122
|
+
matcher,
|
|
1123
|
+
action: {
|
|
1124
|
+
type: "CHANGE_DATE",
|
|
1125
|
+
input: { newDate: new Date(newDate).toISOString() }
|
|
1126
|
+
}
|
|
1127
|
+
})
|
|
1128
|
+
});
|
|
1129
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1130
|
+
return { _tag: "Success", data: void 0 };
|
|
1131
|
+
} catch (e2) {
|
|
1132
|
+
return { _tag: "Failure", error: e2 };
|
|
1133
|
+
}
|
|
1134
|
+
},
|
|
1135
|
+
async removeAdjustment({ adjustmentId }) {
|
|
1136
|
+
try {
|
|
1137
|
+
const res = await fetcher(
|
|
1138
|
+
`/customer/v1/schedules/adjustments/${adjustmentId}`,
|
|
1139
|
+
{ method: "DELETE" }
|
|
1140
|
+
);
|
|
1141
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1142
|
+
return { _tag: "Success", data: void 0 };
|
|
1143
|
+
} catch (e2) {
|
|
1144
|
+
return { _tag: "Failure", error: e2 };
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
const SchedulesServiceContext = createContext("services/schedules");
|
|
1150
|
+
function createSchedulesService(adapter, opts) {
|
|
1151
|
+
const upcoming = d([]);
|
|
1152
|
+
async function refreshUpcoming() {
|
|
1153
|
+
const count = Math.max(upcoming.value.length, 3);
|
|
1154
|
+
const result = await adapter.getUpcomingOrders(count);
|
|
1155
|
+
if (result._tag === "Success") {
|
|
1156
|
+
upcoming.value = result.data;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
return {
|
|
1160
|
+
upcoming,
|
|
1161
|
+
hasPendingBilling: opts.hasPendingBilling,
|
|
1162
|
+
async getUpcomingOrder() {
|
|
1163
|
+
const result = await adapter.getUpcomingOrders(1);
|
|
1164
|
+
if (result._tag === "Failure") return result;
|
|
1165
|
+
upcoming.value = result.data;
|
|
1166
|
+
return { _tag: "Success", data: result.data[0] ?? null };
|
|
1167
|
+
},
|
|
1168
|
+
async getUpcomingOrders(count) {
|
|
1169
|
+
const result = await adapter.getUpcomingOrders(count);
|
|
1170
|
+
if (result._tag === "Success") {
|
|
1171
|
+
upcoming.value = result.data;
|
|
1172
|
+
}
|
|
1173
|
+
return result;
|
|
1174
|
+
},
|
|
1175
|
+
async getAdjustments() {
|
|
1176
|
+
return adapter.getAdjustments();
|
|
1177
|
+
},
|
|
1178
|
+
async skipOrder(params) {
|
|
1179
|
+
const result = await adapter.skipOrder(params);
|
|
1180
|
+
if (result._tag === "Success") {
|
|
1181
|
+
await refreshUpcoming();
|
|
1182
|
+
}
|
|
1183
|
+
return result;
|
|
1184
|
+
},
|
|
1185
|
+
async changeDateAdjustment(params) {
|
|
1186
|
+
const result = await adapter.changeDateAdjustment(params);
|
|
1187
|
+
if (result._tag === "Success") {
|
|
1188
|
+
await refreshUpcoming();
|
|
1189
|
+
}
|
|
1190
|
+
return result;
|
|
1191
|
+
},
|
|
1192
|
+
async removeAdjustment(params) {
|
|
1193
|
+
return adapter.removeAdjustment(params);
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
789
1197
|
function createMockSubscriptionAdapter(options = {}) {
|
|
790
1198
|
const mockSubscriptions = [
|
|
791
1199
|
{
|
|
@@ -798,7 +1206,9 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
798
1206
|
variantId: "variant_1",
|
|
799
1207
|
quantity: 2,
|
|
800
1208
|
price: { amount: "29.99", currencyCode: "USD" },
|
|
801
|
-
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15")
|
|
1209
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1210
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
1211
|
+
deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
|
|
802
1212
|
}
|
|
803
1213
|
],
|
|
804
1214
|
discounts: [
|
|
@@ -826,7 +1236,9 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
826
1236
|
variantId: "variant_2",
|
|
827
1237
|
quantity: 1,
|
|
828
1238
|
price: { amount: "49.99", currencyCode: "USD" },
|
|
829
|
-
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20")
|
|
1239
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
|
|
1240
|
+
billingPolicy: { intervalCount: 2, interval: "WEEK" },
|
|
1241
|
+
deliveryPolicy: { intervalCount: 2, interval: "WEEK" }
|
|
830
1242
|
}
|
|
831
1243
|
],
|
|
832
1244
|
discounts: [],
|
|
@@ -837,6 +1249,24 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
837
1249
|
updatedAt: "2024-10-10T00:00:00Z"
|
|
838
1250
|
}
|
|
839
1251
|
];
|
|
1252
|
+
const mockUpsellProducts = [
|
|
1253
|
+
{
|
|
1254
|
+
id: "upsell-1",
|
|
1255
|
+
variantId: "upsell-v-1",
|
|
1256
|
+
title: "Retinol Night Cream",
|
|
1257
|
+
imageUrl: "https://images.unsplash.com/photo-1617897903246-719242758050?w=200",
|
|
1258
|
+
price: { amount: "38.00", currencyCode: "USD" },
|
|
1259
|
+
description: "Anti-aging night treatment"
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
id: "upsell-2",
|
|
1263
|
+
variantId: "upsell-v-2",
|
|
1264
|
+
title: "SPF 50 Day Shield",
|
|
1265
|
+
imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
|
|
1266
|
+
price: { amount: "24.00", currencyCode: "USD" },
|
|
1267
|
+
description: "Lightweight daily sun protection"
|
|
1268
|
+
}
|
|
1269
|
+
];
|
|
840
1270
|
return {
|
|
841
1271
|
async getById(id) {
|
|
842
1272
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -845,7 +1275,7 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
845
1275
|
},
|
|
846
1276
|
async search(options2 = {}) {
|
|
847
1277
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
848
|
-
const pageSize =
|
|
1278
|
+
const pageSize = 2;
|
|
849
1279
|
const allItems = mockSubscriptions;
|
|
850
1280
|
let startIndex = 0;
|
|
851
1281
|
let endIndex = pageSize;
|
|
@@ -861,20 +1291,22 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
861
1291
|
}
|
|
862
1292
|
}
|
|
863
1293
|
const pageItems = allItems.slice(startIndex, endIndex);
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
items:
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1294
|
+
return {
|
|
1295
|
+
_tag: "Success",
|
|
1296
|
+
data: {
|
|
1297
|
+
items: pageItems.map((sub) => ({
|
|
1298
|
+
...sub,
|
|
1299
|
+
items: sub.items.map((item) => ({ ...item })),
|
|
1300
|
+
discounts: sub.discounts.map((discount) => ({ ...discount }))
|
|
1301
|
+
})),
|
|
1302
|
+
pageInfo: {
|
|
1303
|
+
hasNextPage: endIndex < allItems.length,
|
|
1304
|
+
hasPrevPage: startIndex > 0,
|
|
1305
|
+
startCursor: pageItems.length > 0 ? startIndex.toString() : null,
|
|
1306
|
+
endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
|
|
1307
|
+
}
|
|
875
1308
|
}
|
|
876
1309
|
};
|
|
877
|
-
return { _tag: "Success", data: paginatedList };
|
|
878
1310
|
},
|
|
879
1311
|
async addItem() {
|
|
880
1312
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -912,24 +1344,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
912
1344
|
(sub) => sub.id === params.subscriptionId
|
|
913
1345
|
);
|
|
914
1346
|
if (!subscription) {
|
|
915
|
-
return {
|
|
916
|
-
_tag: "Failure",
|
|
917
|
-
error: "Subscription not found"
|
|
918
|
-
};
|
|
1347
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
919
1348
|
}
|
|
920
1349
|
subscription.nextDeliveryDate = new Date(params.newDate);
|
|
921
1350
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
922
|
-
subscription
|
|
923
|
-
item.nextDeliveryDate = new Date(params.newDate);
|
|
924
|
-
});
|
|
925
|
-
return {
|
|
926
|
-
_tag: "Success",
|
|
927
|
-
data: {
|
|
928
|
-
...subscription,
|
|
929
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
930
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
931
|
-
}
|
|
932
|
-
};
|
|
1351
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
933
1352
|
},
|
|
934
1353
|
async resume(params) {
|
|
935
1354
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -937,21 +1356,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
937
1356
|
(sub) => sub.id === params.subscriptionId
|
|
938
1357
|
);
|
|
939
1358
|
if (!subscription) {
|
|
940
|
-
return {
|
|
941
|
-
_tag: "Failure",
|
|
942
|
-
error: "Subscription not found"
|
|
943
|
-
};
|
|
1359
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
944
1360
|
}
|
|
945
1361
|
subscription.status = "active";
|
|
946
1362
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
947
|
-
return {
|
|
948
|
-
_tag: "Success",
|
|
949
|
-
data: {
|
|
950
|
-
...subscription,
|
|
951
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
952
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
953
|
-
}
|
|
954
|
-
};
|
|
1363
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
955
1364
|
},
|
|
956
1365
|
async pause(params) {
|
|
957
1366
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -959,21 +1368,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
959
1368
|
(sub) => sub.id === params.subscriptionId
|
|
960
1369
|
);
|
|
961
1370
|
if (!subscription) {
|
|
962
|
-
return {
|
|
963
|
-
_tag: "Failure",
|
|
964
|
-
error: "Subscription not found"
|
|
965
|
-
};
|
|
1371
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
966
1372
|
}
|
|
967
1373
|
subscription.status = "paused";
|
|
968
1374
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
969
|
-
return {
|
|
970
|
-
_tag: "Success",
|
|
971
|
-
data: {
|
|
972
|
-
...subscription,
|
|
973
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
974
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
975
|
-
}
|
|
976
|
-
};
|
|
1375
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
977
1376
|
},
|
|
978
1377
|
async reactivate(params) {
|
|
979
1378
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -981,21 +1380,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
981
1380
|
(sub) => sub.id === params.subscriptionId
|
|
982
1381
|
);
|
|
983
1382
|
if (!subscription) {
|
|
984
|
-
return {
|
|
985
|
-
_tag: "Failure",
|
|
986
|
-
error: "Subscription not found"
|
|
987
|
-
};
|
|
1383
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
988
1384
|
}
|
|
989
1385
|
subscription.status = "active";
|
|
990
1386
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
991
|
-
return {
|
|
992
|
-
_tag: "Success",
|
|
993
|
-
data: {
|
|
994
|
-
...subscription,
|
|
995
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
996
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
997
|
-
}
|
|
998
|
-
};
|
|
1387
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
999
1388
|
},
|
|
1000
1389
|
async cancel(params) {
|
|
1001
1390
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1003,21 +1392,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1003
1392
|
(sub) => sub.id === params.subscriptionId
|
|
1004
1393
|
);
|
|
1005
1394
|
if (!subscription) {
|
|
1006
|
-
return {
|
|
1007
|
-
_tag: "Failure",
|
|
1008
|
-
error: "Subscription not found"
|
|
1009
|
-
};
|
|
1395
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1010
1396
|
}
|
|
1011
1397
|
subscription.status = "canceled";
|
|
1012
1398
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1013
|
-
return {
|
|
1014
|
-
_tag: "Success",
|
|
1015
|
-
data: {
|
|
1016
|
-
...subscription,
|
|
1017
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
1018
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
1019
|
-
}
|
|
1020
|
-
};
|
|
1399
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
1021
1400
|
},
|
|
1022
1401
|
async getAvailablePlans() {
|
|
1023
1402
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1049,6 +1428,312 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1049
1428
|
var _a;
|
|
1050
1429
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1051
1430
|
return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
|
|
1431
|
+
},
|
|
1432
|
+
async getUpsellProducts() {
|
|
1433
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1434
|
+
return { _tag: "Success", data: [...mockUpsellProducts] };
|
|
1435
|
+
},
|
|
1436
|
+
async addUpsellProduct() {
|
|
1437
|
+
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
1438
|
+
return { _tag: "Success", data: void 0 };
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
function mapSubscription(raw) {
|
|
1443
|
+
var _a, _b;
|
|
1444
|
+
const status = raw.status === "merged" ? "canceled" : raw.status;
|
|
1445
|
+
const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
|
|
1446
|
+
const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
|
|
1447
|
+
return {
|
|
1448
|
+
id: raw.id,
|
|
1449
|
+
customerId: "",
|
|
1450
|
+
items: (raw.items ?? []).map(
|
|
1451
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1452
|
+
(item) => ({
|
|
1453
|
+
id: item.id,
|
|
1454
|
+
productId: item.productId ?? item.product ?? "",
|
|
1455
|
+
variantId: item.variantId ?? item.variant ?? "",
|
|
1456
|
+
quantity: item.quantity,
|
|
1457
|
+
price: item.price ?? {
|
|
1458
|
+
amount: String(item.totalPrice ?? 0),
|
|
1459
|
+
currencyCode: raw.currencyCode ?? ""
|
|
1460
|
+
},
|
|
1461
|
+
nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null,
|
|
1462
|
+
billingPolicy: item.billingPolicy ?? raw.billingPolicy ?? null,
|
|
1463
|
+
deliveryPolicy: item.deliveryPolicy ?? raw.deliveryPolicy ?? null
|
|
1464
|
+
})
|
|
1465
|
+
),
|
|
1466
|
+
discounts: (raw.discounts ?? []).map(
|
|
1467
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1468
|
+
(d2) => {
|
|
1469
|
+
var _a2, _b2, _c;
|
|
1470
|
+
return {
|
|
1471
|
+
id: d2.id,
|
|
1472
|
+
type: ((_a2 = d2.value) == null ? void 0 : _a2.type) === "fixed-amount" || d2.type === "fixed_amount" ? "fixed_amount" : "percentage",
|
|
1473
|
+
value: ((_b2 = d2.value) == null ? void 0 : _b2.amount) ?? ((_c = d2.value) == null ? void 0 : _c.percentage) ?? d2.value ?? 0,
|
|
1474
|
+
code: d2.title ?? d2.code ?? "",
|
|
1475
|
+
description: d2.description ?? ""
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
),
|
|
1479
|
+
status,
|
|
1480
|
+
nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
|
|
1481
|
+
frequency,
|
|
1482
|
+
createdAt: raw.createdAt,
|
|
1483
|
+
updatedAt: raw.updatedAt
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
function createApiSubscriptionAdapter(fetcher) {
|
|
1487
|
+
return {
|
|
1488
|
+
async search(options) {
|
|
1489
|
+
var _a;
|
|
1490
|
+
try {
|
|
1491
|
+
const params = new URLSearchParams();
|
|
1492
|
+
if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
|
|
1493
|
+
const q2 = options.filters.filter((f2) => Object.keys(f2).length > 0).flatMap((f2) => Object.entries(f2).filter(([, v2]) => Boolean(v2))).map(([k2, v2]) => `${k2}:'${v2}'`).join(" AND ");
|
|
1494
|
+
if (q2) params.set("query", q2);
|
|
1495
|
+
}
|
|
1496
|
+
if (options == null ? void 0 : options.pagination) {
|
|
1497
|
+
if ("after" in options.pagination)
|
|
1498
|
+
params.set("after", options.pagination.after);
|
|
1499
|
+
else if ("before" in options.pagination)
|
|
1500
|
+
params.set("before", options.pagination.before);
|
|
1501
|
+
}
|
|
1502
|
+
const query = params.toString() ? `?${params.toString()}` : "";
|
|
1503
|
+
const res = await fetcher(`/customer/v1/subscriptions${query}`);
|
|
1504
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1505
|
+
const body = await res.json();
|
|
1506
|
+
return {
|
|
1507
|
+
_tag: "Success",
|
|
1508
|
+
data: {
|
|
1509
|
+
items: body.data.map(mapSubscription),
|
|
1510
|
+
pageInfo: {
|
|
1511
|
+
hasNextPage: body.hasNextPage,
|
|
1512
|
+
hasPrevPage: body.hasPrevPage,
|
|
1513
|
+
startCursor: body.startCursor,
|
|
1514
|
+
endCursor: body.endCursor
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
};
|
|
1518
|
+
} catch (e2) {
|
|
1519
|
+
return { _tag: "Failure", error: e2 };
|
|
1520
|
+
}
|
|
1521
|
+
},
|
|
1522
|
+
async getById(id) {
|
|
1523
|
+
try {
|
|
1524
|
+
const res = await fetcher(
|
|
1525
|
+
`/customer/v1/subscriptions?query=id:"${id}"&limit=1`
|
|
1526
|
+
);
|
|
1527
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1528
|
+
const body = await res.json();
|
|
1529
|
+
const raw = body.data[0];
|
|
1530
|
+
return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
|
|
1531
|
+
} catch (e2) {
|
|
1532
|
+
return { _tag: "Failure", error: e2 };
|
|
1533
|
+
}
|
|
1534
|
+
},
|
|
1535
|
+
async addItem({ subscriptionId, item }) {
|
|
1536
|
+
try {
|
|
1537
|
+
const res = await fetcher(
|
|
1538
|
+
`/customer/v1/subscriptions/${subscriptionId}/items`,
|
|
1539
|
+
{
|
|
1540
|
+
method: "POST",
|
|
1541
|
+
headers: { "Content-Type": "application/json" },
|
|
1542
|
+
body: JSON.stringify(item)
|
|
1543
|
+
}
|
|
1544
|
+
);
|
|
1545
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1546
|
+
return { _tag: "Success", data: void 0 };
|
|
1547
|
+
} catch (e2) {
|
|
1548
|
+
return { _tag: "Failure", error: e2 };
|
|
1549
|
+
}
|
|
1550
|
+
},
|
|
1551
|
+
async removeItem({ subscriptionId, itemId }) {
|
|
1552
|
+
try {
|
|
1553
|
+
const res = await fetcher(
|
|
1554
|
+
`/customer/v1/subscriptions/${subscriptionId}/items/${itemId}`,
|
|
1555
|
+
{ method: "DELETE" }
|
|
1556
|
+
);
|
|
1557
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1558
|
+
return { _tag: "Success", data: void 0 };
|
|
1559
|
+
} catch (e2) {
|
|
1560
|
+
return { _tag: "Failure", error: e2 };
|
|
1561
|
+
}
|
|
1562
|
+
},
|
|
1563
|
+
async addDiscount({ subscriptionId, code }) {
|
|
1564
|
+
try {
|
|
1565
|
+
const res = await fetcher(
|
|
1566
|
+
`/customer/v1/subscriptions/${subscriptionId}/discounts`,
|
|
1567
|
+
{
|
|
1568
|
+
method: "POST",
|
|
1569
|
+
headers: { "Content-Type": "application/json" },
|
|
1570
|
+
body: JSON.stringify({ code })
|
|
1571
|
+
}
|
|
1572
|
+
);
|
|
1573
|
+
if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
|
|
1574
|
+
return { _tag: "Success", data: void 0 };
|
|
1575
|
+
} catch (e2) {
|
|
1576
|
+
return { _tag: "Failure", error: { kind: "UNKNOWN", message: e2 instanceof Error ? e2.message : void 0 } };
|
|
1577
|
+
}
|
|
1578
|
+
},
|
|
1579
|
+
async removeDiscount({ subscriptionId, discountId }) {
|
|
1580
|
+
try {
|
|
1581
|
+
const res = await fetcher(
|
|
1582
|
+
`/customer/v1/subscriptions/${subscriptionId}/discounts/${discountId}`,
|
|
1583
|
+
{ method: "DELETE" }
|
|
1584
|
+
);
|
|
1585
|
+
if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
|
|
1586
|
+
return { _tag: "Success", data: void 0 };
|
|
1587
|
+
} catch (e2) {
|
|
1588
|
+
return { _tag: "Failure", error: { kind: "UNKNOWN", message: e2 instanceof Error ? e2.message : void 0 } };
|
|
1589
|
+
}
|
|
1590
|
+
},
|
|
1591
|
+
// Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
|
|
1592
|
+
async reschedule({ subscriptionId, newDate }) {
|
|
1593
|
+
try {
|
|
1594
|
+
const res = await fetcher(
|
|
1595
|
+
`/customer/v1/subscriptions/${subscriptionId}`,
|
|
1596
|
+
{
|
|
1597
|
+
method: "PATCH",
|
|
1598
|
+
headers: { "Content-Type": "application/json" },
|
|
1599
|
+
body: JSON.stringify({ nextBillingDate: newDate })
|
|
1600
|
+
}
|
|
1601
|
+
);
|
|
1602
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1603
|
+
const data = await res.json();
|
|
1604
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1605
|
+
} catch (e2) {
|
|
1606
|
+
return { _tag: "Failure", error: e2 };
|
|
1607
|
+
}
|
|
1608
|
+
},
|
|
1609
|
+
async resume({ subscriptionId }) {
|
|
1610
|
+
try {
|
|
1611
|
+
const res = await fetcher(
|
|
1612
|
+
`/customer/v1/subscriptions/${subscriptionId}/resume`,
|
|
1613
|
+
{ method: "POST" }
|
|
1614
|
+
);
|
|
1615
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1616
|
+
const data = await res.json();
|
|
1617
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1618
|
+
} catch (e2) {
|
|
1619
|
+
return { _tag: "Failure", error: e2 };
|
|
1620
|
+
}
|
|
1621
|
+
},
|
|
1622
|
+
async pause({ subscriptionId }) {
|
|
1623
|
+
try {
|
|
1624
|
+
const res = await fetcher(
|
|
1625
|
+
`/customer/v1/subscriptions/${subscriptionId}/pause`,
|
|
1626
|
+
{ method: "POST" }
|
|
1627
|
+
);
|
|
1628
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1629
|
+
const data = await res.json();
|
|
1630
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1631
|
+
} catch (e2) {
|
|
1632
|
+
return { _tag: "Failure", error: e2 };
|
|
1633
|
+
}
|
|
1634
|
+
},
|
|
1635
|
+
async reactivate({ subscriptionId }) {
|
|
1636
|
+
try {
|
|
1637
|
+
const res = await fetcher(
|
|
1638
|
+
`/customer/v1/subscriptions/${subscriptionId}/reactivate`,
|
|
1639
|
+
{ method: "POST" }
|
|
1640
|
+
);
|
|
1641
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1642
|
+
const data = await res.json();
|
|
1643
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1644
|
+
} catch (e2) {
|
|
1645
|
+
return { _tag: "Failure", error: e2 };
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
async cancel({ subscriptionId, reason }) {
|
|
1649
|
+
try {
|
|
1650
|
+
const res = await fetcher(
|
|
1651
|
+
`/customer/v1/subscriptions/${subscriptionId}/cancel`,
|
|
1652
|
+
{
|
|
1653
|
+
method: "POST",
|
|
1654
|
+
headers: { "Content-Type": "application/json" },
|
|
1655
|
+
body: JSON.stringify({ cancellationReason: reason })
|
|
1656
|
+
}
|
|
1657
|
+
);
|
|
1658
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1659
|
+
const data = await res.json();
|
|
1660
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1661
|
+
} catch (e2) {
|
|
1662
|
+
return { _tag: "Failure", error: e2 };
|
|
1663
|
+
}
|
|
1664
|
+
},
|
|
1665
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
|
|
1666
|
+
async getAvailablePlans({ subscriptionId }) {
|
|
1667
|
+
try {
|
|
1668
|
+
const res = await fetcher(
|
|
1669
|
+
`/customer/subscriptions/${subscriptionId}/plans`
|
|
1670
|
+
);
|
|
1671
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1672
|
+
const data = await res.json();
|
|
1673
|
+
return { _tag: "Success", data };
|
|
1674
|
+
} catch (e2) {
|
|
1675
|
+
return { _tag: "Failure", error: e2 };
|
|
1676
|
+
}
|
|
1677
|
+
},
|
|
1678
|
+
// Legacy endpoint — no /customer/v1 equivalent exists yet.
|
|
1679
|
+
async getDefaultSubscriptionId() {
|
|
1680
|
+
try {
|
|
1681
|
+
const res = await fetcher(
|
|
1682
|
+
`/customer/subscriptions/one-by-next-billing-date`
|
|
1683
|
+
);
|
|
1684
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1685
|
+
const data = await res.json();
|
|
1686
|
+
return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
|
|
1687
|
+
} catch (e2) {
|
|
1688
|
+
return { _tag: "Failure", error: e2 };
|
|
1689
|
+
}
|
|
1690
|
+
},
|
|
1691
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
|
|
1692
|
+
async getUpsellProducts({ subscriptionId }) {
|
|
1693
|
+
try {
|
|
1694
|
+
const res = await fetcher(
|
|
1695
|
+
`/customer/subscriptions/${subscriptionId}/upsell-products`
|
|
1696
|
+
);
|
|
1697
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1698
|
+
const data = await res.json();
|
|
1699
|
+
const products = data.map((p2) => {
|
|
1700
|
+
var _a, _b, _c, _d, _e;
|
|
1701
|
+
return {
|
|
1702
|
+
id: p2.id,
|
|
1703
|
+
variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
|
|
1704
|
+
title: p2.title ?? "",
|
|
1705
|
+
imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
|
|
1706
|
+
price: {
|
|
1707
|
+
amount: String(((_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.price) ?? 0),
|
|
1708
|
+
currencyCode: p2.currencyCode ?? ""
|
|
1709
|
+
}
|
|
1710
|
+
};
|
|
1711
|
+
});
|
|
1712
|
+
return { _tag: "Success", data: products };
|
|
1713
|
+
} catch (e2) {
|
|
1714
|
+
return { _tag: "Failure", error: e2 };
|
|
1715
|
+
}
|
|
1716
|
+
},
|
|
1717
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
|
|
1718
|
+
async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
|
|
1719
|
+
try {
|
|
1720
|
+
const res = await fetcher(
|
|
1721
|
+
`/customer/subscriptions/${subscriptionId}/upsell`,
|
|
1722
|
+
{
|
|
1723
|
+
method: "POST",
|
|
1724
|
+
headers: { "Content-Type": "application/json" },
|
|
1725
|
+
body: JSON.stringify({
|
|
1726
|
+
productVariantId: variantId,
|
|
1727
|
+
quantity,
|
|
1728
|
+
...oneTime ? { renewalLimit: 1 } : {}
|
|
1729
|
+
})
|
|
1730
|
+
}
|
|
1731
|
+
);
|
|
1732
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1733
|
+
return { _tag: "Success", data: void 0 };
|
|
1734
|
+
} catch (e2) {
|
|
1735
|
+
return { _tag: "Failure", error: e2 };
|
|
1736
|
+
}
|
|
1052
1737
|
}
|
|
1053
1738
|
};
|
|
1054
1739
|
}
|
|
@@ -1074,6 +1759,7 @@ function getCurrentSubscriptionId() {
|
|
|
1074
1759
|
}
|
|
1075
1760
|
function createSubscriptionService(adapter) {
|
|
1076
1761
|
const current = d(null);
|
|
1762
|
+
const hasPendingBilling = d(false);
|
|
1077
1763
|
const discountsChangedAtSignal = d(0);
|
|
1078
1764
|
async function refreshAfterDiscountChange(subscriptionId) {
|
|
1079
1765
|
const result = await adapter.getById(subscriptionId);
|
|
@@ -1086,9 +1772,24 @@ function createSubscriptionService(adapter) {
|
|
|
1086
1772
|
var _a;
|
|
1087
1773
|
return ((_a = current.value) == null ? void 0 : _a.id) ?? null;
|
|
1088
1774
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1775
|
+
return {
|
|
1776
|
+
current,
|
|
1777
|
+
hasPendingBilling,
|
|
1778
|
+
discountsChangedAt: discountsChangedAtSignal,
|
|
1779
|
+
async search(options) {
|
|
1780
|
+
const result = await adapter.search(options);
|
|
1781
|
+
if (result._tag === "Success") {
|
|
1782
|
+
current.value = result.data.items[0] ?? null;
|
|
1783
|
+
}
|
|
1784
|
+
return result;
|
|
1785
|
+
},
|
|
1786
|
+
async getById(id) {
|
|
1787
|
+
const result = await adapter.getById(id);
|
|
1788
|
+
if (result._tag === "Success" && result.data) {
|
|
1789
|
+
current.value = result.data;
|
|
1790
|
+
}
|
|
1791
|
+
return result;
|
|
1792
|
+
},
|
|
1092
1793
|
getCurrentSubscriptionId,
|
|
1093
1794
|
addItem: (subscriptionId, item) => adapter.addItem({ subscriptionId, item }),
|
|
1094
1795
|
removeItem: (subscriptionId, itemId) => adapter.removeItem({ subscriptionId, itemId }),
|
|
@@ -1130,10 +1831,9 @@ function createSubscriptionService(adapter) {
|
|
|
1130
1831
|
cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
|
|
1131
1832
|
getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
|
|
1132
1833
|
getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
|
|
1133
|
-
|
|
1134
|
-
|
|
1834
|
+
getUpsellProducts: (params) => adapter.getUpsellProducts ? adapter.getUpsellProducts(params) : Promise.resolve({ _tag: "Success", data: [] }),
|
|
1835
|
+
addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
|
|
1135
1836
|
};
|
|
1136
|
-
return service;
|
|
1137
1837
|
}
|
|
1138
1838
|
const DISCOUNT_REJECTION_REASONS = [
|
|
1139
1839
|
"INVALID_CODE",
|
|
@@ -1409,6 +2109,7 @@ export {
|
|
|
1409
2109
|
OrdersServiceContext,
|
|
1410
2110
|
ProductServiceContext,
|
|
1411
2111
|
RouterServiceContext,
|
|
2112
|
+
SchedulesServiceContext,
|
|
1412
2113
|
S as SelectedBlockContext,
|
|
1413
2114
|
u as Signal,
|
|
1414
2115
|
StateContext,
|
|
@@ -1419,20 +2120,30 @@ export {
|
|
|
1419
2120
|
b as attachEmptyTracking,
|
|
1420
2121
|
e as backfillInstanceDefaults,
|
|
1421
2122
|
w as computed,
|
|
2123
|
+
createApiCustomerAdapter,
|
|
2124
|
+
createApiLoginAdapter,
|
|
2125
|
+
createApiProductAdapter,
|
|
2126
|
+
createApiSchedulesAdapter,
|
|
2127
|
+
createApiSubscriptionAdapter,
|
|
1422
2128
|
f as createBlockInstance,
|
|
1423
2129
|
g as createBlockInstanceFromJSON,
|
|
1424
2130
|
h as createBlockInstanceFromObject,
|
|
1425
2131
|
i as createCeRenderer,
|
|
1426
2132
|
createContext,
|
|
1427
2133
|
createCustomerService,
|
|
2134
|
+
createLoginService,
|
|
2135
|
+
createMockCustomerAdapter,
|
|
2136
|
+
createMockLoginAdapter,
|
|
1428
2137
|
createMockOrdersAdapter,
|
|
1429
2138
|
createMockProductAdapter,
|
|
1430
2139
|
createMockRouterService,
|
|
2140
|
+
createMockSchedulesAdapter,
|
|
1431
2141
|
createMockSubscriptionAdapter,
|
|
1432
2142
|
createMockWorkflowApiAdapter,
|
|
1433
2143
|
createOrdersService,
|
|
1434
2144
|
createProductService,
|
|
1435
2145
|
createRouterService,
|
|
2146
|
+
createSchedulesService,
|
|
1436
2147
|
j as createSelectedBlock,
|
|
1437
2148
|
createState,
|
|
1438
2149
|
createSubscriptionService,
|