@greatapps/common 1.1.749 → 1.1.751
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/handlers.mjs +1 -5
- package/dist/handlers.mjs.map +1 -1
- package/dist/i18n/messages/en-us.mjs +0 -32
- package/dist/i18n/messages/en-us.mjs.map +1 -1
- package/dist/i18n/messages/es-es.mjs +0 -32
- package/dist/i18n/messages/es-es.mjs.map +1 -1
- package/dist/i18n/messages/pt-br.mjs +0 -32
- package/dist/i18n/messages/pt-br.mjs.map +1 -1
- package/dist/index.mjs +43 -71
- package/dist/index.mjs.map +1 -1
- package/dist/modules/auth/utils/get-user-context.mjs +1 -2
- package/dist/modules/auth/utils/get-user-context.mjs.map +1 -1
- package/dist/modules/cards/hooks/cards.hook.mjs +2 -3
- package/dist/modules/cards/hooks/cards.hook.mjs.map +1 -1
- package/dist/modules/ia-credits/services/ia-credits.service.mjs +0 -67
- package/dist/modules/ia-credits/services/ia-credits.service.mjs.map +1 -1
- package/dist/modules/plans/services/plans.service.mjs +2 -2
- package/dist/modules/plans/services/plans.service.mjs.map +1 -1
- package/dist/modules/plans/types/plan.type.mjs.map +1 -1
- package/dist/modules/plans/utils/map-api-plan-to-ui.mjs +1 -0
- package/dist/modules/plans/utils/map-api-plan-to-ui.mjs.map +1 -1
- package/dist/modules/subscriptions/types/calculate-subscription.type.mjs +8 -0
- package/dist/modules/subscriptions/types/calculate-subscription.type.mjs.map +1 -1
- package/package.json +1 -3
- package/src/handlers.ts +0 -2
- package/src/i18n/messages/en-us.ts +0 -33
- package/src/i18n/messages/es-es.ts +0 -33
- package/src/i18n/messages/pt-br.ts +0 -33
- package/src/index.ts +0 -25
- package/src/modules/auth/utils/get-user-context.ts +0 -3
- package/src/modules/cards/hooks/cards.hook.ts +1 -6
- package/src/modules/ia-credits/services/ia-credits.service.ts +0 -92
- package/src/modules/plans/services/plans.service.ts +3 -3
- package/src/modules/plans/types/plan.type.ts +1 -0
- package/src/modules/plans/utils/map-api-plan-to-ui.ts +1 -0
- package/src/modules/subscriptions/types/calculate-subscription.type.ts +8 -0
- package/dist/components/layouts/AiCreditsOfferGate.mjs +0 -92
- package/dist/components/layouts/AiCreditsOfferGate.mjs.map +0 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs +0 -275
- package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs.map +0 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.mjs +0 -55
- package/dist/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.mjs.map +0 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferIllustration.mjs +0 -78
- package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferIllustration.mjs.map +0 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.mjs +0 -60
- package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.mjs.map +0 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/index.mjs +0 -5
- package/dist/components/modals/promo/AiCreditsOfferModal/index.mjs.map +0 -1
- package/dist/modules/ia-credits/constants/ai-credits-offer.constants.mjs +0 -26
- package/dist/modules/ia-credits/constants/ai-credits-offer.constants.mjs.map +0 -1
- package/dist/modules/ia-credits/handlers/get-offer-eligibility.handler.mjs +0 -19
- package/dist/modules/ia-credits/handlers/get-offer-eligibility.handler.mjs.map +0 -1
- package/dist/modules/ia-credits/handlers/purchase-ia-credits.handler.mjs +0 -33
- package/dist/modules/ia-credits/handlers/purchase-ia-credits.handler.mjs.map +0 -1
- package/dist/modules/ia-credits/hooks/purchase-ia-credits.hook.mjs +0 -29
- package/dist/modules/ia-credits/hooks/purchase-ia-credits.hook.mjs.map +0 -1
- package/dist/modules/ia-credits/hooks/use-offer-eligibility.hook.mjs +0 -18
- package/dist/modules/ia-credits/hooks/use-offer-eligibility.hook.mjs.map +0 -1
- package/dist/modules/ia-credits/types/ai-credits-offer.type.mjs +0 -34
- package/dist/modules/ia-credits/types/ai-credits-offer.type.mjs.map +0 -1
- package/dist/modules/ia-credits/utils/offer-stage-cookie.mjs +0 -41
- package/dist/modules/ia-credits/utils/offer-stage-cookie.mjs.map +0 -1
- package/dist/store/useAiCreditsOfferModal.mjs +0 -14
- package/dist/store/useAiCreditsOfferModal.mjs.map +0 -1
- package/src/components/layouts/AiCreditsOfferGate.tsx +0 -171
- package/src/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.tsx +0 -400
- package/src/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.tsx +0 -97
- package/src/components/modals/promo/AiCreditsOfferModal/components/OfferIllustration.tsx +0 -121
- package/src/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.tsx +0 -91
- package/src/components/modals/promo/AiCreditsOfferModal/index.ts +0 -1
- package/src/modules/ia-credits/constants/ai-credits-offer.constants.ts +0 -33
- package/src/modules/ia-credits/handlers/get-offer-eligibility.handler.ts +0 -24
- package/src/modules/ia-credits/handlers/purchase-ia-credits.handler.ts +0 -42
- package/src/modules/ia-credits/hooks/purchase-ia-credits.hook.ts +0 -43
- package/src/modules/ia-credits/hooks/use-offer-eligibility.hook.ts +0 -28
- package/src/modules/ia-credits/types/ai-credits-offer.type.ts +0 -87
- package/src/modules/ia-credits/utils/offer-stage-cookie.ts +0 -66
- package/src/store/useAiCreditsOfferModal.ts +0 -24
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useState } from "react";
|
|
4
|
-
import { useStripe } from "@stripe/react-stripe-js";
|
|
5
|
-
import { IconCheck, IconX } from "@tabler/icons-react";
|
|
6
|
-
import { useLocale, useTranslations } from "next-intl";
|
|
7
|
-
import { toast } from "sonner";
|
|
8
|
-
import { Button } from "../../../ui/buttons/Button";
|
|
9
|
-
import { Dialog, DialogContent, DialogTitle } from "../../../ui/overlay/Dialog";
|
|
10
|
-
import { Toast } from "../../../ui/feedback/Toast";
|
|
11
|
-
import { useAuth } from "../../../../providers/auth.provider";
|
|
12
|
-
import { useWhitelabel, useWhitelabelUrls } from "../../../../providers/whitelabel.provider";
|
|
13
|
-
import { useActiveSubscription } from "../../../../modules/subscriptions/hooks/find-active-subscription.hook";
|
|
14
|
-
import { useCards } from "../../../../modules/cards/hooks/cards.hook";
|
|
15
|
-
import { useCurrencyFormatter } from "../../../../modules/accounts/hooks/use-currency-formatter.hook";
|
|
16
|
-
import { confirmPaymentIfRequired } from "../../../../modules/subscriptions/utils/confirm-payment-if-required";
|
|
17
|
-
import {
|
|
18
|
-
AI_CREDITS_FIRST_PURCHASE_OFFER,
|
|
19
|
-
AiCreditsOfferStage,
|
|
20
|
-
PAYMENT_METHOD_CARD
|
|
21
|
-
} from "../../../../modules/ia-credits/constants/ai-credits-offer.constants";
|
|
22
|
-
import { useAiCreditsOfferEligibility } from "../../../../modules/ia-credits/hooks/use-offer-eligibility.hook";
|
|
23
|
-
import { usePurchaseIaCredits } from "../../../../modules/ia-credits/hooks/purchase-ia-credits.hook";
|
|
24
|
-
import { readOfferStageCookie, writeOfferStageCookie } from "../../../../modules/ia-credits/utils/offer-stage-cookie";
|
|
25
|
-
import { useAiCreditsOfferModal } from "../../../../store/useAiCreditsOfferModal";
|
|
26
|
-
import { useModalManager } from "../../../../store/useModalManager";
|
|
27
|
-
import { LoseOfferDialog } from "./components/LoseOfferDialog";
|
|
28
|
-
import { OfferIllustration } from "./components/OfferIllustration";
|
|
29
|
-
import { OfferPaymentField } from "./components/OfferPaymentField";
|
|
30
|
-
function AiCreditsOfferModal() {
|
|
31
|
-
const translate = useTranslations();
|
|
32
|
-
const locale = useLocale();
|
|
33
|
-
const stripe = useStripe();
|
|
34
|
-
const { open, closeModal } = useAiCreditsOfferModal();
|
|
35
|
-
const { openModal } = useModalManager();
|
|
36
|
-
const { user } = useAuth();
|
|
37
|
-
const { whitelabel } = useWhitelabel();
|
|
38
|
-
const { accountsUrl } = useWhitelabelUrls();
|
|
39
|
-
const { formatCurrencyNumber } = useCurrencyFormatter();
|
|
40
|
-
const [confirmingExit, setConfirmingExit] = useState(false);
|
|
41
|
-
const [pickedCardId, setPickedCardId] = useState("");
|
|
42
|
-
const { data: cardsData } = useCards();
|
|
43
|
-
const { data: subscriptionData } = useActiveSubscription();
|
|
44
|
-
const eligibility = useAiCreditsOfferEligibility();
|
|
45
|
-
const purchaseMutation = usePurchaseIaCredits();
|
|
46
|
-
const subscription = subscriptionData?.data?.[0] ?? null;
|
|
47
|
-
const subscriptionId = subscription?.id;
|
|
48
|
-
const accountId = user?.id_account;
|
|
49
|
-
const defaultCardId = cardsData?.data?.find((card) => card.is_default)?.id?.toString() ?? cardsData?.data?.[0]?.id?.toString() ?? "";
|
|
50
|
-
const pickedCardExists = cardsData?.data?.some((card) => card.id.toString() === pickedCardId);
|
|
51
|
-
const selectedCardId = pickedCardExists ? pickedCardId : defaultCardId;
|
|
52
|
-
const offerPrice = AI_CREDITS_FIRST_PURCHASE_OFFER.price;
|
|
53
|
-
const fullPrice = eligibility.data?.anchorCents != null ? eligibility.data.anchorCents / 100 : null;
|
|
54
|
-
const discountPercent = fullPrice != null ? Math.round((1 - offerPrice / fullPrice) * 100) : null;
|
|
55
|
-
const creditsLabel = AI_CREDITS_FIRST_PURCHASE_OFFER.credits.toLocaleString(locale);
|
|
56
|
-
const discountLabel = discountPercent != null ? translate("common.promo.aiCreditsOffer.discountBadge", { percent: String(discountPercent) }) : "";
|
|
57
|
-
const hasCard = Boolean(selectedCardId);
|
|
58
|
-
const isSubmitting = purchaseMutation.isPending;
|
|
59
|
-
const dismiss = useCallback(() => {
|
|
60
|
-
setConfirmingExit(false);
|
|
61
|
-
closeModal();
|
|
62
|
-
}, [closeModal]);
|
|
63
|
-
const requestClose = useCallback(() => setConfirmingExit(true), []);
|
|
64
|
-
const submitPurchase = useCallback(
|
|
65
|
-
async (cardId) => {
|
|
66
|
-
if (subscriptionId == null) {
|
|
67
|
-
closeModal();
|
|
68
|
-
window.location.assign(`${accountsUrl}/subscriptions?tab=plans`);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const input = {
|
|
72
|
-
subscriptionId,
|
|
73
|
-
offer: AI_CREDITS_FIRST_PURCHASE_OFFER.slug,
|
|
74
|
-
paymentMethod: PAYMENT_METHOD_CARD,
|
|
75
|
-
idCard: cardId
|
|
76
|
-
};
|
|
77
|
-
try {
|
|
78
|
-
let result = await purchaseMutation.mutateAsync(input);
|
|
79
|
-
if (result.status === 2) {
|
|
80
|
-
await confirmPaymentIfRequired(stripe, result, translate("common.payment.authFailed"));
|
|
81
|
-
result = await purchaseMutation.mutateAsync(input);
|
|
82
|
-
}
|
|
83
|
-
if (result.status !== 1) {
|
|
84
|
-
toast.custom((toastId) => /* @__PURE__ */ jsx(
|
|
85
|
-
Toast,
|
|
86
|
-
{
|
|
87
|
-
variant: "error",
|
|
88
|
-
message: translate("common.promo.aiCreditsOffer.errorToast"),
|
|
89
|
-
toastId
|
|
90
|
-
}
|
|
91
|
-
));
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (accountId != null) {
|
|
95
|
-
const cookie = readOfferStageCookie(accountId);
|
|
96
|
-
writeOfferStageCookie(
|
|
97
|
-
accountId,
|
|
98
|
-
{
|
|
99
|
-
stage: AiCreditsOfferStage.CONVERTED,
|
|
100
|
-
lastSessionIat: eligibility.data?.sessionIssuedAt ?? cookie.lastSessionIat
|
|
101
|
-
},
|
|
102
|
-
whitelabel?.domain
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
toast.custom((toastId) => /* @__PURE__ */ jsx(
|
|
106
|
-
Toast,
|
|
107
|
-
{
|
|
108
|
-
variant: "success",
|
|
109
|
-
message: translate("common.promo.aiCreditsOffer.successToast", { credits: creditsLabel }),
|
|
110
|
-
toastId
|
|
111
|
-
}
|
|
112
|
-
));
|
|
113
|
-
dismiss();
|
|
114
|
-
} catch {
|
|
115
|
-
toast.custom((toastId) => /* @__PURE__ */ jsx(
|
|
116
|
-
Toast,
|
|
117
|
-
{
|
|
118
|
-
variant: "error",
|
|
119
|
-
message: translate("common.promo.aiCreditsOffer.errorToast"),
|
|
120
|
-
toastId
|
|
121
|
-
}
|
|
122
|
-
));
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
[
|
|
126
|
-
accountId,
|
|
127
|
-
accountsUrl,
|
|
128
|
-
closeModal,
|
|
129
|
-
creditsLabel,
|
|
130
|
-
dismiss,
|
|
131
|
-
eligibility.data?.sessionIssuedAt,
|
|
132
|
-
purchaseMutation,
|
|
133
|
-
stripe,
|
|
134
|
-
subscriptionId,
|
|
135
|
-
translate,
|
|
136
|
-
whitelabel?.domain
|
|
137
|
-
]
|
|
138
|
-
);
|
|
139
|
-
const handleOpenAddCard = useCallback(() => {
|
|
140
|
-
openModal("addCardModal", {
|
|
141
|
-
onCardAdded: (cardId) => {
|
|
142
|
-
setPickedCardId(cardId);
|
|
143
|
-
void submitPurchase(cardId);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
}, [openModal, submitPurchase]);
|
|
147
|
-
const handleConfirm = useCallback(async () => {
|
|
148
|
-
if (!hasCard) {
|
|
149
|
-
handleOpenAddCard();
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
await submitPurchase(selectedCardId);
|
|
153
|
-
}, [handleOpenAddCard, hasCard, selectedCardId, submitPurchase]);
|
|
154
|
-
const bullets = [
|
|
155
|
-
translate("common.promo.aiCreditsOffer.bullets.first"),
|
|
156
|
-
translate("common.promo.aiCreditsOffer.bullets.second"),
|
|
157
|
-
translate("common.promo.aiCreditsOffer.bullets.third")
|
|
158
|
-
];
|
|
159
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
160
|
-
/* @__PURE__ */ jsx(Dialog, { open, onOpenChange: () => {
|
|
161
|
-
}, children: /* @__PURE__ */ jsx(
|
|
162
|
-
DialogContent,
|
|
163
|
-
{
|
|
164
|
-
showCloseButton: false,
|
|
165
|
-
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
166
|
-
onEscapeKeyDown: (event) => {
|
|
167
|
-
event.preventDefault();
|
|
168
|
-
requestClose();
|
|
169
|
-
},
|
|
170
|
-
onPointerDownOutside: (event) => {
|
|
171
|
-
event.preventDefault();
|
|
172
|
-
requestClose();
|
|
173
|
-
},
|
|
174
|
-
onInteractOutside: (event) => event.preventDefault(),
|
|
175
|
-
overlayClassName: "duration-300",
|
|
176
|
-
className: [
|
|
177
|
-
// mobile: folha de tela cheia (mesma geometria do BuyCreditsModal)
|
|
178
|
-
"flex flex-col gap-0 overflow-hidden rounded-t-2xl rounded-b-none border-0 p-0 shadow-elevated",
|
|
179
|
-
"top-0 right-0 bottom-0 left-0 h-dvh max-w-full translate-x-0 translate-y-0 sm:max-w-full",
|
|
180
|
-
// desktop (≥ md): cartão de duas colunas, centralizado
|
|
181
|
-
"md:top-[50%] md:right-auto md:bottom-auto md:left-[50%] md:h-auto md:max-h-[92vh] md:w-[900px]! md:max-w-[900px]! md:translate-x-[-50%] md:translate-y-[-50%] md:rounded-xl md:border",
|
|
182
|
-
// entrada de baixo pra cima: a classe (e não utilidade `animate-[...]`) porque o
|
|
183
|
-
// `DialogContent` já traz `animate-in` e o merge do Tailwind não funde as duas — ver
|
|
184
|
-
// `.gp-offer-modal` no globals.css.
|
|
185
|
-
"gp-offer-modal"
|
|
186
|
-
].join(" "),
|
|
187
|
-
children: /* @__PURE__ */ jsxs("div", { className: "gm-scroll flex h-full flex-col overflow-y-auto md:h-auto md:min-h-[600px] md:flex-row md:items-stretch md:overflow-visible", children: [
|
|
188
|
-
/* @__PURE__ */ jsxs("div", { className: "relative h-[180px] w-full shrink-0 md:order-2 md:h-auto md:w-1/2", children: [
|
|
189
|
-
/* @__PURE__ */ jsx(OfferIllustration, { credits: AI_CREDITS_FIRST_PURCHASE_OFFER.credits, locale }),
|
|
190
|
-
/* @__PURE__ */ jsx(
|
|
191
|
-
"button",
|
|
192
|
-
{
|
|
193
|
-
type: "button",
|
|
194
|
-
onClick: requestClose,
|
|
195
|
-
"aria-label": translate("common.promo.aiCreditsOffer.close"),
|
|
196
|
-
className: "absolute top-4 right-4 z-10 flex size-8 cursor-pointer items-center justify-center rounded-lg text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-950",
|
|
197
|
-
children: /* @__PURE__ */ jsx(IconX, { className: "size-[18px]" })
|
|
198
|
-
}
|
|
199
|
-
)
|
|
200
|
-
] }),
|
|
201
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-6 p-6 md:order-1 md:w-1/2 md:shrink-0 md:gap-7 md:p-10", children: [
|
|
202
|
-
/* @__PURE__ */ jsx(
|
|
203
|
-
"span",
|
|
204
|
-
{
|
|
205
|
-
className: "gp-offer-rise paragraph-xsmall-semibold w-fit rounded-full bg-cyan-50 px-2.5 py-1 text-cyan-700",
|
|
206
|
-
style: { animationDelay: "160ms" },
|
|
207
|
-
children: translate("common.promo.aiCreditsOffer.eyebrow")
|
|
208
|
-
}
|
|
209
|
-
),
|
|
210
|
-
/* @__PURE__ */ jsx("div", { className: "gp-offer-rise", style: { animationDelay: "220ms" }, children: /* @__PURE__ */ jsx(DialogTitle, { className: "title-large-semibold text-[26px] leading-8 font-bold tracking-[-0.4px] text-zinc-950", children: translate.rich("common.promo.aiCreditsOffer.title", {
|
|
211
|
-
credits: creditsLabel,
|
|
212
|
-
discount: discountLabel,
|
|
213
|
-
break: () => /* @__PURE__ */ jsx("br", {})
|
|
214
|
-
}) }) }),
|
|
215
|
-
/* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-3.5", children: bullets.map((bullet, index) => /* @__PURE__ */ jsxs(
|
|
216
|
-
"li",
|
|
217
|
-
{
|
|
218
|
-
className: "gp-offer-rise flex items-start gap-2.5",
|
|
219
|
-
style: { animationDelay: `${300 + index * 70}ms` },
|
|
220
|
-
children: [
|
|
221
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-cyan-50", children: /* @__PURE__ */ jsx(IconCheck, { size: 13, stroke: 3, className: "text-cyan-700" }) }),
|
|
222
|
-
/* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-zinc-600", children: bullet })
|
|
223
|
-
]
|
|
224
|
-
},
|
|
225
|
-
bullet
|
|
226
|
-
)) }),
|
|
227
|
-
/* @__PURE__ */ jsxs("div", { className: "gp-offer-rise flex flex-col gap-0.5", style: { animationDelay: "520ms" }, children: [
|
|
228
|
-
fullPrice != null && /* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-zinc-400 line-through", children: formatCurrencyNumber(fullPrice) }),
|
|
229
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
230
|
-
/* @__PURE__ */ jsx("span", { className: "title-large-semibold font-bold tracking-[-0.4px] text-zinc-950", children: formatCurrencyNumber(offerPrice) }),
|
|
231
|
-
discountLabel && /* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-semibold rounded-md bg-green-50 px-2 py-1 text-green-600", children: discountLabel })
|
|
232
|
-
] })
|
|
233
|
-
] }),
|
|
234
|
-
/* @__PURE__ */ jsx("div", { className: "gp-offer-rise mt-2", style: { animationDelay: "580ms" }, children: /* @__PURE__ */ jsx(OfferPaymentField, { cardId: selectedCardId, onCardAdded: setPickedCardId }) }),
|
|
235
|
-
/* @__PURE__ */ jsxs(
|
|
236
|
-
"div",
|
|
237
|
-
{
|
|
238
|
-
className: "gp-offer-rise mt-auto flex flex-col gap-2",
|
|
239
|
-
style: { animationDelay: "640ms" },
|
|
240
|
-
children: [
|
|
241
|
-
/* @__PURE__ */ jsx(Button, { className: "w-full", loading: isSubmitting, onClick: handleConfirm, children: hasCard ? translate("common.promo.aiCreditsOffer.cta", { discount: discountLabel }) : translate("common.promo.aiCreditsOffer.ctaAddCard") }),
|
|
242
|
-
/* @__PURE__ */ jsx(
|
|
243
|
-
Button,
|
|
244
|
-
{
|
|
245
|
-
variant: "secondary",
|
|
246
|
-
className: "h-10! w-full",
|
|
247
|
-
disabled: isSubmitting,
|
|
248
|
-
onClick: requestClose,
|
|
249
|
-
children: translate("common.promo.aiCreditsOffer.later")
|
|
250
|
-
}
|
|
251
|
-
)
|
|
252
|
-
]
|
|
253
|
-
}
|
|
254
|
-
)
|
|
255
|
-
] })
|
|
256
|
-
] })
|
|
257
|
-
}
|
|
258
|
-
) }),
|
|
259
|
-
/* @__PURE__ */ jsx(
|
|
260
|
-
LoseOfferDialog,
|
|
261
|
-
{
|
|
262
|
-
open: confirmingExit,
|
|
263
|
-
discountLabel,
|
|
264
|
-
creditsLabel,
|
|
265
|
-
fullPriceLabel: fullPrice != null ? formatCurrencyNumber(fullPrice) : "",
|
|
266
|
-
onKeep: () => setConfirmingExit(false),
|
|
267
|
-
onLose: dismiss
|
|
268
|
-
}
|
|
269
|
-
)
|
|
270
|
-
] });
|
|
271
|
-
}
|
|
272
|
-
export {
|
|
273
|
-
AiCreditsOfferModal as default
|
|
274
|
-
};
|
|
275
|
-
//# sourceMappingURL=AiCreditsOfferModal.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useState } from 'react';\nimport { useStripe } from '@stripe/react-stripe-js';\nimport { IconCheck, IconX } from '@tabler/icons-react';\nimport { useLocale, useTranslations } from 'next-intl';\nimport { toast } from 'sonner';\nimport { Button } from '../../../ui/buttons/Button';\nimport { Dialog, DialogContent, DialogTitle } from '../../../ui/overlay/Dialog';\nimport { Toast } from '../../../ui/feedback/Toast';\nimport { useAuth } from '../../../../providers/auth.provider';\nimport { useWhitelabel, useWhitelabelUrls } from '../../../../providers/whitelabel.provider';\nimport { useActiveSubscription } from '../../../../modules/subscriptions/hooks/find-active-subscription.hook';\nimport { useCards } from '../../../../modules/cards/hooks/cards.hook';\nimport { useCurrencyFormatter } from '../../../../modules/accounts/hooks/use-currency-formatter.hook';\nimport { confirmPaymentIfRequired } from '../../../../modules/subscriptions/utils/confirm-payment-if-required';\nimport {\n AI_CREDITS_FIRST_PURCHASE_OFFER,\n AiCreditsOfferStage,\n PAYMENT_METHOD_CARD,\n} from '../../../../modules/ia-credits/constants/ai-credits-offer.constants';\nimport { useAiCreditsOfferEligibility } from '../../../../modules/ia-credits/hooks/use-offer-eligibility.hook';\nimport { usePurchaseIaCredits } from '../../../../modules/ia-credits/hooks/purchase-ia-credits.hook';\nimport type { PurchaseIaCreditsResult } from '../../../../modules/ia-credits/types/ai-credits-offer.type';\nimport { readOfferStageCookie, writeOfferStageCookie } from '../../../../modules/ia-credits/utils/offer-stage-cookie';\nimport { useAiCreditsOfferModal } from '../../../../store/useAiCreditsOfferModal';\nimport { useModalManager } from '../../../../store/useModalManager';\nimport { LoseOfferDialog } from './components/LoseOfferDialog';\nimport { OfferIllustration } from './components/OfferIllustration';\nimport { OfferPaymentField } from './components/OfferPaymentField';\n\n/**\n * Oferta de primeira compra de créditos de IA — a tela que a base gratuita vê quando o\n * `AiCreditsOfferGate` decide que é a vez dela.\n *\n * Duas colunas, como a referência de produto: à esquerda a oferta e o cartão (via\n * `OfferPaymentField`, que cadastra pelo `addCardModal` padrão da plataforma); à direita o\n * gradiente da tela de login com a ilustração do editor.\n *\n * Fechar não fecha: `X`, `Esc`, clique fora e \"Agora não\" caem todos no `LoseOfferDialog`, que\n * pergunta se a pessoa quer mesmo perder o desconto.\n *\n * ⚠️ PREÇO: o card manda só o SLUG da oferta (`AI_CREDITS_FIRST_PURCHASE_OFFER.slug`) — quem\n * resolve pacote, preço e elegibilidade é o backend (Asana ID-1217333331953496). O \"de/por\" desta\n * tela usa `AI_CREDITS_FIRST_PURCHASE_OFFER.price` (fixo, parte do contrato da oferta) contra\n * `anchorCents` do `useAiCreditsOfferEligibility` (derivado do preço real do addon no plano — nunca\n * hardcoded).\n *\n * ANIMAÇÃO: CSS puro, sem `motion/react` (a lib não depende dela). As classes `gp-offer-rise`,\n * `gp-offer-window-in`, `gp-offer-card-in` e `gp-offer-icon-pulse` vêm do `globals.css` de cada app\n * consumidor — bloco documentado abaixo, precisa estar presente nos 3 apps (gpages/gapps/editor):\n *\n * ```css\n * @keyframes gp-offer-rise {\n * from { opacity: 0; translate: 0 10px; }\n * to { opacity: 1; translate: 0 0; }\n * }\n * .gp-offer-rise { animation: gp-offer-rise 420ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n *\n * @keyframes gp-offer-window-in {\n * from { opacity: 0; translate: 0 18px; scale: 0.985; }\n * to { opacity: 1; translate: 0 0; scale: 1; }\n * }\n * .gp-offer-window-in { animation: gp-offer-window-in 550ms cubic-bezier(0.22, 1, 0.36, 1) 50ms both; }\n *\n * @keyframes gp-offer-card-in {\n * from { opacity: 0; translate: 0 26px; scale: 0.97; }\n * to { opacity: 1; translate: 0 0; scale: 1; }\n * }\n * .gp-offer-card-in { animation: gp-offer-card-in 520ms cubic-bezier(0.34, 1.56, 0.64, 1) 340ms both; }\n *\n * @keyframes gp-offer-icon-pulse {\n * 0%, 100% { scale: 1; }\n * 50% { scale: 1.06; }\n * }\n * .gp-offer-icon-pulse { animation: gp-offer-icon-pulse 2.6s ease-in-out 1.6s infinite; }\n *\n * @media (prefers-reduced-motion: reduce) {\n * .gp-offer-rise, .gp-offer-window-in, .gp-offer-card-in, .gp-offer-icon-pulse { animation: none; }\n * }\n * ```\n *\n * `translate`/`scale` (propriedades CSS independentes, não `transform`) pra conviver sem conflito\n * com utilitários de posicionamento do Tailwind (mesmo problema que já forçou o `.gp-offer-modal`\n * de entrada/saída a sair de `@layer` — ver `AiCreditsOfferGate`/`globals.css`).\n */\nexport default function AiCreditsOfferModal() {\n const translate = useTranslations();\n const locale = useLocale();\n const stripe = useStripe();\n const { open, closeModal } = useAiCreditsOfferModal();\n const { openModal } = useModalManager();\n const { user } = useAuth();\n const { whitelabel } = useWhitelabel();\n const { accountsUrl } = useWhitelabelUrls();\n const { formatCurrencyNumber } = useCurrencyFormatter();\n\n const [confirmingExit, setConfirmingExit] = useState(false);\n const [pickedCardId, setPickedCardId] = useState('');\n\n const { data: cardsData } = useCards();\n const { data: subscriptionData } = useActiveSubscription();\n const eligibility = useAiCreditsOfferEligibility();\n const purchaseMutation = usePurchaseIaCredits();\n\n const subscription = subscriptionData?.data?.[0] ?? null;\n const subscriptionId = subscription?.id;\n const accountId = user?.id_account;\n\n const defaultCardId =\n cardsData?.data?.find((card) => card.is_default)?.id?.toString() ??\n cardsData?.data?.[0]?.id?.toString() ??\n '';\n\n // Os cartões chegam depois da abertura (query no client). Derivar — em vez de sincronizar num\n // efeito — faz o padrão da conta já entrar selecionado no render em que a lista chega, e a\n // escolha explícita da pessoa continua mandando.\n const pickedCardExists = cardsData?.data?.some((card) => card.id.toString() === pickedCardId);\n const selectedCardId = pickedCardExists ? pickedCardId : defaultCardId;\n\n const offerPrice = AI_CREDITS_FIRST_PURCHASE_OFFER.price;\n const fullPrice = eligibility.data?.anchorCents != null ? eligibility.data.anchorCents / 100 : null;\n const discountPercent = fullPrice != null ? Math.round((1 - offerPrice / fullPrice) * 100) : null;\n\n const creditsLabel = AI_CREDITS_FIRST_PURCHASE_OFFER.credits.toLocaleString(locale);\n const discountLabel =\n discountPercent != null\n ? translate('common.promo.aiCreditsOffer.discountBadge', { percent: String(discountPercent) })\n : '';\n\n const hasCard = Boolean(selectedCardId);\n const isSubmitting = purchaseMutation.isPending;\n\n /** Fecha de verdade — só o `LoseOfferDialog` e a compra concluída chegam aqui. */\n const dismiss = useCallback(() => {\n setConfirmingExit(false);\n closeModal();\n }, [closeModal]);\n\n /** Toda tentativa de sair passa por aqui — X, Esc, clique fora e \"Agora não\". */\n const requestClose = useCallback(() => setConfirmingExit(true), []);\n\n /**\n * Cobra o cartão informado e credita — sem tocar na recorrência do plano. Chamado tanto pelo CTA\n * com cartão já selecionado quanto (encadeado, sem segundo clique) pelo `onCardAdded` do\n * `addCardModal` quando a pessoa ainda não tinha cartão.\n */\n const submitPurchase = useCallback(\n async (cardId: string) => {\n // Sem assinatura não dá pra cobrar aqui — o CTA vira a porta do checkout do accounts em vez\n // de virar um botão morto.\n if (subscriptionId == null) {\n closeModal();\n window.location.assign(`${accountsUrl}/subscriptions?tab=plans`);\n return;\n }\n\n const input = {\n subscriptionId,\n offer: AI_CREDITS_FIRST_PURCHASE_OFFER.slug,\n paymentMethod: PAYMENT_METHOD_CARD,\n idCard: cardId,\n };\n\n try {\n let result: PurchaseIaCreditsResult = await purchaseMutation.mutateAsync(input);\n\n // 3DS: confirma o desafio e RE-POSTA o MESMO input — a chave de idempotência do backend\n // devolve a mesma cobrança já paga, então o segundo POST fecha com status 1.\n if (result.status === 2) {\n await confirmPaymentIfRequired(stripe, result, translate('common.payment.authFailed'));\n result = await purchaseMutation.mutateAsync(input);\n }\n\n // O `message` do status 0 é código de máquina do backend (`offer_consumed`,\n // `offer_card_only`…), não copy — a tela mostra o erro genérico do catálogo.\n if (result.status !== 1) {\n toast.custom((toastId) => (\n <Toast\n variant=\"error\"\n message={translate('common.promo.aiCreditsOffer.errorToast')}\n toastId={toastId}\n />\n ));\n return;\n }\n\n if (accountId != null) {\n const cookie = readOfferStageCookie(accountId);\n writeOfferStageCookie(\n accountId,\n {\n stage: AiCreditsOfferStage.CONVERTED,\n lastSessionIat: eligibility.data?.sessionIssuedAt ?? cookie.lastSessionIat,\n },\n whitelabel?.domain\n );\n }\n\n toast.custom((toastId) => (\n <Toast\n variant=\"success\"\n message={translate('common.promo.aiCreditsOffer.successToast', { credits: creditsLabel })}\n toastId={toastId}\n />\n ));\n dismiss();\n } catch {\n // Erro técnico (rede, HTTP não-JSON, resposta fora do `PurchaseIaCreditsResultSchema`) —\n // `error.message` aqui é texto de máquina (inclusive o dump do ZodError), nunca copy. Mesma\n // decisão do `status !== 1` acima: a tela mostra a mensagem genérica do catálogo.\n toast.custom((toastId) => (\n <Toast\n variant=\"error\"\n message={translate('common.promo.aiCreditsOffer.errorToast')}\n toastId={toastId}\n />\n ));\n }\n },\n [\n accountId,\n accountsUrl,\n closeModal,\n creditsLabel,\n dismiss,\n eligibility.data?.sessionIssuedAt,\n purchaseMutation,\n stripe,\n subscriptionId,\n translate,\n whitelabel?.domain,\n ]\n );\n\n const handleOpenAddCard = useCallback(() => {\n openModal('addCardModal', {\n onCardAdded: (cardId: string) => {\n setPickedCardId(cardId);\n void submitPurchase(cardId);\n },\n });\n }, [openModal, submitPurchase]);\n\n const handleConfirm = useCallback(async () => {\n // 1º passo do fluxo: sem cartão, o CTA é o próprio cadastro — a compra encadeia sozinha\n // assim que o cartão é criado (ver `handleOpenAddCard`).\n if (!hasCard) {\n handleOpenAddCard();\n return;\n }\n await submitPurchase(selectedCardId);\n }, [handleOpenAddCard, hasCard, selectedCardId, submitPurchase]);\n\n const bullets = [\n translate('common.promo.aiCreditsOffer.bullets.first'),\n translate('common.promo.aiCreditsOffer.bullets.second'),\n translate('common.promo.aiCreditsOffer.bullets.third'),\n ];\n\n return (\n <>\n <Dialog open={open} onOpenChange={() => {}}>\n <DialogContent\n showCloseButton={false}\n onOpenAutoFocus={(event) => event.preventDefault()}\n onEscapeKeyDown={(event) => {\n event.preventDefault();\n requestClose();\n }}\n onPointerDownOutside={(event) => {\n event.preventDefault();\n requestClose();\n }}\n onInteractOutside={(event) => event.preventDefault()}\n overlayClassName=\"duration-300\"\n className={[\n // mobile: folha de tela cheia (mesma geometria do BuyCreditsModal)\n 'flex flex-col gap-0 overflow-hidden rounded-t-2xl rounded-b-none border-0 p-0 shadow-elevated',\n 'top-0 right-0 bottom-0 left-0 h-dvh max-w-full translate-x-0 translate-y-0 sm:max-w-full',\n // desktop (≥ md): cartão de duas colunas, centralizado\n 'md:top-[50%] md:right-auto md:bottom-auto md:left-[50%] md:h-auto md:max-h-[92vh] md:w-[900px]! md:max-w-[900px]! md:translate-x-[-50%] md:translate-y-[-50%] md:rounded-xl md:border',\n // entrada de baixo pra cima: a classe (e não utilidade `animate-[...]`) porque o\n // `DialogContent` já traz `animate-in` e o merge do Tailwind não funde as duas — ver\n // `.gp-offer-modal` no globals.css.\n 'gp-offer-modal',\n ].join(' ')}\n >\n <div className=\"gm-scroll flex h-full flex-col overflow-y-auto md:h-auto md:min-h-[600px] md:flex-row md:items-stretch md:overflow-visible\">\n {/* Ilustração: topo no mobile, coluna da direita no desktop (`md:order-2`). */}\n <div className=\"relative h-[180px] w-full shrink-0 md:order-2 md:h-auto md:w-1/2\">\n <OfferIllustration credits={AI_CREDITS_FIRST_PURCHASE_OFFER.credits} locale={locale} />\n\n <button\n type=\"button\"\n onClick={requestClose}\n aria-label={translate('common.promo.aiCreditsOffer.close')}\n className=\"absolute top-4 right-4 z-10 flex size-8 cursor-pointer items-center justify-center rounded-lg text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-950\"\n >\n <IconX className=\"size-[18px]\" />\n </button>\n </div>\n\n {/* Coluna da oferta */}\n <div className=\"flex flex-1 flex-col gap-6 p-6 md:order-1 md:w-1/2 md:shrink-0 md:gap-7 md:p-10\">\n <span\n className=\"gp-offer-rise paragraph-xsmall-semibold w-fit rounded-full bg-cyan-50 px-2.5 py-1 text-cyan-700\"\n style={{ animationDelay: '160ms' }}\n >\n {translate('common.promo.aiCreditsOffer.eyebrow')}\n </span>\n\n {/* 26px (2px acima do `title-large`) e Outfit 700: a oferta bate mais forte que um\n título de modal comum. A quebra vem do catálogo, pela tag `<break>`, então cada\n idioma escolhe onde partir a frase. */}\n <div className=\"gp-offer-rise\" style={{ animationDelay: '220ms' }}>\n <DialogTitle className=\"title-large-semibold text-[26px] leading-8 font-bold tracking-[-0.4px] text-zinc-950\">\n {translate.rich('common.promo.aiCreditsOffer.title', {\n credits: creditsLabel,\n discount: discountLabel,\n break: () => <br />,\n })}\n </DialogTitle>\n </div>\n\n <ul className=\"flex flex-col gap-3.5\">\n {bullets.map((bullet, index) => (\n <li\n key={bullet}\n className=\"gp-offer-rise flex items-start gap-2.5\"\n style={{ animationDelay: `${300 + index * 70}ms` }}\n >\n <span className=\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-cyan-50\">\n <IconCheck size={13} stroke={3} className=\"text-cyan-700\" />\n </span>\n <span className=\"paragraph-small-medium text-zinc-600\">{bullet}</span>\n </li>\n ))}\n </ul>\n\n {/* Preço de tabela em cima, pequeno e riscado; o de campanha embaixo, na mesma\n tipografia do título (Outfit 700) pra os dois números não competirem entre si. Sem\n âncora resolvida (elegibilidade ainda carregando) some a linha do preço cheio em\n vez de mostrar um \"de\" vazio. */}\n <div className=\"gp-offer-rise flex flex-col gap-0.5\" style={{ animationDelay: '520ms' }}>\n {fullPrice != null && (\n <span className=\"paragraph-small-medium text-zinc-400 line-through\">\n {formatCurrencyNumber(fullPrice)}\n </span>\n )}\n <div className=\"flex items-center gap-2.5\">\n <span className=\"title-large-semibold font-bold tracking-[-0.4px] text-zinc-950\">\n {formatCurrencyNumber(offerPrice)}\n </span>\n {discountLabel && (\n <span className=\"paragraph-xsmall-semibold rounded-md bg-green-50 px-2 py-1 text-green-600\">\n {discountLabel}\n </span>\n )}\n </div>\n </div>\n\n <div className=\"gp-offer-rise mt-2\" style={{ animationDelay: '580ms' }}>\n <OfferPaymentField cardId={selectedCardId} onCardAdded={setPickedCardId} />\n </div>\n\n <div\n className=\"gp-offer-rise mt-auto flex flex-col gap-2\"\n style={{ animationDelay: '640ms' }}\n >\n <Button className=\"w-full\" loading={isSubmitting} onClick={handleConfirm}>\n {hasCard\n ? translate('common.promo.aiCreditsOffer.cta', { discount: discountLabel })\n : translate('common.promo.aiCreditsOffer.ctaAddCard')}\n </Button>\n <Button\n variant=\"secondary\"\n className=\"h-10! w-full\"\n disabled={isSubmitting}\n onClick={requestClose}\n >\n {translate('common.promo.aiCreditsOffer.later')}\n </Button>\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n <LoseOfferDialog\n open={confirmingExit}\n discountLabel={discountLabel}\n creditsLabel={creditsLabel}\n fullPriceLabel={fullPrice != null ? formatCurrencyNumber(fullPrice) : ''}\n onKeep={() => setConfirmingExit(false)}\n onLose={dismiss}\n />\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useTranslations } from "next-intl";
|
|
4
|
-
import { Button } from "../../../../ui/buttons/Button";
|
|
5
|
-
import {
|
|
6
|
-
Dialog,
|
|
7
|
-
DialogContent,
|
|
8
|
-
DialogDescription,
|
|
9
|
-
DialogHeader,
|
|
10
|
-
DialogTitle
|
|
11
|
-
} from "../../../../ui/overlay/Dialog";
|
|
12
|
-
import { Separator } from "../../../../ui/data-display/Separator";
|
|
13
|
-
import { IconAlertTriangle } from "@tabler/icons-react";
|
|
14
|
-
function LoseOfferDialog({
|
|
15
|
-
open,
|
|
16
|
-
discountLabel,
|
|
17
|
-
creditsLabel,
|
|
18
|
-
fullPriceLabel,
|
|
19
|
-
onKeep,
|
|
20
|
-
onLose
|
|
21
|
-
}) {
|
|
22
|
-
const translate = useTranslations();
|
|
23
|
-
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => !next && onKeep(), children: /* @__PURE__ */ jsxs(
|
|
24
|
-
DialogContent,
|
|
25
|
-
{
|
|
26
|
-
showCloseButton: false,
|
|
27
|
-
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
28
|
-
overlayClassName: "z-[1002] bg-black/40",
|
|
29
|
-
className: "z-[1003] flex flex-col gap-0 p-0 max-[440px]:w-[calc(100%-32px)] md:w-[420px]! lg:w-[420px]! max-w-[420px]! data-[state=open]:animate-[gp-offer-confirm-in_260ms_cubic-bezier(0.16,1,0.3,1)]",
|
|
30
|
-
children: [
|
|
31
|
-
/* @__PURE__ */ jsxs(DialogHeader, { className: "gap-3 p-5 text-left", children: [
|
|
32
|
-
/* @__PURE__ */ jsx("div", { className: "flex w-fit items-center justify-center rounded-lg bg-red-50 p-2.5", children: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 20, className: "text-red-500" }) }),
|
|
33
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
34
|
-
/* @__PURE__ */ jsx(DialogTitle, { className: "paragraph-medium-semibold text-zinc-950", children: translate("common.promo.aiCreditsOffer.exit.title", { discount: discountLabel }) }),
|
|
35
|
-
/* @__PURE__ */ jsx(DialogDescription, { className: "paragraph-small-regular text-zinc-600", children: fullPriceLabel ? translate("common.promo.aiCreditsOffer.exit.description", {
|
|
36
|
-
credits: creditsLabel,
|
|
37
|
-
price: fullPriceLabel
|
|
38
|
-
}) : translate("common.promo.aiCreditsOffer.exit.descriptionNoPrice", {
|
|
39
|
-
credits: creditsLabel
|
|
40
|
-
}) })
|
|
41
|
-
] })
|
|
42
|
-
] }),
|
|
43
|
-
/* @__PURE__ */ jsx(Separator, {}),
|
|
44
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 p-5", children: [
|
|
45
|
-
/* @__PURE__ */ jsx(Button, { className: "h-10! transition-colors", onClick: onKeep, children: translate("common.promo.aiCreditsOffer.exit.keep") }),
|
|
46
|
-
/* @__PURE__ */ jsx(Button, { variant: "secondary", className: "h-10! transition-colors", onClick: onLose, children: translate("common.promo.aiCreditsOffer.exit.lose") })
|
|
47
|
-
] })
|
|
48
|
-
]
|
|
49
|
-
}
|
|
50
|
-
) });
|
|
51
|
-
}
|
|
52
|
-
export {
|
|
53
|
-
LoseOfferDialog
|
|
54
|
-
};
|
|
55
|
-
//# sourceMappingURL=LoseOfferDialog.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { Button } from '../../../../ui/buttons/Button';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../../../../ui/overlay/Dialog';\nimport { Separator } from '../../../../ui/data-display/Separator';\nimport { IconAlertTriangle } from '@tabler/icons-react';\n\ninterface LoseOfferDialogProps {\n open: boolean;\n /** Selo do desconto (\"50% OFF\") — repetido aqui porque é ele que dói. */\n discountLabel: string;\n /** Pacote ofertado, já formatado (\"1.000\"). */\n creditsLabel: string;\n /** Preço cheio formatado: o número que volta a valer. Vazio enquanto a âncora carrega. */\n fullPriceLabel: string;\n /** Volta pra oferta (fecha só esta confirmação). */\n onKeep: () => void;\n /** Abre mão da oferta e fecha tudo. */\n onLose: () => void;\n}\n\n/**\n * Confirmação de saída (\"quer mesmo perder essa oferta?\").\n *\n * É um Dialog SEPARADO, empilhado por cima da oferta com os mesmos z-index do\n * `InsufficientIaCreditsModal` (overlay 1002 / conteúdo 1003) — assim a oferta continua **visível\n * atrás do véu**: a pessoa decide olhando exatamente para o que está prestes a perder, que é o\n * ponto inteiro desta tela.\n *\n * O principal é VOLTAR para a oferta; perder fica como secundário, a um clique de distância. A\n * saída não está escondida — o peso da decisão está na frase, não no botão.\n */\nexport function LoseOfferDialog({\n open,\n discountLabel,\n creditsLabel,\n fullPriceLabel,\n onKeep,\n onLose,\n}: LoseOfferDialogProps) {\n const translate = useTranslations();\n\n return (\n <Dialog open={open} onOpenChange={(next) => !next && onKeep()}>\n <DialogContent\n showCloseButton={false}\n onOpenAutoFocus={(event) => event.preventDefault()}\n overlayClassName=\"z-[1002] bg-black/40\"\n className=\"z-[1003] flex flex-col gap-0 p-0 max-[440px]:w-[calc(100%-32px)] md:w-[420px]! lg:w-[420px]! max-w-[420px]! data-[state=open]:animate-[gp-offer-confirm-in_260ms_cubic-bezier(0.16,1,0.3,1)]\"\n >\n <DialogHeader className=\"gap-3 p-5 text-left\">\n <div className=\"flex w-fit items-center justify-center rounded-lg bg-red-50 p-2.5\">\n <IconAlertTriangle size={20} className=\"text-red-500\" />\n </div>\n <div className=\"flex flex-col gap-2\">\n <DialogTitle className=\"paragraph-medium-semibold text-zinc-950\">\n {translate('common.promo.aiCreditsOffer.exit.title', { discount: discountLabel })}\n </DialogTitle>\n <DialogDescription className=\"paragraph-small-regular text-zinc-600\">\n {/* Sem preço resolvido (elegibilidade ainda carregando) a frase cai na versão sem\n número — prometer \"volta a custar\" sem dizer quanto é pior do que não dizer. */}\n {fullPriceLabel\n ? translate('common.promo.aiCreditsOffer.exit.description', {\n credits: creditsLabel,\n price: fullPriceLabel,\n })\n : translate('common.promo.aiCreditsOffer.exit.descriptionNoPrice', {\n credits: creditsLabel,\n })}\n </DialogDescription>\n </div>\n </DialogHeader>\n\n <Separator />\n\n {/* Rodapé com os dois botões juntos à esquerda (`gap-2`, sem `justify-between`), o\n principal primeiro. Ficar é o caminho principal; perder a oferta continua a um clique,\n mas como ação secundária. */}\n <div className=\"flex items-center gap-2 p-5\">\n <Button className=\"h-10! transition-colors\" onClick={onKeep}>\n {translate('common.promo.aiCreditsOffer.exit.keep')}\n </Button>\n <Button variant=\"secondary\" className=\"h-10! transition-colors\" onClick={onLose}>\n {translate('common.promo.aiCreditsOffer.exit.lose')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":";AA2DY,cAEF,YAFE;AAzDZ,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AA2B3B,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,YAAY,gBAAgB;AAElC,SACE,oBAAC,UAAO,MAAY,cAAc,CAAC,SAAS,CAAC,QAAQ,OAAO,GAC1D;AAAA,IAAC;AAAA;AAAA,MACC,iBAAiB;AAAA,MACjB,iBAAiB,CAAC,UAAU,MAAM,eAAe;AAAA,MACjD,kBAAiB;AAAA,MACjB,WAAU;AAAA,MAEV;AAAA,6BAAC,gBAAa,WAAU,uBACtB;AAAA,8BAAC,SAAI,WAAU,qEACb,8BAAC,qBAAkB,MAAM,IAAI,WAAU,gBAAe,GACxD;AAAA,UACA,qBAAC,SAAI,WAAU,uBACb;AAAA,gCAAC,eAAY,WAAU,2CACpB,oBAAU,0CAA0C,EAAE,UAAU,cAAc,CAAC,GAClF;AAAA,YACA,oBAAC,qBAAkB,WAAU,yCAG1B,2BACG,UAAU,gDAAgD;AAAA,cACxD,SAAS;AAAA,cACT,OAAO;AAAA,YACT,CAAC,IACD,UAAU,uDAAuD;AAAA,cAC/D,SAAS;AAAA,YACX,CAAC,GACP;AAAA,aACF;AAAA,WACF;AAAA,QAEA,oBAAC,aAAU;AAAA,QAKX,qBAAC,SAAI,WAAU,+BACb;AAAA,8BAAC,UAAO,WAAU,2BAA0B,SAAS,QAClD,oBAAU,uCAAuC,GACpD;AAAA,UACA,oBAAC,UAAO,SAAQ,aAAY,WAAU,2BAA0B,SAAS,QACtE,oBAAU,uCAAuC,GACpD;AAAA,WACF;AAAA;AAAA;AAAA,EACF,GACF;AAEJ;","names":[]}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useState } from "react";
|
|
4
|
-
import Image from "next/image";
|
|
5
|
-
import { useTranslations } from "next-intl";
|
|
6
|
-
const COUNT_DELAY_MS = 620;
|
|
7
|
-
const COUNT_DURATION_MS = 1150;
|
|
8
|
-
function easeOutCubic(t) {
|
|
9
|
-
return 1 - (1 - t) ** 3;
|
|
10
|
-
}
|
|
11
|
-
function OfferIllustration({ credits, locale }) {
|
|
12
|
-
const translate = useTranslations();
|
|
13
|
-
const [displayCredits, setDisplayCredits] = useState(0);
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
if (typeof window === "undefined") return;
|
|
16
|
-
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
17
|
-
setDisplayCredits(credits);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
let frameId;
|
|
21
|
-
const startedAt = performance.now() + COUNT_DELAY_MS;
|
|
22
|
-
const tick = (now) => {
|
|
23
|
-
const elapsed = now - startedAt;
|
|
24
|
-
if (elapsed < 0) {
|
|
25
|
-
frameId = requestAnimationFrame(tick);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const progress = Math.min(1, elapsed / COUNT_DURATION_MS);
|
|
29
|
-
setDisplayCredits(Math.round(credits * easeOutCubic(progress)));
|
|
30
|
-
if (progress < 1) frameId = requestAnimationFrame(tick);
|
|
31
|
-
};
|
|
32
|
-
frameId = requestAnimationFrame(tick);
|
|
33
|
-
return () => cancelAnimationFrame(frameId);
|
|
34
|
-
}, [credits]);
|
|
35
|
-
return /* @__PURE__ */ jsxs("div", { className: "relative h-full w-full overflow-hidden", children: [
|
|
36
|
-
/* @__PURE__ */ jsx(
|
|
37
|
-
Image,
|
|
38
|
-
{
|
|
39
|
-
src: "/promo/offer-panel-background.svg",
|
|
40
|
-
alt: "",
|
|
41
|
-
fill: true,
|
|
42
|
-
priority: true,
|
|
43
|
-
draggable: false,
|
|
44
|
-
className: "object-cover select-none"
|
|
45
|
-
}
|
|
46
|
-
),
|
|
47
|
-
/* @__PURE__ */ jsx(
|
|
48
|
-
"div",
|
|
49
|
-
{
|
|
50
|
-
"aria-hidden": true,
|
|
51
|
-
className: "gp-offer-window-in absolute top-[calc(50%-27px)] left-[calc(50%+98px)] aspect-[557.57/388.22] w-[132%] -translate-x-1/2 -translate-y-1/2 md:w-[124.2%]",
|
|
52
|
-
children: /* @__PURE__ */ jsx(
|
|
53
|
-
Image,
|
|
54
|
-
{
|
|
55
|
-
src: "/promo/offer-editor-window.svg",
|
|
56
|
-
alt: "",
|
|
57
|
-
fill: true,
|
|
58
|
-
sizes: "560px",
|
|
59
|
-
priority: true,
|
|
60
|
-
draggable: false,
|
|
61
|
-
className: "select-none"
|
|
62
|
-
}
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
),
|
|
66
|
-
/* @__PURE__ */ jsxs("div", { className: "gp-offer-card-in absolute top-[calc(50%+146px)] left-[49.7%] hidden h-[66px] w-[193.5px] items-center gap-[11.5px] rounded-[15px] border border-[#8BE4FF]/38 bg-white pl-[11.5px] shadow-[0px_4px_25px_0px_rgba(26,26,26,0.03)] md:flex", children: [
|
|
67
|
-
/* @__PURE__ */ jsx("span", { className: "gp-offer-icon-pulse flex size-[43.5px] shrink-0 items-center justify-center rounded-[9px] bg-cyan-50", children: /* @__PURE__ */ jsx("img", { src: "/iaicon.svg", alt: "", width: 24, height: 24, className: "block" }) }),
|
|
68
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-baseline gap-1 whitespace-nowrap", children: [
|
|
69
|
-
/* @__PURE__ */ jsx("span", { className: "title-large-semibold text-[25px] leading-[29px] tabular-nums text-zinc-950", children: displayCredits.toLocaleString(locale) }),
|
|
70
|
-
/* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-[14.5px] text-zinc-500", children: translate("common.promo.aiCreditsOffer.illustration.credits") })
|
|
71
|
-
] })
|
|
72
|
-
] })
|
|
73
|
-
] });
|
|
74
|
-
}
|
|
75
|
-
export {
|
|
76
|
-
OfferIllustration
|
|
77
|
-
};
|
|
78
|
-
//# sourceMappingURL=OfferIllustration.mjs.map
|
package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferIllustration.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/components/modals/promo/AiCreditsOfferModal/components/OfferIllustration.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\nimport Image from 'next/image';\nimport { useTranslations } from 'next-intl';\n\n/**\n * Painel direito da oferta — implementação do design do Figma (`Testes-UI`, nodes `2569:2938` e\n * `2569:4015`), conferida contra o SVG exportado do bloco inteiro.\n *\n * A geometria vem do arquivo, desenhado num painel de **449×606** — praticamente o tamanho real\n * desta coluna (metade de um modal de 900). Por isso os offsets são os px do próprio design,\n * ancorados ao CENTRO do painel: a janela tem altura derivada da largura, então âncora percentual\n * desmontaria a composição quando o modal mudasse de altura; a partir do centro, a distância entre\n * as peças se mantém.\n *\n * O fundo e a janela são os VETORES do arquivo (`offer-panel-background.svg` e\n * `offer-editor-window.svg`, em `public/promo/` do app consumidor — a lib não publica assets\n * estáticos), fatiados do SVG do bloco; o cartão é DOM, pra o contador animar.\n *\n * Animações em CSS puro (classes `gp-offer-*`, ver o bloco no topo do `AiCreditsOfferModal.tsx`) —\n * respeitam `prefers-reduced-motion` por conta própria, sem depender de JS. Só o CONTADOR do\n * cartão é JS (`requestAnimationFrame`): não dá pra fazer \"500\" nascer de \"0\" em CSS puro.\n */\n\nconst COUNT_DELAY_MS = 620;\nconst COUNT_DURATION_MS = 1150;\n\nfunction easeOutCubic(t: number): number {\n return 1 - (1 - t) ** 3;\n}\n\ninterface OfferIllustrationProps {\n /** Pacote ofertado — é ele que o contador persegue. */\n credits: number;\n /** Locale de formatação do número (vem do locale da UI). */\n locale: string;\n}\n\nexport function OfferIllustration({ credits, locale }: OfferIllustrationProps) {\n const translate = useTranslations();\n const [displayCredits, setDisplayCredits] = useState(0);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {\n setDisplayCredits(credits);\n return;\n }\n\n let frameId: number;\n const startedAt = performance.now() + COUNT_DELAY_MS;\n\n const tick = (now: number) => {\n const elapsed = now - startedAt;\n if (elapsed < 0) {\n frameId = requestAnimationFrame(tick);\n return;\n }\n const progress = Math.min(1, elapsed / COUNT_DURATION_MS);\n setDisplayCredits(Math.round(credits * easeOutCubic(progress)));\n if (progress < 1) frameId = requestAnimationFrame(tick);\n };\n\n frameId = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(frameId);\n }, [credits]);\n\n return (\n <div className=\"relative h-full w-full overflow-hidden\">\n {/* Fundo: o próprio vetor do arquivo (gradiente + o brilho difuso da quina), extraído do SVG\n do bloco. Vem como imagem, e não como gradiente CSS + `blur`, pra não haver interpretação\n no meio: a cor e o desfoque são os do design, byte a byte. 4,7KB. */}\n <Image\n src=\"/promo/offer-panel-background.svg\"\n alt=\"\"\n fill\n priority\n draggable={false}\n className=\"object-cover select-none\"\n />\n\n {/* Janela do editor — o vetor exportado do próprio arquivo (chrome, moldura branca, sombra e\n o print do canvas embutido). `gp-offer-window-in` anima `translate`/`scale` (propriedades\n CSS independentes do `transform`), então convive sem conflito com o\n `-translate-x-1/2 -translate-y-1/2` do Tailwind que centraliza o elemento. */}\n <div\n aria-hidden\n className=\"gp-offer-window-in absolute top-[calc(50%-27px)] left-[calc(50%+98px)] aspect-[557.57/388.22] w-[132%] -translate-x-1/2 -translate-y-1/2 md:w-[124.2%]\"\n >\n <Image\n src=\"/promo/offer-editor-window.svg\"\n alt=\"\"\n fill\n sizes=\"560px\"\n priority\n draggable={false}\n className=\"select-none\"\n />\n </div>\n\n {/* Cartão de créditos (node 2569:4015), solto abaixo da janela, com a folga de gradiente que\n o arquivo tem. Só no desktop: no mobile a faixa é baixa e ele brigaria com a janela. */}\n <div className=\"gp-offer-card-in absolute top-[calc(50%+146px)] left-[49.7%] hidden h-[66px] w-[193.5px] items-center gap-[11.5px] rounded-[15px] border border-[#8BE4FF]/38 bg-white pl-[11.5px] shadow-[0px_4px_25px_0px_rgba(26,26,26,0.03)] md:flex\">\n <span className=\"gp-offer-icon-pulse flex size-[43.5px] shrink-0 items-center justify-center rounded-[9px] bg-cyan-50\">\n {/* eslint-disable-next-line @next/next/no-img-element -- ícone de 39KB, pesado demais pra inlinear no componente da lib. */}\n <img src=\"/iaicon.svg\" alt=\"\" width={24} height={24} className=\"block\" />\n </span>\n <span className=\"flex items-baseline gap-1 whitespace-nowrap\">\n <span className=\"title-large-semibold text-[25px] leading-[29px] tabular-nums text-zinc-950\">\n {displayCredits.toLocaleString(locale)}\n </span>\n <span className=\"paragraph-small-medium text-[14.5px] text-zinc-500\">\n {translate('common.promo.aiCreditsOffer.illustration.credits')}\n </span>\n </span>\n </div>\n </div>\n );\n}\n"],"mappings":";AA0EM,cAmCE,YAnCF;AAxEN,SAAS,WAAW,gBAAgB;AACpC,OAAO,WAAW;AAClB,SAAS,uBAAuB;AAqBhC,MAAM,iBAAiB;AACvB,MAAM,oBAAoB;AAE1B,SAAS,aAAa,GAAmB;AACvC,SAAO,KAAK,IAAI,MAAM;AACxB;AASO,SAAS,kBAAkB,EAAE,SAAS,OAAO,GAA2B;AAC7E,QAAM,YAAY,gBAAgB;AAClC,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAS,CAAC;AAEtD,YAAU,MAAM;AACd,QAAI,OAAO,WAAW,YAAa;AAEnC,QAAI,OAAO,WAAW,kCAAkC,EAAE,SAAS;AACjE,wBAAkB,OAAO;AACzB;AAAA,IACF;AAEA,QAAI;AACJ,UAAM,YAAY,YAAY,IAAI,IAAI;AAEtC,UAAM,OAAO,CAAC,QAAgB;AAC5B,YAAM,UAAU,MAAM;AACtB,UAAI,UAAU,GAAG;AACf,kBAAU,sBAAsB,IAAI;AACpC;AAAA,MACF;AACA,YAAM,WAAW,KAAK,IAAI,GAAG,UAAU,iBAAiB;AACxD,wBAAkB,KAAK,MAAM,UAAU,aAAa,QAAQ,CAAC,CAAC;AAC9D,UAAI,WAAW,EAAG,WAAU,sBAAsB,IAAI;AAAA,IACxD;AAEA,cAAU,sBAAsB,IAAI;AACpC,WAAO,MAAM,qBAAqB,OAAO;AAAA,EAC3C,GAAG,CAAC,OAAO,CAAC;AAEZ,SACE,qBAAC,SAAI,WAAU,0CAIb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAI;AAAA,QACJ,KAAI;AAAA,QACJ,MAAI;AAAA,QACJ,UAAQ;AAAA,QACR,WAAW;AAAA,QACX,WAAU;AAAA;AAAA,IACZ;AAAA,IAMA;AAAA,MAAC;AAAA;AAAA,QACC,eAAW;AAAA,QACX,WAAU;AAAA,QAEV;AAAA,UAAC;AAAA;AAAA,YACC,KAAI;AAAA,YACJ,KAAI;AAAA,YACJ,MAAI;AAAA,YACJ,OAAM;AAAA,YACN,UAAQ;AAAA,YACR,WAAW;AAAA,YACX,WAAU;AAAA;AAAA,QACZ;AAAA;AAAA,IACF;AAAA,IAIA,qBAAC,SAAI,WAAU,2OACb;AAAA,0BAAC,UAAK,WAAU,wGAEd,8BAAC,SAAI,KAAI,eAAc,KAAI,IAAG,OAAO,IAAI,QAAQ,IAAI,WAAU,SAAQ,GACzE;AAAA,MACA,qBAAC,UAAK,WAAU,+CACd;AAAA,4BAAC,UAAK,WAAU,8EACb,yBAAe,eAAe,MAAM,GACvC;AAAA,QACA,oBAAC,UAAK,WAAU,sDACb,oBAAU,kDAAkD,GAC/D;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useTranslations } from "next-intl";
|
|
4
|
-
import { CardBrandIcon } from "../../../../ui/data-display/CardBrandIcons";
|
|
5
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from "../../../../ui/overlay/Tooltip";
|
|
6
|
-
import { useCards } from "../../../../../modules/cards/hooks/cards.hook";
|
|
7
|
-
import { useModalManager } from "../../../../../store/useModalManager";
|
|
8
|
-
import { IconPencil, IconPlus } from "@tabler/icons-react";
|
|
9
|
-
function OfferPaymentField({ cardId, onCardAdded }) {
|
|
10
|
-
const translate = useTranslations();
|
|
11
|
-
const { openModal } = useModalManager();
|
|
12
|
-
const { data: cardsData } = useCards();
|
|
13
|
-
const card = cardsData?.data?.find((item) => item.id.toString() === cardId.toString());
|
|
14
|
-
const handleOpenCardModal = () => {
|
|
15
|
-
openModal("addCardModal", { onCardAdded });
|
|
16
|
-
};
|
|
17
|
-
return /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border border-zinc-200", children: /* @__PURE__ */ jsxs("div", { className: "flex h-[60px] items-center gap-3 px-4", children: [
|
|
18
|
-
/* @__PURE__ */ jsx("span", { className: "paragraph-small-medium w-[100px] text-zinc-500", children: translate("common.cards.paymentInfo.payment") }),
|
|
19
|
-
card ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
20
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
21
|
-
/* @__PURE__ */ jsx("span", { className: "paragraph-small-semibold truncate text-zinc-950", children: card.name }),
|
|
22
|
-
/* @__PURE__ */ jsxs("span", { className: "paragraph-small-medium text-zinc-950", children: [
|
|
23
|
-
"\u2022\u2022\u2022\u2022 ",
|
|
24
|
-
card.number?.replaceAll(/\D/g, "")?.slice(-4)
|
|
25
|
-
] })
|
|
26
|
-
] }),
|
|
27
|
-
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
28
|
-
/* @__PURE__ */ jsx(CardBrandIcon, { brand: card.brand }),
|
|
29
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
30
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
31
|
-
"button",
|
|
32
|
-
{
|
|
33
|
-
type: "button",
|
|
34
|
-
onClick: handleOpenCardModal,
|
|
35
|
-
"aria-label": translate("common.promo.aiCreditsOffer.editPayment"),
|
|
36
|
-
className: "flex size-8 cursor-pointer items-center justify-center rounded-lg text-zinc-400 transition-colors hover:bg-zinc-100 hover:text-zinc-950",
|
|
37
|
-
children: /* @__PURE__ */ jsx(IconPencil, { size: 18 })
|
|
38
|
-
}
|
|
39
|
-
) }),
|
|
40
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "top", sideOffset: 6, className: "z-[1002]", children: translate("common.promo.aiCreditsOffer.editPayment") })
|
|
41
|
-
] })
|
|
42
|
-
] })
|
|
43
|
-
] }) : /* @__PURE__ */ jsxs(
|
|
44
|
-
"button",
|
|
45
|
-
{
|
|
46
|
-
type: "button",
|
|
47
|
-
onClick: handleOpenCardModal,
|
|
48
|
-
className: "paragraph-small-semibold flex h-8 w-fit cursor-pointer items-center rounded-md border border-zinc-200 px-2 text-zinc-950 transition-colors hover:border-zinc-400",
|
|
49
|
-
children: [
|
|
50
|
-
/* @__PURE__ */ jsx(IconPlus, { className: "size-[18px]" }),
|
|
51
|
-
/* @__PURE__ */ jsx("span", { className: "px-1.5", children: translate("common.cards.paymentInfo.addCard") })
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
)
|
|
55
|
-
] }) });
|
|
56
|
-
}
|
|
57
|
-
export {
|
|
58
|
-
OfferPaymentField
|
|
59
|
-
};
|
|
60
|
-
//# sourceMappingURL=OfferPaymentField.mjs.map
|
package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { CardBrandIcon } from '../../../../ui/data-display/CardBrandIcons';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../../../ui/overlay/Tooltip';\nimport { useCards } from '../../../../../modules/cards/hooks/cards.hook';\nimport { useModalManager } from '../../../../../store/useModalManager';\nimport { IconPencil, IconPlus } from '@tabler/icons-react';\n\ninterface OfferPaymentFieldProps {\n /** Cartão em uso (padrão da conta ou o que acabou de ser cadastrado). */\n cardId: string;\n /** Recebe o id do cartão cadastrado no `addCardModal`. */\n onCardAdded: (cardId: string) => void;\n}\n\n/**\n * Campo de pagamento da oferta — versão de **um cartão só**.\n *\n * Nasceu do `PaymentInfoCard` (mesma moldura, mesma linha \"Pagamento\", mesmo `CardBrandIcon`,\n * mesmas chaves de tradução `common.cards.*`), mas sem o seletor e sem o \"Nova forma de\n * pagamento\": aqui a pessoa está numa compra única de primeira vez, então ou ela ainda não tem\n * cartão (e o campo é só o botão de cadastrar) ou já tem (e o campo é só o cartão). Oferecer uma\n * lista de cartões numa tela cujo objetivo é UM clique era ruído.\n *\n * Com cartão, o lápis fica sempre visível e abre o `addCardModal` — o mesmo fluxo padrão da\n * plataforma, que é por onde se troca a forma de pagamento (não existe edição de cartão: o\n * gateway guarda o dado, então \"editar\" é cadastrar outro). Nada de formulário próprio.\n */\nexport function OfferPaymentField({ cardId, onCardAdded }: OfferPaymentFieldProps) {\n const translate = useTranslations();\n const { openModal } = useModalManager();\n const { data: cardsData } = useCards();\n\n const card = cardsData?.data?.find((item) => item.id.toString() === cardId.toString());\n\n const handleOpenCardModal = () => {\n openModal('addCardModal', { onCardAdded });\n };\n\n return (\n <div className=\"overflow-hidden rounded-lg border border-zinc-200\">\n <div className=\"flex h-[60px] items-center gap-3 px-4\">\n <span className=\"paragraph-small-medium w-[100px] text-zinc-500\">\n {translate('common.cards.paymentInfo.payment')}\n </span>\n\n {card ? (\n <>\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span className=\"paragraph-small-semibold truncate text-zinc-950\">{card.name}</span>\n <span className=\"paragraph-small-medium text-zinc-950\">\n •••• {card.number?.replaceAll(/\\D/g, '')?.slice(-4)}\n </span>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-2\">\n <CardBrandIcon brand={card.brand} />\n {/* `z-[1002]`: o tooltip nasce num portal e precisa passar por cima do\n `DialogContent` (z-1001), senão fica escondido atrás do modal. */}\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n onClick={handleOpenCardModal}\n aria-label={translate('common.promo.aiCreditsOffer.editPayment')}\n className=\"flex size-8 cursor-pointer items-center justify-center rounded-lg text-zinc-400 transition-colors hover:bg-zinc-100 hover:text-zinc-950\"\n >\n <IconPencil size={18} />\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"top\" sideOffset={6} className=\"z-[1002]\">\n {translate('common.promo.aiCreditsOffer.editPayment')}\n </TooltipContent>\n </Tooltip>\n </div>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={handleOpenCardModal}\n className=\"paragraph-small-semibold flex h-8 w-fit cursor-pointer items-center rounded-md border border-zinc-200 px-2 text-zinc-950 transition-colors hover:border-zinc-400\"\n >\n <IconPlus className=\"size-[18px]\" />\n <span className=\"px-1.5\">{translate('common.cards.paymentInfo.addCard')}</span>\n </button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";AA2CQ,SAKE,UALF,KAQM,YARN;AAzCR,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,gBAAgB;AACzB,SAAS,uBAAuB;AAChC,SAAS,YAAY,gBAAgB;AAsB9B,SAAS,kBAAkB,EAAE,QAAQ,YAAY,GAA2B;AACjF,QAAM,YAAY,gBAAgB;AAClC,QAAM,EAAE,UAAU,IAAI,gBAAgB;AACtC,QAAM,EAAE,MAAM,UAAU,IAAI,SAAS;AAErC,QAAM,OAAO,WAAW,MAAM,KAAK,CAAC,SAAS,KAAK,GAAG,SAAS,MAAM,OAAO,SAAS,CAAC;AAErF,QAAM,sBAAsB,MAAM;AAChC,cAAU,gBAAgB,EAAE,YAAY,CAAC;AAAA,EAC3C;AAEA,SACE,oBAAC,SAAI,WAAU,qDACb,+BAAC,SAAI,WAAU,yCACb;AAAA,wBAAC,UAAK,WAAU,kDACb,oBAAU,kCAAkC,GAC/C;AAAA,IAEC,OACC,iCACE;AAAA,2BAAC,SAAI,WAAU,wCACb;AAAA,4BAAC,UAAK,WAAU,mDAAmD,eAAK,MAAK;AAAA,QAC7E,qBAAC,UAAK,WAAU,wCAAuC;AAAA;AAAA,UAC/C,KAAK,QAAQ,WAAW,OAAO,EAAE,GAAG,MAAM,EAAE;AAAA,WACpD;AAAA,SACF;AAAA,MAEA,qBAAC,SAAI,WAAU,oCACb;AAAA,4BAAC,iBAAc,OAAO,KAAK,OAAO;AAAA,QAGlC,qBAAC,WACC;AAAA,8BAAC,kBAAe,SAAO,MACrB;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,cAAY,UAAU,yCAAyC;AAAA,cAC/D,WAAU;AAAA,cAEV,8BAAC,cAAW,MAAM,IAAI;AAAA;AAAA,UACxB,GACF;AAAA,UACA,oBAAC,kBAAe,MAAK,OAAM,YAAY,GAAG,WAAU,YACjD,oBAAU,yCAAyC,GACtD;AAAA,WACF;AAAA,SACF;AAAA,OACF,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QAEV;AAAA,8BAAC,YAAS,WAAU,eAAc;AAAA,UAClC,oBAAC,UAAK,WAAU,UAAU,oBAAU,kCAAkC,GAAE;AAAA;AAAA;AAAA,IAC1E;AAAA,KAEJ,GACF;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/modals/promo/AiCreditsOfferModal/index.ts"],"sourcesContent":["export { default } from './AiCreditsOfferModal';\n"],"mappings":"AAAA,SAAS,WAAAA,gBAAe;","names":["default"]}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const AI_CREDITS_FIRST_PURCHASE_OFFER = {
|
|
2
|
-
slug: "first-purchase-ia-credits",
|
|
3
|
-
credits: 500,
|
|
4
|
-
price: 4.9
|
|
5
|
-
};
|
|
6
|
-
const PAYMENT_METHOD_CARD = 1;
|
|
7
|
-
const PAYMENT_METHOD_PIX = 3;
|
|
8
|
-
var AiCreditsOfferStage = /* @__PURE__ */ ((AiCreditsOfferStage2) => {
|
|
9
|
-
AiCreditsOfferStage2[AiCreditsOfferStage2["NONE"] = 0] = "NONE";
|
|
10
|
-
AiCreditsOfferStage2[AiCreditsOfferStage2["CAMPAIGN_SHOWN"] = 1] = "CAMPAIGN_SHOWN";
|
|
11
|
-
AiCreditsOfferStage2[AiCreditsOfferStage2["FIRST_USE_SHOWN"] = 2] = "FIRST_USE_SHOWN";
|
|
12
|
-
AiCreditsOfferStage2[AiCreditsOfferStage2["HALF_SHOWN"] = 3] = "HALF_SHOWN";
|
|
13
|
-
AiCreditsOfferStage2[AiCreditsOfferStage2["EXHAUSTED_SHOWN"] = 4] = "EXHAUSTED_SHOWN";
|
|
14
|
-
AiCreditsOfferStage2[AiCreditsOfferStage2["ZERO_SHOWN"] = 5] = "ZERO_SHOWN";
|
|
15
|
-
AiCreditsOfferStage2[AiCreditsOfferStage2["CONVERTED"] = 6] = "CONVERTED";
|
|
16
|
-
return AiCreditsOfferStage2;
|
|
17
|
-
})(AiCreditsOfferStage || {});
|
|
18
|
-
const AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY = ["ai-credits-offer-eligibility"];
|
|
19
|
-
export {
|
|
20
|
-
AI_CREDITS_FIRST_PURCHASE_OFFER,
|
|
21
|
-
AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY,
|
|
22
|
-
AiCreditsOfferStage,
|
|
23
|
-
PAYMENT_METHOD_CARD,
|
|
24
|
-
PAYMENT_METHOD_PIX
|
|
25
|
-
};
|
|
26
|
-
//# sourceMappingURL=ai-credits-offer.constants.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/ia-credits/constants/ai-credits-offer.constants.ts"],"sourcesContent":["/**\n * Oferta de primeira compra de créditos de IA — o slug é o contrato inteiro com o backend\n * (gapps-r3-api): ele resolve pacote/preço/elegibilidade a partir dele, o front nunca manda preço.\n * `credits`/`price` aqui são só o que a UI precisa ANTES do primeiro fetch de elegibilidade (rótulo\n * do CTA, contador da ilustração) — o preço de tabela (\"de R$ 19,90\") é a `anchorCents` que vem do\n * endpoint de elegibilidade, nunca hardcoded.\n */\nexport const AI_CREDITS_FIRST_PURCHASE_OFFER = {\n slug: 'first-purchase-ia-credits',\n credits: 500,\n price: 4.9,\n} as const;\n\n/** 1 = cartão, 3 = pix — mesmos códigos do gapps-r3-api. A oferta só aceita cartão. */\nexport const PAYMENT_METHOD_CARD = 1;\nexport const PAYMENT_METHOD_PIX = 3;\n\n/**\n * Escada de estágios da oferta, persistida no cookie `ai-credits-offer-stage`. A ordem importa: o\n * gate só abre para um estágio MAIOR que o gravado (ver `AiCreditsOfferGate`) — CONVERTED é\n * terminal, nunca reabre depois dele.\n */\nexport enum AiCreditsOfferStage {\n NONE = 0,\n CAMPAIGN_SHOWN = 1,\n FIRST_USE_SHOWN = 2,\n HALF_SHOWN = 3,\n EXHAUSTED_SHOWN = 4,\n ZERO_SHOWN = 5,\n CONVERTED = 6,\n}\n\nexport const AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY = ['ai-credits-offer-eligibility'] as const;\n"],"mappings":"AAOO,MAAM,kCAAkC;AAAA,EAC7C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AACT;AAGO,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAO3B,IAAK,sBAAL,kBAAKA,yBAAL;AACL,EAAAA,0CAAA,UAAO,KAAP;AACA,EAAAA,0CAAA,oBAAiB,KAAjB;AACA,EAAAA,0CAAA,qBAAkB,KAAlB;AACA,EAAAA,0CAAA,gBAAa,KAAb;AACA,EAAAA,0CAAA,qBAAkB,KAAlB;AACA,EAAAA,0CAAA,gBAAa,KAAb;AACA,EAAAA,0CAAA,eAAY,KAAZ;AAPU,SAAAA;AAAA,GAAA;AAUL,MAAM,yCAAyC,CAAC,8BAA8B;","names":["AiCreditsOfferStage"]}
|