@juo/blocks 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ExtensionRoot-CZyBNJZY.js +159 -0
- package/dist/block-VhmfGB3N.js +51 -0
- package/dist/block.d.ts +96 -0
- package/dist/block.d.ts.map +1 -0
- package/dist/bridge-DQsM97cH.js +490 -0
- package/dist/context.d.ts +76 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.test.d.ts +2 -0
- package/dist/context.test.d.ts.map +1 -0
- package/dist/core.js +1163 -0
- package/dist/editor/bridge.d.ts +75 -0
- package/dist/editor/bridge.d.ts.map +1 -0
- package/dist/editor/bridge.test.d.ts +2 -0
- package/dist/editor/bridge.test.d.ts.map +1 -0
- package/dist/editor/components.d.ts +31 -0
- package/dist/editor/components.d.ts.map +1 -0
- package/dist/editor/handler.d.ts +10 -0
- package/dist/editor/handler.d.ts.map +1 -0
- package/dist/editor/main.d.ts +17 -0
- package/dist/editor/main.d.ts.map +1 -0
- package/dist/editor/messages.d.ts +233 -0
- package/dist/editor/messages.d.ts.map +1 -0
- package/dist/editor.js +711 -0
- package/dist/flows/MockWorkflowApiAdapter.d.ts +13 -0
- package/dist/flows/MockWorkflowApiAdapter.d.ts.map +1 -0
- package/dist/flows/WorkflowService.d.ts +175 -0
- package/dist/flows/WorkflowService.d.ts.map +1 -0
- package/dist/flows/__tests__/WorkflowService.test.d.ts +2 -0
- package/dist/flows/__tests__/WorkflowService.test.d.ts.map +1 -0
- package/dist/flows/__tests__/defaults.test.d.ts +2 -0
- package/dist/flows/__tests__/defaults.test.d.ts.map +1 -0
- package/dist/flows/__tests__/juo-workflow.test.d.ts +2 -0
- package/dist/flows/__tests__/juo-workflow.test.d.ts.map +1 -0
- package/dist/flows/__tests__/overlay-behavior.test.d.ts +2 -0
- package/dist/flows/__tests__/overlay-behavior.test.d.ts.map +1 -0
- package/dist/flows/__tests__/theme-state-surfaces.test.d.ts +2 -0
- package/dist/flows/__tests__/theme-state-surfaces.test.d.ts.map +1 -0
- package/dist/flows/defaults.d.ts +15 -0
- package/dist/flows/defaults.d.ts.map +1 -0
- package/dist/flows/index.d.ts +4 -0
- package/dist/flows/index.d.ts.map +1 -0
- package/dist/flows/juo-workflow.d.ts +48 -0
- package/dist/flows/juo-workflow.d.ts.map +1 -0
- package/dist/juo-workflow-RSrOuKUH.js +591 -0
- package/dist/main.d.ts +11 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/preact/block.d.ts +21 -0
- package/dist/preact/block.d.ts.map +1 -0
- package/dist/preact/block.test.d.ts +2 -0
- package/dist/preact/block.test.d.ts.map +1 -0
- package/dist/preact/bridge.d.ts +11 -0
- package/dist/preact/bridge.d.ts.map +1 -0
- package/dist/preact/context.d.ts +31 -0
- package/dist/preact/context.d.ts.map +1 -0
- package/dist/preact/main.d.ts +11 -0
- package/dist/preact/main.d.ts.map +1 -0
- package/dist/preact/signals.d.ts +23 -0
- package/dist/preact/signals.d.ts.map +1 -0
- package/dist/preact.js +230 -0
- package/dist/react/block.d.ts +21 -0
- package/dist/react/block.d.ts.map +1 -0
- package/dist/react/block.test.d.ts +2 -0
- package/dist/react/block.test.d.ts.map +1 -0
- package/dist/react/bridge.d.ts +11 -0
- package/dist/react/bridge.d.ts.map +1 -0
- package/dist/react/context.d.ts +28 -0
- package/dist/react/context.d.ts.map +1 -0
- package/dist/react/main.d.ts +11 -0
- package/dist/react/main.d.ts.map +1 -0
- package/dist/react/signals.d.ts +23 -0
- package/dist/react/signals.d.ts.map +1 -0
- package/dist/react.js +205 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/login/index.d.ts +23 -0
- package/dist/services/login/index.d.ts.map +1 -0
- package/dist/services/login/loginService.d.ts +82 -0
- package/dist/services/login/loginService.d.ts.map +1 -0
- package/dist/services/product/adapter.d.ts +20 -0
- package/dist/services/product/adapter.d.ts.map +1 -0
- package/dist/services/product/index.d.ts +5 -0
- package/dist/services/product/index.d.ts.map +1 -0
- package/dist/services/product/productService.d.ts +12 -0
- package/dist/services/product/productService.d.ts.map +1 -0
- package/dist/services/product/types.d.ts +32 -0
- package/dist/services/product/types.d.ts.map +1 -0
- package/dist/services/subscription/adapter.d.ts +49 -0
- package/dist/services/subscription/adapter.d.ts.map +1 -0
- package/dist/services/subscription/index.d.ts +5 -0
- package/dist/services/subscription/index.d.ts.map +1 -0
- package/dist/services/subscription/list.d.ts +22 -0
- package/dist/services/subscription/list.d.ts.map +1 -0
- package/dist/services/subscription/store.d.ts +30 -0
- package/dist/services/subscription/store.d.ts.map +1 -0
- package/dist/services/subscription/subscriptionService.d.ts +12 -0
- package/dist/services/subscription/subscriptionService.d.ts.map +1 -0
- package/dist/services/subscription/types.d.ts +76 -0
- package/dist/services/subscription/types.d.ts.map +1 -0
- package/dist/shared/block.d.ts +17 -0
- package/dist/shared/block.d.ts.map +1 -0
- package/dist/shared/context.d.ts +16 -0
- package/dist/shared/context.d.ts.map +1 -0
- package/dist/shared/signals.d.ts +17 -0
- package/dist/shared/signals.d.ts.map +1 -0
- package/dist/state.d.ts +81 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/theme-state-BZplRS92.js +886 -0
- package/dist/theme-state.d.ts +85 -0
- package/dist/theme-state.d.ts.map +1 -0
- package/dist/theme-state.locales.test.d.ts +2 -0
- package/dist/theme-state.locales.test.d.ts.map +1 -0
- package/dist/translation.d.ts +14 -0
- package/dist/translation.d.ts.map +1 -0
- package/dist/translation.test.d.ts +2 -0
- package/dist/translation.test.d.ts.map +1 -0
- package/dist/utils/index.d.ts +32 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/login.d.ts +11 -0
- package/dist/utils/login.d.ts.map +1 -0
- package/dist/utils/uuid.d.ts +2 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/vue/block.d.ts +7 -0
- package/dist/vue/block.d.ts.map +1 -0
- package/dist/vue/bridge.d.ts +11 -0
- package/dist/vue/bridge.d.ts.map +1 -0
- package/dist/vue/context.d.ts +24 -0
- package/dist/vue/context.d.ts.map +1 -0
- package/dist/vue/main.d.ts +6 -0
- package/dist/vue/main.d.ts.map +1 -0
- package/dist/vue/signals.d.ts +5 -0
- package/dist/vue/signals.d.ts.map +1 -0
- package/dist/vue/target.d.ts +2 -0
- package/dist/vue/target.d.ts.map +1 -0
- package/dist/vue/transform.d.ts +2 -0
- package/dist/vue/transform.d.ts.map +1 -0
- package/dist/vue.js +254 -0
- package/dist/web-components/Block.d.ts +17 -0
- package/dist/web-components/Block.d.ts.map +1 -0
- package/dist/web-components/Block.test.d.ts +2 -0
- package/dist/web-components/Block.test.d.ts.map +1 -0
- package/dist/web-components/ExtensionRoot.d.ts +29 -0
- package/dist/web-components/ExtensionRoot.d.ts.map +1 -0
- package/dist/web-components/editor/InlineText.d.ts +29 -0
- package/dist/web-components/editor/InlineText.d.ts.map +1 -0
- package/dist/web-components/editor/main.d.ts +2 -0
- package/dist/web-components/editor/main.d.ts.map +1 -0
- package/dist/web-components/runtime/InlineText.d.ts +6 -0
- package/dist/web-components/runtime/InlineText.d.ts.map +1 -0
- package/dist/web-components/runtime/main.d.ts +2 -0
- package/dist/web-components/runtime/main.d.ts.map +1 -0
- package/dist/web-components-editor.js +183 -0
- package/dist/web-components-runtime.js +55 -0
- package/package.json +62 -0
package/dist/core.js
ADDED
|
@@ -0,0 +1,1163 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { c as createContext, d, w, E, u } from "./theme-state-BZplRS92.js";
|
|
5
|
+
import { B, n, C, D, S, T, a, b, e, f, k, l, g, h, i, m, p, o, q, r, s, t, v, j } from "./theme-state-BZplRS92.js";
|
|
6
|
+
import { T as T2, W, c, b as b2, a as a2, g as g2, h as h2 } from "./juo-workflow-RSrOuKUH.js";
|
|
7
|
+
function createState() {
|
|
8
|
+
const discountCode = d(null);
|
|
9
|
+
const products = d([]);
|
|
10
|
+
const subscriptions = d([]);
|
|
11
|
+
const user = d({
|
|
12
|
+
name: "John Doe"
|
|
13
|
+
});
|
|
14
|
+
const upcomingOrder = w(() => {
|
|
15
|
+
if (subscriptions.value.length === 0) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const subscription = subscriptions.value[0];
|
|
19
|
+
const subtotal = Math.round(
|
|
20
|
+
100 * subscription.items.reduce(
|
|
21
|
+
(acc, item) => acc + item.price * item.quantity,
|
|
22
|
+
0
|
|
23
|
+
)
|
|
24
|
+
) / 100;
|
|
25
|
+
const deliveryPrice = subtotal >= 80 ? 0 : 4.9;
|
|
26
|
+
const discount = discountCode.value ? 0.2 : 0;
|
|
27
|
+
const totalDiscount = Math.round(100 * subtotal * discount) / 100;
|
|
28
|
+
return {
|
|
29
|
+
...subscription,
|
|
30
|
+
subtotal,
|
|
31
|
+
totalDiscount,
|
|
32
|
+
deliveryPrice,
|
|
33
|
+
totalPrice: Math.round(100 * (subtotal - totalDiscount + deliveryPrice)) / 100,
|
|
34
|
+
hasFreeProduct: subscription.items.some(({ price }) => price == 0)
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
discountCode,
|
|
39
|
+
products,
|
|
40
|
+
upcomingOrder,
|
|
41
|
+
subscriptions,
|
|
42
|
+
user,
|
|
43
|
+
setDiscountCode(code) {
|
|
44
|
+
discountCode.value = code;
|
|
45
|
+
},
|
|
46
|
+
addProduct(product) {
|
|
47
|
+
products.value = [...products.value, product];
|
|
48
|
+
},
|
|
49
|
+
getProductById(id) {
|
|
50
|
+
const product = products.value.find((product2) => product2.id === id);
|
|
51
|
+
if (product == null) {
|
|
52
|
+
throw new Error("Product not found");
|
|
53
|
+
}
|
|
54
|
+
return product;
|
|
55
|
+
},
|
|
56
|
+
addItemToUpcomingOrder(item) {
|
|
57
|
+
const product = this.getProductById(item.productId);
|
|
58
|
+
subscriptions.value = [
|
|
59
|
+
{
|
|
60
|
+
...subscriptions.value[0],
|
|
61
|
+
items: [
|
|
62
|
+
...subscriptions.value[0].items,
|
|
63
|
+
{ ...item, price: product.price }
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
...subscriptions.value.slice(1)
|
|
67
|
+
];
|
|
68
|
+
},
|
|
69
|
+
addSubscription(subscription) {
|
|
70
|
+
subscriptions.value = [...subscriptions.value, subscription];
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const StateContext = createContext("state");
|
|
75
|
+
const PARAMS = {
|
|
76
|
+
DELETGATED_TOKEN: "delegated-token",
|
|
77
|
+
REDIRECT_TO: "redirect-to",
|
|
78
|
+
ACTION: "action",
|
|
79
|
+
MISSING: "missing",
|
|
80
|
+
CODE: "code",
|
|
81
|
+
ERROR: "error",
|
|
82
|
+
STATE: "state",
|
|
83
|
+
REFRESH: "refresh"
|
|
84
|
+
};
|
|
85
|
+
function uuid() {
|
|
86
|
+
return ("10000000-1000-4000-8000" + -1e11).replace(
|
|
87
|
+
/[018]/g,
|
|
88
|
+
(c2) => (c2 ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c2 / 4).toString(16)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
|
|
92
|
+
AuthenticationErrorReason2["Expired"] = "expired";
|
|
93
|
+
AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
|
|
94
|
+
AuthenticationErrorReason2["TooManyTries"] = "too-many-tries";
|
|
95
|
+
AuthenticationErrorReason2["NoEmail"] = "no-email";
|
|
96
|
+
AuthenticationErrorReason2["Unknown"] = "unknown";
|
|
97
|
+
AuthenticationErrorReason2["Missing"] = "missing";
|
|
98
|
+
return AuthenticationErrorReason2;
|
|
99
|
+
})(AuthenticationErrorReason || {});
|
|
100
|
+
class AuthenticationError extends Error {
|
|
101
|
+
constructor(reason, data) {
|
|
102
|
+
super("Authentication error");
|
|
103
|
+
__publicField(this, "reason");
|
|
104
|
+
__publicField(this, "data");
|
|
105
|
+
this.reason = reason;
|
|
106
|
+
this.data = data ?? {};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function storage(key, value) {
|
|
110
|
+
const storageKey = `auth:${key}`;
|
|
111
|
+
try {
|
|
112
|
+
if (value === null) {
|
|
113
|
+
sessionStorage.removeItem(storageKey);
|
|
114
|
+
} else if (value !== void 0) {
|
|
115
|
+
sessionStorage.setItem(storageKey, value);
|
|
116
|
+
} else {
|
|
117
|
+
return sessionStorage.getItem("auth:" + key);
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.warn(`Failed to use session storage: ${error}`);
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
125
|
+
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
126
|
+
const STORAGE_STATE_KEY = "state";
|
|
127
|
+
function createAuthFunctions(basePath, shop) {
|
|
128
|
+
async function start(email, lang) {
|
|
129
|
+
const response = await fetch(
|
|
130
|
+
`${basePath}/auth/customer/passwordless/start?lang=${lang}`,
|
|
131
|
+
{
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers: {
|
|
134
|
+
Accept: "application/json",
|
|
135
|
+
"Content-Type": "application/json",
|
|
136
|
+
"X-Shopify-Shop-Domain": shop
|
|
137
|
+
},
|
|
138
|
+
body: JSON.stringify({ email })
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
if (response.status !== 200) {
|
|
142
|
+
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function authenticate(data) {
|
|
146
|
+
const response = await fetch(`${basePath}/auth/customer/token`, {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: {
|
|
149
|
+
Accept: "application/json",
|
|
150
|
+
"Content-Type": "application/json",
|
|
151
|
+
"X-Shopify-Shop-Domain": shop
|
|
152
|
+
},
|
|
153
|
+
body: JSON.stringify(data)
|
|
154
|
+
});
|
|
155
|
+
if (response.status === 403) {
|
|
156
|
+
const data2 = await response.json();
|
|
157
|
+
if ("error_description" in data2) {
|
|
158
|
+
if (data2.error_description === "Wrong email or verification code.") {
|
|
159
|
+
throw new AuthenticationError(
|
|
160
|
+
"wrong-code"
|
|
161
|
+
/* IncorrectCode */
|
|
162
|
+
);
|
|
163
|
+
} else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
164
|
+
throw new AuthenticationError(
|
|
165
|
+
"too-many-tries"
|
|
166
|
+
/* TooManyTries */
|
|
167
|
+
);
|
|
168
|
+
} else if (data2.error_description === "The verification code has expired. Please try to login again.") {
|
|
169
|
+
throw new AuthenticationError(
|
|
170
|
+
"expired"
|
|
171
|
+
/* Expired */
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
} else if ("code" in data2) {
|
|
175
|
+
if (data2.code === "AUTH_ERROR") {
|
|
176
|
+
throw new AuthenticationError("no-email", {
|
|
177
|
+
connection: data2.connection
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (response.status !== 200) {
|
|
183
|
+
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
184
|
+
}
|
|
185
|
+
return await response.json();
|
|
186
|
+
}
|
|
187
|
+
return { start, authenticate };
|
|
188
|
+
}
|
|
189
|
+
function nonce(len = 10) {
|
|
190
|
+
return uuid().replace(/-/g, "").slice(0, len);
|
|
191
|
+
}
|
|
192
|
+
function createAuthService(apiUrl, shopDomain) {
|
|
193
|
+
const { start: authStart, authenticate: authAuthenticate } = createAuthFunctions(apiUrl, shopDomain);
|
|
194
|
+
const accessToken = d(null);
|
|
195
|
+
const delegatedToken = d(
|
|
196
|
+
storage(STORAGE_DELEGATED_TOKEN)
|
|
197
|
+
);
|
|
198
|
+
const refreshToken = d(
|
|
199
|
+
storage(STORAGE_REFRESH_TOKEN_KEY)
|
|
200
|
+
);
|
|
201
|
+
const expires = d(null);
|
|
202
|
+
const state = d(storage(STORAGE_STATE_KEY));
|
|
203
|
+
E(() => {
|
|
204
|
+
storage(STORAGE_DELEGATED_TOKEN, delegatedToken.value);
|
|
205
|
+
});
|
|
206
|
+
E(() => {
|
|
207
|
+
storage(STORAGE_REFRESH_TOKEN_KEY, refreshToken.value);
|
|
208
|
+
});
|
|
209
|
+
E(() => {
|
|
210
|
+
storage(STORAGE_STATE_KEY, state.value);
|
|
211
|
+
});
|
|
212
|
+
function store(data) {
|
|
213
|
+
const { access_token, refresh_token, expires_in } = data;
|
|
214
|
+
accessToken.value = access_token;
|
|
215
|
+
refreshToken.value = refresh_token;
|
|
216
|
+
expires.value = new Date(Date.now() + expires_in);
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
state,
|
|
220
|
+
async start(email, lang) {
|
|
221
|
+
await authStart(email, lang);
|
|
222
|
+
},
|
|
223
|
+
async authenticate(data) {
|
|
224
|
+
store(await authAuthenticate(data));
|
|
225
|
+
},
|
|
226
|
+
getDelegatedToken() {
|
|
227
|
+
return delegatedToken.value;
|
|
228
|
+
},
|
|
229
|
+
setDelegatedToken(token) {
|
|
230
|
+
delegatedToken.value = token;
|
|
231
|
+
},
|
|
232
|
+
async getToken() {
|
|
233
|
+
if (refreshToken.value == null) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
237
|
+
accessToken.value = new Promise(
|
|
238
|
+
(resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
|
|
239
|
+
store(data);
|
|
240
|
+
resolve(data.access_token);
|
|
241
|
+
return;
|
|
242
|
+
}).catch(reject)
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
return Promise.resolve(accessToken.value);
|
|
246
|
+
},
|
|
247
|
+
logout() {
|
|
248
|
+
accessToken.value = null;
|
|
249
|
+
delegatedToken.value = null;
|
|
250
|
+
refreshToken.value = null;
|
|
251
|
+
expires.value = null;
|
|
252
|
+
},
|
|
253
|
+
isAuthenticated: w(
|
|
254
|
+
() => delegatedToken.value != null || refreshToken.value != null
|
|
255
|
+
)
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function loginService(deps) {
|
|
259
|
+
return createLoginService({
|
|
260
|
+
apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
|
|
261
|
+
router: deps.router,
|
|
262
|
+
formatError: deps.formatError,
|
|
263
|
+
shop: {
|
|
264
|
+
domain: deps.shop.domain,
|
|
265
|
+
locale: w(() => deps.getLocale())
|
|
266
|
+
},
|
|
267
|
+
onLoginSuccess: deps.onLoginSuccess
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function createLoginService(init) {
|
|
271
|
+
const { formatError, router, shop, onLoginSuccess } = init;
|
|
272
|
+
const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
|
|
273
|
+
const userEmail = d(null);
|
|
274
|
+
async function onSuccess(query) {
|
|
275
|
+
await (onLoginSuccess == null ? void 0 : onLoginSuccess());
|
|
276
|
+
if (query && query.has(PARAMS.REDIRECT_TO)) {
|
|
277
|
+
if (query.has(PARAMS.ACTION)) {
|
|
278
|
+
const redirect = query.get(PARAMS.REDIRECT_TO);
|
|
279
|
+
const queryParams = {};
|
|
280
|
+
query.forEach((value, key) => {
|
|
281
|
+
if (key !== PARAMS.DELETGATED_TOKEN && key !== PARAMS.REDIRECT_TO) {
|
|
282
|
+
queryParams[key] = value;
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
router.push({
|
|
286
|
+
path: decodeURIComponent(redirect),
|
|
287
|
+
query: queryParams
|
|
288
|
+
});
|
|
289
|
+
} else {
|
|
290
|
+
const redirect = query.get(PARAMS.REDIRECT_TO);
|
|
291
|
+
router.replace(decodeURIComponent(redirect));
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
router.replace("/");
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const service = {
|
|
298
|
+
passwordless: {
|
|
299
|
+
email: {
|
|
300
|
+
async start(email) {
|
|
301
|
+
try {
|
|
302
|
+
userEmail.value = email;
|
|
303
|
+
await auth.start(
|
|
304
|
+
userEmail.value,
|
|
305
|
+
shop.locale instanceof u ? shop.locale.value : shop.locale
|
|
306
|
+
);
|
|
307
|
+
return { _tag: "Success", data: void 0 };
|
|
308
|
+
} catch (error) {
|
|
309
|
+
return {
|
|
310
|
+
_tag: "Failure",
|
|
311
|
+
error: formatError(error)
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
async verify(email, code) {
|
|
316
|
+
if (email !== userEmail.value) {
|
|
317
|
+
return {
|
|
318
|
+
_tag: "Failure",
|
|
319
|
+
error: formatError("User email mismatch")
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
const result = await auth.authenticate({
|
|
324
|
+
email: userEmail.value,
|
|
325
|
+
code
|
|
326
|
+
});
|
|
327
|
+
await onSuccess();
|
|
328
|
+
return { _tag: "Success", data: result };
|
|
329
|
+
} catch (error) {
|
|
330
|
+
return {
|
|
331
|
+
_tag: "Failure",
|
|
332
|
+
error: formatError(error)
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
social: {
|
|
339
|
+
async start(provider) {
|
|
340
|
+
function socialUrl(key, shop2) {
|
|
341
|
+
auth.state.value = nonce();
|
|
342
|
+
const url = new URL(
|
|
343
|
+
// TODO: double check admin url
|
|
344
|
+
`${"https://admin.juo.io"}/auth/customer/authorize`
|
|
345
|
+
);
|
|
346
|
+
url.searchParams.append("connection", key);
|
|
347
|
+
url.searchParams.append("state", auth.state.value ?? "");
|
|
348
|
+
url.searchParams.append("shop", shop2);
|
|
349
|
+
return url.toString();
|
|
350
|
+
}
|
|
351
|
+
window.location.href = socialUrl(
|
|
352
|
+
provider === "Facebook" ? "facebook" : "google-oauth2",
|
|
353
|
+
shop.domain
|
|
354
|
+
);
|
|
355
|
+
return {
|
|
356
|
+
_tag: "Success",
|
|
357
|
+
data: void 0
|
|
358
|
+
};
|
|
359
|
+
},
|
|
360
|
+
async verify(code) {
|
|
361
|
+
try {
|
|
362
|
+
const result = await auth.authenticate({ code });
|
|
363
|
+
await onSuccess();
|
|
364
|
+
return { _tag: "Success", data: result };
|
|
365
|
+
} catch (error) {
|
|
366
|
+
return {
|
|
367
|
+
_tag: "Failure",
|
|
368
|
+
error: formatError(error)
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
async logout() {
|
|
374
|
+
await auth.logout();
|
|
375
|
+
return { _tag: "Success", data: void 0 };
|
|
376
|
+
},
|
|
377
|
+
async handleQueryParams(query) {
|
|
378
|
+
if (query.has(PARAMS.DELETGATED_TOKEN)) {
|
|
379
|
+
auth.setDelegatedToken(query.get(PARAMS.DELETGATED_TOKEN));
|
|
380
|
+
await onSuccess(query);
|
|
381
|
+
return { _tag: "Success", data: void 0 };
|
|
382
|
+
}
|
|
383
|
+
if (query.has(PARAMS.ERROR)) {
|
|
384
|
+
return {
|
|
385
|
+
_tag: "Failure",
|
|
386
|
+
error: formatError(query.get(PARAMS.ERROR))
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
const code = query.get(PARAMS.CODE);
|
|
390
|
+
if (code != null) {
|
|
391
|
+
if (query.get(PARAMS.STATE) !== auth.state.value) {
|
|
392
|
+
return {
|
|
393
|
+
_tag: "Failure",
|
|
394
|
+
error: formatError("Invalid state")
|
|
395
|
+
};
|
|
396
|
+
} else {
|
|
397
|
+
return service.social.verify(code);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (query.get(PARAMS.REFRESH) === "1" && await auth.getToken() != null) {
|
|
401
|
+
await onSuccess();
|
|
402
|
+
return { _tag: "Success", data: void 0 };
|
|
403
|
+
}
|
|
404
|
+
if (query.get(PARAMS.MISSING) === "1") {
|
|
405
|
+
return {
|
|
406
|
+
_tag: "Failure",
|
|
407
|
+
error: formatError(
|
|
408
|
+
new AuthenticationError(
|
|
409
|
+
"missing"
|
|
410
|
+
/* Missing */
|
|
411
|
+
)
|
|
412
|
+
)
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return { _tag: "Success", data: void 0 };
|
|
416
|
+
},
|
|
417
|
+
isAuthenticated: auth.isAuthenticated,
|
|
418
|
+
getToken: () => auth.getToken()
|
|
419
|
+
};
|
|
420
|
+
return service;
|
|
421
|
+
}
|
|
422
|
+
const LoginServiceContext = createContext("services/login");
|
|
423
|
+
function createMockProductAdapter() {
|
|
424
|
+
const products = [
|
|
425
|
+
{
|
|
426
|
+
id: "sample-1",
|
|
427
|
+
title: "Hydrating Gel Cleanser for Face and Eye Area",
|
|
428
|
+
featuredImage: {
|
|
429
|
+
url: "https://via.placeholder.com/400x400?text=Product+1"
|
|
430
|
+
},
|
|
431
|
+
hasOnlyDefaultVariant: true,
|
|
432
|
+
variants: [
|
|
433
|
+
{
|
|
434
|
+
id: "sample-1-1",
|
|
435
|
+
title: "150 ml bottle",
|
|
436
|
+
image: {
|
|
437
|
+
url: "https://via.placeholder.com/400x400?text=Product+1"
|
|
438
|
+
},
|
|
439
|
+
price: {
|
|
440
|
+
amount: "12.49",
|
|
441
|
+
currencyCode: "EUR"
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
],
|
|
445
|
+
collections: []
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
id: "sample-2",
|
|
449
|
+
title: "More COLLAGEN",
|
|
450
|
+
featuredImage: {
|
|
451
|
+
url: "https://via.placeholder.com/400x400?text=Product+2"
|
|
452
|
+
},
|
|
453
|
+
hasOnlyDefaultVariant: true,
|
|
454
|
+
variants: [
|
|
455
|
+
{
|
|
456
|
+
id: "sample-2-1",
|
|
457
|
+
title: "blackcurrant",
|
|
458
|
+
image: {
|
|
459
|
+
url: "https://via.placeholder.com/400x400?text=Product+2"
|
|
460
|
+
},
|
|
461
|
+
price: {
|
|
462
|
+
amount: "12.49",
|
|
463
|
+
currencyCode: "EUR"
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
],
|
|
467
|
+
collections: []
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
id: "sample-3",
|
|
471
|
+
title: "Gentle deodorant for underarms and bust",
|
|
472
|
+
featuredImage: {
|
|
473
|
+
url: "https://via.placeholder.com/400x400?text=Product+3"
|
|
474
|
+
},
|
|
475
|
+
hasOnlyDefaultVariant: true,
|
|
476
|
+
variants: [
|
|
477
|
+
{
|
|
478
|
+
id: "sample-3-1",
|
|
479
|
+
title: "Green tea and jasmine",
|
|
480
|
+
image: {
|
|
481
|
+
url: "https://via.placeholder.com/400x400?text=Product+3"
|
|
482
|
+
},
|
|
483
|
+
price: {
|
|
484
|
+
amount: "6.7",
|
|
485
|
+
currencyCode: "EUR"
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
],
|
|
489
|
+
collections: []
|
|
490
|
+
}
|
|
491
|
+
];
|
|
492
|
+
function processProduct(product) {
|
|
493
|
+
return {
|
|
494
|
+
...product,
|
|
495
|
+
variants: {
|
|
496
|
+
items: product.variants,
|
|
497
|
+
pageInfo: {
|
|
498
|
+
hasNextPage: false,
|
|
499
|
+
hasPrevPage: false,
|
|
500
|
+
startCursor: null,
|
|
501
|
+
endCursor: null
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
async getById(id) {
|
|
508
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
509
|
+
const product = products.find((product2) => product2.id === id);
|
|
510
|
+
return product == null ? null : processProduct(product);
|
|
511
|
+
},
|
|
512
|
+
async getByVariantId(variantId) {
|
|
513
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
514
|
+
const product = products.find(
|
|
515
|
+
(product2) => product2.variants.some((variant) => variant.id === variantId)
|
|
516
|
+
);
|
|
517
|
+
return product == null ? null : processProduct(product);
|
|
518
|
+
},
|
|
519
|
+
async search(opts) {
|
|
520
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
521
|
+
let filteredProducts = [...products];
|
|
522
|
+
if (opts == null ? void 0 : opts.query) {
|
|
523
|
+
const queryLower = opts.query.toLowerCase();
|
|
524
|
+
filteredProducts = filteredProducts.filter(
|
|
525
|
+
(product) => product.title.toLowerCase().includes(queryLower) || product.variants.some(
|
|
526
|
+
(variant) => variant.title.toLowerCase().includes(queryLower)
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
let startIndex = 0;
|
|
531
|
+
let endIndex = filteredProducts.length;
|
|
532
|
+
const pageSize = 10;
|
|
533
|
+
if (opts == null ? void 0 : opts.pagination) {
|
|
534
|
+
if ("after" in opts.pagination) {
|
|
535
|
+
const cursorIndex = parseInt(opts.pagination.after, 10);
|
|
536
|
+
startIndex = cursorIndex + 1;
|
|
537
|
+
endIndex = Math.min(startIndex + pageSize, filteredProducts.length);
|
|
538
|
+
} else if ("before" in opts.pagination) {
|
|
539
|
+
const cursorIndex = parseInt(opts.pagination.before, 10);
|
|
540
|
+
endIndex = cursorIndex;
|
|
541
|
+
startIndex = Math.max(0, endIndex - pageSize);
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
endIndex = Math.min(pageSize, filteredProducts.length);
|
|
545
|
+
}
|
|
546
|
+
const pageItems = filteredProducts.slice(startIndex, endIndex);
|
|
547
|
+
return {
|
|
548
|
+
items: pageItems.map(processProduct),
|
|
549
|
+
pageInfo: {
|
|
550
|
+
hasNextPage: endIndex < filteredProducts.length,
|
|
551
|
+
hasPrevPage: startIndex > 0,
|
|
552
|
+
startCursor: pageItems.length > 0 ? startIndex.toString() : null,
|
|
553
|
+
endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
const ProductServiceContext = createContext("services/product");
|
|
560
|
+
function createProductService(adapter) {
|
|
561
|
+
return {
|
|
562
|
+
async getById(id) {
|
|
563
|
+
return adapter.getById(id);
|
|
564
|
+
},
|
|
565
|
+
async getByVariantId(variantId) {
|
|
566
|
+
return adapter.getByVariantId(variantId);
|
|
567
|
+
},
|
|
568
|
+
async search(opts) {
|
|
569
|
+
return adapter.search(opts);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function createMockSubscriptionAdapter() {
|
|
574
|
+
const mockSubscriptions = [
|
|
575
|
+
{
|
|
576
|
+
id: "sub_1",
|
|
577
|
+
customerId: "customer_1",
|
|
578
|
+
items: [
|
|
579
|
+
{
|
|
580
|
+
id: "item_1",
|
|
581
|
+
productId: "product_1",
|
|
582
|
+
variantId: "variant_1",
|
|
583
|
+
quantity: 2,
|
|
584
|
+
price: { amount: "29.99", currencyCode: "USD" },
|
|
585
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15")
|
|
586
|
+
}
|
|
587
|
+
],
|
|
588
|
+
discounts: [
|
|
589
|
+
{
|
|
590
|
+
id: "discount_1",
|
|
591
|
+
type: "percentage",
|
|
592
|
+
value: 10,
|
|
593
|
+
code: "SAVE10",
|
|
594
|
+
description: "10% off subscription"
|
|
595
|
+
}
|
|
596
|
+
],
|
|
597
|
+
status: "active",
|
|
598
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
599
|
+
frequency: "1 MONTH",
|
|
600
|
+
createdAt: "2024-10-01T00:00:00Z",
|
|
601
|
+
updatedAt: "2024-10-14T00:00:00Z"
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
id: "sub_2",
|
|
605
|
+
customerId: "customer_1",
|
|
606
|
+
items: [
|
|
607
|
+
{
|
|
608
|
+
id: "item_2",
|
|
609
|
+
productId: "product_2",
|
|
610
|
+
variantId: "variant_2",
|
|
611
|
+
quantity: 1,
|
|
612
|
+
price: { amount: "49.99", currencyCode: "USD" },
|
|
613
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20")
|
|
614
|
+
}
|
|
615
|
+
],
|
|
616
|
+
discounts: [],
|
|
617
|
+
status: "active",
|
|
618
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
|
|
619
|
+
frequency: "2 WEEK",
|
|
620
|
+
createdAt: "2024-09-15T00:00:00Z",
|
|
621
|
+
updatedAt: "2024-10-10T00:00:00Z"
|
|
622
|
+
}
|
|
623
|
+
];
|
|
624
|
+
return {
|
|
625
|
+
async getById(id) {
|
|
626
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
627
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === id) || null;
|
|
628
|
+
return { _tag: "Success", data: subscription };
|
|
629
|
+
},
|
|
630
|
+
async search(options = {}) {
|
|
631
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
632
|
+
const pageSize = 1;
|
|
633
|
+
const allItems = mockSubscriptions;
|
|
634
|
+
let startIndex = 0;
|
|
635
|
+
let endIndex = pageSize;
|
|
636
|
+
if (options.pagination) {
|
|
637
|
+
if ("after" in options.pagination) {
|
|
638
|
+
const cursorIndex = parseInt(options.pagination.after, 10);
|
|
639
|
+
startIndex = cursorIndex + 1;
|
|
640
|
+
endIndex = startIndex + pageSize;
|
|
641
|
+
} else if ("before" in options.pagination) {
|
|
642
|
+
const cursorIndex = parseInt(options.pagination.before, 10);
|
|
643
|
+
endIndex = cursorIndex;
|
|
644
|
+
startIndex = Math.max(0, endIndex - pageSize);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const pageItems = allItems.slice(startIndex, endIndex);
|
|
648
|
+
const paginatedList = {
|
|
649
|
+
items: pageItems.map((sub) => ({
|
|
650
|
+
...sub,
|
|
651
|
+
items: sub.items.map((item) => ({ ...item })),
|
|
652
|
+
discounts: sub.discounts.map((discount) => ({ ...discount }))
|
|
653
|
+
})),
|
|
654
|
+
pageInfo: {
|
|
655
|
+
hasNextPage: endIndex < allItems.length,
|
|
656
|
+
hasPrevPage: startIndex > 0,
|
|
657
|
+
startCursor: pageItems.length > 0 ? startIndex.toString() : null,
|
|
658
|
+
endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
return { _tag: "Success", data: paginatedList };
|
|
662
|
+
},
|
|
663
|
+
async addItem() {
|
|
664
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
665
|
+
return { _tag: "Success", data: void 0 };
|
|
666
|
+
},
|
|
667
|
+
async removeItem() {
|
|
668
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
669
|
+
return { _tag: "Success", data: void 0 };
|
|
670
|
+
},
|
|
671
|
+
async addDiscount() {
|
|
672
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
673
|
+
return { _tag: "Success", data: void 0 };
|
|
674
|
+
},
|
|
675
|
+
async removeDiscount() {
|
|
676
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
677
|
+
return { _tag: "Success", data: void 0 };
|
|
678
|
+
},
|
|
679
|
+
async reschedule(params) {
|
|
680
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
681
|
+
const subscription = mockSubscriptions.find(
|
|
682
|
+
(sub) => sub.id === params.subscriptionId
|
|
683
|
+
);
|
|
684
|
+
if (!subscription) {
|
|
685
|
+
return {
|
|
686
|
+
_tag: "Failure",
|
|
687
|
+
error: "Subscription not found"
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
subscription.nextDeliveryDate = new Date(params.newDate);
|
|
691
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
692
|
+
subscription.items.forEach((item) => {
|
|
693
|
+
item.nextDeliveryDate = new Date(params.newDate);
|
|
694
|
+
});
|
|
695
|
+
return {
|
|
696
|
+
_tag: "Success",
|
|
697
|
+
data: {
|
|
698
|
+
...subscription,
|
|
699
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
700
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
},
|
|
704
|
+
async resume(params) {
|
|
705
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
706
|
+
const subscription = mockSubscriptions.find(
|
|
707
|
+
(sub) => sub.id === params.subscriptionId
|
|
708
|
+
);
|
|
709
|
+
if (!subscription) {
|
|
710
|
+
return {
|
|
711
|
+
_tag: "Failure",
|
|
712
|
+
error: "Subscription not found"
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
subscription.status = "active";
|
|
716
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
717
|
+
return {
|
|
718
|
+
_tag: "Success",
|
|
719
|
+
data: {
|
|
720
|
+
...subscription,
|
|
721
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
722
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
},
|
|
726
|
+
async pause(params) {
|
|
727
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
728
|
+
const subscription = mockSubscriptions.find(
|
|
729
|
+
(sub) => sub.id === params.subscriptionId
|
|
730
|
+
);
|
|
731
|
+
if (!subscription) {
|
|
732
|
+
return {
|
|
733
|
+
_tag: "Failure",
|
|
734
|
+
error: "Subscription not found"
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
subscription.status = "paused";
|
|
738
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
739
|
+
return {
|
|
740
|
+
_tag: "Success",
|
|
741
|
+
data: {
|
|
742
|
+
...subscription,
|
|
743
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
744
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
},
|
|
748
|
+
async reactivate(params) {
|
|
749
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
750
|
+
const subscription = mockSubscriptions.find(
|
|
751
|
+
(sub) => sub.id === params.subscriptionId
|
|
752
|
+
);
|
|
753
|
+
if (!subscription) {
|
|
754
|
+
return {
|
|
755
|
+
_tag: "Failure",
|
|
756
|
+
error: "Subscription not found"
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
subscription.status = "active";
|
|
760
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
761
|
+
return {
|
|
762
|
+
_tag: "Success",
|
|
763
|
+
data: {
|
|
764
|
+
...subscription,
|
|
765
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
766
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
},
|
|
770
|
+
async cancel(params) {
|
|
771
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
772
|
+
const subscription = mockSubscriptions.find(
|
|
773
|
+
(sub) => sub.id === params.subscriptionId
|
|
774
|
+
);
|
|
775
|
+
if (!subscription) {
|
|
776
|
+
return {
|
|
777
|
+
_tag: "Failure",
|
|
778
|
+
error: "Subscription not found"
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
subscription.status = "canceled";
|
|
782
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
783
|
+
return {
|
|
784
|
+
_tag: "Success",
|
|
785
|
+
data: {
|
|
786
|
+
...subscription,
|
|
787
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
788
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
},
|
|
792
|
+
async getAvailablePlans() {
|
|
793
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
794
|
+
return {
|
|
795
|
+
_tag: "Success",
|
|
796
|
+
data: [
|
|
797
|
+
{
|
|
798
|
+
id: "plan_1",
|
|
799
|
+
name: "Monthly",
|
|
800
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
801
|
+
deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
id: "plan_2",
|
|
805
|
+
name: "Every 2 months",
|
|
806
|
+
billingPolicy: { intervalCount: 2, interval: "MONTH" },
|
|
807
|
+
deliveryPolicy: { intervalCount: 2, interval: "MONTH" }
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
id: "plan_3",
|
|
811
|
+
name: "Every 3 months",
|
|
812
|
+
billingPolicy: { intervalCount: 3, interval: "MONTH" },
|
|
813
|
+
deliveryPolicy: { intervalCount: 3, interval: "MONTH" }
|
|
814
|
+
}
|
|
815
|
+
]
|
|
816
|
+
};
|
|
817
|
+
},
|
|
818
|
+
async getDefaultSubscriptionId() {
|
|
819
|
+
var _a;
|
|
820
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
821
|
+
return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
const SubscriptionServiceContext = createContext("services/subscription");
|
|
826
|
+
function getCurrentSubscriptionId() {
|
|
827
|
+
if (typeof window === "undefined") {
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
const pathname = window.location.pathname;
|
|
831
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
832
|
+
const subscriptionIndex = segments.findIndex(
|
|
833
|
+
(segment) => segment === "subscriptions" || segment === "subscription"
|
|
834
|
+
);
|
|
835
|
+
if (subscriptionIndex !== -1 && segments[subscriptionIndex + 1]) {
|
|
836
|
+
return segments[subscriptionIndex + 1];
|
|
837
|
+
}
|
|
838
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
839
|
+
const lastSegment = segments[segments.length - 1];
|
|
840
|
+
if (lastSegment && uuidPattern.test(lastSegment)) {
|
|
841
|
+
return lastSegment;
|
|
842
|
+
}
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
845
|
+
function createSubscriptionService(adapter) {
|
|
846
|
+
return {
|
|
847
|
+
search: adapter.search,
|
|
848
|
+
getById: adapter.getById,
|
|
849
|
+
getCurrentSubscriptionId,
|
|
850
|
+
addItem: (subscriptionId, item) => adapter.addItem({ subscriptionId, item }),
|
|
851
|
+
removeItem: (subscriptionId, itemId) => adapter.removeItem({ subscriptionId, itemId }),
|
|
852
|
+
addDiscount: (subscriptionId, code) => adapter.addDiscount({ subscriptionId, code }),
|
|
853
|
+
removeDiscount: (subscriptionId, discountId) => adapter.removeDiscount({ subscriptionId, discountId }),
|
|
854
|
+
reschedule: (subscriptionId, newDate) => adapter.reschedule({ subscriptionId, newDate }),
|
|
855
|
+
resume: (subscriptionId) => adapter.resume({ subscriptionId }),
|
|
856
|
+
pause: (subscriptionId) => adapter.pause({ subscriptionId }),
|
|
857
|
+
reactivate: (subscriptionId) => adapter.reactivate({ subscriptionId }),
|
|
858
|
+
cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
|
|
859
|
+
getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
|
|
860
|
+
getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId()
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
const MOCK_FLOWS = {
|
|
864
|
+
cancellation: [
|
|
865
|
+
{
|
|
866
|
+
stepId: "step-reason",
|
|
867
|
+
actionType: "poll",
|
|
868
|
+
actionConfig: { question: "Why are you cancelling?" },
|
|
869
|
+
availableResponses: [
|
|
870
|
+
{ type: "too_expensive", label: "Too expensive" },
|
|
871
|
+
{ type: "not_using", label: "Not using it" },
|
|
872
|
+
{ type: "switching", label: "Switching to competitor" },
|
|
873
|
+
{ type: "other", label: "Other reason" }
|
|
874
|
+
]
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
stepId: "step-offer-discount",
|
|
878
|
+
actionType: "offer_discount",
|
|
879
|
+
actionConfig: {
|
|
880
|
+
headline: "We'd hate to see you go!",
|
|
881
|
+
description: "How about 20% off your next 3 months?",
|
|
882
|
+
discountType: "percentage",
|
|
883
|
+
discountValue: 20,
|
|
884
|
+
recurringCycleLimit: 3
|
|
885
|
+
},
|
|
886
|
+
availableResponses: [
|
|
887
|
+
{ type: "accepted", label: "Accept offer" },
|
|
888
|
+
{ type: "declined", label: "No thanks" }
|
|
889
|
+
]
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
stepId: "step-feedback",
|
|
893
|
+
actionType: "collect_feedback",
|
|
894
|
+
actionConfig: {
|
|
895
|
+
headline: "Any final thoughts?",
|
|
896
|
+
description: "Your feedback helps us improve.",
|
|
897
|
+
placeholder: "Tell us what we could do better...",
|
|
898
|
+
submitLabel: "Submit feedback",
|
|
899
|
+
skipLabel: "Skip"
|
|
900
|
+
},
|
|
901
|
+
availableResponses: [
|
|
902
|
+
{ type: "submitted", label: "Submit" },
|
|
903
|
+
{ type: "skipped", label: "Skip" }
|
|
904
|
+
]
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
stepId: "step-confirmation",
|
|
908
|
+
actionType: "show_confirmation",
|
|
909
|
+
actionConfig: {
|
|
910
|
+
headline: "Thank you!",
|
|
911
|
+
message: "Your feedback has been recorded.",
|
|
912
|
+
buttonLabel: "Done"
|
|
913
|
+
},
|
|
914
|
+
availableResponses: [
|
|
915
|
+
{ type: "acknowledge", label: "Done" }
|
|
916
|
+
]
|
|
917
|
+
}
|
|
918
|
+
],
|
|
919
|
+
retention: [
|
|
920
|
+
{
|
|
921
|
+
stepId: "step-offer-pause",
|
|
922
|
+
actionType: "offer_pause",
|
|
923
|
+
actionConfig: {
|
|
924
|
+
headline: "Need a break?",
|
|
925
|
+
description: "Pause your subscription for up to 30 days instead of cancelling.",
|
|
926
|
+
pauseDurationDays: 30
|
|
927
|
+
},
|
|
928
|
+
availableResponses: [
|
|
929
|
+
{ type: "accepted", label: "Pause subscription" },
|
|
930
|
+
{ type: "declined", label: "No thanks" }
|
|
931
|
+
]
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
stepId: "step-offer-skip",
|
|
935
|
+
actionType: "offer_skip",
|
|
936
|
+
actionConfig: {
|
|
937
|
+
headline: "Skip next delivery?",
|
|
938
|
+
description: "You can skip your next delivery and resume later."
|
|
939
|
+
},
|
|
940
|
+
availableResponses: [
|
|
941
|
+
{ type: "accepted", label: "Skip next delivery" },
|
|
942
|
+
{ type: "declined", label: "No thanks" }
|
|
943
|
+
]
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
stepId: "step-done",
|
|
947
|
+
actionType: "show_confirmation",
|
|
948
|
+
actionConfig: {
|
|
949
|
+
headline: "All set!",
|
|
950
|
+
message: "We've processed your request.",
|
|
951
|
+
buttonLabel: "OK"
|
|
952
|
+
},
|
|
953
|
+
availableResponses: [
|
|
954
|
+
{ type: "acknowledge", label: "OK" }
|
|
955
|
+
]
|
|
956
|
+
}
|
|
957
|
+
],
|
|
958
|
+
dunning: [
|
|
959
|
+
{
|
|
960
|
+
stepId: "step-payment-issue",
|
|
961
|
+
actionType: "show_content",
|
|
962
|
+
actionConfig: {
|
|
963
|
+
headline: "Payment issue",
|
|
964
|
+
body: "<p>We were unable to process your last payment. Please update your payment method to continue your subscription.</p>",
|
|
965
|
+
buttonLabel: "Update payment"
|
|
966
|
+
},
|
|
967
|
+
availableResponses: [
|
|
968
|
+
{ type: "acknowledge", label: "Update payment" }
|
|
969
|
+
]
|
|
970
|
+
}
|
|
971
|
+
],
|
|
972
|
+
onboarding: [
|
|
973
|
+
{
|
|
974
|
+
stepId: "step-welcome",
|
|
975
|
+
actionType: "show_content",
|
|
976
|
+
actionConfig: {
|
|
977
|
+
headline: "Welcome!",
|
|
978
|
+
body: "<p>Thanks for subscribing. Here's what to expect from your subscription.</p>",
|
|
979
|
+
buttonLabel: "Got it"
|
|
980
|
+
},
|
|
981
|
+
availableResponses: [
|
|
982
|
+
{ type: "acknowledge", label: "Got it" }
|
|
983
|
+
]
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
stepId: "step-choice",
|
|
987
|
+
actionType: "choice",
|
|
988
|
+
actionConfig: {
|
|
989
|
+
headline: "How would you like to get started?",
|
|
990
|
+
description: "Choose what works best for you."
|
|
991
|
+
},
|
|
992
|
+
availableResponses: [
|
|
993
|
+
{ type: "tour", label: "Take a tour" },
|
|
994
|
+
{ type: "skip", label: "Skip for now" }
|
|
995
|
+
]
|
|
996
|
+
}
|
|
997
|
+
]
|
|
998
|
+
};
|
|
999
|
+
const mockRuns = /* @__PURE__ */ new Map();
|
|
1000
|
+
let runCounter = 0;
|
|
1001
|
+
function delay(ms = 300) {
|
|
1002
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1003
|
+
}
|
|
1004
|
+
function toStepDefinitions(steps) {
|
|
1005
|
+
return steps.map((step) => ({
|
|
1006
|
+
stepId: step.stepId,
|
|
1007
|
+
actionType: step.actionType,
|
|
1008
|
+
actionConfig: step.actionConfig,
|
|
1009
|
+
availableResponses: step.availableResponses
|
|
1010
|
+
}));
|
|
1011
|
+
}
|
|
1012
|
+
function createInteraction(step, runId) {
|
|
1013
|
+
return {
|
|
1014
|
+
interactionId: `mock-int-${runId}-${step.stepId}`,
|
|
1015
|
+
stepId: step.stepId,
|
|
1016
|
+
actionType: step.actionType,
|
|
1017
|
+
actionBehavior: "input",
|
|
1018
|
+
actionConfig: step.actionConfig,
|
|
1019
|
+
availableResponses: step.availableResponses,
|
|
1020
|
+
presentedAt: /* @__PURE__ */ new Date()
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
function createMockWorkflowApiAdapter() {
|
|
1024
|
+
return {
|
|
1025
|
+
async start(flowType, _context) {
|
|
1026
|
+
await delay(400);
|
|
1027
|
+
const steps = MOCK_FLOWS[flowType];
|
|
1028
|
+
if (!steps) {
|
|
1029
|
+
return { status: "no_workflow" };
|
|
1030
|
+
}
|
|
1031
|
+
runCounter++;
|
|
1032
|
+
const runId = `mock-run-${runCounter}`;
|
|
1033
|
+
const workflowId = `mock-wf-${flowType}`;
|
|
1034
|
+
const run = {
|
|
1035
|
+
workflowId,
|
|
1036
|
+
flowType,
|
|
1037
|
+
steps,
|
|
1038
|
+
currentStepIndex: 0,
|
|
1039
|
+
runVersion: 1
|
|
1040
|
+
};
|
|
1041
|
+
mockRuns.set(runId, run);
|
|
1042
|
+
const step = steps[0];
|
|
1043
|
+
return {
|
|
1044
|
+
workflowId,
|
|
1045
|
+
workflowVersion: 1,
|
|
1046
|
+
runId,
|
|
1047
|
+
runVersion: 1,
|
|
1048
|
+
status: "waiting_interaction",
|
|
1049
|
+
pendingInteraction: createInteraction(step, runId),
|
|
1050
|
+
interactiveSteps: toStepDefinitions(steps)
|
|
1051
|
+
};
|
|
1052
|
+
},
|
|
1053
|
+
async respond(runId, _interactionId, _responseType, _responseData, _expectedRunVersion) {
|
|
1054
|
+
await delay(300);
|
|
1055
|
+
const run = mockRuns.get(runId);
|
|
1056
|
+
if (!run) {
|
|
1057
|
+
throw new Error(`Mock run ${runId} not found`);
|
|
1058
|
+
}
|
|
1059
|
+
run.currentStepIndex++;
|
|
1060
|
+
run.runVersion++;
|
|
1061
|
+
if (run.currentStepIndex >= run.steps.length) {
|
|
1062
|
+
mockRuns.delete(runId);
|
|
1063
|
+
return {
|
|
1064
|
+
workflowId: run.workflowId,
|
|
1065
|
+
workflowVersion: 1,
|
|
1066
|
+
runId,
|
|
1067
|
+
runVersion: run.runVersion,
|
|
1068
|
+
status: "completed",
|
|
1069
|
+
result: { flowType: run.flowType, completedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
const nextStep = run.steps[run.currentStepIndex];
|
|
1073
|
+
return {
|
|
1074
|
+
workflowId: run.workflowId,
|
|
1075
|
+
workflowVersion: 1,
|
|
1076
|
+
runId,
|
|
1077
|
+
runVersion: run.runVersion,
|
|
1078
|
+
status: "waiting_interaction",
|
|
1079
|
+
pendingInteraction: createInteraction(nextStep, runId)
|
|
1080
|
+
};
|
|
1081
|
+
},
|
|
1082
|
+
async getState(runId) {
|
|
1083
|
+
await delay(100);
|
|
1084
|
+
const run = mockRuns.get(runId);
|
|
1085
|
+
if (!run) {
|
|
1086
|
+
throw new Error(`Mock run ${runId} not found`);
|
|
1087
|
+
}
|
|
1088
|
+
const step = run.steps[run.currentStepIndex];
|
|
1089
|
+
return {
|
|
1090
|
+
workflowId: run.workflowId,
|
|
1091
|
+
workflowVersion: 1,
|
|
1092
|
+
runId,
|
|
1093
|
+
runVersion: run.runVersion,
|
|
1094
|
+
status: "waiting_interaction",
|
|
1095
|
+
pendingInteraction: createInteraction(step, runId)
|
|
1096
|
+
};
|
|
1097
|
+
},
|
|
1098
|
+
async cancel(runId) {
|
|
1099
|
+
await delay(100);
|
|
1100
|
+
const run = mockRuns.get(runId);
|
|
1101
|
+
const workflowId = (run == null ? void 0 : run.workflowId) ?? "unknown";
|
|
1102
|
+
mockRuns.delete(runId);
|
|
1103
|
+
return {
|
|
1104
|
+
workflowId,
|
|
1105
|
+
workflowVersion: 1,
|
|
1106
|
+
runId,
|
|
1107
|
+
runVersion: ((run == null ? void 0 : run.runVersion) ?? 0) + 1,
|
|
1108
|
+
status: "cancelled"
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
export {
|
|
1114
|
+
AuthenticationError,
|
|
1115
|
+
AuthenticationErrorReason,
|
|
1116
|
+
B as BlockWrapperEvent,
|
|
1117
|
+
n as ContextProvideEvent,
|
|
1118
|
+
C as ContextRequestEvent,
|
|
1119
|
+
D as DEFAULT_GLOBAL_STYLES,
|
|
1120
|
+
LoginServiceContext,
|
|
1121
|
+
ProductServiceContext,
|
|
1122
|
+
S as SelectedBlockContext,
|
|
1123
|
+
u as Signal,
|
|
1124
|
+
StateContext,
|
|
1125
|
+
SubscriptionServiceContext,
|
|
1126
|
+
T as ThemeStateContext,
|
|
1127
|
+
T2 as TranslationContext,
|
|
1128
|
+
W as WorkflowServiceContext,
|
|
1129
|
+
w as computed,
|
|
1130
|
+
a as createBlockInstance,
|
|
1131
|
+
b as createBlockInstanceFromJSON,
|
|
1132
|
+
e as createBlockInstanceFromObject,
|
|
1133
|
+
f as createCeRenderer,
|
|
1134
|
+
createContext,
|
|
1135
|
+
createMockProductAdapter,
|
|
1136
|
+
createMockSubscriptionAdapter,
|
|
1137
|
+
createMockWorkflowApiAdapter,
|
|
1138
|
+
createProductService,
|
|
1139
|
+
k as createSelectedBlock,
|
|
1140
|
+
createState,
|
|
1141
|
+
createSubscriptionService,
|
|
1142
|
+
l as createThemeState,
|
|
1143
|
+
c as createTranslationService,
|
|
1144
|
+
b2 as createWorkflowApiAdapter,
|
|
1145
|
+
a2 as createWorkflowService,
|
|
1146
|
+
g as defineBlock,
|
|
1147
|
+
h as defineBlockRuntime,
|
|
1148
|
+
E as effect,
|
|
1149
|
+
g2 as getDefaultBlockForAction,
|
|
1150
|
+
i as getDefinedBlocks,
|
|
1151
|
+
h2 as hasDefaultBlockForAction,
|
|
1152
|
+
m as indexOverrides,
|
|
1153
|
+
p as injectContext,
|
|
1154
|
+
loginService,
|
|
1155
|
+
o as onRegisterBlock,
|
|
1156
|
+
q as provideContext,
|
|
1157
|
+
r as registerBlock,
|
|
1158
|
+
s as serializeBlocks,
|
|
1159
|
+
d as signal,
|
|
1160
|
+
t as subscribeContext,
|
|
1161
|
+
v as untracked,
|
|
1162
|
+
j as withWrapper
|
|
1163
|
+
};
|