@hook-sdk/template 0.18.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +348 -233
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +78 -11
- package/dist/index.d.ts +78 -11
- package/dist/index.js +224 -121
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -74,6 +74,17 @@ var DeepLinksSchema = z.object({
|
|
|
74
74
|
passwordReset: z.string().startsWith("/").optional(),
|
|
75
75
|
emailVerify: z.string().startsWith("/").optional()
|
|
76
76
|
}).strict();
|
|
77
|
+
var I18nConfigSchema = z.object({
|
|
78
|
+
defaultLocale: z.string().min(2),
|
|
79
|
+
supportedLocales: z.array(z.string().min(2)).min(1),
|
|
80
|
+
resources: z.record(z.string(), z.record(z.string(), z.string()))
|
|
81
|
+
}).strict().refine((v) => v.supportedLocales.includes(v.defaultLocale), {
|
|
82
|
+
message: "i18n.defaultLocale must be a member of i18n.supportedLocales",
|
|
83
|
+
path: ["defaultLocale"]
|
|
84
|
+
});
|
|
85
|
+
var InstallPromptSchema = z.object({
|
|
86
|
+
position: z.enum(["pre-auth", "post-paywall"]).optional()
|
|
87
|
+
}).strict();
|
|
77
88
|
var AppConfigSchema = z.object({
|
|
78
89
|
slug: z.string().regex(/^[a-z0-9-]+$/),
|
|
79
90
|
name: z.string().min(1),
|
|
@@ -87,12 +98,18 @@ var AppConfigSchema = z.object({
|
|
|
87
98
|
persistedKeys: z.array(PersistedKeySchema),
|
|
88
99
|
onboarding: OnboardingSchema.optional(),
|
|
89
100
|
deepLinks: DeepLinksSchema.optional(),
|
|
101
|
+
i18n: I18nConfigSchema.optional(),
|
|
90
102
|
features_enabled: z.array(z.string()).optional(),
|
|
103
|
+
install_prompt: InstallPromptSchema.optional(),
|
|
91
104
|
// Build-time injected theme metadata (e.g. icon_url for InstallSplash).
|
|
92
105
|
// Apps don't author this directly; deploy workflows fill it from
|
|
93
106
|
// env-resolved bundle host. Permissive shape so apps/workflows can
|
|
94
107
|
// extend without re-bumping the template schema.
|
|
95
|
-
theme: z.object({}).passthrough().optional()
|
|
108
|
+
theme: z.object({}).passthrough().optional(),
|
|
109
|
+
// G133 — per-tenant public app-data keys allowlist. Optional; default
|
|
110
|
+
// backfill em migration 0044 = canonical 6. Apps com keys próprias
|
|
111
|
+
// declaram aqui pra evitar 402 spam pós-signup no SubscriptionGate.
|
|
112
|
+
publicKeys: z.array(z.string().regex(SnakeKeyRE)).optional()
|
|
96
113
|
}).strict();
|
|
97
114
|
function parseAppConfig(input) {
|
|
98
115
|
const r = AppConfigSchema.safeParse(input);
|
|
@@ -252,7 +269,7 @@ var FALLBACK_PAYWALL = {
|
|
|
252
269
|
};
|
|
253
270
|
var isMethodAvailable = (availability, method) => availability[method] !== false;
|
|
254
271
|
function usePaywallState() {
|
|
255
|
-
const { subscription, plan } = useHook3();
|
|
272
|
+
const { subscription, plan, authStatus, track: track2 } = useHook3();
|
|
256
273
|
const configFromCtx = useContext3(AppConfigContext);
|
|
257
274
|
const paywall = configFromCtx?.paywall ?? FALLBACK_PAYWALL;
|
|
258
275
|
const isFree = paywall.mode === "free";
|
|
@@ -341,6 +358,21 @@ function usePaywallState() {
|
|
|
341
358
|
[useDefaultMessages]
|
|
342
359
|
);
|
|
343
360
|
const submit = useCallback(async () => {
|
|
361
|
+
if (authStatus === "loading") return void 0;
|
|
362
|
+
if (authStatus !== "authenticated") {
|
|
363
|
+
track2("unauthenticated_submit_attempted", {
|
|
364
|
+
method: selectedMethod,
|
|
365
|
+
cycle,
|
|
366
|
+
cpf_valid: cpfValid
|
|
367
|
+
});
|
|
368
|
+
return void 0;
|
|
369
|
+
}
|
|
370
|
+
track2("payment_attempted", {
|
|
371
|
+
method: selectedMethod,
|
|
372
|
+
cycle,
|
|
373
|
+
cpf_valid: cpfValid,
|
|
374
|
+
selected_amount_cents: cycle === "YEARLY" ? plan.data?.yearlyPriceCents ?? (isFree ? 0 : paywall.prices.yearlyCents) : plan.data?.priceCents ?? (isFree ? 0 : paywall.prices.monthlyCents)
|
|
375
|
+
});
|
|
344
376
|
setSubmitting(true);
|
|
345
377
|
setError(null);
|
|
346
378
|
const methodToUse = selectedMethod;
|
|
@@ -401,7 +433,7 @@ function usePaywallState() {
|
|
|
401
433
|
setSubmitting(false);
|
|
402
434
|
return void 0;
|
|
403
435
|
}
|
|
404
|
-
}, [selectedMethod, availability, subscription, cycle, cpf, card, buildError]);
|
|
436
|
+
}, [authStatus, track2, selectedMethod, availability, subscription, cycle, cpf, cpfValid, card, buildError, plan, paywall]);
|
|
405
437
|
const checkout = useCallback(
|
|
406
438
|
async (args) => {
|
|
407
439
|
setSubmitting(true);
|
|
@@ -1811,9 +1843,9 @@ var bannerStyle = {
|
|
|
1811
1843
|
|
|
1812
1844
|
// src/components/InstallGate/InstallGate.tsx
|
|
1813
1845
|
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1814
|
-
function InstallGate({ children }) {
|
|
1846
|
+
function InstallGate({ children, position }) {
|
|
1815
1847
|
const { slug, features_enabled } = useTemplateConfig();
|
|
1816
|
-
const enabled = features_enabled.includes("
|
|
1848
|
+
const enabled = features_enabled.includes("pwa-install");
|
|
1817
1849
|
const installState = useInstallPrompt(slug);
|
|
1818
1850
|
const shouldBlock = enabled && shouldBlockInstall(installState);
|
|
1819
1851
|
const trackedRef = useRef2(null);
|
|
@@ -1828,9 +1860,10 @@ function InstallGate({ children }) {
|
|
|
1828
1860
|
platform: installState.platform,
|
|
1829
1861
|
browser: installState.iosBrowser ?? installState.androidBrowser ?? null,
|
|
1830
1862
|
in_app_app: installState.inAppApp,
|
|
1831
|
-
variant: installState.variant
|
|
1863
|
+
variant: installState.variant,
|
|
1864
|
+
...position !== void 0 ? { position } : {}
|
|
1832
1865
|
});
|
|
1833
|
-
}, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp]);
|
|
1866
|
+
}, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp, position]);
|
|
1834
1867
|
if (!enabled) return /* @__PURE__ */ jsx16(Fragment3, { children });
|
|
1835
1868
|
if (installState.isInstalled) return /* @__PURE__ */ jsx16(Fragment3, { children });
|
|
1836
1869
|
if (installState.variant === "desktop") {
|
|
@@ -1953,23 +1986,66 @@ var ErrorBoundary = class extends Component {
|
|
|
1953
1986
|
}
|
|
1954
1987
|
};
|
|
1955
1988
|
|
|
1989
|
+
// src/i18n/I18nProvider.tsx
|
|
1990
|
+
import { useEffect as useEffect7 } from "react";
|
|
1991
|
+
import i18n from "i18next";
|
|
1992
|
+
import { I18nextProvider, initReactI18next } from "react-i18next";
|
|
1993
|
+
import { usePersistedState } from "@hook-sdk/sdk";
|
|
1994
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
1995
|
+
function ensureInitialized(defaultLocale, supportedLocales, resources, initialLocale) {
|
|
1996
|
+
if (i18n.isInitialized) return;
|
|
1997
|
+
i18n.use(initReactI18next).init({
|
|
1998
|
+
resources: Object.fromEntries(
|
|
1999
|
+
supportedLocales.map((l) => [l, { translation: resources[l] ?? {} }])
|
|
2000
|
+
),
|
|
2001
|
+
lng: initialLocale,
|
|
2002
|
+
fallbackLng: defaultLocale,
|
|
2003
|
+
interpolation: { escapeValue: false },
|
|
2004
|
+
// useTranslation suspends by default until i18next is "ready". Inline
|
|
2005
|
+
// resources are sync, so suspending creates a guaranteed empty render
|
|
2006
|
+
// tick — confusing in apps and breaks tests that don't use Suspense.
|
|
2007
|
+
react: { useSuspense: false }
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
function I18nProvider({
|
|
2011
|
+
defaultLocale,
|
|
2012
|
+
supportedLocales,
|
|
2013
|
+
resources,
|
|
2014
|
+
children
|
|
2015
|
+
}) {
|
|
2016
|
+
const [userLocale] = usePersistedState("user-locale", defaultLocale);
|
|
2017
|
+
ensureInitialized(defaultLocale, supportedLocales, resources, userLocale);
|
|
2018
|
+
useEffect7(() => {
|
|
2019
|
+
if (i18n.isInitialized && i18n.language !== userLocale) {
|
|
2020
|
+
i18n.changeLanguage(userLocale);
|
|
2021
|
+
}
|
|
2022
|
+
}, [userLocale]);
|
|
2023
|
+
return /* @__PURE__ */ jsx19(I18nextProvider, { i18n, children });
|
|
2024
|
+
}
|
|
2025
|
+
|
|
1956
2026
|
// src/internal/PaymentReturnHandler.tsx
|
|
1957
|
-
import { useCallback as useCallback3, useEffect as
|
|
2027
|
+
import { useCallback as useCallback3, useEffect as useEffect8, useRef as useRef4, useState as useState5 } from "react";
|
|
1958
2028
|
import { useHook as useHook5 } from "@hook-sdk/sdk";
|
|
1959
|
-
import { Fragment as Fragment5, jsx as
|
|
2029
|
+
import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1960
2030
|
var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
|
|
1961
2031
|
var MAX_CYCLES = 3;
|
|
1962
2032
|
var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
|
|
1963
2033
|
function PaymentReturnHandler({ children }) {
|
|
1964
|
-
const { subscription } = useHook5();
|
|
2034
|
+
const { subscription, track: track2 } = useHook5();
|
|
1965
2035
|
const subRef = useRef4(subscription);
|
|
1966
2036
|
subRef.current = subscription;
|
|
1967
2037
|
const runIdRef = useRef4(0);
|
|
1968
2038
|
const cyclesRef = useRef4(0);
|
|
2039
|
+
const startMsRef = useRef4(0);
|
|
1969
2040
|
const [state, setState] = useState5("idle");
|
|
1970
2041
|
const runPoll = useCallback3(() => {
|
|
1971
2042
|
const runId = ++runIdRef.current;
|
|
2043
|
+
const isFirstRun = cyclesRef.current === 0;
|
|
1972
2044
|
cyclesRef.current += 1;
|
|
2045
|
+
if (isFirstRun) {
|
|
2046
|
+
startMsRef.current = Date.now();
|
|
2047
|
+
track2("payment_confirmation_started", {});
|
|
2048
|
+
}
|
|
1973
2049
|
setState("confirming");
|
|
1974
2050
|
let attempts = 0;
|
|
1975
2051
|
const tick = async () => {
|
|
@@ -1982,6 +2058,11 @@ function PaymentReturnHandler({ children }) {
|
|
|
1982
2058
|
if (runIdRef.current !== runId) return;
|
|
1983
2059
|
const status = subRef.current.status();
|
|
1984
2060
|
if (status === "active" || status === "trialing") {
|
|
2061
|
+
track2("payment_confirmation_succeeded", {
|
|
2062
|
+
cycle_count: cyclesRef.current,
|
|
2063
|
+
attempt_count: attempts,
|
|
2064
|
+
duration_ms: Date.now() - startMsRef.current
|
|
2065
|
+
});
|
|
1985
2066
|
const cleanUrl = new URL(window.location.href);
|
|
1986
2067
|
cleanUrl.searchParams.delete("paymentReturn");
|
|
1987
2068
|
window.history.replaceState({}, "", cleanUrl.toString());
|
|
@@ -1992,6 +2073,9 @@ function PaymentReturnHandler({ children }) {
|
|
|
1992
2073
|
const delay = BACKOFF_MS[attempts - 1];
|
|
1993
2074
|
if (delay === void 0) {
|
|
1994
2075
|
if (cyclesRef.current >= MAX_CYCLES) {
|
|
2076
|
+
track2("payment_confirmation_timed_out", {
|
|
2077
|
+
total_duration_ms: Date.now() - startMsRef.current
|
|
2078
|
+
});
|
|
1995
2079
|
setState("timeout");
|
|
1996
2080
|
} else {
|
|
1997
2081
|
setState("waiting");
|
|
@@ -2001,8 +2085,8 @@ function PaymentReturnHandler({ children }) {
|
|
|
2001
2085
|
setTimeout(tick, delay);
|
|
2002
2086
|
};
|
|
2003
2087
|
void tick();
|
|
2004
|
-
}, []);
|
|
2005
|
-
|
|
2088
|
+
}, [track2]);
|
|
2089
|
+
useEffect8(() => {
|
|
2006
2090
|
if (typeof window === "undefined") return;
|
|
2007
2091
|
const url = new URL(window.location.href);
|
|
2008
2092
|
if (url.searchParams.get("paymentReturn") !== "1") return;
|
|
@@ -2019,19 +2103,19 @@ function PaymentReturnHandler({ children }) {
|
|
|
2019
2103
|
window.location.href = cleanUrl.toString();
|
|
2020
2104
|
}, []);
|
|
2021
2105
|
if (state === "confirming") {
|
|
2022
|
-
return /* @__PURE__ */
|
|
2106
|
+
return /* @__PURE__ */ jsx20("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
|
|
2023
2107
|
}
|
|
2024
2108
|
if (state === "waiting") {
|
|
2025
|
-
return /* @__PURE__ */
|
|
2026
|
-
/* @__PURE__ */
|
|
2027
|
-
/* @__PURE__ */
|
|
2109
|
+
return /* @__PURE__ */ jsx20("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
2110
|
+
/* @__PURE__ */ jsx20("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
|
|
2111
|
+
/* @__PURE__ */ jsx20("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
|
|
2028
2112
|
] }) });
|
|
2029
2113
|
}
|
|
2030
2114
|
if (state === "timeout") {
|
|
2031
|
-
return /* @__PURE__ */
|
|
2032
|
-
/* @__PURE__ */
|
|
2115
|
+
return /* @__PURE__ */ jsx20("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
2116
|
+
/* @__PURE__ */ jsx20("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." }),
|
|
2033
2117
|
/* @__PURE__ */ jsxs13("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
2034
|
-
/* @__PURE__ */
|
|
2118
|
+
/* @__PURE__ */ jsx20(
|
|
2035
2119
|
"button",
|
|
2036
2120
|
{
|
|
2037
2121
|
type: "button",
|
|
@@ -2044,7 +2128,7 @@ function PaymentReturnHandler({ children }) {
|
|
|
2044
2128
|
children: "Tentar de novo"
|
|
2045
2129
|
}
|
|
2046
2130
|
),
|
|
2047
|
-
/* @__PURE__ */
|
|
2131
|
+
/* @__PURE__ */ jsx20(
|
|
2048
2132
|
"button",
|
|
2049
2133
|
{
|
|
2050
2134
|
type: "button",
|
|
@@ -2054,7 +2138,7 @@ function PaymentReturnHandler({ children }) {
|
|
|
2054
2138
|
children: "Voltar pro app"
|
|
2055
2139
|
}
|
|
2056
2140
|
),
|
|
2057
|
-
/* @__PURE__ */
|
|
2141
|
+
/* @__PURE__ */ jsx20(
|
|
2058
2142
|
"a",
|
|
2059
2143
|
{
|
|
2060
2144
|
href: SUPPORT_MAILTO,
|
|
@@ -2066,7 +2150,7 @@ function PaymentReturnHandler({ children }) {
|
|
|
2066
2150
|
] })
|
|
2067
2151
|
] }) });
|
|
2068
2152
|
}
|
|
2069
|
-
return /* @__PURE__ */
|
|
2153
|
+
return /* @__PURE__ */ jsx20(Fragment5, { children });
|
|
2070
2154
|
}
|
|
2071
2155
|
var overlayStyle2 = {
|
|
2072
2156
|
position: "fixed",
|
|
@@ -2105,7 +2189,7 @@ var linkStyle = {
|
|
|
2105
2189
|
};
|
|
2106
2190
|
|
|
2107
2191
|
// src/AppRoot.tsx
|
|
2108
|
-
import { Fragment as Fragment6, jsx as
|
|
2192
|
+
import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2109
2193
|
function buildLegacyConfigShim(config) {
|
|
2110
2194
|
const paywall = config.paywall;
|
|
2111
2195
|
const isFree = paywall.mode === "free";
|
|
@@ -2182,28 +2266,43 @@ function AppRoot(props) {
|
|
|
2182
2266
|
const Router = testRouter === "memory" ? MemoryRouter : BrowserRouter;
|
|
2183
2267
|
const basename = `/app/${config.slug}`;
|
|
2184
2268
|
const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
/* @__PURE__ */
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2269
|
+
const position = config.install_prompt?.position ?? "post-paywall";
|
|
2270
|
+
const subscriptionGated = /* @__PURE__ */ jsx21(SubscriptionGate, { Paywall: Paywall ?? FallbackPaywall, children: position === "post-paywall" ? /* @__PURE__ */ jsxs14(InstallGate, { position: "post-paywall", children: [
|
|
2271
|
+
children,
|
|
2272
|
+
/* @__PURE__ */ jsx21(PushPrompt, {})
|
|
2273
|
+
] }) : /* @__PURE__ */ jsxs14(Fragment6, { children: [
|
|
2274
|
+
children,
|
|
2275
|
+
/* @__PURE__ */ jsx21(PushPrompt, {})
|
|
2276
|
+
] }) });
|
|
2277
|
+
const authGated = /* @__PURE__ */ jsx21(
|
|
2278
|
+
AuthGated,
|
|
2279
|
+
{
|
|
2280
|
+
config,
|
|
2281
|
+
Login,
|
|
2282
|
+
Signup,
|
|
2283
|
+
Forgot,
|
|
2284
|
+
Reset,
|
|
2285
|
+
EmailVerify,
|
|
2286
|
+
Paywall,
|
|
2287
|
+
Onboarding,
|
|
2288
|
+
PreAuthFlow,
|
|
2289
|
+
children: subscriptionGated
|
|
2290
|
+
}
|
|
2291
|
+
);
|
|
2292
|
+
const routedTree = /* @__PURE__ */ jsxs14(Router, { ...routerProps, children: [
|
|
2293
|
+
/* @__PURE__ */ jsx21(DeepLinkHandler, { deepLinks: config.deepLinks }),
|
|
2294
|
+
/* @__PURE__ */ jsx21(SessionExpiredBanner, {}),
|
|
2295
|
+
position === "pre-auth" ? /* @__PURE__ */ jsx21(InstallGate, { position: "pre-auth", children: authGated }) : authGated
|
|
2296
|
+
] });
|
|
2297
|
+
return /* @__PURE__ */ jsx21(ErrorBoundary, { children: /* @__PURE__ */ jsx21(AppConfigProvider, { config, children: /* @__PURE__ */ jsx21(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx21(ThemeProvider, { children: /* @__PURE__ */ jsx21(PersistenceRegistry, { config: config.persistedKeys, children: config.i18n ? /* @__PURE__ */ jsx21(
|
|
2298
|
+
I18nProvider,
|
|
2299
|
+
{
|
|
2300
|
+
defaultLocale: config.i18n.defaultLocale,
|
|
2301
|
+
supportedLocales: config.i18n.supportedLocales,
|
|
2302
|
+
resources: config.i18n.resources,
|
|
2303
|
+
children: routedTree
|
|
2304
|
+
}
|
|
2305
|
+
) : routedTree }) }) }) }) });
|
|
2207
2306
|
}
|
|
2208
2307
|
function AuthGated({
|
|
2209
2308
|
children,
|
|
@@ -2220,31 +2319,31 @@ function AuthGated({
|
|
|
2220
2319
|
if (authStatus !== "authenticated") {
|
|
2221
2320
|
if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
|
|
2222
2321
|
return /* @__PURE__ */ jsxs14(Routes, { children: [
|
|
2223
|
-
/* @__PURE__ */
|
|
2224
|
-
/* @__PURE__ */
|
|
2225
|
-
/* @__PURE__ */
|
|
2226
|
-
/* @__PURE__ */
|
|
2227
|
-
EmailVerify ? /* @__PURE__ */
|
|
2228
|
-
/* @__PURE__ */
|
|
2322
|
+
/* @__PURE__ */ jsx21(Route, { path: "/signin", element: /* @__PURE__ */ jsx21(Login, {}) }),
|
|
2323
|
+
/* @__PURE__ */ jsx21(Route, { path: "/signup", element: /* @__PURE__ */ jsx21(Signup, {}) }),
|
|
2324
|
+
/* @__PURE__ */ jsx21(Route, { path: "/forgot", element: /* @__PURE__ */ jsx21(Forgot, {}) }),
|
|
2325
|
+
/* @__PURE__ */ jsx21(Route, { path: "/reset", element: /* @__PURE__ */ jsx21(Reset, {}) }),
|
|
2326
|
+
EmailVerify ? /* @__PURE__ */ jsx21(Route, { path: "/verify", element: /* @__PURE__ */ jsx21(EmailVerify, {}) }) : null,
|
|
2327
|
+
/* @__PURE__ */ jsx21(Route, { path: "/*", element: /* @__PURE__ */ jsx21(PreAuthFlow, {}) })
|
|
2229
2328
|
] });
|
|
2230
2329
|
}
|
|
2231
2330
|
return /* @__PURE__ */ jsxs14(Routes, { children: [
|
|
2232
|
-
/* @__PURE__ */
|
|
2233
|
-
/* @__PURE__ */
|
|
2234
|
-
/* @__PURE__ */
|
|
2235
|
-
/* @__PURE__ */
|
|
2236
|
-
EmailVerify ? /* @__PURE__ */
|
|
2237
|
-
/* @__PURE__ */
|
|
2331
|
+
/* @__PURE__ */ jsx21(Route, { path: "/", element: /* @__PURE__ */ jsx21(Login, {}) }),
|
|
2332
|
+
/* @__PURE__ */ jsx21(Route, { path: "/signup", element: /* @__PURE__ */ jsx21(Signup, {}) }),
|
|
2333
|
+
/* @__PURE__ */ jsx21(Route, { path: "/forgot", element: /* @__PURE__ */ jsx21(Forgot, {}) }),
|
|
2334
|
+
/* @__PURE__ */ jsx21(Route, { path: "/reset", element: /* @__PURE__ */ jsx21(Reset, {}) }),
|
|
2335
|
+
EmailVerify ? /* @__PURE__ */ jsx21(Route, { path: "/verify", element: /* @__PURE__ */ jsx21(EmailVerify, {}) }) : null,
|
|
2336
|
+
/* @__PURE__ */ jsx21(Route, { path: "*", element: /* @__PURE__ */ jsx21(Navigate, { to: "/", replace: true }) })
|
|
2238
2337
|
] });
|
|
2239
2338
|
}
|
|
2240
|
-
return /* @__PURE__ */
|
|
2339
|
+
return /* @__PURE__ */ jsx21(Fragment6, { children });
|
|
2241
2340
|
}
|
|
2242
2341
|
function FallbackPaywall() {
|
|
2243
2342
|
return null;
|
|
2244
2343
|
}
|
|
2245
2344
|
|
|
2246
2345
|
// src/hooks/usePush.ts
|
|
2247
|
-
import { useCallback as useCallback4, useEffect as
|
|
2346
|
+
import { useCallback as useCallback4, useEffect as useEffect9, useState as useState6 } from "react";
|
|
2248
2347
|
import { useHook as useHook7 } from "@hook-sdk/sdk";
|
|
2249
2348
|
var DISMISS_STORAGE_KEY = "push:dismissed-until";
|
|
2250
2349
|
var DISMISS_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -2291,7 +2390,7 @@ function deriveState(push) {
|
|
|
2291
2390
|
function usePush() {
|
|
2292
2391
|
const { push } = useHook7();
|
|
2293
2392
|
const [state, setState] = useState6(() => deriveState(push));
|
|
2294
|
-
|
|
2393
|
+
useEffect9(() => {
|
|
2295
2394
|
setState(deriveState(push));
|
|
2296
2395
|
}, [push]);
|
|
2297
2396
|
const subscribe = useCallback4(async () => {
|
|
@@ -2328,51 +2427,27 @@ function usePush() {
|
|
|
2328
2427
|
}
|
|
2329
2428
|
|
|
2330
2429
|
// src/components/PushPrompt.tsx
|
|
2331
|
-
import { jsx as
|
|
2332
|
-
function platformRecoveryCopy(texts) {
|
|
2333
|
-
if (typeof navigator === "undefined") return null;
|
|
2334
|
-
const ua = navigator.userAgent || "";
|
|
2335
|
-
const platform = detectPlatform(ua);
|
|
2336
|
-
switch (platform) {
|
|
2337
|
-
case "ios-safari":
|
|
2338
|
-
case "ios-other":
|
|
2339
|
-
return texts.deniedRecoveryIos ?? null;
|
|
2340
|
-
case "android":
|
|
2341
|
-
return texts.deniedRecoveryAndroid ?? null;
|
|
2342
|
-
case "desktop":
|
|
2343
|
-
return texts.deniedRecoveryDesktop ?? null;
|
|
2344
|
-
case "in-app":
|
|
2345
|
-
return texts.deniedRecoveryInApp ?? null;
|
|
2346
|
-
default:
|
|
2347
|
-
return null;
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2430
|
+
import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2350
2431
|
function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
|
|
2351
2432
|
const { state, subscribe } = usePush();
|
|
2352
|
-
if (state.kind === "
|
|
2433
|
+
if (state.kind === "denied" || state.kind === "dismissed" || state.kind === "subscribed") {
|
|
2434
|
+
return null;
|
|
2435
|
+
}
|
|
2353
2436
|
if (state.kind === "ios_needs_install") {
|
|
2354
2437
|
return /* @__PURE__ */ jsxs15("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
|
|
2355
|
-
/* @__PURE__ */
|
|
2356
|
-
/* @__PURE__ */
|
|
2357
|
-
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */
|
|
2358
|
-
] });
|
|
2359
|
-
}
|
|
2360
|
-
if (state.kind === "denied") {
|
|
2361
|
-
const recovery = platformRecoveryCopy(texts);
|
|
2362
|
-
return /* @__PURE__ */ jsxs15("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
|
|
2363
|
-
/* @__PURE__ */ jsx21("h3", { children: texts.deniedTitle }),
|
|
2364
|
-
/* @__PURE__ */ jsx21("p", { children: texts.deniedBody }),
|
|
2365
|
-
recovery && /* @__PURE__ */ jsx21("p", { "data-testid": "denied-recovery", children: recovery })
|
|
2438
|
+
/* @__PURE__ */ jsx22("h3", { children: texts.iosInstallTitle }),
|
|
2439
|
+
/* @__PURE__ */ jsx22("p", { children: texts.iosInstallBody }),
|
|
2440
|
+
onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx22("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
|
|
2366
2441
|
] });
|
|
2367
2442
|
}
|
|
2368
2443
|
if (state.kind === "unsupported") {
|
|
2369
|
-
return /* @__PURE__ */
|
|
2444
|
+
return /* @__PURE__ */ jsx22("div", { className, role: "region", children: /* @__PURE__ */ jsx22("p", { children: texts.unsupportedBody }) });
|
|
2370
2445
|
}
|
|
2371
2446
|
if (state.kind === "error") {
|
|
2372
|
-
return /* @__PURE__ */
|
|
2447
|
+
return /* @__PURE__ */ jsx22("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx22("p", { children: state.message }) });
|
|
2373
2448
|
}
|
|
2374
2449
|
return /* @__PURE__ */ jsxs15("div", { className, role: "region", children: [
|
|
2375
|
-
/* @__PURE__ */
|
|
2450
|
+
/* @__PURE__ */ jsx22(
|
|
2376
2451
|
"button",
|
|
2377
2452
|
{
|
|
2378
2453
|
type: "button",
|
|
@@ -2386,23 +2461,49 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
|
|
|
2386
2461
|
children: texts.cta
|
|
2387
2462
|
}
|
|
2388
2463
|
),
|
|
2389
|
-
onDeclined && /* @__PURE__ */
|
|
2464
|
+
onDeclined && /* @__PURE__ */ jsx22("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
|
|
2465
|
+
] });
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// src/components/LanguageSwitcher.tsx
|
|
2469
|
+
import { usePersistedState as usePersistedState2 } from "@hook-sdk/sdk";
|
|
2470
|
+
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2471
|
+
function LanguageSwitcher({ id, className, label = "Language" }) {
|
|
2472
|
+
const config = useAppConfig();
|
|
2473
|
+
const i18nConfig = config.i18n;
|
|
2474
|
+
const [userLocale, setUserLocale] = usePersistedState2(
|
|
2475
|
+
"user-locale",
|
|
2476
|
+
i18nConfig?.defaultLocale ?? "en-US"
|
|
2477
|
+
);
|
|
2478
|
+
if (!i18nConfig) return null;
|
|
2479
|
+
return /* @__PURE__ */ jsxs16("label", { className, children: [
|
|
2480
|
+
label ? /* @__PURE__ */ jsx23("span", { children: label }) : null,
|
|
2481
|
+
/* @__PURE__ */ jsx23(
|
|
2482
|
+
"select",
|
|
2483
|
+
{
|
|
2484
|
+
id,
|
|
2485
|
+
value: userLocale,
|
|
2486
|
+
onChange: (e) => setUserLocale(e.target.value),
|
|
2487
|
+
"data-testid": "language-switcher",
|
|
2488
|
+
children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */ jsx23("option", { value: loc, children: loc }, loc))
|
|
2489
|
+
}
|
|
2490
|
+
)
|
|
2390
2491
|
] });
|
|
2391
2492
|
}
|
|
2392
2493
|
|
|
2393
2494
|
// src/defaults/LoadingState.tsx
|
|
2394
|
-
import { jsx as
|
|
2495
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
2395
2496
|
function LoadingState({ message }) {
|
|
2396
|
-
return /* @__PURE__ */
|
|
2497
|
+
return /* @__PURE__ */ jsx24("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx24("span", { children: message ?? "Carregando..." }) });
|
|
2397
2498
|
}
|
|
2398
2499
|
|
|
2399
2500
|
// src/defaults/EmptyState.tsx
|
|
2400
|
-
import { jsx as
|
|
2501
|
+
import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2401
2502
|
function EmptyState({ title, description, action }) {
|
|
2402
|
-
return /* @__PURE__ */
|
|
2403
|
-
/* @__PURE__ */
|
|
2404
|
-
description && /* @__PURE__ */
|
|
2405
|
-
action && /* @__PURE__ */
|
|
2503
|
+
return /* @__PURE__ */ jsxs17("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
|
|
2504
|
+
/* @__PURE__ */ jsx25("h2", { style: { marginBottom: 8 }, children: title }),
|
|
2505
|
+
description && /* @__PURE__ */ jsx25("p", { style: { opacity: 0.7 }, children: description }),
|
|
2506
|
+
action && /* @__PURE__ */ jsx25("div", { style: { marginTop: 16 }, children: action })
|
|
2406
2507
|
] });
|
|
2407
2508
|
}
|
|
2408
2509
|
|
|
@@ -2619,7 +2720,7 @@ function useForgotForm() {
|
|
|
2619
2720
|
}
|
|
2620
2721
|
|
|
2621
2722
|
// src/hooks/useResetForm.ts
|
|
2622
|
-
import { useCallback as useCallback8, useEffect as
|
|
2723
|
+
import { useCallback as useCallback8, useEffect as useEffect10, useMemo as useMemo7, useState as useState10 } from "react";
|
|
2623
2724
|
import { useHook as useHook11 } from "@hook-sdk/sdk";
|
|
2624
2725
|
var MIN_PASSWORD3 = 12;
|
|
2625
2726
|
function useResetForm() {
|
|
@@ -2633,7 +2734,7 @@ function useResetForm() {
|
|
|
2633
2734
|
const [touchedPassword, setTouchedPassword] = useState10(false);
|
|
2634
2735
|
const [touchedConfirm, setTouchedConfirm] = useState10(false);
|
|
2635
2736
|
const [formSubmitAttempted, setFormSubmitAttempted] = useState10(false);
|
|
2636
|
-
|
|
2737
|
+
useEffect10(() => {
|
|
2637
2738
|
if (typeof window === "undefined") return;
|
|
2638
2739
|
const params = new URLSearchParams(window.location.search);
|
|
2639
2740
|
const t = params.get("token");
|
|
@@ -2727,12 +2828,12 @@ function discountPercent(anchorCents, realCents) {
|
|
|
2727
2828
|
}
|
|
2728
2829
|
|
|
2729
2830
|
// src/hooks/useAuthPrimitives.ts
|
|
2730
|
-
import { useEffect as
|
|
2831
|
+
import { useEffect as useEffect11 } from "react";
|
|
2731
2832
|
import { useHook as useHook13 } from "@hook-sdk/sdk";
|
|
2732
2833
|
var warned = false;
|
|
2733
2834
|
function useAuthPrimitives() {
|
|
2734
2835
|
const { auth } = useHook13();
|
|
2735
|
-
|
|
2836
|
+
useEffect11(() => {
|
|
2736
2837
|
if (!warned && process.env.NODE_ENV !== "production") {
|
|
2737
2838
|
warned = true;
|
|
2738
2839
|
console.warn(
|
|
@@ -2777,7 +2878,7 @@ function useSubscription() {
|
|
|
2777
2878
|
}
|
|
2778
2879
|
|
|
2779
2880
|
// src/hooks/useReminders.ts
|
|
2780
|
-
import { useCallback as useCallback9, useEffect as
|
|
2881
|
+
import { useCallback as useCallback9, useEffect as useEffect12, useState as useState11 } from "react";
|
|
2781
2882
|
import { useHook as useHook16 } from "@hook-sdk/sdk";
|
|
2782
2883
|
function useReminders() {
|
|
2783
2884
|
const { push } = useHook16();
|
|
@@ -2793,7 +2894,7 @@ function useReminders() {
|
|
|
2793
2894
|
setLoading(false);
|
|
2794
2895
|
}
|
|
2795
2896
|
}, [r]);
|
|
2796
|
-
|
|
2897
|
+
useEffect12(() => {
|
|
2797
2898
|
void reload();
|
|
2798
2899
|
}, [reload]);
|
|
2799
2900
|
const setReminder = useCallback9(async (input) => {
|
|
@@ -2832,20 +2933,20 @@ function useToast() {
|
|
|
2832
2933
|
|
|
2833
2934
|
// src/RouteBoundary.tsx
|
|
2834
2935
|
import { Routes as Routes2, Route as Route2 } from "react-router-dom";
|
|
2835
|
-
import { jsx as
|
|
2936
|
+
import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2836
2937
|
function RouteBoundary({ children }) {
|
|
2837
|
-
return /* @__PURE__ */
|
|
2938
|
+
return /* @__PURE__ */ jsxs18(Routes2, { children: [
|
|
2838
2939
|
children,
|
|
2839
|
-
/* @__PURE__ */
|
|
2940
|
+
/* @__PURE__ */ jsx26(Route2, { path: "*", element: /* @__PURE__ */ jsx26(DefaultNotFound, {}) })
|
|
2840
2941
|
] });
|
|
2841
2942
|
}
|
|
2842
2943
|
function DefaultNotFound() {
|
|
2843
|
-
return /* @__PURE__ */
|
|
2944
|
+
return /* @__PURE__ */ jsx26("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
|
|
2844
2945
|
}
|
|
2845
2946
|
|
|
2846
2947
|
// src/PreAuthShell.tsx
|
|
2847
2948
|
import { BrowserRouter as BrowserRouter2, MemoryRouter as MemoryRouter2, Routes as Routes3 } from "react-router-dom";
|
|
2848
|
-
import { jsx as
|
|
2949
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
2849
2950
|
function PreAuthShell({
|
|
2850
2951
|
basename,
|
|
2851
2952
|
testRouter,
|
|
@@ -2853,14 +2954,14 @@ function PreAuthShell({
|
|
|
2853
2954
|
children
|
|
2854
2955
|
}) {
|
|
2855
2956
|
if (testRouter === "memory") {
|
|
2856
|
-
return /* @__PURE__ */
|
|
2957
|
+
return /* @__PURE__ */ jsx27(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx27(Routes3, { children }) });
|
|
2857
2958
|
}
|
|
2858
|
-
return /* @__PURE__ */
|
|
2959
|
+
return /* @__PURE__ */ jsx27(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx27(Routes3, { children }) });
|
|
2859
2960
|
}
|
|
2860
2961
|
|
|
2861
2962
|
// src/OnboardingFlow.tsx
|
|
2862
|
-
import { useCallback as useCallback11, useEffect as
|
|
2863
|
-
import { usePersistedState, useHook as useHook17 } from "@hook-sdk/sdk";
|
|
2963
|
+
import { useCallback as useCallback11, useEffect as useEffect13, useMemo as useMemo8, useRef as useRef5 } from "react";
|
|
2964
|
+
import { usePersistedState as usePersistedState3, useHook as useHook17 } from "@hook-sdk/sdk";
|
|
2864
2965
|
|
|
2865
2966
|
// src/hooks/useOnboardingStep.ts
|
|
2866
2967
|
import { createContext as createContext3, useContext as useContext4 } from "react";
|
|
@@ -2876,7 +2977,7 @@ function useOnboardingStep() {
|
|
|
2876
2977
|
}
|
|
2877
2978
|
|
|
2878
2979
|
// src/OnboardingFlow.tsx
|
|
2879
|
-
import { jsx as
|
|
2980
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
2880
2981
|
var isFilled = (v) => v != null && v !== "";
|
|
2881
2982
|
var CURRENT_STEP_FIELD = "currentStep";
|
|
2882
2983
|
function readPersistedStepIdx(draft) {
|
|
@@ -2889,7 +2990,7 @@ function OnboardingFlow({
|
|
|
2889
2990
|
onComplete,
|
|
2890
2991
|
persistKey
|
|
2891
2992
|
}) {
|
|
2892
|
-
const [draft, setDraft, status] =
|
|
2993
|
+
const [draft, setDraft, status] = usePersistedState3(persistKey, {});
|
|
2893
2994
|
const draftRef = useRef5(draft);
|
|
2894
2995
|
draftRef.current = draft;
|
|
2895
2996
|
const idx = readPersistedStepIdx(draft);
|
|
@@ -2914,7 +3015,7 @@ function OnboardingFlow({
|
|
|
2914
3015
|
const step = steps[clampedIdx];
|
|
2915
3016
|
const hookCtx = useHook17();
|
|
2916
3017
|
const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
|
|
2917
|
-
|
|
3018
|
+
useEffect13(() => {
|
|
2918
3019
|
if (status.loading) return;
|
|
2919
3020
|
if (!step) return;
|
|
2920
3021
|
if (!track2) return;
|
|
@@ -2966,7 +3067,7 @@ function OnboardingFlow({
|
|
|
2966
3067
|
`[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
|
|
2967
3068
|
);
|
|
2968
3069
|
}
|
|
2969
|
-
return /* @__PURE__ */
|
|
3070
|
+
return /* @__PURE__ */ jsx28(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx28(Screen, {}) });
|
|
2970
3071
|
}
|
|
2971
3072
|
|
|
2972
3073
|
// src/hooks/useFeature.ts
|
|
@@ -2981,8 +3082,10 @@ export {
|
|
|
2981
3082
|
DeepLinkHandler,
|
|
2982
3083
|
EmptyState,
|
|
2983
3084
|
ErrorBoundary,
|
|
3085
|
+
I18nProvider,
|
|
2984
3086
|
InstallGate,
|
|
2985
3087
|
InstallSplash,
|
|
3088
|
+
LanguageSwitcher,
|
|
2986
3089
|
LoadingState,
|
|
2987
3090
|
OnboardingFlow,
|
|
2988
3091
|
PaymentReturnHandler,
|