@juo/blocks 1.1.0-alpha.5 → 1.1.0-alpha.7
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 +526 -608
- 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 +10 -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 {
|
|
@@ -704,7 +827,9 @@ function createMockSubscriptionAdapter() {
|
|
|
704
827
|
variantId: "variant_1",
|
|
705
828
|
quantity: 2,
|
|
706
829
|
price: { amount: "29.99", currencyCode: "USD" },
|
|
707
|
-
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15")
|
|
830
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
831
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
832
|
+
deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
|
|
708
833
|
}
|
|
709
834
|
],
|
|
710
835
|
discounts: [
|
|
@@ -732,7 +857,9 @@ function createMockSubscriptionAdapter() {
|
|
|
732
857
|
variantId: "variant_2",
|
|
733
858
|
quantity: 1,
|
|
734
859
|
price: { amount: "49.99", currencyCode: "USD" },
|
|
735
|
-
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20")
|
|
860
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
|
|
861
|
+
billingPolicy: { intervalCount: 2, interval: "WEEK" },
|
|
862
|
+
deliveryPolicy: { intervalCount: 2, interval: "WEEK" }
|
|
736
863
|
}
|
|
737
864
|
],
|
|
738
865
|
discounts: [],
|
|
@@ -759,14 +886,6 @@ function createMockSubscriptionAdapter() {
|
|
|
759
886
|
imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
|
|
760
887
|
price: { amount: "24.00", currencyCode: "USD" },
|
|
761
888
|
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
889
|
}
|
|
771
890
|
];
|
|
772
891
|
return {
|
|
@@ -777,7 +896,7 @@ function createMockSubscriptionAdapter() {
|
|
|
777
896
|
},
|
|
778
897
|
async search(options = {}) {
|
|
779
898
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
780
|
-
const pageSize =
|
|
899
|
+
const pageSize = 2;
|
|
781
900
|
const allItems = mockSubscriptions;
|
|
782
901
|
let startIndex = 0;
|
|
783
902
|
let endIndex = pageSize;
|
|
@@ -793,20 +912,22 @@ function createMockSubscriptionAdapter() {
|
|
|
793
912
|
}
|
|
794
913
|
}
|
|
795
914
|
const pageItems = allItems.slice(startIndex, endIndex);
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
items:
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
915
|
+
return {
|
|
916
|
+
_tag: "Success",
|
|
917
|
+
data: {
|
|
918
|
+
items: pageItems.map((sub) => ({
|
|
919
|
+
...sub,
|
|
920
|
+
items: sub.items.map((item) => ({ ...item })),
|
|
921
|
+
discounts: sub.discounts.map((discount) => ({ ...discount }))
|
|
922
|
+
})),
|
|
923
|
+
pageInfo: {
|
|
924
|
+
hasNextPage: endIndex < allItems.length,
|
|
925
|
+
hasPrevPage: startIndex > 0,
|
|
926
|
+
startCursor: pageItems.length > 0 ? startIndex.toString() : null,
|
|
927
|
+
endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
|
|
928
|
+
}
|
|
807
929
|
}
|
|
808
930
|
};
|
|
809
|
-
return { _tag: "Success", data: paginatedList };
|
|
810
931
|
},
|
|
811
932
|
async addItem() {
|
|
812
933
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -830,26 +951,11 @@ function createMockSubscriptionAdapter() {
|
|
|
830
951
|
(sub) => sub.id === params.subscriptionId
|
|
831
952
|
);
|
|
832
953
|
if (!subscription) {
|
|
833
|
-
return {
|
|
834
|
-
_tag: "Failure",
|
|
835
|
-
error: "Subscription not found"
|
|
836
|
-
};
|
|
954
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
837
955
|
}
|
|
838
956
|
subscription.nextDeliveryDate = new Date(params.newDate);
|
|
839
957
|
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
|
-
};
|
|
958
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
853
959
|
},
|
|
854
960
|
async resume(params) {
|
|
855
961
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -857,23 +963,11 @@ function createMockSubscriptionAdapter() {
|
|
|
857
963
|
(sub) => sub.id === params.subscriptionId
|
|
858
964
|
);
|
|
859
965
|
if (!subscription) {
|
|
860
|
-
return {
|
|
861
|
-
_tag: "Failure",
|
|
862
|
-
error: "Subscription not found"
|
|
863
|
-
};
|
|
966
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
864
967
|
}
|
|
865
968
|
subscription.status = "active";
|
|
866
969
|
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
|
-
};
|
|
970
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
877
971
|
},
|
|
878
972
|
async pause(params) {
|
|
879
973
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -881,23 +975,11 @@ function createMockSubscriptionAdapter() {
|
|
|
881
975
|
(sub) => sub.id === params.subscriptionId
|
|
882
976
|
);
|
|
883
977
|
if (!subscription) {
|
|
884
|
-
return {
|
|
885
|
-
_tag: "Failure",
|
|
886
|
-
error: "Subscription not found"
|
|
887
|
-
};
|
|
978
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
888
979
|
}
|
|
889
980
|
subscription.status = "paused";
|
|
890
981
|
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
|
-
};
|
|
982
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
901
983
|
},
|
|
902
984
|
async reactivate(params) {
|
|
903
985
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -905,23 +987,11 @@ function createMockSubscriptionAdapter() {
|
|
|
905
987
|
(sub) => sub.id === params.subscriptionId
|
|
906
988
|
);
|
|
907
989
|
if (!subscription) {
|
|
908
|
-
return {
|
|
909
|
-
_tag: "Failure",
|
|
910
|
-
error: "Subscription not found"
|
|
911
|
-
};
|
|
990
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
912
991
|
}
|
|
913
992
|
subscription.status = "active";
|
|
914
993
|
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
|
-
};
|
|
994
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
925
995
|
},
|
|
926
996
|
async cancel(params) {
|
|
927
997
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -929,23 +999,11 @@ function createMockSubscriptionAdapter() {
|
|
|
929
999
|
(sub) => sub.id === params.subscriptionId
|
|
930
1000
|
);
|
|
931
1001
|
if (!subscription) {
|
|
932
|
-
return {
|
|
933
|
-
_tag: "Failure",
|
|
934
|
-
error: "Subscription not found"
|
|
935
|
-
};
|
|
1002
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
936
1003
|
}
|
|
937
1004
|
subscription.status = "canceled";
|
|
938
1005
|
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
|
-
};
|
|
1006
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
949
1007
|
},
|
|
950
1008
|
async getAvailablePlans() {
|
|
951
1009
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -978,26 +1036,6 @@ function createMockSubscriptionAdapter() {
|
|
|
978
1036
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
979
1037
|
return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
|
|
980
1038
|
},
|
|
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
1039
|
async getUpsellProducts() {
|
|
1002
1040
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1003
1041
|
return { _tag: "Success", data: [...mockUpsellProducts] };
|
|
@@ -1005,74 +1043,98 @@ function createMockSubscriptionAdapter() {
|
|
|
1005
1043
|
async addUpsellProduct() {
|
|
1006
1044
|
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
1007
1045
|
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
1046
|
}
|
|
1023
1047
|
};
|
|
1024
1048
|
}
|
|
1049
|
+
function mapSubscription(raw) {
|
|
1050
|
+
var _a, _b;
|
|
1051
|
+
const status = raw.status === "merged" ? "canceled" : raw.status;
|
|
1052
|
+
const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
|
|
1053
|
+
const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
|
|
1054
|
+
return {
|
|
1055
|
+
id: raw.id,
|
|
1056
|
+
customerId: "",
|
|
1057
|
+
items: (raw.items ?? []).map(
|
|
1058
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1059
|
+
(item) => ({
|
|
1060
|
+
id: item.id,
|
|
1061
|
+
productId: item.productId ?? item.product ?? "",
|
|
1062
|
+
variantId: item.variantId ?? item.variant ?? "",
|
|
1063
|
+
quantity: item.quantity,
|
|
1064
|
+
price: item.price ?? {
|
|
1065
|
+
amount: String(item.totalPrice ?? 0),
|
|
1066
|
+
currencyCode: raw.currencyCode ?? ""
|
|
1067
|
+
},
|
|
1068
|
+
nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null,
|
|
1069
|
+
billingPolicy: item.billingPolicy ?? raw.billingPolicy ?? null,
|
|
1070
|
+
deliveryPolicy: item.deliveryPolicy ?? raw.deliveryPolicy ?? null
|
|
1071
|
+
})
|
|
1072
|
+
),
|
|
1073
|
+
discounts: (raw.discounts ?? []).map(
|
|
1074
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1075
|
+
(d2) => {
|
|
1076
|
+
var _a2, _b2, _c;
|
|
1077
|
+
return {
|
|
1078
|
+
id: d2.id,
|
|
1079
|
+
type: ((_a2 = d2.value) == null ? void 0 : _a2.type) === "fixed-amount" || d2.type === "fixed_amount" ? "fixed_amount" : "percentage",
|
|
1080
|
+
value: ((_b2 = d2.value) == null ? void 0 : _b2.amount) ?? ((_c = d2.value) == null ? void 0 : _c.percentage) ?? d2.value ?? 0,
|
|
1081
|
+
code: d2.title ?? d2.code ?? "",
|
|
1082
|
+
description: d2.description ?? ""
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
),
|
|
1086
|
+
status,
|
|
1087
|
+
nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
|
|
1088
|
+
frequency,
|
|
1089
|
+
createdAt: raw.createdAt,
|
|
1090
|
+
updatedAt: raw.updatedAt
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1025
1093
|
function createApiSubscriptionAdapter(fetcher) {
|
|
1026
1094
|
return {
|
|
1027
1095
|
async search(options) {
|
|
1096
|
+
var _a;
|
|
1028
1097
|
try {
|
|
1029
1098
|
const params = new URLSearchParams();
|
|
1030
|
-
if (options == null ? void 0 : options.filters) {
|
|
1031
|
-
|
|
1099
|
+
if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
|
|
1100
|
+
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 ");
|
|
1101
|
+
if (q2) params.set("query", q2);
|
|
1032
1102
|
}
|
|
1033
1103
|
if (options == null ? void 0 : options.pagination) {
|
|
1034
|
-
if ("after" in options.pagination)
|
|
1104
|
+
if ("after" in options.pagination)
|
|
1035
1105
|
params.set("after", options.pagination.after);
|
|
1036
|
-
|
|
1106
|
+
else if ("before" in options.pagination)
|
|
1037
1107
|
params.set("before", options.pagination.before);
|
|
1038
|
-
}
|
|
1039
1108
|
}
|
|
1040
1109
|
const query = params.toString() ? `?${params.toString()}` : "";
|
|
1041
1110
|
const res = await fetcher(`/customer/v1/subscriptions${query}`);
|
|
1042
1111
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1043
1112
|
const body = await res.json();
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1113
|
+
return {
|
|
1114
|
+
_tag: "Success",
|
|
1115
|
+
data: {
|
|
1116
|
+
items: body.data.map(mapSubscription),
|
|
1117
|
+
pageInfo: {
|
|
1118
|
+
hasNextPage: body.hasNextPage,
|
|
1119
|
+
hasPrevPage: body.hasPrevPage,
|
|
1120
|
+
startCursor: body.startCursor,
|
|
1121
|
+
endCursor: body.endCursor
|
|
1051
1122
|
}
|
|
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
1123
|
}
|
|
1062
1124
|
};
|
|
1063
|
-
return { _tag: "Success", data };
|
|
1064
1125
|
} catch (e2) {
|
|
1065
1126
|
return { _tag: "Failure", error: e2 };
|
|
1066
1127
|
}
|
|
1067
1128
|
},
|
|
1068
1129
|
async getById(id) {
|
|
1069
1130
|
try {
|
|
1070
|
-
const res = await fetcher(
|
|
1131
|
+
const res = await fetcher(
|
|
1132
|
+
`/customer/v1/subscriptions?query=id:"${id}"&limit=1`
|
|
1133
|
+
);
|
|
1071
1134
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
return { _tag: "Success", data };
|
|
1135
|
+
const body = await res.json();
|
|
1136
|
+
const raw = body.data[0];
|
|
1137
|
+
return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
|
|
1076
1138
|
} catch (e2) {
|
|
1077
1139
|
return { _tag: "Failure", error: e2 };
|
|
1078
1140
|
}
|
|
@@ -1133,21 +1195,20 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1133
1195
|
return { _tag: "Failure", error: e2 };
|
|
1134
1196
|
}
|
|
1135
1197
|
},
|
|
1198
|
+
// Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
|
|
1136
1199
|
async reschedule({ subscriptionId, newDate }) {
|
|
1137
1200
|
try {
|
|
1138
1201
|
const res = await fetcher(
|
|
1139
|
-
`/customer/v1/subscriptions/${subscriptionId}
|
|
1202
|
+
`/customer/v1/subscriptions/${subscriptionId}`,
|
|
1140
1203
|
{
|
|
1141
|
-
method: "
|
|
1204
|
+
method: "PATCH",
|
|
1142
1205
|
headers: { "Content-Type": "application/json" },
|
|
1143
|
-
body: JSON.stringify({ newDate })
|
|
1206
|
+
body: JSON.stringify({ nextBillingDate: newDate })
|
|
1144
1207
|
}
|
|
1145
1208
|
);
|
|
1146
1209
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1147
1210
|
const data = await res.json();
|
|
1148
|
-
|
|
1149
|
-
data.nextDeliveryDate = new Date(data.nextDeliveryDate);
|
|
1150
|
-
return { _tag: "Success", data };
|
|
1211
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1151
1212
|
} catch (e2) {
|
|
1152
1213
|
return { _tag: "Failure", error: e2 };
|
|
1153
1214
|
}
|
|
@@ -1160,7 +1221,7 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1160
1221
|
);
|
|
1161
1222
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1162
1223
|
const data = await res.json();
|
|
1163
|
-
return { _tag: "Success", data };
|
|
1224
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1164
1225
|
} catch (e2) {
|
|
1165
1226
|
return { _tag: "Failure", error: e2 };
|
|
1166
1227
|
}
|
|
@@ -1173,7 +1234,7 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1173
1234
|
);
|
|
1174
1235
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1175
1236
|
const data = await res.json();
|
|
1176
|
-
return { _tag: "Success", data };
|
|
1237
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1177
1238
|
} catch (e2) {
|
|
1178
1239
|
return { _tag: "Failure", error: e2 };
|
|
1179
1240
|
}
|
|
@@ -1186,7 +1247,7 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1186
1247
|
);
|
|
1187
1248
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1188
1249
|
const data = await res.json();
|
|
1189
|
-
return { _tag: "Success", data };
|
|
1250
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1190
1251
|
} catch (e2) {
|
|
1191
1252
|
return { _tag: "Failure", error: e2 };
|
|
1192
1253
|
}
|
|
@@ -1198,71 +1259,81 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1198
1259
|
{
|
|
1199
1260
|
method: "POST",
|
|
1200
1261
|
headers: { "Content-Type": "application/json" },
|
|
1201
|
-
body: JSON.stringify({ reason })
|
|
1262
|
+
body: JSON.stringify({ cancellationReason: reason })
|
|
1202
1263
|
}
|
|
1203
1264
|
);
|
|
1204
1265
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1205
1266
|
const data = await res.json();
|
|
1206
|
-
return { _tag: "Success", data };
|
|
1267
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
1207
1268
|
} catch (e2) {
|
|
1208
1269
|
return { _tag: "Failure", error: e2 };
|
|
1209
1270
|
}
|
|
1210
1271
|
},
|
|
1272
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
|
|
1211
1273
|
async getAvailablePlans({ subscriptionId }) {
|
|
1212
1274
|
try {
|
|
1213
1275
|
const res = await fetcher(
|
|
1214
|
-
`/customer/
|
|
1276
|
+
`/customer/subscriptions/${subscriptionId}/plans`
|
|
1215
1277
|
);
|
|
1216
1278
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1217
1279
|
const data = await res.json();
|
|
1218
|
-
return { _tag: "Success", data
|
|
1280
|
+
return { _tag: "Success", data };
|
|
1219
1281
|
} catch (e2) {
|
|
1220
1282
|
return { _tag: "Failure", error: e2 };
|
|
1221
1283
|
}
|
|
1222
1284
|
},
|
|
1285
|
+
// Legacy endpoint — no /customer/v1 equivalent exists yet.
|
|
1223
1286
|
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
1287
|
try {
|
|
1236
1288
|
const res = await fetcher(
|
|
1237
|
-
`/customer/
|
|
1289
|
+
`/customer/subscriptions/one-by-next-billing-date`
|
|
1238
1290
|
);
|
|
1239
1291
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1240
1292
|
const data = await res.json();
|
|
1241
|
-
return { _tag: "Success", data: data.
|
|
1293
|
+
return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
|
|
1242
1294
|
} catch (e2) {
|
|
1243
1295
|
return { _tag: "Failure", error: e2 };
|
|
1244
1296
|
}
|
|
1245
1297
|
},
|
|
1298
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
|
|
1246
1299
|
async getUpsellProducts({ subscriptionId }) {
|
|
1247
1300
|
try {
|
|
1248
1301
|
const res = await fetcher(
|
|
1249
|
-
`/customer/
|
|
1302
|
+
`/customer/subscriptions/${subscriptionId}/upsell-products`
|
|
1250
1303
|
);
|
|
1251
1304
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1252
1305
|
const data = await res.json();
|
|
1253
|
-
|
|
1306
|
+
const products = data.map((p2) => {
|
|
1307
|
+
var _a, _b, _c, _d, _e;
|
|
1308
|
+
return {
|
|
1309
|
+
id: p2.id,
|
|
1310
|
+
variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
|
|
1311
|
+
title: p2.title ?? "",
|
|
1312
|
+
imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
|
|
1313
|
+
price: {
|
|
1314
|
+
amount: String(((_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.price) ?? 0),
|
|
1315
|
+
currencyCode: p2.currencyCode ?? ""
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
});
|
|
1319
|
+
return { _tag: "Success", data: products };
|
|
1254
1320
|
} catch (e2) {
|
|
1255
1321
|
return { _tag: "Failure", error: e2 };
|
|
1256
1322
|
}
|
|
1257
1323
|
},
|
|
1324
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
|
|
1258
1325
|
async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
|
|
1259
1326
|
try {
|
|
1260
1327
|
const res = await fetcher(
|
|
1261
|
-
`/customer/
|
|
1328
|
+
`/customer/subscriptions/${subscriptionId}/upsell`,
|
|
1262
1329
|
{
|
|
1263
1330
|
method: "POST",
|
|
1264
1331
|
headers: { "Content-Type": "application/json" },
|
|
1265
|
-
body: JSON.stringify({
|
|
1332
|
+
body: JSON.stringify({
|
|
1333
|
+
productVariantId: variantId,
|
|
1334
|
+
quantity,
|
|
1335
|
+
...oneTime ? { renewalLimit: 1 } : {}
|
|
1336
|
+
})
|
|
1266
1337
|
}
|
|
1267
1338
|
);
|
|
1268
1339
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
@@ -1270,23 +1341,6 @@ function createApiSubscriptionAdapter(fetcher) {
|
|
|
1270
1341
|
} catch (e2) {
|
|
1271
1342
|
return { _tag: "Failure", error: e2 };
|
|
1272
1343
|
}
|
|
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
1344
|
}
|
|
1291
1345
|
};
|
|
1292
1346
|
}
|
|
@@ -1342,10 +1396,8 @@ function createSubscriptionService(adapter) {
|
|
|
1342
1396
|
cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
|
|
1343
1397
|
getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
|
|
1344
1398
|
getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
|
|
1345
|
-
getDeliveryFrequencies: (params) => adapter.getDeliveryFrequencies ? adapter.getDeliveryFrequencies(params) : Promise.resolve({ _tag: "Success", data: [] }),
|
|
1346
1399
|
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" })
|
|
1400
|
+
addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
|
|
1349
1401
|
};
|
|
1350
1402
|
}
|
|
1351
1403
|
const CustomerServiceContext = createContext("services/customer");
|
|
@@ -1357,55 +1409,14 @@ function createCustomerService(adapter) {
|
|
|
1357
1409
|
const result = await adapter.getCurrent();
|
|
1358
1410
|
if (result._tag === "Success") current.value = result.data;
|
|
1359
1411
|
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)
|
|
1412
|
+
}
|
|
1366
1413
|
};
|
|
1367
1414
|
}
|
|
1368
1415
|
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
|
-
];
|
|
1416
|
+
const customer = { id: "customer-1", name: "Customer 1" };
|
|
1381
1417
|
return {
|
|
1382
1418
|
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
1419
|
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
1420
|
}
|
|
1410
1421
|
};
|
|
1411
1422
|
}
|
|
@@ -1413,84 +1424,40 @@ function createApiCustomerAdapter(fetcher) {
|
|
|
1413
1424
|
return {
|
|
1414
1425
|
async getCurrent() {
|
|
1415
1426
|
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() };
|
|
1427
|
+
const res = await fetcher("/customer/info");
|
|
1428
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1438
1429
|
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 };
|
|
1430
|
+
const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
|
|
1431
|
+
return {
|
|
1432
|
+
_tag: "Success",
|
|
1433
|
+
data: { id: data.id, name, email: data.email }
|
|
1434
|
+
};
|
|
1479
1435
|
} catch (e2) {
|
|
1480
1436
|
return { _tag: "Failure", error: e2 };
|
|
1481
1437
|
}
|
|
1482
1438
|
}
|
|
1483
1439
|
};
|
|
1484
1440
|
}
|
|
1441
|
+
function mapOrder(order) {
|
|
1442
|
+
const items = order.items.map((item) => ({
|
|
1443
|
+
id: item.id,
|
|
1444
|
+
productId: item.productId ?? "",
|
|
1445
|
+
variantId: item.variantId ?? "",
|
|
1446
|
+
quantity: item.quantity
|
|
1447
|
+
}));
|
|
1448
|
+
return {
|
|
1449
|
+
id: order.id,
|
|
1450
|
+
customerId: "",
|
|
1451
|
+
items,
|
|
1452
|
+
deliveryDate: order.placedAt ?? null,
|
|
1453
|
+
status: "delivered"
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1485
1456
|
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
|
-
},
|
|
1457
|
+
const now = Date.now();
|
|
1458
|
+
const past1 = new Date(now - 30 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null;
|
|
1459
|
+
const past2 = new Date(now - 60 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null;
|
|
1460
|
+
const orders = [
|
|
1494
1461
|
{
|
|
1495
1462
|
id: "order-2",
|
|
1496
1463
|
customerId: "customer-1",
|
|
@@ -1498,121 +1465,47 @@ function createMockOrderAdapter() {
|
|
|
1498
1465
|
{ id: "item-1", productId: "p1", variantId: "v1", quantity: 1 },
|
|
1499
1466
|
{ id: "item-2", productId: "p2", variantId: "v2", quantity: 2 }
|
|
1500
1467
|
],
|
|
1501
|
-
deliveryDate:
|
|
1468
|
+
deliveryDate: past1,
|
|
1502
1469
|
status: "delivered"
|
|
1503
1470
|
},
|
|
1504
1471
|
{
|
|
1505
1472
|
id: "order-3",
|
|
1506
1473
|
customerId: "customer-1",
|
|
1507
|
-
items: [
|
|
1508
|
-
|
|
1509
|
-
],
|
|
1510
|
-
deliveryDate: new Date(Date.now() - 60 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null,
|
|
1474
|
+
items: [{ id: "item-3", productId: "p1", variantId: "v1", quantity: 1 }],
|
|
1475
|
+
deliveryDate: past2,
|
|
1511
1476
|
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
1477
|
}
|
|
1520
1478
|
];
|
|
1521
1479
|
return {
|
|
1522
|
-
async getUpcoming() {
|
|
1523
|
-
return {
|
|
1524
|
-
_tag: "Success",
|
|
1525
|
-
data: orders.filter((o2) => o2.status === "pending")
|
|
1526
|
-
};
|
|
1527
|
-
},
|
|
1528
1480
|
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
|
-
};
|
|
1481
|
+
return { _tag: "Success", data: orders };
|
|
1533
1482
|
},
|
|
1534
1483
|
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 };
|
|
1484
|
+
return { _tag: "Success", data: orders.find((o2) => o2.id === id) ?? null };
|
|
1559
1485
|
}
|
|
1560
1486
|
};
|
|
1561
1487
|
}
|
|
1562
1488
|
function createApiOrderAdapter(fetcher) {
|
|
1563
1489
|
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
1490
|
async getHistory() {
|
|
1575
1491
|
try {
|
|
1576
|
-
const res = await fetcher("/customer/v1/orders
|
|
1577
|
-
if (!res.ok) return { _tag: "Failure", error:
|
|
1492
|
+
const res = await fetcher("/customer/v1/orders");
|
|
1493
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1578
1494
|
const body = await res.json();
|
|
1579
|
-
return { _tag: "Success", data: body.orders };
|
|
1495
|
+
return { _tag: "Success", data: body.orders.map(mapOrder) };
|
|
1580
1496
|
} catch (e2) {
|
|
1581
1497
|
return { _tag: "Failure", error: e2 };
|
|
1582
1498
|
}
|
|
1583
1499
|
},
|
|
1584
1500
|
async getById(id) {
|
|
1585
1501
|
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 };
|
|
1502
|
+
const res = await fetcher(
|
|
1503
|
+
`/customer/v1/orders?query=id:"${id}"&limit=1`
|
|
1504
|
+
);
|
|
1505
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1506
|
+
const body = await res.json();
|
|
1507
|
+
const raw = body.orders[0];
|
|
1508
|
+
return { _tag: "Success", data: raw ? mapOrder(raw) : null };
|
|
1616
1509
|
} catch (e2) {
|
|
1617
1510
|
return { _tag: "Failure", error: e2 };
|
|
1618
1511
|
}
|
|
@@ -1622,11 +1515,8 @@ function createApiOrderAdapter(fetcher) {
|
|
|
1622
1515
|
const OrderServiceContext = createContext("services/order");
|
|
1623
1516
|
function createOrderService(adapter) {
|
|
1624
1517
|
return {
|
|
1625
|
-
getUpcoming: () => adapter.getUpcoming(),
|
|
1626
1518
|
getHistory: () => adapter.getHistory(),
|
|
1627
|
-
getById: (id) => adapter.getById(id)
|
|
1628
|
-
updateItems: (orderId, items) => adapter.updateItems(orderId, items),
|
|
1629
|
-
skip: (orderId) => adapter.skip(orderId)
|
|
1519
|
+
getById: (id) => adapter.getById(id)
|
|
1630
1520
|
};
|
|
1631
1521
|
}
|
|
1632
1522
|
function futureDateStr(daysFromNow) {
|
|
@@ -1741,9 +1631,7 @@ function createApiSchedulesAdapter(fetcher) {
|
|
|
1741
1631
|
return {
|
|
1742
1632
|
async getUpcomingOrders(count) {
|
|
1743
1633
|
try {
|
|
1744
|
-
const res = await fetcher(
|
|
1745
|
-
`/customer/v1/schedules?count=${count}`
|
|
1746
|
-
);
|
|
1634
|
+
const res = await fetcher(`/customer/v1/schedules/?count=${count}`);
|
|
1747
1635
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1748
1636
|
const data = await res.json();
|
|
1749
1637
|
return { _tag: "Success", data: data.orders };
|
|
@@ -1756,17 +1644,30 @@ function createApiSchedulesAdapter(fetcher) {
|
|
|
1756
1644
|
const res = await fetcher(`/customer/v1/schedules/adjustments`);
|
|
1757
1645
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1758
1646
|
const data = await res.json();
|
|
1759
|
-
|
|
1647
|
+
const adjustments = data.data.map((a3) => ({
|
|
1648
|
+
id: a3.id,
|
|
1649
|
+
subscriptionId: a3.subscriptionId ?? "",
|
|
1650
|
+
type: a3.type === "CHANGE_DATE" ? "date_change" : "skip",
|
|
1651
|
+
newDate: a3.newDate
|
|
1652
|
+
}));
|
|
1653
|
+
return { _tag: "Success", data: adjustments };
|
|
1760
1654
|
} catch (e2) {
|
|
1761
1655
|
return { _tag: "Failure", error: e2 };
|
|
1762
1656
|
}
|
|
1763
1657
|
},
|
|
1764
|
-
async skipOrder(
|
|
1658
|
+
async skipOrder({ cycle, date }) {
|
|
1659
|
+
if (cycle == null && date == null) {
|
|
1660
|
+
return { _tag: "Failure", error: "Either cycle or date is required for skipOrder" };
|
|
1661
|
+
}
|
|
1765
1662
|
try {
|
|
1663
|
+
const matcher = cycle != null ? { type: "CYCLE", input: { cycle } } : { type: "DATE", input: { date: new Date(date).toISOString() } };
|
|
1766
1664
|
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1767
1665
|
method: "POST",
|
|
1768
1666
|
headers: { "Content-Type": "application/json" },
|
|
1769
|
-
body: JSON.stringify({
|
|
1667
|
+
body: JSON.stringify({
|
|
1668
|
+
matcher,
|
|
1669
|
+
action: { type: "SKIP_ORDER", input: { reason: "" } }
|
|
1670
|
+
})
|
|
1770
1671
|
});
|
|
1771
1672
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1772
1673
|
return { _tag: "Success", data: void 0 };
|
|
@@ -1774,12 +1675,25 @@ function createApiSchedulesAdapter(fetcher) {
|
|
|
1774
1675
|
return { _tag: "Failure", error: e2 };
|
|
1775
1676
|
}
|
|
1776
1677
|
},
|
|
1777
|
-
async changeDateAdjustment(
|
|
1678
|
+
async changeDateAdjustment({ newDate, currentDate }) {
|
|
1679
|
+
if (currentDate == null) {
|
|
1680
|
+
return {
|
|
1681
|
+
_tag: "Failure",
|
|
1682
|
+
error: "currentDate is required for changeDateAdjustment"
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1778
1685
|
try {
|
|
1686
|
+
const matcher = { type: "DATE", input: { date: new Date(currentDate).toISOString() } };
|
|
1779
1687
|
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1780
1688
|
method: "POST",
|
|
1781
1689
|
headers: { "Content-Type": "application/json" },
|
|
1782
|
-
body: JSON.stringify({
|
|
1690
|
+
body: JSON.stringify({
|
|
1691
|
+
matcher,
|
|
1692
|
+
action: {
|
|
1693
|
+
type: "CHANGE_DATE",
|
|
1694
|
+
input: { newDate: new Date(newDate).toISOString() }
|
|
1695
|
+
}
|
|
1696
|
+
})
|
|
1783
1697
|
});
|
|
1784
1698
|
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1785
1699
|
return { _tag: "Success", data: void 0 };
|
|
@@ -2122,7 +2036,9 @@ export {
|
|
|
2122
2036
|
W as WorkflowServiceContext,
|
|
2123
2037
|
w as computed,
|
|
2124
2038
|
createApiCustomerAdapter,
|
|
2039
|
+
createApiLoginAdapter,
|
|
2125
2040
|
createApiOrderAdapter,
|
|
2041
|
+
createApiProductAdapter,
|
|
2126
2042
|
createApiSchedulesAdapter,
|
|
2127
2043
|
createApiSubscriptionAdapter,
|
|
2128
2044
|
a as createBlockInstance,
|
|
@@ -2131,7 +2047,9 @@ export {
|
|
|
2131
2047
|
f as createCeRenderer,
|
|
2132
2048
|
createContext,
|
|
2133
2049
|
createCustomerService,
|
|
2050
|
+
createLoginService,
|
|
2134
2051
|
createMockCustomerAdapter,
|
|
2052
|
+
createMockLoginAdapter,
|
|
2135
2053
|
createMockOrderAdapter,
|
|
2136
2054
|
createMockProductAdapter,
|
|
2137
2055
|
createMockSchedulesAdapter,
|