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