@greatapps/common 1.1.292 → 1.1.293
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/components/modals/cards/AddCardModal.mjs +49 -6
- package/dist/components/modals/cards/AddCardModal.mjs.map +1 -1
- package/dist/components/modals/cards/CardFormFields.mjs +8 -11
- package/dist/components/modals/cards/CardFormFields.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/modals/cards/AddCardModal.tsx +56 -7
- package/src/components/modals/cards/CardFormFields.tsx +19 -20
|
@@ -7,16 +7,23 @@ import { Toast } from "../../ui/feedback/Toast";
|
|
|
7
7
|
import { useModalManager } from "../../../store/useModalManager";
|
|
8
8
|
import { useCreateCard } from "../../../modules/cards/hooks/create-card.hook";
|
|
9
9
|
import { useUpdateAccount } from "../../../modules/accounts/hooks/useAccountManagement";
|
|
10
|
-
import { CardFormFields, BillingFormFields, cardFormSchema
|
|
10
|
+
import { CardFormFields, BillingFormFields, cardFormSchema } from "./CardFormFields";
|
|
11
11
|
import { IconX } from "@tabler/icons-react";
|
|
12
12
|
import { useForm } from "react-hook-form";
|
|
13
13
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
14
14
|
import { CardNumberElement, useElements, useStripe } from "@stripe/react-stripe-js";
|
|
15
15
|
import { toast } from "sonner";
|
|
16
|
+
const INITIAL_STRIPE_STATUS = {
|
|
17
|
+
cardNumber: false,
|
|
18
|
+
cardExpiry: false,
|
|
19
|
+
cardCvc: false
|
|
20
|
+
};
|
|
16
21
|
function AddCardModal() {
|
|
17
22
|
const { activeModal, modalData, closeModal } = useModalManager();
|
|
18
23
|
const isOpen = activeModal === "addCardModal";
|
|
19
24
|
const [currentStep, setCurrentStep] = useState(1);
|
|
25
|
+
const [stripeStatus, setStripeStatus] = useState(INITIAL_STRIPE_STATUS);
|
|
26
|
+
const [stripeErrors, setStripeErrors] = useState({});
|
|
20
27
|
const onCardAdded = modalData.onCardAdded;
|
|
21
28
|
const createCardMutation = useCreateCard();
|
|
22
29
|
const updateAccountMutation = useUpdateAccount();
|
|
@@ -34,14 +41,31 @@ function AddCardModal() {
|
|
|
34
41
|
const handleClose = useCallback(() => {
|
|
35
42
|
form.reset();
|
|
36
43
|
setCurrentStep(1);
|
|
44
|
+
setStripeStatus(INITIAL_STRIPE_STATUS);
|
|
45
|
+
setStripeErrors({});
|
|
37
46
|
closeModal();
|
|
38
47
|
}, [form, closeModal]);
|
|
48
|
+
const handleStripeChange = useCallback(
|
|
49
|
+
(field, complete) => {
|
|
50
|
+
setStripeStatus((prev) => ({ ...prev, [field]: complete }));
|
|
51
|
+
if (complete) {
|
|
52
|
+
setStripeErrors((prev) => ({ ...prev, [field]: void 0 }));
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
[]
|
|
56
|
+
);
|
|
39
57
|
const handleContinue = useCallback(async () => {
|
|
40
|
-
const
|
|
41
|
-
|
|
58
|
+
const rhfValid = await form.trigger(["name", "country", "personType", "cpf", "cnpj"]);
|
|
59
|
+
const newStripeErrors = {};
|
|
60
|
+
if (!stripeStatus.cardNumber) newStripeErrors.cardNumber = "N\xFAmero do cart\xE3o \xE9 obrigat\xF3rio";
|
|
61
|
+
if (!stripeStatus.cardExpiry) newStripeErrors.cardExpiry = "Data de validade \xE9 obrigat\xF3ria";
|
|
62
|
+
if (!stripeStatus.cardCvc) newStripeErrors.cardCvc = "C\xF3digo de seguran\xE7a \xE9 obrigat\xF3rio";
|
|
63
|
+
setStripeErrors(newStripeErrors);
|
|
64
|
+
const stripeValid = stripeStatus.cardNumber && stripeStatus.cardExpiry && stripeStatus.cardCvc;
|
|
65
|
+
if (rhfValid && stripeValid) {
|
|
42
66
|
setCurrentStep(2);
|
|
43
67
|
}
|
|
44
|
-
}, [form]);
|
|
68
|
+
}, [form, stripeStatus]);
|
|
45
69
|
const handleBack = useCallback(() => {
|
|
46
70
|
setCurrentStep(1);
|
|
47
71
|
}, []);
|
|
@@ -116,6 +140,9 @@ function AddCardModal() {
|
|
|
116
140
|
}
|
|
117
141
|
}
|
|
118
142
|
const isSubmitting = createCardMutation.isPending || updateAccountMutation.isPending || form.formState.isSubmitting;
|
|
143
|
+
const watchedValues = form.watch(["name", "country", "personType", "cpf", "cnpj"]);
|
|
144
|
+
const [name, country, personType, cpf, cnpj] = watchedValues;
|
|
145
|
+
const isStep1Valid = !!name && !!country && !!personType && (personType === "pf" ? !!cpf && cpf.length >= 14 : !!cnpj && cnpj.length >= 18) && stripeStatus.cardNumber && stripeStatus.cardExpiry && stripeStatus.cardCvc;
|
|
119
146
|
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(
|
|
120
147
|
DialogContent,
|
|
121
148
|
{
|
|
@@ -148,10 +175,26 @@ function AddCardModal() {
|
|
|
148
175
|
onSubmit: form.handleSubmit(handleSubmit),
|
|
149
176
|
className: "flex flex-col flex-1 lg:flex-none",
|
|
150
177
|
children: [
|
|
151
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none", children: currentStep === 1 ? /* @__PURE__ */ jsx(
|
|
178
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none", children: currentStep === 1 ? /* @__PURE__ */ jsx(
|
|
179
|
+
CardFormFields,
|
|
180
|
+
{
|
|
181
|
+
form,
|
|
182
|
+
onStripeChange: handleStripeChange,
|
|
183
|
+
stripeErrors
|
|
184
|
+
}
|
|
185
|
+
) : /* @__PURE__ */ jsx(BillingFormFields, { form }) }),
|
|
152
186
|
/* @__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: [
|
|
153
187
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", type: "button", onClick: handleClose, className: "h-10!", children: "Cancelar" }),
|
|
154
|
-
/* @__PURE__ */ jsx(
|
|
188
|
+
/* @__PURE__ */ jsx(
|
|
189
|
+
Button,
|
|
190
|
+
{
|
|
191
|
+
type: "button",
|
|
192
|
+
className: "h-10!",
|
|
193
|
+
onClick: handleContinue,
|
|
194
|
+
disabled: !isStep1Valid,
|
|
195
|
+
children: "Continuar"
|
|
196
|
+
}
|
|
197
|
+
)
|
|
155
198
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
156
199
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", type: "button", onClick: handleBack, className: "h-10!", children: "Voltar" }),
|
|
157
200
|
/* @__PURE__ */ jsx(Button, { type: "submit", className: "h-10!", disabled: isSubmitting, children: isSubmitting ? "Adicionando cart\xE3o..." : "Adicionar cart\xE3o" })
|
|
@@ -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 { useUpdateAccount } from '../../../modules/accounts/hooks/useAccountManagement';\nimport { CardFormFields, BillingFormFields, cardFormSchema, STEP1_FIELDS } from './CardFormFields';\nimport type { CardFormData } from './CardFormFields';\nimport { IconX } 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\nexport default function AddCardModal() {\n const { activeModal, modalData, closeModal } = useModalManager();\n const isOpen = activeModal === 'addCardModal';\n const [currentStep, setCurrentStep] = useState(1);\n\n const onCardAdded = modalData.onCardAdded as ((cardId: string) => void) | undefined;\n\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 closeModal();\n }, [form, closeModal]);\n\n const handleContinue = useCallback(async () => {\n const valid = await form.trigger(STEP1_FIELDS as unknown as (keyof CardFormData)[]);\n if (valid) {\n setCurrentStep(2);\n }\n }, [form]);\n\n const handleBack = useCallback(() => {\n setCurrentStep(1);\n }, []);\n\n async function handleSubmit(data: CardFormData) {\n if (!stripe || !elements) return;\n\n const cardElement = elements.getElement(CardNumberElement);\n if (!cardElement) return;\n\n const { error, paymentMethod } = await stripe.createPaymentMethod({\n type: 'card',\n card: cardElement,\n billing_details: {\n name: data.fullName,\n address: {\n country: data.country,\n postal_code: data.cep,\n line1: `${data.street}, ${data.streetNumber}`,\n line2: data.complement || undefined,\n city: data.city,\n state: data.state,\n },\n },\n });\n\n if (error) {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message=\"Não foi possível adicionar o cartão na Stripe, tente novamente.\"\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n return;\n }\n\n try {\n const [cardResult] = await Promise.all([\n createCardMutation.mutateAsync({ payment_method_id: paymentMethod.id }),\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 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 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 bg-cyan-400 transition-all duration-300 ease-in-out\"\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\">\n {currentStep === 1 ? (\n <CardFormFields form={form} />\n ) : (\n <BillingFormFields form={form} />\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 type=\"button\" className=\"h-10!\" onClick={handleContinue}>\n 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}>\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 { useUpdateAccount } from '../../../modules/accounts/hooks/useAccountManagement';\nimport { CardFormFields, BillingFormFields, cardFormSchema } from './CardFormFields';\nimport type { CardFormData, StripeElementsStatus } from './CardFormFields';\nimport { IconX } 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 [currentStep, setCurrentStep] = useState(1);\n const [stripeStatus, setStripeStatus] = useState<StripeElementsStatus>(INITIAL_STRIPE_STATUS);\n const [stripeErrors, setStripeErrors] = useState<Record<string, string | undefined>>({});\n\n const onCardAdded = modalData.onCardAdded as ((cardId: string) => void) | undefined;\n\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 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) {\n setCurrentStep(2);\n }\n }, [form, stripeStatus]);\n\n const handleBack = useCallback(() => {\n setCurrentStep(1);\n }, []);\n\n async function handleSubmit(data: CardFormData) {\n if (!stripe || !elements) return;\n\n const cardElement = elements.getElement(CardNumberElement);\n if (!cardElement) return;\n\n const { error, paymentMethod } = await stripe.createPaymentMethod({\n type: 'card',\n card: cardElement,\n billing_details: {\n name: data.fullName,\n address: {\n country: data.country,\n postal_code: data.cep,\n line1: `${data.street}, ${data.streetNumber}`,\n line2: data.complement || undefined,\n city: data.city,\n state: data.state,\n },\n },\n });\n\n if (error) {\n toast.custom(\n (t) => (\n <Toast\n variant=\"error\"\n message=\"Não foi possível adicionar o cartão na Stripe, tente novamente.\"\n toastId={t}\n />\n ),\n { duration: 5000 }\n );\n return;\n }\n\n try {\n const [cardResult] = await Promise.all([\n createCardMutation.mutateAsync({ payment_method_id: paymentMethod.id }),\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 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']);\n const [name, country, personType, cpf, cnpj] = 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\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 bg-cyan-400 transition-all duration-300 ease-in-out\"\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\">\n {currentStep === 1 ? (\n <CardFormFields\n form={form}\n onStripeChange={handleStripeChange}\n stripeErrors={stripeErrors}\n />\n ) : (\n <BillingFormFields form={form} />\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={!isStep1Valid}\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 type=\"submit\" className=\"h-10!\" disabled={isSubmitting}>\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":[]}
|
|
@@ -19,9 +19,6 @@ const stripeElementOptions = {
|
|
|
19
19
|
};
|
|
20
20
|
const cardFormSchema = z.object({
|
|
21
21
|
name: z.string().min(1, "Nome \xE9 obrigat\xF3rio"),
|
|
22
|
-
number: z.string().min(1, "N\xFAmero do cart\xE3o \xE9 obrigat\xF3rio"),
|
|
23
|
-
expiry: z.string().min(1, "Data de validade \xE9 obrigat\xF3ria"),
|
|
24
|
-
cvv: z.string().min(1, "C\xF3digo de seguran\xE7a \xE9 obrigat\xF3rio"),
|
|
25
22
|
country: z.string().min(1, "Pa\xEDs \xE9 obrigat\xF3rio"),
|
|
26
23
|
personType: z.enum(["pf", "pj"]),
|
|
27
24
|
cpf: z.string().optional(),
|
|
@@ -43,8 +40,8 @@ const cardFormSchema = z.object({
|
|
|
43
40
|
path: ["cpf"]
|
|
44
41
|
}
|
|
45
42
|
);
|
|
46
|
-
const STEP1_FIELDS = ["name", "
|
|
47
|
-
function CardFormFields({ form }) {
|
|
43
|
+
const STEP1_FIELDS = ["name", "country", "personType", "cpf", "cnpj"];
|
|
44
|
+
function CardFormFields({ form, onStripeChange, stripeErrors }) {
|
|
48
45
|
const {
|
|
49
46
|
register,
|
|
50
47
|
watch,
|
|
@@ -69,10 +66,10 @@ function CardFormFields({ form }) {
|
|
|
69
66
|
CardNumberElement,
|
|
70
67
|
{
|
|
71
68
|
options: { ...stripeElementOptions, showIcon: true },
|
|
72
|
-
onChange: (e) =>
|
|
69
|
+
onChange: (e) => onStripeChange("cardNumber", e.complete)
|
|
73
70
|
}
|
|
74
71
|
) }) }),
|
|
75
|
-
|
|
72
|
+
stripeErrors.cardNumber && /* @__PURE__ */ jsx("p", { className: "paragraph-xsmall-semibold text-red-600", children: stripeErrors.cardNumber })
|
|
76
73
|
] }),
|
|
77
74
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
78
75
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-1.5", children: [
|
|
@@ -81,10 +78,10 @@ function CardFormFields({ form }) {
|
|
|
81
78
|
CardExpiryElement,
|
|
82
79
|
{
|
|
83
80
|
options: stripeElementOptions,
|
|
84
|
-
onChange: (e) =>
|
|
81
|
+
onChange: (e) => onStripeChange("cardExpiry", e.complete)
|
|
85
82
|
}
|
|
86
83
|
) }) }),
|
|
87
|
-
|
|
84
|
+
stripeErrors.cardExpiry && /* @__PURE__ */ jsx("p", { className: "paragraph-xsmall-semibold text-red-600", children: stripeErrors.cardExpiry })
|
|
88
85
|
] }),
|
|
89
86
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-1.5", children: [
|
|
90
87
|
/* @__PURE__ */ jsx("label", { className: "paragraph-xsmall-semibold text-zinc-600", children: "CVC" }),
|
|
@@ -92,10 +89,10 @@ function CardFormFields({ form }) {
|
|
|
92
89
|
CardCvcElement,
|
|
93
90
|
{
|
|
94
91
|
options: stripeElementOptions,
|
|
95
|
-
onChange: (e) =>
|
|
92
|
+
onChange: (e) => onStripeChange("cardCvc", e.complete)
|
|
96
93
|
}
|
|
97
94
|
) }) }),
|
|
98
|
-
|
|
95
|
+
stripeErrors.cardCvc && /* @__PURE__ */ jsx("p", { className: "paragraph-xsmall-semibold text-red-600", children: stripeErrors.cardCvc })
|
|
99
96
|
] })
|
|
100
97
|
] }),
|
|
101
98
|
/* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/modals/cards/CardFormFields.tsx"],"sourcesContent":["'use client';\n\nimport { UseFormReturn } from 'react-hook-form';\nimport { FormField } from '../../ui/form/FormField';\nimport { SelectField } from '../../ui/form/SelectField';\nimport { formatCPF, formatCNPJ, formatPostalCode } from '../../../utils/format/masks';\nimport z from 'zod';\nimport { CardCvcElement, CardExpiryElement, CardNumberElement } from '@stripe/react-stripe-js';\n\nconst stripeElementOptions = {\n style: {\n base: {\n fontSize: '16px',\n color: '#030712',\n fontFamily: 'Inter, sans-serif',\n '::placeholder': {\n color: '#9ca3af',\n },\n },\n },\n};\n\nexport const cardFormSchema = z\n .object({\n name: z.string().min(1, 'Nome é obrigatório'),\n number: z.string().min(1, 'Número do cartão é obrigatório'),\n expiry: z.string().min(1, 'Data de validade é obrigatória'),\n cvv: z.string().min(1, 'Código de segurança é obrigatório'),\n country: z.string().min(1, 'País é obrigatório'),\n personType: z.enum(['pf', 'pj']),\n cpf: z.string().optional(),\n cnpj: z.string().optional(),\n fullName: z.string().min(1, 'Nome completo é obrigatório'),\n cep: z.string().min(9, 'CEP é obrigatório'),\n street: z.string().min(1, 'Endereço é obrigatório'),\n streetNumber: z.string().min(1, 'Número é obrigatório'),\n city: z.string().min(1, 'Cidade é obrigatória'),\n state: z.string().min(1, 'UF é obrigatório'),\n complement: z.string().optional(),\n })\n .refine(\n (data) => {\n if (data.personType === 'pf') return !!data.cpf && data.cpf.length >= 14;\n return !!data.cnpj && data.cnpj.length >= 18;\n },\n {\n message: 'Documento é obrigatório',\n path: ['cpf'],\n }\n );\n\nexport type CardFormData = z.infer<typeof cardFormSchema>;\n\nexport const STEP1_FIELDS = ['name', 'number', 'expiry', 'cvv', 'country', 'personType', 'cpf', 'cnpj'] as const;\n\ntype CardFormFieldsProps = {\n readonly form: UseFormReturn<CardFormData>;\n};\n\nexport function CardFormFields({ form }: CardFormFieldsProps) {\n const {\n register,\n watch,\n setValue,\n formState: { errors },\n } = form;\n\n const personType = watch('personType');\n\n return (\n <div className=\"flex flex-col gap-5\">\n <FormField\n label=\"Nome impresso no cartão\"\n placeholder=\"Digite aqui\"\n {...register('name')}\n error={!!errors.name}\n errorMessage={errors.name?.message}\n />\n\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"paragraph-xsmall-semibold text-zinc-600\">Número do cartão</label>\n <div className=\"h-10 px-3 flex items-center w-full border border-zinc-200 rounded-lg bg-white transition-colors focus-within:border-zinc-400 relative\">\n <div className=\"w-full\">\n <CardNumberElement\n options={{ ...stripeElementOptions, showIcon: true }}\n onChange={(e) =>\n setValue('number', e.complete ? 'complete' : '', { shouldValidate: true })\n }\n />\n </div>\n </div>\n {errors.number && (\n <p className=\"paragraph-xsmall-semibold text-red-600\">{errors.number.message}</p>\n )}\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1 flex flex-col gap-1.5\">\n <label className=\"paragraph-xsmall-semibold text-zinc-600\">Data de validade</label>\n <div className=\"h-10 px-3 flex items-center w-full border border-zinc-200 rounded-lg bg-white transition-colors focus-within:border-zinc-400\">\n <div className=\"w-full\">\n <CardExpiryElement\n options={stripeElementOptions}\n onChange={(e) =>\n setValue('expiry', e.complete ? 'complete' : '', { shouldValidate: true })\n }\n />\n </div>\n </div>\n {errors.expiry && (\n <p className=\"paragraph-xsmall-semibold text-red-600\">{errors.expiry.message}</p>\n )}\n </div>\n\n <div className=\"flex-1 flex flex-col gap-1.5\">\n <label className=\"paragraph-xsmall-semibold text-zinc-600\">CVC</label>\n <div className=\"h-10 px-3 flex items-center w-full border border-zinc-200 rounded-lg bg-white transition-colors focus-within:border-zinc-400\">\n <div className=\"w-full\">\n <CardCvcElement\n options={stripeElementOptions}\n onChange={(e) =>\n setValue('cvv', e.complete ? 'complete' : '', { shouldValidate: true })\n }\n />\n </div>\n </div>\n {errors.cvv && (\n <p className=\"paragraph-xsmall-semibold text-red-600\">{errors.cvv.message}</p>\n )}\n </div>\n </div>\n\n <SelectField\n label=\"País\"\n placeholder=\"Selecione\"\n value={watch('country')}\n onChange={(value) => setValue('country', value as string)}\n options={[\n { value: 'BR', label: 'Brasil' },\n { value: 'ES', label: 'Espanha' },\n { value: 'US', label: 'Estados Unidos' },\n ]}\n />\n\n <div className=\"flex gap-4\">\n <SelectField\n label=\"Tipo de pessoa\"\n placeholder=\"Selecione\"\n value={personType}\n onChange={(value) => {\n setValue('personType', value as 'pf' | 'pj');\n setValue('cpf', '');\n setValue('cnpj', '');\n }}\n options={[\n { value: 'pf', label: 'Pessoa física' },\n { value: 'pj', label: 'Pessoa jurídica' },\n ]}\n containerClassName=\"flex-1\"\n />\n <div className=\"flex-1\">\n {personType === 'pj' ? (\n <FormField\n label=\"CNPJ\"\n placeholder=\"__.___.___/____-__\"\n {...register('cnpj', {\n onChange: (e) => {\n setValue('cnpj', formatCNPJ(e.target.value));\n },\n })}\n error={!!errors.cpf}\n errorMessage={errors.cpf?.message}\n />\n ) : (\n <FormField\n label=\"CPF\"\n placeholder=\"___.___.___-__\"\n {...register('cpf', {\n onChange: (e) => {\n setValue('cpf', formatCPF(e.target.value));\n },\n })}\n error={!!errors.cpf}\n errorMessage={errors.cpf?.message}\n />\n )}\n </div>\n </div>\n </div>\n );\n}\n\ntype BillingFormFieldsProps = {\n readonly form: UseFormReturn<CardFormData>;\n};\n\nexport function BillingFormFields({ form }: BillingFormFieldsProps) {\n const {\n register,\n setValue,\n formState: { errors },\n } = form;\n\n return (\n <div className=\"flex flex-col gap-5\">\n <FormField\n label=\"Nome completo\"\n placeholder=\"Digite aqui\"\n {...register('fullName')}\n error={!!errors.fullName}\n errorMessage={errors.fullName?.message}\n />\n\n <FormField\n label=\"CEP\"\n placeholder=\"_____-___\"\n {...register('cep', {\n onChange: (e) => {\n setValue('cep', formatPostalCode(e.target.value));\n },\n })}\n error={!!errors.cep}\n errorMessage={errors.cep?.message}\n />\n\n <div className=\"flex gap-4\">\n <div className=\"flex-[2]\">\n <FormField\n label=\"Endereço\"\n placeholder=\"\"\n {...register('street')}\n error={!!errors.street}\n errorMessage={errors.street?.message}\n />\n </div>\n <div className=\"flex-1\">\n <FormField\n label=\"Número\"\n placeholder=\"\"\n {...register('streetNumber')}\n error={!!errors.streetNumber}\n errorMessage={errors.streetNumber?.message}\n />\n </div>\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <FormField\n label=\"Cidade\"\n placeholder=\"\"\n {...register('city')}\n error={!!errors.city}\n errorMessage={errors.city?.message}\n />\n </div>\n <div className=\"flex-1\">\n <FormField\n label=\"UF\"\n placeholder=\"\"\n {...register('state')}\n error={!!errors.state}\n errorMessage={errors.state?.message}\n />\n </div>\n </div>\n\n <FormField\n label=\"Complemento\"\n optional\n placeholder=\"\"\n {...register('complement')}\n error={!!errors.complement}\n errorMessage={errors.complement?.message}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/modals/cards/CardFormFields.tsx"],"sourcesContent":["'use client';\n\nimport { UseFormReturn } from 'react-hook-form';\nimport { FormField } from '../../ui/form/FormField';\nimport { SelectField } from '../../ui/form/SelectField';\nimport { formatCPF, formatCNPJ, formatPostalCode } from '../../../utils/format/masks';\nimport z from 'zod';\nimport { CardCvcElement, CardExpiryElement, CardNumberElement } from '@stripe/react-stripe-js';\n\nconst stripeElementOptions = {\n style: {\n base: {\n fontSize: '16px',\n color: '#030712',\n fontFamily: 'Inter, sans-serif',\n '::placeholder': {\n color: '#9ca3af',\n },\n },\n },\n};\n\nexport const cardFormSchema = z\n .object({\n name: z.string().min(1, 'Nome é obrigatório'),\n country: z.string().min(1, 'País é obrigatório'),\n personType: z.enum(['pf', 'pj']),\n cpf: z.string().optional(),\n cnpj: z.string().optional(),\n fullName: z.string().min(1, 'Nome completo é obrigatório'),\n cep: z.string().min(9, 'CEP é obrigatório'),\n street: z.string().min(1, 'Endereço é obrigatório'),\n streetNumber: z.string().min(1, 'Número é obrigatório'),\n city: z.string().min(1, 'Cidade é obrigatória'),\n state: z.string().min(1, 'UF é obrigatório'),\n complement: z.string().optional(),\n })\n .refine(\n (data) => {\n if (data.personType === 'pf') return !!data.cpf && data.cpf.length >= 14;\n return !!data.cnpj && data.cnpj.length >= 18;\n },\n {\n message: 'Documento é obrigatório',\n path: ['cpf'],\n }\n );\n\nexport type CardFormData = z.infer<typeof cardFormSchema>;\n\nexport type StripeElementsStatus = {\n cardNumber: boolean;\n cardExpiry: boolean;\n cardCvc: boolean;\n};\n\nexport const STEP1_FIELDS = ['name', 'country', 'personType', 'cpf', 'cnpj'] as const;\n\ntype CardFormFieldsProps = {\n readonly form: UseFormReturn<CardFormData>;\n readonly onStripeChange: (field: keyof StripeElementsStatus, complete: boolean) => void;\n readonly stripeErrors: { cardNumber?: string; cardExpiry?: string; cardCvc?: string };\n};\n\nexport function CardFormFields({ form, onStripeChange, stripeErrors }: CardFormFieldsProps) {\n const {\n register,\n watch,\n setValue,\n formState: { errors },\n } = form;\n\n const personType = watch('personType');\n\n return (\n <div className=\"flex flex-col gap-5\">\n <FormField\n label=\"Nome impresso no cartão\"\n placeholder=\"Digite aqui\"\n {...register('name')}\n error={!!errors.name}\n errorMessage={errors.name?.message}\n />\n\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"paragraph-xsmall-semibold text-zinc-600\">Número do cartão</label>\n <div className=\"h-10 px-3 flex items-center w-full border border-zinc-200 rounded-lg bg-white transition-colors focus-within:border-zinc-400 relative\">\n <div className=\"w-full\">\n <CardNumberElement\n options={{ ...stripeElementOptions, showIcon: true }}\n onChange={(e: { complete: boolean }) => onStripeChange('cardNumber', e.complete)}\n />\n </div>\n </div>\n {stripeErrors.cardNumber && (\n <p className=\"paragraph-xsmall-semibold text-red-600\">{stripeErrors.cardNumber}</p>\n )}\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1 flex flex-col gap-1.5\">\n <label className=\"paragraph-xsmall-semibold text-zinc-600\">Data de validade</label>\n <div className=\"h-10 px-3 flex items-center w-full border border-zinc-200 rounded-lg bg-white transition-colors focus-within:border-zinc-400\">\n <div className=\"w-full\">\n <CardExpiryElement\n options={stripeElementOptions}\n onChange={(e: { complete: boolean }) => onStripeChange('cardExpiry', e.complete)}\n />\n </div>\n </div>\n {stripeErrors.cardExpiry && (\n <p className=\"paragraph-xsmall-semibold text-red-600\">{stripeErrors.cardExpiry}</p>\n )}\n </div>\n\n <div className=\"flex-1 flex flex-col gap-1.5\">\n <label className=\"paragraph-xsmall-semibold text-zinc-600\">CVC</label>\n <div className=\"h-10 px-3 flex items-center w-full border border-zinc-200 rounded-lg bg-white transition-colors focus-within:border-zinc-400\">\n <div className=\"w-full\">\n <CardCvcElement\n options={stripeElementOptions}\n onChange={(e: { complete: boolean }) => onStripeChange('cardCvc', e.complete)}\n />\n </div>\n </div>\n {stripeErrors.cardCvc && (\n <p className=\"paragraph-xsmall-semibold text-red-600\">{stripeErrors.cardCvc}</p>\n )}\n </div>\n </div>\n\n <SelectField\n label=\"País\"\n placeholder=\"Selecione\"\n value={watch('country')}\n onChange={(value) => setValue('country', value as string)}\n options={[\n { value: 'BR', label: 'Brasil' },\n { value: 'ES', label: 'Espanha' },\n { value: 'US', label: 'Estados Unidos' },\n ]}\n />\n\n <div className=\"flex gap-4\">\n <SelectField\n label=\"Tipo de pessoa\"\n placeholder=\"Selecione\"\n value={personType}\n onChange={(value) => {\n setValue('personType', value as 'pf' | 'pj');\n setValue('cpf', '');\n setValue('cnpj', '');\n }}\n options={[\n { value: 'pf', label: 'Pessoa física' },\n { value: 'pj', label: 'Pessoa jurídica' },\n ]}\n containerClassName=\"flex-1\"\n />\n <div className=\"flex-1\">\n {personType === 'pj' ? (\n <FormField\n label=\"CNPJ\"\n placeholder=\"__.___.___/____-__\"\n {...register('cnpj', {\n onChange: (e) => {\n setValue('cnpj', formatCNPJ(e.target.value));\n },\n })}\n error={!!errors.cpf}\n errorMessage={errors.cpf?.message}\n />\n ) : (\n <FormField\n label=\"CPF\"\n placeholder=\"___.___.___-__\"\n {...register('cpf', {\n onChange: (e) => {\n setValue('cpf', formatCPF(e.target.value));\n },\n })}\n error={!!errors.cpf}\n errorMessage={errors.cpf?.message}\n />\n )}\n </div>\n </div>\n </div>\n );\n}\n\ntype BillingFormFieldsProps = {\n readonly form: UseFormReturn<CardFormData>;\n};\n\nexport function BillingFormFields({ form }: BillingFormFieldsProps) {\n const {\n register,\n setValue,\n formState: { errors },\n } = form;\n\n return (\n <div className=\"flex flex-col gap-5\">\n <FormField\n label=\"Nome completo\"\n placeholder=\"Digite aqui\"\n {...register('fullName')}\n error={!!errors.fullName}\n errorMessage={errors.fullName?.message}\n />\n\n <FormField\n label=\"CEP\"\n placeholder=\"_____-___\"\n {...register('cep', {\n onChange: (e) => {\n setValue('cep', formatPostalCode(e.target.value));\n },\n })}\n error={!!errors.cep}\n errorMessage={errors.cep?.message}\n />\n\n <div className=\"flex gap-4\">\n <div className=\"flex-[2]\">\n <FormField\n label=\"Endereço\"\n placeholder=\"\"\n {...register('street')}\n error={!!errors.street}\n errorMessage={errors.street?.message}\n />\n </div>\n <div className=\"flex-1\">\n <FormField\n label=\"Número\"\n placeholder=\"\"\n {...register('streetNumber')}\n error={!!errors.streetNumber}\n errorMessage={errors.streetNumber?.message}\n />\n </div>\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <FormField\n label=\"Cidade\"\n placeholder=\"\"\n {...register('city')}\n error={!!errors.city}\n errorMessage={errors.city?.message}\n />\n </div>\n <div className=\"flex-1\">\n <FormField\n label=\"UF\"\n placeholder=\"\"\n {...register('state')}\n error={!!errors.state}\n errorMessage={errors.state?.message}\n />\n </div>\n </div>\n\n <FormField\n label=\"Complemento\"\n optional\n placeholder=\"\"\n {...register('complement')}\n error={!!errors.complement}\n errorMessage={errors.complement?.message}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -7,18 +7,26 @@ import { Toast } from '../../ui/feedback/Toast';
|
|
|
7
7
|
import { useModalManager } from '../../../store/useModalManager';
|
|
8
8
|
import { useCreateCard } from '../../../modules/cards/hooks/create-card.hook';
|
|
9
9
|
import { useUpdateAccount } from '../../../modules/accounts/hooks/useAccountManagement';
|
|
10
|
-
import { CardFormFields, BillingFormFields, cardFormSchema
|
|
11
|
-
import type { CardFormData } from './CardFormFields';
|
|
10
|
+
import { CardFormFields, BillingFormFields, cardFormSchema } from './CardFormFields';
|
|
11
|
+
import type { CardFormData, StripeElementsStatus } from './CardFormFields';
|
|
12
12
|
import { IconX } from '@tabler/icons-react';
|
|
13
13
|
import { useForm } from 'react-hook-form';
|
|
14
14
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
15
15
|
import { CardNumberElement, useElements, useStripe } from '@stripe/react-stripe-js';
|
|
16
16
|
import { toast } from 'sonner';
|
|
17
17
|
|
|
18
|
+
const INITIAL_STRIPE_STATUS: StripeElementsStatus = {
|
|
19
|
+
cardNumber: false,
|
|
20
|
+
cardExpiry: false,
|
|
21
|
+
cardCvc: false,
|
|
22
|
+
};
|
|
23
|
+
|
|
18
24
|
export default function AddCardModal() {
|
|
19
25
|
const { activeModal, modalData, closeModal } = useModalManager();
|
|
20
26
|
const isOpen = activeModal === 'addCardModal';
|
|
21
27
|
const [currentStep, setCurrentStep] = useState(1);
|
|
28
|
+
const [stripeStatus, setStripeStatus] = useState<StripeElementsStatus>(INITIAL_STRIPE_STATUS);
|
|
29
|
+
const [stripeErrors, setStripeErrors] = useState<Record<string, string | undefined>>({});
|
|
22
30
|
|
|
23
31
|
const onCardAdded = modalData.onCardAdded as ((cardId: string) => void) | undefined;
|
|
24
32
|
|
|
@@ -40,15 +48,36 @@ export default function AddCardModal() {
|
|
|
40
48
|
const handleClose = useCallback(() => {
|
|
41
49
|
form.reset();
|
|
42
50
|
setCurrentStep(1);
|
|
51
|
+
setStripeStatus(INITIAL_STRIPE_STATUS);
|
|
52
|
+
setStripeErrors({});
|
|
43
53
|
closeModal();
|
|
44
54
|
}, [form, closeModal]);
|
|
45
55
|
|
|
56
|
+
const handleStripeChange = useCallback(
|
|
57
|
+
(field: keyof StripeElementsStatus, complete: boolean) => {
|
|
58
|
+
setStripeStatus((prev) => ({ ...prev, [field]: complete }));
|
|
59
|
+
if (complete) {
|
|
60
|
+
setStripeErrors((prev) => ({ ...prev, [field]: undefined }));
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
[]
|
|
64
|
+
);
|
|
65
|
+
|
|
46
66
|
const handleContinue = useCallback(async () => {
|
|
47
|
-
const
|
|
48
|
-
|
|
67
|
+
const rhfValid = await form.trigger(['name', 'country', 'personType', 'cpf', 'cnpj']);
|
|
68
|
+
|
|
69
|
+
const newStripeErrors: Record<string, string | undefined> = {};
|
|
70
|
+
if (!stripeStatus.cardNumber) newStripeErrors.cardNumber = 'Número do cartão é obrigatório';
|
|
71
|
+
if (!stripeStatus.cardExpiry) newStripeErrors.cardExpiry = 'Data de validade é obrigatória';
|
|
72
|
+
if (!stripeStatus.cardCvc) newStripeErrors.cardCvc = 'Código de segurança é obrigatório';
|
|
73
|
+
setStripeErrors(newStripeErrors);
|
|
74
|
+
|
|
75
|
+
const stripeValid = stripeStatus.cardNumber && stripeStatus.cardExpiry && stripeStatus.cardCvc;
|
|
76
|
+
|
|
77
|
+
if (rhfValid && stripeValid) {
|
|
49
78
|
setCurrentStep(2);
|
|
50
79
|
}
|
|
51
|
-
}, [form]);
|
|
80
|
+
}, [form, stripeStatus]);
|
|
52
81
|
|
|
53
82
|
const handleBack = useCallback(() => {
|
|
54
83
|
setCurrentStep(1);
|
|
@@ -134,6 +163,17 @@ export default function AddCardModal() {
|
|
|
134
163
|
updateAccountMutation.isPending ||
|
|
135
164
|
form.formState.isSubmitting;
|
|
136
165
|
|
|
166
|
+
const watchedValues = form.watch(['name', 'country', 'personType', 'cpf', 'cnpj']);
|
|
167
|
+
const [name, country, personType, cpf, cnpj] = watchedValues;
|
|
168
|
+
const isStep1Valid =
|
|
169
|
+
!!name &&
|
|
170
|
+
!!country &&
|
|
171
|
+
!!personType &&
|
|
172
|
+
(personType === 'pf' ? !!cpf && cpf.length >= 14 : !!cnpj && cnpj.length >= 18) &&
|
|
173
|
+
stripeStatus.cardNumber &&
|
|
174
|
+
stripeStatus.cardExpiry &&
|
|
175
|
+
stripeStatus.cardCvc;
|
|
176
|
+
|
|
137
177
|
return (
|
|
138
178
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
|
139
179
|
<DialogContent
|
|
@@ -170,7 +210,11 @@ export default function AddCardModal() {
|
|
|
170
210
|
>
|
|
171
211
|
<div className="flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none">
|
|
172
212
|
{currentStep === 1 ? (
|
|
173
|
-
<CardFormFields
|
|
213
|
+
<CardFormFields
|
|
214
|
+
form={form}
|
|
215
|
+
onStripeChange={handleStripeChange}
|
|
216
|
+
stripeErrors={stripeErrors}
|
|
217
|
+
/>
|
|
174
218
|
) : (
|
|
175
219
|
<BillingFormFields form={form} />
|
|
176
220
|
)}
|
|
@@ -182,7 +226,12 @@ export default function AddCardModal() {
|
|
|
182
226
|
<Button variant="secondary" type="button" onClick={handleClose} className="h-10!">
|
|
183
227
|
Cancelar
|
|
184
228
|
</Button>
|
|
185
|
-
<Button
|
|
229
|
+
<Button
|
|
230
|
+
type="button"
|
|
231
|
+
className="h-10!"
|
|
232
|
+
onClick={handleContinue}
|
|
233
|
+
disabled={!isStep1Valid}
|
|
234
|
+
>
|
|
186
235
|
Continuar
|
|
187
236
|
</Button>
|
|
188
237
|
</>
|
|
@@ -23,9 +23,6 @@ const stripeElementOptions = {
|
|
|
23
23
|
export const cardFormSchema = z
|
|
24
24
|
.object({
|
|
25
25
|
name: z.string().min(1, 'Nome é obrigatório'),
|
|
26
|
-
number: z.string().min(1, 'Número do cartão é obrigatório'),
|
|
27
|
-
expiry: z.string().min(1, 'Data de validade é obrigatória'),
|
|
28
|
-
cvv: z.string().min(1, 'Código de segurança é obrigatório'),
|
|
29
26
|
country: z.string().min(1, 'País é obrigatório'),
|
|
30
27
|
personType: z.enum(['pf', 'pj']),
|
|
31
28
|
cpf: z.string().optional(),
|
|
@@ -51,13 +48,21 @@ export const cardFormSchema = z
|
|
|
51
48
|
|
|
52
49
|
export type CardFormData = z.infer<typeof cardFormSchema>;
|
|
53
50
|
|
|
54
|
-
export
|
|
51
|
+
export type StripeElementsStatus = {
|
|
52
|
+
cardNumber: boolean;
|
|
53
|
+
cardExpiry: boolean;
|
|
54
|
+
cardCvc: boolean;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const STEP1_FIELDS = ['name', 'country', 'personType', 'cpf', 'cnpj'] as const;
|
|
55
58
|
|
|
56
59
|
type CardFormFieldsProps = {
|
|
57
60
|
readonly form: UseFormReturn<CardFormData>;
|
|
61
|
+
readonly onStripeChange: (field: keyof StripeElementsStatus, complete: boolean) => void;
|
|
62
|
+
readonly stripeErrors: { cardNumber?: string; cardExpiry?: string; cardCvc?: string };
|
|
58
63
|
};
|
|
59
64
|
|
|
60
|
-
export function CardFormFields({ form }: CardFormFieldsProps) {
|
|
65
|
+
export function CardFormFields({ form, onStripeChange, stripeErrors }: CardFormFieldsProps) {
|
|
61
66
|
const {
|
|
62
67
|
register,
|
|
63
68
|
watch,
|
|
@@ -83,14 +88,12 @@ export function CardFormFields({ form }: CardFormFieldsProps) {
|
|
|
83
88
|
<div className="w-full">
|
|
84
89
|
<CardNumberElement
|
|
85
90
|
options={{ ...stripeElementOptions, showIcon: true }}
|
|
86
|
-
onChange={(e) =>
|
|
87
|
-
setValue('number', e.complete ? 'complete' : '', { shouldValidate: true })
|
|
88
|
-
}
|
|
91
|
+
onChange={(e: { complete: boolean }) => onStripeChange('cardNumber', e.complete)}
|
|
89
92
|
/>
|
|
90
93
|
</div>
|
|
91
94
|
</div>
|
|
92
|
-
{
|
|
93
|
-
<p className="paragraph-xsmall-semibold text-red-600">{
|
|
95
|
+
{stripeErrors.cardNumber && (
|
|
96
|
+
<p className="paragraph-xsmall-semibold text-red-600">{stripeErrors.cardNumber}</p>
|
|
94
97
|
)}
|
|
95
98
|
</div>
|
|
96
99
|
|
|
@@ -101,14 +104,12 @@ export function CardFormFields({ form }: CardFormFieldsProps) {
|
|
|
101
104
|
<div className="w-full">
|
|
102
105
|
<CardExpiryElement
|
|
103
106
|
options={stripeElementOptions}
|
|
104
|
-
onChange={(e) =>
|
|
105
|
-
setValue('expiry', e.complete ? 'complete' : '', { shouldValidate: true })
|
|
106
|
-
}
|
|
107
|
+
onChange={(e: { complete: boolean }) => onStripeChange('cardExpiry', e.complete)}
|
|
107
108
|
/>
|
|
108
109
|
</div>
|
|
109
110
|
</div>
|
|
110
|
-
{
|
|
111
|
-
<p className="paragraph-xsmall-semibold text-red-600">{
|
|
111
|
+
{stripeErrors.cardExpiry && (
|
|
112
|
+
<p className="paragraph-xsmall-semibold text-red-600">{stripeErrors.cardExpiry}</p>
|
|
112
113
|
)}
|
|
113
114
|
</div>
|
|
114
115
|
|
|
@@ -118,14 +119,12 @@ export function CardFormFields({ form }: CardFormFieldsProps) {
|
|
|
118
119
|
<div className="w-full">
|
|
119
120
|
<CardCvcElement
|
|
120
121
|
options={stripeElementOptions}
|
|
121
|
-
onChange={(e) =>
|
|
122
|
-
setValue('cvv', e.complete ? 'complete' : '', { shouldValidate: true })
|
|
123
|
-
}
|
|
122
|
+
onChange={(e: { complete: boolean }) => onStripeChange('cardCvc', e.complete)}
|
|
124
123
|
/>
|
|
125
124
|
</div>
|
|
126
125
|
</div>
|
|
127
|
-
{
|
|
128
|
-
<p className="paragraph-xsmall-semibold text-red-600">{
|
|
126
|
+
{stripeErrors.cardCvc && (
|
|
127
|
+
<p className="paragraph-xsmall-semibold text-red-600">{stripeErrors.cardCvc}</p>
|
|
129
128
|
)}
|
|
130
129
|
</div>
|
|
131
130
|
</div>
|