@hook-sdk/template 0.14.1 → 0.15.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
@@ -69,9 +69,9 @@ __export(index_exports, {
69
69
  module.exports = __toCommonJS(index_exports);
70
70
 
71
71
  // src/AppRoot.tsx
72
- var import_react12 = require("react");
72
+ var import_react13 = require("react");
73
73
  var import_react_router_dom2 = require("react-router-dom");
74
- var import_sdk5 = require("@hook-sdk/sdk");
74
+ var import_sdk6 = require("@hook-sdk/sdk");
75
75
 
76
76
  // src/config/AppConfigContext.tsx
77
77
  var import_react = require("react");
@@ -248,8 +248,8 @@ function ThemeProvider({ children }) {
248
248
  }
249
249
 
250
250
  // src/hooks/usePaywallState.ts
251
- var import_react5 = require("react");
252
- var import_sdk2 = require("@hook-sdk/sdk");
251
+ var import_react6 = require("react");
252
+ var import_sdk3 = require("@hook-sdk/sdk");
253
253
 
254
254
  // src/errors/asaas-pt-br.ts
255
255
  var MAP = {
@@ -270,6 +270,35 @@ function asaasErrorMessage(code) {
270
270
  return MAP[code] ?? "Ocorreu um erro inesperado. Tente novamente em instantes.";
271
271
  }
272
272
 
273
+ // src/hooks/usePaywallTracker.ts
274
+ var import_react5 = require("react");
275
+ var import_sdk2 = require("@hook-sdk/sdk");
276
+ function deriveStep(s) {
277
+ if (s.pixPaid) return "success";
278
+ if (s.hasError) return "error";
279
+ if (s.pixPendingShown) return "pix_qr_shown";
280
+ if (s.selectedMethod === "card" && s.cpfValid) return "card_form";
281
+ if (s.cpfRequired && !s.cpfValid) return "cpf_input";
282
+ if (s.selectedMethod) return "method_select";
283
+ return "plan_select";
284
+ }
285
+ function usePaywallTracker(snapshot) {
286
+ const ctx = (0, import_sdk2.useHook)();
287
+ const track2 = typeof ctx.track === "function" ? ctx.track : void 0;
288
+ const lastStepRef = (0, import_react5.useRef)(null);
289
+ const step = deriveStep(snapshot);
290
+ (0, import_react5.useEffect)(() => {
291
+ if (lastStepRef.current === step) return;
292
+ lastStepRef.current = step;
293
+ if (!track2) return;
294
+ track2("paywall_step_viewed", {
295
+ step,
296
+ method: snapshot.selectedMethod,
297
+ cycle: snapshot.cycle
298
+ });
299
+ }, [step]);
300
+ }
301
+
273
302
  // src/hooks/usePaywallState.ts
274
303
  function isCheckoutFailure(r) {
275
304
  return "ok" in r && r.ok === false;
@@ -284,11 +313,11 @@ var FALLBACK_PAYWALL = {
284
313
  };
285
314
  var isMethodAvailable = (availability, method) => availability[method] !== false;
286
315
  function usePaywallState() {
287
- const { subscription, plan } = (0, import_sdk2.useHook)();
288
- const configFromCtx = (0, import_react5.useContext)(AppConfigContext);
316
+ const { subscription, plan } = (0, import_sdk3.useHook)();
317
+ const configFromCtx = (0, import_react6.useContext)(AppConfigContext);
289
318
  const paywall = configFromCtx?.paywall ?? FALLBACK_PAYWALL;
290
319
  const isFree = paywall.mode === "free";
291
- const declaredMethods = (0, import_react5.useMemo)(
320
+ const declaredMethods = (0, import_react6.useMemo)(
292
321
  () => isFree ? [] : paywall.checkoutMethods,
293
322
  [isFree, paywall]
294
323
  );
@@ -297,33 +326,33 @@ function usePaywallState() {
297
326
  "pix-auto": null,
298
327
  "pix-once": null
299
328
  };
300
- const methods = (0, import_react5.useMemo)(
329
+ const methods = (0, import_react6.useMemo)(
301
330
  () => declaredMethods.filter((m) => isMethodAvailable(availability, m)),
302
331
  [declaredMethods, availability]
303
332
  );
304
333
  const defaultMethod = methods[0] ?? declaredMethods[0] ?? "card";
305
- const [selectedMethodRaw, setSelectedMethod] = (0, import_react5.useState)(defaultMethod);
334
+ const [selectedMethodRaw, setSelectedMethod] = (0, import_react6.useState)(defaultMethod);
306
335
  const selectedMethod = methods.includes(selectedMethodRaw) ? selectedMethodRaw : methods[0] ?? selectedMethodRaw;
307
336
  const initialCycle = isFree ? "MONTHLY" : paywall.cycles[0] ?? "MONTHLY";
308
- const [cycle, setCycle] = (0, import_react5.useState)(initialCycle);
337
+ const [cycle, setCycle] = (0, import_react6.useState)(initialCycle);
309
338
  const cpfRequired = !isFree && paywall.requiresCpf;
310
- const [cpf, setCpf] = (0, import_react5.useState)("");
311
- const cpfValid = (0, import_react5.useMemo)(() => /^[0-9]{11}$/.test(cpf), [cpf]);
312
- const [card, setCardState] = (0, import_react5.useState)({
339
+ const [cpf, setCpf] = (0, import_react6.useState)("");
340
+ const cpfValid = (0, import_react6.useMemo)(() => /^[0-9]{11}$/.test(cpf), [cpf]);
341
+ const [card, setCardState] = (0, import_react6.useState)({
313
342
  number: "",
314
343
  cvv: "",
315
344
  expiry: "",
316
345
  holder: ""
317
346
  });
318
- const setCard = (0, import_react5.useCallback)((patch) => {
347
+ const setCard = (0, import_react6.useCallback)((patch) => {
319
348
  setCardState((prev) => ({ ...prev, ...patch }));
320
349
  }, []);
321
- const [error, setError] = (0, import_react5.useState)(null);
322
- const [submitting, setSubmitting] = (0, import_react5.useState)(false);
350
+ const [error, setError] = (0, import_react6.useState)(null);
351
+ const [submitting, setSubmitting] = (0, import_react6.useState)(false);
323
352
  const status = subscription.status();
324
353
  const daysLeftInTrial = subscription.daysLeftInTrial();
325
354
  const initialLoadComplete = subscription.initialLoadComplete;
326
- const pixPending = (0, import_react5.useMemo)(() => {
355
+ const pixPending = (0, import_react6.useMemo)(() => {
327
356
  const sdkPix = subscription.pixPending;
328
357
  if (!sdkPix) return null;
329
358
  const liveStatus = subscription.current?.status;
@@ -336,7 +365,7 @@ function usePaywallState() {
336
365
  paid
337
366
  };
338
367
  }, [subscription.pixPending, subscription.current]);
339
- const monthlyEquivalent = (0, import_react5.useCallback)(
368
+ const monthlyEquivalent = (0, import_react6.useCallback)(
340
369
  (c) => {
341
370
  const monthlyCents = plan.data?.priceCents ?? (isFree ? 0 : paywall.prices.monthlyCents);
342
371
  const yearlyCents = plan.data?.yearlyPriceCents ?? (isFree ? null : paywall.prices.yearlyCents);
@@ -345,7 +374,7 @@ function usePaywallState() {
345
374
  },
346
375
  [plan, paywall, isFree]
347
376
  );
348
- const planDerived = (0, import_react5.useMemo)(() => {
377
+ const planDerived = (0, import_react6.useMemo)(() => {
349
378
  if (isFree) return null;
350
379
  const monthlyCents = paywall.prices.monthlyCents;
351
380
  const yearlyCents = paywall.prices.yearlyCents;
@@ -361,7 +390,7 @@ function usePaywallState() {
361
390
  };
362
391
  }, [paywall, cycle, isFree]);
363
392
  const useDefaultMessages = paywall.mode !== "free" && paywall.errorMessages === "default";
364
- const buildError = (0, import_react5.useCallback)(
393
+ const buildError = (0, import_react6.useCallback)(
365
394
  (code, fallbackMessage) => ({
366
395
  code,
367
396
  message: fallbackMessage,
@@ -369,7 +398,7 @@ function usePaywallState() {
369
398
  }),
370
399
  [useDefaultMessages]
371
400
  );
372
- const submit = (0, import_react5.useCallback)(async () => {
401
+ const submit = (0, import_react6.useCallback)(async () => {
373
402
  setSubmitting(true);
374
403
  setError(null);
375
404
  const methodToUse = selectedMethod;
@@ -431,7 +460,7 @@ function usePaywallState() {
431
460
  return void 0;
432
461
  }
433
462
  }, [selectedMethod, availability, subscription, cycle, cpf, card, buildError]);
434
- const checkout = (0, import_react5.useCallback)(
463
+ const checkout = (0, import_react6.useCallback)(
435
464
  async (args) => {
436
465
  setSubmitting(true);
437
466
  setError(null);
@@ -495,7 +524,7 @@ function usePaywallState() {
495
524
  },
496
525
  [subscription, buildError]
497
526
  );
498
- const cancel = (0, import_react5.useCallback)(async () => {
527
+ const cancel = (0, import_react6.useCallback)(async () => {
499
528
  try {
500
529
  await subscription.cancel();
501
530
  await subscription.refresh();
@@ -505,14 +534,24 @@ function usePaywallState() {
505
534
  setError(buildError(code, message));
506
535
  }
507
536
  }, [subscription, buildError]);
508
- const cardState = (0, import_react5.useMemo)(
537
+ const cardState = (0, import_react6.useMemo)(
509
538
  () => ({ ...card, set: setCard }),
510
539
  [card, setCard]
511
540
  );
512
- const cpfState = (0, import_react5.useMemo)(
541
+ const cpfState = (0, import_react6.useMemo)(
513
542
  () => ({ required: cpfRequired, value: cpf, set: setCpf, valid: cpfValid }),
514
543
  [cpfRequired, cpf, cpfValid]
515
544
  );
545
+ usePaywallTracker({
546
+ selectedMethod,
547
+ cycle,
548
+ cpfRequired,
549
+ cpfValid,
550
+ pixPendingShown: pixPending !== null,
551
+ pixPaid: pixPending?.paid === true,
552
+ hasError: error !== null,
553
+ submitting
554
+ });
516
555
  return {
517
556
  // Subscription status (reactive, proxied from SDK)
518
557
  status,
@@ -567,10 +606,10 @@ function SubscriptionGate({ Paywall, children }) {
567
606
  }
568
607
 
569
608
  // src/components/InstallGate/InstallGate.tsx
570
- var import_react8 = require("react");
609
+ var import_react9 = require("react");
571
610
 
572
611
  // src/hooks/useInstallPrompt.ts
573
- var import_react6 = require("react");
612
+ var import_react7 = require("react");
574
613
  var ANDROID_PROMPT_WAIT_MS = 3e3;
575
614
  var IOS_RE = /iPad|iPhone|iPod/;
576
615
  var IOS_NON_SAFARI_RE = /CriOS|FxiOS|EdgiOS/;
@@ -705,27 +744,27 @@ function useInstallPrompt(slug) {
705
744
  const iosBrowser = detectIOSBrowser(ua);
706
745
  const androidBrowser = detectAndroidBrowser(ua);
707
746
  const inAppApp = detectInAppApp(ua);
708
- const [isInstallable, setIsInstallable] = (0, import_react6.useState)(() => {
747
+ const [isInstallable, setIsInstallable] = (0, import_react7.useState)(() => {
709
748
  if (typeof window === "undefined") return false;
710
749
  return window.__pwaInstallPrompt != null;
711
750
  });
712
- const [isInstalled, setIsInstalled] = (0, import_react6.useState)(() => {
751
+ const [isInstalled, setIsInstalled] = (0, import_react7.useState)(() => {
713
752
  const { installed } = detectStandalone();
714
753
  return installed || readInstalledMarker(slug);
715
754
  });
716
- const [isDismissedSession, setIsDismissedSession] = (0, import_react6.useState)(() => readSessionSkip(slug));
717
- const [isDismissedPermanent, setIsDismissedPermanent] = (0, import_react6.useState)(() => readPermanentDismiss(slug).dismissed);
718
- const [skipCount, setSkipCount] = (0, import_react6.useState)(() => readSkipCount(slug));
719
- const [promptWaitElapsed, setPromptWaitElapsed] = (0, import_react6.useState)(() => {
755
+ const [isDismissedSession, setIsDismissedSession] = (0, import_react7.useState)(() => readSessionSkip(slug));
756
+ const [isDismissedPermanent, setIsDismissedPermanent] = (0, import_react7.useState)(() => readPermanentDismiss(slug).dismissed);
757
+ const [skipCount, setSkipCount] = (0, import_react7.useState)(() => readSkipCount(slug));
758
+ const [promptWaitElapsed, setPromptWaitElapsed] = (0, import_react7.useState)(() => {
720
759
  if (typeof window === "undefined") return true;
721
760
  return window.__pwaInstallPrompt != null;
722
761
  });
723
- (0, import_react6.useEffect)(() => {
762
+ (0, import_react7.useEffect)(() => {
724
763
  if (promptWaitElapsed) return;
725
764
  const id = setTimeout(() => setPromptWaitElapsed(true), ANDROID_PROMPT_WAIT_MS);
726
765
  return () => clearTimeout(id);
727
766
  }, [promptWaitElapsed]);
728
- (0, import_react6.useEffect)(() => {
767
+ (0, import_react7.useEffect)(() => {
729
768
  if (typeof window === "undefined") return;
730
769
  if (window.__pwaInstallPrompt) {
731
770
  setIsInstallable(true);
@@ -749,7 +788,7 @@ function useInstallPrompt(slug) {
749
788
  window.removeEventListener("appinstalled", onInstalled);
750
789
  };
751
790
  }, [slug]);
752
- (0, import_react6.useEffect)(() => {
791
+ (0, import_react7.useEffect)(() => {
753
792
  if (typeof window === "undefined") return;
754
793
  const mq = window.matchMedia?.("(display-mode: standalone)");
755
794
  if (!mq) return;
@@ -776,7 +815,7 @@ function useInstallPrompt(slug) {
776
815
  },
777
816
  promptWaitElapsed
778
817
  );
779
- const promptInstall = (0, import_react6.useCallback)(async () => {
818
+ const promptInstall = (0, import_react7.useCallback)(async () => {
780
819
  if (typeof window === "undefined") return false;
781
820
  const prompt = window.__pwaInstallPrompt;
782
821
  if (!prompt) return false;
@@ -798,7 +837,7 @@ function useInstallPrompt(slug) {
798
837
  return false;
799
838
  }
800
839
  }, [slug]);
801
- const dismissSession = (0, import_react6.useCallback)(() => {
840
+ const dismissSession = (0, import_react7.useCallback)(() => {
802
841
  if (typeof sessionStorage !== "undefined") {
803
842
  try {
804
843
  sessionStorage.setItem(storageKey.sessionSkip(slug), "true");
@@ -812,20 +851,20 @@ function useInstallPrompt(slug) {
812
851
  setIsDismissedSession(true);
813
852
  track("pwa_install_session_skip", { slug, platform, skip_count: newCount });
814
853
  }, [slug, skipCount, platform]);
815
- const dismissPermanent = (0, import_react6.useCallback)(() => {
854
+ const dismissPermanent = (0, import_react7.useCallback)(() => {
816
855
  const storage = safeStorage();
817
856
  if (storage) storage.setItem(storageKey.dismissedAt(slug), (/* @__PURE__ */ new Date()).toISOString());
818
857
  setIsDismissedPermanent(true);
819
858
  track("pwa_install_permanent_dismiss", { slug, platform, prior_skip_count: skipCount });
820
859
  }, [slug, platform, skipCount]);
821
- const copyLink = (0, import_react6.useCallback)(async () => {
860
+ const copyLink = (0, import_react7.useCallback)(async () => {
822
861
  if (typeof navigator === "undefined" || typeof location === "undefined") return;
823
862
  try {
824
863
  await navigator.clipboard?.writeText?.(location.href);
825
864
  } catch {
826
865
  }
827
866
  }, []);
828
- const reset = (0, import_react6.useCallback)(() => {
867
+ const reset = (0, import_react7.useCallback)(() => {
829
868
  const storage = safeStorage();
830
869
  if (storage) {
831
870
  storage.removeItem(storageKey.dismissedAt(slug));
@@ -1618,7 +1657,7 @@ function IOSOtherVariant({
1618
1657
  }
1619
1658
 
1620
1659
  // src/components/InstallGate/variants/InAppBrowserVariant.tsx
1621
- var import_react7 = require("react");
1660
+ var import_react8 = require("react");
1622
1661
  var import_jsx_runtime14 = require("react/jsx-runtime");
1623
1662
  function InAppBrowserVariant({
1624
1663
  state,
@@ -1628,7 +1667,7 @@ function InAppBrowserVariant({
1628
1667
  const appCopy = INSTALL_COPY.inApp[app] ?? INSTALL_COPY.inApp.other;
1629
1668
  const copy = INSTALL_COPY.inApp;
1630
1669
  const showPermanent = shouldShowPermanentOption(state);
1631
- const [copied, setCopied] = (0, import_react7.useState)(false);
1670
+ const [copied, setCopied] = (0, import_react8.useState)(false);
1632
1671
  const handleCopy = async () => {
1633
1672
  await actions.copyLink();
1634
1673
  setCopied(true);
@@ -1833,8 +1872,8 @@ function InstallGate({ children }) {
1833
1872
  const enabled = features_enabled.includes("install_prompt");
1834
1873
  const installState = useInstallPrompt(slug);
1835
1874
  const shouldBlock = enabled && shouldBlockInstall(installState);
1836
- const trackedRef = (0, import_react8.useRef)(null);
1837
- (0, import_react8.useEffect)(() => {
1875
+ const trackedRef = (0, import_react9.useRef)(null);
1876
+ (0, import_react9.useEffect)(() => {
1838
1877
  if (!shouldBlock) return;
1839
1878
  if (typeof window === "undefined") return;
1840
1879
  const variantKey = `${slug}:${installState.variant}`;
@@ -1883,16 +1922,16 @@ function PushPrompt() {
1883
1922
  }
1884
1923
 
1885
1924
  // src/internal/SessionExpiredBanner.tsx
1886
- var import_react9 = require("react");
1887
- var import_sdk3 = require("@hook-sdk/sdk");
1925
+ var import_react10 = require("react");
1926
+ var import_sdk4 = require("@hook-sdk/sdk");
1888
1927
  var import_jsx_runtime17 = require("react/jsx-runtime");
1889
1928
  var DISMISS_KEY = "hook:session-expired-dismissed-until";
1890
1929
  var DISMISS_TTL_MS = 60 * 60 * 1e3;
1891
1930
  function SessionExpiredBanner() {
1892
- const { authStatus } = (0, import_sdk3.useHook)();
1893
- const wasAuthRef = (0, import_react9.useRef)(false);
1894
- const [show, setShow] = (0, import_react9.useState)(false);
1895
- (0, import_react9.useEffect)(() => {
1931
+ const { authStatus } = (0, import_sdk4.useHook)();
1932
+ const wasAuthRef = (0, import_react10.useRef)(false);
1933
+ const [show, setShow] = (0, import_react10.useState)(false);
1934
+ (0, import_react10.useEffect)(() => {
1896
1935
  if (authStatus === "authenticated") {
1897
1936
  wasAuthRef.current = true;
1898
1937
  setShow(false);
@@ -1942,9 +1981,9 @@ function SessionExpiredBanner() {
1942
1981
  }
1943
1982
 
1944
1983
  // src/defaults/ErrorBoundary.tsx
1945
- var import_react10 = require("react");
1984
+ var import_react11 = require("react");
1946
1985
  var import_jsx_runtime18 = require("react/jsx-runtime");
1947
- var ErrorBoundary = class extends import_react10.Component {
1986
+ var ErrorBoundary = class extends import_react11.Component {
1948
1987
  state = { error: null };
1949
1988
  static getDerivedStateFromError(error) {
1950
1989
  return { error };
@@ -1971,20 +2010,20 @@ var ErrorBoundary = class extends import_react10.Component {
1971
2010
  };
1972
2011
 
1973
2012
  // src/internal/PaymentReturnHandler.tsx
1974
- var import_react11 = require("react");
1975
- var import_sdk4 = require("@hook-sdk/sdk");
2013
+ var import_react12 = require("react");
2014
+ var import_sdk5 = require("@hook-sdk/sdk");
1976
2015
  var import_jsx_runtime19 = require("react/jsx-runtime");
1977
2016
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
1978
2017
  var MAX_CYCLES = 3;
1979
2018
  var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
1980
2019
  function PaymentReturnHandler({ children }) {
1981
- const { subscription } = (0, import_sdk4.useHook)();
1982
- const subRef = (0, import_react11.useRef)(subscription);
2020
+ const { subscription } = (0, import_sdk5.useHook)();
2021
+ const subRef = (0, import_react12.useRef)(subscription);
1983
2022
  subRef.current = subscription;
1984
- const runIdRef = (0, import_react11.useRef)(0);
1985
- const cyclesRef = (0, import_react11.useRef)(0);
1986
- const [state, setState] = (0, import_react11.useState)("idle");
1987
- const runPoll = (0, import_react11.useCallback)(() => {
2023
+ const runIdRef = (0, import_react12.useRef)(0);
2024
+ const cyclesRef = (0, import_react12.useRef)(0);
2025
+ const [state, setState] = (0, import_react12.useState)("idle");
2026
+ const runPoll = (0, import_react12.useCallback)(() => {
1988
2027
  const runId = ++runIdRef.current;
1989
2028
  cyclesRef.current += 1;
1990
2029
  setState("confirming");
@@ -2019,7 +2058,7 @@ function PaymentReturnHandler({ children }) {
2019
2058
  };
2020
2059
  void tick();
2021
2060
  }, []);
2022
- (0, import_react11.useEffect)(() => {
2061
+ (0, import_react12.useEffect)(() => {
2023
2062
  if (typeof window === "undefined") return;
2024
2063
  const url = new URL(window.location.href);
2025
2064
  if (url.searchParams.get("paymentReturn") !== "1") return;
@@ -2029,7 +2068,7 @@ function PaymentReturnHandler({ children }) {
2029
2068
  runIdRef.current++;
2030
2069
  };
2031
2070
  }, [runPoll]);
2032
- const goHome = (0, import_react11.useCallback)(() => {
2071
+ const goHome = (0, import_react12.useCallback)(() => {
2033
2072
  const cleanUrl = new URL(window.location.href);
2034
2073
  cleanUrl.searchParams.delete("paymentReturn");
2035
2074
  cleanUrl.pathname = "/app/home";
@@ -2195,7 +2234,7 @@ function AppRoot(props) {
2195
2234
  "[hook-template] <AppRoot>: PreAuthFlow slot prop is required when config.onboarding.trigger === 'pre_signup_custom'."
2196
2235
  );
2197
2236
  }
2198
- const legacyShim = (0, import_react12.useMemo)(() => buildLegacyConfigShim(config), [config]);
2237
+ const legacyShim = (0, import_react13.useMemo)(() => buildLegacyConfigShim(config), [config]);
2199
2238
  const Router = testRouter === "memory" ? import_react_router_dom2.MemoryRouter : import_react_router_dom2.BrowserRouter;
2200
2239
  const basename = `/app/${config.slug}`;
2201
2240
  const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
@@ -2232,7 +2271,7 @@ function AuthGated({
2232
2271
  EmailVerify,
2233
2272
  PreAuthFlow
2234
2273
  }) {
2235
- const { authStatus } = (0, import_sdk5.useHook)();
2274
+ const { authStatus } = (0, import_sdk6.useHook)();
2236
2275
  if (authStatus === "loading") return null;
2237
2276
  if (authStatus !== "authenticated") {
2238
2277
  if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
@@ -2261,8 +2300,8 @@ function FallbackPaywall() {
2261
2300
  }
2262
2301
 
2263
2302
  // src/hooks/usePush.ts
2264
- var import_react13 = require("react");
2265
- var import_sdk6 = require("@hook-sdk/sdk");
2303
+ var import_react14 = require("react");
2304
+ var import_sdk7 = require("@hook-sdk/sdk");
2266
2305
  var DISMISS_STORAGE_KEY = "push:dismissed-until";
2267
2306
  var DISMISS_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
2268
2307
  function detectIosNeedsInstall() {
@@ -2306,12 +2345,12 @@ function deriveState(push) {
2306
2345
  return { kind: "prompt" };
2307
2346
  }
2308
2347
  function usePush() {
2309
- const { push } = (0, import_sdk6.useHook)();
2310
- const [state, setState] = (0, import_react13.useState)(() => deriveState(push));
2311
- (0, import_react13.useEffect)(() => {
2348
+ const { push } = (0, import_sdk7.useHook)();
2349
+ const [state, setState] = (0, import_react14.useState)(() => deriveState(push));
2350
+ (0, import_react14.useEffect)(() => {
2312
2351
  setState(deriveState(push));
2313
2352
  }, [push]);
2314
- const subscribe = (0, import_react13.useCallback)(async () => {
2353
+ const subscribe = (0, import_react14.useCallback)(async () => {
2315
2354
  try {
2316
2355
  await push.subscribe();
2317
2356
  setState({ kind: "subscribed" });
@@ -2323,7 +2362,7 @@ function usePush() {
2323
2362
  throw e;
2324
2363
  }
2325
2364
  }, [push]);
2326
- const unsubscribe = (0, import_react13.useCallback)(async () => {
2365
+ const unsubscribe = (0, import_react14.useCallback)(async () => {
2327
2366
  try {
2328
2367
  await push.unsubscribe();
2329
2368
  setState({ kind: "prompt" });
@@ -2332,7 +2371,7 @@ function usePush() {
2332
2371
  throw e;
2333
2372
  }
2334
2373
  }, [push]);
2335
- const dismiss = (0, import_react13.useCallback)(() => {
2374
+ const dismiss = (0, import_react14.useCallback)(() => {
2336
2375
  if (typeof localStorage !== "undefined") {
2337
2376
  try {
2338
2377
  localStorage.setItem(DISMISS_STORAGE_KEY, String(Date.now() + DISMISS_TTL_MS2));
@@ -2424,20 +2463,20 @@ function EmptyState({ title, description, action }) {
2424
2463
  }
2425
2464
 
2426
2465
  // src/hooks/useLoginForm.ts
2427
- var import_react14 = require("react");
2428
- var import_sdk8 = require("@hook-sdk/sdk");
2466
+ var import_react15 = require("react");
2467
+ var import_sdk9 = require("@hook-sdk/sdk");
2429
2468
 
2430
2469
  // src/errors.ts
2431
- var import_sdk7 = require("@hook-sdk/sdk");
2470
+ var import_sdk8 = require("@hook-sdk/sdk");
2432
2471
  function mapSdkError(err) {
2433
- if (err instanceof import_sdk7.SdkRateLimitError) {
2472
+ if (err instanceof import_sdk8.SdkRateLimitError) {
2434
2473
  return {
2435
2474
  code: "rate_limited",
2436
2475
  message: `Aguarde ${err.retryAfter}s e tente novamente.`,
2437
2476
  retryAfter: err.retryAfter
2438
2477
  };
2439
2478
  }
2440
- if (err instanceof import_sdk7.SdkAuthError) {
2479
+ if (err instanceof import_sdk8.SdkAuthError) {
2441
2480
  const detail = err.detail;
2442
2481
  if (detail === "email_unverified") {
2443
2482
  return { code: "email_unverified", message: "Confirme seu e-mail antes de entrar." };
@@ -2447,7 +2486,7 @@ function mapSdkError(err) {
2447
2486
  }
2448
2487
  return { code: "invalid_credentials", message: "E-mail ou senha inv\xE1lidos." };
2449
2488
  }
2450
- if (err instanceof import_sdk7.SdkError && err.httpStatus === 0) {
2489
+ if (err instanceof import_sdk8.SdkError && err.httpStatus === 0) {
2451
2490
  return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
2452
2491
  }
2453
2492
  if (err instanceof TypeError) {
@@ -2460,20 +2499,20 @@ function mapSdkError(err) {
2460
2499
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2461
2500
  var MIN_PASSWORD = 8;
2462
2501
  function useLoginForm() {
2463
- const { auth } = (0, import_sdk8.useHook)();
2464
- const [email, setEmail] = (0, import_react14.useState)("");
2465
- const [password, setPassword] = (0, import_react14.useState)("");
2466
- const [submitting, setSubmitting] = (0, import_react14.useState)(false);
2467
- const [error, setError] = (0, import_react14.useState)(null);
2468
- const [touchedEmail, setTouchedEmail] = (0, import_react14.useState)(false);
2469
- const [touchedPassword, setTouchedPassword] = (0, import_react14.useState)(false);
2470
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react14.useState)(false);
2471
- const validateEmail = (0, import_react14.useMemo)(() => {
2502
+ const { auth } = (0, import_sdk9.useHook)();
2503
+ const [email, setEmail] = (0, import_react15.useState)("");
2504
+ const [password, setPassword] = (0, import_react15.useState)("");
2505
+ const [submitting, setSubmitting] = (0, import_react15.useState)(false);
2506
+ const [error, setError] = (0, import_react15.useState)(null);
2507
+ const [touchedEmail, setTouchedEmail] = (0, import_react15.useState)(false);
2508
+ const [touchedPassword, setTouchedPassword] = (0, import_react15.useState)(false);
2509
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react15.useState)(false);
2510
+ const validateEmail = (0, import_react15.useMemo)(() => {
2472
2511
  if (email.length === 0) return null;
2473
2512
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
2474
2513
  return null;
2475
2514
  }, [email]);
2476
- const validatePassword = (0, import_react14.useMemo)(() => {
2515
+ const validatePassword = (0, import_react15.useMemo)(() => {
2477
2516
  if (password.length === 0) return null;
2478
2517
  if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
2479
2518
  return null;
@@ -2481,7 +2520,7 @@ function useLoginForm() {
2481
2520
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2482
2521
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2483
2522
  const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
2484
- const submit = (0, import_react14.useCallback)(async () => {
2523
+ const submit = (0, import_react15.useCallback)(async () => {
2485
2524
  setFormSubmitAttempted(true);
2486
2525
  if (!canSubmit) return false;
2487
2526
  setSubmitting(true);
@@ -2515,32 +2554,32 @@ function useLoginForm() {
2515
2554
  }
2516
2555
 
2517
2556
  // src/hooks/useSignupForm.ts
2518
- var import_react15 = require("react");
2519
- var import_sdk9 = require("@hook-sdk/sdk");
2557
+ var import_react16 = require("react");
2558
+ var import_sdk10 = require("@hook-sdk/sdk");
2520
2559
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2521
2560
  var MIN_PASSWORD2 = 8;
2522
2561
  function useSignupForm() {
2523
- const { auth } = (0, import_sdk9.useHook)();
2524
- const [name, setName] = (0, import_react15.useState)("");
2525
- const [email, setEmail] = (0, import_react15.useState)("");
2526
- const [password, setPassword] = (0, import_react15.useState)("");
2527
- const [submitting, setSubmitting] = (0, import_react15.useState)(false);
2528
- const [error, setError] = (0, import_react15.useState)(null);
2529
- const [touchedName, setTouchedName] = (0, import_react15.useState)(false);
2530
- const [touchedEmail, setTouchedEmail] = (0, import_react15.useState)(false);
2531
- const [touchedPassword, setTouchedPassword] = (0, import_react15.useState)(false);
2532
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react15.useState)(false);
2533
- const validateName = (0, import_react15.useMemo)(() => {
2562
+ const { auth } = (0, import_sdk10.useHook)();
2563
+ const [name, setName] = (0, import_react16.useState)("");
2564
+ const [email, setEmail] = (0, import_react16.useState)("");
2565
+ const [password, setPassword] = (0, import_react16.useState)("");
2566
+ const [submitting, setSubmitting] = (0, import_react16.useState)(false);
2567
+ const [error, setError] = (0, import_react16.useState)(null);
2568
+ const [touchedName, setTouchedName] = (0, import_react16.useState)(false);
2569
+ const [touchedEmail, setTouchedEmail] = (0, import_react16.useState)(false);
2570
+ const [touchedPassword, setTouchedPassword] = (0, import_react16.useState)(false);
2571
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react16.useState)(false);
2572
+ const validateName = (0, import_react16.useMemo)(() => {
2534
2573
  if (name.length === 0) return null;
2535
2574
  if (name.trim().length < 2) return "Nome muito curto.";
2536
2575
  return null;
2537
2576
  }, [name]);
2538
- const validateEmail = (0, import_react15.useMemo)(() => {
2577
+ const validateEmail = (0, import_react16.useMemo)(() => {
2539
2578
  if (email.length === 0) return null;
2540
2579
  if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
2541
2580
  return null;
2542
2581
  }, [email]);
2543
- const validatePassword = (0, import_react15.useMemo)(() => {
2582
+ const validatePassword = (0, import_react16.useMemo)(() => {
2544
2583
  if (password.length === 0) return null;
2545
2584
  if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
2546
2585
  return null;
@@ -2549,7 +2588,7 @@ function useSignupForm() {
2549
2588
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2550
2589
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2551
2590
  const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
2552
- const submit = (0, import_react15.useCallback)(async () => {
2591
+ const submit = (0, import_react16.useCallback)(async () => {
2553
2592
  setFormSubmitAttempted(true);
2554
2593
  if (!canSubmit) return false;
2555
2594
  setSubmitting(true);
@@ -2587,25 +2626,25 @@ function useSignupForm() {
2587
2626
  }
2588
2627
 
2589
2628
  // src/hooks/useForgotForm.ts
2590
- var import_react16 = require("react");
2591
- var import_sdk10 = require("@hook-sdk/sdk");
2629
+ var import_react17 = require("react");
2630
+ var import_sdk11 = require("@hook-sdk/sdk");
2592
2631
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2593
2632
  function useForgotForm() {
2594
- const { auth } = (0, import_sdk10.useHook)();
2595
- const [email, setEmail] = (0, import_react16.useState)("");
2596
- const [submitting, setSubmitting] = (0, import_react16.useState)(false);
2597
- const [sent, setSent] = (0, import_react16.useState)(false);
2598
- const [error, setError] = (0, import_react16.useState)(null);
2599
- const [touchedEmail, setTouchedEmail] = (0, import_react16.useState)(false);
2600
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react16.useState)(false);
2601
- const validateEmail = (0, import_react16.useMemo)(() => {
2633
+ const { auth } = (0, import_sdk11.useHook)();
2634
+ const [email, setEmail] = (0, import_react17.useState)("");
2635
+ const [submitting, setSubmitting] = (0, import_react17.useState)(false);
2636
+ const [sent, setSent] = (0, import_react17.useState)(false);
2637
+ const [error, setError] = (0, import_react17.useState)(null);
2638
+ const [touchedEmail, setTouchedEmail] = (0, import_react17.useState)(false);
2639
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react17.useState)(false);
2640
+ const validateEmail = (0, import_react17.useMemo)(() => {
2602
2641
  if (email.length === 0) return null;
2603
2642
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
2604
2643
  return null;
2605
2644
  }, [email]);
2606
2645
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2607
2646
  const canSubmit = email.length > 0 && validateEmail === null && !submitting;
2608
- const submit = (0, import_react16.useCallback)(async () => {
2647
+ const submit = (0, import_react17.useCallback)(async () => {
2609
2648
  setFormSubmitAttempted(true);
2610
2649
  if (!canSubmit) return false;
2611
2650
  setSubmitting(true);
@@ -2636,32 +2675,32 @@ function useForgotForm() {
2636
2675
  }
2637
2676
 
2638
2677
  // src/hooks/useResetForm.ts
2639
- var import_react17 = require("react");
2640
- var import_sdk11 = require("@hook-sdk/sdk");
2678
+ var import_react18 = require("react");
2679
+ var import_sdk12 = require("@hook-sdk/sdk");
2641
2680
  var MIN_PASSWORD3 = 12;
2642
2681
  function useResetForm() {
2643
- const { auth } = (0, import_sdk11.useHook)();
2644
- const [token, setToken] = (0, import_react17.useState)(null);
2645
- const [password, setPassword] = (0, import_react17.useState)("");
2646
- const [confirm, setConfirm] = (0, import_react17.useState)("");
2647
- const [submitting, setSubmitting] = (0, import_react17.useState)(false);
2648
- const [done, setDone] = (0, import_react17.useState)(false);
2649
- const [error, setError] = (0, import_react17.useState)(null);
2650
- const [touchedPassword, setTouchedPassword] = (0, import_react17.useState)(false);
2651
- const [touchedConfirm, setTouchedConfirm] = (0, import_react17.useState)(false);
2652
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react17.useState)(false);
2653
- (0, import_react17.useEffect)(() => {
2682
+ const { auth } = (0, import_sdk12.useHook)();
2683
+ const [token, setToken] = (0, import_react18.useState)(null);
2684
+ const [password, setPassword] = (0, import_react18.useState)("");
2685
+ const [confirm, setConfirm] = (0, import_react18.useState)("");
2686
+ const [submitting, setSubmitting] = (0, import_react18.useState)(false);
2687
+ const [done, setDone] = (0, import_react18.useState)(false);
2688
+ const [error, setError] = (0, import_react18.useState)(null);
2689
+ const [touchedPassword, setTouchedPassword] = (0, import_react18.useState)(false);
2690
+ const [touchedConfirm, setTouchedConfirm] = (0, import_react18.useState)(false);
2691
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react18.useState)(false);
2692
+ (0, import_react18.useEffect)(() => {
2654
2693
  if (typeof window === "undefined") return;
2655
2694
  const params = new URLSearchParams(window.location.search);
2656
2695
  const t = params.get("token");
2657
2696
  setToken(t && t.length > 0 ? t : null);
2658
2697
  }, []);
2659
- const validatePassword = (0, import_react17.useMemo)(() => {
2698
+ const validatePassword = (0, import_react18.useMemo)(() => {
2660
2699
  if (password.length === 0) return null;
2661
2700
  if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
2662
2701
  return null;
2663
2702
  }, [password]);
2664
- const validateConfirm = (0, import_react17.useMemo)(() => {
2703
+ const validateConfirm = (0, import_react18.useMemo)(() => {
2665
2704
  if (confirm.length === 0) return null;
2666
2705
  if (confirm !== password) return "Senhas n\xE3o coincidem.";
2667
2706
  return null;
@@ -2669,7 +2708,7 @@ function useResetForm() {
2669
2708
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2670
2709
  const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
2671
2710
  const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
2672
- const submit = (0, import_react17.useCallback)(async () => {
2711
+ const submit = (0, import_react18.useCallback)(async () => {
2673
2712
  setFormSubmitAttempted(true);
2674
2713
  if (!canSubmit || token === null) return;
2675
2714
  setSubmitting(true);
@@ -2709,9 +2748,9 @@ function useResetForm() {
2709
2748
  }
2710
2749
 
2711
2750
  // src/hooks/usePlan.ts
2712
- var import_sdk12 = require("@hook-sdk/sdk");
2751
+ var import_sdk13 = require("@hook-sdk/sdk");
2713
2752
  function usePlan() {
2714
- const { plan } = (0, import_sdk12.useHook)();
2753
+ const { plan } = (0, import_sdk13.useHook)();
2715
2754
  return plan;
2716
2755
  }
2717
2756
 
@@ -2744,12 +2783,12 @@ function discountPercent(anchorCents, realCents) {
2744
2783
  }
2745
2784
 
2746
2785
  // src/hooks/useAuthPrimitives.ts
2747
- var import_react18 = require("react");
2748
- var import_sdk13 = require("@hook-sdk/sdk");
2786
+ var import_react19 = require("react");
2787
+ var import_sdk14 = require("@hook-sdk/sdk");
2749
2788
  var warned = false;
2750
2789
  function useAuthPrimitives() {
2751
- const { auth } = (0, import_sdk13.useHook)();
2752
- (0, import_react18.useEffect)(() => {
2790
+ const { auth } = (0, import_sdk14.useHook)();
2791
+ (0, import_react19.useEffect)(() => {
2753
2792
  if (!warned && process.env.NODE_ENV !== "production") {
2754
2793
  warned = true;
2755
2794
  console.warn(
@@ -2771,9 +2810,9 @@ function useAuthPrimitives() {
2771
2810
  }
2772
2811
 
2773
2812
  // src/hooks/useAuth.ts
2774
- var import_sdk14 = require("@hook-sdk/sdk");
2813
+ var import_sdk15 = require("@hook-sdk/sdk");
2775
2814
  function useAuth() {
2776
- const { user, authStatus, auth } = (0, import_sdk14.useHook)();
2815
+ const { user, authStatus, auth } = (0, import_sdk15.useHook)();
2777
2816
  return {
2778
2817
  user,
2779
2818
  authStatus,
@@ -2782,23 +2821,23 @@ function useAuth() {
2782
2821
  }
2783
2822
 
2784
2823
  // src/hooks/useSubscription.ts
2785
- var import_sdk15 = require("@hook-sdk/sdk");
2824
+ var import_sdk16 = require("@hook-sdk/sdk");
2786
2825
  function useSubscription() {
2787
- const { subscription } = (0, import_sdk15.useHook)();
2826
+ const { subscription } = (0, import_sdk16.useHook)();
2788
2827
  return {
2789
2828
  status: subscription.status()
2790
2829
  };
2791
2830
  }
2792
2831
 
2793
2832
  // src/hooks/useReminders.ts
2794
- var import_react19 = require("react");
2795
- var import_sdk16 = require("@hook-sdk/sdk");
2833
+ var import_react20 = require("react");
2834
+ var import_sdk17 = require("@hook-sdk/sdk");
2796
2835
  function useReminders() {
2797
- const { push } = (0, import_sdk16.useHook)();
2836
+ const { push } = (0, import_sdk17.useHook)();
2798
2837
  const r = push.reminders;
2799
- const [reminders, setReminders] = (0, import_react19.useState)([]);
2800
- const [loading, setLoading] = (0, import_react19.useState)(true);
2801
- const reload = (0, import_react19.useCallback)(async () => {
2838
+ const [reminders, setReminders] = (0, import_react20.useState)([]);
2839
+ const [loading, setLoading] = (0, import_react20.useState)(true);
2840
+ const reload = (0, import_react20.useCallback)(async () => {
2802
2841
  setLoading(true);
2803
2842
  try {
2804
2843
  const next = await r.list();
@@ -2807,38 +2846,38 @@ function useReminders() {
2807
2846
  setLoading(false);
2808
2847
  }
2809
2848
  }, [r]);
2810
- (0, import_react19.useEffect)(() => {
2849
+ (0, import_react20.useEffect)(() => {
2811
2850
  void reload();
2812
2851
  }, [reload]);
2813
- const setReminder = (0, import_react19.useCallback)(async (input) => {
2852
+ const setReminder = (0, import_react20.useCallback)(async (input) => {
2814
2853
  await r.set(input);
2815
2854
  await reload();
2816
2855
  }, [r, reload]);
2817
- const deleteReminder = (0, import_react19.useCallback)(async (slot) => {
2856
+ const deleteReminder = (0, import_react20.useCallback)(async (slot) => {
2818
2857
  await r.delete(slot);
2819
2858
  await reload();
2820
2859
  }, [r, reload]);
2821
- const schedule = (0, import_react19.useCallback)(async (items) => {
2860
+ const schedule = (0, import_react20.useCallback)(async (items) => {
2822
2861
  return r.schedule(items);
2823
2862
  }, [r]);
2824
- const setFallbacks = (0, import_react19.useCallback)(async (items) => {
2863
+ const setFallbacks = (0, import_react20.useCallback)(async (items) => {
2825
2864
  return r.setFallbacks(items);
2826
2865
  }, [r]);
2827
2866
  return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
2828
2867
  }
2829
2868
 
2830
2869
  // src/hooks/useToast.ts
2831
- var import_react20 = require("react");
2870
+ var import_react21 = require("react");
2832
2871
  function useToast() {
2833
- const [items, setItems] = (0, import_react20.useState)([]);
2834
- const show = (0, import_react20.useCallback)((message, kind = "info") => {
2872
+ const [items, setItems] = (0, import_react21.useState)([]);
2873
+ const show = (0, import_react21.useCallback)((message, kind = "info") => {
2835
2874
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2836
2875
  setItems((prev) => [...prev, { id, message, kind }]);
2837
2876
  setTimeout(() => {
2838
2877
  setItems((prev) => prev.filter((t) => t.id !== id));
2839
2878
  }, 4e3);
2840
2879
  }, []);
2841
- const dismiss = (0, import_react20.useCallback)((id) => {
2880
+ const dismiss = (0, import_react21.useCallback)((id) => {
2842
2881
  setItems((prev) => prev.filter((t) => t.id !== id));
2843
2882
  }, []);
2844
2883
  return { items, show, dismiss };
@@ -2873,14 +2912,14 @@ function PreAuthShell({
2873
2912
  }
2874
2913
 
2875
2914
  // src/OnboardingFlow.tsx
2876
- var import_react22 = require("react");
2877
- var import_sdk17 = require("@hook-sdk/sdk");
2915
+ var import_react23 = require("react");
2916
+ var import_sdk18 = require("@hook-sdk/sdk");
2878
2917
 
2879
2918
  // src/hooks/useOnboardingStep.ts
2880
- var import_react21 = require("react");
2881
- var OnboardingStepContext = (0, import_react21.createContext)(null);
2919
+ var import_react22 = require("react");
2920
+ var OnboardingStepContext = (0, import_react22.createContext)(null);
2882
2921
  function useOnboardingStep() {
2883
- const ctx = (0, import_react21.useContext)(OnboardingStepContext);
2922
+ const ctx = (0, import_react22.useContext)(OnboardingStepContext);
2884
2923
  if (!ctx) {
2885
2924
  throw new Error(
2886
2925
  "[hook-template] useOnboardingStep must be used inside <OnboardingFlow>. (G75)"
@@ -2903,12 +2942,12 @@ function OnboardingFlow({
2903
2942
  onComplete,
2904
2943
  persistKey
2905
2944
  }) {
2906
- const [draft, setDraft, status] = (0, import_sdk17.usePersistedState)(persistKey, {});
2907
- const draftRef = (0, import_react22.useRef)(draft);
2945
+ const [draft, setDraft, status] = (0, import_sdk18.usePersistedState)(persistKey, {});
2946
+ const draftRef = (0, import_react23.useRef)(draft);
2908
2947
  draftRef.current = draft;
2909
2948
  const idx = readPersistedStepIdx(draft);
2910
2949
  const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
2911
- const setIdx = (0, import_react22.useCallback)(
2950
+ const setIdx = (0, import_react23.useCallback)(
2912
2951
  (n) => {
2913
2952
  setDraft((prev) => {
2914
2953
  const prevIdx = readPersistedStepIdx(prev);
@@ -2918,7 +2957,7 @@ function OnboardingFlow({
2918
2957
  },
2919
2958
  [setDraft]
2920
2959
  );
2921
- const setValue = (0, import_react22.useCallback)(
2960
+ const setValue = (0, import_react23.useCallback)(
2922
2961
  (patch) => {
2923
2962
  draftRef.current = { ...draftRef.current, ...patch };
2924
2963
  setDraft((prev) => ({ ...prev, ...patch }));
@@ -2926,11 +2965,23 @@ function OnboardingFlow({
2926
2965
  [setDraft]
2927
2966
  );
2928
2967
  const step = steps[clampedIdx];
2929
- const valid = (0, import_react22.useMemo)(
2968
+ const hookCtx = (0, import_sdk18.useHook)();
2969
+ const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
2970
+ (0, import_react23.useEffect)(() => {
2971
+ if (status.loading) return;
2972
+ if (!step) return;
2973
+ if (!track2) return;
2974
+ track2("onboarding_step_viewed", {
2975
+ step: step.id,
2976
+ step_index: clampedIdx,
2977
+ total_steps: steps.length
2978
+ });
2979
+ }, [step?.id, clampedIdx, steps.length, status.loading, track2]);
2980
+ const valid = (0, import_react23.useMemo)(
2930
2981
  () => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
2931
2982
  [draft, step]
2932
2983
  );
2933
- const next = (0, import_react22.useCallback)(() => {
2984
+ const next = (0, import_react23.useCallback)(() => {
2934
2985
  if (!step) return;
2935
2986
  const current = draftRef.current;
2936
2987
  const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
@@ -2941,8 +2992,8 @@ function OnboardingFlow({
2941
2992
  setIdx(clampedIdx + 1);
2942
2993
  }
2943
2994
  }, [clampedIdx, onComplete, step, steps.length, setIdx]);
2944
- const prevStep = (0, import_react22.useCallback)(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
2945
- const ctx = (0, import_react22.useMemo)(
2995
+ const prevStep = (0, import_react23.useCallback)(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
2996
+ const ctx = (0, import_react23.useMemo)(
2946
2997
  () => ({
2947
2998
  stepIndex: clampedIdx,
2948
2999
  totalSteps: steps.length,