@hook-sdk/template 0.9.1 → 0.10.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/index.cjs +1229 -1690
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +323 -128
- package/dist/index.d.ts +323 -128
- package/dist/index.js +1183 -1652
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -1,11 +1,142 @@
|
|
|
1
1
|
// src/AppRoot.tsx
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useMemo as useMemo3 } from "react";
|
|
3
|
+
import { BrowserRouter, MemoryRouter, Navigate, Route, Routes } from "react-router-dom";
|
|
4
|
+
import { useHook as useHook4 } from "@hook-sdk/sdk";
|
|
4
5
|
|
|
5
|
-
// src/
|
|
6
|
-
import { createContext, useContext
|
|
6
|
+
// src/config/AppConfigContext.tsx
|
|
7
|
+
import { createContext, useContext } from "react";
|
|
7
8
|
import { jsx } from "react/jsx-runtime";
|
|
8
|
-
var
|
|
9
|
+
var AppConfigContext = createContext(null);
|
|
10
|
+
function AppConfigProvider({
|
|
11
|
+
config,
|
|
12
|
+
children
|
|
13
|
+
}) {
|
|
14
|
+
return /* @__PURE__ */ jsx(AppConfigContext.Provider, { value: config, children });
|
|
15
|
+
}
|
|
16
|
+
function useAppConfig() {
|
|
17
|
+
const v = useContext(AppConfigContext);
|
|
18
|
+
if (!v) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
"[hook-template] useAppConfig: AppConfigProvider missing \u2014 wrap your app in <AppRoot>"
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return v;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/config/schema.ts
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
var SnakeKeyRE = /^[a-z0-9][a-z0-9_.-]{0,127}$/;
|
|
29
|
+
var AuthFlowSchema = z.object({
|
|
30
|
+
minPassword: z.number().int().min(6).max(64),
|
|
31
|
+
requiresEmailVerify: z.boolean(),
|
|
32
|
+
googleOAuth: z.boolean(),
|
|
33
|
+
postAuthLanding: z.string().startsWith("/"),
|
|
34
|
+
preAuthRoutes: z.array(z.string().startsWith("/"))
|
|
35
|
+
});
|
|
36
|
+
var PaywallNonFreeSchema = z.object({
|
|
37
|
+
mode: z.enum(["trial", "pay_first"]),
|
|
38
|
+
trialDays: z.number().int().nonnegative().optional(),
|
|
39
|
+
cycles: z.array(z.enum(["MONTHLY", "YEARLY"])).min(1),
|
|
40
|
+
prices: z.object({
|
|
41
|
+
monthlyCents: z.number().int().nonnegative(),
|
|
42
|
+
yearlyCents: z.number().int().nonnegative()
|
|
43
|
+
}),
|
|
44
|
+
anchorPrices: z.object({
|
|
45
|
+
monthlyCents: z.number().int().nonnegative(),
|
|
46
|
+
yearlyCents: z.number().int().nonnegative()
|
|
47
|
+
}).optional(),
|
|
48
|
+
checkoutMethods: z.array(z.enum(["card", "pix-auto", "pix-once"])).min(1),
|
|
49
|
+
requiresCpf: z.boolean(),
|
|
50
|
+
cancelWindowDays: z.number().int().nonnegative().optional(),
|
|
51
|
+
errorMessages: z.enum(["default", "custom"])
|
|
52
|
+
});
|
|
53
|
+
var PaywallFreeSchema = z.object({ mode: z.literal("free") });
|
|
54
|
+
var PaywallSchema = z.discriminatedUnion("mode", [PaywallNonFreeSchema, PaywallFreeSchema]);
|
|
55
|
+
var PersistedKeySchema = z.object({
|
|
56
|
+
key: z.string().regex(SnakeKeyRE, "key must be snake_case (matches /^[a-z0-9][a-z0-9_.-]{0,127}$/)"),
|
|
57
|
+
default: z.unknown(),
|
|
58
|
+
guardRegen: z.boolean().optional(),
|
|
59
|
+
debounceMs: z.number().int().positive().optional()
|
|
60
|
+
});
|
|
61
|
+
var OnboardingSchema = z.object({
|
|
62
|
+
trigger: z.enum(["pre_signup", "post_signup", "pre_signup_custom", "optional"]),
|
|
63
|
+
steps: z.array(
|
|
64
|
+
z.object({
|
|
65
|
+
id: z.string().regex(SnakeKeyRE),
|
|
66
|
+
screen: z.string(),
|
|
67
|
+
validates: z.array(z.string()).optional()
|
|
68
|
+
})
|
|
69
|
+
).min(1),
|
|
70
|
+
persistTo: z.literal("appData"),
|
|
71
|
+
persistKey: z.string().regex(SnakeKeyRE)
|
|
72
|
+
});
|
|
73
|
+
var DeepLinksSchema = z.object({
|
|
74
|
+
passwordReset: z.string().startsWith("/").optional(),
|
|
75
|
+
emailVerify: z.string().startsWith("/").optional()
|
|
76
|
+
}).strict();
|
|
77
|
+
var AppConfigSchema = z.object({
|
|
78
|
+
slug: z.string().regex(/^[a-z0-9-]+$/),
|
|
79
|
+
name: z.string().min(1),
|
|
80
|
+
branding: z.object({ primaryColor: z.string(), logoUrl: z.string().url() }),
|
|
81
|
+
authFlow: AuthFlowSchema,
|
|
82
|
+
paywall: PaywallSchema,
|
|
83
|
+
persistedKeys: z.array(PersistedKeySchema),
|
|
84
|
+
onboarding: OnboardingSchema.optional(),
|
|
85
|
+
deepLinks: DeepLinksSchema.optional(),
|
|
86
|
+
features_enabled: z.array(z.string()).optional()
|
|
87
|
+
}).strict();
|
|
88
|
+
function parseAppConfig(input) {
|
|
89
|
+
const r = AppConfigSchema.safeParse(input);
|
|
90
|
+
if (!r.success) {
|
|
91
|
+
const messages = r.error.issues.map((i) => `[${i.path.join(".")}] ${i.message}`).join("\n");
|
|
92
|
+
throw new Error(`Invalid app.config.json:
|
|
93
|
+
${messages}`);
|
|
94
|
+
}
|
|
95
|
+
return r.data;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/PersistenceRegistry.tsx
|
|
99
|
+
import { useEffect } from "react";
|
|
100
|
+
import { useHook } from "@hook-sdk/sdk";
|
|
101
|
+
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
102
|
+
function PersistenceRegistry({ config, children }) {
|
|
103
|
+
const { appData } = useHook();
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (config.length === 0) return;
|
|
106
|
+
const keys = config.map((c) => c.key);
|
|
107
|
+
const bulk = appData.bulkRead;
|
|
108
|
+
bulk?.(keys).catch(() => {
|
|
109
|
+
});
|
|
110
|
+
}, [config, appData]);
|
|
111
|
+
return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/DeepLinkHandler.tsx
|
|
115
|
+
import { useEffect as useEffect2 } from "react";
|
|
116
|
+
import { useLocation, useNavigate } from "react-router-dom";
|
|
117
|
+
function DeepLinkHandler({ deepLinks }) {
|
|
118
|
+
const nav = useNavigate();
|
|
119
|
+
const loc = useLocation();
|
|
120
|
+
useEffect2(() => {
|
|
121
|
+
if (!deepLinks) return;
|
|
122
|
+
const params = new URLSearchParams(loc.search);
|
|
123
|
+
const token = params.get("token");
|
|
124
|
+
if (!token) return;
|
|
125
|
+
if (deepLinks.passwordReset && deepLinks.passwordReset.includes(":token") && loc.pathname === "/") {
|
|
126
|
+
nav(deepLinks.passwordReset.replace(":token", token));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (deepLinks.emailVerify && deepLinks.emailVerify.includes(":token") && loc.pathname === "/") {
|
|
130
|
+
nav(deepLinks.emailVerify.replace(":token", token));
|
|
131
|
+
}
|
|
132
|
+
}, [deepLinks, loc, nav]);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/internal/TemplateConfigContext.tsx
|
|
137
|
+
import { createContext as createContext2, useContext as useContext2, useMemo } from "react";
|
|
138
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
139
|
+
var TemplateConfigContext = createContext2(null);
|
|
9
140
|
function TemplateConfigProvider({
|
|
10
141
|
config,
|
|
11
142
|
children
|
|
@@ -14,10 +145,10 @@ function TemplateConfigProvider({
|
|
|
14
145
|
...config,
|
|
15
146
|
mode: config.subscription?.mode ?? "trial"
|
|
16
147
|
}), [config]);
|
|
17
|
-
return /* @__PURE__ */
|
|
148
|
+
return /* @__PURE__ */ jsx3(TemplateConfigContext.Provider, { value, children });
|
|
18
149
|
}
|
|
19
150
|
function useTemplateConfig() {
|
|
20
|
-
const ctx =
|
|
151
|
+
const ctx = useContext2(TemplateConfigContext);
|
|
21
152
|
if (ctx === null) {
|
|
22
153
|
throw new Error("useTemplateConfig must be used inside <TemplateConfigProvider>");
|
|
23
154
|
}
|
|
@@ -25,7 +156,7 @@ function useTemplateConfig() {
|
|
|
25
156
|
}
|
|
26
157
|
|
|
27
158
|
// src/internal/ThemeProvider.tsx
|
|
28
|
-
import { jsx as
|
|
159
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
29
160
|
function ThemeProvider({ children }) {
|
|
30
161
|
const config = useTemplateConfig();
|
|
31
162
|
const style = {
|
|
@@ -34,182 +165,308 @@ function ThemeProvider({ children }) {
|
|
|
34
165
|
"--hook-color-background": config.theme.background_color
|
|
35
166
|
}
|
|
36
167
|
};
|
|
37
|
-
return /* @__PURE__ */
|
|
168
|
+
return /* @__PURE__ */ jsx4("div", { style, children });
|
|
38
169
|
}
|
|
39
170
|
|
|
40
|
-
// src/
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
// src/hooks/useAuth.ts
|
|
44
|
-
import { useHook } from "@hook-sdk/sdk";
|
|
45
|
-
function useAuth() {
|
|
46
|
-
const { user, authStatus, auth } = useHook();
|
|
47
|
-
return {
|
|
48
|
-
user,
|
|
49
|
-
authStatus,
|
|
50
|
-
refresh: auth.refresh
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// src/defaults/LoadingState.tsx
|
|
55
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
56
|
-
function LoadingState({ message }) {
|
|
57
|
-
return /* @__PURE__ */ jsx3("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx3("span", { children: message ?? "Carregando..." }) });
|
|
58
|
-
}
|
|
171
|
+
// src/hooks/usePaywallState.ts
|
|
172
|
+
import { useCallback, useContext as useContext3, useMemo as useMemo2, useState } from "react";
|
|
173
|
+
import { useHook as useHook2 } from "@hook-sdk/sdk";
|
|
59
174
|
|
|
60
|
-
// src/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
const onPop = () => {
|
|
79
|
-
if (detectResetFromUrl()) setScreen("reset");
|
|
80
|
-
};
|
|
81
|
-
window.addEventListener("popstate", onPop);
|
|
82
|
-
return () => window.removeEventListener("popstate", onPop);
|
|
83
|
-
}, []);
|
|
84
|
-
if (authStatus === "loading") return /* @__PURE__ */ jsx4(LoadingState, {});
|
|
85
|
-
if (!user) {
|
|
86
|
-
if (screen === "reset") return /* @__PURE__ */ jsx4(Reset, { onNavigate: setScreen });
|
|
87
|
-
if (screen === "signup") return /* @__PURE__ */ jsx4(Signup, { onNavigate: setScreen });
|
|
88
|
-
if (screen === "forgot") return /* @__PURE__ */ jsx4(Forgot, { onNavigate: setScreen });
|
|
89
|
-
return /* @__PURE__ */ jsx4(Login, { onNavigate: setScreen });
|
|
90
|
-
}
|
|
91
|
-
return /* @__PURE__ */ jsx4(Fragment, { children });
|
|
175
|
+
// src/errors/asaas-pt-br.ts
|
|
176
|
+
var MAP = {
|
|
177
|
+
invalid_cpf: "CPF inv\xE1lido. Confira os n\xFAmeros e tente novamente.",
|
|
178
|
+
cpf_required: "Por favor, informe seu CPF para continuar.",
|
|
179
|
+
card_declined: "Cart\xE3o recusado pela operadora. Tente outro cart\xE3o ou m\xE9todo.",
|
|
180
|
+
insufficient_funds: "Saldo insuficiente no cart\xE3o. Tente outro m\xE9todo.",
|
|
181
|
+
card_expired: "Cart\xE3o expirado. Use um cart\xE3o v\xE1lido.",
|
|
182
|
+
invalid_card_number: "N\xFAmero de cart\xE3o inv\xE1lido.",
|
|
183
|
+
invalid_cvv: "C\xF3digo de seguran\xE7a (CVV) inv\xE1lido.",
|
|
184
|
+
invalid_expiration: "Data de validade inv\xE1lida.",
|
|
185
|
+
generic_decline: "Pagamento recusado. Tente novamente em instantes ou use outro m\xE9todo.",
|
|
186
|
+
webhook_unverified: "N\xE3o conseguimos confirmar seu pagamento. Atualize a p\xE1gina em alguns segundos.",
|
|
187
|
+
pix_expired: "QR Code do PIX expirou. Gere um novo.",
|
|
188
|
+
pix_not_paid_yet: "PIX ainda n\xE3o foi pago. Aguardando confirma\xE7\xE3o."
|
|
189
|
+
};
|
|
190
|
+
function asaasErrorMessage(code) {
|
|
191
|
+
return MAP[code] ?? "Ocorreu um erro inesperado. Tente novamente em instantes.";
|
|
92
192
|
}
|
|
93
193
|
|
|
94
194
|
// src/hooks/usePaywallState.ts
|
|
95
|
-
|
|
96
|
-
|
|
195
|
+
function isCheckoutFailure(r) {
|
|
196
|
+
return "ok" in r && r.ok === false;
|
|
197
|
+
}
|
|
198
|
+
var FALLBACK_PAYWALL = {
|
|
199
|
+
mode: "pay_first",
|
|
200
|
+
cycles: ["MONTHLY"],
|
|
201
|
+
prices: { monthlyCents: 0, yearlyCents: 0 },
|
|
202
|
+
checkoutMethods: ["card", "pix-auto"],
|
|
203
|
+
requiresCpf: true,
|
|
204
|
+
errorMessages: "default"
|
|
205
|
+
};
|
|
206
|
+
var isMethodAvailable = (availability, method) => availability[method] !== false;
|
|
97
207
|
function usePaywallState() {
|
|
98
208
|
const { subscription, plan } = useHook2();
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
const
|
|
209
|
+
const configFromCtx = useContext3(AppConfigContext);
|
|
210
|
+
const paywall = configFromCtx?.paywall ?? FALLBACK_PAYWALL;
|
|
211
|
+
const isFree = paywall.mode === "free";
|
|
212
|
+
const declaredMethods = useMemo2(
|
|
213
|
+
() => isFree ? [] : paywall.checkoutMethods,
|
|
214
|
+
[isFree, paywall]
|
|
215
|
+
);
|
|
216
|
+
const availability = subscription.methodAvailability ?? {
|
|
217
|
+
card: null,
|
|
218
|
+
"pix-auto": null,
|
|
219
|
+
"pix-once": null
|
|
220
|
+
};
|
|
221
|
+
const methods = useMemo2(
|
|
222
|
+
() => declaredMethods.filter((m) => isMethodAvailable(availability, m)),
|
|
223
|
+
[declaredMethods, availability]
|
|
224
|
+
);
|
|
225
|
+
const defaultMethod = methods[0] ?? declaredMethods[0] ?? "card";
|
|
226
|
+
const [selectedMethodRaw, setSelectedMethod] = useState(defaultMethod);
|
|
227
|
+
const selectedMethod = methods.includes(selectedMethodRaw) ? selectedMethodRaw : methods[0] ?? selectedMethodRaw;
|
|
228
|
+
const initialCycle = isFree ? "MONTHLY" : paywall.cycles[0] ?? "MONTHLY";
|
|
229
|
+
const [cycle, setCycle] = useState(initialCycle);
|
|
230
|
+
const cpfRequired = !isFree && paywall.requiresCpf;
|
|
231
|
+
const [cpf, setCpf] = useState("");
|
|
232
|
+
const cpfValid = useMemo2(() => /^[0-9]{11}$/.test(cpf), [cpf]);
|
|
233
|
+
const [card, setCardState] = useState({
|
|
234
|
+
number: "",
|
|
235
|
+
cvv: "",
|
|
236
|
+
expiry: "",
|
|
237
|
+
holder: ""
|
|
238
|
+
});
|
|
239
|
+
const setCard = useCallback((patch) => {
|
|
240
|
+
setCardState((prev) => ({ ...prev, ...patch }));
|
|
241
|
+
}, []);
|
|
242
|
+
const [error, setError] = useState(null);
|
|
243
|
+
const [submitting, setSubmitting] = useState(false);
|
|
102
244
|
const status = subscription.status();
|
|
103
245
|
const daysLeftInTrial = subscription.daysLeftInTrial();
|
|
104
246
|
const initialLoadComplete = subscription.initialLoadComplete;
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
247
|
+
const pixPending = useMemo2(() => {
|
|
248
|
+
const sdkPix = subscription.pixPending;
|
|
249
|
+
if (!sdkPix) return null;
|
|
250
|
+
const liveStatus = subscription.current?.status;
|
|
251
|
+
const paid = liveStatus === "ACTIVE" || liveStatus === "TRIAL";
|
|
252
|
+
return {
|
|
253
|
+
method: sdkPix.method,
|
|
254
|
+
qrCodePayload: sdkPix.qrCodePayload,
|
|
255
|
+
qrCodeBase64: sdkPix.qrCodeBase64,
|
|
256
|
+
expiresAt: null,
|
|
257
|
+
paid
|
|
258
|
+
};
|
|
259
|
+
}, [subscription.pixPending, subscription.current]);
|
|
111
260
|
const monthlyEquivalent = useCallback(
|
|
112
|
-
(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return
|
|
261
|
+
(c) => {
|
|
262
|
+
const monthlyCents = plan.data?.priceCents ?? (isFree ? 0 : paywall.prices.monthlyCents);
|
|
263
|
+
const yearlyCents = plan.data?.yearlyPriceCents ?? (isFree ? null : paywall.prices.yearlyCents);
|
|
264
|
+
if (c === "YEARLY" && yearlyCents) return Math.round(yearlyCents / 12);
|
|
265
|
+
return monthlyCents;
|
|
117
266
|
},
|
|
118
|
-
[
|
|
267
|
+
[plan, paywall, isFree]
|
|
119
268
|
);
|
|
269
|
+
const planDerived = useMemo2(() => {
|
|
270
|
+
if (isFree) return null;
|
|
271
|
+
const monthlyCents = paywall.prices.monthlyCents;
|
|
272
|
+
const yearlyCents = paywall.prices.yearlyCents;
|
|
273
|
+
const anchor = paywall.anchorPrices;
|
|
274
|
+
const discount = anchor && anchor.yearlyCents > 0 ? Math.round((1 - paywall.prices.yearlyCents / anchor.yearlyCents) * 100) : 0;
|
|
275
|
+
return {
|
|
276
|
+
monthlyCents,
|
|
277
|
+
yearlyCents,
|
|
278
|
+
anchorMonthlyCents: anchor?.monthlyCents,
|
|
279
|
+
anchorYearlyCents: anchor?.yearlyCents,
|
|
280
|
+
monthlyEquivalent: cycle === "YEARLY" ? Math.round(yearlyCents / 12) : monthlyCents,
|
|
281
|
+
discountPercent: discount
|
|
282
|
+
};
|
|
283
|
+
}, [paywall, cycle, isFree]);
|
|
284
|
+
const useDefaultMessages = paywall.mode !== "free" && paywall.errorMessages === "default";
|
|
285
|
+
const buildError = useCallback(
|
|
286
|
+
(code, fallbackMessage) => ({
|
|
287
|
+
code,
|
|
288
|
+
message: fallbackMessage,
|
|
289
|
+
userMessage: useDefaultMessages ? asaasErrorMessage(code) : fallbackMessage
|
|
290
|
+
}),
|
|
291
|
+
[useDefaultMessages]
|
|
292
|
+
);
|
|
293
|
+
const submit = useCallback(async () => {
|
|
294
|
+
setSubmitting(true);
|
|
295
|
+
setError(null);
|
|
296
|
+
const methodToUse = selectedMethod;
|
|
297
|
+
if (!isMethodAvailable(availability, methodToUse)) {
|
|
298
|
+
const code = "method_unavailable";
|
|
299
|
+
setError(buildError(code, `method ${methodToUse} unavailable`));
|
|
300
|
+
setSubmitting(false);
|
|
301
|
+
return {
|
|
302
|
+
ok: false,
|
|
303
|
+
code: "method_unavailable",
|
|
304
|
+
method: methodToUse,
|
|
305
|
+
reason: "pre_flight_unavailable"
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
let result;
|
|
310
|
+
if (methodToUse === "card") {
|
|
311
|
+
const [expMonthRaw = "", expYearRaw = ""] = card.expiry.split("/");
|
|
312
|
+
const expYearTrimmed = expYearRaw.trim();
|
|
313
|
+
const cardData = {
|
|
314
|
+
number: card.number,
|
|
315
|
+
holderName: card.holder,
|
|
316
|
+
expiryMonth: expMonthRaw.trim(),
|
|
317
|
+
expiryYear: expYearTrimmed.length === 2 ? `20${expYearTrimmed}` : expYearTrimmed,
|
|
318
|
+
ccv: card.cvv
|
|
319
|
+
};
|
|
320
|
+
const holderInfo = {
|
|
321
|
+
name: card.holder,
|
|
322
|
+
email: "",
|
|
323
|
+
cpfCnpj: cpf,
|
|
324
|
+
postalCode: "",
|
|
325
|
+
addressNumber: ""
|
|
326
|
+
};
|
|
327
|
+
result = await subscription.checkout({
|
|
328
|
+
method: "card",
|
|
329
|
+
cycle,
|
|
330
|
+
cpf,
|
|
331
|
+
card: cardData,
|
|
332
|
+
holderInfo
|
|
333
|
+
});
|
|
334
|
+
} else if (methodToUse === "pix-auto") {
|
|
335
|
+
result = await subscription.checkout({ method: "pix-auto", cycle, cpf });
|
|
336
|
+
} else {
|
|
337
|
+
result = await subscription.checkout({ method: "pix-once", cycle, cpf });
|
|
338
|
+
}
|
|
339
|
+
if (isCheckoutFailure(result)) {
|
|
340
|
+
setError(buildError(result.code, `${result.code}: ${result.reason}`));
|
|
341
|
+
setSubmitting(false);
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
await subscription.refresh();
|
|
345
|
+
setSubmitting(false);
|
|
346
|
+
return result;
|
|
347
|
+
} catch (err) {
|
|
348
|
+
const code = err?.code ?? "generic_decline";
|
|
349
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
350
|
+
setError(buildError(code, message));
|
|
351
|
+
setSubmitting(false);
|
|
352
|
+
return void 0;
|
|
353
|
+
}
|
|
354
|
+
}, [selectedMethod, availability, subscription, cycle, cpf, card, buildError]);
|
|
120
355
|
const checkout = useCallback(
|
|
121
356
|
async (args) => {
|
|
122
|
-
|
|
357
|
+
setSubmitting(true);
|
|
123
358
|
setError(null);
|
|
124
|
-
setPixPending(null);
|
|
125
359
|
try {
|
|
126
360
|
if (args.method === "card") {
|
|
127
361
|
if (!args.card || !args.holderInfo) {
|
|
128
|
-
throw
|
|
362
|
+
throw Object.assign(
|
|
363
|
+
new Error('card and holderInfo are required when method is "card"'),
|
|
364
|
+
{ code: "validation" }
|
|
365
|
+
);
|
|
129
366
|
}
|
|
130
|
-
|
|
367
|
+
const sdkArgs = {
|
|
131
368
|
method: "card",
|
|
132
369
|
cycle: args.cycle,
|
|
133
370
|
cpf: args.cpf,
|
|
134
371
|
card: args.card,
|
|
135
372
|
holderInfo: args.holderInfo,
|
|
136
373
|
...args.remoteIp ? { remoteIp: args.remoteIp } : {}
|
|
137
|
-
}
|
|
374
|
+
};
|
|
375
|
+
const result2 = await subscription.checkout(sdkArgs);
|
|
376
|
+
if (isCheckoutFailure(result2)) {
|
|
377
|
+
setError(buildError(result2.code, `${result2.code}: ${result2.reason}`));
|
|
378
|
+
setSubmitting(false);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
138
381
|
await subscription.refresh();
|
|
139
|
-
|
|
382
|
+
setSubmitting(false);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (args.method === "pix-auto") {
|
|
386
|
+
const result2 = await subscription.checkout({
|
|
387
|
+
method: "pix-auto",
|
|
388
|
+
cycle: args.cycle,
|
|
389
|
+
cpf: args.cpf
|
|
390
|
+
});
|
|
391
|
+
if (isCheckoutFailure(result2)) {
|
|
392
|
+
setError(buildError(result2.code, `${result2.code}: ${result2.reason}`));
|
|
393
|
+
setSubmitting(false);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
setSubmitting(false);
|
|
140
397
|
return;
|
|
141
398
|
}
|
|
142
399
|
const result = await subscription.checkout({
|
|
143
|
-
method: "pix-
|
|
400
|
+
method: "pix-once",
|
|
144
401
|
cycle: args.cycle,
|
|
145
402
|
cpf: args.cpf
|
|
146
403
|
});
|
|
147
|
-
if (result
|
|
148
|
-
|
|
404
|
+
if (isCheckoutFailure(result)) {
|
|
405
|
+
setError(buildError(result.code, `${result.code}: ${result.reason}`));
|
|
406
|
+
setSubmitting(false);
|
|
407
|
+
return;
|
|
149
408
|
}
|
|
150
|
-
|
|
151
|
-
method: "pix-auto",
|
|
152
|
-
qrCodePayload: result.qrCodePayload,
|
|
153
|
-
qrCodeBase64: result.qrCodeBase64,
|
|
154
|
-
expiresAt: null,
|
|
155
|
-
paid: false
|
|
156
|
-
});
|
|
157
|
-
setOpening(false);
|
|
409
|
+
setSubmitting(false);
|
|
158
410
|
} catch (err) {
|
|
159
|
-
|
|
160
|
-
|
|
411
|
+
const code = err?.code ?? "generic_decline";
|
|
412
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
413
|
+
setError(buildError(code, message));
|
|
414
|
+
setSubmitting(false);
|
|
161
415
|
}
|
|
162
416
|
},
|
|
163
|
-
[subscription]
|
|
417
|
+
[subscription, buildError]
|
|
164
418
|
);
|
|
165
419
|
const cancel = useCallback(async () => {
|
|
166
420
|
try {
|
|
167
421
|
await subscription.cancel();
|
|
168
422
|
await subscription.refresh();
|
|
169
423
|
} catch (err) {
|
|
170
|
-
|
|
424
|
+
const code = err?.code ?? "generic_decline";
|
|
425
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
426
|
+
setError(buildError(code, message));
|
|
171
427
|
}
|
|
172
|
-
}, [subscription]);
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const tick = async () => {
|
|
182
|
-
if (cancelled || attempts >= MAX_ATTEMPTS) return;
|
|
183
|
-
attempts++;
|
|
184
|
-
try {
|
|
185
|
-
await subRef.current.refresh();
|
|
186
|
-
if (cancelled) return;
|
|
187
|
-
const s = subRef.current.status();
|
|
188
|
-
if (s === "active" || s === "trialing") {
|
|
189
|
-
setPixPending((prev) => prev ? { ...prev, paid: true } : prev);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
} catch {
|
|
193
|
-
}
|
|
194
|
-
if (!cancelled) setTimeout(tick, 3e3);
|
|
195
|
-
};
|
|
196
|
-
setTimeout(tick, 3e3);
|
|
197
|
-
return () => {
|
|
198
|
-
cancelled = true;
|
|
199
|
-
};
|
|
200
|
-
}, [pixPending]);
|
|
428
|
+
}, [subscription, buildError]);
|
|
429
|
+
const cardState = useMemo2(
|
|
430
|
+
() => ({ ...card, set: setCard }),
|
|
431
|
+
[card, setCard]
|
|
432
|
+
);
|
|
433
|
+
const cpfState = useMemo2(
|
|
434
|
+
() => ({ required: cpfRequired, value: cpf, set: setCpf, valid: cpfValid }),
|
|
435
|
+
[cpfRequired, cpf, cpfValid]
|
|
436
|
+
);
|
|
201
437
|
return {
|
|
438
|
+
// Subscription status (reactive, proxied from SDK)
|
|
202
439
|
status,
|
|
203
440
|
daysLeftInTrial,
|
|
204
441
|
initialLoadComplete,
|
|
442
|
+
// Plan derivation from config (sync, no fetch)
|
|
443
|
+
plan: planDerived,
|
|
444
|
+
// Cycle + method selection
|
|
445
|
+
cycle,
|
|
446
|
+
setCycle,
|
|
447
|
+
methods,
|
|
448
|
+
selectedMethod,
|
|
449
|
+
setSelectedMethod,
|
|
450
|
+
// Form state
|
|
451
|
+
cpfState,
|
|
452
|
+
cardState,
|
|
453
|
+
// High-level + legacy actions
|
|
454
|
+
submit,
|
|
205
455
|
checkout,
|
|
206
456
|
cancel,
|
|
207
|
-
|
|
208
|
-
error,
|
|
457
|
+
// PIX state (proxied from SDK; legacy `paid`/`expiresAt` derived)
|
|
209
458
|
pixPending,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
459
|
+
// Error + submit progress
|
|
460
|
+
error,
|
|
461
|
+
submitting,
|
|
462
|
+
// Backward-compat aliases (deprecated; remove in template 0.11)
|
|
463
|
+
opening: submitting,
|
|
464
|
+
availableMethods: methods,
|
|
465
|
+
monthlyEquivalent,
|
|
466
|
+
dismissPix: () => {
|
|
467
|
+
},
|
|
468
|
+
refreshPlan: () => {
|
|
469
|
+
}
|
|
213
470
|
};
|
|
214
471
|
}
|
|
215
472
|
|
|
@@ -230,51 +487,11 @@ function SubscriptionGate({ Paywall, children }) {
|
|
|
230
487
|
return /* @__PURE__ */ jsx5(Fragment2, { children });
|
|
231
488
|
}
|
|
232
489
|
|
|
233
|
-
// src/internal/PersistedKeysPrefetch.tsx
|
|
234
|
-
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
235
|
-
import { useHook as useHook3 } from "@hook-sdk/sdk";
|
|
236
|
-
import { Fragment as Fragment3, jsx as jsx6 } from "react/jsx-runtime";
|
|
237
|
-
var SAFETY_TIMEOUT_MS = 3e3;
|
|
238
|
-
function PersistedKeysPrefetch({ children }) {
|
|
239
|
-
const { appData } = useHook3();
|
|
240
|
-
const config = useTemplateConfig();
|
|
241
|
-
const hasKeys = !!config.persistedKeys && config.persistedKeys.length > 0;
|
|
242
|
-
const [ready, setReady] = useState3(!hasKeys);
|
|
243
|
-
useEffect3(() => {
|
|
244
|
-
const keys = config.persistedKeys;
|
|
245
|
-
if (!keys || keys.length === 0) {
|
|
246
|
-
setReady(true);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
let cancelled = false;
|
|
250
|
-
appData.cache.startPrefetch(keys, (ks) => appData.bulkRead(ks));
|
|
251
|
-
void appData.cache.waitForPrimed(SAFETY_TIMEOUT_MS).then((result) => {
|
|
252
|
-
if (cancelled) return;
|
|
253
|
-
if (result === "timeout") {
|
|
254
|
-
console.warn(
|
|
255
|
-
`[@hook-sdk/template] PersistedKeysPrefetch: bulk-read n\xE3o completou em ${SAFETY_TIMEOUT_MS}ms. Liberando render mesmo assim \u2014 usePersistedState pode expor defaultValue at\xE9 o fetch terminar (G77).`
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
setReady(true);
|
|
259
|
-
});
|
|
260
|
-
return () => {
|
|
261
|
-
cancelled = true;
|
|
262
|
-
};
|
|
263
|
-
}, [appData, config.persistedKeys]);
|
|
264
|
-
if (!ready) return null;
|
|
265
|
-
return /* @__PURE__ */ jsx6(Fragment3, { children });
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// src/internal/PushPrompt.tsx
|
|
269
|
-
function PushPrompt() {
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
490
|
// src/components/InstallGate/InstallGate.tsx
|
|
274
|
-
import { useEffect as
|
|
491
|
+
import { useEffect as useEffect4, useRef } from "react";
|
|
275
492
|
|
|
276
493
|
// src/hooks/useInstallPrompt.ts
|
|
277
|
-
import { useCallback as useCallback2, useEffect as
|
|
494
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useState as useState2 } from "react";
|
|
278
495
|
var IOS_RE = /iPad|iPhone|iPod/;
|
|
279
496
|
var IOS_NON_SAFARI_RE = /CriOS|FxiOS|EdgiOS/;
|
|
280
497
|
var ANDROID_RE = /Android/;
|
|
@@ -407,18 +624,18 @@ function useInstallPrompt(slug) {
|
|
|
407
624
|
const iosBrowser = detectIOSBrowser(ua);
|
|
408
625
|
const androidBrowser = detectAndroidBrowser(ua);
|
|
409
626
|
const inAppApp = detectInAppApp(ua);
|
|
410
|
-
const [isInstallable, setIsInstallable] =
|
|
627
|
+
const [isInstallable, setIsInstallable] = useState2(() => {
|
|
411
628
|
if (typeof window === "undefined") return false;
|
|
412
629
|
return window.__pwaInstallPrompt != null;
|
|
413
630
|
});
|
|
414
|
-
const [isInstalled, setIsInstalled] =
|
|
631
|
+
const [isInstalled, setIsInstalled] = useState2(() => {
|
|
415
632
|
const { installed } = detectStandalone();
|
|
416
633
|
return installed || readInstalledMarker(slug);
|
|
417
634
|
});
|
|
418
|
-
const [isDismissedSession, setIsDismissedSession] =
|
|
419
|
-
const [isDismissedPermanent, setIsDismissedPermanent] =
|
|
420
|
-
const [skipCount, setSkipCount] =
|
|
421
|
-
|
|
635
|
+
const [isDismissedSession, setIsDismissedSession] = useState2(() => readSessionSkip(slug));
|
|
636
|
+
const [isDismissedPermanent, setIsDismissedPermanent] = useState2(() => readPermanentDismiss(slug).dismissed);
|
|
637
|
+
const [skipCount, setSkipCount] = useState2(() => readSkipCount(slug));
|
|
638
|
+
useEffect3(() => {
|
|
422
639
|
if (typeof window === "undefined") return;
|
|
423
640
|
if (window.__pwaInstallPrompt) {
|
|
424
641
|
setIsInstallable(true);
|
|
@@ -442,7 +659,7 @@ function useInstallPrompt(slug) {
|
|
|
442
659
|
window.removeEventListener("appinstalled", onInstalled);
|
|
443
660
|
};
|
|
444
661
|
}, [slug]);
|
|
445
|
-
|
|
662
|
+
useEffect3(() => {
|
|
446
663
|
if (typeof window === "undefined") return;
|
|
447
664
|
const mq = window.matchMedia?.("(display-mode: standalone)");
|
|
448
665
|
if (!mq) return;
|
|
@@ -697,11 +914,11 @@ var INSTALL_COPY = {
|
|
|
697
914
|
};
|
|
698
915
|
|
|
699
916
|
// src/components/InstallGate/InstallSplash.tsx
|
|
700
|
-
import { jsx as
|
|
917
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
701
918
|
function InstallSplash({ children, title, subtitle }) {
|
|
702
919
|
const { name, theme } = useTemplateConfig();
|
|
703
920
|
const iconUrl = theme.icon_url || theme.logo_url || null;
|
|
704
|
-
return /* @__PURE__ */
|
|
921
|
+
return /* @__PURE__ */ jsx6(
|
|
705
922
|
"div",
|
|
706
923
|
{
|
|
707
924
|
role: "dialog",
|
|
@@ -710,14 +927,14 @@ function InstallSplash({ children, title, subtitle }) {
|
|
|
710
927
|
"aria-describedby": subtitle ? "install-splash-subtitle" : void 0,
|
|
711
928
|
style: overlayStyle,
|
|
712
929
|
children: /* @__PURE__ */ jsxs("div", { style: cardStyle, children: [
|
|
713
|
-
/* @__PURE__ */
|
|
930
|
+
/* @__PURE__ */ jsx6("div", { style: { display: "flex", justifyContent: "center", marginBottom: 16 }, children: iconUrl ? /* @__PURE__ */ jsx6(
|
|
714
931
|
"img",
|
|
715
932
|
{
|
|
716
933
|
src: iconUrl,
|
|
717
934
|
alt: `\xCDcone de ${name}`,
|
|
718
935
|
style: { width: 80, height: 80, borderRadius: 20, objectFit: "cover" }
|
|
719
936
|
}
|
|
720
|
-
) : /* @__PURE__ */
|
|
937
|
+
) : /* @__PURE__ */ jsx6(
|
|
721
938
|
"div",
|
|
722
939
|
{
|
|
723
940
|
style: {
|
|
@@ -735,10 +952,10 @@ function InstallSplash({ children, title, subtitle }) {
|
|
|
735
952
|
children: name.charAt(0).toUpperCase()
|
|
736
953
|
}
|
|
737
954
|
) }),
|
|
738
|
-
/* @__PURE__ */
|
|
739
|
-
subtitle && /* @__PURE__ */
|
|
740
|
-
/* @__PURE__ */
|
|
741
|
-
/* @__PURE__ */
|
|
955
|
+
/* @__PURE__ */ jsx6("h1", { id: "install-splash-title", style: titleStyle, children: title }),
|
|
956
|
+
subtitle && /* @__PURE__ */ jsx6("p", { id: "install-splash-subtitle", style: subtitleStyle, children: subtitle }),
|
|
957
|
+
/* @__PURE__ */ jsx6("div", { style: { marginTop: 24 }, children }),
|
|
958
|
+
/* @__PURE__ */ jsx6("p", { style: footerStyle, children: "por Hook" })
|
|
742
959
|
] })
|
|
743
960
|
}
|
|
744
961
|
);
|
|
@@ -812,7 +1029,7 @@ var footerStyle = {
|
|
|
812
1029
|
};
|
|
813
1030
|
|
|
814
1031
|
// src/components/InstallGate/icons.tsx
|
|
815
|
-
import { jsx as
|
|
1032
|
+
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
816
1033
|
var defaultSvgProps = (size) => ({
|
|
817
1034
|
width: size,
|
|
818
1035
|
height: size,
|
|
@@ -825,55 +1042,55 @@ var defaultSvgProps = (size) => ({
|
|
|
825
1042
|
});
|
|
826
1043
|
function ShareIconIOS({ size = 24, style, className }) {
|
|
827
1044
|
return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
|
|
828
|
-
/* @__PURE__ */
|
|
829
|
-
/* @__PURE__ */
|
|
830
|
-
/* @__PURE__ */
|
|
1045
|
+
/* @__PURE__ */ jsx7("path", { d: "M12 2L12 15" }),
|
|
1046
|
+
/* @__PURE__ */ jsx7("path", { d: "M8 6L12 2L16 6" }),
|
|
1047
|
+
/* @__PURE__ */ jsx7("path", { d: "M4 11v9a2 2 0 002 2h12a2 2 0 002-2v-9" })
|
|
831
1048
|
] });
|
|
832
1049
|
}
|
|
833
1050
|
function MenuDotsVerticalIcon({ size = 24, style, className }) {
|
|
834
1051
|
return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
|
|
835
|
-
/* @__PURE__ */
|
|
836
|
-
/* @__PURE__ */
|
|
837
|
-
/* @__PURE__ */
|
|
1052
|
+
/* @__PURE__ */ jsx7("circle", { cx: "12", cy: "5", r: "1.5" }),
|
|
1053
|
+
/* @__PURE__ */ jsx7("circle", { cx: "12", cy: "12", r: "1.5" }),
|
|
1054
|
+
/* @__PURE__ */ jsx7("circle", { cx: "12", cy: "19", r: "1.5" })
|
|
838
1055
|
] });
|
|
839
1056
|
}
|
|
840
1057
|
function MenuDotsHorizontalIcon({ size = 24, style, className }) {
|
|
841
1058
|
return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
|
|
842
|
-
/* @__PURE__ */
|
|
843
|
-
/* @__PURE__ */
|
|
844
|
-
/* @__PURE__ */
|
|
1059
|
+
/* @__PURE__ */ jsx7("circle", { cx: "5", cy: "12", r: "1.5" }),
|
|
1060
|
+
/* @__PURE__ */ jsx7("circle", { cx: "12", cy: "12", r: "1.5" }),
|
|
1061
|
+
/* @__PURE__ */ jsx7("circle", { cx: "19", cy: "12", r: "1.5" })
|
|
845
1062
|
] });
|
|
846
1063
|
}
|
|
847
1064
|
function SquarePlusIcon({ size = 24, style, className }) {
|
|
848
1065
|
return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
|
|
849
|
-
/* @__PURE__ */
|
|
850
|
-
/* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
1066
|
+
/* @__PURE__ */ jsx7("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
1067
|
+
/* @__PURE__ */ jsx7("path", { d: "M12 8v8" }),
|
|
1068
|
+
/* @__PURE__ */ jsx7("path", { d: "M8 12h8" })
|
|
852
1069
|
] });
|
|
853
1070
|
}
|
|
854
1071
|
function DownloadIcon({ size = 24, style, className }) {
|
|
855
1072
|
return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
|
|
856
|
-
/* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
858
|
-
/* @__PURE__ */
|
|
1073
|
+
/* @__PURE__ */ jsx7("path", { d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" }),
|
|
1074
|
+
/* @__PURE__ */ jsx7("polyline", { points: "7 10 12 15 17 10" }),
|
|
1075
|
+
/* @__PURE__ */ jsx7("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
|
|
859
1076
|
] });
|
|
860
1077
|
}
|
|
861
1078
|
function ExternalLinkIcon({ size = 24, style, className }) {
|
|
862
1079
|
return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
|
|
863
|
-
/* @__PURE__ */
|
|
864
|
-
/* @__PURE__ */
|
|
865
|
-
/* @__PURE__ */
|
|
1080
|
+
/* @__PURE__ */ jsx7("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }),
|
|
1081
|
+
/* @__PURE__ */ jsx7("polyline", { points: "15 3 21 3 21 9" }),
|
|
1082
|
+
/* @__PURE__ */ jsx7("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
|
|
866
1083
|
] });
|
|
867
1084
|
}
|
|
868
1085
|
function XIcon({ size = 20, style, className }) {
|
|
869
1086
|
return /* @__PURE__ */ jsxs2("svg", { ...defaultSvgProps(size), style, className, "aria-hidden": "true", children: [
|
|
870
|
-
/* @__PURE__ */
|
|
871
|
-
/* @__PURE__ */
|
|
1087
|
+
/* @__PURE__ */ jsx7("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1088
|
+
/* @__PURE__ */ jsx7("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
872
1089
|
] });
|
|
873
1090
|
}
|
|
874
1091
|
|
|
875
1092
|
// src/components/InstallGate/variants/AndroidNativeVariant.tsx
|
|
876
|
-
import { jsx as
|
|
1093
|
+
import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
877
1094
|
function AndroidNativeVariant({
|
|
878
1095
|
state,
|
|
879
1096
|
actions
|
|
@@ -889,12 +1106,12 @@ function AndroidNativeVariant({
|
|
|
889
1106
|
onClick: () => void actions.promptInstall(),
|
|
890
1107
|
style: { ...primaryButtonStyle, display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 8 },
|
|
891
1108
|
children: [
|
|
892
|
-
/* @__PURE__ */
|
|
1109
|
+
/* @__PURE__ */ jsx8(DownloadIcon, { size: 18 }),
|
|
893
1110
|
copy.cta
|
|
894
1111
|
]
|
|
895
1112
|
}
|
|
896
1113
|
),
|
|
897
|
-
/* @__PURE__ */
|
|
1114
|
+
/* @__PURE__ */ jsx8(
|
|
898
1115
|
"button",
|
|
899
1116
|
{
|
|
900
1117
|
"data-testid": "install-prompt-skip-session",
|
|
@@ -904,7 +1121,7 @@ function AndroidNativeVariant({
|
|
|
904
1121
|
children: copy.skip
|
|
905
1122
|
}
|
|
906
1123
|
),
|
|
907
|
-
showPermanent && /* @__PURE__ */
|
|
1124
|
+
showPermanent && /* @__PURE__ */ jsx8(
|
|
908
1125
|
"button",
|
|
909
1126
|
{
|
|
910
1127
|
"data-testid": "install-prompt-skip-permanent",
|
|
@@ -918,7 +1135,7 @@ function AndroidNativeVariant({
|
|
|
918
1135
|
}
|
|
919
1136
|
|
|
920
1137
|
// src/components/InstallGate/variants/AndroidManualVariant.tsx
|
|
921
|
-
import { jsx as
|
|
1138
|
+
import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
922
1139
|
function AndroidManualVariant({
|
|
923
1140
|
state,
|
|
924
1141
|
actions
|
|
@@ -926,9 +1143,9 @@ function AndroidManualVariant({
|
|
|
926
1143
|
const copy = INSTALL_COPY.android.manual;
|
|
927
1144
|
const showPermanent = shouldShowPermanentOption(state);
|
|
928
1145
|
return /* @__PURE__ */ jsxs4(InstallSplash, { title: copy.title, children: [
|
|
929
|
-
/* @__PURE__ */
|
|
930
|
-
/* @__PURE__ */
|
|
931
|
-
/* @__PURE__ */
|
|
1146
|
+
/* @__PURE__ */ jsx9(Step, { n: 1, icon: /* @__PURE__ */ jsx9(MenuDotsVerticalIcon, { size: 20 }), children: copy.step1 }),
|
|
1147
|
+
/* @__PURE__ */ jsx9(Step, { n: 2, icon: /* @__PURE__ */ jsx9(DownloadIcon, { size: 18 }), children: copy.step2 }),
|
|
1148
|
+
/* @__PURE__ */ jsx9(
|
|
932
1149
|
"button",
|
|
933
1150
|
{
|
|
934
1151
|
"data-testid": "install-prompt-cta-android-manual",
|
|
@@ -938,7 +1155,7 @@ function AndroidManualVariant({
|
|
|
938
1155
|
children: copy.cta
|
|
939
1156
|
}
|
|
940
1157
|
),
|
|
941
|
-
/* @__PURE__ */
|
|
1158
|
+
/* @__PURE__ */ jsx9(
|
|
942
1159
|
"button",
|
|
943
1160
|
{
|
|
944
1161
|
"data-testid": "install-prompt-skip-session",
|
|
@@ -948,7 +1165,7 @@ function AndroidManualVariant({
|
|
|
948
1165
|
children: copy.skip
|
|
949
1166
|
}
|
|
950
1167
|
),
|
|
951
|
-
showPermanent && /* @__PURE__ */
|
|
1168
|
+
showPermanent && /* @__PURE__ */ jsx9(
|
|
952
1169
|
"button",
|
|
953
1170
|
{
|
|
954
1171
|
"data-testid": "install-prompt-skip-permanent",
|
|
@@ -975,7 +1192,7 @@ function Step({ n, icon, children }) {
|
|
|
975
1192
|
textAlign: "left"
|
|
976
1193
|
},
|
|
977
1194
|
children: [
|
|
978
|
-
/* @__PURE__ */
|
|
1195
|
+
/* @__PURE__ */ jsx9(
|
|
979
1196
|
"div",
|
|
980
1197
|
{
|
|
981
1198
|
style: {
|
|
@@ -994,15 +1211,15 @@ function Step({ n, icon, children }) {
|
|
|
994
1211
|
children: n
|
|
995
1212
|
}
|
|
996
1213
|
),
|
|
997
|
-
/* @__PURE__ */
|
|
998
|
-
/* @__PURE__ */
|
|
1214
|
+
/* @__PURE__ */ jsx9("div", { style: { flex: 1, fontSize: 15, color: "#333" }, children }),
|
|
1215
|
+
/* @__PURE__ */ jsx9("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
|
|
999
1216
|
]
|
|
1000
1217
|
}
|
|
1001
1218
|
);
|
|
1002
1219
|
}
|
|
1003
1220
|
|
|
1004
1221
|
// src/components/InstallGate/Step.tsx
|
|
1005
|
-
import { jsx as
|
|
1222
|
+
import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1006
1223
|
function Step2({
|
|
1007
1224
|
n,
|
|
1008
1225
|
title,
|
|
@@ -1020,7 +1237,7 @@ function Step2({
|
|
|
1020
1237
|
textAlign: "left"
|
|
1021
1238
|
},
|
|
1022
1239
|
children: [
|
|
1023
|
-
/* @__PURE__ */
|
|
1240
|
+
/* @__PURE__ */ jsx10(
|
|
1024
1241
|
"div",
|
|
1025
1242
|
{
|
|
1026
1243
|
style: {
|
|
@@ -1040,8 +1257,8 @@ function Step2({
|
|
|
1040
1257
|
}
|
|
1041
1258
|
),
|
|
1042
1259
|
/* @__PURE__ */ jsxs5("div", { style: { flex: 1 }, children: [
|
|
1043
|
-
/* @__PURE__ */
|
|
1044
|
-
subtitle && /* @__PURE__ */
|
|
1260
|
+
/* @__PURE__ */ jsx10("p", { style: { margin: 0, fontSize: 15, fontWeight: 500, color: "#111", lineHeight: 1.3 }, children: title }),
|
|
1261
|
+
subtitle && /* @__PURE__ */ jsx10("p", { style: { margin: "4px 0 0 0", fontSize: 13, color: "#777" }, children: subtitle }),
|
|
1045
1262
|
visual
|
|
1046
1263
|
] })
|
|
1047
1264
|
]
|
|
@@ -1050,7 +1267,7 @@ function Step2({
|
|
|
1050
1267
|
}
|
|
1051
1268
|
|
|
1052
1269
|
// src/components/InstallGate/variants/IOSafariVariant.tsx
|
|
1053
|
-
import { jsx as
|
|
1270
|
+
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1054
1271
|
function IOSafariVariant({
|
|
1055
1272
|
state,
|
|
1056
1273
|
actions
|
|
@@ -1058,13 +1275,13 @@ function IOSafariVariant({
|
|
|
1058
1275
|
const copy = INSTALL_COPY.iosSafari;
|
|
1059
1276
|
const showPermanent = shouldShowPermanentOption(state);
|
|
1060
1277
|
return /* @__PURE__ */ jsxs6(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
|
|
1061
|
-
/* @__PURE__ */
|
|
1278
|
+
/* @__PURE__ */ jsx11(
|
|
1062
1279
|
Step2,
|
|
1063
1280
|
{
|
|
1064
1281
|
n: 1,
|
|
1065
1282
|
title: copy.step1.title,
|
|
1066
1283
|
subtitle: copy.step1.subtitle,
|
|
1067
|
-
visual: /* @__PURE__ */
|
|
1284
|
+
visual: /* @__PURE__ */ jsx11(
|
|
1068
1285
|
"div",
|
|
1069
1286
|
{
|
|
1070
1287
|
style: {
|
|
@@ -1076,12 +1293,12 @@ function IOSafariVariant({
|
|
|
1076
1293
|
padding: "12px 0",
|
|
1077
1294
|
marginTop: 8
|
|
1078
1295
|
},
|
|
1079
|
-
children: /* @__PURE__ */
|
|
1296
|
+
children: /* @__PURE__ */ jsx11(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
|
|
1080
1297
|
}
|
|
1081
1298
|
)
|
|
1082
1299
|
}
|
|
1083
1300
|
),
|
|
1084
|
-
/* @__PURE__ */
|
|
1301
|
+
/* @__PURE__ */ jsx11(
|
|
1085
1302
|
Step2,
|
|
1086
1303
|
{
|
|
1087
1304
|
n: 2,
|
|
@@ -1099,19 +1316,19 @@ function IOSafariVariant({
|
|
|
1099
1316
|
marginTop: 8
|
|
1100
1317
|
},
|
|
1101
1318
|
children: [
|
|
1102
|
-
/* @__PURE__ */
|
|
1103
|
-
/* @__PURE__ */
|
|
1319
|
+
/* @__PURE__ */ jsx11(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
|
|
1320
|
+
/* @__PURE__ */ jsx11("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
|
|
1104
1321
|
]
|
|
1105
1322
|
}
|
|
1106
1323
|
)
|
|
1107
1324
|
}
|
|
1108
1325
|
),
|
|
1109
|
-
/* @__PURE__ */
|
|
1326
|
+
/* @__PURE__ */ jsx11(
|
|
1110
1327
|
Step2,
|
|
1111
1328
|
{
|
|
1112
1329
|
n: 3,
|
|
1113
1330
|
title: copy.step3.title,
|
|
1114
|
-
visual: /* @__PURE__ */
|
|
1331
|
+
visual: /* @__PURE__ */ jsx11(
|
|
1115
1332
|
"div",
|
|
1116
1333
|
{
|
|
1117
1334
|
style: {
|
|
@@ -1122,7 +1339,7 @@ function IOSafariVariant({
|
|
|
1122
1339
|
padding: "10px 14px",
|
|
1123
1340
|
marginTop: 8
|
|
1124
1341
|
},
|
|
1125
|
-
children: /* @__PURE__ */
|
|
1342
|
+
children: /* @__PURE__ */ jsx11(
|
|
1126
1343
|
"span",
|
|
1127
1344
|
{
|
|
1128
1345
|
style: {
|
|
@@ -1137,7 +1354,7 @@ function IOSafariVariant({
|
|
|
1137
1354
|
)
|
|
1138
1355
|
}
|
|
1139
1356
|
),
|
|
1140
|
-
/* @__PURE__ */
|
|
1357
|
+
/* @__PURE__ */ jsx11(
|
|
1141
1358
|
"button",
|
|
1142
1359
|
{
|
|
1143
1360
|
"data-testid": "install-prompt-skip-session",
|
|
@@ -1147,7 +1364,7 @@ function IOSafariVariant({
|
|
|
1147
1364
|
children: copy.skip
|
|
1148
1365
|
}
|
|
1149
1366
|
),
|
|
1150
|
-
showPermanent && /* @__PURE__ */
|
|
1367
|
+
showPermanent && /* @__PURE__ */ jsx11(
|
|
1151
1368
|
"button",
|
|
1152
1369
|
{
|
|
1153
1370
|
"data-testid": "install-prompt-skip-permanent",
|
|
@@ -1161,7 +1378,7 @@ function IOSafariVariant({
|
|
|
1161
1378
|
}
|
|
1162
1379
|
|
|
1163
1380
|
// src/components/InstallGate/variants/IOSOtherVariant.tsx
|
|
1164
|
-
import { jsx as
|
|
1381
|
+
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1165
1382
|
function IOSOtherVariant({
|
|
1166
1383
|
state,
|
|
1167
1384
|
actions
|
|
@@ -1169,13 +1386,13 @@ function IOSOtherVariant({
|
|
|
1169
1386
|
const copy = INSTALL_COPY.iosOther;
|
|
1170
1387
|
const showPermanent = shouldShowPermanentOption(state);
|
|
1171
1388
|
return /* @__PURE__ */ jsxs7(InstallSplash, { title: copy.title, subtitle: copy.subtitle, children: [
|
|
1172
|
-
/* @__PURE__ */
|
|
1389
|
+
/* @__PURE__ */ jsx12(
|
|
1173
1390
|
Step2,
|
|
1174
1391
|
{
|
|
1175
1392
|
n: 1,
|
|
1176
1393
|
title: copy.step1.title,
|
|
1177
1394
|
subtitle: copy.step1.subtitle,
|
|
1178
|
-
visual: /* @__PURE__ */
|
|
1395
|
+
visual: /* @__PURE__ */ jsx12(
|
|
1179
1396
|
"div",
|
|
1180
1397
|
{
|
|
1181
1398
|
style: {
|
|
@@ -1187,12 +1404,12 @@ function IOSOtherVariant({
|
|
|
1187
1404
|
padding: "12px 0",
|
|
1188
1405
|
marginTop: 8
|
|
1189
1406
|
},
|
|
1190
|
-
children: /* @__PURE__ */
|
|
1407
|
+
children: /* @__PURE__ */ jsx12(ShareIconIOS, { size: 32, style: { color: "var(--hook-color-primary)" } })
|
|
1191
1408
|
}
|
|
1192
1409
|
)
|
|
1193
1410
|
}
|
|
1194
1411
|
),
|
|
1195
|
-
/* @__PURE__ */
|
|
1412
|
+
/* @__PURE__ */ jsx12(
|
|
1196
1413
|
Step2,
|
|
1197
1414
|
{
|
|
1198
1415
|
n: 2,
|
|
@@ -1210,19 +1427,19 @@ function IOSOtherVariant({
|
|
|
1210
1427
|
marginTop: 8
|
|
1211
1428
|
},
|
|
1212
1429
|
children: [
|
|
1213
|
-
/* @__PURE__ */
|
|
1214
|
-
/* @__PURE__ */
|
|
1430
|
+
/* @__PURE__ */ jsx12(SquarePlusIcon, { size: 22, style: { color: "#555" } }),
|
|
1431
|
+
/* @__PURE__ */ jsx12("span", { style: { fontSize: 14, color: "#333" }, children: copy.step2.iconLabel })
|
|
1215
1432
|
]
|
|
1216
1433
|
}
|
|
1217
1434
|
)
|
|
1218
1435
|
}
|
|
1219
1436
|
),
|
|
1220
|
-
/* @__PURE__ */
|
|
1437
|
+
/* @__PURE__ */ jsx12(
|
|
1221
1438
|
Step2,
|
|
1222
1439
|
{
|
|
1223
1440
|
n: 3,
|
|
1224
1441
|
title: copy.step3.title,
|
|
1225
|
-
visual: /* @__PURE__ */
|
|
1442
|
+
visual: /* @__PURE__ */ jsx12(
|
|
1226
1443
|
"div",
|
|
1227
1444
|
{
|
|
1228
1445
|
style: {
|
|
@@ -1233,7 +1450,7 @@ function IOSOtherVariant({
|
|
|
1233
1450
|
padding: "10px 14px",
|
|
1234
1451
|
marginTop: 8
|
|
1235
1452
|
},
|
|
1236
|
-
children: /* @__PURE__ */
|
|
1453
|
+
children: /* @__PURE__ */ jsx12(
|
|
1237
1454
|
"span",
|
|
1238
1455
|
{
|
|
1239
1456
|
style: {
|
|
@@ -1248,7 +1465,7 @@ function IOSOtherVariant({
|
|
|
1248
1465
|
)
|
|
1249
1466
|
}
|
|
1250
1467
|
),
|
|
1251
|
-
/* @__PURE__ */
|
|
1468
|
+
/* @__PURE__ */ jsx12(
|
|
1252
1469
|
"button",
|
|
1253
1470
|
{
|
|
1254
1471
|
"data-testid": "install-prompt-skip-session",
|
|
@@ -1258,7 +1475,7 @@ function IOSOtherVariant({
|
|
|
1258
1475
|
children: copy.skip
|
|
1259
1476
|
}
|
|
1260
1477
|
),
|
|
1261
|
-
showPermanent && /* @__PURE__ */
|
|
1478
|
+
showPermanent && /* @__PURE__ */ jsx12(
|
|
1262
1479
|
"button",
|
|
1263
1480
|
{
|
|
1264
1481
|
"data-testid": "install-prompt-skip-permanent",
|
|
@@ -1272,8 +1489,8 @@ function IOSOtherVariant({
|
|
|
1272
1489
|
}
|
|
1273
1490
|
|
|
1274
1491
|
// src/components/InstallGate/variants/InAppBrowserVariant.tsx
|
|
1275
|
-
import { useState as
|
|
1276
|
-
import { jsx as
|
|
1492
|
+
import { useState as useState3 } from "react";
|
|
1493
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1277
1494
|
function InAppBrowserVariant({
|
|
1278
1495
|
state,
|
|
1279
1496
|
actions
|
|
@@ -1282,7 +1499,7 @@ function InAppBrowserVariant({
|
|
|
1282
1499
|
const appCopy = INSTALL_COPY.inApp[app] ?? INSTALL_COPY.inApp.other;
|
|
1283
1500
|
const copy = INSTALL_COPY.inApp;
|
|
1284
1501
|
const showPermanent = shouldShowPermanentOption(state);
|
|
1285
|
-
const [copied, setCopied] =
|
|
1502
|
+
const [copied, setCopied] = useState3(false);
|
|
1286
1503
|
const handleCopy = async () => {
|
|
1287
1504
|
await actions.copyLink();
|
|
1288
1505
|
setCopied(true);
|
|
@@ -1290,9 +1507,9 @@ function InAppBrowserVariant({
|
|
|
1290
1507
|
};
|
|
1291
1508
|
const DotsIcon = app === "facebook" || app === "telegram" ? MenuDotsVerticalIcon : MenuDotsHorizontalIcon;
|
|
1292
1509
|
return /* @__PURE__ */ jsxs8(InstallSplash, { title: appCopy.title, children: [
|
|
1293
|
-
/* @__PURE__ */
|
|
1294
|
-
/* @__PURE__ */
|
|
1295
|
-
/* @__PURE__ */
|
|
1510
|
+
/* @__PURE__ */ jsx13(Step3, { n: 1, icon: /* @__PURE__ */ jsx13(DotsIcon, { size: 20 }), children: appCopy.step1 }),
|
|
1511
|
+
/* @__PURE__ */ jsx13(Step3, { n: 2, icon: /* @__PURE__ */ jsx13(ExternalLinkIcon, { size: 18 }), children: appCopy.step2 }),
|
|
1512
|
+
/* @__PURE__ */ jsx13(
|
|
1296
1513
|
"button",
|
|
1297
1514
|
{
|
|
1298
1515
|
"data-testid": "install-prompt-cta-inapp-copy",
|
|
@@ -1302,7 +1519,7 @@ function InAppBrowserVariant({
|
|
|
1302
1519
|
children: copied ? copy.copiedToast : copy.copy
|
|
1303
1520
|
}
|
|
1304
1521
|
),
|
|
1305
|
-
/* @__PURE__ */
|
|
1522
|
+
/* @__PURE__ */ jsx13(
|
|
1306
1523
|
"button",
|
|
1307
1524
|
{
|
|
1308
1525
|
"data-testid": "install-prompt-skip-session",
|
|
@@ -1312,7 +1529,7 @@ function InAppBrowserVariant({
|
|
|
1312
1529
|
children: copy.skip
|
|
1313
1530
|
}
|
|
1314
1531
|
),
|
|
1315
|
-
showPermanent && /* @__PURE__ */
|
|
1532
|
+
showPermanent && /* @__PURE__ */ jsx13(
|
|
1316
1533
|
"button",
|
|
1317
1534
|
{
|
|
1318
1535
|
"data-testid": "install-prompt-skip-permanent",
|
|
@@ -1343,7 +1560,7 @@ function Step3({
|
|
|
1343
1560
|
textAlign: "left"
|
|
1344
1561
|
},
|
|
1345
1562
|
children: [
|
|
1346
|
-
/* @__PURE__ */
|
|
1563
|
+
/* @__PURE__ */ jsx13(
|
|
1347
1564
|
"div",
|
|
1348
1565
|
{
|
|
1349
1566
|
style: {
|
|
@@ -1362,15 +1579,15 @@ function Step3({
|
|
|
1362
1579
|
children: n
|
|
1363
1580
|
}
|
|
1364
1581
|
),
|
|
1365
|
-
/* @__PURE__ */
|
|
1366
|
-
/* @__PURE__ */
|
|
1582
|
+
/* @__PURE__ */ jsx13("div", { style: { flex: 1, fontSize: 14, color: "#333" }, children }),
|
|
1583
|
+
/* @__PURE__ */ jsx13("div", { style: { color: "#888", flexShrink: 0 }, children: icon })
|
|
1367
1584
|
]
|
|
1368
1585
|
}
|
|
1369
1586
|
);
|
|
1370
1587
|
}
|
|
1371
1588
|
|
|
1372
1589
|
// src/components/InstallGate/variants/DesktopVariant.tsx
|
|
1373
|
-
import { jsx as
|
|
1590
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1374
1591
|
function DesktopVariant({
|
|
1375
1592
|
state,
|
|
1376
1593
|
actions
|
|
@@ -1386,14 +1603,14 @@ function DesktopVariant({
|
|
|
1386
1603
|
"aria-label": copy.title,
|
|
1387
1604
|
style: bannerStyle,
|
|
1388
1605
|
children: [
|
|
1389
|
-
iconUrl ? /* @__PURE__ */
|
|
1606
|
+
iconUrl ? /* @__PURE__ */ jsx14(
|
|
1390
1607
|
"img",
|
|
1391
1608
|
{
|
|
1392
1609
|
src: iconUrl,
|
|
1393
1610
|
alt: "",
|
|
1394
1611
|
style: { width: 40, height: 40, borderRadius: 10, objectFit: "cover", flexShrink: 0 }
|
|
1395
1612
|
}
|
|
1396
|
-
) : /* @__PURE__ */
|
|
1613
|
+
) : /* @__PURE__ */ jsx14(
|
|
1397
1614
|
"div",
|
|
1398
1615
|
{
|
|
1399
1616
|
style: {
|
|
@@ -1413,8 +1630,8 @@ function DesktopVariant({
|
|
|
1413
1630
|
}
|
|
1414
1631
|
),
|
|
1415
1632
|
/* @__PURE__ */ jsxs9("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1416
|
-
/* @__PURE__ */
|
|
1417
|
-
/* @__PURE__ */
|
|
1633
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 14, fontWeight: 600, color: "#111" }, children: copy.title }),
|
|
1634
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 12, color: "#666" }, children: copy.subtitle })
|
|
1418
1635
|
] }),
|
|
1419
1636
|
/* @__PURE__ */ jsxs9(
|
|
1420
1637
|
"button",
|
|
@@ -1437,12 +1654,12 @@ function DesktopVariant({
|
|
|
1437
1654
|
flexShrink: 0
|
|
1438
1655
|
},
|
|
1439
1656
|
children: [
|
|
1440
|
-
/* @__PURE__ */
|
|
1657
|
+
/* @__PURE__ */ jsx14(DownloadIcon, { size: 14 }),
|
|
1441
1658
|
copy.cta
|
|
1442
1659
|
]
|
|
1443
1660
|
}
|
|
1444
1661
|
),
|
|
1445
|
-
/* @__PURE__ */
|
|
1662
|
+
/* @__PURE__ */ jsx14(
|
|
1446
1663
|
"button",
|
|
1447
1664
|
{
|
|
1448
1665
|
"data-testid": "install-prompt-desktop-close",
|
|
@@ -1457,7 +1674,7 @@ function DesktopVariant({
|
|
|
1457
1674
|
padding: 4,
|
|
1458
1675
|
flexShrink: 0
|
|
1459
1676
|
},
|
|
1460
|
-
children: /* @__PURE__ */
|
|
1677
|
+
children: /* @__PURE__ */ jsx14(XIcon, { size: 16 })
|
|
1461
1678
|
}
|
|
1462
1679
|
)
|
|
1463
1680
|
]
|
|
@@ -1481,14 +1698,14 @@ var bannerStyle = {
|
|
|
1481
1698
|
};
|
|
1482
1699
|
|
|
1483
1700
|
// src/components/InstallGate/InstallGate.tsx
|
|
1484
|
-
import { Fragment as
|
|
1701
|
+
import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1485
1702
|
function InstallGate({ children }) {
|
|
1486
1703
|
const { slug, features_enabled } = useTemplateConfig();
|
|
1487
1704
|
const enabled = features_enabled.includes("install_prompt");
|
|
1488
1705
|
const installState = useInstallPrompt(slug);
|
|
1489
1706
|
const shouldBlock = enabled && shouldBlockInstall(installState);
|
|
1490
|
-
const trackedRef =
|
|
1491
|
-
|
|
1707
|
+
const trackedRef = useRef(null);
|
|
1708
|
+
useEffect4(() => {
|
|
1492
1709
|
if (!shouldBlock) return;
|
|
1493
1710
|
if (typeof window === "undefined") return;
|
|
1494
1711
|
const variantKey = `${slug}:${installState.variant}`;
|
|
@@ -1502,36 +1719,41 @@ function InstallGate({ children }) {
|
|
|
1502
1719
|
variant: installState.variant
|
|
1503
1720
|
});
|
|
1504
1721
|
}, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp]);
|
|
1505
|
-
if (!enabled) return /* @__PURE__ */
|
|
1506
|
-
if (installState.isInstalled) return /* @__PURE__ */
|
|
1722
|
+
if (!enabled) return /* @__PURE__ */ jsx15(Fragment3, { children });
|
|
1723
|
+
if (installState.isInstalled) return /* @__PURE__ */ jsx15(Fragment3, { children });
|
|
1507
1724
|
if (installState.variant === "desktop") {
|
|
1508
1725
|
const showBanner = !installState.isDismissedSession && !installState.isDismissedPermanent;
|
|
1509
|
-
return /* @__PURE__ */ jsxs10(
|
|
1726
|
+
return /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
1510
1727
|
children,
|
|
1511
|
-
showBanner && /* @__PURE__ */
|
|
1728
|
+
showBanner && /* @__PURE__ */ jsx15(DesktopVariant, { state: installState, actions: installState })
|
|
1512
1729
|
] });
|
|
1513
1730
|
}
|
|
1514
|
-
if (!shouldBlock) return /* @__PURE__ */
|
|
1731
|
+
if (!shouldBlock) return /* @__PURE__ */ jsx15(Fragment3, { children });
|
|
1515
1732
|
switch (installState.variant) {
|
|
1516
1733
|
case "android-native":
|
|
1517
|
-
return /* @__PURE__ */
|
|
1734
|
+
return /* @__PURE__ */ jsx15(AndroidNativeVariant, { state: installState, actions: installState });
|
|
1518
1735
|
case "android-manual":
|
|
1519
|
-
return /* @__PURE__ */
|
|
1736
|
+
return /* @__PURE__ */ jsx15(AndroidManualVariant, { state: installState, actions: installState });
|
|
1520
1737
|
case "ios-safari":
|
|
1521
|
-
return /* @__PURE__ */
|
|
1738
|
+
return /* @__PURE__ */ jsx15(IOSafariVariant, { state: installState, actions: installState });
|
|
1522
1739
|
case "ios-other":
|
|
1523
|
-
return /* @__PURE__ */
|
|
1740
|
+
return /* @__PURE__ */ jsx15(IOSOtherVariant, { state: installState, actions: installState });
|
|
1524
1741
|
case "in-app":
|
|
1525
|
-
return /* @__PURE__ */
|
|
1742
|
+
return /* @__PURE__ */ jsx15(InAppBrowserVariant, { state: installState, actions: installState });
|
|
1526
1743
|
case "none":
|
|
1527
1744
|
default:
|
|
1528
|
-
return /* @__PURE__ */
|
|
1745
|
+
return /* @__PURE__ */ jsx15(Fragment3, { children });
|
|
1529
1746
|
}
|
|
1530
1747
|
}
|
|
1531
1748
|
|
|
1749
|
+
// src/internal/PushPrompt.tsx
|
|
1750
|
+
function PushPrompt() {
|
|
1751
|
+
return null;
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1532
1754
|
// src/defaults/ErrorBoundary.tsx
|
|
1533
1755
|
import { Component } from "react";
|
|
1534
|
-
import { Fragment as
|
|
1756
|
+
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1535
1757
|
var ErrorBoundary = class extends Component {
|
|
1536
1758
|
state = { error: null };
|
|
1537
1759
|
static getDerivedStateFromError(error) {
|
|
@@ -1543,68 +1765,396 @@ var ErrorBoundary = class extends Component {
|
|
|
1543
1765
|
render() {
|
|
1544
1766
|
if (this.state.error) {
|
|
1545
1767
|
return this.props.fallback ?? /* @__PURE__ */ jsxs11("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
|
|
1546
|
-
/* @__PURE__ */
|
|
1547
|
-
/* @__PURE__ */
|
|
1768
|
+
/* @__PURE__ */ jsx16("h2", { children: "Algo deu errado" }),
|
|
1769
|
+
/* @__PURE__ */ jsx16("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
|
|
1548
1770
|
] });
|
|
1549
1771
|
}
|
|
1550
|
-
return /* @__PURE__ */
|
|
1772
|
+
return /* @__PURE__ */ jsx16(Fragment4, { children: this.props.children });
|
|
1551
1773
|
}
|
|
1552
1774
|
};
|
|
1553
1775
|
|
|
1554
|
-
// src/
|
|
1555
|
-
import { useCallback as useCallback3,
|
|
1556
|
-
import { useHook as
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1776
|
+
// src/internal/PaymentReturnHandler.tsx
|
|
1777
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useRef as useRef2, useState as useState4 } from "react";
|
|
1778
|
+
import { useHook as useHook3 } from "@hook-sdk/sdk";
|
|
1779
|
+
import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1780
|
+
var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
|
|
1781
|
+
function PaymentReturnHandler({ children }) {
|
|
1782
|
+
const { subscription } = useHook3();
|
|
1783
|
+
const subRef = useRef2(subscription);
|
|
1784
|
+
subRef.current = subscription;
|
|
1785
|
+
const runIdRef = useRef2(0);
|
|
1786
|
+
const [state, setState] = useState4("idle");
|
|
1787
|
+
const runPoll = useCallback3(() => {
|
|
1788
|
+
const runId = ++runIdRef.current;
|
|
1789
|
+
setState("confirming");
|
|
1790
|
+
let attempts = 0;
|
|
1791
|
+
const tick = async () => {
|
|
1792
|
+
if (runIdRef.current !== runId) return;
|
|
1793
|
+
attempts++;
|
|
1794
|
+
try {
|
|
1795
|
+
await subRef.current.refresh();
|
|
1796
|
+
} catch {
|
|
1797
|
+
}
|
|
1798
|
+
if (runIdRef.current !== runId) return;
|
|
1799
|
+
const status = subRef.current.status();
|
|
1800
|
+
if (status === "active" || status === "trialing") {
|
|
1801
|
+
const cleanUrl = new URL(window.location.href);
|
|
1802
|
+
cleanUrl.searchParams.delete("paymentReturn");
|
|
1803
|
+
window.history.replaceState({}, "", cleanUrl.toString());
|
|
1804
|
+
setState("idle");
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
const delay = BACKOFF_MS[attempts - 1];
|
|
1808
|
+
if (delay === void 0) {
|
|
1809
|
+
setState("waiting");
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
setTimeout(tick, delay);
|
|
1566
1813
|
};
|
|
1814
|
+
void tick();
|
|
1815
|
+
}, []);
|
|
1816
|
+
useEffect5(() => {
|
|
1817
|
+
if (typeof window === "undefined") return;
|
|
1818
|
+
const url = new URL(window.location.href);
|
|
1819
|
+
if (url.searchParams.get("paymentReturn") !== "1") return;
|
|
1820
|
+
runPoll();
|
|
1821
|
+
return () => {
|
|
1822
|
+
runIdRef.current++;
|
|
1823
|
+
};
|
|
1824
|
+
}, [runPoll]);
|
|
1825
|
+
if (state === "confirming") {
|
|
1826
|
+
return /* @__PURE__ */ jsx17("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
|
|
1567
1827
|
}
|
|
1568
|
-
if (
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
}
|
|
1573
|
-
if (detail === "account_locked") {
|
|
1574
|
-
return { code: "account_locked", message: "Conta bloqueada. Contate o suporte." };
|
|
1575
|
-
}
|
|
1576
|
-
return { code: "invalid_credentials", message: "E-mail ou senha inv\xE1lidos." };
|
|
1577
|
-
}
|
|
1578
|
-
if (err instanceof SdkError && err.httpStatus === 0) {
|
|
1579
|
-
return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
|
|
1580
|
-
}
|
|
1581
|
-
if (err instanceof TypeError) {
|
|
1582
|
-
return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
|
|
1828
|
+
if (state === "waiting") {
|
|
1829
|
+
return /* @__PURE__ */ jsx17("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs12("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
1830
|
+
/* @__PURE__ */ jsx17("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
|
|
1831
|
+
/* @__PURE__ */ jsx17("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
|
|
1832
|
+
] }) });
|
|
1583
1833
|
}
|
|
1584
|
-
return
|
|
1834
|
+
return /* @__PURE__ */ jsx17(Fragment5, { children });
|
|
1585
1835
|
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1836
|
+
var overlayStyle2 = {
|
|
1837
|
+
position: "fixed",
|
|
1838
|
+
inset: 0,
|
|
1839
|
+
display: "flex",
|
|
1840
|
+
alignItems: "center",
|
|
1841
|
+
justifyContent: "center",
|
|
1842
|
+
background: "rgba(0,0,0,0.4)",
|
|
1843
|
+
zIndex: 9999,
|
|
1844
|
+
color: "white",
|
|
1845
|
+
fontSize: "1rem",
|
|
1846
|
+
padding: 24
|
|
1847
|
+
};
|
|
1848
|
+
var buttonStyle = {
|
|
1849
|
+
background: "white",
|
|
1850
|
+
color: "black",
|
|
1851
|
+
border: "none",
|
|
1852
|
+
borderRadius: 8,
|
|
1853
|
+
padding: "10px 24px",
|
|
1854
|
+
fontSize: "1rem",
|
|
1855
|
+
fontWeight: 600,
|
|
1856
|
+
cursor: "pointer"
|
|
1857
|
+
};
|
|
1858
|
+
|
|
1859
|
+
// src/AppRoot.tsx
|
|
1860
|
+
import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1861
|
+
function buildLegacyConfigShim(config) {
|
|
1862
|
+
const paywall = config.paywall;
|
|
1863
|
+
const isFree = paywall.mode === "free";
|
|
1864
|
+
const monthlyCents = isFree ? 0 : paywall.prices.monthlyCents;
|
|
1865
|
+
const trialDays = isFree ? 0 : paywall.trialDays ?? 0;
|
|
1866
|
+
return {
|
|
1867
|
+
slug: config.slug,
|
|
1868
|
+
name: config.name,
|
|
1869
|
+
email_alias: config.slug,
|
|
1870
|
+
theme: { primary_color: config.branding.primaryColor },
|
|
1871
|
+
features_enabled: config.features_enabled ?? [],
|
|
1872
|
+
dependencies_allowlist: ["react", "react-dom"],
|
|
1873
|
+
subscription: {
|
|
1874
|
+
mode: paywall.mode,
|
|
1875
|
+
price_cents: monthlyCents,
|
|
1876
|
+
currency: "brl",
|
|
1877
|
+
trial_days: trialDays,
|
|
1878
|
+
paywall_config: {
|
|
1879
|
+
title: config.name,
|
|
1880
|
+
benefits: ["Acesso completo"],
|
|
1881
|
+
cta: "Assinar"
|
|
1882
|
+
}
|
|
1883
|
+
},
|
|
1884
|
+
sdk_version_required: ">=0.16.0",
|
|
1885
|
+
max_bundle_size_kb: 500
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
function AppRoot(props) {
|
|
1889
|
+
const {
|
|
1890
|
+
config: rawConfig,
|
|
1891
|
+
children,
|
|
1892
|
+
testRouter,
|
|
1893
|
+
testInitialEntries,
|
|
1894
|
+
Login,
|
|
1895
|
+
Signup,
|
|
1896
|
+
Forgot,
|
|
1897
|
+
Reset,
|
|
1898
|
+
EmailVerify,
|
|
1899
|
+
Paywall,
|
|
1900
|
+
Onboarding,
|
|
1901
|
+
PreAuthFlow
|
|
1902
|
+
} = props;
|
|
1903
|
+
if (!Login || !Signup || !Forgot || !Reset) {
|
|
1904
|
+
throw new Error(
|
|
1905
|
+
"[hook-template] <AppRoot>: Login, Signup, Forgot, Reset slot props are required."
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1908
|
+
const config = parseAppConfig(rawConfig);
|
|
1909
|
+
if (config.paywall.mode !== "free" && !Paywall) {
|
|
1910
|
+
throw new Error(
|
|
1911
|
+
"[hook-template] <AppRoot>: Paywall slot prop is required when config.paywall.mode != 'free'."
|
|
1912
|
+
);
|
|
1913
|
+
}
|
|
1914
|
+
if (config.authFlow.requiresEmailVerify && !EmailVerify) {
|
|
1915
|
+
throw new Error(
|
|
1916
|
+
"[hook-template] <AppRoot>: EmailVerify slot prop is required when config.authFlow.requiresEmailVerify === true."
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
if (config.onboarding?.trigger === "pre_signup_custom" && !PreAuthFlow) {
|
|
1920
|
+
throw new Error(
|
|
1921
|
+
"[hook-template] <AppRoot>: PreAuthFlow slot prop is required when config.onboarding.trigger === 'pre_signup_custom'."
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
const legacyShim = useMemo3(() => buildLegacyConfigShim(config), [config]);
|
|
1925
|
+
const Router = testRouter === "memory" ? MemoryRouter : BrowserRouter;
|
|
1926
|
+
const basename = `/app/${config.slug}`;
|
|
1927
|
+
const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
|
|
1928
|
+
return /* @__PURE__ */ jsx18(ErrorBoundary, { children: /* @__PURE__ */ jsx18(AppConfigProvider, { config, children: /* @__PURE__ */ jsx18(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx18(ThemeProvider, { children: /* @__PURE__ */ jsx18(PersistenceRegistry, { config: config.persistedKeys, children: /* @__PURE__ */ jsxs13(Router, { ...routerProps, children: [
|
|
1929
|
+
/* @__PURE__ */ jsx18(DeepLinkHandler, { deepLinks: config.deepLinks }),
|
|
1930
|
+
/* @__PURE__ */ jsx18(InstallGate, { children: /* @__PURE__ */ jsx18(
|
|
1931
|
+
AuthGated,
|
|
1932
|
+
{
|
|
1933
|
+
config,
|
|
1934
|
+
Login,
|
|
1935
|
+
Signup,
|
|
1936
|
+
Forgot,
|
|
1937
|
+
Reset,
|
|
1938
|
+
EmailVerify,
|
|
1939
|
+
Paywall,
|
|
1940
|
+
Onboarding,
|
|
1941
|
+
PreAuthFlow,
|
|
1942
|
+
children: /* @__PURE__ */ jsxs13(SubscriptionGate, { Paywall: Paywall ?? FallbackPaywall, children: [
|
|
1943
|
+
children,
|
|
1944
|
+
/* @__PURE__ */ jsx18(PushPrompt, {})
|
|
1945
|
+
] })
|
|
1946
|
+
}
|
|
1947
|
+
) })
|
|
1948
|
+
] }) }) }) }) }) });
|
|
1949
|
+
}
|
|
1950
|
+
function AuthGated({
|
|
1951
|
+
children,
|
|
1952
|
+
config,
|
|
1953
|
+
Login,
|
|
1954
|
+
Signup,
|
|
1955
|
+
Forgot,
|
|
1956
|
+
Reset,
|
|
1957
|
+
EmailVerify,
|
|
1958
|
+
PreAuthFlow
|
|
1959
|
+
}) {
|
|
1960
|
+
const { authStatus } = useHook4();
|
|
1961
|
+
if (authStatus === "loading") return null;
|
|
1962
|
+
if (authStatus !== "authenticated") {
|
|
1963
|
+
if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
|
|
1964
|
+
return /* @__PURE__ */ jsxs13(Routes, { children: [
|
|
1965
|
+
/* @__PURE__ */ jsx18(Route, { path: "/signin", element: /* @__PURE__ */ jsx18(Login, {}) }),
|
|
1966
|
+
/* @__PURE__ */ jsx18(Route, { path: "/signup", element: /* @__PURE__ */ jsx18(Signup, {}) }),
|
|
1967
|
+
/* @__PURE__ */ jsx18(Route, { path: "/forgot", element: /* @__PURE__ */ jsx18(Forgot, {}) }),
|
|
1968
|
+
/* @__PURE__ */ jsx18(Route, { path: "/reset", element: /* @__PURE__ */ jsx18(Reset, {}) }),
|
|
1969
|
+
EmailVerify ? /* @__PURE__ */ jsx18(Route, { path: "/verify", element: /* @__PURE__ */ jsx18(EmailVerify, {}) }) : null,
|
|
1970
|
+
/* @__PURE__ */ jsx18(Route, { path: "/*", element: /* @__PURE__ */ jsx18(PreAuthFlow, {}) })
|
|
1971
|
+
] });
|
|
1972
|
+
}
|
|
1973
|
+
return /* @__PURE__ */ jsxs13(Routes, { children: [
|
|
1974
|
+
/* @__PURE__ */ jsx18(Route, { path: "/", element: /* @__PURE__ */ jsx18(Login, {}) }),
|
|
1975
|
+
/* @__PURE__ */ jsx18(Route, { path: "/signup", element: /* @__PURE__ */ jsx18(Signup, {}) }),
|
|
1976
|
+
/* @__PURE__ */ jsx18(Route, { path: "/forgot", element: /* @__PURE__ */ jsx18(Forgot, {}) }),
|
|
1977
|
+
/* @__PURE__ */ jsx18(Route, { path: "/reset", element: /* @__PURE__ */ jsx18(Reset, {}) }),
|
|
1978
|
+
EmailVerify ? /* @__PURE__ */ jsx18(Route, { path: "/verify", element: /* @__PURE__ */ jsx18(EmailVerify, {}) }) : null,
|
|
1979
|
+
/* @__PURE__ */ jsx18(Route, { path: "*", element: /* @__PURE__ */ jsx18(Navigate, { to: "/", replace: true }) })
|
|
1980
|
+
] });
|
|
1981
|
+
}
|
|
1982
|
+
return /* @__PURE__ */ jsx18(Fragment6, { children });
|
|
1983
|
+
}
|
|
1984
|
+
function FallbackPaywall() {
|
|
1985
|
+
return null;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
// src/hooks/usePush.ts
|
|
1989
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useState as useState5 } from "react";
|
|
1990
|
+
import { useHook as useHook5 } from "@hook-sdk/sdk";
|
|
1991
|
+
function detectIosNeedsInstall() {
|
|
1992
|
+
if (typeof navigator === "undefined" || typeof window === "undefined") return false;
|
|
1993
|
+
const ua = navigator.userAgent || "";
|
|
1994
|
+
const isIos = /iPhone|iPad|iPod/.test(ua);
|
|
1995
|
+
if (!isIos) return false;
|
|
1996
|
+
const mm = window.matchMedia?.("(display-mode: standalone)");
|
|
1997
|
+
const standalone = mm?.matches === true;
|
|
1998
|
+
const legacyStandalone = typeof navigator.standalone === "boolean" ? navigator.standalone : false;
|
|
1999
|
+
return !(standalone || legacyStandalone);
|
|
2000
|
+
}
|
|
2001
|
+
function deriveState(push) {
|
|
2002
|
+
if (!push.isAvailable()) {
|
|
2003
|
+
if (detectIosNeedsInstall()) return { kind: "ios_needs_install" };
|
|
2004
|
+
return { kind: "unsupported" };
|
|
2005
|
+
}
|
|
2006
|
+
const status = push.status();
|
|
2007
|
+
if (status === "granted") return { kind: "subscribed" };
|
|
2008
|
+
if (status === "denied") return { kind: "denied" };
|
|
2009
|
+
if (status === "unsupported") {
|
|
2010
|
+
if (detectIosNeedsInstall()) return { kind: "ios_needs_install" };
|
|
2011
|
+
return { kind: "unsupported" };
|
|
2012
|
+
}
|
|
2013
|
+
return { kind: "prompt" };
|
|
2014
|
+
}
|
|
2015
|
+
function usePush() {
|
|
2016
|
+
const { push } = useHook5();
|
|
2017
|
+
const [state, setState] = useState5(() => deriveState(push));
|
|
2018
|
+
useEffect6(() => {
|
|
2019
|
+
setState(deriveState(push));
|
|
2020
|
+
}, [push]);
|
|
2021
|
+
const subscribe = useCallback4(async () => {
|
|
2022
|
+
try {
|
|
2023
|
+
await push.subscribe();
|
|
2024
|
+
setState({ kind: "subscribed" });
|
|
2025
|
+
} catch (e) {
|
|
2026
|
+
const code = e?.code ?? "push.unknown";
|
|
2027
|
+
const message = e?.message ?? "Push subscription failed";
|
|
2028
|
+
if (code === "push.permission_denied") setState({ kind: "denied" });
|
|
2029
|
+
else setState({ kind: "error", code, message });
|
|
2030
|
+
throw e;
|
|
2031
|
+
}
|
|
2032
|
+
}, [push]);
|
|
2033
|
+
const unsubscribe = useCallback4(async () => {
|
|
2034
|
+
try {
|
|
2035
|
+
await push.unsubscribe();
|
|
2036
|
+
setState({ kind: "prompt" });
|
|
2037
|
+
} catch (e) {
|
|
2038
|
+
setState({ kind: "error", code: e?.code ?? "push.unknown", message: e?.message ?? "failed" });
|
|
2039
|
+
throw e;
|
|
2040
|
+
}
|
|
2041
|
+
}, [push]);
|
|
2042
|
+
return { state, subscribe, unsubscribe };
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
// src/components/PushPrompt.tsx
|
|
2046
|
+
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2047
|
+
function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
|
|
2048
|
+
const { state, subscribe } = usePush();
|
|
2049
|
+
if (state.kind === "subscribed") return null;
|
|
2050
|
+
if (state.kind === "ios_needs_install") {
|
|
2051
|
+
return /* @__PURE__ */ jsxs14("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
|
|
2052
|
+
/* @__PURE__ */ jsx19("h3", { children: texts.iosInstallTitle }),
|
|
2053
|
+
/* @__PURE__ */ jsx19("p", { children: texts.iosInstallBody }),
|
|
2054
|
+
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx19("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
|
|
2055
|
+
] });
|
|
2056
|
+
}
|
|
2057
|
+
if (state.kind === "denied") {
|
|
2058
|
+
return /* @__PURE__ */ jsxs14("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
|
|
2059
|
+
/* @__PURE__ */ jsx19("h3", { children: texts.deniedTitle }),
|
|
2060
|
+
/* @__PURE__ */ jsx19("p", { children: texts.deniedBody })
|
|
2061
|
+
] });
|
|
2062
|
+
}
|
|
2063
|
+
if (state.kind === "unsupported") {
|
|
2064
|
+
return /* @__PURE__ */ jsx19("div", { className, role: "region", children: /* @__PURE__ */ jsx19("p", { children: texts.unsupportedBody }) });
|
|
2065
|
+
}
|
|
2066
|
+
if (state.kind === "error") {
|
|
2067
|
+
return /* @__PURE__ */ jsx19("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx19("p", { children: state.message }) });
|
|
2068
|
+
}
|
|
2069
|
+
return /* @__PURE__ */ jsxs14("div", { className, role: "region", children: [
|
|
2070
|
+
/* @__PURE__ */ jsx19(
|
|
2071
|
+
"button",
|
|
2072
|
+
{
|
|
2073
|
+
type: "button",
|
|
2074
|
+
onClick: async () => {
|
|
2075
|
+
try {
|
|
2076
|
+
await subscribe();
|
|
2077
|
+
onSubscribed?.();
|
|
2078
|
+
} catch {
|
|
2079
|
+
}
|
|
2080
|
+
},
|
|
2081
|
+
children: texts.cta
|
|
2082
|
+
}
|
|
2083
|
+
),
|
|
2084
|
+
onDeclined && /* @__PURE__ */ jsx19("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
|
|
2085
|
+
] });
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
// src/defaults/LoadingState.tsx
|
|
2089
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
2090
|
+
function LoadingState({ message }) {
|
|
2091
|
+
return /* @__PURE__ */ jsx20("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx20("span", { children: message ?? "Carregando..." }) });
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
// src/defaults/EmptyState.tsx
|
|
2095
|
+
import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2096
|
+
function EmptyState({ title, description, action }) {
|
|
2097
|
+
return /* @__PURE__ */ jsxs15("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
|
|
2098
|
+
/* @__PURE__ */ jsx21("h2", { style: { marginBottom: 8 }, children: title }),
|
|
2099
|
+
description && /* @__PURE__ */ jsx21("p", { style: { opacity: 0.7 }, children: description }),
|
|
2100
|
+
action && /* @__PURE__ */ jsx21("div", { style: { marginTop: 16 }, children: action })
|
|
2101
|
+
] });
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// src/hooks/useLoginForm.ts
|
|
2105
|
+
import { useCallback as useCallback5, useMemo as useMemo4, useState as useState6 } from "react";
|
|
2106
|
+
import { useHook as useHook6 } from "@hook-sdk/sdk";
|
|
2107
|
+
|
|
2108
|
+
// src/errors.ts
|
|
2109
|
+
import { SdkError, SdkAuthError, SdkRateLimitError } from "@hook-sdk/sdk";
|
|
2110
|
+
function mapSdkError(err) {
|
|
2111
|
+
if (err instanceof SdkRateLimitError) {
|
|
2112
|
+
return {
|
|
2113
|
+
code: "rate_limited",
|
|
2114
|
+
message: `Aguarde ${err.retryAfter}s e tente novamente.`,
|
|
2115
|
+
retryAfter: err.retryAfter
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
if (err instanceof SdkAuthError) {
|
|
2119
|
+
const detail = err.detail;
|
|
2120
|
+
if (detail === "email_unverified") {
|
|
2121
|
+
return { code: "email_unverified", message: "Confirme seu e-mail antes de entrar." };
|
|
2122
|
+
}
|
|
2123
|
+
if (detail === "account_locked") {
|
|
2124
|
+
return { code: "account_locked", message: "Conta bloqueada. Contate o suporte." };
|
|
2125
|
+
}
|
|
2126
|
+
return { code: "invalid_credentials", message: "E-mail ou senha inv\xE1lidos." };
|
|
2127
|
+
}
|
|
2128
|
+
if (err instanceof SdkError && err.httpStatus === 0) {
|
|
2129
|
+
return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
|
|
2130
|
+
}
|
|
2131
|
+
if (err instanceof TypeError) {
|
|
2132
|
+
return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
|
|
2133
|
+
}
|
|
2134
|
+
return { code: "server", message: "Algo deu errado. Tente novamente em instantes." };
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
// src/hooks/useLoginForm.ts
|
|
2138
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2139
|
+
var MIN_PASSWORD = 8;
|
|
1590
2140
|
function useLoginForm() {
|
|
1591
|
-
const { auth } =
|
|
2141
|
+
const { auth } = useHook6();
|
|
1592
2142
|
const [email, setEmail] = useState6("");
|
|
1593
2143
|
const [password, setPassword] = useState6("");
|
|
1594
2144
|
const [submitting, setSubmitting] = useState6(false);
|
|
1595
2145
|
const [error, setError] = useState6(null);
|
|
1596
|
-
const emailError =
|
|
2146
|
+
const emailError = useMemo4(() => {
|
|
1597
2147
|
if (email.length === 0) return null;
|
|
1598
2148
|
if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
1599
2149
|
return null;
|
|
1600
2150
|
}, [email]);
|
|
1601
|
-
const passwordError =
|
|
2151
|
+
const passwordError = useMemo4(() => {
|
|
1602
2152
|
if (password.length === 0) return null;
|
|
1603
2153
|
if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
|
|
1604
2154
|
return null;
|
|
1605
2155
|
}, [password]);
|
|
1606
2156
|
const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && emailError === null && passwordError === null && !submitting;
|
|
1607
|
-
const submit =
|
|
2157
|
+
const submit = useCallback5(async () => {
|
|
1608
2158
|
if (!canSubmit) return false;
|
|
1609
2159
|
setSubmitting(true);
|
|
1610
2160
|
setError(null);
|
|
@@ -1633,259 +2183,35 @@ function useLoginForm() {
|
|
|
1633
2183
|
};
|
|
1634
2184
|
}
|
|
1635
2185
|
|
|
1636
|
-
// src/internal/GoogleSignInButton.tsx
|
|
1637
|
-
import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1638
|
-
function GoogleSignInButton({
|
|
1639
|
-
onClick,
|
|
1640
|
-
testId = "oauth-google",
|
|
1641
|
-
label = "Continuar com Google"
|
|
1642
|
-
}) {
|
|
1643
|
-
return /* @__PURE__ */ jsxs12(
|
|
1644
|
-
"button",
|
|
1645
|
-
{
|
|
1646
|
-
"data-testid": testId,
|
|
1647
|
-
type: "button",
|
|
1648
|
-
onClick,
|
|
1649
|
-
style: {
|
|
1650
|
-
width: "100%",
|
|
1651
|
-
padding: "10px 12px",
|
|
1652
|
-
display: "flex",
|
|
1653
|
-
alignItems: "center",
|
|
1654
|
-
justifyContent: "center",
|
|
1655
|
-
gap: 10,
|
|
1656
|
-
background: "#fff",
|
|
1657
|
-
color: "#1f1f1f",
|
|
1658
|
-
border: "1px solid #dadce0",
|
|
1659
|
-
borderRadius: 8,
|
|
1660
|
-
cursor: "pointer",
|
|
1661
|
-
fontSize: 14,
|
|
1662
|
-
fontWeight: 500,
|
|
1663
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
1664
|
-
},
|
|
1665
|
-
children: [
|
|
1666
|
-
/* @__PURE__ */ jsx18(GoogleGlyph, {}),
|
|
1667
|
-
label
|
|
1668
|
-
]
|
|
1669
|
-
}
|
|
1670
|
-
);
|
|
1671
|
-
}
|
|
1672
|
-
function GoogleGlyph() {
|
|
1673
|
-
return /* @__PURE__ */ jsxs12("svg", { width: "18", height: "18", viewBox: "0 0 18 18", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
|
|
1674
|
-
/* @__PURE__ */ jsx18(
|
|
1675
|
-
"path",
|
|
1676
|
-
{
|
|
1677
|
-
d: "M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844a4.14 4.14 0 0 1-1.796 2.716v2.259h2.908c1.702-1.567 2.684-3.874 2.684-6.615z",
|
|
1678
|
-
fill: "#4285F4"
|
|
1679
|
-
}
|
|
1680
|
-
),
|
|
1681
|
-
/* @__PURE__ */ jsx18(
|
|
1682
|
-
"path",
|
|
1683
|
-
{
|
|
1684
|
-
d: "M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18z",
|
|
1685
|
-
fill: "#34A853"
|
|
1686
|
-
}
|
|
1687
|
-
),
|
|
1688
|
-
/* @__PURE__ */ jsx18(
|
|
1689
|
-
"path",
|
|
1690
|
-
{
|
|
1691
|
-
d: "M3.964 10.71A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.042l3.007-2.332z",
|
|
1692
|
-
fill: "#FBBC05"
|
|
1693
|
-
}
|
|
1694
|
-
),
|
|
1695
|
-
/* @__PURE__ */ jsx18(
|
|
1696
|
-
"path",
|
|
1697
|
-
{
|
|
1698
|
-
d: "M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z",
|
|
1699
|
-
fill: "#EA4335"
|
|
1700
|
-
}
|
|
1701
|
-
)
|
|
1702
|
-
] });
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
|
-
// src/internal/OAuthErrorBanner.tsx
|
|
1706
|
-
import { useEffect as useEffect6, useState as useState7 } from "react";
|
|
1707
|
-
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1708
|
-
var ERROR_MESSAGES = {
|
|
1709
|
-
invalid_state: "Sess\xE3o expirou, tente de novo.",
|
|
1710
|
-
access_denied: "Voc\xEA cancelou o login com Google.",
|
|
1711
|
-
provider_error: "O Google recusou a autentica\xE7\xE3o. Tente de novo em alguns segundos.",
|
|
1712
|
-
invalid_return_to: "Link inv\xE1lido. Tente entrar novamente."
|
|
1713
|
-
};
|
|
1714
|
-
function readErrorCode() {
|
|
1715
|
-
if (typeof window === "undefined") return null;
|
|
1716
|
-
const code = new URLSearchParams(window.location.search).get("oauth_error");
|
|
1717
|
-
if (!code) return null;
|
|
1718
|
-
return code;
|
|
1719
|
-
}
|
|
1720
|
-
function stripErrorFromUrl() {
|
|
1721
|
-
if (typeof window === "undefined") return;
|
|
1722
|
-
const url = new URL(window.location.href);
|
|
1723
|
-
url.searchParams.delete("oauth_error");
|
|
1724
|
-
window.history.replaceState({}, "", url.toString());
|
|
1725
|
-
}
|
|
1726
|
-
function OAuthErrorBanner() {
|
|
1727
|
-
const [code, setCode] = useState7(() => readErrorCode());
|
|
1728
|
-
useEffect6(() => {
|
|
1729
|
-
if (code !== null) stripErrorFromUrl();
|
|
1730
|
-
}, [code]);
|
|
1731
|
-
if (!code) return null;
|
|
1732
|
-
const message = ERROR_MESSAGES[code] ?? "N\xE3o conseguimos conectar ao Google. Tente de novo.";
|
|
1733
|
-
return /* @__PURE__ */ jsxs13(
|
|
1734
|
-
"div",
|
|
1735
|
-
{
|
|
1736
|
-
role: "alert",
|
|
1737
|
-
"data-testid": "oauth-error-banner",
|
|
1738
|
-
style: {
|
|
1739
|
-
padding: "10px 12px",
|
|
1740
|
-
marginBottom: 16,
|
|
1741
|
-
background: "#fce8e6",
|
|
1742
|
-
color: "#a50e0e",
|
|
1743
|
-
borderRadius: 8,
|
|
1744
|
-
fontSize: 14
|
|
1745
|
-
},
|
|
1746
|
-
children: [
|
|
1747
|
-
message,
|
|
1748
|
-
/* @__PURE__ */ jsx19(
|
|
1749
|
-
"button",
|
|
1750
|
-
{
|
|
1751
|
-
type: "button",
|
|
1752
|
-
onClick: () => setCode(null),
|
|
1753
|
-
"aria-label": "Fechar",
|
|
1754
|
-
style: {
|
|
1755
|
-
float: "right",
|
|
1756
|
-
background: "none",
|
|
1757
|
-
border: "none",
|
|
1758
|
-
color: "#a50e0e",
|
|
1759
|
-
cursor: "pointer",
|
|
1760
|
-
fontSize: 16,
|
|
1761
|
-
lineHeight: 1,
|
|
1762
|
-
padding: 0
|
|
1763
|
-
},
|
|
1764
|
-
children: "\xD7"
|
|
1765
|
-
}
|
|
1766
|
-
)
|
|
1767
|
-
]
|
|
1768
|
-
}
|
|
1769
|
-
);
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
// src/defaults/DefaultLoginScreen.tsx
|
|
1773
|
-
import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1774
|
-
function DefaultLoginScreen({ onNavigate }) {
|
|
1775
|
-
const { name } = useTemplateConfig();
|
|
1776
|
-
const f = useLoginForm();
|
|
1777
|
-
return /* @__PURE__ */ jsxs14("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
|
|
1778
|
-
/* @__PURE__ */ jsx20("h1", { style: { marginBottom: 8 }, children: name }),
|
|
1779
|
-
/* @__PURE__ */ jsx20("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
|
|
1780
|
-
/* @__PURE__ */ jsx20(OAuthErrorBanner, {}),
|
|
1781
|
-
/* @__PURE__ */ jsx20(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "login-oauth-google" }),
|
|
1782
|
-
/* @__PURE__ */ jsxs14(
|
|
1783
|
-
"div",
|
|
1784
|
-
{
|
|
1785
|
-
"aria-hidden": "true",
|
|
1786
|
-
style: {
|
|
1787
|
-
display: "flex",
|
|
1788
|
-
alignItems: "center",
|
|
1789
|
-
gap: 8,
|
|
1790
|
-
margin: "16px 0",
|
|
1791
|
-
color: "rgba(0,0,0,0.45)",
|
|
1792
|
-
fontSize: 12
|
|
1793
|
-
},
|
|
1794
|
-
children: [
|
|
1795
|
-
/* @__PURE__ */ jsx20("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
|
|
1796
|
-
"ou",
|
|
1797
|
-
/* @__PURE__ */ jsx20("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
|
|
1798
|
-
]
|
|
1799
|
-
}
|
|
1800
|
-
),
|
|
1801
|
-
/* @__PURE__ */ jsxs14("form", { onSubmit: (e) => {
|
|
1802
|
-
e.preventDefault();
|
|
1803
|
-
void f.submit();
|
|
1804
|
-
}, children: [
|
|
1805
|
-
/* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
|
|
1806
|
-
"E-mail",
|
|
1807
|
-
/* @__PURE__ */ jsx20(
|
|
1808
|
-
"input",
|
|
1809
|
-
{
|
|
1810
|
-
"data-testid": "login-email",
|
|
1811
|
-
type: "email",
|
|
1812
|
-
value: f.email,
|
|
1813
|
-
onChange: (e) => f.setEmail(e.target.value),
|
|
1814
|
-
style: { display: "block", width: "100%" }
|
|
1815
|
-
}
|
|
1816
|
-
),
|
|
1817
|
-
f.emailError && /* @__PURE__ */ jsx20("small", { style: { color: "#c00" }, children: f.emailError })
|
|
1818
|
-
] }),
|
|
1819
|
-
/* @__PURE__ */ jsxs14("label", { style: { display: "block", marginBottom: 12 }, children: [
|
|
1820
|
-
"Senha",
|
|
1821
|
-
/* @__PURE__ */ jsx20(
|
|
1822
|
-
"input",
|
|
1823
|
-
{
|
|
1824
|
-
"data-testid": "login-password",
|
|
1825
|
-
type: "password",
|
|
1826
|
-
value: f.password,
|
|
1827
|
-
onChange: (e) => f.setPassword(e.target.value),
|
|
1828
|
-
style: { display: "block", width: "100%" }
|
|
1829
|
-
}
|
|
1830
|
-
),
|
|
1831
|
-
f.passwordError && /* @__PURE__ */ jsx20("small", { style: { color: "#c00" }, children: f.passwordError })
|
|
1832
|
-
] }),
|
|
1833
|
-
f.error && /* @__PURE__ */ jsx20("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
|
|
1834
|
-
/* @__PURE__ */ jsx20(
|
|
1835
|
-
"button",
|
|
1836
|
-
{
|
|
1837
|
-
"data-testid": "login-submit",
|
|
1838
|
-
type: "submit",
|
|
1839
|
-
disabled: !f.canSubmit,
|
|
1840
|
-
style: {
|
|
1841
|
-
width: "100%",
|
|
1842
|
-
padding: 12,
|
|
1843
|
-
background: "var(--hook-color-primary)",
|
|
1844
|
-
color: "#fff",
|
|
1845
|
-
border: "none",
|
|
1846
|
-
borderRadius: 8,
|
|
1847
|
-
opacity: f.canSubmit ? 1 : 0.5
|
|
1848
|
-
},
|
|
1849
|
-
children: f.submitting ? "Entrando..." : "Entrar"
|
|
1850
|
-
}
|
|
1851
|
-
)
|
|
1852
|
-
] }),
|
|
1853
|
-
/* @__PURE__ */ jsxs14("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between" }, children: [
|
|
1854
|
-
/* @__PURE__ */ jsx20("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
|
|
1855
|
-
/* @__PURE__ */ jsx20("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
|
|
1856
|
-
] })
|
|
1857
|
-
] });
|
|
1858
|
-
}
|
|
1859
|
-
|
|
1860
2186
|
// src/hooks/useSignupForm.ts
|
|
1861
|
-
import { useCallback as
|
|
1862
|
-
import { useHook as
|
|
2187
|
+
import { useCallback as useCallback6, useMemo as useMemo5, useState as useState7 } from "react";
|
|
2188
|
+
import { useHook as useHook7 } from "@hook-sdk/sdk";
|
|
1863
2189
|
var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1864
2190
|
var MIN_PASSWORD2 = 8;
|
|
1865
2191
|
function useSignupForm() {
|
|
1866
|
-
const { auth } =
|
|
1867
|
-
const [name, setName] =
|
|
1868
|
-
const [email, setEmail] =
|
|
1869
|
-
const [password, setPassword] =
|
|
1870
|
-
const [submitting, setSubmitting] =
|
|
1871
|
-
const [error, setError] =
|
|
1872
|
-
const nameError =
|
|
2192
|
+
const { auth } = useHook7();
|
|
2193
|
+
const [name, setName] = useState7("");
|
|
2194
|
+
const [email, setEmail] = useState7("");
|
|
2195
|
+
const [password, setPassword] = useState7("");
|
|
2196
|
+
const [submitting, setSubmitting] = useState7(false);
|
|
2197
|
+
const [error, setError] = useState7(null);
|
|
2198
|
+
const nameError = useMemo5(() => {
|
|
1873
2199
|
if (name.length === 0) return null;
|
|
1874
2200
|
if (name.trim().length < 2) return "Nome muito curto.";
|
|
1875
2201
|
return null;
|
|
1876
2202
|
}, [name]);
|
|
1877
|
-
const emailError =
|
|
2203
|
+
const emailError = useMemo5(() => {
|
|
1878
2204
|
if (email.length === 0) return null;
|
|
1879
2205
|
if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
1880
2206
|
return null;
|
|
1881
2207
|
}, [email]);
|
|
1882
|
-
const passwordError =
|
|
2208
|
+
const passwordError = useMemo5(() => {
|
|
1883
2209
|
if (password.length === 0) return null;
|
|
1884
2210
|
if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
|
|
1885
2211
|
return null;
|
|
1886
2212
|
}, [password]);
|
|
1887
2213
|
const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && nameError === null && emailError === null && passwordError === null && !submitting;
|
|
1888
|
-
const submit =
|
|
2214
|
+
const submit = useCallback6(async () => {
|
|
1889
2215
|
if (!canSubmit) return false;
|
|
1890
2216
|
setSubmitting(true);
|
|
1891
2217
|
setError(null);
|
|
@@ -1899,1100 +2225,173 @@ function useSignupForm() {
|
|
|
1899
2225
|
setSubmitting(false);
|
|
1900
2226
|
}
|
|
1901
2227
|
}, [auth, name, email, password, canSubmit]);
|
|
1902
|
-
return {
|
|
1903
|
-
name,
|
|
1904
|
-
setName,
|
|
1905
|
-
nameError,
|
|
1906
|
-
email,
|
|
1907
|
-
setEmail,
|
|
1908
|
-
emailError,
|
|
1909
|
-
password,
|
|
1910
|
-
setPassword,
|
|
1911
|
-
passwordError,
|
|
1912
|
-
submit,
|
|
1913
|
-
submitting,
|
|
1914
|
-
canSubmit,
|
|
1915
|
-
error,
|
|
1916
|
-
loginWithGoogle: () => auth.loginWithGoogle()
|
|
1917
|
-
};
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
// src/defaults/DefaultSignupScreen.tsx
|
|
1921
|
-
import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1922
|
-
function DefaultSignupScreen({ onNavigate }) {
|
|
1923
|
-
const { name } = useTemplateConfig();
|
|
1924
|
-
const f = useSignupForm();
|
|
1925
|
-
return /* @__PURE__ */ jsxs15("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
|
|
1926
|
-
/* @__PURE__ */ jsx21("h1", { style: { marginBottom: 8 }, children: name }),
|
|
1927
|
-
/* @__PURE__ */ jsx21("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
|
|
1928
|
-
/* @__PURE__ */ jsx21(OAuthErrorBanner, {}),
|
|
1929
|
-
/* @__PURE__ */ jsx21(GoogleSignInButton, { onClick: f.loginWithGoogle, testId: "signup-oauth-google" }),
|
|
1930
|
-
/* @__PURE__ */ jsxs15(
|
|
1931
|
-
"div",
|
|
1932
|
-
{
|
|
1933
|
-
"aria-hidden": "true",
|
|
1934
|
-
style: {
|
|
1935
|
-
display: "flex",
|
|
1936
|
-
alignItems: "center",
|
|
1937
|
-
gap: 8,
|
|
1938
|
-
margin: "16px 0",
|
|
1939
|
-
color: "rgba(0,0,0,0.45)",
|
|
1940
|
-
fontSize: 12
|
|
1941
|
-
},
|
|
1942
|
-
children: [
|
|
1943
|
-
/* @__PURE__ */ jsx21("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } }),
|
|
1944
|
-
"ou",
|
|
1945
|
-
/* @__PURE__ */ jsx21("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.1)" } })
|
|
1946
|
-
]
|
|
1947
|
-
}
|
|
1948
|
-
),
|
|
1949
|
-
/* @__PURE__ */ jsxs15("form", { onSubmit: (e) => {
|
|
1950
|
-
e.preventDefault();
|
|
1951
|
-
void f.submit();
|
|
1952
|
-
}, children: [
|
|
1953
|
-
/* @__PURE__ */ jsxs15("label", { style: { display: "block", marginBottom: 12 }, children: [
|
|
1954
|
-
"Nome",
|
|
1955
|
-
/* @__PURE__ */ jsx21("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
|
|
1956
|
-
f.nameError && /* @__PURE__ */ jsx21("small", { style: { color: "#c00" }, children: f.nameError })
|
|
1957
|
-
] }),
|
|
1958
|
-
/* @__PURE__ */ jsxs15("label", { style: { display: "block", marginBottom: 12 }, children: [
|
|
1959
|
-
"E-mail",
|
|
1960
|
-
/* @__PURE__ */ jsx21("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
|
|
1961
|
-
f.emailError && /* @__PURE__ */ jsx21("small", { style: { color: "#c00" }, children: f.emailError })
|
|
1962
|
-
] }),
|
|
1963
|
-
/* @__PURE__ */ jsxs15("label", { style: { display: "block", marginBottom: 12 }, children: [
|
|
1964
|
-
"Senha",
|
|
1965
|
-
/* @__PURE__ */ jsx21("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
|
|
1966
|
-
f.passwordError && /* @__PURE__ */ jsx21("small", { style: { color: "#c00" }, children: f.passwordError })
|
|
1967
|
-
] }),
|
|
1968
|
-
f.error && /* @__PURE__ */ jsx21("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
|
|
1969
|
-
/* @__PURE__ */ jsx21("button", { "data-testid": "signup-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Criando..." : "Criar conta" })
|
|
1970
|
-
] }),
|
|
1971
|
-
/* @__PURE__ */ jsx21("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx21("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
|
|
1972
|
-
] });
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
// src/hooks/useForgotForm.ts
|
|
1976
|
-
import { useCallback as useCallback5, useMemo as useMemo5, useState as useState9 } from "react";
|
|
1977
|
-
import { useHook as useHook6 } from "@hook-sdk/sdk";
|
|
1978
|
-
var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1979
|
-
function useForgotForm() {
|
|
1980
|
-
const { auth } = useHook6();
|
|
1981
|
-
const [email, setEmail] = useState9("");
|
|
1982
|
-
const [submitting, setSubmitting] = useState9(false);
|
|
1983
|
-
const [sent, setSent] = useState9(false);
|
|
1984
|
-
const [error, setError] = useState9(null);
|
|
1985
|
-
const emailError = useMemo5(() => {
|
|
1986
|
-
if (email.length === 0) return null;
|
|
1987
|
-
if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
1988
|
-
return null;
|
|
1989
|
-
}, [email]);
|
|
1990
|
-
const canSubmit = email.length > 0 && emailError === null && !submitting;
|
|
1991
|
-
const submit = useCallback5(async () => {
|
|
1992
|
-
if (!canSubmit) return false;
|
|
1993
|
-
setSubmitting(true);
|
|
1994
|
-
setError(null);
|
|
1995
|
-
try {
|
|
1996
|
-
await auth.forgot({ email });
|
|
1997
|
-
setSent(true);
|
|
1998
|
-
return true;
|
|
1999
|
-
} catch (err) {
|
|
2000
|
-
setError(mapSdkError(err));
|
|
2001
|
-
return false;
|
|
2002
|
-
} finally {
|
|
2003
|
-
setSubmitting(false);
|
|
2004
|
-
}
|
|
2005
|
-
}, [auth, email, canSubmit]);
|
|
2006
|
-
return {
|
|
2007
|
-
email,
|
|
2008
|
-
setEmail,
|
|
2009
|
-
emailError,
|
|
2010
|
-
submit,
|
|
2011
|
-
submitting,
|
|
2012
|
-
canSubmit,
|
|
2013
|
-
sent,
|
|
2014
|
-
error
|
|
2015
|
-
};
|
|
2016
|
-
}
|
|
2017
|
-
|
|
2018
|
-
// src/defaults/DefaultForgotScreen.tsx
|
|
2019
|
-
import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2020
|
-
function DefaultForgotScreen({ onNavigate }) {
|
|
2021
|
-
const { name } = useTemplateConfig();
|
|
2022
|
-
const f = useForgotForm();
|
|
2023
|
-
if (f.sent) {
|
|
2024
|
-
return /* @__PURE__ */ jsxs16("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
|
|
2025
|
-
/* @__PURE__ */ jsx22("h1", { children: "Verifique seu e-mail" }),
|
|
2026
|
-
/* @__PURE__ */ jsx22("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
|
|
2027
|
-
/* @__PURE__ */ jsx22("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
|
|
2028
|
-
] });
|
|
2029
|
-
}
|
|
2030
|
-
return /* @__PURE__ */ jsxs16("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
|
|
2031
|
-
/* @__PURE__ */ jsx22("h1", { style: { marginBottom: 8 }, children: name }),
|
|
2032
|
-
/* @__PURE__ */ jsx22("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
|
|
2033
|
-
/* @__PURE__ */ jsxs16("form", { onSubmit: (e) => {
|
|
2034
|
-
e.preventDefault();
|
|
2035
|
-
void f.submit();
|
|
2036
|
-
}, children: [
|
|
2037
|
-
/* @__PURE__ */ jsxs16("label", { style: { display: "block", marginBottom: 12 }, children: [
|
|
2038
|
-
"E-mail",
|
|
2039
|
-
/* @__PURE__ */ jsx22("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
|
|
2040
|
-
f.emailError && /* @__PURE__ */ jsx22("small", { style: { color: "#c00" }, children: f.emailError })
|
|
2041
|
-
] }),
|
|
2042
|
-
f.error && /* @__PURE__ */ jsx22("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
|
|
2043
|
-
/* @__PURE__ */ jsx22("button", { "data-testid": "forgot-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Enviando..." : "Enviar link" })
|
|
2044
|
-
] }),
|
|
2045
|
-
/* @__PURE__ */ jsx22("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx22("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
|
|
2046
|
-
] });
|
|
2047
|
-
}
|
|
2048
|
-
|
|
2049
|
-
// src/hooks/useResetForm.ts
|
|
2050
|
-
import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo6, useState as useState10 } from "react";
|
|
2051
|
-
import { useHook as useHook7 } from "@hook-sdk/sdk";
|
|
2052
|
-
var MIN_PASSWORD3 = 12;
|
|
2053
|
-
function useResetForm() {
|
|
2054
|
-
const { auth } = useHook7();
|
|
2055
|
-
const [token, setToken] = useState10(null);
|
|
2056
|
-
const [password, setPassword] = useState10("");
|
|
2057
|
-
const [confirm, setConfirm] = useState10("");
|
|
2058
|
-
const [submitting, setSubmitting] = useState10(false);
|
|
2059
|
-
const [done, setDone] = useState10(false);
|
|
2060
|
-
const [error, setError] = useState10(null);
|
|
2061
|
-
useEffect7(() => {
|
|
2062
|
-
if (typeof window === "undefined") return;
|
|
2063
|
-
const params = new URLSearchParams(window.location.search);
|
|
2064
|
-
const t = params.get("token");
|
|
2065
|
-
setToken(t && t.length > 0 ? t : null);
|
|
2066
|
-
}, []);
|
|
2067
|
-
const passwordError = useMemo6(() => {
|
|
2068
|
-
if (password.length === 0) return null;
|
|
2069
|
-
if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
|
|
2070
|
-
return null;
|
|
2071
|
-
}, [password]);
|
|
2072
|
-
const confirmError = useMemo6(() => {
|
|
2073
|
-
if (confirm.length === 0) return null;
|
|
2074
|
-
if (confirm !== password) return "Senhas n\xE3o coincidem.";
|
|
2075
|
-
return null;
|
|
2076
|
-
}, [confirm, password]);
|
|
2077
|
-
const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && passwordError === null && confirmError === null && !submitting && !done;
|
|
2078
|
-
const submit = useCallback6(async () => {
|
|
2079
|
-
if (!canSubmit || token === null) return;
|
|
2080
|
-
setSubmitting(true);
|
|
2081
|
-
setError(null);
|
|
2082
|
-
try {
|
|
2083
|
-
await auth.reset({ token, newPassword: password });
|
|
2084
|
-
setDone(true);
|
|
2085
|
-
if (typeof window !== "undefined") {
|
|
2086
|
-
const url = new URL(window.location.href);
|
|
2087
|
-
url.searchParams.delete("token");
|
|
2088
|
-
url.searchParams.delete("screen");
|
|
2089
|
-
window.history.replaceState({}, "", url.toString());
|
|
2090
|
-
}
|
|
2091
|
-
} catch (err) {
|
|
2092
|
-
setError(mapSdkError(err));
|
|
2093
|
-
} finally {
|
|
2094
|
-
setSubmitting(false);
|
|
2095
|
-
}
|
|
2096
|
-
}, [auth, token, password, canSubmit]);
|
|
2097
|
-
return {
|
|
2098
|
-
token,
|
|
2099
|
-
password,
|
|
2100
|
-
setPassword,
|
|
2101
|
-
passwordError,
|
|
2102
|
-
confirm,
|
|
2103
|
-
setConfirm,
|
|
2104
|
-
confirmError,
|
|
2105
|
-
submit,
|
|
2106
|
-
submitting,
|
|
2107
|
-
canSubmit,
|
|
2108
|
-
done,
|
|
2109
|
-
error
|
|
2110
|
-
};
|
|
2111
|
-
}
|
|
2112
|
-
|
|
2113
|
-
// src/defaults/DefaultResetScreen.tsx
|
|
2114
|
-
import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2115
|
-
function DefaultResetScreen({ onNavigate }) {
|
|
2116
|
-
const { name } = useTemplateConfig();
|
|
2117
|
-
const f = useResetForm();
|
|
2118
|
-
if (f.done) {
|
|
2119
|
-
return /* @__PURE__ */ jsxs17("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
|
|
2120
|
-
/* @__PURE__ */ jsx23("h1", { children: "Senha alterada" }),
|
|
2121
|
-
/* @__PURE__ */ jsx23("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
|
|
2122
|
-
/* @__PURE__ */ jsx23("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
|
|
2123
|
-
] });
|
|
2124
|
-
}
|
|
2125
|
-
if (f.token === null) {
|
|
2126
|
-
return /* @__PURE__ */ jsxs17("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
|
|
2127
|
-
/* @__PURE__ */ jsx23("h1", { children: "Link inv\xE1lido" }),
|
|
2128
|
-
/* @__PURE__ */ jsx23("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
|
|
2129
|
-
/* @__PURE__ */ jsx23("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
|
|
2130
|
-
] });
|
|
2131
|
-
}
|
|
2132
|
-
return /* @__PURE__ */ jsxs17("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
|
|
2133
|
-
/* @__PURE__ */ jsx23("h1", { style: { marginBottom: 8 }, children: name }),
|
|
2134
|
-
/* @__PURE__ */ jsx23("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
|
|
2135
|
-
/* @__PURE__ */ jsxs17("form", { onSubmit: (e) => {
|
|
2136
|
-
e.preventDefault();
|
|
2137
|
-
void f.submit();
|
|
2138
|
-
}, children: [
|
|
2139
|
-
/* @__PURE__ */ jsxs17("label", { style: { display: "block", marginBottom: 12 }, children: [
|
|
2140
|
-
"Nova senha",
|
|
2141
|
-
/* @__PURE__ */ jsx23("input", { "data-testid": "reset-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
|
|
2142
|
-
f.passwordError && /* @__PURE__ */ jsx23("small", { style: { color: "#c00" }, children: f.passwordError })
|
|
2143
|
-
] }),
|
|
2144
|
-
/* @__PURE__ */ jsxs17("label", { style: { display: "block", marginBottom: 12 }, children: [
|
|
2145
|
-
"Confirmar senha",
|
|
2146
|
-
/* @__PURE__ */ jsx23("input", { "data-testid": "reset-confirm", type: "password", value: f.confirm, onChange: (e) => f.setConfirm(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
|
|
2147
|
-
f.confirmError && /* @__PURE__ */ jsx23("small", { style: { color: "#c00" }, children: f.confirmError })
|
|
2148
|
-
] }),
|
|
2149
|
-
f.error && /* @__PURE__ */ jsx23("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
|
|
2150
|
-
/* @__PURE__ */ jsx23("button", { "data-testid": "reset-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Alterando..." : "Alterar senha" })
|
|
2151
|
-
] })
|
|
2152
|
-
] });
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
|
-
// src/defaults/DefaultPaywall.tsx
|
|
2156
|
-
import { useMemo as useMemo7, useState as useState11 } from "react";
|
|
2157
|
-
|
|
2158
|
-
// src/hooks/usePlan.ts
|
|
2159
|
-
import { useHook as useHook8 } from "@hook-sdk/sdk";
|
|
2160
|
-
function usePlan() {
|
|
2161
|
-
const { plan } = useHook8();
|
|
2162
|
-
return plan;
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
|
-
// src/utils/price.ts
|
|
2166
|
-
function formatBRL(cents) {
|
|
2167
|
-
if (cents === null || cents === void 0) return "";
|
|
2168
|
-
const reais = cents / 100;
|
|
2169
|
-
return new Intl.NumberFormat("pt-BR", {
|
|
2170
|
-
style: "currency",
|
|
2171
|
-
currency: "BRL"
|
|
2172
|
-
}).format(reais);
|
|
2173
|
-
}
|
|
2174
|
-
function monthlyFromYearly(yearlyCents) {
|
|
2175
|
-
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
2176
|
-
return Math.round(yearlyCents / 12);
|
|
2177
|
-
}
|
|
2178
|
-
function dailyFromYearly(yearlyCents) {
|
|
2179
|
-
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
2180
|
-
return Math.round(yearlyCents / 365);
|
|
2181
|
-
}
|
|
2182
|
-
function computeAnchorCents(baseCents, multiplier) {
|
|
2183
|
-
if (multiplier === null || multiplier === void 0) return null;
|
|
2184
|
-
if (!Number.isFinite(multiplier)) return null;
|
|
2185
|
-
if (multiplier <= 1) return null;
|
|
2186
|
-
return Math.round(baseCents * multiplier);
|
|
2187
|
-
}
|
|
2188
|
-
function discountPercent(anchorCents, realCents) {
|
|
2189
|
-
if (anchorCents <= realCents) return 0;
|
|
2190
|
-
return Math.floor((anchorCents - realCents) / anchorCents * 100);
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2193
|
-
// src/defaults/DefaultPaywall.tsx
|
|
2194
|
-
import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2195
|
-
function DefaultPaywall() {
|
|
2196
|
-
const config = useTemplateConfig();
|
|
2197
|
-
const plan = usePlan();
|
|
2198
|
-
const {
|
|
2199
|
-
checkout,
|
|
2200
|
-
opening,
|
|
2201
|
-
error,
|
|
2202
|
-
availableMethods,
|
|
2203
|
-
monthlyEquivalent,
|
|
2204
|
-
pixPending,
|
|
2205
|
-
dismissPix
|
|
2206
|
-
} = usePaywallState();
|
|
2207
|
-
const p = config.subscription.paywall_config;
|
|
2208
|
-
const paywallCfg = plan.data?.paywallConfig ?? {};
|
|
2209
|
-
const anchorMultiplier = paywallCfg.anchorMultiplier ?? p.anchorMultiplier;
|
|
2210
|
-
const anchorPriceCents = paywallCfg.anchorPriceCents ?? p.anchorPriceCents;
|
|
2211
|
-
const [cycle, setCycle] = useState11("MONTHLY");
|
|
2212
|
-
const [method, setMethod] = useState11("card");
|
|
2213
|
-
const [cpf, setCpf] = useState11("");
|
|
2214
|
-
const [cardNumber, setCardNumber] = useState11("");
|
|
2215
|
-
const [cardHolderName, setCardHolderName] = useState11("");
|
|
2216
|
-
const [cardExpiryMonth, setCardExpiryMonth] = useState11("");
|
|
2217
|
-
const [cardExpiryYear, setCardExpiryYear] = useState11("");
|
|
2218
|
-
const [cardCcv, setCardCcv] = useState11("");
|
|
2219
|
-
const [holderName, setHolderName] = useState11("");
|
|
2220
|
-
const [holderEmail, setHolderEmail] = useState11("");
|
|
2221
|
-
const [holderPostalCode, setHolderPostalCode] = useState11("");
|
|
2222
|
-
const [holderAddressNumber, setHolderAddressNumber] = useState11("");
|
|
2223
|
-
const [holderPhone, setHolderPhone] = useState11("");
|
|
2224
|
-
const trialDays = plan.data?.trialDays ?? 0;
|
|
2225
|
-
const monthlyCents = plan.data?.priceCents ?? 0;
|
|
2226
|
-
const yearlyCents = plan.data?.yearlyPriceCents ?? null;
|
|
2227
|
-
const activeCents = cycle === "YEARLY" && yearlyCents ? monthlyEquivalent("YEARLY") : monthlyCents;
|
|
2228
|
-
const cycleValueCents = cycle === "YEARLY" && yearlyCents ? yearlyCents : monthlyCents;
|
|
2229
|
-
const cycleLabel = cycle === "YEARLY" ? "por ano" : "por m\xEAs";
|
|
2230
|
-
const pct = useMemo7(() => {
|
|
2231
|
-
if (!yearlyCents || !monthlyCents) return 0;
|
|
2232
|
-
const derived = Math.round((1 - yearlyCents / 12 / monthlyCents) * 100);
|
|
2233
|
-
return Math.max(0, derived);
|
|
2234
|
-
}, [monthlyCents, yearlyCents]);
|
|
2235
|
-
const anchorBaseCents = cycle === "YEARLY" && yearlyCents ? monthlyFromYearly(yearlyCents) : monthlyCents;
|
|
2236
|
-
const anchorCents = computeAnchorCents(anchorBaseCents, anchorMultiplier) ?? (anchorPriceCents && anchorPriceCents > anchorBaseCents ? anchorPriceCents : null);
|
|
2237
|
-
const anchorDiscount = anchorCents ? discountPercent(anchorCents, activeCents) : 0;
|
|
2238
|
-
const cpfDigits = cpf.replace(/\D/g, "");
|
|
2239
|
-
const cardFieldsFilled = cardNumber.replace(/\s/g, "").length >= 13 && cardHolderName.trim().length > 0 && cardExpiryMonth.length === 2 && cardExpiryYear.length >= 2 && cardCcv.length >= 3 && holderName.trim().length > 0 && /.+@.+\..+/.test(holderEmail) && holderPostalCode.replace(/\D/g, "").length === 8 && holderAddressNumber.trim().length > 0;
|
|
2240
|
-
const canCheckout = cpfDigits.length === 11 && !opening && (method === "pix-auto" || cardFieldsFilled);
|
|
2241
|
-
const ctaLabel = useMemo7(() => {
|
|
2242
|
-
if (opening) return "Abrindo\u2026";
|
|
2243
|
-
if (trialDays > 0) return `Comece trial de ${trialDays} dias gr\xE1tis`;
|
|
2244
|
-
return p.cta ?? "Assinar agora";
|
|
2245
|
-
}, [opening, trialDays, p.cta]);
|
|
2246
|
-
const footer = useMemo7(() => {
|
|
2247
|
-
if (trialDays > 0) {
|
|
2248
|
-
return `Sem cobran\xE7a agora. Cobran\xE7a autom\xE1tica em ${trialDays} dias. Cancele quando quiser.`;
|
|
2249
|
-
}
|
|
2250
|
-
return "Cobran\xE7a imediata. Cancele quando quiser.";
|
|
2251
|
-
}, [trialDays]);
|
|
2252
|
-
const yearSuffix = cardExpiryYear.length === 2 ? `20${cardExpiryYear}` : cardExpiryYear;
|
|
2253
|
-
const phoneDigits = holderPhone.replace(/\D/g, "");
|
|
2254
|
-
const postalDigits = holderPostalCode.replace(/\D/g, "");
|
|
2255
|
-
const submit = () => {
|
|
2256
|
-
if (method === "card") {
|
|
2257
|
-
void checkout({
|
|
2258
|
-
cpf: cpfDigits,
|
|
2259
|
-
cycle,
|
|
2260
|
-
method: "card",
|
|
2261
|
-
card: {
|
|
2262
|
-
number: cardNumber.replace(/\s/g, ""),
|
|
2263
|
-
holderName: cardHolderName.trim(),
|
|
2264
|
-
expiryMonth: cardExpiryMonth,
|
|
2265
|
-
expiryYear: yearSuffix,
|
|
2266
|
-
ccv: cardCcv
|
|
2267
|
-
},
|
|
2268
|
-
holderInfo: {
|
|
2269
|
-
name: holderName.trim(),
|
|
2270
|
-
email: holderEmail.trim(),
|
|
2271
|
-
cpfCnpj: cpfDigits,
|
|
2272
|
-
postalCode: postalDigits,
|
|
2273
|
-
addressNumber: holderAddressNumber.trim(),
|
|
2274
|
-
...phoneDigits ? { phone: phoneDigits } : {}
|
|
2275
|
-
}
|
|
2276
|
-
});
|
|
2277
|
-
return;
|
|
2278
|
-
}
|
|
2279
|
-
void checkout({ cpf: cpfDigits, cycle, method: "pix-auto" });
|
|
2280
|
-
};
|
|
2281
|
-
const inputStyle = {
|
|
2282
|
-
width: "100%",
|
|
2283
|
-
padding: 10,
|
|
2284
|
-
fontSize: 14,
|
|
2285
|
-
borderRadius: 8,
|
|
2286
|
-
border: "1px solid #ccc",
|
|
2287
|
-
boxSizing: "border-box"
|
|
2288
|
-
};
|
|
2289
|
-
const labelStyle = { display: "block", fontSize: 13, opacity: 0.75, marginBottom: 4 };
|
|
2290
|
-
const fieldGroup = { marginBottom: 10, textAlign: "left" };
|
|
2291
|
-
return /* @__PURE__ */ jsxs18("main", { style: { padding: 24, maxWidth: 480, margin: "0 auto", textAlign: "center" }, children: [
|
|
2292
|
-
/* @__PURE__ */ jsx24("h1", { style: { marginBottom: 8 }, children: p.title }),
|
|
2293
|
-
p.subtitle && /* @__PURE__ */ jsx24("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
|
|
2294
|
-
/* @__PURE__ */ jsxs18(
|
|
2295
|
-
"div",
|
|
2296
|
-
{
|
|
2297
|
-
role: "group",
|
|
2298
|
-
"aria-label": "Per\xEDodo de cobran\xE7a",
|
|
2299
|
-
style: { display: "flex", gap: 8, marginBottom: 16 },
|
|
2300
|
-
children: [
|
|
2301
|
-
/* @__PURE__ */ jsx24(
|
|
2302
|
-
"button",
|
|
2303
|
-
{
|
|
2304
|
-
type: "button",
|
|
2305
|
-
"aria-pressed": cycle === "MONTHLY",
|
|
2306
|
-
onClick: () => setCycle("MONTHLY"),
|
|
2307
|
-
style: {
|
|
2308
|
-
flex: 1,
|
|
2309
|
-
padding: 12,
|
|
2310
|
-
borderRadius: 8,
|
|
2311
|
-
border: cycle === "MONTHLY" ? "2px solid var(--hook-color-primary)" : "1px solid #ccc",
|
|
2312
|
-
background: cycle === "MONTHLY" ? "var(--hook-color-primary-soft, #eef)" : "white",
|
|
2313
|
-
cursor: "pointer"
|
|
2314
|
-
},
|
|
2315
|
-
children: "Mensal"
|
|
2316
|
-
}
|
|
2317
|
-
),
|
|
2318
|
-
/* @__PURE__ */ jsxs18(
|
|
2319
|
-
"button",
|
|
2320
|
-
{
|
|
2321
|
-
type: "button",
|
|
2322
|
-
"aria-pressed": cycle === "YEARLY",
|
|
2323
|
-
onClick: () => setCycle("YEARLY"),
|
|
2324
|
-
disabled: !yearlyCents,
|
|
2325
|
-
style: {
|
|
2326
|
-
flex: 1,
|
|
2327
|
-
padding: 12,
|
|
2328
|
-
borderRadius: 8,
|
|
2329
|
-
border: cycle === "YEARLY" ? "2px solid var(--hook-color-primary)" : "1px solid #ccc",
|
|
2330
|
-
background: cycle === "YEARLY" ? "var(--hook-color-primary-soft, #eef)" : "white",
|
|
2331
|
-
cursor: yearlyCents ? "pointer" : "not-allowed",
|
|
2332
|
-
opacity: yearlyCents ? 1 : 0.5
|
|
2333
|
-
},
|
|
2334
|
-
children: [
|
|
2335
|
-
"Anual",
|
|
2336
|
-
pct > 0 ? ` \u2212${pct}%` : ""
|
|
2337
|
-
]
|
|
2338
|
-
}
|
|
2339
|
-
)
|
|
2340
|
-
]
|
|
2341
|
-
}
|
|
2342
|
-
),
|
|
2343
|
-
/* @__PURE__ */ jsxs18("div", { style: { marginBottom: 8 }, children: [
|
|
2344
|
-
/* @__PURE__ */ jsxs18("div", { style: { fontSize: 32, fontWeight: 700, lineHeight: 1 }, children: [
|
|
2345
|
-
formatBRL(activeCents),
|
|
2346
|
-
/* @__PURE__ */ jsx24("span", { style: { fontSize: 16, fontWeight: 400, opacity: 0.7 }, children: "/m\xEAs" })
|
|
2347
|
-
] }),
|
|
2348
|
-
anchorCents && /* @__PURE__ */ jsxs18("div", { style: { fontSize: 14, opacity: 0.6, marginTop: 4 }, children: [
|
|
2349
|
-
/* @__PURE__ */ jsx24("span", { style: { textDecoration: "line-through" }, children: formatBRL(anchorCents) }),
|
|
2350
|
-
anchorDiscount > 0 && /* @__PURE__ */ jsxs18("span", { style: { marginLeft: 6 }, children: [
|
|
2351
|
-
"\u2212",
|
|
2352
|
-
anchorDiscount,
|
|
2353
|
-
"%"
|
|
2354
|
-
] })
|
|
2355
|
-
] }),
|
|
2356
|
-
cycle === "YEARLY" && yearlyCents && /* @__PURE__ */ jsxs18("div", { style: { fontSize: 12, opacity: 0.6, marginTop: 4 }, children: [
|
|
2357
|
-
"Cobrado ",
|
|
2358
|
-
formatBRL(yearlyCents),
|
|
2359
|
-
" por ano"
|
|
2360
|
-
] })
|
|
2361
|
-
] }),
|
|
2362
|
-
/* @__PURE__ */ jsx24("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", margin: "24px 0" }, children: p.benefits.map((b) => /* @__PURE__ */ jsxs18("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
|
|
2363
|
-
/* @__PURE__ */ jsx24("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
|
|
2364
|
-
/* @__PURE__ */ jsx24("span", { children: b })
|
|
2365
|
-
] }, b)) }),
|
|
2366
|
-
/* @__PURE__ */ jsx24("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", style: { display: "flex", gap: 8, marginBottom: 16 }, children: availableMethods.map((m) => /* @__PURE__ */ jsx24(
|
|
2367
|
-
"button",
|
|
2368
|
-
{
|
|
2369
|
-
type: "button",
|
|
2370
|
-
role: "tab",
|
|
2371
|
-
"aria-selected": method === m,
|
|
2372
|
-
onClick: () => setMethod(m),
|
|
2373
|
-
style: {
|
|
2374
|
-
flex: 1,
|
|
2375
|
-
padding: 10,
|
|
2376
|
-
borderRadius: 8,
|
|
2377
|
-
border: method === m ? "2px solid var(--hook-color-primary)" : "1px solid #ccc",
|
|
2378
|
-
background: method === m ? "var(--hook-color-primary-soft, #eef)" : "white",
|
|
2379
|
-
cursor: "pointer"
|
|
2380
|
-
},
|
|
2381
|
-
children: m === "card" ? "\u{1F4B3} Cart\xE3o" : "\u{1F4F1} Pix Autom\xE1tico"
|
|
2382
|
-
},
|
|
2383
|
-
m
|
|
2384
|
-
)) }),
|
|
2385
|
-
method === "card" && /* @__PURE__ */ jsxs18(
|
|
2386
|
-
"fieldset",
|
|
2387
|
-
{
|
|
2388
|
-
"data-testid": "paywall-card-form",
|
|
2389
|
-
style: { border: "none", padding: 0, marginBottom: 16 },
|
|
2390
|
-
children: [
|
|
2391
|
-
/* @__PURE__ */ jsx24("legend", { style: { fontSize: 13, opacity: 0.75, marginBottom: 8 }, children: "Dados do cart\xE3o" }),
|
|
2392
|
-
/* @__PURE__ */ jsxs18("div", { style: fieldGroup, children: [
|
|
2393
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-card-number", style: labelStyle, children: "N\xFAmero do cart\xE3o" }),
|
|
2394
|
-
/* @__PURE__ */ jsx24(
|
|
2395
|
-
"input",
|
|
2396
|
-
{
|
|
2397
|
-
id: "pw-card-number",
|
|
2398
|
-
"data-testid": "pw-card-number",
|
|
2399
|
-
type: "text",
|
|
2400
|
-
inputMode: "numeric",
|
|
2401
|
-
autoComplete: "cc-number",
|
|
2402
|
-
placeholder: "0000 0000 0000 0000",
|
|
2403
|
-
value: cardNumber,
|
|
2404
|
-
onChange: (e) => setCardNumber(e.target.value),
|
|
2405
|
-
style: inputStyle
|
|
2406
|
-
}
|
|
2407
|
-
)
|
|
2408
|
-
] }),
|
|
2409
|
-
/* @__PURE__ */ jsxs18("div", { style: fieldGroup, children: [
|
|
2410
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-card-holder", style: labelStyle, children: "Nome impresso no cart\xE3o" }),
|
|
2411
|
-
/* @__PURE__ */ jsx24(
|
|
2412
|
-
"input",
|
|
2413
|
-
{
|
|
2414
|
-
id: "pw-card-holder",
|
|
2415
|
-
"data-testid": "pw-card-holder",
|
|
2416
|
-
type: "text",
|
|
2417
|
-
autoComplete: "cc-name",
|
|
2418
|
-
placeholder: "NOME SOBRENOME",
|
|
2419
|
-
value: cardHolderName,
|
|
2420
|
-
onChange: (e) => setCardHolderName(e.target.value),
|
|
2421
|
-
style: inputStyle
|
|
2422
|
-
}
|
|
2423
|
-
)
|
|
2424
|
-
] }),
|
|
2425
|
-
/* @__PURE__ */ jsxs18("div", { style: { display: "flex", gap: 8, marginBottom: 10 }, children: [
|
|
2426
|
-
/* @__PURE__ */ jsxs18("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2427
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-card-exp-m", style: labelStyle, children: "M\xEAs" }),
|
|
2428
|
-
/* @__PURE__ */ jsx24(
|
|
2429
|
-
"input",
|
|
2430
|
-
{
|
|
2431
|
-
id: "pw-card-exp-m",
|
|
2432
|
-
"data-testid": "pw-card-exp-m",
|
|
2433
|
-
type: "text",
|
|
2434
|
-
inputMode: "numeric",
|
|
2435
|
-
autoComplete: "cc-exp-month",
|
|
2436
|
-
placeholder: "MM",
|
|
2437
|
-
maxLength: 2,
|
|
2438
|
-
value: cardExpiryMonth,
|
|
2439
|
-
onChange: (e) => setCardExpiryMonth(e.target.value.replace(/\D/g, "")),
|
|
2440
|
-
style: inputStyle
|
|
2441
|
-
}
|
|
2442
|
-
)
|
|
2443
|
-
] }),
|
|
2444
|
-
/* @__PURE__ */ jsxs18("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2445
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-card-exp-y", style: labelStyle, children: "Ano" }),
|
|
2446
|
-
/* @__PURE__ */ jsx24(
|
|
2447
|
-
"input",
|
|
2448
|
-
{
|
|
2449
|
-
id: "pw-card-exp-y",
|
|
2450
|
-
"data-testid": "pw-card-exp-y",
|
|
2451
|
-
type: "text",
|
|
2452
|
-
inputMode: "numeric",
|
|
2453
|
-
autoComplete: "cc-exp-year",
|
|
2454
|
-
placeholder: "AA",
|
|
2455
|
-
maxLength: 4,
|
|
2456
|
-
value: cardExpiryYear,
|
|
2457
|
-
onChange: (e) => setCardExpiryYear(e.target.value.replace(/\D/g, "")),
|
|
2458
|
-
style: inputStyle
|
|
2459
|
-
}
|
|
2460
|
-
)
|
|
2461
|
-
] }),
|
|
2462
|
-
/* @__PURE__ */ jsxs18("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2463
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-card-cvv", style: labelStyle, children: "CVV" }),
|
|
2464
|
-
/* @__PURE__ */ jsx24(
|
|
2465
|
-
"input",
|
|
2466
|
-
{
|
|
2467
|
-
id: "pw-card-cvv",
|
|
2468
|
-
"data-testid": "pw-card-cvv",
|
|
2469
|
-
type: "text",
|
|
2470
|
-
inputMode: "numeric",
|
|
2471
|
-
autoComplete: "cc-csc",
|
|
2472
|
-
placeholder: "123",
|
|
2473
|
-
maxLength: 4,
|
|
2474
|
-
value: cardCcv,
|
|
2475
|
-
onChange: (e) => setCardCcv(e.target.value.replace(/\D/g, "")),
|
|
2476
|
-
style: inputStyle
|
|
2477
|
-
}
|
|
2478
|
-
)
|
|
2479
|
-
] })
|
|
2480
|
-
] }),
|
|
2481
|
-
/* @__PURE__ */ jsx24("legend", { style: { fontSize: 13, opacity: 0.75, marginBottom: 8, marginTop: 8 }, children: "Dados do titular" }),
|
|
2482
|
-
/* @__PURE__ */ jsxs18("div", { style: fieldGroup, children: [
|
|
2483
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-holder-name", style: labelStyle, children: "Nome completo" }),
|
|
2484
|
-
/* @__PURE__ */ jsx24(
|
|
2485
|
-
"input",
|
|
2486
|
-
{
|
|
2487
|
-
id: "pw-holder-name",
|
|
2488
|
-
"data-testid": "pw-holder-name",
|
|
2489
|
-
type: "text",
|
|
2490
|
-
autoComplete: "name",
|
|
2491
|
-
placeholder: "Nome Sobrenome",
|
|
2492
|
-
value: holderName,
|
|
2493
|
-
onChange: (e) => setHolderName(e.target.value),
|
|
2494
|
-
style: inputStyle
|
|
2495
|
-
}
|
|
2496
|
-
)
|
|
2497
|
-
] }),
|
|
2498
|
-
/* @__PURE__ */ jsxs18("div", { style: fieldGroup, children: [
|
|
2499
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-holder-email", style: labelStyle, children: "E-mail" }),
|
|
2500
|
-
/* @__PURE__ */ jsx24(
|
|
2501
|
-
"input",
|
|
2502
|
-
{
|
|
2503
|
-
id: "pw-holder-email",
|
|
2504
|
-
"data-testid": "pw-holder-email",
|
|
2505
|
-
type: "email",
|
|
2506
|
-
autoComplete: "email",
|
|
2507
|
-
placeholder: "voce@email.com",
|
|
2508
|
-
value: holderEmail,
|
|
2509
|
-
onChange: (e) => setHolderEmail(e.target.value),
|
|
2510
|
-
style: inputStyle
|
|
2511
|
-
}
|
|
2512
|
-
)
|
|
2513
|
-
] }),
|
|
2514
|
-
/* @__PURE__ */ jsxs18("div", { style: { display: "flex", gap: 8, marginBottom: 10 }, children: [
|
|
2515
|
-
/* @__PURE__ */ jsxs18("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2516
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-holder-cep", style: labelStyle, children: "CEP" }),
|
|
2517
|
-
/* @__PURE__ */ jsx24(
|
|
2518
|
-
"input",
|
|
2519
|
-
{
|
|
2520
|
-
id: "pw-holder-cep",
|
|
2521
|
-
"data-testid": "pw-holder-cep",
|
|
2522
|
-
type: "text",
|
|
2523
|
-
inputMode: "numeric",
|
|
2524
|
-
autoComplete: "postal-code",
|
|
2525
|
-
placeholder: "00000-000",
|
|
2526
|
-
value: holderPostalCode,
|
|
2527
|
-
onChange: (e) => setHolderPostalCode(e.target.value),
|
|
2528
|
-
style: inputStyle
|
|
2529
|
-
}
|
|
2530
|
-
)
|
|
2531
|
-
] }),
|
|
2532
|
-
/* @__PURE__ */ jsxs18("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2533
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-holder-addr-n", style: labelStyle, children: "N\xFAmero" }),
|
|
2534
|
-
/* @__PURE__ */ jsx24(
|
|
2535
|
-
"input",
|
|
2536
|
-
{
|
|
2537
|
-
id: "pw-holder-addr-n",
|
|
2538
|
-
"data-testid": "pw-holder-addr-n",
|
|
2539
|
-
type: "text",
|
|
2540
|
-
inputMode: "numeric",
|
|
2541
|
-
placeholder: "123",
|
|
2542
|
-
value: holderAddressNumber,
|
|
2543
|
-
onChange: (e) => setHolderAddressNumber(e.target.value),
|
|
2544
|
-
style: inputStyle
|
|
2545
|
-
}
|
|
2546
|
-
)
|
|
2547
|
-
] })
|
|
2548
|
-
] }),
|
|
2549
|
-
/* @__PURE__ */ jsxs18("div", { style: fieldGroup, children: [
|
|
2550
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-holder-phone", style: labelStyle, children: "Telefone (opcional)" }),
|
|
2551
|
-
/* @__PURE__ */ jsx24(
|
|
2552
|
-
"input",
|
|
2553
|
-
{
|
|
2554
|
-
id: "pw-holder-phone",
|
|
2555
|
-
"data-testid": "pw-holder-phone",
|
|
2556
|
-
type: "tel",
|
|
2557
|
-
inputMode: "tel",
|
|
2558
|
-
autoComplete: "tel",
|
|
2559
|
-
placeholder: "(11) 99999-9999",
|
|
2560
|
-
value: holderPhone,
|
|
2561
|
-
onChange: (e) => setHolderPhone(e.target.value),
|
|
2562
|
-
style: inputStyle
|
|
2563
|
-
}
|
|
2564
|
-
)
|
|
2565
|
-
] })
|
|
2566
|
-
]
|
|
2567
|
-
}
|
|
2568
|
-
),
|
|
2569
|
-
/* @__PURE__ */ jsxs18("div", { style: fieldGroup, children: [
|
|
2570
|
-
/* @__PURE__ */ jsx24("label", { htmlFor: "pw-cpf", style: labelStyle, children: "Seu CPF" }),
|
|
2571
|
-
/* @__PURE__ */ jsx24(
|
|
2572
|
-
"input",
|
|
2573
|
-
{
|
|
2574
|
-
id: "pw-cpf",
|
|
2575
|
-
"data-testid": "paywall-cpf",
|
|
2576
|
-
type: "text",
|
|
2577
|
-
inputMode: "numeric",
|
|
2578
|
-
placeholder: "000.000.000-00",
|
|
2579
|
-
value: cpf,
|
|
2580
|
-
onChange: (e) => setCpf(e.target.value),
|
|
2581
|
-
style: inputStyle
|
|
2582
|
-
}
|
|
2583
|
-
)
|
|
2584
|
-
] }),
|
|
2585
|
-
error && /* @__PURE__ */ jsx24("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
|
|
2586
|
-
method === "pix-auto" && /* @__PURE__ */ jsxs18(
|
|
2587
|
-
"div",
|
|
2588
|
-
{
|
|
2589
|
-
"data-testid": "paywall-pix-callout",
|
|
2590
|
-
style: {
|
|
2591
|
-
background: "rgba(0,0,0,0.04)",
|
|
2592
|
-
borderLeft: "3px solid var(--hook-color-primary)",
|
|
2593
|
-
padding: "10px 12px",
|
|
2594
|
-
marginBottom: 12,
|
|
2595
|
-
borderRadius: 4,
|
|
2596
|
-
fontSize: 12,
|
|
2597
|
-
lineHeight: 1.45,
|
|
2598
|
-
textAlign: "left"
|
|
2599
|
-
},
|
|
2600
|
-
children: [
|
|
2601
|
-
/* @__PURE__ */ jsx24("strong", { children: "Como funciona:" }),
|
|
2602
|
-
" no app do seu banco vai aparecer uma cobran\xE7a de",
|
|
2603
|
-
" ",
|
|
2604
|
-
/* @__PURE__ */ jsx24("strong", { children: "R$ 0,01" }),
|
|
2605
|
-
" \u2014 \xE9 simb\xF3lica, s\xF3 pra ativar o PIX Autom\xE1tico.",
|
|
2606
|
-
trialDays > 0 ? /* @__PURE__ */ jsxs18(Fragment6, { children: [
|
|
2607
|
-
" ",
|
|
2608
|
-
"Depois, ",
|
|
2609
|
-
/* @__PURE__ */ jsxs18("strong", { children: [
|
|
2610
|
-
trialDays,
|
|
2611
|
-
" dias gr\xE1tis"
|
|
2612
|
-
] }),
|
|
2613
|
-
"; a cobran\xE7a de",
|
|
2614
|
-
" ",
|
|
2615
|
-
/* @__PURE__ */ jsx24("strong", { children: formatBRL(cycleValueCents) }),
|
|
2616
|
-
" ",
|
|
2617
|
-
cycleLabel,
|
|
2618
|
-
" s\xF3 come\xE7a depois."
|
|
2619
|
-
] }) : /* @__PURE__ */ jsxs18(Fragment6, { children: [
|
|
2620
|
-
" ",
|
|
2621
|
-
"A cobran\xE7a de ",
|
|
2622
|
-
/* @__PURE__ */ jsx24("strong", { children: formatBRL(cycleValueCents) }),
|
|
2623
|
-
" ",
|
|
2624
|
-
cycleLabel,
|
|
2625
|
-
" vem em seguida."
|
|
2626
|
-
] })
|
|
2627
|
-
]
|
|
2628
|
-
}
|
|
2629
|
-
),
|
|
2630
|
-
/* @__PURE__ */ jsx24(
|
|
2631
|
-
"button",
|
|
2632
|
-
{
|
|
2633
|
-
"data-testid": "paywall-cta",
|
|
2634
|
-
type: "button",
|
|
2635
|
-
onClick: submit,
|
|
2636
|
-
disabled: !canCheckout,
|
|
2637
|
-
style: {
|
|
2638
|
-
width: "100%",
|
|
2639
|
-
padding: 14,
|
|
2640
|
-
background: "var(--hook-color-primary)",
|
|
2641
|
-
color: "#fff",
|
|
2642
|
-
border: "none",
|
|
2643
|
-
borderRadius: 8,
|
|
2644
|
-
opacity: canCheckout ? 1 : 0.5,
|
|
2645
|
-
fontSize: 16,
|
|
2646
|
-
fontWeight: 600,
|
|
2647
|
-
cursor: canCheckout ? "pointer" : "not-allowed"
|
|
2648
|
-
},
|
|
2649
|
-
children: ctaLabel
|
|
2650
|
-
}
|
|
2651
|
-
),
|
|
2652
|
-
/* @__PURE__ */ jsx24("p", { style: { opacity: 0.55, marginTop: 16, fontSize: 12 }, children: footer }),
|
|
2653
|
-
p.priceHint && /* @__PURE__ */ jsx24("p", { style: { opacity: 0.6, marginTop: 8, fontSize: 12 }, children: p.priceHint }),
|
|
2654
|
-
p.footerNote && /* @__PURE__ */ jsx24("p", { style: { opacity: 0.5, marginTop: 8, fontSize: 12 }, children: p.footerNote }),
|
|
2655
|
-
pixPending && /* @__PURE__ */ jsx24(
|
|
2656
|
-
"div",
|
|
2657
|
-
{
|
|
2658
|
-
"data-testid": "paywall-pix-modal",
|
|
2659
|
-
role: "dialog",
|
|
2660
|
-
"aria-label": "Pagamento Pix pendente",
|
|
2661
|
-
style: {
|
|
2662
|
-
position: "fixed",
|
|
2663
|
-
inset: 0,
|
|
2664
|
-
background: "rgba(0,0,0,0.6)",
|
|
2665
|
-
display: "flex",
|
|
2666
|
-
alignItems: "center",
|
|
2667
|
-
justifyContent: "center",
|
|
2668
|
-
padding: 24,
|
|
2669
|
-
zIndex: 1e3
|
|
2670
|
-
},
|
|
2671
|
-
children: /* @__PURE__ */ jsxs18("div", { style: {
|
|
2672
|
-
background: "#fff",
|
|
2673
|
-
borderRadius: 12,
|
|
2674
|
-
padding: 24,
|
|
2675
|
-
maxWidth: 360,
|
|
2676
|
-
width: "100%",
|
|
2677
|
-
textAlign: "center"
|
|
2678
|
-
}, children: [
|
|
2679
|
-
pixPending.paid ? /* @__PURE__ */ jsxs18(Fragment6, { children: [
|
|
2680
|
-
/* @__PURE__ */ jsx24("h2", { style: { marginTop: 0 }, children: "Pagamento confirmado!" }),
|
|
2681
|
-
/* @__PURE__ */ jsx24("p", { style: { opacity: 0.7 }, children: "Liberando acesso\u2026" })
|
|
2682
|
-
] }) : /* @__PURE__ */ jsxs18(Fragment6, { children: [
|
|
2683
|
-
/* @__PURE__ */ jsx24("h2", { style: { marginTop: 0, fontSize: 18 }, children: "Pague com Pix Autom\xE1tico" }),
|
|
2684
|
-
/* @__PURE__ */ jsx24("p", { style: { fontSize: 13, opacity: 0.75 }, children: "Escaneie o QR Code no app do seu banco pra autorizar o d\xE9bito recorrente." }),
|
|
2685
|
-
/* @__PURE__ */ jsxs18(
|
|
2686
|
-
"p",
|
|
2687
|
-
{
|
|
2688
|
-
"data-testid": "pix-modal-explain",
|
|
2689
|
-
style: { fontSize: 12, opacity: 0.65, marginTop: 8, lineHeight: 1.4 },
|
|
2690
|
-
children: [
|
|
2691
|
-
"Seu banco vai mostrar uma cobran\xE7a de ",
|
|
2692
|
-
/* @__PURE__ */ jsx24("strong", { children: "R$ 0,01" }),
|
|
2693
|
-
" \u2014 \xE9 simb\xF3lica, s\xF3 pra ativar o PIX Autom\xE1tico.",
|
|
2694
|
-
trialDays > 0 ? /* @__PURE__ */ jsxs18(Fragment6, { children: [
|
|
2695
|
-
" ",
|
|
2696
|
-
"A cobran\xE7a de ",
|
|
2697
|
-
/* @__PURE__ */ jsx24("strong", { children: formatBRL(cycleValueCents) }),
|
|
2698
|
-
" ",
|
|
2699
|
-
cycleLabel,
|
|
2700
|
-
" come\xE7a depois dos ",
|
|
2701
|
-
/* @__PURE__ */ jsxs18("strong", { children: [
|
|
2702
|
-
trialDays,
|
|
2703
|
-
" dias gr\xE1tis"
|
|
2704
|
-
] }),
|
|
2705
|
-
", automaticamente."
|
|
2706
|
-
] }) : /* @__PURE__ */ jsxs18(Fragment6, { children: [
|
|
2707
|
-
" ",
|
|
2708
|
-
"A cobran\xE7a de ",
|
|
2709
|
-
/* @__PURE__ */ jsx24("strong", { children: formatBRL(cycleValueCents) }),
|
|
2710
|
-
" ",
|
|
2711
|
-
cycleLabel,
|
|
2712
|
-
" vem logo em seguida."
|
|
2713
|
-
] })
|
|
2714
|
-
]
|
|
2715
|
-
}
|
|
2716
|
-
),
|
|
2717
|
-
pixPending.qrCodeBase64 && /* @__PURE__ */ jsx24(
|
|
2718
|
-
"img",
|
|
2719
|
-
{
|
|
2720
|
-
"data-testid": "pix-qr-image",
|
|
2721
|
-
alt: "QR Code Pix",
|
|
2722
|
-
src: `data:image/png;base64,${pixPending.qrCodeBase64}`,
|
|
2723
|
-
style: { width: "100%", maxWidth: 240, margin: "12px auto", display: "block" }
|
|
2724
|
-
}
|
|
2725
|
-
),
|
|
2726
|
-
pixPending.qrCodePayload && /* @__PURE__ */ jsx24(
|
|
2727
|
-
"textarea",
|
|
2728
|
-
{
|
|
2729
|
-
"data-testid": "pix-qr-payload",
|
|
2730
|
-
readOnly: true,
|
|
2731
|
-
value: pixPending.qrCodePayload,
|
|
2732
|
-
style: {
|
|
2733
|
-
width: "100%",
|
|
2734
|
-
minHeight: 72,
|
|
2735
|
-
padding: 8,
|
|
2736
|
-
fontSize: 11,
|
|
2737
|
-
fontFamily: "monospace",
|
|
2738
|
-
borderRadius: 6,
|
|
2739
|
-
border: "1px solid #ccc",
|
|
2740
|
-
resize: "none"
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
),
|
|
2744
|
-
/* @__PURE__ */ jsx24("p", { style: { fontSize: 11, opacity: 0.55, marginTop: 12 }, children: "Aguardando confirma\xE7\xE3o do banco\u2026 Pode levar alguns segundos." })
|
|
2745
|
-
] }),
|
|
2746
|
-
/* @__PURE__ */ jsx24(
|
|
2747
|
-
"button",
|
|
2748
|
-
{
|
|
2749
|
-
type: "button",
|
|
2750
|
-
onClick: dismissPix,
|
|
2751
|
-
style: {
|
|
2752
|
-
marginTop: 16,
|
|
2753
|
-
padding: "8px 16px",
|
|
2754
|
-
border: "1px solid #ccc",
|
|
2755
|
-
borderRadius: 6,
|
|
2756
|
-
background: "white",
|
|
2757
|
-
cursor: "pointer"
|
|
2758
|
-
},
|
|
2759
|
-
children: "Fechar"
|
|
2760
|
-
}
|
|
2761
|
-
)
|
|
2762
|
-
] })
|
|
2763
|
-
}
|
|
2764
|
-
)
|
|
2765
|
-
] });
|
|
2766
|
-
}
|
|
2767
|
-
|
|
2768
|
-
// src/AppRoot.tsx
|
|
2769
|
-
import { Fragment as Fragment7, jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2770
|
-
var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
|
|
2771
|
-
function PaymentReturnHandler({ children }) {
|
|
2772
|
-
const { subscription } = useHook9();
|
|
2773
|
-
const subRef = useRef3(subscription);
|
|
2774
|
-
subRef.current = subscription;
|
|
2775
|
-
const runIdRef = useRef3(0);
|
|
2776
|
-
const [state, setState] = useState12("idle");
|
|
2777
|
-
const runPoll = useCallback7(() => {
|
|
2778
|
-
const runId = ++runIdRef.current;
|
|
2779
|
-
setState("confirming");
|
|
2780
|
-
let attempts = 0;
|
|
2781
|
-
const tick = async () => {
|
|
2782
|
-
if (runIdRef.current !== runId) return;
|
|
2783
|
-
attempts++;
|
|
2784
|
-
try {
|
|
2785
|
-
await subRef.current.refresh();
|
|
2786
|
-
} catch {
|
|
2787
|
-
}
|
|
2788
|
-
if (runIdRef.current !== runId) return;
|
|
2789
|
-
const status = subRef.current.status();
|
|
2790
|
-
if (status === "active" || status === "trialing") {
|
|
2791
|
-
const cleanUrl = new URL(window.location.href);
|
|
2792
|
-
cleanUrl.searchParams.delete("paymentReturn");
|
|
2793
|
-
window.history.replaceState({}, "", cleanUrl.toString());
|
|
2794
|
-
setState("idle");
|
|
2795
|
-
return;
|
|
2796
|
-
}
|
|
2797
|
-
const delay = BACKOFF_MS[attempts - 1];
|
|
2798
|
-
if (delay === void 0) {
|
|
2799
|
-
setState("waiting");
|
|
2800
|
-
return;
|
|
2801
|
-
}
|
|
2802
|
-
setTimeout(tick, delay);
|
|
2803
|
-
};
|
|
2804
|
-
void tick();
|
|
2805
|
-
}, []);
|
|
2806
|
-
useEffect8(() => {
|
|
2807
|
-
if (typeof window === "undefined") return;
|
|
2808
|
-
const url = new URL(window.location.href);
|
|
2809
|
-
if (url.searchParams.get("paymentReturn") !== "1") return;
|
|
2810
|
-
runPoll();
|
|
2811
|
-
return () => {
|
|
2812
|
-
runIdRef.current++;
|
|
2813
|
-
};
|
|
2814
|
-
}, [runPoll]);
|
|
2815
|
-
if (state === "confirming") {
|
|
2816
|
-
return /* @__PURE__ */ jsx25(
|
|
2817
|
-
"div",
|
|
2818
|
-
{
|
|
2819
|
-
role: "status",
|
|
2820
|
-
"aria-live": "polite",
|
|
2821
|
-
style: overlayStyle2,
|
|
2822
|
-
children: "Confirmando pagamento\u2026"
|
|
2823
|
-
}
|
|
2824
|
-
);
|
|
2825
|
-
}
|
|
2826
|
-
if (state === "waiting") {
|
|
2827
|
-
return /* @__PURE__ */ jsx25("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs19("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
2828
|
-
/* @__PURE__ */ jsx25("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
|
|
2829
|
-
/* @__PURE__ */ jsx25(
|
|
2830
|
-
"button",
|
|
2831
|
-
{
|
|
2832
|
-
type: "button",
|
|
2833
|
-
onClick: runPoll,
|
|
2834
|
-
style: buttonStyle,
|
|
2835
|
-
children: "Atualizar"
|
|
2836
|
-
}
|
|
2837
|
-
)
|
|
2838
|
-
] }) });
|
|
2839
|
-
}
|
|
2840
|
-
return /* @__PURE__ */ jsx25(Fragment7, { children });
|
|
2841
|
-
}
|
|
2842
|
-
var overlayStyle2 = {
|
|
2843
|
-
position: "fixed",
|
|
2844
|
-
inset: 0,
|
|
2845
|
-
display: "flex",
|
|
2846
|
-
alignItems: "center",
|
|
2847
|
-
justifyContent: "center",
|
|
2848
|
-
background: "rgba(0,0,0,0.4)",
|
|
2849
|
-
zIndex: 9999,
|
|
2850
|
-
color: "white",
|
|
2851
|
-
fontSize: "1rem",
|
|
2852
|
-
padding: 24
|
|
2853
|
-
};
|
|
2854
|
-
var buttonStyle = {
|
|
2855
|
-
background: "white",
|
|
2856
|
-
color: "black",
|
|
2857
|
-
border: "none",
|
|
2858
|
-
borderRadius: 8,
|
|
2859
|
-
padding: "10px 24px",
|
|
2860
|
-
fontSize: "1rem",
|
|
2861
|
-
fontWeight: 600,
|
|
2862
|
-
cursor: "pointer"
|
|
2863
|
-
};
|
|
2864
|
-
function AppRoot({
|
|
2865
|
-
config,
|
|
2866
|
-
children,
|
|
2867
|
-
Login = DefaultLoginScreen,
|
|
2868
|
-
Signup = DefaultSignupScreen,
|
|
2869
|
-
Forgot = DefaultForgotScreen,
|
|
2870
|
-
Reset = DefaultResetScreen,
|
|
2871
|
-
Paywall = DefaultPaywall
|
|
2872
|
-
}) {
|
|
2873
|
-
return /* @__PURE__ */ jsx25(PaymentReturnHandler, { children: /* @__PURE__ */ jsx25(TemplateConfigProvider, { config, children: /* @__PURE__ */ jsx25(ErrorBoundary, { children: /* @__PURE__ */ jsx25(ThemeProvider, { children: /* @__PURE__ */ jsx25(InstallGate, { children: /* @__PURE__ */ jsx25(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ jsx25(PersistedKeysPrefetch, { children: /* @__PURE__ */ jsxs19(SubscriptionGate, { Paywall, children: [
|
|
2874
|
-
children,
|
|
2875
|
-
/* @__PURE__ */ jsx25(PushPrompt, {})
|
|
2876
|
-
] }) }) }) }) }) }) }) });
|
|
2228
|
+
return {
|
|
2229
|
+
name,
|
|
2230
|
+
setName,
|
|
2231
|
+
nameError,
|
|
2232
|
+
email,
|
|
2233
|
+
setEmail,
|
|
2234
|
+
emailError,
|
|
2235
|
+
password,
|
|
2236
|
+
setPassword,
|
|
2237
|
+
passwordError,
|
|
2238
|
+
submit,
|
|
2239
|
+
submitting,
|
|
2240
|
+
canSubmit,
|
|
2241
|
+
error,
|
|
2242
|
+
loginWithGoogle: () => auth.loginWithGoogle()
|
|
2243
|
+
};
|
|
2877
2244
|
}
|
|
2878
2245
|
|
|
2879
|
-
// src/hooks/
|
|
2880
|
-
import { useCallback as
|
|
2881
|
-
import { useHook as
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
const
|
|
2885
|
-
const
|
|
2886
|
-
|
|
2887
|
-
const
|
|
2888
|
-
const
|
|
2889
|
-
const
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
if (status === "denied") return { kind: "denied" };
|
|
2900
|
-
if (status === "unsupported") {
|
|
2901
|
-
if (detectIosNeedsInstall()) return { kind: "ios_needs_install" };
|
|
2902
|
-
return { kind: "unsupported" };
|
|
2903
|
-
}
|
|
2904
|
-
return { kind: "prompt" };
|
|
2905
|
-
}
|
|
2906
|
-
function usePush() {
|
|
2907
|
-
const { push } = useHook10();
|
|
2908
|
-
const [state, setState] = useState13(() => deriveState(push));
|
|
2909
|
-
useEffect9(() => {
|
|
2910
|
-
setState(deriveState(push));
|
|
2911
|
-
}, [push]);
|
|
2912
|
-
const subscribe = useCallback8(async () => {
|
|
2246
|
+
// src/hooks/useForgotForm.ts
|
|
2247
|
+
import { useCallback as useCallback7, useMemo as useMemo6, useState as useState8 } from "react";
|
|
2248
|
+
import { useHook as useHook8 } from "@hook-sdk/sdk";
|
|
2249
|
+
var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2250
|
+
function useForgotForm() {
|
|
2251
|
+
const { auth } = useHook8();
|
|
2252
|
+
const [email, setEmail] = useState8("");
|
|
2253
|
+
const [submitting, setSubmitting] = useState8(false);
|
|
2254
|
+
const [sent, setSent] = useState8(false);
|
|
2255
|
+
const [error, setError] = useState8(null);
|
|
2256
|
+
const emailError = useMemo6(() => {
|
|
2257
|
+
if (email.length === 0) return null;
|
|
2258
|
+
if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2259
|
+
return null;
|
|
2260
|
+
}, [email]);
|
|
2261
|
+
const canSubmit = email.length > 0 && emailError === null && !submitting;
|
|
2262
|
+
const submit = useCallback7(async () => {
|
|
2263
|
+
if (!canSubmit) return false;
|
|
2264
|
+
setSubmitting(true);
|
|
2265
|
+
setError(null);
|
|
2913
2266
|
try {
|
|
2914
|
-
await
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2267
|
+
await auth.forgot({ email });
|
|
2268
|
+
setSent(true);
|
|
2269
|
+
return true;
|
|
2270
|
+
} catch (err) {
|
|
2271
|
+
setError(mapSdkError(err));
|
|
2272
|
+
return false;
|
|
2273
|
+
} finally {
|
|
2274
|
+
setSubmitting(false);
|
|
2922
2275
|
}
|
|
2923
|
-
}, [
|
|
2924
|
-
|
|
2276
|
+
}, [auth, email, canSubmit]);
|
|
2277
|
+
return {
|
|
2278
|
+
email,
|
|
2279
|
+
setEmail,
|
|
2280
|
+
emailError,
|
|
2281
|
+
submit,
|
|
2282
|
+
submitting,
|
|
2283
|
+
canSubmit,
|
|
2284
|
+
sent,
|
|
2285
|
+
error
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
// src/hooks/useResetForm.ts
|
|
2290
|
+
import { useCallback as useCallback8, useEffect as useEffect7, useMemo as useMemo7, useState as useState9 } from "react";
|
|
2291
|
+
import { useHook as useHook9 } from "@hook-sdk/sdk";
|
|
2292
|
+
var MIN_PASSWORD3 = 12;
|
|
2293
|
+
function useResetForm() {
|
|
2294
|
+
const { auth } = useHook9();
|
|
2295
|
+
const [token, setToken] = useState9(null);
|
|
2296
|
+
const [password, setPassword] = useState9("");
|
|
2297
|
+
const [confirm, setConfirm] = useState9("");
|
|
2298
|
+
const [submitting, setSubmitting] = useState9(false);
|
|
2299
|
+
const [done, setDone] = useState9(false);
|
|
2300
|
+
const [error, setError] = useState9(null);
|
|
2301
|
+
useEffect7(() => {
|
|
2302
|
+
if (typeof window === "undefined") return;
|
|
2303
|
+
const params = new URLSearchParams(window.location.search);
|
|
2304
|
+
const t = params.get("token");
|
|
2305
|
+
setToken(t && t.length > 0 ? t : null);
|
|
2306
|
+
}, []);
|
|
2307
|
+
const passwordError = useMemo7(() => {
|
|
2308
|
+
if (password.length === 0) return null;
|
|
2309
|
+
if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
|
|
2310
|
+
return null;
|
|
2311
|
+
}, [password]);
|
|
2312
|
+
const confirmError = useMemo7(() => {
|
|
2313
|
+
if (confirm.length === 0) return null;
|
|
2314
|
+
if (confirm !== password) return "Senhas n\xE3o coincidem.";
|
|
2315
|
+
return null;
|
|
2316
|
+
}, [confirm, password]);
|
|
2317
|
+
const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && passwordError === null && confirmError === null && !submitting && !done;
|
|
2318
|
+
const submit = useCallback8(async () => {
|
|
2319
|
+
if (!canSubmit || token === null) return;
|
|
2320
|
+
setSubmitting(true);
|
|
2321
|
+
setError(null);
|
|
2925
2322
|
try {
|
|
2926
|
-
await
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2323
|
+
await auth.reset({ token, newPassword: password });
|
|
2324
|
+
setDone(true);
|
|
2325
|
+
if (typeof window !== "undefined") {
|
|
2326
|
+
const url = new URL(window.location.href);
|
|
2327
|
+
url.searchParams.delete("token");
|
|
2328
|
+
url.searchParams.delete("screen");
|
|
2329
|
+
window.history.replaceState({}, "", url.toString());
|
|
2330
|
+
}
|
|
2331
|
+
} catch (err) {
|
|
2332
|
+
setError(mapSdkError(err));
|
|
2333
|
+
} finally {
|
|
2334
|
+
setSubmitting(false);
|
|
2931
2335
|
}
|
|
2932
|
-
}, [
|
|
2933
|
-
return {
|
|
2336
|
+
}, [auth, token, password, canSubmit]);
|
|
2337
|
+
return {
|
|
2338
|
+
token,
|
|
2339
|
+
password,
|
|
2340
|
+
setPassword,
|
|
2341
|
+
passwordError,
|
|
2342
|
+
confirm,
|
|
2343
|
+
setConfirm,
|
|
2344
|
+
confirmError,
|
|
2345
|
+
submit,
|
|
2346
|
+
submitting,
|
|
2347
|
+
canSubmit,
|
|
2348
|
+
done,
|
|
2349
|
+
error
|
|
2350
|
+
};
|
|
2934
2351
|
}
|
|
2935
2352
|
|
|
2936
|
-
// src/
|
|
2937
|
-
import {
|
|
2938
|
-
function
|
|
2939
|
-
const {
|
|
2940
|
-
|
|
2941
|
-
if (state.kind === "ios_needs_install") {
|
|
2942
|
-
return /* @__PURE__ */ jsxs20("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
|
|
2943
|
-
/* @__PURE__ */ jsx26("h3", { children: texts.iosInstallTitle }),
|
|
2944
|
-
/* @__PURE__ */ jsx26("p", { children: texts.iosInstallBody }),
|
|
2945
|
-
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx26("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
|
|
2946
|
-
] });
|
|
2947
|
-
}
|
|
2948
|
-
if (state.kind === "denied") {
|
|
2949
|
-
return /* @__PURE__ */ jsxs20("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
|
|
2950
|
-
/* @__PURE__ */ jsx26("h3", { children: texts.deniedTitle }),
|
|
2951
|
-
/* @__PURE__ */ jsx26("p", { children: texts.deniedBody })
|
|
2952
|
-
] });
|
|
2953
|
-
}
|
|
2954
|
-
if (state.kind === "unsupported") {
|
|
2955
|
-
return /* @__PURE__ */ jsx26("div", { className, role: "region", children: /* @__PURE__ */ jsx26("p", { children: texts.unsupportedBody }) });
|
|
2956
|
-
}
|
|
2957
|
-
if (state.kind === "error") {
|
|
2958
|
-
return /* @__PURE__ */ jsx26("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx26("p", { children: state.message }) });
|
|
2959
|
-
}
|
|
2960
|
-
return /* @__PURE__ */ jsxs20("div", { className, role: "region", children: [
|
|
2961
|
-
/* @__PURE__ */ jsx26(
|
|
2962
|
-
"button",
|
|
2963
|
-
{
|
|
2964
|
-
type: "button",
|
|
2965
|
-
onClick: async () => {
|
|
2966
|
-
try {
|
|
2967
|
-
await subscribe();
|
|
2968
|
-
onSubscribed?.();
|
|
2969
|
-
} catch {
|
|
2970
|
-
}
|
|
2971
|
-
},
|
|
2972
|
-
children: texts.cta
|
|
2973
|
-
}
|
|
2974
|
-
),
|
|
2975
|
-
onDeclined && /* @__PURE__ */ jsx26("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
|
|
2976
|
-
] });
|
|
2353
|
+
// src/hooks/usePlan.ts
|
|
2354
|
+
import { useHook as useHook10 } from "@hook-sdk/sdk";
|
|
2355
|
+
function usePlan() {
|
|
2356
|
+
const { plan } = useHook10();
|
|
2357
|
+
return plan;
|
|
2977
2358
|
}
|
|
2978
2359
|
|
|
2979
|
-
// src/
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2360
|
+
// src/utils/price.ts
|
|
2361
|
+
function formatBRL(cents) {
|
|
2362
|
+
if (cents === null || cents === void 0) return "";
|
|
2363
|
+
const reais = cents / 100;
|
|
2364
|
+
return new Intl.NumberFormat("pt-BR", {
|
|
2365
|
+
style: "currency",
|
|
2366
|
+
currency: "BRL"
|
|
2367
|
+
}).format(reais);
|
|
2368
|
+
}
|
|
2369
|
+
function monthlyFromYearly(yearlyCents) {
|
|
2370
|
+
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
2371
|
+
return Math.round(yearlyCents / 12);
|
|
2372
|
+
}
|
|
2373
|
+
function dailyFromYearly(yearlyCents) {
|
|
2374
|
+
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
2375
|
+
return Math.round(yearlyCents / 365);
|
|
2376
|
+
}
|
|
2377
|
+
function computeAnchorCents(baseCents, multiplier) {
|
|
2378
|
+
if (multiplier === null || multiplier === void 0) return null;
|
|
2379
|
+
if (!Number.isFinite(multiplier)) return null;
|
|
2380
|
+
if (multiplier <= 1) return null;
|
|
2381
|
+
return Math.round(baseCents * multiplier);
|
|
2382
|
+
}
|
|
2383
|
+
function discountPercent(anchorCents, realCents) {
|
|
2384
|
+
if (anchorCents <= realCents) return 0;
|
|
2385
|
+
return Math.floor((anchorCents - realCents) / anchorCents * 100);
|
|
2987
2386
|
}
|
|
2988
2387
|
|
|
2989
2388
|
// src/hooks/useAuthPrimitives.ts
|
|
2990
|
-
import { useEffect as
|
|
2389
|
+
import { useEffect as useEffect8 } from "react";
|
|
2991
2390
|
import { useHook as useHook11 } from "@hook-sdk/sdk";
|
|
2992
2391
|
var warned = false;
|
|
2993
2392
|
function useAuthPrimitives() {
|
|
2994
2393
|
const { auth } = useHook11();
|
|
2995
|
-
|
|
2394
|
+
useEffect8(() => {
|
|
2996
2395
|
if (!warned && process.env.NODE_ENV !== "production") {
|
|
2997
2396
|
warned = true;
|
|
2998
2397
|
console.warn(
|
|
@@ -3013,23 +2412,34 @@ function useAuthPrimitives() {
|
|
|
3013
2412
|
};
|
|
3014
2413
|
}
|
|
3015
2414
|
|
|
3016
|
-
// src/hooks/
|
|
2415
|
+
// src/hooks/useAuth.ts
|
|
3017
2416
|
import { useHook as useHook12 } from "@hook-sdk/sdk";
|
|
2417
|
+
function useAuth() {
|
|
2418
|
+
const { user, authStatus, auth } = useHook12();
|
|
2419
|
+
return {
|
|
2420
|
+
user,
|
|
2421
|
+
authStatus,
|
|
2422
|
+
refresh: auth.refresh
|
|
2423
|
+
};
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
// src/hooks/useSubscription.ts
|
|
2427
|
+
import { useHook as useHook13 } from "@hook-sdk/sdk";
|
|
3018
2428
|
function useSubscription() {
|
|
3019
|
-
const { subscription } =
|
|
2429
|
+
const { subscription } = useHook13();
|
|
3020
2430
|
return {
|
|
3021
2431
|
status: subscription.status()
|
|
3022
2432
|
};
|
|
3023
2433
|
}
|
|
3024
2434
|
|
|
3025
2435
|
// src/hooks/useReminders.ts
|
|
3026
|
-
import { useCallback as useCallback9, useEffect as
|
|
3027
|
-
import { useHook as
|
|
2436
|
+
import { useCallback as useCallback9, useEffect as useEffect9, useState as useState10 } from "react";
|
|
2437
|
+
import { useHook as useHook14 } from "@hook-sdk/sdk";
|
|
3028
2438
|
function useReminders() {
|
|
3029
|
-
const { push } =
|
|
2439
|
+
const { push } = useHook14();
|
|
3030
2440
|
const r = push.reminders;
|
|
3031
|
-
const [reminders, setReminders] =
|
|
3032
|
-
const [loading, setLoading] =
|
|
2441
|
+
const [reminders, setReminders] = useState10([]);
|
|
2442
|
+
const [loading, setLoading] = useState10(true);
|
|
3033
2443
|
const reload = useCallback9(async () => {
|
|
3034
2444
|
setLoading(true);
|
|
3035
2445
|
try {
|
|
@@ -3039,7 +2449,7 @@ function useReminders() {
|
|
|
3039
2449
|
setLoading(false);
|
|
3040
2450
|
}
|
|
3041
2451
|
}, [r]);
|
|
3042
|
-
|
|
2452
|
+
useEffect9(() => {
|
|
3043
2453
|
void reload();
|
|
3044
2454
|
}, [reload]);
|
|
3045
2455
|
const setReminder = useCallback9(async (input) => {
|
|
@@ -3060,9 +2470,9 @@ function useReminders() {
|
|
|
3060
2470
|
}
|
|
3061
2471
|
|
|
3062
2472
|
// src/hooks/useToast.ts
|
|
3063
|
-
import { useCallback as useCallback10, useState as
|
|
2473
|
+
import { useCallback as useCallback10, useState as useState11 } from "react";
|
|
3064
2474
|
function useToast() {
|
|
3065
|
-
const [items, setItems] =
|
|
2475
|
+
const [items, setItems] = useState11([]);
|
|
3066
2476
|
const show = useCallback10((message, kind = "info") => {
|
|
3067
2477
|
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3068
2478
|
setItems((prev) => [...prev, { id, message, kind }]);
|
|
@@ -3075,19 +2485,136 @@ function useToast() {
|
|
|
3075
2485
|
}, []);
|
|
3076
2486
|
return { items, show, dismiss };
|
|
3077
2487
|
}
|
|
2488
|
+
|
|
2489
|
+
// src/RouteBoundary.tsx
|
|
2490
|
+
import { Routes as Routes2, Route as Route2 } from "react-router-dom";
|
|
2491
|
+
import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2492
|
+
function RouteBoundary({ children }) {
|
|
2493
|
+
return /* @__PURE__ */ jsxs16(Routes2, { children: [
|
|
2494
|
+
children,
|
|
2495
|
+
/* @__PURE__ */ jsx22(Route2, { path: "*", element: /* @__PURE__ */ jsx22(DefaultNotFound, {}) })
|
|
2496
|
+
] });
|
|
2497
|
+
}
|
|
2498
|
+
function DefaultNotFound() {
|
|
2499
|
+
return /* @__PURE__ */ jsx22("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// src/PreAuthShell.tsx
|
|
2503
|
+
import { BrowserRouter as BrowserRouter2, MemoryRouter as MemoryRouter2, Routes as Routes3 } from "react-router-dom";
|
|
2504
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
2505
|
+
function PreAuthShell({
|
|
2506
|
+
basename,
|
|
2507
|
+
testRouter,
|
|
2508
|
+
testInitialEntries,
|
|
2509
|
+
children
|
|
2510
|
+
}) {
|
|
2511
|
+
if (testRouter === "memory") {
|
|
2512
|
+
return /* @__PURE__ */ jsx23(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx23(Routes3, { children }) });
|
|
2513
|
+
}
|
|
2514
|
+
return /* @__PURE__ */ jsx23(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx23(Routes3, { children }) });
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// src/OnboardingFlow.tsx
|
|
2518
|
+
import { useCallback as useCallback11, useMemo as useMemo8, useRef as useRef3, useState as useState12 } from "react";
|
|
2519
|
+
import { usePersistedState } from "@hook-sdk/sdk";
|
|
2520
|
+
|
|
2521
|
+
// src/hooks/useOnboardingStep.ts
|
|
2522
|
+
import { createContext as createContext3, useContext as useContext4 } from "react";
|
|
2523
|
+
var OnboardingStepContext = createContext3(null);
|
|
2524
|
+
function useOnboardingStep() {
|
|
2525
|
+
const ctx = useContext4(OnboardingStepContext);
|
|
2526
|
+
if (!ctx) {
|
|
2527
|
+
throw new Error(
|
|
2528
|
+
"[hook-template] useOnboardingStep must be used inside <OnboardingFlow>. (G75)"
|
|
2529
|
+
);
|
|
2530
|
+
}
|
|
2531
|
+
return ctx;
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
// src/OnboardingFlow.tsx
|
|
2535
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
2536
|
+
var isFilled = (v) => v != null && v !== "";
|
|
2537
|
+
function OnboardingFlow({
|
|
2538
|
+
steps,
|
|
2539
|
+
screens,
|
|
2540
|
+
onComplete,
|
|
2541
|
+
persistKey
|
|
2542
|
+
}) {
|
|
2543
|
+
const [draft, setDraft] = usePersistedState(persistKey, {});
|
|
2544
|
+
const [idx, setIdx] = useState12(0);
|
|
2545
|
+
const draftRef = useRef3(draft);
|
|
2546
|
+
draftRef.current = draft;
|
|
2547
|
+
const step = steps[idx];
|
|
2548
|
+
if (!step) {
|
|
2549
|
+
throw new Error(
|
|
2550
|
+
`[hook-template] OnboardingFlow: step index ${idx} out of range (steps.length=${steps.length})`
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
const Screen = screens[step.screen];
|
|
2554
|
+
if (!Screen) {
|
|
2555
|
+
throw new Error(
|
|
2556
|
+
`[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
|
|
2557
|
+
);
|
|
2558
|
+
}
|
|
2559
|
+
const valid = useMemo8(
|
|
2560
|
+
() => (step.validates ?? []).every((field) => isFilled(draft[field])),
|
|
2561
|
+
[draft, step]
|
|
2562
|
+
);
|
|
2563
|
+
const setValue = useCallback11(
|
|
2564
|
+
(patch) => {
|
|
2565
|
+
draftRef.current = { ...draftRef.current, ...patch };
|
|
2566
|
+
setDraft((prev2) => ({ ...prev2, ...patch }));
|
|
2567
|
+
},
|
|
2568
|
+
[setDraft]
|
|
2569
|
+
);
|
|
2570
|
+
const next = useCallback11(() => {
|
|
2571
|
+
const current = draftRef.current;
|
|
2572
|
+
const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
|
|
2573
|
+
if (!validNow) return;
|
|
2574
|
+
if (idx + 1 >= steps.length) {
|
|
2575
|
+
onComplete(current);
|
|
2576
|
+
} else {
|
|
2577
|
+
setIdx(idx + 1);
|
|
2578
|
+
}
|
|
2579
|
+
}, [idx, onComplete, step, steps.length]);
|
|
2580
|
+
const prev = useCallback11(() => setIdx((i) => Math.max(0, i - 1)), []);
|
|
2581
|
+
const ctx = useMemo8(
|
|
2582
|
+
() => ({
|
|
2583
|
+
stepIndex: idx,
|
|
2584
|
+
totalSteps: steps.length,
|
|
2585
|
+
value: draft,
|
|
2586
|
+
setValue,
|
|
2587
|
+
valid,
|
|
2588
|
+
next,
|
|
2589
|
+
prev
|
|
2590
|
+
}),
|
|
2591
|
+
[idx, steps.length, draft, setValue, valid, next, prev]
|
|
2592
|
+
);
|
|
2593
|
+
return /* @__PURE__ */ jsx24(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx24(Screen, {}) });
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
// src/hooks/useFeature.ts
|
|
2597
|
+
function useFeature(name) {
|
|
2598
|
+
const config = useAppConfig();
|
|
2599
|
+
return Array.isArray(config.features_enabled) && config.features_enabled.includes(name);
|
|
2600
|
+
}
|
|
3078
2601
|
export {
|
|
2602
|
+
AppConfigProvider,
|
|
2603
|
+
AppConfigSchema,
|
|
3079
2604
|
AppRoot,
|
|
3080
|
-
|
|
3081
|
-
DefaultLoginScreen,
|
|
3082
|
-
DefaultPaywall,
|
|
3083
|
-
DefaultResetScreen,
|
|
3084
|
-
DefaultSignupScreen,
|
|
2605
|
+
DeepLinkHandler,
|
|
3085
2606
|
EmptyState,
|
|
3086
2607
|
ErrorBoundary,
|
|
3087
2608
|
InstallGate,
|
|
3088
2609
|
InstallSplash,
|
|
3089
2610
|
LoadingState,
|
|
2611
|
+
OnboardingFlow,
|
|
2612
|
+
PaymentReturnHandler,
|
|
2613
|
+
PersistenceRegistry,
|
|
2614
|
+
PreAuthShell,
|
|
3090
2615
|
PushPrompt2 as PushPrompt,
|
|
2616
|
+
RouteBoundary,
|
|
2617
|
+
asaasErrorMessage,
|
|
3091
2618
|
computeAnchorCents,
|
|
3092
2619
|
dailyFromYearly,
|
|
3093
2620
|
detectAndroidBrowser,
|
|
@@ -3098,13 +2625,17 @@ export {
|
|
|
3098
2625
|
discountPercent,
|
|
3099
2626
|
formatBRL,
|
|
3100
2627
|
monthlyFromYearly,
|
|
2628
|
+
parseAppConfig,
|
|
3101
2629
|
shouldBlockInstall,
|
|
3102
2630
|
shouldShowPermanentOption,
|
|
2631
|
+
useAppConfig,
|
|
3103
2632
|
useAuth,
|
|
3104
2633
|
useAuthPrimitives,
|
|
2634
|
+
useFeature,
|
|
3105
2635
|
useForgotForm,
|
|
3106
2636
|
useInstallPrompt,
|
|
3107
2637
|
useLoginForm,
|
|
2638
|
+
useOnboardingStep,
|
|
3108
2639
|
usePaywallState,
|
|
3109
2640
|
usePlan,
|
|
3110
2641
|
usePush,
|