@hook-sdk/template 0.27.0 → 0.28.0

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