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