@hook-sdk/template 0.28.4 → 0.28.6
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 +1734 -1733
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1723 -1722
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2781,7 +2781,7 @@ function EmptyState({ title, description, action }) {
|
|
|
2781
2781
|
}
|
|
2782
2782
|
|
|
2783
2783
|
// src/defaults/CheckoutPageDefault.tsx
|
|
2784
|
-
import { useEffect as
|
|
2784
|
+
import { useEffect as useEffect13, useMemo as useMemo6, useState as useState11 } from "react";
|
|
2785
2785
|
import { useNavigate as useNavigate2 } from "react-router-dom";
|
|
2786
2786
|
|
|
2787
2787
|
// src/hooks/useCheckoutForm.ts
|
|
@@ -3030,860 +3030,9 @@ function usePlan() {
|
|
|
3030
3030
|
return plan;
|
|
3031
3031
|
}
|
|
3032
3032
|
|
|
3033
|
-
// src/
|
|
3034
|
-
import {
|
|
3035
|
-
|
|
3036
|
-
var PIX_PAYLOAD_KEY = "hook:paywall:pix-pending";
|
|
3037
|
-
function readIntent() {
|
|
3038
|
-
if (typeof window === "undefined") return {};
|
|
3039
|
-
try {
|
|
3040
|
-
const raw = sessionStorage.getItem(INTENT_KEY);
|
|
3041
|
-
if (!raw) return {};
|
|
3042
|
-
return JSON.parse(raw);
|
|
3043
|
-
} catch {
|
|
3044
|
-
return {};
|
|
3045
|
-
}
|
|
3046
|
-
}
|
|
3047
|
-
function formatBrl(cents) {
|
|
3048
|
-
return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(cents / 100);
|
|
3049
|
-
}
|
|
3050
|
-
function formatCardNumber(v) {
|
|
3051
|
-
const digits = v.replace(/\D/g, "").slice(0, 16);
|
|
3052
|
-
return digits.replace(/(.{4})/g, "$1 ").trim();
|
|
3053
|
-
}
|
|
3054
|
-
function formatExpiryMmAa(v) {
|
|
3055
|
-
const d = v.replace(/\D/g, "").slice(0, 4);
|
|
3056
|
-
if (d.length < 3) return d;
|
|
3057
|
-
return d.slice(0, 2) + "/" + d.slice(2);
|
|
3058
|
-
}
|
|
3059
|
-
function parseExpiryMmAa(v) {
|
|
3060
|
-
const d = v.replace(/\D/g, "");
|
|
3061
|
-
return { month: d.slice(0, 2), year: d.slice(2, 4) };
|
|
3062
|
-
}
|
|
3063
|
-
function formatCpf(v) {
|
|
3064
|
-
const d = v.replace(/\D/g, "").slice(0, 11);
|
|
3065
|
-
if (d.length <= 3) return d;
|
|
3066
|
-
if (d.length <= 6) return d.slice(0, 3) + "." + d.slice(3);
|
|
3067
|
-
if (d.length <= 9) return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6);
|
|
3068
|
-
return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6, 9) + "-" + d.slice(9);
|
|
3069
|
-
}
|
|
3070
|
-
function detectCardBrand(num) {
|
|
3071
|
-
const n = num.replace(/\s/g, "");
|
|
3072
|
-
if (/^4/.test(n)) return "VISA";
|
|
3073
|
-
if (/^(5[1-5]|2[2-7])/.test(n)) return "MASTER";
|
|
3074
|
-
if (/^3[47]/.test(n)) return "AMEX";
|
|
3075
|
-
if (/^(4011|4312|4389|4514|6011|6362|6363)/.test(n)) return "ELO";
|
|
3076
|
-
if (/^(606282|3841)/.test(n)) return "HIPER";
|
|
3077
|
-
return "";
|
|
3078
|
-
}
|
|
3079
|
-
function CheckoutPageDefault() {
|
|
3080
|
-
const navigate = useNavigate2();
|
|
3081
|
-
const plan = usePlan();
|
|
3082
|
-
const intent = useMemo5(readIntent, []);
|
|
3083
|
-
const defaultMethod = intent.method === "pix-auto" ? "pix-auto" : "card";
|
|
3084
|
-
const defaultCycle = intent.cycle === "MONTHLY" ? "MONTHLY" : "YEARLY";
|
|
3085
|
-
const form = useCheckoutForm({ defaultMethod, defaultCycle });
|
|
3086
|
-
const [expiryMmAa, setExpiryMmAa] = useState10("");
|
|
3087
|
-
useEffect11(() => {
|
|
3088
|
-
const { month, year } = parseExpiryMmAa(expiryMmAa);
|
|
3089
|
-
if (month !== form.card.expiryMonth || year !== form.card.expiryYear) {
|
|
3090
|
-
form.setCard({ expiryMonth: month, expiryYear: year });
|
|
3091
|
-
}
|
|
3092
|
-
}, [expiryMmAa]);
|
|
3093
|
-
useEffect11(() => {
|
|
3094
|
-
if (form.emailTaken && form.loginUrl) {
|
|
3095
|
-
const t = setTimeout(() => navigate(form.loginUrl), 1200);
|
|
3096
|
-
return () => clearTimeout(t);
|
|
3097
|
-
}
|
|
3098
|
-
}, [form.emailTaken, form.loginUrl, navigate]);
|
|
3099
|
-
const planInfo = plan.data ? {
|
|
3100
|
-
priceCents: plan.data.priceCents,
|
|
3101
|
-
yearlyPriceCents: plan.data.yearlyPriceCents,
|
|
3102
|
-
trialDays: plan.data.trialDays
|
|
3103
|
-
} : null;
|
|
3104
|
-
const annual = form.cycle === "YEARLY";
|
|
3105
|
-
const cyclePrice = useMemo5(() => {
|
|
3106
|
-
if (!planInfo) return null;
|
|
3107
|
-
return annual ? planInfo.yearlyPriceCents ?? planInfo.priceCents * 12 : planInfo.priceCents;
|
|
3108
|
-
}, [planInfo, annual]);
|
|
3109
|
-
const monthlyText = useMemo5(() => {
|
|
3110
|
-
if (!planInfo) return "";
|
|
3111
|
-
const monthly = annual && planInfo.yearlyPriceCents ? Math.round(planInfo.yearlyPriceCents / 12) : planInfo.priceCents;
|
|
3112
|
-
return formatBrl(monthly);
|
|
3113
|
-
}, [planInfo, annual]);
|
|
3114
|
-
const todayCents = useMemo5(() => {
|
|
3115
|
-
if (form.method === "pix-auto") return cyclePrice ?? 0;
|
|
3116
|
-
const trialDays2 = planInfo?.trialDays ?? 0;
|
|
3117
|
-
if (trialDays2 > 0) return 0;
|
|
3118
|
-
return cyclePrice ?? 0;
|
|
3119
|
-
}, [form.method, cyclePrice, planInfo]);
|
|
3120
|
-
const todayAmount = formatBrl(todayCents);
|
|
3121
|
-
const cyclePriceText = cyclePrice !== null ? formatBrl(cyclePrice) : "";
|
|
3122
|
-
const annualSavingsCents = useMemo5(() => {
|
|
3123
|
-
if (!planInfo || !planInfo.yearlyPriceCents) return 0;
|
|
3124
|
-
return planInfo.priceCents * 12 - planInfo.yearlyPriceCents;
|
|
3125
|
-
}, [planInfo]);
|
|
3126
|
-
const trialDays = planInfo?.trialDays ?? 7;
|
|
3127
|
-
const cardBrand = detectCardBrand(form.card.number);
|
|
3128
|
-
async function onSubmit(e) {
|
|
3129
|
-
e.preventDefault();
|
|
3130
|
-
const result = await form.submit();
|
|
3131
|
-
if (!result) return;
|
|
3132
|
-
if (form.method === "pix-auto" && result.pix_qr_payload) {
|
|
3133
|
-
try {
|
|
3134
|
-
sessionStorage.setItem(
|
|
3135
|
-
PIX_PAYLOAD_KEY,
|
|
3136
|
-
JSON.stringify({
|
|
3137
|
-
payload: result.pix_qr_payload,
|
|
3138
|
-
base64: result.pix_qr_base64 ?? null,
|
|
3139
|
-
subscriptionId: result.subscription_id,
|
|
3140
|
-
pixAuthorizationId: result.pix_authorization_id ?? null
|
|
3141
|
-
})
|
|
3142
|
-
);
|
|
3143
|
-
} catch {
|
|
3144
|
-
}
|
|
3145
|
-
navigate(result.redirect.replace(/^.*\/app\/[^/]+/, ""));
|
|
3146
|
-
return;
|
|
3147
|
-
}
|
|
3148
|
-
navigate(result.redirect.replace(/^.*\/app\/[^/]+/, "") || "/");
|
|
3149
|
-
}
|
|
3150
|
-
return /* @__PURE__ */ jsx28("div", { className: "flex-1 flex flex-col bg-background min-h-0", children: /* @__PURE__ */ jsxs19("form", { onSubmit, className: "flex-1 overflow-y-auto", children: [
|
|
3151
|
-
form.emailTaken ? /* @__PURE__ */ jsx28("div", { className: "px-5 pt-4", children: /* @__PURE__ */ jsxs19("div", { role: "alert", className: "rounded-2xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: [
|
|
3152
|
-
"Esse e-mail j\xE1 tem conta nesse app.",
|
|
3153
|
-
" ",
|
|
3154
|
-
/* @__PURE__ */ jsx28("a", { href: form.loginUrl ?? "/signin", className: "underline font-semibold", children: "Entrar agora" })
|
|
3155
|
-
] }) }) : null,
|
|
3156
|
-
form.error ? /* @__PURE__ */ jsx28("div", { className: "px-5 pt-4", children: /* @__PURE__ */ jsx28("div", { role: "alert", className: "rounded-2xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: form.error.message || "N\xE3o foi poss\xEDvel concluir o pagamento. Tente novamente." }) }) : null,
|
|
3157
|
-
/* @__PURE__ */ jsx28("div", { className: "px-5 pt-4", children: form.method === "card" ? /* @__PURE__ */ jsxs19("div", { className: "rounded-2xl bg-card border-[1.5px] border-foreground p-3.5", children: [
|
|
3158
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
3159
|
-
/* @__PURE__ */ jsx28(ShieldIcon, { className: "w-4 h-4" }),
|
|
3160
|
-
/* @__PURE__ */ jsx28("div", { className: "text-sm font-bold", children: "Voc\xEA N\xC3O ser\xE1 cobrada hoje" })
|
|
3161
|
-
] }),
|
|
3162
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex justify-between items-baseline text-sm text-muted-foreground", children: [
|
|
3163
|
-
/* @__PURE__ */ jsx28("span", { children: "R$ 0,00 agora" }),
|
|
3164
|
-
/* @__PURE__ */ jsx28("span", { className: "opacity-50", children: "\xB7" }),
|
|
3165
|
-
/* @__PURE__ */ jsxs19("span", { children: [
|
|
3166
|
-
monthlyText,
|
|
3167
|
-
"/m\xEAs ap\xF3s ",
|
|
3168
|
-
trialDays,
|
|
3169
|
-
" dias"
|
|
3170
|
-
] })
|
|
3171
|
-
] }),
|
|
3172
|
-
/* @__PURE__ */ jsxs19("div", { className: "mt-2.5 text-[11px] text-muted-foreground flex items-center gap-1.5", children: [
|
|
3173
|
-
/* @__PURE__ */ jsx28(BellIcon, { className: "w-2.5 h-2.5" }),
|
|
3174
|
-
"Avisamos por email 2 dias antes da primeira cobran\xE7a"
|
|
3175
|
-
] })
|
|
3176
|
-
] }) : /* @__PURE__ */ jsxs19("div", { className: "rounded-2xl p-3.5 bg-emerald-50 border-[1.5px] border-emerald-600/60", children: [
|
|
3177
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2 mb-2 text-emerald-900", children: [
|
|
3178
|
-
/* @__PURE__ */ jsx28(ShieldIcon, { className: "w-4 h-4" }),
|
|
3179
|
-
/* @__PURE__ */ jsxs19("div", { className: "text-sm font-bold", children: [
|
|
3180
|
-
"Garantia incondicional de ",
|
|
3181
|
-
trialDays,
|
|
3182
|
-
" dias"
|
|
3183
|
-
] })
|
|
3184
|
-
] }),
|
|
3185
|
-
/* @__PURE__ */ jsxs19("div", { className: "text-sm text-emerald-900 leading-snug", children: [
|
|
3186
|
-
"Voc\xEA paga ",
|
|
3187
|
-
/* @__PURE__ */ jsx28("b", { children: todayAmount }),
|
|
3188
|
-
" agora via Pix.",
|
|
3189
|
-
/* @__PURE__ */ jsx28("br", {}),
|
|
3190
|
-
"N\xE3o gostou em ",
|
|
3191
|
-
trialDays,
|
|
3192
|
-
" dias? Devolvemos ",
|
|
3193
|
-
/* @__PURE__ */ jsx28("b", { children: "100%" }),
|
|
3194
|
-
" sem perguntas \u2014 direto pelo app."
|
|
3195
|
-
] })
|
|
3196
|
-
] }) }),
|
|
3197
|
-
/* @__PURE__ */ jsxs19("section", { className: "px-5 pt-5", children: [
|
|
3198
|
-
/* @__PURE__ */ jsx28("h2", { className: "font-display text-2xl mb-3.5 leading-tight text-foreground", children: "Quase l\xE1." }),
|
|
3199
|
-
/* @__PURE__ */ jsx28(FieldLabel, { children: "Email" }),
|
|
3200
|
-
/* @__PURE__ */ jsx28(
|
|
3201
|
-
FieldInput,
|
|
3202
|
-
{
|
|
3203
|
-
type: "email",
|
|
3204
|
-
inputMode: "email",
|
|
3205
|
-
autoComplete: "email",
|
|
3206
|
-
autoCapitalize: "none",
|
|
3207
|
-
autoCorrect: "off",
|
|
3208
|
-
spellCheck: false,
|
|
3209
|
-
placeholder: "seu@email.com",
|
|
3210
|
-
value: form.email,
|
|
3211
|
-
onChange: form.setEmail,
|
|
3212
|
-
onBlur: form.markEmailTouched,
|
|
3213
|
-
error: form.emailError,
|
|
3214
|
-
valid: form.emailStatus === "available"
|
|
3215
|
-
}
|
|
3216
|
-
),
|
|
3217
|
-
!form.emailError && /* @__PURE__ */ jsx28(FieldHint, { children: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : "Voc\xEA vai usar este email para entrar no app" }),
|
|
3218
|
-
/* @__PURE__ */ jsx28("div", { className: "h-3" }),
|
|
3219
|
-
/* @__PURE__ */ jsx28(FieldLabel, { children: "Nome completo" }),
|
|
3220
|
-
/* @__PURE__ */ jsx28(
|
|
3221
|
-
FieldInput,
|
|
3222
|
-
{
|
|
3223
|
-
type: "text",
|
|
3224
|
-
autoComplete: "name",
|
|
3225
|
-
placeholder: "como est\xE1 no documento",
|
|
3226
|
-
value: form.name,
|
|
3227
|
-
onChange: form.setName,
|
|
3228
|
-
onBlur: form.markNameTouched,
|
|
3229
|
-
error: form.nameError,
|
|
3230
|
-
valid: !!form.name && !form.nameError
|
|
3231
|
-
}
|
|
3232
|
-
),
|
|
3233
|
-
/* @__PURE__ */ jsx28("div", { className: "h-3" }),
|
|
3234
|
-
/* @__PURE__ */ jsx28(FieldLabel, { children: "CPF" }),
|
|
3235
|
-
/* @__PURE__ */ jsx28(
|
|
3236
|
-
FieldInput,
|
|
3237
|
-
{
|
|
3238
|
-
type: "text",
|
|
3239
|
-
inputMode: "numeric",
|
|
3240
|
-
placeholder: "000.000.000-00",
|
|
3241
|
-
value: form.cpf,
|
|
3242
|
-
onChange: (v) => form.setCpf(formatCpf(v)),
|
|
3243
|
-
onBlur: form.markCpfTouched,
|
|
3244
|
-
error: form.cpfError,
|
|
3245
|
-
valid: !!form.cpf && !form.cpfError
|
|
3246
|
-
}
|
|
3247
|
-
),
|
|
3248
|
-
form.method === "card" ? /* @__PURE__ */ jsxs19(Fragment7, { children: [
|
|
3249
|
-
/* @__PURE__ */ jsx28("div", { className: "h-3" }),
|
|
3250
|
-
/* @__PURE__ */ jsx28(FieldLabel, { children: "Telefone" }),
|
|
3251
|
-
/* @__PURE__ */ jsx28(
|
|
3252
|
-
FieldInput,
|
|
3253
|
-
{
|
|
3254
|
-
type: "tel",
|
|
3255
|
-
inputMode: "tel",
|
|
3256
|
-
autoComplete: "tel",
|
|
3257
|
-
placeholder: "(11) 99999-9999",
|
|
3258
|
-
value: form.phone,
|
|
3259
|
-
onChange: form.setPhone,
|
|
3260
|
-
onBlur: form.markPhoneTouched,
|
|
3261
|
-
error: form.phoneError,
|
|
3262
|
-
valid: !!form.phone && !form.phoneError
|
|
3263
|
-
}
|
|
3264
|
-
),
|
|
3265
|
-
!form.phoneError && /* @__PURE__ */ jsx28(FieldHint, { children: "Usado pra confirmar pagamento e tratar disputas." })
|
|
3266
|
-
] }) : null
|
|
3267
|
-
] }),
|
|
3268
|
-
/* @__PURE__ */ jsxs19("section", { className: "px-5 pt-5", children: [
|
|
3269
|
-
/* @__PURE__ */ jsx28(FieldLabel, { children: "Forma de pagamento" }),
|
|
3270
|
-
/* @__PURE__ */ jsxs19("div", { role: "tablist", className: "flex gap-1.5 bg-muted p-1 rounded-xl", children: [
|
|
3271
|
-
/* @__PURE__ */ jsx28(
|
|
3272
|
-
TabButton,
|
|
3273
|
-
{
|
|
3274
|
-
active: form.method === "card",
|
|
3275
|
-
onClick: () => form.setMethod("card"),
|
|
3276
|
-
icon: /* @__PURE__ */ jsx28(CardIcon, { className: "w-3.5 h-3.5" }),
|
|
3277
|
-
label: "Cart\xE3o",
|
|
3278
|
-
subtitle: trialDays > 0 ? `${trialDays} dias gr\xE1tis` : "pague hoje",
|
|
3279
|
-
subtitleActiveClass: "text-emerald-700"
|
|
3280
|
-
}
|
|
3281
|
-
),
|
|
3282
|
-
/* @__PURE__ */ jsx28(
|
|
3283
|
-
TabButton,
|
|
3284
|
-
{
|
|
3285
|
-
active: form.method === "pix-auto",
|
|
3286
|
-
onClick: () => form.setMethod("pix-auto"),
|
|
3287
|
-
icon: /* @__PURE__ */ jsx28(PixIcon, { className: "w-3.5 h-3.5" }),
|
|
3288
|
-
label: "Pix",
|
|
3289
|
-
subtitle: `pague hoje \xB7 garantia ${trialDays}d`,
|
|
3290
|
-
subtitleActiveClass: "text-foreground/70"
|
|
3291
|
-
}
|
|
3292
|
-
)
|
|
3293
|
-
] })
|
|
3294
|
-
] }),
|
|
3295
|
-
form.method === "card" ? /* @__PURE__ */ jsxs19("section", { className: "px-5 pt-3.5", children: [
|
|
3296
|
-
/* @__PURE__ */ jsx28(FieldLabel, { children: "N\xFAmero do cart\xE3o" }),
|
|
3297
|
-
/* @__PURE__ */ jsxs19("div", { className: "relative", children: [
|
|
3298
|
-
/* @__PURE__ */ jsx28(
|
|
3299
|
-
FieldInput,
|
|
3300
|
-
{
|
|
3301
|
-
type: "text",
|
|
3302
|
-
inputMode: "numeric",
|
|
3303
|
-
autoComplete: "cc-number",
|
|
3304
|
-
placeholder: "0000 0000 0000 0000",
|
|
3305
|
-
value: form.card.number,
|
|
3306
|
-
onChange: (v) => form.setCard({ number: formatCardNumber(v) }),
|
|
3307
|
-
style: cardBrand ? { paddingRight: "4.5rem" } : void 0
|
|
3308
|
-
}
|
|
3309
|
-
),
|
|
3310
|
-
cardBrand && /* @__PURE__ */ jsx28("span", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-[10px] font-bold tracking-wide px-1.5 py-0.5 rounded bg-muted text-muted-foreground", children: cardBrand })
|
|
3311
|
-
] }),
|
|
3312
|
-
/* @__PURE__ */ jsx28("div", { className: "h-3" }),
|
|
3313
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex gap-2.5", children: [
|
|
3314
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex-1", children: [
|
|
3315
|
-
/* @__PURE__ */ jsx28(FieldLabel, { children: "Validade" }),
|
|
3316
|
-
/* @__PURE__ */ jsx28(
|
|
3317
|
-
FieldInput,
|
|
3318
|
-
{
|
|
3319
|
-
type: "text",
|
|
3320
|
-
inputMode: "numeric",
|
|
3321
|
-
autoComplete: "cc-exp",
|
|
3322
|
-
placeholder: "MM/AA",
|
|
3323
|
-
value: expiryMmAa,
|
|
3324
|
-
onChange: (v) => setExpiryMmAa(formatExpiryMmAa(v))
|
|
3325
|
-
}
|
|
3326
|
-
)
|
|
3327
|
-
] }),
|
|
3328
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex-1", children: [
|
|
3329
|
-
/* @__PURE__ */ jsx28(FieldLabel, { children: "CVV" }),
|
|
3330
|
-
/* @__PURE__ */ jsx28(
|
|
3331
|
-
FieldInput,
|
|
3332
|
-
{
|
|
3333
|
-
type: "text",
|
|
3334
|
-
inputMode: "numeric",
|
|
3335
|
-
autoComplete: "cc-csc",
|
|
3336
|
-
placeholder: "3 d\xEDgitos",
|
|
3337
|
-
value: form.card.ccv,
|
|
3338
|
-
onChange: (v) => form.setCard({ ccv: v.replace(/\D/g, "").slice(0, 4) })
|
|
3339
|
-
}
|
|
3340
|
-
)
|
|
3341
|
-
] })
|
|
3342
|
-
] }),
|
|
3343
|
-
/* @__PURE__ */ jsx28("div", { className: "h-3" }),
|
|
3344
|
-
/* @__PURE__ */ jsx28(FieldLabel, { children: "Nome no cart\xE3o" }),
|
|
3345
|
-
/* @__PURE__ */ jsx28(
|
|
3346
|
-
FieldInput,
|
|
3347
|
-
{
|
|
3348
|
-
type: "text",
|
|
3349
|
-
autoComplete: "cc-name",
|
|
3350
|
-
placeholder: "como est\xE1 no cart\xE3o",
|
|
3351
|
-
value: form.card.holderName,
|
|
3352
|
-
onChange: (v) => form.setCard({ holderName: v })
|
|
3353
|
-
}
|
|
3354
|
-
)
|
|
3355
|
-
] }) : /* @__PURE__ */ jsx28("section", { className: "px-5 pt-3.5", children: /* @__PURE__ */ jsxs19("div", { className: "rounded-2xl bg-card border border-border p-3.5 flex gap-3.5 items-center", children: [
|
|
3356
|
-
/* @__PURE__ */ jsx28("div", { className: "w-[72px] h-[72px] rounded-xl shrink-0 border-2 border-foreground relative overflow-hidden bg-muted", children: /* @__PURE__ */ jsx28("div", { className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[22px] h-[22px] bg-background flex items-center justify-center", children: /* @__PURE__ */ jsx28(PixIcon, { className: "w-3.5 h-3.5 text-foreground" }) }) }),
|
|
3357
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex-1", children: [
|
|
3358
|
-
/* @__PURE__ */ jsx28("div", { className: "text-xs font-bold uppercase tracking-wider text-muted-foreground", children: "pagamento em segundos" }),
|
|
3359
|
-
/* @__PURE__ */ jsxs19("div", { className: "text-sm text-foreground mt-1 leading-snug", children: [
|
|
3360
|
-
"Geramos seu ",
|
|
3361
|
-
/* @__PURE__ */ jsx28("b", { children: "QR Pix" }),
|
|
3362
|
-
" no pr\xF3ximo passo. Pague pelo app do banco e seu acesso libera ",
|
|
3363
|
-
/* @__PURE__ */ jsx28("b", { children: "imediatamente" }),
|
|
3364
|
-
"."
|
|
3365
|
-
] })
|
|
3366
|
-
] })
|
|
3367
|
-
] }) }),
|
|
3368
|
-
/* @__PURE__ */ jsx28("section", { className: "px-5 pt-5", children: /* @__PURE__ */ jsxs19("div", { className: "bg-muted rounded-2xl p-4", children: [
|
|
3369
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex justify-between mb-2.5", children: [
|
|
3370
|
-
/* @__PURE__ */ jsxs19("div", { children: [
|
|
3371
|
-
/* @__PURE__ */ jsx28("div", { className: "text-sm font-semibold text-foreground", children: annual ? "Plano Anual" : "Plano Mensal" }),
|
|
3372
|
-
/* @__PURE__ */ jsx28("div", { className: "text-[11px] text-muted-foreground", children: "Coach" })
|
|
3373
|
-
] }),
|
|
3374
|
-
/* @__PURE__ */ jsxs19("div", { className: "text-right", children: [
|
|
3375
|
-
/* @__PURE__ */ jsxs19("div", { className: "text-sm font-bold text-foreground", children: [
|
|
3376
|
-
cyclePriceText,
|
|
3377
|
-
"/",
|
|
3378
|
-
annual ? "ano" : "m\xEAs"
|
|
3379
|
-
] }),
|
|
3380
|
-
annual && annualSavingsCents > 0 && /* @__PURE__ */ jsxs19("div", { className: "text-[11px] text-emerald-700 font-semibold", children: [
|
|
3381
|
-
"economia ",
|
|
3382
|
-
formatBrl(annualSavingsCents)
|
|
3383
|
-
] })
|
|
3384
|
-
] })
|
|
3385
|
-
] }),
|
|
3386
|
-
/* @__PURE__ */ jsx28("div", { className: "h-px bg-border my-3" }),
|
|
3387
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex justify-between items-baseline", children: [
|
|
3388
|
-
/* @__PURE__ */ jsxs19("div", { children: [
|
|
3389
|
-
/* @__PURE__ */ jsx28("div", { className: "text-sm font-bold text-foreground", children: "Voc\xEA paga hoje" }),
|
|
3390
|
-
form.method === "card" && trialDays > 0 && /* @__PURE__ */ jsxs19("div", { className: "text-[11px] text-muted-foreground mt-0.5", children: [
|
|
3391
|
-
"cobran\xE7a inicia no dia ",
|
|
3392
|
-
trialDays
|
|
3393
|
-
] }),
|
|
3394
|
-
form.method === "pix-auto" && /* @__PURE__ */ jsxs19("div", { className: "text-[11px] text-emerald-700 mt-0.5 font-semibold", children: [
|
|
3395
|
-
"reembolso garantido at\xE9 o dia ",
|
|
3396
|
-
trialDays
|
|
3397
|
-
] })
|
|
3398
|
-
] }),
|
|
3399
|
-
/* @__PURE__ */ jsx28("div", { className: "text-2xl font-bold font-display tracking-tight text-foreground", children: todayAmount })
|
|
3400
|
-
] })
|
|
3401
|
-
] }) }),
|
|
3402
|
-
/* @__PURE__ */ jsx28("div", { className: "h-4" }),
|
|
3403
|
-
/* @__PURE__ */ jsxs19("div", { className: "sticky bottom-0 px-5 pt-3.5 pb-6 bg-gradient-to-b from-transparent to-background", children: [
|
|
3404
|
-
/* @__PURE__ */ jsx28(
|
|
3405
|
-
"button",
|
|
3406
|
-
{
|
|
3407
|
-
type: "submit",
|
|
3408
|
-
disabled: !form.canSubmit,
|
|
3409
|
-
className: "w-full rounded-full bg-primary text-primary-foreground min-h-14 px-5 text-base font-bold inline-flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg",
|
|
3410
|
-
children: form.submitting ? /* @__PURE__ */ jsxs19(Fragment7, { children: [
|
|
3411
|
-
/* @__PURE__ */ jsx28(Spinner2, {}),
|
|
3412
|
-
" ",
|
|
3413
|
-
form.method === "pix-auto" ? "Gerando QR\u2026" : "Confirmando\u2026"
|
|
3414
|
-
] }) : form.method === "card" ? /* @__PURE__ */ jsxs19(Fragment7, { children: [
|
|
3415
|
-
/* @__PURE__ */ jsx28(LockIcon, { className: "w-3.5 h-3.5" }),
|
|
3416
|
-
" Confirmar e come\xE7ar gr\xE1tis"
|
|
3417
|
-
] }) : /* @__PURE__ */ jsxs19(Fragment7, { children: [
|
|
3418
|
-
/* @__PURE__ */ jsx28(PixIcon, { className: "w-3.5 h-3.5" }),
|
|
3419
|
-
" Gerar QR \xB7 pagar ",
|
|
3420
|
-
todayAmount
|
|
3421
|
-
] })
|
|
3422
|
-
}
|
|
3423
|
-
),
|
|
3424
|
-
/* @__PURE__ */ jsxs19("div", { className: "text-center mt-2.5 text-xs text-muted-foreground", children: [
|
|
3425
|
-
"Ao continuar, voc\xEA concorda com nossos ",
|
|
3426
|
-
/* @__PURE__ */ jsx28("u", { children: "Termos" }),
|
|
3427
|
-
". Pagamento seguro Asaas."
|
|
3428
|
-
] }),
|
|
3429
|
-
/* @__PURE__ */ jsxs19("div", { className: "mt-3 flex items-center justify-center gap-3.5 text-[11px] text-muted-foreground", children: [
|
|
3430
|
-
/* @__PURE__ */ jsxs19("span", { className: "inline-flex items-center gap-1", children: [
|
|
3431
|
-
/* @__PURE__ */ jsx28(LockIcon, { className: "w-2.5 h-2.5 opacity-60" }),
|
|
3432
|
-
" SSL 256-bit"
|
|
3433
|
-
] }),
|
|
3434
|
-
/* @__PURE__ */ jsx28("span", { className: "w-px h-2.5 bg-border" }),
|
|
3435
|
-
/* @__PURE__ */ jsx28("span", { children: "Pagamento via Asaas" }),
|
|
3436
|
-
/* @__PURE__ */ jsx28("span", { className: "w-px h-2.5 bg-border" }),
|
|
3437
|
-
/* @__PURE__ */ jsxs19("span", { children: [
|
|
3438
|
-
"Garantia ",
|
|
3439
|
-
trialDays,
|
|
3440
|
-
" dias"
|
|
3441
|
-
] })
|
|
3442
|
-
] })
|
|
3443
|
-
] })
|
|
3444
|
-
] }) });
|
|
3445
|
-
}
|
|
3446
|
-
function FieldLabel({ children }) {
|
|
3447
|
-
return /* @__PURE__ */ jsx28("div", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-1.5", children });
|
|
3448
|
-
}
|
|
3449
|
-
function FieldInput(props) {
|
|
3450
|
-
const baseClass = "w-full px-4 rounded-xl bg-card text-base text-foreground outline-none border-[1.5px] transition-colors";
|
|
3451
|
-
const stateClass = props.error ? "border-destructive focus:border-destructive" : props.valid ? "border-emerald-600 focus:border-emerald-700" : "border-border focus:border-foreground";
|
|
3452
|
-
return /* @__PURE__ */ jsxs19(Fragment7, { children: [
|
|
3453
|
-
/* @__PURE__ */ jsx28(
|
|
3454
|
-
"input",
|
|
3455
|
-
{
|
|
3456
|
-
type: props.type ?? "text",
|
|
3457
|
-
inputMode: props.inputMode,
|
|
3458
|
-
autoComplete: props.autoComplete,
|
|
3459
|
-
autoCapitalize: props.autoCapitalize,
|
|
3460
|
-
autoCorrect: props.autoCorrect,
|
|
3461
|
-
spellCheck: props.spellCheck,
|
|
3462
|
-
placeholder: props.placeholder,
|
|
3463
|
-
value: props.value,
|
|
3464
|
-
onChange: (e) => props.onChange(e.target.value),
|
|
3465
|
-
onBlur: props.onBlur,
|
|
3466
|
-
style: { height: "52px", ...props.style },
|
|
3467
|
-
className: `${baseClass} ${stateClass}`
|
|
3468
|
-
}
|
|
3469
|
-
),
|
|
3470
|
-
props.error ? /* @__PURE__ */ jsx28("div", { className: "mt-1.5 text-xs text-destructive font-medium", children: props.error }) : null
|
|
3471
|
-
] });
|
|
3472
|
-
}
|
|
3473
|
-
function FieldHint({ children }) {
|
|
3474
|
-
return /* @__PURE__ */ jsx28("div", { className: "mt-1.5 text-xs text-muted-foreground", children });
|
|
3475
|
-
}
|
|
3476
|
-
function TabButton({ active, onClick, icon, label, subtitle, subtitleActiveClass }) {
|
|
3477
|
-
return /* @__PURE__ */ jsxs19(
|
|
3478
|
-
"button",
|
|
3479
|
-
{
|
|
3480
|
-
type: "button",
|
|
3481
|
-
role: "tab",
|
|
3482
|
-
"aria-selected": active,
|
|
3483
|
-
onClick,
|
|
3484
|
-
className: `flex-1 flex flex-col items-center justify-center gap-0.5 py-2.5 px-1.5 rounded-lg text-sm font-semibold transition-colors ${active ? "bg-card text-foreground shadow-sm" : "bg-transparent text-muted-foreground"}`,
|
|
3485
|
-
style: { minHeight: 56 },
|
|
3486
|
-
children: [
|
|
3487
|
-
/* @__PURE__ */ jsxs19("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
3488
|
-
icon,
|
|
3489
|
-
" ",
|
|
3490
|
-
label
|
|
3491
|
-
] }),
|
|
3492
|
-
/* @__PURE__ */ jsx28("span", { className: `text-[10px] font-medium ${active ? subtitleActiveClass : "text-muted-foreground/70"}`, children: subtitle })
|
|
3493
|
-
]
|
|
3494
|
-
}
|
|
3495
|
-
);
|
|
3496
|
-
}
|
|
3497
|
-
function CardIcon({ className }) {
|
|
3498
|
-
return /* @__PURE__ */ jsxs19("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
3499
|
-
/* @__PURE__ */ jsx28("rect", { x: "2.5", y: "5.5", width: "19", height: "13", rx: "2.5" }),
|
|
3500
|
-
/* @__PURE__ */ jsx28("path", { d: "M2.5 10h19" })
|
|
3501
|
-
] });
|
|
3502
|
-
}
|
|
3503
|
-
function PixIcon({ className }) {
|
|
3504
|
-
return /* @__PURE__ */ jsx28("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx28("path", { d: "M12 2L2 12l10 10 10-10L12 2zm0 4.83L17.17 12 12 17.17 6.83 12 12 6.83z" }) });
|
|
3505
|
-
}
|
|
3506
|
-
function LockIcon({ className }) {
|
|
3507
|
-
return /* @__PURE__ */ jsxs19("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
3508
|
-
/* @__PURE__ */ jsx28("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2.5" }),
|
|
3509
|
-
/* @__PURE__ */ jsx28("path", { d: "M7.5 10V7a4.5 4.5 0 119 0v3" })
|
|
3510
|
-
] });
|
|
3511
|
-
}
|
|
3512
|
-
function ShieldIcon({ className }) {
|
|
3513
|
-
return /* @__PURE__ */ jsxs19("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
3514
|
-
/* @__PURE__ */ jsx28("path", { d: "M12 2.5l8 3v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10v-6l8-3z" }),
|
|
3515
|
-
/* @__PURE__ */ jsx28("path", { d: "M9 12l2 2 4-4" })
|
|
3516
|
-
] });
|
|
3517
|
-
}
|
|
3518
|
-
function BellIcon({ className }) {
|
|
3519
|
-
return /* @__PURE__ */ jsxs19("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
3520
|
-
/* @__PURE__ */ jsx28("path", { d: "M6 17V11a6 6 0 1112 0v6l1.5 2H4.5L6 17z" }),
|
|
3521
|
-
/* @__PURE__ */ jsx28("path", { d: "M10 21a2 2 0 004 0" })
|
|
3522
|
-
] });
|
|
3523
|
-
}
|
|
3524
|
-
function Spinner2() {
|
|
3525
|
-
return /* @__PURE__ */ jsx28(
|
|
3526
|
-
"span",
|
|
3527
|
-
{
|
|
3528
|
-
className: "w-4 h-4 rounded-full border-2 border-white/40 border-t-white",
|
|
3529
|
-
style: { animation: "spin 0.7s linear infinite" }
|
|
3530
|
-
}
|
|
3531
|
-
);
|
|
3532
|
-
}
|
|
3533
|
-
|
|
3534
|
-
// src/defaults/PixWaitingPageDefault.tsx
|
|
3535
|
-
import { useEffect as useEffect12, useMemo as useMemo6, useState as useState11 } from "react";
|
|
3536
|
-
import { useNavigate as useNavigate3 } from "react-router-dom";
|
|
3537
|
-
import { useHook as useHook12 } from "@hook-sdk/sdk";
|
|
3538
|
-
import { jsx as jsx29, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3539
|
-
var PIX_PAYLOAD_KEY2 = "hook:paywall:pix-pending";
|
|
3540
|
-
var TIMEOUT_MS = 30 * 60 * 1e3;
|
|
3541
|
-
function readPixPayload() {
|
|
3542
|
-
if (typeof window === "undefined") return null;
|
|
3543
|
-
try {
|
|
3544
|
-
const raw = sessionStorage.getItem(PIX_PAYLOAD_KEY2);
|
|
3545
|
-
if (!raw) return null;
|
|
3546
|
-
return JSON.parse(raw);
|
|
3547
|
-
} catch {
|
|
3548
|
-
return null;
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
function clearPixPayload() {
|
|
3552
|
-
if (typeof window === "undefined") return;
|
|
3553
|
-
try {
|
|
3554
|
-
sessionStorage.removeItem(PIX_PAYLOAD_KEY2);
|
|
3555
|
-
} catch {
|
|
3556
|
-
}
|
|
3557
|
-
}
|
|
3558
|
-
function PixWaitingPageDefault() {
|
|
3559
|
-
const navigate = useNavigate3();
|
|
3560
|
-
const { subscription } = useHook12();
|
|
3561
|
-
const payload = useMemo6(readPixPayload, []);
|
|
3562
|
-
const [copied, setCopied] = useState11(false);
|
|
3563
|
-
const [timedOut, setTimedOut] = useState11(false);
|
|
3564
|
-
useEffect12(() => {
|
|
3565
|
-
if (!payload) navigate("/paywall/checkout", { replace: true });
|
|
3566
|
-
}, [payload, navigate]);
|
|
3567
|
-
useEffect12(() => {
|
|
3568
|
-
const t = setTimeout(() => setTimedOut(true), TIMEOUT_MS);
|
|
3569
|
-
return () => clearTimeout(t);
|
|
3570
|
-
}, []);
|
|
3571
|
-
const hasAccess = subscription.hasAccess;
|
|
3572
|
-
useEffect12(() => {
|
|
3573
|
-
if (hasAccess) {
|
|
3574
|
-
clearPixPayload();
|
|
3575
|
-
navigate("/", { replace: true });
|
|
3576
|
-
}
|
|
3577
|
-
}, [hasAccess, navigate]);
|
|
3578
|
-
async function copyPayload() {
|
|
3579
|
-
if (!payload?.payload) return;
|
|
3580
|
-
try {
|
|
3581
|
-
await navigator.clipboard.writeText(payload.payload);
|
|
3582
|
-
setCopied(true);
|
|
3583
|
-
setTimeout(() => setCopied(false), 2e3);
|
|
3584
|
-
} catch {
|
|
3585
|
-
}
|
|
3586
|
-
}
|
|
3587
|
-
if (!payload) return null;
|
|
3588
|
-
if (timedOut) {
|
|
3589
|
-
return /* @__PURE__ */ jsxs20("div", { className: "flex-1 flex flex-col items-center justify-center px-6 py-10 text-center bg-background space-y-4", children: [
|
|
3590
|
-
/* @__PURE__ */ jsx29("h1", { className: "font-display text-2xl text-foreground", children: "PIX expirado" }),
|
|
3591
|
-
/* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: "O tempo pra pagar acabou. Gere um novo PIX." }),
|
|
3592
|
-
/* @__PURE__ */ jsx29(
|
|
3593
|
-
"button",
|
|
3594
|
-
{
|
|
3595
|
-
onClick: () => {
|
|
3596
|
-
clearPixPayload();
|
|
3597
|
-
navigate("/paywall/checkout", { replace: true });
|
|
3598
|
-
},
|
|
3599
|
-
className: "rounded-xl bg-primary px-6 py-3 text-base font-semibold text-primary-foreground",
|
|
3600
|
-
children: "Tentar novamente"
|
|
3601
|
-
}
|
|
3602
|
-
)
|
|
3603
|
-
] });
|
|
3604
|
-
}
|
|
3605
|
-
return /* @__PURE__ */ jsxs20("div", { className: "flex-1 flex flex-col items-center px-6 py-8 bg-background space-y-6", children: [
|
|
3606
|
-
/* @__PURE__ */ jsxs20("header", { className: "text-center space-y-2", children: [
|
|
3607
|
-
/* @__PURE__ */ jsx29("h1", { className: "font-display text-2xl text-foreground", children: "Pague o PIX" }),
|
|
3608
|
-
/* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: "Escaneie o QR Code no app do seu banco. O acesso libera assim que confirmarmos o pagamento." })
|
|
3609
|
-
] }),
|
|
3610
|
-
payload.base64 ? /* @__PURE__ */ jsx29(
|
|
3611
|
-
"img",
|
|
3612
|
-
{
|
|
3613
|
-
src: `data:image/png;base64,${payload.base64}`,
|
|
3614
|
-
alt: "QR Code PIX",
|
|
3615
|
-
className: "w-64 h-64 rounded-2xl border border-border bg-card p-2"
|
|
3616
|
-
}
|
|
3617
|
-
) : /* @__PURE__ */ jsx29("div", { className: "w-64 h-64 rounded-2xl border border-border bg-card flex items-center justify-center text-sm text-muted-foreground", children: "QR indispon\xEDvel" }),
|
|
3618
|
-
payload.payload ? /* @__PURE__ */ jsx29(
|
|
3619
|
-
"button",
|
|
3620
|
-
{
|
|
3621
|
-
onClick: copyPayload,
|
|
3622
|
-
className: "w-full max-w-xs rounded-xl border border-border bg-card px-4 py-3 text-sm font-medium text-foreground",
|
|
3623
|
-
children: copied ? "\u2713 Copiado!" : "Copiar c\xF3digo PIX"
|
|
3624
|
-
}
|
|
3625
|
-
) : null,
|
|
3626
|
-
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
3627
|
-
/* @__PURE__ */ jsx29("span", { className: "inline-block w-2 h-2 rounded-full bg-primary animate-pulse" }),
|
|
3628
|
-
"Aguardando pagamento\u2026"
|
|
3629
|
-
] }),
|
|
3630
|
-
/* @__PURE__ */ jsx29("p", { className: "text-center text-xs text-muted-foreground", children: "Pode fechar essa janela \u2014 tamb\xE9m enviamos um link de acesso pro seu e-mail." })
|
|
3631
|
-
] });
|
|
3632
|
-
}
|
|
3633
|
-
|
|
3634
|
-
// src/hooks/useLoginForm.ts
|
|
3635
|
-
import { useCallback as useCallback7, useMemo as useMemo7, useState as useState12 } from "react";
|
|
3636
|
-
import { useHook as useHook13 } from "@hook-sdk/sdk";
|
|
3637
|
-
var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3638
|
-
var MIN_PASSWORD = 8;
|
|
3639
|
-
function useLoginForm() {
|
|
3640
|
-
const { auth } = useHook13();
|
|
3641
|
-
const [email, setEmail] = useState12("");
|
|
3642
|
-
const [password, setPassword] = useState12("");
|
|
3643
|
-
const [submitting, setSubmitting] = useState12(false);
|
|
3644
|
-
const [error, setError] = useState12(null);
|
|
3645
|
-
const [touchedEmail, setTouchedEmail] = useState12(false);
|
|
3646
|
-
const [touchedPassword, setTouchedPassword] = useState12(false);
|
|
3647
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = useState12(false);
|
|
3648
|
-
const validateEmail = useMemo7(() => {
|
|
3649
|
-
if (email.length === 0) return null;
|
|
3650
|
-
if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
3651
|
-
return null;
|
|
3652
|
-
}, [email]);
|
|
3653
|
-
const validatePassword = useMemo7(() => {
|
|
3654
|
-
if (password.length === 0) return null;
|
|
3655
|
-
if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
|
|
3656
|
-
return null;
|
|
3657
|
-
}, [password]);
|
|
3658
|
-
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
3659
|
-
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
3660
|
-
const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
|
|
3661
|
-
const submit = useCallback7(async () => {
|
|
3662
|
-
setFormSubmitAttempted(true);
|
|
3663
|
-
if (!canSubmit) return false;
|
|
3664
|
-
setSubmitting(true);
|
|
3665
|
-
setError(null);
|
|
3666
|
-
try {
|
|
3667
|
-
await auth.login({ email, password });
|
|
3668
|
-
return true;
|
|
3669
|
-
} catch (err) {
|
|
3670
|
-
setError(mapSdkError(err));
|
|
3671
|
-
return false;
|
|
3672
|
-
} finally {
|
|
3673
|
-
setSubmitting(false);
|
|
3674
|
-
}
|
|
3675
|
-
}, [auth, email, password, canSubmit]);
|
|
3676
|
-
return {
|
|
3677
|
-
email,
|
|
3678
|
-
setEmail,
|
|
3679
|
-
emailError,
|
|
3680
|
-
markEmailTouched: () => setTouchedEmail(true),
|
|
3681
|
-
password,
|
|
3682
|
-
setPassword,
|
|
3683
|
-
passwordError,
|
|
3684
|
-
markPasswordTouched: () => setTouchedPassword(true),
|
|
3685
|
-
formSubmitAttempted,
|
|
3686
|
-
submit,
|
|
3687
|
-
submitting,
|
|
3688
|
-
canSubmit,
|
|
3689
|
-
error,
|
|
3690
|
-
loginWithGoogle: () => auth.loginWithGoogle()
|
|
3691
|
-
};
|
|
3692
|
-
}
|
|
3693
|
-
|
|
3694
|
-
// src/hooks/useSignupForm.ts
|
|
3695
|
-
import { useCallback as useCallback8, useMemo as useMemo8, useState as useState13 } from "react";
|
|
3696
|
-
import { useHook as useHook14 } from "@hook-sdk/sdk";
|
|
3697
|
-
var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3698
|
-
var MIN_PASSWORD2 = 8;
|
|
3699
|
-
function useSignupForm() {
|
|
3700
|
-
const { auth } = useHook14();
|
|
3701
|
-
const [name, setName] = useState13("");
|
|
3702
|
-
const [email, setEmail] = useState13("");
|
|
3703
|
-
const [password, setPassword] = useState13("");
|
|
3704
|
-
const [submitting, setSubmitting] = useState13(false);
|
|
3705
|
-
const [error, setError] = useState13(null);
|
|
3706
|
-
const [touchedName, setTouchedName] = useState13(false);
|
|
3707
|
-
const [touchedEmail, setTouchedEmail] = useState13(false);
|
|
3708
|
-
const [touchedPassword, setTouchedPassword] = useState13(false);
|
|
3709
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = useState13(false);
|
|
3710
|
-
const validateName = useMemo8(() => {
|
|
3711
|
-
if (name.length === 0) return null;
|
|
3712
|
-
if (name.trim().length < 2) return "Nome muito curto.";
|
|
3713
|
-
return null;
|
|
3714
|
-
}, [name]);
|
|
3715
|
-
const validateEmail = useMemo8(() => {
|
|
3716
|
-
if (email.length === 0) return null;
|
|
3717
|
-
if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
3718
|
-
return null;
|
|
3719
|
-
}, [email]);
|
|
3720
|
-
const validatePassword = useMemo8(() => {
|
|
3721
|
-
if (password.length === 0) return null;
|
|
3722
|
-
if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
|
|
3723
|
-
return null;
|
|
3724
|
-
}, [password]);
|
|
3725
|
-
const nameError = touchedName || formSubmitAttempted ? validateName : null;
|
|
3726
|
-
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
3727
|
-
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
3728
|
-
const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
|
|
3729
|
-
const submit = useCallback8(async () => {
|
|
3730
|
-
setFormSubmitAttempted(true);
|
|
3731
|
-
if (!canSubmit) return false;
|
|
3732
|
-
setSubmitting(true);
|
|
3733
|
-
setError(null);
|
|
3734
|
-
try {
|
|
3735
|
-
await auth.signup({ name, email, password });
|
|
3736
|
-
return true;
|
|
3737
|
-
} catch (err) {
|
|
3738
|
-
setError(mapSdkError(err));
|
|
3739
|
-
return false;
|
|
3740
|
-
} finally {
|
|
3741
|
-
setSubmitting(false);
|
|
3742
|
-
}
|
|
3743
|
-
}, [auth, name, email, password, canSubmit]);
|
|
3744
|
-
return {
|
|
3745
|
-
name,
|
|
3746
|
-
setName,
|
|
3747
|
-
nameError,
|
|
3748
|
-
markNameTouched: () => setTouchedName(true),
|
|
3749
|
-
email,
|
|
3750
|
-
setEmail,
|
|
3751
|
-
emailError,
|
|
3752
|
-
markEmailTouched: () => setTouchedEmail(true),
|
|
3753
|
-
password,
|
|
3754
|
-
setPassword,
|
|
3755
|
-
passwordError,
|
|
3756
|
-
markPasswordTouched: () => setTouchedPassword(true),
|
|
3757
|
-
formSubmitAttempted,
|
|
3758
|
-
submit,
|
|
3759
|
-
submitting,
|
|
3760
|
-
canSubmit,
|
|
3761
|
-
error,
|
|
3762
|
-
loginWithGoogle: () => auth.loginWithGoogle()
|
|
3763
|
-
};
|
|
3764
|
-
}
|
|
3765
|
-
|
|
3766
|
-
// src/hooks/useForgotForm.ts
|
|
3767
|
-
import { useCallback as useCallback9, useMemo as useMemo9, useState as useState14 } from "react";
|
|
3768
|
-
import { useHook as useHook15 } from "@hook-sdk/sdk";
|
|
3769
|
-
var EMAIL_RE4 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3770
|
-
function useForgotForm() {
|
|
3771
|
-
const { auth } = useHook15();
|
|
3772
|
-
const [email, setEmail] = useState14("");
|
|
3773
|
-
const [submitting, setSubmitting] = useState14(false);
|
|
3774
|
-
const [sent, setSent] = useState14(false);
|
|
3775
|
-
const [error, setError] = useState14(null);
|
|
3776
|
-
const [touchedEmail, setTouchedEmail] = useState14(false);
|
|
3777
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = useState14(false);
|
|
3778
|
-
const validateEmail = useMemo9(() => {
|
|
3779
|
-
if (email.length === 0) return null;
|
|
3780
|
-
if (!EMAIL_RE4.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
3781
|
-
return null;
|
|
3782
|
-
}, [email]);
|
|
3783
|
-
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
3784
|
-
const canSubmit = email.length > 0 && validateEmail === null && !submitting;
|
|
3785
|
-
const submit = useCallback9(async () => {
|
|
3786
|
-
setFormSubmitAttempted(true);
|
|
3787
|
-
if (!canSubmit) return false;
|
|
3788
|
-
setSubmitting(true);
|
|
3789
|
-
setError(null);
|
|
3790
|
-
try {
|
|
3791
|
-
await auth.forgot({ email });
|
|
3792
|
-
setSent(true);
|
|
3793
|
-
return true;
|
|
3794
|
-
} catch (err) {
|
|
3795
|
-
setError(mapSdkError(err));
|
|
3796
|
-
return false;
|
|
3797
|
-
} finally {
|
|
3798
|
-
setSubmitting(false);
|
|
3799
|
-
}
|
|
3800
|
-
}, [auth, email, canSubmit]);
|
|
3801
|
-
return {
|
|
3802
|
-
email,
|
|
3803
|
-
setEmail,
|
|
3804
|
-
emailError,
|
|
3805
|
-
markEmailTouched: () => setTouchedEmail(true),
|
|
3806
|
-
formSubmitAttempted,
|
|
3807
|
-
submit,
|
|
3808
|
-
submitting,
|
|
3809
|
-
canSubmit,
|
|
3810
|
-
sent,
|
|
3811
|
-
error
|
|
3812
|
-
};
|
|
3813
|
-
}
|
|
3814
|
-
|
|
3815
|
-
// src/hooks/useResetForm.ts
|
|
3816
|
-
import { useCallback as useCallback10, useEffect as useEffect13, useMemo as useMemo10, useState as useState15 } from "react";
|
|
3817
|
-
import { useHook as useHook16 } from "@hook-sdk/sdk";
|
|
3818
|
-
var MIN_PASSWORD3 = 8;
|
|
3819
|
-
function useResetForm() {
|
|
3820
|
-
const { auth } = useHook16();
|
|
3821
|
-
const [token, setToken] = useState15(null);
|
|
3822
|
-
const [password, setPassword] = useState15("");
|
|
3823
|
-
const [confirm, setConfirm] = useState15("");
|
|
3824
|
-
const [submitting, setSubmitting] = useState15(false);
|
|
3825
|
-
const [done, setDone] = useState15(false);
|
|
3826
|
-
const [error, setError] = useState15(null);
|
|
3827
|
-
const [touchedPassword, setTouchedPassword] = useState15(false);
|
|
3828
|
-
const [touchedConfirm, setTouchedConfirm] = useState15(false);
|
|
3829
|
-
const [formSubmitAttempted, setFormSubmitAttempted] = useState15(false);
|
|
3830
|
-
useEffect13(() => {
|
|
3831
|
-
if (typeof window === "undefined") return;
|
|
3832
|
-
const params = new URLSearchParams(window.location.search);
|
|
3833
|
-
const t = params.get("token");
|
|
3834
|
-
setToken(t && t.length > 0 ? t : null);
|
|
3835
|
-
}, []);
|
|
3836
|
-
const validatePassword = useMemo10(() => {
|
|
3837
|
-
if (password.length === 0) return null;
|
|
3838
|
-
if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
|
|
3839
|
-
return null;
|
|
3840
|
-
}, [password]);
|
|
3841
|
-
const validateConfirm = useMemo10(() => {
|
|
3842
|
-
if (confirm.length === 0) return null;
|
|
3843
|
-
if (confirm !== password) return "Senhas n\xE3o coincidem.";
|
|
3844
|
-
return null;
|
|
3845
|
-
}, [confirm, password]);
|
|
3846
|
-
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
3847
|
-
const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
|
|
3848
|
-
const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
|
|
3849
|
-
const submit = useCallback10(async () => {
|
|
3850
|
-
setFormSubmitAttempted(true);
|
|
3851
|
-
if (!canSubmit || token === null) return;
|
|
3852
|
-
setSubmitting(true);
|
|
3853
|
-
setError(null);
|
|
3854
|
-
try {
|
|
3855
|
-
await auth.reset({ token, newPassword: password });
|
|
3856
|
-
setDone(true);
|
|
3857
|
-
if (typeof window !== "undefined") {
|
|
3858
|
-
const url = new URL(window.location.href);
|
|
3859
|
-
url.searchParams.delete("token");
|
|
3860
|
-
url.searchParams.delete("screen");
|
|
3861
|
-
window.history.replaceState({}, "", url.toString());
|
|
3862
|
-
}
|
|
3863
|
-
} catch (err) {
|
|
3864
|
-
setError(mapSdkError(err));
|
|
3865
|
-
} finally {
|
|
3866
|
-
setSubmitting(false);
|
|
3867
|
-
}
|
|
3868
|
-
}, [auth, token, password, canSubmit]);
|
|
3869
|
-
return {
|
|
3870
|
-
token,
|
|
3871
|
-
password,
|
|
3872
|
-
setPassword,
|
|
3873
|
-
passwordError,
|
|
3874
|
-
markPasswordTouched: () => setTouchedPassword(true),
|
|
3875
|
-
confirm,
|
|
3876
|
-
setConfirm,
|
|
3877
|
-
confirmError,
|
|
3878
|
-
markConfirmTouched: () => setTouchedConfirm(true),
|
|
3879
|
-
formSubmitAttempted,
|
|
3880
|
-
submit,
|
|
3881
|
-
submitting,
|
|
3882
|
-
canSubmit,
|
|
3883
|
-
done,
|
|
3884
|
-
error
|
|
3885
|
-
};
|
|
3886
|
-
}
|
|
3033
|
+
// src/components/paywall/Paywall.tsx
|
|
3034
|
+
import { useEffect as useEffect11, useMemo as useMemo5 } from "react";
|
|
3035
|
+
import { useHook as useHook12 } from "@hook-sdk/sdk";
|
|
3887
3036
|
|
|
3888
3037
|
// src/utils/price.ts
|
|
3889
3038
|
function formatBRL(cents) {
|
|
@@ -3913,978 +3062,1830 @@ function discountPercent(anchorCents, realCents) {
|
|
|
3913
3062
|
return Math.floor((anchorCents - realCents) / anchorCents * 100);
|
|
3914
3063
|
}
|
|
3915
3064
|
|
|
3916
|
-
// src/
|
|
3917
|
-
import {
|
|
3918
|
-
import {
|
|
3919
|
-
var
|
|
3920
|
-
function
|
|
3921
|
-
const
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3065
|
+
// src/components/paywall/PaywallProvider.tsx
|
|
3066
|
+
import { createContext as createContext3 } from "react";
|
|
3067
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
3068
|
+
var PaywallContext = createContext3(null);
|
|
3069
|
+
function PaywallProvider({ children }) {
|
|
3070
|
+
const state = usePaywallState();
|
|
3071
|
+
return /* @__PURE__ */ jsx28(PaywallContext.Provider, { value: state, children });
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
// src/components/paywall/usePaywallContext.ts
|
|
3075
|
+
import { useContext as useContext4 } from "react";
|
|
3076
|
+
function usePaywallContext() {
|
|
3077
|
+
const ctx = useContext4(PaywallContext);
|
|
3078
|
+
if (!ctx) {
|
|
3079
|
+
throw new Error("usePaywallContext must be used within <PaywallProvider>");
|
|
3080
|
+
}
|
|
3081
|
+
return ctx;
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
// src/components/paywall/PaywallMethodTabs.tsx
|
|
3085
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
3086
|
+
function PaywallMethodTabs({
|
|
3087
|
+
labels,
|
|
3088
|
+
className,
|
|
3089
|
+
tabClassName,
|
|
3090
|
+
tabActiveClassName
|
|
3091
|
+
}) {
|
|
3092
|
+
const { methods, selectedMethod, selectMethod } = usePaywallContext();
|
|
3093
|
+
if (methods.length < 2) return null;
|
|
3094
|
+
return /* @__PURE__ */ jsx29("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
|
|
3095
|
+
const active = m === selectedMethod;
|
|
3096
|
+
const label = labels[m] ?? m;
|
|
3097
|
+
return /* @__PURE__ */ jsx29(
|
|
3098
|
+
"button",
|
|
3099
|
+
{
|
|
3100
|
+
type: "button",
|
|
3101
|
+
role: "tab",
|
|
3102
|
+
"aria-selected": active,
|
|
3103
|
+
"aria-controls": `paywall-tab-${m}`,
|
|
3104
|
+
tabIndex: active ? 0 : -1,
|
|
3105
|
+
onClick: () => selectMethod(m),
|
|
3106
|
+
className: [tabClassName, active ? tabActiveClassName : ""].filter(Boolean).join(" "),
|
|
3107
|
+
children: label
|
|
3108
|
+
},
|
|
3109
|
+
m
|
|
3110
|
+
);
|
|
3111
|
+
}) });
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
// src/components/paywall/PaywallMethodContent.tsx
|
|
3115
|
+
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
3116
|
+
function PaywallMethodContent({ copy, className, rowClassName }) {
|
|
3117
|
+
const { selectedMethod, hasConsumedTrial } = usePaywallContext();
|
|
3118
|
+
const useCardConsumed = selectedMethod === "card" && hasConsumedTrial && copy.cardConsumedTrial;
|
|
3119
|
+
const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : selectedMethod === "pix-auto" || selectedMethod === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
|
|
3120
|
+
return /* @__PURE__ */ jsx30("div", { role: "tabpanel", id: `paywall-tab-${selectedMethod}`, className, children: rows.map((row, i) => /* @__PURE__ */ jsx30("div", { className: rowClassName, children: row }, i)) });
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
// src/components/paywall/PaywallCyclePicker.tsx
|
|
3124
|
+
import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
3125
|
+
var VARIANT_CLASSES = {
|
|
3126
|
+
default: { card: "", cardSelected: "" },
|
|
3127
|
+
"premium-gold": {
|
|
3128
|
+
card: "",
|
|
3129
|
+
cardSelected: "border-2 border-yellow-400/80 ring-2 ring-yellow-400/20"
|
|
3130
|
+
},
|
|
3131
|
+
"pink-pill": {
|
|
3132
|
+
card: "rounded-2xl",
|
|
3133
|
+
cardSelected: "border-2 border-pink-500"
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
function PaywallCyclePicker({
|
|
3137
|
+
labels,
|
|
3138
|
+
className,
|
|
3139
|
+
cardClassName,
|
|
3140
|
+
cardSelectedClassName,
|
|
3141
|
+
anchorClassName,
|
|
3142
|
+
variant = "default",
|
|
3143
|
+
render
|
|
3144
|
+
}) {
|
|
3145
|
+
const ctx = usePaywallContext();
|
|
3146
|
+
const { cycle: selected, setCycle, plan, anchorPriceCents } = ctx;
|
|
3147
|
+
const cycles = ["MONTHLY", "YEARLY"];
|
|
3148
|
+
if (render) {
|
|
3149
|
+
return /* @__PURE__ */ jsx31("div", { className, children: render({ cycles, selected, setCycle, plan, anchorPriceCents }) });
|
|
3150
|
+
}
|
|
3151
|
+
if (cycles.length < 2) return null;
|
|
3152
|
+
const v = VARIANT_CLASSES[variant];
|
|
3153
|
+
const composedCardClassName = [v.card, cardClassName].filter(Boolean).join(" ");
|
|
3154
|
+
const composedCardSelectedClassName = [v.cardSelected, cardSelectedClassName].filter(Boolean).join(" ");
|
|
3155
|
+
const monthlyCents = plan?.monthlyCents ?? 0;
|
|
3156
|
+
const yearlyCents = plan?.yearlyCents ?? 0;
|
|
3157
|
+
const anchorMonthly = plan?.anchorMonthlyCents ?? null;
|
|
3158
|
+
const anchorYearly = plan?.anchorYearlyCents ?? null;
|
|
3159
|
+
return /* @__PURE__ */ jsx31(
|
|
3160
|
+
"div",
|
|
3161
|
+
{
|
|
3162
|
+
role: "radiogroup",
|
|
3163
|
+
"aria-label": "Ciclo de cobran\xE7a",
|
|
3164
|
+
className: ["flex flex-row gap-2", className].filter(Boolean).join(" "),
|
|
3165
|
+
children: cycles.map((c) => {
|
|
3166
|
+
const active = c === selected;
|
|
3167
|
+
const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
|
|
3168
|
+
const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
|
|
3169
|
+
const mainCents = c === "YEARLY" ? Math.round(yearlyCents / 12) : monthlyCents;
|
|
3170
|
+
const anchorCents = c === "YEARLY" ? anchorYearly : anchorMonthly;
|
|
3171
|
+
return /* @__PURE__ */ jsxs19(
|
|
3172
|
+
"button",
|
|
3173
|
+
{
|
|
3174
|
+
type: "button",
|
|
3175
|
+
role: "radio",
|
|
3176
|
+
"aria-checked": active,
|
|
3177
|
+
onClick: () => setCycle(c),
|
|
3178
|
+
className: [
|
|
3179
|
+
"flex flex-col items-center gap-0.5",
|
|
3180
|
+
composedCardClassName,
|
|
3181
|
+
active ? composedCardSelectedClassName : ""
|
|
3182
|
+
].filter(Boolean).join(" "),
|
|
3183
|
+
children: [
|
|
3184
|
+
/* @__PURE__ */ jsx31("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
|
|
3185
|
+
/* @__PURE__ */ jsx31("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
|
|
3186
|
+
/* @__PURE__ */ jsx31("span", { className: "text-xs opacity-60 leading-tight", children: label }),
|
|
3187
|
+
anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx31("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ jsx31("s", { children: formatBRL(anchorCents) }) }) : null
|
|
3188
|
+
]
|
|
3189
|
+
},
|
|
3190
|
+
c
|
|
3191
|
+
);
|
|
3192
|
+
})
|
|
3928
3193
|
}
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3194
|
+
);
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
// src/components/paywall/Paywall.tsx
|
|
3198
|
+
import { jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3199
|
+
var NBSP = "\xA0";
|
|
3200
|
+
function Paywall({
|
|
3201
|
+
copy,
|
|
3202
|
+
themeClasses = {},
|
|
3203
|
+
slots = {},
|
|
3204
|
+
onBeforeCheckout
|
|
3205
|
+
}) {
|
|
3206
|
+
return /* @__PURE__ */ jsx32(PaywallProvider, { children: /* @__PURE__ */ jsx32(
|
|
3207
|
+
PaywallInner,
|
|
3208
|
+
{
|
|
3209
|
+
copy,
|
|
3210
|
+
themeClasses,
|
|
3211
|
+
slots,
|
|
3212
|
+
onBeforeCheckout
|
|
3213
|
+
}
|
|
3214
|
+
) });
|
|
3215
|
+
}
|
|
3216
|
+
function PaywallInner({
|
|
3217
|
+
copy,
|
|
3218
|
+
themeClasses = {},
|
|
3219
|
+
slots = {},
|
|
3220
|
+
onBeforeCheckout
|
|
3221
|
+
}) {
|
|
3222
|
+
const { track: track2 } = useHook12();
|
|
3223
|
+
const s = usePaywallContext();
|
|
3224
|
+
const priceLabel = formatBRL(s.currentPriceCents).replace(new RegExp(NBSP, "g"), " ");
|
|
3225
|
+
const trialDaysCardLabel = String(s.trialDaysCard);
|
|
3226
|
+
const ctaLabel = useMemo5(() => {
|
|
3227
|
+
if (s.isFree) return copy.freeCta ?? "Come\xE7ar agora";
|
|
3228
|
+
if (s.selectedMethod === "card") {
|
|
3229
|
+
if (s.hasConsumedTrial && copy.cardConsumedTrial) {
|
|
3230
|
+
return interp(copy.cardConsumedTrial.ctaTemplate, {
|
|
3231
|
+
price: priceLabel,
|
|
3232
|
+
days: trialDaysCardLabel
|
|
3233
|
+
});
|
|
3234
|
+
}
|
|
3235
|
+
if (s.trialDaysCard > 0) {
|
|
3236
|
+
return interp(copy.card.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
|
|
3237
|
+
}
|
|
3238
|
+
return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, {
|
|
3239
|
+
price: priceLabel,
|
|
3240
|
+
days: trialDaysCardLabel
|
|
3241
|
+
}) : `Assinar por ${priceLabel}`;
|
|
3242
|
+
}
|
|
3243
|
+
return interp(copy.pix.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
|
|
3244
|
+
}, [
|
|
3245
|
+
s.isFree,
|
|
3246
|
+
s.selectedMethod,
|
|
3247
|
+
s.hasConsumedTrial,
|
|
3248
|
+
s.trialDaysCard,
|
|
3249
|
+
copy,
|
|
3250
|
+
priceLabel,
|
|
3251
|
+
trialDaysCardLabel
|
|
3252
|
+
]);
|
|
3253
|
+
const switchHint = useMemo5(() => {
|
|
3254
|
+
if (s.methods.length < 2) return void 0;
|
|
3255
|
+
return s.selectedMethod === "card" ? copy.card.switchHint : copy.pix.switchHint;
|
|
3256
|
+
}, [s.methods.length, s.selectedMethod, copy]);
|
|
3257
|
+
useEffect11(() => {
|
|
3258
|
+
if (!s.initialLoadComplete) return;
|
|
3259
|
+
track2("paywall_view", {
|
|
3260
|
+
default_method: s.selectedMethod,
|
|
3261
|
+
default_cycle: s.cycle,
|
|
3262
|
+
available_methods: s.methods
|
|
3263
|
+
});
|
|
3264
|
+
}, [s.initialLoadComplete]);
|
|
3265
|
+
const handleCta = async () => {
|
|
3266
|
+
track2("paywall_cta_clicked", {
|
|
3267
|
+
method: s.selectedMethod,
|
|
3268
|
+
cycle: s.cycle,
|
|
3269
|
+
price_cents: s.currentPriceCents,
|
|
3270
|
+
had_consumed_trial: s.hasConsumedTrial
|
|
3271
|
+
});
|
|
3272
|
+
if (onBeforeCheckout) {
|
|
3273
|
+
await onBeforeCheckout(s.selectedMethod, s.cycle);
|
|
3274
|
+
return;
|
|
3275
|
+
}
|
|
3276
|
+
await s.submit();
|
|
3940
3277
|
};
|
|
3278
|
+
const ctaTheme = s.selectedMethod === "card" ? themeClasses.ctaCard : themeClasses.ctaPix;
|
|
3279
|
+
return /* @__PURE__ */ jsxs20("div", { className: themeClasses.container, children: [
|
|
3280
|
+
slots.heroSlot,
|
|
3281
|
+
/* @__PURE__ */ jsx32("h1", { className: themeClasses.headline, children: copy.headline }),
|
|
3282
|
+
/* @__PURE__ */ jsx32("ul", { children: copy.features.map((f) => /* @__PURE__ */ jsxs20("li", { className: themeClasses.feature, children: [
|
|
3283
|
+
"\u2713 ",
|
|
3284
|
+
/* @__PURE__ */ jsx32("span", { children: f })
|
|
3285
|
+
] }, f)) }),
|
|
3286
|
+
copy.socialProof ? /* @__PURE__ */ jsx32("p", { className: themeClasses.socialProof, children: copy.socialProof }) : null,
|
|
3287
|
+
slots.cyclePickerSlot ?? /* @__PURE__ */ jsx32(
|
|
3288
|
+
PaywallCyclePicker,
|
|
3289
|
+
{
|
|
3290
|
+
labels: copy.cycle,
|
|
3291
|
+
cardClassName: themeClasses.cycleCard,
|
|
3292
|
+
cardSelectedClassName: themeClasses.cycleCardSelected,
|
|
3293
|
+
anchorClassName: themeClasses.anchorPrice
|
|
3294
|
+
}
|
|
3295
|
+
),
|
|
3296
|
+
/* @__PURE__ */ jsx32(
|
|
3297
|
+
PaywallMethodTabs,
|
|
3298
|
+
{
|
|
3299
|
+
labels: { "pix-auto": copy.pix.tabLabel, card: copy.card.tabLabel },
|
|
3300
|
+
className: themeClasses.tabs,
|
|
3301
|
+
tabClassName: themeClasses.tab,
|
|
3302
|
+
tabActiveClassName: themeClasses.tabActive
|
|
3303
|
+
}
|
|
3304
|
+
),
|
|
3305
|
+
/* @__PURE__ */ jsx32(
|
|
3306
|
+
PaywallMethodContent,
|
|
3307
|
+
{
|
|
3308
|
+
copy: {
|
|
3309
|
+
pix: interpolateCopy(copy.pix, priceLabel, trialDaysCardLabel),
|
|
3310
|
+
card: interpolateCopy(copy.card, priceLabel, trialDaysCardLabel),
|
|
3311
|
+
cardConsumedTrial: copy.cardConsumedTrial ? {
|
|
3312
|
+
bodyRows: copy.cardConsumedTrial.bodyRows.map(
|
|
3313
|
+
(r) => interp(r, { price: priceLabel, days: trialDaysCardLabel })
|
|
3314
|
+
),
|
|
3315
|
+
ctaTemplate: copy.cardConsumedTrial.ctaTemplate
|
|
3316
|
+
} : void 0
|
|
3317
|
+
},
|
|
3318
|
+
className: themeClasses.tabContent,
|
|
3319
|
+
rowClassName: themeClasses.tabContentRow
|
|
3320
|
+
}
|
|
3321
|
+
),
|
|
3322
|
+
slots.beforeCtaSlot,
|
|
3323
|
+
/* @__PURE__ */ jsxs20("div", { children: [
|
|
3324
|
+
/* @__PURE__ */ jsx32(
|
|
3325
|
+
"button",
|
|
3326
|
+
{
|
|
3327
|
+
type: "button",
|
|
3328
|
+
onClick: () => {
|
|
3329
|
+
void handleCta();
|
|
3330
|
+
},
|
|
3331
|
+
disabled: s.submitting,
|
|
3332
|
+
className: ctaTheme,
|
|
3333
|
+
children: s.submitting ? "Abrindo checkout\u2026" : ctaLabel
|
|
3334
|
+
}
|
|
3335
|
+
),
|
|
3336
|
+
switchHint ? /* @__PURE__ */ jsx32("p", { className: themeClasses.switchHint, children: switchHint }) : null,
|
|
3337
|
+
/* @__PURE__ */ jsx32("p", { className: themeClasses.trustLine, children: copy.trustLine })
|
|
3338
|
+
] })
|
|
3339
|
+
] });
|
|
3941
3340
|
}
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
import { useHook as useHook18 } from "@hook-sdk/sdk";
|
|
3945
|
-
function useAuth() {
|
|
3946
|
-
const { user, authStatus, auth } = useHook18();
|
|
3947
|
-
return {
|
|
3948
|
-
user,
|
|
3949
|
-
authStatus,
|
|
3950
|
-
refresh: auth.refresh
|
|
3951
|
-
};
|
|
3341
|
+
function interp(tpl, vars) {
|
|
3342
|
+
return tpl.replace(/\{(\w+)\}/g, (_m, k) => vars[k] ?? "");
|
|
3952
3343
|
}
|
|
3953
|
-
|
|
3954
|
-
// src/index.ts
|
|
3955
|
-
import { useTrackOnboardingStep } from "@hook-sdk/sdk";
|
|
3956
|
-
|
|
3957
|
-
// src/hooks/useSubscription.ts
|
|
3958
|
-
import { useHook as useHook19 } from "@hook-sdk/sdk";
|
|
3959
|
-
function useSubscription() {
|
|
3960
|
-
const { subscription } = useHook19();
|
|
3344
|
+
function interpolateCopy(m, price, days) {
|
|
3961
3345
|
return {
|
|
3962
|
-
|
|
3346
|
+
tabLabel: m.tabLabel,
|
|
3347
|
+
bodyRows: m.bodyRows.map((r) => interp(r, { price, days })),
|
|
3348
|
+
ctaTemplate: m.ctaTemplate,
|
|
3349
|
+
switchHint: m.switchHint
|
|
3963
3350
|
};
|
|
3964
3351
|
}
|
|
3965
3352
|
|
|
3966
|
-
// src/
|
|
3967
|
-
import {
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
return r.schedule(items);
|
|
3996
|
-
}, [r]);
|
|
3997
|
-
const setFallbacks = useCallback11(async (items) => {
|
|
3998
|
-
return r.setFallbacks(items);
|
|
3999
|
-
}, [r]);
|
|
4000
|
-
return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
|
|
4001
|
-
}
|
|
4002
|
-
|
|
4003
|
-
// src/hooks/useToast.ts
|
|
4004
|
-
import { useCallback as useCallback12, useState as useState17 } from "react";
|
|
4005
|
-
function useToast() {
|
|
4006
|
-
const [items, setItems] = useState17([]);
|
|
4007
|
-
const show = useCallback12((message, kind = "info") => {
|
|
4008
|
-
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4009
|
-
setItems((prev) => [...prev, { id, message, kind }]);
|
|
4010
|
-
setTimeout(() => {
|
|
4011
|
-
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
4012
|
-
}, 4e3);
|
|
4013
|
-
}, []);
|
|
4014
|
-
const dismiss = useCallback12((id) => {
|
|
4015
|
-
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
4016
|
-
}, []);
|
|
4017
|
-
return { items, show, dismiss };
|
|
4018
|
-
}
|
|
4019
|
-
|
|
4020
|
-
// src/RouteBoundary.tsx
|
|
4021
|
-
import { Routes as Routes2, Route as Route2 } from "react-router-dom";
|
|
4022
|
-
import { jsx as jsx30, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
4023
|
-
function RouteBoundary({ children }) {
|
|
4024
|
-
return /* @__PURE__ */ jsxs21(Routes2, { children: [
|
|
4025
|
-
children,
|
|
4026
|
-
/* @__PURE__ */ jsx30(Route2, { path: "*", element: /* @__PURE__ */ jsx30(DefaultNotFound, {}) })
|
|
3353
|
+
// src/components/paywall/PaywallCta.tsx
|
|
3354
|
+
import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
3355
|
+
function PaywallCta({
|
|
3356
|
+
ctaLabel,
|
|
3357
|
+
loadingLabel,
|
|
3358
|
+
switchHint,
|
|
3359
|
+
trustLine,
|
|
3360
|
+
className,
|
|
3361
|
+
buttonClassName,
|
|
3362
|
+
switchHintClassName,
|
|
3363
|
+
trustClassName
|
|
3364
|
+
}) {
|
|
3365
|
+
const { submit, submitting } = usePaywallContext();
|
|
3366
|
+
const label = submitting && loadingLabel ? loadingLabel : ctaLabel;
|
|
3367
|
+
return /* @__PURE__ */ jsxs21("div", { className, children: [
|
|
3368
|
+
/* @__PURE__ */ jsx33(
|
|
3369
|
+
"button",
|
|
3370
|
+
{
|
|
3371
|
+
type: "button",
|
|
3372
|
+
onClick: () => {
|
|
3373
|
+
void submit();
|
|
3374
|
+
},
|
|
3375
|
+
disabled: submitting,
|
|
3376
|
+
className: buttonClassName,
|
|
3377
|
+
children: label
|
|
3378
|
+
}
|
|
3379
|
+
),
|
|
3380
|
+
switchHint ? /* @__PURE__ */ jsx33("p", { className: switchHintClassName, children: switchHint }) : null,
|
|
3381
|
+
/* @__PURE__ */ jsx33("p", { className: trustClassName, children: trustLine })
|
|
4027
3382
|
] });
|
|
4028
3383
|
}
|
|
4029
|
-
|
|
4030
|
-
|
|
3384
|
+
|
|
3385
|
+
// src/components/paywall/blocks/PaywallEyebrow.tsx
|
|
3386
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
3387
|
+
var DEFAULT_EYEBROW_CLASSES = "text-xs uppercase tracking-widest font-semibold opacity-70";
|
|
3388
|
+
function PaywallEyebrow({ text, className }) {
|
|
3389
|
+
return /* @__PURE__ */ jsx34("div", { className: [DEFAULT_EYEBROW_CLASSES, className].filter(Boolean).join(" "), children: text });
|
|
4031
3390
|
}
|
|
4032
3391
|
|
|
4033
|
-
// src/
|
|
4034
|
-
import {
|
|
4035
|
-
|
|
4036
|
-
function
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
3392
|
+
// src/components/paywall/blocks/PaywallHero.tsx
|
|
3393
|
+
import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
3394
|
+
var DEFAULT_GRADIENT = "absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent";
|
|
3395
|
+
function PaywallHero({
|
|
3396
|
+
src,
|
|
3397
|
+
alt = "",
|
|
3398
|
+
headline,
|
|
3399
|
+
aspectRatio = "16/9",
|
|
3400
|
+
gradientClassName,
|
|
3401
|
+
className,
|
|
3402
|
+
headlineClassName,
|
|
3403
|
+
imgClassName,
|
|
3404
|
+
render
|
|
4041
3405
|
}) {
|
|
4042
|
-
if (
|
|
4043
|
-
return /* @__PURE__ */
|
|
3406
|
+
if (render) {
|
|
3407
|
+
return /* @__PURE__ */ jsx35("div", { className, children: render({ src, headline }) });
|
|
4044
3408
|
}
|
|
4045
|
-
return /* @__PURE__ */
|
|
3409
|
+
return /* @__PURE__ */ jsxs22(
|
|
3410
|
+
"div",
|
|
3411
|
+
{
|
|
3412
|
+
className: ["relative overflow-hidden", className].filter(Boolean).join(" "),
|
|
3413
|
+
style: { aspectRatio },
|
|
3414
|
+
children: [
|
|
3415
|
+
/* @__PURE__ */ jsx35(
|
|
3416
|
+
"img",
|
|
3417
|
+
{
|
|
3418
|
+
src,
|
|
3419
|
+
alt,
|
|
3420
|
+
className: ["absolute inset-0 w-full h-full object-cover", imgClassName].filter(Boolean).join(" ")
|
|
3421
|
+
}
|
|
3422
|
+
),
|
|
3423
|
+
/* @__PURE__ */ jsx35("div", { className: gradientClassName ?? DEFAULT_GRADIENT, "aria-hidden": "true" }),
|
|
3424
|
+
headline ? /* @__PURE__ */ jsx35(
|
|
3425
|
+
"h1",
|
|
3426
|
+
{
|
|
3427
|
+
className: ["absolute bottom-0 left-0 right-0 p-4 text-white font-bold text-2xl", headlineClassName].filter(Boolean).join(" "),
|
|
3428
|
+
children: headline
|
|
3429
|
+
}
|
|
3430
|
+
) : null
|
|
3431
|
+
]
|
|
3432
|
+
}
|
|
3433
|
+
);
|
|
4046
3434
|
}
|
|
4047
3435
|
|
|
4048
|
-
// src/
|
|
4049
|
-
import {
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
var OnboardingStepContext = createContext3(null);
|
|
4055
|
-
function useOnboardingStep() {
|
|
4056
|
-
const ctx = useContext4(OnboardingStepContext);
|
|
4057
|
-
if (!ctx) {
|
|
4058
|
-
throw new Error(
|
|
4059
|
-
"[hook-template] useOnboardingStep must be used inside <OnboardingFlow>. (G75)"
|
|
4060
|
-
);
|
|
4061
|
-
}
|
|
4062
|
-
return ctx;
|
|
3436
|
+
// src/components/paywall/blocks/PaywallHeadline.tsx
|
|
3437
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
3438
|
+
var DEFAULT_HEADLINE_CLASSES = "text-2xl font-bold leading-tight";
|
|
3439
|
+
function PaywallHeadline({ text, className, as = "h1" }) {
|
|
3440
|
+
const Tag = as;
|
|
3441
|
+
return /* @__PURE__ */ jsx36(Tag, { className: [DEFAULT_HEADLINE_CLASSES, className].filter(Boolean).join(" "), children: text });
|
|
4063
3442
|
}
|
|
4064
3443
|
|
|
4065
|
-
// src/
|
|
4066
|
-
import { jsx as
|
|
4067
|
-
var
|
|
4068
|
-
var
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
persistKey
|
|
3444
|
+
// src/components/paywall/blocks/PaywallPriceHeadline.tsx
|
|
3445
|
+
import { jsx as jsx37 } from "react/jsx-runtime";
|
|
3446
|
+
var DEFAULT_CLASS = "text-2xl font-bold leading-tight";
|
|
3447
|
+
var CYCLE_LABEL = {
|
|
3448
|
+
MONTHLY: "mensal",
|
|
3449
|
+
YEARLY: "anual"
|
|
3450
|
+
};
|
|
3451
|
+
function PaywallPriceHeadline({
|
|
3452
|
+
template,
|
|
3453
|
+
className,
|
|
3454
|
+
as = "h1",
|
|
3455
|
+
render
|
|
4078
3456
|
}) {
|
|
4079
|
-
const
|
|
4080
|
-
const
|
|
4081
|
-
|
|
4082
|
-
const
|
|
4083
|
-
const
|
|
4084
|
-
const
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
const nextIdx = typeof n === "function" ? n(prevIdx) : n;
|
|
4089
|
-
return { ...prev, [CURRENT_STEP_FIELD]: nextIdx };
|
|
4090
|
-
});
|
|
4091
|
-
},
|
|
4092
|
-
[setDraft]
|
|
4093
|
-
);
|
|
4094
|
-
const setValue = useCallback13(
|
|
4095
|
-
(patch) => {
|
|
4096
|
-
draftRef.current = { ...draftRef.current, ...patch };
|
|
4097
|
-
setDraft((prev) => ({ ...prev, ...patch }));
|
|
4098
|
-
},
|
|
4099
|
-
[setDraft]
|
|
4100
|
-
);
|
|
4101
|
-
const step = steps[clampedIdx];
|
|
4102
|
-
const hookCtx = useHook21();
|
|
4103
|
-
const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
|
|
4104
|
-
useEffect16(() => {
|
|
4105
|
-
if (status.loading) return;
|
|
4106
|
-
if (!step) return;
|
|
4107
|
-
if (!track2) return;
|
|
4108
|
-
track2("onboarding_step_viewed", {
|
|
4109
|
-
step: step.id,
|
|
4110
|
-
step_index: clampedIdx,
|
|
4111
|
-
total_steps: steps.length
|
|
4112
|
-
});
|
|
4113
|
-
}, [step?.id, clampedIdx, steps.length, status.loading, track2]);
|
|
4114
|
-
const valid = useMemo11(
|
|
4115
|
-
() => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
|
|
4116
|
-
[draft, step]
|
|
4117
|
-
);
|
|
4118
|
-
const next = useCallback13(() => {
|
|
4119
|
-
if (!step) return;
|
|
4120
|
-
const current = draftRef.current;
|
|
4121
|
-
const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
|
|
4122
|
-
if (!validNow) return;
|
|
4123
|
-
if (clampedIdx + 1 >= steps.length) {
|
|
4124
|
-
onComplete(current);
|
|
4125
|
-
} else {
|
|
4126
|
-
setIdx(clampedIdx + 1);
|
|
4127
|
-
}
|
|
4128
|
-
}, [clampedIdx, onComplete, step, steps.length, setIdx]);
|
|
4129
|
-
const prevStep = useCallback13(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
|
|
4130
|
-
const ctx = useMemo11(
|
|
4131
|
-
() => ({
|
|
4132
|
-
stepIndex: clampedIdx,
|
|
4133
|
-
totalSteps: steps.length,
|
|
4134
|
-
value: draft,
|
|
4135
|
-
setValue,
|
|
4136
|
-
valid,
|
|
4137
|
-
next,
|
|
4138
|
-
prev: prevStep
|
|
4139
|
-
}),
|
|
4140
|
-
[clampedIdx, steps.length, draft, setValue, valid, next, prevStep]
|
|
4141
|
-
);
|
|
4142
|
-
if (status.loading) {
|
|
4143
|
-
return null;
|
|
3457
|
+
const { cycle, currentMonthlyEquivCents, plan } = usePaywallContext();
|
|
3458
|
+
const yearlyCents = plan?.yearlyCents ?? null;
|
|
3459
|
+
const pricePerDay = formatBRL(dailyFromYearly(yearlyCents));
|
|
3460
|
+
const monthlyEquiv = currentMonthlyEquivCents ?? 0;
|
|
3461
|
+
const cycleLabel = CYCLE_LABEL[cycle] ?? cycle.toLowerCase();
|
|
3462
|
+
const rootClasses = [DEFAULT_CLASS, className].filter(Boolean).join(" ");
|
|
3463
|
+
if (render) {
|
|
3464
|
+
const RootTag2 = as;
|
|
3465
|
+
return /* @__PURE__ */ jsx37(RootTag2, { className: [className].filter(Boolean).join(" ") || void 0, children: render({ pricePerDay, currentMonthlyEquivCents: monthlyEquiv, cycle }) });
|
|
4144
3466
|
}
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
3467
|
+
const text = template.replaceAll("{pricePerDay}", pricePerDay).replaceAll("{currentMonthlyEquiv}", formatBRL(monthlyEquiv)).replaceAll("{cycle}", cycleLabel);
|
|
3468
|
+
const RootTag = as;
|
|
3469
|
+
return /* @__PURE__ */ jsx37(RootTag, { className: rootClasses, children: text });
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
// src/components/paywall/blocks/PaywallCountdown.tsx
|
|
3473
|
+
import { useEffect as useEffect12, useRef as useRef7, useState as useState10 } from "react";
|
|
3474
|
+
import { jsx as jsx38 } from "react/jsx-runtime";
|
|
3475
|
+
var DEFAULT_COUNTDOWN_CLASSES = "font-mono tabular-nums";
|
|
3476
|
+
function resolveDeadlineMs(deadline) {
|
|
3477
|
+
if (deadline instanceof Date) return deadline.getTime();
|
|
3478
|
+
if (typeof deadline === "string") return new Date(deadline).getTime();
|
|
3479
|
+
const { sessionStorageKey, durationMs } = deadline;
|
|
3480
|
+
if (typeof window === "undefined" || typeof window.sessionStorage === "undefined") {
|
|
3481
|
+
return Date.now() + durationMs;
|
|
4149
3482
|
}
|
|
4150
|
-
const
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
3483
|
+
const stored = window.sessionStorage.getItem(sessionStorageKey);
|
|
3484
|
+
const parsed = stored ? Number.parseInt(stored, 10) : NaN;
|
|
3485
|
+
const now = Date.now();
|
|
3486
|
+
if (!Number.isFinite(parsed) || parsed < now) {
|
|
3487
|
+
const target = now + durationMs;
|
|
3488
|
+
window.sessionStorage.setItem(sessionStorageKey, String(target));
|
|
3489
|
+
return target;
|
|
3490
|
+
}
|
|
3491
|
+
return parsed;
|
|
3492
|
+
}
|
|
3493
|
+
function computeRemaining(deadlineMs) {
|
|
3494
|
+
const diff = Math.max(0, deadlineMs - Date.now());
|
|
3495
|
+
const totalSeconds = Math.floor(diff / 1e3);
|
|
3496
|
+
const h = Math.floor(totalSeconds / 3600);
|
|
3497
|
+
const m = Math.floor(totalSeconds % 3600 / 60);
|
|
3498
|
+
const s = totalSeconds % 60;
|
|
3499
|
+
return { h, m, s, expired: diff === 0 };
|
|
3500
|
+
}
|
|
3501
|
+
function pad(n) {
|
|
3502
|
+
return String(n).padStart(2, "0");
|
|
3503
|
+
}
|
|
3504
|
+
function PaywallCountdown({
|
|
3505
|
+
deadline,
|
|
3506
|
+
format = "h:m:s",
|
|
3507
|
+
onExpire,
|
|
3508
|
+
className,
|
|
3509
|
+
render
|
|
3510
|
+
}) {
|
|
3511
|
+
const deadlineMsRef = useRef7(null);
|
|
3512
|
+
if (deadlineMsRef.current === null) {
|
|
3513
|
+
deadlineMsRef.current = resolveDeadlineMs(deadline);
|
|
3514
|
+
}
|
|
3515
|
+
const [state, setState] = useState10(() => computeRemaining(deadlineMsRef.current));
|
|
3516
|
+
const expiredCalledRef = useRef7(false);
|
|
3517
|
+
useEffect12(() => {
|
|
3518
|
+
if (state.expired) {
|
|
3519
|
+
if (!expiredCalledRef.current) {
|
|
3520
|
+
expiredCalledRef.current = true;
|
|
3521
|
+
onExpire?.();
|
|
3522
|
+
}
|
|
3523
|
+
return;
|
|
3524
|
+
}
|
|
3525
|
+
const tick = () => {
|
|
3526
|
+
const next = computeRemaining(deadlineMsRef.current);
|
|
3527
|
+
setState(next);
|
|
3528
|
+
if (next.expired && !expiredCalledRef.current) {
|
|
3529
|
+
expiredCalledRef.current = true;
|
|
3530
|
+
onExpire?.();
|
|
3531
|
+
}
|
|
3532
|
+
};
|
|
3533
|
+
const id = setInterval(tick, 1e3);
|
|
3534
|
+
return () => clearInterval(id);
|
|
3535
|
+
}, [state.expired]);
|
|
3536
|
+
if (render) {
|
|
3537
|
+
return /* @__PURE__ */ jsx38("div", { className, children: render(state) });
|
|
4155
3538
|
}
|
|
4156
|
-
|
|
3539
|
+
const formatted = format === "h:m:s" ? `${pad(state.h)}:${pad(state.m)}:${pad(state.s)}` : `${pad(state.h * 60 + state.m)}:${pad(state.s)}`;
|
|
3540
|
+
return /* @__PURE__ */ jsx38("div", { className: [DEFAULT_COUNTDOWN_CLASSES, className].filter(Boolean).join(" "), children: formatted });
|
|
4157
3541
|
}
|
|
4158
3542
|
|
|
4159
|
-
// src/
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
3543
|
+
// src/components/paywall/blocks/PaywallFeatures.tsx
|
|
3544
|
+
import { jsx as jsx39, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
3545
|
+
function PaywallFeatures({
|
|
3546
|
+
items,
|
|
3547
|
+
IconComponent,
|
|
3548
|
+
className,
|
|
3549
|
+
itemClassName,
|
|
3550
|
+
iconClassName,
|
|
3551
|
+
render,
|
|
3552
|
+
renderItem
|
|
3553
|
+
}) {
|
|
3554
|
+
if (render) {
|
|
3555
|
+
return /* @__PURE__ */ jsx39("div", { className, children: render({ items }) });
|
|
3556
|
+
}
|
|
3557
|
+
if (renderItem) {
|
|
3558
|
+
return /* @__PURE__ */ jsx39("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsx39("li", { children: renderItem(item, idx) }, idx)) });
|
|
3559
|
+
}
|
|
3560
|
+
return /* @__PURE__ */ jsx39("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsxs23("li", { className: itemClassName, children: [
|
|
3561
|
+
IconComponent ? /* @__PURE__ */ jsx39(IconComponent, { className: iconClassName }) : /* @__PURE__ */ jsx39("span", { className: iconClassName, "aria-hidden": "true", children: "\u2713" }),
|
|
3562
|
+
/* @__PURE__ */ jsx39("span", { children: item })
|
|
3563
|
+
] }, idx)) });
|
|
4163
3564
|
}
|
|
4164
3565
|
|
|
4165
|
-
// src/components/paywall/
|
|
4166
|
-
import {
|
|
4167
|
-
|
|
3566
|
+
// src/components/paywall/blocks/PaywallFeaturesCard.tsx
|
|
3567
|
+
import { jsx as jsx40, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3568
|
+
var DEFAULT_CARD_CLASSES = "rounded-xl border p-4";
|
|
3569
|
+
function PaywallFeaturesCard({
|
|
3570
|
+
title,
|
|
3571
|
+
items,
|
|
3572
|
+
className,
|
|
3573
|
+
cardClassName,
|
|
3574
|
+
titleClassName,
|
|
3575
|
+
itemClassName,
|
|
3576
|
+
renderItem
|
|
3577
|
+
}) {
|
|
3578
|
+
return /* @__PURE__ */ jsx40("div", { className, children: /* @__PURE__ */ jsxs24("div", { className: [DEFAULT_CARD_CLASSES, cardClassName].filter(Boolean).join(" "), children: [
|
|
3579
|
+
title ? /* @__PURE__ */ jsx40("div", { className: ["font-semibold mb-2", titleClassName].filter(Boolean).join(" "), children: title }) : null,
|
|
3580
|
+
/* @__PURE__ */ jsx40("ul", { children: items.map(
|
|
3581
|
+
(item, idx) => renderItem ? /* @__PURE__ */ jsx40("li", { children: renderItem(item, idx) }, idx) : /* @__PURE__ */ jsxs24("li", { className: itemClassName, children: [
|
|
3582
|
+
/* @__PURE__ */ jsx40("span", { "aria-hidden": "true", children: "\u2022" }),
|
|
3583
|
+
" ",
|
|
3584
|
+
/* @__PURE__ */ jsx40("span", { children: item })
|
|
3585
|
+
] }, idx)
|
|
3586
|
+
) })
|
|
3587
|
+
] }) });
|
|
3588
|
+
}
|
|
4168
3589
|
|
|
4169
|
-
// src/components/paywall/
|
|
4170
|
-
import {
|
|
4171
|
-
|
|
4172
|
-
var
|
|
4173
|
-
function
|
|
4174
|
-
|
|
4175
|
-
|
|
3590
|
+
// src/components/paywall/blocks/PaywallTrophyBadge.tsx
|
|
3591
|
+
import { jsx as jsx41, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3592
|
+
var DEFAULT_CHIP_CLASSES = "inline-flex items-center gap-1 px-3 py-1 rounded-full bg-yellow-100 text-yellow-900 text-sm font-medium";
|
|
3593
|
+
var FLOATING_CLASSES = "absolute top-2 right-2 z-10 shadow-md";
|
|
3594
|
+
function PaywallTrophyBadge({
|
|
3595
|
+
text,
|
|
3596
|
+
className,
|
|
3597
|
+
iconClassName,
|
|
3598
|
+
floating = false,
|
|
3599
|
+
render
|
|
3600
|
+
}) {
|
|
3601
|
+
if (render) {
|
|
3602
|
+
return /* @__PURE__ */ jsx41("div", { className, children: render({ text }) });
|
|
3603
|
+
}
|
|
3604
|
+
const rootClasses = [
|
|
3605
|
+
DEFAULT_CHIP_CLASSES,
|
|
3606
|
+
floating ? FLOATING_CLASSES : "",
|
|
3607
|
+
className
|
|
3608
|
+
].filter(Boolean).join(" ");
|
|
3609
|
+
return /* @__PURE__ */ jsxs25("div", { className: rootClasses, children: [
|
|
3610
|
+
/* @__PURE__ */ jsx41("span", { className: iconClassName, "aria-hidden": "true", children: "\u{1F3C6}" }),
|
|
3611
|
+
/* @__PURE__ */ jsx41("span", { children: text })
|
|
3612
|
+
] });
|
|
4176
3613
|
}
|
|
4177
3614
|
|
|
4178
|
-
// src/components/paywall/
|
|
4179
|
-
import {
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
3615
|
+
// src/components/paywall/blocks/PaywallAnchorPrice.tsx
|
|
3616
|
+
import { jsx as jsx42 } from "react/jsx-runtime";
|
|
3617
|
+
var DEFAULT_CLASS2 = "text-sm opacity-60 line-through";
|
|
3618
|
+
function PaywallAnchorPrice({
|
|
3619
|
+
className,
|
|
3620
|
+
render
|
|
3621
|
+
}) {
|
|
3622
|
+
const { anchorPriceCents, cycle } = usePaywallContext();
|
|
3623
|
+
if (anchorPriceCents === null || anchorPriceCents === void 0 || anchorPriceCents <= 0) {
|
|
3624
|
+
return null;
|
|
4184
3625
|
}
|
|
4185
|
-
|
|
3626
|
+
void cycle;
|
|
3627
|
+
const formatted = formatBRL(anchorPriceCents);
|
|
3628
|
+
const rootClasses = [DEFAULT_CLASS2, className].filter(Boolean).join(" ");
|
|
3629
|
+
if (render) {
|
|
3630
|
+
return /* @__PURE__ */ jsx42("span", { className: className || void 0, children: render({ anchorCents: anchorPriceCents, formatted }) });
|
|
3631
|
+
}
|
|
3632
|
+
return /* @__PURE__ */ jsx42("span", { className: rootClasses, children: formatted });
|
|
4186
3633
|
}
|
|
4187
3634
|
|
|
4188
|
-
// src/components/paywall/
|
|
4189
|
-
import { jsx as
|
|
4190
|
-
|
|
4191
|
-
|
|
3635
|
+
// src/components/paywall/blocks/PaywallTestimonials.tsx
|
|
3636
|
+
import { jsx as jsx43, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
3637
|
+
var DEFAULT_ROOT = "flex gap-3 overflow-x-auto snap-x snap-mandatory pb-2";
|
|
3638
|
+
var DEFAULT_CARD = "snap-start shrink-0 w-72 rounded-2xl border p-4 flex flex-col gap-2";
|
|
3639
|
+
var DEFAULT_AVATAR = "w-10 h-10 rounded-full object-cover";
|
|
3640
|
+
var DEFAULT_QUOTE = "text-sm leading-snug";
|
|
3641
|
+
var DEFAULT_NAME = "text-xs font-semibold opacity-80";
|
|
3642
|
+
var DEFAULT_STARS = "text-yellow-500 text-sm";
|
|
3643
|
+
function clampStars(n) {
|
|
3644
|
+
return Math.max(0, Math.min(5, Math.round(n)));
|
|
3645
|
+
}
|
|
3646
|
+
function PaywallTestimonials({
|
|
3647
|
+
items,
|
|
4192
3648
|
className,
|
|
4193
|
-
|
|
4194
|
-
|
|
3649
|
+
cardClassName,
|
|
3650
|
+
avatarClassName,
|
|
3651
|
+
quoteClassName,
|
|
3652
|
+
nameClassName,
|
|
3653
|
+
starsClassName,
|
|
3654
|
+
renderItem
|
|
4195
3655
|
}) {
|
|
4196
|
-
const
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
3656
|
+
const rootClasses = [DEFAULT_ROOT, className].filter(Boolean).join(" ");
|
|
3657
|
+
const cardClasses = [DEFAULT_CARD, cardClassName].filter(Boolean).join(" ");
|
|
3658
|
+
const avatarClasses = [DEFAULT_AVATAR, avatarClassName].filter(Boolean).join(" ");
|
|
3659
|
+
const quoteClasses = [DEFAULT_QUOTE, quoteClassName].filter(Boolean).join(" ");
|
|
3660
|
+
const nameClasses = [DEFAULT_NAME, nameClassName].filter(Boolean).join(" ");
|
|
3661
|
+
const starsClasses = [DEFAULT_STARS, starsClassName].filter(Boolean).join(" ");
|
|
3662
|
+
return /* @__PURE__ */ jsx43("div", { className: rootClasses, children: items.map((item, idx) => {
|
|
3663
|
+
if (renderItem) return renderItem(item, idx);
|
|
3664
|
+
const filled = clampStars(item.stars);
|
|
3665
|
+
const empty = 5 - filled;
|
|
3666
|
+
return /* @__PURE__ */ jsxs26("div", { className: cardClasses, children: [
|
|
3667
|
+
/* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
|
|
3668
|
+
item.avatar ? /* @__PURE__ */ jsx43(
|
|
3669
|
+
"img",
|
|
3670
|
+
{
|
|
3671
|
+
src: item.avatar,
|
|
3672
|
+
alt: "",
|
|
3673
|
+
loading: "lazy",
|
|
3674
|
+
className: avatarClasses,
|
|
3675
|
+
"aria-hidden": "true"
|
|
3676
|
+
}
|
|
3677
|
+
) : null,
|
|
3678
|
+
/* @__PURE__ */ jsx43("div", { className: nameClasses, children: item.name })
|
|
3679
|
+
] }),
|
|
3680
|
+
/* @__PURE__ */ jsxs26("div", { className: starsClasses, "aria-label": `${filled} de 5 estrelas`, children: [
|
|
3681
|
+
"\u2605".repeat(filled),
|
|
3682
|
+
"\u2606".repeat(empty)
|
|
3683
|
+
] }),
|
|
3684
|
+
/* @__PURE__ */ jsx43("p", { className: quoteClasses, children: item.quote })
|
|
3685
|
+
] }, idx);
|
|
4215
3686
|
}) });
|
|
4216
3687
|
}
|
|
4217
3688
|
|
|
4218
|
-
// src/components/paywall/
|
|
4219
|
-
import { jsx as
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
3689
|
+
// src/components/paywall/blocks/PaywallStatsRow.tsx
|
|
3690
|
+
import { jsx as jsx44, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
3691
|
+
var DEFAULT_ROOT2 = "grid grid-cols-3 gap-4";
|
|
3692
|
+
var DEFAULT_CELL = "flex flex-col items-center text-center";
|
|
3693
|
+
var DEFAULT_VALUE = "text-2xl font-bold";
|
|
3694
|
+
var DEFAULT_LABEL = "text-xs opacity-70";
|
|
3695
|
+
function PaywallStatsRow({
|
|
3696
|
+
stats,
|
|
3697
|
+
className,
|
|
3698
|
+
cellClassName,
|
|
3699
|
+
valueClassName,
|
|
3700
|
+
labelClassName,
|
|
3701
|
+
renderCell
|
|
3702
|
+
}) {
|
|
3703
|
+
const rootClasses = [DEFAULT_ROOT2, className].filter(Boolean).join(" ");
|
|
3704
|
+
const cellClasses = [DEFAULT_CELL, cellClassName].filter(Boolean).join(" ");
|
|
3705
|
+
const valueClasses = [DEFAULT_VALUE, valueClassName].filter(Boolean).join(" ");
|
|
3706
|
+
const labelClasses = [DEFAULT_LABEL, labelClassName].filter(Boolean).join(" ");
|
|
3707
|
+
return /* @__PURE__ */ jsx44("div", { className: rootClasses, children: stats.map((stat, idx) => {
|
|
3708
|
+
if (renderCell) return renderCell(stat, idx);
|
|
3709
|
+
return /* @__PURE__ */ jsxs27("div", { className: cellClasses, children: [
|
|
3710
|
+
stat.icon ? /* @__PURE__ */ jsx44("div", { "aria-hidden": "true", children: stat.icon }) : null,
|
|
3711
|
+
/* @__PURE__ */ jsx44("div", { className: valueClasses, children: stat.value }),
|
|
3712
|
+
/* @__PURE__ */ jsx44("div", { className: labelClasses, children: stat.label })
|
|
3713
|
+
] }, idx);
|
|
3714
|
+
}) });
|
|
4225
3715
|
}
|
|
4226
3716
|
|
|
4227
|
-
// src/components/paywall/
|
|
4228
|
-
import { jsx as
|
|
4229
|
-
var
|
|
4230
|
-
|
|
4231
|
-
"
|
|
4232
|
-
|
|
4233
|
-
cardSelected: "border-2 border-yellow-400/80 ring-2 ring-yellow-400/20"
|
|
4234
|
-
},
|
|
4235
|
-
"pink-pill": {
|
|
4236
|
-
card: "rounded-2xl",
|
|
4237
|
-
cardSelected: "border-2 border-pink-500"
|
|
4238
|
-
}
|
|
3717
|
+
// src/components/paywall/blocks/PaywallFinePrint.tsx
|
|
3718
|
+
import { jsx as jsx45 } from "react/jsx-runtime";
|
|
3719
|
+
var DEFAULT_CLASS3 = "text-xs opacity-60 leading-snug";
|
|
3720
|
+
var CYCLE_LABEL2 = {
|
|
3721
|
+
MONTHLY: "mensal",
|
|
3722
|
+
YEARLY: "anual"
|
|
4239
3723
|
};
|
|
4240
|
-
function
|
|
4241
|
-
|
|
3724
|
+
function PaywallFinePrint({
|
|
3725
|
+
template,
|
|
4242
3726
|
className,
|
|
4243
|
-
cardClassName,
|
|
4244
|
-
cardSelectedClassName,
|
|
4245
|
-
anchorClassName,
|
|
4246
|
-
variant = "default",
|
|
4247
3727
|
render
|
|
4248
3728
|
}) {
|
|
4249
|
-
const
|
|
4250
|
-
|
|
4251
|
-
|
|
3729
|
+
const {
|
|
3730
|
+
currentPriceCents,
|
|
3731
|
+
cycle,
|
|
3732
|
+
trialDaysCard,
|
|
3733
|
+
trialDaysPix,
|
|
3734
|
+
selectedMethod
|
|
3735
|
+
} = usePaywallContext();
|
|
3736
|
+
const trialDays = selectedMethod === "card" ? trialDaysCard : trialDaysPix;
|
|
3737
|
+
const cycleLabel = CYCLE_LABEL2[cycle] ?? cycle.toLowerCase();
|
|
3738
|
+
const priceFormatted = formatBRL(currentPriceCents ?? 0);
|
|
3739
|
+
const rootClasses = [DEFAULT_CLASS3, className].filter(Boolean).join(" ");
|
|
4252
3740
|
if (render) {
|
|
4253
|
-
return /* @__PURE__ */
|
|
3741
|
+
return /* @__PURE__ */ jsx45("p", { className: className || void 0, children: render({
|
|
3742
|
+
currentPriceCents: currentPriceCents ?? 0,
|
|
3743
|
+
cycle,
|
|
3744
|
+
trialDays: trialDays ?? 0,
|
|
3745
|
+
selectedMethod
|
|
3746
|
+
}) });
|
|
4254
3747
|
}
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
const composedCardClassName = [v.card, cardClassName].filter(Boolean).join(" ");
|
|
4258
|
-
const composedCardSelectedClassName = [v.cardSelected, cardSelectedClassName].filter(Boolean).join(" ");
|
|
4259
|
-
const monthlyCents = plan?.monthlyCents ?? 0;
|
|
4260
|
-
const yearlyCents = plan?.yearlyCents ?? 0;
|
|
4261
|
-
const anchorMonthly = plan?.anchorMonthlyCents ?? null;
|
|
4262
|
-
const anchorYearly = plan?.anchorYearlyCents ?? null;
|
|
4263
|
-
return /* @__PURE__ */ jsx36(
|
|
4264
|
-
"div",
|
|
4265
|
-
{
|
|
4266
|
-
role: "radiogroup",
|
|
4267
|
-
"aria-label": "Ciclo de cobran\xE7a",
|
|
4268
|
-
className: ["flex flex-row gap-2", className].filter(Boolean).join(" "),
|
|
4269
|
-
children: cycles.map((c) => {
|
|
4270
|
-
const active = c === selected;
|
|
4271
|
-
const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
|
|
4272
|
-
const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
|
|
4273
|
-
const mainCents = c === "YEARLY" ? Math.round(yearlyCents / 12) : monthlyCents;
|
|
4274
|
-
const anchorCents = c === "YEARLY" ? anchorYearly : anchorMonthly;
|
|
4275
|
-
return /* @__PURE__ */ jsxs22(
|
|
4276
|
-
"button",
|
|
4277
|
-
{
|
|
4278
|
-
type: "button",
|
|
4279
|
-
role: "radio",
|
|
4280
|
-
"aria-checked": active,
|
|
4281
|
-
onClick: () => setCycle(c),
|
|
4282
|
-
className: [
|
|
4283
|
-
"flex flex-col items-center gap-0.5",
|
|
4284
|
-
composedCardClassName,
|
|
4285
|
-
active ? composedCardSelectedClassName : ""
|
|
4286
|
-
].filter(Boolean).join(" "),
|
|
4287
|
-
children: [
|
|
4288
|
-
/* @__PURE__ */ jsx36("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
|
|
4289
|
-
/* @__PURE__ */ jsx36("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
|
|
4290
|
-
/* @__PURE__ */ jsx36("span", { className: "text-xs opacity-60 leading-tight", children: label }),
|
|
4291
|
-
anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx36("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ jsx36("s", { children: formatBRL(anchorCents) }) }) : null
|
|
4292
|
-
]
|
|
4293
|
-
},
|
|
4294
|
-
c
|
|
4295
|
-
);
|
|
4296
|
-
})
|
|
4297
|
-
}
|
|
4298
|
-
);
|
|
3748
|
+
const text = template.replaceAll("{price}", priceFormatted).replaceAll("{trialDays}", String(trialDays ?? 0)).replaceAll("{cycle}", cycleLabel);
|
|
3749
|
+
return /* @__PURE__ */ jsx45("p", { className: rootClasses, children: text });
|
|
4299
3750
|
}
|
|
4300
3751
|
|
|
4301
|
-
// src/components/paywall/
|
|
4302
|
-
import { jsx as
|
|
4303
|
-
var
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
3752
|
+
// src/components/paywall/blocks/PaywallTrustLine.tsx
|
|
3753
|
+
import { jsx as jsx46, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3754
|
+
var DEFAULT_ROOT3 = "flex items-center gap-3";
|
|
3755
|
+
var DEFAULT_ITEM = "flex items-center gap-1.5 text-xs";
|
|
3756
|
+
function PaywallTrustLine({
|
|
3757
|
+
items,
|
|
3758
|
+
className,
|
|
3759
|
+
itemClassName,
|
|
3760
|
+
renderItem
|
|
4309
3761
|
}) {
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
}
|
|
4318
|
-
) });
|
|
3762
|
+
const rootClasses = [DEFAULT_ROOT3, className].filter(Boolean).join(" ");
|
|
3763
|
+
const itemClasses = [DEFAULT_ITEM, itemClassName].filter(Boolean).join(" ");
|
|
3764
|
+
return /* @__PURE__ */ jsx46("div", { className: rootClasses, children: items.map((item, idx) => {
|
|
3765
|
+
if (renderItem) return renderItem(item, idx);
|
|
3766
|
+
return /* @__PURE__ */ jsxs28("span", { className: itemClasses, children: [
|
|
3767
|
+
/* @__PURE__ */ jsx46("span", { "aria-hidden": "true", children: item.icon }),
|
|
3768
|
+
/* @__PURE__ */ jsx46("span", { children: item.text })
|
|
3769
|
+
] }, idx);
|
|
3770
|
+
}) });
|
|
4319
3771
|
}
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
3772
|
+
|
|
3773
|
+
// src/components/paywall/blocks/PaywallStickyFooter.tsx
|
|
3774
|
+
import { jsx as jsx47 } from "react/jsx-runtime";
|
|
3775
|
+
var DEFAULT_CLASSES = "sticky bottom-0 left-0 right-0 bg-background";
|
|
3776
|
+
var SAFE_AREA_CLASS = "pb-[env(safe-area-inset-bottom)]";
|
|
3777
|
+
function PaywallStickyFooter({
|
|
3778
|
+
children,
|
|
3779
|
+
className,
|
|
3780
|
+
safeAreaInsets = true
|
|
4325
3781
|
}) {
|
|
4326
|
-
const
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
3782
|
+
const classes = [DEFAULT_CLASSES, safeAreaInsets ? SAFE_AREA_CLASS : null, className].filter(Boolean).join(" ");
|
|
3783
|
+
return /* @__PURE__ */ jsx47("div", { className: classes, children });
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3786
|
+
// src/defaults/CheckoutPageDefault.tsx
|
|
3787
|
+
import { Fragment as Fragment7, jsx as jsx48, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3788
|
+
var INTENT_KEY = "hook:paywall:intent";
|
|
3789
|
+
var PIX_PAYLOAD_KEY = "hook:paywall:pix-pending";
|
|
3790
|
+
function readIntent() {
|
|
3791
|
+
if (typeof window === "undefined") return {};
|
|
3792
|
+
try {
|
|
3793
|
+
const raw = sessionStorage.getItem(INTENT_KEY);
|
|
3794
|
+
if (!raw) return {};
|
|
3795
|
+
return JSON.parse(raw);
|
|
3796
|
+
} catch {
|
|
3797
|
+
return {};
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
function formatBrl(cents) {
|
|
3801
|
+
return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(cents / 100);
|
|
3802
|
+
}
|
|
3803
|
+
function formatCardNumber(v) {
|
|
3804
|
+
const digits = v.replace(/\D/g, "").slice(0, 16);
|
|
3805
|
+
return digits.replace(/(.{4})/g, "$1 ").trim();
|
|
3806
|
+
}
|
|
3807
|
+
function formatExpiryMmAa(v) {
|
|
3808
|
+
const d = v.replace(/\D/g, "").slice(0, 4);
|
|
3809
|
+
if (d.length < 3) return d;
|
|
3810
|
+
return d.slice(0, 2) + "/" + d.slice(2);
|
|
3811
|
+
}
|
|
3812
|
+
function parseExpiryMmAa(v) {
|
|
3813
|
+
const d = v.replace(/\D/g, "");
|
|
3814
|
+
return { month: d.slice(0, 2), year: d.slice(2, 4) };
|
|
3815
|
+
}
|
|
3816
|
+
function formatCpf(v) {
|
|
3817
|
+
const d = v.replace(/\D/g, "").slice(0, 11);
|
|
3818
|
+
if (d.length <= 3) return d;
|
|
3819
|
+
if (d.length <= 6) return d.slice(0, 3) + "." + d.slice(3);
|
|
3820
|
+
if (d.length <= 9) return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6);
|
|
3821
|
+
return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6, 9) + "-" + d.slice(9);
|
|
3822
|
+
}
|
|
3823
|
+
function detectCardBrand(num) {
|
|
3824
|
+
const n = num.replace(/\s/g, "");
|
|
3825
|
+
if (/^4/.test(n)) return "VISA";
|
|
3826
|
+
if (/^(5[1-5]|2[2-7])/.test(n)) return "MASTER";
|
|
3827
|
+
if (/^3[47]/.test(n)) return "AMEX";
|
|
3828
|
+
if (/^(4011|4312|4389|4514|6011|6362|6363)/.test(n)) return "ELO";
|
|
3829
|
+
if (/^(606282|3841)/.test(n)) return "HIPER";
|
|
3830
|
+
return "";
|
|
3831
|
+
}
|
|
3832
|
+
function CheckoutPageDefault() {
|
|
3833
|
+
const navigate = useNavigate2();
|
|
3834
|
+
const plan = usePlan();
|
|
3835
|
+
const intent = useMemo6(readIntent, []);
|
|
3836
|
+
const defaultMethod = intent.method === "pix-auto" ? "pix-auto" : "card";
|
|
3837
|
+
const defaultCycle = intent.cycle === "MONTHLY" ? "MONTHLY" : "YEARLY";
|
|
3838
|
+
const form = useCheckoutForm({ defaultMethod, defaultCycle });
|
|
3839
|
+
const [expiryMmAa, setExpiryMmAa] = useState11("");
|
|
3840
|
+
useEffect13(() => {
|
|
3841
|
+
const { month, year } = parseExpiryMmAa(expiryMmAa);
|
|
3842
|
+
if (month !== form.card.expiryMonth || year !== form.card.expiryYear) {
|
|
3843
|
+
form.setCard({ expiryMonth: month, expiryYear: year });
|
|
4346
3844
|
}
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
}, [
|
|
4369
|
-
const
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
3845
|
+
}, [expiryMmAa]);
|
|
3846
|
+
useEffect13(() => {
|
|
3847
|
+
if (form.emailTaken && form.loginUrl) {
|
|
3848
|
+
const t = setTimeout(() => navigate(form.loginUrl), 1200);
|
|
3849
|
+
return () => clearTimeout(t);
|
|
3850
|
+
}
|
|
3851
|
+
}, [form.emailTaken, form.loginUrl, navigate]);
|
|
3852
|
+
const planInfo = plan.data ? {
|
|
3853
|
+
priceCents: plan.data.priceCents,
|
|
3854
|
+
yearlyPriceCents: plan.data.yearlyPriceCents,
|
|
3855
|
+
trialDays: plan.data.trialDays
|
|
3856
|
+
} : null;
|
|
3857
|
+
const annual = form.cycle === "YEARLY";
|
|
3858
|
+
const cyclePrice = useMemo6(() => {
|
|
3859
|
+
if (!planInfo) return null;
|
|
3860
|
+
return annual ? planInfo.yearlyPriceCents ?? planInfo.priceCents * 12 : planInfo.priceCents;
|
|
3861
|
+
}, [planInfo, annual]);
|
|
3862
|
+
const monthlyText = useMemo6(() => {
|
|
3863
|
+
if (!planInfo) return "";
|
|
3864
|
+
const monthly = annual && planInfo.yearlyPriceCents ? Math.round(planInfo.yearlyPriceCents / 12) : planInfo.priceCents;
|
|
3865
|
+
return formatBrl(monthly);
|
|
3866
|
+
}, [planInfo, annual]);
|
|
3867
|
+
const todayCents = useMemo6(() => {
|
|
3868
|
+
if (form.method === "pix-auto") return cyclePrice ?? 0;
|
|
3869
|
+
const trialDays2 = planInfo?.trialDays ?? 0;
|
|
3870
|
+
if (trialDays2 > 0) return 0;
|
|
3871
|
+
return cyclePrice ?? 0;
|
|
3872
|
+
}, [form.method, cyclePrice, planInfo]);
|
|
3873
|
+
const todayAmount = formatBrl(todayCents);
|
|
3874
|
+
const cyclePriceText = cyclePrice !== null ? formatBrl(cyclePrice) : "";
|
|
3875
|
+
const annualSavingsCents = useMemo6(() => {
|
|
3876
|
+
if (!planInfo || !planInfo.yearlyPriceCents) return 0;
|
|
3877
|
+
return planInfo.priceCents * 12 - planInfo.yearlyPriceCents;
|
|
3878
|
+
}, [planInfo]);
|
|
3879
|
+
const trialDays = planInfo?.trialDays ?? 7;
|
|
3880
|
+
const cardBrand = detectCardBrand(form.card.number);
|
|
3881
|
+
async function onSubmit(e) {
|
|
3882
|
+
e.preventDefault();
|
|
3883
|
+
const result = await form.submit();
|
|
3884
|
+
if (!result) return;
|
|
3885
|
+
if (form.method === "pix-auto" && result.pix_qr_payload) {
|
|
3886
|
+
try {
|
|
3887
|
+
sessionStorage.setItem(
|
|
3888
|
+
PIX_PAYLOAD_KEY,
|
|
3889
|
+
JSON.stringify({
|
|
3890
|
+
payload: result.pix_qr_payload,
|
|
3891
|
+
base64: result.pix_qr_base64 ?? null,
|
|
3892
|
+
subscriptionId: result.subscription_id,
|
|
3893
|
+
pixAuthorizationId: result.pix_authorization_id ?? null
|
|
3894
|
+
})
|
|
3895
|
+
);
|
|
3896
|
+
} catch {
|
|
3897
|
+
}
|
|
3898
|
+
navigate(result.redirect.replace(/^.*\/app\/[^/]+/, ""));
|
|
4378
3899
|
return;
|
|
4379
3900
|
}
|
|
4380
|
-
|
|
4381
|
-
}
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
/* @__PURE__ */
|
|
3901
|
+
navigate(result.redirect.replace(/^.*\/app\/[^/]+/, "") || "/");
|
|
3902
|
+
}
|
|
3903
|
+
return /* @__PURE__ */ jsx48("div", { className: "flex-1 flex flex-col bg-background min-h-0", children: /* @__PURE__ */ jsxs29("form", { onSubmit, className: "flex-1 flex flex-col min-h-0", children: [
|
|
3904
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex-1 overflow-y-auto pb-4", children: [
|
|
3905
|
+
form.emailTaken ? /* @__PURE__ */ jsx48("div", { className: "px-5 pt-4", children: /* @__PURE__ */ jsxs29("div", { role: "alert", className: "rounded-2xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: [
|
|
3906
|
+
"Esse e-mail j\xE1 tem conta nesse app.",
|
|
3907
|
+
" ",
|
|
3908
|
+
/* @__PURE__ */ jsx48("a", { href: form.loginUrl ?? "/signin", className: "underline font-semibold", children: "Entrar agora" })
|
|
3909
|
+
] }) }) : null,
|
|
3910
|
+
form.error ? /* @__PURE__ */ jsx48("div", { className: "px-5 pt-4", children: /* @__PURE__ */ jsx48("div", { role: "alert", className: "rounded-2xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: form.error.message || "N\xE3o foi poss\xEDvel concluir o pagamento. Tente novamente." }) }) : null,
|
|
3911
|
+
/* @__PURE__ */ jsx48("div", { className: "px-5 pt-4", children: form.method === "card" ? /* @__PURE__ */ jsxs29("div", { className: "rounded-2xl bg-card border-[1.5px] border-foreground p-3.5", children: [
|
|
3912
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
3913
|
+
/* @__PURE__ */ jsx48(ShieldIcon, { className: "w-4 h-4" }),
|
|
3914
|
+
/* @__PURE__ */ jsx48("div", { className: "text-sm font-bold", children: "Voc\xEA N\xC3O ser\xE1 cobrada hoje" })
|
|
3915
|
+
] }),
|
|
3916
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex justify-between items-baseline text-sm text-muted-foreground", children: [
|
|
3917
|
+
/* @__PURE__ */ jsx48("span", { children: "R$ 0,00 agora" }),
|
|
3918
|
+
/* @__PURE__ */ jsx48("span", { className: "opacity-50", children: "\xB7" }),
|
|
3919
|
+
/* @__PURE__ */ jsxs29("span", { children: [
|
|
3920
|
+
monthlyText,
|
|
3921
|
+
"/m\xEAs ap\xF3s ",
|
|
3922
|
+
trialDays,
|
|
3923
|
+
" dias"
|
|
3924
|
+
] })
|
|
3925
|
+
] }),
|
|
3926
|
+
/* @__PURE__ */ jsxs29("div", { className: "mt-2.5 text-[11px] text-muted-foreground flex items-center gap-1.5", children: [
|
|
3927
|
+
/* @__PURE__ */ jsx48(BellIcon, { className: "w-2.5 h-2.5" }),
|
|
3928
|
+
"Avisamos por email 2 dias antes da primeira cobran\xE7a"
|
|
3929
|
+
] })
|
|
3930
|
+
] }) : /* @__PURE__ */ jsxs29("div", { className: "rounded-2xl p-3.5 bg-emerald-50 border-[1.5px] border-emerald-600/60", children: [
|
|
3931
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2 mb-2 text-emerald-900", children: [
|
|
3932
|
+
/* @__PURE__ */ jsx48(ShieldIcon, { className: "w-4 h-4" }),
|
|
3933
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-sm font-bold", children: [
|
|
3934
|
+
"Garantia incondicional de ",
|
|
3935
|
+
trialDays,
|
|
3936
|
+
" dias"
|
|
3937
|
+
] })
|
|
3938
|
+
] }),
|
|
3939
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-sm text-emerald-900 leading-snug", children: [
|
|
3940
|
+
"Voc\xEA paga hoje via Pix.",
|
|
3941
|
+
/* @__PURE__ */ jsx48("br", {}),
|
|
3942
|
+
"N\xE3o gostou em ",
|
|
3943
|
+
trialDays,
|
|
3944
|
+
" dias? Devolvemos ",
|
|
3945
|
+
/* @__PURE__ */ jsx48("b", { children: "100%" }),
|
|
3946
|
+
" sem perguntas \u2014 direto pelo app."
|
|
3947
|
+
] })
|
|
3948
|
+
] }) }),
|
|
3949
|
+
/* @__PURE__ */ jsxs29("section", { className: "px-5 pt-5", children: [
|
|
3950
|
+
/* @__PURE__ */ jsx48("h2", { className: "font-display text-2xl mb-3.5 leading-tight text-foreground", children: "Quase l\xE1." }),
|
|
3951
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Email" }),
|
|
3952
|
+
/* @__PURE__ */ jsx48(
|
|
3953
|
+
FieldInput,
|
|
3954
|
+
{
|
|
3955
|
+
type: "email",
|
|
3956
|
+
inputMode: "email",
|
|
3957
|
+
autoComplete: "email",
|
|
3958
|
+
autoCapitalize: "none",
|
|
3959
|
+
autoCorrect: "off",
|
|
3960
|
+
spellCheck: false,
|
|
3961
|
+
placeholder: "seu@email.com",
|
|
3962
|
+
value: form.email,
|
|
3963
|
+
onChange: form.setEmail,
|
|
3964
|
+
onBlur: form.markEmailTouched,
|
|
3965
|
+
error: form.emailError,
|
|
3966
|
+
valid: form.emailStatus === "available"
|
|
3967
|
+
}
|
|
3968
|
+
),
|
|
3969
|
+
!form.emailError && /* @__PURE__ */ jsx48(FieldHint, { children: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : "Voc\xEA vai usar este email para entrar no app" }),
|
|
3970
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
3971
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Nome completo" }),
|
|
3972
|
+
/* @__PURE__ */ jsx48(
|
|
3973
|
+
FieldInput,
|
|
3974
|
+
{
|
|
3975
|
+
type: "text",
|
|
3976
|
+
autoComplete: "name",
|
|
3977
|
+
placeholder: "como est\xE1 no documento",
|
|
3978
|
+
value: form.name,
|
|
3979
|
+
onChange: form.setName,
|
|
3980
|
+
onBlur: form.markNameTouched,
|
|
3981
|
+
error: form.nameError,
|
|
3982
|
+
valid: !!form.name && !form.nameError
|
|
3983
|
+
}
|
|
3984
|
+
),
|
|
3985
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
3986
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "CPF" }),
|
|
3987
|
+
/* @__PURE__ */ jsx48(
|
|
3988
|
+
FieldInput,
|
|
3989
|
+
{
|
|
3990
|
+
type: "text",
|
|
3991
|
+
inputMode: "numeric",
|
|
3992
|
+
placeholder: "000.000.000-00",
|
|
3993
|
+
value: form.cpf,
|
|
3994
|
+
onChange: (v) => form.setCpf(formatCpf(v)),
|
|
3995
|
+
onBlur: form.markCpfTouched,
|
|
3996
|
+
error: form.cpfError,
|
|
3997
|
+
valid: !!form.cpf && !form.cpfError
|
|
3998
|
+
}
|
|
3999
|
+
),
|
|
4000
|
+
form.method === "card" ? /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4001
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
4002
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Telefone" }),
|
|
4003
|
+
/* @__PURE__ */ jsx48(
|
|
4004
|
+
FieldInput,
|
|
4005
|
+
{
|
|
4006
|
+
type: "tel",
|
|
4007
|
+
inputMode: "tel",
|
|
4008
|
+
autoComplete: "tel",
|
|
4009
|
+
placeholder: "(11) 99999-9999",
|
|
4010
|
+
value: form.phone,
|
|
4011
|
+
onChange: form.setPhone,
|
|
4012
|
+
onBlur: form.markPhoneTouched,
|
|
4013
|
+
error: form.phoneError,
|
|
4014
|
+
valid: !!form.phone && !form.phoneError
|
|
4015
|
+
}
|
|
4016
|
+
),
|
|
4017
|
+
!form.phoneError && /* @__PURE__ */ jsx48(FieldHint, { children: "Usado pra confirmar pagamento e tratar disputas." })
|
|
4018
|
+
] }) : null
|
|
4019
|
+
] }),
|
|
4020
|
+
/* @__PURE__ */ jsxs29("section", { className: "px-5 pt-5", children: [
|
|
4021
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Forma de pagamento" }),
|
|
4022
|
+
/* @__PURE__ */ jsxs29("div", { role: "tablist", className: "flex gap-1.5 bg-muted p-1 rounded-xl", children: [
|
|
4023
|
+
/* @__PURE__ */ jsx48(
|
|
4024
|
+
TabButton,
|
|
4025
|
+
{
|
|
4026
|
+
active: form.method === "card",
|
|
4027
|
+
onClick: () => form.setMethod("card"),
|
|
4028
|
+
icon: /* @__PURE__ */ jsx48(CardIcon, { className: "w-3.5 h-3.5" }),
|
|
4029
|
+
label: "Cart\xE3o",
|
|
4030
|
+
subtitle: trialDays > 0 ? `${trialDays} dias gr\xE1tis` : "pague hoje",
|
|
4031
|
+
subtitleActiveClass: "text-emerald-700"
|
|
4032
|
+
}
|
|
4033
|
+
),
|
|
4034
|
+
/* @__PURE__ */ jsx48(
|
|
4035
|
+
TabButton,
|
|
4036
|
+
{
|
|
4037
|
+
active: form.method === "pix-auto",
|
|
4038
|
+
onClick: () => form.setMethod("pix-auto"),
|
|
4039
|
+
icon: /* @__PURE__ */ jsx48(PixIcon, { className: "w-3.5 h-3.5" }),
|
|
4040
|
+
label: "Pix",
|
|
4041
|
+
subtitle: `pague hoje \xB7 garantia ${trialDays}d`,
|
|
4042
|
+
subtitleActiveClass: "text-foreground/70"
|
|
4043
|
+
}
|
|
4044
|
+
)
|
|
4045
|
+
] })
|
|
4046
|
+
] }),
|
|
4047
|
+
form.method === "card" ? /* @__PURE__ */ jsxs29("section", { className: "px-5 pt-3.5", children: [
|
|
4048
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "N\xFAmero do cart\xE3o" }),
|
|
4049
|
+
/* @__PURE__ */ jsxs29("div", { className: "relative", children: [
|
|
4050
|
+
/* @__PURE__ */ jsx48(
|
|
4051
|
+
FieldInput,
|
|
4052
|
+
{
|
|
4053
|
+
type: "text",
|
|
4054
|
+
inputMode: "numeric",
|
|
4055
|
+
autoComplete: "cc-number",
|
|
4056
|
+
placeholder: "0000 0000 0000 0000",
|
|
4057
|
+
value: form.card.number,
|
|
4058
|
+
onChange: (v) => form.setCard({ number: formatCardNumber(v) }),
|
|
4059
|
+
style: cardBrand ? { paddingRight: "4.5rem" } : void 0
|
|
4060
|
+
}
|
|
4061
|
+
),
|
|
4062
|
+
cardBrand && /* @__PURE__ */ jsx48("span", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-[10px] font-bold tracking-wide px-1.5 py-0.5 rounded bg-muted text-muted-foreground", children: cardBrand })
|
|
4063
|
+
] }),
|
|
4064
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
4065
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex gap-2.5", children: [
|
|
4066
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex-1", children: [
|
|
4067
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Validade" }),
|
|
4068
|
+
/* @__PURE__ */ jsx48(
|
|
4069
|
+
FieldInput,
|
|
4070
|
+
{
|
|
4071
|
+
type: "text",
|
|
4072
|
+
inputMode: "numeric",
|
|
4073
|
+
autoComplete: "cc-exp",
|
|
4074
|
+
placeholder: "MM/AA",
|
|
4075
|
+
value: expiryMmAa,
|
|
4076
|
+
onChange: (v) => setExpiryMmAa(formatExpiryMmAa(v))
|
|
4077
|
+
}
|
|
4078
|
+
)
|
|
4079
|
+
] }),
|
|
4080
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex-1", children: [
|
|
4081
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "CVV" }),
|
|
4082
|
+
/* @__PURE__ */ jsx48(
|
|
4083
|
+
FieldInput,
|
|
4084
|
+
{
|
|
4085
|
+
type: "text",
|
|
4086
|
+
inputMode: "numeric",
|
|
4087
|
+
autoComplete: "cc-csc",
|
|
4088
|
+
placeholder: "3 d\xEDgitos",
|
|
4089
|
+
value: form.card.ccv,
|
|
4090
|
+
onChange: (v) => form.setCard({ ccv: v.replace(/\D/g, "").slice(0, 4) })
|
|
4091
|
+
}
|
|
4092
|
+
)
|
|
4093
|
+
] })
|
|
4094
|
+
] }),
|
|
4095
|
+
/* @__PURE__ */ jsx48("div", { className: "h-3" }),
|
|
4096
|
+
/* @__PURE__ */ jsx48(FieldLabel, { children: "Nome no cart\xE3o" }),
|
|
4097
|
+
/* @__PURE__ */ jsx48(
|
|
4098
|
+
FieldInput,
|
|
4099
|
+
{
|
|
4100
|
+
type: "text",
|
|
4101
|
+
autoComplete: "cc-name",
|
|
4102
|
+
placeholder: "como est\xE1 no cart\xE3o",
|
|
4103
|
+
value: form.card.holderName,
|
|
4104
|
+
onChange: (v) => form.setCard({ holderName: v })
|
|
4105
|
+
}
|
|
4106
|
+
)
|
|
4107
|
+
] }) : /* @__PURE__ */ jsx48("section", { className: "px-5 pt-3.5", children: /* @__PURE__ */ jsxs29("div", { className: "rounded-2xl bg-card border border-border p-3.5 flex gap-3.5 items-center", children: [
|
|
4108
|
+
/* @__PURE__ */ jsx48("div", { className: "w-[72px] h-[72px] rounded-xl shrink-0 border-2 border-foreground relative overflow-hidden bg-muted", children: /* @__PURE__ */ jsx48("div", { className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[22px] h-[22px] bg-background flex items-center justify-center", children: /* @__PURE__ */ jsx48(PixIcon, { className: "w-3.5 h-3.5 text-foreground" }) }) }),
|
|
4109
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex-1", children: [
|
|
4110
|
+
/* @__PURE__ */ jsx48("div", { className: "text-xs font-bold uppercase tracking-wider text-muted-foreground", children: "pagamento em segundos" }),
|
|
4111
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-sm text-foreground mt-1 leading-snug", children: [
|
|
4112
|
+
"Geramos seu ",
|
|
4113
|
+
/* @__PURE__ */ jsx48("b", { children: "QR Pix" }),
|
|
4114
|
+
" no pr\xF3ximo passo. Pague pelo app do banco e seu acesso libera ",
|
|
4115
|
+
/* @__PURE__ */ jsx48("b", { children: "imediatamente" }),
|
|
4116
|
+
"."
|
|
4117
|
+
] })
|
|
4118
|
+
] })
|
|
4119
|
+
] }) }),
|
|
4120
|
+
/* @__PURE__ */ jsx48("section", { className: "px-5 pt-5", children: /* @__PURE__ */ jsxs29("div", { className: "bg-muted rounded-2xl p-4", children: [
|
|
4121
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex justify-between mb-2.5", children: [
|
|
4122
|
+
/* @__PURE__ */ jsxs29("div", { children: [
|
|
4123
|
+
/* @__PURE__ */ jsx48("div", { className: "text-sm font-semibold text-foreground", children: annual ? "Plano Anual" : "Plano Mensal" }),
|
|
4124
|
+
/* @__PURE__ */ jsx48("div", { className: "text-[11px] text-muted-foreground", children: "Coach" })
|
|
4125
|
+
] }),
|
|
4126
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-right", children: [
|
|
4127
|
+
form.method !== "pix-auto" && /* @__PURE__ */ jsxs29("div", { className: "text-sm font-bold text-foreground", children: [
|
|
4128
|
+
cyclePriceText,
|
|
4129
|
+
"/",
|
|
4130
|
+
annual ? "ano" : "m\xEAs"
|
|
4131
|
+
] }),
|
|
4132
|
+
annual && annualSavingsCents > 0 && /* @__PURE__ */ jsxs29("div", { className: "text-[11px] text-emerald-700 font-semibold", children: [
|
|
4133
|
+
"economia ",
|
|
4134
|
+
formatBrl(annualSavingsCents)
|
|
4135
|
+
] })
|
|
4136
|
+
] })
|
|
4137
|
+
] }),
|
|
4138
|
+
/* @__PURE__ */ jsx48("div", { className: "h-px bg-border my-3" }),
|
|
4139
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex justify-between items-baseline", children: [
|
|
4140
|
+
/* @__PURE__ */ jsxs29("div", { children: [
|
|
4141
|
+
/* @__PURE__ */ jsx48("div", { className: "text-sm font-bold text-foreground", children: "Voc\xEA paga hoje" }),
|
|
4142
|
+
form.method === "card" && trialDays > 0 && /* @__PURE__ */ jsxs29("div", { className: "text-[11px] text-muted-foreground mt-0.5", children: [
|
|
4143
|
+
"cobran\xE7a inicia no dia ",
|
|
4144
|
+
trialDays
|
|
4145
|
+
] }),
|
|
4146
|
+
form.method === "pix-auto" && /* @__PURE__ */ jsxs29("div", { className: "text-[11px] text-emerald-700 mt-0.5 font-semibold", children: [
|
|
4147
|
+
"reembolso garantido at\xE9 o dia ",
|
|
4148
|
+
trialDays
|
|
4149
|
+
] })
|
|
4150
|
+
] }),
|
|
4151
|
+
/* @__PURE__ */ jsx48("div", { className: "text-2xl font-bold font-display tracking-tight text-foreground", children: todayAmount })
|
|
4152
|
+
] })
|
|
4153
|
+
] }) })
|
|
4154
|
+
] }),
|
|
4155
|
+
/* @__PURE__ */ jsxs29(PaywallStickyFooter, { className: "px-5 pt-3.5 pb-5 border-t border-border", children: [
|
|
4156
|
+
/* @__PURE__ */ jsx48(
|
|
4429
4157
|
"button",
|
|
4430
4158
|
{
|
|
4431
|
-
type: "
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4159
|
+
type: "submit",
|
|
4160
|
+
disabled: !form.canSubmit,
|
|
4161
|
+
className: "w-full rounded-full bg-primary text-primary-foreground h-14 px-5 text-base font-bold inline-flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg",
|
|
4162
|
+
children: form.submitting ? /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4163
|
+
/* @__PURE__ */ jsx48(Spinner2, {}),
|
|
4164
|
+
" ",
|
|
4165
|
+
form.method === "pix-auto" ? "Gerando QR\u2026" : "Confirmando\u2026"
|
|
4166
|
+
] }) : form.method === "card" ? /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4167
|
+
/* @__PURE__ */ jsx48(LockIcon, { className: "w-3.5 h-3.5" }),
|
|
4168
|
+
" Confirmar e come\xE7ar gr\xE1tis"
|
|
4169
|
+
] }) : /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4170
|
+
/* @__PURE__ */ jsx48(PixIcon, { className: "w-3.5 h-3.5" }),
|
|
4171
|
+
" Gerar QR Pix"
|
|
4172
|
+
] })
|
|
4438
4173
|
}
|
|
4439
4174
|
),
|
|
4440
|
-
|
|
4441
|
-
|
|
4175
|
+
/* @__PURE__ */ jsxs29("div", { className: "text-center mt-2.5 text-xs text-muted-foreground", children: [
|
|
4176
|
+
"Ao continuar, voc\xEA concorda com nossos ",
|
|
4177
|
+
/* @__PURE__ */ jsx48("u", { children: "Termos" }),
|
|
4178
|
+
". Pagamento seguro Asaas."
|
|
4179
|
+
] }),
|
|
4180
|
+
/* @__PURE__ */ jsxs29("div", { className: "mt-3 flex items-center justify-center gap-3.5 text-[11px] text-muted-foreground", children: [
|
|
4181
|
+
/* @__PURE__ */ jsxs29("span", { className: "inline-flex items-center gap-1", children: [
|
|
4182
|
+
/* @__PURE__ */ jsx48(LockIcon, { className: "w-2.5 h-2.5 opacity-60" }),
|
|
4183
|
+
" SSL 256-bit"
|
|
4184
|
+
] }),
|
|
4185
|
+
/* @__PURE__ */ jsx48("span", { className: "w-px h-2.5 bg-border" }),
|
|
4186
|
+
/* @__PURE__ */ jsx48("span", { children: "Pagamento via Asaas" }),
|
|
4187
|
+
/* @__PURE__ */ jsx48("span", { className: "w-px h-2.5 bg-border" }),
|
|
4188
|
+
/* @__PURE__ */ jsxs29("span", { children: [
|
|
4189
|
+
"Garantia ",
|
|
4190
|
+
trialDays,
|
|
4191
|
+
" dias"
|
|
4192
|
+
] })
|
|
4193
|
+
] })
|
|
4442
4194
|
] })
|
|
4443
|
-
] });
|
|
4444
|
-
}
|
|
4445
|
-
function interp(tpl, vars) {
|
|
4446
|
-
return tpl.replace(/\{(\w+)\}/g, (_m, k) => vars[k] ?? "");
|
|
4195
|
+
] }) });
|
|
4447
4196
|
}
|
|
4448
|
-
function
|
|
4449
|
-
return {
|
|
4450
|
-
tabLabel: m.tabLabel,
|
|
4451
|
-
bodyRows: m.bodyRows.map((r) => interp(r, { price, days })),
|
|
4452
|
-
ctaTemplate: m.ctaTemplate,
|
|
4453
|
-
switchHint: m.switchHint
|
|
4454
|
-
};
|
|
4197
|
+
function FieldLabel({ children }) {
|
|
4198
|
+
return /* @__PURE__ */ jsx48("div", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-1.5", children });
|
|
4455
4199
|
}
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
switchHint,
|
|
4463
|
-
trustLine,
|
|
4464
|
-
className,
|
|
4465
|
-
buttonClassName,
|
|
4466
|
-
switchHintClassName,
|
|
4467
|
-
trustClassName
|
|
4468
|
-
}) {
|
|
4469
|
-
const { submit, submitting } = usePaywallContext();
|
|
4470
|
-
const label = submitting && loadingLabel ? loadingLabel : ctaLabel;
|
|
4471
|
-
return /* @__PURE__ */ jsxs24("div", { className, children: [
|
|
4472
|
-
/* @__PURE__ */ jsx38(
|
|
4473
|
-
"button",
|
|
4200
|
+
function FieldInput(props) {
|
|
4201
|
+
const baseClass = "w-full px-4 rounded-xl bg-card text-base text-foreground outline-none border-[1.5px] transition-colors";
|
|
4202
|
+
const stateClass = props.error ? "border-destructive focus:border-destructive" : props.valid ? "border-emerald-600 focus:border-emerald-700" : "border-border focus:border-foreground";
|
|
4203
|
+
return /* @__PURE__ */ jsxs29(Fragment7, { children: [
|
|
4204
|
+
/* @__PURE__ */ jsx48(
|
|
4205
|
+
"input",
|
|
4474
4206
|
{
|
|
4475
|
-
type: "
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4207
|
+
type: props.type ?? "text",
|
|
4208
|
+
inputMode: props.inputMode,
|
|
4209
|
+
autoComplete: props.autoComplete,
|
|
4210
|
+
autoCapitalize: props.autoCapitalize,
|
|
4211
|
+
autoCorrect: props.autoCorrect,
|
|
4212
|
+
spellCheck: props.spellCheck,
|
|
4213
|
+
placeholder: props.placeholder,
|
|
4214
|
+
value: props.value,
|
|
4215
|
+
onChange: (e) => props.onChange(e.target.value),
|
|
4216
|
+
onBlur: props.onBlur,
|
|
4217
|
+
style: { height: "52px", ...props.style },
|
|
4218
|
+
className: `${baseClass} ${stateClass}`
|
|
4482
4219
|
}
|
|
4483
4220
|
),
|
|
4484
|
-
|
|
4485
|
-
/* @__PURE__ */ jsx38("p", { className: trustClassName, children: trustLine })
|
|
4221
|
+
props.error ? /* @__PURE__ */ jsx48("div", { className: "mt-1.5 text-xs text-destructive font-medium", children: props.error }) : null
|
|
4486
4222
|
] });
|
|
4487
4223
|
}
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
import { jsx as jsx39 } from "react/jsx-runtime";
|
|
4491
|
-
var DEFAULT_EYEBROW_CLASSES = "text-xs uppercase tracking-widest font-semibold opacity-70";
|
|
4492
|
-
function PaywallEyebrow({ text, className }) {
|
|
4493
|
-
return /* @__PURE__ */ jsx39("div", { className: [DEFAULT_EYEBROW_CLASSES, className].filter(Boolean).join(" "), children: text });
|
|
4224
|
+
function FieldHint({ children }) {
|
|
4225
|
+
return /* @__PURE__ */ jsx48("div", { className: "mt-1.5 text-xs text-muted-foreground", children });
|
|
4494
4226
|
}
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
var DEFAULT_GRADIENT = "absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent";
|
|
4499
|
-
function PaywallHero({
|
|
4500
|
-
src,
|
|
4501
|
-
alt = "",
|
|
4502
|
-
headline,
|
|
4503
|
-
aspectRatio = "16/9",
|
|
4504
|
-
gradientClassName,
|
|
4505
|
-
className,
|
|
4506
|
-
headlineClassName,
|
|
4507
|
-
imgClassName,
|
|
4508
|
-
render
|
|
4509
|
-
}) {
|
|
4510
|
-
if (render) {
|
|
4511
|
-
return /* @__PURE__ */ jsx40("div", { className, children: render({ src, headline }) });
|
|
4512
|
-
}
|
|
4513
|
-
return /* @__PURE__ */ jsxs25(
|
|
4514
|
-
"div",
|
|
4227
|
+
function TabButton({ active, onClick, icon, label, subtitle, subtitleActiveClass }) {
|
|
4228
|
+
return /* @__PURE__ */ jsxs29(
|
|
4229
|
+
"button",
|
|
4515
4230
|
{
|
|
4516
|
-
|
|
4517
|
-
|
|
4231
|
+
type: "button",
|
|
4232
|
+
role: "tab",
|
|
4233
|
+
"aria-selected": active,
|
|
4234
|
+
onClick,
|
|
4235
|
+
className: `flex-1 flex flex-col items-center justify-center gap-0.5 py-2.5 px-1.5 rounded-lg text-sm font-semibold transition-colors ${active ? "bg-card text-foreground shadow-sm" : "bg-transparent text-muted-foreground"}`,
|
|
4236
|
+
style: { minHeight: 56 },
|
|
4518
4237
|
children: [
|
|
4519
|
-
/* @__PURE__ */
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
}
|
|
4526
|
-
),
|
|
4527
|
-
/* @__PURE__ */ jsx40("div", { className: gradientClassName ?? DEFAULT_GRADIENT, "aria-hidden": "true" }),
|
|
4528
|
-
headline ? /* @__PURE__ */ jsx40(
|
|
4529
|
-
"h1",
|
|
4530
|
-
{
|
|
4531
|
-
className: ["absolute bottom-0 left-0 right-0 p-4 text-white font-bold text-2xl", headlineClassName].filter(Boolean).join(" "),
|
|
4532
|
-
children: headline
|
|
4533
|
-
}
|
|
4534
|
-
) : null
|
|
4238
|
+
/* @__PURE__ */ jsxs29("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
4239
|
+
icon,
|
|
4240
|
+
" ",
|
|
4241
|
+
label
|
|
4242
|
+
] }),
|
|
4243
|
+
/* @__PURE__ */ jsx48("span", { className: `text-[10px] font-medium ${active ? subtitleActiveClass : "text-muted-foreground/70"}`, children: subtitle })
|
|
4535
4244
|
]
|
|
4536
4245
|
}
|
|
4537
4246
|
);
|
|
4538
4247
|
}
|
|
4248
|
+
function CardIcon({ className }) {
|
|
4249
|
+
return /* @__PURE__ */ jsxs29("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4250
|
+
/* @__PURE__ */ jsx48("rect", { x: "2.5", y: "5.5", width: "19", height: "13", rx: "2.5" }),
|
|
4251
|
+
/* @__PURE__ */ jsx48("path", { d: "M2.5 10h19" })
|
|
4252
|
+
] });
|
|
4253
|
+
}
|
|
4254
|
+
function PixIcon({ className }) {
|
|
4255
|
+
return /* @__PURE__ */ jsx48("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx48("path", { d: "M12 2L2 12l10 10 10-10L12 2zm0 4.83L17.17 12 12 17.17 6.83 12 12 6.83z" }) });
|
|
4256
|
+
}
|
|
4257
|
+
function LockIcon({ className }) {
|
|
4258
|
+
return /* @__PURE__ */ jsxs29("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4259
|
+
/* @__PURE__ */ jsx48("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2.5" }),
|
|
4260
|
+
/* @__PURE__ */ jsx48("path", { d: "M7.5 10V7a4.5 4.5 0 119 0v3" })
|
|
4261
|
+
] });
|
|
4262
|
+
}
|
|
4263
|
+
function ShieldIcon({ className }) {
|
|
4264
|
+
return /* @__PURE__ */ jsxs29("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4265
|
+
/* @__PURE__ */ jsx48("path", { d: "M12 2.5l8 3v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10v-6l8-3z" }),
|
|
4266
|
+
/* @__PURE__ */ jsx48("path", { d: "M9 12l2 2 4-4" })
|
|
4267
|
+
] });
|
|
4268
|
+
}
|
|
4269
|
+
function BellIcon({ className }) {
|
|
4270
|
+
return /* @__PURE__ */ jsxs29("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
4271
|
+
/* @__PURE__ */ jsx48("path", { d: "M6 17V11a6 6 0 1112 0v6l1.5 2H4.5L6 17z" }),
|
|
4272
|
+
/* @__PURE__ */ jsx48("path", { d: "M10 21a2 2 0 004 0" })
|
|
4273
|
+
] });
|
|
4274
|
+
}
|
|
4275
|
+
function Spinner2() {
|
|
4276
|
+
return /* @__PURE__ */ jsx48(
|
|
4277
|
+
"span",
|
|
4278
|
+
{
|
|
4279
|
+
className: "w-4 h-4 rounded-full border-2 border-white/40 border-t-white",
|
|
4280
|
+
style: { animation: "spin 0.7s linear infinite" }
|
|
4281
|
+
}
|
|
4282
|
+
);
|
|
4283
|
+
}
|
|
4539
4284
|
|
|
4540
|
-
// src/
|
|
4541
|
-
import {
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4285
|
+
// src/defaults/PixWaitingPageDefault.tsx
|
|
4286
|
+
import { useEffect as useEffect14, useMemo as useMemo7, useState as useState12 } from "react";
|
|
4287
|
+
import { useNavigate as useNavigate3 } from "react-router-dom";
|
|
4288
|
+
import { useHook as useHook13 } from "@hook-sdk/sdk";
|
|
4289
|
+
import { jsx as jsx49, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
4290
|
+
var PIX_PAYLOAD_KEY2 = "hook:paywall:pix-pending";
|
|
4291
|
+
var TIMEOUT_MS = 30 * 60 * 1e3;
|
|
4292
|
+
function readPixPayload() {
|
|
4293
|
+
if (typeof window === "undefined") return null;
|
|
4294
|
+
try {
|
|
4295
|
+
const raw = sessionStorage.getItem(PIX_PAYLOAD_KEY2);
|
|
4296
|
+
if (!raw) return null;
|
|
4297
|
+
return JSON.parse(raw);
|
|
4298
|
+
} catch {
|
|
4299
|
+
return null;
|
|
4300
|
+
}
|
|
4546
4301
|
}
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
MONTHLY: "mensal",
|
|
4553
|
-
YEARLY: "anual"
|
|
4554
|
-
};
|
|
4555
|
-
function PaywallPriceHeadline({
|
|
4556
|
-
template,
|
|
4557
|
-
className,
|
|
4558
|
-
as = "h1",
|
|
4559
|
-
render
|
|
4560
|
-
}) {
|
|
4561
|
-
const { cycle, currentMonthlyEquivCents, plan } = usePaywallContext();
|
|
4562
|
-
const yearlyCents = plan?.yearlyCents ?? null;
|
|
4563
|
-
const pricePerDay = formatBRL(dailyFromYearly(yearlyCents));
|
|
4564
|
-
const monthlyEquiv = currentMonthlyEquivCents ?? 0;
|
|
4565
|
-
const cycleLabel = CYCLE_LABEL[cycle] ?? cycle.toLowerCase();
|
|
4566
|
-
const rootClasses = [DEFAULT_CLASS, className].filter(Boolean).join(" ");
|
|
4567
|
-
if (render) {
|
|
4568
|
-
const RootTag2 = as;
|
|
4569
|
-
return /* @__PURE__ */ jsx42(RootTag2, { className: [className].filter(Boolean).join(" ") || void 0, children: render({ pricePerDay, currentMonthlyEquivCents: monthlyEquiv, cycle }) });
|
|
4302
|
+
function clearPixPayload() {
|
|
4303
|
+
if (typeof window === "undefined") return;
|
|
4304
|
+
try {
|
|
4305
|
+
sessionStorage.removeItem(PIX_PAYLOAD_KEY2);
|
|
4306
|
+
} catch {
|
|
4570
4307
|
}
|
|
4571
|
-
const text = template.replaceAll("{pricePerDay}", pricePerDay).replaceAll("{currentMonthlyEquiv}", formatBRL(monthlyEquiv)).replaceAll("{cycle}", cycleLabel);
|
|
4572
|
-
const RootTag = as;
|
|
4573
|
-
return /* @__PURE__ */ jsx42(RootTag, { className: rootClasses, children: text });
|
|
4574
4308
|
}
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4309
|
+
function PixWaitingPageDefault() {
|
|
4310
|
+
const navigate = useNavigate3();
|
|
4311
|
+
const { subscription } = useHook13();
|
|
4312
|
+
const payload = useMemo7(readPixPayload, []);
|
|
4313
|
+
const [copied, setCopied] = useState12(false);
|
|
4314
|
+
const [timedOut, setTimedOut] = useState12(false);
|
|
4315
|
+
useEffect14(() => {
|
|
4316
|
+
if (!payload) navigate("/paywall/checkout", { replace: true });
|
|
4317
|
+
}, [payload, navigate]);
|
|
4318
|
+
useEffect14(() => {
|
|
4319
|
+
window.scrollTo(0, 0);
|
|
4320
|
+
}, []);
|
|
4321
|
+
useEffect14(() => {
|
|
4322
|
+
const t = setTimeout(() => setTimedOut(true), TIMEOUT_MS);
|
|
4323
|
+
return () => clearTimeout(t);
|
|
4324
|
+
}, []);
|
|
4325
|
+
const hasAccess = subscription.hasAccess;
|
|
4326
|
+
useEffect14(() => {
|
|
4327
|
+
if (hasAccess) {
|
|
4328
|
+
clearPixPayload();
|
|
4329
|
+
navigate("/", { replace: true });
|
|
4330
|
+
}
|
|
4331
|
+
}, [hasAccess, navigate]);
|
|
4332
|
+
async function copyPayload() {
|
|
4333
|
+
if (!payload?.payload) return;
|
|
4334
|
+
try {
|
|
4335
|
+
await navigator.clipboard.writeText(payload.payload);
|
|
4336
|
+
setCopied(true);
|
|
4337
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
4338
|
+
} catch {
|
|
4339
|
+
}
|
|
4586
4340
|
}
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4341
|
+
if (!payload) return null;
|
|
4342
|
+
if (timedOut) {
|
|
4343
|
+
return /* @__PURE__ */ jsxs30("div", { className: "flex-1 flex flex-col items-center justify-center px-6 py-10 text-center bg-background space-y-4", children: [
|
|
4344
|
+
/* @__PURE__ */ jsx49("h1", { className: "font-display text-2xl text-foreground", children: "PIX expirado" }),
|
|
4345
|
+
/* @__PURE__ */ jsx49("p", { className: "text-sm text-muted-foreground", children: "O tempo pra pagar acabou. Gere um novo PIX." }),
|
|
4346
|
+
/* @__PURE__ */ jsx49(
|
|
4347
|
+
"button",
|
|
4348
|
+
{
|
|
4349
|
+
onClick: () => {
|
|
4350
|
+
clearPixPayload();
|
|
4351
|
+
navigate("/paywall/checkout", { replace: true });
|
|
4352
|
+
},
|
|
4353
|
+
className: "rounded-xl bg-primary px-6 py-3 text-base font-semibold text-primary-foreground",
|
|
4354
|
+
children: "Tentar novamente"
|
|
4355
|
+
}
|
|
4356
|
+
)
|
|
4357
|
+
] });
|
|
4594
4358
|
}
|
|
4595
|
-
return
|
|
4359
|
+
return /* @__PURE__ */ jsxs30("div", { className: "flex-1 flex flex-col items-center px-6 py-8 bg-background space-y-6", children: [
|
|
4360
|
+
/* @__PURE__ */ jsxs30("header", { className: "text-center space-y-2", children: [
|
|
4361
|
+
/* @__PURE__ */ jsx49("h1", { className: "font-display text-2xl text-foreground", children: "Pague o PIX" }),
|
|
4362
|
+
/* @__PURE__ */ jsx49("p", { className: "text-sm text-muted-foreground", children: "Escaneie o QR Code no app do seu banco. O acesso libera assim que confirmarmos o pagamento." })
|
|
4363
|
+
] }),
|
|
4364
|
+
payload.base64 ? /* @__PURE__ */ jsx49(
|
|
4365
|
+
"img",
|
|
4366
|
+
{
|
|
4367
|
+
src: `data:image/png;base64,${payload.base64}`,
|
|
4368
|
+
alt: "QR Code PIX",
|
|
4369
|
+
className: "w-64 h-64 rounded-2xl border border-border bg-card p-2"
|
|
4370
|
+
}
|
|
4371
|
+
) : /* @__PURE__ */ jsx49("div", { className: "w-64 h-64 rounded-2xl border border-border bg-card flex items-center justify-center text-sm text-muted-foreground", children: "QR indispon\xEDvel" }),
|
|
4372
|
+
payload.payload ? /* @__PURE__ */ jsx49(
|
|
4373
|
+
"button",
|
|
4374
|
+
{
|
|
4375
|
+
onClick: copyPayload,
|
|
4376
|
+
className: "w-full max-w-xs rounded-xl border border-border bg-card px-4 py-3 text-sm font-medium text-foreground",
|
|
4377
|
+
children: copied ? "\u2713 Copiado!" : "Copiar c\xF3digo PIX"
|
|
4378
|
+
}
|
|
4379
|
+
) : null,
|
|
4380
|
+
/* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
4381
|
+
/* @__PURE__ */ jsx49("span", { className: "inline-block w-2 h-2 rounded-full bg-primary animate-pulse" }),
|
|
4382
|
+
"Aguardando pagamento\u2026"
|
|
4383
|
+
] }),
|
|
4384
|
+
/* @__PURE__ */ jsx49("p", { className: "text-center text-xs text-muted-foreground", children: "Pode fechar essa janela \u2014 tamb\xE9m enviamos um link de acesso pro seu e-mail." })
|
|
4385
|
+
] });
|
|
4596
4386
|
}
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4387
|
+
|
|
4388
|
+
// src/hooks/useLoginForm.ts
|
|
4389
|
+
import { useCallback as useCallback7, useMemo as useMemo8, useState as useState13 } from "react";
|
|
4390
|
+
import { useHook as useHook14 } from "@hook-sdk/sdk";
|
|
4391
|
+
var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4392
|
+
var MIN_PASSWORD = 8;
|
|
4393
|
+
function useLoginForm() {
|
|
4394
|
+
const { auth } = useHook14();
|
|
4395
|
+
const [email, setEmail] = useState13("");
|
|
4396
|
+
const [password, setPassword] = useState13("");
|
|
4397
|
+
const [submitting, setSubmitting] = useState13(false);
|
|
4398
|
+
const [error, setError] = useState13(null);
|
|
4399
|
+
const [touchedEmail, setTouchedEmail] = useState13(false);
|
|
4400
|
+
const [touchedPassword, setTouchedPassword] = useState13(false);
|
|
4401
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState13(false);
|
|
4402
|
+
const validateEmail = useMemo8(() => {
|
|
4403
|
+
if (email.length === 0) return null;
|
|
4404
|
+
if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
4405
|
+
return null;
|
|
4406
|
+
}, [email]);
|
|
4407
|
+
const validatePassword = useMemo8(() => {
|
|
4408
|
+
if (password.length === 0) return null;
|
|
4409
|
+
if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
|
|
4410
|
+
return null;
|
|
4411
|
+
}, [password]);
|
|
4412
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
4413
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
4414
|
+
const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
|
|
4415
|
+
const submit = useCallback7(async () => {
|
|
4416
|
+
setFormSubmitAttempted(true);
|
|
4417
|
+
if (!canSubmit) return false;
|
|
4418
|
+
setSubmitting(true);
|
|
4419
|
+
setError(null);
|
|
4420
|
+
try {
|
|
4421
|
+
await auth.login({ email, password });
|
|
4422
|
+
return true;
|
|
4423
|
+
} catch (err) {
|
|
4424
|
+
setError(mapSdkError(err));
|
|
4425
|
+
return false;
|
|
4426
|
+
} finally {
|
|
4427
|
+
setSubmitting(false);
|
|
4428
|
+
}
|
|
4429
|
+
}, [auth, email, password, canSubmit]);
|
|
4430
|
+
return {
|
|
4431
|
+
email,
|
|
4432
|
+
setEmail,
|
|
4433
|
+
emailError,
|
|
4434
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
4435
|
+
password,
|
|
4436
|
+
setPassword,
|
|
4437
|
+
passwordError,
|
|
4438
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
4439
|
+
formSubmitAttempted,
|
|
4440
|
+
submit,
|
|
4441
|
+
submitting,
|
|
4442
|
+
canSubmit,
|
|
4443
|
+
error,
|
|
4444
|
+
loginWithGoogle: () => auth.loginWithGoogle()
|
|
4445
|
+
};
|
|
4446
|
+
}
|
|
4447
|
+
|
|
4448
|
+
// src/hooks/useSignupForm.ts
|
|
4449
|
+
import { useCallback as useCallback8, useMemo as useMemo9, useState as useState14 } from "react";
|
|
4450
|
+
import { useHook as useHook15 } from "@hook-sdk/sdk";
|
|
4451
|
+
var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4452
|
+
var MIN_PASSWORD2 = 8;
|
|
4453
|
+
function useSignupForm() {
|
|
4454
|
+
const { auth } = useHook15();
|
|
4455
|
+
const [name, setName] = useState14("");
|
|
4456
|
+
const [email, setEmail] = useState14("");
|
|
4457
|
+
const [password, setPassword] = useState14("");
|
|
4458
|
+
const [submitting, setSubmitting] = useState14(false);
|
|
4459
|
+
const [error, setError] = useState14(null);
|
|
4460
|
+
const [touchedName, setTouchedName] = useState14(false);
|
|
4461
|
+
const [touchedEmail, setTouchedEmail] = useState14(false);
|
|
4462
|
+
const [touchedPassword, setTouchedPassword] = useState14(false);
|
|
4463
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState14(false);
|
|
4464
|
+
const validateName = useMemo9(() => {
|
|
4465
|
+
if (name.length === 0) return null;
|
|
4466
|
+
if (name.trim().length < 2) return "Nome muito curto.";
|
|
4467
|
+
return null;
|
|
4468
|
+
}, [name]);
|
|
4469
|
+
const validateEmail = useMemo9(() => {
|
|
4470
|
+
if (email.length === 0) return null;
|
|
4471
|
+
if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
4472
|
+
return null;
|
|
4473
|
+
}, [email]);
|
|
4474
|
+
const validatePassword = useMemo9(() => {
|
|
4475
|
+
if (password.length === 0) return null;
|
|
4476
|
+
if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
|
|
4477
|
+
return null;
|
|
4478
|
+
}, [password]);
|
|
4479
|
+
const nameError = touchedName || formSubmitAttempted ? validateName : null;
|
|
4480
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
4481
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
4482
|
+
const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
|
|
4483
|
+
const submit = useCallback8(async () => {
|
|
4484
|
+
setFormSubmitAttempted(true);
|
|
4485
|
+
if (!canSubmit) return false;
|
|
4486
|
+
setSubmitting(true);
|
|
4487
|
+
setError(null);
|
|
4488
|
+
try {
|
|
4489
|
+
await auth.signup({ name, email, password });
|
|
4490
|
+
return true;
|
|
4491
|
+
} catch (err) {
|
|
4492
|
+
setError(mapSdkError(err));
|
|
4493
|
+
return false;
|
|
4494
|
+
} finally {
|
|
4495
|
+
setSubmitting(false);
|
|
4496
|
+
}
|
|
4497
|
+
}, [auth, name, email, password, canSubmit]);
|
|
4498
|
+
return {
|
|
4499
|
+
name,
|
|
4500
|
+
setName,
|
|
4501
|
+
nameError,
|
|
4502
|
+
markNameTouched: () => setTouchedName(true),
|
|
4503
|
+
email,
|
|
4504
|
+
setEmail,
|
|
4505
|
+
emailError,
|
|
4506
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
4507
|
+
password,
|
|
4508
|
+
setPassword,
|
|
4509
|
+
passwordError,
|
|
4510
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
4511
|
+
formSubmitAttempted,
|
|
4512
|
+
submit,
|
|
4513
|
+
submitting,
|
|
4514
|
+
canSubmit,
|
|
4515
|
+
error,
|
|
4516
|
+
loginWithGoogle: () => auth.loginWithGoogle()
|
|
4517
|
+
};
|
|
4604
4518
|
}
|
|
4605
|
-
|
|
4606
|
-
|
|
4519
|
+
|
|
4520
|
+
// src/hooks/useForgotForm.ts
|
|
4521
|
+
import { useCallback as useCallback9, useMemo as useMemo10, useState as useState15 } from "react";
|
|
4522
|
+
import { useHook as useHook16 } from "@hook-sdk/sdk";
|
|
4523
|
+
var EMAIL_RE4 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4524
|
+
function useForgotForm() {
|
|
4525
|
+
const { auth } = useHook16();
|
|
4526
|
+
const [email, setEmail] = useState15("");
|
|
4527
|
+
const [submitting, setSubmitting] = useState15(false);
|
|
4528
|
+
const [sent, setSent] = useState15(false);
|
|
4529
|
+
const [error, setError] = useState15(null);
|
|
4530
|
+
const [touchedEmail, setTouchedEmail] = useState15(false);
|
|
4531
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState15(false);
|
|
4532
|
+
const validateEmail = useMemo10(() => {
|
|
4533
|
+
if (email.length === 0) return null;
|
|
4534
|
+
if (!EMAIL_RE4.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
4535
|
+
return null;
|
|
4536
|
+
}, [email]);
|
|
4537
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
4538
|
+
const canSubmit = email.length > 0 && validateEmail === null && !submitting;
|
|
4539
|
+
const submit = useCallback9(async () => {
|
|
4540
|
+
setFormSubmitAttempted(true);
|
|
4541
|
+
if (!canSubmit) return false;
|
|
4542
|
+
setSubmitting(true);
|
|
4543
|
+
setError(null);
|
|
4544
|
+
try {
|
|
4545
|
+
await auth.forgot({ email });
|
|
4546
|
+
setSent(true);
|
|
4547
|
+
return true;
|
|
4548
|
+
} catch (err) {
|
|
4549
|
+
setError(mapSdkError(err));
|
|
4550
|
+
return false;
|
|
4551
|
+
} finally {
|
|
4552
|
+
setSubmitting(false);
|
|
4553
|
+
}
|
|
4554
|
+
}, [auth, email, canSubmit]);
|
|
4555
|
+
return {
|
|
4556
|
+
email,
|
|
4557
|
+
setEmail,
|
|
4558
|
+
emailError,
|
|
4559
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
4560
|
+
formSubmitAttempted,
|
|
4561
|
+
submit,
|
|
4562
|
+
submitting,
|
|
4563
|
+
canSubmit,
|
|
4564
|
+
sent,
|
|
4565
|
+
error
|
|
4566
|
+
};
|
|
4607
4567
|
}
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
})
|
|
4615
|
-
const
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
const [
|
|
4620
|
-
const
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4568
|
+
|
|
4569
|
+
// src/hooks/useResetForm.ts
|
|
4570
|
+
import { useCallback as useCallback10, useEffect as useEffect15, useMemo as useMemo11, useState as useState16 } from "react";
|
|
4571
|
+
import { useHook as useHook17 } from "@hook-sdk/sdk";
|
|
4572
|
+
var MIN_PASSWORD3 = 8;
|
|
4573
|
+
function useResetForm() {
|
|
4574
|
+
const { auth } = useHook17();
|
|
4575
|
+
const [token, setToken] = useState16(null);
|
|
4576
|
+
const [password, setPassword] = useState16("");
|
|
4577
|
+
const [confirm, setConfirm] = useState16("");
|
|
4578
|
+
const [submitting, setSubmitting] = useState16(false);
|
|
4579
|
+
const [done, setDone] = useState16(false);
|
|
4580
|
+
const [error, setError] = useState16(null);
|
|
4581
|
+
const [touchedPassword, setTouchedPassword] = useState16(false);
|
|
4582
|
+
const [touchedConfirm, setTouchedConfirm] = useState16(false);
|
|
4583
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState16(false);
|
|
4584
|
+
useEffect15(() => {
|
|
4585
|
+
if (typeof window === "undefined") return;
|
|
4586
|
+
const params = new URLSearchParams(window.location.search);
|
|
4587
|
+
const t = params.get("token");
|
|
4588
|
+
setToken(t && t.length > 0 ? t : null);
|
|
4589
|
+
}, []);
|
|
4590
|
+
const validatePassword = useMemo11(() => {
|
|
4591
|
+
if (password.length === 0) return null;
|
|
4592
|
+
if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
|
|
4593
|
+
return null;
|
|
4594
|
+
}, [password]);
|
|
4595
|
+
const validateConfirm = useMemo11(() => {
|
|
4596
|
+
if (confirm.length === 0) return null;
|
|
4597
|
+
if (confirm !== password) return "Senhas n\xE3o coincidem.";
|
|
4598
|
+
return null;
|
|
4599
|
+
}, [confirm, password]);
|
|
4600
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
4601
|
+
const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
|
|
4602
|
+
const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
|
|
4603
|
+
const submit = useCallback10(async () => {
|
|
4604
|
+
setFormSubmitAttempted(true);
|
|
4605
|
+
if (!canSubmit || token === null) return;
|
|
4606
|
+
setSubmitting(true);
|
|
4607
|
+
setError(null);
|
|
4608
|
+
try {
|
|
4609
|
+
await auth.reset({ token, newPassword: password });
|
|
4610
|
+
setDone(true);
|
|
4611
|
+
if (typeof window !== "undefined") {
|
|
4612
|
+
const url = new URL(window.location.href);
|
|
4613
|
+
url.searchParams.delete("token");
|
|
4614
|
+
url.searchParams.delete("screen");
|
|
4615
|
+
window.history.replaceState({}, "", url.toString());
|
|
4626
4616
|
}
|
|
4627
|
-
|
|
4617
|
+
} catch (err) {
|
|
4618
|
+
setError(mapSdkError(err));
|
|
4619
|
+
} finally {
|
|
4620
|
+
setSubmitting(false);
|
|
4628
4621
|
}
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4622
|
+
}, [auth, token, password, canSubmit]);
|
|
4623
|
+
return {
|
|
4624
|
+
token,
|
|
4625
|
+
password,
|
|
4626
|
+
setPassword,
|
|
4627
|
+
passwordError,
|
|
4628
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
4629
|
+
confirm,
|
|
4630
|
+
setConfirm,
|
|
4631
|
+
confirmError,
|
|
4632
|
+
markConfirmTouched: () => setTouchedConfirm(true),
|
|
4633
|
+
formSubmitAttempted,
|
|
4634
|
+
submit,
|
|
4635
|
+
submitting,
|
|
4636
|
+
canSubmit,
|
|
4637
|
+
done,
|
|
4638
|
+
error
|
|
4639
|
+
};
|
|
4645
4640
|
}
|
|
4646
4641
|
|
|
4647
|
-
// src/
|
|
4648
|
-
import {
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
}
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4642
|
+
// src/hooks/useAuthPrimitives.ts
|
|
4643
|
+
import { useEffect as useEffect16 } from "react";
|
|
4644
|
+
import { useHook as useHook18 } from "@hook-sdk/sdk";
|
|
4645
|
+
var warned = false;
|
|
4646
|
+
function useAuthPrimitives() {
|
|
4647
|
+
const { auth } = useHook18();
|
|
4648
|
+
useEffect16(() => {
|
|
4649
|
+
if (!warned && process.env.NODE_ENV !== "production") {
|
|
4650
|
+
warned = true;
|
|
4651
|
+
console.warn(
|
|
4652
|
+
"[@hook-sdk/template] useAuthPrimitives() \xE9 escape hatch. Pra login/signup/forgot, use useLoginForm/useSignupForm/useForgotForm. Docs: docs/19-golden-template.md#escape-hatch"
|
|
4653
|
+
);
|
|
4654
|
+
}
|
|
4655
|
+
}, []);
|
|
4656
|
+
return {
|
|
4657
|
+
login: auth.login,
|
|
4658
|
+
signup: auth.signup,
|
|
4659
|
+
logout: auth.logout,
|
|
4660
|
+
logoutAll: auth.logoutAll,
|
|
4661
|
+
forgot: auth.forgot,
|
|
4662
|
+
resendVerify: auth.resendVerify,
|
|
4663
|
+
changePassword: auth.changePassword,
|
|
4664
|
+
changeEmail: auth.changeEmail,
|
|
4665
|
+
refresh: auth.refresh
|
|
4666
|
+
};
|
|
4668
4667
|
}
|
|
4669
4668
|
|
|
4670
|
-
// src/
|
|
4671
|
-
import {
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
itemClassName,
|
|
4680
|
-
renderItem
|
|
4681
|
-
}) {
|
|
4682
|
-
return /* @__PURE__ */ jsx45("div", { className, children: /* @__PURE__ */ jsxs27("div", { className: [DEFAULT_CARD_CLASSES, cardClassName].filter(Boolean).join(" "), children: [
|
|
4683
|
-
title ? /* @__PURE__ */ jsx45("div", { className: ["font-semibold mb-2", titleClassName].filter(Boolean).join(" "), children: title }) : null,
|
|
4684
|
-
/* @__PURE__ */ jsx45("ul", { children: items.map(
|
|
4685
|
-
(item, idx) => renderItem ? /* @__PURE__ */ jsx45("li", { children: renderItem(item, idx) }, idx) : /* @__PURE__ */ jsxs27("li", { className: itemClassName, children: [
|
|
4686
|
-
/* @__PURE__ */ jsx45("span", { "aria-hidden": "true", children: "\u2022" }),
|
|
4687
|
-
" ",
|
|
4688
|
-
/* @__PURE__ */ jsx45("span", { children: item })
|
|
4689
|
-
] }, idx)
|
|
4690
|
-
) })
|
|
4691
|
-
] }) });
|
|
4669
|
+
// src/hooks/useAuth.ts
|
|
4670
|
+
import { useHook as useHook19 } from "@hook-sdk/sdk";
|
|
4671
|
+
function useAuth() {
|
|
4672
|
+
const { user, authStatus, auth } = useHook19();
|
|
4673
|
+
return {
|
|
4674
|
+
user,
|
|
4675
|
+
authStatus,
|
|
4676
|
+
refresh: auth.refresh
|
|
4677
|
+
};
|
|
4692
4678
|
}
|
|
4693
4679
|
|
|
4694
|
-
// src/
|
|
4695
|
-
import {
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
}) {
|
|
4705
|
-
if (render) {
|
|
4706
|
-
return /* @__PURE__ */ jsx46("div", { className, children: render({ text }) });
|
|
4707
|
-
}
|
|
4708
|
-
const rootClasses = [
|
|
4709
|
-
DEFAULT_CHIP_CLASSES,
|
|
4710
|
-
floating ? FLOATING_CLASSES : "",
|
|
4711
|
-
className
|
|
4712
|
-
].filter(Boolean).join(" ");
|
|
4713
|
-
return /* @__PURE__ */ jsxs28("div", { className: rootClasses, children: [
|
|
4714
|
-
/* @__PURE__ */ jsx46("span", { className: iconClassName, "aria-hidden": "true", children: "\u{1F3C6}" }),
|
|
4715
|
-
/* @__PURE__ */ jsx46("span", { children: text })
|
|
4716
|
-
] });
|
|
4680
|
+
// src/index.ts
|
|
4681
|
+
import { useTrackOnboardingStep } from "@hook-sdk/sdk";
|
|
4682
|
+
|
|
4683
|
+
// src/hooks/useSubscription.ts
|
|
4684
|
+
import { useHook as useHook20 } from "@hook-sdk/sdk";
|
|
4685
|
+
function useSubscription() {
|
|
4686
|
+
const { subscription } = useHook20();
|
|
4687
|
+
return {
|
|
4688
|
+
status: subscription.status()
|
|
4689
|
+
};
|
|
4717
4690
|
}
|
|
4718
4691
|
|
|
4719
|
-
// src/
|
|
4720
|
-
import {
|
|
4721
|
-
|
|
4722
|
-
function
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
const
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
}
|
|
4736
|
-
|
|
4692
|
+
// src/hooks/useReminders.ts
|
|
4693
|
+
import { useCallback as useCallback11, useEffect as useEffect17, useState as useState17 } from "react";
|
|
4694
|
+
import { useHook as useHook21 } from "@hook-sdk/sdk";
|
|
4695
|
+
function useReminders() {
|
|
4696
|
+
const { push } = useHook21();
|
|
4697
|
+
const r = push.reminders;
|
|
4698
|
+
const [reminders, setReminders] = useState17([]);
|
|
4699
|
+
const [loading, setLoading] = useState17(true);
|
|
4700
|
+
const reload = useCallback11(async () => {
|
|
4701
|
+
setLoading(true);
|
|
4702
|
+
try {
|
|
4703
|
+
const next = await r.list();
|
|
4704
|
+
setReminders(next);
|
|
4705
|
+
} finally {
|
|
4706
|
+
setLoading(false);
|
|
4707
|
+
}
|
|
4708
|
+
}, [r]);
|
|
4709
|
+
useEffect17(() => {
|
|
4710
|
+
void reload();
|
|
4711
|
+
}, [reload]);
|
|
4712
|
+
const setReminder = useCallback11(async (input) => {
|
|
4713
|
+
await r.set(input);
|
|
4714
|
+
await reload();
|
|
4715
|
+
}, [r, reload]);
|
|
4716
|
+
const deleteReminder = useCallback11(async (slot) => {
|
|
4717
|
+
await r.delete(slot);
|
|
4718
|
+
await reload();
|
|
4719
|
+
}, [r, reload]);
|
|
4720
|
+
const schedule = useCallback11(async (items) => {
|
|
4721
|
+
return r.schedule(items);
|
|
4722
|
+
}, [r]);
|
|
4723
|
+
const setFallbacks = useCallback11(async (items) => {
|
|
4724
|
+
return r.setFallbacks(items);
|
|
4725
|
+
}, [r]);
|
|
4726
|
+
return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
|
|
4737
4727
|
}
|
|
4738
4728
|
|
|
4739
|
-
// src/
|
|
4740
|
-
import {
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
}
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
avatarClassName,
|
|
4755
|
-
quoteClassName,
|
|
4756
|
-
nameClassName,
|
|
4757
|
-
starsClassName,
|
|
4758
|
-
renderItem
|
|
4759
|
-
}) {
|
|
4760
|
-
const rootClasses = [DEFAULT_ROOT, className].filter(Boolean).join(" ");
|
|
4761
|
-
const cardClasses = [DEFAULT_CARD, cardClassName].filter(Boolean).join(" ");
|
|
4762
|
-
const avatarClasses = [DEFAULT_AVATAR, avatarClassName].filter(Boolean).join(" ");
|
|
4763
|
-
const quoteClasses = [DEFAULT_QUOTE, quoteClassName].filter(Boolean).join(" ");
|
|
4764
|
-
const nameClasses = [DEFAULT_NAME, nameClassName].filter(Boolean).join(" ");
|
|
4765
|
-
const starsClasses = [DEFAULT_STARS, starsClassName].filter(Boolean).join(" ");
|
|
4766
|
-
return /* @__PURE__ */ jsx48("div", { className: rootClasses, children: items.map((item, idx) => {
|
|
4767
|
-
if (renderItem) return renderItem(item, idx);
|
|
4768
|
-
const filled = clampStars(item.stars);
|
|
4769
|
-
const empty = 5 - filled;
|
|
4770
|
-
return /* @__PURE__ */ jsxs29("div", { className: cardClasses, children: [
|
|
4771
|
-
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2", children: [
|
|
4772
|
-
item.avatar ? /* @__PURE__ */ jsx48(
|
|
4773
|
-
"img",
|
|
4774
|
-
{
|
|
4775
|
-
src: item.avatar,
|
|
4776
|
-
alt: "",
|
|
4777
|
-
loading: "lazy",
|
|
4778
|
-
className: avatarClasses,
|
|
4779
|
-
"aria-hidden": "true"
|
|
4780
|
-
}
|
|
4781
|
-
) : null,
|
|
4782
|
-
/* @__PURE__ */ jsx48("div", { className: nameClasses, children: item.name })
|
|
4783
|
-
] }),
|
|
4784
|
-
/* @__PURE__ */ jsxs29("div", { className: starsClasses, "aria-label": `${filled} de 5 estrelas`, children: [
|
|
4785
|
-
"\u2605".repeat(filled),
|
|
4786
|
-
"\u2606".repeat(empty)
|
|
4787
|
-
] }),
|
|
4788
|
-
/* @__PURE__ */ jsx48("p", { className: quoteClasses, children: item.quote })
|
|
4789
|
-
] }, idx);
|
|
4790
|
-
}) });
|
|
4729
|
+
// src/hooks/useToast.ts
|
|
4730
|
+
import { useCallback as useCallback12, useState as useState18 } from "react";
|
|
4731
|
+
function useToast() {
|
|
4732
|
+
const [items, setItems] = useState18([]);
|
|
4733
|
+
const show = useCallback12((message, kind = "info") => {
|
|
4734
|
+
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4735
|
+
setItems((prev) => [...prev, { id, message, kind }]);
|
|
4736
|
+
setTimeout(() => {
|
|
4737
|
+
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
4738
|
+
}, 4e3);
|
|
4739
|
+
}, []);
|
|
4740
|
+
const dismiss = useCallback12((id) => {
|
|
4741
|
+
setItems((prev) => prev.filter((t) => t.id !== id));
|
|
4742
|
+
}, []);
|
|
4743
|
+
return { items, show, dismiss };
|
|
4791
4744
|
}
|
|
4792
4745
|
|
|
4793
|
-
// src/
|
|
4794
|
-
import {
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
labelClassName,
|
|
4805
|
-
renderCell
|
|
4806
|
-
}) {
|
|
4807
|
-
const rootClasses = [DEFAULT_ROOT2, className].filter(Boolean).join(" ");
|
|
4808
|
-
const cellClasses = [DEFAULT_CELL, cellClassName].filter(Boolean).join(" ");
|
|
4809
|
-
const valueClasses = [DEFAULT_VALUE, valueClassName].filter(Boolean).join(" ");
|
|
4810
|
-
const labelClasses = [DEFAULT_LABEL, labelClassName].filter(Boolean).join(" ");
|
|
4811
|
-
return /* @__PURE__ */ jsx49("div", { className: rootClasses, children: stats.map((stat, idx) => {
|
|
4812
|
-
if (renderCell) return renderCell(stat, idx);
|
|
4813
|
-
return /* @__PURE__ */ jsxs30("div", { className: cellClasses, children: [
|
|
4814
|
-
stat.icon ? /* @__PURE__ */ jsx49("div", { "aria-hidden": "true", children: stat.icon }) : null,
|
|
4815
|
-
/* @__PURE__ */ jsx49("div", { className: valueClasses, children: stat.value }),
|
|
4816
|
-
/* @__PURE__ */ jsx49("div", { className: labelClasses, children: stat.label })
|
|
4817
|
-
] }, idx);
|
|
4818
|
-
}) });
|
|
4746
|
+
// src/RouteBoundary.tsx
|
|
4747
|
+
import { Routes as Routes2, Route as Route2 } from "react-router-dom";
|
|
4748
|
+
import { jsx as jsx50, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
4749
|
+
function RouteBoundary({ children }) {
|
|
4750
|
+
return /* @__PURE__ */ jsxs31(Routes2, { children: [
|
|
4751
|
+
children,
|
|
4752
|
+
/* @__PURE__ */ jsx50(Route2, { path: "*", element: /* @__PURE__ */ jsx50(DefaultNotFound, {}) })
|
|
4753
|
+
] });
|
|
4754
|
+
}
|
|
4755
|
+
function DefaultNotFound() {
|
|
4756
|
+
return /* @__PURE__ */ jsx50("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
|
|
4819
4757
|
}
|
|
4820
4758
|
|
|
4821
|
-
// src/
|
|
4822
|
-
import {
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
template,
|
|
4830
|
-
className,
|
|
4831
|
-
render
|
|
4759
|
+
// src/PreAuthShell.tsx
|
|
4760
|
+
import { BrowserRouter as BrowserRouter2, MemoryRouter as MemoryRouter2, Routes as Routes3 } from "react-router-dom";
|
|
4761
|
+
import { jsx as jsx51 } from "react/jsx-runtime";
|
|
4762
|
+
function PreAuthShell({
|
|
4763
|
+
basename,
|
|
4764
|
+
testRouter,
|
|
4765
|
+
testInitialEntries,
|
|
4766
|
+
children
|
|
4832
4767
|
}) {
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
cycle,
|
|
4836
|
-
trialDaysCard,
|
|
4837
|
-
trialDaysPix,
|
|
4838
|
-
selectedMethod
|
|
4839
|
-
} = usePaywallContext();
|
|
4840
|
-
const trialDays = selectedMethod === "card" ? trialDaysCard : trialDaysPix;
|
|
4841
|
-
const cycleLabel = CYCLE_LABEL2[cycle] ?? cycle.toLowerCase();
|
|
4842
|
-
const priceFormatted = formatBRL(currentPriceCents ?? 0);
|
|
4843
|
-
const rootClasses = [DEFAULT_CLASS3, className].filter(Boolean).join(" ");
|
|
4844
|
-
if (render) {
|
|
4845
|
-
return /* @__PURE__ */ jsx50("p", { className: className || void 0, children: render({
|
|
4846
|
-
currentPriceCents: currentPriceCents ?? 0,
|
|
4847
|
-
cycle,
|
|
4848
|
-
trialDays: trialDays ?? 0,
|
|
4849
|
-
selectedMethod
|
|
4850
|
-
}) });
|
|
4768
|
+
if (testRouter === "memory") {
|
|
4769
|
+
return /* @__PURE__ */ jsx51(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx51(Routes3, { children }) });
|
|
4851
4770
|
}
|
|
4852
|
-
|
|
4853
|
-
return /* @__PURE__ */ jsx50("p", { className: rootClasses, children: text });
|
|
4771
|
+
return /* @__PURE__ */ jsx51(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx51(Routes3, { children }) });
|
|
4854
4772
|
}
|
|
4855
4773
|
|
|
4856
|
-
// src/
|
|
4857
|
-
import {
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
/* @__PURE__ */ jsx51("span", { "aria-hidden": "true", children: item.icon }),
|
|
4872
|
-
/* @__PURE__ */ jsx51("span", { children: item.text })
|
|
4873
|
-
] }, idx);
|
|
4874
|
-
}) });
|
|
4774
|
+
// src/OnboardingFlow.tsx
|
|
4775
|
+
import { useCallback as useCallback13, useEffect as useEffect18, useMemo as useMemo12, useRef as useRef8 } from "react";
|
|
4776
|
+
import { usePersistedState as usePersistedState4, useHook as useHook22 } from "@hook-sdk/sdk";
|
|
4777
|
+
|
|
4778
|
+
// src/hooks/useOnboardingStep.ts
|
|
4779
|
+
import { createContext as createContext4, useContext as useContext5 } from "react";
|
|
4780
|
+
var OnboardingStepContext = createContext4(null);
|
|
4781
|
+
function useOnboardingStep() {
|
|
4782
|
+
const ctx = useContext5(OnboardingStepContext);
|
|
4783
|
+
if (!ctx) {
|
|
4784
|
+
throw new Error(
|
|
4785
|
+
"[hook-template] useOnboardingStep must be used inside <OnboardingFlow>. (G75)"
|
|
4786
|
+
);
|
|
4787
|
+
}
|
|
4788
|
+
return ctx;
|
|
4875
4789
|
}
|
|
4876
4790
|
|
|
4877
|
-
// src/
|
|
4791
|
+
// src/OnboardingFlow.tsx
|
|
4878
4792
|
import { jsx as jsx52 } from "react/jsx-runtime";
|
|
4879
|
-
var
|
|
4880
|
-
var
|
|
4881
|
-
function
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4793
|
+
var isFilled = (v) => v != null && v !== "";
|
|
4794
|
+
var CURRENT_STEP_FIELD = "currentStep";
|
|
4795
|
+
function readPersistedStepIdx(draft) {
|
|
4796
|
+
const raw = draft[CURRENT_STEP_FIELD];
|
|
4797
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : 0;
|
|
4798
|
+
}
|
|
4799
|
+
function OnboardingFlow({
|
|
4800
|
+
steps,
|
|
4801
|
+
screens,
|
|
4802
|
+
onComplete,
|
|
4803
|
+
persistKey
|
|
4885
4804
|
}) {
|
|
4886
|
-
const
|
|
4887
|
-
|
|
4805
|
+
const [draft, setDraft, status] = usePersistedState4(persistKey, {});
|
|
4806
|
+
const draftRef = useRef8(draft);
|
|
4807
|
+
draftRef.current = draft;
|
|
4808
|
+
const idx = readPersistedStepIdx(draft);
|
|
4809
|
+
const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
|
|
4810
|
+
const setIdx = useCallback13(
|
|
4811
|
+
(n) => {
|
|
4812
|
+
setDraft((prev) => {
|
|
4813
|
+
const prevIdx = readPersistedStepIdx(prev);
|
|
4814
|
+
const nextIdx = typeof n === "function" ? n(prevIdx) : n;
|
|
4815
|
+
return { ...prev, [CURRENT_STEP_FIELD]: nextIdx };
|
|
4816
|
+
});
|
|
4817
|
+
},
|
|
4818
|
+
[setDraft]
|
|
4819
|
+
);
|
|
4820
|
+
const setValue = useCallback13(
|
|
4821
|
+
(patch) => {
|
|
4822
|
+
draftRef.current = { ...draftRef.current, ...patch };
|
|
4823
|
+
setDraft((prev) => ({ ...prev, ...patch }));
|
|
4824
|
+
},
|
|
4825
|
+
[setDraft]
|
|
4826
|
+
);
|
|
4827
|
+
const step = steps[clampedIdx];
|
|
4828
|
+
const hookCtx = useHook22();
|
|
4829
|
+
const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
|
|
4830
|
+
useEffect18(() => {
|
|
4831
|
+
if (status.loading) return;
|
|
4832
|
+
if (!step) return;
|
|
4833
|
+
if (!track2) return;
|
|
4834
|
+
track2("onboarding_step_viewed", {
|
|
4835
|
+
step: step.id,
|
|
4836
|
+
step_index: clampedIdx,
|
|
4837
|
+
total_steps: steps.length
|
|
4838
|
+
});
|
|
4839
|
+
}, [step?.id, clampedIdx, steps.length, status.loading, track2]);
|
|
4840
|
+
const valid = useMemo12(
|
|
4841
|
+
() => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
|
|
4842
|
+
[draft, step]
|
|
4843
|
+
);
|
|
4844
|
+
const next = useCallback13(() => {
|
|
4845
|
+
if (!step) return;
|
|
4846
|
+
const current = draftRef.current;
|
|
4847
|
+
const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
|
|
4848
|
+
if (!validNow) return;
|
|
4849
|
+
if (clampedIdx + 1 >= steps.length) {
|
|
4850
|
+
onComplete(current);
|
|
4851
|
+
} else {
|
|
4852
|
+
setIdx(clampedIdx + 1);
|
|
4853
|
+
}
|
|
4854
|
+
}, [clampedIdx, onComplete, step, steps.length, setIdx]);
|
|
4855
|
+
const prevStep = useCallback13(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
|
|
4856
|
+
const ctx = useMemo12(
|
|
4857
|
+
() => ({
|
|
4858
|
+
stepIndex: clampedIdx,
|
|
4859
|
+
totalSteps: steps.length,
|
|
4860
|
+
value: draft,
|
|
4861
|
+
setValue,
|
|
4862
|
+
valid,
|
|
4863
|
+
next,
|
|
4864
|
+
prev: prevStep
|
|
4865
|
+
}),
|
|
4866
|
+
[clampedIdx, steps.length, draft, setValue, valid, next, prevStep]
|
|
4867
|
+
);
|
|
4868
|
+
if (status.loading) {
|
|
4869
|
+
return null;
|
|
4870
|
+
}
|
|
4871
|
+
if (!step) {
|
|
4872
|
+
throw new Error(
|
|
4873
|
+
`[hook-template] OnboardingFlow: step index ${clampedIdx} out of range (steps.length=${steps.length})`
|
|
4874
|
+
);
|
|
4875
|
+
}
|
|
4876
|
+
const Screen = screens[step.screen];
|
|
4877
|
+
if (!Screen) {
|
|
4878
|
+
throw new Error(
|
|
4879
|
+
`[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
|
|
4880
|
+
);
|
|
4881
|
+
}
|
|
4882
|
+
return /* @__PURE__ */ jsx52(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx52(Screen, {}) });
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
// src/hooks/useFeature.ts
|
|
4886
|
+
function useFeature(name) {
|
|
4887
|
+
const config = useAppConfig();
|
|
4888
|
+
return Array.isArray(config.features_enabled) && config.features_enabled.includes(name);
|
|
4888
4889
|
}
|
|
4889
4890
|
export {
|
|
4890
4891
|
AppConfigProvider,
|