@juo/blocks 1.1.0-alpha.5 → 1.1.0-alpha.6
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 +518 -606
- package/dist/services/customer/adapter.d.ts +1 -6
- package/dist/services/customer/adapter.d.ts.map +1 -1
- package/dist/services/customer/customerService.d.ts.map +1 -1
- package/dist/services/customer/index.d.ts +1 -1
- package/dist/services/customer/index.d.ts.map +1 -1
- package/dist/services/customer/types.d.ts +0 -6
- package/dist/services/customer/types.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 +2 -1
- package/dist/services/login/index.d.ts.map +1 -1
- package/dist/services/login/loginService.d.ts +4 -2
- package/dist/services/login/loginService.d.ts.map +1 -1
- package/dist/services/order/adapter.d.ts +1 -4
- package/dist/services/order/adapter.d.ts.map +1 -1
- package/dist/services/order/orderService.d.ts.map +1 -1
- package/dist/services/order/types.d.ts +0 -4
- package/dist/services/order/types.d.ts.map +1 -1
- 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/schedules/adapter.d.ts +2 -0
- package/dist/services/schedules/adapter.d.ts.map +1 -1
- package/dist/services/schedules/types.d.ts +2 -0
- package/dist/services/schedules/types.d.ts.map +1 -1
- package/dist/services/subscription/adapter.d.ts +0 -7
- package/dist/services/subscription/adapter.d.ts.map +1 -1
- package/dist/services/subscription/subscriptionService.d.ts +0 -4
- package/dist/services/subscription/subscriptionService.d.ts.map +1 -1
- package/dist/services/subscription/types.d.ts +8 -15
- package/dist/services/subscription/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -155,6 +155,122 @@ function uuid() {
|
|
|
155
155
|
(c2) => (c2 ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c2 / 4).toString(16)
|
|
156
156
|
);
|
|
157
157
|
}
|
|
158
|
+
function createMockLoginAdapter() {
|
|
159
|
+
return {
|
|
160
|
+
async passwordlessStart() {
|
|
161
|
+
return { _tag: "Success", data: void 0 };
|
|
162
|
+
},
|
|
163
|
+
async authenticate() {
|
|
164
|
+
return {
|
|
165
|
+
_tag: "Success",
|
|
166
|
+
data: {
|
|
167
|
+
access_token: "mock-access-token",
|
|
168
|
+
refresh_token: "mock-refresh-token",
|
|
169
|
+
expires_in: 3600
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
getAuthorizeUrl({ connection, state, shop, appUrl }) {
|
|
174
|
+
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
175
|
+
url.searchParams.set("connection", connection);
|
|
176
|
+
url.searchParams.set("state", state);
|
|
177
|
+
url.searchParams.set("shop", shop);
|
|
178
|
+
return url.toString();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function createApiLoginAdapter(fetcher, opts) {
|
|
183
|
+
const { shopDomain, appUrl } = opts;
|
|
184
|
+
async function callAuthEndpoint(path, body, lang) {
|
|
185
|
+
const url = lang ? `${path}?lang=${lang}` : path;
|
|
186
|
+
const res = await fetcher(url, {
|
|
187
|
+
method: "POST",
|
|
188
|
+
headers: {
|
|
189
|
+
Accept: "application/json",
|
|
190
|
+
"Content-Type": "application/json",
|
|
191
|
+
"X-Shopify-Shop-Domain": shopDomain
|
|
192
|
+
},
|
|
193
|
+
body: JSON.stringify(body)
|
|
194
|
+
});
|
|
195
|
+
if (res.status === 403) {
|
|
196
|
+
const data = await res.json();
|
|
197
|
+
if ("error_description" in data) {
|
|
198
|
+
if (data.error_description === "Wrong email or verification code.") {
|
|
199
|
+
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
200
|
+
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
201
|
+
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
202
|
+
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
203
|
+
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
204
|
+
}
|
|
205
|
+
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
206
|
+
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
207
|
+
connection: data.connection
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (!res.ok) {
|
|
212
|
+
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
213
|
+
}
|
|
214
|
+
return { _tag: "Success", data: void 0 };
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
async passwordlessStart({ email, lang }) {
|
|
218
|
+
try {
|
|
219
|
+
await callAuthEndpoint(
|
|
220
|
+
"/auth/customer/passwordless/start",
|
|
221
|
+
{ email },
|
|
222
|
+
lang
|
|
223
|
+
);
|
|
224
|
+
return { _tag: "Success", data: void 0 };
|
|
225
|
+
} catch (e2) {
|
|
226
|
+
return { _tag: "Failure", error: e2 };
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
async authenticate(params) {
|
|
230
|
+
try {
|
|
231
|
+
const res = await fetcher("/auth/customer/token", {
|
|
232
|
+
method: "POST",
|
|
233
|
+
headers: {
|
|
234
|
+
Accept: "application/json",
|
|
235
|
+
"Content-Type": "application/json",
|
|
236
|
+
"X-Shopify-Shop-Domain": shopDomain
|
|
237
|
+
},
|
|
238
|
+
body: JSON.stringify(params)
|
|
239
|
+
});
|
|
240
|
+
if (res.status === 403) {
|
|
241
|
+
const data = await res.json();
|
|
242
|
+
if ("error_description" in data) {
|
|
243
|
+
if (data.error_description === "Wrong email or verification code.") {
|
|
244
|
+
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
245
|
+
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
246
|
+
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
247
|
+
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
248
|
+
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
249
|
+
}
|
|
250
|
+
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
251
|
+
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
252
|
+
connection: data.connection
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (!res.ok) {
|
|
257
|
+
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
258
|
+
}
|
|
259
|
+
return { _tag: "Success", data: await res.json() };
|
|
260
|
+
} catch (e2) {
|
|
261
|
+
return { _tag: "Failure", error: e2 };
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
getAuthorizeUrl({ connection, state, shop }) {
|
|
265
|
+
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
266
|
+
url.searchParams.set("connection", connection);
|
|
267
|
+
url.searchParams.set("state", state);
|
|
268
|
+
url.searchParams.set("shop", shop);
|
|
269
|
+
return url.toString();
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
const __vite_import_meta_env__ = {};
|
|
158
274
|
var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
|
|
159
275
|
AuthenticationErrorReason2["Expired"] = "expired";
|
|
160
276
|
AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
|
|
@@ -173,6 +289,9 @@ class AuthenticationError extends Error {
|
|
|
173
289
|
this.data = data ?? {};
|
|
174
290
|
}
|
|
175
291
|
}
|
|
292
|
+
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
293
|
+
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
294
|
+
const STORAGE_STATE_KEY = "state";
|
|
176
295
|
function storage(key, value) {
|
|
177
296
|
const storageKey = `auth:${key}`;
|
|
178
297
|
try {
|
|
@@ -188,83 +307,33 @@ function storage(key, value) {
|
|
|
188
307
|
}
|
|
189
308
|
return null;
|
|
190
309
|
}
|
|
191
|
-
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
192
|
-
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
193
|
-
const STORAGE_STATE_KEY = "state";
|
|
194
|
-
function createAuthFunctions(basePath, shop) {
|
|
195
|
-
async function start(email, lang) {
|
|
196
|
-
const response = await fetch(
|
|
197
|
-
`${basePath}/auth/customer/passwordless/start?lang=${lang}`,
|
|
198
|
-
{
|
|
199
|
-
method: "POST",
|
|
200
|
-
headers: {
|
|
201
|
-
Accept: "application/json",
|
|
202
|
-
"Content-Type": "application/json",
|
|
203
|
-
"X-Shopify-Shop-Domain": shop
|
|
204
|
-
},
|
|
205
|
-
body: JSON.stringify({ email })
|
|
206
|
-
}
|
|
207
|
-
);
|
|
208
|
-
if (response.status !== 200) {
|
|
209
|
-
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
async function authenticate(data) {
|
|
213
|
-
const response = await fetch(`${basePath}/auth/customer/token`, {
|
|
214
|
-
method: "POST",
|
|
215
|
-
headers: {
|
|
216
|
-
Accept: "application/json",
|
|
217
|
-
"Content-Type": "application/json",
|
|
218
|
-
"X-Shopify-Shop-Domain": shop
|
|
219
|
-
},
|
|
220
|
-
body: JSON.stringify(data)
|
|
221
|
-
});
|
|
222
|
-
if (response.status === 403) {
|
|
223
|
-
const data2 = await response.json();
|
|
224
|
-
if ("error_description" in data2) {
|
|
225
|
-
if (data2.error_description === "Wrong email or verification code.") {
|
|
226
|
-
throw new AuthenticationError(
|
|
227
|
-
"wrong-code"
|
|
228
|
-
/* IncorrectCode */
|
|
229
|
-
);
|
|
230
|
-
} else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
231
|
-
throw new AuthenticationError(
|
|
232
|
-
"too-many-tries"
|
|
233
|
-
/* TooManyTries */
|
|
234
|
-
);
|
|
235
|
-
} else if (data2.error_description === "The verification code has expired. Please try to login again.") {
|
|
236
|
-
throw new AuthenticationError(
|
|
237
|
-
"expired"
|
|
238
|
-
/* Expired */
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
} else if ("code" in data2) {
|
|
242
|
-
if (data2.code === "AUTH_ERROR") {
|
|
243
|
-
throw new AuthenticationError("no-email", {
|
|
244
|
-
connection: data2.connection
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
if (response.status !== 200) {
|
|
250
|
-
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
251
|
-
}
|
|
252
|
-
return await response.json();
|
|
253
|
-
}
|
|
254
|
-
return { start, authenticate };
|
|
255
|
-
}
|
|
256
310
|
function nonce(len = 10) {
|
|
257
311
|
return uuid().replace(/-/g, "").slice(0, len);
|
|
258
312
|
}
|
|
259
|
-
function
|
|
260
|
-
const
|
|
313
|
+
function loginService(deps) {
|
|
314
|
+
const apiBase = deps.shop.rootUrl.replace(/\/$/, "") + "/api";
|
|
315
|
+
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";
|
|
316
|
+
const fetcher = (path, init) => fetch(`${apiBase}${path}`, init);
|
|
317
|
+
const adapter = createApiLoginAdapter(fetcher, {
|
|
318
|
+
shopDomain: deps.shop.domain,
|
|
319
|
+
appUrl
|
|
320
|
+
});
|
|
321
|
+
return createLoginService({
|
|
322
|
+
adapter,
|
|
323
|
+
router: deps.router,
|
|
324
|
+
formatError: deps.formatError,
|
|
325
|
+
shop: {
|
|
326
|
+
domain: deps.shop.domain,
|
|
327
|
+
locale: w(() => deps.getLocale())
|
|
328
|
+
},
|
|
329
|
+
onLoginSuccess: deps.onLoginSuccess
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function createLoginService(init) {
|
|
333
|
+
const { adapter, formatError, router, shop, onLoginSuccess } = init;
|
|
261
334
|
const accessToken = d(null);
|
|
262
|
-
const delegatedToken = d(
|
|
263
|
-
|
|
264
|
-
);
|
|
265
|
-
const refreshToken = d(
|
|
266
|
-
storage(STORAGE_REFRESH_TOKEN_KEY)
|
|
267
|
-
);
|
|
335
|
+
const delegatedToken = d(storage(STORAGE_DELEGATED_TOKEN));
|
|
336
|
+
const refreshToken = d(storage(STORAGE_REFRESH_TOKEN_KEY));
|
|
268
337
|
const expires = d(null);
|
|
269
338
|
const state = d(storage(STORAGE_STATE_KEY));
|
|
270
339
|
E(() => {
|
|
@@ -276,67 +345,28 @@ function createAuthService(apiUrl, shopDomain) {
|
|
|
276
345
|
E(() => {
|
|
277
346
|
storage(STORAGE_STATE_KEY, state.value);
|
|
278
347
|
});
|
|
279
|
-
function
|
|
348
|
+
function storeTokens(data) {
|
|
280
349
|
const { access_token, refresh_token, expires_in } = data;
|
|
281
350
|
accessToken.value = access_token;
|
|
282
351
|
refreshToken.value = refresh_token;
|
|
283
352
|
expires.value = new Date(Date.now() + expires_in);
|
|
284
353
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
},
|
|
293
|
-
getDelegatedToken() {
|
|
294
|
-
return delegatedToken.value;
|
|
295
|
-
},
|
|
296
|
-
setDelegatedToken(token) {
|
|
297
|
-
delegatedToken.value = token;
|
|
298
|
-
},
|
|
299
|
-
async getToken() {
|
|
300
|
-
if (refreshToken.value == null) {
|
|
301
|
-
return null;
|
|
302
|
-
}
|
|
303
|
-
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
304
|
-
accessToken.value = new Promise(
|
|
305
|
-
(resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
|
|
306
|
-
store(data);
|
|
307
|
-
resolve(data.access_token);
|
|
354
|
+
async function getToken() {
|
|
355
|
+
if (refreshToken.value == null) return null;
|
|
356
|
+
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
357
|
+
accessToken.value = new Promise(
|
|
358
|
+
(resolve, reject) => adapter.authenticate({ refreshToken: refreshToken.value }).then((result) => {
|
|
359
|
+
if (result._tag === "Failure") {
|
|
360
|
+
reject(result.error);
|
|
308
361
|
return;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
refreshToken.value = null;
|
|
318
|
-
expires.value = null;
|
|
319
|
-
},
|
|
320
|
-
isAuthenticated: w(
|
|
321
|
-
() => delegatedToken.value != null || refreshToken.value != null
|
|
322
|
-
)
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
function loginService(deps) {
|
|
326
|
-
return createLoginService({
|
|
327
|
-
apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
|
|
328
|
-
router: deps.router,
|
|
329
|
-
formatError: deps.formatError,
|
|
330
|
-
shop: {
|
|
331
|
-
domain: deps.shop.domain,
|
|
332
|
-
locale: w(() => deps.getLocale())
|
|
333
|
-
},
|
|
334
|
-
onLoginSuccess: deps.onLoginSuccess
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
function createLoginService(init) {
|
|
338
|
-
const { formatError, router, shop, onLoginSuccess } = init;
|
|
339
|
-
const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
|
|
362
|
+
}
|
|
363
|
+
storeTokens(result.data);
|
|
364
|
+
resolve(result.data.access_token);
|
|
365
|
+
}).catch(reject)
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
return Promise.resolve(accessToken.value);
|
|
369
|
+
}
|
|
340
370
|
const userEmail = d(null);
|
|
341
371
|
async function onSuccess(query) {
|
|
342
372
|
await (onLoginSuccess == null ? void 0 : onLoginSuccess());
|
|
@@ -349,13 +379,9 @@ function createLoginService(init) {
|
|
|
349
379
|
queryParams[key] = value;
|
|
350
380
|
}
|
|
351
381
|
});
|
|
352
|
-
router.push({
|
|
353
|
-
path: decodeURIComponent(redirect),
|
|
354
|
-
query: queryParams
|
|
355
|
-
});
|
|
382
|
+
router.push({ path: decodeURIComponent(redirect), query: queryParams });
|
|
356
383
|
} else {
|
|
357
|
-
|
|
358
|
-
router.replace(decodeURIComponent(redirect));
|
|
384
|
+
router.replace(decodeURIComponent(query.get(PARAMS.REDIRECT_TO)));
|
|
359
385
|
}
|
|
360
386
|
} else {
|
|
361
387
|
router.replace("/");
|
|
@@ -367,16 +393,17 @@ function createLoginService(init) {
|
|
|
367
393
|
async start(email) {
|
|
368
394
|
try {
|
|
369
395
|
userEmail.value = email;
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
396
|
+
const lang = shop.locale instanceof u ? shop.locale.value : shop.locale;
|
|
397
|
+
const result = await adapter.passwordlessStart({
|
|
398
|
+
email: userEmail.value,
|
|
399
|
+
lang
|
|
400
|
+
});
|
|
401
|
+
if (result._tag === "Failure") {
|
|
402
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
403
|
+
}
|
|
374
404
|
return { _tag: "Success", data: void 0 };
|
|
375
405
|
} catch (error) {
|
|
376
|
-
return {
|
|
377
|
-
_tag: "Failure",
|
|
378
|
-
error: formatError(error)
|
|
379
|
-
};
|
|
406
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
380
407
|
}
|
|
381
408
|
},
|
|
382
409
|
async verify(email, code) {
|
|
@@ -387,63 +414,61 @@ function createLoginService(init) {
|
|
|
387
414
|
};
|
|
388
415
|
}
|
|
389
416
|
try {
|
|
390
|
-
const result = await
|
|
417
|
+
const result = await adapter.authenticate({
|
|
391
418
|
email: userEmail.value,
|
|
392
419
|
code
|
|
393
420
|
});
|
|
421
|
+
if (result._tag === "Failure") {
|
|
422
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
423
|
+
}
|
|
424
|
+
storeTokens(result.data);
|
|
394
425
|
await onSuccess();
|
|
395
|
-
return { _tag: "Success", data:
|
|
426
|
+
return { _tag: "Success", data: void 0 };
|
|
396
427
|
} catch (error) {
|
|
397
|
-
return {
|
|
398
|
-
_tag: "Failure",
|
|
399
|
-
error: formatError(error)
|
|
400
|
-
};
|
|
428
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
401
429
|
}
|
|
402
430
|
}
|
|
403
431
|
}
|
|
404
432
|
},
|
|
405
433
|
social: {
|
|
406
434
|
async start(provider) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
provider === "Facebook" ? "facebook" : "google-oauth2",
|
|
420
|
-
shop.domain
|
|
421
|
-
);
|
|
422
|
-
return {
|
|
423
|
-
_tag: "Success",
|
|
424
|
-
data: void 0
|
|
425
|
-
};
|
|
435
|
+
state.value = nonce();
|
|
436
|
+
const connection = provider === "Facebook" ? "facebook" : "google-oauth2";
|
|
437
|
+
window.location.href = adapter.getAuthorizeUrl({
|
|
438
|
+
connection,
|
|
439
|
+
state: state.value ?? "",
|
|
440
|
+
shop: shop.domain,
|
|
441
|
+
appUrl: (
|
|
442
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
443
|
+
(typeof import.meta !== "undefined" ? __vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_APP_URL : void 0) ?? "https://admin.juo.io"
|
|
444
|
+
)
|
|
445
|
+
});
|
|
446
|
+
return { _tag: "Success", data: void 0 };
|
|
426
447
|
},
|
|
427
448
|
async verify(code) {
|
|
428
449
|
try {
|
|
429
|
-
const result = await
|
|
450
|
+
const result = await adapter.authenticate({ code });
|
|
451
|
+
if (result._tag === "Failure") {
|
|
452
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
453
|
+
}
|
|
454
|
+
storeTokens(result.data);
|
|
430
455
|
await onSuccess();
|
|
431
|
-
return { _tag: "Success", data:
|
|
456
|
+
return { _tag: "Success", data: void 0 };
|
|
432
457
|
} catch (error) {
|
|
433
|
-
return {
|
|
434
|
-
_tag: "Failure",
|
|
435
|
-
error: formatError(error)
|
|
436
|
-
};
|
|
458
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
437
459
|
}
|
|
438
460
|
}
|
|
439
461
|
},
|
|
440
462
|
async logout() {
|
|
441
|
-
|
|
463
|
+
accessToken.value = null;
|
|
464
|
+
delegatedToken.value = null;
|
|
465
|
+
refreshToken.value = null;
|
|
466
|
+
expires.value = null;
|
|
442
467
|
return { _tag: "Success", data: void 0 };
|
|
443
468
|
},
|
|
444
469
|
async handleQueryParams(query) {
|
|
445
470
|
if (query.has(PARAMS.DELETGATED_TOKEN)) {
|
|
446
|
-
|
|
471
|
+
delegatedToken.value = query.get(PARAMS.DELETGATED_TOKEN);
|
|
447
472
|
await onSuccess(query);
|
|
448
473
|
return { _tag: "Success", data: void 0 };
|
|
449
474
|
}
|
|
@@ -455,16 +480,13 @@ function createLoginService(init) {
|
|
|
455
480
|
}
|
|
456
481
|
const code = query.get(PARAMS.CODE);
|
|
457
482
|
if (code != null) {
|
|
458
|
-
if (query.get(PARAMS.STATE) !==
|
|
459
|
-
return {
|
|
460
|
-
_tag: "Failure",
|
|
461
|
-
error: formatError("Invalid state")
|
|
462
|
-
};
|
|
483
|
+
if (query.get(PARAMS.STATE) !== state.value) {
|
|
484
|
+
return { _tag: "Failure", error: formatError("Invalid state") };
|
|
463
485
|
} else {
|
|
464
486
|
return service.social.verify(code);
|
|
465
487
|
}
|
|
466
488
|
}
|
|
467
|
-
if (query.get(PARAMS.REFRESH) === "1" && await
|
|
489
|
+
if (query.get(PARAMS.REFRESH) === "1" && await getToken() != null) {
|
|
468
490
|
await onSuccess();
|
|
469
491
|
return { _tag: "Success", data: void 0 };
|
|
470
492
|
}
|
|
@@ -481,14 +503,15 @@ function createLoginService(init) {
|
|
|
481
503
|
}
|
|
482
504
|
return { _tag: "Success", data: void 0 };
|
|
483
505
|
},
|
|
484
|
-
isAuthenticated:
|
|
485
|
-
|
|
506
|
+
isAuthenticated: w(
|
|
507
|
+
() => delegatedToken.value != null || refreshToken.value != null
|
|
508
|
+
),
|
|
509
|
+
getToken,
|
|
486
510
|
async getAuthHeaders() {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
return { "X-Delegated-Token": delegated };
|
|
511
|
+
if (delegatedToken.value) {
|
|
512
|
+
return { "X-Delegated-Token": delegatedToken.value };
|
|
490
513
|
}
|
|
491
|
-
const token = await
|
|
514
|
+
const token = await getToken();
|
|
492
515
|
if (token) {
|
|
493
516
|
return { Authorization: `Bearer ${token}` };
|
|
494
517
|
}
|
|
@@ -678,6 +701,106 @@ function createMockProductAdapter() {
|
|
|
678
701
|
}
|
|
679
702
|
};
|
|
680
703
|
}
|
|
704
|
+
function mapVariant(variant, currencyCode) {
|
|
705
|
+
var _a;
|
|
706
|
+
return {
|
|
707
|
+
id: variant.id,
|
|
708
|
+
title: variant.title,
|
|
709
|
+
image: variant.image == null ? null : { url: variant.image.url, altText: variant.image.altText ?? void 0 },
|
|
710
|
+
price: { amount: variant.price.toFixed(2), currencyCode },
|
|
711
|
+
...((_a = variant.purchaseOptions) == null ? void 0 : _a.length) ? {
|
|
712
|
+
purchaseOptions: variant.purchaseOptions.map((opt) => ({
|
|
713
|
+
name: opt.name,
|
|
714
|
+
price: { amount: opt.price.toFixed(2), currencyCode },
|
|
715
|
+
interval: opt.interval
|
|
716
|
+
}))
|
|
717
|
+
} : {}
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
function mapProduct(product, currencyCode) {
|
|
721
|
+
return {
|
|
722
|
+
id: product.id,
|
|
723
|
+
title: product.title,
|
|
724
|
+
featuredImage: product.image == null ? null : { url: product.image.url, altText: product.image.altText ?? void 0 },
|
|
725
|
+
hasOnlyDefaultVariant: product.variants.length <= 1,
|
|
726
|
+
variants: {
|
|
727
|
+
items: product.variants.map((v2) => mapVariant(v2, currencyCode)),
|
|
728
|
+
pageInfo: {
|
|
729
|
+
hasNextPage: false,
|
|
730
|
+
hasPrevPage: false,
|
|
731
|
+
startCursor: null,
|
|
732
|
+
endCursor: null
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
collections: product.collections
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
function createApiProductAdapter(fetcher, opts = { currencyCode: "USD" }) {
|
|
739
|
+
const { currencyCode } = opts;
|
|
740
|
+
async function fetchProducts(params) {
|
|
741
|
+
const query = params.toString() ? `?${params.toString()}` : "";
|
|
742
|
+
const res = await fetcher(`/customer/v1/products${query}`);
|
|
743
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
744
|
+
return res.json();
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
async getById(id) {
|
|
748
|
+
try {
|
|
749
|
+
const params = new URLSearchParams({ query: `id:"${id}"`, limit: "1" });
|
|
750
|
+
const body = await fetchProducts(params);
|
|
751
|
+
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
752
|
+
} catch {
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
},
|
|
756
|
+
async getByVariantId(variantId) {
|
|
757
|
+
try {
|
|
758
|
+
const params = new URLSearchParams({
|
|
759
|
+
query: `variants.id:"${variantId}"`,
|
|
760
|
+
limit: "1"
|
|
761
|
+
});
|
|
762
|
+
const body = await fetchProducts(params);
|
|
763
|
+
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
764
|
+
} catch {
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
async search(opts2) {
|
|
769
|
+
try {
|
|
770
|
+
const params = new URLSearchParams();
|
|
771
|
+
if (opts2 == null ? void 0 : opts2.query) {
|
|
772
|
+
params.set("query", `"${opts2.query.trim().replace(/"/g, '\\"')}"`);
|
|
773
|
+
}
|
|
774
|
+
if (opts2 == null ? void 0 : opts2.pagination) {
|
|
775
|
+
if ("after" in opts2.pagination)
|
|
776
|
+
params.set("after", opts2.pagination.after);
|
|
777
|
+
else if ("before" in opts2.pagination)
|
|
778
|
+
params.set("before", opts2.pagination.before);
|
|
779
|
+
}
|
|
780
|
+
const body = await fetchProducts(params);
|
|
781
|
+
return {
|
|
782
|
+
items: body.data.map((p2) => mapProduct(p2, currencyCode)),
|
|
783
|
+
pageInfo: {
|
|
784
|
+
hasNextPage: body.hasNextPage,
|
|
785
|
+
hasPrevPage: body.hasPrevPage,
|
|
786
|
+
startCursor: body.startCursor,
|
|
787
|
+
endCursor: body.endCursor
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
} catch {
|
|
791
|
+
return {
|
|
792
|
+
items: [],
|
|
793
|
+
pageInfo: {
|
|
794
|
+
hasNextPage: false,
|
|
795
|
+
hasPrevPage: false,
|
|
796
|
+
startCursor: null,
|
|
797
|
+
endCursor: null
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
681
804
|
const ProductServiceContext = createContext("services/product");
|
|
682
805
|
function createProductService(adapter) {
|
|
683
806
|
return {
|
|
@@ -759,14 +882,6 @@ function createMockSubscriptionAdapter() {
|
|
|
759
882
|
imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
|
|
760
883
|
price: { amount: "24.00", currencyCode: "USD" },
|
|
761
884
|
description: "Lightweight daily sun protection"
|
|
762
|
-
},
|
|
763
|
-
{
|
|
764
|
-
id: "upsell-3",
|
|
765
|
-
variantId: "upsell-v-3",
|
|
766
|
-
title: "Collagen Eye Patches",
|
|
767
|
-
imageUrl: "https://images.unsplash.com/photo-1512290923902-8a9f81dc236c?w=200",
|
|
768
|
-
price: { amount: "18.00", currencyCode: "USD" },
|
|
769
|
-
description: "Depuffing under-eye treatment"
|
|
770
885
|
}
|
|
771
886
|
];
|
|
772
887
|
return {
|
|
@@ -777,7 +892,7 @@ function createMockSubscriptionAdapter() {
|
|
|
777
892
|
},
|
|
778
893
|
async search(options = {}) {
|
|
779
894
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
780
|
-
const pageSize =
|
|
895
|
+
const pageSize = 2;
|
|
781
896
|
const allItems = mockSubscriptions;
|
|
782
897
|
let startIndex = 0;
|
|
783
898
|
let endIndex = pageSize;
|
|
@@ -793,20 +908,22 @@ function createMockSubscriptionAdapter() {
|
|
|
793
908
|
}
|
|
794
909
|
}
|
|
795
910
|
const pageItems = allItems.slice(startIndex, endIndex);
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
items:
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
911
|
+
return {
|
|
912
|
+
_tag: "Success",
|
|
913
|
+
data: {
|
|
914
|
+
items: pageItems.map((sub) => ({
|
|
915
|
+
...sub,
|
|
916
|
+
items: sub.items.map((item) => ({ ...item })),
|
|
917
|
+
discounts: sub.discounts.map((discount) => ({ ...discount }))
|
|
918
|
+
})),
|
|
919
|
+
pageInfo: {
|
|
920
|
+
hasNextPage: endIndex < allItems.length,
|
|
921
|
+
hasPrevPage: startIndex > 0,
|
|
922
|
+
startCursor: pageItems.length > 0 ? startIndex.toString() : null,
|
|
923
|
+
endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
|
|
924
|
+
}
|
|
807
925
|
}
|
|
808
926
|
};
|
|
809
|
-
return { _tag: "Success", data: paginatedList };
|
|
810
927
|
},
|
|
811
928
|
async addItem() {
|
|
812
929
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -830,26 +947,11 @@ function createMockSubscriptionAdapter() {
|
|
|
830
947
|
(sub) => sub.id === params.subscriptionId
|
|
831
948
|
);
|
|
832
949
|
if (!subscription) {
|
|
833
|
-
return {
|
|
834
|
-
_tag: "Failure",
|
|
835
|
-
error: "Subscription not found"
|
|
836
|
-
};
|
|
950
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
837
951
|
}
|
|
838
952
|
subscription.nextDeliveryDate = new Date(params.newDate);
|
|
839
953
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
840
|
-
subscription
|
|
841
|
-
item.nextDeliveryDate = new Date(params.newDate);
|
|
842
|
-
});
|
|
843
|
-
return {
|
|
844
|
-
_tag: "Success",
|
|
845
|
-
data: {
|
|
846
|
-
...subscription,
|
|
847
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
848
|
-
discounts: subscription.discounts.map((discount) => ({
|
|
849
|
-
...discount
|
|
850
|
-
}))
|
|
851
|
-
}
|
|
852
|
-
};
|
|
954
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
853
955
|
},
|
|
854
956
|
async resume(params) {
|
|
855
957
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -857,23 +959,11 @@ function createMockSubscriptionAdapter() {
|
|
|
857
959
|
(sub) => sub.id === params.subscriptionId
|
|
858
960
|
);
|
|
859
961
|
if (!subscription) {
|
|
860
|
-
return {
|
|
861
|
-
_tag: "Failure",
|
|
862
|
-
error: "Subscription not found"
|
|
863
|
-
};
|
|
962
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
864
963
|
}
|
|
865
964
|
subscription.status = "active";
|
|
866
965
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
867
|
-
return {
|
|
868
|
-
_tag: "Success",
|
|
869
|
-
data: {
|
|
870
|
-
...subscription,
|
|
871
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
872
|
-
discounts: subscription.discounts.map((discount) => ({
|
|
873
|
-
...discount
|
|
874
|
-
}))
|
|
875
|
-
}
|
|
876
|
-
};
|
|
966
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
877
967
|
},
|
|
878
968
|
async pause(params) {
|
|
879
969
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -881,23 +971,11 @@ function createMockSubscriptionAdapter() {
|
|
|
881
971
|
(sub) => sub.id === params.subscriptionId
|
|
882
972
|
);
|
|
883
973
|
if (!subscription) {
|
|
884
|
-
return {
|
|
885
|
-
_tag: "Failure",
|
|
886
|
-
error: "Subscription not found"
|
|
887
|
-
};
|
|
974
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
888
975
|
}
|
|
889
976
|
subscription.status = "paused";
|
|
890
977
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
891
|
-
return {
|
|
892
|
-
_tag: "Success",
|
|
893
|
-
data: {
|
|
894
|
-
...subscription,
|
|
895
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
896
|
-
discounts: subscription.discounts.map((discount) => ({
|
|
897
|
-
...discount
|
|
898
|
-
}))
|
|
899
|
-
}
|
|
900
|
-
};
|
|
978
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
901
979
|
},
|
|
902
980
|
async reactivate(params) {
|
|
903
981
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -905,23 +983,11 @@ function createMockSubscriptionAdapter() {
|
|
|
905
983
|
(sub) => sub.id === params.subscriptionId
|
|
906
984
|
);
|
|
907
985
|
if (!subscription) {
|
|
908
|
-
return {
|
|
909
|
-
_tag: "Failure",
|
|
910
|
-
error: "Subscription not found"
|
|
911
|
-
};
|
|
986
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
912
987
|
}
|
|
913
988
|
subscription.status = "active";
|
|
914
989
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
915
|
-
return {
|
|
916
|
-
_tag: "Success",
|
|
917
|
-
data: {
|
|
918
|
-
...subscription,
|
|
919
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
920
|
-
discounts: subscription.discounts.map((discount) => ({
|
|
921
|
-
...discount
|
|
922
|
-
}))
|
|
923
|
-
}
|
|
924
|
-
};
|
|
990
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
925
991
|
},
|
|
926
992
|
async cancel(params) {
|
|
927
993
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -929,23 +995,11 @@ function createMockSubscriptionAdapter() {
|
|
|
929
995
|
(sub) => sub.id === params.subscriptionId
|
|
930
996
|
);
|
|
931
997
|
if (!subscription) {
|
|
932
|
-
return {
|
|
933
|
-
_tag: "Failure",
|
|
934
|
-
error: "Subscription not found"
|
|
935
|
-
};
|
|
998
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
936
999
|
}
|
|
937
1000
|
subscription.status = "canceled";
|
|
938
1001
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
939
|
-
return {
|
|
940
|
-
_tag: "Success",
|
|
941
|
-
data: {
|
|
942
|
-
...subscription,
|
|
943
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
944
|
-
discounts: subscription.discounts.map((discount) => ({
|
|
945
|
-
...discount
|
|
946
|
-
}))
|
|
947
|
-
}
|
|
948
|
-
};
|
|
1002
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
949
1003
|
},
|
|
950
1004
|
async getAvailablePlans() {
|
|
951
1005
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -978,26 +1032,6 @@ function createMockSubscriptionAdapter() {
|
|
|
978
1032
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
979
1033
|
return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
|
|
980
1034
|
},
|
|
981
|
-
async getDeliveryFrequencies() {
|
|
982
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
983
|
-
return {
|
|
984
|
-
_tag: "Success",
|
|
985
|
-
data: [
|
|
986
|
-
{
|
|
987
|
-
id: "plan_1",
|
|
988
|
-
name: "Monthly",
|
|
989
|
-
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
990
|
-
deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
|
|
991
|
-
},
|
|
992
|
-
{
|
|
993
|
-
id: "plan_2",
|
|
994
|
-
name: "Every 2 months",
|
|
995
|
-
billingPolicy: { intervalCount: 2, interval: "MONTH" },
|
|
996
|
-
deliveryPolicy: { intervalCount: 2, interval: "MONTH" }
|
|
997
|
-
}
|
|
998
|
-
]
|
|
999
|
-
};
|
|
1000
|
-
},
|
|
1001
1035
|
async getUpsellProducts() {
|
|
1002
1036
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1003
1037
|
return { _tag: "Success", data: [...mockUpsellProducts] };
|
|
@@ -1005,74 +1039,96 @@ function createMockSubscriptionAdapter() {
|
|
|
1005
1039
|
async addUpsellProduct() {
|
|
1006
1040
|
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
1007
1041
|
return { _tag: "Success", data: void 0 };
|
|
1008
|
-
},
|
|
1009
|
-
async changeFrequency(params) {
|
|
1010
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1011
|
-
const subscription = mockSubscriptions.find(
|
|
1012
|
-
(sub) => sub.id === params.subscriptionId
|
|
1013
|
-
);
|
|
1014
|
-
if (!subscription) {
|
|
1015
|
-
return { _tag: "Failure", error: "Subscription not found" };
|
|
1016
|
-
}
|
|
1017
|
-
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1018
|
-
return {
|
|
1019
|
-
_tag: "Success",
|
|
1020
|
-
data: { ...subscription }
|
|
1021
|
-
};
|
|
1022
1042
|
}
|
|
1023
1043
|
};
|
|
1024
1044
|
}
|
|
1045
|
+
function mapSubscription(raw) {
|
|
1046
|
+
var _a, _b;
|
|
1047
|
+
const status = raw.status === "merged" ? "canceled" : raw.status;
|
|
1048
|
+
const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
|
|
1049
|
+
const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
|
|
1050
|
+
return {
|
|
1051
|
+
id: raw.id,
|
|
1052
|
+
customerId: "",
|
|
1053
|
+
items: (raw.items ?? []).map(
|
|
1054
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1055
|
+
(item) => ({
|
|
1056
|
+
id: item.id,
|
|
1057
|
+
productId: item.productId ?? item.product ?? "",
|
|
1058
|
+
variantId: item.variantId ?? item.variant ?? "",
|
|
1059
|
+
quantity: item.quantity,
|
|
1060
|
+
price: item.price ?? {
|
|
1061
|
+
amount: String(item.totalPrice ?? 0),
|
|
1062
|
+
currencyCode: raw.currencyCode ?? ""
|
|
1063
|
+
},
|
|
1064
|
+
nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null
|
|
1065
|
+
})
|
|
1066
|
+
),
|
|
1067
|
+
discounts: (raw.discounts ?? []).map(
|
|
1068
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1069
|
+
(d2) => {
|
|
1070
|
+
var _a2, _b2, _c;
|
|
1071
|
+
return {
|
|
1072
|
+
id: d2.id,
|
|
1073
|
+
type: ((_a2 = d2.value) == null ? void 0 : _a2.type) === "fixed-amount" || d2.type === "fixed_amount" ? "fixed_amount" : "percentage",
|
|
1074
|
+
value: ((_b2 = d2.value) == null ? void 0 : _b2.amount) ?? ((_c = d2.value) == null ? void 0 : _c.percentage) ?? d2.value ?? 0,
|
|
1075
|
+
code: d2.title ?? d2.code ?? "",
|
|
1076
|
+
description: d2.description ?? ""
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
),
|
|
1080
|
+
status,
|
|
1081
|
+
nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
|
|
1082
|
+
frequency,
|
|
1083
|
+
createdAt: raw.createdAt,
|
|
1084
|
+
updatedAt: raw.updatedAt
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1025
1087
|
function createApiSubscriptionAdapter(fetcher) {
|
|
1026
1088
|
return {
|
|
1027
1089
|
async search(options) {
|
|
1090
|
+
var _a;
|
|
1028
1091
|
try {
|
|
1029
1092
|
const params = new URLSearchParams();
|
|
1030
|
-
if (options == null ? void 0 : options.filters) {
|
|
1031
|
-
|
|
1093
|
+
if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
|
|
1094
|
+
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 ");
|
|
1095
|
+
if (q2) params.set("query", q2);
|
|
1032
1096
|
}
|
|
1033
1097
|
if (options == null ? void 0 : options.pagination) {
|
|
1034
|
-
if ("after" in options.pagination)
|
|
1098
|
+
if ("after" in options.pagination)
|
|
1035
1099
|
params.set("after", options.pagination.after);
|
|
1036
|
-
|
|
1100
|
+
else if ("before" in options.pagination)
|
|
1037
1101
|
params.set("before", options.pagination.before);
|
|
1038
|
-
}
|
|
1039
1102
|
}
|
|
1040
1103
|
const query = params.toString() ? `?${params.toString()}` : "";
|
|
1041
1104
|
const res = await fetcher(`/customer/v1/subscriptions${query}`);
|
|
1042
1105
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1043
1106
|
const body = await res.json();
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1107
|
+
return {
|
|
1108
|
+
_tag: "Success",
|
|
1109
|
+
data: {
|
|
1110
|
+
items: body.data.map(mapSubscription),
|
|
1111
|
+
pageInfo: {
|
|
1112
|
+
hasNextPage: body.hasNextPage,
|
|
1113
|
+
hasPrevPage: body.hasPrevPage,
|
|
1114
|
+
startCursor: body.startCursor,
|
|
1115
|
+
endCursor: body.endCursor
|
|
1051
1116
|
}
|
|
1052
|
-
});
|
|
1053
|
-
});
|
|
1054
|
-
const data = {
|
|
1055
|
-
items: body.data,
|
|
1056
|
-
pageInfo: {
|
|
1057
|
-
hasNextPage: body.hasNextPage,
|
|
1058
|
-
hasPrevPage: body.hasPrevPage,
|
|
1059
|
-
startCursor: body.startCursor,
|
|
1060
|
-
endCursor: body.endCursor
|
|
1061
1117
|
}
|
|
1062
1118
|
};
|
|
1063
|
-
return { _tag: "Success", data };
|
|
1064
1119
|
} catch (e2) {
|
|
1065
1120
|
return { _tag: "Failure", error: e2 };
|
|
1066
1121
|
}
|
|
1067
1122
|
},
|
|
1068
1123
|
async getById(id) {
|
|
1069
1124
|
try {
|
|
1070
|
-
const res = await fetcher(
|
|
1125
|
+
const res = await fetcher(
|
|
1126
|
+
`/customer/v1/subscriptions?query=id:"${id}"&limit=1`
|
|
1127
|
+
);
|
|
1071
1128
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
return { _tag: "Success", data };
|
|
1129
|
+
const body = await res.json();
|
|
1130
|
+
const raw = body.data[0];
|
|
1131
|
+
return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
|
|
1076
1132
|
} catch (e2) {
|
|
1077
1133
|
return { _tag: "Failure", error: e2 };
|
|
1078
1134
|
}
|
|
@@ -1133,21 +1189,20 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1133
1189
|
return { _tag: "Failure", error: e2 };
|
|
1134
1190
|
}
|
|
1135
1191
|
},
|
|
1192
|
+
// Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
|
|
1136
1193
|
async reschedule({ subscriptionId, newDate }) {
|
|
1137
1194
|
try {
|
|
1138
1195
|
const res = await fetcher(
|
|
1139
|
-
`/customer/v1/subscriptions/${subscriptionId}
|
|
1196
|
+
`/customer/v1/subscriptions/${subscriptionId}`,
|
|
1140
1197
|
{
|
|
1141
|
-
method: "
|
|
1198
|
+
method: "PATCH",
|
|
1142
1199
|
headers: { "Content-Type": "application/json" },
|
|
1143
|
-
body: JSON.stringify({ newDate })
|
|
1200
|
+
body: JSON.stringify({ nextBillingDate: newDate })
|
|
1144
1201
|
}
|
|
1145
1202
|
);
|
|
1146
1203
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1147
1204
|
const data = await res.json();
|
|
1148
|
-
|
|
1149
|
-
data.nextDeliveryDate = new Date(data.nextDeliveryDate);
|
|
1150
|
-
return { _tag: "Success", data };
|
|
1205
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1151
1206
|
} catch (e2) {
|
|
1152
1207
|
return { _tag: "Failure", error: e2 };
|
|
1153
1208
|
}
|
|
@@ -1160,7 +1215,7 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1160
1215
|
);
|
|
1161
1216
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1162
1217
|
const data = await res.json();
|
|
1163
|
-
return { _tag: "Success", data };
|
|
1218
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1164
1219
|
} catch (e2) {
|
|
1165
1220
|
return { _tag: "Failure", error: e2 };
|
|
1166
1221
|
}
|
|
@@ -1173,7 +1228,7 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1173
1228
|
);
|
|
1174
1229
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1175
1230
|
const data = await res.json();
|
|
1176
|
-
return { _tag: "Success", data };
|
|
1231
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1177
1232
|
} catch (e2) {
|
|
1178
1233
|
return { _tag: "Failure", error: e2 };
|
|
1179
1234
|
}
|
|
@@ -1186,7 +1241,7 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1186
1241
|
);
|
|
1187
1242
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1188
1243
|
const data = await res.json();
|
|
1189
|
-
return { _tag: "Success", data };
|
|
1244
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1190
1245
|
} catch (e2) {
|
|
1191
1246
|
return { _tag: "Failure", error: e2 };
|
|
1192
1247
|
}
|
|
@@ -1198,71 +1253,81 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1198
1253
|
{
|
|
1199
1254
|
method: "POST",
|
|
1200
1255
|
headers: { "Content-Type": "application/json" },
|
|
1201
|
-
body: JSON.stringify({ reason })
|
|
1256
|
+
body: JSON.stringify({ cancellationReason: reason })
|
|
1202
1257
|
}
|
|
1203
1258
|
);
|
|
1204
1259
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1205
1260
|
const data = await res.json();
|
|
1206
|
-
return { _tag: "Success", data };
|
|
1261
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1207
1262
|
} catch (e2) {
|
|
1208
1263
|
return { _tag: "Failure", error: e2 };
|
|
1209
1264
|
}
|
|
1210
1265
|
},
|
|
1266
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
|
|
1211
1267
|
async getAvailablePlans({ subscriptionId }) {
|
|
1212
1268
|
try {
|
|
1213
1269
|
const res = await fetcher(
|
|
1214
|
-
`/customer/
|
|
1270
|
+
`/customer/subscriptions/${subscriptionId}/plans`
|
|
1215
1271
|
);
|
|
1216
1272
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1217
1273
|
const data = await res.json();
|
|
1218
|
-
return { _tag: "Success", data
|
|
1274
|
+
return { _tag: "Success", data };
|
|
1219
1275
|
} catch (e2) {
|
|
1220
1276
|
return { _tag: "Failure", error: e2 };
|
|
1221
1277
|
}
|
|
1222
1278
|
},
|
|
1279
|
+
// Legacy endpoint — no /customer/v1 equivalent exists yet.
|
|
1223
1280
|
async getDefaultSubscriptionId() {
|
|
1224
|
-
var _a;
|
|
1225
|
-
try {
|
|
1226
|
-
const res = await fetcher(`/customer/v1/subscriptions?limit=1`);
|
|
1227
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1228
|
-
const body = await res.json();
|
|
1229
|
-
return { _tag: "Success", data: ((_a = body.data[0]) == null ? void 0 : _a.id) ?? null };
|
|
1230
|
-
} catch (e2) {
|
|
1231
|
-
return { _tag: "Failure", error: e2 };
|
|
1232
|
-
}
|
|
1233
|
-
},
|
|
1234
|
-
async getDeliveryFrequencies({ subscriptionId }) {
|
|
1235
1281
|
try {
|
|
1236
1282
|
const res = await fetcher(
|
|
1237
|
-
`/customer/
|
|
1283
|
+
`/customer/subscriptions/one-by-next-billing-date`
|
|
1238
1284
|
);
|
|
1239
1285
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1240
1286
|
const data = await res.json();
|
|
1241
|
-
return { _tag: "Success", data: data.
|
|
1287
|
+
return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
|
|
1242
1288
|
} catch (e2) {
|
|
1243
1289
|
return { _tag: "Failure", error: e2 };
|
|
1244
1290
|
}
|
|
1245
1291
|
},
|
|
1292
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
|
|
1246
1293
|
async getUpsellProducts({ subscriptionId }) {
|
|
1247
1294
|
try {
|
|
1248
1295
|
const res = await fetcher(
|
|
1249
|
-
`/customer/
|
|
1296
|
+
`/customer/subscriptions/${subscriptionId}/upsell-products`
|
|
1250
1297
|
);
|
|
1251
1298
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1252
1299
|
const data = await res.json();
|
|
1253
|
-
|
|
1300
|
+
const products = data.map((p2) => {
|
|
1301
|
+
var _a, _b, _c, _d, _e;
|
|
1302
|
+
return {
|
|
1303
|
+
id: p2.id,
|
|
1304
|
+
variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
|
|
1305
|
+
title: p2.title ?? "",
|
|
1306
|
+
imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
|
|
1307
|
+
price: {
|
|
1308
|
+
amount: String(((_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.price) ?? 0),
|
|
1309
|
+
currencyCode: p2.currencyCode ?? ""
|
|
1310
|
+
}
|
|
1311
|
+
};
|
|
1312
|
+
});
|
|
1313
|
+
return { _tag: "Success", data: products };
|
|
1254
1314
|
} catch (e2) {
|
|
1255
1315
|
return { _tag: "Failure", error: e2 };
|
|
1256
1316
|
}
|
|
1257
1317
|
},
|
|
1318
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
|
|
1258
1319
|
async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
|
|
1259
1320
|
try {
|
|
1260
1321
|
const res = await fetcher(
|
|
1261
|
-
`/customer/
|
|
1322
|
+
`/customer/subscriptions/${subscriptionId}/upsell`,
|
|
1262
1323
|
{
|
|
1263
1324
|
method: "POST",
|
|
1264
1325
|
headers: { "Content-Type": "application/json" },
|
|
1265
|
-
body: JSON.stringify({
|
|
1326
|
+
body: JSON.stringify({
|
|
1327
|
+
productVariantId: variantId,
|
|
1328
|
+
quantity,
|
|
1329
|
+
...oneTime ? { renewalLimit: 1 } : {}
|
|
1330
|
+
})
|
|
1266
1331
|
}
|
|
1267
1332
|
);
|
|
1268
1333
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
@@ -1270,23 +1335,6 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1270
1335
|
} catch (e2) {
|
|
1271
1336
|
return { _tag: "Failure", error: e2 };
|
|
1272
1337
|
}
|
|
1273
|
-
},
|
|
1274
|
-
async changeFrequency({ subscriptionId, sellingPlanId }) {
|
|
1275
|
-
try {
|
|
1276
|
-
const res = await fetcher(
|
|
1277
|
-
`/customer/v1/subscriptions/${subscriptionId}/frequency`,
|
|
1278
|
-
{
|
|
1279
|
-
method: "POST",
|
|
1280
|
-
headers: { "Content-Type": "application/json" },
|
|
1281
|
-
body: JSON.stringify({ sellingPlanId })
|
|
1282
|
-
}
|
|
1283
|
-
);
|
|
1284
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1285
|
-
const data = await res.json();
|
|
1286
|
-
return { _tag: "Success", data };
|
|
1287
|
-
} catch (e2) {
|
|
1288
|
-
return { _tag: "Failure", error: e2 };
|
|
1289
|
-
}
|
|
1290
1338
|
}
|
|
1291
1339
|
};
|
|
1292
1340
|
}
|
|
@@ -1342,10 +1390,8 @@ function createSubscriptionService(adapter) {
|
|
|
1342
1390
|
cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
|
|
1343
1391
|
getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
|
|
1344
1392
|
getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
|
|
1345
|
-
getDeliveryFrequencies: (params) => adapter.getDeliveryFrequencies ? adapter.getDeliveryFrequencies(params) : Promise.resolve({ _tag: "Success", data: [] }),
|
|
1346
1393
|
getUpsellProducts: (params) => adapter.getUpsellProducts ? adapter.getUpsellProducts(params) : Promise.resolve({ _tag: "Success", data: [] }),
|
|
1347
|
-
addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
|
|
1348
|
-
changeFrequency: (params) => adapter.changeFrequency ? adapter.changeFrequency(params) : Promise.resolve({ _tag: "Failure", error: "Not implemented" })
|
|
1394
|
+
addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
|
|
1349
1395
|
};
|
|
1350
1396
|
}
|
|
1351
1397
|
const CustomerServiceContext = createContext("services/customer");
|
|
@@ -1357,55 +1403,14 @@ function createCustomerService(adapter) {
|
|
|
1357
1403
|
const result = await adapter.getCurrent();
|
|
1358
1404
|
if (result._tag === "Success") current.value = result.data;
|
|
1359
1405
|
return result;
|
|
1360
|
-
}
|
|
1361
|
-
getAll: () => adapter.getAll(),
|
|
1362
|
-
getById: (id) => adapter.getById(id),
|
|
1363
|
-
create: (data) => adapter.create(data),
|
|
1364
|
-
update: (id, data) => adapter.update(id, data),
|
|
1365
|
-
remove: (id) => adapter.remove(id)
|
|
1406
|
+
}
|
|
1366
1407
|
};
|
|
1367
1408
|
}
|
|
1368
1409
|
function createMockCustomerAdapter() {
|
|
1369
|
-
|
|
1370
|
-
{
|
|
1371
|
-
id: "customer-1",
|
|
1372
|
-
name: "Customer 1",
|
|
1373
|
-
metadata: { mealCount: 3 }
|
|
1374
|
-
},
|
|
1375
|
-
{
|
|
1376
|
-
id: "customer-2",
|
|
1377
|
-
name: "Customer 2",
|
|
1378
|
-
metadata: { mealCount: 4 }
|
|
1379
|
-
}
|
|
1380
|
-
];
|
|
1410
|
+
const customer = { id: "customer-1", name: "Customer 1" };
|
|
1381
1411
|
return {
|
|
1382
1412
|
async getCurrent() {
|
|
1383
|
-
return { _tag: "Success", data: customers[0] ?? null };
|
|
1384
|
-
},
|
|
1385
|
-
async getAll() {
|
|
1386
|
-
return { _tag: "Success", data: [...customers] };
|
|
1387
|
-
},
|
|
1388
|
-
async getById(id) {
|
|
1389
|
-
return {
|
|
1390
|
-
_tag: "Success",
|
|
1391
|
-
data: customers.find((c2) => c2.id === id) ?? null
|
|
1392
|
-
};
|
|
1393
|
-
},
|
|
1394
|
-
async create(data) {
|
|
1395
|
-
const customer = { id: `customer-${Date.now()}`, ...data };
|
|
1396
|
-
customers = [...customers, customer];
|
|
1397
1413
|
return { _tag: "Success", data: customer };
|
|
1398
|
-
},
|
|
1399
|
-
async update(id, data) {
|
|
1400
|
-
const existing = customers.find((c2) => c2.id === id);
|
|
1401
|
-
if (!existing) return { _tag: "Failure", error: "Customer not found" };
|
|
1402
|
-
const updated = { ...existing, ...data };
|
|
1403
|
-
customers = customers.map((c2) => c2.id === id ? updated : c2);
|
|
1404
|
-
return { _tag: "Success", data: updated };
|
|
1405
|
-
},
|
|
1406
|
-
async remove(id) {
|
|
1407
|
-
customers = customers.filter((c2) => c2.id !== id);
|
|
1408
|
-
return { _tag: "Success", data: void 0 };
|
|
1409
1414
|
}
|
|
1410
1415
|
};
|
|
1411
1416
|
}
|
|
@@ -1413,84 +1418,40 @@ function createApiCustomerAdapter(fetcher) {
|
|
|
1413
1418
|
return {
|
|
1414
1419
|
async getCurrent() {
|
|
1415
1420
|
try {
|
|
1416
|
-
const res = await fetcher("/customer/
|
|
1417
|
-
if (!res.ok) return { _tag: "Failure", error:
|
|
1418
|
-
const data = await res.json();
|
|
1419
|
-
return { _tag: "Success", data };
|
|
1420
|
-
} catch (e2) {
|
|
1421
|
-
return { _tag: "Failure", error: e2 };
|
|
1422
|
-
}
|
|
1423
|
-
},
|
|
1424
|
-
async getAll() {
|
|
1425
|
-
try {
|
|
1426
|
-
const res = await fetcher("/customer/v1/customers");
|
|
1427
|
-
if (!res.ok) return { _tag: "Failure", error: await res.text() };
|
|
1428
|
-
const data = await res.json();
|
|
1429
|
-
return { _tag: "Success", data };
|
|
1430
|
-
} catch (e2) {
|
|
1431
|
-
return { _tag: "Failure", error: e2 };
|
|
1432
|
-
}
|
|
1433
|
-
},
|
|
1434
|
-
async getById(id) {
|
|
1435
|
-
try {
|
|
1436
|
-
const res = await fetcher(`/customer/v1/customers/${id}`);
|
|
1437
|
-
if (!res.ok) return { _tag: "Failure", error: await res.text() };
|
|
1421
|
+
const res = await fetcher("/customer/info");
|
|
1422
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1438
1423
|
const data = await res.json();
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
async create(data) {
|
|
1445
|
-
try {
|
|
1446
|
-
const res = await fetcher("/customer/v1/customers", {
|
|
1447
|
-
method: "POST",
|
|
1448
|
-
headers: { "Content-Type": "application/json" },
|
|
1449
|
-
body: JSON.stringify(data)
|
|
1450
|
-
});
|
|
1451
|
-
if (!res.ok) return { _tag: "Failure", error: await res.text() };
|
|
1452
|
-
const created = await res.json();
|
|
1453
|
-
return { _tag: "Success", data: created };
|
|
1454
|
-
} catch (e2) {
|
|
1455
|
-
return { _tag: "Failure", error: e2 };
|
|
1456
|
-
}
|
|
1457
|
-
},
|
|
1458
|
-
async update(id, data) {
|
|
1459
|
-
try {
|
|
1460
|
-
const res = await fetcher(`/customer/v1/customers/${id}`, {
|
|
1461
|
-
method: "PATCH",
|
|
1462
|
-
headers: { "Content-Type": "application/json" },
|
|
1463
|
-
body: JSON.stringify(data)
|
|
1464
|
-
});
|
|
1465
|
-
if (!res.ok) return { _tag: "Failure", error: await res.text() };
|
|
1466
|
-
const updated = await res.json();
|
|
1467
|
-
return { _tag: "Success", data: updated };
|
|
1468
|
-
} catch (e2) {
|
|
1469
|
-
return { _tag: "Failure", error: e2 };
|
|
1470
|
-
}
|
|
1471
|
-
},
|
|
1472
|
-
async remove(id) {
|
|
1473
|
-
try {
|
|
1474
|
-
const res = await fetcher(`/customer/v1/customers/${id}`, {
|
|
1475
|
-
method: "DELETE"
|
|
1476
|
-
});
|
|
1477
|
-
if (!res.ok) return { _tag: "Failure", error: await res.text() };
|
|
1478
|
-
return { _tag: "Success", data: void 0 };
|
|
1424
|
+
const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
|
|
1425
|
+
return {
|
|
1426
|
+
_tag: "Success",
|
|
1427
|
+
data: { id: data.id, name, email: data.email }
|
|
1428
|
+
};
|
|
1479
1429
|
} catch (e2) {
|
|
1480
1430
|
return { _tag: "Failure", error: e2 };
|
|
1481
1431
|
}
|
|
1482
1432
|
}
|
|
1483
1433
|
};
|
|
1484
1434
|
}
|
|
1435
|
+
function mapOrder(order) {
|
|
1436
|
+
const items = order.items.map((item) => ({
|
|
1437
|
+
id: item.id,
|
|
1438
|
+
productId: item.productId ?? "",
|
|
1439
|
+
variantId: item.variantId ?? "",
|
|
1440
|
+
quantity: item.quantity
|
|
1441
|
+
}));
|
|
1442
|
+
return {
|
|
1443
|
+
id: order.id,
|
|
1444
|
+
customerId: "",
|
|
1445
|
+
items,
|
|
1446
|
+
deliveryDate: order.placedAt ?? null,
|
|
1447
|
+
status: "delivered"
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1485
1450
|
function createMockOrderAdapter() {
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
items: [],
|
|
1491
|
-
deliveryDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null,
|
|
1492
|
-
status: "pending"
|
|
1493
|
-
},
|
|
1451
|
+
const now = Date.now();
|
|
1452
|
+
const past1 = new Date(now - 30 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null;
|
|
1453
|
+
const past2 = new Date(now - 60 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null;
|
|
1454
|
+
const orders = [
|
|
1494
1455
|
{
|
|
1495
1456
|
id: "order-2",
|
|
1496
1457
|
customerId: "customer-1",
|
|
@@ -1498,121 +1459,47 @@ function createMockOrderAdapter() {
|
|
|
1498
1459
|
{ id: "item-1", productId: "p1", variantId: "v1", quantity: 1 },
|
|
1499
1460
|
{ id: "item-2", productId: "p2", variantId: "v2", quantity: 2 }
|
|
1500
1461
|
],
|
|
1501
|
-
deliveryDate:
|
|
1462
|
+
deliveryDate: past1,
|
|
1502
1463
|
status: "delivered"
|
|
1503
1464
|
},
|
|
1504
1465
|
{
|
|
1505
1466
|
id: "order-3",
|
|
1506
1467
|
customerId: "customer-1",
|
|
1507
|
-
items: [
|
|
1508
|
-
|
|
1509
|
-
],
|
|
1510
|
-
deliveryDate: new Date(Date.now() - 60 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null,
|
|
1468
|
+
items: [{ id: "item-3", productId: "p1", variantId: "v1", quantity: 1 }],
|
|
1469
|
+
deliveryDate: past2,
|
|
1511
1470
|
status: "delivered"
|
|
1512
|
-
},
|
|
1513
|
-
{
|
|
1514
|
-
id: "order-4",
|
|
1515
|
-
customerId: "customer-1",
|
|
1516
|
-
items: [],
|
|
1517
|
-
deliveryDate: new Date(Date.now() - 90 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null,
|
|
1518
|
-
status: "skipped"
|
|
1519
1471
|
}
|
|
1520
1472
|
];
|
|
1521
1473
|
return {
|
|
1522
|
-
async getUpcoming() {
|
|
1523
|
-
return {
|
|
1524
|
-
_tag: "Success",
|
|
1525
|
-
data: orders.filter((o2) => o2.status === "pending")
|
|
1526
|
-
};
|
|
1527
|
-
},
|
|
1528
1474
|
async getHistory() {
|
|
1529
|
-
return {
|
|
1530
|
-
_tag: "Success",
|
|
1531
|
-
data: orders.filter((o2) => o2.status === "delivered" || o2.status === "skipped").sort((a3, b3) => (b3.deliveryDate ?? "").localeCompare(a3.deliveryDate ?? ""))
|
|
1532
|
-
};
|
|
1475
|
+
return { _tag: "Success", data: orders };
|
|
1533
1476
|
},
|
|
1534
1477
|
async getById(id) {
|
|
1535
|
-
return {
|
|
1536
|
-
_tag: "Success",
|
|
1537
|
-
data: orders.find((o2) => o2.id === id) ?? null
|
|
1538
|
-
};
|
|
1539
|
-
},
|
|
1540
|
-
async updateItems(orderId, items) {
|
|
1541
|
-
const order = orders.find((o2) => o2.id === orderId);
|
|
1542
|
-
if (!order) return { _tag: "Failure", error: "Order not found" };
|
|
1543
|
-
const updated = {
|
|
1544
|
-
...order,
|
|
1545
|
-
items: items.map((item, i2) => ({
|
|
1546
|
-
id: `item-${Date.now()}-${i2}`,
|
|
1547
|
-
...item
|
|
1548
|
-
}))
|
|
1549
|
-
};
|
|
1550
|
-
orders = orders.map((o2) => o2.id === orderId ? updated : o2);
|
|
1551
|
-
return { _tag: "Success", data: updated };
|
|
1552
|
-
},
|
|
1553
|
-
async skip(orderId) {
|
|
1554
|
-
const order = orders.find((o2) => o2.id === orderId);
|
|
1555
|
-
if (!order) return { _tag: "Failure", error: "Order not found" };
|
|
1556
|
-
const updated = { ...order, status: "skipped" };
|
|
1557
|
-
orders = orders.map((o2) => o2.id === orderId ? updated : o2);
|
|
1558
|
-
return { _tag: "Success", data: updated };
|
|
1478
|
+
return { _tag: "Success", data: orders.find((o2) => o2.id === id) ?? null };
|
|
1559
1479
|
}
|
|
1560
1480
|
};
|
|
1561
1481
|
}
|
|
1562
1482
|
function createApiOrderAdapter(fetcher) {
|
|
1563
1483
|
return {
|
|
1564
|
-
async getUpcoming() {
|
|
1565
|
-
try {
|
|
1566
|
-
const res = await fetcher("/customer/v1/orders?status=pending");
|
|
1567
|
-
if (!res.ok) return { _tag: "Failure", error: await res.text() };
|
|
1568
|
-
const body = await res.json();
|
|
1569
|
-
return { _tag: "Success", data: body.orders };
|
|
1570
|
-
} catch (e2) {
|
|
1571
|
-
return { _tag: "Failure", error: e2 };
|
|
1572
|
-
}
|
|
1573
|
-
},
|
|
1574
1484
|
async getHistory() {
|
|
1575
1485
|
try {
|
|
1576
|
-
const res = await fetcher("/customer/v1/orders
|
|
1577
|
-
if (!res.ok) return { _tag: "Failure", error:
|
|
1486
|
+
const res = await fetcher("/customer/v1/orders");
|
|
1487
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1578
1488
|
const body = await res.json();
|
|
1579
|
-
return { _tag: "Success", data: body.orders };
|
|
1489
|
+
return { _tag: "Success", data: body.orders.map(mapOrder) };
|
|
1580
1490
|
} catch (e2) {
|
|
1581
1491
|
return { _tag: "Failure", error: e2 };
|
|
1582
1492
|
}
|
|
1583
1493
|
},
|
|
1584
1494
|
async getById(id) {
|
|
1585
1495
|
try {
|
|
1586
|
-
const res = await fetcher(
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
return { _tag: "
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
},
|
|
1594
|
-
async updateItems(orderId, items) {
|
|
1595
|
-
try {
|
|
1596
|
-
const res = await fetcher(`/customer/v1/orders/${orderId}/items`, {
|
|
1597
|
-
method: "PUT",
|
|
1598
|
-
headers: { "Content-Type": "application/json" },
|
|
1599
|
-
body: JSON.stringify({ items })
|
|
1600
|
-
});
|
|
1601
|
-
if (!res.ok) return { _tag: "Failure", error: await res.text() };
|
|
1602
|
-
const data = await res.json();
|
|
1603
|
-
return { _tag: "Success", data };
|
|
1604
|
-
} catch (e2) {
|
|
1605
|
-
return { _tag: "Failure", error: e2 };
|
|
1606
|
-
}
|
|
1607
|
-
},
|
|
1608
|
-
async skip(orderId) {
|
|
1609
|
-
try {
|
|
1610
|
-
const res = await fetcher(`/customer/v1/orders/${orderId}/skip`, {
|
|
1611
|
-
method: "POST"
|
|
1612
|
-
});
|
|
1613
|
-
if (!res.ok) return { _tag: "Failure", error: await res.text() };
|
|
1614
|
-
const data = await res.json();
|
|
1615
|
-
return { _tag: "Success", data };
|
|
1496
|
+
const res = await fetcher(
|
|
1497
|
+
`/customer/v1/orders?query=id:"${id}"&limit=1`
|
|
1498
|
+
);
|
|
1499
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1500
|
+
const body = await res.json();
|
|
1501
|
+
const raw = body.orders[0];
|
|
1502
|
+
return { _tag: "Success", data: raw ? mapOrder(raw) : null };
|
|
1616
1503
|
} catch (e2) {
|
|
1617
1504
|
return { _tag: "Failure", error: e2 };
|
|
1618
1505
|
}
|
|
@@ -1622,11 +1509,8 @@ function createApiOrderAdapter(fetcher) {
|
|
|
1622
1509
|
const OrderServiceContext = createContext("services/order");
|
|
1623
1510
|
function createOrderService(adapter) {
|
|
1624
1511
|
return {
|
|
1625
|
-
getUpcoming: () => adapter.getUpcoming(),
|
|
1626
1512
|
getHistory: () => adapter.getHistory(),
|
|
1627
|
-
getById: (id) => adapter.getById(id)
|
|
1628
|
-
updateItems: (orderId, items) => adapter.updateItems(orderId, items),
|
|
1629
|
-
skip: (orderId) => adapter.skip(orderId)
|
|
1513
|
+
getById: (id) => adapter.getById(id)
|
|
1630
1514
|
};
|
|
1631
1515
|
}
|
|
1632
1516
|
function futureDateStr(daysFromNow) {
|
|
@@ -1741,9 +1625,7 @@ function createApiSchedulesAdapter(fetcher) {
|
|
|
1741
1625
|
return {
|
|
1742
1626
|
async getUpcomingOrders(count) {
|
|
1743
1627
|
try {
|
|
1744
|
-
const res = await fetcher(
|
|
1745
|
-
`/customer/v1/schedules?count=${count}`
|
|
1746
|
-
);
|
|
1628
|
+
const res = await fetcher(`/customer/v1/schedules/?count=${count}`);
|
|
1747
1629
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1748
1630
|
const data = await res.json();
|
|
1749
1631
|
return { _tag: "Success", data: data.orders };
|
|
@@ -1756,17 +1638,30 @@ function createApiSchedulesAdapter(fetcher) {
|
|
|
1756
1638
|
const res = await fetcher(`/customer/v1/schedules/adjustments`);
|
|
1757
1639
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1758
1640
|
const data = await res.json();
|
|
1759
|
-
|
|
1641
|
+
const adjustments = data.data.map((a3) => ({
|
|
1642
|
+
id: a3.id,
|
|
1643
|
+
subscriptionId: a3.subscriptionId ?? "",
|
|
1644
|
+
type: a3.type === "CHANGE_DATE" ? "date_change" : "skip",
|
|
1645
|
+
newDate: a3.newDate
|
|
1646
|
+
}));
|
|
1647
|
+
return { _tag: "Success", data: adjustments };
|
|
1760
1648
|
} catch (e2) {
|
|
1761
1649
|
return { _tag: "Failure", error: e2 };
|
|
1762
1650
|
}
|
|
1763
1651
|
},
|
|
1764
|
-
async skipOrder(
|
|
1652
|
+
async skipOrder({ cycle, date }) {
|
|
1653
|
+
if (cycle == null && date == null) {
|
|
1654
|
+
return { _tag: "Failure", error: "Either cycle or date is required for skipOrder" };
|
|
1655
|
+
}
|
|
1765
1656
|
try {
|
|
1657
|
+
const matcher = cycle != null ? { type: "CYCLE", input: { cycle } } : { type: "DATE", input: { date: new Date(date).toISOString() } };
|
|
1766
1658
|
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1767
1659
|
method: "POST",
|
|
1768
1660
|
headers: { "Content-Type": "application/json" },
|
|
1769
|
-
body: JSON.stringify({
|
|
1661
|
+
body: JSON.stringify({
|
|
1662
|
+
matcher,
|
|
1663
|
+
action: { type: "SKIP_ORDER", input: { reason: "" } }
|
|
1664
|
+
})
|
|
1770
1665
|
});
|
|
1771
1666
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1772
1667
|
return { _tag: "Success", data: void 0 };
|
|
@@ -1774,12 +1669,25 @@ function createApiSchedulesAdapter(fetcher) {
|
|
|
1774
1669
|
return { _tag: "Failure", error: e2 };
|
|
1775
1670
|
}
|
|
1776
1671
|
},
|
|
1777
|
-
async changeDateAdjustment(
|
|
1672
|
+
async changeDateAdjustment({ newDate, currentDate }) {
|
|
1673
|
+
if (currentDate == null) {
|
|
1674
|
+
return {
|
|
1675
|
+
_tag: "Failure",
|
|
1676
|
+
error: "currentDate is required for changeDateAdjustment"
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1778
1679
|
try {
|
|
1680
|
+
const matcher = { type: "DATE", input: { date: new Date(currentDate).toISOString() } };
|
|
1779
1681
|
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1780
1682
|
method: "POST",
|
|
1781
1683
|
headers: { "Content-Type": "application/json" },
|
|
1782
|
-
body: JSON.stringify({
|
|
1684
|
+
body: JSON.stringify({
|
|
1685
|
+
matcher,
|
|
1686
|
+
action: {
|
|
1687
|
+
type: "CHANGE_DATE",
|
|
1688
|
+
input: { newDate: new Date(newDate).toISOString() }
|
|
1689
|
+
}
|
|
1690
|
+
})
|
|
1783
1691
|
});
|
|
1784
1692
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1785
1693
|
return { _tag: "Success", data: void 0 };
|
|
@@ -2122,7 +2030,9 @@ export {
|
|
|
2122
2030
|
W as WorkflowServiceContext,
|
|
2123
2031
|
w as computed,
|
|
2124
2032
|
createApiCustomerAdapter,
|
|
2033
|
+
createApiLoginAdapter,
|
|
2125
2034
|
createApiOrderAdapter,
|
|
2035
|
+
createApiProductAdapter,
|
|
2126
2036
|
createApiSchedulesAdapter,
|
|
2127
2037
|
createApiSubscriptionAdapter,
|
|
2128
2038
|
a as createBlockInstance,
|
|
@@ -2131,7 +2041,9 @@ export {
|
|
|
2131
2041
|
f as createCeRenderer,
|
|
2132
2042
|
createContext,
|
|
2133
2043
|
createCustomerService,
|
|
2044
|
+
createLoginService,
|
|
2134
2045
|
createMockCustomerAdapter,
|
|
2046
|
+
createMockLoginAdapter,
|
|
2135
2047
|
createMockOrderAdapter,
|
|
2136
2048
|
createMockProductAdapter,
|
|
2137
2049
|
createMockSchedulesAdapter,
|