@hook-sdk/template 0.27.0 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2292,6 +2292,13 @@ function DevSkipOnboardingFab({ defaults }) {
2292
2292
  const [state, setState] = (0, import_react13.useState)("idle");
2293
2293
  const [errorMsg, setErrorMsg] = (0, import_react13.useState)(null);
2294
2294
  const timerRef = (0, import_react13.useRef)(null);
2295
+ const isAuthed = hook.authStatus === "authenticated";
2296
+ const [onboarding] = (0, import_sdk6.usePersistedState)(
2297
+ "onboarding_data",
2298
+ null,
2299
+ { enabled: isAuthed }
2300
+ );
2301
+ const onboardingCompleted = isAuthed && onboarding?.onboarding_completed === true;
2295
2302
  const clearTimer = (0, import_react13.useCallback)(() => {
2296
2303
  if (timerRef.current) {
2297
2304
  clearTimeout(timerRef.current);
@@ -2322,6 +2329,7 @@ function DevSkipOnboardingFab({ defaults }) {
2322
2329
  if (state === "error") return `failed \u2014 tap to retry`;
2323
2330
  return hook.authStatus === "authenticated" ? "\u26A1 skip onboarding" : "\u26A1 skip + signup";
2324
2331
  })();
2332
+ if (onboardingCompleted) return null;
2325
2333
  const style = {
2326
2334
  ...STYLES.base,
2327
2335
  ...state === "confirm" || state === "error" ? STYLES.confirm : {},
@@ -2959,7 +2967,7 @@ function useCheckoutForm(args) {
2959
2967
  const emailConfirmError = touchedEmailConfirm || formSubmitAttempted ? validateEmailConfirm : null;
2960
2968
  const phoneError = touchedPhone || formSubmitAttempted ? validatePhone : null;
2961
2969
  const cpfError = touchedCpf || formSubmitAttempted ? validateCpf : null;
2962
- const canSubmit = name.trim().length >= 2 && EMAIL_RE.test(email) && emailConfirm === email && PHONE_RE.test(phone) && validateCpf === null && cpf.replace(/\D/g, "").length === 11 && emailStatus !== "exists" && !submitting && (method !== "card" || card.number.length >= 12 && card.ccv.length >= 3 && card.expiryMonth.length >= 1 && card.expiryYear.length >= 2 && card.holderName.length >= 1);
2970
+ const canSubmit = name.trim().length >= 2 && EMAIL_RE.test(email) && (emailConfirm === "" || emailConfirm === email) && (phone === "" || PHONE_RE.test(phone)) && validateCpf === null && cpf.replace(/\D/g, "").length === 11 && emailStatus !== "exists" && !submitting && (method !== "card" || card.number.length >= 12 && card.ccv.length >= 3 && card.expiryMonth.length >= 1 && card.expiryYear.length >= 2 && card.holderName.length >= 1);
2963
2971
  const submit = (0, import_react17.useCallback)(async () => {
2964
2972
  setFormSubmitAttempted(true);
2965
2973
  setError(null);
@@ -2974,7 +2982,7 @@ function useCheckoutForm(args) {
2974
2982
  method: "card",
2975
2983
  name: name.trim(),
2976
2984
  email,
2977
- emailConfirm,
2985
+ emailConfirm: emailConfirm || email,
2978
2986
  phone,
2979
2987
  cpf: cpf.replace(/\D/g, ""),
2980
2988
  cycle,
@@ -3001,7 +3009,7 @@ function useCheckoutForm(args) {
3001
3009
  method: "pix-auto",
3002
3010
  name: name.trim(),
3003
3011
  email,
3004
- emailConfirm,
3012
+ emailConfirm: emailConfirm || email,
3005
3013
  phone,
3006
3014
  cpf: cpf.replace(/\D/g, ""),
3007
3015
  cycle
@@ -3090,13 +3098,49 @@ function readIntent() {
3090
3098
  function formatBrl(cents) {
3091
3099
  return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(cents / 100);
3092
3100
  }
3101
+ function formatCardNumber(v) {
3102
+ const digits = v.replace(/\D/g, "").slice(0, 16);
3103
+ return digits.replace(/(.{4})/g, "$1 ").trim();
3104
+ }
3105
+ function formatExpiryMmAa(v) {
3106
+ const d = v.replace(/\D/g, "").slice(0, 4);
3107
+ if (d.length < 3) return d;
3108
+ return d.slice(0, 2) + "/" + d.slice(2);
3109
+ }
3110
+ function parseExpiryMmAa(v) {
3111
+ const d = v.replace(/\D/g, "");
3112
+ return { month: d.slice(0, 2), year: d.slice(2, 4) };
3113
+ }
3114
+ function formatCpf(v) {
3115
+ const d = v.replace(/\D/g, "").slice(0, 11);
3116
+ if (d.length <= 3) return d;
3117
+ if (d.length <= 6) return d.slice(0, 3) + "." + d.slice(3);
3118
+ if (d.length <= 9) return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6);
3119
+ return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6, 9) + "-" + d.slice(9);
3120
+ }
3121
+ function detectCardBrand(num) {
3122
+ const n = num.replace(/\s/g, "");
3123
+ if (/^4/.test(n)) return "VISA";
3124
+ if (/^(5[1-5]|2[2-7])/.test(n)) return "MASTER";
3125
+ if (/^3[47]/.test(n)) return "AMEX";
3126
+ if (/^(4011|4312|4389|4514|6011|6362|6363)/.test(n)) return "ELO";
3127
+ if (/^(606282|3841)/.test(n)) return "HIPER";
3128
+ return "";
3129
+ }
3093
3130
  function CheckoutPageDefault() {
3094
3131
  const navigate = (0, import_react_router_dom3.useNavigate)();
3095
3132
  const plan = usePlan();
3096
3133
  const intent = (0, import_react18.useMemo)(readIntent, []);
3097
3134
  const defaultMethod = intent.method === "pix-auto" ? "pix-auto" : "card";
3098
- const defaultCycle = intent.cycle === "YEARLY" ? "YEARLY" : "MONTHLY";
3135
+ const defaultCycle = intent.cycle === "MONTHLY" ? "MONTHLY" : "YEARLY";
3099
3136
  const form = useCheckoutForm({ defaultMethod, defaultCycle });
3137
+ const [expiryMmAa, setExpiryMmAa] = (0, import_react18.useState)("");
3138
+ (0, import_react18.useEffect)(() => {
3139
+ const { month, year } = parseExpiryMmAa(expiryMmAa);
3140
+ if (month !== form.card.expiryMonth || year !== form.card.expiryYear) {
3141
+ form.setCard({ expiryMonth: month, expiryYear: year });
3142
+ }
3143
+ }, [expiryMmAa]);
3100
3144
  (0, import_react18.useEffect)(() => {
3101
3145
  if (form.emailTaken && form.loginUrl) {
3102
3146
  const t = setTimeout(() => navigate(form.loginUrl), 1200);
@@ -3108,21 +3152,30 @@ function CheckoutPageDefault() {
3108
3152
  yearlyPriceCents: plan.data.yearlyPriceCents,
3109
3153
  trialDays: plan.data.trialDays
3110
3154
  } : null;
3155
+ const annual = form.cycle === "YEARLY";
3111
3156
  const cyclePrice = (0, import_react18.useMemo)(() => {
3112
3157
  if (!planInfo) return null;
3113
- return form.cycle === "YEARLY" ? planInfo.yearlyPriceCents ?? planInfo.priceCents * 12 : planInfo.priceCents;
3114
- }, [planInfo, form.cycle]);
3115
- const submitLabel = (0, import_react18.useMemo)(() => {
3116
- if (form.submitting) return "Processando\u2026";
3117
- if (form.method === "card") {
3118
- const trial = planInfo?.trialDays ?? 0;
3119
- if (trial > 0 && cyclePrice) {
3120
- return `Come\xE7ar ${trial} dias gr\xE1tis \xB7 ${formatBrl(cyclePrice)} depois`;
3121
- }
3122
- return cyclePrice ? `Pagar ${formatBrl(cyclePrice)}` : "Continuar";
3123
- }
3124
- return cyclePrice ? `Pagar ${formatBrl(cyclePrice)} via PIX` : "Continuar via PIX";
3125
- }, [form.method, form.submitting, planInfo, cyclePrice]);
3158
+ return annual ? planInfo.yearlyPriceCents ?? planInfo.priceCents * 12 : planInfo.priceCents;
3159
+ }, [planInfo, annual]);
3160
+ const monthlyText = (0, import_react18.useMemo)(() => {
3161
+ if (!planInfo) return "";
3162
+ const monthly = annual && planInfo.yearlyPriceCents ? Math.round(planInfo.yearlyPriceCents / 12) : planInfo.priceCents;
3163
+ return formatBrl(monthly);
3164
+ }, [planInfo, annual]);
3165
+ const todayCents = (0, import_react18.useMemo)(() => {
3166
+ if (form.method === "pix-auto") return cyclePrice ?? 0;
3167
+ const trialDays2 = planInfo?.trialDays ?? 0;
3168
+ if (trialDays2 > 0) return 0;
3169
+ return cyclePrice ?? 0;
3170
+ }, [form.method, cyclePrice, planInfo]);
3171
+ const todayAmount = formatBrl(todayCents);
3172
+ const cyclePriceText = cyclePrice !== null ? formatBrl(cyclePrice) : "";
3173
+ const annualSavingsCents = (0, import_react18.useMemo)(() => {
3174
+ if (!planInfo || !planInfo.yearlyPriceCents) return 0;
3175
+ return planInfo.priceCents * 12 - planInfo.yearlyPriceCents;
3176
+ }, [planInfo]);
3177
+ const trialDays = planInfo?.trialDays ?? 7;
3178
+ const cardBrand = detectCardBrand(form.card.number);
3126
3179
  async function onSubmit(e) {
3127
3180
  e.preventDefault();
3128
3181
  const result = await form.submit();
@@ -3145,201 +3198,367 @@ function CheckoutPageDefault() {
3145
3198
  }
3146
3199
  navigate(result.redirect.replace(/^.*\/app\/[^/]+/, "") || "/");
3147
3200
  }
3148
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "flex-1 flex flex-col bg-background min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("form", { onSubmit, className: "flex-1 overflow-y-auto px-5 py-6 space-y-6", children: [
3149
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("header", { className: "space-y-2", children: [
3150
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h1", { className: "font-display text-2xl text-foreground", children: "Finalizar assinatura" }),
3151
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm text-muted-foreground", children: "Preencha seus dados pra liberar o app. Voc\xEA j\xE1 entra com acesso na hora." })
3152
- ] }),
3153
- form.emailTaken ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { role: "alert", className: "rounded-xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: [
3201
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "flex-1 flex flex-col bg-background min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("form", { onSubmit, className: "flex-1 overflow-y-auto", children: [
3202
+ form.emailTaken ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "px-5 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { role: "alert", className: "rounded-2xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: [
3154
3203
  "Esse e-mail j\xE1 tem conta nesse app.",
3155
3204
  " ",
3156
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("a", { href: form.loginUrl ?? "/signin", className: "underline font-medium", children: "Entrar agora" })
3157
- ] }) : null,
3158
- form.error ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { role: "alert", className: "rounded-xl 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,
3159
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "space-y-3", children: [
3160
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { className: "text-sm font-semibold text-foreground uppercase tracking-wide", children: "Voc\xEA" }),
3161
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3162
- FieldRow,
3163
- {
3164
- label: "Nome completo",
3165
- value: form.name,
3166
- onChange: form.setName,
3167
- onBlur: form.markNameTouched,
3168
- error: form.nameError,
3169
- autoComplete: "name"
3170
- }
3171
- ),
3205
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("a", { href: form.loginUrl ?? "/signin", className: "underline font-semibold", children: "Entrar agora" })
3206
+ ] }) }) : null,
3207
+ form.error ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "px-5 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("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,
3208
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "px-5 pt-4", children: form.method === "card" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "rounded-2xl bg-card border-[1.5px] border-foreground p-3.5", children: [
3209
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
3210
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShieldIcon, { className: "w-4 h-4" }),
3211
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-sm font-bold", children: "Voc\xEA N\xC3O ser\xE1 cobrada hoje" })
3212
+ ] }),
3213
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between items-baseline text-sm text-muted-foreground", children: [
3214
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "R$ 0,00 agora" }),
3215
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "opacity-50", children: "\xB7" }),
3216
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
3217
+ monthlyText,
3218
+ "/m\xEAs ap\xF3s ",
3219
+ trialDays,
3220
+ " dias"
3221
+ ] })
3222
+ ] }),
3223
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "mt-2.5 text-[11px] text-muted-foreground flex items-center gap-1.5", children: [
3224
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BellIcon, { className: "w-2.5 h-2.5" }),
3225
+ "Avisamos por email 2 dias antes da primeira cobran\xE7a"
3226
+ ] })
3227
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "rounded-2xl p-3.5 bg-emerald-50 border-[1.5px] border-emerald-600/60", children: [
3228
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2 mb-2 text-emerald-900", children: [
3229
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShieldIcon, { className: "w-4 h-4" }),
3230
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-sm font-bold", children: [
3231
+ "Garantia incondicional de ",
3232
+ trialDays,
3233
+ " dias"
3234
+ ] })
3235
+ ] }),
3236
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-sm text-emerald-900 leading-snug", children: [
3237
+ "Voc\xEA paga ",
3238
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("b", { children: todayAmount }),
3239
+ " agora via Pix.",
3240
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("br", {}),
3241
+ "N\xE3o gostou em ",
3242
+ trialDays,
3243
+ " dias? Devolvemos ",
3244
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("b", { children: "100%" }),
3245
+ " sem perguntas \u2014 direto pelo app."
3246
+ ] })
3247
+ ] }) }),
3248
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "px-5 pt-5", children: [
3249
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { className: "font-display text-2xl mb-3.5 leading-tight text-foreground", children: "Quase l\xE1." }),
3250
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Email" }),
3172
3251
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3173
- FieldRow,
3252
+ FieldInput,
3174
3253
  {
3175
- label: "E-mail",
3176
3254
  type: "email",
3177
- value: form.email,
3178
- onChange: form.setEmail,
3179
- onBlur: form.markEmailTouched,
3180
- error: form.emailError,
3255
+ inputMode: "email",
3181
3256
  autoComplete: "email",
3182
3257
  autoCapitalize: "none",
3183
3258
  autoCorrect: "off",
3184
3259
  spellCheck: false,
3185
- hint: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : form.emailStatus === "exists" ? null : null
3186
- }
3187
- ),
3188
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3189
- FieldRow,
3190
- {
3191
- label: "Confirme o e-mail",
3192
- type: "email",
3193
- value: form.emailConfirm,
3194
- onChange: form.setEmailConfirm,
3195
- onBlur: form.markEmailConfirmTouched,
3196
- error: form.emailConfirmError,
3197
- autoComplete: "email",
3198
- autoCapitalize: "none",
3199
- autoCorrect: "off",
3200
- spellCheck: false
3260
+ placeholder: "seu@email.com",
3261
+ value: form.email,
3262
+ onChange: form.setEmail,
3263
+ onBlur: form.markEmailTouched,
3264
+ error: form.emailError,
3265
+ valid: form.emailStatus === "available"
3201
3266
  }
3202
3267
  ),
3268
+ !form.emailError && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldHint, { children: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : "Voc\xEA vai usar este email para entrar no app" }),
3269
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-3" }),
3270
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Nome completo" }),
3203
3271
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3204
- FieldRow,
3272
+ FieldInput,
3205
3273
  {
3206
- label: "Telefone",
3207
- type: "tel",
3208
- value: form.phone,
3209
- onChange: form.setPhone,
3210
- onBlur: form.markPhoneTouched,
3211
- error: form.phoneError,
3212
- autoComplete: "tel",
3213
- placeholder: "(11) 99999-9999"
3274
+ type: "text",
3275
+ autoComplete: "name",
3276
+ placeholder: "como est\xE1 no documento",
3277
+ value: form.name,
3278
+ onChange: form.setName,
3279
+ onBlur: form.markNameTouched,
3280
+ error: form.nameError,
3281
+ valid: !!form.name && !form.nameError
3214
3282
  }
3215
3283
  ),
3284
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-3" }),
3285
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "CPF" }),
3216
3286
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3217
- FieldRow,
3287
+ FieldInput,
3218
3288
  {
3219
- label: "CPF",
3220
3289
  type: "text",
3221
3290
  inputMode: "numeric",
3291
+ placeholder: "000.000.000-00",
3222
3292
  value: form.cpf,
3223
- onChange: form.setCpf,
3293
+ onChange: (v) => form.setCpf(formatCpf(v)),
3224
3294
  onBlur: form.markCpfTouched,
3225
3295
  error: form.cpfError,
3226
- autoComplete: "off",
3227
- placeholder: "000.000.000-00"
3296
+ valid: !!form.cpf && !form.cpfError
3228
3297
  }
3229
3298
  )
3230
3299
  ] }),
3231
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "space-y-3", children: [
3232
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { className: "text-sm font-semibold text-foreground uppercase tracking-wide", children: "Pagamento" }),
3233
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-2 gap-2", role: "tablist", children: [
3234
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MethodTab, { active: form.method === "card", onClick: () => form.setMethod("card"), children: "Cart\xE3o" }),
3235
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MethodTab, { active: form.method === "pix-auto", onClick: () => form.setMethod("pix-auto"), children: "PIX" })
3236
- ] }),
3237
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-2 gap-2", role: "tablist", children: [
3238
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MethodTab, { active: form.cycle === "MONTHLY", onClick: () => form.setCycle("MONTHLY"), children: "Mensal" }),
3239
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MethodTab, { active: form.cycle === "YEARLY", onClick: () => form.setCycle("YEARLY"), children: "Anual" })
3240
- ] }),
3241
- form.method === "card" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "space-y-3 rounded-xl border border-border p-4", children: [
3300
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "px-5 pt-5", children: [
3301
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Forma de pagamento" }),
3302
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { role: "tablist", className: "flex gap-1.5 bg-muted p-1 rounded-xl", children: [
3242
3303
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3243
- FieldRow,
3304
+ TabButton,
3244
3305
  {
3245
- label: "Nome no cart\xE3o",
3246
- value: form.card.holderName,
3247
- onChange: (v) => form.setCard({ holderName: v }),
3248
- autoComplete: "cc-name"
3306
+ active: form.method === "card",
3307
+ onClick: () => form.setMethod("card"),
3308
+ icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(CardIcon, { className: "w-3.5 h-3.5" }),
3309
+ label: "Cart\xE3o",
3310
+ subtitle: trialDays > 0 ? `${trialDays} dias gr\xE1tis` : "pague hoje",
3311
+ subtitleActiveClass: "text-emerald-700"
3249
3312
  }
3250
3313
  ),
3251
3314
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3252
- FieldRow,
3315
+ TabButton,
3253
3316
  {
3254
- label: "N\xFAmero",
3255
- value: form.card.number,
3256
- onChange: (v) => form.setCard({ number: v }),
3257
- autoComplete: "cc-number",
3317
+ active: form.method === "pix-auto",
3318
+ onClick: () => form.setMethod("pix-auto"),
3319
+ icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PixIcon, { className: "w-3.5 h-3.5" }),
3320
+ label: "Pix",
3321
+ subtitle: `pague hoje \xB7 garantia ${trialDays}d`,
3322
+ subtitleActiveClass: "text-foreground/70"
3323
+ }
3324
+ )
3325
+ ] })
3326
+ ] }),
3327
+ form.method === "card" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "px-5 pt-3.5", children: [
3328
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "N\xFAmero do cart\xE3o" }),
3329
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "relative", children: [
3330
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3331
+ FieldInput,
3332
+ {
3333
+ type: "text",
3258
3334
  inputMode: "numeric",
3259
- placeholder: "0000 0000 0000 0000"
3335
+ autoComplete: "cc-number",
3336
+ placeholder: "0000 0000 0000 0000",
3337
+ value: form.card.number,
3338
+ onChange: (v) => form.setCard({ number: formatCardNumber(v) }),
3339
+ style: cardBrand ? { paddingRight: "4.5rem" } : void 0
3260
3340
  }
3261
3341
  ),
3262
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
3342
+ cardBrand && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("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 })
3343
+ ] }),
3344
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-3" }),
3345
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex gap-2.5", children: [
3346
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex-1", children: [
3347
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Validade" }),
3263
3348
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3264
- FieldRow,
3349
+ FieldInput,
3265
3350
  {
3266
- label: "M\xEAs",
3267
- value: form.card.expiryMonth,
3268
- onChange: (v) => form.setCard({ expiryMonth: v }),
3269
- autoComplete: "cc-exp-month",
3351
+ type: "text",
3270
3352
  inputMode: "numeric",
3271
- placeholder: "MM"
3353
+ autoComplete: "cc-exp",
3354
+ placeholder: "MM/AA",
3355
+ value: expiryMmAa,
3356
+ onChange: (v) => setExpiryMmAa(formatExpiryMmAa(v))
3272
3357
  }
3273
- ),
3358
+ )
3359
+ ] }),
3360
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex-1", children: [
3361
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "CVV" }),
3274
3362
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3275
- FieldRow,
3363
+ FieldInput,
3276
3364
  {
3277
- label: "Ano",
3278
- value: form.card.expiryYear,
3279
- onChange: (v) => form.setCard({ expiryYear: v }),
3280
- autoComplete: "cc-exp-year",
3365
+ type: "text",
3281
3366
  inputMode: "numeric",
3282
- placeholder: "AA"
3283
- }
3284
- ),
3285
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3286
- FieldRow,
3287
- {
3288
- label: "CVV",
3289
- value: form.card.ccv,
3290
- onChange: (v) => form.setCard({ ccv: v }),
3291
3367
  autoComplete: "cc-csc",
3292
- inputMode: "numeric",
3293
- placeholder: "123"
3368
+ placeholder: "3 d\xEDgitos",
3369
+ value: form.card.ccv,
3370
+ onChange: (v) => form.setCard({ ccv: v.replace(/\D/g, "").slice(0, 4) })
3294
3371
  }
3295
3372
  )
3296
3373
  ] })
3297
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "rounded-xl border border-border p-4 text-sm text-muted-foreground", children: "Ap\xF3s confirmar, mostraremos o QR Code PIX. Pagamento \xE0 vista." })
3298
- ] }),
3299
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3300
- "button",
3301
- {
3302
- type: "submit",
3303
- disabled: !form.canSubmit,
3304
- className: "w-full rounded-xl bg-primary py-4 text-base font-semibold text-primary-foreground disabled:opacity-50 disabled:cursor-not-allowed",
3305
- children: submitLabel
3306
- }
3307
- ),
3308
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-center text-xs text-muted-foreground", children: "\u{1F512} SSL \xB7 via Asaas \xB7 Garantia 7 dias" })
3374
+ ] }),
3375
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-3" }),
3376
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Nome no cart\xE3o" }),
3377
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3378
+ FieldInput,
3379
+ {
3380
+ type: "text",
3381
+ autoComplete: "cc-name",
3382
+ placeholder: "como est\xE1 no cart\xE3o",
3383
+ value: form.card.holderName,
3384
+ onChange: (v) => form.setCard({ holderName: v })
3385
+ }
3386
+ )
3387
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("section", { className: "px-5 pt-3.5", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "rounded-2xl bg-card border border-border p-3.5 flex gap-3.5 items-center", children: [
3388
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "w-[72px] h-[72px] rounded-xl shrink-0 border-2 border-foreground relative overflow-hidden bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("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__ */ (0, import_jsx_runtime27.jsx)(PixIcon, { className: "w-3.5 h-3.5 text-foreground" }) }) }),
3389
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex-1", children: [
3390
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-xs font-bold uppercase tracking-wider text-muted-foreground", children: "pagamento em segundos" }),
3391
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-sm text-foreground mt-1 leading-snug", children: [
3392
+ "Geramos seu ",
3393
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("b", { children: "QR Pix" }),
3394
+ " no pr\xF3ximo passo. Pague pelo app do banco e seu acesso libera ",
3395
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("b", { children: "imediatamente" }),
3396
+ "."
3397
+ ] })
3398
+ ] })
3399
+ ] }) }),
3400
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("section", { className: "px-5 pt-5", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "bg-muted rounded-2xl p-4", children: [
3401
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between mb-2.5", children: [
3402
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { children: [
3403
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-sm font-semibold text-foreground", children: annual ? "Plano Anual" : "Plano Mensal" }),
3404
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-[11px] text-muted-foreground", children: "Coach" })
3405
+ ] }),
3406
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-right", children: [
3407
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-sm font-bold text-foreground", children: [
3408
+ cyclePriceText,
3409
+ "/",
3410
+ annual ? "ano" : "m\xEAs"
3411
+ ] }),
3412
+ annual && annualSavingsCents > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-[11px] text-emerald-700 font-semibold", children: [
3413
+ "economia ",
3414
+ formatBrl(annualSavingsCents)
3415
+ ] })
3416
+ ] })
3417
+ ] }),
3418
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-px bg-border my-3" }),
3419
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between items-baseline", children: [
3420
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { children: [
3421
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-sm font-bold text-foreground", children: "Voc\xEA paga hoje" }),
3422
+ form.method === "card" && trialDays > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-[11px] text-muted-foreground mt-0.5", children: [
3423
+ "cobran\xE7a inicia no dia ",
3424
+ trialDays
3425
+ ] }),
3426
+ form.method === "pix-auto" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-[11px] text-emerald-700 mt-0.5 font-semibold", children: [
3427
+ "reembolso garantido at\xE9 o dia ",
3428
+ trialDays
3429
+ ] })
3430
+ ] }),
3431
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-2xl font-bold font-display tracking-tight text-foreground", children: todayAmount })
3432
+ ] })
3433
+ ] }) }),
3434
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-4" }),
3435
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "sticky bottom-0 px-5 pt-3.5 pb-6 bg-gradient-to-b from-transparent to-background", children: [
3436
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3437
+ "button",
3438
+ {
3439
+ type: "submit",
3440
+ disabled: !form.canSubmit,
3441
+ 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",
3442
+ children: form.submitting ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3443
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Spinner2, {}),
3444
+ " ",
3445
+ form.method === "pix-auto" ? "Gerando QR\u2026" : "Confirmando\u2026"
3446
+ ] }) : form.method === "card" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3447
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(LockIcon, { className: "w-3.5 h-3.5" }),
3448
+ " Confirmar e come\xE7ar gr\xE1tis"
3449
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3450
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PixIcon, { className: "w-3.5 h-3.5" }),
3451
+ " Gerar QR \xB7 pagar ",
3452
+ todayAmount
3453
+ ] })
3454
+ }
3455
+ ),
3456
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-center mt-2.5 text-xs text-muted-foreground", children: [
3457
+ "Ao continuar, voc\xEA concorda com nossos ",
3458
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("u", { children: "Termos" }),
3459
+ ". Pagamento seguro Asaas."
3460
+ ] }),
3461
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "mt-3 flex items-center justify-center gap-3.5 text-[11px] text-muted-foreground", children: [
3462
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
3463
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(LockIcon, { className: "w-2.5 h-2.5 opacity-60" }),
3464
+ " SSL 256-bit"
3465
+ ] }),
3466
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "w-px h-2.5 bg-border" }),
3467
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Pagamento via Asaas" }),
3468
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "w-px h-2.5 bg-border" }),
3469
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
3470
+ "Garantia ",
3471
+ trialDays,
3472
+ " dias"
3473
+ ] })
3474
+ ] })
3475
+ ] })
3309
3476
  ] }) });
3310
3477
  }
3311
- function FieldRow(props) {
3312
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("label", { className: "block space-y-1", children: [
3313
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "block text-sm font-medium text-foreground", children: props.label }),
3478
+ function FieldLabel({ children }) {
3479
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-1.5", children });
3480
+ }
3481
+ function FieldInput(props) {
3482
+ const baseClass = "w-full px-4 rounded-xl bg-card text-base text-foreground outline-none border-[1.5px] transition-colors";
3483
+ const stateClass = props.error ? "border-destructive focus:border-destructive" : props.valid ? "border-emerald-600 focus:border-emerald-700" : "border-border focus:border-foreground";
3484
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3314
3485
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3315
3486
  "input",
3316
3487
  {
3317
3488
  type: props.type ?? "text",
3318
- value: props.value,
3319
- onChange: (e) => props.onChange(e.target.value),
3320
- onBlur: props.onBlur,
3489
+ inputMode: props.inputMode,
3321
3490
  autoComplete: props.autoComplete,
3322
3491
  autoCapitalize: props.autoCapitalize,
3323
3492
  autoCorrect: props.autoCorrect,
3324
3493
  spellCheck: props.spellCheck,
3325
- inputMode: props.inputMode,
3326
3494
  placeholder: props.placeholder,
3327
- className: `w-full rounded-xl border bg-card px-3 py-2 text-base text-foreground outline-none focus:ring-2 focus:ring-primary ${props.error ? "border-destructive" : "border-border"}`
3495
+ value: props.value,
3496
+ onChange: (e) => props.onChange(e.target.value),
3497
+ onBlur: props.onBlur,
3498
+ style: { height: "52px", ...props.style },
3499
+ className: `${baseClass} ${stateClass}`
3328
3500
  }
3329
3501
  ),
3330
- props.error ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "block text-xs text-destructive", children: props.error }) : props.hint ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "block text-xs text-muted-foreground", children: props.hint }) : null
3502
+ props.error ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "mt-1.5 text-xs text-destructive font-medium", children: props.error }) : null
3331
3503
  ] });
3332
3504
  }
3333
- function MethodTab({ active, onClick, children }) {
3334
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3505
+ function FieldHint({ children }) {
3506
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "mt-1.5 text-xs text-muted-foreground", children });
3507
+ }
3508
+ function TabButton({ active, onClick, icon, label, subtitle, subtitleActiveClass }) {
3509
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3335
3510
  "button",
3336
3511
  {
3337
3512
  type: "button",
3338
3513
  role: "tab",
3339
3514
  "aria-selected": active,
3340
3515
  onClick,
3341
- className: `rounded-xl border px-4 py-2 text-sm font-medium transition ${active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground"}`,
3342
- children
3516
+ 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"}`,
3517
+ style: { minHeight: 56 },
3518
+ children: [
3519
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "inline-flex items-center gap-1.5", children: [
3520
+ icon,
3521
+ " ",
3522
+ label
3523
+ ] }),
3524
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: `text-[10px] font-medium ${active ? subtitleActiveClass : "text-muted-foreground/70"}`, children: subtitle })
3525
+ ]
3526
+ }
3527
+ );
3528
+ }
3529
+ function CardIcon({ className }) {
3530
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinejoin: "round", "aria-hidden": "true", children: [
3531
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("rect", { x: "2.5", y: "5.5", width: "19", height: "13", rx: "2.5" }),
3532
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M2.5 10h19" })
3533
+ ] });
3534
+ }
3535
+ function PixIcon({ className }) {
3536
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M12 2L2 12l10 10 10-10L12 2zm0 4.83L17.17 12 12 17.17 6.83 12 12 6.83z" }) });
3537
+ }
3538
+ function LockIcon({ className }) {
3539
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3540
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2.5" }),
3541
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M7.5 10V7a4.5 4.5 0 119 0v3" })
3542
+ ] });
3543
+ }
3544
+ function ShieldIcon({ className }) {
3545
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3546
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M12 2.5l8 3v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10v-6l8-3z" }),
3547
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M9 12l2 2 4-4" })
3548
+ ] });
3549
+ }
3550
+ function BellIcon({ className }) {
3551
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3552
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M6 17V11a6 6 0 1112 0v6l1.5 2H4.5L6 17z" }),
3553
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M10 21a2 2 0 004 0" })
3554
+ ] });
3555
+ }
3556
+ function Spinner2() {
3557
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3558
+ "span",
3559
+ {
3560
+ className: "w-4 h-4 rounded-full border-2 border-white/40 border-t-white",
3561
+ style: { animation: "spin 0.7s linear infinite" }
3343
3562
  }
3344
3563
  );
3345
3564
  }