@juo/blocks 1.11.0 → 1.12.0-alpha.1
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-9XfhG8YM.js → ExtensionRoot-Z2DlF9X6.js} +14 -3
- package/dist/block-Bh3z0B3y.js +971 -0
- package/dist/block.d.ts +50 -0
- package/dist/block.d.ts.map +1 -1
- package/dist/block.devtools.test.d.ts +2 -0
- package/dist/block.devtools.test.d.ts.map +1 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.devtools.test.d.ts +2 -0
- package/dist/context.devtools.test.d.ts.map +1 -0
- package/dist/core.js +1677 -330
- package/dist/editor/components.d.ts.map +1 -1
- package/dist/editor/main.d.ts +4 -0
- package/dist/editor/main.d.ts.map +1 -1
- package/dist/editor/main.test.d.ts +2 -0
- package/dist/editor/main.test.d.ts.map +1 -0
- package/dist/editor.js +19 -2
- package/dist/{juo-workflow-CkyZRq60.js → juo-workflow-D3c29mxb.js} +52 -871
- package/dist/main.d.ts +2 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/overlay-wrapper.d.ts +68 -0
- package/dist/overlay-wrapper.d.ts.map +1 -0
- package/dist/registry.devtools.test.d.ts +2 -0
- package/dist/registry.devtools.test.d.ts.map +1 -0
- 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 +3 -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/orders/adapter.d.ts +2 -1
- package/dist/services/orders/adapter.d.ts.map +1 -1
- package/dist/services/orders/types.d.ts +8 -0
- package/dist/services/orders/types.d.ts.map +1 -1
- package/dist/services/product/adapter.d.ts +27 -2
- package/dist/services/product/adapter.d.ts.map +1 -1
- package/dist/services/product/adapter.test.d.ts +2 -0
- package/dist/services/product/adapter.test.d.ts.map +1 -0
- package/dist/services/product/index.d.ts +1 -1
- package/dist/services/product/index.d.ts.map +1 -1
- package/dist/services/product/productService.d.ts.map +1 -1
- package/dist/services/product/types.d.ts +68 -0
- package/dist/services/product/types.d.ts.map +1 -1
- package/dist/services/schedules/adapter.d.ts +25 -0
- package/dist/services/schedules/adapter.d.ts.map +1 -0
- package/dist/services/schedules/adapter.test.d.ts +2 -0
- package/dist/services/schedules/adapter.test.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 +11 -0
- package/dist/services/schedules/schedulesService.d.ts.map +1 -0
- package/dist/services/schedules/schedulesService.test.d.ts +2 -0
- package/dist/services/schedules/schedulesService.test.d.ts.map +1 -0
- package/dist/services/schedules/types.d.ts +101 -0
- package/dist/services/schedules/types.d.ts.map +1 -0
- package/dist/services/settings/index.d.ts +3 -0
- package/dist/services/settings/index.d.ts.map +1 -0
- package/dist/services/settings/settingsService.d.ts +7 -0
- package/dist/services/settings/settingsService.d.ts.map +1 -0
- package/dist/services/settings/settingsService.test.d.ts +2 -0
- package/dist/services/settings/settingsService.test.d.ts.map +1 -0
- package/dist/services/settings/types.d.ts +35 -0
- package/dist/services/settings/types.d.ts.map +1 -0
- package/dist/services/shop/index.d.ts +3 -0
- package/dist/services/shop/index.d.ts.map +1 -0
- package/dist/services/shop/shopService.d.ts +12 -0
- package/dist/services/shop/shopService.d.ts.map +1 -0
- package/dist/services/shop/shopService.test.d.ts +2 -0
- package/dist/services/shop/shopService.test.d.ts.map +1 -0
- package/dist/services/shop/types.d.ts +7 -0
- package/dist/services/shop/types.d.ts.map +1 -0
- package/dist/services/subscription/adapter.d.ts +40 -2
- package/dist/services/subscription/adapter.d.ts.map +1 -1
- package/dist/services/subscription/adapter.test.d.ts +2 -0
- package/dist/services/subscription/adapter.test.d.ts.map +1 -0
- 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 +186 -9
- package/dist/services/subscription/types.d.ts.map +1 -1
- package/dist/services/subscription/types.test.d.ts +2 -0
- package/dist/services/subscription/types.test.d.ts.map +1 -0
- package/dist/theme-state.d.ts +2 -1
- package/dist/theme-state.d.ts.map +1 -1
- package/dist/theme-state.params.test.d.ts +2 -0
- package/dist/theme-state.params.test.d.ts.map +1 -0
- package/dist/theme-state.translations.test.d.ts +2 -0
- package/dist/theme-state.translations.test.d.ts.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/vue/block.d.ts.map +1 -1
- package/dist/vue.js +23 -3
- package/dist/web-components/ExtensionRoot.d.ts +1 -0
- package/dist/web-components/ExtensionRoot.d.ts.map +1 -1
- package/dist/web-components-editor.js +3 -2
- package/dist/web-components-runtime.js +3 -2
- package/package.json +3 -2
package/dist/core.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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 "./
|
|
5
|
-
import { B, C,
|
|
4
|
+
import { c as createContext, d, w, a as defineBlock, b as createBlockInstance, r as registerBlock, E, u } from "./block-Bh3z0B3y.js";
|
|
5
|
+
import { B, C, e, S, _, f, g, h, i, j, k, l, m, n, o, p, q, s, t, v, x, y, z, A, D, F } from "./block-Bh3z0B3y.js";
|
|
6
|
+
import { D as D2, T, a, W, c, b, d as d2, e as e2, g as g2, h as h2, i as i2 } from "./juo-workflow-D3c29mxb.js";
|
|
6
7
|
import { reportBlockWarning, reportBlockError } from "./observability.js";
|
|
7
8
|
function createState() {
|
|
8
9
|
const discountCode = d(null);
|
|
@@ -72,6 +73,49 @@ function createState() {
|
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
75
|
const StateContext = createContext("state");
|
|
76
|
+
const OVERLAY_WRAPPER_SCHEMA = {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {
|
|
79
|
+
props: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {
|
|
82
|
+
layout: {
|
|
83
|
+
type: "string",
|
|
84
|
+
enum: ["modal", "sidebar"]
|
|
85
|
+
},
|
|
86
|
+
canAbort: {
|
|
87
|
+
type: "boolean"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
required: ["layout"],
|
|
91
|
+
additionalProperties: false
|
|
92
|
+
},
|
|
93
|
+
slots: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
content: {}
|
|
97
|
+
},
|
|
98
|
+
additionalProperties: false
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
required: ["props", "slots"],
|
|
102
|
+
additionalProperties: false
|
|
103
|
+
};
|
|
104
|
+
function defineOverlayWrapper(opts) {
|
|
105
|
+
const layout = opts.layout ?? "sidebar";
|
|
106
|
+
const canAbort = opts.canAbort ?? true;
|
|
107
|
+
return defineBlock(opts.name, {
|
|
108
|
+
group: "internal",
|
|
109
|
+
schema: OVERLAY_WRAPPER_SCHEMA,
|
|
110
|
+
initialValue: () => ({
|
|
111
|
+
props: { layout, canAbort },
|
|
112
|
+
slots: {
|
|
113
|
+
content: [createBlockInstance(opts.content)]
|
|
114
|
+
}
|
|
115
|
+
}),
|
|
116
|
+
renderer: () => null
|
|
117
|
+
});
|
|
118
|
+
}
|
|
75
119
|
function isLocalhostOrigin(origin) {
|
|
76
120
|
try {
|
|
77
121
|
const url = new URL(origin);
|
|
@@ -132,7 +176,7 @@ async function loadBlockRegistry(options = {}) {
|
|
|
132
176
|
if ("registerBlocks" in module && typeof module.registerBlocks === "function") {
|
|
133
177
|
module.registerBlocks();
|
|
134
178
|
} else {
|
|
135
|
-
Object.entries(module).forEach(([
|
|
179
|
+
Object.entries(module).forEach(([_2, item]) => {
|
|
136
180
|
if (typeof item === "object" && item != null) {
|
|
137
181
|
registerBlock(item);
|
|
138
182
|
}
|
|
@@ -156,13 +200,43 @@ async function loadBlockRegistry(options = {}) {
|
|
|
156
200
|
function createCustomerService(options) {
|
|
157
201
|
const current = d(null);
|
|
158
202
|
const accountOrdersUrl = d(options.getAccountOrdersUrl());
|
|
159
|
-
|
|
203
|
+
return {
|
|
160
204
|
current,
|
|
161
|
-
accountOrdersUrl
|
|
205
|
+
accountOrdersUrl,
|
|
206
|
+
async getCurrent() {
|
|
207
|
+
const result = await options.adapter.getCurrent();
|
|
208
|
+
if (result._tag === "Success") current.value = result.data;
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
162
211
|
};
|
|
163
|
-
return service;
|
|
164
212
|
}
|
|
165
213
|
const CustomerServiceContext = createContext("services/customer");
|
|
214
|
+
function createMockCustomerAdapter() {
|
|
215
|
+
const customer = { id: "customer-1", name: "Customer 1" };
|
|
216
|
+
return {
|
|
217
|
+
async getCurrent() {
|
|
218
|
+
return { _tag: "Success", data: customer };
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function createApiCustomerAdapter(fetcher) {
|
|
223
|
+
return {
|
|
224
|
+
async getCurrent() {
|
|
225
|
+
try {
|
|
226
|
+
const res = await fetcher("/customer/info");
|
|
227
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
228
|
+
const data = await res.json();
|
|
229
|
+
const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
|
|
230
|
+
return {
|
|
231
|
+
_tag: "Success",
|
|
232
|
+
data: { id: data.id, name, email: data.email }
|
|
233
|
+
};
|
|
234
|
+
} catch (e3) {
|
|
235
|
+
return { _tag: "Failure", error: e3 };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
166
240
|
const PARAMS = {
|
|
167
241
|
DELETGATED_TOKEN: "delegated-token",
|
|
168
242
|
REDIRECT_TO: "redirect-to",
|
|
@@ -176,9 +250,125 @@ const PARAMS = {
|
|
|
176
250
|
function uuid() {
|
|
177
251
|
return ("10000000-1000-4000-8000" + -1e11).replace(
|
|
178
252
|
/[018]/g,
|
|
179
|
-
(
|
|
253
|
+
(c2) => (c2 ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c2 / 4).toString(16)
|
|
180
254
|
);
|
|
181
255
|
}
|
|
256
|
+
function createMockLoginAdapter() {
|
|
257
|
+
return {
|
|
258
|
+
async passwordlessStart() {
|
|
259
|
+
return { _tag: "Success", data: void 0 };
|
|
260
|
+
},
|
|
261
|
+
async authenticate() {
|
|
262
|
+
return {
|
|
263
|
+
_tag: "Success",
|
|
264
|
+
data: {
|
|
265
|
+
access_token: "mock-access-token",
|
|
266
|
+
refresh_token: "mock-refresh-token",
|
|
267
|
+
expires_in: 3600
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
getAuthorizeUrl({ connection, state, shop, appUrl }) {
|
|
272
|
+
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
273
|
+
url.searchParams.set("connection", connection);
|
|
274
|
+
url.searchParams.set("state", state);
|
|
275
|
+
url.searchParams.set("shop", shop);
|
|
276
|
+
return url.toString();
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function createApiLoginAdapter(fetcher, opts) {
|
|
281
|
+
const { shopDomain, appUrl } = opts;
|
|
282
|
+
async function callAuthEndpoint(path, body, lang) {
|
|
283
|
+
const url = lang ? `${path}?lang=${lang}` : path;
|
|
284
|
+
const res = await fetcher(url, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: {
|
|
287
|
+
Accept: "application/json",
|
|
288
|
+
"Content-Type": "application/json",
|
|
289
|
+
"X-Shopify-Shop-Domain": shopDomain
|
|
290
|
+
},
|
|
291
|
+
body: JSON.stringify(body)
|
|
292
|
+
});
|
|
293
|
+
if (res.status === 403) {
|
|
294
|
+
const data = await res.json();
|
|
295
|
+
if ("error_description" in data) {
|
|
296
|
+
if (data.error_description === "Wrong email or verification code.") {
|
|
297
|
+
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
298
|
+
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
299
|
+
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
300
|
+
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
301
|
+
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
302
|
+
}
|
|
303
|
+
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
304
|
+
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
305
|
+
connection: data.connection
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (!res.ok) {
|
|
310
|
+
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
311
|
+
}
|
|
312
|
+
return { _tag: "Success", data: void 0 };
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
async passwordlessStart({ email, lang }) {
|
|
316
|
+
try {
|
|
317
|
+
await callAuthEndpoint(
|
|
318
|
+
"/auth/customer/passwordless/start",
|
|
319
|
+
{ email },
|
|
320
|
+
lang
|
|
321
|
+
);
|
|
322
|
+
return { _tag: "Success", data: void 0 };
|
|
323
|
+
} catch (e3) {
|
|
324
|
+
return { _tag: "Failure", error: e3 };
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
async authenticate(params) {
|
|
328
|
+
try {
|
|
329
|
+
const res = await fetcher("/auth/customer/token", {
|
|
330
|
+
method: "POST",
|
|
331
|
+
headers: {
|
|
332
|
+
Accept: "application/json",
|
|
333
|
+
"Content-Type": "application/json",
|
|
334
|
+
"X-Shopify-Shop-Domain": shopDomain
|
|
335
|
+
},
|
|
336
|
+
body: JSON.stringify(params)
|
|
337
|
+
});
|
|
338
|
+
if (res.status === 403) {
|
|
339
|
+
const data = await res.json();
|
|
340
|
+
if ("error_description" in data) {
|
|
341
|
+
if (data.error_description === "Wrong email or verification code.") {
|
|
342
|
+
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
343
|
+
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
344
|
+
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
345
|
+
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
346
|
+
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
347
|
+
}
|
|
348
|
+
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
349
|
+
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
350
|
+
connection: data.connection
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (!res.ok) {
|
|
355
|
+
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
356
|
+
}
|
|
357
|
+
return { _tag: "Success", data: await res.json() };
|
|
358
|
+
} catch (e3) {
|
|
359
|
+
return { _tag: "Failure", error: e3 };
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
getAuthorizeUrl({ connection, state, shop }) {
|
|
363
|
+
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
364
|
+
url.searchParams.set("connection", connection);
|
|
365
|
+
url.searchParams.set("state", state);
|
|
366
|
+
url.searchParams.set("shop", shop);
|
|
367
|
+
return url.toString();
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const __vite_import_meta_env__ = {};
|
|
182
372
|
var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
|
|
183
373
|
AuthenticationErrorReason2["Expired"] = "expired";
|
|
184
374
|
AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
|
|
@@ -197,6 +387,9 @@ class AuthenticationError extends Error {
|
|
|
197
387
|
this.data = data ?? {};
|
|
198
388
|
}
|
|
199
389
|
}
|
|
390
|
+
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
391
|
+
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
392
|
+
const STORAGE_STATE_KEY = "state";
|
|
200
393
|
function storage(key, value) {
|
|
201
394
|
const storageKey = `auth:${key}`;
|
|
202
395
|
try {
|
|
@@ -212,83 +405,33 @@ function storage(key, value) {
|
|
|
212
405
|
}
|
|
213
406
|
return null;
|
|
214
407
|
}
|
|
215
|
-
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
216
|
-
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
217
|
-
const STORAGE_STATE_KEY = "state";
|
|
218
|
-
function createAuthFunctions(basePath, shop) {
|
|
219
|
-
async function start(email, lang) {
|
|
220
|
-
const response = await fetch(
|
|
221
|
-
`${basePath}/auth/customer/passwordless/start?lang=${lang}`,
|
|
222
|
-
{
|
|
223
|
-
method: "POST",
|
|
224
|
-
headers: {
|
|
225
|
-
Accept: "application/json",
|
|
226
|
-
"Content-Type": "application/json",
|
|
227
|
-
"X-Shopify-Shop-Domain": shop
|
|
228
|
-
},
|
|
229
|
-
body: JSON.stringify({ email })
|
|
230
|
-
}
|
|
231
|
-
);
|
|
232
|
-
if (response.status !== 200) {
|
|
233
|
-
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
async function authenticate(data) {
|
|
237
|
-
const response = await fetch(`${basePath}/auth/customer/token`, {
|
|
238
|
-
method: "POST",
|
|
239
|
-
headers: {
|
|
240
|
-
Accept: "application/json",
|
|
241
|
-
"Content-Type": "application/json",
|
|
242
|
-
"X-Shopify-Shop-Domain": shop
|
|
243
|
-
},
|
|
244
|
-
body: JSON.stringify(data)
|
|
245
|
-
});
|
|
246
|
-
if (response.status === 403) {
|
|
247
|
-
const data2 = await response.json();
|
|
248
|
-
if ("error_description" in data2) {
|
|
249
|
-
if (data2.error_description === "Wrong email or verification code.") {
|
|
250
|
-
throw new AuthenticationError(
|
|
251
|
-
"wrong-code"
|
|
252
|
-
/* IncorrectCode */
|
|
253
|
-
);
|
|
254
|
-
} else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
255
|
-
throw new AuthenticationError(
|
|
256
|
-
"too-many-tries"
|
|
257
|
-
/* TooManyTries */
|
|
258
|
-
);
|
|
259
|
-
} else if (data2.error_description === "The verification code has expired. Please try to login again.") {
|
|
260
|
-
throw new AuthenticationError(
|
|
261
|
-
"expired"
|
|
262
|
-
/* Expired */
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
} else if ("code" in data2) {
|
|
266
|
-
if (data2.code === "AUTH_ERROR") {
|
|
267
|
-
throw new AuthenticationError("no-email", {
|
|
268
|
-
connection: data2.connection
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (response.status !== 200) {
|
|
274
|
-
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
275
|
-
}
|
|
276
|
-
return await response.json();
|
|
277
|
-
}
|
|
278
|
-
return { start, authenticate };
|
|
279
|
-
}
|
|
280
408
|
function nonce(len = 10) {
|
|
281
409
|
return uuid().replace(/-/g, "").slice(0, len);
|
|
282
410
|
}
|
|
283
|
-
function
|
|
284
|
-
const
|
|
411
|
+
function loginService(deps) {
|
|
412
|
+
const apiBase = deps.shop.rootUrl.replace(/\/$/, "") + "/api";
|
|
413
|
+
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";
|
|
414
|
+
const fetcher = (path, init) => fetch(`${apiBase}${path}`, init);
|
|
415
|
+
const adapter = createApiLoginAdapter(fetcher, {
|
|
416
|
+
shopDomain: deps.shop.domain,
|
|
417
|
+
appUrl
|
|
418
|
+
});
|
|
419
|
+
return createLoginService({
|
|
420
|
+
adapter,
|
|
421
|
+
router: deps.router,
|
|
422
|
+
formatError: deps.formatError,
|
|
423
|
+
shop: {
|
|
424
|
+
domain: deps.shop.domain,
|
|
425
|
+
locale: w(() => deps.getLocale())
|
|
426
|
+
},
|
|
427
|
+
onLoginSuccess: deps.onLoginSuccess
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
function createLoginService(init) {
|
|
431
|
+
const { adapter, formatError, router, shop, onLoginSuccess } = init;
|
|
285
432
|
const accessToken = d(null);
|
|
286
|
-
const delegatedToken = d(
|
|
287
|
-
|
|
288
|
-
);
|
|
289
|
-
const refreshToken = d(
|
|
290
|
-
storage(STORAGE_REFRESH_TOKEN_KEY)
|
|
291
|
-
);
|
|
433
|
+
const delegatedToken = d(storage(STORAGE_DELEGATED_TOKEN));
|
|
434
|
+
const refreshToken = d(storage(STORAGE_REFRESH_TOKEN_KEY));
|
|
292
435
|
const expires = d(null);
|
|
293
436
|
const state = d(storage(STORAGE_STATE_KEY));
|
|
294
437
|
E(() => {
|
|
@@ -300,67 +443,28 @@ function createAuthService(apiUrl, shopDomain) {
|
|
|
300
443
|
E(() => {
|
|
301
444
|
storage(STORAGE_STATE_KEY, state.value);
|
|
302
445
|
});
|
|
303
|
-
function
|
|
446
|
+
function storeTokens(data) {
|
|
304
447
|
const { access_token, refresh_token, expires_in } = data;
|
|
305
448
|
accessToken.value = access_token;
|
|
306
449
|
refreshToken.value = refresh_token;
|
|
307
450
|
expires.value = new Date(Date.now() + expires_in);
|
|
308
451
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
},
|
|
317
|
-
getDelegatedToken() {
|
|
318
|
-
return delegatedToken.value;
|
|
319
|
-
},
|
|
320
|
-
setDelegatedToken(token) {
|
|
321
|
-
delegatedToken.value = token;
|
|
322
|
-
},
|
|
323
|
-
async getToken() {
|
|
324
|
-
if (refreshToken.value == null) {
|
|
325
|
-
return null;
|
|
326
|
-
}
|
|
327
|
-
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
328
|
-
accessToken.value = new Promise(
|
|
329
|
-
(resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
|
|
330
|
-
store(data);
|
|
331
|
-
resolve(data.access_token);
|
|
452
|
+
async function getToken() {
|
|
453
|
+
if (refreshToken.value == null) return null;
|
|
454
|
+
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
455
|
+
accessToken.value = new Promise(
|
|
456
|
+
(resolve, reject) => adapter.authenticate({ refreshToken: refreshToken.value }).then((result) => {
|
|
457
|
+
if (result._tag === "Failure") {
|
|
458
|
+
reject(result.error);
|
|
332
459
|
return;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
refreshToken.value = null;
|
|
342
|
-
expires.value = null;
|
|
343
|
-
},
|
|
344
|
-
isAuthenticated: w(
|
|
345
|
-
() => delegatedToken.value != null || refreshToken.value != null
|
|
346
|
-
)
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
function loginService(deps) {
|
|
350
|
-
return createLoginService({
|
|
351
|
-
apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
|
|
352
|
-
router: deps.router,
|
|
353
|
-
formatError: deps.formatError,
|
|
354
|
-
shop: {
|
|
355
|
-
domain: deps.shop.domain,
|
|
356
|
-
locale: w(() => deps.getLocale())
|
|
357
|
-
},
|
|
358
|
-
onLoginSuccess: deps.onLoginSuccess
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
function createLoginService(init) {
|
|
362
|
-
const { formatError, router, shop, onLoginSuccess } = init;
|
|
363
|
-
const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
|
|
460
|
+
}
|
|
461
|
+
storeTokens(result.data);
|
|
462
|
+
resolve(result.data.access_token);
|
|
463
|
+
}).catch(reject)
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
return Promise.resolve(accessToken.value);
|
|
467
|
+
}
|
|
364
468
|
const userEmail = d(null);
|
|
365
469
|
async function onSuccess(query) {
|
|
366
470
|
await (onLoginSuccess == null ? void 0 : onLoginSuccess());
|
|
@@ -373,13 +477,9 @@ function createLoginService(init) {
|
|
|
373
477
|
queryParams[key] = value;
|
|
374
478
|
}
|
|
375
479
|
});
|
|
376
|
-
router.push({
|
|
377
|
-
path: decodeURIComponent(redirect),
|
|
378
|
-
query: queryParams
|
|
379
|
-
});
|
|
480
|
+
router.push({ path: decodeURIComponent(redirect), query: queryParams });
|
|
380
481
|
} else {
|
|
381
|
-
|
|
382
|
-
router.replace(decodeURIComponent(redirect));
|
|
482
|
+
router.replace(decodeURIComponent(query.get(PARAMS.REDIRECT_TO)));
|
|
383
483
|
}
|
|
384
484
|
} else {
|
|
385
485
|
router.replace("/");
|
|
@@ -391,16 +491,17 @@ function createLoginService(init) {
|
|
|
391
491
|
async start(email) {
|
|
392
492
|
try {
|
|
393
493
|
userEmail.value = email;
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
494
|
+
const lang = shop.locale instanceof u ? shop.locale.value : shop.locale;
|
|
495
|
+
const result = await adapter.passwordlessStart({
|
|
496
|
+
email: userEmail.value,
|
|
497
|
+
lang
|
|
498
|
+
});
|
|
499
|
+
if (result._tag === "Failure") {
|
|
500
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
501
|
+
}
|
|
398
502
|
return { _tag: "Success", data: void 0 };
|
|
399
503
|
} catch (error) {
|
|
400
|
-
return {
|
|
401
|
-
_tag: "Failure",
|
|
402
|
-
error: formatError(error)
|
|
403
|
-
};
|
|
504
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
404
505
|
}
|
|
405
506
|
},
|
|
406
507
|
async verify(email, code) {
|
|
@@ -411,63 +512,61 @@ function createLoginService(init) {
|
|
|
411
512
|
};
|
|
412
513
|
}
|
|
413
514
|
try {
|
|
414
|
-
const result = await
|
|
515
|
+
const result = await adapter.authenticate({
|
|
415
516
|
email: userEmail.value,
|
|
416
517
|
code
|
|
417
518
|
});
|
|
519
|
+
if (result._tag === "Failure") {
|
|
520
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
521
|
+
}
|
|
522
|
+
storeTokens(result.data);
|
|
418
523
|
await onSuccess();
|
|
419
|
-
return { _tag: "Success", data:
|
|
524
|
+
return { _tag: "Success", data: void 0 };
|
|
420
525
|
} catch (error) {
|
|
421
|
-
return {
|
|
422
|
-
_tag: "Failure",
|
|
423
|
-
error: formatError(error)
|
|
424
|
-
};
|
|
526
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
425
527
|
}
|
|
426
528
|
}
|
|
427
529
|
}
|
|
428
530
|
},
|
|
429
531
|
social: {
|
|
430
532
|
async start(provider) {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
provider === "Facebook" ? "facebook" : "google-oauth2",
|
|
444
|
-
shop.domain
|
|
445
|
-
);
|
|
446
|
-
return {
|
|
447
|
-
_tag: "Success",
|
|
448
|
-
data: void 0
|
|
449
|
-
};
|
|
533
|
+
state.value = nonce();
|
|
534
|
+
const connection = provider === "Facebook" ? "facebook" : "google-oauth2";
|
|
535
|
+
window.location.href = adapter.getAuthorizeUrl({
|
|
536
|
+
connection,
|
|
537
|
+
state: state.value ?? "",
|
|
538
|
+
shop: shop.domain,
|
|
539
|
+
appUrl: (
|
|
540
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
541
|
+
(typeof import.meta !== "undefined" ? __vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_APP_URL : void 0) ?? "https://admin.juo.io"
|
|
542
|
+
)
|
|
543
|
+
});
|
|
544
|
+
return { _tag: "Success", data: void 0 };
|
|
450
545
|
},
|
|
451
546
|
async verify(code) {
|
|
452
547
|
try {
|
|
453
|
-
const result = await
|
|
548
|
+
const result = await adapter.authenticate({ code });
|
|
549
|
+
if (result._tag === "Failure") {
|
|
550
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
551
|
+
}
|
|
552
|
+
storeTokens(result.data);
|
|
454
553
|
await onSuccess();
|
|
455
|
-
return { _tag: "Success", data:
|
|
554
|
+
return { _tag: "Success", data: void 0 };
|
|
456
555
|
} catch (error) {
|
|
457
|
-
return {
|
|
458
|
-
_tag: "Failure",
|
|
459
|
-
error: formatError(error)
|
|
460
|
-
};
|
|
556
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
461
557
|
}
|
|
462
558
|
}
|
|
463
559
|
},
|
|
464
560
|
async logout() {
|
|
465
|
-
|
|
561
|
+
accessToken.value = null;
|
|
562
|
+
delegatedToken.value = null;
|
|
563
|
+
refreshToken.value = null;
|
|
564
|
+
expires.value = null;
|
|
466
565
|
return { _tag: "Success", data: void 0 };
|
|
467
566
|
},
|
|
468
567
|
async handleQueryParams(query) {
|
|
469
568
|
if (query.has(PARAMS.DELETGATED_TOKEN)) {
|
|
470
|
-
|
|
569
|
+
delegatedToken.value = query.get(PARAMS.DELETGATED_TOKEN);
|
|
471
570
|
await onSuccess(query);
|
|
472
571
|
return { _tag: "Success", data: void 0 };
|
|
473
572
|
}
|
|
@@ -479,16 +578,13 @@ function createLoginService(init) {
|
|
|
479
578
|
}
|
|
480
579
|
const code = query.get(PARAMS.CODE);
|
|
481
580
|
if (code != null) {
|
|
482
|
-
if (query.get(PARAMS.STATE) !==
|
|
483
|
-
return {
|
|
484
|
-
_tag: "Failure",
|
|
485
|
-
error: formatError("Invalid state")
|
|
486
|
-
};
|
|
581
|
+
if (query.get(PARAMS.STATE) !== state.value) {
|
|
582
|
+
return { _tag: "Failure", error: formatError("Invalid state") };
|
|
487
583
|
} else {
|
|
488
584
|
return service.social.verify(code);
|
|
489
585
|
}
|
|
490
586
|
}
|
|
491
|
-
if (query.get(PARAMS.REFRESH) === "1" && await
|
|
587
|
+
if (query.get(PARAMS.REFRESH) === "1" && await getToken() != null) {
|
|
492
588
|
await onSuccess();
|
|
493
589
|
return { _tag: "Success", data: void 0 };
|
|
494
590
|
}
|
|
@@ -505,17 +601,75 @@ function createLoginService(init) {
|
|
|
505
601
|
}
|
|
506
602
|
return { _tag: "Success", data: void 0 };
|
|
507
603
|
},
|
|
508
|
-
isAuthenticated:
|
|
509
|
-
|
|
604
|
+
isAuthenticated: w(
|
|
605
|
+
() => delegatedToken.value != null || refreshToken.value != null
|
|
606
|
+
),
|
|
607
|
+
getToken,
|
|
608
|
+
async getAuthHeaders() {
|
|
609
|
+
if (delegatedToken.value) {
|
|
610
|
+
return { "X-Delegated-Token": delegatedToken.value };
|
|
611
|
+
}
|
|
612
|
+
const token = await getToken();
|
|
613
|
+
if (token) {
|
|
614
|
+
return { Authorization: `Bearer ${token}` };
|
|
615
|
+
}
|
|
616
|
+
return {};
|
|
617
|
+
}
|
|
510
618
|
};
|
|
511
619
|
return service;
|
|
512
620
|
}
|
|
513
621
|
const LoginServiceContext = createContext("services/login");
|
|
514
622
|
function createMockOrdersAdapter() {
|
|
623
|
+
const mockOrders = [
|
|
624
|
+
{
|
|
625
|
+
id: "10001",
|
|
626
|
+
createdAt: "2024-10-15T09:30:00Z",
|
|
627
|
+
placedAt: "2024-10-15T09:30:00Z",
|
|
628
|
+
currencyCode: "USD",
|
|
629
|
+
totalPrice: 89.97,
|
|
630
|
+
financialStatus: "PAID",
|
|
631
|
+
fulfillmentStatus: "FULFILLED",
|
|
632
|
+
lines: [
|
|
633
|
+
{
|
|
634
|
+
id: "line_1",
|
|
635
|
+
title: "Monthly Coffee Subscription",
|
|
636
|
+
quantity: 1,
|
|
637
|
+
variantImage: null,
|
|
638
|
+
customAttributes: [
|
|
639
|
+
{ key: "roast", value: "dark" },
|
|
640
|
+
{ key: "size", value: "1lb" }
|
|
641
|
+
]
|
|
642
|
+
}
|
|
643
|
+
],
|
|
644
|
+
customAttributes: [
|
|
645
|
+
{ key: "order_type", value: "subscription_renewal" },
|
|
646
|
+
{ key: "gift_wrap", value: "false" }
|
|
647
|
+
]
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
id: "10002",
|
|
651
|
+
createdAt: "2024-09-20T14:15:00Z",
|
|
652
|
+
placedAt: "2024-09-20T14:15:00Z",
|
|
653
|
+
currencyCode: "USD",
|
|
654
|
+
totalPrice: 45.99,
|
|
655
|
+
financialStatus: "PAID",
|
|
656
|
+
fulfillmentStatus: "PARTIALLY_FULFILLED",
|
|
657
|
+
lines: [
|
|
658
|
+
{
|
|
659
|
+
id: "line_2",
|
|
660
|
+
title: "Premium Tea Set",
|
|
661
|
+
quantity: 2,
|
|
662
|
+
variantImage: null,
|
|
663
|
+
customAttributes: []
|
|
664
|
+
}
|
|
665
|
+
],
|
|
666
|
+
customAttributes: []
|
|
667
|
+
}
|
|
668
|
+
];
|
|
515
669
|
return {
|
|
516
670
|
async getOrders() {
|
|
517
671
|
return {
|
|
518
|
-
items:
|
|
672
|
+
items: mockOrders,
|
|
519
673
|
pageInfo: {
|
|
520
674
|
hasNextPage: false,
|
|
521
675
|
hasPrevPage: false,
|
|
@@ -679,6 +833,18 @@ function createMockProductAdapter() {
|
|
|
679
833
|
);
|
|
680
834
|
return product == null ? null : processProduct(product);
|
|
681
835
|
},
|
|
836
|
+
async getProductsByIds() {
|
|
837
|
+
return [];
|
|
838
|
+
},
|
|
839
|
+
async getBoxVariants() {
|
|
840
|
+
return [];
|
|
841
|
+
},
|
|
842
|
+
async getAvailableProducts() {
|
|
843
|
+
return [];
|
|
844
|
+
},
|
|
845
|
+
async getBox() {
|
|
846
|
+
return null;
|
|
847
|
+
},
|
|
682
848
|
async search(opts) {
|
|
683
849
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
684
850
|
let filteredProducts = [...products];
|
|
@@ -719,6 +885,185 @@ function createMockProductAdapter() {
|
|
|
719
885
|
}
|
|
720
886
|
};
|
|
721
887
|
}
|
|
888
|
+
function mapVariant(variant, currencyCode) {
|
|
889
|
+
var _a;
|
|
890
|
+
return {
|
|
891
|
+
id: variant.id,
|
|
892
|
+
title: variant.title,
|
|
893
|
+
image: variant.image == null ? null : { url: variant.image.url, altText: variant.image.altText ?? void 0 },
|
|
894
|
+
price: { amount: variant.price.toFixed(2), currencyCode },
|
|
895
|
+
...((_a = variant.purchaseOptions) == null ? void 0 : _a.length) ? {
|
|
896
|
+
purchaseOptions: variant.purchaseOptions.map((opt) => ({
|
|
897
|
+
name: opt.name,
|
|
898
|
+
price: { amount: opt.price.toFixed(2), currencyCode },
|
|
899
|
+
interval: opt.interval
|
|
900
|
+
}))
|
|
901
|
+
} : {}
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
function mapProduct(product, currencyCode) {
|
|
905
|
+
return {
|
|
906
|
+
id: product.id,
|
|
907
|
+
title: product.title,
|
|
908
|
+
featuredImage: product.image == null ? null : { url: product.image.url, altText: product.image.altText ?? void 0 },
|
|
909
|
+
hasOnlyDefaultVariant: product.variants.length <= 1,
|
|
910
|
+
variants: {
|
|
911
|
+
items: product.variants.map((v2) => mapVariant(v2, currencyCode)),
|
|
912
|
+
pageInfo: {
|
|
913
|
+
hasNextPage: false,
|
|
914
|
+
hasPrevPage: false,
|
|
915
|
+
startCursor: null,
|
|
916
|
+
endCursor: null
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
collections: product.collections
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
function createApiProductAdapter(fetcher, opts = { currencyCode: "USD" }) {
|
|
923
|
+
const { currencyCode } = opts;
|
|
924
|
+
async function fetchProducts(params) {
|
|
925
|
+
const query = params.toString() ? `?${params.toString()}` : "";
|
|
926
|
+
const res = await fetcher(`/customer/v1/products${query}`);
|
|
927
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
928
|
+
return res.json();
|
|
929
|
+
}
|
|
930
|
+
return {
|
|
931
|
+
async getById(id) {
|
|
932
|
+
try {
|
|
933
|
+
const params = new URLSearchParams({ query: `id:"${id}"`, limit: "1" });
|
|
934
|
+
const body = await fetchProducts(params);
|
|
935
|
+
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
936
|
+
} catch {
|
|
937
|
+
return null;
|
|
938
|
+
}
|
|
939
|
+
},
|
|
940
|
+
async getByVariantId(variantId) {
|
|
941
|
+
try {
|
|
942
|
+
const params = new URLSearchParams({
|
|
943
|
+
query: `variants.id:"${variantId}"`,
|
|
944
|
+
limit: "1"
|
|
945
|
+
});
|
|
946
|
+
const body = await fetchProducts(params);
|
|
947
|
+
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
948
|
+
} catch {
|
|
949
|
+
return null;
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
async search(opts2) {
|
|
953
|
+
try {
|
|
954
|
+
const params = new URLSearchParams();
|
|
955
|
+
if (opts2 == null ? void 0 : opts2.query) {
|
|
956
|
+
params.set("query", `"${opts2.query.trim().replace(/"/g, '\\"')}"`);
|
|
957
|
+
}
|
|
958
|
+
if (opts2 == null ? void 0 : opts2.pagination) {
|
|
959
|
+
if ("after" in opts2.pagination)
|
|
960
|
+
params.set("after", opts2.pagination.after);
|
|
961
|
+
else if ("before" in opts2.pagination)
|
|
962
|
+
params.set("before", opts2.pagination.before);
|
|
963
|
+
}
|
|
964
|
+
const body = await fetchProducts(params);
|
|
965
|
+
return {
|
|
966
|
+
items: body.data.map((p2) => mapProduct(p2, currencyCode)),
|
|
967
|
+
pageInfo: {
|
|
968
|
+
hasNextPage: body.hasNextPage,
|
|
969
|
+
hasPrevPage: body.hasPrevPage,
|
|
970
|
+
startCursor: body.startCursor,
|
|
971
|
+
endCursor: body.endCursor
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
} catch {
|
|
975
|
+
return {
|
|
976
|
+
items: [],
|
|
977
|
+
pageInfo: {
|
|
978
|
+
hasNextPage: false,
|
|
979
|
+
hasPrevPage: false,
|
|
980
|
+
startCursor: null,
|
|
981
|
+
endCursor: null
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
},
|
|
986
|
+
async getProductsByIds({ ids }) {
|
|
987
|
+
const params = new URLSearchParams();
|
|
988
|
+
for (const id of ids) params.append("ids", id);
|
|
989
|
+
const res = await fetcher(`/customer/products/by-ids?${params.toString()}`);
|
|
990
|
+
if (!res.ok) return [];
|
|
991
|
+
const data = await res.json();
|
|
992
|
+
return (data ?? []).map((p2) => ({
|
|
993
|
+
id: p2.id,
|
|
994
|
+
title: p2.title ?? "",
|
|
995
|
+
featuredImage: p2.featuredImage ? { url: p2.featuredImage.url, altText: p2.featuredImage.altText } : null,
|
|
996
|
+
hasOnlyDefaultVariant: true,
|
|
997
|
+
collections: []
|
|
998
|
+
}));
|
|
999
|
+
},
|
|
1000
|
+
async getBoxVariants({ ids, subscriptionId }) {
|
|
1001
|
+
const params = new URLSearchParams();
|
|
1002
|
+
for (const id of ids) params.append("ids", id);
|
|
1003
|
+
params.set("subscriptionId", subscriptionId);
|
|
1004
|
+
const res = await fetcher(`/customer/products/variants?${params.toString()}`);
|
|
1005
|
+
if (!res.ok) return [];
|
|
1006
|
+
const data = await res.json();
|
|
1007
|
+
return (data ?? []).map((p2) => {
|
|
1008
|
+
var _a;
|
|
1009
|
+
return {
|
|
1010
|
+
id: p2.id,
|
|
1011
|
+
title: p2.title,
|
|
1012
|
+
description: p2.description ?? "",
|
|
1013
|
+
featuredImage: p2.featuredImage,
|
|
1014
|
+
hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
|
|
1015
|
+
variants: (p2.variants ?? []).map((v2) => ({
|
|
1016
|
+
id: v2.id,
|
|
1017
|
+
availableForSale: v2.availableForSale,
|
|
1018
|
+
title: v2.title,
|
|
1019
|
+
currentPrice: v2.currentPrice,
|
|
1020
|
+
basePrice: v2.basePrice,
|
|
1021
|
+
image: v2.image,
|
|
1022
|
+
recurring: v2.recurring
|
|
1023
|
+
}))
|
|
1024
|
+
};
|
|
1025
|
+
});
|
|
1026
|
+
},
|
|
1027
|
+
async getAvailableProducts({ subscriptionId, lineId, q: q2, mode, action, pagination }) {
|
|
1028
|
+
const params = new URLSearchParams();
|
|
1029
|
+
params.set("action", action);
|
|
1030
|
+
if (q2) params.set("q", q2);
|
|
1031
|
+
if (mode) params.set("mode", mode);
|
|
1032
|
+
if (pagination && "after" in pagination) params.set("after", pagination.after);
|
|
1033
|
+
if (pagination && "before" in pagination) params.set("before", pagination.before);
|
|
1034
|
+
const res = await fetcher(
|
|
1035
|
+
`/customer/subscriptions/${subscriptionId}/lines/${lineId}/available-products?${params.toString()}`
|
|
1036
|
+
);
|
|
1037
|
+
if (!res.ok) return [];
|
|
1038
|
+
const data = await res.json();
|
|
1039
|
+
return (data ?? []).map((p2) => {
|
|
1040
|
+
var _a;
|
|
1041
|
+
return {
|
|
1042
|
+
id: p2.id,
|
|
1043
|
+
title: p2.title,
|
|
1044
|
+
description: p2.description ?? "",
|
|
1045
|
+
featuredImage: p2.featuredImage,
|
|
1046
|
+
hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
|
|
1047
|
+
variants: (p2.variants ?? []).map((v2) => ({
|
|
1048
|
+
id: v2.id,
|
|
1049
|
+
availableForSale: v2.availableForSale,
|
|
1050
|
+
title: v2.title,
|
|
1051
|
+
currentPrice: v2.currentPrice,
|
|
1052
|
+
basePrice: v2.basePrice,
|
|
1053
|
+
image: v2.image,
|
|
1054
|
+
recurring: v2.recurring
|
|
1055
|
+
}))
|
|
1056
|
+
};
|
|
1057
|
+
});
|
|
1058
|
+
},
|
|
1059
|
+
async getBox({ id }) {
|
|
1060
|
+
const res = await fetcher(`/customer/boxes/${id}`);
|
|
1061
|
+
if (!res.ok) return null;
|
|
1062
|
+
const data = await res.json();
|
|
1063
|
+
return { id, ...data };
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
722
1067
|
const ProductServiceContext = createContext("services/product");
|
|
723
1068
|
function createProductService(adapter) {
|
|
724
1069
|
return {
|
|
@@ -730,7 +1075,11 @@ function createProductService(adapter) {
|
|
|
730
1075
|
},
|
|
731
1076
|
async search(opts) {
|
|
732
1077
|
return adapter.search(opts);
|
|
733
|
-
}
|
|
1078
|
+
},
|
|
1079
|
+
getProductsByIds: (ids) => adapter.getProductsByIds({ ids }),
|
|
1080
|
+
getBoxVariants: (ids, subscriptionId) => adapter.getBoxVariants({ ids, subscriptionId }),
|
|
1081
|
+
getAvailableProducts: (params) => adapter.getAvailableProducts(params),
|
|
1082
|
+
getBox: (id) => adapter.getBox({ id })
|
|
734
1083
|
};
|
|
735
1084
|
}
|
|
736
1085
|
function routeNameTableFromPages(pages) {
|
|
@@ -784,19 +1133,428 @@ function createMockRouterService() {
|
|
|
784
1133
|
}
|
|
785
1134
|
}).routerService;
|
|
786
1135
|
}
|
|
787
|
-
function
|
|
788
|
-
const
|
|
1136
|
+
function futureDateStr(daysFromNow) {
|
|
1137
|
+
const d3 = /* @__PURE__ */ new Date();
|
|
1138
|
+
d3.setDate(d3.getDate() + daysFromNow);
|
|
1139
|
+
return d3.toISOString().split("T")[0] ?? "";
|
|
1140
|
+
}
|
|
1141
|
+
function createMockSchedulesAdapter() {
|
|
1142
|
+
let orders = [
|
|
789
1143
|
{
|
|
790
|
-
id: "
|
|
1144
|
+
id: "sched_1",
|
|
791
1145
|
customerId: "customer_1",
|
|
1146
|
+
subscriptionId: "sub_1",
|
|
1147
|
+
date: "2026-05-22T12:00:00Z",
|
|
1148
|
+
skipped: false,
|
|
1149
|
+
currencyCode: "USD",
|
|
1150
|
+
subtotal: 2999,
|
|
1151
|
+
deliveryPrice: 500,
|
|
1152
|
+
cycleIndex: 4,
|
|
1153
|
+
deliveryDate: futureDateStr(7),
|
|
1154
|
+
status: "scheduled",
|
|
792
1155
|
items: [
|
|
793
1156
|
{
|
|
794
|
-
id: "
|
|
795
|
-
|
|
1157
|
+
id: "sched_item_1",
|
|
1158
|
+
title: "Rose Face Serum",
|
|
1159
|
+
subtitle: null,
|
|
1160
|
+
imageUrl: "https://images.unsplash.com/photo-1611930022073-b7a4ba5fcccd?w=200",
|
|
796
1161
|
variantId: "variant_1",
|
|
797
|
-
quantity:
|
|
798
|
-
price:
|
|
799
|
-
|
|
1162
|
+
quantity: 1,
|
|
1163
|
+
price: 2999,
|
|
1164
|
+
totalPrice: 2999,
|
|
1165
|
+
variantImage: null,
|
|
1166
|
+
variantAvailableForSale: true,
|
|
1167
|
+
subscriptionId: "sub_1",
|
|
1168
|
+
subscriptionLineId: "item_1",
|
|
1169
|
+
productId: "product_1",
|
|
1170
|
+
parentProductId: null,
|
|
1171
|
+
parentLineId: null,
|
|
1172
|
+
isVirtual: false,
|
|
1173
|
+
isUpsell: false,
|
|
1174
|
+
isOneTime: false,
|
|
1175
|
+
customAttributes: []
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
id: "sched_item_2",
|
|
1179
|
+
title: "Vitamin C Moisturizer",
|
|
1180
|
+
subtitle: null,
|
|
1181
|
+
imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
|
|
1182
|
+
variantId: "variant_2",
|
|
1183
|
+
quantity: 1,
|
|
1184
|
+
price: 2999,
|
|
1185
|
+
totalPrice: 2999,
|
|
1186
|
+
variantImage: null,
|
|
1187
|
+
variantAvailableForSale: true,
|
|
1188
|
+
subscriptionId: "sub_1",
|
|
1189
|
+
subscriptionLineId: "item_2",
|
|
1190
|
+
productId: "product_2",
|
|
1191
|
+
parentProductId: null,
|
|
1192
|
+
parentLineId: null,
|
|
1193
|
+
isVirtual: false,
|
|
1194
|
+
isUpsell: false,
|
|
1195
|
+
isOneTime: false,
|
|
1196
|
+
customAttributes: []
|
|
1197
|
+
}
|
|
1198
|
+
],
|
|
1199
|
+
discounts: []
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
id: "sched_2",
|
|
1203
|
+
customerId: "customer_1",
|
|
1204
|
+
subscriptionId: "sub_1",
|
|
1205
|
+
date: "2026-05-22T12:00:00Z",
|
|
1206
|
+
skipped: false,
|
|
1207
|
+
currencyCode: "USD",
|
|
1208
|
+
subtotal: 2999,
|
|
1209
|
+
deliveryPrice: 500,
|
|
1210
|
+
cycleIndex: 4,
|
|
1211
|
+
deliveryDate: futureDateStr(7),
|
|
1212
|
+
status: "scheduled",
|
|
1213
|
+
items: [
|
|
1214
|
+
{
|
|
1215
|
+
id: "sched_item_3",
|
|
1216
|
+
title: "Rose Face Serum",
|
|
1217
|
+
subtitle: null,
|
|
1218
|
+
imageUrl: "https://images.unsplash.com/photo-1611930022073-b7a4ba5fcccd?w=200",
|
|
1219
|
+
variantId: "variant_1",
|
|
1220
|
+
quantity: 1,
|
|
1221
|
+
price: 2999,
|
|
1222
|
+
totalPrice: 2999,
|
|
1223
|
+
variantImage: null,
|
|
1224
|
+
variantAvailableForSale: true,
|
|
1225
|
+
subscriptionId: "sub_1",
|
|
1226
|
+
subscriptionLineId: "item_1",
|
|
1227
|
+
productId: "product_1",
|
|
1228
|
+
parentProductId: null,
|
|
1229
|
+
parentLineId: null,
|
|
1230
|
+
isVirtual: false,
|
|
1231
|
+
isUpsell: false,
|
|
1232
|
+
isOneTime: false,
|
|
1233
|
+
customAttributes: []
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
id: "sched_item_4",
|
|
1237
|
+
title: "Hyaluronic Acid Serum",
|
|
1238
|
+
subtitle: null,
|
|
1239
|
+
imageUrl: "https://images.unsplash.com/photo-1621232201696-ba6c9db10a97?w=200",
|
|
1240
|
+
variantId: "variant_3",
|
|
1241
|
+
quantity: 1,
|
|
1242
|
+
price: 2999,
|
|
1243
|
+
totalPrice: 2999,
|
|
1244
|
+
variantImage: null,
|
|
1245
|
+
variantAvailableForSale: true,
|
|
1246
|
+
subscriptionId: "sub_1",
|
|
1247
|
+
subscriptionLineId: "item_3",
|
|
1248
|
+
productId: "product_3",
|
|
1249
|
+
parentProductId: null,
|
|
1250
|
+
parentLineId: null,
|
|
1251
|
+
isVirtual: false,
|
|
1252
|
+
isUpsell: false,
|
|
1253
|
+
isOneTime: false,
|
|
1254
|
+
customAttributes: []
|
|
1255
|
+
}
|
|
1256
|
+
],
|
|
1257
|
+
discounts: []
|
|
1258
|
+
}
|
|
1259
|
+
];
|
|
1260
|
+
let adjustments = [];
|
|
1261
|
+
return {
|
|
1262
|
+
async getUpcomingOrders(params) {
|
|
1263
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1264
|
+
const filtered = params.subscriptionId ? orders.filter((o2) => o2.subscriptionId === params.subscriptionId) : orders;
|
|
1265
|
+
return { _tag: "Success", data: filtered.slice(0, params.count) };
|
|
1266
|
+
},
|
|
1267
|
+
async getAdjustments() {
|
|
1268
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1269
|
+
return { _tag: "Success", data: [...adjustments] };
|
|
1270
|
+
},
|
|
1271
|
+
async skipOrder({ subscriptionId }) {
|
|
1272
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
1273
|
+
const order = orders.find(
|
|
1274
|
+
(o2) => o2.subscriptionId === subscriptionId && o2.status === "scheduled"
|
|
1275
|
+
);
|
|
1276
|
+
if (!order) return { _tag: "Failure", error: "No scheduled order found" };
|
|
1277
|
+
orders = orders.map(
|
|
1278
|
+
(o2) => o2.id === order.id ? { ...o2, status: "skipped" } : o2
|
|
1279
|
+
);
|
|
1280
|
+
const adj = {
|
|
1281
|
+
id: `adj-${Date.now()}`,
|
|
1282
|
+
subscriptionId,
|
|
1283
|
+
type: "skip"
|
|
1284
|
+
};
|
|
1285
|
+
adjustments = [...adjustments, adj];
|
|
1286
|
+
return { _tag: "Success", data: void 0 };
|
|
1287
|
+
},
|
|
1288
|
+
async changeDateAdjustment({ subscriptionId, newDate }) {
|
|
1289
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
1290
|
+
const order = orders.find(
|
|
1291
|
+
(o2) => o2.subscriptionId === subscriptionId && o2.status === "scheduled"
|
|
1292
|
+
);
|
|
1293
|
+
if (!order) return { _tag: "Failure", error: "No scheduled order found" };
|
|
1294
|
+
orders = orders.map(
|
|
1295
|
+
(o2) => o2.id === order.id ? { ...o2, deliveryDate: newDate } : o2
|
|
1296
|
+
);
|
|
1297
|
+
const adj = {
|
|
1298
|
+
id: `adj-${Date.now()}`,
|
|
1299
|
+
subscriptionId,
|
|
1300
|
+
type: "date_change",
|
|
1301
|
+
newDate
|
|
1302
|
+
};
|
|
1303
|
+
adjustments = [...adjustments, adj];
|
|
1304
|
+
return { _tag: "Success", data: void 0 };
|
|
1305
|
+
},
|
|
1306
|
+
async removeAdjustment({ adjustmentId }) {
|
|
1307
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1308
|
+
adjustments = adjustments.filter((a2) => a2.id !== adjustmentId);
|
|
1309
|
+
return { _tag: "Success", data: void 0 };
|
|
1310
|
+
}
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
function createApiSchedulesAdapter(fetcher) {
|
|
1314
|
+
return {
|
|
1315
|
+
async getUpcomingOrders({ count, subscriptionId }) {
|
|
1316
|
+
try {
|
|
1317
|
+
const params = new URLSearchParams({ count: String(count) });
|
|
1318
|
+
if (subscriptionId) params.set("query", `id:"${subscriptionId}"`);
|
|
1319
|
+
const res = await fetcher(`/customer/v1/schedules/?${params.toString()}`);
|
|
1320
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1321
|
+
const data = await res.json();
|
|
1322
|
+
return { _tag: "Success", data: data.orders };
|
|
1323
|
+
} catch (e3) {
|
|
1324
|
+
return { _tag: "Failure", error: e3 };
|
|
1325
|
+
}
|
|
1326
|
+
},
|
|
1327
|
+
async getAdjustments() {
|
|
1328
|
+
try {
|
|
1329
|
+
const res = await fetcher(`/customer/v1/schedules/adjustments`);
|
|
1330
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1331
|
+
const data = await res.json();
|
|
1332
|
+
const adjustments = data.data.map((a2) => ({
|
|
1333
|
+
id: a2.id,
|
|
1334
|
+
subscriptionId: a2.subscriptionId ?? "",
|
|
1335
|
+
type: a2.type === "CHANGE_DATE" ? "date_change" : "skip",
|
|
1336
|
+
newDate: a2.newDate
|
|
1337
|
+
}));
|
|
1338
|
+
return { _tag: "Success", data: adjustments };
|
|
1339
|
+
} catch (e3) {
|
|
1340
|
+
return { _tag: "Failure", error: e3 };
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
async skipOrder({ cycle, date }) {
|
|
1344
|
+
if (cycle == null && date == null) {
|
|
1345
|
+
return { _tag: "Failure", error: "Either cycle or date is required for skipOrder" };
|
|
1346
|
+
}
|
|
1347
|
+
try {
|
|
1348
|
+
const matcher = cycle != null ? { type: "CYCLE", input: { cycle } } : { type: "DATE", input: { date: new Date(date).toISOString() } };
|
|
1349
|
+
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1350
|
+
method: "POST",
|
|
1351
|
+
headers: { "Content-Type": "application/json" },
|
|
1352
|
+
body: JSON.stringify({
|
|
1353
|
+
matcher,
|
|
1354
|
+
action: { type: "SKIP_ORDER", input: { reason: "" } }
|
|
1355
|
+
})
|
|
1356
|
+
});
|
|
1357
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1358
|
+
return { _tag: "Success", data: void 0 };
|
|
1359
|
+
} catch (e3) {
|
|
1360
|
+
return { _tag: "Failure", error: e3 };
|
|
1361
|
+
}
|
|
1362
|
+
},
|
|
1363
|
+
async changeDateAdjustment({ newDate, currentDate }) {
|
|
1364
|
+
if (currentDate == null) {
|
|
1365
|
+
return {
|
|
1366
|
+
_tag: "Failure",
|
|
1367
|
+
error: "currentDate is required for changeDateAdjustment"
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1370
|
+
try {
|
|
1371
|
+
const matcher = { type: "DATE", input: { date: new Date(currentDate).toISOString() } };
|
|
1372
|
+
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1373
|
+
method: "POST",
|
|
1374
|
+
headers: { "Content-Type": "application/json" },
|
|
1375
|
+
body: JSON.stringify({
|
|
1376
|
+
matcher,
|
|
1377
|
+
action: {
|
|
1378
|
+
type: "CHANGE_DATE",
|
|
1379
|
+
input: { newDate: new Date(newDate).toISOString() }
|
|
1380
|
+
}
|
|
1381
|
+
})
|
|
1382
|
+
});
|
|
1383
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1384
|
+
return { _tag: "Success", data: void 0 };
|
|
1385
|
+
} catch (e3) {
|
|
1386
|
+
return { _tag: "Failure", error: e3 };
|
|
1387
|
+
}
|
|
1388
|
+
},
|
|
1389
|
+
async removeAdjustment({ adjustmentId }) {
|
|
1390
|
+
try {
|
|
1391
|
+
const res = await fetcher(
|
|
1392
|
+
`/customer/v1/schedules/adjustments/${adjustmentId}`,
|
|
1393
|
+
{ method: "DELETE" }
|
|
1394
|
+
);
|
|
1395
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1396
|
+
return { _tag: "Success", data: void 0 };
|
|
1397
|
+
} catch (e3) {
|
|
1398
|
+
return { _tag: "Failure", error: e3 };
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
const SchedulesServiceContext = createContext("services/schedules");
|
|
1404
|
+
function createSchedulesService(adapter, opts) {
|
|
1405
|
+
const current = d(null);
|
|
1406
|
+
const currentLoading = d(false);
|
|
1407
|
+
const hasPendingBilling = (opts == null ? void 0 : opts.hasPendingBilling) ?? d(false);
|
|
1408
|
+
let reloadToken = 0;
|
|
1409
|
+
async function getUpcomingOrder() {
|
|
1410
|
+
const result = await adapter.getUpcomingOrders({ count: 1 });
|
|
1411
|
+
if (result._tag === "Failure") return result;
|
|
1412
|
+
return { _tag: "Success", data: result.data[0] ?? null };
|
|
1413
|
+
}
|
|
1414
|
+
async function getUpcomingOrders(count) {
|
|
1415
|
+
return adapter.getUpcomingOrders({ count });
|
|
1416
|
+
}
|
|
1417
|
+
async function reloadCurrent(subscriptionId) {
|
|
1418
|
+
var _a;
|
|
1419
|
+
const token = ++reloadToken;
|
|
1420
|
+
currentLoading.value = true;
|
|
1421
|
+
const count = Math.max(((_a = current.value) == null ? void 0 : _a.length) ?? 0, 3);
|
|
1422
|
+
const result = await adapter.getUpcomingOrders({
|
|
1423
|
+
count,
|
|
1424
|
+
subscriptionId
|
|
1425
|
+
});
|
|
1426
|
+
if (token !== reloadToken) return;
|
|
1427
|
+
currentLoading.value = false;
|
|
1428
|
+
if (result._tag === "Success") {
|
|
1429
|
+
current.value = result.data;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return {
|
|
1433
|
+
current,
|
|
1434
|
+
currentLoading,
|
|
1435
|
+
hasPendingBilling,
|
|
1436
|
+
getUpcomingOrder,
|
|
1437
|
+
getUpcomingOrders,
|
|
1438
|
+
async getAdjustments() {
|
|
1439
|
+
return adapter.getAdjustments();
|
|
1440
|
+
},
|
|
1441
|
+
async skipOrder(params) {
|
|
1442
|
+
const result = await adapter.skipOrder(params);
|
|
1443
|
+
if (result._tag === "Success") {
|
|
1444
|
+
await reloadCurrent(params.subscriptionId);
|
|
1445
|
+
}
|
|
1446
|
+
return result;
|
|
1447
|
+
},
|
|
1448
|
+
async changeDateAdjustment(params) {
|
|
1449
|
+
const result = await adapter.changeDateAdjustment(params);
|
|
1450
|
+
if (result._tag === "Success") {
|
|
1451
|
+
await reloadCurrent(params.subscriptionId);
|
|
1452
|
+
}
|
|
1453
|
+
return result;
|
|
1454
|
+
},
|
|
1455
|
+
async removeAdjustment(params) {
|
|
1456
|
+
return adapter.removeAdjustment(params);
|
|
1457
|
+
},
|
|
1458
|
+
reloadCurrent
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
const SettingsServiceContext = createContext("services/settings");
|
|
1462
|
+
function createSettingsService(initial = {}) {
|
|
1463
|
+
const current = d(initial);
|
|
1464
|
+
return {
|
|
1465
|
+
current,
|
|
1466
|
+
get(key) {
|
|
1467
|
+
return current.value[key];
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
function createShopService(options) {
|
|
1472
|
+
const contactEmail = d(options.getContactEmail());
|
|
1473
|
+
const service = { contactEmail };
|
|
1474
|
+
return service;
|
|
1475
|
+
}
|
|
1476
|
+
const ShopServiceContext = createContext("services/shop");
|
|
1477
|
+
function createMockSubscriptionAdapter(options = {}) {
|
|
1478
|
+
const richBase = (id, itemId, renewalId) => ({
|
|
1479
|
+
id,
|
|
1480
|
+
customerId: "customer_1",
|
|
1481
|
+
items: [
|
|
1482
|
+
{
|
|
1483
|
+
id: itemId,
|
|
1484
|
+
productId: "product_1",
|
|
1485
|
+
variantId: "variant_1",
|
|
1486
|
+
quantity: 2,
|
|
1487
|
+
price: { amount: "29.99", currencyCode: "USD" },
|
|
1488
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1489
|
+
isUpsell: false,
|
|
1490
|
+
isVirtual: false,
|
|
1491
|
+
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1492
|
+
currentPrice: { amount: "29.99", currencyCode: "USD" },
|
|
1493
|
+
lineDiscountedPrice: { amount: "26.99", currencyCode: "USD" },
|
|
1494
|
+
variantAvailableForSale: true
|
|
1495
|
+
}
|
|
1496
|
+
],
|
|
1497
|
+
discounts: [
|
|
1498
|
+
{
|
|
1499
|
+
id: "discount_1",
|
|
1500
|
+
type: "percentage",
|
|
1501
|
+
value: 10,
|
|
1502
|
+
code: "SAVE10",
|
|
1503
|
+
description: "10% off subscription"
|
|
1504
|
+
}
|
|
1505
|
+
],
|
|
1506
|
+
status: "active",
|
|
1507
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1508
|
+
frequency: "1 MONTH",
|
|
1509
|
+
createdAt: "2024-10-01T00:00:00Z",
|
|
1510
|
+
updatedAt: "2024-10-14T00:00:00Z",
|
|
1511
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
|
|
1512
|
+
deliveryPolicy: {
|
|
1513
|
+
intervalCount: 1,
|
|
1514
|
+
interval: "MONTH",
|
|
1515
|
+
anchors: [{ day: 15, type: "MONTHDAY" }]
|
|
1516
|
+
},
|
|
1517
|
+
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1518
|
+
nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
|
|
1519
|
+
lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
|
|
1520
|
+
nextFulfillment: "2025-11-17T00:00:00Z",
|
|
1521
|
+
estimatedOrderDate: "2025-11-13T00:00:00Z",
|
|
1522
|
+
customerPaymentMethod: {
|
|
1523
|
+
id: "pm_1",
|
|
1524
|
+
instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
|
|
1525
|
+
active: true,
|
|
1526
|
+
isRecurring: true
|
|
1527
|
+
},
|
|
1528
|
+
requiredBillingCyclesLeft: 3,
|
|
1529
|
+
renewal: { id: renewalId, isInActiveProcessState: false },
|
|
1530
|
+
lastPaymentStatus: "succeeded",
|
|
1531
|
+
pausePeriod: null,
|
|
1532
|
+
isEditable: true
|
|
1533
|
+
});
|
|
1534
|
+
const mockSubscriptions = [
|
|
1535
|
+
{
|
|
1536
|
+
id: "sub_1",
|
|
1537
|
+
customerId: "customer_1",
|
|
1538
|
+
items: [
|
|
1539
|
+
{
|
|
1540
|
+
id: "item_1",
|
|
1541
|
+
productId: "product_1",
|
|
1542
|
+
variantId: "variant_1",
|
|
1543
|
+
quantity: 2,
|
|
1544
|
+
price: { amount: "29.99", currencyCode: "USD" },
|
|
1545
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1546
|
+
isUpsell: false,
|
|
1547
|
+
isVirtual: false,
|
|
1548
|
+
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1549
|
+
currentPrice: { amount: "29.99", currencyCode: "USD" },
|
|
1550
|
+
lineDiscountedPrice: { amount: "26.99", currencyCode: "USD" },
|
|
1551
|
+
variantAvailableForSale: true,
|
|
1552
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
1553
|
+
deliveryPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
1554
|
+
customAttributes: [
|
|
1555
|
+
{ key: "roast_level", value: "medium" },
|
|
1556
|
+
{ key: "grind_type", value: "whole_bean" }
|
|
1557
|
+
]
|
|
800
1558
|
}
|
|
801
1559
|
],
|
|
802
1560
|
discounts: [
|
|
@@ -812,7 +1570,33 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
812
1570
|
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
813
1571
|
frequency: "1 MONTH",
|
|
814
1572
|
createdAt: "2024-10-01T00:00:00Z",
|
|
815
|
-
updatedAt: "2024-10-14T00:00:00Z"
|
|
1573
|
+
updatedAt: "2024-10-14T00:00:00Z",
|
|
1574
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
|
|
1575
|
+
deliveryPolicy: {
|
|
1576
|
+
intervalCount: 1,
|
|
1577
|
+
interval: "MONTH",
|
|
1578
|
+
anchors: [{ day: 15, type: "MONTHDAY" }]
|
|
1579
|
+
},
|
|
1580
|
+
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1581
|
+
nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
|
|
1582
|
+
lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
|
|
1583
|
+
nextFulfillment: "2025-11-17T00:00:00Z",
|
|
1584
|
+
estimatedOrderDate: "2025-11-13T00:00:00Z",
|
|
1585
|
+
customerPaymentMethod: {
|
|
1586
|
+
id: "pm_1",
|
|
1587
|
+
instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
|
|
1588
|
+
active: true,
|
|
1589
|
+
isRecurring: true
|
|
1590
|
+
},
|
|
1591
|
+
requiredBillingCyclesLeft: 3,
|
|
1592
|
+
renewal: { id: "ren_1", isInActiveProcessState: false },
|
|
1593
|
+
lastPaymentStatus: "succeeded",
|
|
1594
|
+
pausePeriod: null,
|
|
1595
|
+
isEditable: true,
|
|
1596
|
+
customAttributes: [
|
|
1597
|
+
{ key: "subscription_tier", value: "premium" },
|
|
1598
|
+
{ key: "gift_message", value: "Enjoy your coffee!" }
|
|
1599
|
+
]
|
|
816
1600
|
},
|
|
817
1601
|
{
|
|
818
1602
|
id: "sub_2",
|
|
@@ -824,7 +1608,10 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
824
1608
|
variantId: "variant_2",
|
|
825
1609
|
quantity: 1,
|
|
826
1610
|
price: { amount: "49.99", currencyCode: "USD" },
|
|
827
|
-
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20")
|
|
1611
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
|
|
1612
|
+
billingPolicy: { intervalCount: 2, interval: "WEEK" },
|
|
1613
|
+
deliveryPolicy: { intervalCount: 2, interval: "WEEK" },
|
|
1614
|
+
customAttributes: []
|
|
828
1615
|
}
|
|
829
1616
|
],
|
|
830
1617
|
discounts: [],
|
|
@@ -832,7 +1619,50 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
832
1619
|
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
|
|
833
1620
|
frequency: "2 WEEK",
|
|
834
1621
|
createdAt: "2024-09-15T00:00:00Z",
|
|
835
|
-
updatedAt: "2024-10-10T00:00:00Z"
|
|
1622
|
+
updatedAt: "2024-10-10T00:00:00Z",
|
|
1623
|
+
customAttributes: []
|
|
1624
|
+
},
|
|
1625
|
+
// Per-status fixtures: groundwork for the deferred editor preview picker
|
|
1626
|
+
// (docs/superpowers/specs/2026-06-17-renewalsummary-parity-and-mock-groundwork-design.md §9).
|
|
1627
|
+
// Not consumed yet — the editor still seeds the active sub_1 via getDefaultSubscriptionId().
|
|
1628
|
+
{
|
|
1629
|
+
...richBase("sub_paused", "item_paused", "ren_paused"),
|
|
1630
|
+
status: "paused",
|
|
1631
|
+
pausePeriod: { start: "2025-11-01T00:00:00Z", end: "2026-01-01T00:00:00Z" }
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
...richBase("sub_canceled", "item_canceled", "ren_canceled"),
|
|
1635
|
+
status: "canceled",
|
|
1636
|
+
pausePeriod: null
|
|
1637
|
+
},
|
|
1638
|
+
{
|
|
1639
|
+
...richBase("sub_failed", "item_failed", "ren_failed"),
|
|
1640
|
+
status: "failed",
|
|
1641
|
+
lastPaymentStatus: "failed",
|
|
1642
|
+
pausePeriod: null
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
...richBase("sub_expired", "item_expired", "ren_expired"),
|
|
1646
|
+
status: "expired",
|
|
1647
|
+
pausePeriod: null
|
|
1648
|
+
}
|
|
1649
|
+
];
|
|
1650
|
+
const mockUpsellProducts = [
|
|
1651
|
+
{
|
|
1652
|
+
id: "upsell-1",
|
|
1653
|
+
variantId: "upsell-v-1",
|
|
1654
|
+
title: "Retinol Night Cream",
|
|
1655
|
+
imageUrl: "https://images.unsplash.com/photo-1617897903246-719242758050?w=200",
|
|
1656
|
+
price: { amount: "38.00", currencyCode: "USD" },
|
|
1657
|
+
description: "Anti-aging night treatment"
|
|
1658
|
+
},
|
|
1659
|
+
{
|
|
1660
|
+
id: "upsell-2",
|
|
1661
|
+
variantId: "upsell-v-2",
|
|
1662
|
+
title: "SPF 50 Day Shield",
|
|
1663
|
+
imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
|
|
1664
|
+
price: { amount: "24.00", currencyCode: "USD" },
|
|
1665
|
+
description: "Lightweight daily sun protection"
|
|
836
1666
|
}
|
|
837
1667
|
];
|
|
838
1668
|
return {
|
|
@@ -843,7 +1673,7 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
843
1673
|
},
|
|
844
1674
|
async search(options2 = {}) {
|
|
845
1675
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
846
|
-
const pageSize =
|
|
1676
|
+
const pageSize = 2;
|
|
847
1677
|
const allItems = mockSubscriptions;
|
|
848
1678
|
let startIndex = 0;
|
|
849
1679
|
let endIndex = pageSize;
|
|
@@ -859,20 +1689,22 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
859
1689
|
}
|
|
860
1690
|
}
|
|
861
1691
|
const pageItems = allItems.slice(startIndex, endIndex);
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
items:
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1692
|
+
return {
|
|
1693
|
+
_tag: "Success",
|
|
1694
|
+
data: {
|
|
1695
|
+
items: pageItems.map((sub) => ({
|
|
1696
|
+
...sub,
|
|
1697
|
+
items: sub.items.map((item) => ({ ...item })),
|
|
1698
|
+
discounts: sub.discounts.map((discount) => ({ ...discount }))
|
|
1699
|
+
})),
|
|
1700
|
+
pageInfo: {
|
|
1701
|
+
hasNextPage: endIndex < allItems.length,
|
|
1702
|
+
hasPrevPage: startIndex > 0,
|
|
1703
|
+
startCursor: pageItems.length > 0 ? startIndex.toString() : null,
|
|
1704
|
+
endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
|
|
1705
|
+
}
|
|
873
1706
|
}
|
|
874
1707
|
};
|
|
875
|
-
return { _tag: "Success", data: paginatedList };
|
|
876
1708
|
},
|
|
877
1709
|
async addItem() {
|
|
878
1710
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -882,6 +1714,17 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
882
1714
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
883
1715
|
return { _tag: "Success", data: void 0 };
|
|
884
1716
|
},
|
|
1717
|
+
async updateItem() {
|
|
1718
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1719
|
+
return { _tag: "Success", data: void 0 };
|
|
1720
|
+
},
|
|
1721
|
+
async skipLineItem() {
|
|
1722
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1723
|
+
return { _tag: "Success", data: void 0 };
|
|
1724
|
+
},
|
|
1725
|
+
async updateBoxItems() {
|
|
1726
|
+
return { _tag: "Success", data: void 0 };
|
|
1727
|
+
},
|
|
885
1728
|
async addDiscount() {
|
|
886
1729
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
887
1730
|
const behavior = options.addDiscountBehavior ?? { kind: "success" };
|
|
@@ -910,24 +1753,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
910
1753
|
(sub) => sub.id === params.subscriptionId
|
|
911
1754
|
);
|
|
912
1755
|
if (!subscription) {
|
|
913
|
-
return {
|
|
914
|
-
_tag: "Failure",
|
|
915
|
-
error: "Subscription not found"
|
|
916
|
-
};
|
|
1756
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
917
1757
|
}
|
|
918
1758
|
subscription.nextDeliveryDate = new Date(params.newDate);
|
|
919
1759
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
920
|
-
subscription
|
|
921
|
-
item.nextDeliveryDate = new Date(params.newDate);
|
|
922
|
-
});
|
|
923
|
-
return {
|
|
924
|
-
_tag: "Success",
|
|
925
|
-
data: {
|
|
926
|
-
...subscription,
|
|
927
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
928
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
929
|
-
}
|
|
930
|
-
};
|
|
1760
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
931
1761
|
},
|
|
932
1762
|
async resume(params) {
|
|
933
1763
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -935,21 +1765,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
935
1765
|
(sub) => sub.id === params.subscriptionId
|
|
936
1766
|
);
|
|
937
1767
|
if (!subscription) {
|
|
938
|
-
return {
|
|
939
|
-
_tag: "Failure",
|
|
940
|
-
error: "Subscription not found"
|
|
941
|
-
};
|
|
1768
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
942
1769
|
}
|
|
943
1770
|
subscription.status = "active";
|
|
944
1771
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
945
|
-
return {
|
|
946
|
-
_tag: "Success",
|
|
947
|
-
data: {
|
|
948
|
-
...subscription,
|
|
949
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
950
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
951
|
-
}
|
|
952
|
-
};
|
|
1772
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
953
1773
|
},
|
|
954
1774
|
async pause(params) {
|
|
955
1775
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -957,21 +1777,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
957
1777
|
(sub) => sub.id === params.subscriptionId
|
|
958
1778
|
);
|
|
959
1779
|
if (!subscription) {
|
|
960
|
-
return {
|
|
961
|
-
_tag: "Failure",
|
|
962
|
-
error: "Subscription not found"
|
|
963
|
-
};
|
|
1780
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
964
1781
|
}
|
|
965
1782
|
subscription.status = "paused";
|
|
966
1783
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
967
|
-
return {
|
|
968
|
-
_tag: "Success",
|
|
969
|
-
data: {
|
|
970
|
-
...subscription,
|
|
971
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
972
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
973
|
-
}
|
|
974
|
-
};
|
|
1784
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
975
1785
|
},
|
|
976
1786
|
async reactivate(params) {
|
|
977
1787
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -979,21 +1789,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
979
1789
|
(sub) => sub.id === params.subscriptionId
|
|
980
1790
|
);
|
|
981
1791
|
if (!subscription) {
|
|
982
|
-
return {
|
|
983
|
-
_tag: "Failure",
|
|
984
|
-
error: "Subscription not found"
|
|
985
|
-
};
|
|
1792
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
986
1793
|
}
|
|
987
1794
|
subscription.status = "active";
|
|
988
1795
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
989
|
-
return {
|
|
990
|
-
_tag: "Success",
|
|
991
|
-
data: {
|
|
992
|
-
...subscription,
|
|
993
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
994
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
995
|
-
}
|
|
996
|
-
};
|
|
1796
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
997
1797
|
},
|
|
998
1798
|
async cancel(params) {
|
|
999
1799
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1001,21 +1801,11 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1001
1801
|
(sub) => sub.id === params.subscriptionId
|
|
1002
1802
|
);
|
|
1003
1803
|
if (!subscription) {
|
|
1004
|
-
return {
|
|
1005
|
-
_tag: "Failure",
|
|
1006
|
-
error: "Subscription not found"
|
|
1007
|
-
};
|
|
1804
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1008
1805
|
}
|
|
1009
1806
|
subscription.status = "canceled";
|
|
1010
1807
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1011
|
-
return {
|
|
1012
|
-
_tag: "Success",
|
|
1013
|
-
data: {
|
|
1014
|
-
...subscription,
|
|
1015
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
1016
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
1017
|
-
}
|
|
1018
|
-
};
|
|
1808
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
1019
1809
|
},
|
|
1020
1810
|
async getAvailablePlans() {
|
|
1021
1811
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1043,13 +1833,514 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1043
1833
|
]
|
|
1044
1834
|
};
|
|
1045
1835
|
},
|
|
1836
|
+
async getLastOrder(params) {
|
|
1837
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1838
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
|
|
1839
|
+
if (!subscription) return { _tag: "Success", data: null };
|
|
1840
|
+
return {
|
|
1841
|
+
_tag: "Success",
|
|
1842
|
+
data: {
|
|
1843
|
+
id: `mock_last_order_${params.subscriptionId}`,
|
|
1844
|
+
createdAt: "2026-04-22T12:00:00Z"
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
},
|
|
1848
|
+
async renewNow(_params) {
|
|
1849
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1850
|
+
return { _tag: "Success", data: { result: "success" } };
|
|
1851
|
+
},
|
|
1852
|
+
async changeFrequency(params) {
|
|
1853
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1854
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
|
|
1855
|
+
if (!subscription) {
|
|
1856
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1857
|
+
}
|
|
1858
|
+
if (typeof params.plan === "string") {
|
|
1859
|
+
subscription.frequency = `1 ${params.plan}`;
|
|
1860
|
+
} else {
|
|
1861
|
+
subscription.frequency = `${params.plan.intervalCount} ${params.plan.interval}`;
|
|
1862
|
+
if (subscription.billingPolicy) {
|
|
1863
|
+
subscription.billingPolicy = { ...subscription.billingPolicy, ...params.plan };
|
|
1864
|
+
}
|
|
1865
|
+
if (subscription.deliveryPolicy) {
|
|
1866
|
+
subscription.deliveryPolicy = { ...subscription.deliveryPolicy, ...params.plan };
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1870
|
+
return {
|
|
1871
|
+
_tag: "Success",
|
|
1872
|
+
data: {
|
|
1873
|
+
...subscription,
|
|
1874
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
1875
|
+
discounts: subscription.discounts.map((d3) => ({ ...d3 }))
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1878
|
+
},
|
|
1046
1879
|
async getDefaultSubscriptionId() {
|
|
1047
1880
|
var _a;
|
|
1048
1881
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1049
1882
|
return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
|
|
1883
|
+
},
|
|
1884
|
+
async getUpsellProducts() {
|
|
1885
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1886
|
+
return { _tag: "Success", data: [...mockUpsellProducts] };
|
|
1887
|
+
},
|
|
1888
|
+
async addUpsellProduct() {
|
|
1889
|
+
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
1890
|
+
return { _tag: "Success", data: void 0 };
|
|
1050
1891
|
}
|
|
1051
1892
|
};
|
|
1052
1893
|
}
|
|
1894
|
+
function normalizeInstrumentType(type) {
|
|
1895
|
+
return type.replace(/-/g, "_");
|
|
1896
|
+
}
|
|
1897
|
+
function mapPaymentMethod(rawPm) {
|
|
1898
|
+
if (!rawPm) return null;
|
|
1899
|
+
if (typeof rawPm === "string") {
|
|
1900
|
+
return { id: rawPm };
|
|
1901
|
+
}
|
|
1902
|
+
return {
|
|
1903
|
+
id: rawPm.id ?? "",
|
|
1904
|
+
...rawPm.instrument != null ? {
|
|
1905
|
+
instrument: {
|
|
1906
|
+
...rawPm.instrument,
|
|
1907
|
+
type: normalizeInstrumentType(rawPm.instrument.type)
|
|
1908
|
+
}
|
|
1909
|
+
} : {},
|
|
1910
|
+
...rawPm.active != null ? { active: rawPm.active } : {},
|
|
1911
|
+
...rawPm.isRecurring != null ? { isRecurring: rawPm.isRecurring } : {}
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
function mapSubscription(raw) {
|
|
1915
|
+
var _a, _b;
|
|
1916
|
+
const status = raw.status === "merged" ? "canceled" : raw.status;
|
|
1917
|
+
const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
|
|
1918
|
+
const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
|
|
1919
|
+
return {
|
|
1920
|
+
id: raw.id,
|
|
1921
|
+
customerId: "",
|
|
1922
|
+
serial: raw.serial,
|
|
1923
|
+
items: (raw.items ?? []).map(
|
|
1924
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1925
|
+
(item) => ({
|
|
1926
|
+
id: item.id,
|
|
1927
|
+
productId: item.productId ?? item.product ?? "",
|
|
1928
|
+
variantId: item.variantId ?? item.variant ?? "",
|
|
1929
|
+
quantity: item.quantity,
|
|
1930
|
+
price: item.price ?? {
|
|
1931
|
+
amount: String(item.totalPrice ?? 0),
|
|
1932
|
+
currencyCode: raw.currencyCode ?? ""
|
|
1933
|
+
},
|
|
1934
|
+
nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null,
|
|
1935
|
+
billingPolicy: item.billingPolicy ?? raw.billingPolicy ?? null,
|
|
1936
|
+
deliveryPolicy: item.deliveryPolicy ?? raw.deliveryPolicy ?? null,
|
|
1937
|
+
// Map human-readable titles from the v1 SubscriptionItemResource
|
|
1938
|
+
title: item.title,
|
|
1939
|
+
variantTitle: item.subtitle ?? void 0,
|
|
1940
|
+
// recurringCycleLimit === 1 means the item charges exactly once (one-time upsell)
|
|
1941
|
+
isOneTime: item.recurringCycleLimit === 1
|
|
1942
|
+
})
|
|
1943
|
+
),
|
|
1944
|
+
discounts: (raw.discounts ?? []).map(
|
|
1945
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1946
|
+
(d3) => {
|
|
1947
|
+
var _a2, _b2, _c;
|
|
1948
|
+
return {
|
|
1949
|
+
id: d3.id,
|
|
1950
|
+
type: ((_a2 = d3.value) == null ? void 0 : _a2.type) === "fixed-amount" || d3.type === "fixed_amount" ? "fixed_amount" : "percentage",
|
|
1951
|
+
value: ((_b2 = d3.value) == null ? void 0 : _b2.amount) ?? ((_c = d3.value) == null ? void 0 : _c.percentage) ?? d3.value ?? 0,
|
|
1952
|
+
code: d3.title ?? d3.code ?? "",
|
|
1953
|
+
description: d3.description ?? ""
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
),
|
|
1957
|
+
status,
|
|
1958
|
+
nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
|
|
1959
|
+
// Map nextBillingDate as the raw ISO string for ProductSelect skip() and other consumers
|
|
1960
|
+
nextBillingDate: raw.nextBillingDate ?? null,
|
|
1961
|
+
// Map customerPaymentMethod — note: arrives as bare id (not expanded) since the
|
|
1962
|
+
// subscriptions endpoint does not support ?expand=paymentMethod; instrument digits
|
|
1963
|
+
// remain a data gap pending backend API extension.
|
|
1964
|
+
customerPaymentMethod: mapPaymentMethod(raw.paymentMethod),
|
|
1965
|
+
frequency,
|
|
1966
|
+
createdAt: raw.createdAt,
|
|
1967
|
+
updatedAt: raw.updatedAt,
|
|
1968
|
+
billingPolicy: raw.billingPolicy ?? null,
|
|
1969
|
+
deliveryPolicy: raw.deliveryPolicy ?? null
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
function createApiSubscriptionAdapter(fetcher) {
|
|
1973
|
+
const adapter = {
|
|
1974
|
+
async search(options) {
|
|
1975
|
+
var _a;
|
|
1976
|
+
try {
|
|
1977
|
+
const params = new URLSearchParams();
|
|
1978
|
+
if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
|
|
1979
|
+
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 ");
|
|
1980
|
+
if (q2) params.set("query", q2);
|
|
1981
|
+
}
|
|
1982
|
+
if (options == null ? void 0 : options.pagination) {
|
|
1983
|
+
if ("after" in options.pagination)
|
|
1984
|
+
params.set("after", options.pagination.after);
|
|
1985
|
+
else if ("before" in options.pagination)
|
|
1986
|
+
params.set("before", options.pagination.before);
|
|
1987
|
+
}
|
|
1988
|
+
const query = params.toString() ? `?${params.toString()}` : "";
|
|
1989
|
+
const res = await fetcher(`/customer/v1/subscriptions${query}`);
|
|
1990
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1991
|
+
const body = await res.json();
|
|
1992
|
+
return {
|
|
1993
|
+
_tag: "Success",
|
|
1994
|
+
data: {
|
|
1995
|
+
items: body.data.map(mapSubscription),
|
|
1996
|
+
pageInfo: {
|
|
1997
|
+
hasNextPage: body.hasNextPage,
|
|
1998
|
+
hasPrevPage: body.hasPrevPage,
|
|
1999
|
+
startCursor: body.startCursor,
|
|
2000
|
+
endCursor: body.endCursor
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
2004
|
+
} catch (e3) {
|
|
2005
|
+
return { _tag: "Failure", error: e3 };
|
|
2006
|
+
}
|
|
2007
|
+
},
|
|
2008
|
+
async getById(id) {
|
|
2009
|
+
try {
|
|
2010
|
+
const res = await fetcher(
|
|
2011
|
+
`/customer/v1/subscriptions?query=id:"${id}"&limit=1`
|
|
2012
|
+
);
|
|
2013
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2014
|
+
const body = await res.json();
|
|
2015
|
+
const raw = body.data[0];
|
|
2016
|
+
return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
|
|
2017
|
+
} catch (e3) {
|
|
2018
|
+
return { _tag: "Failure", error: e3 };
|
|
2019
|
+
}
|
|
2020
|
+
},
|
|
2021
|
+
async addItem({ subscriptionId, item }) {
|
|
2022
|
+
try {
|
|
2023
|
+
const res = await fetcher(
|
|
2024
|
+
`/customer/v1/subscriptions/${subscriptionId}/items`,
|
|
2025
|
+
{
|
|
2026
|
+
method: "POST",
|
|
2027
|
+
headers: { "Content-Type": "application/json" },
|
|
2028
|
+
body: JSON.stringify(item)
|
|
2029
|
+
}
|
|
2030
|
+
);
|
|
2031
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2032
|
+
return { _tag: "Success", data: void 0 };
|
|
2033
|
+
} catch (e3) {
|
|
2034
|
+
return { _tag: "Failure", error: e3 };
|
|
2035
|
+
}
|
|
2036
|
+
},
|
|
2037
|
+
async removeItem({ subscriptionId, itemId }) {
|
|
2038
|
+
try {
|
|
2039
|
+
const res = await fetcher(
|
|
2040
|
+
`/customer/v1/subscriptions/${subscriptionId}/items/${itemId}`,
|
|
2041
|
+
{ method: "DELETE" }
|
|
2042
|
+
);
|
|
2043
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2044
|
+
return { _tag: "Success", data: void 0 };
|
|
2045
|
+
} catch (e3) {
|
|
2046
|
+
return { _tag: "Failure", error: e3 };
|
|
2047
|
+
}
|
|
2048
|
+
},
|
|
2049
|
+
async updateItem({ subscriptionId, lineId, update }) {
|
|
2050
|
+
try {
|
|
2051
|
+
const body = {
|
|
2052
|
+
...update.variantId !== void 0 ? { productVariantId: update.variantId } : {},
|
|
2053
|
+
...update.quantity !== void 0 ? { quantity: update.quantity } : {},
|
|
2054
|
+
...update.billingPolicy !== void 0 ? { billingPolicy: update.billingPolicy } : {},
|
|
2055
|
+
...update.shippingMethodHandle !== void 0 ? { shippingMethodHandle: update.shippingMethodHandle } : {}
|
|
2056
|
+
};
|
|
2057
|
+
const res = await fetcher(
|
|
2058
|
+
`/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
|
|
2059
|
+
{
|
|
2060
|
+
method: "PUT",
|
|
2061
|
+
headers: { "Content-Type": "application/json" },
|
|
2062
|
+
body: JSON.stringify(body)
|
|
2063
|
+
}
|
|
2064
|
+
);
|
|
2065
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2066
|
+
return { _tag: "Success", data: void 0 };
|
|
2067
|
+
} catch (e3) {
|
|
2068
|
+
return { _tag: "Failure", error: e3 };
|
|
2069
|
+
}
|
|
2070
|
+
},
|
|
2071
|
+
async skipLineItem({ subscriptionId, lineId, nextBillingDate }) {
|
|
2072
|
+
try {
|
|
2073
|
+
const res = await fetcher(
|
|
2074
|
+
`/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
|
|
2075
|
+
{
|
|
2076
|
+
method: "PUT",
|
|
2077
|
+
headers: { "Content-Type": "application/json" },
|
|
2078
|
+
body: JSON.stringify({ nextBillingDate })
|
|
2079
|
+
}
|
|
2080
|
+
);
|
|
2081
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2082
|
+
return { _tag: "Success", data: void 0 };
|
|
2083
|
+
} catch (e3) {
|
|
2084
|
+
return { _tag: "Failure", error: e3 };
|
|
2085
|
+
}
|
|
2086
|
+
},
|
|
2087
|
+
async updateBoxItems({ subscriptionId, parentId, items, shippingMethodHandle }) {
|
|
2088
|
+
try {
|
|
2089
|
+
const res = await fetcher(
|
|
2090
|
+
`/customer/subscriptions/${subscriptionId}/update-box-products`,
|
|
2091
|
+
{
|
|
2092
|
+
method: "POST",
|
|
2093
|
+
headers: { "Content-Type": "application/json" },
|
|
2094
|
+
body: JSON.stringify({ parentId, items, shippingMethodHandle })
|
|
2095
|
+
}
|
|
2096
|
+
);
|
|
2097
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2098
|
+
return { _tag: "Success", data: void 0 };
|
|
2099
|
+
} catch (e3) {
|
|
2100
|
+
return { _tag: "Failure", error: e3 };
|
|
2101
|
+
}
|
|
2102
|
+
},
|
|
2103
|
+
async addDiscount({ subscriptionId, code }) {
|
|
2104
|
+
try {
|
|
2105
|
+
const res = await fetcher(
|
|
2106
|
+
`/customer/v1/subscriptions/${subscriptionId}/discounts`,
|
|
2107
|
+
{
|
|
2108
|
+
method: "POST",
|
|
2109
|
+
headers: { "Content-Type": "application/json" },
|
|
2110
|
+
body: JSON.stringify({ code })
|
|
2111
|
+
}
|
|
2112
|
+
);
|
|
2113
|
+
if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
|
|
2114
|
+
return { _tag: "Success", data: void 0 };
|
|
2115
|
+
} catch (e3) {
|
|
2116
|
+
return { _tag: "Failure", error: { kind: "UNKNOWN", message: e3 instanceof Error ? e3.message : void 0 } };
|
|
2117
|
+
}
|
|
2118
|
+
},
|
|
2119
|
+
async removeDiscount({ subscriptionId, discountId }) {
|
|
2120
|
+
try {
|
|
2121
|
+
const res = await fetcher(
|
|
2122
|
+
`/customer/v1/subscriptions/${subscriptionId}/discounts/${discountId}`,
|
|
2123
|
+
{ method: "DELETE" }
|
|
2124
|
+
);
|
|
2125
|
+
if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
|
|
2126
|
+
return { _tag: "Success", data: void 0 };
|
|
2127
|
+
} catch (e3) {
|
|
2128
|
+
return { _tag: "Failure", error: { kind: "UNKNOWN", message: e3 instanceof Error ? e3.message : void 0 } };
|
|
2129
|
+
}
|
|
2130
|
+
},
|
|
2131
|
+
// Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
|
|
2132
|
+
async reschedule({ subscriptionId, newDate }) {
|
|
2133
|
+
try {
|
|
2134
|
+
const res = await fetcher(
|
|
2135
|
+
`/customer/v1/subscriptions/${subscriptionId}`,
|
|
2136
|
+
{
|
|
2137
|
+
method: "PATCH",
|
|
2138
|
+
headers: { "Content-Type": "application/json" },
|
|
2139
|
+
body: JSON.stringify({ nextBillingDate: newDate })
|
|
2140
|
+
}
|
|
2141
|
+
);
|
|
2142
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2143
|
+
const data = await res.json();
|
|
2144
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2145
|
+
} catch (e3) {
|
|
2146
|
+
return { _tag: "Failure", error: e3 };
|
|
2147
|
+
}
|
|
2148
|
+
},
|
|
2149
|
+
async resume({ subscriptionId }) {
|
|
2150
|
+
try {
|
|
2151
|
+
const res = await fetcher(
|
|
2152
|
+
`/customer/v1/subscriptions/${subscriptionId}/resume`,
|
|
2153
|
+
{ method: "POST" }
|
|
2154
|
+
);
|
|
2155
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2156
|
+
const data = await res.json();
|
|
2157
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2158
|
+
} catch (e3) {
|
|
2159
|
+
return { _tag: "Failure", error: e3 };
|
|
2160
|
+
}
|
|
2161
|
+
},
|
|
2162
|
+
async pause({ subscriptionId }) {
|
|
2163
|
+
try {
|
|
2164
|
+
const res = await fetcher(
|
|
2165
|
+
`/customer/v1/subscriptions/${subscriptionId}/pause`,
|
|
2166
|
+
{ method: "POST" }
|
|
2167
|
+
);
|
|
2168
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2169
|
+
const data = await res.json();
|
|
2170
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2171
|
+
} catch (e3) {
|
|
2172
|
+
return { _tag: "Failure", error: e3 };
|
|
2173
|
+
}
|
|
2174
|
+
},
|
|
2175
|
+
async reactivate({ subscriptionId }) {
|
|
2176
|
+
try {
|
|
2177
|
+
const res = await fetcher(
|
|
2178
|
+
`/customer/v1/subscriptions/${subscriptionId}/reactivate`,
|
|
2179
|
+
{ method: "POST" }
|
|
2180
|
+
);
|
|
2181
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2182
|
+
const data = await res.json();
|
|
2183
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2184
|
+
} catch (e3) {
|
|
2185
|
+
return { _tag: "Failure", error: e3 };
|
|
2186
|
+
}
|
|
2187
|
+
},
|
|
2188
|
+
async cancel({ subscriptionId, reason }) {
|
|
2189
|
+
try {
|
|
2190
|
+
const res = await fetcher(
|
|
2191
|
+
`/customer/v1/subscriptions/${subscriptionId}/cancel`,
|
|
2192
|
+
{
|
|
2193
|
+
method: "POST",
|
|
2194
|
+
headers: { "Content-Type": "application/json" },
|
|
2195
|
+
body: JSON.stringify({ cancellationReason: reason })
|
|
2196
|
+
}
|
|
2197
|
+
);
|
|
2198
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2199
|
+
const data = await res.json();
|
|
2200
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2201
|
+
} catch (e3) {
|
|
2202
|
+
return { _tag: "Failure", error: e3 };
|
|
2203
|
+
}
|
|
2204
|
+
},
|
|
2205
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
|
|
2206
|
+
async getAvailablePlans({ subscriptionId }) {
|
|
2207
|
+
try {
|
|
2208
|
+
const res = await fetcher(
|
|
2209
|
+
`/customer/subscriptions/${subscriptionId}/plans`
|
|
2210
|
+
);
|
|
2211
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2212
|
+
const data = await res.json();
|
|
2213
|
+
return { _tag: "Success", data };
|
|
2214
|
+
} catch (e3) {
|
|
2215
|
+
return { _tag: "Failure", error: e3 };
|
|
2216
|
+
}
|
|
2217
|
+
},
|
|
2218
|
+
// Legacy endpoint — no /customer/v1 equivalent exists yet.
|
|
2219
|
+
async getDefaultSubscriptionId() {
|
|
2220
|
+
try {
|
|
2221
|
+
const res = await fetcher(
|
|
2222
|
+
`/customer/subscriptions/one-by-next-billing-date`
|
|
2223
|
+
);
|
|
2224
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2225
|
+
const data = await res.json();
|
|
2226
|
+
return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
|
|
2227
|
+
} catch (e3) {
|
|
2228
|
+
return { _tag: "Failure", error: e3 };
|
|
2229
|
+
}
|
|
2230
|
+
},
|
|
2231
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
|
|
2232
|
+
async getUpsellProducts({ subscriptionId }) {
|
|
2233
|
+
try {
|
|
2234
|
+
const res = await fetcher(
|
|
2235
|
+
`/customer/subscriptions/${subscriptionId}/upsell-products`
|
|
2236
|
+
);
|
|
2237
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2238
|
+
const data = await res.json();
|
|
2239
|
+
const products = data.map((p2) => {
|
|
2240
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2241
|
+
return {
|
|
2242
|
+
id: p2.id,
|
|
2243
|
+
variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
|
|
2244
|
+
title: p2.title ?? "",
|
|
2245
|
+
imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
|
|
2246
|
+
price: {
|
|
2247
|
+
amount: String(((_f = (_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.currentPrice) == null ? void 0 : _f.amount) ?? 0),
|
|
2248
|
+
currencyCode: ((_i = (_h = (_g = p2.variants) == null ? void 0 : _g[0]) == null ? void 0 : _h.currentPrice) == null ? void 0 : _i.currencyCode) ?? ""
|
|
2249
|
+
},
|
|
2250
|
+
// recurring comes from the upsell rule on variants[0]; present in PreparedProductSchema
|
|
2251
|
+
recurring: (_k = (_j = p2.variants) == null ? void 0 : _j[0]) == null ? void 0 : _k.recurring
|
|
2252
|
+
};
|
|
2253
|
+
});
|
|
2254
|
+
return { _tag: "Success", data: products };
|
|
2255
|
+
} catch (e3) {
|
|
2256
|
+
return { _tag: "Failure", error: e3 };
|
|
2257
|
+
}
|
|
2258
|
+
},
|
|
2259
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
|
|
2260
|
+
async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
|
|
2261
|
+
try {
|
|
2262
|
+
const res = await fetcher(
|
|
2263
|
+
`/customer/subscriptions/${subscriptionId}/upsell`,
|
|
2264
|
+
{
|
|
2265
|
+
method: "POST",
|
|
2266
|
+
headers: { "Content-Type": "application/json" },
|
|
2267
|
+
body: JSON.stringify({
|
|
2268
|
+
productVariantId: variantId,
|
|
2269
|
+
quantity,
|
|
2270
|
+
...oneTime ? { renewalLimit: 1 } : {}
|
|
2271
|
+
})
|
|
2272
|
+
}
|
|
2273
|
+
);
|
|
2274
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2275
|
+
return { _tag: "Success", data: void 0 };
|
|
2276
|
+
} catch (e3) {
|
|
2277
|
+
return { _tag: "Failure", error: e3 };
|
|
2278
|
+
}
|
|
2279
|
+
},
|
|
2280
|
+
// Legacy endpoint — no /customer/v1 equivalent exists yet.
|
|
2281
|
+
async getLastOrder({ subscriptionId }) {
|
|
2282
|
+
try {
|
|
2283
|
+
const res = await fetcher(
|
|
2284
|
+
`/customer/subscriptions/${subscriptionId}/last-order`
|
|
2285
|
+
);
|
|
2286
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2287
|
+
const data = await res.json();
|
|
2288
|
+
if (!data) return { _tag: "Success", data: null };
|
|
2289
|
+
return {
|
|
2290
|
+
_tag: "Success",
|
|
2291
|
+
data: { id: data.id, createdAt: data.createdAt }
|
|
2292
|
+
};
|
|
2293
|
+
} catch (e3) {
|
|
2294
|
+
return { _tag: "Failure", error: e3 };
|
|
2295
|
+
}
|
|
2296
|
+
},
|
|
2297
|
+
// Legacy endpoints, split by stage (see SubscriptionService.renewNow docs):
|
|
2298
|
+
// retry → POST /customer/subscriptions/{id}/retry
|
|
2299
|
+
// renewalId → POST /customer/renewals/{renewalId}/renew
|
|
2300
|
+
// default → POST /customer/subscriptions/{id}/order-now
|
|
2301
|
+
async renewNow({ subscriptionId, renewalId, retry }) {
|
|
2302
|
+
try {
|
|
2303
|
+
const url = retry ? `/customer/subscriptions/${subscriptionId}/retry` : renewalId ? `/customer/renewals/${renewalId}/renew` : `/customer/subscriptions/${subscriptionId}/order-now`;
|
|
2304
|
+
const res = await fetcher(url, { method: "POST" });
|
|
2305
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2306
|
+
const body = res.status === 204 ? null : await res.json();
|
|
2307
|
+
return {
|
|
2308
|
+
_tag: "Success",
|
|
2309
|
+
data: {
|
|
2310
|
+
nextActionUrl: (body == null ? void 0 : body.nextActionUrl) ?? void 0,
|
|
2311
|
+
result: (body == null ? void 0 : body.result) ?? "success"
|
|
2312
|
+
}
|
|
2313
|
+
};
|
|
2314
|
+
} catch (e3) {
|
|
2315
|
+
return { _tag: "Failure", error: e3 };
|
|
2316
|
+
}
|
|
2317
|
+
},
|
|
2318
|
+
// Legacy endpoint — POST /plan returns 204; re-fetch to return the updated subscription.
|
|
2319
|
+
async changeFrequency({ subscriptionId, plan }) {
|
|
2320
|
+
try {
|
|
2321
|
+
const res = await fetcher(
|
|
2322
|
+
`/customer/subscriptions/${subscriptionId}/plan`,
|
|
2323
|
+
{
|
|
2324
|
+
method: "POST",
|
|
2325
|
+
headers: { "Content-Type": "application/json" },
|
|
2326
|
+
body: JSON.stringify({ plan })
|
|
2327
|
+
}
|
|
2328
|
+
);
|
|
2329
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2330
|
+
const refreshed = await adapter.getById(subscriptionId);
|
|
2331
|
+
if (refreshed._tag === "Failure") return refreshed;
|
|
2332
|
+
if (refreshed.data) return { _tag: "Success", data: refreshed.data };
|
|
2333
|
+
return {
|
|
2334
|
+
_tag: "Failure",
|
|
2335
|
+
error: "Subscription not found after frequency change"
|
|
2336
|
+
};
|
|
2337
|
+
} catch (e3) {
|
|
2338
|
+
return { _tag: "Failure", error: e3 };
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
};
|
|
2342
|
+
return adapter;
|
|
2343
|
+
}
|
|
1053
2344
|
const SubscriptionServiceContext = createContext("services/subscription");
|
|
1054
2345
|
function getCurrentSubscriptionId() {
|
|
1055
2346
|
if (typeof window === "undefined") {
|
|
@@ -1072,41 +2363,75 @@ function getCurrentSubscriptionId() {
|
|
|
1072
2363
|
}
|
|
1073
2364
|
function createSubscriptionService(adapter) {
|
|
1074
2365
|
const current = d(null);
|
|
1075
|
-
const
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
}
|
|
1083
|
-
async function refreshAfterDiscountChange(subscriptionId) {
|
|
1084
|
-
await refreshCurrent(subscriptionId);
|
|
1085
|
-
discountsChangedAtSignal.value = discountsChangedAtSignal.value + 1;
|
|
2366
|
+
const hasPendingBilling = d(false);
|
|
2367
|
+
const discountsChangedAt = d(0);
|
|
2368
|
+
const itemsChangedAt = d(0);
|
|
2369
|
+
const lastOrder = d(null);
|
|
2370
|
+
async function refreshAfterDiscountChange() {
|
|
2371
|
+
await reload();
|
|
2372
|
+
discountsChangedAt.value = discountsChangedAt.value + 1;
|
|
1086
2373
|
}
|
|
1087
|
-
async function refreshAfterItemChange(
|
|
1088
|
-
await
|
|
1089
|
-
|
|
2374
|
+
async function refreshAfterItemChange() {
|
|
2375
|
+
await reload();
|
|
2376
|
+
itemsChangedAt.value = itemsChangedAt.value + 1;
|
|
1090
2377
|
}
|
|
1091
2378
|
function subscriptionIdFromCurrent() {
|
|
1092
2379
|
var _a;
|
|
1093
2380
|
return ((_a = current.value) == null ? void 0 : _a.id) ?? null;
|
|
1094
2381
|
}
|
|
2382
|
+
async function reload() {
|
|
2383
|
+
var _a, _b;
|
|
2384
|
+
const id = (_a = current.value) == null ? void 0 : _a.id;
|
|
2385
|
+
if (!id) return;
|
|
2386
|
+
const [subResult, lastOrderResult] = await Promise.all([
|
|
2387
|
+
adapter.getById(id),
|
|
2388
|
+
adapter.getLastOrder({ subscriptionId: id })
|
|
2389
|
+
]);
|
|
2390
|
+
if (((_b = current.value) == null ? void 0 : _b.id) !== id) return;
|
|
2391
|
+
if (subResult._tag === "Success") current.value = subResult.data;
|
|
2392
|
+
if (lastOrderResult._tag === "Success") lastOrder.value = lastOrderResult.data;
|
|
2393
|
+
}
|
|
1095
2394
|
const service = {
|
|
2395
|
+
current,
|
|
2396
|
+
hasPendingBilling,
|
|
2397
|
+
discountsChangedAt,
|
|
2398
|
+
itemsChangedAt,
|
|
2399
|
+
lastOrder,
|
|
1096
2400
|
search: adapter.search,
|
|
1097
2401
|
getById: adapter.getById,
|
|
1098
2402
|
getCurrentSubscriptionId,
|
|
1099
2403
|
addItem: async (subscriptionId, item) => {
|
|
1100
2404
|
const result = await adapter.addItem({ subscriptionId, item });
|
|
1101
2405
|
if (result._tag === "Success") {
|
|
1102
|
-
await refreshAfterItemChange(
|
|
2406
|
+
await refreshAfterItemChange();
|
|
1103
2407
|
}
|
|
1104
2408
|
return result;
|
|
1105
2409
|
},
|
|
1106
2410
|
removeItem: async (subscriptionId, itemId) => {
|
|
1107
2411
|
const result = await adapter.removeItem({ subscriptionId, itemId });
|
|
1108
2412
|
if (result._tag === "Success") {
|
|
1109
|
-
await refreshAfterItemChange(
|
|
2413
|
+
await refreshAfterItemChange();
|
|
2414
|
+
}
|
|
2415
|
+
return result;
|
|
2416
|
+
},
|
|
2417
|
+
updateItem: async (subscriptionId, lineId, update) => {
|
|
2418
|
+
const result = await adapter.updateItem({ subscriptionId, lineId, update });
|
|
2419
|
+
if (result._tag === "Success") {
|
|
2420
|
+
await refreshAfterItemChange();
|
|
2421
|
+
}
|
|
2422
|
+
return result;
|
|
2423
|
+
},
|
|
2424
|
+
skipLineItem: async (subscriptionId, lineId, nextBillingDate) => {
|
|
2425
|
+
const result = await adapter.skipLineItem({ subscriptionId, lineId, nextBillingDate });
|
|
2426
|
+
if (result._tag === "Success") {
|
|
2427
|
+
await refreshAfterItemChange();
|
|
2428
|
+
}
|
|
2429
|
+
return result;
|
|
2430
|
+
},
|
|
2431
|
+
updateBoxItems: async (subscriptionId, params) => {
|
|
2432
|
+
const result = await adapter.updateBoxItems({ subscriptionId, ...params });
|
|
2433
|
+
if (result._tag === "Success") {
|
|
2434
|
+
await refreshAfterItemChange();
|
|
1110
2435
|
}
|
|
1111
2436
|
return result;
|
|
1112
2437
|
},
|
|
@@ -1120,7 +2445,7 @@ function createSubscriptionService(adapter) {
|
|
|
1120
2445
|
}
|
|
1121
2446
|
const result = await adapter.addDiscount({ subscriptionId: id, code });
|
|
1122
2447
|
if (result._tag === "Success") {
|
|
1123
|
-
await refreshAfterDiscountChange(
|
|
2448
|
+
await refreshAfterDiscountChange();
|
|
1124
2449
|
}
|
|
1125
2450
|
return result;
|
|
1126
2451
|
},
|
|
@@ -1137,7 +2462,7 @@ function createSubscriptionService(adapter) {
|
|
|
1137
2462
|
discountId
|
|
1138
2463
|
});
|
|
1139
2464
|
if (result._tag === "Success") {
|
|
1140
|
-
await refreshAfterDiscountChange(
|
|
2465
|
+
await refreshAfterDiscountChange();
|
|
1141
2466
|
}
|
|
1142
2467
|
return result;
|
|
1143
2468
|
},
|
|
@@ -1146,11 +2471,13 @@ function createSubscriptionService(adapter) {
|
|
|
1146
2471
|
pause: (subscriptionId) => adapter.pause({ subscriptionId }),
|
|
1147
2472
|
reactivate: (subscriptionId) => adapter.reactivate({ subscriptionId }),
|
|
1148
2473
|
cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
|
|
2474
|
+
renewNow: (params) => adapter.renewNow(params),
|
|
2475
|
+
changeFrequency: (subscriptionId, plan) => adapter.changeFrequency({ subscriptionId, plan }),
|
|
2476
|
+
reload,
|
|
1149
2477
|
getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
|
|
1150
2478
|
getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
itemsChangedAt: itemsChangedAtSignal
|
|
2479
|
+
getUpsellProducts: (params) => adapter.getUpsellProducts ? adapter.getUpsellProducts(params) : Promise.resolve({ _tag: "Success", data: [] }),
|
|
2480
|
+
addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
|
|
1154
2481
|
};
|
|
1155
2482
|
return service;
|
|
1156
2483
|
}
|
|
@@ -1420,65 +2747,85 @@ export {
|
|
|
1420
2747
|
AuthenticationErrorReason,
|
|
1421
2748
|
B as BlockWrapperEvent,
|
|
1422
2749
|
C as ContextProvideEvent,
|
|
1423
|
-
|
|
2750
|
+
e as ContextRequestEvent,
|
|
1424
2751
|
CustomerServiceContext,
|
|
1425
|
-
|
|
2752
|
+
D2 as DEFAULT_GLOBAL_STYLES,
|
|
1426
2753
|
DISCOUNT_REJECTION_REASONS,
|
|
1427
2754
|
LoginServiceContext,
|
|
1428
2755
|
OrdersServiceContext,
|
|
1429
2756
|
ProductServiceContext,
|
|
1430
2757
|
RouterServiceContext,
|
|
2758
|
+
SchedulesServiceContext,
|
|
1431
2759
|
S as SelectedBlockContext,
|
|
2760
|
+
SettingsServiceContext,
|
|
2761
|
+
ShopServiceContext,
|
|
1432
2762
|
u as Signal,
|
|
1433
2763
|
StateContext,
|
|
1434
2764
|
SubscriptionServiceContext,
|
|
1435
2765
|
T as ThemeStateContext,
|
|
1436
|
-
|
|
2766
|
+
a as TranslationContext,
|
|
1437
2767
|
W as WorkflowServiceContext,
|
|
1438
|
-
|
|
1439
|
-
f as
|
|
2768
|
+
_ as __recordDevtoolsInstance,
|
|
2769
|
+
f as __recordDevtoolsPropSetter,
|
|
2770
|
+
g as attachEmptyTracking,
|
|
2771
|
+
h as backfillInstanceDefaults,
|
|
1440
2772
|
w as computed,
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
2773
|
+
createApiCustomerAdapter,
|
|
2774
|
+
createApiLoginAdapter,
|
|
2775
|
+
createApiProductAdapter,
|
|
2776
|
+
createApiSchedulesAdapter,
|
|
2777
|
+
createApiSubscriptionAdapter,
|
|
2778
|
+
createBlockInstance,
|
|
2779
|
+
i as createBlockInstanceFromJSON,
|
|
2780
|
+
j as createBlockInstanceFromObject,
|
|
2781
|
+
k as createCeRenderer,
|
|
1445
2782
|
createContext,
|
|
1446
2783
|
createCustomerService,
|
|
2784
|
+
createLoginService,
|
|
2785
|
+
createMockCustomerAdapter,
|
|
2786
|
+
createMockLoginAdapter,
|
|
1447
2787
|
createMockOrdersAdapter,
|
|
1448
2788
|
createMockProductAdapter,
|
|
1449
2789
|
createMockRouterService,
|
|
2790
|
+
createMockSchedulesAdapter,
|
|
1450
2791
|
createMockSubscriptionAdapter,
|
|
1451
2792
|
createMockWorkflowApiAdapter,
|
|
1452
2793
|
createOrdersService,
|
|
1453
2794
|
createProductService,
|
|
1454
2795
|
createRouterService,
|
|
1455
|
-
|
|
2796
|
+
createSchedulesService,
|
|
2797
|
+
l as createSelectedBlock,
|
|
2798
|
+
createSettingsService,
|
|
2799
|
+
createShopService,
|
|
1456
2800
|
createState,
|
|
1457
2801
|
createSubscriptionService,
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
2802
|
+
c as createThemeState,
|
|
2803
|
+
b as createTranslationService,
|
|
2804
|
+
d2 as createWorkflowApiAdapter,
|
|
2805
|
+
e2 as createWorkflowService,
|
|
2806
|
+
defineBlock,
|
|
2807
|
+
m as defineBlockRuntime,
|
|
2808
|
+
defineOverlayWrapper,
|
|
1464
2809
|
E as effect,
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
2810
|
+
g2 as getDefaultBlockForAction,
|
|
2811
|
+
n as getDefinedBlocks,
|
|
2812
|
+
o as getDevtoolsBlockState,
|
|
2813
|
+
h2 as hasDefaultBlockForAction,
|
|
2814
|
+
p as hasRenderedContent,
|
|
2815
|
+
i2 as indexOverrides,
|
|
2816
|
+
q as injectContext,
|
|
2817
|
+
s as isEditorPreview,
|
|
2818
|
+
t as isStubDefinition,
|
|
1473
2819
|
loadBlockRegistry,
|
|
1474
2820
|
loginService,
|
|
1475
|
-
|
|
1476
|
-
|
|
2821
|
+
v as onRegisterBlock,
|
|
2822
|
+
x as provideContext,
|
|
1477
2823
|
registerBlock,
|
|
1478
|
-
|
|
2824
|
+
y as serializeBlocks,
|
|
2825
|
+
z as setDevtoolsBlockProp,
|
|
1479
2826
|
d as signal,
|
|
1480
|
-
|
|
1481
|
-
|
|
2827
|
+
A as subscribeContext,
|
|
2828
|
+
D as untracked,
|
|
1482
2829
|
waitForDevBlocksUrl,
|
|
1483
|
-
|
|
2830
|
+
F as withWrapper
|
|
1484
2831
|
};
|