@greatapps/common 1.1.741 → 1.1.743
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/BuyCreditsModal.mjs +42 -30
- package/dist/components/modals/BuyCreditsModal.mjs.map +1 -1
- package/dist/components/modals/billing/RequiredBillingDataModal.mjs +14 -30
- package/dist/components/modals/billing/RequiredBillingDataModal.mjs.map +1 -1
- package/dist/components/modals/cards/AddCardModal.mjs +2 -1
- package/dist/components/modals/cards/AddCardModal.mjs.map +1 -1
- package/dist/index.mjs +26 -6
- package/dist/index.mjs.map +1 -1
- package/dist/modules/accounts/hooks/use-currency-formatter.hook.mjs +3 -1
- package/dist/modules/accounts/hooks/use-currency-formatter.hook.mjs.map +1 -1
- package/dist/modules/accounts/hooks/use-required-billing-data.hook.mjs +3 -4
- package/dist/modules/accounts/hooks/use-required-billing-data.hook.mjs.map +1 -1
- package/dist/modules/accounts/utils/billing-data.mjs +10 -14
- package/dist/modules/accounts/utils/billing-data.mjs.map +1 -1
- package/dist/modules/plans/types/plan.type.mjs +15 -0
- package/dist/modules/plans/types/plan.type.mjs.map +1 -1
- package/dist/modules/plans/utils/map-api-plan-to-ui.mjs +10 -2
- package/dist/modules/plans/utils/map-api-plan-to-ui.mjs.map +1 -1
- package/dist/modules/subscriptions/constants/addons.constants.mjs +15 -2
- package/dist/modules/subscriptions/constants/addons.constants.mjs.map +1 -1
- package/dist/utils/format/currency.mjs +4 -1
- package/dist/utils/format/currency.mjs.map +1 -1
- package/dist/utils/format/gateway.mjs +18 -0
- package/dist/utils/format/gateway.mjs.map +1 -0
- package/package.json +1 -1
- package/src/components/modals/BuyCreditsModal.tsx +48 -32
- package/src/components/modals/billing/RequiredBillingDataModal.tsx +32 -66
- package/src/components/modals/cards/AddCardModal.tsx +2 -1
- package/src/index.ts +14 -2
- package/src/modules/accounts/hooks/use-currency-formatter.hook.tsx +3 -0
- package/src/modules/accounts/hooks/use-required-billing-data.hook.ts +3 -3
- package/src/modules/accounts/utils/billing-data.ts +22 -26
- package/src/modules/plans/types/plan.type.ts +17 -0
- package/src/modules/plans/utils/map-api-plan-to-ui.ts +12 -4
- package/src/modules/subscriptions/constants/addons.constants.ts +41 -28
- package/src/utils/format/currency.ts +5 -29
- package/src/utils/format/gateway.ts +22 -0
|
@@ -12,14 +12,16 @@ import { Toast } from "../ui/feedback/Toast";
|
|
|
12
12
|
import { SelectField } from "../ui/form/SelectField";
|
|
13
13
|
import { Skeleton } from "../ui/feedback/Skeleton";
|
|
14
14
|
import { PaymentInfoCard } from "../ui/data-display/PaymentInfoCard";
|
|
15
|
-
import { ADDON_IDS } from "../../modules/subscriptions/constants/addons.constants";
|
|
16
15
|
import {
|
|
17
|
-
|
|
18
|
-
AI_CREDIT_VALUES
|
|
16
|
+
ADDON_IDS,
|
|
17
|
+
AI_CREDIT_VALUES,
|
|
18
|
+
buildAiCreditOptions
|
|
19
19
|
} from "../../modules/subscriptions/constants/addons.constants";
|
|
20
20
|
import { useBuyCreditsModal } from "../../store/useBuyCreditsModal";
|
|
21
21
|
import { useModalManager } from "../../store/useModalManager";
|
|
22
22
|
import { useCards } from "../../modules/cards/hooks/cards.hook";
|
|
23
|
+
import { useCurrencyFormatter } from "../../modules/accounts/hooks/use-currency-formatter.hook";
|
|
24
|
+
import { usePlanById } from "../../modules/plans/hooks/use-plan-by-id.hook";
|
|
23
25
|
import { useActiveSubscription } from "../../modules/subscriptions/hooks/find-active-subscription.hook";
|
|
24
26
|
import { useCalculateSubscription } from "../../modules/subscriptions/hooks/calculate-subscription.hook";
|
|
25
27
|
import { useUpdateSubscriptionPlan } from "../../modules/subscriptions/hooks/update-subscription-plan.hook";
|
|
@@ -45,6 +47,7 @@ function BuyCreditsModal() {
|
|
|
45
47
|
const isDefaultWl = useIsDefaultWhitelabel();
|
|
46
48
|
const { isExternalContracting, redirectToExternal } = useExternalContracting();
|
|
47
49
|
const { data: cardsData } = useCards();
|
|
50
|
+
const { locale, formatCurrencyNumber } = useCurrencyFormatter();
|
|
48
51
|
const { data: subscriptionData } = useActiveSubscription();
|
|
49
52
|
const subscription = subscriptionData?.data?.[0] ?? null;
|
|
50
53
|
const defaultCardId = cardsData?.data?.find((card) => card.is_default)?.id?.toString() ?? cardsData?.data?.[0]?.id?.toString() ?? "";
|
|
@@ -63,9 +66,17 @@ function BuyCreditsModal() {
|
|
|
63
66
|
const currentCredits = aiItem?.quantity ?? 0;
|
|
64
67
|
const subscriptionId = subscription?.id;
|
|
65
68
|
const planId = subscription?.id_plan;
|
|
69
|
+
const { data: planData } = usePlanById(planId);
|
|
70
|
+
const perCreditPrice = useMemo(() => {
|
|
71
|
+
const planAiItem = planData?.data?.items?.find(
|
|
72
|
+
(item) => item.id_addon === ADDON_IDS.AI && (item.value ?? 0) > 0
|
|
73
|
+
);
|
|
74
|
+
return planAiItem?.value ?? aiItem?.value ?? 0;
|
|
75
|
+
}, [planData, aiItem]);
|
|
76
|
+
const hasPerCreditPrice = perCreditPrice > 0;
|
|
77
|
+
const aiCreditOptions = useMemo(() => buildAiCreditOptions(perCreditPrice), [perCreditPrice]);
|
|
66
78
|
const buildPlanExtras = useCallback(
|
|
67
79
|
(aiQuantity) => {
|
|
68
|
-
const aiOption = AI_CREDIT_OPTIONS.find((opt) => opt.credits === aiQuantity);
|
|
69
80
|
return [
|
|
70
81
|
...(subscription?.items ?? []).filter((item) => item.id_addon != null && item.id_addon !== ADDON_IDS.AI).map((item) => ({
|
|
71
82
|
id_addon: Number(item.id_addon),
|
|
@@ -75,8 +86,7 @@ function BuyCreditsModal() {
|
|
|
75
86
|
})),
|
|
76
87
|
{
|
|
77
88
|
id_addon: ADDON_IDS.AI,
|
|
78
|
-
quantity: aiQuantity
|
|
79
|
-
value: aiOption?.price ?? 0
|
|
89
|
+
quantity: aiQuantity
|
|
80
90
|
}
|
|
81
91
|
];
|
|
82
92
|
},
|
|
@@ -134,13 +144,12 @@ function BuyCreditsModal() {
|
|
|
134
144
|
shouldValidate: true,
|
|
135
145
|
shouldDirty: true
|
|
136
146
|
});
|
|
137
|
-
|
|
138
|
-
if (!option) return;
|
|
147
|
+
if (!AI_CREDIT_VALUES.has(credits)) return;
|
|
139
148
|
calculate({
|
|
140
149
|
id_product: 1,
|
|
141
150
|
...planId != null && { id_plan: planId },
|
|
142
151
|
...subscriptionId != null && { id_subscription: subscriptionId },
|
|
143
|
-
plan_extras: buildPlanExtras(
|
|
152
|
+
plan_extras: buildPlanExtras(credits)
|
|
144
153
|
});
|
|
145
154
|
},
|
|
146
155
|
[form, subscriptionId, planId, calculate, buildPlanExtras]
|
|
@@ -153,13 +162,7 @@ function BuyCreditsModal() {
|
|
|
153
162
|
if (!calculatedData) return null;
|
|
154
163
|
return getPriceFromCalculatedData(calculatedData, subscription?.periodicity);
|
|
155
164
|
}, [calculatedData, subscription]);
|
|
156
|
-
const
|
|
157
|
-
return price.toLocaleString("pt-BR", {
|
|
158
|
-
style: "currency",
|
|
159
|
-
currency: "BRL"
|
|
160
|
-
});
|
|
161
|
-
}, []);
|
|
162
|
-
const isConfirmDisabled = !isDirty || !isValid || isCalculatePending || isSubmitting;
|
|
165
|
+
const isConfirmDisabled = !isDirty || !isValid || isCalculatePending || isSubmitting || !hasPerCreditPrice;
|
|
163
166
|
const handleClose = useCallback(() => {
|
|
164
167
|
form.reset();
|
|
165
168
|
closeModal();
|
|
@@ -168,48 +171,57 @@ function BuyCreditsModal() {
|
|
|
168
171
|
openModal("addCardModal");
|
|
169
172
|
}, [openModal]);
|
|
170
173
|
const selectOptions = useMemo(() => {
|
|
171
|
-
return
|
|
174
|
+
return aiCreditOptions.map((opt) => ({
|
|
172
175
|
value: opt.credits,
|
|
173
176
|
label: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 w-full", children: [
|
|
174
|
-
/* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-zinc-950 flex-1", children: translate("common.credits.buy.creditsOptionLabel", { credits: opt.credits.toLocaleString(
|
|
175
|
-
/* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-zinc-500 opacity-70", children: opt.
|
|
177
|
+
/* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-zinc-950 flex-1", children: translate("common.credits.buy.creditsOptionLabel", { credits: opt.credits.toLocaleString(locale) }) }),
|
|
178
|
+
hasPerCreditPrice && /* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-zinc-500 opacity-70", children: formatCurrencyNumber(opt.price) })
|
|
176
179
|
] }),
|
|
177
|
-
displayValue: translate("common.credits.buy.creditsOptionDisplayValue", { credits: opt.credits.toLocaleString(
|
|
180
|
+
displayValue: translate("common.credits.buy.creditsOptionDisplayValue", { credits: opt.credits.toLocaleString(locale) })
|
|
178
181
|
}));
|
|
179
|
-
}, [translate]);
|
|
182
|
+
}, [aiCreditOptions, translate, locale, formatCurrencyNumber, hasPerCreditPrice]);
|
|
180
183
|
const onSubmit = useCallback(
|
|
181
184
|
async (data) => {
|
|
182
185
|
if (!subscriptionId) return;
|
|
183
|
-
|
|
184
|
-
if (!
|
|
186
|
+
if (!hasPerCreditPrice) return;
|
|
187
|
+
if (!AI_CREDIT_VALUES.has(data.creditOption)) return;
|
|
185
188
|
await updatePlanMutation.mutateAsync({
|
|
186
189
|
subscriptionId,
|
|
187
190
|
data: {
|
|
188
|
-
plan_extras: buildPlanExtras(
|
|
191
|
+
plan_extras: buildPlanExtras(data.creditOption)
|
|
189
192
|
}
|
|
190
193
|
});
|
|
191
194
|
toast.custom((t) => /* @__PURE__ */ jsx(
|
|
192
195
|
Toast,
|
|
193
196
|
{
|
|
194
197
|
variant: "success",
|
|
195
|
-
message: translate("common.credits.buy.creditsChangedToast", { credits: data.creditOption.toLocaleString(
|
|
198
|
+
message: translate("common.credits.buy.creditsChangedToast", { credits: data.creditOption.toLocaleString(locale) }),
|
|
196
199
|
toastId: t
|
|
197
200
|
}
|
|
198
201
|
));
|
|
199
202
|
form.reset();
|
|
200
203
|
closeModal();
|
|
201
204
|
},
|
|
202
|
-
[
|
|
205
|
+
[
|
|
206
|
+
subscriptionId,
|
|
207
|
+
hasPerCreditPrice,
|
|
208
|
+
updatePlanMutation,
|
|
209
|
+
form,
|
|
210
|
+
closeModal,
|
|
211
|
+
buildPlanExtras,
|
|
212
|
+
translate,
|
|
213
|
+
locale
|
|
214
|
+
]
|
|
203
215
|
);
|
|
204
216
|
const renderPriceDisplay = useCallback(() => {
|
|
205
217
|
if (isCalculatePending) {
|
|
206
218
|
return /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-24" });
|
|
207
219
|
}
|
|
208
220
|
if (totalPrice !== null) {
|
|
209
|
-
return /* @__PURE__ */ jsx("span", { className: "paragraph-xlarge-semibold text-zinc-950", children: totalPrice === 0 ? translate("common.credits.buy.defaultPrice") :
|
|
221
|
+
return /* @__PURE__ */ jsx("span", { className: "paragraph-xlarge-semibold text-zinc-950", children: totalPrice === 0 ? translate("common.credits.buy.defaultPrice") : formatCurrencyNumber(totalPrice) });
|
|
210
222
|
}
|
|
211
223
|
return /* @__PURE__ */ jsx("span", { className: "paragraph-xlarge-semibold text-zinc-400", children: "--" });
|
|
212
|
-
}, [isCalculatePending, totalPrice,
|
|
224
|
+
}, [isCalculatePending, totalPrice, formatCurrencyNumber, translate]);
|
|
213
225
|
if (isExternalContracting) {
|
|
214
226
|
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(DialogContent, { className: "flex flex-col p-0 gap-0 w-full md:w-[410px] lg:w-[410px]", children: [
|
|
215
227
|
/* @__PURE__ */ jsxs(DialogHeader, { className: "gap-3 text-left p-5", children: [
|
|
@@ -284,7 +296,7 @@ function BuyCreditsModal() {
|
|
|
284
296
|
/* @__PURE__ */ jsxs("div", { className: "hidden lg:flex flex-col gap-2", children: [
|
|
285
297
|
immediateChargeTotal !== null && immediateChargeTotal > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
286
298
|
/* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-medium text-zinc-500", children: translate("common.credits.buy.immediateCharge") }),
|
|
287
|
-
isCalculatePending ? /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-20" }) : /* @__PURE__ */ jsx("span", { className: "paragraph-small-semibold text-zinc-950", children:
|
|
299
|
+
isCalculatePending ? /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-20" }) : /* @__PURE__ */ jsx("span", { className: "paragraph-small-semibold text-zinc-950", children: formatCurrencyNumber(immediateChargeTotal) })
|
|
288
300
|
] }),
|
|
289
301
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
290
302
|
/* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-zinc-500", children: translate("common.credits.buy.totalPlanValue") }),
|
|
@@ -308,7 +320,7 @@ function BuyCreditsModal() {
|
|
|
308
320
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
309
321
|
immediateChargeTotal !== null && immediateChargeTotal > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
310
322
|
/* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-medium text-zinc-500", children: translate("common.credits.buy.immediateCharge") }),
|
|
311
|
-
isCalculatePending ? /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-20" }) : /* @__PURE__ */ jsx("span", { className: "paragraph-small-semibold text-zinc-950", children:
|
|
323
|
+
isCalculatePending ? /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-20" }) : /* @__PURE__ */ jsx("span", { className: "paragraph-small-semibold text-zinc-950", children: formatCurrencyNumber(immediateChargeTotal) })
|
|
312
324
|
] }),
|
|
313
325
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
314
326
|
/* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-zinc-500", children: translate("common.credits.buy.totalPlanValue") }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/modals/BuyCreditsModal.tsx"],"sourcesContent":["'use client';\n\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '../ui/overlay/Dialog';\nimport { Button } from '../ui/buttons/Button';\nimport { Separator } from '../ui/data-display/Separator';\nimport { Toast } from '../ui/feedback/Toast';\nimport { SelectField } from '../ui/form/SelectField';\nimport { Skeleton } from '../ui/feedback/Skeleton';\nimport { PaymentInfoCard } from '../ui/data-display/PaymentInfoCard';\nimport { ADDON_IDS } from '../../modules/subscriptions/constants/addons.constants';\nimport {\n AI_CREDIT_OPTIONS,\n AI_CREDIT_VALUES,\n} from '../../modules/subscriptions/constants/addons.constants';\nimport { useBuyCreditsModal } from '../../store/useBuyCreditsModal';\nimport { useModalManager } from '../../store/useModalManager';\nimport { useCards } from '../../modules/cards/hooks/cards.hook';\nimport { useActiveSubscription } from '../../modules/subscriptions/hooks/find-active-subscription.hook';\nimport { useCalculateSubscription } from '../../modules/subscriptions/hooks/calculate-subscription.hook';\nimport { useUpdateSubscriptionPlan } from '../../modules/subscriptions/hooks/update-subscription-plan.hook';\nimport { getPriceFromCalculatedData } from '../../modules/subscriptions/utils/periodicity';\nimport { useIsDefaultWhitelabel, useExternalContracting } from '../../providers/whitelabel.provider';\nimport { IconInfoCircle, IconX, IconLoader2, IconSparkles } from '@tabler/icons-react';\nimport { useTranslations } from 'next-intl';\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport z from 'zod';\nimport { toast } from 'sonner';\n\nconst buyCreditSchema = z.object({\n creditOption: z.number().refine((val) => AI_CREDIT_VALUES.has(val), {\n message: 'Selecione uma opção de créditos',\n }),\n selectedCardId: z.string().min(1, 'Selecione um cartão'),\n});\n\ntype BuyCreditFormData = z.infer<typeof buyCreditSchema>;\n\nexport default function BuyCreditsModal() {\n const translate = useTranslations();\n const { open, closeModal } = useBuyCreditsModal();\n const { openModal } = useModalManager();\n const isDefaultWl = useIsDefaultWhitelabel();\n const { isExternalContracting, redirectToExternal } = useExternalContracting();\n const { data: cardsData } = useCards();\n const { data: subscriptionData } = useActiveSubscription();\n const subscription = subscriptionData?.data?.[0] ?? null;\n const defaultCardId =\n cardsData?.data?.find((card) => card.is_default)?.id?.toString() ??\n cardsData?.data?.[0]?.id?.toString() ??\n '';\n\n const {\n mutate: calculate,\n isPending: isCalculatePending,\n data: calculateData,\n } = useCalculateSubscription();\n const updatePlanMutation = useUpdateSubscriptionPlan();\n\n const aiItem = useMemo(\n () =>\n subscription?.items?.find(\n (item) => item.id_addon === ADDON_IDS.AI && (item.value ?? 0) > 0\n ),\n [subscription]\n );\n\n const currentCredits = aiItem?.quantity ?? 0;\n const subscriptionId = subscription?.id;\n const planId = subscription?.id_plan;\n\n const buildPlanExtras = useCallback(\n (aiQuantity: number) => {\n const aiOption = AI_CREDIT_OPTIONS.find(opt => opt.credits === aiQuantity);\n return [\n ...(subscription?.items ?? [])\n .filter((item) => item.id_addon != null && item.id_addon !== ADDON_IDS.AI)\n .map((item: any) => ({ \n id_addon: Number(item.id_addon), \n quantity: item.quantity ?? 0,\n value: Number(item.value ?? 0),\n price: item.price,\n })),\n { \n id_addon: ADDON_IDS.AI, \n quantity: aiQuantity,\n value: aiOption?.price ?? 0 \n },\n ];\n },\n [subscription]\n );\n\n const form = useForm<BuyCreditFormData>({\n resolver: zodResolver(buyCreditSchema),\n mode: 'onChange',\n defaultValues: {\n creditOption: currentCredits,\n selectedCardId: defaultCardId,\n },\n });\n\n const hasInitialized = useRef(false);\n\n useEffect(() => {\n if (!open) {\n hasInitialized.current = false;\n return;\n }\n\n if (hasInitialized.current || !subscription) return;\n hasInitialized.current = true;\n\n form.reset({\n creditOption: currentCredits,\n selectedCardId: defaultCardId,\n });\n\n calculate({\n id_product: 1,\n ...(planId != null && { id_plan: planId }),\n ...(subscriptionId != null && { id_subscription: subscriptionId }),\n plan_extras: buildPlanExtras(currentCredits),\n });\n }, [\n open,\n subscription,\n currentCredits,\n defaultCardId,\n form,\n calculate,\n planId,\n subscriptionId,\n buildPlanExtras,\n ]);\n\n // Sync selectedCardId when cards load after modal opened\n useEffect(() => {\n if (open && defaultCardId && !form.getValues('selectedCardId')) {\n form.setValue('selectedCardId', defaultCardId, { shouldValidate: true });\n }\n }, [open, defaultCardId, form]);\n\n const creditOption = form.watch('creditOption');\n const selectedCardId = form.watch('selectedCardId');\n const { isDirty, isSubmitting, isValid } = form.formState;\n\n\n const handleCreditOptionChange = useCallback(\n (value: string | number | boolean) => {\n const credits = Number(value);\n\n form.setValue('creditOption', credits, {\n shouldValidate: true,\n shouldDirty: true,\n });\n\n const option = AI_CREDIT_OPTIONS.find((opt) => opt.credits === credits);\n if (!option) return;\n\n calculate({\n id_product: 1,\n ...(planId != null && { id_plan: planId }),\n ...(subscriptionId != null && { id_subscription: subscriptionId }),\n plan_extras: buildPlanExtras(option.credits),\n });\n },\n [form, subscriptionId, planId, calculate, buildPlanExtras]\n );\n\n const calculatedData = calculateData?.data ?? null;\n\n const isIncreasingCredits = creditOption > currentCredits;\n const hasChanged = creditOption !== currentCredits;\n\n const immediateChargeTotal = calculatedData?.target?.immediate_charge?.total ?? null;\n\n const totalPrice = useMemo(() => {\n if (!calculatedData) return null;\n return getPriceFromCalculatedData(calculatedData, subscription?.periodicity);\n }, [calculatedData, subscription]);\n\n const formatPrice = useCallback((price: number) => {\n return price.toLocaleString('pt-BR', {\n style: 'currency',\n currency: 'BRL',\n });\n }, []);\n\n const isConfirmDisabled = !isDirty || !isValid || isCalculatePending || isSubmitting;\n\n const handleClose = useCallback(() => {\n form.reset();\n closeModal();\n }, [form, closeModal]);\n\n const handleOpenAddCardModal = useCallback(() => {\n openModal('addCardModal');\n }, [openModal]);\n\n const selectOptions = useMemo(() => {\n return AI_CREDIT_OPTIONS.map((opt) => ({\n value: opt.credits,\n label: (\n <div className=\"flex items-center justify-between gap-2 w-full\">\n <span className=\"paragraph-small-medium text-zinc-950 flex-1\">\n {translate('common.credits.buy.creditsOptionLabel', { credits: opt.credits.toLocaleString('pt-BR') })}\n </span>\n <span className=\"paragraph-small-medium text-zinc-500 opacity-70\">{opt.priceLabel}</span>\n </div>\n ),\n displayValue: translate('common.credits.buy.creditsOptionDisplayValue', { credits: opt.credits.toLocaleString('pt-BR') }),\n }));\n }, [translate]);\n\n const onSubmit = useCallback(\n async (data: BuyCreditFormData) => {\n if (!subscriptionId) return;\n\n const option = AI_CREDIT_OPTIONS.find((opt) => opt.credits === data.creditOption);\n if (!option) return;\n\n await updatePlanMutation.mutateAsync({\n subscriptionId,\n data: {\n plan_extras: buildPlanExtras(option.credits),\n },\n });\n\n toast.custom((t) => (\n <Toast\n variant=\"success\"\n message={translate('common.credits.buy.creditsChangedToast', { credits: data.creditOption.toLocaleString('pt-BR') })}\n toastId={t}\n />\n ));\n form.reset();\n closeModal();\n },\n [subscriptionId, updatePlanMutation, form, closeModal, buildPlanExtras]\n );\n\n const renderPriceDisplay = useCallback(() => {\n if (isCalculatePending) {\n return <Skeleton className=\"h-7 w-24\" />;\n }\n if (totalPrice !== null) {\n return (\n <span className=\"paragraph-xlarge-semibold text-zinc-950\">\n {totalPrice === 0 ? translate('common.credits.buy.defaultPrice') : formatPrice(totalPrice)}\n </span>\n );\n }\n return <span className=\"paragraph-xlarge-semibold text-zinc-400\">--</span>;\n }, [isCalculatePending, totalPrice, formatPrice]);\n\n if (isExternalContracting) {\n return (\n <Dialog open={open} 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 <IconSparkles 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 {translate('common.credits.buy.externalTitle')}\n </DialogTitle>\n <span className=\"paragraph-small-regular text-zinc-600\">\n {translate('common.credits.buy.externalDescription')}\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 closeModal();\n redirectToExternal('contracting');\n }}\n >\n {translate('common.credits.buy.externalButton')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n );\n }\n\n return (\n <Dialog open={open} 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-[433px] 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-hidden lg:overflow-y-auto\"\n >\n <DialogHeader className=\"px-4 py-3 border-b border-zinc-200 shrink-0 relative\">\n <DialogTitle className=\"paragraph-medium-semibold text-zinc-950 text-center\">\n {translate('common.credits.buy.title')}\n </DialogTitle>\n <button\n type=\"button\"\n onClick={handleClose}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 size-8 flex items-center justify-center rounded-lg cursor-pointer hover:bg-zinc-100 transition-colors\"\n >\n <IconX size={18} className=\"text-zinc-400\" />\n </button>\n </DialogHeader>\n\n <form\n onSubmit={form.handleSubmit(onSubmit)}\n className=\"flex flex-col flex-1 lg:flex-none overflow-hidden\"\n >\n <div className=\"flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none overflow-y-auto\">\n <div className=\"flex flex-col gap-4\">\n <SelectField\n options={selectOptions}\n value={creditOption}\n onChange={handleCreditOptionChange}\n className=\"h-12!\"\n />\n\n <PaymentInfoCard\n selectedCardId={selectedCardId}\n onCardSelect={(cardId) =>\n form.setValue('selectedCardId', cardId, { shouldValidate: true })\n }\n onOpenAddCard={handleOpenAddCardModal}\n />\n\n {hasChanged && (\n <div className={`flex items-center gap-3 p-4 rounded-lg ${isDefaultWl ? 'bg-cyan-50' : 'bg-zinc-50'}`}>\n <IconInfoCircle className=\"size-4 text-zinc-950 shrink-0\" />\n <span className=\"paragraph-small-regular text-zinc-950\">\n {isIncreasingCredits\n ? translate('common.credits.buy.paymentOnConfirm')\n : translate('common.credits.buy.nextInvoiceChanges')}\n </span>\n </div>\n )}\n </div>\n\n <Separator className=\"bg-zinc-200 hidden lg:block\" />\n\n <div className=\"hidden lg:flex flex-col gap-2\">\n {immediateChargeTotal !== null && immediateChargeTotal > 0 && (\n <div className=\"flex items-center justify-between\">\n <span className=\"paragraph-xsmall-medium text-zinc-500\">{translate('common.credits.buy.immediateCharge')}</span>\n {isCalculatePending ? (\n <Skeleton className=\"h-5 w-20\" />\n ) : (\n <span className=\"paragraph-small-semibold text-zinc-950\">\n {formatPrice(immediateChargeTotal)}\n </span>\n )}\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"paragraph-small-medium text-zinc-500\">{translate('common.credits.buy.totalPlanValue')}</span>\n {renderPriceDisplay()}\n </div>\n </div>\n\n <Button\n type=\"submit\"\n disabled={isConfirmDisabled}\n className=\"w-full hidden lg:inline-flex\"\n >\n {isSubmitting && <IconLoader2 className=\"size-4 animate-spin\" />}\n {translate('common.credits.buy.confirmAndPay')}\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-4 p-4 border-t border-zinc-200 shrink-0 lg:hidden\">\n <div className=\"flex flex-col gap-2\">\n {immediateChargeTotal !== null && immediateChargeTotal > 0 && (\n <div className=\"flex items-center justify-between\">\n <span className=\"paragraph-xsmall-medium text-zinc-500\">{translate('common.credits.buy.immediateCharge')}</span>\n {isCalculatePending ? (\n <Skeleton className=\"h-5 w-20\" />\n ) : (\n <span className=\"paragraph-small-semibold text-zinc-950\">\n {formatPrice(immediateChargeTotal)}\n </span>\n )}\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"paragraph-small-medium text-zinc-500\">{translate('common.credits.buy.totalPlanValue')}</span>\n {renderPriceDisplay()}\n </div>\n </div>\n <Button type=\"submit\" disabled={isConfirmDisabled} className=\"w-full\">\n {isSubmitting && <IconLoader2 className=\"size-4 animate-spin\" />}\n {translate('common.credits.buy.confirmAndPay')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/modals/BuyCreditsModal.tsx"],"sourcesContent":["'use client';\n\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '../ui/overlay/Dialog';\nimport { Button } from '../ui/buttons/Button';\nimport { Separator } from '../ui/data-display/Separator';\nimport { Toast } from '../ui/feedback/Toast';\nimport { SelectField } from '../ui/form/SelectField';\nimport { Skeleton } from '../ui/feedback/Skeleton';\nimport { PaymentInfoCard } from '../ui/data-display/PaymentInfoCard';\nimport {\n ADDON_IDS,\n AI_CREDIT_VALUES,\n buildAiCreditOptions,\n} from '../../modules/subscriptions/constants/addons.constants';\nimport { useBuyCreditsModal } from '../../store/useBuyCreditsModal';\nimport { useModalManager } from '../../store/useModalManager';\nimport { useCards } from '../../modules/cards/hooks/cards.hook';\nimport { useCurrencyFormatter } from '../../modules/accounts/hooks/use-currency-formatter.hook';\nimport { usePlanById } from '../../modules/plans/hooks/use-plan-by-id.hook';\nimport { useActiveSubscription } from '../../modules/subscriptions/hooks/find-active-subscription.hook';\nimport { useCalculateSubscription } from '../../modules/subscriptions/hooks/calculate-subscription.hook';\nimport { useUpdateSubscriptionPlan } from '../../modules/subscriptions/hooks/update-subscription-plan.hook';\nimport { getPriceFromCalculatedData } from '../../modules/subscriptions/utils/periodicity';\nimport { useIsDefaultWhitelabel, useExternalContracting } from '../../providers/whitelabel.provider';\nimport { IconInfoCircle, IconX, IconLoader2, IconSparkles } from '@tabler/icons-react';\nimport { useTranslations } from 'next-intl';\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport z from 'zod';\nimport { toast } from 'sonner';\n\nconst buyCreditSchema = z.object({\n creditOption: z.number().refine((val) => AI_CREDIT_VALUES.has(val), {\n message: 'Selecione uma opção de créditos',\n }),\n selectedCardId: z.string().min(1, 'Selecione um cartão'),\n});\n\ntype BuyCreditFormData = z.infer<typeof buyCreditSchema>;\n\nexport default function BuyCreditsModal() {\n const translate = useTranslations();\n const { open, closeModal } = useBuyCreditsModal();\n const { openModal } = useModalManager();\n const isDefaultWl = useIsDefaultWhitelabel();\n const { isExternalContracting, redirectToExternal } = useExternalContracting();\n const { data: cardsData } = useCards();\n const { locale, formatCurrencyNumber } = useCurrencyFormatter();\n const { data: subscriptionData } = useActiveSubscription();\n const subscription = subscriptionData?.data?.[0] ?? null;\n const defaultCardId =\n cardsData?.data?.find((card) => card.is_default)?.id?.toString() ??\n cardsData?.data?.[0]?.id?.toString() ??\n '';\n\n const {\n mutate: calculate,\n isPending: isCalculatePending,\n data: calculateData,\n } = useCalculateSubscription();\n const updatePlanMutation = useUpdateSubscriptionPlan();\n\n const aiItem = useMemo(\n () =>\n subscription?.items?.find(\n (item) => item.id_addon === ADDON_IDS.AI && (item.value ?? 0) > 0\n ),\n [subscription]\n );\n\n const currentCredits = aiItem?.quantity ?? 0;\n const subscriptionId = subscription?.id;\n const planId = subscription?.id_plan;\n\n const { data: planData } = usePlanById(planId);\n const perCreditPrice = useMemo(() => {\n const planAiItem = planData?.data?.items?.find(\n (item) => item.id_addon === ADDON_IDS.AI && (item.value ?? 0) > 0\n );\n return planAiItem?.value ?? aiItem?.value ?? 0;\n }, [planData, aiItem]);\n\n const hasPerCreditPrice = perCreditPrice > 0;\n\n const aiCreditOptions = useMemo(() => buildAiCreditOptions(perCreditPrice), [perCreditPrice]);\n\n const buildPlanExtras = useCallback(\n (aiQuantity: number) => {\n return [\n ...(subscription?.items ?? [])\n .filter((item) => item.id_addon != null && item.id_addon !== ADDON_IDS.AI)\n .map((item: any) => ({ \n id_addon: Number(item.id_addon), \n quantity: item.quantity ?? 0,\n value: Number(item.value ?? 0),\n price: item.price,\n })),\n {\n id_addon: ADDON_IDS.AI,\n quantity: aiQuantity,\n },\n ];\n },\n [subscription]\n );\n\n const form = useForm<BuyCreditFormData>({\n resolver: zodResolver(buyCreditSchema),\n mode: 'onChange',\n defaultValues: {\n creditOption: currentCredits,\n selectedCardId: defaultCardId,\n },\n });\n\n const hasInitialized = useRef(false);\n\n useEffect(() => {\n if (!open) {\n hasInitialized.current = false;\n return;\n }\n\n if (hasInitialized.current || !subscription) return;\n hasInitialized.current = true;\n\n form.reset({\n creditOption: currentCredits,\n selectedCardId: defaultCardId,\n });\n\n calculate({\n id_product: 1,\n ...(planId != null && { id_plan: planId }),\n ...(subscriptionId != null && { id_subscription: subscriptionId }),\n plan_extras: buildPlanExtras(currentCredits),\n });\n }, [\n open,\n subscription,\n currentCredits,\n defaultCardId,\n form,\n calculate,\n planId,\n subscriptionId,\n buildPlanExtras,\n ]);\n\n // Sync selectedCardId when cards load after modal opened\n useEffect(() => {\n if (open && defaultCardId && !form.getValues('selectedCardId')) {\n form.setValue('selectedCardId', defaultCardId, { shouldValidate: true });\n }\n }, [open, defaultCardId, form]);\n\n const creditOption = form.watch('creditOption');\n const selectedCardId = form.watch('selectedCardId');\n const { isDirty, isSubmitting, isValid } = form.formState;\n\n\n const handleCreditOptionChange = useCallback(\n (value: string | number | boolean) => {\n const credits = Number(value);\n\n form.setValue('creditOption', credits, {\n shouldValidate: true,\n shouldDirty: true,\n });\n\n if (!AI_CREDIT_VALUES.has(credits)) return;\n\n calculate({\n id_product: 1,\n ...(planId != null && { id_plan: planId }),\n ...(subscriptionId != null && { id_subscription: subscriptionId }),\n plan_extras: buildPlanExtras(credits),\n });\n },\n [form, subscriptionId, planId, calculate, buildPlanExtras]\n );\n\n const calculatedData = calculateData?.data ?? null;\n\n const isIncreasingCredits = creditOption > currentCredits;\n const hasChanged = creditOption !== currentCredits;\n\n const immediateChargeTotal = calculatedData?.target?.immediate_charge?.total ?? null;\n\n const totalPrice = useMemo(() => {\n if (!calculatedData) return null;\n return getPriceFromCalculatedData(calculatedData, subscription?.periodicity);\n }, [calculatedData, subscription]);\n\n const isConfirmDisabled =\n !isDirty || !isValid || isCalculatePending || isSubmitting || !hasPerCreditPrice;\n\n const handleClose = useCallback(() => {\n form.reset();\n closeModal();\n }, [form, closeModal]);\n\n const handleOpenAddCardModal = useCallback(() => {\n openModal('addCardModal');\n }, [openModal]);\n\n const selectOptions = useMemo(() => {\n return aiCreditOptions.map((opt) => ({\n value: opt.credits,\n label: (\n <div className=\"flex items-center justify-between gap-2 w-full\">\n <span className=\"paragraph-small-medium text-zinc-950 flex-1\">\n {translate('common.credits.buy.creditsOptionLabel', { credits: opt.credits.toLocaleString(locale) })}\n </span>\n {hasPerCreditPrice && (\n <span className=\"paragraph-small-medium text-zinc-500 opacity-70\">{formatCurrencyNumber(opt.price)}</span>\n )}\n </div>\n ),\n displayValue: translate('common.credits.buy.creditsOptionDisplayValue', { credits: opt.credits.toLocaleString(locale) }),\n }));\n }, [aiCreditOptions, translate, locale, formatCurrencyNumber, hasPerCreditPrice]);\n\n const onSubmit = useCallback(\n async (data: BuyCreditFormData) => {\n if (!subscriptionId) return;\n if (!hasPerCreditPrice) return;\n if (!AI_CREDIT_VALUES.has(data.creditOption)) return;\n\n await updatePlanMutation.mutateAsync({\n subscriptionId,\n data: {\n plan_extras: buildPlanExtras(data.creditOption),\n },\n });\n\n toast.custom((t) => (\n <Toast\n variant=\"success\"\n message={translate('common.credits.buy.creditsChangedToast', { credits: data.creditOption.toLocaleString(locale) })}\n toastId={t}\n />\n ));\n form.reset();\n closeModal();\n },\n [\n subscriptionId,\n hasPerCreditPrice,\n updatePlanMutation,\n form,\n closeModal,\n buildPlanExtras,\n translate,\n locale,\n ]\n );\n\n const renderPriceDisplay = useCallback(() => {\n if (isCalculatePending) {\n return <Skeleton className=\"h-7 w-24\" />;\n }\n if (totalPrice !== null) {\n return (\n <span className=\"paragraph-xlarge-semibold text-zinc-950\">\n {totalPrice === 0 ? translate('common.credits.buy.defaultPrice') : formatCurrencyNumber(totalPrice)}\n </span>\n );\n }\n return <span className=\"paragraph-xlarge-semibold text-zinc-400\">--</span>;\n }, [isCalculatePending, totalPrice, formatCurrencyNumber, translate]);\n\n if (isExternalContracting) {\n return (\n <Dialog open={open} 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 <IconSparkles 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 {translate('common.credits.buy.externalTitle')}\n </DialogTitle>\n <span className=\"paragraph-small-regular text-zinc-600\">\n {translate('common.credits.buy.externalDescription')}\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 closeModal();\n redirectToExternal('contracting');\n }}\n >\n {translate('common.credits.buy.externalButton')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n );\n }\n\n return (\n <Dialog open={open} 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-[433px] 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-hidden lg:overflow-y-auto\"\n >\n <DialogHeader className=\"px-4 py-3 border-b border-zinc-200 shrink-0 relative\">\n <DialogTitle className=\"paragraph-medium-semibold text-zinc-950 text-center\">\n {translate('common.credits.buy.title')}\n </DialogTitle>\n <button\n type=\"button\"\n onClick={handleClose}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 size-8 flex items-center justify-center rounded-lg cursor-pointer hover:bg-zinc-100 transition-colors\"\n >\n <IconX size={18} className=\"text-zinc-400\" />\n </button>\n </DialogHeader>\n\n <form\n onSubmit={form.handleSubmit(onSubmit)}\n className=\"flex flex-col flex-1 lg:flex-none overflow-hidden\"\n >\n <div className=\"flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none overflow-y-auto\">\n <div className=\"flex flex-col gap-4\">\n <SelectField\n options={selectOptions}\n value={creditOption}\n onChange={handleCreditOptionChange}\n className=\"h-12!\"\n />\n\n <PaymentInfoCard\n selectedCardId={selectedCardId}\n onCardSelect={(cardId) =>\n form.setValue('selectedCardId', cardId, { shouldValidate: true })\n }\n onOpenAddCard={handleOpenAddCardModal}\n />\n\n {hasChanged && (\n <div className={`flex items-center gap-3 p-4 rounded-lg ${isDefaultWl ? 'bg-cyan-50' : 'bg-zinc-50'}`}>\n <IconInfoCircle className=\"size-4 text-zinc-950 shrink-0\" />\n <span className=\"paragraph-small-regular text-zinc-950\">\n {isIncreasingCredits\n ? translate('common.credits.buy.paymentOnConfirm')\n : translate('common.credits.buy.nextInvoiceChanges')}\n </span>\n </div>\n )}\n </div>\n\n <Separator className=\"bg-zinc-200 hidden lg:block\" />\n\n <div className=\"hidden lg:flex flex-col gap-2\">\n {immediateChargeTotal !== null && immediateChargeTotal > 0 && (\n <div className=\"flex items-center justify-between\">\n <span className=\"paragraph-xsmall-medium text-zinc-500\">{translate('common.credits.buy.immediateCharge')}</span>\n {isCalculatePending ? (\n <Skeleton className=\"h-5 w-20\" />\n ) : (\n <span className=\"paragraph-small-semibold text-zinc-950\">\n {formatCurrencyNumber(immediateChargeTotal)}\n </span>\n )}\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"paragraph-small-medium text-zinc-500\">{translate('common.credits.buy.totalPlanValue')}</span>\n {renderPriceDisplay()}\n </div>\n </div>\n\n <Button\n type=\"submit\"\n disabled={isConfirmDisabled}\n className=\"w-full hidden lg:inline-flex\"\n >\n {isSubmitting && <IconLoader2 className=\"size-4 animate-spin\" />}\n {translate('common.credits.buy.confirmAndPay')}\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-4 p-4 border-t border-zinc-200 shrink-0 lg:hidden\">\n <div className=\"flex flex-col gap-2\">\n {immediateChargeTotal !== null && immediateChargeTotal > 0 && (\n <div className=\"flex items-center justify-between\">\n <span className=\"paragraph-xsmall-medium text-zinc-500\">{translate('common.credits.buy.immediateCharge')}</span>\n {isCalculatePending ? (\n <Skeleton className=\"h-5 w-20\" />\n ) : (\n <span className=\"paragraph-small-semibold text-zinc-950\">\n {formatCurrencyNumber(immediateChargeTotal)}\n </span>\n )}\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"paragraph-small-medium text-zinc-500\">{translate('common.credits.buy.totalPlanValue')}</span>\n {renderPriceDisplay()}\n </div>\n </div>\n <Button type=\"submit\" disabled={isConfirmDisabled} className=\"w-full\">\n {isSubmitting && <IconLoader2 className=\"size-4 animate-spin\" />}\n {translate('common.credits.buy.confirmAndPay')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -24,7 +24,7 @@ const COUNTRY_OPTIONS = COUNTRIES.map((country) => ({
|
|
|
24
24
|
value: country.iso,
|
|
25
25
|
label: country.name
|
|
26
26
|
}));
|
|
27
|
-
function buildBillingDataSchema(
|
|
27
|
+
function buildBillingDataSchema(translate) {
|
|
28
28
|
const requiredText = z.string().trim().min(1, translate("common.billing.requiredData.requiredField"));
|
|
29
29
|
const base = z.object({
|
|
30
30
|
personType: z.enum(["pf", "pj"]),
|
|
@@ -40,7 +40,6 @@ function buildBillingDataSchema(isInternational, translate) {
|
|
|
40
40
|
state: requiredText,
|
|
41
41
|
country: requiredText
|
|
42
42
|
});
|
|
43
|
-
if (isInternational) return base;
|
|
44
43
|
return base.superRefine((data, ctx) => {
|
|
45
44
|
if (!data.streetNumber) {
|
|
46
45
|
ctx.addIssue({
|
|
@@ -90,14 +89,14 @@ function formatDocument(document, isCompany) {
|
|
|
90
89
|
function RequiredBillingDataModalContent() {
|
|
91
90
|
const translate = useTranslations();
|
|
92
91
|
const { user } = useAuth();
|
|
93
|
-
const { isRequired, account
|
|
92
|
+
const { isRequired, account } = useRequiredBillingData();
|
|
94
93
|
const { mutateAsync: updateBillingData } = useUpdateBillingData();
|
|
95
94
|
const { mutateAsync: lookupCep } = useViaCep();
|
|
96
95
|
const [isSubmitted, setIsSubmitted] = useState(false);
|
|
97
96
|
const isPrefilled = useRef(false);
|
|
98
97
|
const schema = useMemo(
|
|
99
|
-
() => buildBillingDataSchema(
|
|
100
|
-
[
|
|
98
|
+
() => buildBillingDataSchema(translate),
|
|
99
|
+
[translate]
|
|
101
100
|
);
|
|
102
101
|
const form = useForm({
|
|
103
102
|
resolver: zodResolver(schema),
|
|
@@ -144,9 +143,9 @@ function RequiredBillingDataModalContent() {
|
|
|
144
143
|
neighborhood: account.neighborhood ?? "",
|
|
145
144
|
city: account.city ?? "",
|
|
146
145
|
state: account.state ?? "",
|
|
147
|
-
country: account.country ||
|
|
146
|
+
country: account.country || "BR"
|
|
148
147
|
});
|
|
149
|
-
}, [account,
|
|
148
|
+
}, [account, reset, user?.email]);
|
|
150
149
|
const handleDocumentChange = (rawValue) => {
|
|
151
150
|
setValue("document", isCompany ? formatCNPJ(rawValue) : formatCPF(rawValue), {
|
|
152
151
|
shouldValidate: true
|
|
@@ -158,10 +157,6 @@ function RequiredBillingDataModalContent() {
|
|
|
158
157
|
setValue("personType", value, { shouldValidate: true });
|
|
159
158
|
};
|
|
160
159
|
const handleCepChange = (rawValue) => {
|
|
161
|
-
if (isInternational) {
|
|
162
|
-
setValue("cep", rawValue, { shouldValidate: true });
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
160
|
setValue("cep", formatPostalCode(rawValue), { shouldValidate: true });
|
|
166
161
|
const digits = rawValue.replace(/\D/g, "");
|
|
167
162
|
if (digits.length !== 8) return;
|
|
@@ -175,10 +170,8 @@ function RequiredBillingDataModalContent() {
|
|
|
175
170
|
};
|
|
176
171
|
async function onSubmit(data) {
|
|
177
172
|
const payload = {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
financial_document: data.document
|
|
181
|
-
},
|
|
173
|
+
financial_document_type: data.personType === "pf" ? 1 : 2,
|
|
174
|
+
financial_document: data.document,
|
|
182
175
|
financial_name: data.financialName,
|
|
183
176
|
financial_email: data.financialEmail,
|
|
184
177
|
zipcode: data.cep,
|
|
@@ -238,7 +231,7 @@ function RequiredBillingDataModalContent() {
|
|
|
238
231
|
children: [
|
|
239
232
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 p-4 lg:p-5 flex-1 overflow-y-auto", children: [
|
|
240
233
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
|
|
241
|
-
|
|
234
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
242
235
|
/* @__PURE__ */ jsx(
|
|
243
236
|
Controller,
|
|
244
237
|
{
|
|
@@ -307,8 +300,8 @@ function RequiredBillingDataModalContent() {
|
|
|
307
300
|
/* @__PURE__ */ jsx(
|
|
308
301
|
FormField,
|
|
309
302
|
{
|
|
310
|
-
label:
|
|
311
|
-
placeholder:
|
|
303
|
+
label: translate("common.billing.requiredData.cep"),
|
|
304
|
+
placeholder: "_____-___",
|
|
312
305
|
error: !!errors.cep,
|
|
313
306
|
errorMessage: errors.cep?.message,
|
|
314
307
|
...register("cep", {
|
|
@@ -327,7 +320,7 @@ function RequiredBillingDataModalContent() {
|
|
|
327
320
|
...register("street")
|
|
328
321
|
}
|
|
329
322
|
) }),
|
|
330
|
-
|
|
323
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
331
324
|
FormField,
|
|
332
325
|
{
|
|
333
326
|
label: translate("common.billing.requiredData.streetNumber"),
|
|
@@ -339,7 +332,7 @@ function RequiredBillingDataModalContent() {
|
|
|
339
332
|
) })
|
|
340
333
|
] }),
|
|
341
334
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
342
|
-
|
|
335
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
343
336
|
FormField,
|
|
344
337
|
{
|
|
345
338
|
label: translate("common.billing.requiredData.neighborhood"),
|
|
@@ -370,16 +363,7 @@ function RequiredBillingDataModalContent() {
|
|
|
370
363
|
...register("city")
|
|
371
364
|
}
|
|
372
365
|
) }),
|
|
373
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1", children:
|
|
374
|
-
FormField,
|
|
375
|
-
{
|
|
376
|
-
label: translate("common.billing.requiredData.stateProvince"),
|
|
377
|
-
placeholder: translate("common.billing.requiredData.typeHere"),
|
|
378
|
-
error: !!errors.state,
|
|
379
|
-
errorMessage: errors.state?.message,
|
|
380
|
-
...register("state")
|
|
381
|
-
}
|
|
382
|
-
) : /* @__PURE__ */ jsx(
|
|
366
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
383
367
|
Controller,
|
|
384
368
|
{
|
|
385
369
|
name: "state",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/modals/billing/RequiredBillingDataModal.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any -- o generic Translator do next-intl não é\n expressável estruturalmente; o `any` do TranslateFn é intencional e está contido neste arquivo\n (mesmo padrão de modules/plans/utils/map-api-plan-to-ui.ts). */\n'use client';\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { Controller, useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { useTranslations } from 'next-intl';\nimport { toast } from 'sonner';\nimport z from 'zod';\nimport { IconFileInvoice } from '@tabler/icons-react';\n\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/overlay/Dialog';\nimport { Button } from '../../ui/buttons/Button';\nimport { FormField } from '../../ui/form/FormField';\nimport { SelectField } from '../../ui/form/SelectField';\nimport { Toast } from '../../ui/feedback/Toast';\nimport { useAuth } from '../../../providers/auth.provider';\nimport { useViaCep } from '../../../modules/accounts/hooks/useViaCep';\nimport { useUpdateBillingData } from '../../../modules/accounts/hooks/useAccountManagement';\nimport { useRequiredBillingData } from '../../../modules/accounts/hooks/use-required-billing-data.hook';\nimport { COUNTRIES } from '../../../utils/countries';\nimport { BR_STATE_OPTIONS } from '../../../utils/constants/br-states';\nimport { formatCNPJ, formatCPF, formatPostalCode } from '../../../utils/format/masks';\nimport { isValidCNPJ, isValidCPF } from '../../../utils/validators/common';\nimport type { UpdateBillingDataRequest } from '../../../modules/accounts/types';\n\ntype TranslateFn = (key: any, values?: Record<string, string | number>) => string;\n\ntype BillingDataFormValues = {\n personType: 'pf' | 'pj';\n document: string;\n financialName: string;\n financialEmail: string;\n cep: string;\n street: string;\n streetNumber: string;\n complement?: string;\n neighborhood: string;\n city: string;\n state: string;\n country: string;\n};\n\nconst COUNTRY_OPTIONS = COUNTRIES.map((country: { iso: string; name: string }) => ({\n value: country.iso,\n label: country.name,\n}));\n\nfunction buildBillingDataSchema(isInternational: boolean, translate: TranslateFn) {\n const requiredText = z\n .string()\n .trim()\n .min(1, translate('common.billing.requiredData.requiredField'));\n\n const base = z.object({\n personType: z.enum(['pf', 'pj']),\n document: z.string().trim(),\n financialName: requiredText,\n financialEmail: z\n .string()\n .trim()\n .min(1, translate('common.billing.requiredData.requiredField'))\n .email(translate('common.billing.requiredData.invalidEmail')),\n cep: requiredText,\n street: requiredText,\n streetNumber: z.string().trim(),\n complement: z.string().trim().optional(),\n neighborhood: z.string().trim(),\n city: requiredText,\n state: requiredText,\n country: requiredText,\n });\n\n /* Conta internacional não tem CPF/CNPJ, número nem bairro — mesmo recorte do\n * AddCardModal. Exigir esses campos aqui trancaria o cliente fora do app, já que\n * o modal é bloqueante. */\n if (isInternational) return base;\n\n return base.superRefine((data, ctx) => {\n if (!data.streetNumber) {\n ctx.addIssue({\n code: 'custom',\n path: ['streetNumber'],\n message: translate('common.billing.requiredData.requiredField'),\n });\n }\n\n if (!data.neighborhood) {\n ctx.addIssue({\n code: 'custom',\n path: ['neighborhood'],\n message: translate('common.billing.requiredData.requiredField'),\n });\n }\n\n if (data.cep.replace(/\\D/g, '').length !== 8) {\n ctx.addIssue({\n code: 'custom',\n path: ['cep'],\n message: translate('common.billing.requiredData.invalidCep'),\n });\n }\n\n const isCompany = data.personType === 'pj';\n const digits = data.document.replace(/\\D/g, '');\n\n if (digits.length !== (isCompany ? 14 : 11)) {\n ctx.addIssue({\n code: 'custom',\n path: ['document'],\n message: isCompany\n ? translate('common.billing.requiredData.invalidCnpj')\n : translate('common.billing.requiredData.invalidCpf'),\n });\n return;\n }\n\n /* Dígito verificador, mesmo algoritmo do backend: sem isso 111.111.111-11 passa\n * aqui e o PUT da conta é recusado depois, sem o usuário saber o motivo. */\n const isValid = isCompany ? isValidCNPJ(digits) : isValidCPF(digits);\n if (!isValid) {\n ctx.addIssue({\n code: 'custom',\n path: ['document'],\n message: isCompany\n ? translate('common.billing.requiredData.invalidCnpjChecksum')\n : translate('common.billing.requiredData.invalidCpfChecksum'),\n });\n }\n });\n}\n\nfunction formatDocument(document: string | null | undefined, isCompany: boolean) {\n if (!document) return '';\n return isCompany ? formatCNPJ(document) : formatCPF(document);\n}\n\nfunction RequiredBillingDataModalContent() {\n const translate = useTranslations();\n const { user } = useAuth();\n const { isRequired, account, isInternational } = useRequiredBillingData();\n const { mutateAsync: updateBillingData } = useUpdateBillingData();\n const { mutateAsync: lookupCep } = useViaCep();\n\n /* O modal fecha na hora do submit e não reabre enquanto o invalidate do account\n * não volta — sem isso o usuário vê o modal piscar de novo com o cache antigo. */\n const [isSubmitted, setIsSubmitted] = useState(false);\n const isPrefilled = useRef(false);\n\n const schema = useMemo(\n () => buildBillingDataSchema(isInternational, translate),\n [isInternational, translate],\n );\n\n const form = useForm<BillingDataFormValues>({\n resolver: zodResolver(schema),\n mode: 'onChange',\n defaultValues: {\n personType: 'pf',\n document: '',\n financialName: '',\n financialEmail: '',\n cep: '',\n street: '',\n streetNumber: '',\n complement: '',\n neighborhood: '',\n city: '',\n state: '',\n country: 'BR',\n },\n });\n\n const {\n register,\n control,\n handleSubmit,\n reset,\n setValue,\n watch,\n formState: { errors, isSubmitting, isValid },\n } = form;\n\n const personType = watch('personType');\n const isCompany = personType === 'pj';\n\n useEffect(() => {\n if (!account || isPrefilled.current) return;\n isPrefilled.current = true;\n\n const accountIsCompany = account.financial_document_type === 2;\n reset({\n personType: accountIsCompany ? 'pj' : 'pf',\n document: formatDocument(account.financial_document, accountIsCompany),\n financialName: account.financial_name ?? '',\n financialEmail: account.financial_email || user?.email || '',\n cep: account.zipcode ?? '',\n street: account.address ?? '',\n streetNumber: account.address_number ?? '',\n complement: account.address_complement ?? '',\n neighborhood: account.neighborhood ?? '',\n city: account.city ?? '',\n state: account.state ?? '',\n country: account.country || (isInternational ? 'US' : 'BR'),\n });\n }, [account, isInternational, reset, user?.email]);\n\n const handleDocumentChange = (rawValue: string) => {\n setValue('document', isCompany ? formatCNPJ(rawValue) : formatCPF(rawValue), {\n shouldValidate: true,\n });\n };\n\n const handlePersonTypeChange = (value: 'pf' | 'pj') => {\n if (value === personType) return;\n setValue('document', '');\n /* shouldValidate reroda o resolver: sem isso o `isValid` fica preso no valor\n * do documento antigo e o botão continua habilitado com o campo já limpo. */\n setValue('personType', value, { shouldValidate: true });\n };\n\n const handleCepChange = (rawValue: string) => {\n if (isInternational) {\n setValue('cep', rawValue, { shouldValidate: true });\n return;\n }\n\n setValue('cep', formatPostalCode(rawValue), { shouldValidate: true });\n\n const digits = rawValue.replace(/\\D/g, '');\n if (digits.length !== 8) return;\n\n lookupCep(digits)\n .then((data) => {\n setValue('street', data.logradouro, { shouldValidate: true });\n setValue('neighborhood', data.bairro, { shouldValidate: true });\n setValue('city', data.localidade, { shouldValidate: true });\n setValue('state', data.uf, { shouldValidate: true });\n })\n .catch(() => {\n // CEP não encontrado: o usuário preenche o endereço na mão.\n });\n };\n\n async function onSubmit(data: BillingDataFormValues) {\n const payload: UpdateBillingDataRequest = {\n ...(isInternational\n ? {}\n : {\n financial_document_type: data.personType === 'pf' ? 1 : 2,\n financial_document: data.document,\n }),\n financial_name: data.financialName,\n financial_email: data.financialEmail,\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 country: data.country,\n };\n\n try {\n await updateBillingData(payload);\n setIsSubmitted(true);\n toast.custom(\n (t) => (\n <Toast\n variant=\"success\"\n message={translate('common.billing.requiredData.successToast')}\n toastId={t}\n />\n ),\n { duration: 5000 },\n );\n } catch (error) {\n /* O backend devolve a razão exata da recusa (ex.: documento fiscal inválido).\n * Trocar por um texto genérico deixaria o usuário travado sem saber o que corrigir. */\n const message =\n error instanceof Error && error.message\n ? error.message\n : translate('common.billing.requiredData.errorToast');\n toast.custom((t) => <Toast variant=\"error\" message={message} toastId={t} />, {\n duration: 5000,\n });\n }\n }\n\n if (!isRequired || isSubmitted) return null;\n\n return (\n <Dialog open onOpenChange={() => {}}>\n <DialogContent\n showCloseButton={false}\n onEscapeKeyDown={(event: Event) => event.preventDefault()}\n onPointerDownOutside={(event: Event) => event.preventDefault()}\n onInteractOutside={(event: Event) => event.preventDefault()}\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-[502px] 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-hidden\"\n >\n <DialogHeader className=\"gap-3 text-left p-4 lg:p-5 border-b border-zinc-200 shrink-0\">\n <div className=\"flex items-center justify-center w-10 h-10 bg-zinc-50 rounded-lg\">\n <IconFileInvoice 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 {translate('common.billing.requiredData.title')}\n </DialogTitle>\n <span className=\"paragraph-small-regular text-zinc-600\">\n {translate('common.billing.requiredData.description')}\n </span>\n </div>\n </DialogHeader>\n\n <form\n onSubmit={handleSubmit(onSubmit)}\n className=\"flex flex-col flex-1 min-h-0\"\n noValidate\n >\n <div className=\"flex flex-col gap-6 p-4 lg:p-5 flex-1 overflow-y-auto\">\n <div className=\"flex flex-col gap-5\">\n {!isInternational && (\n <div className=\"flex gap-4\">\n <Controller\n name=\"personType\"\n control={control}\n render={({ field }) => (\n <SelectField\n label={translate('common.billing.requiredData.personType')}\n placeholder={translate('common.billing.requiredData.select')}\n value={field.value}\n onChange={(value) => handlePersonTypeChange(value as 'pf' | 'pj')}\n options={[\n {\n value: 'pf',\n label: translate('common.billing.requiredData.personTypePf'),\n },\n {\n value: 'pj',\n label: translate('common.billing.requiredData.personTypePj'),\n },\n ]}\n containerClassName=\"flex-1\"\n />\n )}\n />\n <div className=\"flex-1\">\n <FormField\n label={\n isCompany\n ? translate('common.billing.requiredData.cnpj')\n : translate('common.billing.requiredData.cpf')\n }\n placeholder={isCompany ? '__.___.___/____-__' : '___.___.___-__'}\n error={!!errors.document}\n errorMessage={errors.document?.message}\n {...register('document', {\n onChange: (event) => handleDocumentChange(event.target.value),\n })}\n />\n </div>\n </div>\n )}\n\n <FormField\n label={\n isCompany\n ? translate('common.billing.requiredData.companyName')\n : translate('common.billing.requiredData.fullName')\n }\n placeholder={translate('common.billing.requiredData.typeHere')}\n error={!!errors.financialName}\n errorMessage={errors.financialName?.message}\n {...register('financialName')}\n />\n\n <FormField\n label={translate('common.billing.requiredData.financialEmail')}\n placeholder={translate('common.billing.requiredData.financialEmailPlaceholder')}\n type=\"email\"\n error={!!errors.financialEmail}\n errorMessage={errors.financialEmail?.message}\n {...register('financialEmail')}\n />\n </div>\n\n <div className=\"h-px bg-zinc-200\" />\n\n <div className=\"flex flex-col gap-5\">\n <span className=\"paragraph-small-semibold text-zinc-950\">\n {translate('common.billing.requiredData.addressHeading')}\n </span>\n\n <FormField\n label={\n isInternational\n ? translate('common.billing.requiredData.postalCode')\n : translate('common.billing.requiredData.cep')\n }\n placeholder={isInternational ? '' : '_____-___'}\n error={!!errors.cep}\n errorMessage={errors.cep?.message}\n {...register('cep', {\n onChange: (event) => handleCepChange(event.target.value),\n })}\n />\n\n <div className=\"flex gap-4\">\n <div className=\"flex-[3]\">\n <FormField\n label={translate('common.billing.requiredData.street')}\n placeholder={translate('common.billing.requiredData.typeHere')}\n error={!!errors.street}\n errorMessage={errors.street?.message}\n {...register('street')}\n />\n </div>\n {!isInternational && (\n <div className=\"flex-1\">\n <FormField\n label={translate('common.billing.requiredData.streetNumber')}\n placeholder=\"000\"\n error={!!errors.streetNumber}\n errorMessage={errors.streetNumber?.message}\n {...register('streetNumber')}\n />\n </div>\n )}\n </div>\n\n <div className=\"flex gap-4\">\n {!isInternational && (\n <div className=\"flex-1\">\n <FormField\n label={translate('common.billing.requiredData.neighborhood')}\n placeholder={translate('common.billing.requiredData.typeHere')}\n error={!!errors.neighborhood}\n errorMessage={errors.neighborhood?.message}\n {...register('neighborhood')}\n />\n </div>\n )}\n <div className=\"flex-1\">\n <FormField\n label={translate('common.billing.requiredData.complement')}\n optional\n placeholder={translate('common.billing.requiredData.typeHere')}\n {...register('complement')}\n />\n </div>\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <FormField\n label={translate('common.billing.requiredData.city')}\n placeholder={translate('common.billing.requiredData.typeHere')}\n error={!!errors.city}\n errorMessage={errors.city?.message}\n {...register('city')}\n />\n </div>\n <div className=\"flex-1\">\n {isInternational ? (\n <FormField\n label={translate('common.billing.requiredData.stateProvince')}\n placeholder={translate('common.billing.requiredData.typeHere')}\n error={!!errors.state}\n errorMessage={errors.state?.message}\n {...register('state')}\n />\n ) : (\n <Controller\n name=\"state\"\n control={control}\n render={({ field }) => (\n <SelectField\n label={translate('common.billing.requiredData.state')}\n placeholder={translate('common.billing.requiredData.select')}\n value={field.value || undefined}\n onChange={(value) =>\n setValue('state', value as string, { shouldValidate: true })\n }\n options={BR_STATE_OPTIONS}\n error={!!errors.state}\n errorMessage={errors.state?.message}\n />\n )}\n />\n )}\n </div>\n </div>\n\n <Controller\n name=\"country\"\n control={control}\n render={({ field }) => (\n <SelectField\n label={translate('common.billing.requiredData.country')}\n placeholder={translate('common.billing.requiredData.select')}\n value={field.value || undefined}\n onChange={(value) =>\n setValue('country', value as string, { shouldValidate: true })\n }\n options={COUNTRY_OPTIONS}\n error={!!errors.country}\n errorMessage={errors.country?.message}\n />\n )}\n />\n </div>\n </div>\n\n <div className=\"flex items-center justify-end p-4 lg:p-5 border-t border-zinc-200 shrink-0\">\n <Button\n type=\"submit\"\n className=\"h-10! w-full lg:w-fit\"\n disabled={!isValid || isSubmitting}\n loading={isSubmitting}\n >\n {isSubmitting\n ? translate('common.billing.requiredData.submitting')\n : translate('common.billing.requiredData.submit')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * Modal bloqueante que exige o cadastro de cobrança/fiscal de contas que já\n * pagaram sem ele — sem esses dados a nota fiscal não é emitida. Monte uma vez\n * no layout raiz do app; ele mesmo decide se aparece (ver `useRequiredBillingData`).\n */\nexport default function RequiredBillingDataModal() {\n const { user, isLoading } = useAuth();\n\n /* Sem sessão não há conta nem assinatura pra consultar — evita disparar as\n * queries (e tomar 401) em /login, /register e demais rotas públicas. */\n if (isLoading || !user) return null;\n\n return <RequiredBillingDataModalContent />;\n}\n"],"mappings":";AA+QU,cAoCA,YApCA;AA1QV,SAAS,WAAW,SAAS,QAAQ,gBAAgB;AACrD,SAAS,YAAY,eAAe;AACpC,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,aAAa;AACtB,OAAO,OAAO;AACd,SAAS,uBAAuB;AAEhC,SAAS,QAAQ,eAAe,cAAc,mBAAmB;AACjE,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,aAAa;AACtB,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB;AAC1B,SAAS,wBAAwB;AACjC,SAAS,YAAY,WAAW,wBAAwB;AACxD,SAAS,aAAa,kBAAkB;AAoBxC,MAAM,kBAAkB,UAAU,IAAI,CAAC,aAA4C;AAAA,EACjF,OAAO,QAAQ;AAAA,EACf,OAAO,QAAQ;AACjB,EAAE;AAEF,SAAS,uBAAuB,iBAA0B,WAAwB;AAChF,QAAM,eAAe,EAClB,OAAO,EACP,KAAK,EACL,IAAI,GAAG,UAAU,2CAA2C,CAAC;AAEhE,QAAM,OAAO,EAAE,OAAO;AAAA,IACpB,YAAY,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;AAAA,IAC/B,UAAU,EAAE,OAAO,EAAE,KAAK;AAAA,IAC1B,eAAe;AAAA,IACf,gBAAgB,EACb,OAAO,EACP,KAAK,EACL,IAAI,GAAG,UAAU,2CAA2C,CAAC,EAC7D,MAAM,UAAU,0CAA0C,CAAC;AAAA,IAC9D,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,cAAc,EAAE,OAAO,EAAE,KAAK;AAAA,IAC9B,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,IACvC,cAAc,EAAE,OAAO,EAAE,KAAK;AAAA,IAC9B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,EACX,CAAC;AAKD,MAAI,gBAAiB,QAAO;AAE5B,SAAO,KAAK,YAAY,CAAC,MAAM,QAAQ;AACrC,QAAI,CAAC,KAAK,cAAc;AACtB,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,cAAc;AAAA,QACrB,SAAS,UAAU,2CAA2C;AAAA,MAChE,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,KAAK,cAAc;AACtB,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,cAAc;AAAA,QACrB,SAAS,UAAU,2CAA2C;AAAA,MAChE,CAAC;AAAA,IACH;AAEA,QAAI,KAAK,IAAI,QAAQ,OAAO,EAAE,EAAE,WAAW,GAAG;AAC5C,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,KAAK;AAAA,QACZ,SAAS,UAAU,wCAAwC;AAAA,MAC7D,CAAC;AAAA,IACH;AAEA,UAAM,YAAY,KAAK,eAAe;AACtC,UAAM,SAAS,KAAK,SAAS,QAAQ,OAAO,EAAE;AAE9C,QAAI,OAAO,YAAY,YAAY,KAAK,KAAK;AAC3C,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,UAAU;AAAA,QACjB,SAAS,YACL,UAAU,yCAAyC,IACnD,UAAU,wCAAwC;AAAA,MACxD,CAAC;AACD;AAAA,IACF;AAIA,UAAM,UAAU,YAAY,YAAY,MAAM,IAAI,WAAW,MAAM;AACnE,QAAI,CAAC,SAAS;AACZ,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,UAAU;AAAA,QACjB,SAAS,YACL,UAAU,iDAAiD,IAC3D,UAAU,gDAAgD;AAAA,MAChE,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,SAAS,eAAe,UAAqC,WAAoB;AAC/E,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,YAAY,WAAW,QAAQ,IAAI,UAAU,QAAQ;AAC9D;AAEA,SAAS,kCAAkC;AACzC,QAAM,YAAY,gBAAgB;AAClC,QAAM,EAAE,KAAK,IAAI,QAAQ;AACzB,QAAM,EAAE,YAAY,SAAS,gBAAgB,IAAI,uBAAuB;AACxE,QAAM,EAAE,aAAa,kBAAkB,IAAI,qBAAqB;AAChE,QAAM,EAAE,aAAa,UAAU,IAAI,UAAU;AAI7C,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,cAAc,OAAO,KAAK;AAEhC,QAAM,SAAS;AAAA,IACb,MAAM,uBAAuB,iBAAiB,SAAS;AAAA,IACvD,CAAC,iBAAiB,SAAS;AAAA,EAC7B;AAEA,QAAM,OAAO,QAA+B;AAAA,IAC1C,UAAU,YAAY,MAAM;AAAA,IAC5B,MAAM;AAAA,IACN,eAAe;AAAA,MACb,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS;AAAA,IACX;AAAA,EACF,CAAC;AAED,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,EAAE,QAAQ,cAAc,QAAQ;AAAA,EAC7C,IAAI;AAEJ,QAAM,aAAa,MAAM,YAAY;AACrC,QAAM,YAAY,eAAe;AAEjC,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,YAAY,QAAS;AACrC,gBAAY,UAAU;AAEtB,UAAM,mBAAmB,QAAQ,4BAA4B;AAC7D,UAAM;AAAA,MACJ,YAAY,mBAAmB,OAAO;AAAA,MACtC,UAAU,eAAe,QAAQ,oBAAoB,gBAAgB;AAAA,MACrE,eAAe,QAAQ,kBAAkB;AAAA,MACzC,gBAAgB,QAAQ,mBAAmB,MAAM,SAAS;AAAA,MAC1D,KAAK,QAAQ,WAAW;AAAA,MACxB,QAAQ,QAAQ,WAAW;AAAA,MAC3B,cAAc,QAAQ,kBAAkB;AAAA,MACxC,YAAY,QAAQ,sBAAsB;AAAA,MAC1C,cAAc,QAAQ,gBAAgB;AAAA,MACtC,MAAM,QAAQ,QAAQ;AAAA,MACtB,OAAO,QAAQ,SAAS;AAAA,MACxB,SAAS,QAAQ,YAAY,kBAAkB,OAAO;AAAA,IACxD,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,iBAAiB,OAAO,MAAM,KAAK,CAAC;AAEjD,QAAM,uBAAuB,CAAC,aAAqB;AACjD,aAAS,YAAY,YAAY,WAAW,QAAQ,IAAI,UAAU,QAAQ,GAAG;AAAA,MAC3E,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,QAAM,yBAAyB,CAAC,UAAuB;AACrD,QAAI,UAAU,WAAY;AAC1B,aAAS,YAAY,EAAE;AAGvB,aAAS,cAAc,OAAO,EAAE,gBAAgB,KAAK,CAAC;AAAA,EACxD;AAEA,QAAM,kBAAkB,CAAC,aAAqB;AAC5C,QAAI,iBAAiB;AACnB,eAAS,OAAO,UAAU,EAAE,gBAAgB,KAAK,CAAC;AAClD;AAAA,IACF;AAEA,aAAS,OAAO,iBAAiB,QAAQ,GAAG,EAAE,gBAAgB,KAAK,CAAC;AAEpE,UAAM,SAAS,SAAS,QAAQ,OAAO,EAAE;AACzC,QAAI,OAAO,WAAW,EAAG;AAEzB,cAAU,MAAM,EACb,KAAK,CAAC,SAAS;AACd,eAAS,UAAU,KAAK,YAAY,EAAE,gBAAgB,KAAK,CAAC;AAC5D,eAAS,gBAAgB,KAAK,QAAQ,EAAE,gBAAgB,KAAK,CAAC;AAC9D,eAAS,QAAQ,KAAK,YAAY,EAAE,gBAAgB,KAAK,CAAC;AAC1D,eAAS,SAAS,KAAK,IAAI,EAAE,gBAAgB,KAAK,CAAC;AAAA,IACrD,CAAC,EACA,MAAM,MAAM;AAAA,IAEb,CAAC;AAAA,EACL;AAEA,iBAAe,SAAS,MAA6B;AACnD,UAAM,UAAoC;AAAA,MACxC,GAAI,kBACA,CAAC,IACD;AAAA,QACE,yBAAyB,KAAK,eAAe,OAAO,IAAI;AAAA,QACxD,oBAAoB,KAAK;AAAA,MAC3B;AAAA,MACJ,gBAAgB,KAAK;AAAA,MACrB,iBAAiB,KAAK;AAAA,MACtB,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,MACd,gBAAgB,KAAK;AAAA,MACrB,oBAAoB,KAAK,cAAc;AAAA,MACvC,cAAc,KAAK;AAAA,MACnB,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,SAAS,KAAK;AAAA,IAChB;AAEA,QAAI;AACF,YAAM,kBAAkB,OAAO;AAC/B,qBAAe,IAAI;AACnB,YAAM;AAAA,QACJ,CAAC,MACC;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS,UAAU,0CAA0C;AAAA,YAC7D,SAAS;AAAA;AAAA,QACX;AAAA,QAEF,EAAE,UAAU,IAAK;AAAA,MACnB;AAAA,IACF,SAAS,OAAO;AAGd,YAAM,UACJ,iBAAiB,SAAS,MAAM,UAC5B,MAAM,UACN,UAAU,wCAAwC;AACxD,YAAM,OAAO,CAAC,MAAM,oBAAC,SAAM,SAAQ,SAAQ,SAAkB,SAAS,GAAG,GAAI;AAAA,QAC3E,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,CAAC,cAAc,YAAa,QAAO;AAEvC,SACE,oBAAC,UAAO,MAAI,MAAC,cAAc,MAAM;AAAA,EAAC,GAChC;AAAA,IAAC;AAAA;AAAA,MACC,iBAAiB;AAAA,MACjB,iBAAiB,CAAC,UAAiB,MAAM,eAAe;AAAA,MACxD,sBAAsB,CAAC,UAAiB,MAAM,eAAe;AAAA,MAC7D,mBAAmB,CAAC,UAAiB,MAAM,eAAe;AAAA,MAC1D,WAAU;AAAA,MAEV;AAAA,6BAAC,gBAAa,WAAU,gEACtB;AAAA,8BAAC,SAAI,WAAU,oEACb,8BAAC,mBAAgB,MAAM,IAAI,WAAU,iBAAgB,GACvD;AAAA,UACA,qBAAC,SAAI,WAAU,uBACb;AAAA,gCAAC,eAAY,WAAU,2CACpB,oBAAU,mCAAmC,GAChD;AAAA,YACA,oBAAC,UAAK,WAAU,yCACb,oBAAU,yCAAyC,GACtD;AAAA,aACF;AAAA,WACF;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,UAAU,aAAa,QAAQ;AAAA,YAC/B,WAAU;AAAA,YACV,YAAU;AAAA,YAEV;AAAA,mCAAC,SAAI,WAAU,yDACb;AAAA,qCAAC,SAAI,WAAU,uBACZ;AAAA,mBAAC,mBACA,qBAAC,SAAI,WAAU,cACb;AAAA;AAAA,sBAAC;AAAA;AAAA,wBACC,MAAK;AAAA,wBACL;AAAA,wBACA,QAAQ,CAAC,EAAE,MAAM,MACf;AAAA,0BAAC;AAAA;AAAA,4BACC,OAAO,UAAU,wCAAwC;AAAA,4BACzD,aAAa,UAAU,oCAAoC;AAAA,4BAC3D,OAAO,MAAM;AAAA,4BACb,UAAU,CAAC,UAAU,uBAAuB,KAAoB;AAAA,4BAChE,SAAS;AAAA,8BACP;AAAA,gCACE,OAAO;AAAA,gCACP,OAAO,UAAU,0CAA0C;AAAA,8BAC7D;AAAA,8BACA;AAAA,gCACE,OAAO;AAAA,gCACP,OAAO,UAAU,0CAA0C;AAAA,8BAC7D;AAAA,4BACF;AAAA,4BACA,oBAAmB;AAAA;AAAA,wBACrB;AAAA;AAAA,oBAEJ;AAAA,oBACA,oBAAC,SAAI,WAAU,UACb;AAAA,sBAAC;AAAA;AAAA,wBACC,OACE,YACI,UAAU,kCAAkC,IAC5C,UAAU,iCAAiC;AAAA,wBAEjD,aAAa,YAAY,uBAAuB;AAAA,wBAChD,OAAO,CAAC,CAAC,OAAO;AAAA,wBAChB,cAAc,OAAO,UAAU;AAAA,wBAC9B,GAAG,SAAS,YAAY;AAAA,0BACvB,UAAU,CAAC,UAAU,qBAAqB,MAAM,OAAO,KAAK;AAAA,wBAC9D,CAAC;AAAA;AAAA,oBACH,GACF;AAAA,qBACF;AAAA,kBAGF;AAAA,oBAAC;AAAA;AAAA,sBACC,OACE,YACI,UAAU,yCAAyC,IACnD,UAAU,sCAAsC;AAAA,sBAEtD,aAAa,UAAU,sCAAsC;AAAA,sBAC7D,OAAO,CAAC,CAAC,OAAO;AAAA,sBAChB,cAAc,OAAO,eAAe;AAAA,sBACnC,GAAG,SAAS,eAAe;AAAA;AAAA,kBAC9B;AAAA,kBAEA;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,UAAU,4CAA4C;AAAA,sBAC7D,aAAa,UAAU,uDAAuD;AAAA,sBAC9E,MAAK;AAAA,sBACL,OAAO,CAAC,CAAC,OAAO;AAAA,sBAChB,cAAc,OAAO,gBAAgB;AAAA,sBACpC,GAAG,SAAS,gBAAgB;AAAA;AAAA,kBAC/B;AAAA,mBACF;AAAA,gBAEA,oBAAC,SAAI,WAAU,oBAAmB;AAAA,gBAElC,qBAAC,SAAI,WAAU,uBACb;AAAA,sCAAC,UAAK,WAAU,0CACb,oBAAU,4CAA4C,GACzD;AAAA,kBAEA;AAAA,oBAAC;AAAA;AAAA,sBACC,OACE,kBACI,UAAU,wCAAwC,IAClD,UAAU,iCAAiC;AAAA,sBAEjD,aAAa,kBAAkB,KAAK;AAAA,sBACpC,OAAO,CAAC,CAAC,OAAO;AAAA,sBAChB,cAAc,OAAO,KAAK;AAAA,sBACzB,GAAG,SAAS,OAAO;AAAA,wBAClB,UAAU,CAAC,UAAU,gBAAgB,MAAM,OAAO,KAAK;AAAA,sBACzD,CAAC;AAAA;AAAA,kBACH;AAAA,kBAEA,qBAAC,SAAI,WAAU,cACb;AAAA,wCAAC,SAAI,WAAU,YACb;AAAA,sBAAC;AAAA;AAAA,wBACC,OAAO,UAAU,oCAAoC;AAAA,wBACrD,aAAa,UAAU,sCAAsC;AAAA,wBAC7D,OAAO,CAAC,CAAC,OAAO;AAAA,wBAChB,cAAc,OAAO,QAAQ;AAAA,wBAC5B,GAAG,SAAS,QAAQ;AAAA;AAAA,oBACvB,GACF;AAAA,oBACC,CAAC,mBACA,oBAAC,SAAI,WAAU,UACb;AAAA,sBAAC;AAAA;AAAA,wBACC,OAAO,UAAU,0CAA0C;AAAA,wBAC3D,aAAY;AAAA,wBACZ,OAAO,CAAC,CAAC,OAAO;AAAA,wBAChB,cAAc,OAAO,cAAc;AAAA,wBAClC,GAAG,SAAS,cAAc;AAAA;AAAA,oBAC7B,GACF;AAAA,qBAEJ;AAAA,kBAEA,qBAAC,SAAI,WAAU,cACZ;AAAA,qBAAC,mBACA,oBAAC,SAAI,WAAU,UACb;AAAA,sBAAC;AAAA;AAAA,wBACC,OAAO,UAAU,0CAA0C;AAAA,wBAC3D,aAAa,UAAU,sCAAsC;AAAA,wBAC7D,OAAO,CAAC,CAAC,OAAO;AAAA,wBAChB,cAAc,OAAO,cAAc;AAAA,wBAClC,GAAG,SAAS,cAAc;AAAA;AAAA,oBAC7B,GACF;AAAA,oBAEF,oBAAC,SAAI,WAAU,UACb;AAAA,sBAAC;AAAA;AAAA,wBACC,OAAO,UAAU,wCAAwC;AAAA,wBACzD,UAAQ;AAAA,wBACR,aAAa,UAAU,sCAAsC;AAAA,wBAC5D,GAAG,SAAS,YAAY;AAAA;AAAA,oBAC3B,GACF;AAAA,qBACF;AAAA,kBAEA,qBAAC,SAAI,WAAU,cACb;AAAA,wCAAC,SAAI,WAAU,UACb;AAAA,sBAAC;AAAA;AAAA,wBACC,OAAO,UAAU,kCAAkC;AAAA,wBACnD,aAAa,UAAU,sCAAsC;AAAA,wBAC7D,OAAO,CAAC,CAAC,OAAO;AAAA,wBAChB,cAAc,OAAO,MAAM;AAAA,wBAC1B,GAAG,SAAS,MAAM;AAAA;AAAA,oBACrB,GACF;AAAA,oBACA,oBAAC,SAAI,WAAU,UACZ,4BACC;AAAA,sBAAC;AAAA;AAAA,wBACC,OAAO,UAAU,2CAA2C;AAAA,wBAC5D,aAAa,UAAU,sCAAsC;AAAA,wBAC7D,OAAO,CAAC,CAAC,OAAO;AAAA,wBAChB,cAAc,OAAO,OAAO;AAAA,wBAC3B,GAAG,SAAS,OAAO;AAAA;AAAA,oBACtB,IAEA;AAAA,sBAAC;AAAA;AAAA,wBACC,MAAK;AAAA,wBACL;AAAA,wBACA,QAAQ,CAAC,EAAE,MAAM,MACf;AAAA,0BAAC;AAAA;AAAA,4BACC,OAAO,UAAU,mCAAmC;AAAA,4BACpD,aAAa,UAAU,oCAAoC;AAAA,4BAC3D,OAAO,MAAM,SAAS;AAAA,4BACtB,UAAU,CAAC,UACT,SAAS,SAAS,OAAiB,EAAE,gBAAgB,KAAK,CAAC;AAAA,4BAE7D,SAAS;AAAA,4BACT,OAAO,CAAC,CAAC,OAAO;AAAA,4BAChB,cAAc,OAAO,OAAO;AAAA;AAAA,wBAC9B;AAAA;AAAA,oBAEJ,GAEJ;AAAA,qBACF;AAAA,kBAEA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL;AAAA,sBACA,QAAQ,CAAC,EAAE,MAAM,MACf;AAAA,wBAAC;AAAA;AAAA,0BACC,OAAO,UAAU,qCAAqC;AAAA,0BACtD,aAAa,UAAU,oCAAoC;AAAA,0BAC3D,OAAO,MAAM,SAAS;AAAA,0BACtB,UAAU,CAAC,UACT,SAAS,WAAW,OAAiB,EAAE,gBAAgB,KAAK,CAAC;AAAA,0BAE/D,SAAS;AAAA,0BACT,OAAO,CAAC,CAAC,OAAO;AAAA,0BAChB,cAAc,OAAO,SAAS;AAAA;AAAA,sBAChC;AAAA;AAAA,kBAEJ;AAAA,mBACF;AAAA,iBACF;AAAA,cAEA,oBAAC,SAAI,WAAU,8EACb;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,WAAU;AAAA,kBACV,UAAU,CAAC,WAAW;AAAA,kBACtB,SAAS;AAAA,kBAER,yBACG,UAAU,wCAAwC,IAClD,UAAU,oCAAoC;AAAA;AAAA,cACpD,GACF;AAAA;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF,GACF;AAEJ;AAOe,SAAR,2BAA4C;AACjD,QAAM,EAAE,MAAM,UAAU,IAAI,QAAQ;AAIpC,MAAI,aAAa,CAAC,KAAM,QAAO;AAE/B,SAAO,oBAAC,mCAAgC;AAC1C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/modals/billing/RequiredBillingDataModal.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any -- o generic Translator do next-intl não é\n expressável estruturalmente; o `any` do TranslateFn é intencional e está contido neste arquivo\n (mesmo padrão de modules/plans/utils/map-api-plan-to-ui.ts). */\n'use client';\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { Controller, useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { useTranslations } from 'next-intl';\nimport { toast } from 'sonner';\nimport z from 'zod';\nimport { IconFileInvoice } from '@tabler/icons-react';\n\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/overlay/Dialog';\nimport { Button } from '../../ui/buttons/Button';\nimport { FormField } from '../../ui/form/FormField';\nimport { SelectField } from '../../ui/form/SelectField';\nimport { Toast } from '../../ui/feedback/Toast';\nimport { useAuth } from '../../../providers/auth.provider';\nimport { useViaCep } from '../../../modules/accounts/hooks/useViaCep';\nimport { useUpdateBillingData } from '../../../modules/accounts/hooks/useAccountManagement';\nimport { useRequiredBillingData } from '../../../modules/accounts/hooks/use-required-billing-data.hook';\nimport { COUNTRIES } from '../../../utils/countries';\nimport { BR_STATE_OPTIONS } from '../../../utils/constants/br-states';\nimport { formatCNPJ, formatCPF, formatPostalCode } from '../../../utils/format/masks';\nimport { isValidCNPJ, isValidCPF } from '../../../utils/validators/common';\nimport type { UpdateBillingDataRequest } from '../../../modules/accounts/types';\n\ntype TranslateFn = (key: any, values?: Record<string, string | number>) => string;\n\ntype BillingDataFormValues = {\n personType: 'pf' | 'pj';\n document: string;\n financialName: string;\n financialEmail: string;\n cep: string;\n street: string;\n streetNumber: string;\n complement?: string;\n neighborhood: string;\n city: string;\n state: string;\n country: string;\n};\n\nconst COUNTRY_OPTIONS = COUNTRIES.map((country: { iso: string; name: string }) => ({\n value: country.iso,\n label: country.name,\n}));\n\nfunction buildBillingDataSchema(translate: TranslateFn) {\n const requiredText = z\n .string()\n .trim()\n .min(1, translate('common.billing.requiredData.requiredField'));\n\n const base = z.object({\n personType: z.enum(['pf', 'pj']),\n document: z.string().trim(),\n financialName: requiredText,\n financialEmail: z\n .string()\n .trim()\n .min(1, translate('common.billing.requiredData.requiredField'))\n .email(translate('common.billing.requiredData.invalidEmail')),\n cep: requiredText,\n street: requiredText,\n streetNumber: z.string().trim(),\n complement: z.string().trim().optional(),\n neighborhood: z.string().trim(),\n city: requiredText,\n state: requiredText,\n country: requiredText,\n });\n\n return base.superRefine((data, ctx) => {\n if (!data.streetNumber) {\n ctx.addIssue({\n code: 'custom',\n path: ['streetNumber'],\n message: translate('common.billing.requiredData.requiredField'),\n });\n }\n\n if (!data.neighborhood) {\n ctx.addIssue({\n code: 'custom',\n path: ['neighborhood'],\n message: translate('common.billing.requiredData.requiredField'),\n });\n }\n\n if (data.cep.replace(/\\D/g, '').length !== 8) {\n ctx.addIssue({\n code: 'custom',\n path: ['cep'],\n message: translate('common.billing.requiredData.invalidCep'),\n });\n }\n\n const isCompany = data.personType === 'pj';\n const digits = data.document.replace(/\\D/g, '');\n\n if (digits.length !== (isCompany ? 14 : 11)) {\n ctx.addIssue({\n code: 'custom',\n path: ['document'],\n message: isCompany\n ? translate('common.billing.requiredData.invalidCnpj')\n : translate('common.billing.requiredData.invalidCpf'),\n });\n return;\n }\n\n /* Dígito verificador, mesmo algoritmo do backend: sem isso 111.111.111-11 passa\n * aqui e o PUT da conta é recusado depois, sem o usuário saber o motivo. */\n const isValid = isCompany ? isValidCNPJ(digits) : isValidCPF(digits);\n if (!isValid) {\n ctx.addIssue({\n code: 'custom',\n path: ['document'],\n message: isCompany\n ? translate('common.billing.requiredData.invalidCnpjChecksum')\n : translate('common.billing.requiredData.invalidCpfChecksum'),\n });\n }\n });\n}\n\nfunction formatDocument(document: string | null | undefined, isCompany: boolean) {\n if (!document) return '';\n return isCompany ? formatCNPJ(document) : formatCPF(document);\n}\n\nfunction RequiredBillingDataModalContent() {\n const translate = useTranslations();\n const { user } = useAuth();\n const { isRequired, account } = useRequiredBillingData();\n const { mutateAsync: updateBillingData } = useUpdateBillingData();\n const { mutateAsync: lookupCep } = useViaCep();\n\n /* O modal fecha na hora do submit e não reabre enquanto o invalidate do account\n * não volta — sem isso o usuário vê o modal piscar de novo com o cache antigo. */\n const [isSubmitted, setIsSubmitted] = useState(false);\n const isPrefilled = useRef(false);\n\n const schema = useMemo(\n () => buildBillingDataSchema(translate),\n [translate],\n );\n\n const form = useForm<BillingDataFormValues>({\n resolver: zodResolver(schema),\n mode: 'onChange',\n defaultValues: {\n personType: 'pf',\n document: '',\n financialName: '',\n financialEmail: '',\n cep: '',\n street: '',\n streetNumber: '',\n complement: '',\n neighborhood: '',\n city: '',\n state: '',\n country: 'BR',\n },\n });\n\n const {\n register,\n control,\n handleSubmit,\n reset,\n setValue,\n watch,\n formState: { errors, isSubmitting, isValid },\n } = form;\n\n const personType = watch('personType');\n const isCompany = personType === 'pj';\n\n useEffect(() => {\n if (!account || isPrefilled.current) return;\n isPrefilled.current = true;\n\n const accountIsCompany = account.financial_document_type === 2;\n reset({\n personType: accountIsCompany ? 'pj' : 'pf',\n document: formatDocument(account.financial_document, accountIsCompany),\n financialName: account.financial_name ?? '',\n financialEmail: account.financial_email || user?.email || '',\n cep: account.zipcode ?? '',\n street: account.address ?? '',\n streetNumber: account.address_number ?? '',\n complement: account.address_complement ?? '',\n neighborhood: account.neighborhood ?? '',\n city: account.city ?? '',\n state: account.state ?? '',\n country: account.country || 'BR',\n });\n }, [account, reset, user?.email]);\n\n const handleDocumentChange = (rawValue: string) => {\n setValue('document', isCompany ? formatCNPJ(rawValue) : formatCPF(rawValue), {\n shouldValidate: true,\n });\n };\n\n const handlePersonTypeChange = (value: 'pf' | 'pj') => {\n if (value === personType) return;\n setValue('document', '');\n /* shouldValidate reroda o resolver: sem isso o `isValid` fica preso no valor\n * do documento antigo e o botão continua habilitado com o campo já limpo. */\n setValue('personType', value, { shouldValidate: true });\n };\n\n const handleCepChange = (rawValue: string) => {\n setValue('cep', formatPostalCode(rawValue), { shouldValidate: true });\n\n const digits = rawValue.replace(/\\D/g, '');\n if (digits.length !== 8) return;\n\n lookupCep(digits)\n .then((data) => {\n setValue('street', data.logradouro, { shouldValidate: true });\n setValue('neighborhood', data.bairro, { shouldValidate: true });\n setValue('city', data.localidade, { shouldValidate: true });\n setValue('state', data.uf, { shouldValidate: true });\n })\n .catch(() => {\n // CEP não encontrado: o usuário preenche o endereço na mão.\n });\n };\n\n async function onSubmit(data: BillingDataFormValues) {\n const payload: UpdateBillingDataRequest = {\n financial_document_type: data.personType === 'pf' ? 1 : 2,\n financial_document: data.document,\n financial_name: data.financialName,\n financial_email: data.financialEmail,\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 country: data.country,\n };\n\n try {\n await updateBillingData(payload);\n setIsSubmitted(true);\n toast.custom(\n (t) => (\n <Toast\n variant=\"success\"\n message={translate('common.billing.requiredData.successToast')}\n toastId={t}\n />\n ),\n { duration: 5000 },\n );\n } catch (error) {\n /* O backend devolve a razão exata da recusa (ex.: documento fiscal inválido).\n * Trocar por um texto genérico deixaria o usuário travado sem saber o que corrigir. */\n const message =\n error instanceof Error && error.message\n ? error.message\n : translate('common.billing.requiredData.errorToast');\n toast.custom((t) => <Toast variant=\"error\" message={message} toastId={t} />, {\n duration: 5000,\n });\n }\n }\n\n if (!isRequired || isSubmitted) return null;\n\n return (\n <Dialog open onOpenChange={() => {}}>\n <DialogContent\n showCloseButton={false}\n onEscapeKeyDown={(event: Event) => event.preventDefault()}\n onPointerDownOutside={(event: Event) => event.preventDefault()}\n onInteractOutside={(event: Event) => event.preventDefault()}\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-[502px] 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-hidden\"\n >\n <DialogHeader className=\"gap-3 text-left p-4 lg:p-5 border-b border-zinc-200 shrink-0\">\n <div className=\"flex items-center justify-center w-10 h-10 bg-zinc-50 rounded-lg\">\n <IconFileInvoice 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 {translate('common.billing.requiredData.title')}\n </DialogTitle>\n <span className=\"paragraph-small-regular text-zinc-600\">\n {translate('common.billing.requiredData.description')}\n </span>\n </div>\n </DialogHeader>\n\n <form\n onSubmit={handleSubmit(onSubmit)}\n className=\"flex flex-col flex-1 min-h-0\"\n noValidate\n >\n <div className=\"flex flex-col gap-6 p-4 lg:p-5 flex-1 overflow-y-auto\">\n <div className=\"flex flex-col gap-5\">\n <div className=\"flex gap-4\">\n <Controller\n name=\"personType\"\n control={control}\n render={({ field }) => (\n <SelectField\n label={translate('common.billing.requiredData.personType')}\n placeholder={translate('common.billing.requiredData.select')}\n value={field.value}\n onChange={(value) => handlePersonTypeChange(value as 'pf' | 'pj')}\n options={[\n {\n value: 'pf',\n label: translate('common.billing.requiredData.personTypePf'),\n },\n {\n value: 'pj',\n label: translate('common.billing.requiredData.personTypePj'),\n },\n ]}\n containerClassName=\"flex-1\"\n />\n )}\n />\n <div className=\"flex-1\">\n <FormField\n label={\n isCompany\n ? translate('common.billing.requiredData.cnpj')\n : translate('common.billing.requiredData.cpf')\n }\n placeholder={isCompany ? '__.___.___/____-__' : '___.___.___-__'}\n error={!!errors.document}\n errorMessage={errors.document?.message}\n {...register('document', {\n onChange: (event) => handleDocumentChange(event.target.value),\n })}\n />\n </div>\n </div>\n\n <FormField\n label={\n isCompany\n ? translate('common.billing.requiredData.companyName')\n : translate('common.billing.requiredData.fullName')\n }\n placeholder={translate('common.billing.requiredData.typeHere')}\n error={!!errors.financialName}\n errorMessage={errors.financialName?.message}\n {...register('financialName')}\n />\n\n <FormField\n label={translate('common.billing.requiredData.financialEmail')}\n placeholder={translate('common.billing.requiredData.financialEmailPlaceholder')}\n type=\"email\"\n error={!!errors.financialEmail}\n errorMessage={errors.financialEmail?.message}\n {...register('financialEmail')}\n />\n </div>\n\n <div className=\"h-px bg-zinc-200\" />\n\n <div className=\"flex flex-col gap-5\">\n <span className=\"paragraph-small-semibold text-zinc-950\">\n {translate('common.billing.requiredData.addressHeading')}\n </span>\n\n <FormField\n label={translate('common.billing.requiredData.cep')}\n placeholder=\"_____-___\"\n error={!!errors.cep}\n errorMessage={errors.cep?.message}\n {...register('cep', {\n onChange: (event) => handleCepChange(event.target.value),\n })}\n />\n\n <div className=\"flex gap-4\">\n <div className=\"flex-[3]\">\n <FormField\n label={translate('common.billing.requiredData.street')}\n placeholder={translate('common.billing.requiredData.typeHere')}\n error={!!errors.street}\n errorMessage={errors.street?.message}\n {...register('street')}\n />\n </div>\n <div className=\"flex-1\">\n <FormField\n label={translate('common.billing.requiredData.streetNumber')}\n placeholder=\"000\"\n error={!!errors.streetNumber}\n errorMessage={errors.streetNumber?.message}\n {...register('streetNumber')}\n />\n </div>\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <FormField\n label={translate('common.billing.requiredData.neighborhood')}\n placeholder={translate('common.billing.requiredData.typeHere')}\n error={!!errors.neighborhood}\n errorMessage={errors.neighborhood?.message}\n {...register('neighborhood')}\n />\n </div>\n <div className=\"flex-1\">\n <FormField\n label={translate('common.billing.requiredData.complement')}\n optional\n placeholder={translate('common.billing.requiredData.typeHere')}\n {...register('complement')}\n />\n </div>\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <FormField\n label={translate('common.billing.requiredData.city')}\n placeholder={translate('common.billing.requiredData.typeHere')}\n error={!!errors.city}\n errorMessage={errors.city?.message}\n {...register('city')}\n />\n </div>\n <div className=\"flex-1\">\n <Controller\n name=\"state\"\n control={control}\n render={({ field }) => (\n <SelectField\n label={translate('common.billing.requiredData.state')}\n placeholder={translate('common.billing.requiredData.select')}\n value={field.value || undefined}\n onChange={(value) =>\n setValue('state', value as string, { shouldValidate: true })\n }\n options={BR_STATE_OPTIONS}\n error={!!errors.state}\n errorMessage={errors.state?.message}\n />\n )}\n />\n </div>\n </div>\n\n <Controller\n name=\"country\"\n control={control}\n render={({ field }) => (\n <SelectField\n label={translate('common.billing.requiredData.country')}\n placeholder={translate('common.billing.requiredData.select')}\n value={field.value || undefined}\n onChange={(value) =>\n setValue('country', value as string, { shouldValidate: true })\n }\n options={COUNTRY_OPTIONS}\n error={!!errors.country}\n errorMessage={errors.country?.message}\n />\n )}\n />\n </div>\n </div>\n\n <div className=\"flex items-center justify-end p-4 lg:p-5 border-t border-zinc-200 shrink-0\">\n <Button\n type=\"submit\"\n className=\"h-10! w-full lg:w-fit\"\n disabled={!isValid || isSubmitting}\n loading={isSubmitting}\n >\n {isSubmitting\n ? translate('common.billing.requiredData.submitting')\n : translate('common.billing.requiredData.submit')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * Modal bloqueante que exige o cadastro de cobrança/fiscal de contas que já\n * pagaram sem ele — sem esses dados a nota fiscal não é emitida. Monte uma vez\n * no layout raiz do app; ele mesmo decide se aparece (ver `useRequiredBillingData`).\n */\nexport default function RequiredBillingDataModal() {\n const { user, isLoading } = useAuth();\n\n /* Sem sessão não há conta nem assinatura pra consultar — evita disparar as\n * queries (e tomar 401) em /login, /register e demais rotas públicas. */\n if (isLoading || !user) return null;\n\n return <RequiredBillingDataModalContent />;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -9,6 +9,7 @@ import { useCreateCard } from "../../../modules/cards/hooks/create-card.hook";
|
|
|
9
9
|
import { useCreateSetupIntent } from "../../../modules/cards/hooks/create-setup-intent.hook";
|
|
10
10
|
import { useCurrentAccount } from "../../../modules/accounts/hooks/current-account.hook";
|
|
11
11
|
import { isValidTaxId } from "../../../utils/validators/common";
|
|
12
|
+
import { isUsdAccount } from "../../../utils/format/gateway";
|
|
12
13
|
import { useExternalContracting } from "../../../providers/whitelabel.provider";
|
|
13
14
|
import { CardFormFields, BillingFormFields, buildCardFormSchema } from "./CardFormFields";
|
|
14
15
|
import { IconX, IconCreditCard, IconCheck, IconChevronRight } from "@tabler/icons-react";
|
|
@@ -67,7 +68,7 @@ function AddCardModal() {
|
|
|
67
68
|
const isOpen = activeModal === "addCardModal";
|
|
68
69
|
const { isExternalContracting, redirectToExternal } = useExternalContracting();
|
|
69
70
|
const { data: account } = useCurrentAccount();
|
|
70
|
-
const isInternational = account
|
|
71
|
+
const isInternational = isUsdAccount(account);
|
|
71
72
|
const [currentStep, setCurrentStep] = useState(1);
|
|
72
73
|
const [stripeStatus, setStripeStatus] = useState(INITIAL_STRIPE_STATUS);
|
|
73
74
|
const [stripeErrors, setStripeErrors] = useState({});
|