@hook-sdk/template 0.1.3 → 0.2.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.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ComponentType, Component } from 'react';
3
3
  import { z } from 'zod';
4
4
  import * as _hook_sdk_sdk from '@hook-sdk/sdk';
5
+ import { ReminderSlot } from '@hook-sdk/sdk';
5
6
 
6
7
  declare const AppConfigSchema: z.ZodObject<{
7
8
  $schema: z.ZodOptional<z.ZodString>;
@@ -34,6 +35,12 @@ declare const AppConfigSchema: z.ZodObject<{
34
35
  pay_first: "pay_first";
35
36
  free: "free";
36
37
  }>>;
38
+ cpf_required: z.ZodOptional<z.ZodBoolean>;
39
+ trial_type: z.ZodOptional<z.ZodEnum<{
40
+ sem_cartao: "sem_cartao";
41
+ com_cartao: "com_cartao";
42
+ nenhum: "nenhum";
43
+ }>>;
37
44
  paywall_config: z.ZodObject<{
38
45
  title: z.ZodString;
39
46
  subtitle: z.ZodOptional<z.ZodString>;
@@ -43,6 +50,14 @@ declare const AppConfigSchema: z.ZodObject<{
43
50
  footerNote: z.ZodOptional<z.ZodString>;
44
51
  }, z.core.$strict>;
45
52
  }, z.core.$strict>;
53
+ auth: z.ZodOptional<z.ZodObject<{
54
+ has_forgot: z.ZodDefault<z.ZodBoolean>;
55
+ email_verification_required: z.ZodDefault<z.ZodBoolean>;
56
+ min_password_length: z.ZodDefault<z.ZodNumber>;
57
+ pre_auth_routes: z.ZodDefault<z.ZodArray<z.ZodString>>;
58
+ post_auth_landing: z.ZodDefault<z.ZodString>;
59
+ signup_anti_enum: z.ZodDefault<z.ZodBoolean>;
60
+ }, z.core.$strict>>;
46
61
  sdk_version_required: z.ZodString;
47
62
  template_version_required: z.ZodOptional<z.ZodString>;
48
63
  max_bundle_size_kb: z.ZodNumber;
@@ -71,6 +86,25 @@ interface AppRootProps {
71
86
  }
72
87
  declare function AppRoot({ config, children, Login, Signup, Forgot, Reset, Paywall, }: AppRootProps): react_jsx_runtime.JSX.Element;
73
88
 
89
+ interface PushPromptTexts {
90
+ cta: string;
91
+ declineCta: string;
92
+ iosInstallTitle: string;
93
+ iosInstallBody: string;
94
+ iosInstallCta?: string;
95
+ deniedTitle: string;
96
+ deniedBody: string;
97
+ unsupportedBody: string;
98
+ }
99
+ interface PushPromptProps {
100
+ texts: PushPromptTexts;
101
+ onSubscribed?: () => void;
102
+ onDeclined?: () => void;
103
+ onInstallRequested?: () => void;
104
+ className?: string;
105
+ }
106
+ declare function PushPrompt({ texts, onSubscribed, onDeclined, onInstallRequested, className }: PushPromptProps): react_jsx_runtime.JSX.Element | null;
107
+
74
108
  declare function DefaultSignupScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
75
109
 
76
110
  declare function DefaultForgotScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
@@ -279,18 +313,56 @@ declare function useSubscription(): {
279
313
  status: _hook_sdk_sdk.SdkSubscriptionStatus;
280
314
  };
281
315
 
282
- /**
283
- * Wrapper fino sobre `push` do SDK. MVP: stub retorna 'unsupported' no status
284
- * e subscribe/unsubscribe lançam SdkError. A shape segue o SDK real.
285
- */
316
+ type PushUiState = {
317
+ kind: 'unsupported';
318
+ } | {
319
+ kind: 'ios_needs_install';
320
+ } | {
321
+ kind: 'prompt';
322
+ } | {
323
+ kind: 'subscribed';
324
+ } | {
325
+ kind: 'denied';
326
+ } | {
327
+ kind: 'error';
328
+ code: string;
329
+ message: string;
330
+ };
286
331
  declare function usePush(): {
287
- status: _hook_sdk_sdk.PushStatus;
288
- subscribe: () => Promise<{
289
- subscribed: true;
290
- }>;
291
- unsubscribe: () => Promise<{
292
- subscribed: false;
332
+ state: PushUiState;
333
+ subscribe: () => Promise<void>;
334
+ unsubscribe: () => Promise<void>;
335
+ };
336
+
337
+ declare function useReminders(): {
338
+ reminders: ReminderSlot[];
339
+ loading: boolean;
340
+ setReminder: (input: {
341
+ slot: string;
342
+ timeLocal: string;
343
+ timezone: string;
344
+ enabled: boolean;
345
+ }) => Promise<void>;
346
+ deleteReminder: (slot: string) => Promise<void>;
347
+ schedule: (items: Parameters<(items: Array<{
348
+ slot: string;
349
+ sendAt: string | Date;
350
+ title: string;
351
+ body: string;
352
+ url?: string;
353
+ }>) => Promise<{
354
+ accepted: number;
355
+ rejected: number;
356
+ }>>[0]) => Promise<{
357
+ accepted: number;
358
+ rejected: number;
293
359
  }>;
360
+ setFallbacks: (items: Parameters<(items: Array<{
361
+ slot: string;
362
+ title: string;
363
+ body: string;
364
+ url?: string;
365
+ }>) => Promise<void>>[0]) => Promise<void>;
294
366
  };
295
367
 
296
368
  interface ToastItem {
@@ -308,4 +380,4 @@ declare function useToast(): {
308
380
  dismiss: (id: string) => void;
309
381
  };
310
382
 
311
- export { AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, LoadingState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, useAuth, useAuthPrimitives, useForgotForm, useLoginForm, usePaywallState, usePush, useResetForm, useSignupForm, useSubscription, useToast };
383
+ export { AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, LoadingState, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, useAuth, useAuthPrimitives, useForgotForm, useLoginForm, usePaywallState, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
package/dist/index.js CHANGED
@@ -777,23 +777,123 @@ function AppRoot({
777
777
  ] }) }) }) }) }) }) });
778
778
  }
779
779
 
780
- // src/defaults/EmptyState.tsx
780
+ // src/hooks/usePush.ts
781
+ import { useCallback as useCallback7, useEffect as useEffect5, useState as useState9 } from "react";
782
+ import { useHook as useHook9 } from "@hook-sdk/sdk";
783
+ function detectIosNeedsInstall() {
784
+ if (typeof navigator === "undefined" || typeof window === "undefined") return false;
785
+ const ua = navigator.userAgent || "";
786
+ const isIos = /iPhone|iPad|iPod/.test(ua);
787
+ if (!isIos) return false;
788
+ const mm = window.matchMedia?.("(display-mode: standalone)");
789
+ const standalone = mm?.matches === true;
790
+ const legacyStandalone = typeof navigator.standalone === "boolean" ? navigator.standalone : false;
791
+ return !(standalone || legacyStandalone);
792
+ }
793
+ function deriveState(push) {
794
+ if (!push.isAvailable()) {
795
+ if (detectIosNeedsInstall()) return { kind: "ios_needs_install" };
796
+ return { kind: "unsupported" };
797
+ }
798
+ const status = push.status();
799
+ if (status === "granted") return { kind: "subscribed" };
800
+ if (status === "denied") return { kind: "denied" };
801
+ if (status === "unsupported") {
802
+ if (detectIosNeedsInstall()) return { kind: "ios_needs_install" };
803
+ return { kind: "unsupported" };
804
+ }
805
+ return { kind: "prompt" };
806
+ }
807
+ function usePush() {
808
+ const { push } = useHook9();
809
+ const [state, setState] = useState9(() => deriveState(push));
810
+ useEffect5(() => {
811
+ setState(deriveState(push));
812
+ }, [push]);
813
+ const subscribe = useCallback7(async () => {
814
+ try {
815
+ await push.subscribe();
816
+ setState({ kind: "subscribed" });
817
+ } catch (e) {
818
+ const code = e?.code ?? "push.unknown";
819
+ const message = e?.message ?? "Push subscription failed";
820
+ if (code === "push.permission_denied") setState({ kind: "denied" });
821
+ else setState({ kind: "error", code, message });
822
+ throw e;
823
+ }
824
+ }, [push]);
825
+ const unsubscribe = useCallback7(async () => {
826
+ try {
827
+ await push.unsubscribe();
828
+ setState({ kind: "prompt" });
829
+ } catch (e) {
830
+ setState({ kind: "error", code: e?.code ?? "push.unknown", message: e?.message ?? "failed" });
831
+ throw e;
832
+ }
833
+ }, [push]);
834
+ return { state, subscribe, unsubscribe };
835
+ }
836
+
837
+ // src/components/PushPrompt.tsx
781
838
  import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
839
+ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
840
+ const { state, subscribe } = usePush();
841
+ if (state.kind === "subscribed") return null;
842
+ if (state.kind === "ios_needs_install") {
843
+ return /* @__PURE__ */ jsxs8("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
844
+ /* @__PURE__ */ jsx14("h3", { children: texts.iosInstallTitle }),
845
+ /* @__PURE__ */ jsx14("p", { children: texts.iosInstallBody }),
846
+ onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx14("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
847
+ ] });
848
+ }
849
+ if (state.kind === "denied") {
850
+ return /* @__PURE__ */ jsxs8("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
851
+ /* @__PURE__ */ jsx14("h3", { children: texts.deniedTitle }),
852
+ /* @__PURE__ */ jsx14("p", { children: texts.deniedBody })
853
+ ] });
854
+ }
855
+ if (state.kind === "unsupported") {
856
+ return /* @__PURE__ */ jsx14("div", { className, role: "region", children: /* @__PURE__ */ jsx14("p", { children: texts.unsupportedBody }) });
857
+ }
858
+ if (state.kind === "error") {
859
+ return /* @__PURE__ */ jsx14("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx14("p", { children: state.message }) });
860
+ }
861
+ return /* @__PURE__ */ jsxs8("div", { className, role: "region", children: [
862
+ /* @__PURE__ */ jsx14(
863
+ "button",
864
+ {
865
+ type: "button",
866
+ onClick: async () => {
867
+ try {
868
+ await subscribe();
869
+ onSubscribed?.();
870
+ } catch {
871
+ }
872
+ },
873
+ children: texts.cta
874
+ }
875
+ ),
876
+ onDeclined && /* @__PURE__ */ jsx14("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
877
+ ] });
878
+ }
879
+
880
+ // src/defaults/EmptyState.tsx
881
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
782
882
  function EmptyState({ title, description, action }) {
783
- return /* @__PURE__ */ jsxs8("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
784
- /* @__PURE__ */ jsx14("h2", { style: { marginBottom: 8 }, children: title }),
785
- description && /* @__PURE__ */ jsx14("p", { style: { opacity: 0.7 }, children: description }),
786
- action && /* @__PURE__ */ jsx14("div", { style: { marginTop: 16 }, children: action })
883
+ return /* @__PURE__ */ jsxs9("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
884
+ /* @__PURE__ */ jsx15("h2", { style: { marginBottom: 8 }, children: title }),
885
+ description && /* @__PURE__ */ jsx15("p", { style: { opacity: 0.7 }, children: description }),
886
+ action && /* @__PURE__ */ jsx15("div", { style: { marginTop: 16 }, children: action })
787
887
  ] });
788
888
  }
789
889
 
790
890
  // src/hooks/useAuthPrimitives.ts
791
- import { useEffect as useEffect5 } from "react";
792
- import { useHook as useHook9 } from "@hook-sdk/sdk";
891
+ import { useEffect as useEffect6 } from "react";
892
+ import { useHook as useHook10 } from "@hook-sdk/sdk";
793
893
  var warned = false;
794
894
  function useAuthPrimitives() {
795
- const { auth } = useHook9();
796
- useEffect5(() => {
895
+ const { auth } = useHook10();
896
+ useEffect6(() => {
797
897
  if (!warned && process.env.NODE_ENV !== "production") {
798
898
  warned = true;
799
899
  console.warn(
@@ -815,37 +915,63 @@ function useAuthPrimitives() {
815
915
  }
816
916
 
817
917
  // src/hooks/useSubscription.ts
818
- import { useHook as useHook10 } from "@hook-sdk/sdk";
918
+ import { useHook as useHook11 } from "@hook-sdk/sdk";
819
919
  function useSubscription() {
820
- const { subscription } = useHook10();
920
+ const { subscription } = useHook11();
821
921
  return {
822
922
  status: subscription.status()
823
923
  };
824
924
  }
825
925
 
826
- // src/hooks/usePush.ts
827
- import { useHook as useHook11 } from "@hook-sdk/sdk";
828
- function usePush() {
829
- const { push } = useHook11();
830
- return {
831
- status: push.status(),
832
- subscribe: push.subscribe,
833
- unsubscribe: push.unsubscribe
834
- };
926
+ // src/hooks/useReminders.ts
927
+ import { useCallback as useCallback8, useEffect as useEffect7, useState as useState10 } from "react";
928
+ import { useHook as useHook12 } from "@hook-sdk/sdk";
929
+ function useReminders() {
930
+ const { push } = useHook12();
931
+ const r = push.reminders;
932
+ const [reminders, setReminders] = useState10([]);
933
+ const [loading, setLoading] = useState10(true);
934
+ const reload = useCallback8(async () => {
935
+ setLoading(true);
936
+ try {
937
+ const next = await r.list();
938
+ setReminders(next);
939
+ } finally {
940
+ setLoading(false);
941
+ }
942
+ }, [r]);
943
+ useEffect7(() => {
944
+ void reload();
945
+ }, [reload]);
946
+ const setReminder = useCallback8(async (input) => {
947
+ await r.set(input);
948
+ await reload();
949
+ }, [r, reload]);
950
+ const deleteReminder = useCallback8(async (slot) => {
951
+ await r.delete(slot);
952
+ await reload();
953
+ }, [r, reload]);
954
+ const schedule = useCallback8(async (items) => {
955
+ return r.schedule(items);
956
+ }, [r]);
957
+ const setFallbacks = useCallback8(async (items) => {
958
+ return r.setFallbacks(items);
959
+ }, [r]);
960
+ return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
835
961
  }
836
962
 
837
963
  // src/hooks/useToast.ts
838
- import { useCallback as useCallback7, useState as useState9 } from "react";
964
+ import { useCallback as useCallback9, useState as useState11 } from "react";
839
965
  function useToast() {
840
- const [items, setItems] = useState9([]);
841
- const show = useCallback7((message, kind = "info") => {
966
+ const [items, setItems] = useState11([]);
967
+ const show = useCallback9((message, kind = "info") => {
842
968
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
843
969
  setItems((prev) => [...prev, { id, message, kind }]);
844
970
  setTimeout(() => {
845
971
  setItems((prev) => prev.filter((t) => t.id !== id));
846
972
  }, 4e3);
847
973
  }, []);
848
- const dismiss = useCallback7((id) => {
974
+ const dismiss = useCallback9((id) => {
849
975
  setItems((prev) => prev.filter((t) => t.id !== id));
850
976
  }, []);
851
977
  return { items, show, dismiss };
@@ -860,12 +986,14 @@ export {
860
986
  EmptyState,
861
987
  ErrorBoundary,
862
988
  LoadingState,
989
+ PushPrompt2 as PushPrompt,
863
990
  useAuth,
864
991
  useAuthPrimitives,
865
992
  useForgotForm,
866
993
  useLoginForm,
867
994
  usePaywallState,
868
995
  usePush,
996
+ useReminders,
869
997
  useResetForm,
870
998
  useSignupForm,
871
999
  useSubscription,