@hook-sdk/template 0.9.1 → 0.10.0

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