@hook-sdk/template 0.28.2 → 0.28.5
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 +1944 -1887
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1912 -1855
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/AppRoot.tsx
|
|
2
2
|
import { useMemo as useMemo3 } from "react";
|
|
3
3
|
import { BrowserRouter, MemoryRouter, Navigate, Route, Routes } from "react-router-dom";
|
|
4
|
-
import { useHook as
|
|
4
|
+
import { useHook as useHook8 } from "@hook-sdk/sdk";
|
|
5
5
|
|
|
6
6
|
// src/config/AppConfigContext.tsx
|
|
7
7
|
import { createContext, useContext } from "react";
|
|
@@ -2033,9 +2033,53 @@ function SessionExpiredBanner() {
|
|
|
2033
2033
|
] });
|
|
2034
2034
|
}
|
|
2035
2035
|
|
|
2036
|
+
// src/internal/EmailVerifyBanner.tsx
|
|
2037
|
+
import { useState as useState5 } from "react";
|
|
2038
|
+
import { useHook as useHook5 } from "@hook-sdk/sdk";
|
|
2039
|
+
import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2040
|
+
function EmailVerifyBanner() {
|
|
2041
|
+
const { user, auth } = useHook5();
|
|
2042
|
+
const [sending, setSending] = useState5(false);
|
|
2043
|
+
const [sent, setSent] = useState5(false);
|
|
2044
|
+
if (!user || user.emailVerified) return null;
|
|
2045
|
+
async function handleResend() {
|
|
2046
|
+
if (sending || sent) return;
|
|
2047
|
+
setSending(true);
|
|
2048
|
+
try {
|
|
2049
|
+
await auth.resendVerify();
|
|
2050
|
+
setSent(true);
|
|
2051
|
+
} catch {
|
|
2052
|
+
} finally {
|
|
2053
|
+
setSending(false);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
const label = sent ? "Enviado!" : sending ? "Enviando..." : "Reenviar link";
|
|
2057
|
+
return /* @__PURE__ */ jsxs12(
|
|
2058
|
+
"div",
|
|
2059
|
+
{
|
|
2060
|
+
role: "status",
|
|
2061
|
+
"data-testid": "email-verify-banner",
|
|
2062
|
+
className: "sticky top-0 inset-x-0 z-50 bg-yellow-100 text-yellow-900 border-b border-yellow-200 px-4 py-2 flex items-center justify-between gap-3 text-sm",
|
|
2063
|
+
children: [
|
|
2064
|
+
/* @__PURE__ */ jsx18("span", { children: "Confirma teu e-mail pra liberar tudo." }),
|
|
2065
|
+
/* @__PURE__ */ jsx18(
|
|
2066
|
+
"button",
|
|
2067
|
+
{
|
|
2068
|
+
type: "button",
|
|
2069
|
+
onClick: handleResend,
|
|
2070
|
+
disabled: sending || sent,
|
|
2071
|
+
className: "px-3 py-1 rounded text-xs font-medium bg-yellow-900 text-yellow-50 hover:bg-yellow-800 disabled:opacity-60 disabled:cursor-not-allowed",
|
|
2072
|
+
children: label
|
|
2073
|
+
}
|
|
2074
|
+
)
|
|
2075
|
+
]
|
|
2076
|
+
}
|
|
2077
|
+
);
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2036
2080
|
// src/defaults/ErrorBoundary.tsx
|
|
2037
2081
|
import { Component } from "react";
|
|
2038
|
-
import { Fragment as Fragment4, jsx as
|
|
2082
|
+
import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2039
2083
|
var ErrorBoundary = class extends Component {
|
|
2040
2084
|
state = { error: null };
|
|
2041
2085
|
static getDerivedStateFromError(error) {
|
|
@@ -2053,12 +2097,12 @@ var ErrorBoundary = class extends Component {
|
|
|
2053
2097
|
}
|
|
2054
2098
|
render() {
|
|
2055
2099
|
if (this.state.error) {
|
|
2056
|
-
return this.props.fallback ?? /* @__PURE__ */
|
|
2057
|
-
/* @__PURE__ */
|
|
2058
|
-
/* @__PURE__ */
|
|
2100
|
+
return this.props.fallback ?? /* @__PURE__ */ jsxs13("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
|
|
2101
|
+
/* @__PURE__ */ jsx19("h2", { children: "Algo deu errado" }),
|
|
2102
|
+
/* @__PURE__ */ jsx19("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
|
|
2059
2103
|
] });
|
|
2060
2104
|
}
|
|
2061
|
-
return /* @__PURE__ */
|
|
2105
|
+
return /* @__PURE__ */ jsx19(Fragment4, { children: this.props.children });
|
|
2062
2106
|
}
|
|
2063
2107
|
};
|
|
2064
2108
|
|
|
@@ -2067,7 +2111,7 @@ import { useEffect as useEffect7 } from "react";
|
|
|
2067
2111
|
import i18n from "i18next";
|
|
2068
2112
|
import { I18nextProvider, initReactI18next } from "react-i18next";
|
|
2069
2113
|
import { usePersistedState } from "@hook-sdk/sdk";
|
|
2070
|
-
import { jsx as
|
|
2114
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
2071
2115
|
function ensureInitialized(defaultLocale, supportedLocales, resources, initialLocale) {
|
|
2072
2116
|
if (i18n.isInitialized) return;
|
|
2073
2117
|
i18n.use(initReactI18next).init({
|
|
@@ -2096,7 +2140,7 @@ function I18nProvider({
|
|
|
2096
2140
|
i18n.changeLanguage(userLocale);
|
|
2097
2141
|
}
|
|
2098
2142
|
}, [userLocale]);
|
|
2099
|
-
return /* @__PURE__ */
|
|
2143
|
+
return /* @__PURE__ */ jsx20(I18nextProvider, { i18n, children });
|
|
2100
2144
|
}
|
|
2101
2145
|
|
|
2102
2146
|
// src/dev/env.ts
|
|
@@ -2107,9 +2151,9 @@ function isDevToolsEnabled() {
|
|
|
2107
2151
|
}
|
|
2108
2152
|
|
|
2109
2153
|
// src/dev/DevSkipOnboardingFab.tsx
|
|
2110
|
-
import { useCallback as useCallback3, useRef as useRef4, useState as
|
|
2111
|
-
import { useHook as
|
|
2112
|
-
import { jsx as
|
|
2154
|
+
import { useCallback as useCallback3, useRef as useRef4, useState as useState6 } from "react";
|
|
2155
|
+
import { useHook as useHook6, usePersistedState as usePersistedState2 } from "@hook-sdk/sdk";
|
|
2156
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
2113
2157
|
var STORAGE_KEY = "hook_dev_skip_email";
|
|
2114
2158
|
var TEST_EMAIL_DOMAIN = "@hook.test";
|
|
2115
2159
|
var TEST_PASSWORD = "SkipTest!2026";
|
|
@@ -2176,10 +2220,10 @@ var STYLES = {
|
|
|
2176
2220
|
};
|
|
2177
2221
|
var CONFIRM_TIMEOUT_MS = 3e3;
|
|
2178
2222
|
function DevSkipOnboardingFab({ defaults }) {
|
|
2179
|
-
const hook =
|
|
2223
|
+
const hook = useHook6();
|
|
2180
2224
|
const { slug } = useAppConfig();
|
|
2181
|
-
const [state, setState] =
|
|
2182
|
-
const [errorMsg, setErrorMsg] =
|
|
2225
|
+
const [state, setState] = useState6("idle");
|
|
2226
|
+
const [errorMsg, setErrorMsg] = useState6(null);
|
|
2183
2227
|
const timerRef = useRef4(null);
|
|
2184
2228
|
const isAuthed = hook.authStatus === "authenticated";
|
|
2185
2229
|
const [onboarding] = usePersistedState2(
|
|
@@ -2224,7 +2268,7 @@ function DevSkipOnboardingFab({ defaults }) {
|
|
|
2224
2268
|
...state === "confirm" || state === "error" ? STYLES.confirm : {},
|
|
2225
2269
|
...state === "busy" ? STYLES.busy : {}
|
|
2226
2270
|
};
|
|
2227
|
-
return /* @__PURE__ */
|
|
2271
|
+
return /* @__PURE__ */ jsx21(
|
|
2228
2272
|
"button",
|
|
2229
2273
|
{
|
|
2230
2274
|
type: "button",
|
|
@@ -2239,20 +2283,20 @@ function DevSkipOnboardingFab({ defaults }) {
|
|
|
2239
2283
|
}
|
|
2240
2284
|
|
|
2241
2285
|
// src/internal/PaymentReturnHandler.tsx
|
|
2242
|
-
import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef5, useState as
|
|
2243
|
-
import { useHook as
|
|
2244
|
-
import { Fragment as Fragment5, jsx as
|
|
2286
|
+
import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef5, useState as useState7 } from "react";
|
|
2287
|
+
import { useHook as useHook7 } from "@hook-sdk/sdk";
|
|
2288
|
+
import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2245
2289
|
var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
|
|
2246
2290
|
var MAX_CYCLES = 3;
|
|
2247
2291
|
var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
|
|
2248
2292
|
function PaymentReturnHandler({ children }) {
|
|
2249
|
-
const { subscription, track: track2 } =
|
|
2293
|
+
const { subscription, track: track2 } = useHook7();
|
|
2250
2294
|
const subRef = useRef5(subscription);
|
|
2251
2295
|
subRef.current = subscription;
|
|
2252
2296
|
const runIdRef = useRef5(0);
|
|
2253
2297
|
const cyclesRef = useRef5(0);
|
|
2254
2298
|
const startMsRef = useRef5(0);
|
|
2255
|
-
const [state, setState] =
|
|
2299
|
+
const [state, setState] = useState7("idle");
|
|
2256
2300
|
const runPoll = useCallback4(() => {
|
|
2257
2301
|
const runId = ++runIdRef.current;
|
|
2258
2302
|
const isFirstRun = cyclesRef.current === 0;
|
|
@@ -2318,19 +2362,19 @@ function PaymentReturnHandler({ children }) {
|
|
|
2318
2362
|
window.location.href = cleanUrl.toString();
|
|
2319
2363
|
}, []);
|
|
2320
2364
|
if (state === "confirming") {
|
|
2321
|
-
return /* @__PURE__ */
|
|
2365
|
+
return /* @__PURE__ */ jsx22("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
|
|
2322
2366
|
}
|
|
2323
2367
|
if (state === "waiting") {
|
|
2324
|
-
return /* @__PURE__ */
|
|
2325
|
-
/* @__PURE__ */
|
|
2326
|
-
/* @__PURE__ */
|
|
2368
|
+
return /* @__PURE__ */ jsx22("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs14("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
2369
|
+
/* @__PURE__ */ jsx22("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
|
|
2370
|
+
/* @__PURE__ */ jsx22("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
|
|
2327
2371
|
] }) });
|
|
2328
2372
|
}
|
|
2329
2373
|
if (state === "timeout") {
|
|
2330
|
-
return /* @__PURE__ */
|
|
2331
|
-
/* @__PURE__ */
|
|
2332
|
-
/* @__PURE__ */
|
|
2333
|
-
/* @__PURE__ */
|
|
2374
|
+
return /* @__PURE__ */ jsx22("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs14("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
2375
|
+
/* @__PURE__ */ jsx22("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." }),
|
|
2376
|
+
/* @__PURE__ */ jsxs14("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
2377
|
+
/* @__PURE__ */ jsx22(
|
|
2334
2378
|
"button",
|
|
2335
2379
|
{
|
|
2336
2380
|
type: "button",
|
|
@@ -2343,7 +2387,7 @@ function PaymentReturnHandler({ children }) {
|
|
|
2343
2387
|
children: "Tentar de novo"
|
|
2344
2388
|
}
|
|
2345
2389
|
),
|
|
2346
|
-
/* @__PURE__ */
|
|
2390
|
+
/* @__PURE__ */ jsx22(
|
|
2347
2391
|
"button",
|
|
2348
2392
|
{
|
|
2349
2393
|
type: "button",
|
|
@@ -2353,7 +2397,7 @@ function PaymentReturnHandler({ children }) {
|
|
|
2353
2397
|
children: "Voltar pro app"
|
|
2354
2398
|
}
|
|
2355
2399
|
),
|
|
2356
|
-
/* @__PURE__ */
|
|
2400
|
+
/* @__PURE__ */ jsx22(
|
|
2357
2401
|
"a",
|
|
2358
2402
|
{
|
|
2359
2403
|
href: SUPPORT_MAILTO,
|
|
@@ -2365,7 +2409,7 @@ function PaymentReturnHandler({ children }) {
|
|
|
2365
2409
|
] })
|
|
2366
2410
|
] }) });
|
|
2367
2411
|
}
|
|
2368
|
-
return /* @__PURE__ */
|
|
2412
|
+
return /* @__PURE__ */ jsx22(Fragment5, { children });
|
|
2369
2413
|
}
|
|
2370
2414
|
var overlayStyle2 = {
|
|
2371
2415
|
position: "fixed",
|
|
@@ -2404,7 +2448,7 @@ var linkStyle = {
|
|
|
2404
2448
|
};
|
|
2405
2449
|
|
|
2406
2450
|
// src/AppRoot.tsx
|
|
2407
|
-
import { Fragment as Fragment6, jsx as
|
|
2451
|
+
import { Fragment as Fragment6, jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2408
2452
|
function buildLegacyConfigShim(config) {
|
|
2409
2453
|
const paywall = config.paywall;
|
|
2410
2454
|
const isFree = paywall.mode === "free";
|
|
@@ -2483,14 +2527,17 @@ function AppRoot(props) {
|
|
|
2483
2527
|
const basename = `/app/${config.slug}`;
|
|
2484
2528
|
const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
|
|
2485
2529
|
const position = config.install_prompt?.position ?? "post-paywall";
|
|
2486
|
-
const subscriptionGated = /* @__PURE__ */
|
|
2487
|
-
|
|
2488
|
-
/* @__PURE__ */
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
/* @__PURE__ */
|
|
2492
|
-
|
|
2493
|
-
|
|
2530
|
+
const subscriptionGated = /* @__PURE__ */ jsxs15(SubscriptionGate, { Paywall: Paywall2 ?? FallbackPaywall, children: [
|
|
2531
|
+
/* @__PURE__ */ jsx23(EmailVerifyBanner, {}),
|
|
2532
|
+
position === "post-paywall" ? /* @__PURE__ */ jsxs15(InstallGate, { position: "post-paywall", children: [
|
|
2533
|
+
children,
|
|
2534
|
+
/* @__PURE__ */ jsx23(PushPrompt, {})
|
|
2535
|
+
] }) : /* @__PURE__ */ jsxs15(Fragment6, { children: [
|
|
2536
|
+
children,
|
|
2537
|
+
/* @__PURE__ */ jsx23(PushPrompt, {})
|
|
2538
|
+
] })
|
|
2539
|
+
] });
|
|
2540
|
+
const authGated = /* @__PURE__ */ jsx23(
|
|
2494
2541
|
AuthGated,
|
|
2495
2542
|
{
|
|
2496
2543
|
config,
|
|
@@ -2505,13 +2552,13 @@ function AppRoot(props) {
|
|
|
2505
2552
|
children: subscriptionGated
|
|
2506
2553
|
}
|
|
2507
2554
|
);
|
|
2508
|
-
const routedTree = /* @__PURE__ */
|
|
2509
|
-
/* @__PURE__ */
|
|
2510
|
-
/* @__PURE__ */
|
|
2511
|
-
position === "pre-auth" ? /* @__PURE__ */
|
|
2512
|
-
isDevToolsEnabled() && devSkipOnboarding ? /* @__PURE__ */
|
|
2555
|
+
const routedTree = /* @__PURE__ */ jsxs15(Router, { ...routerProps, children: [
|
|
2556
|
+
/* @__PURE__ */ jsx23(DeepLinkHandler, { deepLinks: config.deepLinks }),
|
|
2557
|
+
/* @__PURE__ */ jsx23(SessionExpiredBanner, {}),
|
|
2558
|
+
position === "pre-auth" ? /* @__PURE__ */ jsx23(InstallGate, { position: "pre-auth", children: authGated }) : authGated,
|
|
2559
|
+
isDevToolsEnabled() && devSkipOnboarding ? /* @__PURE__ */ jsx23(DevSkipOnboardingFab, { defaults: devSkipOnboarding.defaults }) : null
|
|
2513
2560
|
] });
|
|
2514
|
-
return /* @__PURE__ */
|
|
2561
|
+
return /* @__PURE__ */ jsx23(ErrorBoundary, { children: /* @__PURE__ */ jsx23(AppConfigProvider, { config, children: /* @__PURE__ */ jsx23(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx23(ThemeProvider, { children: /* @__PURE__ */ jsx23(PersistenceRegistry, { config: config.persistedKeys, children: config.i18n ? /* @__PURE__ */ jsx23(
|
|
2515
2562
|
I18nProvider,
|
|
2516
2563
|
{
|
|
2517
2564
|
defaultLocale: config.i18n.defaultLocale,
|
|
@@ -2531,46 +2578,46 @@ function AuthGated({
|
|
|
2531
2578
|
EmailVerify,
|
|
2532
2579
|
PreAuthFlow
|
|
2533
2580
|
}) {
|
|
2534
|
-
const { authStatus } =
|
|
2581
|
+
const { authStatus } = useHook8();
|
|
2535
2582
|
if (authStatus === "loading") return null;
|
|
2536
2583
|
if (authStatus !== "authenticated") {
|
|
2537
2584
|
if (config.authFlow.signupMode === "pay_first" && PreAuthFlow) {
|
|
2538
|
-
return /* @__PURE__ */
|
|
2539
|
-
/* @__PURE__ */
|
|
2540
|
-
/* @__PURE__ */
|
|
2541
|
-
/* @__PURE__ */
|
|
2542
|
-
EmailVerify ? /* @__PURE__ */
|
|
2543
|
-
/* @__PURE__ */
|
|
2585
|
+
return /* @__PURE__ */ jsxs15(Routes, { children: [
|
|
2586
|
+
/* @__PURE__ */ jsx23(Route, { path: "/signin", element: /* @__PURE__ */ jsx23(Login, {}) }),
|
|
2587
|
+
/* @__PURE__ */ jsx23(Route, { path: "/forgot", element: /* @__PURE__ */ jsx23(Forgot, {}) }),
|
|
2588
|
+
/* @__PURE__ */ jsx23(Route, { path: "/reset", element: /* @__PURE__ */ jsx23(Reset, {}) }),
|
|
2589
|
+
EmailVerify ? /* @__PURE__ */ jsx23(Route, { path: "/verify", element: /* @__PURE__ */ jsx23(EmailVerify, {}) }) : null,
|
|
2590
|
+
/* @__PURE__ */ jsx23(Route, { path: "/*", element: /* @__PURE__ */ jsx23(PreAuthFlow, {}) })
|
|
2544
2591
|
] });
|
|
2545
2592
|
}
|
|
2546
2593
|
if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
|
|
2547
|
-
return /* @__PURE__ */
|
|
2548
|
-
/* @__PURE__ */
|
|
2549
|
-
/* @__PURE__ */
|
|
2550
|
-
/* @__PURE__ */
|
|
2551
|
-
/* @__PURE__ */
|
|
2552
|
-
EmailVerify ? /* @__PURE__ */
|
|
2553
|
-
/* @__PURE__ */
|
|
2594
|
+
return /* @__PURE__ */ jsxs15(Routes, { children: [
|
|
2595
|
+
/* @__PURE__ */ jsx23(Route, { path: "/signin", element: /* @__PURE__ */ jsx23(Login, {}) }),
|
|
2596
|
+
/* @__PURE__ */ jsx23(Route, { path: "/signup", element: /* @__PURE__ */ jsx23(Signup, {}) }),
|
|
2597
|
+
/* @__PURE__ */ jsx23(Route, { path: "/forgot", element: /* @__PURE__ */ jsx23(Forgot, {}) }),
|
|
2598
|
+
/* @__PURE__ */ jsx23(Route, { path: "/reset", element: /* @__PURE__ */ jsx23(Reset, {}) }),
|
|
2599
|
+
EmailVerify ? /* @__PURE__ */ jsx23(Route, { path: "/verify", element: /* @__PURE__ */ jsx23(EmailVerify, {}) }) : null,
|
|
2600
|
+
/* @__PURE__ */ jsx23(Route, { path: "/*", element: /* @__PURE__ */ jsx23(PreAuthFlow, {}) })
|
|
2554
2601
|
] });
|
|
2555
2602
|
}
|
|
2556
|
-
return /* @__PURE__ */
|
|
2557
|
-
/* @__PURE__ */
|
|
2558
|
-
/* @__PURE__ */
|
|
2559
|
-
/* @__PURE__ */
|
|
2560
|
-
/* @__PURE__ */
|
|
2561
|
-
EmailVerify ? /* @__PURE__ */
|
|
2562
|
-
/* @__PURE__ */
|
|
2603
|
+
return /* @__PURE__ */ jsxs15(Routes, { children: [
|
|
2604
|
+
/* @__PURE__ */ jsx23(Route, { path: "/", element: /* @__PURE__ */ jsx23(Login, {}) }),
|
|
2605
|
+
/* @__PURE__ */ jsx23(Route, { path: "/signup", element: /* @__PURE__ */ jsx23(Signup, {}) }),
|
|
2606
|
+
/* @__PURE__ */ jsx23(Route, { path: "/forgot", element: /* @__PURE__ */ jsx23(Forgot, {}) }),
|
|
2607
|
+
/* @__PURE__ */ jsx23(Route, { path: "/reset", element: /* @__PURE__ */ jsx23(Reset, {}) }),
|
|
2608
|
+
EmailVerify ? /* @__PURE__ */ jsx23(Route, { path: "/verify", element: /* @__PURE__ */ jsx23(EmailVerify, {}) }) : null,
|
|
2609
|
+
/* @__PURE__ */ jsx23(Route, { path: "*", element: /* @__PURE__ */ jsx23(Navigate, { to: "/", replace: true }) })
|
|
2563
2610
|
] });
|
|
2564
2611
|
}
|
|
2565
|
-
return /* @__PURE__ */
|
|
2612
|
+
return /* @__PURE__ */ jsx23(Fragment6, { children });
|
|
2566
2613
|
}
|
|
2567
2614
|
function FallbackPaywall() {
|
|
2568
2615
|
return null;
|
|
2569
2616
|
}
|
|
2570
2617
|
|
|
2571
2618
|
// src/hooks/usePush.ts
|
|
2572
|
-
import { useCallback as useCallback5, useEffect as useEffect9, useState as
|
|
2573
|
-
import { useHook as
|
|
2619
|
+
import { useCallback as useCallback5, useEffect as useEffect9, useState as useState8 } from "react";
|
|
2620
|
+
import { useHook as useHook9 } from "@hook-sdk/sdk";
|
|
2574
2621
|
var DISMISS_STORAGE_KEY = "push:dismissed-until";
|
|
2575
2622
|
var DISMISS_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
|
|
2576
2623
|
function detectIosNeedsInstall() {
|
|
@@ -2614,8 +2661,8 @@ function deriveState(push) {
|
|
|
2614
2661
|
return { kind: "prompt" };
|
|
2615
2662
|
}
|
|
2616
2663
|
function usePush() {
|
|
2617
|
-
const { push } =
|
|
2618
|
-
const [state, setState] =
|
|
2664
|
+
const { push } = useHook9();
|
|
2665
|
+
const [state, setState] = useState8(() => deriveState(push));
|
|
2619
2666
|
useEffect9(() => {
|
|
2620
2667
|
setState(deriveState(push));
|
|
2621
2668
|
}, [push]);
|
|
@@ -2653,27 +2700,27 @@ function usePush() {
|
|
|
2653
2700
|
}
|
|
2654
2701
|
|
|
2655
2702
|
// src/components/PushPrompt.tsx
|
|
2656
|
-
import { jsx as
|
|
2703
|
+
import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2657
2704
|
function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
|
|
2658
2705
|
const { state, subscribe } = usePush();
|
|
2659
2706
|
if (state.kind === "denied" || state.kind === "dismissed" || state.kind === "subscribed") {
|
|
2660
2707
|
return null;
|
|
2661
2708
|
}
|
|
2662
2709
|
if (state.kind === "ios_needs_install") {
|
|
2663
|
-
return /* @__PURE__ */
|
|
2664
|
-
/* @__PURE__ */
|
|
2665
|
-
/* @__PURE__ */
|
|
2666
|
-
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */
|
|
2710
|
+
return /* @__PURE__ */ jsxs16("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
|
|
2711
|
+
/* @__PURE__ */ jsx24("h3", { children: texts.iosInstallTitle }),
|
|
2712
|
+
/* @__PURE__ */ jsx24("p", { children: texts.iosInstallBody }),
|
|
2713
|
+
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx24("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
|
|
2667
2714
|
] });
|
|
2668
2715
|
}
|
|
2669
2716
|
if (state.kind === "unsupported") {
|
|
2670
|
-
return /* @__PURE__ */
|
|
2717
|
+
return /* @__PURE__ */ jsx24("div", { className, role: "region", children: /* @__PURE__ */ jsx24("p", { children: texts.unsupportedBody }) });
|
|
2671
2718
|
}
|
|
2672
2719
|
if (state.kind === "error") {
|
|
2673
|
-
return /* @__PURE__ */
|
|
2720
|
+
return /* @__PURE__ */ jsx24("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx24("p", { children: state.message }) });
|
|
2674
2721
|
}
|
|
2675
|
-
return /* @__PURE__ */
|
|
2676
|
-
/* @__PURE__ */
|
|
2722
|
+
return /* @__PURE__ */ jsxs16("div", { className, role: "region", children: [
|
|
2723
|
+
/* @__PURE__ */ jsx24(
|
|
2677
2724
|
"button",
|
|
2678
2725
|
{
|
|
2679
2726
|
type: "button",
|
|
@@ -2687,13 +2734,13 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
|
|
|
2687
2734
|
children: texts.cta
|
|
2688
2735
|
}
|
|
2689
2736
|
),
|
|
2690
|
-
onDeclined && /* @__PURE__ */
|
|
2737
|
+
onDeclined && /* @__PURE__ */ jsx24("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
|
|
2691
2738
|
] });
|
|
2692
2739
|
}
|
|
2693
2740
|
|
|
2694
2741
|
// src/components/LanguageSwitcher.tsx
|
|
2695
2742
|
import { usePersistedState as usePersistedState3 } from "@hook-sdk/sdk";
|
|
2696
|
-
import { jsx as
|
|
2743
|
+
import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2697
2744
|
function LanguageSwitcher({ id, className, label = "Language" }) {
|
|
2698
2745
|
const config = useAppConfig();
|
|
2699
2746
|
const i18nConfig = config.i18n;
|
|
@@ -2702,50 +2749,50 @@ function LanguageSwitcher({ id, className, label = "Language" }) {
|
|
|
2702
2749
|
i18nConfig?.defaultLocale ?? "en-US"
|
|
2703
2750
|
);
|
|
2704
2751
|
if (!i18nConfig) return null;
|
|
2705
|
-
return /* @__PURE__ */
|
|
2706
|
-
label ? /* @__PURE__ */
|
|
2707
|
-
/* @__PURE__ */
|
|
2752
|
+
return /* @__PURE__ */ jsxs17("label", { className, children: [
|
|
2753
|
+
label ? /* @__PURE__ */ jsx25("span", { children: label }) : null,
|
|
2754
|
+
/* @__PURE__ */ jsx25(
|
|
2708
2755
|
"select",
|
|
2709
2756
|
{
|
|
2710
2757
|
id,
|
|
2711
2758
|
value: userLocale,
|
|
2712
2759
|
onChange: (e) => setUserLocale(e.target.value),
|
|
2713
2760
|
"data-testid": "language-switcher",
|
|
2714
|
-
children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */
|
|
2761
|
+
children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */ jsx25("option", { value: loc, children: loc }, loc))
|
|
2715
2762
|
}
|
|
2716
2763
|
)
|
|
2717
2764
|
] });
|
|
2718
2765
|
}
|
|
2719
2766
|
|
|
2720
2767
|
// src/defaults/LoadingState.tsx
|
|
2721
|
-
import { jsx as
|
|
2768
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
2722
2769
|
function LoadingState({ message }) {
|
|
2723
|
-
return /* @__PURE__ */
|
|
2770
|
+
return /* @__PURE__ */ jsx26("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx26("span", { children: message ?? "Carregando..." }) });
|
|
2724
2771
|
}
|
|
2725
2772
|
|
|
2726
2773
|
// src/defaults/EmptyState.tsx
|
|
2727
|
-
import { jsx as
|
|
2774
|
+
import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2728
2775
|
function EmptyState({ title, description, action }) {
|
|
2729
|
-
return /* @__PURE__ */
|
|
2730
|
-
/* @__PURE__ */
|
|
2731
|
-
description && /* @__PURE__ */
|
|
2732
|
-
action && /* @__PURE__ */
|
|
2776
|
+
return /* @__PURE__ */ jsxs18("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
|
|
2777
|
+
/* @__PURE__ */ jsx27("h2", { style: { marginBottom: 8 }, children: title }),
|
|
2778
|
+
description && /* @__PURE__ */ jsx27("p", { style: { opacity: 0.7 }, children: description }),
|
|
2779
|
+
action && /* @__PURE__ */ jsx27("div", { style: { marginTop: 16 }, children: action })
|
|
2733
2780
|
] });
|
|
2734
2781
|
}
|
|
2735
2782
|
|
|
2736
2783
|
// src/defaults/CheckoutPageDefault.tsx
|
|
2737
|
-
import { useEffect as
|
|
2784
|
+
import { useEffect as useEffect13, useMemo as useMemo6, useState as useState11 } from "react";
|
|
2738
2785
|
import { useNavigate as useNavigate2 } from "react-router-dom";
|
|
2739
2786
|
|
|
2740
2787
|
// src/hooks/useCheckoutForm.ts
|
|
2741
|
-
import { useCallback as useCallback6, useEffect as useEffect10, useMemo as useMemo4, useRef as useRef6, useState as
|
|
2788
|
+
import { useCallback as useCallback6, useEffect as useEffect10, useMemo as useMemo4, useRef as useRef6, useState as useState9 } from "react";
|
|
2742
2789
|
import {
|
|
2743
|
-
useHook as
|
|
2790
|
+
useHook as useHook10,
|
|
2744
2791
|
EmailTakenError
|
|
2745
2792
|
} from "@hook-sdk/sdk";
|
|
2746
2793
|
|
|
2747
2794
|
// src/errors.ts
|
|
2748
|
-
import { SdkError, SdkAuthError, SdkRateLimitError } from "@hook-sdk/sdk";
|
|
2795
|
+
import { SdkError, SdkAuthError, SdkRateLimitError, SdkValidationError } from "@hook-sdk/sdk";
|
|
2749
2796
|
function mapSdkError(err) {
|
|
2750
2797
|
if (err instanceof SdkRateLimitError) {
|
|
2751
2798
|
return {
|
|
@@ -2764,6 +2811,9 @@ function mapSdkError(err) {
|
|
|
2764
2811
|
}
|
|
2765
2812
|
return { code: "invalid_credentials", message: "E-mail ou senha inv\xE1lidos." };
|
|
2766
2813
|
}
|
|
2814
|
+
if (err instanceof SdkValidationError && err.code === "auth.email_taken") {
|
|
2815
|
+
return { code: "email_taken", message: "Esse e-mail j\xE1 tem conta." };
|
|
2816
|
+
}
|
|
2767
2817
|
if (err instanceof SdkError && err.httpStatus === 0) {
|
|
2768
2818
|
return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
|
|
2769
2819
|
}
|
|
@@ -2778,15 +2828,15 @@ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
2778
2828
|
var PHONE_RE = /^[0-9()+\-\s]{8,20}$/;
|
|
2779
2829
|
var CHECK_DEBOUNCE_MS = 400;
|
|
2780
2830
|
function useCheckoutForm(args) {
|
|
2781
|
-
const { auth } =
|
|
2782
|
-
const [name, setName] =
|
|
2783
|
-
const [email, setEmail] =
|
|
2784
|
-
const [emailConfirm, setEmailConfirm] =
|
|
2785
|
-
const [phone, setPhone] =
|
|
2786
|
-
const [cpf, setCpf] =
|
|
2787
|
-
const [method, setMethod] =
|
|
2788
|
-
const [cycle, setCycle] =
|
|
2789
|
-
const [card, setCardState] =
|
|
2831
|
+
const { auth } = useHook10();
|
|
2832
|
+
const [name, setName] = useState9("");
|
|
2833
|
+
const [email, setEmail] = useState9("");
|
|
2834
|
+
const [emailConfirm, setEmailConfirm] = useState9("");
|
|
2835
|
+
const [phone, setPhone] = useState9("");
|
|
2836
|
+
const [cpf, setCpf] = useState9("");
|
|
2837
|
+
const [method, setMethod] = useState9(args.defaultMethod);
|
|
2838
|
+
const [cycle, setCycle] = useState9(args.defaultCycle);
|
|
2839
|
+
const [card, setCardState] = useState9({
|
|
2790
2840
|
number: "",
|
|
2791
2841
|
expiryMonth: "",
|
|
2792
2842
|
expiryYear: "",
|
|
@@ -2796,17 +2846,17 @@ function useCheckoutForm(args) {
|
|
|
2796
2846
|
const setCard = useCallback6((patch) => {
|
|
2797
2847
|
setCardState((prev) => ({ ...prev, ...patch }));
|
|
2798
2848
|
}, []);
|
|
2799
|
-
const [touchedName, setTouchedName] =
|
|
2800
|
-
const [touchedEmail, setTouchedEmail] =
|
|
2801
|
-
const [touchedEmailConfirm, setTouchedEmailConfirm] =
|
|
2802
|
-
const [touchedPhone, setTouchedPhone] =
|
|
2803
|
-
const [touchedCpf, setTouchedCpf] =
|
|
2804
|
-
const [formSubmitAttempted, setFormSubmitAttempted] =
|
|
2805
|
-
const [submitting, setSubmitting] =
|
|
2806
|
-
const [error, setError] =
|
|
2807
|
-
const [emailTaken, setEmailTaken] =
|
|
2808
|
-
const [loginUrl, setLoginUrl] =
|
|
2809
|
-
const [emailStatus, setEmailStatus] =
|
|
2849
|
+
const [touchedName, setTouchedName] = useState9(false);
|
|
2850
|
+
const [touchedEmail, setTouchedEmail] = useState9(false);
|
|
2851
|
+
const [touchedEmailConfirm, setTouchedEmailConfirm] = useState9(false);
|
|
2852
|
+
const [touchedPhone, setTouchedPhone] = useState9(false);
|
|
2853
|
+
const [touchedCpf, setTouchedCpf] = useState9(false);
|
|
2854
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState9(false);
|
|
2855
|
+
const [submitting, setSubmitting] = useState9(false);
|
|
2856
|
+
const [error, setError] = useState9(null);
|
|
2857
|
+
const [emailTaken, setEmailTaken] = useState9(false);
|
|
2858
|
+
const [loginUrl, setLoginUrl] = useState9(null);
|
|
2859
|
+
const [emailStatus, setEmailStatus] = useState9("idle");
|
|
2810
2860
|
const lastCheckedEmail = useRef6("");
|
|
2811
2861
|
useEffect10(() => {
|
|
2812
2862
|
if (!email || !EMAIL_RE.test(email)) {
|
|
@@ -2890,10 +2940,16 @@ function useCheckoutForm(args) {
|
|
|
2890
2940
|
name: card.holderName || name.trim(),
|
|
2891
2941
|
email,
|
|
2892
2942
|
cpfCnpj: cpf.replace(/\D/g, ""),
|
|
2893
|
-
//
|
|
2894
|
-
//
|
|
2895
|
-
|
|
2896
|
-
|
|
2943
|
+
// Plan-V 0.28.4 — Asaas's `creditCardHolderInfo.postalCode`
|
|
2944
|
+
// rejects all-zeros with `tokenize_failed:invalid_holderInfo`
|
|
2945
|
+
// ("O CEP informado é inválido."). The default CheckoutPage
|
|
2946
|
+
// doesn't collect CEP from the user (reference design skipped
|
|
2947
|
+
// it), so ship a known-valid placeholder. Apps that need real
|
|
2948
|
+
// customer addresses must override CheckoutPageDefault and
|
|
2949
|
+
// collect CEP — see personalburn's PaywallStepPagamento for
|
|
2950
|
+
// the pattern.
|
|
2951
|
+
postalCode: "01001000",
|
|
2952
|
+
addressNumber: "100",
|
|
2897
2953
|
phone
|
|
2898
2954
|
}
|
|
2899
2955
|
};
|
|
@@ -2968,1867 +3024,1868 @@ function mod11(digits, len) {
|
|
|
2968
3024
|
}
|
|
2969
3025
|
|
|
2970
3026
|
// src/hooks/usePlan.ts
|
|
2971
|
-
import { useHook as
|
|
3027
|
+
import { useHook as useHook11 } from "@hook-sdk/sdk";
|
|
2972
3028
|
function usePlan() {
|
|
2973
|
-
const { plan } =
|
|
3029
|
+
const { plan } = useHook11();
|
|
2974
3030
|
return plan;
|
|
2975
3031
|
}
|
|
2976
3032
|
|
|
2977
|
-
// src/
|
|
2978
|
-
import {
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
}
|
|
3033
|
+
// src/components/paywall/Paywall.tsx
|
|
3034
|
+
import { useEffect as useEffect11, useMemo as useMemo5 } from "react";
|
|
3035
|
+
import { useHook as useHook12 } from "@hook-sdk/sdk";
|
|
3036
|
+
|
|
3037
|
+
// src/utils/price.ts
|
|
3038
|
+
function formatBRL(cents) {
|
|
3039
|
+
if (cents === null || cents === void 0) return "";
|
|
3040
|
+
const reais = cents / 100;
|
|
3041
|
+
return new Intl.NumberFormat("pt-BR", {
|
|
3042
|
+
style: "currency",
|
|
3043
|
+
currency: "BRL"
|
|
3044
|
+
}).format(reais);
|
|
2990
3045
|
}
|
|
2991
|
-
function
|
|
2992
|
-
|
|
3046
|
+
function monthlyFromYearly(yearlyCents) {
|
|
3047
|
+
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
3048
|
+
return Math.round(yearlyCents / 12);
|
|
2993
3049
|
}
|
|
2994
|
-
function
|
|
2995
|
-
|
|
2996
|
-
return
|
|
3050
|
+
function dailyFromYearly(yearlyCents) {
|
|
3051
|
+
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
3052
|
+
return Math.round(yearlyCents / 365);
|
|
2997
3053
|
}
|
|
2998
|
-
function
|
|
2999
|
-
|
|
3000
|
-
if (
|
|
3001
|
-
|
|
3054
|
+
function computeAnchorCents(baseCents, multiplier) {
|
|
3055
|
+
if (multiplier === null || multiplier === void 0) return null;
|
|
3056
|
+
if (!Number.isFinite(multiplier)) return null;
|
|
3057
|
+
if (multiplier <= 1) return null;
|
|
3058
|
+
return Math.round(baseCents * multiplier);
|
|
3002
3059
|
}
|
|
3003
|
-
function
|
|
3004
|
-
|
|
3005
|
-
return
|
|
3060
|
+
function discountPercent(anchorCents, realCents) {
|
|
3061
|
+
if (anchorCents <= realCents) return 0;
|
|
3062
|
+
return Math.floor((anchorCents - realCents) / anchorCents * 100);
|
|
3006
3063
|
}
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3064
|
+
|
|
3065
|
+
// src/components/paywall/PaywallProvider.tsx
|
|
3066
|
+
import { createContext as createContext3 } from "react";
|
|
3067
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
3068
|
+
var PaywallContext = createContext3(null);
|
|
3069
|
+
function PaywallProvider({ children }) {
|
|
3070
|
+
const state = usePaywallState();
|
|
3071
|
+
return /* @__PURE__ */ jsx28(PaywallContext.Provider, { value: state, children });
|
|
3013
3072
|
}
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
if (
|
|
3020
|
-
|
|
3021
|
-
|
|
3073
|
+
|
|
3074
|
+
// src/components/paywall/usePaywallContext.ts
|
|
3075
|
+
import { useContext as useContext4 } from "react";
|
|
3076
|
+
function usePaywallContext() {
|
|
3077
|
+
const ctx = useContext4(PaywallContext);
|
|
3078
|
+
if (!ctx) {
|
|
3079
|
+
throw new Error("usePaywallContext must be used within <PaywallProvider>");
|
|
3080
|
+
}
|
|
3081
|
+
return ctx;
|
|
3022
3082
|
}
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3083
|
+
|
|
3084
|
+
// src/components/paywall/PaywallMethodTabs.tsx
|
|
3085
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
3086
|
+
function PaywallMethodTabs({
|
|
3087
|
+
labels,
|
|
3088
|
+
className,
|
|
3089
|
+
tabClassName,
|
|
3090
|
+
tabActiveClassName
|
|
3091
|
+
}) {
|
|
3092
|
+
const { methods, selectedMethod, selectMethod } = usePaywallContext();
|
|
3093
|
+
if (methods.length < 2) return null;
|
|
3094
|
+
return /* @__PURE__ */ jsx29("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
|
|
3095
|
+
const active = m === selectedMethod;
|
|
3096
|
+
const label = labels[m] ?? m;
|
|
3097
|
+
return /* @__PURE__ */ jsx29(
|
|
3098
|
+
"button",
|
|
3099
|
+
{
|
|
3100
|
+
type: "button",
|
|
3101
|
+
role: "tab",
|
|
3102
|
+
"aria-selected": active,
|
|
3103
|
+
"aria-controls": `paywall-tab-${m}`,
|
|
3104
|
+
tabIndex: active ? 0 : -1,
|
|
3105
|
+
onClick: () => selectMethod(m),
|
|
3106
|
+
className: [tabClassName, active ? tabActiveClassName : ""].filter(Boolean).join(" "),
|
|
3107
|
+
children: label
|
|
3108
|
+
},
|
|
3109
|
+
m
|
|
3110
|
+
);
|
|
3111
|
+
}) });
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
// src/components/paywall/PaywallMethodContent.tsx
|
|
3115
|
+
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
3116
|
+
function PaywallMethodContent({ copy, className, rowClassName }) {
|
|
3117
|
+
const { selectedMethod, hasConsumedTrial } = usePaywallContext();
|
|
3118
|
+
const useCardConsumed = selectedMethod === "card" && hasConsumedTrial && copy.cardConsumedTrial;
|
|
3119
|
+
const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : selectedMethod === "pix-auto" || selectedMethod === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
|
|
3120
|
+
return /* @__PURE__ */ jsx30("div", { role: "tabpanel", id: `paywall-tab-${selectedMethod}`, className, children: rows.map((row, i) => /* @__PURE__ */ jsx30("div", { className: rowClassName, children: row }, i)) });
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
// src/components/paywall/PaywallCyclePicker.tsx
|
|
3124
|
+
import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
3125
|
+
var VARIANT_CLASSES = {
|
|
3126
|
+
default: { card: "", cardSelected: "" },
|
|
3127
|
+
"premium-gold": {
|
|
3128
|
+
card: "",
|
|
3129
|
+
cardSelected: "border-2 border-yellow-400/80 ring-2 ring-yellow-400/20"
|
|
3130
|
+
},
|
|
3131
|
+
"pink-pill": {
|
|
3132
|
+
card: "rounded-2xl",
|
|
3133
|
+
cardSelected: "border-2 border-pink-500"
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
function PaywallCyclePicker({
|
|
3137
|
+
labels,
|
|
3138
|
+
className,
|
|
3139
|
+
cardClassName,
|
|
3140
|
+
cardSelectedClassName,
|
|
3141
|
+
anchorClassName,
|
|
3142
|
+
variant = "default",
|
|
3143
|
+
render
|
|
3144
|
+
}) {
|
|
3145
|
+
const ctx = usePaywallContext();
|
|
3146
|
+
const { cycle: selected, setCycle, plan, anchorPriceCents } = ctx;
|
|
3147
|
+
const cycles = ["MONTHLY", "YEARLY"];
|
|
3148
|
+
if (render) {
|
|
3149
|
+
return /* @__PURE__ */ jsx31("div", { className, children: render({ cycles, selected, setCycle, plan, anchorPriceCents }) });
|
|
3150
|
+
}
|
|
3151
|
+
if (cycles.length < 2) return null;
|
|
3152
|
+
const v = VARIANT_CLASSES[variant];
|
|
3153
|
+
const composedCardClassName = [v.card, cardClassName].filter(Boolean).join(" ");
|
|
3154
|
+
const composedCardSelectedClassName = [v.cardSelected, cardSelectedClassName].filter(Boolean).join(" ");
|
|
3155
|
+
const monthlyCents = plan?.monthlyCents ?? 0;
|
|
3156
|
+
const yearlyCents = plan?.yearlyCents ?? 0;
|
|
3157
|
+
const anchorMonthly = plan?.anchorMonthlyCents ?? null;
|
|
3158
|
+
const anchorYearly = plan?.anchorYearlyCents ?? null;
|
|
3159
|
+
return /* @__PURE__ */ jsx31(
|
|
3160
|
+
"div",
|
|
3161
|
+
{
|
|
3162
|
+
role: "radiogroup",
|
|
3163
|
+
"aria-label": "Ciclo de cobran\xE7a",
|
|
3164
|
+
className: ["flex flex-row gap-2", className].filter(Boolean).join(" "),
|
|
3165
|
+
children: cycles.map((c) => {
|
|
3166
|
+
const active = c === selected;
|
|
3167
|
+
const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
|
|
3168
|
+
const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
|
|
3169
|
+
const mainCents = c === "YEARLY" ? Math.round(yearlyCents / 12) : monthlyCents;
|
|
3170
|
+
const anchorCents = c === "YEARLY" ? anchorYearly : anchorMonthly;
|
|
3171
|
+
return /* @__PURE__ */ jsxs19(
|
|
3172
|
+
"button",
|
|
3173
|
+
{
|
|
3174
|
+
type: "button",
|
|
3175
|
+
role: "radio",
|
|
3176
|
+
"aria-checked": active,
|
|
3177
|
+
onClick: () => setCycle(c),
|
|
3178
|
+
className: [
|
|
3179
|
+
"flex flex-col items-center gap-0.5",
|
|
3180
|
+
composedCardClassName,
|
|
3181
|
+
active ? composedCardSelectedClassName : ""
|
|
3182
|
+
].filter(Boolean).join(" "),
|
|
3183
|
+
children: [
|
|
3184
|
+
/* @__PURE__ */ jsx31("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
|
|
3185
|
+
/* @__PURE__ */ jsx31("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
|
|
3186
|
+
/* @__PURE__ */ jsx31("span", { className: "text-xs opacity-60 leading-tight", children: label }),
|
|
3187
|
+
anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx31("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ jsx31("s", { children: formatBRL(anchorCents) }) }) : null
|
|
3188
|
+
]
|
|
3189
|
+
},
|
|
3190
|
+
c
|
|
3191
|
+
);
|
|
3192
|
+
})
|
|
3035
3193
|
}
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3194
|
+
);
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
// src/components/paywall/Paywall.tsx
|
|
3198
|
+
import { jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3199
|
+
var NBSP = "\xA0";
|
|
3200
|
+
function Paywall({
|
|
3201
|
+
copy,
|
|
3202
|
+
themeClasses = {},
|
|
3203
|
+
slots = {},
|
|
3204
|
+
onBeforeCheckout
|
|
3205
|
+
}) {
|
|
3206
|
+
return /* @__PURE__ */ jsx32(PaywallProvider, { children: /* @__PURE__ */ jsx32(
|
|
3207
|
+
PaywallInner,
|
|
3208
|
+
{
|
|
3209
|
+
copy,
|
|
3210
|
+
themeClasses,
|
|
3211
|
+
slots,
|
|
3212
|
+
onBeforeCheckout
|
|
3041
3213
|
}
|
|
3042
|
-
}
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
const
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
return cyclePrice ?? 0;
|
|
3063
|
-
}, [form.method, cyclePrice, planInfo]);
|
|
3064
|
-
const todayAmount = formatBrl(todayCents);
|
|
3065
|
-
const cyclePriceText = cyclePrice !== null ? formatBrl(cyclePrice) : "";
|
|
3066
|
-
const annualSavingsCents = useMemo5(() => {
|
|
3067
|
-
if (!planInfo || !planInfo.yearlyPriceCents) return 0;
|
|
3068
|
-
return planInfo.priceCents * 12 - planInfo.yearlyPriceCents;
|
|
3069
|
-
}, [planInfo]);
|
|
3070
|
-
const trialDays = planInfo?.trialDays ?? 7;
|
|
3071
|
-
const cardBrand = detectCardBrand(form.card.number);
|
|
3072
|
-
async function onSubmit(e) {
|
|
3073
|
-
e.preventDefault();
|
|
3074
|
-
const result = await form.submit();
|
|
3075
|
-
if (!result) return;
|
|
3076
|
-
if (form.method === "pix-auto" && result.pix_qr_payload) {
|
|
3077
|
-
try {
|
|
3078
|
-
sessionStorage.setItem(
|
|
3079
|
-
PIX_PAYLOAD_KEY,
|
|
3080
|
-
JSON.stringify({
|
|
3081
|
-
payload: result.pix_qr_payload,
|
|
3082
|
-
base64: result.pix_qr_base64 ?? null,
|
|
3083
|
-
subscriptionId: result.subscription_id,
|
|
3084
|
-
pixAuthorizationId: result.pix_authorization_id ?? null
|
|
3085
|
-
})
|
|
3086
|
-
);
|
|
3087
|
-
} catch {
|
|
3214
|
+
) });
|
|
3215
|
+
}
|
|
3216
|
+
function PaywallInner({
|
|
3217
|
+
copy,
|
|
3218
|
+
themeClasses = {},
|
|
3219
|
+
slots = {},
|
|
3220
|
+
onBeforeCheckout
|
|
3221
|
+
}) {
|
|
3222
|
+
const { track: track2 } = useHook12();
|
|
3223
|
+
const s = usePaywallContext();
|
|
3224
|
+
const priceLabel = formatBRL(s.currentPriceCents).replace(new RegExp(NBSP, "g"), " ");
|
|
3225
|
+
const trialDaysCardLabel = String(s.trialDaysCard);
|
|
3226
|
+
const ctaLabel = useMemo5(() => {
|
|
3227
|
+
if (s.isFree) return copy.freeCta ?? "Come\xE7ar agora";
|
|
3228
|
+
if (s.selectedMethod === "card") {
|
|
3229
|
+
if (s.hasConsumedTrial && copy.cardConsumedTrial) {
|
|
3230
|
+
return interp(copy.cardConsumedTrial.ctaTemplate, {
|
|
3231
|
+
price: priceLabel,
|
|
3232
|
+
days: trialDaysCardLabel
|
|
3233
|
+
});
|
|
3088
3234
|
}
|
|
3089
|
-
|
|
3235
|
+
if (s.trialDaysCard > 0) {
|
|
3236
|
+
return interp(copy.card.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
|
|
3237
|
+
}
|
|
3238
|
+
return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, {
|
|
3239
|
+
price: priceLabel,
|
|
3240
|
+
days: trialDaysCardLabel
|
|
3241
|
+
}) : `Assinar por ${priceLabel}`;
|
|
3242
|
+
}
|
|
3243
|
+
return interp(copy.pix.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
|
|
3244
|
+
}, [
|
|
3245
|
+
s.isFree,
|
|
3246
|
+
s.selectedMethod,
|
|
3247
|
+
s.hasConsumedTrial,
|
|
3248
|
+
s.trialDaysCard,
|
|
3249
|
+
copy,
|
|
3250
|
+
priceLabel,
|
|
3251
|
+
trialDaysCardLabel
|
|
3252
|
+
]);
|
|
3253
|
+
const switchHint = useMemo5(() => {
|
|
3254
|
+
if (s.methods.length < 2) return void 0;
|
|
3255
|
+
return s.selectedMethod === "card" ? copy.card.switchHint : copy.pix.switchHint;
|
|
3256
|
+
}, [s.methods.length, s.selectedMethod, copy]);
|
|
3257
|
+
useEffect11(() => {
|
|
3258
|
+
if (!s.initialLoadComplete) return;
|
|
3259
|
+
track2("paywall_view", {
|
|
3260
|
+
default_method: s.selectedMethod,
|
|
3261
|
+
default_cycle: s.cycle,
|
|
3262
|
+
available_methods: s.methods
|
|
3263
|
+
});
|
|
3264
|
+
}, [s.initialLoadComplete]);
|
|
3265
|
+
const handleCta = async () => {
|
|
3266
|
+
track2("paywall_cta_clicked", {
|
|
3267
|
+
method: s.selectedMethod,
|
|
3268
|
+
cycle: s.cycle,
|
|
3269
|
+
price_cents: s.currentPriceCents,
|
|
3270
|
+
had_consumed_trial: s.hasConsumedTrial
|
|
3271
|
+
});
|
|
3272
|
+
if (onBeforeCheckout) {
|
|
3273
|
+
await onBeforeCheckout(s.selectedMethod, s.cycle);
|
|
3090
3274
|
return;
|
|
3091
3275
|
}
|
|
3092
|
-
|
|
3093
|
-
}
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
/* @__PURE__ */ jsxs18("section", { className: "px-5 pt-5", children: [
|
|
3142
|
-
/* @__PURE__ */ jsx27("h2", { className: "font-display text-2xl mb-3.5 leading-tight text-foreground", children: "Quase l\xE1." }),
|
|
3143
|
-
/* @__PURE__ */ jsx27(FieldLabel, { children: "Email" }),
|
|
3144
|
-
/* @__PURE__ */ jsx27(
|
|
3145
|
-
FieldInput,
|
|
3146
|
-
{
|
|
3147
|
-
type: "email",
|
|
3148
|
-
inputMode: "email",
|
|
3149
|
-
autoComplete: "email",
|
|
3150
|
-
autoCapitalize: "none",
|
|
3151
|
-
autoCorrect: "off",
|
|
3152
|
-
spellCheck: false,
|
|
3153
|
-
placeholder: "seu@email.com",
|
|
3154
|
-
value: form.email,
|
|
3155
|
-
onChange: form.setEmail,
|
|
3156
|
-
onBlur: form.markEmailTouched,
|
|
3157
|
-
error: form.emailError,
|
|
3158
|
-
valid: form.emailStatus === "available"
|
|
3159
|
-
}
|
|
3160
|
-
),
|
|
3161
|
-
!form.emailError && /* @__PURE__ */ jsx27(FieldHint, { children: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : "Voc\xEA vai usar este email para entrar no app" }),
|
|
3162
|
-
/* @__PURE__ */ jsx27("div", { className: "h-3" }),
|
|
3163
|
-
/* @__PURE__ */ jsx27(FieldLabel, { children: "Nome completo" }),
|
|
3164
|
-
/* @__PURE__ */ jsx27(
|
|
3165
|
-
FieldInput,
|
|
3166
|
-
{
|
|
3167
|
-
type: "text",
|
|
3168
|
-
autoComplete: "name",
|
|
3169
|
-
placeholder: "como est\xE1 no documento",
|
|
3170
|
-
value: form.name,
|
|
3171
|
-
onChange: form.setName,
|
|
3172
|
-
onBlur: form.markNameTouched,
|
|
3173
|
-
error: form.nameError,
|
|
3174
|
-
valid: !!form.name && !form.nameError
|
|
3175
|
-
}
|
|
3176
|
-
),
|
|
3177
|
-
/* @__PURE__ */ jsx27("div", { className: "h-3" }),
|
|
3178
|
-
/* @__PURE__ */ jsx27(FieldLabel, { children: "CPF" }),
|
|
3179
|
-
/* @__PURE__ */ jsx27(
|
|
3180
|
-
FieldInput,
|
|
3181
|
-
{
|
|
3182
|
-
type: "text",
|
|
3183
|
-
inputMode: "numeric",
|
|
3184
|
-
placeholder: "000.000.000-00",
|
|
3185
|
-
value: form.cpf,
|
|
3186
|
-
onChange: (v) => form.setCpf(formatCpf(v)),
|
|
3187
|
-
onBlur: form.markCpfTouched,
|
|
3188
|
-
error: form.cpfError,
|
|
3189
|
-
valid: !!form.cpf && !form.cpfError
|
|
3190
|
-
}
|
|
3191
|
-
),
|
|
3192
|
-
form.method === "card" ? /* @__PURE__ */ jsxs18(Fragment7, { children: [
|
|
3193
|
-
/* @__PURE__ */ jsx27("div", { className: "h-3" }),
|
|
3194
|
-
/* @__PURE__ */ jsx27(FieldLabel, { children: "Telefone" }),
|
|
3195
|
-
/* @__PURE__ */ jsx27(
|
|
3196
|
-
FieldInput,
|
|
3197
|
-
{
|
|
3198
|
-
type: "tel",
|
|
3199
|
-
inputMode: "tel",
|
|
3200
|
-
autoComplete: "tel",
|
|
3201
|
-
placeholder: "(11) 99999-9999",
|
|
3202
|
-
value: form.phone,
|
|
3203
|
-
onChange: form.setPhone,
|
|
3204
|
-
onBlur: form.markPhoneTouched,
|
|
3205
|
-
error: form.phoneError,
|
|
3206
|
-
valid: !!form.phone && !form.phoneError
|
|
3207
|
-
}
|
|
3208
|
-
),
|
|
3209
|
-
!form.phoneError && /* @__PURE__ */ jsx27(FieldHint, { children: "Usado pra confirmar pagamento e tratar disputas." })
|
|
3210
|
-
] }) : null
|
|
3211
|
-
] }),
|
|
3212
|
-
/* @__PURE__ */ jsxs18("section", { className: "px-5 pt-5", children: [
|
|
3213
|
-
/* @__PURE__ */ jsx27(FieldLabel, { children: "Forma de pagamento" }),
|
|
3214
|
-
/* @__PURE__ */ jsxs18("div", { role: "tablist", className: "flex gap-1.5 bg-muted p-1 rounded-xl", children: [
|
|
3215
|
-
/* @__PURE__ */ jsx27(
|
|
3216
|
-
TabButton,
|
|
3217
|
-
{
|
|
3218
|
-
active: form.method === "card",
|
|
3219
|
-
onClick: () => form.setMethod("card"),
|
|
3220
|
-
icon: /* @__PURE__ */ jsx27(CardIcon, { className: "w-3.5 h-3.5" }),
|
|
3221
|
-
label: "Cart\xE3o",
|
|
3222
|
-
subtitle: trialDays > 0 ? `${trialDays} dias gr\xE1tis` : "pague hoje",
|
|
3223
|
-
subtitleActiveClass: "text-emerald-700"
|
|
3224
|
-
}
|
|
3225
|
-
),
|
|
3226
|
-
/* @__PURE__ */ jsx27(
|
|
3227
|
-
TabButton,
|
|
3228
|
-
{
|
|
3229
|
-
active: form.method === "pix-auto",
|
|
3230
|
-
onClick: () => form.setMethod("pix-auto"),
|
|
3231
|
-
icon: /* @__PURE__ */ jsx27(PixIcon, { className: "w-3.5 h-3.5" }),
|
|
3232
|
-
label: "Pix",
|
|
3233
|
-
subtitle: `pague hoje \xB7 garantia ${trialDays}d`,
|
|
3234
|
-
subtitleActiveClass: "text-foreground/70"
|
|
3235
|
-
}
|
|
3236
|
-
)
|
|
3237
|
-
] })
|
|
3238
|
-
] }),
|
|
3239
|
-
form.method === "card" ? /* @__PURE__ */ jsxs18("section", { className: "px-5 pt-3.5", children: [
|
|
3240
|
-
/* @__PURE__ */ jsx27(FieldLabel, { children: "N\xFAmero do cart\xE3o" }),
|
|
3241
|
-
/* @__PURE__ */ jsxs18("div", { className: "relative", children: [
|
|
3242
|
-
/* @__PURE__ */ jsx27(
|
|
3243
|
-
FieldInput,
|
|
3244
|
-
{
|
|
3245
|
-
type: "text",
|
|
3246
|
-
inputMode: "numeric",
|
|
3247
|
-
autoComplete: "cc-number",
|
|
3248
|
-
placeholder: "0000 0000 0000 0000",
|
|
3249
|
-
value: form.card.number,
|
|
3250
|
-
onChange: (v) => form.setCard({ number: formatCardNumber(v) }),
|
|
3251
|
-
style: cardBrand ? { paddingRight: "4.5rem" } : void 0
|
|
3252
|
-
}
|
|
3253
|
-
),
|
|
3254
|
-
cardBrand && /* @__PURE__ */ jsx27("span", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-[10px] font-bold tracking-wide px-1.5 py-0.5 rounded bg-muted text-muted-foreground", children: cardBrand })
|
|
3255
|
-
] }),
|
|
3256
|
-
/* @__PURE__ */ jsx27("div", { className: "h-3" }),
|
|
3257
|
-
/* @__PURE__ */ jsxs18("div", { className: "flex gap-2.5", children: [
|
|
3258
|
-
/* @__PURE__ */ jsxs18("div", { className: "flex-1", children: [
|
|
3259
|
-
/* @__PURE__ */ jsx27(FieldLabel, { children: "Validade" }),
|
|
3260
|
-
/* @__PURE__ */ jsx27(
|
|
3261
|
-
FieldInput,
|
|
3262
|
-
{
|
|
3263
|
-
type: "text",
|
|
3264
|
-
inputMode: "numeric",
|
|
3265
|
-
autoComplete: "cc-exp",
|
|
3266
|
-
placeholder: "MM/AA",
|
|
3267
|
-
value: expiryMmAa,
|
|
3268
|
-
onChange: (v) => setExpiryMmAa(formatExpiryMmAa(v))
|
|
3269
|
-
}
|
|
3270
|
-
)
|
|
3271
|
-
] }),
|
|
3272
|
-
/* @__PURE__ */ jsxs18("div", { className: "flex-1", children: [
|
|
3273
|
-
/* @__PURE__ */ jsx27(FieldLabel, { children: "CVV" }),
|
|
3274
|
-
/* @__PURE__ */ jsx27(
|
|
3275
|
-
FieldInput,
|
|
3276
|
-
{
|
|
3277
|
-
type: "text",
|
|
3278
|
-
inputMode: "numeric",
|
|
3279
|
-
autoComplete: "cc-csc",
|
|
3280
|
-
placeholder: "3 d\xEDgitos",
|
|
3281
|
-
value: form.card.ccv,
|
|
3282
|
-
onChange: (v) => form.setCard({ ccv: v.replace(/\D/g, "").slice(0, 4) })
|
|
3283
|
-
}
|
|
3284
|
-
)
|
|
3285
|
-
] })
|
|
3286
|
-
] }),
|
|
3287
|
-
/* @__PURE__ */ jsx27("div", { className: "h-3" }),
|
|
3288
|
-
/* @__PURE__ */ jsx27(FieldLabel, { children: "Nome no cart\xE3o" }),
|
|
3289
|
-
/* @__PURE__ */ jsx27(
|
|
3290
|
-
FieldInput,
|
|
3291
|
-
{
|
|
3292
|
-
type: "text",
|
|
3293
|
-
autoComplete: "cc-name",
|
|
3294
|
-
placeholder: "como est\xE1 no cart\xE3o",
|
|
3295
|
-
value: form.card.holderName,
|
|
3296
|
-
onChange: (v) => form.setCard({ holderName: v })
|
|
3297
|
-
}
|
|
3298
|
-
)
|
|
3299
|
-
] }) : /* @__PURE__ */ jsx27("section", { className: "px-5 pt-3.5", children: /* @__PURE__ */ jsxs18("div", { className: "rounded-2xl bg-card border border-border p-3.5 flex gap-3.5 items-center", children: [
|
|
3300
|
-
/* @__PURE__ */ jsx27("div", { className: "w-[72px] h-[72px] rounded-xl shrink-0 border-2 border-foreground relative overflow-hidden bg-muted", children: /* @__PURE__ */ jsx27("div", { className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[22px] h-[22px] bg-background flex items-center justify-center", children: /* @__PURE__ */ jsx27(PixIcon, { className: "w-3.5 h-3.5 text-foreground" }) }) }),
|
|
3301
|
-
/* @__PURE__ */ jsxs18("div", { className: "flex-1", children: [
|
|
3302
|
-
/* @__PURE__ */ jsx27("div", { className: "text-xs font-bold uppercase tracking-wider text-muted-foreground", children: "pagamento em segundos" }),
|
|
3303
|
-
/* @__PURE__ */ jsxs18("div", { className: "text-sm text-foreground mt-1 leading-snug", children: [
|
|
3304
|
-
"Geramos seu ",
|
|
3305
|
-
/* @__PURE__ */ jsx27("b", { children: "QR Pix" }),
|
|
3306
|
-
" no pr\xF3ximo passo. Pague pelo app do banco e seu acesso libera ",
|
|
3307
|
-
/* @__PURE__ */ jsx27("b", { children: "imediatamente" }),
|
|
3308
|
-
"."
|
|
3309
|
-
] })
|
|
3310
|
-
] })
|
|
3311
|
-
] }) }),
|
|
3312
|
-
/* @__PURE__ */ jsx27("section", { className: "px-5 pt-5", children: /* @__PURE__ */ jsxs18("div", { className: "bg-muted rounded-2xl p-4", children: [
|
|
3313
|
-
/* @__PURE__ */ jsxs18("div", { className: "flex justify-between mb-2.5", children: [
|
|
3314
|
-
/* @__PURE__ */ jsxs18("div", { children: [
|
|
3315
|
-
/* @__PURE__ */ jsx27("div", { className: "text-sm font-semibold text-foreground", children: annual ? "Plano Anual" : "Plano Mensal" }),
|
|
3316
|
-
/* @__PURE__ */ jsx27("div", { className: "text-[11px] text-muted-foreground", children: "Coach" })
|
|
3317
|
-
] }),
|
|
3318
|
-
/* @__PURE__ */ jsxs18("div", { className: "text-right", children: [
|
|
3319
|
-
/* @__PURE__ */ jsxs18("div", { className: "text-sm font-bold text-foreground", children: [
|
|
3320
|
-
cyclePriceText,
|
|
3321
|
-
"/",
|
|
3322
|
-
annual ? "ano" : "m\xEAs"
|
|
3323
|
-
] }),
|
|
3324
|
-
annual && annualSavingsCents > 0 && /* @__PURE__ */ jsxs18("div", { className: "text-[11px] text-emerald-700 font-semibold", children: [
|
|
3325
|
-
"economia ",
|
|
3326
|
-
formatBrl(annualSavingsCents)
|
|
3327
|
-
] })
|
|
3328
|
-
] })
|
|
3329
|
-
] }),
|
|
3330
|
-
/* @__PURE__ */ jsx27("div", { className: "h-px bg-border my-3" }),
|
|
3331
|
-
/* @__PURE__ */ jsxs18("div", { className: "flex justify-between items-baseline", children: [
|
|
3332
|
-
/* @__PURE__ */ jsxs18("div", { children: [
|
|
3333
|
-
/* @__PURE__ */ jsx27("div", { className: "text-sm font-bold text-foreground", children: "Voc\xEA paga hoje" }),
|
|
3334
|
-
form.method === "card" && trialDays > 0 && /* @__PURE__ */ jsxs18("div", { className: "text-[11px] text-muted-foreground mt-0.5", children: [
|
|
3335
|
-
"cobran\xE7a inicia no dia ",
|
|
3336
|
-
trialDays
|
|
3337
|
-
] }),
|
|
3338
|
-
form.method === "pix-auto" && /* @__PURE__ */ jsxs18("div", { className: "text-[11px] text-emerald-700 mt-0.5 font-semibold", children: [
|
|
3339
|
-
"reembolso garantido at\xE9 o dia ",
|
|
3340
|
-
trialDays
|
|
3341
|
-
] })
|
|
3342
|
-
] }),
|
|
3343
|
-
/* @__PURE__ */ jsx27("div", { className: "text-2xl font-bold font-display tracking-tight text-foreground", children: todayAmount })
|
|
3344
|
-
] })
|
|
3345
|
-
] }) }),
|
|
3346
|
-
/* @__PURE__ */ jsx27("div", { className: "h-4" }),
|
|
3347
|
-
/* @__PURE__ */ jsxs18("div", { className: "sticky bottom-0 px-5 pt-3.5 pb-6 bg-gradient-to-b from-transparent to-background", children: [
|
|
3348
|
-
/* @__PURE__ */ jsx27(
|
|
3276
|
+
await s.submit();
|
|
3277
|
+
};
|
|
3278
|
+
const ctaTheme = s.selectedMethod === "card" ? themeClasses.ctaCard : themeClasses.ctaPix;
|
|
3279
|
+
return /* @__PURE__ */ jsxs20("div", { className: themeClasses.container, children: [
|
|
3280
|
+
slots.heroSlot,
|
|
3281
|
+
/* @__PURE__ */ jsx32("h1", { className: themeClasses.headline, children: copy.headline }),
|
|
3282
|
+
/* @__PURE__ */ jsx32("ul", { children: copy.features.map((f) => /* @__PURE__ */ jsxs20("li", { className: themeClasses.feature, children: [
|
|
3283
|
+
"\u2713 ",
|
|
3284
|
+
/* @__PURE__ */ jsx32("span", { children: f })
|
|
3285
|
+
] }, f)) }),
|
|
3286
|
+
copy.socialProof ? /* @__PURE__ */ jsx32("p", { className: themeClasses.socialProof, children: copy.socialProof }) : null,
|
|
3287
|
+
slots.cyclePickerSlot ?? /* @__PURE__ */ jsx32(
|
|
3288
|
+
PaywallCyclePicker,
|
|
3289
|
+
{
|
|
3290
|
+
labels: copy.cycle,
|
|
3291
|
+
cardClassName: themeClasses.cycleCard,
|
|
3292
|
+
cardSelectedClassName: themeClasses.cycleCardSelected,
|
|
3293
|
+
anchorClassName: themeClasses.anchorPrice
|
|
3294
|
+
}
|
|
3295
|
+
),
|
|
3296
|
+
/* @__PURE__ */ jsx32(
|
|
3297
|
+
PaywallMethodTabs,
|
|
3298
|
+
{
|
|
3299
|
+
labels: { "pix-auto": copy.pix.tabLabel, card: copy.card.tabLabel },
|
|
3300
|
+
className: themeClasses.tabs,
|
|
3301
|
+
tabClassName: themeClasses.tab,
|
|
3302
|
+
tabActiveClassName: themeClasses.tabActive
|
|
3303
|
+
}
|
|
3304
|
+
),
|
|
3305
|
+
/* @__PURE__ */ jsx32(
|
|
3306
|
+
PaywallMethodContent,
|
|
3307
|
+
{
|
|
3308
|
+
copy: {
|
|
3309
|
+
pix: interpolateCopy(copy.pix, priceLabel, trialDaysCardLabel),
|
|
3310
|
+
card: interpolateCopy(copy.card, priceLabel, trialDaysCardLabel),
|
|
3311
|
+
cardConsumedTrial: copy.cardConsumedTrial ? {
|
|
3312
|
+
bodyRows: copy.cardConsumedTrial.bodyRows.map(
|
|
3313
|
+
(r) => interp(r, { price: priceLabel, days: trialDaysCardLabel })
|
|
3314
|
+
),
|
|
3315
|
+
ctaTemplate: copy.cardConsumedTrial.ctaTemplate
|
|
3316
|
+
} : void 0
|
|
3317
|
+
},
|
|
3318
|
+
className: themeClasses.tabContent,
|
|
3319
|
+
rowClassName: themeClasses.tabContentRow
|
|
3320
|
+
}
|
|
3321
|
+
),
|
|
3322
|
+
slots.beforeCtaSlot,
|
|
3323
|
+
/* @__PURE__ */ jsxs20("div", { children: [
|
|
3324
|
+
/* @__PURE__ */ jsx32(
|
|
3349
3325
|
"button",
|
|
3350
3326
|
{
|
|
3351
|
-
type: "
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
] }) : form.method === "card" ? /* @__PURE__ */ jsxs18(Fragment7, { children: [
|
|
3359
|
-
/* @__PURE__ */ jsx27(LockIcon, { className: "w-3.5 h-3.5" }),
|
|
3360
|
-
" Confirmar e come\xE7ar gr\xE1tis"
|
|
3361
|
-
] }) : /* @__PURE__ */ jsxs18(Fragment7, { children: [
|
|
3362
|
-
/* @__PURE__ */ jsx27(PixIcon, { className: "w-3.5 h-3.5" }),
|
|
3363
|
-
" Gerar QR \xB7 pagar ",
|
|
3364
|
-
todayAmount
|
|
3365
|
-
] })
|
|
3327
|
+
type: "button",
|
|
3328
|
+
onClick: () => {
|
|
3329
|
+
void handleCta();
|
|
3330
|
+
},
|
|
3331
|
+
disabled: s.submitting,
|
|
3332
|
+
className: ctaTheme,
|
|
3333
|
+
children: s.submitting ? "Abrindo checkout\u2026" : ctaLabel
|
|
3366
3334
|
}
|
|
3367
3335
|
),
|
|
3368
|
-
/* @__PURE__ */
|
|
3369
|
-
|
|
3370
|
-
/* @__PURE__ */ jsx27("u", { children: "Termos" }),
|
|
3371
|
-
". Pagamento seguro Asaas."
|
|
3372
|
-
] }),
|
|
3373
|
-
/* @__PURE__ */ jsxs18("div", { className: "mt-3 flex items-center justify-center gap-3.5 text-[11px] text-muted-foreground", children: [
|
|
3374
|
-
/* @__PURE__ */ jsxs18("span", { className: "inline-flex items-center gap-1", children: [
|
|
3375
|
-
/* @__PURE__ */ jsx27(LockIcon, { className: "w-2.5 h-2.5 opacity-60" }),
|
|
3376
|
-
" SSL 256-bit"
|
|
3377
|
-
] }),
|
|
3378
|
-
/* @__PURE__ */ jsx27("span", { className: "w-px h-2.5 bg-border" }),
|
|
3379
|
-
/* @__PURE__ */ jsx27("span", { children: "Pagamento via Asaas" }),
|
|
3380
|
-
/* @__PURE__ */ jsx27("span", { className: "w-px h-2.5 bg-border" }),
|
|
3381
|
-
/* @__PURE__ */ jsxs18("span", { children: [
|
|
3382
|
-
"Garantia ",
|
|
3383
|
-
trialDays,
|
|
3384
|
-
" dias"
|
|
3385
|
-
] })
|
|
3386
|
-
] })
|
|
3336
|
+
switchHint ? /* @__PURE__ */ jsx32("p", { className: themeClasses.switchHint, children: switchHint }) : null,
|
|
3337
|
+
/* @__PURE__ */ jsx32("p", { className: themeClasses.trustLine, children: copy.trustLine })
|
|
3387
3338
|
] })
|
|
3388
|
-
] }) });
|
|
3389
|
-
}
|
|
3390
|
-
function FieldLabel({ children }) {
|
|
3391
|
-
return /* @__PURE__ */ jsx27("div", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-1.5", children });
|
|
3392
|
-
}
|
|
3393
|
-
function FieldInput(props) {
|
|
3394
|
-
const baseClass = "w-full px-4 rounded-xl bg-card text-base text-foreground outline-none border-[1.5px] transition-colors";
|
|
3395
|
-
const stateClass = props.error ? "border-destructive focus:border-destructive" : props.valid ? "border-emerald-600 focus:border-emerald-700" : "border-border focus:border-foreground";
|
|
3396
|
-
return /* @__PURE__ */ jsxs18(Fragment7, { children: [
|
|
3397
|
-
/* @__PURE__ */ jsx27(
|
|
3398
|
-
"input",
|
|
3399
|
-
{
|
|
3400
|
-
type: props.type ?? "text",
|
|
3401
|
-
inputMode: props.inputMode,
|
|
3402
|
-
autoComplete: props.autoComplete,
|
|
3403
|
-
autoCapitalize: props.autoCapitalize,
|
|
3404
|
-
autoCorrect: props.autoCorrect,
|
|
3405
|
-
spellCheck: props.spellCheck,
|
|
3406
|
-
placeholder: props.placeholder,
|
|
3407
|
-
value: props.value,
|
|
3408
|
-
onChange: (e) => props.onChange(e.target.value),
|
|
3409
|
-
onBlur: props.onBlur,
|
|
3410
|
-
style: { height: "52px", ...props.style },
|
|
3411
|
-
className: `${baseClass} ${stateClass}`
|
|
3412
|
-
}
|
|
3413
|
-
),
|
|
3414
|
-
props.error ? /* @__PURE__ */ jsx27("div", { className: "mt-1.5 text-xs text-destructive font-medium", children: props.error }) : null
|
|
3415
3339
|
] });
|
|
3416
3340
|
}
|
|
3417
|
-
function
|
|
3418
|
-
return
|
|
3341
|
+
function interp(tpl, vars) {
|
|
3342
|
+
return tpl.replace(/\{(\w+)\}/g, (_m, k) => vars[k] ?? "");
|
|
3419
3343
|
}
|
|
3420
|
-
function
|
|
3421
|
-
return
|
|
3422
|
-
|
|
3423
|
-
{
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
onClick,
|
|
3428
|
-
className: `flex-1 flex flex-col items-center justify-center gap-0.5 py-2.5 px-1.5 rounded-lg text-sm font-semibold transition-colors ${active ? "bg-card text-foreground shadow-sm" : "bg-transparent text-muted-foreground"}`,
|
|
3429
|
-
style: { minHeight: 56 },
|
|
3430
|
-
children: [
|
|
3431
|
-
/* @__PURE__ */ jsxs18("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
3432
|
-
icon,
|
|
3433
|
-
" ",
|
|
3434
|
-
label
|
|
3435
|
-
] }),
|
|
3436
|
-
/* @__PURE__ */ jsx27("span", { className: `text-[10px] font-medium ${active ? subtitleActiveClass : "text-muted-foreground/70"}`, children: subtitle })
|
|
3437
|
-
]
|
|
3438
|
-
}
|
|
3439
|
-
);
|
|
3440
|
-
}
|
|
3441
|
-
function CardIcon({ className }) {
|
|
3442
|
-
return /* @__PURE__ */ jsxs18("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
3443
|
-
/* @__PURE__ */ jsx27("rect", { x: "2.5", y: "5.5", width: "19", height: "13", rx: "2.5" }),
|
|
3444
|
-
/* @__PURE__ */ jsx27("path", { d: "M2.5 10h19" })
|
|
3445
|
-
] });
|
|
3446
|
-
}
|
|
3447
|
-
function PixIcon({ className }) {
|
|
3448
|
-
return /* @__PURE__ */ jsx27("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx27("path", { d: "M12 2L2 12l10 10 10-10L12 2zm0 4.83L17.17 12 12 17.17 6.83 12 12 6.83z" }) });
|
|
3449
|
-
}
|
|
3450
|
-
function LockIcon({ className }) {
|
|
3451
|
-
return /* @__PURE__ */ jsxs18("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
3452
|
-
/* @__PURE__ */ jsx27("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2.5" }),
|
|
3453
|
-
/* @__PURE__ */ jsx27("path", { d: "M7.5 10V7a4.5 4.5 0 119 0v3" })
|
|
3454
|
-
] });
|
|
3344
|
+
function interpolateCopy(m, price, days) {
|
|
3345
|
+
return {
|
|
3346
|
+
tabLabel: m.tabLabel,
|
|
3347
|
+
bodyRows: m.bodyRows.map((r) => interp(r, { price, days })),
|
|
3348
|
+
ctaTemplate: m.ctaTemplate,
|
|
3349
|
+
switchHint: m.switchHint
|
|
3350
|
+
};
|
|
3455
3351
|
}
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3352
|
+
|
|
3353
|
+
// src/components/paywall/PaywallCta.tsx
|
|
3354
|
+
import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
3355
|
+
function PaywallCta({
|
|
3356
|
+
ctaLabel,
|
|
3357
|
+
loadingLabel,
|
|
3358
|
+
switchHint,
|
|
3359
|
+
trustLine,
|
|
3360
|
+
className,
|
|
3361
|
+
buttonClassName,
|
|
3362
|
+
switchHintClassName,
|
|
3363
|
+
trustClassName
|
|
3364
|
+
}) {
|
|
3365
|
+
const { submit, submitting } = usePaywallContext();
|
|
3366
|
+
const label = submitting && loadingLabel ? loadingLabel : ctaLabel;
|
|
3367
|
+
return /* @__PURE__ */ jsxs21("div", { className, children: [
|
|
3368
|
+
/* @__PURE__ */ jsx33(
|
|
3369
|
+
"button",
|
|
3370
|
+
{
|
|
3371
|
+
type: "button",
|
|
3372
|
+
onClick: () => {
|
|
3373
|
+
void submit();
|
|
3374
|
+
},
|
|
3375
|
+
disabled: submitting,
|
|
3376
|
+
className: buttonClassName,
|
|
3377
|
+
children: label
|
|
3378
|
+
}
|
|
3379
|
+
),
|
|
3380
|
+
switchHint ? /* @__PURE__ */ jsx33("p", { className: switchHintClassName, children: switchHint }) : null,
|
|
3381
|
+
/* @__PURE__ */ jsx33("p", { className: trustClassName, children: trustLine })
|
|
3460
3382
|
] });
|
|
3461
3383
|
}
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3384
|
+
|
|
3385
|
+
// src/components/paywall/blocks/PaywallEyebrow.tsx
|
|
3386
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
3387
|
+
var DEFAULT_EYEBROW_CLASSES = "text-xs uppercase tracking-widest font-semibold opacity-70";
|
|
3388
|
+
function PaywallEyebrow({ text, className }) {
|
|
3389
|
+
return /* @__PURE__ */ jsx34("div", { className: [DEFAULT_EYEBROW_CLASSES, className].filter(Boolean).join(" "), children: text });
|
|
3467
3390
|
}
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3391
|
+
|
|
3392
|
+
// src/components/paywall/blocks/PaywallHero.tsx
|
|
3393
|
+
import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
3394
|
+
var DEFAULT_GRADIENT = "absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent";
|
|
3395
|
+
function PaywallHero({
|
|
3396
|
+
src,
|
|
3397
|
+
alt = "",
|
|
3398
|
+
headline,
|
|
3399
|
+
aspectRatio = "16/9",
|
|
3400
|
+
gradientClassName,
|
|
3401
|
+
className,
|
|
3402
|
+
headlineClassName,
|
|
3403
|
+
imgClassName,
|
|
3404
|
+
render
|
|
3405
|
+
}) {
|
|
3406
|
+
if (render) {
|
|
3407
|
+
return /* @__PURE__ */ jsx35("div", { className, children: render({ src, headline }) });
|
|
3408
|
+
}
|
|
3409
|
+
return /* @__PURE__ */ jsxs22(
|
|
3410
|
+
"div",
|
|
3471
3411
|
{
|
|
3472
|
-
className: "
|
|
3473
|
-
style: {
|
|
3412
|
+
className: ["relative overflow-hidden", className].filter(Boolean).join(" "),
|
|
3413
|
+
style: { aspectRatio },
|
|
3414
|
+
children: [
|
|
3415
|
+
/* @__PURE__ */ jsx35(
|
|
3416
|
+
"img",
|
|
3417
|
+
{
|
|
3418
|
+
src,
|
|
3419
|
+
alt,
|
|
3420
|
+
className: ["absolute inset-0 w-full h-full object-cover", imgClassName].filter(Boolean).join(" ")
|
|
3421
|
+
}
|
|
3422
|
+
),
|
|
3423
|
+
/* @__PURE__ */ jsx35("div", { className: gradientClassName ?? DEFAULT_GRADIENT, "aria-hidden": "true" }),
|
|
3424
|
+
headline ? /* @__PURE__ */ jsx35(
|
|
3425
|
+
"h1",
|
|
3426
|
+
{
|
|
3427
|
+
className: ["absolute bottom-0 left-0 right-0 p-4 text-white font-bold text-2xl", headlineClassName].filter(Boolean).join(" "),
|
|
3428
|
+
children: headline
|
|
3429
|
+
}
|
|
3430
|
+
) : null
|
|
3431
|
+
]
|
|
3474
3432
|
}
|
|
3475
3433
|
);
|
|
3476
3434
|
}
|
|
3477
3435
|
|
|
3478
|
-
// src/
|
|
3479
|
-
import {
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
var TIMEOUT_MS = 30 * 60 * 1e3;
|
|
3485
|
-
function readPixPayload() {
|
|
3486
|
-
if (typeof window === "undefined") return null;
|
|
3487
|
-
try {
|
|
3488
|
-
const raw = sessionStorage.getItem(PIX_PAYLOAD_KEY2);
|
|
3489
|
-
if (!raw) return null;
|
|
3490
|
-
return JSON.parse(raw);
|
|
3491
|
-
} catch {
|
|
3492
|
-
return null;
|
|
3493
|
-
}
|
|
3436
|
+
// src/components/paywall/blocks/PaywallHeadline.tsx
|
|
3437
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
3438
|
+
var DEFAULT_HEADLINE_CLASSES = "text-2xl font-bold leading-tight";
|
|
3439
|
+
function PaywallHeadline({ text, className, as = "h1" }) {
|
|
3440
|
+
const Tag = as;
|
|
3441
|
+
return /* @__PURE__ */ jsx36(Tag, { className: [DEFAULT_HEADLINE_CLASSES, className].filter(Boolean).join(" "), children: text });
|
|
3494
3442
|
}
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3443
|
+
|
|
3444
|
+
// src/components/paywall/blocks/PaywallPriceHeadline.tsx
|
|
3445
|
+
import { jsx as jsx37 } from "react/jsx-runtime";
|
|
3446
|
+
var DEFAULT_CLASS = "text-2xl font-bold leading-tight";
|
|
3447
|
+
var CYCLE_LABEL = {
|
|
3448
|
+
MONTHLY: "mensal",
|
|
3449
|
+
YEARLY: "anual"
|
|
3450
|
+
};
|
|
3451
|
+
function PaywallPriceHeadline({
|
|
3452
|
+
template,
|
|
3453
|
+
className,
|
|
3454
|
+
as = "h1",
|
|
3455
|
+
render
|
|
3456
|
+
}) {
|
|
3457
|
+
const { cycle, currentMonthlyEquivCents, plan } = usePaywallContext();
|
|
3458
|
+
const yearlyCents = plan?.yearlyCents ?? null;
|
|
3459
|
+
const pricePerDay = formatBRL(dailyFromYearly(yearlyCents));
|
|
3460
|
+
const monthlyEquiv = currentMonthlyEquivCents ?? 0;
|
|
3461
|
+
const cycleLabel = CYCLE_LABEL[cycle] ?? cycle.toLowerCase();
|
|
3462
|
+
const rootClasses = [DEFAULT_CLASS, className].filter(Boolean).join(" ");
|
|
3463
|
+
if (render) {
|
|
3464
|
+
const RootTag2 = as;
|
|
3465
|
+
return /* @__PURE__ */ jsx37(RootTag2, { className: [className].filter(Boolean).join(" ") || void 0, children: render({ pricePerDay, currentMonthlyEquivCents: monthlyEquiv, cycle }) });
|
|
3500
3466
|
}
|
|
3467
|
+
const text = template.replaceAll("{pricePerDay}", pricePerDay).replaceAll("{currentMonthlyEquiv}", formatBRL(monthlyEquiv)).replaceAll("{cycle}", cycleLabel);
|
|
3468
|
+
const RootTag = as;
|
|
3469
|
+
return /* @__PURE__ */ jsx37(RootTag, { className: rootClasses, children: text });
|
|
3501
3470
|
}
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
return () => clearTimeout(t);
|
|
3514
|
-
}, []);
|
|
3515
|
-
const hasAccess = subscription.hasAccess;
|
|
3516
|
-
useEffect12(() => {
|
|
3517
|
-
if (hasAccess) {
|
|
3518
|
-
clearPixPayload();
|
|
3519
|
-
navigate("/", { replace: true });
|
|
3520
|
-
}
|
|
3521
|
-
}, [hasAccess, navigate]);
|
|
3522
|
-
async function copyPayload() {
|
|
3523
|
-
if (!payload?.payload) return;
|
|
3524
|
-
try {
|
|
3525
|
-
await navigator.clipboard.writeText(payload.payload);
|
|
3526
|
-
setCopied(true);
|
|
3527
|
-
setTimeout(() => setCopied(false), 2e3);
|
|
3528
|
-
} catch {
|
|
3529
|
-
}
|
|
3471
|
+
|
|
3472
|
+
// src/components/paywall/blocks/PaywallCountdown.tsx
|
|
3473
|
+
import { useEffect as useEffect12, useRef as useRef7, useState as useState10 } from "react";
|
|
3474
|
+
import { jsx as jsx38 } from "react/jsx-runtime";
|
|
3475
|
+
var DEFAULT_COUNTDOWN_CLASSES = "font-mono tabular-nums";
|
|
3476
|
+
function resolveDeadlineMs(deadline) {
|
|
3477
|
+
if (deadline instanceof Date) return deadline.getTime();
|
|
3478
|
+
if (typeof deadline === "string") return new Date(deadline).getTime();
|
|
3479
|
+
const { sessionStorageKey, durationMs } = deadline;
|
|
3480
|
+
if (typeof window === "undefined" || typeof window.sessionStorage === "undefined") {
|
|
3481
|
+
return Date.now() + durationMs;
|
|
3530
3482
|
}
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
{
|
|
3539
|
-
onClick: () => {
|
|
3540
|
-
clearPixPayload();
|
|
3541
|
-
navigate("/paywall/checkout", { replace: true });
|
|
3542
|
-
},
|
|
3543
|
-
className: "rounded-xl bg-primary px-6 py-3 text-base font-semibold text-primary-foreground",
|
|
3544
|
-
children: "Tentar novamente"
|
|
3545
|
-
}
|
|
3546
|
-
)
|
|
3547
|
-
] });
|
|
3483
|
+
const stored = window.sessionStorage.getItem(sessionStorageKey);
|
|
3484
|
+
const parsed = stored ? Number.parseInt(stored, 10) : NaN;
|
|
3485
|
+
const now = Date.now();
|
|
3486
|
+
if (!Number.isFinite(parsed) || parsed < now) {
|
|
3487
|
+
const target = now + durationMs;
|
|
3488
|
+
window.sessionStorage.setItem(sessionStorageKey, String(target));
|
|
3489
|
+
return target;
|
|
3548
3490
|
}
|
|
3549
|
-
return
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3491
|
+
return parsed;
|
|
3492
|
+
}
|
|
3493
|
+
function computeRemaining(deadlineMs) {
|
|
3494
|
+
const diff = Math.max(0, deadlineMs - Date.now());
|
|
3495
|
+
const totalSeconds = Math.floor(diff / 1e3);
|
|
3496
|
+
const h = Math.floor(totalSeconds / 3600);
|
|
3497
|
+
const m = Math.floor(totalSeconds % 3600 / 60);
|
|
3498
|
+
const s = totalSeconds % 60;
|
|
3499
|
+
return { h, m, s, expired: diff === 0 };
|
|
3500
|
+
}
|
|
3501
|
+
function pad(n) {
|
|
3502
|
+
return String(n).padStart(2, "0");
|
|
3503
|
+
}
|
|
3504
|
+
function PaywallCountdown({
|
|
3505
|
+
deadline,
|
|
3506
|
+
format = "h:m:s",
|
|
3507
|
+
onExpire,
|
|
3508
|
+
className,
|
|
3509
|
+
render
|
|
3510
|
+
}) {
|
|
3511
|
+
const deadlineMsRef = useRef7(null);
|
|
3512
|
+
if (deadlineMsRef.current === null) {
|
|
3513
|
+
deadlineMsRef.current = resolveDeadlineMs(deadline);
|
|
3514
|
+
}
|
|
3515
|
+
const [state, setState] = useState10(() => computeRemaining(deadlineMsRef.current));
|
|
3516
|
+
const expiredCalledRef = useRef7(false);
|
|
3517
|
+
useEffect12(() => {
|
|
3518
|
+
if (state.expired) {
|
|
3519
|
+
if (!expiredCalledRef.current) {
|
|
3520
|
+
expiredCalledRef.current = true;
|
|
3521
|
+
onExpire?.();
|
|
3560
3522
|
}
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3523
|
+
return;
|
|
3524
|
+
}
|
|
3525
|
+
const tick = () => {
|
|
3526
|
+
const next = computeRemaining(deadlineMsRef.current);
|
|
3527
|
+
setState(next);
|
|
3528
|
+
if (next.expired && !expiredCalledRef.current) {
|
|
3529
|
+
expiredCalledRef.current = true;
|
|
3530
|
+
onExpire?.();
|
|
3568
3531
|
}
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
/* @__PURE__ */
|
|
3575
|
-
|
|
3532
|
+
};
|
|
3533
|
+
const id = setInterval(tick, 1e3);
|
|
3534
|
+
return () => clearInterval(id);
|
|
3535
|
+
}, [state.expired]);
|
|
3536
|
+
if (render) {
|
|
3537
|
+
return /* @__PURE__ */ jsx38("div", { className, children: render(state) });
|
|
3538
|
+
}
|
|
3539
|
+
const formatted = format === "h:m:s" ? `${pad(state.h)}:${pad(state.m)}:${pad(state.s)}` : `${pad(state.h * 60 + state.m)}:${pad(state.s)}`;
|
|
3540
|
+
return /* @__PURE__ */ jsx38("div", { className: [DEFAULT_COUNTDOWN_CLASSES, className].filter(Boolean).join(" "), children: formatted });
|
|
3576
3541
|
}
|
|
3577
3542
|
|
|
3578
|
-
// src/
|
|
3579
|
-
import {
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
const [error, setError] = useState11(null);
|
|
3589
|
-
const [touchedEmail, setTouchedEmail] = useState11(false);
|
|
3590
|
-
const [touchedPassword, setTouchedPassword] = useState11(false);
|
|
3591
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = useState11(false);
|
|
3592
|
-
const validateEmail = useMemo7(() => {
|
|
3593
|
-
if (email.length === 0) return null;
|
|
3594
|
-
if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
3595
|
-
return null;
|
|
3596
|
-
}, [email]);
|
|
3597
|
-
const validatePassword = useMemo7(() => {
|
|
3598
|
-
if (password.length === 0) return null;
|
|
3599
|
-
if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
|
|
3600
|
-
return null;
|
|
3601
|
-
}, [password]);
|
|
3602
|
-
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
3603
|
-
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
3604
|
-
const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
|
|
3605
|
-
const submit = useCallback7(async () => {
|
|
3606
|
-
setFormSubmitAttempted(true);
|
|
3607
|
-
if (!canSubmit) return false;
|
|
3608
|
-
setSubmitting(true);
|
|
3609
|
-
setError(null);
|
|
3610
|
-
try {
|
|
3611
|
-
await auth.login({ email, password });
|
|
3612
|
-
return true;
|
|
3613
|
-
} catch (err) {
|
|
3614
|
-
setError(mapSdkError(err));
|
|
3615
|
-
return false;
|
|
3616
|
-
} finally {
|
|
3617
|
-
setSubmitting(false);
|
|
3618
|
-
}
|
|
3619
|
-
}, [auth, email, password, canSubmit]);
|
|
3620
|
-
return {
|
|
3621
|
-
email,
|
|
3622
|
-
setEmail,
|
|
3623
|
-
emailError,
|
|
3624
|
-
markEmailTouched: () => setTouchedEmail(true),
|
|
3625
|
-
password,
|
|
3626
|
-
setPassword,
|
|
3627
|
-
passwordError,
|
|
3628
|
-
markPasswordTouched: () => setTouchedPassword(true),
|
|
3629
|
-
formSubmitAttempted,
|
|
3630
|
-
submit,
|
|
3631
|
-
submitting,
|
|
3632
|
-
canSubmit,
|
|
3633
|
-
error,
|
|
3634
|
-
loginWithGoogle: () => auth.loginWithGoogle()
|
|
3635
|
-
};
|
|
3636
|
-
}
|
|
3637
|
-
|
|
3638
|
-
// src/hooks/useSignupForm.ts
|
|
3639
|
-
import { useCallback as useCallback8, useMemo as useMemo8, useState as useState12 } from "react";
|
|
3640
|
-
import { useHook as useHook13 } from "@hook-sdk/sdk";
|
|
3641
|
-
var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3642
|
-
var MIN_PASSWORD2 = 8;
|
|
3643
|
-
function useSignupForm() {
|
|
3644
|
-
const { auth } = useHook13();
|
|
3645
|
-
const [name, setName] = useState12("");
|
|
3646
|
-
const [email, setEmail] = useState12("");
|
|
3647
|
-
const [password, setPassword] = useState12("");
|
|
3648
|
-
const [submitting, setSubmitting] = useState12(false);
|
|
3649
|
-
const [error, setError] = useState12(null);
|
|
3650
|
-
const [touchedName, setTouchedName] = useState12(false);
|
|
3651
|
-
const [touchedEmail, setTouchedEmail] = useState12(false);
|
|
3652
|
-
const [touchedPassword, setTouchedPassword] = useState12(false);
|
|
3653
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = useState12(false);
|
|
3654
|
-
const validateName = useMemo8(() => {
|
|
3655
|
-
if (name.length === 0) return null;
|
|
3656
|
-
if (name.trim().length < 2) return "Nome muito curto.";
|
|
3657
|
-
return null;
|
|
3658
|
-
}, [name]);
|
|
3659
|
-
const validateEmail = useMemo8(() => {
|
|
3660
|
-
if (email.length === 0) return null;
|
|
3661
|
-
if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
3662
|
-
return null;
|
|
3663
|
-
}, [email]);
|
|
3664
|
-
const validatePassword = useMemo8(() => {
|
|
3665
|
-
if (password.length === 0) return null;
|
|
3666
|
-
if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
|
|
3667
|
-
return null;
|
|
3668
|
-
}, [password]);
|
|
3669
|
-
const nameError = touchedName || formSubmitAttempted ? validateName : null;
|
|
3670
|
-
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
3671
|
-
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
3672
|
-
const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
|
|
3673
|
-
const submit = useCallback8(async () => {
|
|
3674
|
-
setFormSubmitAttempted(true);
|
|
3675
|
-
if (!canSubmit) return false;
|
|
3676
|
-
setSubmitting(true);
|
|
3677
|
-
setError(null);
|
|
3678
|
-
try {
|
|
3679
|
-
await auth.signup({ name, email, password });
|
|
3680
|
-
return true;
|
|
3681
|
-
} catch (err) {
|
|
3682
|
-
setError(mapSdkError(err));
|
|
3683
|
-
return false;
|
|
3684
|
-
} finally {
|
|
3685
|
-
setSubmitting(false);
|
|
3686
|
-
}
|
|
3687
|
-
}, [auth, name, email, password, canSubmit]);
|
|
3688
|
-
return {
|
|
3689
|
-
name,
|
|
3690
|
-
setName,
|
|
3691
|
-
nameError,
|
|
3692
|
-
markNameTouched: () => setTouchedName(true),
|
|
3693
|
-
email,
|
|
3694
|
-
setEmail,
|
|
3695
|
-
emailError,
|
|
3696
|
-
markEmailTouched: () => setTouchedEmail(true),
|
|
3697
|
-
password,
|
|
3698
|
-
setPassword,
|
|
3699
|
-
passwordError,
|
|
3700
|
-
markPasswordTouched: () => setTouchedPassword(true),
|
|
3701
|
-
formSubmitAttempted,
|
|
3702
|
-
submit,
|
|
3703
|
-
submitting,
|
|
3704
|
-
canSubmit,
|
|
3705
|
-
error,
|
|
3706
|
-
loginWithGoogle: () => auth.loginWithGoogle()
|
|
3707
|
-
};
|
|
3708
|
-
}
|
|
3709
|
-
|
|
3710
|
-
// src/hooks/useForgotForm.ts
|
|
3711
|
-
import { useCallback as useCallback9, useMemo as useMemo9, useState as useState13 } from "react";
|
|
3712
|
-
import { useHook as useHook14 } from "@hook-sdk/sdk";
|
|
3713
|
-
var EMAIL_RE4 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3714
|
-
function useForgotForm() {
|
|
3715
|
-
const { auth } = useHook14();
|
|
3716
|
-
const [email, setEmail] = useState13("");
|
|
3717
|
-
const [submitting, setSubmitting] = useState13(false);
|
|
3718
|
-
const [sent, setSent] = useState13(false);
|
|
3719
|
-
const [error, setError] = useState13(null);
|
|
3720
|
-
const [touchedEmail, setTouchedEmail] = useState13(false);
|
|
3721
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = useState13(false);
|
|
3722
|
-
const validateEmail = useMemo9(() => {
|
|
3723
|
-
if (email.length === 0) return null;
|
|
3724
|
-
if (!EMAIL_RE4.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
3725
|
-
return null;
|
|
3726
|
-
}, [email]);
|
|
3727
|
-
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
3728
|
-
const canSubmit = email.length > 0 && validateEmail === null && !submitting;
|
|
3729
|
-
const submit = useCallback9(async () => {
|
|
3730
|
-
setFormSubmitAttempted(true);
|
|
3731
|
-
if (!canSubmit) return false;
|
|
3732
|
-
setSubmitting(true);
|
|
3733
|
-
setError(null);
|
|
3734
|
-
try {
|
|
3735
|
-
await auth.forgot({ email });
|
|
3736
|
-
setSent(true);
|
|
3737
|
-
return true;
|
|
3738
|
-
} catch (err) {
|
|
3739
|
-
setError(mapSdkError(err));
|
|
3740
|
-
return false;
|
|
3741
|
-
} finally {
|
|
3742
|
-
setSubmitting(false);
|
|
3743
|
-
}
|
|
3744
|
-
}, [auth, email, canSubmit]);
|
|
3745
|
-
return {
|
|
3746
|
-
email,
|
|
3747
|
-
setEmail,
|
|
3748
|
-
emailError,
|
|
3749
|
-
markEmailTouched: () => setTouchedEmail(true),
|
|
3750
|
-
formSubmitAttempted,
|
|
3751
|
-
submit,
|
|
3752
|
-
submitting,
|
|
3753
|
-
canSubmit,
|
|
3754
|
-
sent,
|
|
3755
|
-
error
|
|
3756
|
-
};
|
|
3757
|
-
}
|
|
3758
|
-
|
|
3759
|
-
// src/hooks/useResetForm.ts
|
|
3760
|
-
import { useCallback as useCallback10, useEffect as useEffect13, useMemo as useMemo10, useState as useState14 } from "react";
|
|
3761
|
-
import { useHook as useHook15 } from "@hook-sdk/sdk";
|
|
3762
|
-
var MIN_PASSWORD3 = 12;
|
|
3763
|
-
function useResetForm() {
|
|
3764
|
-
const { auth } = useHook15();
|
|
3765
|
-
const [token, setToken] = useState14(null);
|
|
3766
|
-
const [password, setPassword] = useState14("");
|
|
3767
|
-
const [confirm, setConfirm] = useState14("");
|
|
3768
|
-
const [submitting, setSubmitting] = useState14(false);
|
|
3769
|
-
const [done, setDone] = useState14(false);
|
|
3770
|
-
const [error, setError] = useState14(null);
|
|
3771
|
-
const [touchedPassword, setTouchedPassword] = useState14(false);
|
|
3772
|
-
const [touchedConfirm, setTouchedConfirm] = useState14(false);
|
|
3773
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = useState14(false);
|
|
3774
|
-
useEffect13(() => {
|
|
3775
|
-
if (typeof window === "undefined") return;
|
|
3776
|
-
const params = new URLSearchParams(window.location.search);
|
|
3777
|
-
const t = params.get("token");
|
|
3778
|
-
setToken(t && t.length > 0 ? t : null);
|
|
3779
|
-
}, []);
|
|
3780
|
-
const validatePassword = useMemo10(() => {
|
|
3781
|
-
if (password.length === 0) return null;
|
|
3782
|
-
if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
|
|
3783
|
-
return null;
|
|
3784
|
-
}, [password]);
|
|
3785
|
-
const validateConfirm = useMemo10(() => {
|
|
3786
|
-
if (confirm.length === 0) return null;
|
|
3787
|
-
if (confirm !== password) return "Senhas n\xE3o coincidem.";
|
|
3788
|
-
return null;
|
|
3789
|
-
}, [confirm, password]);
|
|
3790
|
-
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
3791
|
-
const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
|
|
3792
|
-
const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
|
|
3793
|
-
const submit = useCallback10(async () => {
|
|
3794
|
-
setFormSubmitAttempted(true);
|
|
3795
|
-
if (!canSubmit || token === null) return;
|
|
3796
|
-
setSubmitting(true);
|
|
3797
|
-
setError(null);
|
|
3798
|
-
try {
|
|
3799
|
-
await auth.reset({ token, newPassword: password });
|
|
3800
|
-
setDone(true);
|
|
3801
|
-
if (typeof window !== "undefined") {
|
|
3802
|
-
const url = new URL(window.location.href);
|
|
3803
|
-
url.searchParams.delete("token");
|
|
3804
|
-
url.searchParams.delete("screen");
|
|
3805
|
-
window.history.replaceState({}, "", url.toString());
|
|
3806
|
-
}
|
|
3807
|
-
} catch (err) {
|
|
3808
|
-
setError(mapSdkError(err));
|
|
3809
|
-
} finally {
|
|
3810
|
-
setSubmitting(false);
|
|
3811
|
-
}
|
|
3812
|
-
}, [auth, token, password, canSubmit]);
|
|
3813
|
-
return {
|
|
3814
|
-
token,
|
|
3815
|
-
password,
|
|
3816
|
-
setPassword,
|
|
3817
|
-
passwordError,
|
|
3818
|
-
markPasswordTouched: () => setTouchedPassword(true),
|
|
3819
|
-
confirm,
|
|
3820
|
-
setConfirm,
|
|
3821
|
-
confirmError,
|
|
3822
|
-
markConfirmTouched: () => setTouchedConfirm(true),
|
|
3823
|
-
formSubmitAttempted,
|
|
3824
|
-
submit,
|
|
3825
|
-
submitting,
|
|
3826
|
-
canSubmit,
|
|
3827
|
-
done,
|
|
3828
|
-
error
|
|
3829
|
-
};
|
|
3830
|
-
}
|
|
3831
|
-
|
|
3832
|
-
// src/utils/price.ts
|
|
3833
|
-
function formatBRL(cents) {
|
|
3834
|
-
if (cents === null || cents === void 0) return "";
|
|
3835
|
-
const reais = cents / 100;
|
|
3836
|
-
return new Intl.NumberFormat("pt-BR", {
|
|
3837
|
-
style: "currency",
|
|
3838
|
-
currency: "BRL"
|
|
3839
|
-
}).format(reais);
|
|
3840
|
-
}
|
|
3841
|
-
function monthlyFromYearly(yearlyCents) {
|
|
3842
|
-
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
3843
|
-
return Math.round(yearlyCents / 12);
|
|
3844
|
-
}
|
|
3845
|
-
function dailyFromYearly(yearlyCents) {
|
|
3846
|
-
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
3847
|
-
return Math.round(yearlyCents / 365);
|
|
3848
|
-
}
|
|
3849
|
-
function computeAnchorCents(baseCents, multiplier) {
|
|
3850
|
-
if (multiplier === null || multiplier === void 0) return null;
|
|
3851
|
-
if (!Number.isFinite(multiplier)) return null;
|
|
3852
|
-
if (multiplier <= 1) return null;
|
|
3853
|
-
return Math.round(baseCents * multiplier);
|
|
3854
|
-
}
|
|
3855
|
-
function discountPercent(anchorCents, realCents) {
|
|
3856
|
-
if (anchorCents <= realCents) return 0;
|
|
3857
|
-
return Math.floor((anchorCents - realCents) / anchorCents * 100);
|
|
3858
|
-
}
|
|
3859
|
-
|
|
3860
|
-
// src/hooks/useAuthPrimitives.ts
|
|
3861
|
-
import { useEffect as useEffect14 } from "react";
|
|
3862
|
-
import { useHook as useHook16 } from "@hook-sdk/sdk";
|
|
3863
|
-
var warned = false;
|
|
3864
|
-
function useAuthPrimitives() {
|
|
3865
|
-
const { auth } = useHook16();
|
|
3866
|
-
useEffect14(() => {
|
|
3867
|
-
if (!warned && process.env.NODE_ENV !== "production") {
|
|
3868
|
-
warned = true;
|
|
3869
|
-
console.warn(
|
|
3870
|
-
"[@hook-sdk/template] useAuthPrimitives() \xE9 escape hatch. Pra login/signup/forgot, use useLoginForm/useSignupForm/useForgotForm. Docs: docs/19-golden-template.md#escape-hatch"
|
|
3871
|
-
);
|
|
3872
|
-
}
|
|
3873
|
-
}, []);
|
|
3874
|
-
return {
|
|
3875
|
-
login: auth.login,
|
|
3876
|
-
signup: auth.signup,
|
|
3877
|
-
logout: auth.logout,
|
|
3878
|
-
logoutAll: auth.logoutAll,
|
|
3879
|
-
forgot: auth.forgot,
|
|
3880
|
-
resendVerify: auth.resendVerify,
|
|
3881
|
-
changePassword: auth.changePassword,
|
|
3882
|
-
changeEmail: auth.changeEmail,
|
|
3883
|
-
refresh: auth.refresh
|
|
3884
|
-
};
|
|
3885
|
-
}
|
|
3886
|
-
|
|
3887
|
-
// src/hooks/useAuth.ts
|
|
3888
|
-
import { useHook as useHook17 } from "@hook-sdk/sdk";
|
|
3889
|
-
function useAuth() {
|
|
3890
|
-
const { user, authStatus, auth } = useHook17();
|
|
3891
|
-
return {
|
|
3892
|
-
user,
|
|
3893
|
-
authStatus,
|
|
3894
|
-
refresh: auth.refresh
|
|
3895
|
-
};
|
|
3896
|
-
}
|
|
3897
|
-
|
|
3898
|
-
// src/index.ts
|
|
3899
|
-
import { useTrackOnboardingStep } from "@hook-sdk/sdk";
|
|
3900
|
-
|
|
3901
|
-
// src/hooks/useSubscription.ts
|
|
3902
|
-
import { useHook as useHook18 } from "@hook-sdk/sdk";
|
|
3903
|
-
function useSubscription() {
|
|
3904
|
-
const { subscription } = useHook18();
|
|
3905
|
-
return {
|
|
3906
|
-
status: subscription.status()
|
|
3907
|
-
};
|
|
3908
|
-
}
|
|
3909
|
-
|
|
3910
|
-
// src/hooks/useReminders.ts
|
|
3911
|
-
import { useCallback as useCallback11, useEffect as useEffect15, useState as useState15 } from "react";
|
|
3912
|
-
import { useHook as useHook19 } from "@hook-sdk/sdk";
|
|
3913
|
-
function useReminders() {
|
|
3914
|
-
const { push } = useHook19();
|
|
3915
|
-
const r = push.reminders;
|
|
3916
|
-
const [reminders, setReminders] = useState15([]);
|
|
3917
|
-
const [loading, setLoading] = useState15(true);
|
|
3918
|
-
const reload = useCallback11(async () => {
|
|
3919
|
-
setLoading(true);
|
|
3920
|
-
try {
|
|
3921
|
-
const next = await r.list();
|
|
3922
|
-
setReminders(next);
|
|
3923
|
-
} finally {
|
|
3924
|
-
setLoading(false);
|
|
3925
|
-
}
|
|
3926
|
-
}, [r]);
|
|
3927
|
-
useEffect15(() => {
|
|
3928
|
-
void reload();
|
|
3929
|
-
}, [reload]);
|
|
3930
|
-
const setReminder = useCallback11(async (input) => {
|
|
3931
|
-
await r.set(input);
|
|
3932
|
-
await reload();
|
|
3933
|
-
}, [r, reload]);
|
|
3934
|
-
const deleteReminder = useCallback11(async (slot) => {
|
|
3935
|
-
await r.delete(slot);
|
|
3936
|
-
await reload();
|
|
3937
|
-
}, [r, reload]);
|
|
3938
|
-
const schedule = useCallback11(async (items) => {
|
|
3939
|
-
return r.schedule(items);
|
|
3940
|
-
}, [r]);
|
|
3941
|
-
const setFallbacks = useCallback11(async (items) => {
|
|
3942
|
-
return r.setFallbacks(items);
|
|
3943
|
-
}, [r]);
|
|
3944
|
-
return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
|
|
3945
|
-
}
|
|
3946
|
-
|
|
3947
|
-
// src/hooks/useToast.ts
|
|
3948
|
-
import { useCallback as useCallback12, useState as useState16 } from "react";
|
|
3949
|
-
function useToast() {
|
|
3950
|
-
const [items, setItems] = useState16([]);
|
|
3951
|
-
const show = useCallback12((message, kind = "info") => {
|
|
3952
|
-
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3953
|
-
setItems((prev) => [...prev, { id, message, kind }]);
|
|
3954
|
-
setTimeout(() => {
|
|
3955
|
-
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
3956
|
-
}, 4e3);
|
|
3957
|
-
}, []);
|
|
3958
|
-
const dismiss = useCallback12((id) => {
|
|
3959
|
-
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
3960
|
-
}, []);
|
|
3961
|
-
return { items, show, dismiss };
|
|
3962
|
-
}
|
|
3963
|
-
|
|
3964
|
-
// src/RouteBoundary.tsx
|
|
3965
|
-
import { Routes as Routes2, Route as Route2 } from "react-router-dom";
|
|
3966
|
-
import { jsx as jsx29, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3967
|
-
function RouteBoundary({ children }) {
|
|
3968
|
-
return /* @__PURE__ */ jsxs20(Routes2, { children: [
|
|
3969
|
-
children,
|
|
3970
|
-
/* @__PURE__ */ jsx29(Route2, { path: "*", element: /* @__PURE__ */ jsx29(DefaultNotFound, {}) })
|
|
3971
|
-
] });
|
|
3972
|
-
}
|
|
3973
|
-
function DefaultNotFound() {
|
|
3974
|
-
return /* @__PURE__ */ jsx29("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
|
|
3975
|
-
}
|
|
3976
|
-
|
|
3977
|
-
// src/PreAuthShell.tsx
|
|
3978
|
-
import { BrowserRouter as BrowserRouter2, MemoryRouter as MemoryRouter2, Routes as Routes3 } from "react-router-dom";
|
|
3979
|
-
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
3980
|
-
function PreAuthShell({
|
|
3981
|
-
basename,
|
|
3982
|
-
testRouter,
|
|
3983
|
-
testInitialEntries,
|
|
3984
|
-
children
|
|
3543
|
+
// src/components/paywall/blocks/PaywallFeatures.tsx
|
|
3544
|
+
import { jsx as jsx39, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
3545
|
+
function PaywallFeatures({
|
|
3546
|
+
items,
|
|
3547
|
+
IconComponent,
|
|
3548
|
+
className,
|
|
3549
|
+
itemClassName,
|
|
3550
|
+
iconClassName,
|
|
3551
|
+
render,
|
|
3552
|
+
renderItem
|
|
3985
3553
|
}) {
|
|
3986
|
-
if (
|
|
3987
|
-
return /* @__PURE__ */
|
|
3554
|
+
if (render) {
|
|
3555
|
+
return /* @__PURE__ */ jsx39("div", { className, children: render({ items }) });
|
|
3556
|
+
}
|
|
3557
|
+
if (renderItem) {
|
|
3558
|
+
return /* @__PURE__ */ jsx39("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsx39("li", { children: renderItem(item, idx) }, idx)) });
|
|
3988
3559
|
}
|
|
3989
|
-
return /* @__PURE__ */
|
|
3560
|
+
return /* @__PURE__ */ jsx39("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsxs23("li", { className: itemClassName, children: [
|
|
3561
|
+
IconComponent ? /* @__PURE__ */ jsx39(IconComponent, { className: iconClassName }) : /* @__PURE__ */ jsx39("span", { className: iconClassName, "aria-hidden": "true", children: "\u2713" }),
|
|
3562
|
+
/* @__PURE__ */ jsx39("span", { children: item })
|
|
3563
|
+
] }, idx)) });
|
|
3990
3564
|
}
|
|
3991
3565
|
|
|
3992
|
-
// src/
|
|
3993
|
-
import {
|
|
3994
|
-
|
|
3566
|
+
// src/components/paywall/blocks/PaywallFeaturesCard.tsx
|
|
3567
|
+
import { jsx as jsx40, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3568
|
+
var DEFAULT_CARD_CLASSES = "rounded-xl border p-4";
|
|
3569
|
+
function PaywallFeaturesCard({
|
|
3570
|
+
title,
|
|
3571
|
+
items,
|
|
3572
|
+
className,
|
|
3573
|
+
cardClassName,
|
|
3574
|
+
titleClassName,
|
|
3575
|
+
itemClassName,
|
|
3576
|
+
renderItem
|
|
3577
|
+
}) {
|
|
3578
|
+
return /* @__PURE__ */ jsx40("div", { className, children: /* @__PURE__ */ jsxs24("div", { className: [DEFAULT_CARD_CLASSES, cardClassName].filter(Boolean).join(" "), children: [
|
|
3579
|
+
title ? /* @__PURE__ */ jsx40("div", { className: ["font-semibold mb-2", titleClassName].filter(Boolean).join(" "), children: title }) : null,
|
|
3580
|
+
/* @__PURE__ */ jsx40("ul", { children: items.map(
|
|
3581
|
+
(item, idx) => renderItem ? /* @__PURE__ */ jsx40("li", { children: renderItem(item, idx) }, idx) : /* @__PURE__ */ jsxs24("li", { className: itemClassName, children: [
|
|
3582
|
+
/* @__PURE__ */ jsx40("span", { "aria-hidden": "true", children: "\u2022" }),
|
|
3583
|
+
" ",
|
|
3584
|
+
/* @__PURE__ */ jsx40("span", { children: item })
|
|
3585
|
+
] }, idx)
|
|
3586
|
+
) })
|
|
3587
|
+
] }) });
|
|
3588
|
+
}
|
|
3995
3589
|
|
|
3996
|
-
// src/
|
|
3997
|
-
import {
|
|
3998
|
-
var
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
3590
|
+
// src/components/paywall/blocks/PaywallTrophyBadge.tsx
|
|
3591
|
+
import { jsx as jsx41, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3592
|
+
var DEFAULT_CHIP_CLASSES = "inline-flex items-center gap-1 px-3 py-1 rounded-full bg-yellow-100 text-yellow-900 text-sm font-medium";
|
|
3593
|
+
var FLOATING_CLASSES = "absolute top-2 right-2 z-10 shadow-md";
|
|
3594
|
+
function PaywallTrophyBadge({
|
|
3595
|
+
text,
|
|
3596
|
+
className,
|
|
3597
|
+
iconClassName,
|
|
3598
|
+
floating = false,
|
|
3599
|
+
render
|
|
3600
|
+
}) {
|
|
3601
|
+
if (render) {
|
|
3602
|
+
return /* @__PURE__ */ jsx41("div", { className, children: render({ text }) });
|
|
4005
3603
|
}
|
|
4006
|
-
|
|
3604
|
+
const rootClasses = [
|
|
3605
|
+
DEFAULT_CHIP_CLASSES,
|
|
3606
|
+
floating ? FLOATING_CLASSES : "",
|
|
3607
|
+
className
|
|
3608
|
+
].filter(Boolean).join(" ");
|
|
3609
|
+
return /* @__PURE__ */ jsxs25("div", { className: rootClasses, children: [
|
|
3610
|
+
/* @__PURE__ */ jsx41("span", { className: iconClassName, "aria-hidden": "true", children: "\u{1F3C6}" }),
|
|
3611
|
+
/* @__PURE__ */ jsx41("span", { children: text })
|
|
3612
|
+
] });
|
|
4007
3613
|
}
|
|
4008
3614
|
|
|
4009
|
-
// src/
|
|
4010
|
-
import { jsx as
|
|
4011
|
-
var
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
return typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : 0;
|
|
4016
|
-
}
|
|
4017
|
-
function OnboardingFlow({
|
|
4018
|
-
steps,
|
|
4019
|
-
screens,
|
|
4020
|
-
onComplete,
|
|
4021
|
-
persistKey
|
|
3615
|
+
// src/components/paywall/blocks/PaywallAnchorPrice.tsx
|
|
3616
|
+
import { jsx as jsx42 } from "react/jsx-runtime";
|
|
3617
|
+
var DEFAULT_CLASS2 = "text-sm opacity-60 line-through";
|
|
3618
|
+
function PaywallAnchorPrice({
|
|
3619
|
+
className,
|
|
3620
|
+
render
|
|
4022
3621
|
}) {
|
|
4023
|
-
const
|
|
4024
|
-
|
|
4025
|
-
draftRef.current = draft;
|
|
4026
|
-
const idx = readPersistedStepIdx(draft);
|
|
4027
|
-
const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
|
|
4028
|
-
const setIdx = useCallback13(
|
|
4029
|
-
(n) => {
|
|
4030
|
-
setDraft((prev) => {
|
|
4031
|
-
const prevIdx = readPersistedStepIdx(prev);
|
|
4032
|
-
const nextIdx = typeof n === "function" ? n(prevIdx) : n;
|
|
4033
|
-
return { ...prev, [CURRENT_STEP_FIELD]: nextIdx };
|
|
4034
|
-
});
|
|
4035
|
-
},
|
|
4036
|
-
[setDraft]
|
|
4037
|
-
);
|
|
4038
|
-
const setValue = useCallback13(
|
|
4039
|
-
(patch) => {
|
|
4040
|
-
draftRef.current = { ...draftRef.current, ...patch };
|
|
4041
|
-
setDraft((prev) => ({ ...prev, ...patch }));
|
|
4042
|
-
},
|
|
4043
|
-
[setDraft]
|
|
4044
|
-
);
|
|
4045
|
-
const step = steps[clampedIdx];
|
|
4046
|
-
const hookCtx = useHook20();
|
|
4047
|
-
const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
|
|
4048
|
-
useEffect16(() => {
|
|
4049
|
-
if (status.loading) return;
|
|
4050
|
-
if (!step) return;
|
|
4051
|
-
if (!track2) return;
|
|
4052
|
-
track2("onboarding_step_viewed", {
|
|
4053
|
-
step: step.id,
|
|
4054
|
-
step_index: clampedIdx,
|
|
4055
|
-
total_steps: steps.length
|
|
4056
|
-
});
|
|
4057
|
-
}, [step?.id, clampedIdx, steps.length, status.loading, track2]);
|
|
4058
|
-
const valid = useMemo11(
|
|
4059
|
-
() => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
|
|
4060
|
-
[draft, step]
|
|
4061
|
-
);
|
|
4062
|
-
const next = useCallback13(() => {
|
|
4063
|
-
if (!step) return;
|
|
4064
|
-
const current = draftRef.current;
|
|
4065
|
-
const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
|
|
4066
|
-
if (!validNow) return;
|
|
4067
|
-
if (clampedIdx + 1 >= steps.length) {
|
|
4068
|
-
onComplete(current);
|
|
4069
|
-
} else {
|
|
4070
|
-
setIdx(clampedIdx + 1);
|
|
4071
|
-
}
|
|
4072
|
-
}, [clampedIdx, onComplete, step, steps.length, setIdx]);
|
|
4073
|
-
const prevStep = useCallback13(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
|
|
4074
|
-
const ctx = useMemo11(
|
|
4075
|
-
() => ({
|
|
4076
|
-
stepIndex: clampedIdx,
|
|
4077
|
-
totalSteps: steps.length,
|
|
4078
|
-
value: draft,
|
|
4079
|
-
setValue,
|
|
4080
|
-
valid,
|
|
4081
|
-
next,
|
|
4082
|
-
prev: prevStep
|
|
4083
|
-
}),
|
|
4084
|
-
[clampedIdx, steps.length, draft, setValue, valid, next, prevStep]
|
|
4085
|
-
);
|
|
4086
|
-
if (status.loading) {
|
|
3622
|
+
const { anchorPriceCents, cycle } = usePaywallContext();
|
|
3623
|
+
if (anchorPriceCents === null || anchorPriceCents === void 0 || anchorPriceCents <= 0) {
|
|
4087
3624
|
return null;
|
|
4088
3625
|
}
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
const Screen = screens[step.screen];
|
|
4095
|
-
if (!Screen) {
|
|
4096
|
-
throw new Error(
|
|
4097
|
-
`[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
|
|
4098
|
-
);
|
|
3626
|
+
void cycle;
|
|
3627
|
+
const formatted = formatBRL(anchorPriceCents);
|
|
3628
|
+
const rootClasses = [DEFAULT_CLASS2, className].filter(Boolean).join(" ");
|
|
3629
|
+
if (render) {
|
|
3630
|
+
return /* @__PURE__ */ jsx42("span", { className: className || void 0, children: render({ anchorCents: anchorPriceCents, formatted }) });
|
|
4099
3631
|
}
|
|
4100
|
-
return /* @__PURE__ */
|
|
4101
|
-
}
|
|
4102
|
-
|
|
4103
|
-
// src/hooks/useFeature.ts
|
|
4104
|
-
function useFeature(name) {
|
|
4105
|
-
const config = useAppConfig();
|
|
4106
|
-
return Array.isArray(config.features_enabled) && config.features_enabled.includes(name);
|
|
4107
|
-
}
|
|
4108
|
-
|
|
4109
|
-
// src/components/paywall/Paywall.tsx
|
|
4110
|
-
import { useEffect as useEffect17, useMemo as useMemo12 } from "react";
|
|
4111
|
-
import { useHook as useHook21 } from "@hook-sdk/sdk";
|
|
4112
|
-
|
|
4113
|
-
// src/components/paywall/PaywallProvider.tsx
|
|
4114
|
-
import { createContext as createContext4 } from "react";
|
|
4115
|
-
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
4116
|
-
var PaywallContext = createContext4(null);
|
|
4117
|
-
function PaywallProvider({ children }) {
|
|
4118
|
-
const state = usePaywallState();
|
|
4119
|
-
return /* @__PURE__ */ jsx32(PaywallContext.Provider, { value: state, children });
|
|
3632
|
+
return /* @__PURE__ */ jsx42("span", { className: rootClasses, children: formatted });
|
|
4120
3633
|
}
|
|
4121
3634
|
|
|
4122
|
-
// src/components/paywall/
|
|
4123
|
-
import {
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
3635
|
+
// src/components/paywall/blocks/PaywallTestimonials.tsx
|
|
3636
|
+
import { jsx as jsx43, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
3637
|
+
var DEFAULT_ROOT = "flex gap-3 overflow-x-auto snap-x snap-mandatory pb-2";
|
|
3638
|
+
var DEFAULT_CARD = "snap-start shrink-0 w-72 rounded-2xl border p-4 flex flex-col gap-2";
|
|
3639
|
+
var DEFAULT_AVATAR = "w-10 h-10 rounded-full object-cover";
|
|
3640
|
+
var DEFAULT_QUOTE = "text-sm leading-snug";
|
|
3641
|
+
var DEFAULT_NAME = "text-xs font-semibold opacity-80";
|
|
3642
|
+
var DEFAULT_STARS = "text-yellow-500 text-sm";
|
|
3643
|
+
function clampStars(n) {
|
|
3644
|
+
return Math.max(0, Math.min(5, Math.round(n)));
|
|
4130
3645
|
}
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
import { jsx as jsx33 } from "react/jsx-runtime";
|
|
4134
|
-
function PaywallMethodTabs({
|
|
4135
|
-
labels,
|
|
3646
|
+
function PaywallTestimonials({
|
|
3647
|
+
items,
|
|
4136
3648
|
className,
|
|
4137
|
-
|
|
4138
|
-
|
|
3649
|
+
cardClassName,
|
|
3650
|
+
avatarClassName,
|
|
3651
|
+
quoteClassName,
|
|
3652
|
+
nameClassName,
|
|
3653
|
+
starsClassName,
|
|
3654
|
+
renderItem
|
|
4139
3655
|
}) {
|
|
4140
|
-
const
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
3656
|
+
const rootClasses = [DEFAULT_ROOT, className].filter(Boolean).join(" ");
|
|
3657
|
+
const cardClasses = [DEFAULT_CARD, cardClassName].filter(Boolean).join(" ");
|
|
3658
|
+
const avatarClasses = [DEFAULT_AVATAR, avatarClassName].filter(Boolean).join(" ");
|
|
3659
|
+
const quoteClasses = [DEFAULT_QUOTE, quoteClassName].filter(Boolean).join(" ");
|
|
3660
|
+
const nameClasses = [DEFAULT_NAME, nameClassName].filter(Boolean).join(" ");
|
|
3661
|
+
const starsClasses = [DEFAULT_STARS, starsClassName].filter(Boolean).join(" ");
|
|
3662
|
+
return /* @__PURE__ */ jsx43("div", { className: rootClasses, children: items.map((item, idx) => {
|
|
3663
|
+
if (renderItem) return renderItem(item, idx);
|
|
3664
|
+
const filled = clampStars(item.stars);
|
|
3665
|
+
const empty = 5 - filled;
|
|
3666
|
+
return /* @__PURE__ */ jsxs26("div", { className: cardClasses, children: [
|
|
3667
|
+
/* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
|
|
3668
|
+
item.avatar ? /* @__PURE__ */ jsx43(
|
|
3669
|
+
"img",
|
|
3670
|
+
{
|
|
3671
|
+
src: item.avatar,
|
|
3672
|
+
alt: "",
|
|
3673
|
+
loading: "lazy",
|
|
3674
|
+
className: avatarClasses,
|
|
3675
|
+
"aria-hidden": "true"
|
|
3676
|
+
}
|
|
3677
|
+
) : null,
|
|
3678
|
+
/* @__PURE__ */ jsx43("div", { className: nameClasses, children: item.name })
|
|
3679
|
+
] }),
|
|
3680
|
+
/* @__PURE__ */ jsxs26("div", { className: starsClasses, "aria-label": `${filled} de 5 estrelas`, children: [
|
|
3681
|
+
"\u2605".repeat(filled),
|
|
3682
|
+
"\u2606".repeat(empty)
|
|
3683
|
+
] }),
|
|
3684
|
+
/* @__PURE__ */ jsx43("p", { className: quoteClasses, children: item.quote })
|
|
3685
|
+
] }, idx);
|
|
4159
3686
|
}) });
|
|
4160
3687
|
}
|
|
4161
3688
|
|
|
4162
|
-
// src/components/paywall/
|
|
4163
|
-
import { jsx as
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
"
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
3689
|
+
// src/components/paywall/blocks/PaywallStatsRow.tsx
|
|
3690
|
+
import { jsx as jsx44, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
3691
|
+
var DEFAULT_ROOT2 = "grid grid-cols-3 gap-4";
|
|
3692
|
+
var DEFAULT_CELL = "flex flex-col items-center text-center";
|
|
3693
|
+
var DEFAULT_VALUE = "text-2xl font-bold";
|
|
3694
|
+
var DEFAULT_LABEL = "text-xs opacity-70";
|
|
3695
|
+
function PaywallStatsRow({
|
|
3696
|
+
stats,
|
|
3697
|
+
className,
|
|
3698
|
+
cellClassName,
|
|
3699
|
+
valueClassName,
|
|
3700
|
+
labelClassName,
|
|
3701
|
+
renderCell
|
|
3702
|
+
}) {
|
|
3703
|
+
const rootClasses = [DEFAULT_ROOT2, className].filter(Boolean).join(" ");
|
|
3704
|
+
const cellClasses = [DEFAULT_CELL, cellClassName].filter(Boolean).join(" ");
|
|
3705
|
+
const valueClasses = [DEFAULT_VALUE, valueClassName].filter(Boolean).join(" ");
|
|
3706
|
+
const labelClasses = [DEFAULT_LABEL, labelClassName].filter(Boolean).join(" ");
|
|
3707
|
+
return /* @__PURE__ */ jsx44("div", { className: rootClasses, children: stats.map((stat, idx) => {
|
|
3708
|
+
if (renderCell) return renderCell(stat, idx);
|
|
3709
|
+
return /* @__PURE__ */ jsxs27("div", { className: cellClasses, children: [
|
|
3710
|
+
stat.icon ? /* @__PURE__ */ jsx44("div", { "aria-hidden": "true", children: stat.icon }) : null,
|
|
3711
|
+
/* @__PURE__ */ jsx44("div", { className: valueClasses, children: stat.value }),
|
|
3712
|
+
/* @__PURE__ */ jsx44("div", { className: labelClasses, children: stat.label })
|
|
3713
|
+
] }, idx);
|
|
3714
|
+
}) });
|
|
3715
|
+
}
|
|
3716
|
+
|
|
3717
|
+
// src/components/paywall/blocks/PaywallFinePrint.tsx
|
|
3718
|
+
import { jsx as jsx45 } from "react/jsx-runtime";
|
|
3719
|
+
var DEFAULT_CLASS3 = "text-xs opacity-60 leading-snug";
|
|
3720
|
+
var CYCLE_LABEL2 = {
|
|
3721
|
+
MONTHLY: "mensal",
|
|
3722
|
+
YEARLY: "anual"
|
|
4183
3723
|
};
|
|
4184
|
-
function
|
|
4185
|
-
|
|
3724
|
+
function PaywallFinePrint({
|
|
3725
|
+
template,
|
|
4186
3726
|
className,
|
|
4187
|
-
cardClassName,
|
|
4188
|
-
cardSelectedClassName,
|
|
4189
|
-
anchorClassName,
|
|
4190
|
-
variant = "default",
|
|
4191
3727
|
render
|
|
4192
3728
|
}) {
|
|
4193
|
-
const
|
|
4194
|
-
|
|
4195
|
-
|
|
3729
|
+
const {
|
|
3730
|
+
currentPriceCents,
|
|
3731
|
+
cycle,
|
|
3732
|
+
trialDaysCard,
|
|
3733
|
+
trialDaysPix,
|
|
3734
|
+
selectedMethod
|
|
3735
|
+
} = usePaywallContext();
|
|
3736
|
+
const trialDays = selectedMethod === "card" ? trialDaysCard : trialDaysPix;
|
|
3737
|
+
const cycleLabel = CYCLE_LABEL2[cycle] ?? cycle.toLowerCase();
|
|
3738
|
+
const priceFormatted = formatBRL(currentPriceCents ?? 0);
|
|
3739
|
+
const rootClasses = [DEFAULT_CLASS3, className].filter(Boolean).join(" ");
|
|
4196
3740
|
if (render) {
|
|
4197
|
-
return /* @__PURE__ */
|
|
3741
|
+
return /* @__PURE__ */ jsx45("p", { className: className || void 0, children: render({
|
|
3742
|
+
currentPriceCents: currentPriceCents ?? 0,
|
|
3743
|
+
cycle,
|
|
3744
|
+
trialDays: trialDays ?? 0,
|
|
3745
|
+
selectedMethod
|
|
3746
|
+
}) });
|
|
4198
3747
|
}
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
const composedCardClassName = [v.card, cardClassName].filter(Boolean).join(" ");
|
|
4202
|
-
const composedCardSelectedClassName = [v.cardSelected, cardSelectedClassName].filter(Boolean).join(" ");
|
|
4203
|
-
const monthlyCents = plan?.monthlyCents ?? 0;
|
|
4204
|
-
const yearlyCents = plan?.yearlyCents ?? 0;
|
|
4205
|
-
const anchorMonthly = plan?.anchorMonthlyCents ?? null;
|
|
4206
|
-
const anchorYearly = plan?.anchorYearlyCents ?? null;
|
|
4207
|
-
return /* @__PURE__ */ jsx35(
|
|
4208
|
-
"div",
|
|
4209
|
-
{
|
|
4210
|
-
role: "radiogroup",
|
|
4211
|
-
"aria-label": "Ciclo de cobran\xE7a",
|
|
4212
|
-
className: ["flex flex-row gap-2", className].filter(Boolean).join(" "),
|
|
4213
|
-
children: cycles.map((c) => {
|
|
4214
|
-
const active = c === selected;
|
|
4215
|
-
const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
|
|
4216
|
-
const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
|
|
4217
|
-
const mainCents = c === "YEARLY" ? Math.round(yearlyCents / 12) : monthlyCents;
|
|
4218
|
-
const anchorCents = c === "YEARLY" ? anchorYearly : anchorMonthly;
|
|
4219
|
-
return /* @__PURE__ */ jsxs21(
|
|
4220
|
-
"button",
|
|
4221
|
-
{
|
|
4222
|
-
type: "button",
|
|
4223
|
-
role: "radio",
|
|
4224
|
-
"aria-checked": active,
|
|
4225
|
-
onClick: () => setCycle(c),
|
|
4226
|
-
className: [
|
|
4227
|
-
"flex flex-col items-center gap-0.5",
|
|
4228
|
-
composedCardClassName,
|
|
4229
|
-
active ? composedCardSelectedClassName : ""
|
|
4230
|
-
].filter(Boolean).join(" "),
|
|
4231
|
-
children: [
|
|
4232
|
-
/* @__PURE__ */ jsx35("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
|
|
4233
|
-
/* @__PURE__ */ jsx35("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
|
|
4234
|
-
/* @__PURE__ */ jsx35("span", { className: "text-xs opacity-60 leading-tight", children: label }),
|
|
4235
|
-
anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx35("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ jsx35("s", { children: formatBRL(anchorCents) }) }) : null
|
|
4236
|
-
]
|
|
4237
|
-
},
|
|
4238
|
-
c
|
|
4239
|
-
);
|
|
4240
|
-
})
|
|
4241
|
-
}
|
|
4242
|
-
);
|
|
3748
|
+
const text = template.replaceAll("{price}", priceFormatted).replaceAll("{trialDays}", String(trialDays ?? 0)).replaceAll("{cycle}", cycleLabel);
|
|
3749
|
+
return /* @__PURE__ */ jsx45("p", { className: rootClasses, children: text });
|
|
4243
3750
|
}
|
|
4244
3751
|
|
|
4245
|
-
// src/components/paywall/
|
|
4246
|
-
import { jsx as
|
|
4247
|
-
var
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
3752
|
+
// src/components/paywall/blocks/PaywallTrustLine.tsx
|
|
3753
|
+
import { jsx as jsx46, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3754
|
+
var DEFAULT_ROOT3 = "flex items-center gap-3";
|
|
3755
|
+
var DEFAULT_ITEM = "flex items-center gap-1.5 text-xs";
|
|
3756
|
+
function PaywallTrustLine({
|
|
3757
|
+
items,
|
|
3758
|
+
className,
|
|
3759
|
+
itemClassName,
|
|
3760
|
+
renderItem
|
|
4253
3761
|
}) {
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
}
|
|
4262
|
-
) });
|
|
3762
|
+
const rootClasses = [DEFAULT_ROOT3, className].filter(Boolean).join(" ");
|
|
3763
|
+
const itemClasses = [DEFAULT_ITEM, itemClassName].filter(Boolean).join(" ");
|
|
3764
|
+
return /* @__PURE__ */ jsx46("div", { className: rootClasses, children: items.map((item, idx) => {
|
|
3765
|
+
if (renderItem) return renderItem(item, idx);
|
|
3766
|
+
return /* @__PURE__ */ jsxs28("span", { className: itemClasses, children: [
|
|
3767
|
+
/* @__PURE__ */ jsx46("span", { "aria-hidden": "true", children: item.icon }),
|
|
3768
|
+
/* @__PURE__ */ jsx46("span", { children: item.text })
|
|
3769
|
+
] }, idx);
|
|
3770
|
+
}) });
|
|
4263
3771
|
}
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
3772
|
+
|
|
3773
|
+
// src/components/paywall/blocks/PaywallStickyFooter.tsx
|
|
3774
|
+
import { jsx as jsx47 } from "react/jsx-runtime";
|
|
3775
|
+
var DEFAULT_CLASSES = "sticky bottom-0 left-0 right-0 bg-background";
|
|
3776
|
+
var SAFE_AREA_CLASS = "pb-[env(safe-area-inset-bottom)]";
|
|
3777
|
+
function PaywallStickyFooter({
|
|
3778
|
+
children,
|
|
3779
|
+
className,
|
|
3780
|
+
safeAreaInsets = true
|
|
4269
3781
|
}) {
|
|
4270
|
-
const
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
3782
|
+
const classes = [DEFAULT_CLASSES, safeAreaInsets ? SAFE_AREA_CLASS : null, className].filter(Boolean).join(" ");
|
|
3783
|
+
return /* @__PURE__ */ jsx47("div", { className: classes, children });
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3786
|
+
// src/defaults/CheckoutPageDefault.tsx
|
|
3787
|
+
import { Fragment as Fragment7, jsx as jsx48, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3788
|
+
var INTENT_KEY = "hook:paywall:intent";
|
|
3789
|
+
var PIX_PAYLOAD_KEY = "hook:paywall:pix-pending";
|
|
3790
|
+
function readIntent() {
|
|
3791
|
+
if (typeof window === "undefined") return {};
|
|
3792
|
+
try {
|
|
3793
|
+
const raw = sessionStorage.getItem(INTENT_KEY);
|
|
3794
|
+
if (!raw) return {};
|
|
3795
|
+
return JSON.parse(raw);
|
|
3796
|
+
} catch {
|
|
3797
|
+
return {};
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
function formatBrl(cents) {
|
|
3801
|
+
return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(cents / 100);
|
|
3802
|
+
}
|
|
3803
|
+
function formatCardNumber(v) {
|
|
3804
|
+
const digits = v.replace(/\D/g, "").slice(0, 16);
|
|
3805
|
+
return digits.replace(/(.{4})/g, "$1 ").trim();
|
|
3806
|
+
}
|
|
3807
|
+
function formatExpiryMmAa(v) {
|
|
3808
|
+
const d = v.replace(/\D/g, "").slice(0, 4);
|
|
3809
|
+
if (d.length < 3) return d;
|
|
3810
|
+
return d.slice(0, 2) + "/" + d.slice(2);
|
|
3811
|
+
}
|
|
3812
|
+
function parseExpiryMmAa(v) {
|
|
3813
|
+
const d = v.replace(/\D/g, "");
|
|
3814
|
+
return { month: d.slice(0, 2), year: d.slice(2, 4) };
|
|
3815
|
+
}
|
|
3816
|
+
function formatCpf(v) {
|
|
3817
|
+
const d = v.replace(/\D/g, "").slice(0, 11);
|
|
3818
|
+
if (d.length <= 3) return d;
|
|
3819
|
+
if (d.length <= 6) return d.slice(0, 3) + "." + d.slice(3);
|
|
3820
|
+
if (d.length <= 9) return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6);
|
|
3821
|
+
return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6, 9) + "-" + d.slice(9);
|
|
3822
|
+
}
|
|
3823
|
+
function detectCardBrand(num) {
|
|
3824
|
+
const n = num.replace(/\s/g, "");
|
|
3825
|
+
if (/^4/.test(n)) return "VISA";
|
|
3826
|
+
if (/^(5[1-5]|2[2-7])/.test(n)) return "MASTER";
|
|
3827
|
+
if (/^3[47]/.test(n)) return "AMEX";
|
|
3828
|
+
if (/^(4011|4312|4389|4514|6011|6362|6363)/.test(n)) return "ELO";
|
|
3829
|
+
if (/^(606282|3841)/.test(n)) return "HIPER";
|
|
3830
|
+
return "";
|
|
3831
|
+
}
|
|
3832
|
+
function CheckoutPageDefault() {
|
|
3833
|
+
const navigate = useNavigate2();
|
|
3834
|
+
const plan = usePlan();
|
|
3835
|
+
const intent = useMemo6(readIntent, []);
|
|
3836
|
+
const defaultMethod = intent.method === "pix-auto" ? "pix-auto" : "card";
|
|
3837
|
+
const defaultCycle = intent.cycle === "MONTHLY" ? "MONTHLY" : "YEARLY";
|
|
3838
|
+
const form = useCheckoutForm({ defaultMethod, defaultCycle });
|
|
3839
|
+
const [expiryMmAa, setExpiryMmAa] = useState11("");
|
|
3840
|
+
useEffect13(() => {
|
|
3841
|
+
const { month, year } = parseExpiryMmAa(expiryMmAa);
|
|
3842
|
+
if (month !== form.card.expiryMonth || year !== form.card.expiryYear) {
|
|
3843
|
+
form.setCard({ expiryMonth: month, expiryYear: year });
|
|
4290
3844
|
}
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
}, [
|
|
4313
|
-
const
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
3845
|
+
}, [expiryMmAa]);
|
|
3846
|
+
useEffect13(() => {
|
|
3847
|
+
if (form.emailTaken && form.loginUrl) {
|
|
3848
|
+
const t = setTimeout(() => navigate(form.loginUrl), 1200);
|
|
3849
|
+
return () => clearTimeout(t);
|
|
3850
|
+
}
|
|
3851
|
+
}, [form.emailTaken, form.loginUrl, navigate]);
|
|
3852
|
+
const planInfo = plan.data ? {
|
|
3853
|
+
priceCents: plan.data.priceCents,
|
|
3854
|
+
yearlyPriceCents: plan.data.yearlyPriceCents,
|
|
3855
|
+
trialDays: plan.data.trialDays
|
|
3856
|
+
} : null;
|
|
3857
|
+
const annual = form.cycle === "YEARLY";
|
|
3858
|
+
const cyclePrice = useMemo6(() => {
|
|
3859
|
+
if (!planInfo) return null;
|
|
3860
|
+
return annual ? planInfo.yearlyPriceCents ?? planInfo.priceCents * 12 : planInfo.priceCents;
|
|
3861
|
+
}, [planInfo, annual]);
|
|
3862
|
+
const monthlyText = useMemo6(() => {
|
|
3863
|
+
if (!planInfo) return "";
|
|
3864
|
+
const monthly = annual && planInfo.yearlyPriceCents ? Math.round(planInfo.yearlyPriceCents / 12) : planInfo.priceCents;
|
|
3865
|
+
return formatBrl(monthly);
|
|
3866
|
+
}, [planInfo, annual]);
|
|
3867
|
+
const todayCents = useMemo6(() => {
|
|
3868
|
+
if (form.method === "pix-auto") return cyclePrice ?? 0;
|
|
3869
|
+
const trialDays2 = planInfo?.trialDays ?? 0;
|
|
3870
|
+
if (trialDays2 > 0) return 0;
|
|
3871
|
+
return cyclePrice ?? 0;
|
|
3872
|
+
}, [form.method, cyclePrice, planInfo]);
|
|
3873
|
+
const todayAmount = formatBrl(todayCents);
|
|
3874
|
+
const cyclePriceText = cyclePrice !== null ? formatBrl(cyclePrice) : "";
|
|
3875
|
+
const annualSavingsCents = useMemo6(() => {
|
|
3876
|
+
if (!planInfo || !planInfo.yearlyPriceCents) return 0;
|
|
3877
|
+
return planInfo.priceCents * 12 - planInfo.yearlyPriceCents;
|
|
3878
|
+
}, [planInfo]);
|
|
3879
|
+
const trialDays = planInfo?.trialDays ?? 7;
|
|
3880
|
+
const cardBrand = detectCardBrand(form.card.number);
|
|
3881
|
+
async function onSubmit(e) {
|
|
3882
|
+
e.preventDefault();
|
|
3883
|
+
const result = await form.submit();
|
|
3884
|
+
if (!result) return;
|
|
3885
|
+
if (form.method === "pix-auto" && result.pix_qr_payload) {
|
|
3886
|
+
try {
|
|
3887
|
+
sessionStorage.setItem(
|
|
3888
|
+
PIX_PAYLOAD_KEY,
|
|
3889
|
+
JSON.stringify({
|
|
3890
|
+
payload: result.pix_qr_payload,
|
|
3891
|
+
base64: result.pix_qr_base64 ?? null,
|
|
3892
|
+
subscriptionId: result.subscription_id,
|
|
3893
|
+
pixAuthorizationId: result.pix_authorization_id ?? null
|
|
3894
|
+
})
|
|
3895
|
+
);
|
|
3896
|
+
} catch {
|
|
3897
|
+
}
|
|
3898
|
+
navigate(result.redirect.replace(/^.*\/app\/[^/]+/, ""));
|
|
4322
3899
|
return;
|
|
4323
3900
|
}
|
|
4324
|
-
|
|
4325
|
-
}
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
/* @__PURE__ */
|
|
3901
|
+
navigate(result.redirect.replace(/^.*\/app\/[^/]+/, "") || "/");
|
|
3902
|
+
}
|
|
3903
|
+
return /* @__PURE__ */ jsx48("div", { className: "flex-1 flex flex-col bg-background min-h-0", children: /* @__PURE__ */ jsxs29("form", { onSubmit, className: "flex-1 flex flex-col min-h-0", children: [
|
|
3904
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex-1 overflow-y-auto pb-4", children: [
|
|
3905
|
+
form.emailTaken ? /* @__PURE__ */ jsx48("div", { className: "px-5 pt-4", children: /* @__PURE__ */ jsxs29("div", { role: "alert", className: "rounded-2xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: [
|
|
3906
|
+
"Esse e-mail j\xE1 tem conta nesse app.",
|
|
3907
|
+
" ",
|
|
3908
|
+
/* @__PURE__ */ jsx48("a", { href: form.loginUrl ?? "/signin", className: "underline font-semibold", children: "Entrar agora" })
|
|
3909
|
+
] }) }) : null,
|
|
3910
|
+
form.error ? /* @__PURE__ */ jsx48("div", { className: "px-5 pt-4", children: /* @__PURE__ */ jsx48("div", { role: "alert", className: "rounded-2xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: form.error.message || "N\xE3o foi poss\xEDvel concluir o pagamento. Tente novamente." }) }) : null,
|
|
3911
|
+
/* @__PURE__ */ jsx48("div", { className: "px-5 pt-4", children: form.method === "card" ? /* @__PURE__ */ jsxs29("div", { className: "rounded-2xl bg-card border-[1.5px] border-foreground p-3.5", children: [
|
|
3912
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
3913
|
+
/* @__PURE__ */ jsx48(ShieldIcon, { className: "w-4 h-4" }),
|
|
3914
|
+
/* @__PURE__ */ jsx48("div", { className: "text-sm font-bold", children: "Voc\xEA N\xC3O ser\xE1 cobrada hoje" })
|
|
3915
|
+
] }),
|
|
3916
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex justify-between items-baseline text-sm text-muted-foreground", children: [
|
|
3917
|
+
/* @__PURE__ */ jsx48("span", { children: "R$ 0,00 agora" }),
|
|
3918
|
+
/* @__PURE__ */ jsx48("span", { className: "opacity-50", children: "\xB7" }),
|
|
3919
|
+
/* @__PURE__ */ jsxs29("span", { children: [
|
|
3920
|
+
monthlyText,
|
|
3921
|
+
"/m\xEAs ap\xF3s ",
|
|
3922
|
+
trialDays,
|
|
3923
|
+
" dias"
|
|
3924
|
+
] })
|
|
3925
|
+
] }),
|
|
3926
|
+
/* @__PURE__ */ jsxs29("div", { className: "mt-2.5 text-[11px] text-muted-foreground flex items-center gap-1.5", children: [
|
|
3927
|
+
/* @__PURE__ */ jsx48(BellIcon, { className: "w-2.5 h-2.5" }),
|
|
3928
|
+
"Avisamos por email 2 dias antes da primeira cobran\xE7a"
|
|
3929
|
+
] })
|
|
3930
|
+
] }) : /* @__PURE__ */ jsxs29("div", { className: "rounded-2xl p-3.5 bg-emerald-50 border-[1.5px] border-emerald-600/60", children: [
|
|
3931
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2 mb-2 text-emerald-900", children: [
|
|
3932
|
+
/* @__PURE__ */ jsx48(ShieldIcon, { className: "w-4 h-4" }),
|
|
3933
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-sm font-bold", children: [
|
|
3934
|
+
"Garantia incondicional de ",
|
|
3935
|
+
trialDays,
|
|
3936
|
+
" dias"
|
|
3937
|
+
] })
|
|
3938
|
+
] }),
|
|
3939
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-sm text-emerald-900 leading-snug", children: [
|
|
3940
|
+
"Voc\xEA paga hoje via Pix.",
|
|
3941
|
+
/* @__PURE__ */ jsx48("br", {}),
|
|
3942
|
+
"N\xE3o gostou em ",
|
|
3943
|
+
trialDays,
|
|
3944
|
+
" dias? Devolvemos ",
|
|
3945
|
+
/* @__PURE__ */ jsx48("b", { children: "100%" }),
|
|
3946
|
+
" sem perguntas \u2014 direto pelo app."
|
|
3947
|
+
] })
|
|
3948
|
+
] }) }),
|
|
3949
|
+
/* @__PURE__ */ jsxs29("section", { className: "px-5 pt-5", children: [
|
|
3950
|
+
/* @__PURE__ */ jsx48("h2", { className: "font-display text-2xl mb-3.5 leading-tight text-foreground", children: "Quase l\xE1." }),
|
|
3951
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Email" }),
|
|
3952
|
+
/* @__PURE__ */ jsx48(
|
|
3953
|
+
FieldInput,
|
|
3954
|
+
{
|
|
3955
|
+
type: "email",
|
|
3956
|
+
inputMode: "email",
|
|
3957
|
+
autoComplete: "email",
|
|
3958
|
+
autoCapitalize: "none",
|
|
3959
|
+
autoCorrect: "off",
|
|
3960
|
+
spellCheck: false,
|
|
3961
|
+
placeholder: "seu@email.com",
|
|
3962
|
+
value: form.email,
|
|
3963
|
+
onChange: form.setEmail,
|
|
3964
|
+
onBlur: form.markEmailTouched,
|
|
3965
|
+
error: form.emailError,
|
|
3966
|
+
valid: form.emailStatus === "available"
|
|
3967
|
+
}
|
|
3968
|
+
),
|
|
3969
|
+
!form.emailError && /* @__PURE__ */ jsx48(FieldHint, { children: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : "Voc\xEA vai usar este email para entrar no app" }),
|
|
3970
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
3971
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Nome completo" }),
|
|
3972
|
+
/* @__PURE__ */ jsx48(
|
|
3973
|
+
FieldInput,
|
|
3974
|
+
{
|
|
3975
|
+
type: "text",
|
|
3976
|
+
autoComplete: "name",
|
|
3977
|
+
placeholder: "como est\xE1 no documento",
|
|
3978
|
+
value: form.name,
|
|
3979
|
+
onChange: form.setName,
|
|
3980
|
+
onBlur: form.markNameTouched,
|
|
3981
|
+
error: form.nameError,
|
|
3982
|
+
valid: !!form.name && !form.nameError
|
|
3983
|
+
}
|
|
3984
|
+
),
|
|
3985
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
3986
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "CPF" }),
|
|
3987
|
+
/* @__PURE__ */ jsx48(
|
|
3988
|
+
FieldInput,
|
|
3989
|
+
{
|
|
3990
|
+
type: "text",
|
|
3991
|
+
inputMode: "numeric",
|
|
3992
|
+
placeholder: "000.000.000-00",
|
|
3993
|
+
value: form.cpf,
|
|
3994
|
+
onChange: (v) => form.setCpf(formatCpf(v)),
|
|
3995
|
+
onBlur: form.markCpfTouched,
|
|
3996
|
+
error: form.cpfError,
|
|
3997
|
+
valid: !!form.cpf && !form.cpfError
|
|
3998
|
+
}
|
|
3999
|
+
),
|
|
4000
|
+
form.method === "card" ? /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4001
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
4002
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Telefone" }),
|
|
4003
|
+
/* @__PURE__ */ jsx48(
|
|
4004
|
+
FieldInput,
|
|
4005
|
+
{
|
|
4006
|
+
type: "tel",
|
|
4007
|
+
inputMode: "tel",
|
|
4008
|
+
autoComplete: "tel",
|
|
4009
|
+
placeholder: "(11) 99999-9999",
|
|
4010
|
+
value: form.phone,
|
|
4011
|
+
onChange: form.setPhone,
|
|
4012
|
+
onBlur: form.markPhoneTouched,
|
|
4013
|
+
error: form.phoneError,
|
|
4014
|
+
valid: !!form.phone && !form.phoneError
|
|
4015
|
+
}
|
|
4016
|
+
),
|
|
4017
|
+
!form.phoneError && /* @__PURE__ */ jsx48(FieldHint, { children: "Usado pra confirmar pagamento e tratar disputas." })
|
|
4018
|
+
] }) : null
|
|
4019
|
+
] }),
|
|
4020
|
+
/* @__PURE__ */ jsxs29("section", { className: "px-5 pt-5", children: [
|
|
4021
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Forma de pagamento" }),
|
|
4022
|
+
/* @__PURE__ */ jsxs29("div", { role: "tablist", className: "flex gap-1.5 bg-muted p-1 rounded-xl", children: [
|
|
4023
|
+
/* @__PURE__ */ jsx48(
|
|
4024
|
+
TabButton,
|
|
4025
|
+
{
|
|
4026
|
+
active: form.method === "card",
|
|
4027
|
+
onClick: () => form.setMethod("card"),
|
|
4028
|
+
icon: /* @__PURE__ */ jsx48(CardIcon, { className: "w-3.5 h-3.5" }),
|
|
4029
|
+
label: "Cart\xE3o",
|
|
4030
|
+
subtitle: trialDays > 0 ? `${trialDays} dias gr\xE1tis` : "pague hoje",
|
|
4031
|
+
subtitleActiveClass: "text-emerald-700"
|
|
4032
|
+
}
|
|
4033
|
+
),
|
|
4034
|
+
/* @__PURE__ */ jsx48(
|
|
4035
|
+
TabButton,
|
|
4036
|
+
{
|
|
4037
|
+
active: form.method === "pix-auto",
|
|
4038
|
+
onClick: () => form.setMethod("pix-auto"),
|
|
4039
|
+
icon: /* @__PURE__ */ jsx48(PixIcon, { className: "w-3.5 h-3.5" }),
|
|
4040
|
+
label: "Pix",
|
|
4041
|
+
subtitle: `pague hoje \xB7 garantia ${trialDays}d`,
|
|
4042
|
+
subtitleActiveClass: "text-foreground/70"
|
|
4043
|
+
}
|
|
4044
|
+
)
|
|
4045
|
+
] })
|
|
4046
|
+
] }),
|
|
4047
|
+
form.method === "card" ? /* @__PURE__ */ jsxs29("section", { className: "px-5 pt-3.5", children: [
|
|
4048
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "N\xFAmero do cart\xE3o" }),
|
|
4049
|
+
/* @__PURE__ */ jsxs29("div", { className: "relative", children: [
|
|
4050
|
+
/* @__PURE__ */ jsx48(
|
|
4051
|
+
FieldInput,
|
|
4052
|
+
{
|
|
4053
|
+
type: "text",
|
|
4054
|
+
inputMode: "numeric",
|
|
4055
|
+
autoComplete: "cc-number",
|
|
4056
|
+
placeholder: "0000 0000 0000 0000",
|
|
4057
|
+
value: form.card.number,
|
|
4058
|
+
onChange: (v) => form.setCard({ number: formatCardNumber(v) }),
|
|
4059
|
+
style: cardBrand ? { paddingRight: "4.5rem" } : void 0
|
|
4060
|
+
}
|
|
4061
|
+
),
|
|
4062
|
+
cardBrand && /* @__PURE__ */ jsx48("span", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-[10px] font-bold tracking-wide px-1.5 py-0.5 rounded bg-muted text-muted-foreground", children: cardBrand })
|
|
4063
|
+
] }),
|
|
4064
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
4065
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex gap-2.5", children: [
|
|
4066
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex-1", children: [
|
|
4067
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Validade" }),
|
|
4068
|
+
/* @__PURE__ */ jsx48(
|
|
4069
|
+
FieldInput,
|
|
4070
|
+
{
|
|
4071
|
+
type: "text",
|
|
4072
|
+
inputMode: "numeric",
|
|
4073
|
+
autoComplete: "cc-exp",
|
|
4074
|
+
placeholder: "MM/AA",
|
|
4075
|
+
value: expiryMmAa,
|
|
4076
|
+
onChange: (v) => setExpiryMmAa(formatExpiryMmAa(v))
|
|
4077
|
+
}
|
|
4078
|
+
)
|
|
4079
|
+
] }),
|
|
4080
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex-1", children: [
|
|
4081
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "CVV" }),
|
|
4082
|
+
/* @__PURE__ */ jsx48(
|
|
4083
|
+
FieldInput,
|
|
4084
|
+
{
|
|
4085
|
+
type: "text",
|
|
4086
|
+
inputMode: "numeric",
|
|
4087
|
+
autoComplete: "cc-csc",
|
|
4088
|
+
placeholder: "3 d\xEDgitos",
|
|
4089
|
+
value: form.card.ccv,
|
|
4090
|
+
onChange: (v) => form.setCard({ ccv: v.replace(/\D/g, "").slice(0, 4) })
|
|
4091
|
+
}
|
|
4092
|
+
)
|
|
4093
|
+
] })
|
|
4094
|
+
] }),
|
|
4095
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
4096
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Nome no cart\xE3o" }),
|
|
4097
|
+
/* @__PURE__ */ jsx48(
|
|
4098
|
+
FieldInput,
|
|
4099
|
+
{
|
|
4100
|
+
type: "text",
|
|
4101
|
+
autoComplete: "cc-name",
|
|
4102
|
+
placeholder: "como est\xE1 no cart\xE3o",
|
|
4103
|
+
value: form.card.holderName,
|
|
4104
|
+
onChange: (v) => form.setCard({ holderName: v })
|
|
4105
|
+
}
|
|
4106
|
+
)
|
|
4107
|
+
] }) : /* @__PURE__ */ jsx48("section", { className: "px-5 pt-3.5", children: /* @__PURE__ */ jsxs29("div", { className: "rounded-2xl bg-card border border-border p-3.5 flex gap-3.5 items-center", children: [
|
|
4108
|
+
/* @__PURE__ */ jsx48("div", { className: "w-[72px] h-[72px] rounded-xl shrink-0 border-2 border-foreground relative overflow-hidden bg-muted", children: /* @__PURE__ */ jsx48("div", { className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[22px] h-[22px] bg-background flex items-center justify-center", children: /* @__PURE__ */ jsx48(PixIcon, { className: "w-3.5 h-3.5 text-foreground" }) }) }),
|
|
4109
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex-1", children: [
|
|
4110
|
+
/* @__PURE__ */ jsx48("div", { className: "text-xs font-bold uppercase tracking-wider text-muted-foreground", children: "pagamento em segundos" }),
|
|
4111
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-sm text-foreground mt-1 leading-snug", children: [
|
|
4112
|
+
"Geramos seu ",
|
|
4113
|
+
/* @__PURE__ */ jsx48("b", { children: "QR Pix" }),
|
|
4114
|
+
" no pr\xF3ximo passo. Pague pelo app do banco e seu acesso libera ",
|
|
4115
|
+
/* @__PURE__ */ jsx48("b", { children: "imediatamente" }),
|
|
4116
|
+
"."
|
|
4117
|
+
] })
|
|
4118
|
+
] })
|
|
4119
|
+
] }) }),
|
|
4120
|
+
/* @__PURE__ */ jsx48("section", { className: "px-5 pt-5", children: /* @__PURE__ */ jsxs29("div", { className: "bg-muted rounded-2xl p-4", children: [
|
|
4121
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex justify-between mb-2.5", children: [
|
|
4122
|
+
/* @__PURE__ */ jsxs29("div", { children: [
|
|
4123
|
+
/* @__PURE__ */ jsx48("div", { className: "text-sm font-semibold text-foreground", children: annual ? "Plano Anual" : "Plano Mensal" }),
|
|
4124
|
+
/* @__PURE__ */ jsx48("div", { className: "text-[11px] text-muted-foreground", children: "Coach" })
|
|
4125
|
+
] }),
|
|
4126
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-right", children: [
|
|
4127
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-sm font-bold text-foreground", children: [
|
|
4128
|
+
cyclePriceText,
|
|
4129
|
+
"/",
|
|
4130
|
+
annual ? "ano" : "m\xEAs"
|
|
4131
|
+
] }),
|
|
4132
|
+
annual && annualSavingsCents > 0 && /* @__PURE__ */ jsxs29("div", { className: "text-[11px] text-emerald-700 font-semibold", children: [
|
|
4133
|
+
"economia ",
|
|
4134
|
+
formatBrl(annualSavingsCents)
|
|
4135
|
+
] })
|
|
4136
|
+
] })
|
|
4137
|
+
] }),
|
|
4138
|
+
/* @__PURE__ */ jsx48("div", { className: "h-px bg-border my-3" }),
|
|
4139
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex justify-between items-baseline", children: [
|
|
4140
|
+
/* @__PURE__ */ jsxs29("div", { children: [
|
|
4141
|
+
/* @__PURE__ */ jsx48("div", { className: "text-sm font-bold text-foreground", children: "Voc\xEA paga hoje" }),
|
|
4142
|
+
form.method === "card" && trialDays > 0 && /* @__PURE__ */ jsxs29("div", { className: "text-[11px] text-muted-foreground mt-0.5", children: [
|
|
4143
|
+
"cobran\xE7a inicia no dia ",
|
|
4144
|
+
trialDays
|
|
4145
|
+
] }),
|
|
4146
|
+
form.method === "pix-auto" && /* @__PURE__ */ jsxs29("div", { className: "text-[11px] text-emerald-700 mt-0.5 font-semibold", children: [
|
|
4147
|
+
"reembolso garantido at\xE9 o dia ",
|
|
4148
|
+
trialDays
|
|
4149
|
+
] })
|
|
4150
|
+
] }),
|
|
4151
|
+
/* @__PURE__ */ jsx48("div", { className: "text-2xl font-bold font-display tracking-tight text-foreground", children: todayAmount })
|
|
4152
|
+
] })
|
|
4153
|
+
] }) })
|
|
4154
|
+
] }),
|
|
4155
|
+
/* @__PURE__ */ jsxs29(PaywallStickyFooter, { className: "px-5 pt-3.5 pb-5 border-t border-border", children: [
|
|
4156
|
+
/* @__PURE__ */ jsx48(
|
|
4373
4157
|
"button",
|
|
4374
4158
|
{
|
|
4375
|
-
type: "
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4159
|
+
type: "submit",
|
|
4160
|
+
disabled: !form.canSubmit,
|
|
4161
|
+
className: "w-full rounded-full bg-primary text-primary-foreground min-h-14 px-5 text-base font-bold inline-flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg",
|
|
4162
|
+
children: form.submitting ? /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4163
|
+
/* @__PURE__ */ jsx48(Spinner2, {}),
|
|
4164
|
+
" ",
|
|
4165
|
+
form.method === "pix-auto" ? "Gerando QR\u2026" : "Confirmando\u2026"
|
|
4166
|
+
] }) : form.method === "card" ? /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4167
|
+
/* @__PURE__ */ jsx48(LockIcon, { className: "w-3.5 h-3.5" }),
|
|
4168
|
+
" Confirmar e come\xE7ar gr\xE1tis"
|
|
4169
|
+
] }) : /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4170
|
+
/* @__PURE__ */ jsx48(PixIcon, { className: "w-3.5 h-3.5" }),
|
|
4171
|
+
" Gerar QR Pix"
|
|
4172
|
+
] })
|
|
4382
4173
|
}
|
|
4383
4174
|
),
|
|
4384
|
-
|
|
4385
|
-
|
|
4175
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-center mt-2.5 text-xs text-muted-foreground", children: [
|
|
4176
|
+
"Ao continuar, voc\xEA concorda com nossos ",
|
|
4177
|
+
/* @__PURE__ */ jsx48("u", { children: "Termos" }),
|
|
4178
|
+
". Pagamento seguro Asaas."
|
|
4179
|
+
] }),
|
|
4180
|
+
/* @__PURE__ */ jsxs29("div", { className: "mt-3 flex items-center justify-center gap-3.5 text-[11px] text-muted-foreground", children: [
|
|
4181
|
+
/* @__PURE__ */ jsxs29("span", { className: "inline-flex items-center gap-1", children: [
|
|
4182
|
+
/* @__PURE__ */ jsx48(LockIcon, { className: "w-2.5 h-2.5 opacity-60" }),
|
|
4183
|
+
" SSL 256-bit"
|
|
4184
|
+
] }),
|
|
4185
|
+
/* @__PURE__ */ jsx48("span", { className: "w-px h-2.5 bg-border" }),
|
|
4186
|
+
/* @__PURE__ */ jsx48("span", { children: "Pagamento via Asaas" }),
|
|
4187
|
+
/* @__PURE__ */ jsx48("span", { className: "w-px h-2.5 bg-border" }),
|
|
4188
|
+
/* @__PURE__ */ jsxs29("span", { children: [
|
|
4189
|
+
"Garantia ",
|
|
4190
|
+
trialDays,
|
|
4191
|
+
" dias"
|
|
4192
|
+
] })
|
|
4193
|
+
] })
|
|
4386
4194
|
] })
|
|
4387
|
-
] });
|
|
4388
|
-
}
|
|
4389
|
-
function interp(tpl, vars) {
|
|
4390
|
-
return tpl.replace(/\{(\w+)\}/g, (_m, k) => vars[k] ?? "");
|
|
4195
|
+
] }) });
|
|
4391
4196
|
}
|
|
4392
|
-
function
|
|
4393
|
-
return {
|
|
4394
|
-
tabLabel: m.tabLabel,
|
|
4395
|
-
bodyRows: m.bodyRows.map((r) => interp(r, { price, days })),
|
|
4396
|
-
ctaTemplate: m.ctaTemplate,
|
|
4397
|
-
switchHint: m.switchHint
|
|
4398
|
-
};
|
|
4197
|
+
function FieldLabel({ children }) {
|
|
4198
|
+
return /* @__PURE__ */ jsx48("div", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-1.5", children });
|
|
4399
4199
|
}
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
switchHint,
|
|
4407
|
-
trustLine,
|
|
4408
|
-
className,
|
|
4409
|
-
buttonClassName,
|
|
4410
|
-
switchHintClassName,
|
|
4411
|
-
trustClassName
|
|
4412
|
-
}) {
|
|
4413
|
-
const { submit, submitting } = usePaywallContext();
|
|
4414
|
-
const label = submitting && loadingLabel ? loadingLabel : ctaLabel;
|
|
4415
|
-
return /* @__PURE__ */ jsxs23("div", { className, children: [
|
|
4416
|
-
/* @__PURE__ */ jsx37(
|
|
4417
|
-
"button",
|
|
4200
|
+
function FieldInput(props) {
|
|
4201
|
+
const baseClass = "w-full px-4 rounded-xl bg-card text-base text-foreground outline-none border-[1.5px] transition-colors";
|
|
4202
|
+
const stateClass = props.error ? "border-destructive focus:border-destructive" : props.valid ? "border-emerald-600 focus:border-emerald-700" : "border-border focus:border-foreground";
|
|
4203
|
+
return /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4204
|
+
/* @__PURE__ */ jsx48(
|
|
4205
|
+
"input",
|
|
4418
4206
|
{
|
|
4419
|
-
type: "
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4207
|
+
type: props.type ?? "text",
|
|
4208
|
+
inputMode: props.inputMode,
|
|
4209
|
+
autoComplete: props.autoComplete,
|
|
4210
|
+
autoCapitalize: props.autoCapitalize,
|
|
4211
|
+
autoCorrect: props.autoCorrect,
|
|
4212
|
+
spellCheck: props.spellCheck,
|
|
4213
|
+
placeholder: props.placeholder,
|
|
4214
|
+
value: props.value,
|
|
4215
|
+
onChange: (e) => props.onChange(e.target.value),
|
|
4216
|
+
onBlur: props.onBlur,
|
|
4217
|
+
style: { height: "52px", ...props.style },
|
|
4218
|
+
className: `${baseClass} ${stateClass}`
|
|
4426
4219
|
}
|
|
4427
4220
|
),
|
|
4428
|
-
|
|
4429
|
-
/* @__PURE__ */ jsx37("p", { className: trustClassName, children: trustLine })
|
|
4221
|
+
props.error ? /* @__PURE__ */ jsx48("div", { className: "mt-1.5 text-xs text-destructive font-medium", children: props.error }) : null
|
|
4430
4222
|
] });
|
|
4431
4223
|
}
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
import { jsx as jsx38 } from "react/jsx-runtime";
|
|
4435
|
-
var DEFAULT_EYEBROW_CLASSES = "text-xs uppercase tracking-widest font-semibold opacity-70";
|
|
4436
|
-
function PaywallEyebrow({ text, className }) {
|
|
4437
|
-
return /* @__PURE__ */ jsx38("div", { className: [DEFAULT_EYEBROW_CLASSES, className].filter(Boolean).join(" "), children: text });
|
|
4224
|
+
function FieldHint({ children }) {
|
|
4225
|
+
return /* @__PURE__ */ jsx48("div", { className: "mt-1.5 text-xs text-muted-foreground", children });
|
|
4438
4226
|
}
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
var DEFAULT_GRADIENT = "absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent";
|
|
4443
|
-
function PaywallHero({
|
|
4444
|
-
src,
|
|
4445
|
-
alt = "",
|
|
4446
|
-
headline,
|
|
4447
|
-
aspectRatio = "16/9",
|
|
4448
|
-
gradientClassName,
|
|
4449
|
-
className,
|
|
4450
|
-
headlineClassName,
|
|
4451
|
-
imgClassName,
|
|
4452
|
-
render
|
|
4453
|
-
}) {
|
|
4454
|
-
if (render) {
|
|
4455
|
-
return /* @__PURE__ */ jsx39("div", { className, children: render({ src, headline }) });
|
|
4456
|
-
}
|
|
4457
|
-
return /* @__PURE__ */ jsxs24(
|
|
4458
|
-
"div",
|
|
4227
|
+
function TabButton({ active, onClick, icon, label, subtitle, subtitleActiveClass }) {
|
|
4228
|
+
return /* @__PURE__ */ jsxs29(
|
|
4229
|
+
"button",
|
|
4459
4230
|
{
|
|
4460
|
-
|
|
4461
|
-
|
|
4231
|
+
type: "button",
|
|
4232
|
+
role: "tab",
|
|
4233
|
+
"aria-selected": active,
|
|
4234
|
+
onClick,
|
|
4235
|
+
className: `flex-1 flex flex-col items-center justify-center gap-0.5 py-2.5 px-1.5 rounded-lg text-sm font-semibold transition-colors ${active ? "bg-card text-foreground shadow-sm" : "bg-transparent text-muted-foreground"}`,
|
|
4236
|
+
style: { minHeight: 56 },
|
|
4462
4237
|
children: [
|
|
4463
|
-
/* @__PURE__ */
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
}
|
|
4470
|
-
),
|
|
4471
|
-
/* @__PURE__ */ jsx39("div", { className: gradientClassName ?? DEFAULT_GRADIENT, "aria-hidden": "true" }),
|
|
4472
|
-
headline ? /* @__PURE__ */ jsx39(
|
|
4473
|
-
"h1",
|
|
4474
|
-
{
|
|
4475
|
-
className: ["absolute bottom-0 left-0 right-0 p-4 text-white font-bold text-2xl", headlineClassName].filter(Boolean).join(" "),
|
|
4476
|
-
children: headline
|
|
4477
|
-
}
|
|
4478
|
-
) : null
|
|
4238
|
+
/* @__PURE__ */ jsxs29("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
4239
|
+
icon,
|
|
4240
|
+
" ",
|
|
4241
|
+
label
|
|
4242
|
+
] }),
|
|
4243
|
+
/* @__PURE__ */ jsx48("span", { className: `text-[10px] font-medium ${active ? subtitleActiveClass : "text-muted-foreground/70"}`, children: subtitle })
|
|
4479
4244
|
]
|
|
4480
4245
|
}
|
|
4481
4246
|
);
|
|
4482
4247
|
}
|
|
4248
|
+
function CardIcon({ className }) {
|
|
4249
|
+
return /* @__PURE__ */ jsxs29("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4250
|
+
/* @__PURE__ */ jsx48("rect", { x: "2.5", y: "5.5", width: "19", height: "13", rx: "2.5" }),
|
|
4251
|
+
/* @__PURE__ */ jsx48("path", { d: "M2.5 10h19" })
|
|
4252
|
+
] });
|
|
4253
|
+
}
|
|
4254
|
+
function PixIcon({ className }) {
|
|
4255
|
+
return /* @__PURE__ */ jsx48("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx48("path", { d: "M12 2L2 12l10 10 10-10L12 2zm0 4.83L17.17 12 12 17.17 6.83 12 12 6.83z" }) });
|
|
4256
|
+
}
|
|
4257
|
+
function LockIcon({ className }) {
|
|
4258
|
+
return /* @__PURE__ */ jsxs29("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4259
|
+
/* @__PURE__ */ jsx48("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2.5" }),
|
|
4260
|
+
/* @__PURE__ */ jsx48("path", { d: "M7.5 10V7a4.5 4.5 0 119 0v3" })
|
|
4261
|
+
] });
|
|
4262
|
+
}
|
|
4263
|
+
function ShieldIcon({ className }) {
|
|
4264
|
+
return /* @__PURE__ */ jsxs29("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4265
|
+
/* @__PURE__ */ jsx48("path", { d: "M12 2.5l8 3v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10v-6l8-3z" }),
|
|
4266
|
+
/* @__PURE__ */ jsx48("path", { d: "M9 12l2 2 4-4" })
|
|
4267
|
+
] });
|
|
4268
|
+
}
|
|
4269
|
+
function BellIcon({ className }) {
|
|
4270
|
+
return /* @__PURE__ */ jsxs29("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4271
|
+
/* @__PURE__ */ jsx48("path", { d: "M6 17V11a6 6 0 1112 0v6l1.5 2H4.5L6 17z" }),
|
|
4272
|
+
/* @__PURE__ */ jsx48("path", { d: "M10 21a2 2 0 004 0" })
|
|
4273
|
+
] });
|
|
4274
|
+
}
|
|
4275
|
+
function Spinner2() {
|
|
4276
|
+
return /* @__PURE__ */ jsx48(
|
|
4277
|
+
"span",
|
|
4278
|
+
{
|
|
4279
|
+
className: "w-4 h-4 rounded-full border-2 border-white/40 border-t-white",
|
|
4280
|
+
style: { animation: "spin 0.7s linear infinite" }
|
|
4281
|
+
}
|
|
4282
|
+
);
|
|
4283
|
+
}
|
|
4483
4284
|
|
|
4484
|
-
// src/
|
|
4485
|
-
import {
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4285
|
+
// src/defaults/PixWaitingPageDefault.tsx
|
|
4286
|
+
import { useEffect as useEffect14, useMemo as useMemo7, useState as useState12 } from "react";
|
|
4287
|
+
import { useNavigate as useNavigate3 } from "react-router-dom";
|
|
4288
|
+
import { useHook as useHook13 } from "@hook-sdk/sdk";
|
|
4289
|
+
import { jsx as jsx49, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
4290
|
+
var PIX_PAYLOAD_KEY2 = "hook:paywall:pix-pending";
|
|
4291
|
+
var TIMEOUT_MS = 30 * 60 * 1e3;
|
|
4292
|
+
function readPixPayload() {
|
|
4293
|
+
if (typeof window === "undefined") return null;
|
|
4294
|
+
try {
|
|
4295
|
+
const raw = sessionStorage.getItem(PIX_PAYLOAD_KEY2);
|
|
4296
|
+
if (!raw) return null;
|
|
4297
|
+
return JSON.parse(raw);
|
|
4298
|
+
} catch {
|
|
4299
|
+
return null;
|
|
4300
|
+
}
|
|
4490
4301
|
}
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
MONTHLY: "mensal",
|
|
4497
|
-
YEARLY: "anual"
|
|
4498
|
-
};
|
|
4499
|
-
function PaywallPriceHeadline({
|
|
4500
|
-
template,
|
|
4501
|
-
className,
|
|
4502
|
-
as = "h1",
|
|
4503
|
-
render
|
|
4504
|
-
}) {
|
|
4505
|
-
const { cycle, currentMonthlyEquivCents, plan } = usePaywallContext();
|
|
4506
|
-
const yearlyCents = plan?.yearlyCents ?? null;
|
|
4507
|
-
const pricePerDay = formatBRL(dailyFromYearly(yearlyCents));
|
|
4508
|
-
const monthlyEquiv = currentMonthlyEquivCents ?? 0;
|
|
4509
|
-
const cycleLabel = CYCLE_LABEL[cycle] ?? cycle.toLowerCase();
|
|
4510
|
-
const rootClasses = [DEFAULT_CLASS, className].filter(Boolean).join(" ");
|
|
4511
|
-
if (render) {
|
|
4512
|
-
const RootTag2 = as;
|
|
4513
|
-
return /* @__PURE__ */ jsx41(RootTag2, { className: [className].filter(Boolean).join(" ") || void 0, children: render({ pricePerDay, currentMonthlyEquivCents: monthlyEquiv, cycle }) });
|
|
4302
|
+
function clearPixPayload() {
|
|
4303
|
+
if (typeof window === "undefined") return;
|
|
4304
|
+
try {
|
|
4305
|
+
sessionStorage.removeItem(PIX_PAYLOAD_KEY2);
|
|
4306
|
+
} catch {
|
|
4514
4307
|
}
|
|
4515
|
-
const text = template.replaceAll("{pricePerDay}", pricePerDay).replaceAll("{currentMonthlyEquiv}", formatBRL(monthlyEquiv)).replaceAll("{cycle}", cycleLabel);
|
|
4516
|
-
const RootTag = as;
|
|
4517
|
-
return /* @__PURE__ */ jsx41(RootTag, { className: rootClasses, children: text });
|
|
4518
4308
|
}
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4309
|
+
function PixWaitingPageDefault() {
|
|
4310
|
+
const navigate = useNavigate3();
|
|
4311
|
+
const { subscription } = useHook13();
|
|
4312
|
+
const payload = useMemo7(readPixPayload, []);
|
|
4313
|
+
const [copied, setCopied] = useState12(false);
|
|
4314
|
+
const [timedOut, setTimedOut] = useState12(false);
|
|
4315
|
+
useEffect14(() => {
|
|
4316
|
+
if (!payload) navigate("/paywall/checkout", { replace: true });
|
|
4317
|
+
}, [payload, navigate]);
|
|
4318
|
+
useEffect14(() => {
|
|
4319
|
+
window.scrollTo(0, 0);
|
|
4320
|
+
}, []);
|
|
4321
|
+
useEffect14(() => {
|
|
4322
|
+
const t = setTimeout(() => setTimedOut(true), TIMEOUT_MS);
|
|
4323
|
+
return () => clearTimeout(t);
|
|
4324
|
+
}, []);
|
|
4325
|
+
const hasAccess = subscription.hasAccess;
|
|
4326
|
+
useEffect14(() => {
|
|
4327
|
+
if (hasAccess) {
|
|
4328
|
+
clearPixPayload();
|
|
4329
|
+
navigate("/", { replace: true });
|
|
4330
|
+
}
|
|
4331
|
+
}, [hasAccess, navigate]);
|
|
4332
|
+
async function copyPayload() {
|
|
4333
|
+
if (!payload?.payload) return;
|
|
4334
|
+
try {
|
|
4335
|
+
await navigator.clipboard.writeText(payload.payload);
|
|
4336
|
+
setCopied(true);
|
|
4337
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
4338
|
+
} catch {
|
|
4339
|
+
}
|
|
4530
4340
|
}
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4341
|
+
if (!payload) return null;
|
|
4342
|
+
if (timedOut) {
|
|
4343
|
+
return /* @__PURE__ */ jsxs30("div", { className: "flex-1 flex flex-col items-center justify-center px-6 py-10 text-center bg-background space-y-4", children: [
|
|
4344
|
+
/* @__PURE__ */ jsx49("h1", { className: "font-display text-2xl text-foreground", children: "PIX expirado" }),
|
|
4345
|
+
/* @__PURE__ */ jsx49("p", { className: "text-sm text-muted-foreground", children: "O tempo pra pagar acabou. Gere um novo PIX." }),
|
|
4346
|
+
/* @__PURE__ */ jsx49(
|
|
4347
|
+
"button",
|
|
4348
|
+
{
|
|
4349
|
+
onClick: () => {
|
|
4350
|
+
clearPixPayload();
|
|
4351
|
+
navigate("/paywall/checkout", { replace: true });
|
|
4352
|
+
},
|
|
4353
|
+
className: "rounded-xl bg-primary px-6 py-3 text-base font-semibold text-primary-foreground",
|
|
4354
|
+
children: "Tentar novamente"
|
|
4355
|
+
}
|
|
4356
|
+
)
|
|
4357
|
+
] });
|
|
4538
4358
|
}
|
|
4539
|
-
return
|
|
4359
|
+
return /* @__PURE__ */ jsxs30("div", { className: "flex-1 flex flex-col items-center px-6 py-8 bg-background space-y-6", children: [
|
|
4360
|
+
/* @__PURE__ */ jsxs30("header", { className: "text-center space-y-2", children: [
|
|
4361
|
+
/* @__PURE__ */ jsx49("h1", { className: "font-display text-2xl text-foreground", children: "Pague o PIX" }),
|
|
4362
|
+
/* @__PURE__ */ jsx49("p", { className: "text-sm text-muted-foreground", children: "Escaneie o QR Code no app do seu banco. O acesso libera assim que confirmarmos o pagamento." })
|
|
4363
|
+
] }),
|
|
4364
|
+
payload.base64 ? /* @__PURE__ */ jsx49(
|
|
4365
|
+
"img",
|
|
4366
|
+
{
|
|
4367
|
+
src: `data:image/png;base64,${payload.base64}`,
|
|
4368
|
+
alt: "QR Code PIX",
|
|
4369
|
+
className: "w-64 h-64 rounded-2xl border border-border bg-card p-2"
|
|
4370
|
+
}
|
|
4371
|
+
) : /* @__PURE__ */ jsx49("div", { className: "w-64 h-64 rounded-2xl border border-border bg-card flex items-center justify-center text-sm text-muted-foreground", children: "QR indispon\xEDvel" }),
|
|
4372
|
+
payload.payload ? /* @__PURE__ */ jsx49(
|
|
4373
|
+
"button",
|
|
4374
|
+
{
|
|
4375
|
+
onClick: copyPayload,
|
|
4376
|
+
className: "w-full max-w-xs rounded-xl border border-border bg-card px-4 py-3 text-sm font-medium text-foreground",
|
|
4377
|
+
children: copied ? "\u2713 Copiado!" : "Copiar c\xF3digo PIX"
|
|
4378
|
+
}
|
|
4379
|
+
) : null,
|
|
4380
|
+
/* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
4381
|
+
/* @__PURE__ */ jsx49("span", { className: "inline-block w-2 h-2 rounded-full bg-primary animate-pulse" }),
|
|
4382
|
+
"Aguardando pagamento\u2026"
|
|
4383
|
+
] }),
|
|
4384
|
+
/* @__PURE__ */ jsx49("p", { className: "text-center text-xs text-muted-foreground", children: "Pode fechar essa janela \u2014 tamb\xE9m enviamos um link de acesso pro seu e-mail." })
|
|
4385
|
+
] });
|
|
4540
4386
|
}
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4387
|
+
|
|
4388
|
+
// src/hooks/useLoginForm.ts
|
|
4389
|
+
import { useCallback as useCallback7, useMemo as useMemo8, useState as useState13 } from "react";
|
|
4390
|
+
import { useHook as useHook14 } from "@hook-sdk/sdk";
|
|
4391
|
+
var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4392
|
+
var MIN_PASSWORD = 8;
|
|
4393
|
+
function useLoginForm() {
|
|
4394
|
+
const { auth } = useHook14();
|
|
4395
|
+
const [email, setEmail] = useState13("");
|
|
4396
|
+
const [password, setPassword] = useState13("");
|
|
4397
|
+
const [submitting, setSubmitting] = useState13(false);
|
|
4398
|
+
const [error, setError] = useState13(null);
|
|
4399
|
+
const [touchedEmail, setTouchedEmail] = useState13(false);
|
|
4400
|
+
const [touchedPassword, setTouchedPassword] = useState13(false);
|
|
4401
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState13(false);
|
|
4402
|
+
const validateEmail = useMemo8(() => {
|
|
4403
|
+
if (email.length === 0) return null;
|
|
4404
|
+
if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
4405
|
+
return null;
|
|
4406
|
+
}, [email]);
|
|
4407
|
+
const validatePassword = useMemo8(() => {
|
|
4408
|
+
if (password.length === 0) return null;
|
|
4409
|
+
if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
|
|
4410
|
+
return null;
|
|
4411
|
+
}, [password]);
|
|
4412
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
4413
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
4414
|
+
const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
|
|
4415
|
+
const submit = useCallback7(async () => {
|
|
4416
|
+
setFormSubmitAttempted(true);
|
|
4417
|
+
if (!canSubmit) return false;
|
|
4418
|
+
setSubmitting(true);
|
|
4419
|
+
setError(null);
|
|
4420
|
+
try {
|
|
4421
|
+
await auth.login({ email, password });
|
|
4422
|
+
return true;
|
|
4423
|
+
} catch (err) {
|
|
4424
|
+
setError(mapSdkError(err));
|
|
4425
|
+
return false;
|
|
4426
|
+
} finally {
|
|
4427
|
+
setSubmitting(false);
|
|
4428
|
+
}
|
|
4429
|
+
}, [auth, email, password, canSubmit]);
|
|
4430
|
+
return {
|
|
4431
|
+
email,
|
|
4432
|
+
setEmail,
|
|
4433
|
+
emailError,
|
|
4434
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
4435
|
+
password,
|
|
4436
|
+
setPassword,
|
|
4437
|
+
passwordError,
|
|
4438
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
4439
|
+
formSubmitAttempted,
|
|
4440
|
+
submit,
|
|
4441
|
+
submitting,
|
|
4442
|
+
canSubmit,
|
|
4443
|
+
error,
|
|
4444
|
+
loginWithGoogle: () => auth.loginWithGoogle()
|
|
4445
|
+
};
|
|
4446
|
+
}
|
|
4447
|
+
|
|
4448
|
+
// src/hooks/useSignupForm.ts
|
|
4449
|
+
import { useCallback as useCallback8, useMemo as useMemo9, useState as useState14 } from "react";
|
|
4450
|
+
import { useHook as useHook15 } from "@hook-sdk/sdk";
|
|
4451
|
+
var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4452
|
+
var MIN_PASSWORD2 = 8;
|
|
4453
|
+
function useSignupForm() {
|
|
4454
|
+
const { auth } = useHook15();
|
|
4455
|
+
const [name, setName] = useState14("");
|
|
4456
|
+
const [email, setEmail] = useState14("");
|
|
4457
|
+
const [password, setPassword] = useState14("");
|
|
4458
|
+
const [submitting, setSubmitting] = useState14(false);
|
|
4459
|
+
const [error, setError] = useState14(null);
|
|
4460
|
+
const [touchedName, setTouchedName] = useState14(false);
|
|
4461
|
+
const [touchedEmail, setTouchedEmail] = useState14(false);
|
|
4462
|
+
const [touchedPassword, setTouchedPassword] = useState14(false);
|
|
4463
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState14(false);
|
|
4464
|
+
const validateName = useMemo9(() => {
|
|
4465
|
+
if (name.length === 0) return null;
|
|
4466
|
+
if (name.trim().length < 2) return "Nome muito curto.";
|
|
4467
|
+
return null;
|
|
4468
|
+
}, [name]);
|
|
4469
|
+
const validateEmail = useMemo9(() => {
|
|
4470
|
+
if (email.length === 0) return null;
|
|
4471
|
+
if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
4472
|
+
return null;
|
|
4473
|
+
}, [email]);
|
|
4474
|
+
const validatePassword = useMemo9(() => {
|
|
4475
|
+
if (password.length === 0) return null;
|
|
4476
|
+
if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
|
|
4477
|
+
return null;
|
|
4478
|
+
}, [password]);
|
|
4479
|
+
const nameError = touchedName || formSubmitAttempted ? validateName : null;
|
|
4480
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
4481
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
4482
|
+
const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
|
|
4483
|
+
const submit = useCallback8(async () => {
|
|
4484
|
+
setFormSubmitAttempted(true);
|
|
4485
|
+
if (!canSubmit) return false;
|
|
4486
|
+
setSubmitting(true);
|
|
4487
|
+
setError(null);
|
|
4488
|
+
try {
|
|
4489
|
+
await auth.signup({ name, email, password });
|
|
4490
|
+
return true;
|
|
4491
|
+
} catch (err) {
|
|
4492
|
+
setError(mapSdkError(err));
|
|
4493
|
+
return false;
|
|
4494
|
+
} finally {
|
|
4495
|
+
setSubmitting(false);
|
|
4496
|
+
}
|
|
4497
|
+
}, [auth, name, email, password, canSubmit]);
|
|
4498
|
+
return {
|
|
4499
|
+
name,
|
|
4500
|
+
setName,
|
|
4501
|
+
nameError,
|
|
4502
|
+
markNameTouched: () => setTouchedName(true),
|
|
4503
|
+
email,
|
|
4504
|
+
setEmail,
|
|
4505
|
+
emailError,
|
|
4506
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
4507
|
+
password,
|
|
4508
|
+
setPassword,
|
|
4509
|
+
passwordError,
|
|
4510
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
4511
|
+
formSubmitAttempted,
|
|
4512
|
+
submit,
|
|
4513
|
+
submitting,
|
|
4514
|
+
canSubmit,
|
|
4515
|
+
error,
|
|
4516
|
+
loginWithGoogle: () => auth.loginWithGoogle()
|
|
4517
|
+
};
|
|
4548
4518
|
}
|
|
4549
|
-
|
|
4550
|
-
|
|
4519
|
+
|
|
4520
|
+
// src/hooks/useForgotForm.ts
|
|
4521
|
+
import { useCallback as useCallback9, useMemo as useMemo10, useState as useState15 } from "react";
|
|
4522
|
+
import { useHook as useHook16 } from "@hook-sdk/sdk";
|
|
4523
|
+
var EMAIL_RE4 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4524
|
+
function useForgotForm() {
|
|
4525
|
+
const { auth } = useHook16();
|
|
4526
|
+
const [email, setEmail] = useState15("");
|
|
4527
|
+
const [submitting, setSubmitting] = useState15(false);
|
|
4528
|
+
const [sent, setSent] = useState15(false);
|
|
4529
|
+
const [error, setError] = useState15(null);
|
|
4530
|
+
const [touchedEmail, setTouchedEmail] = useState15(false);
|
|
4531
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState15(false);
|
|
4532
|
+
const validateEmail = useMemo10(() => {
|
|
4533
|
+
if (email.length === 0) return null;
|
|
4534
|
+
if (!EMAIL_RE4.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
4535
|
+
return null;
|
|
4536
|
+
}, [email]);
|
|
4537
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
4538
|
+
const canSubmit = email.length > 0 && validateEmail === null && !submitting;
|
|
4539
|
+
const submit = useCallback9(async () => {
|
|
4540
|
+
setFormSubmitAttempted(true);
|
|
4541
|
+
if (!canSubmit) return false;
|
|
4542
|
+
setSubmitting(true);
|
|
4543
|
+
setError(null);
|
|
4544
|
+
try {
|
|
4545
|
+
await auth.forgot({ email });
|
|
4546
|
+
setSent(true);
|
|
4547
|
+
return true;
|
|
4548
|
+
} catch (err) {
|
|
4549
|
+
setError(mapSdkError(err));
|
|
4550
|
+
return false;
|
|
4551
|
+
} finally {
|
|
4552
|
+
setSubmitting(false);
|
|
4553
|
+
}
|
|
4554
|
+
}, [auth, email, canSubmit]);
|
|
4555
|
+
return {
|
|
4556
|
+
email,
|
|
4557
|
+
setEmail,
|
|
4558
|
+
emailError,
|
|
4559
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
4560
|
+
formSubmitAttempted,
|
|
4561
|
+
submit,
|
|
4562
|
+
submitting,
|
|
4563
|
+
canSubmit,
|
|
4564
|
+
sent,
|
|
4565
|
+
error
|
|
4566
|
+
};
|
|
4551
4567
|
}
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
})
|
|
4559
|
-
const
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
const [
|
|
4564
|
-
const
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4568
|
+
|
|
4569
|
+
// src/hooks/useResetForm.ts
|
|
4570
|
+
import { useCallback as useCallback10, useEffect as useEffect15, useMemo as useMemo11, useState as useState16 } from "react";
|
|
4571
|
+
import { useHook as useHook17 } from "@hook-sdk/sdk";
|
|
4572
|
+
var MIN_PASSWORD3 = 8;
|
|
4573
|
+
function useResetForm() {
|
|
4574
|
+
const { auth } = useHook17();
|
|
4575
|
+
const [token, setToken] = useState16(null);
|
|
4576
|
+
const [password, setPassword] = useState16("");
|
|
4577
|
+
const [confirm, setConfirm] = useState16("");
|
|
4578
|
+
const [submitting, setSubmitting] = useState16(false);
|
|
4579
|
+
const [done, setDone] = useState16(false);
|
|
4580
|
+
const [error, setError] = useState16(null);
|
|
4581
|
+
const [touchedPassword, setTouchedPassword] = useState16(false);
|
|
4582
|
+
const [touchedConfirm, setTouchedConfirm] = useState16(false);
|
|
4583
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState16(false);
|
|
4584
|
+
useEffect15(() => {
|
|
4585
|
+
if (typeof window === "undefined") return;
|
|
4586
|
+
const params = new URLSearchParams(window.location.search);
|
|
4587
|
+
const t = params.get("token");
|
|
4588
|
+
setToken(t && t.length > 0 ? t : null);
|
|
4589
|
+
}, []);
|
|
4590
|
+
const validatePassword = useMemo11(() => {
|
|
4591
|
+
if (password.length === 0) return null;
|
|
4592
|
+
if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
|
|
4593
|
+
return null;
|
|
4594
|
+
}, [password]);
|
|
4595
|
+
const validateConfirm = useMemo11(() => {
|
|
4596
|
+
if (confirm.length === 0) return null;
|
|
4597
|
+
if (confirm !== password) return "Senhas n\xE3o coincidem.";
|
|
4598
|
+
return null;
|
|
4599
|
+
}, [confirm, password]);
|
|
4600
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
4601
|
+
const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
|
|
4602
|
+
const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
|
|
4603
|
+
const submit = useCallback10(async () => {
|
|
4604
|
+
setFormSubmitAttempted(true);
|
|
4605
|
+
if (!canSubmit || token === null) return;
|
|
4606
|
+
setSubmitting(true);
|
|
4607
|
+
setError(null);
|
|
4608
|
+
try {
|
|
4609
|
+
await auth.reset({ token, newPassword: password });
|
|
4610
|
+
setDone(true);
|
|
4611
|
+
if (typeof window !== "undefined") {
|
|
4612
|
+
const url = new URL(window.location.href);
|
|
4613
|
+
url.searchParams.delete("token");
|
|
4614
|
+
url.searchParams.delete("screen");
|
|
4615
|
+
window.history.replaceState({}, "", url.toString());
|
|
4570
4616
|
}
|
|
4571
|
-
|
|
4617
|
+
} catch (err) {
|
|
4618
|
+
setError(mapSdkError(err));
|
|
4619
|
+
} finally {
|
|
4620
|
+
setSubmitting(false);
|
|
4572
4621
|
}
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4622
|
+
}, [auth, token, password, canSubmit]);
|
|
4623
|
+
return {
|
|
4624
|
+
token,
|
|
4625
|
+
password,
|
|
4626
|
+
setPassword,
|
|
4627
|
+
passwordError,
|
|
4628
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
4629
|
+
confirm,
|
|
4630
|
+
setConfirm,
|
|
4631
|
+
confirmError,
|
|
4632
|
+
markConfirmTouched: () => setTouchedConfirm(true),
|
|
4633
|
+
formSubmitAttempted,
|
|
4634
|
+
submit,
|
|
4635
|
+
submitting,
|
|
4636
|
+
canSubmit,
|
|
4637
|
+
done,
|
|
4638
|
+
error
|
|
4639
|
+
};
|
|
4589
4640
|
}
|
|
4590
4641
|
|
|
4591
|
-
// src/
|
|
4592
|
-
import {
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
}
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4642
|
+
// src/hooks/useAuthPrimitives.ts
|
|
4643
|
+
import { useEffect as useEffect16 } from "react";
|
|
4644
|
+
import { useHook as useHook18 } from "@hook-sdk/sdk";
|
|
4645
|
+
var warned = false;
|
|
4646
|
+
function useAuthPrimitives() {
|
|
4647
|
+
const { auth } = useHook18();
|
|
4648
|
+
useEffect16(() => {
|
|
4649
|
+
if (!warned && process.env.NODE_ENV !== "production") {
|
|
4650
|
+
warned = true;
|
|
4651
|
+
console.warn(
|
|
4652
|
+
"[@hook-sdk/template] useAuthPrimitives() \xE9 escape hatch. Pra login/signup/forgot, use useLoginForm/useSignupForm/useForgotForm. Docs: docs/19-golden-template.md#escape-hatch"
|
|
4653
|
+
);
|
|
4654
|
+
}
|
|
4655
|
+
}, []);
|
|
4656
|
+
return {
|
|
4657
|
+
login: auth.login,
|
|
4658
|
+
signup: auth.signup,
|
|
4659
|
+
logout: auth.logout,
|
|
4660
|
+
logoutAll: auth.logoutAll,
|
|
4661
|
+
forgot: auth.forgot,
|
|
4662
|
+
resendVerify: auth.resendVerify,
|
|
4663
|
+
changePassword: auth.changePassword,
|
|
4664
|
+
changeEmail: auth.changeEmail,
|
|
4665
|
+
refresh: auth.refresh
|
|
4666
|
+
};
|
|
4612
4667
|
}
|
|
4613
4668
|
|
|
4614
|
-
// src/
|
|
4615
|
-
import {
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
itemClassName,
|
|
4624
|
-
renderItem
|
|
4625
|
-
}) {
|
|
4626
|
-
return /* @__PURE__ */ jsx44("div", { className, children: /* @__PURE__ */ jsxs26("div", { className: [DEFAULT_CARD_CLASSES, cardClassName].filter(Boolean).join(" "), children: [
|
|
4627
|
-
title ? /* @__PURE__ */ jsx44("div", { className: ["font-semibold mb-2", titleClassName].filter(Boolean).join(" "), children: title }) : null,
|
|
4628
|
-
/* @__PURE__ */ jsx44("ul", { children: items.map(
|
|
4629
|
-
(item, idx) => renderItem ? /* @__PURE__ */ jsx44("li", { children: renderItem(item, idx) }, idx) : /* @__PURE__ */ jsxs26("li", { className: itemClassName, children: [
|
|
4630
|
-
/* @__PURE__ */ jsx44("span", { "aria-hidden": "true", children: "\u2022" }),
|
|
4631
|
-
" ",
|
|
4632
|
-
/* @__PURE__ */ jsx44("span", { children: item })
|
|
4633
|
-
] }, idx)
|
|
4634
|
-
) })
|
|
4635
|
-
] }) });
|
|
4669
|
+
// src/hooks/useAuth.ts
|
|
4670
|
+
import { useHook as useHook19 } from "@hook-sdk/sdk";
|
|
4671
|
+
function useAuth() {
|
|
4672
|
+
const { user, authStatus, auth } = useHook19();
|
|
4673
|
+
return {
|
|
4674
|
+
user,
|
|
4675
|
+
authStatus,
|
|
4676
|
+
refresh: auth.refresh
|
|
4677
|
+
};
|
|
4636
4678
|
}
|
|
4637
4679
|
|
|
4638
|
-
// src/
|
|
4639
|
-
import {
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
}
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
]
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4680
|
+
// src/index.ts
|
|
4681
|
+
import { useTrackOnboardingStep } from "@hook-sdk/sdk";
|
|
4682
|
+
|
|
4683
|
+
// src/hooks/useSubscription.ts
|
|
4684
|
+
import { useHook as useHook20 } from "@hook-sdk/sdk";
|
|
4685
|
+
function useSubscription() {
|
|
4686
|
+
const { subscription } = useHook20();
|
|
4687
|
+
return {
|
|
4688
|
+
status: subscription.status()
|
|
4689
|
+
};
|
|
4690
|
+
}
|
|
4691
|
+
|
|
4692
|
+
// src/hooks/useReminders.ts
|
|
4693
|
+
import { useCallback as useCallback11, useEffect as useEffect17, useState as useState17 } from "react";
|
|
4694
|
+
import { useHook as useHook21 } from "@hook-sdk/sdk";
|
|
4695
|
+
function useReminders() {
|
|
4696
|
+
const { push } = useHook21();
|
|
4697
|
+
const r = push.reminders;
|
|
4698
|
+
const [reminders, setReminders] = useState17([]);
|
|
4699
|
+
const [loading, setLoading] = useState17(true);
|
|
4700
|
+
const reload = useCallback11(async () => {
|
|
4701
|
+
setLoading(true);
|
|
4702
|
+
try {
|
|
4703
|
+
const next = await r.list();
|
|
4704
|
+
setReminders(next);
|
|
4705
|
+
} finally {
|
|
4706
|
+
setLoading(false);
|
|
4707
|
+
}
|
|
4708
|
+
}, [r]);
|
|
4709
|
+
useEffect17(() => {
|
|
4710
|
+
void reload();
|
|
4711
|
+
}, [reload]);
|
|
4712
|
+
const setReminder = useCallback11(async (input) => {
|
|
4713
|
+
await r.set(input);
|
|
4714
|
+
await reload();
|
|
4715
|
+
}, [r, reload]);
|
|
4716
|
+
const deleteReminder = useCallback11(async (slot) => {
|
|
4717
|
+
await r.delete(slot);
|
|
4718
|
+
await reload();
|
|
4719
|
+
}, [r, reload]);
|
|
4720
|
+
const schedule = useCallback11(async (items) => {
|
|
4721
|
+
return r.schedule(items);
|
|
4722
|
+
}, [r]);
|
|
4723
|
+
const setFallbacks = useCallback11(async (items) => {
|
|
4724
|
+
return r.setFallbacks(items);
|
|
4725
|
+
}, [r]);
|
|
4726
|
+
return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
|
|
4661
4727
|
}
|
|
4662
4728
|
|
|
4663
|
-
// src/
|
|
4664
|
-
import {
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
}
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
return /* @__PURE__ */ jsx46("span", { className: className || void 0, children: render({ anchorCents: anchorPriceCents, formatted }) });
|
|
4679
|
-
}
|
|
4680
|
-
return /* @__PURE__ */ jsx46("span", { className: rootClasses, children: formatted });
|
|
4729
|
+
// src/hooks/useToast.ts
|
|
4730
|
+
import { useCallback as useCallback12, useState as useState18 } from "react";
|
|
4731
|
+
function useToast() {
|
|
4732
|
+
const [items, setItems] = useState18([]);
|
|
4733
|
+
const show = useCallback12((message, kind = "info") => {
|
|
4734
|
+
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4735
|
+
setItems((prev) => [...prev, { id, message, kind }]);
|
|
4736
|
+
setTimeout(() => {
|
|
4737
|
+
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
4738
|
+
}, 4e3);
|
|
4739
|
+
}, []);
|
|
4740
|
+
const dismiss = useCallback12((id) => {
|
|
4741
|
+
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
4742
|
+
}, []);
|
|
4743
|
+
return { items, show, dismiss };
|
|
4681
4744
|
}
|
|
4682
4745
|
|
|
4683
|
-
// src/
|
|
4684
|
-
import {
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
function clampStars(n) {
|
|
4692
|
-
return Math.max(0, Math.min(5, Math.round(n)));
|
|
4746
|
+
// src/RouteBoundary.tsx
|
|
4747
|
+
import { Routes as Routes2, Route as Route2 } from "react-router-dom";
|
|
4748
|
+
import { jsx as jsx50, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
4749
|
+
function RouteBoundary({ children }) {
|
|
4750
|
+
return /* @__PURE__ */ jsxs31(Routes2, { children: [
|
|
4751
|
+
children,
|
|
4752
|
+
/* @__PURE__ */ jsx50(Route2, { path: "*", element: /* @__PURE__ */ jsx50(DefaultNotFound, {}) })
|
|
4753
|
+
] });
|
|
4693
4754
|
}
|
|
4694
|
-
function
|
|
4695
|
-
|
|
4696
|
-
className,
|
|
4697
|
-
cardClassName,
|
|
4698
|
-
avatarClassName,
|
|
4699
|
-
quoteClassName,
|
|
4700
|
-
nameClassName,
|
|
4701
|
-
starsClassName,
|
|
4702
|
-
renderItem
|
|
4703
|
-
}) {
|
|
4704
|
-
const rootClasses = [DEFAULT_ROOT, className].filter(Boolean).join(" ");
|
|
4705
|
-
const cardClasses = [DEFAULT_CARD, cardClassName].filter(Boolean).join(" ");
|
|
4706
|
-
const avatarClasses = [DEFAULT_AVATAR, avatarClassName].filter(Boolean).join(" ");
|
|
4707
|
-
const quoteClasses = [DEFAULT_QUOTE, quoteClassName].filter(Boolean).join(" ");
|
|
4708
|
-
const nameClasses = [DEFAULT_NAME, nameClassName].filter(Boolean).join(" ");
|
|
4709
|
-
const starsClasses = [DEFAULT_STARS, starsClassName].filter(Boolean).join(" ");
|
|
4710
|
-
return /* @__PURE__ */ jsx47("div", { className: rootClasses, children: items.map((item, idx) => {
|
|
4711
|
-
if (renderItem) return renderItem(item, idx);
|
|
4712
|
-
const filled = clampStars(item.stars);
|
|
4713
|
-
const empty = 5 - filled;
|
|
4714
|
-
return /* @__PURE__ */ jsxs28("div", { className: cardClasses, children: [
|
|
4715
|
-
/* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-2", children: [
|
|
4716
|
-
item.avatar ? /* @__PURE__ */ jsx47(
|
|
4717
|
-
"img",
|
|
4718
|
-
{
|
|
4719
|
-
src: item.avatar,
|
|
4720
|
-
alt: "",
|
|
4721
|
-
loading: "lazy",
|
|
4722
|
-
className: avatarClasses,
|
|
4723
|
-
"aria-hidden": "true"
|
|
4724
|
-
}
|
|
4725
|
-
) : null,
|
|
4726
|
-
/* @__PURE__ */ jsx47("div", { className: nameClasses, children: item.name })
|
|
4727
|
-
] }),
|
|
4728
|
-
/* @__PURE__ */ jsxs28("div", { className: starsClasses, "aria-label": `${filled} de 5 estrelas`, children: [
|
|
4729
|
-
"\u2605".repeat(filled),
|
|
4730
|
-
"\u2606".repeat(empty)
|
|
4731
|
-
] }),
|
|
4732
|
-
/* @__PURE__ */ jsx47("p", { className: quoteClasses, children: item.quote })
|
|
4733
|
-
] }, idx);
|
|
4734
|
-
}) });
|
|
4755
|
+
function DefaultNotFound() {
|
|
4756
|
+
return /* @__PURE__ */ jsx50("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
|
|
4735
4757
|
}
|
|
4736
4758
|
|
|
4737
|
-
// src/
|
|
4738
|
-
import {
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
className,
|
|
4746
|
-
cellClassName,
|
|
4747
|
-
valueClassName,
|
|
4748
|
-
labelClassName,
|
|
4749
|
-
renderCell
|
|
4759
|
+
// src/PreAuthShell.tsx
|
|
4760
|
+
import { BrowserRouter as BrowserRouter2, MemoryRouter as MemoryRouter2, Routes as Routes3 } from "react-router-dom";
|
|
4761
|
+
import { jsx as jsx51 } from "react/jsx-runtime";
|
|
4762
|
+
function PreAuthShell({
|
|
4763
|
+
basename,
|
|
4764
|
+
testRouter,
|
|
4765
|
+
testInitialEntries,
|
|
4766
|
+
children
|
|
4750
4767
|
}) {
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
return /* @__PURE__ */ jsx48("div", { className: rootClasses, children: stats.map((stat, idx) => {
|
|
4756
|
-
if (renderCell) return renderCell(stat, idx);
|
|
4757
|
-
return /* @__PURE__ */ jsxs29("div", { className: cellClasses, children: [
|
|
4758
|
-
stat.icon ? /* @__PURE__ */ jsx48("div", { "aria-hidden": "true", children: stat.icon }) : null,
|
|
4759
|
-
/* @__PURE__ */ jsx48("div", { className: valueClasses, children: stat.value }),
|
|
4760
|
-
/* @__PURE__ */ jsx48("div", { className: labelClasses, children: stat.label })
|
|
4761
|
-
] }, idx);
|
|
4762
|
-
}) });
|
|
4768
|
+
if (testRouter === "memory") {
|
|
4769
|
+
return /* @__PURE__ */ jsx51(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx51(Routes3, { children }) });
|
|
4770
|
+
}
|
|
4771
|
+
return /* @__PURE__ */ jsx51(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx51(Routes3, { children }) });
|
|
4763
4772
|
}
|
|
4764
4773
|
|
|
4765
|
-
// src/
|
|
4766
|
-
import {
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
function
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
currentPriceCents,
|
|
4779
|
-
cycle,
|
|
4780
|
-
trialDaysCard,
|
|
4781
|
-
trialDaysPix,
|
|
4782
|
-
selectedMethod
|
|
4783
|
-
} = usePaywallContext();
|
|
4784
|
-
const trialDays = selectedMethod === "card" ? trialDaysCard : trialDaysPix;
|
|
4785
|
-
const cycleLabel = CYCLE_LABEL2[cycle] ?? cycle.toLowerCase();
|
|
4786
|
-
const priceFormatted = formatBRL(currentPriceCents ?? 0);
|
|
4787
|
-
const rootClasses = [DEFAULT_CLASS3, className].filter(Boolean).join(" ");
|
|
4788
|
-
if (render) {
|
|
4789
|
-
return /* @__PURE__ */ jsx49("p", { className: className || void 0, children: render({
|
|
4790
|
-
currentPriceCents: currentPriceCents ?? 0,
|
|
4791
|
-
cycle,
|
|
4792
|
-
trialDays: trialDays ?? 0,
|
|
4793
|
-
selectedMethod
|
|
4794
|
-
}) });
|
|
4774
|
+
// src/OnboardingFlow.tsx
|
|
4775
|
+
import { useCallback as useCallback13, useEffect as useEffect18, useMemo as useMemo12, useRef as useRef8 } from "react";
|
|
4776
|
+
import { usePersistedState as usePersistedState4, useHook as useHook22 } from "@hook-sdk/sdk";
|
|
4777
|
+
|
|
4778
|
+
// src/hooks/useOnboardingStep.ts
|
|
4779
|
+
import { createContext as createContext4, useContext as useContext5 } from "react";
|
|
4780
|
+
var OnboardingStepContext = createContext4(null);
|
|
4781
|
+
function useOnboardingStep() {
|
|
4782
|
+
const ctx = useContext5(OnboardingStepContext);
|
|
4783
|
+
if (!ctx) {
|
|
4784
|
+
throw new Error(
|
|
4785
|
+
"[hook-template] useOnboardingStep must be used inside <OnboardingFlow>. (G75)"
|
|
4786
|
+
);
|
|
4795
4787
|
}
|
|
4796
|
-
|
|
4797
|
-
return /* @__PURE__ */ jsx49("p", { className: rootClasses, children: text });
|
|
4788
|
+
return ctx;
|
|
4798
4789
|
}
|
|
4799
4790
|
|
|
4800
|
-
// src/
|
|
4801
|
-
import { jsx as
|
|
4802
|
-
var
|
|
4803
|
-
var
|
|
4804
|
-
function
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4791
|
+
// src/OnboardingFlow.tsx
|
|
4792
|
+
import { jsx as jsx52 } from "react/jsx-runtime";
|
|
4793
|
+
var isFilled = (v) => v != null && v !== "";
|
|
4794
|
+
var CURRENT_STEP_FIELD = "currentStep";
|
|
4795
|
+
function readPersistedStepIdx(draft) {
|
|
4796
|
+
const raw = draft[CURRENT_STEP_FIELD];
|
|
4797
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : 0;
|
|
4798
|
+
}
|
|
4799
|
+
function OnboardingFlow({
|
|
4800
|
+
steps,
|
|
4801
|
+
screens,
|
|
4802
|
+
onComplete,
|
|
4803
|
+
persistKey
|
|
4809
4804
|
}) {
|
|
4810
|
-
const
|
|
4811
|
-
const
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4805
|
+
const [draft, setDraft, status] = usePersistedState4(persistKey, {});
|
|
4806
|
+
const draftRef = useRef8(draft);
|
|
4807
|
+
draftRef.current = draft;
|
|
4808
|
+
const idx = readPersistedStepIdx(draft);
|
|
4809
|
+
const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
|
|
4810
|
+
const setIdx = useCallback13(
|
|
4811
|
+
(n) => {
|
|
4812
|
+
setDraft((prev) => {
|
|
4813
|
+
const prevIdx = readPersistedStepIdx(prev);
|
|
4814
|
+
const nextIdx = typeof n === "function" ? n(prevIdx) : n;
|
|
4815
|
+
return { ...prev, [CURRENT_STEP_FIELD]: nextIdx };
|
|
4816
|
+
});
|
|
4817
|
+
},
|
|
4818
|
+
[setDraft]
|
|
4819
|
+
);
|
|
4820
|
+
const setValue = useCallback13(
|
|
4821
|
+
(patch) => {
|
|
4822
|
+
draftRef.current = { ...draftRef.current, ...patch };
|
|
4823
|
+
setDraft((prev) => ({ ...prev, ...patch }));
|
|
4824
|
+
},
|
|
4825
|
+
[setDraft]
|
|
4826
|
+
);
|
|
4827
|
+
const step = steps[clampedIdx];
|
|
4828
|
+
const hookCtx = useHook22();
|
|
4829
|
+
const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
|
|
4830
|
+
useEffect18(() => {
|
|
4831
|
+
if (status.loading) return;
|
|
4832
|
+
if (!step) return;
|
|
4833
|
+
if (!track2) return;
|
|
4834
|
+
track2("onboarding_step_viewed", {
|
|
4835
|
+
step: step.id,
|
|
4836
|
+
step_index: clampedIdx,
|
|
4837
|
+
total_steps: steps.length
|
|
4838
|
+
});
|
|
4839
|
+
}, [step?.id, clampedIdx, steps.length, status.loading, track2]);
|
|
4840
|
+
const valid = useMemo12(
|
|
4841
|
+
() => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
|
|
4842
|
+
[draft, step]
|
|
4843
|
+
);
|
|
4844
|
+
const next = useCallback13(() => {
|
|
4845
|
+
if (!step) return;
|
|
4846
|
+
const current = draftRef.current;
|
|
4847
|
+
const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
|
|
4848
|
+
if (!validNow) return;
|
|
4849
|
+
if (clampedIdx + 1 >= steps.length) {
|
|
4850
|
+
onComplete(current);
|
|
4851
|
+
} else {
|
|
4852
|
+
setIdx(clampedIdx + 1);
|
|
4853
|
+
}
|
|
4854
|
+
}, [clampedIdx, onComplete, step, steps.length, setIdx]);
|
|
4855
|
+
const prevStep = useCallback13(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
|
|
4856
|
+
const ctx = useMemo12(
|
|
4857
|
+
() => ({
|
|
4858
|
+
stepIndex: clampedIdx,
|
|
4859
|
+
totalSteps: steps.length,
|
|
4860
|
+
value: draft,
|
|
4861
|
+
setValue,
|
|
4862
|
+
valid,
|
|
4863
|
+
next,
|
|
4864
|
+
prev: prevStep
|
|
4865
|
+
}),
|
|
4866
|
+
[clampedIdx, steps.length, draft, setValue, valid, next, prevStep]
|
|
4867
|
+
);
|
|
4868
|
+
if (status.loading) {
|
|
4869
|
+
return null;
|
|
4870
|
+
}
|
|
4871
|
+
if (!step) {
|
|
4872
|
+
throw new Error(
|
|
4873
|
+
`[hook-template] OnboardingFlow: step index ${clampedIdx} out of range (steps.length=${steps.length})`
|
|
4874
|
+
);
|
|
4875
|
+
}
|
|
4876
|
+
const Screen = screens[step.screen];
|
|
4877
|
+
if (!Screen) {
|
|
4878
|
+
throw new Error(
|
|
4879
|
+
`[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
|
|
4880
|
+
);
|
|
4881
|
+
}
|
|
4882
|
+
return /* @__PURE__ */ jsx52(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx52(Screen, {}) });
|
|
4819
4883
|
}
|
|
4820
4884
|
|
|
4821
|
-
// src/
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
function PaywallStickyFooter({
|
|
4826
|
-
children,
|
|
4827
|
-
className,
|
|
4828
|
-
safeAreaInsets = true
|
|
4829
|
-
}) {
|
|
4830
|
-
const classes = [DEFAULT_CLASSES, safeAreaInsets ? SAFE_AREA_CLASS : null, className].filter(Boolean).join(" ");
|
|
4831
|
-
return /* @__PURE__ */ jsx51("div", { className: classes, children });
|
|
4885
|
+
// src/hooks/useFeature.ts
|
|
4886
|
+
function useFeature(name) {
|
|
4887
|
+
const config = useAppConfig();
|
|
4888
|
+
return Array.isArray(config.features_enabled) && config.features_enabled.includes(name);
|
|
4832
4889
|
}
|
|
4833
4890
|
export {
|
|
4834
4891
|
AppConfigProvider,
|