@greatapps/common 1.1.606 → 1.1.607
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.
|
@@ -10,7 +10,8 @@ import { useCreateSetupIntent } from "../../../modules/cards/hooks/create-setup-
|
|
|
10
10
|
import { useUpdateAccount } from "../../../modules/accounts/hooks/useAccountManagement";
|
|
11
11
|
import { useExternalContracting } from "../../../providers/whitelabel.provider";
|
|
12
12
|
import { CardFormFields, BillingFormFields, cardFormSchema } from "./CardFormFields";
|
|
13
|
-
import { IconLoader2, IconX, IconCreditCard } from "@tabler/icons-react";
|
|
13
|
+
import { IconLoader2, IconX, IconCreditCard, IconCheck, IconChevronRight } from "@tabler/icons-react";
|
|
14
|
+
import { cn } from "../../../infra/utils/clsx";
|
|
14
15
|
import { useForm } from "react-hook-form";
|
|
15
16
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
16
17
|
import { CardNumberElement, useElements, useStripe } from "@stripe/react-stripe-js";
|
|
@@ -20,6 +21,43 @@ const INITIAL_STRIPE_STATUS = {
|
|
|
20
21
|
cardExpiry: false,
|
|
21
22
|
cardCvc: false
|
|
22
23
|
};
|
|
24
|
+
const STEPS = [
|
|
25
|
+
{ number: 1, label: "Cobran\xE7a" },
|
|
26
|
+
{ number: 2, label: "Cart\xE3o" }
|
|
27
|
+
];
|
|
28
|
+
function CardStepper({ currentStep }) {
|
|
29
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center gap-2 px-4 py-3 border-b border-zinc-200 lg:border-b-0", children: STEPS.map((step, idx) => {
|
|
30
|
+
const isCompleted = currentStep > step.number;
|
|
31
|
+
const isCurrent = currentStep === step.number;
|
|
32
|
+
const isPending = !isCompleted && !isCurrent;
|
|
33
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
34
|
+
/* @__PURE__ */ jsx(
|
|
35
|
+
"div",
|
|
36
|
+
{
|
|
37
|
+
className: cn(
|
|
38
|
+
"flex items-center justify-center size-6 rounded-full paragraph-xsmall-semibold",
|
|
39
|
+
isCompleted && "bg-green-500 text-white",
|
|
40
|
+
isCurrent && "bg-primary text-white",
|
|
41
|
+
isPending && "border border-zinc-200 text-zinc-400"
|
|
42
|
+
),
|
|
43
|
+
children: isCompleted ? /* @__PURE__ */ jsx(IconCheck, { size: 14, stroke: 3 }) : step.number
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
/* @__PURE__ */ jsx(
|
|
47
|
+
"span",
|
|
48
|
+
{
|
|
49
|
+
className: cn(
|
|
50
|
+
"paragraph-xsmall-semibold",
|
|
51
|
+
(isCompleted || isCurrent) && "text-zinc-950",
|
|
52
|
+
isPending && "text-zinc-400"
|
|
53
|
+
),
|
|
54
|
+
children: step.label
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
idx < STEPS.length - 1 && /* @__PURE__ */ jsx(IconChevronRight, { size: 16, className: "text-zinc-300 ml-1" })
|
|
58
|
+
] }, step.number);
|
|
59
|
+
}) });
|
|
60
|
+
}
|
|
23
61
|
function AddCardModal() {
|
|
24
62
|
const { activeModal, modalData, closeModal } = useModalManager();
|
|
25
63
|
const isOpen = activeModal === "addCardModal";
|
|
@@ -27,7 +65,6 @@ function AddCardModal() {
|
|
|
27
65
|
const [currentStep, setCurrentStep] = useState(1);
|
|
28
66
|
const [stripeStatus, setStripeStatus] = useState(INITIAL_STRIPE_STATUS);
|
|
29
67
|
const [stripeErrors, setStripeErrors] = useState({});
|
|
30
|
-
const [setupIntentId, setSetupIntentId] = useState(null);
|
|
31
68
|
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
|
32
69
|
const onCardAdded = modalData.onCardAdded;
|
|
33
70
|
const createSetupIntentMutation = useCreateSetupIntent();
|
|
@@ -49,7 +86,6 @@ function AddCardModal() {
|
|
|
49
86
|
setCurrentStep(1);
|
|
50
87
|
setStripeStatus(INITIAL_STRIPE_STATUS);
|
|
51
88
|
setStripeErrors({});
|
|
52
|
-
setSetupIntentId(null);
|
|
53
89
|
setIsAuthenticating(false);
|
|
54
90
|
closeModal();
|
|
55
91
|
}, [form, closeModal]);
|
|
@@ -63,14 +99,29 @@ function AddCardModal() {
|
|
|
63
99
|
[]
|
|
64
100
|
);
|
|
65
101
|
const handleContinue = useCallback(async () => {
|
|
66
|
-
const
|
|
102
|
+
const isValid = await form.trigger([
|
|
103
|
+
"fullName",
|
|
104
|
+
"cep",
|
|
105
|
+
"street",
|
|
106
|
+
"streetNumber",
|
|
107
|
+
"neighborhood",
|
|
108
|
+
"city",
|
|
109
|
+
"state"
|
|
110
|
+
]);
|
|
111
|
+
if (!isValid) return;
|
|
112
|
+
setCurrentStep(2);
|
|
113
|
+
}, [form]);
|
|
114
|
+
const handleBack = useCallback(() => {
|
|
115
|
+
setCurrentStep(1);
|
|
116
|
+
}, []);
|
|
117
|
+
async function handleSubmit(data) {
|
|
67
118
|
const newStripeErrors = {};
|
|
68
119
|
if (!stripeStatus.cardNumber) newStripeErrors.cardNumber = "N\xFAmero do cart\xE3o \xE9 obrigat\xF3rio";
|
|
69
120
|
if (!stripeStatus.cardExpiry) newStripeErrors.cardExpiry = "Data de validade \xE9 obrigat\xF3ria";
|
|
70
121
|
if (!stripeStatus.cardCvc) newStripeErrors.cardCvc = "C\xF3digo de seguran\xE7a \xE9 obrigat\xF3rio";
|
|
71
122
|
setStripeErrors(newStripeErrors);
|
|
72
123
|
const stripeValid = stripeStatus.cardNumber && stripeStatus.cardExpiry && stripeStatus.cardCvc;
|
|
73
|
-
if (!
|
|
124
|
+
if (!stripeValid) return;
|
|
74
125
|
if (!stripe || !elements) return;
|
|
75
126
|
const cardElement = elements.getElement(CardNumberElement);
|
|
76
127
|
if (!cardElement) return;
|
|
@@ -95,7 +146,7 @@ function AddCardModal() {
|
|
|
95
146
|
const { setupIntent, error } = await stripe.confirmCardSetup(intent.client_secret, {
|
|
96
147
|
payment_method: {
|
|
97
148
|
card: cardElement,
|
|
98
|
-
billing_details: { name:
|
|
149
|
+
billing_details: { name: data.name }
|
|
99
150
|
}
|
|
100
151
|
});
|
|
101
152
|
if (error) {
|
|
@@ -126,34 +177,9 @@ function AddCardModal() {
|
|
|
126
177
|
);
|
|
127
178
|
return;
|
|
128
179
|
}
|
|
129
|
-
setSetupIntentId(intent.setup_intent_id);
|
|
130
|
-
form.clearErrors(["fullName", "cep", "street", "streetNumber", "neighborhood", "city", "state"]);
|
|
131
|
-
setCurrentStep(2);
|
|
132
|
-
} catch {
|
|
133
|
-
toast.custom(
|
|
134
|
-
(t) => /* @__PURE__ */ jsx(
|
|
135
|
-
Toast,
|
|
136
|
-
{
|
|
137
|
-
variant: "error",
|
|
138
|
-
message: "N\xE3o foi poss\xEDvel iniciar a autentica\xE7\xE3o do cart\xE3o, tente novamente.",
|
|
139
|
-
toastId: t
|
|
140
|
-
}
|
|
141
|
-
),
|
|
142
|
-
{ duration: 5e3 }
|
|
143
|
-
);
|
|
144
|
-
} finally {
|
|
145
|
-
setIsAuthenticating(false);
|
|
146
|
-
}
|
|
147
|
-
}, [form, stripeStatus, stripe, elements, createSetupIntentMutation]);
|
|
148
|
-
const handleBack = useCallback(() => {
|
|
149
|
-
setCurrentStep(1);
|
|
150
|
-
}, []);
|
|
151
|
-
async function handleSubmit(data) {
|
|
152
|
-
if (!setupIntentId) return;
|
|
153
|
-
try {
|
|
154
180
|
const [cardResult] = await Promise.all([
|
|
155
181
|
createCardMutation.mutateAsync({
|
|
156
|
-
setup_intent_id:
|
|
182
|
+
setup_intent_id: intent.setup_intent_id,
|
|
157
183
|
name: data.name,
|
|
158
184
|
set_default: true
|
|
159
185
|
}),
|
|
@@ -190,13 +216,15 @@ function AddCardModal() {
|
|
|
190
216
|
),
|
|
191
217
|
{ duration: 5e3 }
|
|
192
218
|
);
|
|
219
|
+
} finally {
|
|
220
|
+
setIsAuthenticating(false);
|
|
193
221
|
}
|
|
194
222
|
}
|
|
195
223
|
const isSubmitting = createCardMutation.isPending || updateAccountMutation.isPending || form.formState.isSubmitting;
|
|
196
224
|
const watchedValues = form.watch(["name", "country", "personType", "cpf", "cnpj", "fullName", "cep", "street", "streetNumber", "neighborhood", "city", "state"]);
|
|
197
225
|
const [name, country, personType, cpf, cnpj, fullName, cep, street, streetNumber, neighborhood, city, state] = watchedValues;
|
|
198
|
-
const
|
|
199
|
-
const
|
|
226
|
+
const isBillingValid = !!fullName && !!cep && cep.length >= 9 && !!street && !!streetNumber && !!neighborhood && !!city && !!state;
|
|
227
|
+
const isCardValid = !!name && !!country && !!personType && (personType === "pf" ? !!cpf && cpf.length >= 14 : !!cnpj && cnpj.length >= 18) && stripeStatus.cardNumber && stripeStatus.cardExpiry && stripeStatus.cardCvc;
|
|
200
228
|
if (isExternalContracting) {
|
|
201
229
|
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(DialogContent, { className: "flex flex-col p-0 gap-0 w-full md:w-[410px] lg:w-[410px]", children: [
|
|
202
230
|
/* @__PURE__ */ jsxs(DialogHeader, { className: "gap-3 text-left p-5", children: [
|
|
@@ -226,7 +254,7 @@ function AddCardModal() {
|
|
|
226
254
|
className: "flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-dvh top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:max-h-[90vh] lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%] overflow-y-auto",
|
|
227
255
|
children: [
|
|
228
256
|
/* @__PURE__ */ jsx(DialogHeader, { className: "p-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center px-4 py-3 relative border-b border-zinc-200 lg:border-b-0", children: [
|
|
229
|
-
/* @__PURE__ */ jsx(DialogTitle, { className: "paragraph-medium-semibold text-zinc-950 text-center", children:
|
|
257
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: "paragraph-medium-semibold text-zinc-950 text-center", children: "Adicionar cart\xE3o" }),
|
|
230
258
|
/* @__PURE__ */ jsx(
|
|
231
259
|
Button,
|
|
232
260
|
{
|
|
@@ -238,28 +266,28 @@ function AddCardModal() {
|
|
|
238
266
|
}
|
|
239
267
|
)
|
|
240
268
|
] }) }),
|
|
241
|
-
/* @__PURE__ */ jsx(
|
|
242
|
-
"div",
|
|
243
|
-
{
|
|
244
|
-
className: "h-full transition-all duration-300 ease-in-out bg-primary",
|
|
245
|
-
style: { width: currentStep === 1 ? "50%" : "100%" }
|
|
246
|
-
}
|
|
247
|
-
) }),
|
|
269
|
+
/* @__PURE__ */ jsx(CardStepper, { currentStep }),
|
|
248
270
|
/* @__PURE__ */ jsxs(
|
|
249
271
|
"form",
|
|
250
272
|
{
|
|
251
273
|
onSubmit: form.handleSubmit(handleSubmit),
|
|
252
274
|
className: "flex flex-col flex-1 lg:flex-none",
|
|
253
275
|
children: [
|
|
254
|
-
/* @__PURE__ */
|
|
255
|
-
|
|
256
|
-
{
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
276
|
+
/* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 1 ? "hidden" : ""}`, children: [
|
|
277
|
+
/* @__PURE__ */ jsx("h2", { className: "font-[family-name:var(--font-outfit)] text-[18px] leading-tight font-semibold text-zinc-950", children: "Adicione suas informa\xE7\xF5es de cobran\xE7a" }),
|
|
278
|
+
/* @__PURE__ */ jsx(BillingFormFields, { form })
|
|
279
|
+
] }),
|
|
280
|
+
/* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 2 ? "hidden" : ""}`, children: [
|
|
281
|
+
/* @__PURE__ */ jsx("h2", { className: "font-[family-name:var(--font-outfit)] text-[18px] leading-tight font-semibold text-zinc-950", children: "Adicione informa\xE7\xF5es do seu cart\xE3o" }),
|
|
282
|
+
/* @__PURE__ */ jsx(
|
|
283
|
+
CardFormFields,
|
|
284
|
+
{
|
|
285
|
+
form,
|
|
286
|
+
onStripeChange: handleStripeChange,
|
|
287
|
+
stripeErrors
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
] }),
|
|
263
291
|
/* @__PURE__ */ jsx("div", { className: "flex justify-between items-center p-4 lg:p-5 border-t border-zinc-200 gap-2 mt-auto lg:mt-0", children: currentStep === 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
264
292
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", type: "button", onClick: handleClose, className: "h-10!", children: "Cancelar" }),
|
|
265
293
|
/* @__PURE__ */ jsx(
|
|
@@ -268,17 +296,25 @@ function AddCardModal() {
|
|
|
268
296
|
type: "button",
|
|
269
297
|
className: "h-10!",
|
|
270
298
|
onClick: handleContinue,
|
|
271
|
-
disabled: !
|
|
272
|
-
|
|
273
|
-
children: isAuthenticating ? "Autenticando..." : "Continuar"
|
|
299
|
+
disabled: !isBillingValid,
|
|
300
|
+
children: "Continuar"
|
|
274
301
|
}
|
|
275
302
|
)
|
|
276
303
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
277
304
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", type: "button", onClick: handleBack, className: "h-10!", children: "Voltar" }),
|
|
278
|
-
/* @__PURE__ */ jsxs(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
305
|
+
/* @__PURE__ */ jsxs(
|
|
306
|
+
Button,
|
|
307
|
+
{
|
|
308
|
+
type: "submit",
|
|
309
|
+
className: "h-10!",
|
|
310
|
+
disabled: isSubmitting || isAuthenticating || !isCardValid,
|
|
311
|
+
loading: isAuthenticating || isSubmitting,
|
|
312
|
+
children: [
|
|
313
|
+
isAuthenticating && /* @__PURE__ */ jsx(IconLoader2, { className: "size-4 animate-spin" }),
|
|
314
|
+
isAuthenticating ? "Autenticando..." : isSubmitting ? "Adicionando cart\xE3o..." : "Adicionar cart\xE3o"
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
)
|
|
282
318
|
] }) })
|
|
283
319
|
]
|
|
284
320
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/modals/cards/AddCardModal.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useState } from 'react';\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/overlay/Dialog';\nimport { Button } from '../../ui/buttons/Button';\nimport { Toast } from '../../ui/feedback/Toast';\nimport { useModalManager } from '../../../store/useModalManager';\nimport { useCreateCard } from '../../../modules/cards/hooks/create-card.hook';\nimport { useCreateSetupIntent } from '../../../modules/cards/hooks/create-setup-intent.hook';\nimport { useUpdateAccount } from '../../../modules/accounts/hooks/useAccountManagement';\nimport { useExternalContracting } from '../../../providers/whitelabel.provider';\nimport { CardFormFields, BillingFormFields, cardFormSchema } from './CardFormFields';\nimport type { CardFormData, StripeElementsStatus } from './CardFormFields';\nimport { IconLoader2, IconX, IconCreditCard } from '@tabler/icons-react';\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { CardNumberElement, useElements, useStripe } from '@stripe/react-stripe-js';\nimport { toast } from 'sonner';\n\nconst INITIAL_STRIPE_STATUS: StripeElementsStatus = {\n cardNumber: false,\n cardExpiry: false,\n cardCvc: false,\n};\n\nexport default function AddCardModal() {\n const { activeModal, modalData, closeModal } = useModalManager();\n const isOpen = activeModal === 'addCardModal';\n const { isExternalContracting, redirectToExternal } = useExternalContracting();\n const [currentStep, setCurrentStep] = useState(1);\n const [stripeStatus, setStripeStatus] = useState<StripeElementsStatus>(INITIAL_STRIPE_STATUS);\n const [stripeErrors, setStripeErrors] = useState<Record<string, string | undefined>>({});\n const [setupIntentId, setSetupIntentId] = useState<string | null>(null);\n const [isAuthenticating, setIsAuthenticating] = useState(false);\n\n const onCardAdded = modalData.onCardAdded as ((cardId: string) => void) | undefined;\n\n const createSetupIntentMutation = useCreateSetupIntent();\n const createCardMutation = useCreateCard();\n const updateAccountMutation = useUpdateAccount();\n const elements = useElements();\n const stripe = useStripe();\n\n const form = useForm<CardFormData>({\n resolver: zodResolver(cardFormSchema),\n mode: 'onSubmit',\n reValidateMode: 'onChange',\n defaultValues: {\n country: 'BR',\n personType: 'pf',\n },\n });\n\n const handleClose = useCallback(() => {\n form.reset();\n setCurrentStep(1);\n setStripeStatus(INITIAL_STRIPE_STATUS);\n setStripeErrors({});\n setSetupIntentId(null);\n setIsAuthenticating(false);\n closeModal();\n }, [form, closeModal]);\n\n const handleStripeChange = useCallback(\n (field: keyof StripeElementsStatus, complete: boolean) => {\n setStripeStatus((prev) => ({ ...prev, [field]: complete }));\n if (complete) {\n setStripeErrors((prev) => ({ ...prev, [field]: undefined }));\n }\n },\n []\n );\n\n const handleContinue = useCallback(async () => {\n const rhfValid = await form.trigger(['name', 'country', 'personType', 'cpf', 'cnpj']);\n\n const newStripeErrors: Record<string, string | undefined> = {};\n if (!stripeStatus.cardNumber) newStripeErrors.cardNumber = 'Número do cartão é obrigatório';\n if (!stripeStatus.cardExpiry) newStripeErrors.cardExpiry = 'Data de validade é obrigatória';\n if (!stripeStatus.cardCvc) newStripeErrors.cardCvc = 'Código de segurança é obrigatório';\n setStripeErrors(newStripeErrors);\n\n const stripeValid = stripeStatus.cardNumber && stripeStatus.cardExpiry && stripeStatus.cardCvc;\n\n if (!rhfValid || !stripeValid) return;\n if (!stripe || !elements) return;\n\n const cardElement = elements.getElement(CardNumberElement);\n if (!cardElement) return;\n\n setIsAuthenticating(true);\n try {\n const intentResult = await createSetupIntentMutation.mutateAsync();\n const intent = intentResult?.data;\n if (!intent?.client_secret || !intent?.setup_intent_id) {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message=\"Não foi possível iniciar a autenticação do cartão, tente novamente.\"\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n return;\n }\n\n const { setupIntent, error } = await stripe.confirmCardSetup(intent.client_secret, {\n payment_method: {\n card: cardElement,\n billing_details: { name: form.getValues('name') },\n },\n });\n\n if (error) {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message={error.message || 'Falha na autenticação do cartão.'}\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n return;\n }\n\n if (setupIntent?.status !== 'succeeded') {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message={`Autenticação não concluída (status: ${setupIntent?.status ?? 'desconhecido'}).`}\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n return;\n }\n\n setSetupIntentId(intent.setup_intent_id);\n form.clearErrors(['fullName', 'cep', 'street', 'streetNumber', 'neighborhood', 'city', 'state']);\n setCurrentStep(2);\n } catch {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message=\"Não foi possível iniciar a autenticação do cartão, tente novamente.\"\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n } finally {\n setIsAuthenticating(false);\n }\n }, [form, stripeStatus, stripe, elements, createSetupIntentMutation]);\n\n const handleBack = useCallback(() => {\n setCurrentStep(1);\n }, []);\n\n async function handleSubmit(data: CardFormData) {\n if (!setupIntentId) return;\n\n try {\n const [cardResult] = await Promise.all([\n createCardMutation.mutateAsync({\n setup_intent_id: setupIntentId,\n name: data.name,\n set_default: true,\n }),\n updateAccountMutation.mutateAsync({\n financial_document_type: data.personType === 'pf' ? 1 : 2,\n financial_document: data.personType === 'pf' ? data.cpf : data.cnpj,\n financial_name: data.fullName,\n zipcode: data.cep,\n address: data.street,\n address_number: data.streetNumber,\n address_complement: data.complement || '',\n neighborhood: data.neighborhood,\n city: data.city,\n state: data.state,\n }),\n ]);\n\n if (cardResult.success && cardResult.data?.id != null) {\n onCardAdded?.(cardResult.data.id.toString());\n }\n\n toast.custom(\n (t) => <Toast variant=\"success\" message=\"Cartão adicionado com sucesso.\" toastId={t} />,\n { duration: 5000 }\n );\n handleClose();\n } catch {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message=\"Não foi possível salvar o seu novo cartão, tente novamente.\"\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n }\n }\n\n const isSubmitting =\n createCardMutation.isPending ||\n updateAccountMutation.isPending ||\n form.formState.isSubmitting;\n\n const watchedValues = form.watch(['name', 'country', 'personType', 'cpf', 'cnpj', 'fullName', 'cep', 'street', 'streetNumber', 'neighborhood', 'city', 'state']);\n const [name, country, personType, cpf, cnpj, fullName, cep, street, streetNumber, neighborhood, city, state] = watchedValues;\n const isStep1Valid =\n !!name &&\n !!country &&\n !!personType &&\n (personType === 'pf' ? !!cpf && cpf.length >= 14 : !!cnpj && cnpj.length >= 18) &&\n stripeStatus.cardNumber &&\n stripeStatus.cardExpiry &&\n stripeStatus.cardCvc;\n const isStep2Valid =\n !!fullName &&\n !!cep && cep.length >= 9 &&\n !!street &&\n !!streetNumber &&\n !!neighborhood &&\n !!city &&\n !!state;\n\n if (isExternalContracting) {\n return (\n <Dialog open={isOpen} onOpenChange={handleClose}>\n <DialogContent className=\"flex flex-col p-0 gap-0 w-full md:w-[410px] lg:w-[410px]\">\n <DialogHeader className=\"gap-3 text-left p-5\">\n <div className=\"flex items-center justify-center w-10 h-10 bg-zinc-50 rounded-lg\">\n <IconCreditCard size={24} className=\"text-zinc-950\" />\n </div>\n <div className=\"flex flex-col gap-2\">\n <DialogTitle className=\"paragraph-medium-semibold text-zinc-950\">\n Gerenciamento de cartões indisponível\n </DialogTitle>\n <span className=\"paragraph-small-regular text-zinc-600\">\n Acesse sua área de assinatura para gerenciar formas de pagamento.\n </span>\n </div>\n </DialogHeader>\n <div className=\"flex items-center gap-2 p-5 border-t border-zinc-200\">\n <Button\n className=\"w-fit h-10!\"\n onClick={() => {\n handleClose();\n redirectToExternal('contracting');\n }}\n >\n Acessar área de assinatura\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n );\n }\n\n return (\n <Dialog open={isOpen} onOpenChange={handleClose}>\n <DialogContent\n showCloseButton={false}\n className=\"flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-dvh top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:max-h-[90vh] lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%] overflow-y-auto\"\n >\n <DialogHeader className=\"p-0\">\n <div className=\"flex items-center justify-center px-4 py-3 relative border-b border-zinc-200 lg:border-b-0\">\n <DialogTitle className=\"paragraph-medium-semibold text-zinc-950 text-center\">\n {currentStep === 1 ? 'Adicionar cartão' : 'Dados de cobrança'}\n </DialogTitle>\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"size-8! p-0 absolute right-4\"\n onClick={handleClose}\n >\n <IconX size={18} />\n </Button>\n </div>\n </DialogHeader>\n\n {/* Progress bar */}\n <div className=\"h-1 bg-zinc-100 w-full\">\n <div\n className=\"h-full transition-all duration-300 ease-in-out bg-primary\"\n style={{ width: currentStep === 1 ? '50%' : '100%' }}\n />\n </div>\n\n <form\n onSubmit={form.handleSubmit(handleSubmit)}\n className=\"flex flex-col flex-1 lg:flex-none\"\n >\n <div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 1 ? 'hidden' : ''}`}>\n <CardFormFields\n form={form}\n onStripeChange={handleStripeChange}\n stripeErrors={stripeErrors}\n />\n </div>\n <div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 2 ? 'hidden' : ''}`}>\n <BillingFormFields form={form} />\n </div>\n\n <div className=\"flex justify-between items-center p-4 lg:p-5 border-t border-zinc-200 gap-2 mt-auto lg:mt-0\">\n {currentStep === 1 ? (\n <>\n <Button variant=\"secondary\" type=\"button\" onClick={handleClose} className=\"h-10!\">\n Cancelar\n </Button>\n <Button\n type=\"button\"\n className=\"h-10!\"\n onClick={handleContinue}\n disabled={!isStep1Valid}\n loading={isAuthenticating}\n >\n {isAuthenticating ? 'Autenticando...' : 'Continuar'}\n </Button>\n </>\n ) : (\n <>\n <Button variant=\"secondary\" type=\"button\" onClick={handleBack} className=\"h-10!\">\n Voltar\n </Button>\n <Button type=\"submit\" className=\"h-10!\" disabled={isSubmitting || !isStep2Valid}>\n {isSubmitting && <IconLoader2 className=\"size-4 animate-spin\" />}\n {isSubmitting ? 'Adicionando cartão...' : 'Adicionar cartão'}\n </Button>\n </>\n )}\n </div>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/modals/cards/AddCardModal.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useState } from 'react';\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/overlay/Dialog';\nimport { Button } from '../../ui/buttons/Button';\nimport { Toast } from '../../ui/feedback/Toast';\nimport { useModalManager } from '../../../store/useModalManager';\nimport { useCreateCard } from '../../../modules/cards/hooks/create-card.hook';\nimport { useCreateSetupIntent } from '../../../modules/cards/hooks/create-setup-intent.hook';\nimport { useUpdateAccount } from '../../../modules/accounts/hooks/useAccountManagement';\nimport { useExternalContracting } from '../../../providers/whitelabel.provider';\nimport { CardFormFields, BillingFormFields, cardFormSchema } from './CardFormFields';\nimport type { CardFormData, StripeElementsStatus } from './CardFormFields';\nimport { IconLoader2, IconX, IconCreditCard, IconCheck, IconChevronRight } from '@tabler/icons-react';\nimport { cn } from '../../../infra/utils/clsx';\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { CardNumberElement, useElements, useStripe } from '@stripe/react-stripe-js';\nimport { toast } from 'sonner';\n\nconst INITIAL_STRIPE_STATUS: StripeElementsStatus = {\n cardNumber: false,\n cardExpiry: false,\n cardCvc: false,\n};\n\nconst STEPS = [\n { number: 1, label: 'Cobrança' },\n { number: 2, label: 'Cartão' },\n] as const;\n\nfunction CardStepper({ currentStep }: { readonly currentStep: number }) {\n return (\n <div className=\"flex items-center justify-center gap-2 px-4 py-3 border-b border-zinc-200 lg:border-b-0\">\n {STEPS.map((step, idx) => {\n const isCompleted = currentStep > step.number;\n const isCurrent = currentStep === step.number;\n const isPending = !isCompleted && !isCurrent;\n return (\n <div key={step.number} className=\"flex items-center gap-2\">\n <div\n className={cn(\n 'flex items-center justify-center size-6 rounded-full paragraph-xsmall-semibold',\n isCompleted && 'bg-green-500 text-white',\n isCurrent && 'bg-primary text-white',\n isPending && 'border border-zinc-200 text-zinc-400',\n )}\n >\n {isCompleted ? <IconCheck size={14} stroke={3} /> : step.number}\n </div>\n <span\n className={cn(\n 'paragraph-xsmall-semibold',\n (isCompleted || isCurrent) && 'text-zinc-950',\n isPending && 'text-zinc-400',\n )}\n >\n {step.label}\n </span>\n {idx < STEPS.length - 1 && (\n <IconChevronRight size={16} className=\"text-zinc-300 ml-1\" />\n )}\n </div>\n );\n })}\n </div>\n );\n}\n\nexport default function AddCardModal() {\n const { activeModal, modalData, closeModal } = useModalManager();\n const isOpen = activeModal === 'addCardModal';\n const { isExternalContracting, redirectToExternal } = useExternalContracting();\n const [currentStep, setCurrentStep] = useState(1);\n const [stripeStatus, setStripeStatus] = useState<StripeElementsStatus>(INITIAL_STRIPE_STATUS);\n const [stripeErrors, setStripeErrors] = useState<Record<string, string | undefined>>({});\n const [isAuthenticating, setIsAuthenticating] = useState(false);\n\n const onCardAdded = modalData.onCardAdded as ((cardId: string) => void) | undefined;\n\n const createSetupIntentMutation = useCreateSetupIntent();\n const createCardMutation = useCreateCard();\n const updateAccountMutation = useUpdateAccount();\n const elements = useElements();\n const stripe = useStripe();\n\n const form = useForm<CardFormData>({\n resolver: zodResolver(cardFormSchema),\n mode: 'onSubmit',\n reValidateMode: 'onChange',\n defaultValues: {\n country: 'BR',\n personType: 'pf',\n },\n });\n\n const handleClose = useCallback(() => {\n form.reset();\n setCurrentStep(1);\n setStripeStatus(INITIAL_STRIPE_STATUS);\n setStripeErrors({});\n setIsAuthenticating(false);\n closeModal();\n }, [form, closeModal]);\n\n const handleStripeChange = useCallback(\n (field: keyof StripeElementsStatus, complete: boolean) => {\n setStripeStatus((prev) => ({ ...prev, [field]: complete }));\n if (complete) {\n setStripeErrors((prev) => ({ ...prev, [field]: undefined }));\n }\n },\n []\n );\n\n const handleContinue = useCallback(async () => {\n const isValid = await form.trigger([\n 'fullName',\n 'cep',\n 'street',\n 'streetNumber',\n 'neighborhood',\n 'city',\n 'state',\n ]);\n if (!isValid) return;\n setCurrentStep(2);\n }, [form]);\n\n const handleBack = useCallback(() => {\n setCurrentStep(1);\n }, []);\n\n async function handleSubmit(data: CardFormData) {\n const newStripeErrors: Record<string, string | undefined> = {};\n if (!stripeStatus.cardNumber) newStripeErrors.cardNumber = 'Número do cartão é obrigatório';\n if (!stripeStatus.cardExpiry) newStripeErrors.cardExpiry = 'Data de validade é obrigatória';\n if (!stripeStatus.cardCvc) newStripeErrors.cardCvc = 'Código de segurança é obrigatório';\n setStripeErrors(newStripeErrors);\n\n const stripeValid =\n stripeStatus.cardNumber && stripeStatus.cardExpiry && stripeStatus.cardCvc;\n if (!stripeValid) return;\n if (!stripe || !elements) return;\n\n const cardElement = elements.getElement(CardNumberElement);\n if (!cardElement) return;\n\n setIsAuthenticating(true);\n try {\n const intentResult = await createSetupIntentMutation.mutateAsync();\n const intent = intentResult?.data;\n if (!intent?.client_secret || !intent?.setup_intent_id) {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message=\"Não foi possível iniciar a autenticação do cartão, tente novamente.\"\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n return;\n }\n\n const { setupIntent, error } = await stripe.confirmCardSetup(intent.client_secret, {\n payment_method: {\n card: cardElement,\n billing_details: { name: data.name },\n },\n });\n\n if (error) {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message={error.message || 'Falha na autenticação do cartão.'}\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n return;\n }\n\n if (setupIntent?.status !== 'succeeded') {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message={`Autenticação não concluída (status: ${setupIntent?.status ?? 'desconhecido'}).`}\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n return;\n }\n\n const [cardResult] = await Promise.all([\n createCardMutation.mutateAsync({\n setup_intent_id: intent.setup_intent_id,\n name: data.name,\n set_default: true,\n }),\n updateAccountMutation.mutateAsync({\n financial_document_type: data.personType === 'pf' ? 1 : 2,\n financial_document: data.personType === 'pf' ? data.cpf : data.cnpj,\n financial_name: data.fullName,\n zipcode: data.cep,\n address: data.street,\n address_number: data.streetNumber,\n address_complement: data.complement || '',\n neighborhood: data.neighborhood,\n city: data.city,\n state: data.state,\n }),\n ]);\n\n if (cardResult.success && cardResult.data?.id != null) {\n onCardAdded?.(cardResult.data.id.toString());\n }\n\n toast.custom(\n (t) => <Toast variant=\"success\" message=\"Cartão adicionado com sucesso.\" toastId={t} />,\n { duration: 5000 }\n );\n handleClose();\n } catch {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message=\"Não foi possível salvar o seu novo cartão, tente novamente.\"\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n } finally {\n setIsAuthenticating(false);\n }\n }\n\n const isSubmitting =\n createCardMutation.isPending ||\n updateAccountMutation.isPending ||\n form.formState.isSubmitting;\n\n const watchedValues = form.watch(['name', 'country', 'personType', 'cpf', 'cnpj', 'fullName', 'cep', 'street', 'streetNumber', 'neighborhood', 'city', 'state']);\n const [name, country, personType, cpf, cnpj, fullName, cep, street, streetNumber, neighborhood, city, state] = watchedValues;\n const isBillingValid =\n !!fullName &&\n !!cep && cep.length >= 9 &&\n !!street &&\n !!streetNumber &&\n !!neighborhood &&\n !!city &&\n !!state;\n const isCardValid =\n !!name &&\n !!country &&\n !!personType &&\n (personType === 'pf' ? !!cpf && cpf.length >= 14 : !!cnpj && cnpj.length >= 18) &&\n stripeStatus.cardNumber &&\n stripeStatus.cardExpiry &&\n stripeStatus.cardCvc;\n\n if (isExternalContracting) {\n return (\n <Dialog open={isOpen} onOpenChange={handleClose}>\n <DialogContent className=\"flex flex-col p-0 gap-0 w-full md:w-[410px] lg:w-[410px]\">\n <DialogHeader className=\"gap-3 text-left p-5\">\n <div className=\"flex items-center justify-center w-10 h-10 bg-zinc-50 rounded-lg\">\n <IconCreditCard size={24} className=\"text-zinc-950\" />\n </div>\n <div className=\"flex flex-col gap-2\">\n <DialogTitle className=\"paragraph-medium-semibold text-zinc-950\">\n Gerenciamento de cartões indisponível\n </DialogTitle>\n <span className=\"paragraph-small-regular text-zinc-600\">\n Acesse sua área de assinatura para gerenciar formas de pagamento.\n </span>\n </div>\n </DialogHeader>\n <div className=\"flex items-center gap-2 p-5 border-t border-zinc-200\">\n <Button\n className=\"w-fit h-10!\"\n onClick={() => {\n handleClose();\n redirectToExternal('contracting');\n }}\n >\n Acessar área de assinatura\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n );\n }\n\n return (\n <Dialog open={isOpen} onOpenChange={handleClose}>\n <DialogContent\n showCloseButton={false}\n className=\"flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-dvh top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:max-h-[90vh] lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%] overflow-y-auto\"\n >\n <DialogHeader className=\"p-0\">\n <div className=\"flex items-center justify-center px-4 py-3 relative border-b border-zinc-200 lg:border-b-0\">\n <DialogTitle className=\"paragraph-medium-semibold text-zinc-950 text-center\">\n Adicionar cartão\n </DialogTitle>\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"size-8! p-0 absolute right-4\"\n onClick={handleClose}\n >\n <IconX size={18} />\n </Button>\n </div>\n </DialogHeader>\n\n <CardStepper currentStep={currentStep} />\n\n <form\n onSubmit={form.handleSubmit(handleSubmit)}\n className=\"flex flex-col flex-1 lg:flex-none\"\n >\n <div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 1 ? 'hidden' : ''}`}>\n <h2 className=\"font-[family-name:var(--font-outfit)] text-[18px] leading-tight font-semibold text-zinc-950\">\n Adicione suas informações de cobrança\n </h2>\n <BillingFormFields form={form} />\n </div>\n <div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 2 ? 'hidden' : ''}`}>\n <h2 className=\"font-[family-name:var(--font-outfit)] text-[18px] leading-tight font-semibold text-zinc-950\">\n Adicione informações do seu cartão\n </h2>\n <CardFormFields\n form={form}\n onStripeChange={handleStripeChange}\n stripeErrors={stripeErrors}\n />\n </div>\n\n <div className=\"flex justify-between items-center p-4 lg:p-5 border-t border-zinc-200 gap-2 mt-auto lg:mt-0\">\n {currentStep === 1 ? (\n <>\n <Button variant=\"secondary\" type=\"button\" onClick={handleClose} className=\"h-10!\">\n Cancelar\n </Button>\n <Button\n type=\"button\"\n className=\"h-10!\"\n onClick={handleContinue}\n disabled={!isBillingValid}\n >\n Continuar\n </Button>\n </>\n ) : (\n <>\n <Button variant=\"secondary\" type=\"button\" onClick={handleBack} className=\"h-10!\">\n Voltar\n </Button>\n <Button\n type=\"submit\"\n className=\"h-10!\"\n disabled={isSubmitting || isAuthenticating || !isCardValid}\n loading={isAuthenticating || isSubmitting}\n >\n {isAuthenticating && <IconLoader2 className=\"size-4 animate-spin\" />}\n {isAuthenticating\n ? 'Autenticando...'\n : isSubmitting\n ? 'Adicionando cartão...'\n : 'Adicionar cartão'}\n </Button>\n </>\n )}\n </div>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -11,7 +11,8 @@ import { useUpdateAccount } from '../../../modules/accounts/hooks/useAccountMana
|
|
|
11
11
|
import { useExternalContracting } from '../../../providers/whitelabel.provider';
|
|
12
12
|
import { CardFormFields, BillingFormFields, cardFormSchema } from './CardFormFields';
|
|
13
13
|
import type { CardFormData, StripeElementsStatus } from './CardFormFields';
|
|
14
|
-
import { IconLoader2, IconX, IconCreditCard } from '@tabler/icons-react';
|
|
14
|
+
import { IconLoader2, IconX, IconCreditCard, IconCheck, IconChevronRight } from '@tabler/icons-react';
|
|
15
|
+
import { cn } from '../../../infra/utils/clsx';
|
|
15
16
|
import { useForm } from 'react-hook-form';
|
|
16
17
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
17
18
|
import { CardNumberElement, useElements, useStripe } from '@stripe/react-stripe-js';
|
|
@@ -23,6 +24,49 @@ const INITIAL_STRIPE_STATUS: StripeElementsStatus = {
|
|
|
23
24
|
cardCvc: false,
|
|
24
25
|
};
|
|
25
26
|
|
|
27
|
+
const STEPS = [
|
|
28
|
+
{ number: 1, label: 'Cobrança' },
|
|
29
|
+
{ number: 2, label: 'Cartão' },
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
function CardStepper({ currentStep }: { readonly currentStep: number }) {
|
|
33
|
+
return (
|
|
34
|
+
<div className="flex items-center justify-center gap-2 px-4 py-3 border-b border-zinc-200 lg:border-b-0">
|
|
35
|
+
{STEPS.map((step, idx) => {
|
|
36
|
+
const isCompleted = currentStep > step.number;
|
|
37
|
+
const isCurrent = currentStep === step.number;
|
|
38
|
+
const isPending = !isCompleted && !isCurrent;
|
|
39
|
+
return (
|
|
40
|
+
<div key={step.number} className="flex items-center gap-2">
|
|
41
|
+
<div
|
|
42
|
+
className={cn(
|
|
43
|
+
'flex items-center justify-center size-6 rounded-full paragraph-xsmall-semibold',
|
|
44
|
+
isCompleted && 'bg-green-500 text-white',
|
|
45
|
+
isCurrent && 'bg-primary text-white',
|
|
46
|
+
isPending && 'border border-zinc-200 text-zinc-400',
|
|
47
|
+
)}
|
|
48
|
+
>
|
|
49
|
+
{isCompleted ? <IconCheck size={14} stroke={3} /> : step.number}
|
|
50
|
+
</div>
|
|
51
|
+
<span
|
|
52
|
+
className={cn(
|
|
53
|
+
'paragraph-xsmall-semibold',
|
|
54
|
+
(isCompleted || isCurrent) && 'text-zinc-950',
|
|
55
|
+
isPending && 'text-zinc-400',
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{step.label}
|
|
59
|
+
</span>
|
|
60
|
+
{idx < STEPS.length - 1 && (
|
|
61
|
+
<IconChevronRight size={16} className="text-zinc-300 ml-1" />
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
})}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
26
70
|
export default function AddCardModal() {
|
|
27
71
|
const { activeModal, modalData, closeModal } = useModalManager();
|
|
28
72
|
const isOpen = activeModal === 'addCardModal';
|
|
@@ -30,7 +74,6 @@ export default function AddCardModal() {
|
|
|
30
74
|
const [currentStep, setCurrentStep] = useState(1);
|
|
31
75
|
const [stripeStatus, setStripeStatus] = useState<StripeElementsStatus>(INITIAL_STRIPE_STATUS);
|
|
32
76
|
const [stripeErrors, setStripeErrors] = useState<Record<string, string | undefined>>({});
|
|
33
|
-
const [setupIntentId, setSetupIntentId] = useState<string | null>(null);
|
|
34
77
|
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
|
35
78
|
|
|
36
79
|
const onCardAdded = modalData.onCardAdded as ((cardId: string) => void) | undefined;
|
|
@@ -56,7 +99,6 @@ export default function AddCardModal() {
|
|
|
56
99
|
setCurrentStep(1);
|
|
57
100
|
setStripeStatus(INITIAL_STRIPE_STATUS);
|
|
58
101
|
setStripeErrors({});
|
|
59
|
-
setSetupIntentId(null);
|
|
60
102
|
setIsAuthenticating(false);
|
|
61
103
|
closeModal();
|
|
62
104
|
}, [form, closeModal]);
|
|
@@ -72,17 +114,33 @@ export default function AddCardModal() {
|
|
|
72
114
|
);
|
|
73
115
|
|
|
74
116
|
const handleContinue = useCallback(async () => {
|
|
75
|
-
const
|
|
117
|
+
const isValid = await form.trigger([
|
|
118
|
+
'fullName',
|
|
119
|
+
'cep',
|
|
120
|
+
'street',
|
|
121
|
+
'streetNumber',
|
|
122
|
+
'neighborhood',
|
|
123
|
+
'city',
|
|
124
|
+
'state',
|
|
125
|
+
]);
|
|
126
|
+
if (!isValid) return;
|
|
127
|
+
setCurrentStep(2);
|
|
128
|
+
}, [form]);
|
|
129
|
+
|
|
130
|
+
const handleBack = useCallback(() => {
|
|
131
|
+
setCurrentStep(1);
|
|
132
|
+
}, []);
|
|
76
133
|
|
|
134
|
+
async function handleSubmit(data: CardFormData) {
|
|
77
135
|
const newStripeErrors: Record<string, string | undefined> = {};
|
|
78
136
|
if (!stripeStatus.cardNumber) newStripeErrors.cardNumber = 'Número do cartão é obrigatório';
|
|
79
137
|
if (!stripeStatus.cardExpiry) newStripeErrors.cardExpiry = 'Data de validade é obrigatória';
|
|
80
138
|
if (!stripeStatus.cardCvc) newStripeErrors.cardCvc = 'Código de segurança é obrigatório';
|
|
81
139
|
setStripeErrors(newStripeErrors);
|
|
82
140
|
|
|
83
|
-
const stripeValid =
|
|
84
|
-
|
|
85
|
-
if (!
|
|
141
|
+
const stripeValid =
|
|
142
|
+
stripeStatus.cardNumber && stripeStatus.cardExpiry && stripeStatus.cardCvc;
|
|
143
|
+
if (!stripeValid) return;
|
|
86
144
|
if (!stripe || !elements) return;
|
|
87
145
|
|
|
88
146
|
const cardElement = elements.getElement(CardNumberElement);
|
|
@@ -109,7 +167,7 @@ export default function AddCardModal() {
|
|
|
109
167
|
const { setupIntent, error } = await stripe.confirmCardSetup(intent.client_secret, {
|
|
110
168
|
payment_method: {
|
|
111
169
|
card: cardElement,
|
|
112
|
-
billing_details: { name:
|
|
170
|
+
billing_details: { name: data.name },
|
|
113
171
|
},
|
|
114
172
|
});
|
|
115
173
|
|
|
@@ -141,36 +199,9 @@ export default function AddCardModal() {
|
|
|
141
199
|
return;
|
|
142
200
|
}
|
|
143
201
|
|
|
144
|
-
setSetupIntentId(intent.setup_intent_id);
|
|
145
|
-
form.clearErrors(['fullName', 'cep', 'street', 'streetNumber', 'neighborhood', 'city', 'state']);
|
|
146
|
-
setCurrentStep(2);
|
|
147
|
-
} catch {
|
|
148
|
-
toast.custom(
|
|
149
|
-
(t) => (
|
|
150
|
-
<Toast
|
|
151
|
-
variant="error"
|
|
152
|
-
message="Não foi possível iniciar a autenticação do cartão, tente novamente."
|
|
153
|
-
toastId={t}
|
|
154
|
-
/>
|
|
155
|
-
),
|
|
156
|
-
{ duration: 5000 }
|
|
157
|
-
);
|
|
158
|
-
} finally {
|
|
159
|
-
setIsAuthenticating(false);
|
|
160
|
-
}
|
|
161
|
-
}, [form, stripeStatus, stripe, elements, createSetupIntentMutation]);
|
|
162
|
-
|
|
163
|
-
const handleBack = useCallback(() => {
|
|
164
|
-
setCurrentStep(1);
|
|
165
|
-
}, []);
|
|
166
|
-
|
|
167
|
-
async function handleSubmit(data: CardFormData) {
|
|
168
|
-
if (!setupIntentId) return;
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
202
|
const [cardResult] = await Promise.all([
|
|
172
203
|
createCardMutation.mutateAsync({
|
|
173
|
-
setup_intent_id:
|
|
204
|
+
setup_intent_id: intent.setup_intent_id,
|
|
174
205
|
name: data.name,
|
|
175
206
|
set_default: true,
|
|
176
207
|
}),
|
|
@@ -208,6 +239,8 @@ export default function AddCardModal() {
|
|
|
208
239
|
),
|
|
209
240
|
{ duration: 5000 }
|
|
210
241
|
);
|
|
242
|
+
} finally {
|
|
243
|
+
setIsAuthenticating(false);
|
|
211
244
|
}
|
|
212
245
|
}
|
|
213
246
|
|
|
@@ -218,15 +251,7 @@ export default function AddCardModal() {
|
|
|
218
251
|
|
|
219
252
|
const watchedValues = form.watch(['name', 'country', 'personType', 'cpf', 'cnpj', 'fullName', 'cep', 'street', 'streetNumber', 'neighborhood', 'city', 'state']);
|
|
220
253
|
const [name, country, personType, cpf, cnpj, fullName, cep, street, streetNumber, neighborhood, city, state] = watchedValues;
|
|
221
|
-
const
|
|
222
|
-
!!name &&
|
|
223
|
-
!!country &&
|
|
224
|
-
!!personType &&
|
|
225
|
-
(personType === 'pf' ? !!cpf && cpf.length >= 14 : !!cnpj && cnpj.length >= 18) &&
|
|
226
|
-
stripeStatus.cardNumber &&
|
|
227
|
-
stripeStatus.cardExpiry &&
|
|
228
|
-
stripeStatus.cardCvc;
|
|
229
|
-
const isStep2Valid =
|
|
254
|
+
const isBillingValid =
|
|
230
255
|
!!fullName &&
|
|
231
256
|
!!cep && cep.length >= 9 &&
|
|
232
257
|
!!street &&
|
|
@@ -234,6 +259,14 @@ export default function AddCardModal() {
|
|
|
234
259
|
!!neighborhood &&
|
|
235
260
|
!!city &&
|
|
236
261
|
!!state;
|
|
262
|
+
const isCardValid =
|
|
263
|
+
!!name &&
|
|
264
|
+
!!country &&
|
|
265
|
+
!!personType &&
|
|
266
|
+
(personType === 'pf' ? !!cpf && cpf.length >= 14 : !!cnpj && cnpj.length >= 18) &&
|
|
267
|
+
stripeStatus.cardNumber &&
|
|
268
|
+
stripeStatus.cardExpiry &&
|
|
269
|
+
stripeStatus.cardCvc;
|
|
237
270
|
|
|
238
271
|
if (isExternalContracting) {
|
|
239
272
|
return (
|
|
@@ -277,7 +310,7 @@ export default function AddCardModal() {
|
|
|
277
310
|
<DialogHeader className="p-0">
|
|
278
311
|
<div className="flex items-center justify-center px-4 py-3 relative border-b border-zinc-200 lg:border-b-0">
|
|
279
312
|
<DialogTitle className="paragraph-medium-semibold text-zinc-950 text-center">
|
|
280
|
-
|
|
313
|
+
Adicionar cartão
|
|
281
314
|
</DialogTitle>
|
|
282
315
|
<Button
|
|
283
316
|
type="button"
|
|
@@ -290,28 +323,28 @@ export default function AddCardModal() {
|
|
|
290
323
|
</div>
|
|
291
324
|
</DialogHeader>
|
|
292
325
|
|
|
293
|
-
{
|
|
294
|
-
<div className="h-1 bg-zinc-100 w-full">
|
|
295
|
-
<div
|
|
296
|
-
className="h-full transition-all duration-300 ease-in-out bg-primary"
|
|
297
|
-
style={{ width: currentStep === 1 ? '50%' : '100%' }}
|
|
298
|
-
/>
|
|
299
|
-
</div>
|
|
326
|
+
<CardStepper currentStep={currentStep} />
|
|
300
327
|
|
|
301
328
|
<form
|
|
302
329
|
onSubmit={form.handleSubmit(handleSubmit)}
|
|
303
330
|
className="flex flex-col flex-1 lg:flex-none"
|
|
304
331
|
>
|
|
305
332
|
<div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 1 ? 'hidden' : ''}`}>
|
|
333
|
+
<h2 className="font-[family-name:var(--font-outfit)] text-[18px] leading-tight font-semibold text-zinc-950">
|
|
334
|
+
Adicione suas informações de cobrança
|
|
335
|
+
</h2>
|
|
336
|
+
<BillingFormFields form={form} />
|
|
337
|
+
</div>
|
|
338
|
+
<div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 2 ? 'hidden' : ''}`}>
|
|
339
|
+
<h2 className="font-[family-name:var(--font-outfit)] text-[18px] leading-tight font-semibold text-zinc-950">
|
|
340
|
+
Adicione informações do seu cartão
|
|
341
|
+
</h2>
|
|
306
342
|
<CardFormFields
|
|
307
343
|
form={form}
|
|
308
344
|
onStripeChange={handleStripeChange}
|
|
309
345
|
stripeErrors={stripeErrors}
|
|
310
346
|
/>
|
|
311
347
|
</div>
|
|
312
|
-
<div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 2 ? 'hidden' : ''}`}>
|
|
313
|
-
<BillingFormFields form={form} />
|
|
314
|
-
</div>
|
|
315
348
|
|
|
316
349
|
<div className="flex justify-between items-center p-4 lg:p-5 border-t border-zinc-200 gap-2 mt-auto lg:mt-0">
|
|
317
350
|
{currentStep === 1 ? (
|
|
@@ -323,10 +356,9 @@ export default function AddCardModal() {
|
|
|
323
356
|
type="button"
|
|
324
357
|
className="h-10!"
|
|
325
358
|
onClick={handleContinue}
|
|
326
|
-
disabled={!
|
|
327
|
-
loading={isAuthenticating}
|
|
359
|
+
disabled={!isBillingValid}
|
|
328
360
|
>
|
|
329
|
-
|
|
361
|
+
Continuar
|
|
330
362
|
</Button>
|
|
331
363
|
</>
|
|
332
364
|
) : (
|
|
@@ -334,9 +366,18 @@ export default function AddCardModal() {
|
|
|
334
366
|
<Button variant="secondary" type="button" onClick={handleBack} className="h-10!">
|
|
335
367
|
Voltar
|
|
336
368
|
</Button>
|
|
337
|
-
<Button
|
|
338
|
-
|
|
339
|
-
|
|
369
|
+
<Button
|
|
370
|
+
type="submit"
|
|
371
|
+
className="h-10!"
|
|
372
|
+
disabled={isSubmitting || isAuthenticating || !isCardValid}
|
|
373
|
+
loading={isAuthenticating || isSubmitting}
|
|
374
|
+
>
|
|
375
|
+
{isAuthenticating && <IconLoader2 className="size-4 animate-spin" />}
|
|
376
|
+
{isAuthenticating
|
|
377
|
+
? 'Autenticando...'
|
|
378
|
+
: isSubmitting
|
|
379
|
+
? 'Adicionando cartão...'
|
|
380
|
+
: 'Adicionar cartão'}
|
|
340
381
|
</Button>
|
|
341
382
|
</>
|
|
342
383
|
)}
|