@hook-sdk/template 0.19.0 → 0.21.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 +747 -230
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +250 -10
- package/dist/index.d.ts +250 -10
- package/dist/index.js +699 -191
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
AppConfigSchema: () => AppConfigSchema,
|
|
35
35
|
AppRoot: () => AppRoot,
|
|
36
36
|
DeepLinkHandler: () => DeepLinkHandler,
|
|
37
|
+
DevSkipOnboardingFab: () => DevSkipOnboardingFab,
|
|
37
38
|
EmptyState: () => EmptyState,
|
|
38
39
|
ErrorBoundary: () => ErrorBoundary,
|
|
39
40
|
I18nProvider: () => I18nProvider,
|
|
@@ -43,6 +44,11 @@ __export(index_exports, {
|
|
|
43
44
|
LoadingState: () => LoadingState,
|
|
44
45
|
OnboardingFlow: () => OnboardingFlow,
|
|
45
46
|
PaymentReturnHandler: () => PaymentReturnHandler,
|
|
47
|
+
Paywall: () => Paywall,
|
|
48
|
+
PaywallCta: () => PaywallCta,
|
|
49
|
+
PaywallCyclePicker: () => PaywallCyclePicker,
|
|
50
|
+
PaywallMethodContent: () => PaywallMethodContent,
|
|
51
|
+
PaywallMethodTabs: () => PaywallMethodTabs,
|
|
46
52
|
PersistenceRegistry: () => PersistenceRegistry,
|
|
47
53
|
PreAuthShell: () => PreAuthShell,
|
|
48
54
|
PushPrompt: () => PushPrompt2,
|
|
@@ -57,10 +63,12 @@ __export(index_exports, {
|
|
|
57
63
|
detectStandalone: () => detectStandalone,
|
|
58
64
|
discountPercent: () => discountPercent,
|
|
59
65
|
formatBRL: () => formatBRL,
|
|
66
|
+
isDevToolsEnabled: () => isDevToolsEnabled,
|
|
60
67
|
monthlyFromYearly: () => monthlyFromYearly,
|
|
61
68
|
parseAppConfig: () => parseAppConfig,
|
|
62
69
|
shouldBlockInstall: () => shouldBlockInstall,
|
|
63
70
|
shouldShowPermanentOption: () => shouldShowPermanentOption,
|
|
71
|
+
skipOnboarding: () => skipOnboarding,
|
|
64
72
|
useAppConfig: () => useAppConfig,
|
|
65
73
|
useAuth: () => useAuth,
|
|
66
74
|
useAuthPrimitives: () => useAuthPrimitives,
|
|
@@ -77,14 +85,14 @@ __export(index_exports, {
|
|
|
77
85
|
useSignupForm: () => useSignupForm,
|
|
78
86
|
useSubscription: () => useSubscription,
|
|
79
87
|
useToast: () => useToast,
|
|
80
|
-
useTrackOnboardingStep: () =>
|
|
88
|
+
useTrackOnboardingStep: () => import_sdk23.useTrackOnboardingStep
|
|
81
89
|
});
|
|
82
90
|
module.exports = __toCommonJS(index_exports);
|
|
83
91
|
|
|
84
92
|
// src/AppRoot.tsx
|
|
85
|
-
var
|
|
93
|
+
var import_react15 = require("react");
|
|
86
94
|
var import_react_router_dom2 = require("react-router-dom");
|
|
87
|
-
var
|
|
95
|
+
var import_sdk8 = require("@hook-sdk/sdk");
|
|
88
96
|
|
|
89
97
|
// src/config/AppConfigContext.tsx
|
|
90
98
|
var import_react = require("react");
|
|
@@ -118,7 +126,16 @@ var AuthFlowSchema = import_zod.z.object({
|
|
|
118
126
|
});
|
|
119
127
|
var PaywallNonFreeSchema = import_zod.z.object({
|
|
120
128
|
mode: import_zod.z.enum(["trial", "pay_first"]),
|
|
129
|
+
// Legacy flat trial — fallback when per-method fields aren't set.
|
|
121
130
|
trialDays: import_zod.z.number().int().nonnegative().optional(),
|
|
131
|
+
// Per-method trial (ADR-022 Amendment 2026-05-12 + G154). PIX Auto can't
|
|
132
|
+
// offer trial on Asaas today (Jornada 2 unavailable), so apps that mix
|
|
133
|
+
// methods must split the value to avoid bait-and-switch on PIX shoppers.
|
|
134
|
+
trialDaysCard: import_zod.z.number().int().nonnegative().optional(),
|
|
135
|
+
trialDaysPix: import_zod.z.number().int().nonnegative().optional(),
|
|
136
|
+
// Which method the paywall preselects. null/undefined = no preselection
|
|
137
|
+
// (user picks). Per-app override reflects audience, not Hook bias.
|
|
138
|
+
defaultMethod: import_zod.z.enum(["card", "pix-auto", "pix-once"]).nullable().optional(),
|
|
122
139
|
cycles: import_zod.z.array(import_zod.z.enum(["MONTHLY", "YEARLY"])).min(1),
|
|
123
140
|
prices: import_zod.z.object({
|
|
124
141
|
monthlyCents: import_zod.z.number().int().nonnegative(),
|
|
@@ -352,7 +369,7 @@ var FALLBACK_PAYWALL = {
|
|
|
352
369
|
};
|
|
353
370
|
var isMethodAvailable = (availability, method) => availability[method] !== false;
|
|
354
371
|
function usePaywallState() {
|
|
355
|
-
const { subscription, plan } = (0, import_sdk3.useHook)();
|
|
372
|
+
const { subscription, plan, authStatus, track: track2 } = (0, import_sdk3.useHook)();
|
|
356
373
|
const configFromCtx = (0, import_react6.useContext)(AppConfigContext);
|
|
357
374
|
const paywall = configFromCtx?.paywall ?? FALLBACK_PAYWALL;
|
|
358
375
|
const isFree = paywall.mode === "free";
|
|
@@ -369,7 +386,8 @@ function usePaywallState() {
|
|
|
369
386
|
() => declaredMethods.filter((m) => isMethodAvailable(availability, m)),
|
|
370
387
|
[declaredMethods, availability]
|
|
371
388
|
);
|
|
372
|
-
const
|
|
389
|
+
const configDefault = !isFree && "defaultMethod" in paywall ? paywall.defaultMethod ?? null : null;
|
|
390
|
+
const defaultMethod = (configDefault && methods.includes(configDefault) ? configDefault : null) ?? methods[0] ?? declaredMethods[0] ?? "card";
|
|
373
391
|
const [selectedMethodRaw, setSelectedMethod] = (0, import_react6.useState)(defaultMethod);
|
|
374
392
|
const selectedMethod = methods.includes(selectedMethodRaw) ? selectedMethodRaw : methods[0] ?? selectedMethodRaw;
|
|
375
393
|
const initialCycle = isFree ? "MONTHLY" : paywall.cycles[0] ?? "MONTHLY";
|
|
@@ -390,6 +408,22 @@ function usePaywallState() {
|
|
|
390
408
|
const [submitting, setSubmitting] = (0, import_react6.useState)(false);
|
|
391
409
|
const status = subscription.status();
|
|
392
410
|
const daysLeftInTrial = subscription.daysLeftInTrial();
|
|
411
|
+
const trialDaysForMethod = (0, import_react6.useCallback)(
|
|
412
|
+
(method) => {
|
|
413
|
+
if (isFree) return 0;
|
|
414
|
+
if (method === "card") {
|
|
415
|
+
if (typeof paywall.trialDaysCard === "number") return paywall.trialDaysCard;
|
|
416
|
+
if (typeof paywall.trialDays === "number") return paywall.trialDays;
|
|
417
|
+
return 7;
|
|
418
|
+
}
|
|
419
|
+
if (typeof paywall.trialDaysPix === "number") return paywall.trialDaysPix;
|
|
420
|
+
if (typeof paywall.trialDays === "number") return paywall.trialDays;
|
|
421
|
+
return 0;
|
|
422
|
+
},
|
|
423
|
+
[isFree, paywall]
|
|
424
|
+
);
|
|
425
|
+
const trialDaysCard = trialDaysForMethod("card");
|
|
426
|
+
const trialDaysPix = trialDaysForMethod("pix-auto");
|
|
393
427
|
const initialLoadComplete = subscription.initialLoadComplete;
|
|
394
428
|
const hasAccess = subscription.hasAccess;
|
|
395
429
|
const pixPending = (0, import_react6.useMemo)(() => {
|
|
@@ -429,6 +463,40 @@ function usePaywallState() {
|
|
|
429
463
|
discountPercent: discount
|
|
430
464
|
};
|
|
431
465
|
}, [paywall, cycle, isFree]);
|
|
466
|
+
const hasConsumedTrial = (0, import_react6.useMemo)(() => {
|
|
467
|
+
return ["active", "trialing", "past_due", "canceled", "expired"].includes(status);
|
|
468
|
+
}, [status]);
|
|
469
|
+
const currentPriceCents = (0, import_react6.useMemo)(() => {
|
|
470
|
+
if (isFree) return 0;
|
|
471
|
+
return cycle === "YEARLY" ? paywall.prices.yearlyCents : paywall.prices.monthlyCents;
|
|
472
|
+
}, [paywall, cycle, isFree]);
|
|
473
|
+
const currentMonthlyEquivCents = (0, import_react6.useMemo)(() => {
|
|
474
|
+
if (isFree) return 0;
|
|
475
|
+
if (cycle === "YEARLY") return Math.round(paywall.prices.yearlyCents / 12);
|
|
476
|
+
return paywall.prices.monthlyCents;
|
|
477
|
+
}, [paywall, cycle, isFree]);
|
|
478
|
+
const anchorPriceCents = (0, import_react6.useMemo)(() => {
|
|
479
|
+
if (isFree) return null;
|
|
480
|
+
const a = paywall.anchorPrices;
|
|
481
|
+
if (!a) return null;
|
|
482
|
+
return cycle === "YEARLY" ? a.yearlyCents : a.monthlyCents;
|
|
483
|
+
}, [paywall, cycle, isFree]);
|
|
484
|
+
const selectMethod = (0, import_react6.useCallback)(
|
|
485
|
+
(next) => {
|
|
486
|
+
if (next === selectedMethod) return;
|
|
487
|
+
track2("paywall_method_tab_clicked", { method: next, from_method: selectedMethod });
|
|
488
|
+
setSelectedMethod(next);
|
|
489
|
+
},
|
|
490
|
+
[selectedMethod, track2]
|
|
491
|
+
);
|
|
492
|
+
const selectCycle = (0, import_react6.useCallback)(
|
|
493
|
+
(next) => {
|
|
494
|
+
if (next === cycle) return;
|
|
495
|
+
track2("paywall_cycle_clicked", { cycle: next, from_cycle: cycle });
|
|
496
|
+
setCycle(next);
|
|
497
|
+
},
|
|
498
|
+
[cycle, track2]
|
|
499
|
+
);
|
|
432
500
|
const useDefaultMessages = paywall.mode !== "free" && paywall.errorMessages === "default";
|
|
433
501
|
const buildError = (0, import_react6.useCallback)(
|
|
434
502
|
(code, fallbackMessage) => ({
|
|
@@ -441,6 +509,21 @@ function usePaywallState() {
|
|
|
441
509
|
[useDefaultMessages]
|
|
442
510
|
);
|
|
443
511
|
const submit = (0, import_react6.useCallback)(async () => {
|
|
512
|
+
if (authStatus === "loading") return void 0;
|
|
513
|
+
if (authStatus !== "authenticated") {
|
|
514
|
+
track2("unauthenticated_submit_attempted", {
|
|
515
|
+
method: selectedMethod,
|
|
516
|
+
cycle,
|
|
517
|
+
cpf_valid: cpfValid
|
|
518
|
+
});
|
|
519
|
+
return void 0;
|
|
520
|
+
}
|
|
521
|
+
track2("payment_attempted", {
|
|
522
|
+
method: selectedMethod,
|
|
523
|
+
cycle,
|
|
524
|
+
cpf_valid: cpfValid,
|
|
525
|
+
selected_amount_cents: cycle === "YEARLY" ? plan.data?.yearlyPriceCents ?? (isFree ? 0 : paywall.prices.yearlyCents) : plan.data?.priceCents ?? (isFree ? 0 : paywall.prices.monthlyCents)
|
|
526
|
+
});
|
|
444
527
|
setSubmitting(true);
|
|
445
528
|
setError(null);
|
|
446
529
|
const methodToUse = selectedMethod;
|
|
@@ -501,7 +584,7 @@ function usePaywallState() {
|
|
|
501
584
|
setSubmitting(false);
|
|
502
585
|
return void 0;
|
|
503
586
|
}
|
|
504
|
-
}, [selectedMethod, availability, subscription, cycle, cpf, card, buildError]);
|
|
587
|
+
}, [authStatus, track2, selectedMethod, availability, subscription, cycle, cpf, cpfValid, card, buildError, plan, paywall]);
|
|
505
588
|
const checkout = (0, import_react6.useCallback)(
|
|
506
589
|
async (args) => {
|
|
507
590
|
setSubmitting(true);
|
|
@@ -608,6 +691,20 @@ function usePaywallState() {
|
|
|
608
691
|
methods,
|
|
609
692
|
selectedMethod,
|
|
610
693
|
setSelectedMethod,
|
|
694
|
+
// Conversion-max derivations (template 0.21)
|
|
695
|
+
hasConsumedTrial,
|
|
696
|
+
currentPriceCents,
|
|
697
|
+
currentMonthlyEquivCents,
|
|
698
|
+
anchorPriceCents,
|
|
699
|
+
selectMethod,
|
|
700
|
+
selectCycle,
|
|
701
|
+
isFree,
|
|
702
|
+
// Per-method trial (ADR-022 Amendment 2026-05-12). Use these to render
|
|
703
|
+
// asymmetric copy per method card on the paywall — never a global
|
|
704
|
+
// "free trial!" headline when both methods are visible.
|
|
705
|
+
trialDaysForMethod,
|
|
706
|
+
trialDaysCard,
|
|
707
|
+
trialDaysPix,
|
|
611
708
|
// Form state
|
|
612
709
|
cpfState,
|
|
613
710
|
cardState,
|
|
@@ -639,13 +736,13 @@ var BLOCKING = /* @__PURE__ */ new Set([
|
|
|
639
736
|
"canceled",
|
|
640
737
|
"none"
|
|
641
738
|
]);
|
|
642
|
-
function SubscriptionGate({ Paywall, children }) {
|
|
739
|
+
function SubscriptionGate({ Paywall: Paywall2, children }) {
|
|
643
740
|
const { mode } = useTemplateConfig();
|
|
644
741
|
const { status, hasAccess, initialLoadComplete } = usePaywallState();
|
|
645
742
|
if (mode === "free") return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
|
|
646
743
|
if (!initialLoadComplete && status === "none") return null;
|
|
647
744
|
if (hasAccess === true && status !== "none") return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
|
|
648
|
-
if (BLOCKING.has(status)) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
745
|
+
if (BLOCKING.has(status)) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Paywall2, {});
|
|
649
746
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
|
|
650
747
|
}
|
|
651
748
|
|
|
@@ -2091,23 +2188,163 @@ function I18nProvider({
|
|
|
2091
2188
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_i18next.I18nextProvider, { i18n: import_i18next.default, children });
|
|
2092
2189
|
}
|
|
2093
2190
|
|
|
2094
|
-
// src/
|
|
2191
|
+
// src/dev/env.ts
|
|
2192
|
+
var import_meta = {};
|
|
2193
|
+
function isDevToolsEnabled() {
|
|
2194
|
+
const meta = import_meta;
|
|
2195
|
+
if (meta.env?.VITE_HOOK_DEV_TOOLS !== "1") return false;
|
|
2196
|
+
if (typeof window === "undefined") return false;
|
|
2197
|
+
const host = window.location.hostname;
|
|
2198
|
+
return host.includes(".staging.") || host === "localhost" || host === "127.0.0.1";
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// src/dev/DevSkipOnboardingFab.tsx
|
|
2095
2202
|
var import_react13 = require("react");
|
|
2096
2203
|
var import_sdk6 = require("@hook-sdk/sdk");
|
|
2097
2204
|
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
2205
|
+
var STORAGE_KEY = "hook_dev_skip_email";
|
|
2206
|
+
var TEST_EMAIL_DOMAIN = "@hook.test";
|
|
2207
|
+
var TEST_PASSWORD = "SkipTest!2026";
|
|
2208
|
+
function makeEmail() {
|
|
2209
|
+
return `ryan+skip-${Date.now()}${TEST_EMAIL_DOMAIN}`;
|
|
2210
|
+
}
|
|
2211
|
+
async function ensureSignedIn(hook, maxAttempts = 3) {
|
|
2212
|
+
if (hook.authStatus === "authenticated") return null;
|
|
2213
|
+
let lastErr;
|
|
2214
|
+
for (let i = 0; i < maxAttempts; i += 1) {
|
|
2215
|
+
const email = makeEmail();
|
|
2216
|
+
try {
|
|
2217
|
+
await hook.auth.signup({ email, password: TEST_PASSWORD, name: "Ryan Test" });
|
|
2218
|
+
try {
|
|
2219
|
+
window.sessionStorage.setItem(STORAGE_KEY, email);
|
|
2220
|
+
} catch {
|
|
2221
|
+
}
|
|
2222
|
+
return email;
|
|
2223
|
+
} catch (err) {
|
|
2224
|
+
lastErr = err;
|
|
2225
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
throw lastErr ?? new Error("signup failed after retries");
|
|
2229
|
+
}
|
|
2230
|
+
async function skipOnboarding(hook, defaults, appSlug) {
|
|
2231
|
+
const { __seed, ...rest } = defaults;
|
|
2232
|
+
await ensureSignedIn(hook);
|
|
2233
|
+
await hook.appData.set("onboarding_data", {
|
|
2234
|
+
...rest,
|
|
2235
|
+
onboarding_completed: true
|
|
2236
|
+
});
|
|
2237
|
+
if (__seed) {
|
|
2238
|
+
await __seed(hook);
|
|
2239
|
+
}
|
|
2240
|
+
console.info("[hook-template] dev_skip_onboarding fired", {
|
|
2241
|
+
app_slug: appSlug,
|
|
2242
|
+
hostname: window.location.hostname
|
|
2243
|
+
});
|
|
2244
|
+
window.location.assign(`/app/${appSlug}/`);
|
|
2245
|
+
}
|
|
2246
|
+
var STYLES = {
|
|
2247
|
+
base: {
|
|
2248
|
+
position: "fixed",
|
|
2249
|
+
bottom: "16px",
|
|
2250
|
+
right: "16px",
|
|
2251
|
+
zIndex: 2147483647,
|
|
2252
|
+
padding: "10px 14px",
|
|
2253
|
+
borderRadius: "999px",
|
|
2254
|
+
border: "none",
|
|
2255
|
+
background: "#F59E0B",
|
|
2256
|
+
color: "#111827",
|
|
2257
|
+
fontWeight: 600,
|
|
2258
|
+
fontSize: "13px",
|
|
2259
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
2260
|
+
boxShadow: "0 4px 14px rgba(0, 0, 0, 0.25)",
|
|
2261
|
+
cursor: "pointer",
|
|
2262
|
+
display: "flex",
|
|
2263
|
+
alignItems: "center",
|
|
2264
|
+
gap: "6px"
|
|
2265
|
+
},
|
|
2266
|
+
confirm: { background: "#DC2626", color: "#FFFFFF" },
|
|
2267
|
+
busy: { opacity: 0.6, cursor: "wait" }
|
|
2268
|
+
};
|
|
2269
|
+
var CONFIRM_TIMEOUT_MS = 3e3;
|
|
2270
|
+
function DevSkipOnboardingFab({ defaults }) {
|
|
2271
|
+
const hook = (0, import_sdk6.useHook)();
|
|
2272
|
+
const { slug } = useAppConfig();
|
|
2273
|
+
const [state, setState] = (0, import_react13.useState)("idle");
|
|
2274
|
+
const [errorMsg, setErrorMsg] = (0, import_react13.useState)(null);
|
|
2275
|
+
const timerRef = (0, import_react13.useRef)(null);
|
|
2276
|
+
const clearTimer = (0, import_react13.useCallback)(() => {
|
|
2277
|
+
if (timerRef.current) {
|
|
2278
|
+
clearTimeout(timerRef.current);
|
|
2279
|
+
timerRef.current = null;
|
|
2280
|
+
}
|
|
2281
|
+
}, []);
|
|
2282
|
+
const onClick = (0, import_react13.useCallback)(async () => {
|
|
2283
|
+
if (state === "busy") return;
|
|
2284
|
+
if (state === "idle" || state === "error") {
|
|
2285
|
+
setState("confirm");
|
|
2286
|
+
setErrorMsg(null);
|
|
2287
|
+
clearTimer();
|
|
2288
|
+
timerRef.current = setTimeout(() => setState("idle"), CONFIRM_TIMEOUT_MS);
|
|
2289
|
+
return;
|
|
2290
|
+
}
|
|
2291
|
+
clearTimer();
|
|
2292
|
+
setState("busy");
|
|
2293
|
+
try {
|
|
2294
|
+
await skipOnboarding(hook, defaults, slug);
|
|
2295
|
+
} catch (err) {
|
|
2296
|
+
setState("error");
|
|
2297
|
+
setErrorMsg(err instanceof Error ? err.message : String(err));
|
|
2298
|
+
}
|
|
2299
|
+
}, [state, hook, defaults, slug, clearTimer]);
|
|
2300
|
+
const label = (() => {
|
|
2301
|
+
if (state === "busy") return "skipping\u2026";
|
|
2302
|
+
if (state === "confirm") return "tap again to confirm";
|
|
2303
|
+
if (state === "error") return `failed \u2014 tap to retry`;
|
|
2304
|
+
return hook.authStatus === "authenticated" ? "\u26A1 skip onboarding" : "\u26A1 skip + signup";
|
|
2305
|
+
})();
|
|
2306
|
+
const style = {
|
|
2307
|
+
...STYLES.base,
|
|
2308
|
+
...state === "confirm" || state === "error" ? STYLES.confirm : {},
|
|
2309
|
+
...state === "busy" ? STYLES.busy : {}
|
|
2310
|
+
};
|
|
2311
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2312
|
+
"button",
|
|
2313
|
+
{
|
|
2314
|
+
type: "button",
|
|
2315
|
+
"data-testid": "dev-skip-onboarding-fab",
|
|
2316
|
+
"aria-label": "Skip onboarding (staging dev only)",
|
|
2317
|
+
style,
|
|
2318
|
+
onClick,
|
|
2319
|
+
title: errorMsg ?? void 0,
|
|
2320
|
+
children: label
|
|
2321
|
+
}
|
|
2322
|
+
);
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
// src/internal/PaymentReturnHandler.tsx
|
|
2326
|
+
var import_react14 = require("react");
|
|
2327
|
+
var import_sdk7 = require("@hook-sdk/sdk");
|
|
2328
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
2098
2329
|
var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
|
|
2099
2330
|
var MAX_CYCLES = 3;
|
|
2100
2331
|
var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
|
|
2101
2332
|
function PaymentReturnHandler({ children }) {
|
|
2102
|
-
const { subscription } = (0,
|
|
2103
|
-
const subRef = (0,
|
|
2333
|
+
const { subscription, track: track2 } = (0, import_sdk7.useHook)();
|
|
2334
|
+
const subRef = (0, import_react14.useRef)(subscription);
|
|
2104
2335
|
subRef.current = subscription;
|
|
2105
|
-
const runIdRef = (0,
|
|
2106
|
-
const cyclesRef = (0,
|
|
2107
|
-
const
|
|
2108
|
-
const
|
|
2336
|
+
const runIdRef = (0, import_react14.useRef)(0);
|
|
2337
|
+
const cyclesRef = (0, import_react14.useRef)(0);
|
|
2338
|
+
const startMsRef = (0, import_react14.useRef)(0);
|
|
2339
|
+
const [state, setState] = (0, import_react14.useState)("idle");
|
|
2340
|
+
const runPoll = (0, import_react14.useCallback)(() => {
|
|
2109
2341
|
const runId = ++runIdRef.current;
|
|
2342
|
+
const isFirstRun = cyclesRef.current === 0;
|
|
2110
2343
|
cyclesRef.current += 1;
|
|
2344
|
+
if (isFirstRun) {
|
|
2345
|
+
startMsRef.current = Date.now();
|
|
2346
|
+
track2("payment_confirmation_started", {});
|
|
2347
|
+
}
|
|
2111
2348
|
setState("confirming");
|
|
2112
2349
|
let attempts = 0;
|
|
2113
2350
|
const tick = async () => {
|
|
@@ -2120,6 +2357,11 @@ function PaymentReturnHandler({ children }) {
|
|
|
2120
2357
|
if (runIdRef.current !== runId) return;
|
|
2121
2358
|
const status = subRef.current.status();
|
|
2122
2359
|
if (status === "active" || status === "trialing") {
|
|
2360
|
+
track2("payment_confirmation_succeeded", {
|
|
2361
|
+
cycle_count: cyclesRef.current,
|
|
2362
|
+
attempt_count: attempts,
|
|
2363
|
+
duration_ms: Date.now() - startMsRef.current
|
|
2364
|
+
});
|
|
2123
2365
|
const cleanUrl = new URL(window.location.href);
|
|
2124
2366
|
cleanUrl.searchParams.delete("paymentReturn");
|
|
2125
2367
|
window.history.replaceState({}, "", cleanUrl.toString());
|
|
@@ -2130,6 +2372,9 @@ function PaymentReturnHandler({ children }) {
|
|
|
2130
2372
|
const delay = BACKOFF_MS[attempts - 1];
|
|
2131
2373
|
if (delay === void 0) {
|
|
2132
2374
|
if (cyclesRef.current >= MAX_CYCLES) {
|
|
2375
|
+
track2("payment_confirmation_timed_out", {
|
|
2376
|
+
total_duration_ms: Date.now() - startMsRef.current
|
|
2377
|
+
});
|
|
2133
2378
|
setState("timeout");
|
|
2134
2379
|
} else {
|
|
2135
2380
|
setState("waiting");
|
|
@@ -2139,8 +2384,8 @@ function PaymentReturnHandler({ children }) {
|
|
|
2139
2384
|
setTimeout(tick, delay);
|
|
2140
2385
|
};
|
|
2141
2386
|
void tick();
|
|
2142
|
-
}, []);
|
|
2143
|
-
(0,
|
|
2387
|
+
}, [track2]);
|
|
2388
|
+
(0, import_react14.useEffect)(() => {
|
|
2144
2389
|
if (typeof window === "undefined") return;
|
|
2145
2390
|
const url = new URL(window.location.href);
|
|
2146
2391
|
if (url.searchParams.get("paymentReturn") !== "1") return;
|
|
@@ -2150,26 +2395,26 @@ function PaymentReturnHandler({ children }) {
|
|
|
2150
2395
|
runIdRef.current++;
|
|
2151
2396
|
};
|
|
2152
2397
|
}, [runPoll]);
|
|
2153
|
-
const goHome = (0,
|
|
2398
|
+
const goHome = (0, import_react14.useCallback)(() => {
|
|
2154
2399
|
const cleanUrl = new URL(window.location.href);
|
|
2155
2400
|
cleanUrl.searchParams.delete("paymentReturn");
|
|
2156
2401
|
cleanUrl.pathname = "/app/home";
|
|
2157
2402
|
window.location.href = cleanUrl.toString();
|
|
2158
2403
|
}, []);
|
|
2159
2404
|
if (state === "confirming") {
|
|
2160
|
-
return /* @__PURE__ */ (0,
|
|
2405
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
|
|
2161
2406
|
}
|
|
2162
2407
|
if (state === "waiting") {
|
|
2163
|
-
return /* @__PURE__ */ (0,
|
|
2164
|
-
/* @__PURE__ */ (0,
|
|
2165
|
-
/* @__PURE__ */ (0,
|
|
2408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
2409
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
|
|
2410
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
|
|
2166
2411
|
] }) });
|
|
2167
2412
|
}
|
|
2168
2413
|
if (state === "timeout") {
|
|
2169
|
-
return /* @__PURE__ */ (0,
|
|
2170
|
-
/* @__PURE__ */ (0,
|
|
2171
|
-
/* @__PURE__ */ (0,
|
|
2172
|
-
/* @__PURE__ */ (0,
|
|
2414
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
2415
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { marginBottom: 16 }, children: "Ainda n\xE3o conseguimos confirmar seu pagamento com o banco. Voc\xEA pode tentar de novo, voltar pro app, ou falar com a gente." }),
|
|
2416
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
2417
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2173
2418
|
"button",
|
|
2174
2419
|
{
|
|
2175
2420
|
type: "button",
|
|
@@ -2182,7 +2427,7 @@ function PaymentReturnHandler({ children }) {
|
|
|
2182
2427
|
children: "Tentar de novo"
|
|
2183
2428
|
}
|
|
2184
2429
|
),
|
|
2185
|
-
/* @__PURE__ */ (0,
|
|
2430
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2186
2431
|
"button",
|
|
2187
2432
|
{
|
|
2188
2433
|
type: "button",
|
|
@@ -2192,7 +2437,7 @@ function PaymentReturnHandler({ children }) {
|
|
|
2192
2437
|
children: "Voltar pro app"
|
|
2193
2438
|
}
|
|
2194
2439
|
),
|
|
2195
|
-
/* @__PURE__ */ (0,
|
|
2440
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2196
2441
|
"a",
|
|
2197
2442
|
{
|
|
2198
2443
|
href: SUPPORT_MAILTO,
|
|
@@ -2204,7 +2449,7 @@ function PaymentReturnHandler({ children }) {
|
|
|
2204
2449
|
] })
|
|
2205
2450
|
] }) });
|
|
2206
2451
|
}
|
|
2207
|
-
return /* @__PURE__ */ (0,
|
|
2452
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
|
|
2208
2453
|
}
|
|
2209
2454
|
var overlayStyle2 = {
|
|
2210
2455
|
position: "fixed",
|
|
@@ -2243,7 +2488,7 @@ var linkStyle = {
|
|
|
2243
2488
|
};
|
|
2244
2489
|
|
|
2245
2490
|
// src/AppRoot.tsx
|
|
2246
|
-
var
|
|
2491
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
2247
2492
|
function buildLegacyConfigShim(config) {
|
|
2248
2493
|
const paywall = config.paywall;
|
|
2249
2494
|
const isFree = paywall.mode === "free";
|
|
@@ -2291,9 +2536,10 @@ function AppRoot(props) {
|
|
|
2291
2536
|
Forgot,
|
|
2292
2537
|
Reset,
|
|
2293
2538
|
EmailVerify,
|
|
2294
|
-
Paywall,
|
|
2539
|
+
Paywall: Paywall2,
|
|
2295
2540
|
Onboarding,
|
|
2296
|
-
PreAuthFlow
|
|
2541
|
+
PreAuthFlow,
|
|
2542
|
+
devSkipOnboarding
|
|
2297
2543
|
} = props;
|
|
2298
2544
|
if (!Login || !Signup || !Forgot || !Reset) {
|
|
2299
2545
|
throw new Error(
|
|
@@ -2301,7 +2547,7 @@ function AppRoot(props) {
|
|
|
2301
2547
|
);
|
|
2302
2548
|
}
|
|
2303
2549
|
const config = parseAppConfig(rawConfig);
|
|
2304
|
-
if (config.paywall.mode !== "free" && !
|
|
2550
|
+
if (config.paywall.mode !== "free" && !Paywall2) {
|
|
2305
2551
|
throw new Error(
|
|
2306
2552
|
"[hook-template] <AppRoot>: Paywall slot prop is required when config.paywall.mode != 'free'."
|
|
2307
2553
|
);
|
|
@@ -2316,19 +2562,19 @@ function AppRoot(props) {
|
|
|
2316
2562
|
"[hook-template] <AppRoot>: PreAuthFlow slot prop is required when config.onboarding.trigger === 'pre_signup_custom'."
|
|
2317
2563
|
);
|
|
2318
2564
|
}
|
|
2319
|
-
const legacyShim = (0,
|
|
2565
|
+
const legacyShim = (0, import_react15.useMemo)(() => buildLegacyConfigShim(config), [config]);
|
|
2320
2566
|
const Router = testRouter === "memory" ? import_react_router_dom2.MemoryRouter : import_react_router_dom2.BrowserRouter;
|
|
2321
2567
|
const basename = `/app/${config.slug}`;
|
|
2322
2568
|
const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
|
|
2323
2569
|
const position = config.install_prompt?.position ?? "post-paywall";
|
|
2324
|
-
const subscriptionGated = /* @__PURE__ */ (0,
|
|
2570
|
+
const subscriptionGated = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SubscriptionGate, { Paywall: Paywall2 ?? FallbackPaywall, children: position === "post-paywall" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(InstallGate, { position: "post-paywall", children: [
|
|
2325
2571
|
children,
|
|
2326
|
-
/* @__PURE__ */ (0,
|
|
2327
|
-
] }) : /* @__PURE__ */ (0,
|
|
2572
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(PushPrompt, {})
|
|
2573
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
2328
2574
|
children,
|
|
2329
|
-
/* @__PURE__ */ (0,
|
|
2575
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(PushPrompt, {})
|
|
2330
2576
|
] }) });
|
|
2331
|
-
const authGated = /* @__PURE__ */ (0,
|
|
2577
|
+
const authGated = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2332
2578
|
AuthGated,
|
|
2333
2579
|
{
|
|
2334
2580
|
config,
|
|
@@ -2337,18 +2583,19 @@ function AppRoot(props) {
|
|
|
2337
2583
|
Forgot,
|
|
2338
2584
|
Reset,
|
|
2339
2585
|
EmailVerify,
|
|
2340
|
-
Paywall,
|
|
2586
|
+
Paywall: Paywall2,
|
|
2341
2587
|
Onboarding,
|
|
2342
2588
|
PreAuthFlow,
|
|
2343
2589
|
children: subscriptionGated
|
|
2344
2590
|
}
|
|
2345
2591
|
);
|
|
2346
|
-
const routedTree = /* @__PURE__ */ (0,
|
|
2347
|
-
/* @__PURE__ */ (0,
|
|
2348
|
-
/* @__PURE__ */ (0,
|
|
2349
|
-
position === "pre-auth" ? /* @__PURE__ */ (0,
|
|
2592
|
+
const routedTree = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Router, { ...routerProps, children: [
|
|
2593
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DeepLinkHandler, { deepLinks: config.deepLinks }),
|
|
2594
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SessionExpiredBanner, {}),
|
|
2595
|
+
position === "pre-auth" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(InstallGate, { position: "pre-auth", children: authGated }) : authGated,
|
|
2596
|
+
isDevToolsEnabled() && devSkipOnboarding ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DevSkipOnboardingFab, { defaults: devSkipOnboarding.defaults }) : null
|
|
2350
2597
|
] });
|
|
2351
|
-
return /* @__PURE__ */ (0,
|
|
2598
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ErrorBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(AppConfigProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(PersistenceRegistry, { config: config.persistedKeys, children: config.i18n ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2352
2599
|
I18nProvider,
|
|
2353
2600
|
{
|
|
2354
2601
|
defaultLocale: config.i18n.defaultLocale,
|
|
@@ -2368,37 +2615,37 @@ function AuthGated({
|
|
|
2368
2615
|
EmailVerify,
|
|
2369
2616
|
PreAuthFlow
|
|
2370
2617
|
}) {
|
|
2371
|
-
const { authStatus } = (0,
|
|
2618
|
+
const { authStatus } = (0, import_sdk8.useHook)();
|
|
2372
2619
|
if (authStatus === "loading") return null;
|
|
2373
2620
|
if (authStatus !== "authenticated") {
|
|
2374
2621
|
if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
|
|
2375
|
-
return /* @__PURE__ */ (0,
|
|
2376
|
-
/* @__PURE__ */ (0,
|
|
2377
|
-
/* @__PURE__ */ (0,
|
|
2378
|
-
/* @__PURE__ */ (0,
|
|
2379
|
-
/* @__PURE__ */ (0,
|
|
2380
|
-
EmailVerify ? /* @__PURE__ */ (0,
|
|
2381
|
-
/* @__PURE__ */ (0,
|
|
2622
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react_router_dom2.Routes, { children: [
|
|
2623
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/signin", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Login, {}) }),
|
|
2624
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/signup", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Signup, {}) }),
|
|
2625
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/forgot", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Forgot, {}) }),
|
|
2626
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/reset", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Reset, {}) }),
|
|
2627
|
+
EmailVerify ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/verify", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(EmailVerify, {}) }) : null,
|
|
2628
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/*", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(PreAuthFlow, {}) })
|
|
2382
2629
|
] });
|
|
2383
2630
|
}
|
|
2384
|
-
return /* @__PURE__ */ (0,
|
|
2385
|
-
/* @__PURE__ */ (0,
|
|
2386
|
-
/* @__PURE__ */ (0,
|
|
2387
|
-
/* @__PURE__ */ (0,
|
|
2388
|
-
/* @__PURE__ */ (0,
|
|
2389
|
-
EmailVerify ? /* @__PURE__ */ (0,
|
|
2390
|
-
/* @__PURE__ */ (0,
|
|
2631
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react_router_dom2.Routes, { children: [
|
|
2632
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Login, {}) }),
|
|
2633
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/signup", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Signup, {}) }),
|
|
2634
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/forgot", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Forgot, {}) }),
|
|
2635
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/reset", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Reset, {}) }),
|
|
2636
|
+
EmailVerify ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "/verify", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(EmailVerify, {}) }) : null,
|
|
2637
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_router_dom2.Navigate, { to: "/", replace: true }) })
|
|
2391
2638
|
] });
|
|
2392
2639
|
}
|
|
2393
|
-
return /* @__PURE__ */ (0,
|
|
2640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_jsx_runtime22.Fragment, { children });
|
|
2394
2641
|
}
|
|
2395
2642
|
function FallbackPaywall() {
|
|
2396
2643
|
return null;
|
|
2397
2644
|
}
|
|
2398
2645
|
|
|
2399
2646
|
// src/hooks/usePush.ts
|
|
2400
|
-
var
|
|
2401
|
-
var
|
|
2647
|
+
var import_react16 = require("react");
|
|
2648
|
+
var import_sdk9 = require("@hook-sdk/sdk");
|
|
2402
2649
|
var DISMISS_STORAGE_KEY = "push:dismissed-until";
|
|
2403
2650
|
var DISMISS_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
|
|
2404
2651
|
function detectIosNeedsInstall() {
|
|
@@ -2442,12 +2689,12 @@ function deriveState(push) {
|
|
|
2442
2689
|
return { kind: "prompt" };
|
|
2443
2690
|
}
|
|
2444
2691
|
function usePush() {
|
|
2445
|
-
const { push } = (0,
|
|
2446
|
-
const [state, setState] = (0,
|
|
2447
|
-
(0,
|
|
2692
|
+
const { push } = (0, import_sdk9.useHook)();
|
|
2693
|
+
const [state, setState] = (0, import_react16.useState)(() => deriveState(push));
|
|
2694
|
+
(0, import_react16.useEffect)(() => {
|
|
2448
2695
|
setState(deriveState(push));
|
|
2449
2696
|
}, [push]);
|
|
2450
|
-
const subscribe = (0,
|
|
2697
|
+
const subscribe = (0, import_react16.useCallback)(async () => {
|
|
2451
2698
|
try {
|
|
2452
2699
|
await push.subscribe();
|
|
2453
2700
|
setState({ kind: "subscribed" });
|
|
@@ -2459,7 +2706,7 @@ function usePush() {
|
|
|
2459
2706
|
throw e;
|
|
2460
2707
|
}
|
|
2461
2708
|
}, [push]);
|
|
2462
|
-
const unsubscribe = (0,
|
|
2709
|
+
const unsubscribe = (0, import_react16.useCallback)(async () => {
|
|
2463
2710
|
try {
|
|
2464
2711
|
await push.unsubscribe();
|
|
2465
2712
|
setState({ kind: "prompt" });
|
|
@@ -2468,7 +2715,7 @@ function usePush() {
|
|
|
2468
2715
|
throw e;
|
|
2469
2716
|
}
|
|
2470
2717
|
}, [push]);
|
|
2471
|
-
const dismiss = (0,
|
|
2718
|
+
const dismiss = (0, import_react16.useCallback)(() => {
|
|
2472
2719
|
if (typeof localStorage !== "undefined") {
|
|
2473
2720
|
try {
|
|
2474
2721
|
localStorage.setItem(DISMISS_STORAGE_KEY, String(Date.now() + DISMISS_TTL_MS2));
|
|
@@ -2481,51 +2728,27 @@ function usePush() {
|
|
|
2481
2728
|
}
|
|
2482
2729
|
|
|
2483
2730
|
// src/components/PushPrompt.tsx
|
|
2484
|
-
var
|
|
2485
|
-
function platformRecoveryCopy(texts) {
|
|
2486
|
-
if (typeof navigator === "undefined") return null;
|
|
2487
|
-
const ua = navigator.userAgent || "";
|
|
2488
|
-
const platform = detectPlatform(ua);
|
|
2489
|
-
switch (platform) {
|
|
2490
|
-
case "ios-safari":
|
|
2491
|
-
case "ios-other":
|
|
2492
|
-
return texts.deniedRecoveryIos ?? null;
|
|
2493
|
-
case "android":
|
|
2494
|
-
return texts.deniedRecoveryAndroid ?? null;
|
|
2495
|
-
case "desktop":
|
|
2496
|
-
return texts.deniedRecoveryDesktop ?? null;
|
|
2497
|
-
case "in-app":
|
|
2498
|
-
return texts.deniedRecoveryInApp ?? null;
|
|
2499
|
-
default:
|
|
2500
|
-
return null;
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2731
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
2503
2732
|
function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
|
|
2504
2733
|
const { state, subscribe } = usePush();
|
|
2505
|
-
if (state.kind === "
|
|
2506
|
-
|
|
2507
|
-
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
|
|
2508
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h3", { children: texts.iosInstallTitle }),
|
|
2509
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { children: texts.iosInstallBody }),
|
|
2510
|
-
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
|
|
2511
|
-
] });
|
|
2734
|
+
if (state.kind === "denied" || state.kind === "dismissed" || state.kind === "subscribed") {
|
|
2735
|
+
return null;
|
|
2512
2736
|
}
|
|
2513
|
-
if (state.kind === "
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
/* @__PURE__ */ (0,
|
|
2517
|
-
/* @__PURE__ */ (0,
|
|
2518
|
-
recovery && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { "data-testid": "denied-recovery", children: recovery })
|
|
2737
|
+
if (state.kind === "ios_needs_install") {
|
|
2738
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
|
|
2739
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h3", { children: texts.iosInstallTitle }),
|
|
2740
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { children: texts.iosInstallBody }),
|
|
2741
|
+
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
|
|
2519
2742
|
] });
|
|
2520
2743
|
}
|
|
2521
2744
|
if (state.kind === "unsupported") {
|
|
2522
|
-
return /* @__PURE__ */ (0,
|
|
2745
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className, role: "region", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { children: texts.unsupportedBody }) });
|
|
2523
2746
|
}
|
|
2524
2747
|
if (state.kind === "error") {
|
|
2525
|
-
return /* @__PURE__ */ (0,
|
|
2748
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { children: state.message }) });
|
|
2526
2749
|
}
|
|
2527
|
-
return /* @__PURE__ */ (0,
|
|
2528
|
-
/* @__PURE__ */ (0,
|
|
2750
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className, role: "region", children: [
|
|
2751
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2529
2752
|
"button",
|
|
2530
2753
|
{
|
|
2531
2754
|
type: "button",
|
|
@@ -2539,67 +2762,67 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
|
|
|
2539
2762
|
children: texts.cta
|
|
2540
2763
|
}
|
|
2541
2764
|
),
|
|
2542
|
-
onDeclined && /* @__PURE__ */ (0,
|
|
2765
|
+
onDeclined && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
|
|
2543
2766
|
] });
|
|
2544
2767
|
}
|
|
2545
2768
|
|
|
2546
2769
|
// src/components/LanguageSwitcher.tsx
|
|
2547
|
-
var
|
|
2548
|
-
var
|
|
2770
|
+
var import_sdk10 = require("@hook-sdk/sdk");
|
|
2771
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2549
2772
|
function LanguageSwitcher({ id, className, label = "Language" }) {
|
|
2550
2773
|
const config = useAppConfig();
|
|
2551
2774
|
const i18nConfig = config.i18n;
|
|
2552
|
-
const [userLocale, setUserLocale] = (0,
|
|
2775
|
+
const [userLocale, setUserLocale] = (0, import_sdk10.usePersistedState)(
|
|
2553
2776
|
"user-locale",
|
|
2554
2777
|
i18nConfig?.defaultLocale ?? "en-US"
|
|
2555
2778
|
);
|
|
2556
2779
|
if (!i18nConfig) return null;
|
|
2557
|
-
return /* @__PURE__ */ (0,
|
|
2558
|
-
label ? /* @__PURE__ */ (0,
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
2780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className, children: [
|
|
2781
|
+
label ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: label }) : null,
|
|
2782
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2560
2783
|
"select",
|
|
2561
2784
|
{
|
|
2562
2785
|
id,
|
|
2563
2786
|
value: userLocale,
|
|
2564
2787
|
onChange: (e) => setUserLocale(e.target.value),
|
|
2565
2788
|
"data-testid": "language-switcher",
|
|
2566
|
-
children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */ (0,
|
|
2789
|
+
children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("option", { value: loc, children: loc }, loc))
|
|
2567
2790
|
}
|
|
2568
2791
|
)
|
|
2569
2792
|
] });
|
|
2570
2793
|
}
|
|
2571
2794
|
|
|
2572
2795
|
// src/defaults/LoadingState.tsx
|
|
2573
|
-
var
|
|
2796
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2574
2797
|
function LoadingState({ message }) {
|
|
2575
|
-
return /* @__PURE__ */ (0,
|
|
2798
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: message ?? "Carregando..." }) });
|
|
2576
2799
|
}
|
|
2577
2800
|
|
|
2578
2801
|
// src/defaults/EmptyState.tsx
|
|
2579
|
-
var
|
|
2802
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
2580
2803
|
function EmptyState({ title, description, action }) {
|
|
2581
|
-
return /* @__PURE__ */ (0,
|
|
2582
|
-
/* @__PURE__ */ (0,
|
|
2583
|
-
description && /* @__PURE__ */ (0,
|
|
2584
|
-
action && /* @__PURE__ */ (0,
|
|
2804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
|
|
2805
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h2", { style: { marginBottom: 8 }, children: title }),
|
|
2806
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { style: { opacity: 0.7 }, children: description }),
|
|
2807
|
+
action && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { style: { marginTop: 16 }, children: action })
|
|
2585
2808
|
] });
|
|
2586
2809
|
}
|
|
2587
2810
|
|
|
2588
2811
|
// src/hooks/useLoginForm.ts
|
|
2589
|
-
var
|
|
2590
|
-
var
|
|
2812
|
+
var import_react17 = require("react");
|
|
2813
|
+
var import_sdk12 = require("@hook-sdk/sdk");
|
|
2591
2814
|
|
|
2592
2815
|
// src/errors.ts
|
|
2593
|
-
var
|
|
2816
|
+
var import_sdk11 = require("@hook-sdk/sdk");
|
|
2594
2817
|
function mapSdkError(err) {
|
|
2595
|
-
if (err instanceof
|
|
2818
|
+
if (err instanceof import_sdk11.SdkRateLimitError) {
|
|
2596
2819
|
return {
|
|
2597
2820
|
code: "rate_limited",
|
|
2598
2821
|
message: `Aguarde ${err.retryAfter}s e tente novamente.`,
|
|
2599
2822
|
retryAfter: err.retryAfter
|
|
2600
2823
|
};
|
|
2601
2824
|
}
|
|
2602
|
-
if (err instanceof
|
|
2825
|
+
if (err instanceof import_sdk11.SdkAuthError) {
|
|
2603
2826
|
const detail = err.detail;
|
|
2604
2827
|
if (detail === "email_unverified") {
|
|
2605
2828
|
return { code: "email_unverified", message: "Confirme seu e-mail antes de entrar." };
|
|
@@ -2609,7 +2832,7 @@ function mapSdkError(err) {
|
|
|
2609
2832
|
}
|
|
2610
2833
|
return { code: "invalid_credentials", message: "E-mail ou senha inv\xE1lidos." };
|
|
2611
2834
|
}
|
|
2612
|
-
if (err instanceof
|
|
2835
|
+
if (err instanceof import_sdk11.SdkError && err.httpStatus === 0) {
|
|
2613
2836
|
return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
|
|
2614
2837
|
}
|
|
2615
2838
|
if (err instanceof TypeError) {
|
|
@@ -2622,20 +2845,20 @@ function mapSdkError(err) {
|
|
|
2622
2845
|
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2623
2846
|
var MIN_PASSWORD = 8;
|
|
2624
2847
|
function useLoginForm() {
|
|
2625
|
-
const { auth } = (0,
|
|
2626
|
-
const [email, setEmail] = (0,
|
|
2627
|
-
const [password, setPassword] = (0,
|
|
2628
|
-
const [submitting, setSubmitting] = (0,
|
|
2629
|
-
const [error, setError] = (0,
|
|
2630
|
-
const [touchedEmail, setTouchedEmail] = (0,
|
|
2631
|
-
const [touchedPassword, setTouchedPassword] = (0,
|
|
2632
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = (0,
|
|
2633
|
-
const validateEmail = (0,
|
|
2848
|
+
const { auth } = (0, import_sdk12.useHook)();
|
|
2849
|
+
const [email, setEmail] = (0, import_react17.useState)("");
|
|
2850
|
+
const [password, setPassword] = (0, import_react17.useState)("");
|
|
2851
|
+
const [submitting, setSubmitting] = (0, import_react17.useState)(false);
|
|
2852
|
+
const [error, setError] = (0, import_react17.useState)(null);
|
|
2853
|
+
const [touchedEmail, setTouchedEmail] = (0, import_react17.useState)(false);
|
|
2854
|
+
const [touchedPassword, setTouchedPassword] = (0, import_react17.useState)(false);
|
|
2855
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react17.useState)(false);
|
|
2856
|
+
const validateEmail = (0, import_react17.useMemo)(() => {
|
|
2634
2857
|
if (email.length === 0) return null;
|
|
2635
2858
|
if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2636
2859
|
return null;
|
|
2637
2860
|
}, [email]);
|
|
2638
|
-
const validatePassword = (0,
|
|
2861
|
+
const validatePassword = (0, import_react17.useMemo)(() => {
|
|
2639
2862
|
if (password.length === 0) return null;
|
|
2640
2863
|
if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
|
|
2641
2864
|
return null;
|
|
@@ -2643,7 +2866,7 @@ function useLoginForm() {
|
|
|
2643
2866
|
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
2644
2867
|
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
2645
2868
|
const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
|
|
2646
|
-
const submit = (0,
|
|
2869
|
+
const submit = (0, import_react17.useCallback)(async () => {
|
|
2647
2870
|
setFormSubmitAttempted(true);
|
|
2648
2871
|
if (!canSubmit) return false;
|
|
2649
2872
|
setSubmitting(true);
|
|
@@ -2677,32 +2900,32 @@ function useLoginForm() {
|
|
|
2677
2900
|
}
|
|
2678
2901
|
|
|
2679
2902
|
// src/hooks/useSignupForm.ts
|
|
2680
|
-
var
|
|
2681
|
-
var
|
|
2903
|
+
var import_react18 = require("react");
|
|
2904
|
+
var import_sdk13 = require("@hook-sdk/sdk");
|
|
2682
2905
|
var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2683
2906
|
var MIN_PASSWORD2 = 8;
|
|
2684
2907
|
function useSignupForm() {
|
|
2685
|
-
const { auth } = (0,
|
|
2686
|
-
const [name, setName] = (0,
|
|
2687
|
-
const [email, setEmail] = (0,
|
|
2688
|
-
const [password, setPassword] = (0,
|
|
2689
|
-
const [submitting, setSubmitting] = (0,
|
|
2690
|
-
const [error, setError] = (0,
|
|
2691
|
-
const [touchedName, setTouchedName] = (0,
|
|
2692
|
-
const [touchedEmail, setTouchedEmail] = (0,
|
|
2693
|
-
const [touchedPassword, setTouchedPassword] = (0,
|
|
2694
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = (0,
|
|
2695
|
-
const validateName = (0,
|
|
2908
|
+
const { auth } = (0, import_sdk13.useHook)();
|
|
2909
|
+
const [name, setName] = (0, import_react18.useState)("");
|
|
2910
|
+
const [email, setEmail] = (0, import_react18.useState)("");
|
|
2911
|
+
const [password, setPassword] = (0, import_react18.useState)("");
|
|
2912
|
+
const [submitting, setSubmitting] = (0, import_react18.useState)(false);
|
|
2913
|
+
const [error, setError] = (0, import_react18.useState)(null);
|
|
2914
|
+
const [touchedName, setTouchedName] = (0, import_react18.useState)(false);
|
|
2915
|
+
const [touchedEmail, setTouchedEmail] = (0, import_react18.useState)(false);
|
|
2916
|
+
const [touchedPassword, setTouchedPassword] = (0, import_react18.useState)(false);
|
|
2917
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react18.useState)(false);
|
|
2918
|
+
const validateName = (0, import_react18.useMemo)(() => {
|
|
2696
2919
|
if (name.length === 0) return null;
|
|
2697
2920
|
if (name.trim().length < 2) return "Nome muito curto.";
|
|
2698
2921
|
return null;
|
|
2699
2922
|
}, [name]);
|
|
2700
|
-
const validateEmail = (0,
|
|
2923
|
+
const validateEmail = (0, import_react18.useMemo)(() => {
|
|
2701
2924
|
if (email.length === 0) return null;
|
|
2702
2925
|
if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2703
2926
|
return null;
|
|
2704
2927
|
}, [email]);
|
|
2705
|
-
const validatePassword = (0,
|
|
2928
|
+
const validatePassword = (0, import_react18.useMemo)(() => {
|
|
2706
2929
|
if (password.length === 0) return null;
|
|
2707
2930
|
if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
|
|
2708
2931
|
return null;
|
|
@@ -2711,7 +2934,7 @@ function useSignupForm() {
|
|
|
2711
2934
|
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
2712
2935
|
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
2713
2936
|
const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
|
|
2714
|
-
const submit = (0,
|
|
2937
|
+
const submit = (0, import_react18.useCallback)(async () => {
|
|
2715
2938
|
setFormSubmitAttempted(true);
|
|
2716
2939
|
if (!canSubmit) return false;
|
|
2717
2940
|
setSubmitting(true);
|
|
@@ -2749,25 +2972,25 @@ function useSignupForm() {
|
|
|
2749
2972
|
}
|
|
2750
2973
|
|
|
2751
2974
|
// src/hooks/useForgotForm.ts
|
|
2752
|
-
var
|
|
2753
|
-
var
|
|
2975
|
+
var import_react19 = require("react");
|
|
2976
|
+
var import_sdk14 = require("@hook-sdk/sdk");
|
|
2754
2977
|
var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2755
2978
|
function useForgotForm() {
|
|
2756
|
-
const { auth } = (0,
|
|
2757
|
-
const [email, setEmail] = (0,
|
|
2758
|
-
const [submitting, setSubmitting] = (0,
|
|
2759
|
-
const [sent, setSent] = (0,
|
|
2760
|
-
const [error, setError] = (0,
|
|
2761
|
-
const [touchedEmail, setTouchedEmail] = (0,
|
|
2762
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = (0,
|
|
2763
|
-
const validateEmail = (0,
|
|
2979
|
+
const { auth } = (0, import_sdk14.useHook)();
|
|
2980
|
+
const [email, setEmail] = (0, import_react19.useState)("");
|
|
2981
|
+
const [submitting, setSubmitting] = (0, import_react19.useState)(false);
|
|
2982
|
+
const [sent, setSent] = (0, import_react19.useState)(false);
|
|
2983
|
+
const [error, setError] = (0, import_react19.useState)(null);
|
|
2984
|
+
const [touchedEmail, setTouchedEmail] = (0, import_react19.useState)(false);
|
|
2985
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react19.useState)(false);
|
|
2986
|
+
const validateEmail = (0, import_react19.useMemo)(() => {
|
|
2764
2987
|
if (email.length === 0) return null;
|
|
2765
2988
|
if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2766
2989
|
return null;
|
|
2767
2990
|
}, [email]);
|
|
2768
2991
|
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
2769
2992
|
const canSubmit = email.length > 0 && validateEmail === null && !submitting;
|
|
2770
|
-
const submit = (0,
|
|
2993
|
+
const submit = (0, import_react19.useCallback)(async () => {
|
|
2771
2994
|
setFormSubmitAttempted(true);
|
|
2772
2995
|
if (!canSubmit) return false;
|
|
2773
2996
|
setSubmitting(true);
|
|
@@ -2798,32 +3021,32 @@ function useForgotForm() {
|
|
|
2798
3021
|
}
|
|
2799
3022
|
|
|
2800
3023
|
// src/hooks/useResetForm.ts
|
|
2801
|
-
var
|
|
2802
|
-
var
|
|
3024
|
+
var import_react20 = require("react");
|
|
3025
|
+
var import_sdk15 = require("@hook-sdk/sdk");
|
|
2803
3026
|
var MIN_PASSWORD3 = 12;
|
|
2804
3027
|
function useResetForm() {
|
|
2805
|
-
const { auth } = (0,
|
|
2806
|
-
const [token, setToken] = (0,
|
|
2807
|
-
const [password, setPassword] = (0,
|
|
2808
|
-
const [confirm, setConfirm] = (0,
|
|
2809
|
-
const [submitting, setSubmitting] = (0,
|
|
2810
|
-
const [done, setDone] = (0,
|
|
2811
|
-
const [error, setError] = (0,
|
|
2812
|
-
const [touchedPassword, setTouchedPassword] = (0,
|
|
2813
|
-
const [touchedConfirm, setTouchedConfirm] = (0,
|
|
2814
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = (0,
|
|
2815
|
-
(0,
|
|
3028
|
+
const { auth } = (0, import_sdk15.useHook)();
|
|
3029
|
+
const [token, setToken] = (0, import_react20.useState)(null);
|
|
3030
|
+
const [password, setPassword] = (0, import_react20.useState)("");
|
|
3031
|
+
const [confirm, setConfirm] = (0, import_react20.useState)("");
|
|
3032
|
+
const [submitting, setSubmitting] = (0, import_react20.useState)(false);
|
|
3033
|
+
const [done, setDone] = (0, import_react20.useState)(false);
|
|
3034
|
+
const [error, setError] = (0, import_react20.useState)(null);
|
|
3035
|
+
const [touchedPassword, setTouchedPassword] = (0, import_react20.useState)(false);
|
|
3036
|
+
const [touchedConfirm, setTouchedConfirm] = (0, import_react20.useState)(false);
|
|
3037
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react20.useState)(false);
|
|
3038
|
+
(0, import_react20.useEffect)(() => {
|
|
2816
3039
|
if (typeof window === "undefined") return;
|
|
2817
3040
|
const params = new URLSearchParams(window.location.search);
|
|
2818
3041
|
const t = params.get("token");
|
|
2819
3042
|
setToken(t && t.length > 0 ? t : null);
|
|
2820
3043
|
}, []);
|
|
2821
|
-
const validatePassword = (0,
|
|
3044
|
+
const validatePassword = (0, import_react20.useMemo)(() => {
|
|
2822
3045
|
if (password.length === 0) return null;
|
|
2823
3046
|
if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
|
|
2824
3047
|
return null;
|
|
2825
3048
|
}, [password]);
|
|
2826
|
-
const validateConfirm = (0,
|
|
3049
|
+
const validateConfirm = (0, import_react20.useMemo)(() => {
|
|
2827
3050
|
if (confirm.length === 0) return null;
|
|
2828
3051
|
if (confirm !== password) return "Senhas n\xE3o coincidem.";
|
|
2829
3052
|
return null;
|
|
@@ -2831,7 +3054,7 @@ function useResetForm() {
|
|
|
2831
3054
|
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
2832
3055
|
const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
|
|
2833
3056
|
const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
|
|
2834
|
-
const submit = (0,
|
|
3057
|
+
const submit = (0, import_react20.useCallback)(async () => {
|
|
2835
3058
|
setFormSubmitAttempted(true);
|
|
2836
3059
|
if (!canSubmit || token === null) return;
|
|
2837
3060
|
setSubmitting(true);
|
|
@@ -2871,9 +3094,9 @@ function useResetForm() {
|
|
|
2871
3094
|
}
|
|
2872
3095
|
|
|
2873
3096
|
// src/hooks/usePlan.ts
|
|
2874
|
-
var
|
|
3097
|
+
var import_sdk16 = require("@hook-sdk/sdk");
|
|
2875
3098
|
function usePlan() {
|
|
2876
|
-
const { plan } = (0,
|
|
3099
|
+
const { plan } = (0, import_sdk16.useHook)();
|
|
2877
3100
|
return plan;
|
|
2878
3101
|
}
|
|
2879
3102
|
|
|
@@ -2906,12 +3129,12 @@ function discountPercent(anchorCents, realCents) {
|
|
|
2906
3129
|
}
|
|
2907
3130
|
|
|
2908
3131
|
// src/hooks/useAuthPrimitives.ts
|
|
2909
|
-
var
|
|
2910
|
-
var
|
|
3132
|
+
var import_react21 = require("react");
|
|
3133
|
+
var import_sdk17 = require("@hook-sdk/sdk");
|
|
2911
3134
|
var warned = false;
|
|
2912
3135
|
function useAuthPrimitives() {
|
|
2913
|
-
const { auth } = (0,
|
|
2914
|
-
(0,
|
|
3136
|
+
const { auth } = (0, import_sdk17.useHook)();
|
|
3137
|
+
(0, import_react21.useEffect)(() => {
|
|
2915
3138
|
if (!warned && process.env.NODE_ENV !== "production") {
|
|
2916
3139
|
warned = true;
|
|
2917
3140
|
console.warn(
|
|
@@ -2933,9 +3156,9 @@ function useAuthPrimitives() {
|
|
|
2933
3156
|
}
|
|
2934
3157
|
|
|
2935
3158
|
// src/hooks/useAuth.ts
|
|
2936
|
-
var
|
|
3159
|
+
var import_sdk18 = require("@hook-sdk/sdk");
|
|
2937
3160
|
function useAuth() {
|
|
2938
|
-
const { user, authStatus, auth } = (0,
|
|
3161
|
+
const { user, authStatus, auth } = (0, import_sdk18.useHook)();
|
|
2939
3162
|
return {
|
|
2940
3163
|
user,
|
|
2941
3164
|
authStatus,
|
|
@@ -2944,26 +3167,26 @@ function useAuth() {
|
|
|
2944
3167
|
}
|
|
2945
3168
|
|
|
2946
3169
|
// src/index.ts
|
|
2947
|
-
var
|
|
3170
|
+
var import_sdk23 = require("@hook-sdk/sdk");
|
|
2948
3171
|
|
|
2949
3172
|
// src/hooks/useSubscription.ts
|
|
2950
|
-
var
|
|
3173
|
+
var import_sdk19 = require("@hook-sdk/sdk");
|
|
2951
3174
|
function useSubscription() {
|
|
2952
|
-
const { subscription } = (0,
|
|
3175
|
+
const { subscription } = (0, import_sdk19.useHook)();
|
|
2953
3176
|
return {
|
|
2954
3177
|
status: subscription.status()
|
|
2955
3178
|
};
|
|
2956
3179
|
}
|
|
2957
3180
|
|
|
2958
3181
|
// src/hooks/useReminders.ts
|
|
2959
|
-
var
|
|
2960
|
-
var
|
|
3182
|
+
var import_react22 = require("react");
|
|
3183
|
+
var import_sdk20 = require("@hook-sdk/sdk");
|
|
2961
3184
|
function useReminders() {
|
|
2962
|
-
const { push } = (0,
|
|
3185
|
+
const { push } = (0, import_sdk20.useHook)();
|
|
2963
3186
|
const r = push.reminders;
|
|
2964
|
-
const [reminders, setReminders] = (0,
|
|
2965
|
-
const [loading, setLoading] = (0,
|
|
2966
|
-
const reload = (0,
|
|
3187
|
+
const [reminders, setReminders] = (0, import_react22.useState)([]);
|
|
3188
|
+
const [loading, setLoading] = (0, import_react22.useState)(true);
|
|
3189
|
+
const reload = (0, import_react22.useCallback)(async () => {
|
|
2967
3190
|
setLoading(true);
|
|
2968
3191
|
try {
|
|
2969
3192
|
const next = await r.list();
|
|
@@ -2972,38 +3195,38 @@ function useReminders() {
|
|
|
2972
3195
|
setLoading(false);
|
|
2973
3196
|
}
|
|
2974
3197
|
}, [r]);
|
|
2975
|
-
(0,
|
|
3198
|
+
(0, import_react22.useEffect)(() => {
|
|
2976
3199
|
void reload();
|
|
2977
3200
|
}, [reload]);
|
|
2978
|
-
const setReminder = (0,
|
|
3201
|
+
const setReminder = (0, import_react22.useCallback)(async (input) => {
|
|
2979
3202
|
await r.set(input);
|
|
2980
3203
|
await reload();
|
|
2981
3204
|
}, [r, reload]);
|
|
2982
|
-
const deleteReminder = (0,
|
|
3205
|
+
const deleteReminder = (0, import_react22.useCallback)(async (slot) => {
|
|
2983
3206
|
await r.delete(slot);
|
|
2984
3207
|
await reload();
|
|
2985
3208
|
}, [r, reload]);
|
|
2986
|
-
const schedule = (0,
|
|
3209
|
+
const schedule = (0, import_react22.useCallback)(async (items) => {
|
|
2987
3210
|
return r.schedule(items);
|
|
2988
3211
|
}, [r]);
|
|
2989
|
-
const setFallbacks = (0,
|
|
3212
|
+
const setFallbacks = (0, import_react22.useCallback)(async (items) => {
|
|
2990
3213
|
return r.setFallbacks(items);
|
|
2991
3214
|
}, [r]);
|
|
2992
3215
|
return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
|
|
2993
3216
|
}
|
|
2994
3217
|
|
|
2995
3218
|
// src/hooks/useToast.ts
|
|
2996
|
-
var
|
|
3219
|
+
var import_react23 = require("react");
|
|
2997
3220
|
function useToast() {
|
|
2998
|
-
const [items, setItems] = (0,
|
|
2999
|
-
const show = (0,
|
|
3221
|
+
const [items, setItems] = (0, import_react23.useState)([]);
|
|
3222
|
+
const show = (0, import_react23.useCallback)((message, kind = "info") => {
|
|
3000
3223
|
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3001
3224
|
setItems((prev) => [...prev, { id, message, kind }]);
|
|
3002
3225
|
setTimeout(() => {
|
|
3003
3226
|
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
3004
3227
|
}, 4e3);
|
|
3005
3228
|
}, []);
|
|
3006
|
-
const dismiss = (0,
|
|
3229
|
+
const dismiss = (0, import_react23.useCallback)((id) => {
|
|
3007
3230
|
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
3008
3231
|
}, []);
|
|
3009
3232
|
return { items, show, dismiss };
|
|
@@ -3011,20 +3234,20 @@ function useToast() {
|
|
|
3011
3234
|
|
|
3012
3235
|
// src/RouteBoundary.tsx
|
|
3013
3236
|
var import_react_router_dom3 = require("react-router-dom");
|
|
3014
|
-
var
|
|
3237
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
3015
3238
|
function RouteBoundary({ children }) {
|
|
3016
|
-
return /* @__PURE__ */ (0,
|
|
3239
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_react_router_dom3.Routes, { children: [
|
|
3017
3240
|
children,
|
|
3018
|
-
/* @__PURE__ */ (0,
|
|
3241
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_router_dom3.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(DefaultNotFound, {}) })
|
|
3019
3242
|
] });
|
|
3020
3243
|
}
|
|
3021
3244
|
function DefaultNotFound() {
|
|
3022
|
-
return /* @__PURE__ */ (0,
|
|
3245
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
|
|
3023
3246
|
}
|
|
3024
3247
|
|
|
3025
3248
|
// src/PreAuthShell.tsx
|
|
3026
3249
|
var import_react_router_dom4 = require("react-router-dom");
|
|
3027
|
-
var
|
|
3250
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
3028
3251
|
function PreAuthShell({
|
|
3029
3252
|
basename,
|
|
3030
3253
|
testRouter,
|
|
@@ -3032,20 +3255,20 @@ function PreAuthShell({
|
|
|
3032
3255
|
children
|
|
3033
3256
|
}) {
|
|
3034
3257
|
if (testRouter === "memory") {
|
|
3035
|
-
return /* @__PURE__ */ (0,
|
|
3258
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_router_dom4.MemoryRouter, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_router_dom4.Routes, { children }) });
|
|
3036
3259
|
}
|
|
3037
|
-
return /* @__PURE__ */ (0,
|
|
3260
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_router_dom4.BrowserRouter, { basename, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_router_dom4.Routes, { children }) });
|
|
3038
3261
|
}
|
|
3039
3262
|
|
|
3040
3263
|
// src/OnboardingFlow.tsx
|
|
3041
|
-
var
|
|
3042
|
-
var
|
|
3264
|
+
var import_react25 = require("react");
|
|
3265
|
+
var import_sdk21 = require("@hook-sdk/sdk");
|
|
3043
3266
|
|
|
3044
3267
|
// src/hooks/useOnboardingStep.ts
|
|
3045
|
-
var
|
|
3046
|
-
var OnboardingStepContext = (0,
|
|
3268
|
+
var import_react24 = require("react");
|
|
3269
|
+
var OnboardingStepContext = (0, import_react24.createContext)(null);
|
|
3047
3270
|
function useOnboardingStep() {
|
|
3048
|
-
const ctx = (0,
|
|
3271
|
+
const ctx = (0, import_react24.useContext)(OnboardingStepContext);
|
|
3049
3272
|
if (!ctx) {
|
|
3050
3273
|
throw new Error(
|
|
3051
3274
|
"[hook-template] useOnboardingStep must be used inside <OnboardingFlow>. (G75)"
|
|
@@ -3055,7 +3278,7 @@ function useOnboardingStep() {
|
|
|
3055
3278
|
}
|
|
3056
3279
|
|
|
3057
3280
|
// src/OnboardingFlow.tsx
|
|
3058
|
-
var
|
|
3281
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
3059
3282
|
var isFilled = (v) => v != null && v !== "";
|
|
3060
3283
|
var CURRENT_STEP_FIELD = "currentStep";
|
|
3061
3284
|
function readPersistedStepIdx(draft) {
|
|
@@ -3068,12 +3291,12 @@ function OnboardingFlow({
|
|
|
3068
3291
|
onComplete,
|
|
3069
3292
|
persistKey
|
|
3070
3293
|
}) {
|
|
3071
|
-
const [draft, setDraft, status] = (0,
|
|
3072
|
-
const draftRef = (0,
|
|
3294
|
+
const [draft, setDraft, status] = (0, import_sdk21.usePersistedState)(persistKey, {});
|
|
3295
|
+
const draftRef = (0, import_react25.useRef)(draft);
|
|
3073
3296
|
draftRef.current = draft;
|
|
3074
3297
|
const idx = readPersistedStepIdx(draft);
|
|
3075
3298
|
const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
|
|
3076
|
-
const setIdx = (0,
|
|
3299
|
+
const setIdx = (0, import_react25.useCallback)(
|
|
3077
3300
|
(n) => {
|
|
3078
3301
|
setDraft((prev) => {
|
|
3079
3302
|
const prevIdx = readPersistedStepIdx(prev);
|
|
@@ -3083,7 +3306,7 @@ function OnboardingFlow({
|
|
|
3083
3306
|
},
|
|
3084
3307
|
[setDraft]
|
|
3085
3308
|
);
|
|
3086
|
-
const setValue = (0,
|
|
3309
|
+
const setValue = (0, import_react25.useCallback)(
|
|
3087
3310
|
(patch) => {
|
|
3088
3311
|
draftRef.current = { ...draftRef.current, ...patch };
|
|
3089
3312
|
setDraft((prev) => ({ ...prev, ...patch }));
|
|
@@ -3091,9 +3314,9 @@ function OnboardingFlow({
|
|
|
3091
3314
|
[setDraft]
|
|
3092
3315
|
);
|
|
3093
3316
|
const step = steps[clampedIdx];
|
|
3094
|
-
const hookCtx = (0,
|
|
3317
|
+
const hookCtx = (0, import_sdk21.useHook)();
|
|
3095
3318
|
const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
|
|
3096
|
-
(0,
|
|
3319
|
+
(0, import_react25.useEffect)(() => {
|
|
3097
3320
|
if (status.loading) return;
|
|
3098
3321
|
if (!step) return;
|
|
3099
3322
|
if (!track2) return;
|
|
@@ -3103,11 +3326,11 @@ function OnboardingFlow({
|
|
|
3103
3326
|
total_steps: steps.length
|
|
3104
3327
|
});
|
|
3105
3328
|
}, [step?.id, clampedIdx, steps.length, status.loading, track2]);
|
|
3106
|
-
const valid = (0,
|
|
3329
|
+
const valid = (0, import_react25.useMemo)(
|
|
3107
3330
|
() => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
|
|
3108
3331
|
[draft, step]
|
|
3109
3332
|
);
|
|
3110
|
-
const next = (0,
|
|
3333
|
+
const next = (0, import_react25.useCallback)(() => {
|
|
3111
3334
|
if (!step) return;
|
|
3112
3335
|
const current = draftRef.current;
|
|
3113
3336
|
const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
|
|
@@ -3118,8 +3341,8 @@ function OnboardingFlow({
|
|
|
3118
3341
|
setIdx(clampedIdx + 1);
|
|
3119
3342
|
}
|
|
3120
3343
|
}, [clampedIdx, onComplete, step, steps.length, setIdx]);
|
|
3121
|
-
const prevStep = (0,
|
|
3122
|
-
const ctx = (0,
|
|
3344
|
+
const prevStep = (0, import_react25.useCallback)(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
|
|
3345
|
+
const ctx = (0, import_react25.useMemo)(
|
|
3123
3346
|
() => ({
|
|
3124
3347
|
stepIndex: clampedIdx,
|
|
3125
3348
|
totalSteps: steps.length,
|
|
@@ -3145,7 +3368,7 @@ function OnboardingFlow({
|
|
|
3145
3368
|
`[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
|
|
3146
3369
|
);
|
|
3147
3370
|
}
|
|
3148
|
-
return /* @__PURE__ */ (0,
|
|
3371
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Screen, {}) });
|
|
3149
3372
|
}
|
|
3150
3373
|
|
|
3151
3374
|
// src/hooks/useFeature.ts
|
|
@@ -3153,12 +3376,299 @@ function useFeature(name) {
|
|
|
3153
3376
|
const config = useAppConfig();
|
|
3154
3377
|
return Array.isArray(config.features_enabled) && config.features_enabled.includes(name);
|
|
3155
3378
|
}
|
|
3379
|
+
|
|
3380
|
+
// src/components/paywall/Paywall.tsx
|
|
3381
|
+
var import_react26 = require("react");
|
|
3382
|
+
var import_sdk22 = require("@hook-sdk/sdk");
|
|
3383
|
+
|
|
3384
|
+
// src/components/paywall/PaywallMethodTabs.tsx
|
|
3385
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
3386
|
+
function PaywallMethodTabs({
|
|
3387
|
+
methods,
|
|
3388
|
+
selected,
|
|
3389
|
+
onSelect,
|
|
3390
|
+
labels,
|
|
3391
|
+
className,
|
|
3392
|
+
tabClassName,
|
|
3393
|
+
tabActiveClassName
|
|
3394
|
+
}) {
|
|
3395
|
+
if (methods.length < 2) return null;
|
|
3396
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
|
|
3397
|
+
const active = m === selected;
|
|
3398
|
+
const label = labels[m] ?? m;
|
|
3399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3400
|
+
"button",
|
|
3401
|
+
{
|
|
3402
|
+
type: "button",
|
|
3403
|
+
role: "tab",
|
|
3404
|
+
"aria-selected": active,
|
|
3405
|
+
"aria-controls": `paywall-tab-${m}`,
|
|
3406
|
+
tabIndex: active ? 0 : -1,
|
|
3407
|
+
onClick: () => onSelect(m),
|
|
3408
|
+
className: [tabClassName, active ? tabActiveClassName : ""].filter(Boolean).join(" "),
|
|
3409
|
+
children: label
|
|
3410
|
+
},
|
|
3411
|
+
m
|
|
3412
|
+
);
|
|
3413
|
+
}) });
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
// src/components/paywall/PaywallMethodContent.tsx
|
|
3417
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
3418
|
+
function PaywallMethodContent({
|
|
3419
|
+
method,
|
|
3420
|
+
copy,
|
|
3421
|
+
hasConsumedTrial = false,
|
|
3422
|
+
className,
|
|
3423
|
+
rowClassName
|
|
3424
|
+
}) {
|
|
3425
|
+
const useCardConsumed = method === "card" && hasConsumedTrial && copy.cardConsumedTrial;
|
|
3426
|
+
const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : method === "pix-auto" || method === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
|
|
3427
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { role: "tabpanel", id: `paywall-tab-${method}`, className, children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: rowClassName, children: row }, i)) });
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
// src/components/paywall/PaywallCyclePicker.tsx
|
|
3431
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
3432
|
+
function PaywallCyclePicker({
|
|
3433
|
+
cycles,
|
|
3434
|
+
selected,
|
|
3435
|
+
onSelect,
|
|
3436
|
+
priceCentsByCycle,
|
|
3437
|
+
anchorCentsByCycle,
|
|
3438
|
+
monthlyEquivByCycle,
|
|
3439
|
+
labels,
|
|
3440
|
+
className,
|
|
3441
|
+
cardClassName,
|
|
3442
|
+
cardSelectedClassName,
|
|
3443
|
+
anchorClassName
|
|
3444
|
+
}) {
|
|
3445
|
+
if (cycles.length < 2) return null;
|
|
3446
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { role: "radiogroup", "aria-label": "Ciclo de cobran\xE7a", className, children: cycles.map((c) => {
|
|
3447
|
+
const active = c === selected;
|
|
3448
|
+
const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
|
|
3449
|
+
const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
|
|
3450
|
+
const mainCents = c === "YEARLY" ? monthlyEquivByCycle[c] : priceCentsByCycle[c];
|
|
3451
|
+
const anchorCents = anchorCentsByCycle[c];
|
|
3452
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
3453
|
+
"button",
|
|
3454
|
+
{
|
|
3455
|
+
type: "button",
|
|
3456
|
+
role: "radio",
|
|
3457
|
+
"aria-checked": active,
|
|
3458
|
+
onClick: () => onSelect(c),
|
|
3459
|
+
className: [cardClassName, active ? cardSelectedClassName : ""].filter(Boolean).join(" "),
|
|
3460
|
+
children: [
|
|
3461
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("strong", { children: formatBRL(mainCents) }),
|
|
3462
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { children: suffix }),
|
|
3463
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { children: label }),
|
|
3464
|
+
anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: anchorClassName, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("s", { children: formatBRL(anchorCents) }) }) : null
|
|
3465
|
+
]
|
|
3466
|
+
},
|
|
3467
|
+
c
|
|
3468
|
+
);
|
|
3469
|
+
}) });
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
// src/components/paywall/PaywallCta.tsx
|
|
3473
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
3474
|
+
function PaywallCta({
|
|
3475
|
+
ctaLabel,
|
|
3476
|
+
loadingLabel,
|
|
3477
|
+
switchHint,
|
|
3478
|
+
trustLine,
|
|
3479
|
+
onClick,
|
|
3480
|
+
disabled = false,
|
|
3481
|
+
loading = false,
|
|
3482
|
+
className,
|
|
3483
|
+
buttonClassName,
|
|
3484
|
+
switchHintClassName,
|
|
3485
|
+
trustClassName
|
|
3486
|
+
}) {
|
|
3487
|
+
const label = loading && loadingLabel ? loadingLabel : ctaLabel;
|
|
3488
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className, children: [
|
|
3489
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3490
|
+
"button",
|
|
3491
|
+
{
|
|
3492
|
+
type: "button",
|
|
3493
|
+
onClick,
|
|
3494
|
+
disabled: disabled || loading,
|
|
3495
|
+
className: buttonClassName,
|
|
3496
|
+
children: label
|
|
3497
|
+
}
|
|
3498
|
+
),
|
|
3499
|
+
switchHint ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: switchHintClassName, children: switchHint }) : null,
|
|
3500
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: trustClassName, children: trustLine })
|
|
3501
|
+
] });
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3504
|
+
// src/components/paywall/Paywall.tsx
|
|
3505
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
3506
|
+
var NBSP = "\xA0";
|
|
3507
|
+
function Paywall({
|
|
3508
|
+
copy,
|
|
3509
|
+
themeClasses = {},
|
|
3510
|
+
slots = {},
|
|
3511
|
+
onBeforeCheckout
|
|
3512
|
+
}) {
|
|
3513
|
+
const { track: track2 } = (0, import_sdk22.useHook)();
|
|
3514
|
+
const s = usePaywallState();
|
|
3515
|
+
const priceLabel = formatBRL(s.currentPriceCents).replace(new RegExp(NBSP, "g"), " ");
|
|
3516
|
+
const monthlyEquivLabel = formatBRL(s.currentMonthlyEquivCents).replace(new RegExp(NBSP, "g"), " ");
|
|
3517
|
+
const trialDaysCardLabel = String(s.trialDaysCard);
|
|
3518
|
+
const ctaLabel = (0, import_react26.useMemo)(() => {
|
|
3519
|
+
if (s.isFree) return copy.freeCta ?? "Come\xE7ar agora";
|
|
3520
|
+
if (s.selectedMethod === "card") {
|
|
3521
|
+
if (s.hasConsumedTrial && copy.cardConsumedTrial) {
|
|
3522
|
+
return interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
|
|
3523
|
+
}
|
|
3524
|
+
if (s.trialDaysCard > 0) {
|
|
3525
|
+
return interp(copy.card.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
|
|
3526
|
+
}
|
|
3527
|
+
return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel }) : `Assinar por ${priceLabel}`;
|
|
3528
|
+
}
|
|
3529
|
+
return interp(copy.pix.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
|
|
3530
|
+
}, [
|
|
3531
|
+
s.isFree,
|
|
3532
|
+
s.selectedMethod,
|
|
3533
|
+
s.hasConsumedTrial,
|
|
3534
|
+
s.trialDaysCard,
|
|
3535
|
+
copy,
|
|
3536
|
+
priceLabel,
|
|
3537
|
+
trialDaysCardLabel
|
|
3538
|
+
]);
|
|
3539
|
+
const switchHint = (0, import_react26.useMemo)(() => {
|
|
3540
|
+
if (s.methods.length < 2) return void 0;
|
|
3541
|
+
return s.selectedMethod === "card" ? copy.card.switchHint : copy.pix.switchHint;
|
|
3542
|
+
}, [s.methods.length, s.selectedMethod, copy]);
|
|
3543
|
+
(0, import_react26.useEffect)(() => {
|
|
3544
|
+
if (!s.initialLoadComplete) return;
|
|
3545
|
+
track2("paywall_view", {
|
|
3546
|
+
default_method: s.selectedMethod,
|
|
3547
|
+
default_cycle: s.cycle,
|
|
3548
|
+
available_methods: s.methods
|
|
3549
|
+
});
|
|
3550
|
+
}, [s.initialLoadComplete]);
|
|
3551
|
+
const handleCta = async () => {
|
|
3552
|
+
track2("paywall_cta_clicked", {
|
|
3553
|
+
method: s.selectedMethod,
|
|
3554
|
+
cycle: s.cycle,
|
|
3555
|
+
price_cents: s.currentPriceCents,
|
|
3556
|
+
had_consumed_trial: s.hasConsumedTrial
|
|
3557
|
+
});
|
|
3558
|
+
if (onBeforeCheckout) {
|
|
3559
|
+
await onBeforeCheckout(s.selectedMethod, s.cycle);
|
|
3560
|
+
return;
|
|
3561
|
+
}
|
|
3562
|
+
await s.submit();
|
|
3563
|
+
};
|
|
3564
|
+
const ctaTheme = s.selectedMethod === "card" ? themeClasses.ctaCard : themeClasses.ctaPix;
|
|
3565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: themeClasses.container, children: [
|
|
3566
|
+
slots.heroSlot,
|
|
3567
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h1", { className: themeClasses.headline, children: copy.headline }),
|
|
3568
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { children: copy.features.map((f) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("li", { className: themeClasses.feature, children: [
|
|
3569
|
+
"\u2713 ",
|
|
3570
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: f })
|
|
3571
|
+
] }, f)) }),
|
|
3572
|
+
copy.socialProof ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: themeClasses.socialProof, children: copy.socialProof }) : null,
|
|
3573
|
+
slots.cyclePickerSlot ?? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3574
|
+
PaywallCyclePicker,
|
|
3575
|
+
{
|
|
3576
|
+
cycles: ["MONTHLY", "YEARLY"],
|
|
3577
|
+
selected: s.cycle,
|
|
3578
|
+
onSelect: s.selectCycle,
|
|
3579
|
+
priceCentsByCycle: {
|
|
3580
|
+
MONTHLY: priceCentsForCycle(s, "MONTHLY"),
|
|
3581
|
+
YEARLY: priceCentsForCycle(s, "YEARLY")
|
|
3582
|
+
},
|
|
3583
|
+
anchorCentsByCycle: {
|
|
3584
|
+
MONTHLY: anchorForCycle(s, "MONTHLY"),
|
|
3585
|
+
YEARLY: anchorForCycle(s, "YEARLY")
|
|
3586
|
+
},
|
|
3587
|
+
monthlyEquivByCycle: {
|
|
3588
|
+
MONTHLY: priceCentsForCycle(s, "MONTHLY"),
|
|
3589
|
+
YEARLY: Math.round(priceCentsForCycle(s, "YEARLY") / 12)
|
|
3590
|
+
},
|
|
3591
|
+
labels: copy.cycle,
|
|
3592
|
+
cardClassName: themeClasses.cycleCard,
|
|
3593
|
+
cardSelectedClassName: themeClasses.cycleCardSelected,
|
|
3594
|
+
anchorClassName: themeClasses.anchorPrice
|
|
3595
|
+
}
|
|
3596
|
+
),
|
|
3597
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3598
|
+
PaywallMethodTabs,
|
|
3599
|
+
{
|
|
3600
|
+
methods: s.methods,
|
|
3601
|
+
selected: s.selectedMethod,
|
|
3602
|
+
onSelect: s.selectMethod,
|
|
3603
|
+
labels: { "pix-auto": copy.pix.tabLabel, card: copy.card.tabLabel },
|
|
3604
|
+
className: themeClasses.tabs,
|
|
3605
|
+
tabClassName: themeClasses.tab,
|
|
3606
|
+
tabActiveClassName: themeClasses.tabActive
|
|
3607
|
+
}
|
|
3608
|
+
),
|
|
3609
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3610
|
+
PaywallMethodContent,
|
|
3611
|
+
{
|
|
3612
|
+
method: s.selectedMethod,
|
|
3613
|
+
copy: {
|
|
3614
|
+
pix: interpolateCopy(copy.pix, priceLabel, trialDaysCardLabel),
|
|
3615
|
+
card: interpolateCopy(copy.card, priceLabel, trialDaysCardLabel),
|
|
3616
|
+
cardConsumedTrial: copy.cardConsumedTrial ? {
|
|
3617
|
+
bodyRows: copy.cardConsumedTrial.bodyRows.map(
|
|
3618
|
+
(r) => interp(r, { price: priceLabel, days: trialDaysCardLabel })
|
|
3619
|
+
),
|
|
3620
|
+
ctaTemplate: copy.cardConsumedTrial.ctaTemplate
|
|
3621
|
+
} : void 0
|
|
3622
|
+
},
|
|
3623
|
+
hasConsumedTrial: s.hasConsumedTrial,
|
|
3624
|
+
className: themeClasses.tabContent,
|
|
3625
|
+
rowClassName: themeClasses.tabContentRow
|
|
3626
|
+
}
|
|
3627
|
+
),
|
|
3628
|
+
slots.beforeCtaSlot,
|
|
3629
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3630
|
+
PaywallCta,
|
|
3631
|
+
{
|
|
3632
|
+
ctaLabel,
|
|
3633
|
+
loadingLabel: "Abrindo checkout\u2026",
|
|
3634
|
+
switchHint,
|
|
3635
|
+
trustLine: copy.trustLine,
|
|
3636
|
+
onClick: handleCta,
|
|
3637
|
+
disabled: !s.initialLoadComplete,
|
|
3638
|
+
loading: s.submitting,
|
|
3639
|
+
buttonClassName: ctaTheme,
|
|
3640
|
+
switchHintClassName: themeClasses.switchHint,
|
|
3641
|
+
trustClassName: themeClasses.trustLine
|
|
3642
|
+
}
|
|
3643
|
+
)
|
|
3644
|
+
] });
|
|
3645
|
+
}
|
|
3646
|
+
function interp(tpl, vars) {
|
|
3647
|
+
return tpl.replace(/\{(\w+)\}/g, (_m, k) => vars[k] ?? "");
|
|
3648
|
+
}
|
|
3649
|
+
function interpolateCopy(m, price, days) {
|
|
3650
|
+
return {
|
|
3651
|
+
tabLabel: m.tabLabel,
|
|
3652
|
+
bodyRows: m.bodyRows.map((r) => interp(r, { price, days })),
|
|
3653
|
+
ctaTemplate: m.ctaTemplate,
|
|
3654
|
+
switchHint: m.switchHint
|
|
3655
|
+
};
|
|
3656
|
+
}
|
|
3657
|
+
function priceCentsForCycle(s, c) {
|
|
3658
|
+
return s.plan ? c === "YEARLY" ? s.plan.yearlyCents ?? 0 : s.plan.monthlyCents : 0;
|
|
3659
|
+
}
|
|
3660
|
+
function anchorForCycle(s, c) {
|
|
3661
|
+
if (!s.plan) return null;
|
|
3662
|
+
if (c === "YEARLY") return s.plan.anchorYearlyCents ?? null;
|
|
3663
|
+
return s.plan.anchorMonthlyCents ?? null;
|
|
3664
|
+
}
|
|
3156
3665
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3157
3666
|
0 && (module.exports = {
|
|
3158
3667
|
AppConfigProvider,
|
|
3159
3668
|
AppConfigSchema,
|
|
3160
3669
|
AppRoot,
|
|
3161
3670
|
DeepLinkHandler,
|
|
3671
|
+
DevSkipOnboardingFab,
|
|
3162
3672
|
EmptyState,
|
|
3163
3673
|
ErrorBoundary,
|
|
3164
3674
|
I18nProvider,
|
|
@@ -3168,6 +3678,11 @@ function useFeature(name) {
|
|
|
3168
3678
|
LoadingState,
|
|
3169
3679
|
OnboardingFlow,
|
|
3170
3680
|
PaymentReturnHandler,
|
|
3681
|
+
Paywall,
|
|
3682
|
+
PaywallCta,
|
|
3683
|
+
PaywallCyclePicker,
|
|
3684
|
+
PaywallMethodContent,
|
|
3685
|
+
PaywallMethodTabs,
|
|
3171
3686
|
PersistenceRegistry,
|
|
3172
3687
|
PreAuthShell,
|
|
3173
3688
|
PushPrompt,
|
|
@@ -3182,10 +3697,12 @@ function useFeature(name) {
|
|
|
3182
3697
|
detectStandalone,
|
|
3183
3698
|
discountPercent,
|
|
3184
3699
|
formatBRL,
|
|
3700
|
+
isDevToolsEnabled,
|
|
3185
3701
|
monthlyFromYearly,
|
|
3186
3702
|
parseAppConfig,
|
|
3187
3703
|
shouldBlockInstall,
|
|
3188
3704
|
shouldShowPermanentOption,
|
|
3705
|
+
skipOnboarding,
|
|
3189
3706
|
useAppConfig,
|
|
3190
3707
|
useAuth,
|
|
3191
3708
|
useAuthPrimitives,
|