@greatapps/common 1.1.749 → 1.1.750
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/layouts/AiCreditsOfferGate.mjs +15 -2
- package/dist/components/layouts/AiCreditsOfferGate.mjs.map +1 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs +2 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs.map +1 -1
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/dist/modules/ia-credits/constants/ai-credits-offer.constants.mjs +2 -0
- package/dist/modules/ia-credits/constants/ai-credits-offer.constants.mjs.map +1 -1
- package/dist/modules/ia-credits/utils/offer-stage-cookie.mjs +6 -4
- package/dist/modules/ia-credits/utils/offer-stage-cookie.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/layouts/AiCreditsOfferGate.tsx +34 -6
- package/src/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.tsx +1 -0
- package/src/index.ts +1 -0
- package/src/modules/ia-credits/constants/ai-credits-offer.constants.ts +8 -0
- package/src/modules/ia-credits/utils/offer-stage-cookie.ts +7 -3
|
@@ -6,11 +6,13 @@ import { useActiveSubscription } from "../../modules/subscriptions/hooks/find-ac
|
|
|
6
6
|
import { hasActivePaidSubscription } from "../../modules/subscriptions/utils/has-active-paid-subscription";
|
|
7
7
|
import { ADDON_IDS } from "../../modules/subscriptions/constants/addons.constants";
|
|
8
8
|
import { useCurrencyFormatter } from "../../modules/accounts/hooks/use-currency-formatter.hook";
|
|
9
|
+
import { useRequiredBillingData } from "../../modules/accounts/hooks/use-required-billing-data.hook";
|
|
9
10
|
import { useCards } from "../../modules/cards/hooks/cards.hook";
|
|
10
11
|
import { useIaCredits } from "../../modules/ia-credits/hooks/ia-credits.hook";
|
|
11
12
|
import { useAiCreditsOfferEligibility } from "../../modules/ia-credits/hooks/use-offer-eligibility.hook";
|
|
12
13
|
import {
|
|
13
14
|
AI_CREDITS_FIRST_PURCHASE_OFFER,
|
|
15
|
+
AI_CREDITS_OFFER_MAX_REPEATS,
|
|
14
16
|
AiCreditsOfferStage
|
|
15
17
|
} from "../../modules/ia-credits/constants/ai-credits-offer.constants";
|
|
16
18
|
import {
|
|
@@ -21,6 +23,9 @@ import { useAiCreditsOfferModal } from "../../store/useAiCreditsOfferModal";
|
|
|
21
23
|
import { useModalManager } from "../../store/useModalManager";
|
|
22
24
|
const OPEN_DELAY_MS = 1e3;
|
|
23
25
|
function resolveCandidateStage(summary) {
|
|
26
|
+
if (summary.totalCredits === 0 && summary.usedCredits === 0) {
|
|
27
|
+
return AiCreditsOfferStage.CAMPAIGN_SHOWN;
|
|
28
|
+
}
|
|
24
29
|
if (summary.availableCredits === 0 || summary.totalCredits === 0) {
|
|
25
30
|
return AiCreditsOfferStage.ZERO_SHOWN;
|
|
26
31
|
}
|
|
@@ -39,7 +44,9 @@ function AiCreditsOfferGate({ suppressWhen = false }) {
|
|
|
39
44
|
const { data: subscriptionData } = useActiveSubscription();
|
|
40
45
|
const { currency } = useCurrencyFormatter();
|
|
41
46
|
const { summary, isLoading: isCreditsLoading } = useIaCredits();
|
|
47
|
+
const { isRequired: isBillingDataRequired } = useRequiredBillingData();
|
|
42
48
|
const hasArmedRef = useRef(false);
|
|
49
|
+
const isProfileIncomplete = user != null && (!user.name || !user.last_name);
|
|
43
50
|
const subscription = subscriptionData?.data?.[0] ?? null;
|
|
44
51
|
const accountId = user?.id_account;
|
|
45
52
|
const canManageBilling = user?.profile === "owner" || user?.profile === "admin";
|
|
@@ -51,6 +58,7 @@ function AiCreditsOfferGate({ suppressWhen = false }) {
|
|
|
51
58
|
useEffect(() => {
|
|
52
59
|
if (hasArmedRef.current) return;
|
|
53
60
|
if (suppressWhen || activeModal) return;
|
|
61
|
+
if (isBillingDataRequired || isProfileIncomplete) return;
|
|
54
62
|
if (!passesClientCutoff || isCreditsLoading) return;
|
|
55
63
|
if (!eligibilityData || !eligibilityData.eligible || accountId == null) return;
|
|
56
64
|
if (!cardsQuery.isSuccess || cardsQuery.data.data.length > 0) return;
|
|
@@ -59,13 +67,16 @@ function AiCreditsOfferGate({ suppressWhen = false }) {
|
|
|
59
67
|
const alreadyShownThisSession = cookie.lastSessionIat === eligibilityData.sessionIssuedAt;
|
|
60
68
|
if (alreadyShownThisSession) return;
|
|
61
69
|
const isRepeatableStage = candidateStage === AiCreditsOfferStage.CAMPAIGN_SHOWN || candidateStage === AiCreditsOfferStage.ZERO_SHOWN;
|
|
62
|
-
const
|
|
70
|
+
const isRepeating = isRepeatableStage && candidateStage === cookie.stage;
|
|
71
|
+
const shouldOpen = candidateStage > cookie.stage || isRepeating;
|
|
63
72
|
if (!shouldOpen) return;
|
|
73
|
+
const repeatCount = isRepeating ? cookie.repeatCount + 1 : 0;
|
|
74
|
+
if (isRepeating && cookie.repeatCount >= AI_CREDITS_OFFER_MAX_REPEATS) return;
|
|
64
75
|
hasArmedRef.current = true;
|
|
65
76
|
const timer = setTimeout(() => {
|
|
66
77
|
writeOfferStageCookie(
|
|
67
78
|
accountId,
|
|
68
|
-
{ stage: candidateStage, lastSessionIat: eligibilityData.sessionIssuedAt },
|
|
79
|
+
{ stage: candidateStage, lastSessionIat: eligibilityData.sessionIssuedAt, repeatCount },
|
|
69
80
|
whitelabel?.domain
|
|
70
81
|
);
|
|
71
82
|
openModal();
|
|
@@ -77,7 +88,9 @@ function AiCreditsOfferGate({ suppressWhen = false }) {
|
|
|
77
88
|
cardsQuery.data,
|
|
78
89
|
cardsQuery.isSuccess,
|
|
79
90
|
eligibilityData,
|
|
91
|
+
isBillingDataRequired,
|
|
80
92
|
isCreditsLoading,
|
|
93
|
+
isProfileIncomplete,
|
|
81
94
|
openModal,
|
|
82
95
|
passesClientCutoff,
|
|
83
96
|
suppressWhen,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/layouts/AiCreditsOfferGate.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\nimport { useAuth } from '../../providers/auth.provider';\nimport { useIsDefaultWhitelabel, useWhitelabel } from '../../providers/whitelabel.provider';\nimport { useActiveSubscription } from '../../modules/subscriptions/hooks/find-active-subscription.hook';\nimport { hasActivePaidSubscription } from '../../modules/subscriptions/utils/has-active-paid-subscription';\nimport { ADDON_IDS } from '../../modules/subscriptions/constants/addons.constants';\nimport { useCurrencyFormatter } from '../../modules/accounts/hooks/use-currency-formatter.hook';\nimport { useCards } from '../../modules/cards/hooks/cards.hook';\nimport { useIaCredits } from '../../modules/ia-credits/hooks/ia-credits.hook';\nimport { useAiCreditsOfferEligibility } from '../../modules/ia-credits/hooks/use-offer-eligibility.hook';\nimport {\n AI_CREDITS_FIRST_PURCHASE_OFFER,\n AiCreditsOfferStage,\n} from '../../modules/ia-credits/constants/ai-credits-offer.constants';\nimport {\n readOfferStageCookie,\n writeOfferStageCookie,\n} from '../../modules/ia-credits/utils/offer-stage-cookie';\nimport { useAiCreditsOfferModal } from '../../store/useAiCreditsOfferModal';\nimport { useModalManager } from '../../store/useModalManager';\nimport type { IaCreditsSummary } from '../../modules/ia-credits/types';\n\n/** Espera pedida pelo produto: a oferta entra 1s depois de o gatilho decidir, não junto. */\nconst OPEN_DELAY_MS = 1000;\n\n/**\n * Estágio que os créditos ATUAIS da conta sustentam, na escada do Diogo — sempre o mais alto que os\n * números batem (ordem de checagem: ZERO > EXHAUSTED > HALF > FIRST_USE > CAMPAIGN).\n *\n * `totalCredits === 0` (lote mensal expirado, ainda sem renovar) cai em ZERO, não em divisão por\n * zero — mesma régua do `availableCredits === 0`.\n */\nfunction resolveCandidateStage(\n summary: Pick<IaCreditsSummary, 'totalCredits' | 'usedCredits' | 'availableCredits'>\n): AiCreditsOfferStage {\n if (summary.availableCredits === 0 || summary.totalCredits === 0) {\n return AiCreditsOfferStage.ZERO_SHOWN;\n }\n const usedRatio = summary.usedCredits / summary.totalCredits;\n if (usedRatio >= 0.9) return AiCreditsOfferStage.EXHAUSTED_SHOWN;\n if (usedRatio >= 0.5) return AiCreditsOfferStage.HALF_SHOWN;\n if (summary.usedCredits >= 1) return AiCreditsOfferStage.FIRST_USE_SHOWN;\n return AiCreditsOfferStage.CAMPAIGN_SHOWN;\n}\n\nexport interface AiCreditsOfferGateProps {\n /**\n * Overlay local do host, fora do `useModalManager` compartilhado (ex.: geração de IA em\n * progresso, `InsufficientCreditsModal` do editor) — a oferta não empilha em cima nem espera a\n * vez enquanto `true`; ela volta a tentar no próximo render em que a condição cair, porque o\n * estágio só é gravado quando o modal realmente abre.\n */\n suppressWhen?: boolean;\n}\n\n/**\n * Quem decide se a oferta de primeira compra de créditos de IA aparece, e em qual estágio (gatilhos\n * do Diogo — ver Asana ID-1217333331953496). Mora no root layout de cada app, ao lado dos outros\n * gates de modal.\n *\n * Precisa passar por, nesta ordem:\n *\n * 1. **Corte barato do cliente.** Whitelabel padrão + conta em BRL + `owner`/`admin` + primeira\n * compra de verdade (sem assinatura paga vigente, sem NENHUMA cobrança no histórico) + menos de\n * 500 créditos já contratados. Evita bater no backend pra quem obviamente não se qualifica.\n * 2. **Elegibilidade do servidor**, com FAIL CLOSED: erro, timeout ou ainda carregando conta como\n * inelegível — sem confirmação do backend não existe oferta (ele é quem sabe se a conta já\n * consumiu a oferta em QUALQUER assinatura, cross-cancelamento incluso).\n * 2.1. **Cartão salvo, também FAIL CLOSED.** O card exige as duas condições juntas — plano\n * elegível E ainda sem cartão. O backend valida isso na elegibilidade, mas o gate confere de\n * novo com `useCards`: sem confirmação de que a lista veio vazia, não abre (evita a corrida em\n * que alguém cadastra o cartão pelo CTA e desiste antes de confirmar, e o cache local de\n * elegibilidade ainda não invalidou).\n * 3. **Estágio candidato**, derivado dos créditos atuais (`resolveCandidateStage`).\n * 4. **Escada monotônica do cookie `ai-credits-offer-stage`**: só abre se o candidato é MAIOR que o\n * estágio gravado, OU se o candidato é CAMPAIGN/ZERO — os dois únicos que reapresentam, e só\n * numa sessão de login nova (`sessionIssuedAt` ≠ `lastSessionIat`). Nenhum dos dois tem teto de\n * repetições: o Diogo pediu literalmente \"mostrar sempre que iniciar sessão nova\" para a\n * campanha e \"pode ficar mostrando\" para quem está sem crédito (Asana ID-1217333331953496).\n * 5. **Máx. 1 apresentação por sessão de login.** Sai de graça da regra acima: se o cookie já tem\n * `lastSessionIat` igual ao da sessão atual, NADA abre — nem uma escalada de estágio dentro da\n * mesma sessão (ex.: FIRST_USE → HALF → EXHAUSTED na mesma geração, no Free de 10 créditos).\n */\nexport function AiCreditsOfferGate({ suppressWhen = false }: AiCreditsOfferGateProps) {\n const { activeModal } = useModalManager();\n const { openModal } = useAiCreditsOfferModal();\n const { user } = useAuth();\n const isDefaultWl = useIsDefaultWhitelabel();\n const { whitelabel } = useWhitelabel();\n const { data: subscriptionData } = useActiveSubscription();\n const { currency } = useCurrencyFormatter();\n const { summary, isLoading: isCreditsLoading } = useIaCredits();\n const hasArmedRef = useRef(false);\n\n const subscription = subscriptionData?.data?.[0] ?? null;\n const accountId = user?.id_account;\n const canManageBilling = user?.profile === 'owner' || user?.profile === 'admin';\n const currentAiCredits =\n subscription?.items?.find((item) => item.id_addon === ADDON_IDS.AI)?.quantity ?? 0;\n const isFirstPurchase = !hasActivePaidSubscription(subscription) && !subscription?.charged;\n\n const passesClientCutoff =\n isDefaultWl &&\n currency === 'brl' &&\n canManageBilling &&\n accountId != null &&\n isFirstPurchase &&\n currentAiCredits < AI_CREDITS_FIRST_PURCHASE_OFFER.credits;\n\n // Só consulta o servidor depois do corte barato: quem não passa aqui nunca veria a oferta, e a\n // consulta custa duas chamadas no gapps-r3-api.\n const { data: eligibilityData } = useAiCreditsOfferEligibility({ enabled: passesClientCutoff });\n const cardsQuery = useCards({ enabled: passesClientCutoff });\n\n useEffect(() => {\n if (hasArmedRef.current) return;\n if (suppressWhen || activeModal) return;\n if (!passesClientCutoff || isCreditsLoading) return;\n // Fail closed: sem resposta do servidor (carregando, erro, ou reason preenchido), não abre.\n if (!eligibilityData || !eligibilityData.eligible || accountId == null) return;\n // Fail closed: sem confirmação de que a lista de cartões carregou vazia, não abre — o backend\n // já valida \"sem cartão\" na elegibilidade, isto é a segunda camada do lado do cliente.\n if (!cardsQuery.isSuccess || cardsQuery.data.data.length > 0) return;\n\n const candidateStage = resolveCandidateStage(summary);\n const cookie = readOfferStageCookie(accountId);\n\n const alreadyShownThisSession = cookie.lastSessionIat === eligibilityData.sessionIssuedAt;\n if (alreadyShownThisSession) return;\n\n const isRepeatableStage =\n candidateStage === AiCreditsOfferStage.CAMPAIGN_SHOWN ||\n candidateStage === AiCreditsOfferStage.ZERO_SHOWN;\n\n const shouldOpen =\n candidateStage > cookie.stage || (isRepeatableStage && candidateStage === cookie.stage);\n if (!shouldOpen) return;\n\n // Arma uma única vez por montagem: sem isso, qualquer re-render limparia o timer pendente.\n hasArmedRef.current = true;\n\n const timer = setTimeout(() => {\n // O estágio é gravado junto com a abertura (e não antes): fechar a aba durante o delay não\n // pode queimar a apresentação.\n writeOfferStageCookie(\n accountId,\n { stage: candidateStage, lastSessionIat: eligibilityData.sessionIssuedAt },\n whitelabel?.domain\n );\n openModal();\n }, OPEN_DELAY_MS);\n\n return () => clearTimeout(timer);\n }, [\n accountId,\n activeModal,\n cardsQuery.data,\n cardsQuery.isSuccess,\n eligibilityData,\n isCreditsLoading,\n openModal,\n passesClientCutoff,\n suppressWhen,\n summary,\n whitelabel?.domain,\n ]);\n\n return null;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/layouts/AiCreditsOfferGate.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\nimport { useAuth } from '../../providers/auth.provider';\nimport { useIsDefaultWhitelabel, useWhitelabel } from '../../providers/whitelabel.provider';\nimport { useActiveSubscription } from '../../modules/subscriptions/hooks/find-active-subscription.hook';\nimport { hasActivePaidSubscription } from '../../modules/subscriptions/utils/has-active-paid-subscription';\nimport { ADDON_IDS } from '../../modules/subscriptions/constants/addons.constants';\nimport { useCurrencyFormatter } from '../../modules/accounts/hooks/use-currency-formatter.hook';\nimport { useRequiredBillingData } from '../../modules/accounts/hooks/use-required-billing-data.hook';\nimport { useCards } from '../../modules/cards/hooks/cards.hook';\nimport { useIaCredits } from '../../modules/ia-credits/hooks/ia-credits.hook';\nimport { useAiCreditsOfferEligibility } from '../../modules/ia-credits/hooks/use-offer-eligibility.hook';\nimport {\n AI_CREDITS_FIRST_PURCHASE_OFFER,\n AI_CREDITS_OFFER_MAX_REPEATS,\n AiCreditsOfferStage,\n} from '../../modules/ia-credits/constants/ai-credits-offer.constants';\nimport {\n readOfferStageCookie,\n writeOfferStageCookie,\n} from '../../modules/ia-credits/utils/offer-stage-cookie';\nimport { useAiCreditsOfferModal } from '../../store/useAiCreditsOfferModal';\nimport { useModalManager } from '../../store/useModalManager';\nimport type { IaCreditsSummary } from '../../modules/ia-credits/types';\n\n/** Espera pedida pelo produto: a oferta entra 1s depois de o gatilho decidir, não junto. */\nconst OPEN_DELAY_MS = 1000;\n\n/**\n * Estágio que os créditos ATUAIS da conta sustentam, na escada do Diogo — sempre o mais alto que os\n * números batem (ordem de checagem: ZERO > EXHAUSTED > HALF > FIRST_USE > CAMPAIGN).\n *\n * `totalCredits === 0` (lote mensal expirado, ainda sem renovar) cai em ZERO, não em divisão por\n * zero — mesma régua do `availableCredits === 0`.\n */\nfunction resolveCandidateStage(\n summary: Pick<IaCreditsSummary, 'totalCredits' | 'usedCredits' | 'availableCredits'>\n): AiCreditsOfferStage {\n // Conta que ainda NÃO recebeu lote nenhum (medido em produção: o free só ganha o primeiro lote\n // meses depois do cadastro) não é o mesmo que conta que gastou tudo. Sem esta distinção o\n // recém-cadastrado entrava direto em ZERO_SHOWN, o degrau mais alto, e como a escada só sobe os\n // gatilhos de uso — primeira geração, 50%, 90% — nunca mais disparavam para ele.\n if (summary.totalCredits === 0 && summary.usedCredits === 0) {\n return AiCreditsOfferStage.CAMPAIGN_SHOWN;\n }\n if (summary.availableCredits === 0 || summary.totalCredits === 0) {\n return AiCreditsOfferStage.ZERO_SHOWN;\n }\n const usedRatio = summary.usedCredits / summary.totalCredits;\n if (usedRatio >= 0.9) return AiCreditsOfferStage.EXHAUSTED_SHOWN;\n if (usedRatio >= 0.5) return AiCreditsOfferStage.HALF_SHOWN;\n if (summary.usedCredits >= 1) return AiCreditsOfferStage.FIRST_USE_SHOWN;\n return AiCreditsOfferStage.CAMPAIGN_SHOWN;\n}\n\nexport interface AiCreditsOfferGateProps {\n /**\n * Overlay local do host, fora do `useModalManager` compartilhado (ex.: geração de IA em\n * progresso, `InsufficientCreditsModal` do editor) — a oferta não empilha em cima nem espera a\n * vez enquanto `true`; ela volta a tentar no próximo render em que a condição cair, porque o\n * estágio só é gravado quando o modal realmente abre.\n */\n suppressWhen?: boolean;\n}\n\n/**\n * Quem decide se a oferta de primeira compra de créditos de IA aparece, e em qual estágio (gatilhos\n * do Diogo — ver Asana ID-1217333331953496). Mora no root layout de cada app, ao lado dos outros\n * gates de modal.\n *\n * Precisa passar por, nesta ordem:\n *\n * 1. **Corte barato do cliente.** Whitelabel padrão + conta em BRL + `owner`/`admin` + primeira\n * compra de verdade (sem assinatura paga vigente, sem NENHUMA cobrança no histórico) + menos de\n * 500 créditos já contratados. Evita bater no backend pra quem obviamente não se qualifica.\n * 2. **Elegibilidade do servidor**, com FAIL CLOSED: erro, timeout ou ainda carregando conta como\n * inelegível — sem confirmação do backend não existe oferta (ele é quem sabe se a conta já\n * consumiu a oferta em QUALQUER assinatura, cross-cancelamento incluso).\n * 2.1. **Cartão salvo, também FAIL CLOSED.** O card exige as duas condições juntas — plano\n * elegível E ainda sem cartão. O backend valida isso na elegibilidade, mas o gate confere de\n * novo com `useCards`: sem confirmação de que a lista veio vazia, não abre (evita a corrida em\n * que alguém cadastra o cartão pelo CTA e desiste antes de confirmar, e o cache local de\n * elegibilidade ainda não invalidou).\n * 3. **Estágio candidato**, derivado dos créditos atuais (`resolveCandidateStage`).\n * 4. **Escada monotônica do cookie `ai-credits-offer-stage`**: só abre se o candidato é MAIOR que o\n * estágio gravado, OU se o candidato é CAMPAIGN/ZERO — os dois únicos que reapresentam, e só\n * numa sessão de login nova (`sessionIssuedAt` ≠ `lastSessionIat`). A repetição respeita\n * `AI_CREDITS_OFFER_MAX_REPEATS`: a campanha é agressiva de propósito (vale inclusive para quem\n * acabou de se cadastrar), mas não pode virar o mesmo pop em toda sessão para sempre.\n * 5. **Máx. 1 apresentação por sessão de login.** Sai de graça da regra acima: se o cookie já tem\n * `lastSessionIat` igual ao da sessão atual, NADA abre — nem uma escalada de estágio dentro da\n * mesma sessão (ex.: FIRST_USE → HALF → EXHAUSTED na mesma geração, no Free de 10 créditos).\n */\nexport function AiCreditsOfferGate({ suppressWhen = false }: AiCreditsOfferGateProps) {\n const { activeModal } = useModalManager();\n const { openModal } = useAiCreditsOfferModal();\n const { user } = useAuth();\n const isDefaultWl = useIsDefaultWhitelabel();\n const { whitelabel } = useWhitelabel();\n const { data: subscriptionData } = useActiveSubscription();\n const { currency } = useCurrencyFormatter();\n const { summary, isLoading: isCreditsLoading } = useIaCredits();\n const { isRequired: isBillingDataRequired } = useRequiredBillingData();\n const hasArmedRef = useRef(false);\n\n /* Modais bloqueantes que NÃO passam pelo `useModalManager` — cada um controla a própria abertura\n * com estado local, então `activeModal` não os enxerga e a oferta subiria por cima:\n * - `RequiredBillingDataModal` (lib), aberto por `useRequiredBillingData().isRequired`;\n * - `CompleteProfileGuard` (app de contas), aberto quando falta nome ou sobrenome.\n * Checar a condição de origem, e não o modal em si, é o que funciona para os três apps sem que\n * cada um precise lembrar de passar `suppressWhen`. */\n const isProfileIncomplete = user != null && (!user.name || !user.last_name);\n\n const subscription = subscriptionData?.data?.[0] ?? null;\n const accountId = user?.id_account;\n const canManageBilling = user?.profile === 'owner' || user?.profile === 'admin';\n const currentAiCredits =\n subscription?.items?.find((item) => item.id_addon === ADDON_IDS.AI)?.quantity ?? 0;\n const isFirstPurchase = !hasActivePaidSubscription(subscription) && !subscription?.charged;\n\n const passesClientCutoff =\n isDefaultWl &&\n currency === 'brl' &&\n canManageBilling &&\n accountId != null &&\n isFirstPurchase &&\n currentAiCredits < AI_CREDITS_FIRST_PURCHASE_OFFER.credits;\n\n // Só consulta o servidor depois do corte barato: quem não passa aqui nunca veria a oferta, e a\n // consulta custa duas chamadas no gapps-r3-api.\n const { data: eligibilityData } = useAiCreditsOfferEligibility({ enabled: passesClientCutoff });\n const cardsQuery = useCards({ enabled: passesClientCutoff });\n\n useEffect(() => {\n if (hasArmedRef.current) return;\n if (suppressWhen || activeModal) return;\n if (isBillingDataRequired || isProfileIncomplete) return;\n if (!passesClientCutoff || isCreditsLoading) return;\n // Fail closed: sem resposta do servidor (carregando, erro, ou reason preenchido), não abre.\n if (!eligibilityData || !eligibilityData.eligible || accountId == null) return;\n // Fail closed: sem confirmação de que a lista de cartões carregou vazia, não abre — o backend\n // já valida \"sem cartão\" na elegibilidade, isto é a segunda camada do lado do cliente.\n if (!cardsQuery.isSuccess || cardsQuery.data.data.length > 0) return;\n\n const candidateStage = resolveCandidateStage(summary);\n const cookie = readOfferStageCookie(accountId);\n\n const alreadyShownThisSession = cookie.lastSessionIat === eligibilityData.sessionIssuedAt;\n if (alreadyShownThisSession) return;\n\n const isRepeatableStage =\n candidateStage === AiCreditsOfferStage.CAMPAIGN_SHOWN ||\n candidateStage === AiCreditsOfferStage.ZERO_SHOWN;\n\n const isRepeating = isRepeatableStage && candidateStage === cookie.stage;\n\n const shouldOpen = candidateStage > cookie.stage || isRepeating;\n if (!shouldOpen) return;\n\n // Subir de estágio zera o contador: o teto limita a insistência DENTRO de um estágio, não a\n // escada inteira. Sem isso, uma conta que já gastou o teto na campanha nunca veria a oferta no\n // momento em que ela mais importa (crédito no fim).\n const repeatCount = isRepeating ? cookie.repeatCount + 1 : 0;\n if (isRepeating && cookie.repeatCount >= AI_CREDITS_OFFER_MAX_REPEATS) return;\n\n // Arma uma única vez por montagem: sem isso, qualquer re-render limparia o timer pendente.\n hasArmedRef.current = true;\n\n const timer = setTimeout(() => {\n // O estágio é gravado junto com a abertura (e não antes): fechar a aba durante o delay não\n // pode queimar a apresentação.\n writeOfferStageCookie(\n accountId,\n { stage: candidateStage, lastSessionIat: eligibilityData.sessionIssuedAt, repeatCount },\n whitelabel?.domain\n );\n openModal();\n }, OPEN_DELAY_MS);\n\n return () => clearTimeout(timer);\n }, [\n accountId,\n activeModal,\n cardsQuery.data,\n cardsQuery.isSuccess,\n eligibilityData,\n isBillingDataRequired,\n isCreditsLoading,\n isProfileIncomplete,\n openModal,\n passesClientCutoff,\n suppressWhen,\n summary,\n whitelabel?.domain,\n ]);\n\n return null;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -97,7 +97,8 @@ function AiCreditsOfferModal() {
|
|
|
97
97
|
accountId,
|
|
98
98
|
{
|
|
99
99
|
stage: AiCreditsOfferStage.CONVERTED,
|
|
100
|
-
lastSessionIat: eligibility.data?.sessionIssuedAt ?? cookie.lastSessionIat
|
|
100
|
+
lastSessionIat: eligibility.data?.sessionIssuedAt ?? cookie.lastSessionIat,
|
|
101
|
+
repeatCount: cookie.repeatCount
|
|
101
102
|
},
|
|
102
103
|
whitelabel?.domain
|
|
103
104
|
);
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"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 repeatCount: cookie.repeatCount,\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":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -90,6 +90,7 @@ import { useAiCreditsOfferEligibility } from "./modules/ia-credits/hooks/use-off
|
|
|
90
90
|
import {
|
|
91
91
|
AI_CREDITS_FIRST_PURCHASE_OFFER,
|
|
92
92
|
AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY,
|
|
93
|
+
AI_CREDITS_OFFER_MAX_REPEATS,
|
|
93
94
|
AiCreditsOfferStage,
|
|
94
95
|
PAYMENT_METHOD_CARD,
|
|
95
96
|
PAYMENT_METHOD_PIX
|
|
@@ -487,6 +488,7 @@ export {
|
|
|
487
488
|
ADDON_IDS,
|
|
488
489
|
AI_CREDITS_FIRST_PURCHASE_OFFER,
|
|
489
490
|
AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY,
|
|
491
|
+
AI_CREDITS_OFFER_MAX_REPEATS,
|
|
490
492
|
AI_CREDIT_OPTIONS,
|
|
491
493
|
AI_CREDIT_SIZES,
|
|
492
494
|
AI_CREDIT_VALUES,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Types\nexport * from \"./modules/auth/schema\";\nexport * from \"./modules/users/schema\";\nexport * from \"./modules/whitelabel/schema\";\n// Style types now come from whitelabel schema directly\nexport * from \"./infra/api/types\";\n\n// Contexts\nexport * from \"./providers/query.provider\";\nexport * from \"./providers/auth.provider\";\nexport * from \"./providers/whitelabel.provider\";\nexport {\n NavigationProvider,\n useNavigationFallback,\n} from \"./providers/navigation.provider\";\n\n// Navigation (safe drop-in replacement for next/navigation)\nexport { useRouter } from \"./hooks/useRouter\";\nexport type { SafeAppRouter } from \"./hooks/useRouter\";\nexport {\n usePathname,\n useSearchParams,\n useParams,\n redirect,\n notFound,\n} from \"./utils/next-navigation\";\n\n// Hooks\nexport {\n useListProjectUsers,\n LIST_PROJECT_USERS_QUERY_KEY,\n LIST_PROJECT_USERS_BASE_KEY,\n} from \"./modules/projects/hooks/list-project-users.hook\";\nexport {\n useListAvailableUsers,\n LIST_AVAILABLE_USERS_QUERY_KEY,\n LIST_AVAILABLE_USERS_BASE_KEY,\n} from \"./modules/projects/hooks/list-available-users.hook\";\nexport {\n useListAllAccountUsers,\n LIST_ALL_ACCOUNT_USERS_QUERY_KEY,\n LIST_ALL_ACCOUNT_USERS_BASE_KEY,\n} from \"./modules/projects/hooks/list-all-account-users.hook\";\nexport {\n useProjects,\n PROJECTS_BASE_KEY,\n PROJECTS_QUERY_KEY,\n} from \"./modules/projects/hooks/list-projects.hook\";\nexport {\n useInfiniteProjects,\n INFINITE_PROJECTS_QUERY_KEY,\n} from \"./modules/projects/hooks/list-infinite-projects.hook\";\nexport { useProject } from \"./modules/projects/hooks/find-project.hook\";\nexport { useCreateProject } from \"./modules/projects/hooks/create-project.hook\";\nexport { useUpdateProject } from \"./modules/projects/hooks/update-project.hook\";\nexport { useDeleteProject } from \"./modules/projects/hooks/delete-project.hook\";\nexport type {\n Project,\n ProjectsPage,\n CreateProjectRequest,\n UpdateProjectRequest,\n ProjectUser,\n AccountUser,\n AddRemoveProjectUsersParams,\n ListUsersParams,\n UsersPage,\n} from \"./modules/projects/types\";\nexport { ProjectSchema } from \"./modules/projects/types\";\nexport type { ListProjectsActionParams } from \"./modules/projects/actions/list-projects.action\";\nexport {\n useUserQuery,\n useUserValidateSession,\n useInvalidateUser,\n useSetUserData,\n useTwoFactorVerify,\n USER_QUERY_KEY,\n} from \"./modules/auth/hooks/useUserQuery\";\nexport { useManagementPermissions } from \"./modules/auth/hooks/useManagementPermissions\";\nexport type { ManagementPermission } from \"./modules/auth/types/management-permission.type\";\nexport { useSocialGoogleLogin } from \"./modules/auth/hooks/social-google-login.hook\";\nexport { useSocialGoogleOnboarding } from \"./modules/auth/hooks/social-google-onboarding.hook\";\nexport { useUnlinkGoogle } from \"./modules/auth/hooks/unlink-google.hook\";\nexport { useSubscriptions } from \"./modules/subscriptions/hooks/list-subscriptions.hook\";\nexport {\n SUBSCRIPTIONS_QUERY_KEY,\n CHARGES_QUERY_KEY,\n FREEZE_NOTICE_QUERY_KEY,\n} from \"./modules/subscriptions/constants/query-keys.constants\";\nexport { useActiveSubscription } from \"./modules/subscriptions/hooks/find-active-subscription.hook\";\nexport { useCancelledSubscriptionGuard } from \"./modules/subscriptions/hooks/use-cancelled-subscription-guard\";\nexport { useAccountFreeze } from \"./modules/subscriptions/hooks/use-account-freeze.hook\";\nexport { useFreezeNotice } from \"./modules/subscriptions/hooks/use-freeze-notice.hook\";\nexport { usePlanById } from \"./modules/plans/hooks/use-plan-by-id.hook\";\nexport { useHasPlanAddon } from \"./modules/plans/hooks/use-has-plan-addon.hook\";\nexport { useCalculateSubscription } from \"./modules/subscriptions/hooks/calculate-subscription.hook\";\nexport { useUpdateSubscriptionPlan } from \"./modules/subscriptions/hooks/update-subscription-plan.hook\";\nexport { useUpdateSubscriptionPayment } from \"./modules/subscriptions/hooks/update-subscription-payment.hook\";\nexport { useCards, CARDS_QUERY_KEY } from \"./modules/cards/hooks/cards.hook\";\nexport { useCardById } from \"./modules/cards/hooks/card-by-id.hook\";\nexport { useCreateCard } from \"./modules/cards/hooks/create-card.hook\";\nexport { useCreateSetupIntent } from \"./modules/cards/hooks/create-setup-intent.hook\";\nexport { useSetDefaultCard } from \"./modules/cards/hooks/set-default-card.hook\";\nexport { useDeleteCard } from \"./modules/cards/hooks/delete-card.hook\";\nexport { useDeleteConfirmation } from \"./modules/cards/hooks/delete-confirmation.hook\";\nexport type { DeleteConfirmationFormData } from \"./modules/cards/hooks/delete-confirmation.hook\";\nexport { useHandleDeleteCard } from \"./modules/cards/hooks/handle-delete-card.hook\";\nexport { useCharges } from \"./modules/charges/hooks/charges.hook\";\nexport { useChargeById } from \"./modules/charges/hooks/charge-by-id.hook\";\nexport { useChargeAction } from \"./modules/charges/hooks/charge-action.hook\";\nexport { useIaCredits } from \"./modules/ia-credits/hooks/ia-credits.hook\";\nexport type {\n IaCreditsSummary,\n IaCreditsSummaryData,\n IaCreditAddBatch,\n IaCreditOperation,\n} from \"./modules/ia-credits/types\";\nexport { usePurchaseIaCredits } from \"./modules/ia-credits/hooks/purchase-ia-credits.hook\";\nexport { useAiCreditsOfferEligibility } from \"./modules/ia-credits/hooks/use-offer-eligibility.hook\";\nexport {\n AI_CREDITS_FIRST_PURCHASE_OFFER,\n AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY,\n AiCreditsOfferStage,\n PAYMENT_METHOD_CARD,\n PAYMENT_METHOD_PIX,\n} from \"./modules/ia-credits/constants/ai-credits-offer.constants\";\nexport type {\n AiCreditsOfferEligibility,\n AiCreditsOfferEligibilityResponse,\n AiCreditsOfferIneligibilityReason,\n PurchaseIaCreditsInput,\n PurchaseIaCreditsResult,\n} from \"./modules/ia-credits/types/ai-credits-offer.type\";\nexport {\n readOfferStageCookie,\n writeOfferStageCookie,\n} from \"./modules/ia-credits/utils/offer-stage-cookie\";\nexport type { OfferStageRecord } from \"./modules/ia-credits/utils/offer-stage-cookie\";\nexport type {\n Subscription,\n SubscriptionItem,\n FindSubscriptionsParams,\n} from \"./modules/subscriptions/types/subscription.type\";\nexport {\n SubscriptionSchema,\n SubscriptionItemSchema,\n} from \"./modules/subscriptions/types/subscription.type\";\nexport {\n ADDON_IDS,\n AI_CREDIT_OPTIONS,\n AI_CREDIT_SIZES,\n AI_CREDIT_VALUES,\n buildAiCreditOptions,\n resolveAiCreditPrice,\n} from \"./modules/subscriptions/constants/addons.constants\";\nexport type {\n AddonKindEnum,\n AiCreditOption,\n} from \"./modules/subscriptions/constants/addons.constants\";\nexport type {\n Plan,\n PlanItem,\n UiPlan,\n PlanFeature,\n PlanMainFeatures,\n PlanPricingByPeriod,\n PlanTooltips,\n} from \"./modules/plans/types/plan.type\";\nexport { PlanSchema, PlanItemSchema } from \"./modules/plans/types/plan.type\";\nexport {\n mapApiPlanToUiPlan,\n mapApiPlanToUiPlanForCurrency,\n} from \"./modules/plans/utils/map-api-plan-to-ui\";\nexport type { PlanBillingPeriod } from \"./modules/plans/utils/map-api-plan-to-ui\";\nexport {\n usePlans,\n PLANS_QUERY_KEY,\n} from \"./modules/plans/hooks/list-plans.hook\";\nexport type { BillingPeriod } from \"./modules/subscriptions/types/billing-period.type\";\nexport type {\n CalculateSubscriptionRequest,\n CalculateSubscriptionResponse,\n UpdateSubscriptionPlanRequest,\n} from \"./modules/subscriptions/types/calculate-subscription.type\";\nexport {\n PixPendingDataSchema,\n SubscriptionPendingPixResponseSchema,\n isSubscriptionPendingPixResponse,\n} from \"./modules/subscriptions/types/pix-pending.type\";\nexport type {\n PixPendingData,\n SubscriptionPendingPixResponse,\n} from \"./modules/subscriptions/types/pix-pending.type\";\nexport type {\n Card as PaymentCard,\n CreateCardRequest,\n FindCardsParams,\n SetupIntent,\n} from \"./modules/cards/types\";\nexport {\n CardSchema as PaymentCardSchema,\n CreateCardRequestSchema,\n FindCardsParamsSchema,\n SetupIntentSchema,\n} from \"./modules/cards/types\";\nexport type {\n Charge,\n FindChargesParams,\n PayChargeInput,\n} from \"./modules/charges/types/charge.type\";\nexport {\n ChargeSchema,\n FindChargesParamsSchema,\n PayChargeInputSchema,\n} from \"./modules/charges/types/charge.type\";\nexport { buildPlanExtras } from \"./modules/subscriptions/utils/build-plan-extras\";\nexport { hasSubscriptionExpired } from \"./modules/subscriptions/utils/has-subscription-expired\";\nexport { hasActivePaidSubscription } from \"./modules/subscriptions/utils/has-active-paid-subscription\";\nexport { hasPaidSubscription } from \"./modules/subscriptions/utils/has-paid-subscription\";\nexport {\n confirmPaymentIfRequired,\n PaymentAuthenticationError,\n} from \"./modules/subscriptions/utils/confirm-payment-if-required\";\nexport { SUBSCRIPTION_GRACE_PERIOD_DAYS } from \"./modules/subscriptions/constants/subscription.constants\";\nexport { getSubscriptionCancellationState } from \"./modules/subscriptions/utils/get-subscription-cancellation-state\";\nexport type { SubscriptionCancellationState } from \"./modules/subscriptions/utils/get-subscription-cancellation-state\";\nexport {\n ACCOUNT_FREEZE_AFTER_DAYS,\n FREEZE_WARNING_DAYS_BEFORE,\n} from \"./modules/subscriptions/constants/subscription.constants\";\nexport { getAccountFreezeState } from \"./modules/subscriptions/utils/get-account-freeze-state\";\nexport type { AccountFreezeState } from \"./modules/subscriptions/utils/get-account-freeze-state\";\nexport { getAccountFreezeDate } from \"./modules/subscriptions/utils/get-account-freeze-date\";\nexport type { FreezeNotice } from \"./modules/subscriptions/types/freeze-notice.type\";\nexport {\n useCountPages,\n COUNT_PAGES_QUERY_KEY,\n} from \"./modules/pages/hooks/count-pages.hook\";\nexport { PAGES_QUERY_KEY } from \"./modules/pages/constants/query-keys.constants\";\nexport {\n getPriceFromCalculatedData,\n getOriginalPriceFromCalculatedData,\n periodicityToBillingPeriod,\n} from \"./modules/subscriptions/utils/periodicity\";\nexport { useBuyCreditsModal } from \"./store/useBuyCreditsModal\";\nexport { useCreditsDisabledModal } from \"./store/useCreditsDisabledModal\";\nexport { usePaidPlanRequiredModal } from \"./store/usePaidPlanRequiredModal\";\nexport { useAccountDeletionWarningModal } from \"./store/useAccountDeletionWarningModal\";\nexport { useAiCreditsOfferModal } from \"./store/useAiCreditsOfferModal\";\nexport { default as BuyCreditsModal } from \"./components/modals/BuyCreditsModal\";\nexport { default as AiCreditsOfferModal } from \"./components/modals/promo/AiCreditsOfferModal\";\nexport { AiCreditsOfferGate } from \"./components/layouts/AiCreditsOfferGate\";\nexport type { AiCreditsOfferGateProps } from \"./components/layouts/AiCreditsOfferGate\";\nexport { default as CreditsDisabledModal } from \"./components/modals/CreditsDisabledModal\";\nexport { default as PaidPlanRequiredModal } from \"./components/modals/PaidPlanRequiredModal\";\nexport { default as AccountFrozenModal } from \"./components/modals/AccountFrozenModal\";\nexport { default as AccountDeletionWarningModal } from \"./components/modals/AccountDeletionWarningModal\";\nexport { default as RequiredBillingDataModal } from \"./components/modals/billing/RequiredBillingDataModal\";\nexport { default as AddCardModal } from \"./components/modals/cards/AddCardModal\";\nexport { DeleteCardModal } from \"./components/modals/cards/DeleteCardModal\";\nexport { CannotDeleteCardModal } from \"./components/modals/cards/CannotDeleteCardModal\";\nexport {\n CardFormFields,\n cardFormSchema,\n buildCardFormSchema,\n} from \"./components/modals/cards/CardFormFields\";\nexport type { CardFormData } from \"./components/modals/cards/CardFormFields\";\nexport { Skeleton } from \"./components/ui/feedback/Skeleton\";\nexport { PaymentInfoCard } from \"./components/ui/data-display/PaymentInfoCard\";\nexport { CardBrandIcon } from \"./components/ui/data-display/CardBrandIcons\";\nexport { CardItem } from \"./components/ui/data-display/CardItem\";\n\n// Providers\nexport { QueryProvider } from \"./providers/query.provider\";\n\n// Middleware\nexport { createAuthMiddleware } from \"./middlewares/create-auth-middleware\";\nexport { createMiddlewareChain } from \"./middlewares/chain\";\nexport { continueChain, stopChain } from \"./middlewares/types\";\nexport type {\n MiddlewareFunction,\n MiddlewareConfig,\n MiddlewareResult,\n} from \"./middlewares/types\";\n\n// Utils\nexport { cn } from \"./infra/utils/clsx\";\nexport { formatShortDate, formatDateTime, calendarDateSchema, toCalendarDate, daysBetween } from \"./infra/utils/date\";\nexport { buildQueryParams } from \"./infra/utils/params\";\nexport { parseSchema, parseResult } from \"./infra/utils/parser\";\nexport { withAction } from \"./utils/withAction\";\nexport { resolveSafeRedirect, sameHostOr } from \"./utils/redirect\";\nexport { COUNTRIES, flagUrl } from \"./utils/countries\";\nexport type { Country } from \"./utils/countries\";\n\n// Layout\nexport {\n MainLayout,\n MainLayoutContent,\n MainLayoutSpacer,\n MainLayoutMain,\n} from \"./components/layouts/MainLayout\";\nexport { NavBar } from \"./components/layouts/NavBar\";\nexport type { NavBarProps } from \"./components/layouts/NavBar\";\nexport { AppNavBar } from \"./components/layouts/AppNavBar\";\nexport type { AppNavBarProps } from \"./components/layouts/AppNavBar\";\nexport { AppMobileNavBar } from \"./components/layouts/AppMobileNavBar\";\nexport type { AppMobileNavBarProps } from \"./components/layouts/AppMobileNavBar\";\nexport { SideBarNavigation } from \"./components/layouts/SideBarNavigation\";\nexport { MdSideBarNavigation } from \"./components/layouts/MdSideBarNavigation\";\nexport { default as NotificationCard } from \"./components/widgets/notifications/NotificationCard\";\nexport type { NotificationCardProps } from \"./components/widgets/notifications/NotificationCard\";\nexport { NotificationsPopover } from \"./components/layouts/NotificationsPopover\";\nexport type { NotificationsPopoverProps } from \"./components/layouts/NotificationsPopover\";\nexport { NotificationPageContent } from \"./components/pages/notifications/Notifications\";\nexport { NotFoundPage } from \"./components/pages/NotFoundPage\";\nexport { UsersSelectorPopover } from \"./components/layouts/UsersSelectorPopover\";\nexport type { UsersSelectorPopoverProps } from \"./components/layouts/UsersSelectorPopover\";\nexport { ProfilePopover } from \"./components/layouts/ProfilePopover\";\nexport type {\n ProfilePopoverProps,\n ProfileMenuItem,\n} from \"./components/layouts/ProfilePopover\";\nexport { default as WhitelabelCodes } from \"./components/layouts/WhitelabelCodes\";\nexport { NavBarItem } from \"./components/layouts/NavBarItem\";\nexport type { NavBarItemProps } from \"./components/layouts/NavBarItem\";\n\n// Navigation\nexport { AppNavigation } from \"./components/navigation/AppNavigation\";\nexport { CancelledSubscriptionBanner } from \"./components/navigation/CancelledSubscriptionBanner\";\nexport { SubscriptionBanner } from \"./components/navigation/SubscriptionBanner\";\nexport { OverdueInvoiceBanner } from \"./components/navigation/OverdueInvoiceBanner\";\nexport { UpcomingInvoiceBanner } from \"./components/navigation/UpcomingInvoiceBanner\";\nexport { TrialBanner } from \"./components/navigation/TrialBanner\";\nexport { FrozenAccountBanner } from \"./components/navigation/FrozenAccountBanner\";\nexport { FreezeWarningBanner } from \"./components/navigation/FreezeWarningBanner\";\nexport type { NavItemConfig } from \"./components/navigation/subcomponents/NavItems\";\nexport { useMobileNavbarSheet } from \"./store/useMobileNavbarSheet\";\n\n// Auth Query Key Utilities\nexport {\n useAuthQueryKey,\n useWlQueryKey,\n useWlId,\n} from \"./hooks/useAuthQueryKey\";\n\n// Hooks\nexport { default as useCopyToClipboard } from \"./hooks/copy-to-clipboard.hook\";\n\n// UI - Buttons\nexport { Button, buttonVariants } from \"./components/ui/buttons/Button\";\nexport { CopyButton } from \"./components/ui/buttons/CopyButton\";\nexport { default as CancelButton } from \"./components/ui/buttons/CancelButton\";\n\n// UI - Data Display\nexport {\n Accordion,\n AccordionItem,\n AccordionTrigger,\n AccordionContent,\n} from \"./components/ui/data-display/Accordion\";\nexport { Badge, badgeVariants } from \"./components/ui/data-display/Badge\";\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardAction,\n CardDescription,\n CardContent,\n} from \"./components/ui/data-display/Card\";\nexport {\n Pagination,\n PaginationContent,\n PaginationLink,\n PaginationItem,\n PaginationPrevious,\n PaginationNext,\n PaginationEllipsis,\n} from \"./components/ui/data-display/Pagination\";\nexport { ScrollArea, ScrollBar } from \"./components/ui/data-display/ScrollArea\";\nexport { Separator } from \"./components/ui/data-display/Separator\";\nexport {\n Table,\n TableHeader,\n TableBody,\n TableFooter,\n TableHead,\n TableRow,\n TableCell,\n TableCaption,\n} from \"./components/ui/data-display/Table\";\nexport {\n Tabs,\n TabsList,\n TabsTrigger,\n TabsContent,\n} from \"./components/ui/data-display/Tabs\";\nexport { UserAvatar } from \"./components/ui/data-display/UserAvatar\";\nexport type { UserAvatarProps } from \"./components/ui/data-display/UserAvatar\";\n\n// UI - Feedback\nexport { default as CircularProgress } from \"./components/ui/feedback/CircularProgress\";\nexport { default as DefaultCircularProgress } from \"./components/ui/feedback/DefaultCircularProgress\";\nexport { Progress } from \"./components/ui/feedback/Progress\";\nexport { LoadingOverlay } from \"./components/ui/feedback/LoadingOverlay\";\nexport { useBrandLoadingIcon } from \"./hooks/useBrandLoadingIcon\";\nexport {\n Toast,\n toastVariants,\n toastIconContainerVariants,\n} from \"./components/ui/feedback/Toast\";\nexport type { ToastProps } from \"./components/ui/feedback/Toast\";\n\n// UI - Form\nexport { Calendar, CalendarDayButton } from \"./components/ui/form/Calendar\";\nexport { Checkbox } from \"./components/ui/form/Checkbox\";\nexport { DatePicker } from \"./components/ui/form/DatePicker\";\nexport { DateRangePicker } from \"./components/ui/form/DateRangePicker\";\nexport { Input } from \"./components/ui/form/Input\";\nexport {\n InputOTP,\n InputOTPGroup,\n InputOTPSlot,\n InputOTPSeparator,\n} from \"./components/ui/form/InputOtp\";\nexport { RadioGroup, RadioGroupItem } from \"./components/ui/form/RadioGroup\";\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n} from \"./components/ui/form/Select\";\nexport { Switch } from \"./components/ui/form/Switch\";\nexport { Textarea } from \"./components/ui/form/Textarea\";\n\n// UI - Overlay\nexport {\n Command,\n CommandDialog,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandShortcut,\n CommandSeparator,\n} from \"./components/ui/overlay/Command\";\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n} from \"./components/ui/overlay/Dialog\";\nexport {\n Popover,\n PopoverTrigger,\n PopoverContent,\n PopoverAnchor,\n} from \"./components/ui/overlay/Popover\";\nexport {\n Sheet,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n} from \"./components/ui/overlay/Sheet\";\nexport {\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider,\n} from \"./components/ui/overlay/Tooltip\";\n\n// Embeds\nexport { FrillEmbed } from \"./components/embeds/FrillEmbed\";\nexport {\n CrispEmbed,\n openCrispHelpdesk,\n hideCrisp,\n showCrisp,\n updateCrispUser,\n} from \"./components/embeds/CrispEmbed\";\nexport { EmbedWidgets } from \"./components/embeds/EmbedWidgets\";\nexport { ClarityEmbed } from \"./components/embeds/ClarityEmbed\";\n\n// Store\nexport { useMdSidebarStore } from \"./store/useMdSidebarStore\";\nexport { useDesktopSidebarStore } from \"./store/useDesktopSidebarStore\";\nexport { useModalManager } from \"./store/useModalManager\";\nexport type { ModalData } from \"./store/useModalManager\";\n\n// Enums\nexport { AccountSectionType } from \"./enums/AccountSectionType\";\n\n// Hooks\nexport { default as usePasswordVisibility } from \"./hooks/usePasswordVisibility\";\nexport { default as useCountdownTimer } from \"./hooks/useCountdownTimer\";\nexport { default as useIsMobile } from \"./hooks/useIsMobile\";\nexport { useDebounce } from \"./hooks/useDebounce\";\nexport { useDebouncedEffect } from \"./hooks/useDebouncedEffect\";\nexport { useDebounceState } from \"./hooks/useDebounceState\";\n\n// Utils\nexport {\n formatPhone,\n formatTimer,\n formatFullName,\n formatCPF,\n formatCNPJ,\n formatCardNumber,\n} from \"./utils/format/masks\";\nexport {\n formatCurrency,\n formatCurrencyNumber,\n parseCurrencyToNumber,\n getCurrencyForGateway,\n getLocaleForCurrency,\n} from \"./utils/format/currency\";\nexport type { CurrencyCode } from \"./utils/format/currency\";\nexport {\n USD_GATEWAY,\n isUsdGateway,\n isUsdAccount,\n allowedPaymentMethods,\n isPaymentMethodAllowed,\n canBuyStandaloneAiCredits,\n} from \"./utils/format/gateway\";\nexport type { AccountGatewayLike, PaymentMethodKind } from \"./utils/format/gateway\";\nexport { isValidCPF, isValidCNPJ, isValidTaxId } from \"./utils/validators/common\";\nexport { BR_STATE_OPTIONS } from \"./utils/constants/br-states\";\nexport type { TimezoneOption } from \"./modules/accounts/services/timezone.service\";\nexport {\n buildLocaleOptions,\n LANGUAGE_OPTIONS as LOCALE_LANGUAGE_OPTIONS,\n} from \"./utils/intl/locales\";\nexport type { LocaleOption } from \"./utils/intl/locales\";\nexport { copyToClipboard, readFromClipboard } from \"./utils/browser/clipboard\";\n\n// UI - Form (new widgets)\nexport { FormField } from \"./components/ui/form/FormField\";\nexport { SelectField } from \"./components/ui/form/SelectField\";\nexport { ComboboxField } from \"./components/ui/form/ComboboxField\";\nexport type { ComboboxOption } from \"./components/ui/form/ComboboxField\";\nexport { default as PhoneInput } from \"./components/ui/form/PhoneInput\";\nexport { default as SwitchOptionFieldWithIcon } from \"./components/ui/form/SwitchOptionFieldWithIcon\";\nexport { TextAreaField } from \"./components/ui/form/TextAreaField\";\n\n// Account Module - Hooks\nexport {\n useCurrentAccount,\n ACCOUNT_QUERY_KEY,\n} from \"./modules/accounts/hooks/current-account.hook\";\nexport { useCurrencyFormatter } from \"./modules/accounts/hooks/use-currency-formatter.hook\";\nexport {\n useUpdateAccount,\n useUpdateAccountUser,\n useUpdateAccountUserById,\n useUpdateBillingData,\n useDeleteAccountUser,\n useDeleteAccount,\n ACCOUNT_USERS_QUERY_KEY,\n} from \"./modules/accounts/hooks/useAccountManagement\";\nexport { useViaCep } from \"./modules/accounts/hooks/useViaCep\";\nexport { useAccountToken } from \"./modules/accounts/hooks/use-account-token.hook\";\nexport { useRequiredBillingData } from \"./modules/accounts/hooks/use-required-billing-data.hook\";\nexport {\n hasCompleteBillingData,\n isBrazilianBillingAccount,\n} from \"./modules/accounts/utils/billing-data\";\nexport type { BillingDataSnapshot } from \"./modules/accounts/utils/billing-data\";\n\n// Account Types\nexport type {\n UpdateAccountRequest,\n UpdateBillingDataRequest,\n UpdateAccountUserRequest,\n ChangePasswordRequest,\n DeleteAccountActionResult,\n DeleteUserActionResult,\n UpdateAccountActionResult,\n UpdateUserActionResult,\n TwoFactorGenerateResult,\n TwoFactorActionResult,\n ContactResetResult,\n} from \"./modules/accounts/types\";\n\nexport { default as AccountModals } from \"./components/account/AccountModals\";\nexport { useAccountModals } from \"./store/useAccountModals\";\nexport type { AccountModalsConfig } from \"./store/useAccountModals\";\n\nexport { ModalManager } from \"./components/modals/ModalManager\";\nexport { Modals } from \"./components/modals/Modals\";\n\nexport { default as TwoFactorAuthModal } from \"./components/account/TwoFactorAuthModal\";\nexport { default as DisableTwoFactorAuthModal } from \"./components/account/DisableTwoFactorAuthModal\";\nexport { default as ConfirmGlobalPreferencesModal } from \"./components/account/ConfirmGlobalPreferencesModal\";\nexport { MyProfileSection } from \"./components/account/sections/MyProfileSection\";\nexport { PreferencesSection } from \"./components/account/sections/PreferencesSection\";\nexport { SecuritySection } from \"./components/account/sections/SecuritySection\";\nexport { ChangePasswordSection } from \"./components/account/sections/ChangePasswordSection\";\nexport { ChangeEmailModal } from \"./components/account/sections/ChangeEmailModal\";\nexport { ChangePhoneModal } from \"./components/account/sections/ChangePhoneModal\";\n\n// Account Constants\nexport {\n GENDER_OPTIONS,\n CURRENCY_OPTIONS,\n TIME_FORMAT_OPTIONS,\n NOTIFICATION_TYPES,\n LANGUAGE_OPTIONS,\n} from \"./components/account/constants\";\n\n// Image Upload\nexport {\n ImageUpload,\n ImageCropModal,\n ImageTooSmallModal,\n} from \"./components/widgets/ImageUpload\";\nexport {\n useImageUpload,\n type ImageTooSmallError,\n} from \"./modules/images/hooks/use-image-upload.hook\";\nexport {\n ACCEPTED_IMAGE_FORMATS,\n ACCEPTED_IMAGE_FORMATS_STRING,\n MAX_FILE_SIZE_MB,\n} from \"./modules/images/constants/image.constants\";\nexport type {\n ImageConfig,\n CropArea,\n ProcessedImage,\n CompressImageOptions,\n} from \"./modules/images/types/image.type\";\nexport { compressImage } from \"./modules/images/utils/compress-image\";\nexport { cropImageToCanvas } from \"./modules/images/utils/crop-image\";\nexport { base64ToFile, fileToBase64 } from \"./modules/images/utils/base64\";\nexport {\n validateImage,\n validateImageFormat,\n validateImageSize,\n getImageDimensions,\n} from \"./modules/images/utils/validate-image\";\n"],"mappings":"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","names":["default","LANGUAGE_OPTIONS"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Types\nexport * from \"./modules/auth/schema\";\nexport * from \"./modules/users/schema\";\nexport * from \"./modules/whitelabel/schema\";\n// Style types now come from whitelabel schema directly\nexport * from \"./infra/api/types\";\n\n// Contexts\nexport * from \"./providers/query.provider\";\nexport * from \"./providers/auth.provider\";\nexport * from \"./providers/whitelabel.provider\";\nexport {\n NavigationProvider,\n useNavigationFallback,\n} from \"./providers/navigation.provider\";\n\n// Navigation (safe drop-in replacement for next/navigation)\nexport { useRouter } from \"./hooks/useRouter\";\nexport type { SafeAppRouter } from \"./hooks/useRouter\";\nexport {\n usePathname,\n useSearchParams,\n useParams,\n redirect,\n notFound,\n} from \"./utils/next-navigation\";\n\n// Hooks\nexport {\n useListProjectUsers,\n LIST_PROJECT_USERS_QUERY_KEY,\n LIST_PROJECT_USERS_BASE_KEY,\n} from \"./modules/projects/hooks/list-project-users.hook\";\nexport {\n useListAvailableUsers,\n LIST_AVAILABLE_USERS_QUERY_KEY,\n LIST_AVAILABLE_USERS_BASE_KEY,\n} from \"./modules/projects/hooks/list-available-users.hook\";\nexport {\n useListAllAccountUsers,\n LIST_ALL_ACCOUNT_USERS_QUERY_KEY,\n LIST_ALL_ACCOUNT_USERS_BASE_KEY,\n} from \"./modules/projects/hooks/list-all-account-users.hook\";\nexport {\n useProjects,\n PROJECTS_BASE_KEY,\n PROJECTS_QUERY_KEY,\n} from \"./modules/projects/hooks/list-projects.hook\";\nexport {\n useInfiniteProjects,\n INFINITE_PROJECTS_QUERY_KEY,\n} from \"./modules/projects/hooks/list-infinite-projects.hook\";\nexport { useProject } from \"./modules/projects/hooks/find-project.hook\";\nexport { useCreateProject } from \"./modules/projects/hooks/create-project.hook\";\nexport { useUpdateProject } from \"./modules/projects/hooks/update-project.hook\";\nexport { useDeleteProject } from \"./modules/projects/hooks/delete-project.hook\";\nexport type {\n Project,\n ProjectsPage,\n CreateProjectRequest,\n UpdateProjectRequest,\n ProjectUser,\n AccountUser,\n AddRemoveProjectUsersParams,\n ListUsersParams,\n UsersPage,\n} from \"./modules/projects/types\";\nexport { ProjectSchema } from \"./modules/projects/types\";\nexport type { ListProjectsActionParams } from \"./modules/projects/actions/list-projects.action\";\nexport {\n useUserQuery,\n useUserValidateSession,\n useInvalidateUser,\n useSetUserData,\n useTwoFactorVerify,\n USER_QUERY_KEY,\n} from \"./modules/auth/hooks/useUserQuery\";\nexport { useManagementPermissions } from \"./modules/auth/hooks/useManagementPermissions\";\nexport type { ManagementPermission } from \"./modules/auth/types/management-permission.type\";\nexport { useSocialGoogleLogin } from \"./modules/auth/hooks/social-google-login.hook\";\nexport { useSocialGoogleOnboarding } from \"./modules/auth/hooks/social-google-onboarding.hook\";\nexport { useUnlinkGoogle } from \"./modules/auth/hooks/unlink-google.hook\";\nexport { useSubscriptions } from \"./modules/subscriptions/hooks/list-subscriptions.hook\";\nexport {\n SUBSCRIPTIONS_QUERY_KEY,\n CHARGES_QUERY_KEY,\n FREEZE_NOTICE_QUERY_KEY,\n} from \"./modules/subscriptions/constants/query-keys.constants\";\nexport { useActiveSubscription } from \"./modules/subscriptions/hooks/find-active-subscription.hook\";\nexport { useCancelledSubscriptionGuard } from \"./modules/subscriptions/hooks/use-cancelled-subscription-guard\";\nexport { useAccountFreeze } from \"./modules/subscriptions/hooks/use-account-freeze.hook\";\nexport { useFreezeNotice } from \"./modules/subscriptions/hooks/use-freeze-notice.hook\";\nexport { usePlanById } from \"./modules/plans/hooks/use-plan-by-id.hook\";\nexport { useHasPlanAddon } from \"./modules/plans/hooks/use-has-plan-addon.hook\";\nexport { useCalculateSubscription } from \"./modules/subscriptions/hooks/calculate-subscription.hook\";\nexport { useUpdateSubscriptionPlan } from \"./modules/subscriptions/hooks/update-subscription-plan.hook\";\nexport { useUpdateSubscriptionPayment } from \"./modules/subscriptions/hooks/update-subscription-payment.hook\";\nexport { useCards, CARDS_QUERY_KEY } from \"./modules/cards/hooks/cards.hook\";\nexport { useCardById } from \"./modules/cards/hooks/card-by-id.hook\";\nexport { useCreateCard } from \"./modules/cards/hooks/create-card.hook\";\nexport { useCreateSetupIntent } from \"./modules/cards/hooks/create-setup-intent.hook\";\nexport { useSetDefaultCard } from \"./modules/cards/hooks/set-default-card.hook\";\nexport { useDeleteCard } from \"./modules/cards/hooks/delete-card.hook\";\nexport { useDeleteConfirmation } from \"./modules/cards/hooks/delete-confirmation.hook\";\nexport type { DeleteConfirmationFormData } from \"./modules/cards/hooks/delete-confirmation.hook\";\nexport { useHandleDeleteCard } from \"./modules/cards/hooks/handle-delete-card.hook\";\nexport { useCharges } from \"./modules/charges/hooks/charges.hook\";\nexport { useChargeById } from \"./modules/charges/hooks/charge-by-id.hook\";\nexport { useChargeAction } from \"./modules/charges/hooks/charge-action.hook\";\nexport { useIaCredits } from \"./modules/ia-credits/hooks/ia-credits.hook\";\nexport type {\n IaCreditsSummary,\n IaCreditsSummaryData,\n IaCreditAddBatch,\n IaCreditOperation,\n} from \"./modules/ia-credits/types\";\nexport { usePurchaseIaCredits } from \"./modules/ia-credits/hooks/purchase-ia-credits.hook\";\nexport { useAiCreditsOfferEligibility } from \"./modules/ia-credits/hooks/use-offer-eligibility.hook\";\nexport {\n AI_CREDITS_FIRST_PURCHASE_OFFER,\n AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY,\n AI_CREDITS_OFFER_MAX_REPEATS,\n AiCreditsOfferStage,\n PAYMENT_METHOD_CARD,\n PAYMENT_METHOD_PIX,\n} from \"./modules/ia-credits/constants/ai-credits-offer.constants\";\nexport type {\n AiCreditsOfferEligibility,\n AiCreditsOfferEligibilityResponse,\n AiCreditsOfferIneligibilityReason,\n PurchaseIaCreditsInput,\n PurchaseIaCreditsResult,\n} from \"./modules/ia-credits/types/ai-credits-offer.type\";\nexport {\n readOfferStageCookie,\n writeOfferStageCookie,\n} from \"./modules/ia-credits/utils/offer-stage-cookie\";\nexport type { OfferStageRecord } from \"./modules/ia-credits/utils/offer-stage-cookie\";\nexport type {\n Subscription,\n SubscriptionItem,\n FindSubscriptionsParams,\n} from \"./modules/subscriptions/types/subscription.type\";\nexport {\n SubscriptionSchema,\n SubscriptionItemSchema,\n} from \"./modules/subscriptions/types/subscription.type\";\nexport {\n ADDON_IDS,\n AI_CREDIT_OPTIONS,\n AI_CREDIT_SIZES,\n AI_CREDIT_VALUES,\n buildAiCreditOptions,\n resolveAiCreditPrice,\n} from \"./modules/subscriptions/constants/addons.constants\";\nexport type {\n AddonKindEnum,\n AiCreditOption,\n} from \"./modules/subscriptions/constants/addons.constants\";\nexport type {\n Plan,\n PlanItem,\n UiPlan,\n PlanFeature,\n PlanMainFeatures,\n PlanPricingByPeriod,\n PlanTooltips,\n} from \"./modules/plans/types/plan.type\";\nexport { PlanSchema, PlanItemSchema } from \"./modules/plans/types/plan.type\";\nexport {\n mapApiPlanToUiPlan,\n mapApiPlanToUiPlanForCurrency,\n} from \"./modules/plans/utils/map-api-plan-to-ui\";\nexport type { PlanBillingPeriod } from \"./modules/plans/utils/map-api-plan-to-ui\";\nexport {\n usePlans,\n PLANS_QUERY_KEY,\n} from \"./modules/plans/hooks/list-plans.hook\";\nexport type { BillingPeriod } from \"./modules/subscriptions/types/billing-period.type\";\nexport type {\n CalculateSubscriptionRequest,\n CalculateSubscriptionResponse,\n UpdateSubscriptionPlanRequest,\n} from \"./modules/subscriptions/types/calculate-subscription.type\";\nexport {\n PixPendingDataSchema,\n SubscriptionPendingPixResponseSchema,\n isSubscriptionPendingPixResponse,\n} from \"./modules/subscriptions/types/pix-pending.type\";\nexport type {\n PixPendingData,\n SubscriptionPendingPixResponse,\n} from \"./modules/subscriptions/types/pix-pending.type\";\nexport type {\n Card as PaymentCard,\n CreateCardRequest,\n FindCardsParams,\n SetupIntent,\n} from \"./modules/cards/types\";\nexport {\n CardSchema as PaymentCardSchema,\n CreateCardRequestSchema,\n FindCardsParamsSchema,\n SetupIntentSchema,\n} from \"./modules/cards/types\";\nexport type {\n Charge,\n FindChargesParams,\n PayChargeInput,\n} from \"./modules/charges/types/charge.type\";\nexport {\n ChargeSchema,\n FindChargesParamsSchema,\n PayChargeInputSchema,\n} from \"./modules/charges/types/charge.type\";\nexport { buildPlanExtras } from \"./modules/subscriptions/utils/build-plan-extras\";\nexport { hasSubscriptionExpired } from \"./modules/subscriptions/utils/has-subscription-expired\";\nexport { hasActivePaidSubscription } from \"./modules/subscriptions/utils/has-active-paid-subscription\";\nexport { hasPaidSubscription } from \"./modules/subscriptions/utils/has-paid-subscription\";\nexport {\n confirmPaymentIfRequired,\n PaymentAuthenticationError,\n} from \"./modules/subscriptions/utils/confirm-payment-if-required\";\nexport { SUBSCRIPTION_GRACE_PERIOD_DAYS } from \"./modules/subscriptions/constants/subscription.constants\";\nexport { getSubscriptionCancellationState } from \"./modules/subscriptions/utils/get-subscription-cancellation-state\";\nexport type { SubscriptionCancellationState } from \"./modules/subscriptions/utils/get-subscription-cancellation-state\";\nexport {\n ACCOUNT_FREEZE_AFTER_DAYS,\n FREEZE_WARNING_DAYS_BEFORE,\n} from \"./modules/subscriptions/constants/subscription.constants\";\nexport { getAccountFreezeState } from \"./modules/subscriptions/utils/get-account-freeze-state\";\nexport type { AccountFreezeState } from \"./modules/subscriptions/utils/get-account-freeze-state\";\nexport { getAccountFreezeDate } from \"./modules/subscriptions/utils/get-account-freeze-date\";\nexport type { FreezeNotice } from \"./modules/subscriptions/types/freeze-notice.type\";\nexport {\n useCountPages,\n COUNT_PAGES_QUERY_KEY,\n} from \"./modules/pages/hooks/count-pages.hook\";\nexport { PAGES_QUERY_KEY } from \"./modules/pages/constants/query-keys.constants\";\nexport {\n getPriceFromCalculatedData,\n getOriginalPriceFromCalculatedData,\n periodicityToBillingPeriod,\n} from \"./modules/subscriptions/utils/periodicity\";\nexport { useBuyCreditsModal } from \"./store/useBuyCreditsModal\";\nexport { useCreditsDisabledModal } from \"./store/useCreditsDisabledModal\";\nexport { usePaidPlanRequiredModal } from \"./store/usePaidPlanRequiredModal\";\nexport { useAccountDeletionWarningModal } from \"./store/useAccountDeletionWarningModal\";\nexport { useAiCreditsOfferModal } from \"./store/useAiCreditsOfferModal\";\nexport { default as BuyCreditsModal } from \"./components/modals/BuyCreditsModal\";\nexport { default as AiCreditsOfferModal } from \"./components/modals/promo/AiCreditsOfferModal\";\nexport { AiCreditsOfferGate } from \"./components/layouts/AiCreditsOfferGate\";\nexport type { AiCreditsOfferGateProps } from \"./components/layouts/AiCreditsOfferGate\";\nexport { default as CreditsDisabledModal } from \"./components/modals/CreditsDisabledModal\";\nexport { default as PaidPlanRequiredModal } from \"./components/modals/PaidPlanRequiredModal\";\nexport { default as AccountFrozenModal } from \"./components/modals/AccountFrozenModal\";\nexport { default as AccountDeletionWarningModal } from \"./components/modals/AccountDeletionWarningModal\";\nexport { default as RequiredBillingDataModal } from \"./components/modals/billing/RequiredBillingDataModal\";\nexport { default as AddCardModal } from \"./components/modals/cards/AddCardModal\";\nexport { DeleteCardModal } from \"./components/modals/cards/DeleteCardModal\";\nexport { CannotDeleteCardModal } from \"./components/modals/cards/CannotDeleteCardModal\";\nexport {\n CardFormFields,\n cardFormSchema,\n buildCardFormSchema,\n} from \"./components/modals/cards/CardFormFields\";\nexport type { CardFormData } from \"./components/modals/cards/CardFormFields\";\nexport { Skeleton } from \"./components/ui/feedback/Skeleton\";\nexport { PaymentInfoCard } from \"./components/ui/data-display/PaymentInfoCard\";\nexport { CardBrandIcon } from \"./components/ui/data-display/CardBrandIcons\";\nexport { CardItem } from \"./components/ui/data-display/CardItem\";\n\n// Providers\nexport { QueryProvider } from \"./providers/query.provider\";\n\n// Middleware\nexport { createAuthMiddleware } from \"./middlewares/create-auth-middleware\";\nexport { createMiddlewareChain } from \"./middlewares/chain\";\nexport { continueChain, stopChain } from \"./middlewares/types\";\nexport type {\n MiddlewareFunction,\n MiddlewareConfig,\n MiddlewareResult,\n} from \"./middlewares/types\";\n\n// Utils\nexport { cn } from \"./infra/utils/clsx\";\nexport { formatShortDate, formatDateTime, calendarDateSchema, toCalendarDate, daysBetween } from \"./infra/utils/date\";\nexport { buildQueryParams } from \"./infra/utils/params\";\nexport { parseSchema, parseResult } from \"./infra/utils/parser\";\nexport { withAction } from \"./utils/withAction\";\nexport { resolveSafeRedirect, sameHostOr } from \"./utils/redirect\";\nexport { COUNTRIES, flagUrl } from \"./utils/countries\";\nexport type { Country } from \"./utils/countries\";\n\n// Layout\nexport {\n MainLayout,\n MainLayoutContent,\n MainLayoutSpacer,\n MainLayoutMain,\n} from \"./components/layouts/MainLayout\";\nexport { NavBar } from \"./components/layouts/NavBar\";\nexport type { NavBarProps } from \"./components/layouts/NavBar\";\nexport { AppNavBar } from \"./components/layouts/AppNavBar\";\nexport type { AppNavBarProps } from \"./components/layouts/AppNavBar\";\nexport { AppMobileNavBar } from \"./components/layouts/AppMobileNavBar\";\nexport type { AppMobileNavBarProps } from \"./components/layouts/AppMobileNavBar\";\nexport { SideBarNavigation } from \"./components/layouts/SideBarNavigation\";\nexport { MdSideBarNavigation } from \"./components/layouts/MdSideBarNavigation\";\nexport { default as NotificationCard } from \"./components/widgets/notifications/NotificationCard\";\nexport type { NotificationCardProps } from \"./components/widgets/notifications/NotificationCard\";\nexport { NotificationsPopover } from \"./components/layouts/NotificationsPopover\";\nexport type { NotificationsPopoverProps } from \"./components/layouts/NotificationsPopover\";\nexport { NotificationPageContent } from \"./components/pages/notifications/Notifications\";\nexport { NotFoundPage } from \"./components/pages/NotFoundPage\";\nexport { UsersSelectorPopover } from \"./components/layouts/UsersSelectorPopover\";\nexport type { UsersSelectorPopoverProps } from \"./components/layouts/UsersSelectorPopover\";\nexport { ProfilePopover } from \"./components/layouts/ProfilePopover\";\nexport type {\n ProfilePopoverProps,\n ProfileMenuItem,\n} from \"./components/layouts/ProfilePopover\";\nexport { default as WhitelabelCodes } from \"./components/layouts/WhitelabelCodes\";\nexport { NavBarItem } from \"./components/layouts/NavBarItem\";\nexport type { NavBarItemProps } from \"./components/layouts/NavBarItem\";\n\n// Navigation\nexport { AppNavigation } from \"./components/navigation/AppNavigation\";\nexport { CancelledSubscriptionBanner } from \"./components/navigation/CancelledSubscriptionBanner\";\nexport { SubscriptionBanner } from \"./components/navigation/SubscriptionBanner\";\nexport { OverdueInvoiceBanner } from \"./components/navigation/OverdueInvoiceBanner\";\nexport { UpcomingInvoiceBanner } from \"./components/navigation/UpcomingInvoiceBanner\";\nexport { TrialBanner } from \"./components/navigation/TrialBanner\";\nexport { FrozenAccountBanner } from \"./components/navigation/FrozenAccountBanner\";\nexport { FreezeWarningBanner } from \"./components/navigation/FreezeWarningBanner\";\nexport type { NavItemConfig } from \"./components/navigation/subcomponents/NavItems\";\nexport { useMobileNavbarSheet } from \"./store/useMobileNavbarSheet\";\n\n// Auth Query Key Utilities\nexport {\n useAuthQueryKey,\n useWlQueryKey,\n useWlId,\n} from \"./hooks/useAuthQueryKey\";\n\n// Hooks\nexport { default as useCopyToClipboard } from \"./hooks/copy-to-clipboard.hook\";\n\n// UI - Buttons\nexport { Button, buttonVariants } from \"./components/ui/buttons/Button\";\nexport { CopyButton } from \"./components/ui/buttons/CopyButton\";\nexport { default as CancelButton } from \"./components/ui/buttons/CancelButton\";\n\n// UI - Data Display\nexport {\n Accordion,\n AccordionItem,\n AccordionTrigger,\n AccordionContent,\n} from \"./components/ui/data-display/Accordion\";\nexport { Badge, badgeVariants } from \"./components/ui/data-display/Badge\";\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardAction,\n CardDescription,\n CardContent,\n} from \"./components/ui/data-display/Card\";\nexport {\n Pagination,\n PaginationContent,\n PaginationLink,\n PaginationItem,\n PaginationPrevious,\n PaginationNext,\n PaginationEllipsis,\n} from \"./components/ui/data-display/Pagination\";\nexport { ScrollArea, ScrollBar } from \"./components/ui/data-display/ScrollArea\";\nexport { Separator } from \"./components/ui/data-display/Separator\";\nexport {\n Table,\n TableHeader,\n TableBody,\n TableFooter,\n TableHead,\n TableRow,\n TableCell,\n TableCaption,\n} from \"./components/ui/data-display/Table\";\nexport {\n Tabs,\n TabsList,\n TabsTrigger,\n TabsContent,\n} from \"./components/ui/data-display/Tabs\";\nexport { UserAvatar } from \"./components/ui/data-display/UserAvatar\";\nexport type { UserAvatarProps } from \"./components/ui/data-display/UserAvatar\";\n\n// UI - Feedback\nexport { default as CircularProgress } from \"./components/ui/feedback/CircularProgress\";\nexport { default as DefaultCircularProgress } from \"./components/ui/feedback/DefaultCircularProgress\";\nexport { Progress } from \"./components/ui/feedback/Progress\";\nexport { LoadingOverlay } from \"./components/ui/feedback/LoadingOverlay\";\nexport { useBrandLoadingIcon } from \"./hooks/useBrandLoadingIcon\";\nexport {\n Toast,\n toastVariants,\n toastIconContainerVariants,\n} from \"./components/ui/feedback/Toast\";\nexport type { ToastProps } from \"./components/ui/feedback/Toast\";\n\n// UI - Form\nexport { Calendar, CalendarDayButton } from \"./components/ui/form/Calendar\";\nexport { Checkbox } from \"./components/ui/form/Checkbox\";\nexport { DatePicker } from \"./components/ui/form/DatePicker\";\nexport { DateRangePicker } from \"./components/ui/form/DateRangePicker\";\nexport { Input } from \"./components/ui/form/Input\";\nexport {\n InputOTP,\n InputOTPGroup,\n InputOTPSlot,\n InputOTPSeparator,\n} from \"./components/ui/form/InputOtp\";\nexport { RadioGroup, RadioGroupItem } from \"./components/ui/form/RadioGroup\";\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n} from \"./components/ui/form/Select\";\nexport { Switch } from \"./components/ui/form/Switch\";\nexport { Textarea } from \"./components/ui/form/Textarea\";\n\n// UI - Overlay\nexport {\n Command,\n CommandDialog,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandShortcut,\n CommandSeparator,\n} from \"./components/ui/overlay/Command\";\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n} from \"./components/ui/overlay/Dialog\";\nexport {\n Popover,\n PopoverTrigger,\n PopoverContent,\n PopoverAnchor,\n} from \"./components/ui/overlay/Popover\";\nexport {\n Sheet,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n} from \"./components/ui/overlay/Sheet\";\nexport {\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider,\n} from \"./components/ui/overlay/Tooltip\";\n\n// Embeds\nexport { FrillEmbed } from \"./components/embeds/FrillEmbed\";\nexport {\n CrispEmbed,\n openCrispHelpdesk,\n hideCrisp,\n showCrisp,\n updateCrispUser,\n} from \"./components/embeds/CrispEmbed\";\nexport { EmbedWidgets } from \"./components/embeds/EmbedWidgets\";\nexport { ClarityEmbed } from \"./components/embeds/ClarityEmbed\";\n\n// Store\nexport { useMdSidebarStore } from \"./store/useMdSidebarStore\";\nexport { useDesktopSidebarStore } from \"./store/useDesktopSidebarStore\";\nexport { useModalManager } from \"./store/useModalManager\";\nexport type { ModalData } from \"./store/useModalManager\";\n\n// Enums\nexport { AccountSectionType } from \"./enums/AccountSectionType\";\n\n// Hooks\nexport { default as usePasswordVisibility } from \"./hooks/usePasswordVisibility\";\nexport { default as useCountdownTimer } from \"./hooks/useCountdownTimer\";\nexport { default as useIsMobile } from \"./hooks/useIsMobile\";\nexport { useDebounce } from \"./hooks/useDebounce\";\nexport { useDebouncedEffect } from \"./hooks/useDebouncedEffect\";\nexport { useDebounceState } from \"./hooks/useDebounceState\";\n\n// Utils\nexport {\n formatPhone,\n formatTimer,\n formatFullName,\n formatCPF,\n formatCNPJ,\n formatCardNumber,\n} from \"./utils/format/masks\";\nexport {\n formatCurrency,\n formatCurrencyNumber,\n parseCurrencyToNumber,\n getCurrencyForGateway,\n getLocaleForCurrency,\n} from \"./utils/format/currency\";\nexport type { CurrencyCode } from \"./utils/format/currency\";\nexport {\n USD_GATEWAY,\n isUsdGateway,\n isUsdAccount,\n allowedPaymentMethods,\n isPaymentMethodAllowed,\n canBuyStandaloneAiCredits,\n} from \"./utils/format/gateway\";\nexport type { AccountGatewayLike, PaymentMethodKind } from \"./utils/format/gateway\";\nexport { isValidCPF, isValidCNPJ, isValidTaxId } from \"./utils/validators/common\";\nexport { BR_STATE_OPTIONS } from \"./utils/constants/br-states\";\nexport type { TimezoneOption } from \"./modules/accounts/services/timezone.service\";\nexport {\n buildLocaleOptions,\n LANGUAGE_OPTIONS as LOCALE_LANGUAGE_OPTIONS,\n} from \"./utils/intl/locales\";\nexport type { LocaleOption } from \"./utils/intl/locales\";\nexport { copyToClipboard, readFromClipboard } from \"./utils/browser/clipboard\";\n\n// UI - Form (new widgets)\nexport { FormField } from \"./components/ui/form/FormField\";\nexport { SelectField } from \"./components/ui/form/SelectField\";\nexport { ComboboxField } from \"./components/ui/form/ComboboxField\";\nexport type { ComboboxOption } from \"./components/ui/form/ComboboxField\";\nexport { default as PhoneInput } from \"./components/ui/form/PhoneInput\";\nexport { default as SwitchOptionFieldWithIcon } from \"./components/ui/form/SwitchOptionFieldWithIcon\";\nexport { TextAreaField } from \"./components/ui/form/TextAreaField\";\n\n// Account Module - Hooks\nexport {\n useCurrentAccount,\n ACCOUNT_QUERY_KEY,\n} from \"./modules/accounts/hooks/current-account.hook\";\nexport { useCurrencyFormatter } from \"./modules/accounts/hooks/use-currency-formatter.hook\";\nexport {\n useUpdateAccount,\n useUpdateAccountUser,\n useUpdateAccountUserById,\n useUpdateBillingData,\n useDeleteAccountUser,\n useDeleteAccount,\n ACCOUNT_USERS_QUERY_KEY,\n} from \"./modules/accounts/hooks/useAccountManagement\";\nexport { useViaCep } from \"./modules/accounts/hooks/useViaCep\";\nexport { useAccountToken } from \"./modules/accounts/hooks/use-account-token.hook\";\nexport { useRequiredBillingData } from \"./modules/accounts/hooks/use-required-billing-data.hook\";\nexport {\n hasCompleteBillingData,\n isBrazilianBillingAccount,\n} from \"./modules/accounts/utils/billing-data\";\nexport type { BillingDataSnapshot } from \"./modules/accounts/utils/billing-data\";\n\n// Account Types\nexport type {\n UpdateAccountRequest,\n UpdateBillingDataRequest,\n UpdateAccountUserRequest,\n ChangePasswordRequest,\n DeleteAccountActionResult,\n DeleteUserActionResult,\n UpdateAccountActionResult,\n UpdateUserActionResult,\n TwoFactorGenerateResult,\n TwoFactorActionResult,\n ContactResetResult,\n} from \"./modules/accounts/types\";\n\nexport { default as AccountModals } from \"./components/account/AccountModals\";\nexport { useAccountModals } from \"./store/useAccountModals\";\nexport type { AccountModalsConfig } from \"./store/useAccountModals\";\n\nexport { ModalManager } from \"./components/modals/ModalManager\";\nexport { Modals } from \"./components/modals/Modals\";\n\nexport { default as TwoFactorAuthModal } from \"./components/account/TwoFactorAuthModal\";\nexport { default as DisableTwoFactorAuthModal } from \"./components/account/DisableTwoFactorAuthModal\";\nexport { default as ConfirmGlobalPreferencesModal } from \"./components/account/ConfirmGlobalPreferencesModal\";\nexport { MyProfileSection } from \"./components/account/sections/MyProfileSection\";\nexport { PreferencesSection } from \"./components/account/sections/PreferencesSection\";\nexport { SecuritySection } from \"./components/account/sections/SecuritySection\";\nexport { ChangePasswordSection } from \"./components/account/sections/ChangePasswordSection\";\nexport { ChangeEmailModal } from \"./components/account/sections/ChangeEmailModal\";\nexport { ChangePhoneModal } from \"./components/account/sections/ChangePhoneModal\";\n\n// Account Constants\nexport {\n GENDER_OPTIONS,\n CURRENCY_OPTIONS,\n TIME_FORMAT_OPTIONS,\n NOTIFICATION_TYPES,\n LANGUAGE_OPTIONS,\n} from \"./components/account/constants\";\n\n// Image Upload\nexport {\n ImageUpload,\n ImageCropModal,\n ImageTooSmallModal,\n} from \"./components/widgets/ImageUpload\";\nexport {\n useImageUpload,\n type ImageTooSmallError,\n} from \"./modules/images/hooks/use-image-upload.hook\";\nexport {\n ACCEPTED_IMAGE_FORMATS,\n ACCEPTED_IMAGE_FORMATS_STRING,\n MAX_FILE_SIZE_MB,\n} from \"./modules/images/constants/image.constants\";\nexport type {\n ImageConfig,\n CropArea,\n ProcessedImage,\n CompressImageOptions,\n} from \"./modules/images/types/image.type\";\nexport { compressImage } from \"./modules/images/utils/compress-image\";\nexport { cropImageToCanvas } from \"./modules/images/utils/crop-image\";\nexport { base64ToFile, fileToBase64 } from \"./modules/images/utils/base64\";\nexport {\n validateImage,\n validateImageFormat,\n validateImageSize,\n getImageDimensions,\n} from \"./modules/images/utils/validate-image\";\n"],"mappings":"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","names":["default","LANGUAGE_OPTIONS"]}
|
|
@@ -15,10 +15,12 @@ var AiCreditsOfferStage = /* @__PURE__ */ ((AiCreditsOfferStage2) => {
|
|
|
15
15
|
AiCreditsOfferStage2[AiCreditsOfferStage2["CONVERTED"] = 6] = "CONVERTED";
|
|
16
16
|
return AiCreditsOfferStage2;
|
|
17
17
|
})(AiCreditsOfferStage || {});
|
|
18
|
+
const AI_CREDITS_OFFER_MAX_REPEATS = 3;
|
|
18
19
|
const AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY = ["ai-credits-offer-eligibility"];
|
|
19
20
|
export {
|
|
20
21
|
AI_CREDITS_FIRST_PURCHASE_OFFER,
|
|
21
22
|
AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY,
|
|
23
|
+
AI_CREDITS_OFFER_MAX_REPEATS,
|
|
22
24
|
AiCreditsOfferStage,
|
|
23
25
|
PAYMENT_METHOD_CARD,
|
|
24
26
|
PAYMENT_METHOD_PIX
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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\n/**\n * Teto de REPETIÇÕES dos estágios repetíveis (campanha e sem-crédito) — repetições ALÉM da primeira\n * aparição, então `3` permite 4 exibições daquele estágio. A campanha é deliberadamente agressiva\n * (vale inclusive para quem acabou de se cadastrar), mas \"agressiva\" não é \"toda sessão para\n * sempre\": esgotado o teto, a oferta só volta se a conta subir de estágio na escada.\n */\nexport const AI_CREDITS_OFFER_MAX_REPEATS = 3;\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;AAgBL,MAAM,+BAA+B;AAErC,MAAM,yCAAyC,CAAC,8BAA8B;","names":["AiCreditsOfferStage"]}
|
|
@@ -4,25 +4,27 @@ const COOKIE_NAME = "ai-credits-offer-stage";
|
|
|
4
4
|
const ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;
|
|
5
5
|
const EMPTY_RECORD = {
|
|
6
6
|
stage: AiCreditsOfferStage.NONE,
|
|
7
|
-
lastSessionIat: 0
|
|
7
|
+
lastSessionIat: 0,
|
|
8
|
+
repeatCount: 0
|
|
8
9
|
};
|
|
9
10
|
function readOfferStageCookie(idAccount) {
|
|
10
11
|
if (typeof document === "undefined") return EMPTY_RECORD;
|
|
11
12
|
const match = new RegExp(`(?:^|;\\s*)${COOKIE_NAME}=([^;]+)`).exec(document.cookie);
|
|
12
13
|
if (!match) return EMPTY_RECORD;
|
|
13
14
|
const raw = decodeURIComponent(match[1]);
|
|
14
|
-
const [rawAccount, rawStage, rawIat] = raw.split(":");
|
|
15
|
+
const [rawAccount, rawStage, rawIat, rawRepeat] = raw.split(":");
|
|
15
16
|
if (Number(rawAccount) !== idAccount) return EMPTY_RECORD;
|
|
16
17
|
const stage = Number(rawStage);
|
|
17
18
|
if (!(stage in AiCreditsOfferStage)) return EMPTY_RECORD;
|
|
18
19
|
return {
|
|
19
20
|
stage,
|
|
20
|
-
lastSessionIat: Number(rawIat) || 0
|
|
21
|
+
lastSessionIat: Number(rawIat) || 0,
|
|
22
|
+
repeatCount: Number(rawRepeat) || 0
|
|
21
23
|
};
|
|
22
24
|
}
|
|
23
25
|
function writeOfferStageCookie(idAccount, record, whitelabelDomain) {
|
|
24
26
|
if (typeof document === "undefined") return;
|
|
25
|
-
const value = `${idAccount}:${record.stage}:${record.lastSessionIat}`;
|
|
27
|
+
const value = `${idAccount}:${record.stage}:${record.lastSessionIat}:${record.repeatCount}`;
|
|
26
28
|
const domain = cookieDomainFromDomain(whitelabelDomain);
|
|
27
29
|
const parts = [
|
|
28
30
|
`${COOKIE_NAME}=${encodeURIComponent(value)}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/ia-credits/utils/offer-stage-cookie.ts"],"sourcesContent":["import { cookieDomainFromDomain } from '../../../utils/intl/cookie-domain';\nimport { AiCreditsOfferStage } from '../constants/ai-credits-offer.constants';\n\nconst COOKIE_NAME = 'ai-credits-offer-stage';\nconst ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;\n\nexport interface OfferStageRecord {\n stage: AiCreditsOfferStage;\n /** `iat` do JWT na sessão em que este estágio foi gravado — é o que diferencia \"sessão nova\". */\n lastSessionIat: number;\n}\n\nconst EMPTY_RECORD: OfferStageRecord = {\n stage: AiCreditsOfferStage.NONE,\n lastSessionIat: 0,\n};\n\n/**\n * Cookie `ai-credits-offer-stage`, no domínio pai da whitelabel (`cookieDomainFromDomain` — mesmo\n * mecanismo do cookie de locale, que por sua vez espelha o cookie de auth `greatapps`): sustenta a\n * escada de estágios entre app/accounts/editor, 1 ano de validade.\n *\n * Valor: `${idAccount}:${stage}:${lastSessionIat}`. Registro de OUTRA conta\n * (id diferente do gravado) lê como estágio zerado — last-writer-wins, aceito porque o cookie é por\n * navegador e trocar de conta paga no mesmo device durante a janela da oferta é evento raro.\n */\nexport function readOfferStageCookie(idAccount: number): OfferStageRecord {\n if (typeof document === 'undefined') return EMPTY_RECORD;\n\n const match = new RegExp(`(?:^|;\\\\s*)${COOKIE_NAME}=([^;]+)`).exec(document.cookie);\n if (!match) return EMPTY_RECORD;\n\n const raw = decodeURIComponent(match[1]);\n const [rawAccount, rawStage, rawIat] = raw.split(':');\n\n if (Number(rawAccount) !== idAccount) return EMPTY_RECORD;\n\n const stage = Number(rawStage);\n if (!(stage in AiCreditsOfferStage)) return EMPTY_RECORD;\n\n return {\n stage,\n lastSessionIat: Number(rawIat) || 0,\n };\n}\n\nexport function writeOfferStageCookie(\n idAccount: number,\n record: OfferStageRecord,\n whitelabelDomain?: string | null\n): void {\n if (typeof document === 'undefined') return;\n\n const value = `${idAccount}:${record.stage}:${record.lastSessionIat}`;\n const domain = cookieDomainFromDomain(whitelabelDomain);\n const parts = [\n `${COOKIE_NAME}=${encodeURIComponent(value)}`,\n 'path=/',\n `max-age=${ONE_YEAR_SECONDS}`,\n 'samesite=lax',\n domain ? `domain=${domain}` : '',\n typeof window !== 'undefined' && window.location.protocol === 'https:' ? 'secure' : '',\n ].filter(Boolean);\n\n document.cookie = parts.join('; ');\n}\n"],"mappings":"AAAA,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AAEpC,MAAM,cAAc;AACpB,MAAM,mBAAmB,KAAK,KAAK,KAAK;
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/ia-credits/utils/offer-stage-cookie.ts"],"sourcesContent":["import { cookieDomainFromDomain } from '../../../utils/intl/cookie-domain';\nimport { AiCreditsOfferStage } from '../constants/ai-credits-offer.constants';\n\nconst COOKIE_NAME = 'ai-credits-offer-stage';\nconst ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;\n\nexport interface OfferStageRecord {\n stage: AiCreditsOfferStage;\n /** `iat` do JWT na sessão em que este estágio foi gravado — é o que diferencia \"sessão nova\". */\n lastSessionIat: number;\n /** Quantas vezes um estágio REPETÍVEL (campanha / sem crédito) já foi apresentado. */\n repeatCount: number;\n}\n\nconst EMPTY_RECORD: OfferStageRecord = {\n stage: AiCreditsOfferStage.NONE,\n lastSessionIat: 0,\n repeatCount: 0,\n};\n\n/**\n * Cookie `ai-credits-offer-stage`, no domínio pai da whitelabel (`cookieDomainFromDomain` — mesmo\n * mecanismo do cookie de locale, que por sua vez espelha o cookie de auth `greatapps`): sustenta a\n * escada de estágios entre app/accounts/editor, 1 ano de validade.\n *\n * Valor: `${idAccount}:${stage}:${lastSessionIat}:${repeatCount}`. Registro de OUTRA conta\n * (id diferente do gravado) lê como estágio zerado — last-writer-wins, aceito porque o cookie é por\n * navegador e trocar de conta paga no mesmo device durante a janela da oferta é evento raro.\n */\nexport function readOfferStageCookie(idAccount: number): OfferStageRecord {\n if (typeof document === 'undefined') return EMPTY_RECORD;\n\n const match = new RegExp(`(?:^|;\\\\s*)${COOKIE_NAME}=([^;]+)`).exec(document.cookie);\n if (!match) return EMPTY_RECORD;\n\n const raw = decodeURIComponent(match[1]);\n const [rawAccount, rawStage, rawIat, rawRepeat] = raw.split(':');\n\n if (Number(rawAccount) !== idAccount) return EMPTY_RECORD;\n\n const stage = Number(rawStage);\n if (!(stage in AiCreditsOfferStage)) return EMPTY_RECORD;\n\n return {\n stage,\n lastSessionIat: Number(rawIat) || 0,\n repeatCount: Number(rawRepeat) || 0,\n };\n}\n\nexport function writeOfferStageCookie(\n idAccount: number,\n record: OfferStageRecord,\n whitelabelDomain?: string | null\n): void {\n if (typeof document === 'undefined') return;\n\n const value = `${idAccount}:${record.stage}:${record.lastSessionIat}:${record.repeatCount}`;\n const domain = cookieDomainFromDomain(whitelabelDomain);\n const parts = [\n `${COOKIE_NAME}=${encodeURIComponent(value)}`,\n 'path=/',\n `max-age=${ONE_YEAR_SECONDS}`,\n 'samesite=lax',\n domain ? `domain=${domain}` : '',\n typeof window !== 'undefined' && window.location.protocol === 'https:' ? 'secure' : '',\n ].filter(Boolean);\n\n document.cookie = parts.join('; ');\n}\n"],"mappings":"AAAA,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AAEpC,MAAM,cAAc;AACpB,MAAM,mBAAmB,KAAK,KAAK,KAAK;AAUxC,MAAM,eAAiC;AAAA,EACrC,OAAO,oBAAoB;AAAA,EAC3B,gBAAgB;AAAA,EAChB,aAAa;AACf;AAWO,SAAS,qBAAqB,WAAqC;AACxE,MAAI,OAAO,aAAa,YAAa,QAAO;AAE5C,QAAM,QAAQ,IAAI,OAAO,cAAc,WAAW,UAAU,EAAE,KAAK,SAAS,MAAM;AAClF,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,MAAM,mBAAmB,MAAM,CAAC,CAAC;AACvC,QAAM,CAAC,YAAY,UAAU,QAAQ,SAAS,IAAI,IAAI,MAAM,GAAG;AAE/D,MAAI,OAAO,UAAU,MAAM,UAAW,QAAO;AAE7C,QAAM,QAAQ,OAAO,QAAQ;AAC7B,MAAI,EAAE,SAAS,qBAAsB,QAAO;AAE5C,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB,OAAO,MAAM,KAAK;AAAA,IAClC,aAAa,OAAO,SAAS,KAAK;AAAA,EACpC;AACF;AAEO,SAAS,sBACd,WACA,QACA,kBACM;AACN,MAAI,OAAO,aAAa,YAAa;AAErC,QAAM,QAAQ,GAAG,SAAS,IAAI,OAAO,KAAK,IAAI,OAAO,cAAc,IAAI,OAAO,WAAW;AACzF,QAAM,SAAS,uBAAuB,gBAAgB;AACtD,QAAM,QAAQ;AAAA,IACZ,GAAG,WAAW,IAAI,mBAAmB,KAAK,CAAC;AAAA,IAC3C;AAAA,IACA,WAAW,gBAAgB;AAAA,IAC3B;AAAA,IACA,SAAS,UAAU,MAAM,KAAK;AAAA,IAC9B,OAAO,WAAW,eAAe,OAAO,SAAS,aAAa,WAAW,WAAW;AAAA,EACtF,EAAE,OAAO,OAAO;AAEhB,WAAS,SAAS,MAAM,KAAK,IAAI;AACnC;","names":[]}
|
package/package.json
CHANGED
|
@@ -7,11 +7,13 @@ import { useActiveSubscription } from '../../modules/subscriptions/hooks/find-ac
|
|
|
7
7
|
import { hasActivePaidSubscription } from '../../modules/subscriptions/utils/has-active-paid-subscription';
|
|
8
8
|
import { ADDON_IDS } from '../../modules/subscriptions/constants/addons.constants';
|
|
9
9
|
import { useCurrencyFormatter } from '../../modules/accounts/hooks/use-currency-formatter.hook';
|
|
10
|
+
import { useRequiredBillingData } from '../../modules/accounts/hooks/use-required-billing-data.hook';
|
|
10
11
|
import { useCards } from '../../modules/cards/hooks/cards.hook';
|
|
11
12
|
import { useIaCredits } from '../../modules/ia-credits/hooks/ia-credits.hook';
|
|
12
13
|
import { useAiCreditsOfferEligibility } from '../../modules/ia-credits/hooks/use-offer-eligibility.hook';
|
|
13
14
|
import {
|
|
14
15
|
AI_CREDITS_FIRST_PURCHASE_OFFER,
|
|
16
|
+
AI_CREDITS_OFFER_MAX_REPEATS,
|
|
15
17
|
AiCreditsOfferStage,
|
|
16
18
|
} from '../../modules/ia-credits/constants/ai-credits-offer.constants';
|
|
17
19
|
import {
|
|
@@ -35,6 +37,13 @@ const OPEN_DELAY_MS = 1000;
|
|
|
35
37
|
function resolveCandidateStage(
|
|
36
38
|
summary: Pick<IaCreditsSummary, 'totalCredits' | 'usedCredits' | 'availableCredits'>
|
|
37
39
|
): AiCreditsOfferStage {
|
|
40
|
+
// Conta que ainda NÃO recebeu lote nenhum (medido em produção: o free só ganha o primeiro lote
|
|
41
|
+
// meses depois do cadastro) não é o mesmo que conta que gastou tudo. Sem esta distinção o
|
|
42
|
+
// recém-cadastrado entrava direto em ZERO_SHOWN, o degrau mais alto, e como a escada só sobe os
|
|
43
|
+
// gatilhos de uso — primeira geração, 50%, 90% — nunca mais disparavam para ele.
|
|
44
|
+
if (summary.totalCredits === 0 && summary.usedCredits === 0) {
|
|
45
|
+
return AiCreditsOfferStage.CAMPAIGN_SHOWN;
|
|
46
|
+
}
|
|
38
47
|
if (summary.availableCredits === 0 || summary.totalCredits === 0) {
|
|
39
48
|
return AiCreditsOfferStage.ZERO_SHOWN;
|
|
40
49
|
}
|
|
@@ -76,9 +85,9 @@ export interface AiCreditsOfferGateProps {
|
|
|
76
85
|
* 3. **Estágio candidato**, derivado dos créditos atuais (`resolveCandidateStage`).
|
|
77
86
|
* 4. **Escada monotônica do cookie `ai-credits-offer-stage`**: só abre se o candidato é MAIOR que o
|
|
78
87
|
* estágio gravado, OU se o candidato é CAMPAIGN/ZERO — os dois únicos que reapresentam, e só
|
|
79
|
-
* numa sessão de login nova (`sessionIssuedAt` ≠ `lastSessionIat`).
|
|
80
|
-
*
|
|
81
|
-
*
|
|
88
|
+
* numa sessão de login nova (`sessionIssuedAt` ≠ `lastSessionIat`). A repetição respeita
|
|
89
|
+
* `AI_CREDITS_OFFER_MAX_REPEATS`: a campanha é agressiva de propósito (vale inclusive para quem
|
|
90
|
+
* acabou de se cadastrar), mas não pode virar o mesmo pop em toda sessão para sempre.
|
|
82
91
|
* 5. **Máx. 1 apresentação por sessão de login.** Sai de graça da regra acima: se o cookie já tem
|
|
83
92
|
* `lastSessionIat` igual ao da sessão atual, NADA abre — nem uma escalada de estágio dentro da
|
|
84
93
|
* mesma sessão (ex.: FIRST_USE → HALF → EXHAUSTED na mesma geração, no Free de 10 créditos).
|
|
@@ -92,8 +101,17 @@ export function AiCreditsOfferGate({ suppressWhen = false }: AiCreditsOfferGateP
|
|
|
92
101
|
const { data: subscriptionData } = useActiveSubscription();
|
|
93
102
|
const { currency } = useCurrencyFormatter();
|
|
94
103
|
const { summary, isLoading: isCreditsLoading } = useIaCredits();
|
|
104
|
+
const { isRequired: isBillingDataRequired } = useRequiredBillingData();
|
|
95
105
|
const hasArmedRef = useRef(false);
|
|
96
106
|
|
|
107
|
+
/* Modais bloqueantes que NÃO passam pelo `useModalManager` — cada um controla a própria abertura
|
|
108
|
+
* com estado local, então `activeModal` não os enxerga e a oferta subiria por cima:
|
|
109
|
+
* - `RequiredBillingDataModal` (lib), aberto por `useRequiredBillingData().isRequired`;
|
|
110
|
+
* - `CompleteProfileGuard` (app de contas), aberto quando falta nome ou sobrenome.
|
|
111
|
+
* Checar a condição de origem, e não o modal em si, é o que funciona para os três apps sem que
|
|
112
|
+
* cada um precise lembrar de passar `suppressWhen`. */
|
|
113
|
+
const isProfileIncomplete = user != null && (!user.name || !user.last_name);
|
|
114
|
+
|
|
97
115
|
const subscription = subscriptionData?.data?.[0] ?? null;
|
|
98
116
|
const accountId = user?.id_account;
|
|
99
117
|
const canManageBilling = user?.profile === 'owner' || user?.profile === 'admin';
|
|
@@ -117,6 +135,7 @@ export function AiCreditsOfferGate({ suppressWhen = false }: AiCreditsOfferGateP
|
|
|
117
135
|
useEffect(() => {
|
|
118
136
|
if (hasArmedRef.current) return;
|
|
119
137
|
if (suppressWhen || activeModal) return;
|
|
138
|
+
if (isBillingDataRequired || isProfileIncomplete) return;
|
|
120
139
|
if (!passesClientCutoff || isCreditsLoading) return;
|
|
121
140
|
// Fail closed: sem resposta do servidor (carregando, erro, ou reason preenchido), não abre.
|
|
122
141
|
if (!eligibilityData || !eligibilityData.eligible || accountId == null) return;
|
|
@@ -134,10 +153,17 @@ export function AiCreditsOfferGate({ suppressWhen = false }: AiCreditsOfferGateP
|
|
|
134
153
|
candidateStage === AiCreditsOfferStage.CAMPAIGN_SHOWN ||
|
|
135
154
|
candidateStage === AiCreditsOfferStage.ZERO_SHOWN;
|
|
136
155
|
|
|
137
|
-
const
|
|
138
|
-
|
|
156
|
+
const isRepeating = isRepeatableStage && candidateStage === cookie.stage;
|
|
157
|
+
|
|
158
|
+
const shouldOpen = candidateStage > cookie.stage || isRepeating;
|
|
139
159
|
if (!shouldOpen) return;
|
|
140
160
|
|
|
161
|
+
// Subir de estágio zera o contador: o teto limita a insistência DENTRO de um estágio, não a
|
|
162
|
+
// escada inteira. Sem isso, uma conta que já gastou o teto na campanha nunca veria a oferta no
|
|
163
|
+
// momento em que ela mais importa (crédito no fim).
|
|
164
|
+
const repeatCount = isRepeating ? cookie.repeatCount + 1 : 0;
|
|
165
|
+
if (isRepeating && cookie.repeatCount >= AI_CREDITS_OFFER_MAX_REPEATS) return;
|
|
166
|
+
|
|
141
167
|
// Arma uma única vez por montagem: sem isso, qualquer re-render limparia o timer pendente.
|
|
142
168
|
hasArmedRef.current = true;
|
|
143
169
|
|
|
@@ -146,7 +172,7 @@ export function AiCreditsOfferGate({ suppressWhen = false }: AiCreditsOfferGateP
|
|
|
146
172
|
// pode queimar a apresentação.
|
|
147
173
|
writeOfferStageCookie(
|
|
148
174
|
accountId,
|
|
149
|
-
{ stage: candidateStage, lastSessionIat: eligibilityData.sessionIssuedAt },
|
|
175
|
+
{ stage: candidateStage, lastSessionIat: eligibilityData.sessionIssuedAt, repeatCount },
|
|
150
176
|
whitelabel?.domain
|
|
151
177
|
);
|
|
152
178
|
openModal();
|
|
@@ -159,7 +185,9 @@ export function AiCreditsOfferGate({ suppressWhen = false }: AiCreditsOfferGateP
|
|
|
159
185
|
cardsQuery.data,
|
|
160
186
|
cardsQuery.isSuccess,
|
|
161
187
|
eligibilityData,
|
|
188
|
+
isBillingDataRequired,
|
|
162
189
|
isCreditsLoading,
|
|
190
|
+
isProfileIncomplete,
|
|
163
191
|
openModal,
|
|
164
192
|
passesClientCutoff,
|
|
165
193
|
suppressWhen,
|
package/src/index.ts
CHANGED
|
@@ -119,6 +119,7 @@ export { useAiCreditsOfferEligibility } from "./modules/ia-credits/hooks/use-off
|
|
|
119
119
|
export {
|
|
120
120
|
AI_CREDITS_FIRST_PURCHASE_OFFER,
|
|
121
121
|
AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY,
|
|
122
|
+
AI_CREDITS_OFFER_MAX_REPEATS,
|
|
122
123
|
AiCreditsOfferStage,
|
|
123
124
|
PAYMENT_METHOD_CARD,
|
|
124
125
|
PAYMENT_METHOD_PIX,
|
|
@@ -30,4 +30,12 @@ export enum AiCreditsOfferStage {
|
|
|
30
30
|
CONVERTED = 6,
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Teto de REPETIÇÕES dos estágios repetíveis (campanha e sem-crédito) — repetições ALÉM da primeira
|
|
35
|
+
* aparição, então `3` permite 4 exibições daquele estágio. A campanha é deliberadamente agressiva
|
|
36
|
+
* (vale inclusive para quem acabou de se cadastrar), mas "agressiva" não é "toda sessão para
|
|
37
|
+
* sempre": esgotado o teto, a oferta só volta se a conta subir de estágio na escada.
|
|
38
|
+
*/
|
|
39
|
+
export const AI_CREDITS_OFFER_MAX_REPEATS = 3;
|
|
40
|
+
|
|
33
41
|
export const AI_CREDITS_OFFER_ELIGIBILITY_QUERY_KEY = ['ai-credits-offer-eligibility'] as const;
|
|
@@ -8,11 +8,14 @@ export interface OfferStageRecord {
|
|
|
8
8
|
stage: AiCreditsOfferStage;
|
|
9
9
|
/** `iat` do JWT na sessão em que este estágio foi gravado — é o que diferencia "sessão nova". */
|
|
10
10
|
lastSessionIat: number;
|
|
11
|
+
/** Quantas vezes um estágio REPETÍVEL (campanha / sem crédito) já foi apresentado. */
|
|
12
|
+
repeatCount: number;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
const EMPTY_RECORD: OfferStageRecord = {
|
|
14
16
|
stage: AiCreditsOfferStage.NONE,
|
|
15
17
|
lastSessionIat: 0,
|
|
18
|
+
repeatCount: 0,
|
|
16
19
|
};
|
|
17
20
|
|
|
18
21
|
/**
|
|
@@ -20,7 +23,7 @@ const EMPTY_RECORD: OfferStageRecord = {
|
|
|
20
23
|
* mecanismo do cookie de locale, que por sua vez espelha o cookie de auth `greatapps`): sustenta a
|
|
21
24
|
* escada de estágios entre app/accounts/editor, 1 ano de validade.
|
|
22
25
|
*
|
|
23
|
-
* Valor: `${idAccount}:${stage}:${lastSessionIat}`. Registro de OUTRA conta
|
|
26
|
+
* Valor: `${idAccount}:${stage}:${lastSessionIat}:${repeatCount}`. Registro de OUTRA conta
|
|
24
27
|
* (id diferente do gravado) lê como estágio zerado — last-writer-wins, aceito porque o cookie é por
|
|
25
28
|
* navegador e trocar de conta paga no mesmo device durante a janela da oferta é evento raro.
|
|
26
29
|
*/
|
|
@@ -31,7 +34,7 @@ export function readOfferStageCookie(idAccount: number): OfferStageRecord {
|
|
|
31
34
|
if (!match) return EMPTY_RECORD;
|
|
32
35
|
|
|
33
36
|
const raw = decodeURIComponent(match[1]);
|
|
34
|
-
const [rawAccount, rawStage, rawIat] = raw.split(':');
|
|
37
|
+
const [rawAccount, rawStage, rawIat, rawRepeat] = raw.split(':');
|
|
35
38
|
|
|
36
39
|
if (Number(rawAccount) !== idAccount) return EMPTY_RECORD;
|
|
37
40
|
|
|
@@ -41,6 +44,7 @@ export function readOfferStageCookie(idAccount: number): OfferStageRecord {
|
|
|
41
44
|
return {
|
|
42
45
|
stage,
|
|
43
46
|
lastSessionIat: Number(rawIat) || 0,
|
|
47
|
+
repeatCount: Number(rawRepeat) || 0,
|
|
44
48
|
};
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -51,7 +55,7 @@ export function writeOfferStageCookie(
|
|
|
51
55
|
): void {
|
|
52
56
|
if (typeof document === 'undefined') return;
|
|
53
57
|
|
|
54
|
-
const value = `${idAccount}:${record.stage}:${record.lastSessionIat}`;
|
|
58
|
+
const value = `${idAccount}:${record.stage}:${record.lastSessionIat}:${record.repeatCount}`;
|
|
55
59
|
const domain = cookieDomainFromDomain(whitelabelDomain);
|
|
56
60
|
const parts = [
|
|
57
61
|
`${COOKIE_NAME}=${encodeURIComponent(value)}`,
|