@greatapps/common 1.1.741 → 1.1.743

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/components/modals/BuyCreditsModal.mjs +42 -30
  2. package/dist/components/modals/BuyCreditsModal.mjs.map +1 -1
  3. package/dist/components/modals/billing/RequiredBillingDataModal.mjs +14 -30
  4. package/dist/components/modals/billing/RequiredBillingDataModal.mjs.map +1 -1
  5. package/dist/components/modals/cards/AddCardModal.mjs +2 -1
  6. package/dist/components/modals/cards/AddCardModal.mjs.map +1 -1
  7. package/dist/index.mjs +26 -6
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/modules/accounts/hooks/use-currency-formatter.hook.mjs +3 -1
  10. package/dist/modules/accounts/hooks/use-currency-formatter.hook.mjs.map +1 -1
  11. package/dist/modules/accounts/hooks/use-required-billing-data.hook.mjs +3 -4
  12. package/dist/modules/accounts/hooks/use-required-billing-data.hook.mjs.map +1 -1
  13. package/dist/modules/accounts/utils/billing-data.mjs +10 -14
  14. package/dist/modules/accounts/utils/billing-data.mjs.map +1 -1
  15. package/dist/modules/plans/types/plan.type.mjs +15 -0
  16. package/dist/modules/plans/types/plan.type.mjs.map +1 -1
  17. package/dist/modules/plans/utils/map-api-plan-to-ui.mjs +10 -2
  18. package/dist/modules/plans/utils/map-api-plan-to-ui.mjs.map +1 -1
  19. package/dist/modules/subscriptions/constants/addons.constants.mjs +15 -2
  20. package/dist/modules/subscriptions/constants/addons.constants.mjs.map +1 -1
  21. package/dist/utils/format/currency.mjs +4 -1
  22. package/dist/utils/format/currency.mjs.map +1 -1
  23. package/dist/utils/format/gateway.mjs +18 -0
  24. package/dist/utils/format/gateway.mjs.map +1 -0
  25. package/package.json +1 -1
  26. package/src/components/modals/BuyCreditsModal.tsx +48 -32
  27. package/src/components/modals/billing/RequiredBillingDataModal.tsx +32 -66
  28. package/src/components/modals/cards/AddCardModal.tsx +2 -1
  29. package/src/index.ts +14 -2
  30. package/src/modules/accounts/hooks/use-currency-formatter.hook.tsx +3 -0
  31. package/src/modules/accounts/hooks/use-required-billing-data.hook.ts +3 -3
  32. package/src/modules/accounts/utils/billing-data.ts +22 -26
  33. package/src/modules/plans/types/plan.type.ts +17 -0
  34. package/src/modules/plans/utils/map-api-plan-to-ui.ts +12 -4
  35. package/src/modules/subscriptions/constants/addons.constants.ts +41 -28
  36. package/src/utils/format/currency.ts +5 -29
  37. package/src/utils/format/gateway.ts +22 -0
@@ -44,12 +44,20 @@ function buildMainFeaturesFromItems(items, translate) {
44
44
  const users = translate ? translate("common.plans.mainFeatures.usersUnlimited") : "Usu\xE1rios ilimitados";
45
45
  return { pages, domains, users };
46
46
  }
47
- function computePricingByPeriod(apiPlan, currency) {
47
+ function computePricingByPeriod(apiPlan) {
48
48
  const monthly = apiPlan.value;
49
49
  if (monthly == null) {
50
50
  return { monthly: 0, semiannual: 0, annual: 0 };
51
51
  }
52
52
  const round2 = (n) => Math.round(n * 100) / 100;
53
+ const pricing = apiPlan.pricing;
54
+ if (pricing) {
55
+ return {
56
+ monthly,
57
+ semiannual: round2(pricing.semester.charged / 6),
58
+ annual: round2(pricing.annual.charged / 12)
59
+ };
60
+ }
53
61
  return {
54
62
  monthly,
55
63
  semiannual: round2(monthly * (1 - apiPlan.discount_semester / 100)),
@@ -59,7 +67,7 @@ function computePricingByPeriod(apiPlan, currency) {
59
67
  function buildUiPlan(plan, currency, translate) {
60
68
  const popular = plan.id === 2 || plan.id_plan === 2;
61
69
  const buttonVariant = popular ? "brand" : "default";
62
- const pricingByPeriod = computePricingByPeriod(plan, currency);
70
+ const pricingByPeriod = computePricingByPeriod(plan);
63
71
  const monthlyFinal = pricingByPeriod.monthly;
64
72
  const isStarterPlan = plan.id === STARTER_PLAN_ID || plan.id_plan === STARTER_PLAN_ID;
65
73
  const hasProjectAndOnboarding = plan.name.toLowerCase().includes("ag\xEAncia") || plan.name.toLowerCase().includes("agencia");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/modules/plans/utils/map-api-plan-to-ui.ts"],"sourcesContent":["import type {\n Plan,\n PlanFeature,\n PlanMainFeatures,\n PlanPricingByPeriod,\n UiPlan,\n} from \"../types/plan.type\";\nimport { ADDON_IDS } from \"../../subscriptions/constants/addons.constants\";\nimport {\n formatCurrencyNumber,\n type CurrencyCode,\n} from \"../../../utils/format/currency\";\n\nexport type PlanBillingPeriod = \"monthly\" | \"semiannual\" | \"annual\";\n\nconst STARTER_PLAN_ID = 415;\n\ntype TranslateFn = (key: any, values?: Record<string, string | number>) => string;\n\nconst BASE_FEATURES_KEYS = [\n { icon: \"check\", key: \"plans.features.unlimitedVisits\", fallback: \"Visitas ilimitadas\" },\n { icon: \"check\", key: \"plans.features.unlimitedLeads\", fallback: \"Leads ilimitadas\" },\n { icon: \"check\", key: \"plans.features.sharePages\", fallback: \"Compartilhar páginas\", tooltip: \"sharePages\" },\n { icon: \"check\", key: \"plans.features.hosting\", fallback: \"Hospedagem inclusa\" },\n // SSL (HTTPS) + CDN — brand/acronym: never translated\n { icon: \"check\", key: null, fallback: \"SSL (HTTPS) + CDN\" },\n { icon: \"check\", key: \"plans.features.freeTemplates\", fallback: \"Templates gratuitos\" },\n] as const;\n\nfunction buildBaseFeatures(translate?: TranslateFn): PlanFeature[] {\n return BASE_FEATURES_KEYS.map(({ icon, key, fallback, ...rest }) => {\n const text = key && translate ? translate(`common.${key}`) : fallback;\n const feature: PlanFeature = { icon, text };\n if (\"tooltip\" in rest && rest.tooltip) feature.tooltip = rest.tooltip;\n return feature;\n });\n}\n\nfunction buildMainFeaturesFromItems(\n items: Plan[\"items\"],\n translate?: TranslateFn\n): PlanMainFeatures {\n const pagesItem = items.find((i) => i.id_addon === ADDON_IDS.PAGES);\n const domainsItem = items.find((i) => i.id_addon === ADDON_IDS.DOMAINS);\n\n const pagesQty = pagesItem?.quantity ?? 0;\n const domainsQty = domainsItem?.quantity ?? 1;\n\n let pages: string;\n if (pagesQty === 0) {\n pages = translate\n ? translate(\"common.plans.mainFeatures.pagesUnlimited\")\n : \"Páginas ilimitadas\";\n } else if (pagesQty === 1) {\n pages = translate\n ? translate(\"common.plans.mainFeatures.pageSingular\")\n : \"1 página\";\n } else {\n pages = translate\n ? translate(\"common.plans.mainFeatures.pagesPlural\", { count: pagesQty })\n : `${pagesQty} páginas`;\n }\n\n let domains: string;\n if (domainsQty >= 999) {\n domains = translate\n ? translate(\"common.plans.mainFeatures.domainsUnlimited\")\n : \"Domínios ilimitados\";\n } else if (domainsQty === 1) {\n domains = translate\n ? translate(\"common.plans.mainFeatures.domainSingular\")\n : \"1 domínio externo\";\n } else {\n domains = translate\n ? translate(\"common.plans.mainFeatures.domainsPlural\", { count: domainsQty })\n : `${domainsQty} domínios externos`;\n }\n\n const users = translate\n ? translate(\"common.plans.mainFeatures.usersUnlimited\")\n : \"Usuários ilimitados\";\n\n return { pages, domains, users };\n}\n\nfunction computePricingByPeriod(apiPlan: Plan, currency: CurrencyCode): PlanPricingByPeriod {\n // Backend agora resolve value por moeda da conta; mapper apenas lê.\n // value pode ser null (ex.: conta USD, preço USD não cadastrado).\n const monthly = apiPlan.value;\n if (monthly == null) {\n return { monthly: 0, semiannual: 0, annual: 0 };\n }\n const round2 = (n: number) => Math.round(n * 100) / 100;\n return {\n monthly,\n semiannual: round2(monthly * (1 - apiPlan.discount_semester / 100)),\n annual: round2(monthly * (1 - apiPlan.discount_annual / 100)),\n };\n}\n\nfunction buildUiPlan(plan: Plan, currency: CurrencyCode, translate?: TranslateFn): UiPlan {\n const popular = plan.id === 2 || plan.id_plan === 2;\n const buttonVariant = popular ? \"brand\" : \"default\";\n\n const pricingByPeriod = computePricingByPeriod(plan, currency);\n const monthlyFinal = pricingByPeriod.monthly;\n\n const isStarterPlan = plan.id === STARTER_PLAN_ID || plan.id_plan === STARTER_PLAN_ID;\n const hasProjectAndOnboarding =\n plan.name.toLowerCase().includes(\"agência\") || plan.name.toLowerCase().includes(\"agencia\");\n\n const onboardingText = translate\n ? translate(\"common.plans.features.onboarding\")\n : \"Reunião de Onboarding\";\n const projectMgmtText = translate\n ? translate(\"common.plans.features.projectManagement\")\n : \"Gestão de projetos\";\n const sharePagesText = translate\n ? translate(\"common.plans.features.sharePages\")\n : \"Compartilhar páginas\";\n\n const features: PlanFeature[] = [\n ...buildBaseFeatures(translate).map((feature) =>\n feature.text === sharePagesText && isStarterPlan\n ? { ...feature, icon: \"x\" as const, disabled: true }\n : feature\n ),\n hasProjectAndOnboarding\n ? { icon: \"check\", text: onboardingText }\n : { icon: \"x\", text: onboardingText, disabled: true },\n hasProjectAndOnboarding\n ? { icon: \"check\", text: projectMgmtText, tooltip: \"projectManagement\" }\n : { icon: \"x\", text: projectMgmtText, disabled: true, tooltip: \"projectManagement\" },\n ];\n\n const currencyAwareItems = plan.items.map((item) => ({\n ...item,\n value: item.value ?? 0,\n }));\n\n const priceUnavailable = plan.value == null;\n const unavailableLabel = translate ? translate(\"common.plans.usdNotConfigured\") : \"USD não configurado\";\n\n return {\n planId: plan.id,\n name: plan.name,\n isPopular: popular,\n buttonVariant,\n mainFeatures: buildMainFeaturesFromItems(plan.items, translate),\n features,\n originalPrice: priceUnavailable ? unavailableLabel : formatCurrencyNumber(monthlyFinal, 2, currency),\n price: priceUnavailable ? unavailableLabel : formatCurrencyNumber(monthlyFinal, 2, currency),\n pricingByPeriod,\n priceUnavailable,\n items: currencyAwareItems,\n };\n}\n\n/**\n * Mapeia um plano da API para UiPlan em BRL.\n * Assinatura de 1 argumento — seguro em `plans.map(mapApiPlanToUiPlan)`.\n * Passa `translate` opcional para i18n; sem ele cai nos literais pt-br (back-compat).\n */\nexport function mapApiPlanToUiPlan(plan: Plan, translate?: TranslateFn): UiPlan {\n return buildUiPlan(plan, \"brl\", translate);\n}\n\n/**\n * Variante currency-aware: a moeda vem do gateway da conta (getCurrencyForGateway).\n * Fábrica para uso ergonômico em `.map`: `plans.map(mapApiPlanToUiPlanForCurrency(currency, translate))`.\n * Mantida separada de mapApiPlanToUiPlan para não quebrar callers que passam a fn direto pro `.map`.\n */\nexport const mapApiPlanToUiPlanForCurrency =\n (currency: CurrencyCode, translate?: TranslateFn) =>\n (plan: Plan): UiPlan =>\n buildUiPlan(plan, currency, translate);\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/modules/plans/utils/map-api-plan-to-ui.ts"],"sourcesContent":["import type {\n Plan,\n PlanFeature,\n PlanMainFeatures,\n PlanPricingByPeriod,\n UiPlan,\n} from \"../types/plan.type\";\nimport { ADDON_IDS } from \"../../subscriptions/constants/addons.constants\";\nimport {\n formatCurrencyNumber,\n type CurrencyCode,\n} from \"../../../utils/format/currency\";\n\nexport type PlanBillingPeriod = \"monthly\" | \"semiannual\" | \"annual\";\n\nconst STARTER_PLAN_ID = 415;\n\ntype TranslateFn = (key: any, values?: Record<string, string | number>) => string;\n\nconst BASE_FEATURES_KEYS = [\n { icon: \"check\", key: \"plans.features.unlimitedVisits\", fallback: \"Visitas ilimitadas\" },\n { icon: \"check\", key: \"plans.features.unlimitedLeads\", fallback: \"Leads ilimitadas\" },\n { icon: \"check\", key: \"plans.features.sharePages\", fallback: \"Compartilhar páginas\", tooltip: \"sharePages\" },\n { icon: \"check\", key: \"plans.features.hosting\", fallback: \"Hospedagem inclusa\" },\n // SSL (HTTPS) + CDN — brand/acronym: never translated\n { icon: \"check\", key: null, fallback: \"SSL (HTTPS) + CDN\" },\n { icon: \"check\", key: \"plans.features.freeTemplates\", fallback: \"Templates gratuitos\" },\n] as const;\n\nfunction buildBaseFeatures(translate?: TranslateFn): PlanFeature[] {\n return BASE_FEATURES_KEYS.map(({ icon, key, fallback, ...rest }) => {\n const text = key && translate ? translate(`common.${key}`) : fallback;\n const feature: PlanFeature = { icon, text };\n if (\"tooltip\" in rest && rest.tooltip) feature.tooltip = rest.tooltip;\n return feature;\n });\n}\n\nfunction buildMainFeaturesFromItems(\n items: Plan[\"items\"],\n translate?: TranslateFn\n): PlanMainFeatures {\n const pagesItem = items.find((i) => i.id_addon === ADDON_IDS.PAGES);\n const domainsItem = items.find((i) => i.id_addon === ADDON_IDS.DOMAINS);\n\n const pagesQty = pagesItem?.quantity ?? 0;\n const domainsQty = domainsItem?.quantity ?? 1;\n\n let pages: string;\n if (pagesQty === 0) {\n pages = translate\n ? translate(\"common.plans.mainFeatures.pagesUnlimited\")\n : \"Páginas ilimitadas\";\n } else if (pagesQty === 1) {\n pages = translate\n ? translate(\"common.plans.mainFeatures.pageSingular\")\n : \"1 página\";\n } else {\n pages = translate\n ? translate(\"common.plans.mainFeatures.pagesPlural\", { count: pagesQty })\n : `${pagesQty} páginas`;\n }\n\n let domains: string;\n if (domainsQty >= 999) {\n domains = translate\n ? translate(\"common.plans.mainFeatures.domainsUnlimited\")\n : \"Domínios ilimitados\";\n } else if (domainsQty === 1) {\n domains = translate\n ? translate(\"common.plans.mainFeatures.domainSingular\")\n : \"1 domínio externo\";\n } else {\n domains = translate\n ? translate(\"common.plans.mainFeatures.domainsPlural\", { count: domainsQty })\n : `${domainsQty} domínios externos`;\n }\n\n const users = translate\n ? translate(\"common.plans.mainFeatures.usersUnlimited\")\n : \"Usuários ilimitados\";\n\n return { pages, domains, users };\n}\n\nfunction computePricingByPeriod(apiPlan: Plan): PlanPricingByPeriod {\n const monthly = apiPlan.value;\n if (monthly == null) {\n return { monthly: 0, semiannual: 0, annual: 0 };\n }\n const round2 = (n: number) => Math.round(n * 100) / 100;\n\n const pricing = apiPlan.pricing;\n if (pricing) {\n return {\n monthly,\n semiannual: round2(pricing.semester.charged / 6),\n annual: round2(pricing.annual.charged / 12),\n };\n }\n\n return {\n monthly,\n semiannual: round2(monthly * (1 - apiPlan.discount_semester / 100)),\n annual: round2(monthly * (1 - apiPlan.discount_annual / 100)),\n };\n}\n\nfunction buildUiPlan(plan: Plan, currency: CurrencyCode, translate?: TranslateFn): UiPlan {\n const popular = plan.id === 2 || plan.id_plan === 2;\n const buttonVariant = popular ? \"brand\" : \"default\";\n\n const pricingByPeriod = computePricingByPeriod(plan);\n const monthlyFinal = pricingByPeriod.monthly;\n\n const isStarterPlan = plan.id === STARTER_PLAN_ID || plan.id_plan === STARTER_PLAN_ID;\n const hasProjectAndOnboarding =\n plan.name.toLowerCase().includes(\"agência\") || plan.name.toLowerCase().includes(\"agencia\");\n\n const onboardingText = translate\n ? translate(\"common.plans.features.onboarding\")\n : \"Reunião de Onboarding\";\n const projectMgmtText = translate\n ? translate(\"common.plans.features.projectManagement\")\n : \"Gestão de projetos\";\n const sharePagesText = translate\n ? translate(\"common.plans.features.sharePages\")\n : \"Compartilhar páginas\";\n\n const features: PlanFeature[] = [\n ...buildBaseFeatures(translate).map((feature) =>\n feature.text === sharePagesText && isStarterPlan\n ? { ...feature, icon: \"x\" as const, disabled: true }\n : feature\n ),\n hasProjectAndOnboarding\n ? { icon: \"check\", text: onboardingText }\n : { icon: \"x\", text: onboardingText, disabled: true },\n hasProjectAndOnboarding\n ? { icon: \"check\", text: projectMgmtText, tooltip: \"projectManagement\" }\n : { icon: \"x\", text: projectMgmtText, disabled: true, tooltip: \"projectManagement\" },\n ];\n\n const currencyAwareItems = plan.items.map((item) => ({\n ...item,\n value: item.value ?? 0,\n }));\n\n const priceUnavailable = plan.value == null;\n const unavailableLabel = translate ? translate(\"common.plans.usdNotConfigured\") : \"USD não configurado\";\n\n return {\n planId: plan.id,\n name: plan.name,\n isPopular: popular,\n buttonVariant,\n mainFeatures: buildMainFeaturesFromItems(plan.items, translate),\n features,\n originalPrice: priceUnavailable ? unavailableLabel : formatCurrencyNumber(monthlyFinal, 2, currency),\n price: priceUnavailable ? unavailableLabel : formatCurrencyNumber(monthlyFinal, 2, currency),\n pricingByPeriod,\n priceUnavailable,\n items: currencyAwareItems,\n };\n}\n\n/**\n * Mapeia um plano da API para UiPlan em BRL.\n * Assinatura de 1 argumento — seguro em `plans.map(mapApiPlanToUiPlan)`.\n * Passa `translate` opcional para i18n; sem ele cai nos literais pt-br (back-compat).\n */\nexport function mapApiPlanToUiPlan(plan: Plan, translate?: TranslateFn): UiPlan {\n return buildUiPlan(plan, \"brl\", translate);\n}\n\n/**\n * Variante currency-aware: a moeda vem do gateway da conta (getCurrencyForGateway).\n * Fábrica para uso ergonômico em `.map`: `plans.map(mapApiPlanToUiPlanForCurrency(currency, translate))`.\n * Mantida separada de mapApiPlanToUiPlan para não quebrar callers que passam a fn direto pro `.map`.\n */\nexport const mapApiPlanToUiPlanForCurrency =\n (currency: CurrencyCode, translate?: TranslateFn) =>\n (plan: Plan): UiPlan =>\n buildUiPlan(plan, currency, translate);\n"],"mappings":"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","names":[]}
@@ -6,6 +6,16 @@ const ADDON_IDS = {
6
6
  HEATMAP: 5,
7
7
  AI: 6
8
8
  };
9
+ const AI_CREDIT_SIZES = [0, 250, 500, 1e3, 2500, 5e3, 1e4];
10
+ function resolveAiCreditPrice(quantity, perCreditPrice) {
11
+ return Math.round(quantity * perCreditPrice * 100) / 100;
12
+ }
13
+ function buildAiCreditOptions(perCreditPrice) {
14
+ return AI_CREDIT_SIZES.map((credits) => ({
15
+ credits,
16
+ price: resolveAiCreditPrice(credits, perCreditPrice)
17
+ }));
18
+ }
9
19
  const AI_CREDIT_OPTIONS = [
10
20
  { credits: 0, price: 0, priceLabel: "R$ 0,00" },
11
21
  { credits: 250, price: 9.99, priceLabel: "R$ 9,99" },
@@ -15,10 +25,13 @@ const AI_CREDIT_OPTIONS = [
15
25
  { credits: 5e3, price: 199.8, priceLabel: "R$ 199,80" },
16
26
  { credits: 1e4, price: 399.6, priceLabel: "R$ 399,60" }
17
27
  ];
18
- const AI_CREDIT_VALUES = new Set(AI_CREDIT_OPTIONS.map((opt) => opt.credits));
28
+ const AI_CREDIT_VALUES = new Set(AI_CREDIT_SIZES);
19
29
  export {
20
30
  ADDON_IDS,
21
31
  AI_CREDIT_OPTIONS,
22
- AI_CREDIT_VALUES
32
+ AI_CREDIT_SIZES,
33
+ AI_CREDIT_VALUES,
34
+ buildAiCreditOptions,
35
+ resolveAiCreditPrice
23
36
  };
24
37
  //# sourceMappingURL=addons.constants.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/modules/subscriptions/constants/addons.constants.ts"],"sourcesContent":["export const ADDON_IDS = {\r\n DOMAINS: 1,\r\n PAGES: 2,\r\n PROJECTS: 3,\r\n AB_TEST: 4,\r\n HEATMAP: 5,\r\n AI: 6,\r\n} as const;\r\n\r\nexport type AddonKindEnum = (typeof ADDON_IDS)[keyof typeof ADDON_IDS];\r\n\r\nexport interface AiCreditOption {\r\n credits: number;\r\n price: number;\r\n priceLabel: string;\r\n}\r\n\r\nexport const AI_CREDIT_OPTIONS: AiCreditOption[] = [\r\n { credits: 0, price: 0, priceLabel: 'R$ 0,00' },\r\n { credits: 250, price: 9.99, priceLabel: 'R$ 9,99' },\r\n { credits: 500, price: 19.98, priceLabel: 'R$ 19,98' },\r\n { credits: 1000, price: 39.96, priceLabel: 'R$ 39,96' },\r\n { credits: 2500, price: 99.90, priceLabel: 'R$ 99,90' },\r\n { credits: 5000, price: 199.80, priceLabel: 'R$ 199,80' },\r\n { credits: 10000, price: 399.60, priceLabel: 'R$ 399,60' },\r\n];\r\n\r\nexport const AI_CREDIT_VALUES = new Set(AI_CREDIT_OPTIONS.map((opt) => opt.credits));\r\n"],"mappings":"AAAO,MAAM,YAAY;AAAA,EACvB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,IAAI;AACN;AAUO,MAAM,oBAAsC;AAAA,EACjD,EAAE,SAAS,GAAG,OAAO,GAAG,YAAY,UAAU;AAAA,EAC9C,EAAE,SAAS,KAAK,OAAO,MAAM,YAAY,UAAU;AAAA,EACnD,EAAE,SAAS,KAAK,OAAO,OAAO,YAAY,WAAW;AAAA,EACrD,EAAE,SAAS,KAAM,OAAO,OAAO,YAAY,WAAW;AAAA,EACtD,EAAE,SAAS,MAAM,OAAO,MAAO,YAAY,WAAW;AAAA,EACtD,EAAE,SAAS,KAAM,OAAO,OAAQ,YAAY,YAAY;AAAA,EACxD,EAAE,SAAS,KAAO,OAAO,OAAQ,YAAY,YAAY;AAC3D;AAEO,MAAM,mBAAmB,IAAI,IAAI,kBAAkB,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../../../src/modules/subscriptions/constants/addons.constants.ts"],"sourcesContent":["export const ADDON_IDS = {\n DOMAINS: 1,\n PAGES: 2,\n PROJECTS: 3,\n AB_TEST: 4,\n HEATMAP: 5,\n AI: 6,\n} as const;\n\nexport type AddonKindEnum = (typeof ADDON_IDS)[keyof typeof ADDON_IDS];\n\nexport interface AiCreditOption {\n credits: number;\n price: number;\n priceLabel?: string;\n}\n\nexport const AI_CREDIT_SIZES = [0, 250, 500, 1000, 2500, 5000, 10000] as const;\n\nexport function resolveAiCreditPrice(quantity: number, perCreditPrice: number): number {\n return Math.round(quantity * perCreditPrice * 100) / 100;\n}\n\nexport function buildAiCreditOptions(perCreditPrice: number): AiCreditOption[] {\n return AI_CREDIT_SIZES.map((credits) => ({\n credits,\n price: resolveAiCreditPrice(credits, perCreditPrice),\n }));\n}\n\nexport const AI_CREDIT_OPTIONS: AiCreditOption[] = [\n { credits: 0, price: 0, priceLabel: 'R$ 0,00' },\n { credits: 250, price: 9.99, priceLabel: 'R$ 9,99' },\n { credits: 500, price: 19.98, priceLabel: 'R$ 19,98' },\n { credits: 1000, price: 39.96, priceLabel: 'R$ 39,96' },\n { credits: 2500, price: 99.90, priceLabel: 'R$ 99,90' },\n { credits: 5000, price: 199.80, priceLabel: 'R$ 199,80' },\n { credits: 10000, price: 399.60, priceLabel: 'R$ 399,60' },\n];\n\nexport const AI_CREDIT_VALUES: ReadonlySet<number> = new Set(AI_CREDIT_SIZES);\n"],"mappings":"AAAO,MAAM,YAAY;AAAA,EACvB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,IAAI;AACN;AAUO,MAAM,kBAAkB,CAAC,GAAG,KAAK,KAAK,KAAM,MAAM,KAAM,GAAK;AAE7D,SAAS,qBAAqB,UAAkB,gBAAgC;AACrF,SAAO,KAAK,MAAM,WAAW,iBAAiB,GAAG,IAAI;AACvD;AAEO,SAAS,qBAAqB,gBAA0C;AAC7E,SAAO,gBAAgB,IAAI,CAAC,aAAa;AAAA,IACvC;AAAA,IACA,OAAO,qBAAqB,SAAS,cAAc;AAAA,EACrD,EAAE;AACJ;AAEO,MAAM,oBAAsC;AAAA,EACjD,EAAE,SAAS,GAAG,OAAO,GAAG,YAAY,UAAU;AAAA,EAC9C,EAAE,SAAS,KAAK,OAAO,MAAM,YAAY,UAAU;AAAA,EACnD,EAAE,SAAS,KAAK,OAAO,OAAO,YAAY,WAAW;AAAA,EACrD,EAAE,SAAS,KAAM,OAAO,OAAO,YAAY,WAAW;AAAA,EACtD,EAAE,SAAS,MAAM,OAAO,MAAO,YAAY,WAAW;AAAA,EACtD,EAAE,SAAS,KAAM,OAAO,OAAQ,YAAY,YAAY;AAAA,EACxD,EAAE,SAAS,KAAO,OAAO,OAAQ,YAAY,YAAY;AAC3D;AAEO,MAAM,mBAAwC,IAAI,IAAI,eAAe;","names":[]}
@@ -1,8 +1,10 @@
1
+ import { isUsdGateway } from "./gateway";
1
2
  const CURRENCY_FORMAT = {
2
3
  brl: { locale: "pt-BR", currency: "BRL" },
3
4
  usd: { locale: "en-US", currency: "USD" }
4
5
  };
5
- const getCurrencyForGateway = (gateway) => gateway === "stripe_usd" ? "usd" : "brl";
6
+ const getCurrencyForGateway = (gateway) => isUsdGateway(gateway) ? "usd" : "brl";
7
+ const getLocaleForCurrency = (currency = "brl") => CURRENCY_FORMAT[currency].locale;
6
8
  const formatCurrency = (value, currency = "brl") => {
7
9
  const fmt = CURRENCY_FORMAT[currency];
8
10
  if (typeof value === "number") {
@@ -36,6 +38,7 @@ export {
36
38
  formatCurrency,
37
39
  formatCurrencyNumber,
38
40
  getCurrencyForGateway,
41
+ getLocaleForCurrency,
39
42
  parseCurrencyToNumber
40
43
  };
41
44
  //# sourceMappingURL=currency.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/format/currency.ts"],"sourcesContent":["/**\n * Moeda de exibição. Espelha getCurrencyForGateway do backend (gapps-r3-api):\n * 'stripe_usd' → USD; demais gateways → BRL.\n */\nexport type CurrencyCode = 'brl' | 'usd';\n\nconst CURRENCY_FORMAT: Record<CurrencyCode, { locale: string; currency: string }> = {\n brl: { locale: 'pt-BR', currency: 'BRL' },\n usd: { locale: 'en-US', currency: 'USD' },\n};\n\n/**\n * Deriva a moeda de exibição a partir do gateway da conta.\n * A moeda é definida pelo backend no roteamento de gateway — read-only para o usuário.\n * @param gateway - account.gateway ('stripe_usd' | 'stripe_brl' | 'vindi' | ...)\n */\nexport const getCurrencyForGateway = (gateway?: string | null): CurrencyCode =>\n gateway === 'stripe_usd' ? 'usd' : 'brl';\n\n/**\n * Formata um valor (string ou número) como moeda.\n * String: assume centavos (remove não-dígitos e divide por 100) — comportamento legado.\n * @param value - valor a formatar\n * @param currency - moeda de exibição (padrão 'brl' por retrocompatibilidade)\n * @returns String formatada (ex: \"R$ 1.234,56\" ou \"$1,234.56\")\n */\nexport const formatCurrency = (value: string | number, currency: CurrencyCode = 'brl'): string => {\n const fmt = CURRENCY_FORMAT[currency];\n if (typeof value === 'number') {\n return value.toLocaleString(fmt.locale, {\n style: 'currency',\n currency: fmt.currency,\n });\n }\n\n const numericValue = value.replace(/\\D/g, '');\n const numberValue = parseFloat(numericValue) / 100;\n return numberValue.toLocaleString(fmt.locale, {\n style: 'currency',\n currency: fmt.currency,\n });\n};\n\n/**\n * Converte uma string de moeda para número (centavos / 100). Independente de moeda.\n * @param value - String com valor de moeda\n * @returns Número decimal\n */\nexport const parseCurrencyToNumber = (value: string): number => {\n const numericValue = value.replace(/\\D/g, '');\n return parseFloat(numericValue) / 100;\n};\n\n/**\n * Formata um número (opcional) como moeda.\n * @param value - Número a formatar (pode ser undefined/null → 0)\n * @param decimals - Casas decimais (padrão 2)\n * @param currency - Moeda de exibição (padrão 'brl' por retrocompatibilidade)\n * @returns String formatada (ex: \"R$ 1.234,56\" ou \"$1,234.56\")\n */\nexport const formatCurrencyNumber = (value?: number | null, decimals = 2, currency: CurrencyCode = 'brl'): string => {\n const safeValue = value ?? 0;\n const fmt = CURRENCY_FORMAT[currency];\n return safeValue.toLocaleString(fmt.locale, {\n style: 'currency',\n currency: fmt.currency,\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n });\n};\n"],"mappings":"AAMA,MAAM,kBAA8E;AAAA,EAClF,KAAK,EAAE,QAAQ,SAAS,UAAU,MAAM;AAAA,EACxC,KAAK,EAAE,QAAQ,SAAS,UAAU,MAAM;AAC1C;AAOO,MAAM,wBAAwB,CAAC,YACpC,YAAY,eAAe,QAAQ;AAS9B,MAAM,iBAAiB,CAAC,OAAwB,WAAyB,UAAkB;AAChG,QAAM,MAAM,gBAAgB,QAAQ;AACpC,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,MAAM,eAAe,IAAI,QAAQ;AAAA,MACtC,OAAO;AAAA,MACP,UAAU,IAAI;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,MAAM,QAAQ,OAAO,EAAE;AAC5C,QAAM,cAAc,WAAW,YAAY,IAAI;AAC/C,SAAO,YAAY,eAAe,IAAI,QAAQ;AAAA,IAC5C,OAAO;AAAA,IACP,UAAU,IAAI;AAAA,EAChB,CAAC;AACH;AAOO,MAAM,wBAAwB,CAAC,UAA0B;AAC9D,QAAM,eAAe,MAAM,QAAQ,OAAO,EAAE;AAC5C,SAAO,WAAW,YAAY,IAAI;AACpC;AASO,MAAM,uBAAuB,CAAC,OAAuB,WAAW,GAAG,WAAyB,UAAkB;AACnH,QAAM,YAAY,SAAS;AAC3B,QAAM,MAAM,gBAAgB,QAAQ;AACpC,SAAO,UAAU,eAAe,IAAI,QAAQ;AAAA,IAC1C,OAAO;AAAA,IACP,UAAU,IAAI;AAAA,IACd,uBAAuB;AAAA,IACvB,uBAAuB;AAAA,EACzB,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../../src/utils/format/currency.ts"],"sourcesContent":["import { isUsdGateway } from './gateway';\n\nexport type CurrencyCode = 'brl' | 'usd';\n\nconst CURRENCY_FORMAT: Record<CurrencyCode, { locale: string; currency: string }> = {\n brl: { locale: 'pt-BR', currency: 'BRL' },\n usd: { locale: 'en-US', currency: 'USD' },\n};\n\nexport const getCurrencyForGateway = (gateway?: string | null): CurrencyCode =>\n isUsdGateway(gateway) ? 'usd' : 'brl';\n\nexport const getLocaleForCurrency = (currency: CurrencyCode = 'brl'): string => CURRENCY_FORMAT[currency].locale;\n\nexport const formatCurrency = (value: string | number, currency: CurrencyCode = 'brl'): string => {\n const fmt = CURRENCY_FORMAT[currency];\n if (typeof value === 'number') {\n return value.toLocaleString(fmt.locale, {\n style: 'currency',\n currency: fmt.currency,\n });\n }\n\n const numericValue = value.replace(/\\D/g, '');\n const numberValue = parseFloat(numericValue) / 100;\n return numberValue.toLocaleString(fmt.locale, {\n style: 'currency',\n currency: fmt.currency,\n });\n};\n\nexport const parseCurrencyToNumber = (value: string): number => {\n const numericValue = value.replace(/\\D/g, '');\n return parseFloat(numericValue) / 100;\n};\n\nexport const formatCurrencyNumber = (value?: number | null, decimals = 2, currency: CurrencyCode = 'brl'): string => {\n const safeValue = value ?? 0;\n const fmt = CURRENCY_FORMAT[currency];\n return safeValue.toLocaleString(fmt.locale, {\n style: 'currency',\n currency: fmt.currency,\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n });\n};\n"],"mappings":"AAAA,SAAS,oBAAoB;AAI7B,MAAM,kBAA8E;AAAA,EAClF,KAAK,EAAE,QAAQ,SAAS,UAAU,MAAM;AAAA,EACxC,KAAK,EAAE,QAAQ,SAAS,UAAU,MAAM;AAC1C;AAEO,MAAM,wBAAwB,CAAC,YACpC,aAAa,OAAO,IAAI,QAAQ;AAE3B,MAAM,uBAAuB,CAAC,WAAyB,UAAkB,gBAAgB,QAAQ,EAAE;AAEnG,MAAM,iBAAiB,CAAC,OAAwB,WAAyB,UAAkB;AAChG,QAAM,MAAM,gBAAgB,QAAQ;AACpC,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,MAAM,eAAe,IAAI,QAAQ;AAAA,MACtC,OAAO;AAAA,MACP,UAAU,IAAI;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,MAAM,QAAQ,OAAO,EAAE;AAC5C,QAAM,cAAc,WAAW,YAAY,IAAI;AAC/C,SAAO,YAAY,eAAe,IAAI,QAAQ;AAAA,IAC5C,OAAO;AAAA,IACP,UAAU,IAAI;AAAA,EAChB,CAAC;AACH;AAEO,MAAM,wBAAwB,CAAC,UAA0B;AAC9D,QAAM,eAAe,MAAM,QAAQ,OAAO,EAAE;AAC5C,SAAO,WAAW,YAAY,IAAI;AACpC;AAEO,MAAM,uBAAuB,CAAC,OAAuB,WAAW,GAAG,WAAyB,UAAkB;AACnH,QAAM,YAAY,SAAS;AAC3B,QAAM,MAAM,gBAAgB,QAAQ;AACpC,SAAO,UAAU,eAAe,IAAI,QAAQ;AAAA,IAC1C,OAAO;AAAA,IACP,UAAU,IAAI;AAAA,IACd,uBAAuB;AAAA,IACvB,uBAAuB;AAAA,EACzB,CAAC;AACH;","names":[]}
@@ -0,0 +1,18 @@
1
+ const USD_GATEWAY = "stripe_usd";
2
+ const normalizeGateway = (gateway) => (gateway ?? "").trim().toLowerCase();
3
+ const isUsdGateway = (gateway) => normalizeGateway(gateway) === USD_GATEWAY;
4
+ const isUsdAccount = (account) => isUsdGateway(account?.gateway);
5
+ const USD_PAYMENT_METHODS = ["card"];
6
+ const BRL_PAYMENT_METHODS = ["card", "pix", "boleto"];
7
+ const allowedPaymentMethods = (account) => isUsdAccount(account) ? USD_PAYMENT_METHODS : BRL_PAYMENT_METHODS;
8
+ const isPaymentMethodAllowed = (account, method) => allowedPaymentMethods(account).includes(method);
9
+ const canBuyStandaloneAiCredits = (account) => !isUsdAccount(account);
10
+ export {
11
+ USD_GATEWAY,
12
+ allowedPaymentMethods,
13
+ canBuyStandaloneAiCredits,
14
+ isPaymentMethodAllowed,
15
+ isUsdAccount,
16
+ isUsdGateway
17
+ };
18
+ //# sourceMappingURL=gateway.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/format/gateway.ts"],"sourcesContent":["export const USD_GATEWAY = 'stripe_usd';\n\nexport type PaymentMethodKind = 'card' | 'pix' | 'boleto';\n\nexport type AccountGatewayLike = { gateway?: string | null } | null | undefined;\n\nconst normalizeGateway = (gateway?: string | null): string => (gateway ?? '').trim().toLowerCase();\n\nexport const isUsdGateway = (gateway?: string | null): boolean => normalizeGateway(gateway) === USD_GATEWAY;\n\nexport const isUsdAccount = (account: AccountGatewayLike): boolean => isUsdGateway(account?.gateway);\n\nconst USD_PAYMENT_METHODS: readonly PaymentMethodKind[] = ['card'];\nconst BRL_PAYMENT_METHODS: readonly PaymentMethodKind[] = ['card', 'pix', 'boleto'];\n\nexport const allowedPaymentMethods = (account: AccountGatewayLike): readonly PaymentMethodKind[] =>\n isUsdAccount(account) ? USD_PAYMENT_METHODS : BRL_PAYMENT_METHODS;\n\nexport const isPaymentMethodAllowed = (account: AccountGatewayLike, method: PaymentMethodKind): boolean =>\n allowedPaymentMethods(account).includes(method);\n\nexport const canBuyStandaloneAiCredits = (account: AccountGatewayLike): boolean => !isUsdAccount(account);\n"],"mappings":"AAAO,MAAM,cAAc;AAM3B,MAAM,mBAAmB,CAAC,aAAqC,WAAW,IAAI,KAAK,EAAE,YAAY;AAE1F,MAAM,eAAe,CAAC,YAAqC,iBAAiB,OAAO,MAAM;AAEzF,MAAM,eAAe,CAAC,YAAyC,aAAa,SAAS,OAAO;AAEnG,MAAM,sBAAoD,CAAC,MAAM;AACjE,MAAM,sBAAoD,CAAC,QAAQ,OAAO,QAAQ;AAE3E,MAAM,wBAAwB,CAAC,YACpC,aAAa,OAAO,IAAI,sBAAsB;AAEzC,MAAM,yBAAyB,CAAC,SAA6B,WAClE,sBAAsB,OAAO,EAAE,SAAS,MAAM;AAEzC,MAAM,4BAA4B,CAAC,YAAyC,CAAC,aAAa,OAAO;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/common",
3
- "version": "1.1.741",
3
+ "version": "1.1.743",
4
4
  "description": "Shared library for GreatApps frontend applications",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -12,14 +12,16 @@ import { Toast } from '../ui/feedback/Toast';
12
12
  import { SelectField } from '../ui/form/SelectField';
13
13
  import { Skeleton } from '../ui/feedback/Skeleton';
14
14
  import { PaymentInfoCard } from '../ui/data-display/PaymentInfoCard';
15
- import { ADDON_IDS } from '../../modules/subscriptions/constants/addons.constants';
16
15
  import {
17
- AI_CREDIT_OPTIONS,
16
+ ADDON_IDS,
18
17
  AI_CREDIT_VALUES,
18
+ buildAiCreditOptions,
19
19
  } from '../../modules/subscriptions/constants/addons.constants';
20
20
  import { useBuyCreditsModal } from '../../store/useBuyCreditsModal';
21
21
  import { useModalManager } from '../../store/useModalManager';
22
22
  import { useCards } from '../../modules/cards/hooks/cards.hook';
23
+ import { useCurrencyFormatter } from '../../modules/accounts/hooks/use-currency-formatter.hook';
24
+ import { usePlanById } from '../../modules/plans/hooks/use-plan-by-id.hook';
23
25
  import { useActiveSubscription } from '../../modules/subscriptions/hooks/find-active-subscription.hook';
24
26
  import { useCalculateSubscription } from '../../modules/subscriptions/hooks/calculate-subscription.hook';
25
27
  import { useUpdateSubscriptionPlan } from '../../modules/subscriptions/hooks/update-subscription-plan.hook';
@@ -49,6 +51,7 @@ export default function BuyCreditsModal() {
49
51
  const isDefaultWl = useIsDefaultWhitelabel();
50
52
  const { isExternalContracting, redirectToExternal } = useExternalContracting();
51
53
  const { data: cardsData } = useCards();
54
+ const { locale, formatCurrencyNumber } = useCurrencyFormatter();
52
55
  const { data: subscriptionData } = useActiveSubscription();
53
56
  const subscription = subscriptionData?.data?.[0] ?? null;
54
57
  const defaultCardId =
@@ -75,9 +78,20 @@ export default function BuyCreditsModal() {
75
78
  const subscriptionId = subscription?.id;
76
79
  const planId = subscription?.id_plan;
77
80
 
81
+ const { data: planData } = usePlanById(planId);
82
+ const perCreditPrice = useMemo(() => {
83
+ const planAiItem = planData?.data?.items?.find(
84
+ (item) => item.id_addon === ADDON_IDS.AI && (item.value ?? 0) > 0
85
+ );
86
+ return planAiItem?.value ?? aiItem?.value ?? 0;
87
+ }, [planData, aiItem]);
88
+
89
+ const hasPerCreditPrice = perCreditPrice > 0;
90
+
91
+ const aiCreditOptions = useMemo(() => buildAiCreditOptions(perCreditPrice), [perCreditPrice]);
92
+
78
93
  const buildPlanExtras = useCallback(
79
94
  (aiQuantity: number) => {
80
- const aiOption = AI_CREDIT_OPTIONS.find(opt => opt.credits === aiQuantity);
81
95
  return [
82
96
  ...(subscription?.items ?? [])
83
97
  .filter((item) => item.id_addon != null && item.id_addon !== ADDON_IDS.AI)
@@ -87,10 +101,9 @@ export default function BuyCreditsModal() {
87
101
  value: Number(item.value ?? 0),
88
102
  price: item.price,
89
103
  })),
90
- {
91
- id_addon: ADDON_IDS.AI,
104
+ {
105
+ id_addon: ADDON_IDS.AI,
92
106
  quantity: aiQuantity,
93
- value: aiOption?.price ?? 0
94
107
  },
95
108
  ];
96
109
  },
@@ -161,14 +174,13 @@ export default function BuyCreditsModal() {
161
174
  shouldDirty: true,
162
175
  });
163
176
 
164
- const option = AI_CREDIT_OPTIONS.find((opt) => opt.credits === credits);
165
- if (!option) return;
177
+ if (!AI_CREDIT_VALUES.has(credits)) return;
166
178
 
167
179
  calculate({
168
180
  id_product: 1,
169
181
  ...(planId != null && { id_plan: planId }),
170
182
  ...(subscriptionId != null && { id_subscription: subscriptionId }),
171
- plan_extras: buildPlanExtras(option.credits),
183
+ plan_extras: buildPlanExtras(credits),
172
184
  });
173
185
  },
174
186
  [form, subscriptionId, planId, calculate, buildPlanExtras]
@@ -186,14 +198,8 @@ export default function BuyCreditsModal() {
186
198
  return getPriceFromCalculatedData(calculatedData, subscription?.periodicity);
187
199
  }, [calculatedData, subscription]);
188
200
 
189
- const formatPrice = useCallback((price: number) => {
190
- return price.toLocaleString('pt-BR', {
191
- style: 'currency',
192
- currency: 'BRL',
193
- });
194
- }, []);
195
-
196
- const isConfirmDisabled = !isDirty || !isValid || isCalculatePending || isSubmitting;
201
+ const isConfirmDisabled =
202
+ !isDirty || !isValid || isCalculatePending || isSubmitting || !hasPerCreditPrice;
197
203
 
198
204
  const handleClose = useCallback(() => {
199
205
  form.reset();
@@ -205,45 +211,55 @@ export default function BuyCreditsModal() {
205
211
  }, [openModal]);
206
212
 
207
213
  const selectOptions = useMemo(() => {
208
- return AI_CREDIT_OPTIONS.map((opt) => ({
214
+ return aiCreditOptions.map((opt) => ({
209
215
  value: opt.credits,
210
216
  label: (
211
217
  <div className="flex items-center justify-between gap-2 w-full">
212
218
  <span className="paragraph-small-medium text-zinc-950 flex-1">
213
- {translate('common.credits.buy.creditsOptionLabel', { credits: opt.credits.toLocaleString('pt-BR') })}
219
+ {translate('common.credits.buy.creditsOptionLabel', { credits: opt.credits.toLocaleString(locale) })}
214
220
  </span>
215
- <span className="paragraph-small-medium text-zinc-500 opacity-70">{opt.priceLabel}</span>
221
+ {hasPerCreditPrice && (
222
+ <span className="paragraph-small-medium text-zinc-500 opacity-70">{formatCurrencyNumber(opt.price)}</span>
223
+ )}
216
224
  </div>
217
225
  ),
218
- displayValue: translate('common.credits.buy.creditsOptionDisplayValue', { credits: opt.credits.toLocaleString('pt-BR') }),
226
+ displayValue: translate('common.credits.buy.creditsOptionDisplayValue', { credits: opt.credits.toLocaleString(locale) }),
219
227
  }));
220
- }, [translate]);
228
+ }, [aiCreditOptions, translate, locale, formatCurrencyNumber, hasPerCreditPrice]);
221
229
 
222
230
  const onSubmit = useCallback(
223
231
  async (data: BuyCreditFormData) => {
224
232
  if (!subscriptionId) return;
225
-
226
- const option = AI_CREDIT_OPTIONS.find((opt) => opt.credits === data.creditOption);
227
- if (!option) return;
233
+ if (!hasPerCreditPrice) return;
234
+ if (!AI_CREDIT_VALUES.has(data.creditOption)) return;
228
235
 
229
236
  await updatePlanMutation.mutateAsync({
230
237
  subscriptionId,
231
238
  data: {
232
- plan_extras: buildPlanExtras(option.credits),
239
+ plan_extras: buildPlanExtras(data.creditOption),
233
240
  },
234
241
  });
235
242
 
236
243
  toast.custom((t) => (
237
244
  <Toast
238
245
  variant="success"
239
- message={translate('common.credits.buy.creditsChangedToast', { credits: data.creditOption.toLocaleString('pt-BR') })}
246
+ message={translate('common.credits.buy.creditsChangedToast', { credits: data.creditOption.toLocaleString(locale) })}
240
247
  toastId={t}
241
248
  />
242
249
  ));
243
250
  form.reset();
244
251
  closeModal();
245
252
  },
246
- [subscriptionId, updatePlanMutation, form, closeModal, buildPlanExtras]
253
+ [
254
+ subscriptionId,
255
+ hasPerCreditPrice,
256
+ updatePlanMutation,
257
+ form,
258
+ closeModal,
259
+ buildPlanExtras,
260
+ translate,
261
+ locale,
262
+ ]
247
263
  );
248
264
 
249
265
  const renderPriceDisplay = useCallback(() => {
@@ -253,12 +269,12 @@ export default function BuyCreditsModal() {
253
269
  if (totalPrice !== null) {
254
270
  return (
255
271
  <span className="paragraph-xlarge-semibold text-zinc-950">
256
- {totalPrice === 0 ? translate('common.credits.buy.defaultPrice') : formatPrice(totalPrice)}
272
+ {totalPrice === 0 ? translate('common.credits.buy.defaultPrice') : formatCurrencyNumber(totalPrice)}
257
273
  </span>
258
274
  );
259
275
  }
260
276
  return <span className="paragraph-xlarge-semibold text-zinc-400">--</span>;
261
- }, [isCalculatePending, totalPrice, formatPrice]);
277
+ }, [isCalculatePending, totalPrice, formatCurrencyNumber, translate]);
262
278
 
263
279
  if (isExternalContracting) {
264
280
  return (
@@ -355,7 +371,7 @@ export default function BuyCreditsModal() {
355
371
  <Skeleton className="h-5 w-20" />
356
372
  ) : (
357
373
  <span className="paragraph-small-semibold text-zinc-950">
358
- {formatPrice(immediateChargeTotal)}
374
+ {formatCurrencyNumber(immediateChargeTotal)}
359
375
  </span>
360
376
  )}
361
377
  </div>
@@ -385,7 +401,7 @@ export default function BuyCreditsModal() {
385
401
  <Skeleton className="h-5 w-20" />
386
402
  ) : (
387
403
  <span className="paragraph-small-semibold text-zinc-950">
388
- {formatPrice(immediateChargeTotal)}
404
+ {formatCurrencyNumber(immediateChargeTotal)}
389
405
  </span>
390
406
  )}
391
407
  </div>
@@ -48,7 +48,7 @@ const COUNTRY_OPTIONS = COUNTRIES.map((country: { iso: string; name: string }) =
48
48
  label: country.name,
49
49
  }));
50
50
 
51
- function buildBillingDataSchema(isInternational: boolean, translate: TranslateFn) {
51
+ function buildBillingDataSchema(translate: TranslateFn) {
52
52
  const requiredText = z
53
53
  .string()
54
54
  .trim()
@@ -73,11 +73,6 @@ function buildBillingDataSchema(isInternational: boolean, translate: TranslateFn
73
73
  country: requiredText,
74
74
  });
75
75
 
76
- /* Conta internacional não tem CPF/CNPJ, número nem bairro — mesmo recorte do
77
- * AddCardModal. Exigir esses campos aqui trancaria o cliente fora do app, já que
78
- * o modal é bloqueante. */
79
- if (isInternational) return base;
80
-
81
76
  return base.superRefine((data, ctx) => {
82
77
  if (!data.streetNumber) {
83
78
  ctx.addIssue({
@@ -140,7 +135,7 @@ function formatDocument(document: string | null | undefined, isCompany: boolean)
140
135
  function RequiredBillingDataModalContent() {
141
136
  const translate = useTranslations();
142
137
  const { user } = useAuth();
143
- const { isRequired, account, isInternational } = useRequiredBillingData();
138
+ const { isRequired, account } = useRequiredBillingData();
144
139
  const { mutateAsync: updateBillingData } = useUpdateBillingData();
145
140
  const { mutateAsync: lookupCep } = useViaCep();
146
141
 
@@ -150,8 +145,8 @@ function RequiredBillingDataModalContent() {
150
145
  const isPrefilled = useRef(false);
151
146
 
152
147
  const schema = useMemo(
153
- () => buildBillingDataSchema(isInternational, translate),
154
- [isInternational, translate],
148
+ () => buildBillingDataSchema(translate),
149
+ [translate],
155
150
  );
156
151
 
157
152
  const form = useForm<BillingDataFormValues>({
@@ -203,9 +198,9 @@ function RequiredBillingDataModalContent() {
203
198
  neighborhood: account.neighborhood ?? '',
204
199
  city: account.city ?? '',
205
200
  state: account.state ?? '',
206
- country: account.country || (isInternational ? 'US' : 'BR'),
201
+ country: account.country || 'BR',
207
202
  });
208
- }, [account, isInternational, reset, user?.email]);
203
+ }, [account, reset, user?.email]);
209
204
 
210
205
  const handleDocumentChange = (rawValue: string) => {
211
206
  setValue('document', isCompany ? formatCNPJ(rawValue) : formatCPF(rawValue), {
@@ -222,11 +217,6 @@ function RequiredBillingDataModalContent() {
222
217
  };
223
218
 
224
219
  const handleCepChange = (rawValue: string) => {
225
- if (isInternational) {
226
- setValue('cep', rawValue, { shouldValidate: true });
227
- return;
228
- }
229
-
230
220
  setValue('cep', formatPostalCode(rawValue), { shouldValidate: true });
231
221
 
232
222
  const digits = rawValue.replace(/\D/g, '');
@@ -246,12 +236,8 @@ function RequiredBillingDataModalContent() {
246
236
 
247
237
  async function onSubmit(data: BillingDataFormValues) {
248
238
  const payload: UpdateBillingDataRequest = {
249
- ...(isInternational
250
- ? {}
251
- : {
252
- financial_document_type: data.personType === 'pf' ? 1 : 2,
253
- financial_document: data.document,
254
- }),
239
+ financial_document_type: data.personType === 'pf' ? 1 : 2,
240
+ financial_document: data.document,
255
241
  financial_name: data.financialName,
256
242
  financial_email: data.financialEmail,
257
243
  zipcode: data.cep,
@@ -322,8 +308,7 @@ function RequiredBillingDataModalContent() {
322
308
  >
323
309
  <div className="flex flex-col gap-6 p-4 lg:p-5 flex-1 overflow-y-auto">
324
310
  <div className="flex flex-col gap-5">
325
- {!isInternational && (
326
- <div className="flex gap-4">
311
+ <div className="flex gap-4">
327
312
  <Controller
328
313
  name="personType"
329
314
  control={control}
@@ -362,8 +347,7 @@ function RequiredBillingDataModalContent() {
362
347
  })}
363
348
  />
364
349
  </div>
365
- </div>
366
- )}
350
+ </div>
367
351
 
368
352
  <FormField
369
353
  label={
@@ -395,12 +379,8 @@ function RequiredBillingDataModalContent() {
395
379
  </span>
396
380
 
397
381
  <FormField
398
- label={
399
- isInternational
400
- ? translate('common.billing.requiredData.postalCode')
401
- : translate('common.billing.requiredData.cep')
402
- }
403
- placeholder={isInternational ? '' : '_____-___'}
382
+ label={translate('common.billing.requiredData.cep')}
383
+ placeholder="_____-___"
404
384
  error={!!errors.cep}
405
385
  errorMessage={errors.cep?.message}
406
386
  {...register('cep', {
@@ -418,31 +398,27 @@ function RequiredBillingDataModalContent() {
418
398
  {...register('street')}
419
399
  />
420
400
  </div>
421
- {!isInternational && (
422
- <div className="flex-1">
423
- <FormField
424
- label={translate('common.billing.requiredData.streetNumber')}
425
- placeholder="000"
426
- error={!!errors.streetNumber}
427
- errorMessage={errors.streetNumber?.message}
428
- {...register('streetNumber')}
429
- />
430
- </div>
431
- )}
401
+ <div className="flex-1">
402
+ <FormField
403
+ label={translate('common.billing.requiredData.streetNumber')}
404
+ placeholder="000"
405
+ error={!!errors.streetNumber}
406
+ errorMessage={errors.streetNumber?.message}
407
+ {...register('streetNumber')}
408
+ />
409
+ </div>
432
410
  </div>
433
411
 
434
412
  <div className="flex gap-4">
435
- {!isInternational && (
436
- <div className="flex-1">
437
- <FormField
438
- label={translate('common.billing.requiredData.neighborhood')}
439
- placeholder={translate('common.billing.requiredData.typeHere')}
440
- error={!!errors.neighborhood}
441
- errorMessage={errors.neighborhood?.message}
442
- {...register('neighborhood')}
443
- />
444
- </div>
445
- )}
413
+ <div className="flex-1">
414
+ <FormField
415
+ label={translate('common.billing.requiredData.neighborhood')}
416
+ placeholder={translate('common.billing.requiredData.typeHere')}
417
+ error={!!errors.neighborhood}
418
+ errorMessage={errors.neighborhood?.message}
419
+ {...register('neighborhood')}
420
+ />
421
+ </div>
446
422
  <div className="flex-1">
447
423
  <FormField
448
424
  label={translate('common.billing.requiredData.complement')}
@@ -464,16 +440,7 @@ function RequiredBillingDataModalContent() {
464
440
  />
465
441
  </div>
466
442
  <div className="flex-1">
467
- {isInternational ? (
468
- <FormField
469
- label={translate('common.billing.requiredData.stateProvince')}
470
- placeholder={translate('common.billing.requiredData.typeHere')}
471
- error={!!errors.state}
472
- errorMessage={errors.state?.message}
473
- {...register('state')}
474
- />
475
- ) : (
476
- <Controller
443
+ <Controller
477
444
  name="state"
478
445
  control={control}
479
446
  render={({ field }) => (
@@ -489,8 +456,7 @@ function RequiredBillingDataModalContent() {
489
456
  errorMessage={errors.state?.message}
490
457
  />
491
458
  )}
492
- />
493
- )}
459
+ />
494
460
  </div>
495
461
  </div>
496
462
 
@@ -9,6 +9,7 @@ import { useCreateCard } from '../../../modules/cards/hooks/create-card.hook';
9
9
  import { useCreateSetupIntent } from '../../../modules/cards/hooks/create-setup-intent.hook';
10
10
  import { useCurrentAccount } from '../../../modules/accounts/hooks/current-account.hook';
11
11
  import { isValidTaxId } from '../../../utils/validators/common';
12
+ import { isUsdAccount } from '../../../utils/format/gateway';
12
13
  import { useExternalContracting } from '../../../providers/whitelabel.provider';
13
14
  import { CardFormFields, BillingFormFields, buildCardFormSchema } from './CardFormFields';
14
15
  import type { CardFormData, StripeElementsStatus } from './CardFormFields';
@@ -79,7 +80,7 @@ export default function AddCardModal() {
79
80
  const isOpen = activeModal === 'addCardModal';
80
81
  const { isExternalContracting, redirectToExternal } = useExternalContracting();
81
82
  const { data: account } = useCurrentAccount();
82
- const isInternational = account?.gateway === 'stripe_usd';
83
+ const isInternational = isUsdAccount(account);
83
84
  const [currentStep, setCurrentStep] = useState(1);
84
85
  const [stripeStatus, setStripeStatus] = useState<StripeElementsStatus>(INITIAL_STRIPE_STATUS);
85
86
  const [stripeErrors, setStripeErrors] = useState<Record<string, string | undefined>>({});
package/src/index.ts CHANGED
@@ -123,7 +123,10 @@ export {
123
123
  export {
124
124
  ADDON_IDS,
125
125
  AI_CREDIT_OPTIONS,
126
+ AI_CREDIT_SIZES,
126
127
  AI_CREDIT_VALUES,
128
+ buildAiCreditOptions,
129
+ resolveAiCreditPrice,
127
130
  } from "./modules/subscriptions/constants/addons.constants";
128
131
  export type {
129
132
  AddonKindEnum,
@@ -487,8 +490,18 @@ export {
487
490
  formatCurrencyNumber,
488
491
  parseCurrencyToNumber,
489
492
  getCurrencyForGateway,
493
+ getLocaleForCurrency,
490
494
  } from "./utils/format/currency";
491
495
  export type { CurrencyCode } from "./utils/format/currency";
496
+ export {
497
+ USD_GATEWAY,
498
+ isUsdGateway,
499
+ isUsdAccount,
500
+ allowedPaymentMethods,
501
+ isPaymentMethodAllowed,
502
+ canBuyStandaloneAiCredits,
503
+ } from "./utils/format/gateway";
504
+ export type { AccountGatewayLike, PaymentMethodKind } from "./utils/format/gateway";
492
505
  export { isValidCPF, isValidCNPJ, isValidTaxId } from "./utils/validators/common";
493
506
  export { BR_STATE_OPTIONS } from "./utils/constants/br-states";
494
507
  export type { TimezoneOption } from "./modules/accounts/services/timezone.service";
@@ -528,8 +541,7 @@ export { useAccountToken } from "./modules/accounts/hooks/use-account-token.hook
528
541
  export { useRequiredBillingData } from "./modules/accounts/hooks/use-required-billing-data.hook";
529
542
  export {
530
543
  hasCompleteBillingData,
531
- isInternationalAccount,
532
- INTERNATIONAL_GATEWAY,
544
+ isBrazilianBillingAccount,
533
545
  } from "./modules/accounts/utils/billing-data";
534
546
  export type { BillingDataSnapshot } from "./modules/accounts/utils/billing-data";
535
547