@greatapps/common 1.1.670 → 1.1.672

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 (118) hide show
  1. package/dist/components/layouts/NavBar.mjs +5 -3
  2. package/dist/components/layouts/NavBar.mjs.map +1 -1
  3. package/dist/components/layouts/NotificationsPopover.mjs +5 -3
  4. package/dist/components/layouts/NotificationsPopover.mjs.map +1 -1
  5. package/dist/components/layouts/ProfilePopover.mjs +6 -4
  6. package/dist/components/layouts/ProfilePopover.mjs.map +1 -1
  7. package/dist/components/layouts/UsersSelectorPopover.mjs +9 -7
  8. package/dist/components/layouts/UsersSelectorPopover.mjs.map +1 -1
  9. package/dist/components/modals/BuyCreditsModal.mjs +17 -18
  10. package/dist/components/modals/BuyCreditsModal.mjs.map +1 -1
  11. package/dist/components/modals/CreditsDisabledModal.mjs +6 -4
  12. package/dist/components/modals/CreditsDisabledModal.mjs.map +1 -1
  13. package/dist/components/modals/PaidPlanRequiredModal.mjs +5 -3
  14. package/dist/components/modals/PaidPlanRequiredModal.mjs.map +1 -1
  15. package/dist/components/modals/cards/AddCardModal.mjs +27 -24
  16. package/dist/components/modals/cards/AddCardModal.mjs.map +1 -1
  17. package/dist/components/modals/cards/CannotDeleteCardModal.mjs +9 -7
  18. package/dist/components/modals/cards/CannotDeleteCardModal.mjs.map +1 -1
  19. package/dist/components/modals/cards/CardFormFields.mjs +25 -22
  20. package/dist/components/modals/cards/CardFormFields.mjs.map +1 -1
  21. package/dist/components/modals/cards/DeleteCardModal.mjs +10 -8
  22. package/dist/components/modals/cards/DeleteCardModal.mjs.map +1 -1
  23. package/dist/components/navigation/CancelledSubscriptionBanner.mjs +4 -2
  24. package/dist/components/navigation/CancelledSubscriptionBanner.mjs.map +1 -1
  25. package/dist/components/navigation/OverdueInvoiceBanner.mjs +4 -2
  26. package/dist/components/navigation/OverdueInvoiceBanner.mjs.map +1 -1
  27. package/dist/components/navigation/PendingCancellationBanner.mjs +4 -6
  28. package/dist/components/navigation/PendingCancellationBanner.mjs.map +1 -1
  29. package/dist/components/navigation/ProjectSelectorPopover.mjs +8 -6
  30. package/dist/components/navigation/ProjectSelectorPopover.mjs.map +1 -1
  31. package/dist/components/navigation/UpcomingInvoiceBanner.mjs +4 -6
  32. package/dist/components/navigation/UpcomingInvoiceBanner.mjs.map +1 -1
  33. package/dist/components/navigation/subcomponents/BottomLinks.mjs +5 -3
  34. package/dist/components/navigation/subcomponents/BottomLinks.mjs.map +1 -1
  35. package/dist/components/navigation/subcomponents/CreditsCard.mjs +3 -1
  36. package/dist/components/navigation/subcomponents/CreditsCard.mjs.map +1 -1
  37. package/dist/components/navigation/subcomponents/PlanCard.mjs +3 -1
  38. package/dist/components/navigation/subcomponents/PlanCard.mjs.map +1 -1
  39. package/dist/components/navigation/subcomponents/ProjectSelector.mjs +5 -3
  40. package/dist/components/navigation/subcomponents/ProjectSelector.mjs.map +1 -1
  41. package/dist/components/pages/NotFoundPage.mjs +5 -3
  42. package/dist/components/pages/NotFoundPage.mjs.map +1 -1
  43. package/dist/components/pages/notifications/Notifications.mjs +13 -10
  44. package/dist/components/pages/notifications/Notifications.mjs.map +1 -1
  45. package/dist/components/ui/buttons/CopyButton.mjs +9 -5
  46. package/dist/components/ui/buttons/CopyButton.mjs.map +1 -1
  47. package/dist/components/ui/data-display/CardItem.mjs +7 -5
  48. package/dist/components/ui/data-display/CardItem.mjs.map +1 -1
  49. package/dist/components/ui/data-display/PaymentInfoCard.mjs +10 -8
  50. package/dist/components/ui/data-display/PaymentInfoCard.mjs.map +1 -1
  51. package/dist/components/ui/feedback/DefaultCircularProgress.mjs +3 -1
  52. package/dist/components/ui/feedback/DefaultCircularProgress.mjs.map +1 -1
  53. package/dist/components/ui/form/ComboboxField.mjs +11 -6
  54. package/dist/components/ui/form/ComboboxField.mjs.map +1 -1
  55. package/dist/components/ui/form/DatePicker.mjs +5 -2
  56. package/dist/components/ui/form/DatePicker.mjs.map +1 -1
  57. package/dist/components/ui/form/DateRangePicker.mjs +5 -2
  58. package/dist/components/ui/form/DateRangePicker.mjs.map +1 -1
  59. package/dist/components/ui/form/PhoneInput.mjs +5 -3
  60. package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
  61. package/dist/components/ui/form/SelectField.mjs +6 -3
  62. package/dist/components/ui/form/SelectField.mjs.map +1 -1
  63. package/dist/components/widgets/ImageUpload/ImageCropModal.mjs +11 -18
  64. package/dist/components/widgets/ImageUpload/ImageCropModal.mjs.map +1 -1
  65. package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs +6 -15
  66. package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs.map +1 -1
  67. package/dist/i18n/messages/en-us.mjs +231 -0
  68. package/dist/i18n/messages/en-us.mjs.map +1 -1
  69. package/dist/i18n/messages/es-es.mjs +231 -0
  70. package/dist/i18n/messages/es-es.mjs.map +1 -1
  71. package/dist/i18n/messages/pt-br.mjs +239 -0
  72. package/dist/i18n/messages/pt-br.mjs.map +1 -1
  73. package/dist/index.mjs +5 -1
  74. package/dist/index.mjs.map +1 -1
  75. package/dist/modules/cards/hooks/handle-delete-card.hook.mjs +2 -1
  76. package/dist/modules/cards/hooks/handle-delete-card.hook.mjs.map +1 -1
  77. package/dist/modules/plans/utils/map-api-plan-to-ui.mjs +7 -2
  78. package/dist/modules/plans/utils/map-api-plan-to-ui.mjs.map +1 -1
  79. package/package.json +1 -1
  80. package/src/components/layouts/NavBar.tsx +5 -3
  81. package/src/components/layouts/NotificationsPopover.tsx +5 -3
  82. package/src/components/layouts/ProfilePopover.tsx +6 -4
  83. package/src/components/layouts/UsersSelectorPopover.tsx +9 -7
  84. package/src/components/modals/BuyCreditsModal.tsx +18 -16
  85. package/src/components/modals/CreditsDisabledModal.tsx +6 -6
  86. package/src/components/modals/PaidPlanRequiredModal.tsx +5 -3
  87. package/src/components/modals/cards/AddCardModal.tsx +29 -27
  88. package/src/components/modals/cards/CannotDeleteCardModal.tsx +9 -8
  89. package/src/components/modals/cards/CardFormFields.tsx +25 -22
  90. package/src/components/modals/cards/DeleteCardModal.tsx +10 -8
  91. package/src/components/navigation/CancelledSubscriptionBanner.tsx +4 -2
  92. package/src/components/navigation/OverdueInvoiceBanner.tsx +4 -2
  93. package/src/components/navigation/PendingCancellationBanner.tsx +4 -2
  94. package/src/components/navigation/ProjectSelectorPopover.tsx +8 -6
  95. package/src/components/navigation/UpcomingInvoiceBanner.tsx +4 -2
  96. package/src/components/navigation/subcomponents/BottomLinks.tsx +5 -3
  97. package/src/components/navigation/subcomponents/CreditsCard.tsx +3 -1
  98. package/src/components/navigation/subcomponents/PlanCard.tsx +3 -1
  99. package/src/components/navigation/subcomponents/ProjectSelector.tsx +5 -3
  100. package/src/components/pages/NotFoundPage.tsx +5 -3
  101. package/src/components/pages/notifications/Notifications.tsx +13 -10
  102. package/src/components/ui/buttons/CopyButton.tsx +10 -5
  103. package/src/components/ui/data-display/CardItem.tsx +7 -5
  104. package/src/components/ui/data-display/PaymentInfoCard.tsx +10 -8
  105. package/src/components/ui/feedback/DefaultCircularProgress.tsx +3 -1
  106. package/src/components/ui/form/ComboboxField.tsx +11 -6
  107. package/src/components/ui/form/DatePicker.tsx +5 -2
  108. package/src/components/ui/form/DateRangePicker.tsx +5 -2
  109. package/src/components/ui/form/PhoneInput.tsx +5 -3
  110. package/src/components/ui/form/SelectField.tsx +6 -3
  111. package/src/components/widgets/ImageUpload/ImageCropModal.tsx +19 -11
  112. package/src/components/widgets/ImageUpload/ImageTooSmallModal.tsx +6 -5
  113. package/src/i18n/messages/en-us.ts +234 -0
  114. package/src/i18n/messages/es-es.ts +234 -0
  115. package/src/i18n/messages/pt-br.ts +248 -0
  116. package/src/index.ts +4 -1
  117. package/src/modules/cards/hooks/handle-delete-card.hook.ts +8 -3
  118. package/src/modules/plans/utils/map-api-plan-to-ui.ts +19 -1
@@ -3,6 +3,7 @@
3
3
  import { useState, useEffect, useCallback } from 'react';
4
4
  import { IconAlertTriangle, IconX } from '@tabler/icons-react';
5
5
  import { formatShortDate } from '../../infra/utils/date';
6
+ import { useTranslations } from 'next-intl';
6
7
 
7
8
  interface PendingCancellationBannerProps {
8
9
  limitDate?: Date | null;
@@ -13,6 +14,7 @@ export function PendingCancellationBanner({
13
14
  limitDate,
14
15
  onReactivate,
15
16
  }: PendingCancellationBannerProps) {
17
+ const translate = useTranslations();
16
18
  const [visible, setVisible] = useState(true);
17
19
  const [animated, setAnimated] = useState(false);
18
20
 
@@ -35,7 +37,7 @@ export function PendingCancellationBanner({
35
37
  <div className="flex items-center gap-2 flex-1 w-full sm:w-auto">
36
38
  <IconAlertTriangle size={16} className="text-zinc-950 shrink-0" />
37
39
  <span className="paragraph-xsmall-semibold text-zinc-950">
38
- Sua assinatura foi cancelada, você possui até {formattedLimitDate} com suas páginas online.
40
+ {translate('common.navigation.banners.pending.message', { limitDate: formattedLimitDate ?? '' })}
39
41
  </span>
40
42
  </div>
41
43
  <div className="flex items-center gap-1.5 w-full sm:w-auto justify-between sm:justify-start">
@@ -45,7 +47,7 @@ export function PendingCancellationBanner({
45
47
  onClick={onReactivate}
46
48
  >
47
49
  <span className="paragraph-xsmall-semibold text-zinc-950 underline">
48
- Reativar assinatura
50
+ {translate('common.navigation.banners.pending.reactivate')}
49
51
  </span>
50
52
  </button>
51
53
  <button type="button" className="cursor-pointer shrink-0" onClick={dismiss}>
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { Dispatch, RefObject, SetStateAction, UIEvent, useCallback } from 'react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import {
5
6
  Button,
6
7
  Command,
@@ -61,6 +62,7 @@ export function ProjectSelectorPopover({
61
62
  onSearchChange,
62
63
  children,
63
64
  }: ProjectSelectorPopoverProps) {
65
+ const translate = useTranslations();
64
66
  const handleListScroll = useCallback((event: UIEvent<HTMLDivElement>) => {
65
67
  if (!hasNextPage || isFetchingNextPage || !onLoadMore) return;
66
68
 
@@ -96,10 +98,10 @@ export function ProjectSelectorPopover({
96
98
  >
97
99
  <div className="flex flex-col p-3 gap-2">
98
100
  <div className="flex items-center justify-between">
99
- <span className="paragraph-small-semibold">Projetos</span>
101
+ <span className="paragraph-small-semibold">{translate('common.navigation.projectSelector.title')}</span>
100
102
  </div>
101
103
  <Command className="gap-2" defaultValue=" " shouldFilter={!onSearchChange} value=" " onValueChange={() => {}}>
102
- <CommandInput placeholder="Busque aqui" value={search} onValueChange={onSearchChange} />
104
+ <CommandInput placeholder={translate('common.navigation.projectSelector.searchPlaceholder')} value={search} onValueChange={onSearchChange} />
103
105
  <CommandList
104
106
  className="custom-scrollbar paragraph-small-medium text-gray-600 max-h-[227px]"
105
107
  onScroll={handleListScroll}
@@ -115,14 +117,14 @@ export function ProjectSelectorPopover({
115
117
  <div className="flex flex-col items-center justify-center gap-2 h-full text-gray-400 py-4">
116
118
  <IconFolderOpen size={28} className="text-gray-300" />
117
119
  <span className="paragraph-small-medium text-center text-gray-500">
118
- Nenhum projeto encontrado.
120
+ {translate('common.navigation.projectSelector.emptyLine1')}
119
121
  <br />
120
- Crie um projeto para começar.
122
+ {translate('common.navigation.projectSelector.emptyLine2')}
121
123
  </span>
122
124
  </div>
123
125
  ) : (
124
126
  <>
125
- <CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
127
+ <CommandEmpty>{translate('common.navigation.projectSelector.noResults')}</CommandEmpty>
126
128
  {projects.map((project) => {
127
129
  const isSelected = selectedProject?.id === project.id;
128
130
  return (
@@ -167,7 +169,7 @@ export function ProjectSelectorPopover({
167
169
  className="h-10! paragraph-small-medium text-gray-600 hover:text-gray-950 hover:bg-zinc-50 w-full justify-center"
168
170
  onClick={onManageProjects}
169
171
  >
170
- Gerenciar projetos
172
+ {translate('common.navigation.projectSelector.manageProjects')}
171
173
  </Button>
172
174
  </>
173
175
  )}
@@ -3,6 +3,7 @@
3
3
  import { useState, useEffect, useCallback } from 'react';
4
4
  import { IconAlertTriangle, IconX } from '@tabler/icons-react';
5
5
  import { formatShortDate } from '../../infra/utils/date';
6
+ import { useTranslations } from 'next-intl';
6
7
 
7
8
  interface UpcomingInvoiceBannerProps {
8
9
  dueDate?: Date | null;
@@ -10,6 +11,7 @@ interface UpcomingInvoiceBannerProps {
10
11
  }
11
12
 
12
13
  export function UpcomingInvoiceBanner({ dueDate, onDetails }: UpcomingInvoiceBannerProps) {
14
+ const translate = useTranslations();
13
15
  const [visible, setVisible] = useState(true);
14
16
  const [animated, setAnimated] = useState(false);
15
17
 
@@ -32,7 +34,7 @@ export function UpcomingInvoiceBanner({ dueDate, onDetails }: UpcomingInvoiceBan
32
34
  <div className="flex items-center gap-2 flex-1 w-full sm:w-auto">
33
35
  <IconAlertTriangle size={16} className="text-zinc-950 shrink-0" />
34
36
  <span className="paragraph-xsmall-semibold text-zinc-950">
35
- Fatura em aberto. Pague até {formattedDueDate} para evitar a suspensão das suas páginas.
37
+ {translate('common.navigation.banners.upcoming.message', { dueDate: formattedDueDate ?? '' })}
36
38
  </span>
37
39
  </div>
38
40
  <div className="flex items-center gap-1.5 w-full sm:w-auto justify-between sm:justify-start">
@@ -41,7 +43,7 @@ export function UpcomingInvoiceBanner({ dueDate, onDetails }: UpcomingInvoiceBan
41
43
  className="flex items-center justify-center p-1.5 cursor-pointer"
42
44
  onClick={onDetails}
43
45
  >
44
- <span className="paragraph-xsmall-semibold text-zinc-950 underline">Detalhes</span>
46
+ <span className="paragraph-xsmall-semibold text-zinc-950 underline">{translate('common.navigation.banners.upcoming.details')}</span>
45
47
  </button>
46
48
  <button type="button" className="cursor-pointer shrink-0" onClick={dismiss}>
47
49
  <IconX size={18} className="text-zinc-700" />
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { IconHelp, IconBulb, IconStar, type TablerIcon } from '@tabler/icons-react';
4
4
  import { useIsDefaultWhitelabel, useWhitelabel } from '../../../providers/whitelabel.provider';
5
+ import { useTranslations } from 'next-intl';
5
6
 
6
7
  interface BottomLink {
7
8
  label: string;
@@ -17,6 +18,7 @@ function openFrillWidget() {
17
18
  }
18
19
 
19
20
  export function BottomLinks() {
21
+ const translate = useTranslations();
20
22
  const { whitelabel } = useWhitelabel();
21
23
  const isMainWhitelabel = useIsDefaultWhitelabel();
22
24
 
@@ -26,7 +28,7 @@ export function BottomLinks() {
26
28
  ...(isMainWhitelabel
27
29
  ? [
28
30
  {
29
- label: 'Novidades',
31
+ label: translate('common.navigation.bottomLinks.news'),
30
32
  icon: IconStar,
31
33
  className: 'novidades',
32
34
  onClick: openFrillWidget,
@@ -34,14 +36,14 @@ export function BottomLinks() {
34
36
  ]
35
37
  : []),
36
38
  {
37
- label: 'Central de ajuda',
39
+ label: translate('common.navigation.bottomLinks.helpCenter'),
38
40
  icon: IconHelp,
39
41
  href: helpUrl,
40
42
  },
41
43
  ...(isMainWhitelabel
42
44
  ? [
43
45
  {
44
- label: 'Enviar sugestões',
46
+ label: translate('common.navigation.bottomLinks.sendSuggestions'),
45
47
  icon: IconBulb,
46
48
  href: 'https://ideias.greatpages.com.br/b/y0geg1ve/feature-ideas',
47
49
  },
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useAppNavigationContext } from '../AppNavigationContext';
4
4
  import { Progress } from '../../../index';
5
+ import { useTranslations } from 'next-intl';
5
6
  import { useBuyCreditsModal } from '../../../store/useBuyCreditsModal';
6
7
  import { useCreditsDisabledModal } from '../../../store/useCreditsDisabledModal';
7
8
  import { useAuth } from '../../../providers/auth.provider';
@@ -13,6 +14,7 @@ export interface CreditsCardProps {
13
14
  }
14
15
 
15
16
  export function CreditsCard({ used, total, onClick }: CreditsCardProps) {
17
+ const translate = useTranslations();
16
18
  const { breakpoint } = useAppNavigationContext();
17
19
  const { openModal } = useBuyCreditsModal();
18
20
  const { openModal: openDisabledModal } = useCreditsDisabledModal();
@@ -35,7 +37,7 @@ export function CreditsCard({ used, total, onClick }: CreditsCardProps) {
35
37
  onClick={handleClick}
36
38
  >
37
39
  <div className="flex items-center justify-between">
38
- <span className="paragraph-xsmall-semibold text-white">Créditos usados</span>
40
+ <span className="paragraph-xsmall-semibold text-white">{translate('common.navigation.creditsCard.label')}</span>
39
41
  <span className="paragraph-xsmall-medium text-white/50">{used} / {total}</span>
40
42
  </div>
41
43
  <Progress className="bg-white/10 h-[6px]" indicatorColor="bg-white" value={Math.round((used / total) * 100)} />
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { Button, Progress } from '../../../index';
4
+ import { useTranslations } from 'next-intl';
4
5
 
5
6
  export interface PlanCardProps {
6
7
  planName: string;
@@ -10,6 +11,7 @@ export interface PlanCardProps {
10
11
  }
11
12
 
12
13
  export function PlanCard({ planName, trialLabel, progressValue, onKnowPlans }: PlanCardProps) {
14
+ const translate = useTranslations();
13
15
  return (
14
16
  <div className="flex flex-col rounded-md p-4 gap-4 bg-gray-50">
15
17
  <div className="flex flex-col gap-1">
@@ -22,7 +24,7 @@ export function PlanCard({ planName, trialLabel, progressValue, onKnowPlans }: P
22
24
  className="paragraph-small-semibold text-gray-950"
23
25
  onClick={onKnowPlans}
24
26
  >
25
- Conheça os planos
27
+ {translate('common.navigation.planCard.knowPlans')}
26
28
  </Button>
27
29
  </div>
28
30
  );
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useRef, useEffect, useMemo, RefObject } from 'react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import { useMobileNavbarSheet } from '../../../store/useMobileNavbarSheet';
5
6
  import { useAppNavigationContext } from '../AppNavigationContext';
6
7
  import { ProjectSelectorPopover } from '../ProjectSelectorPopover';
@@ -30,6 +31,7 @@ export function ProjectSelector({
30
31
  onSelectProject,
31
32
  onManageProjects,
32
33
  }: ProjectSelectorProps) {
34
+ const translate = useTranslations();
33
35
  const isMainWhitelabel = useIsDefaultWhitelabel();
34
36
  const { breakpoint } = useAppNavigationContext();
35
37
  const [isPopoverOpen, setIsPopoverOpen] = useState(false);
@@ -124,10 +126,10 @@ export function ProjectSelector({
124
126
  {activeSection === 'projects' && (
125
127
  <div className="flex flex-col p-3 gap-2 border border-gray-200 rounded-lg animate-in fade-in duration-400">
126
128
  <div className="flex items-center justify-between">
127
- <span className="paragraph-small-semibold">Projetos</span>
129
+ <span className="paragraph-small-semibold">{translate('common.navigation.projectSelector.title')}</span>
128
130
  </div>
129
131
  <Command className="gap-2" shouldFilter={false}>
130
- <CommandInput placeholder="Busque aqui" value={search} onValueChange={setSearch} />
132
+ <CommandInput placeholder={translate('common.navigation.projectSelector.searchPlaceholder')} value={search} onValueChange={setSearch} />
131
133
  <CommandList
132
134
  className="custom-scrollbar paragraph-small-medium text-gray-600 h-[227px]"
133
135
  onScroll={(event) => {
@@ -141,7 +143,7 @@ export function ProjectSelector({
141
143
  }
142
144
  }}
143
145
  >
144
- <CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
146
+ <CommandEmpty>{translate('common.navigation.projectSelector.noResults')}</CommandEmpty>
145
147
  {showListSkeleton ? (
146
148
  <>
147
149
  <ProjectItemSkeleton />
@@ -2,6 +2,7 @@
2
2
 
3
3
  import Image from "next/image";
4
4
  import { useEffect } from "react";
5
+ import { useTranslations } from "next-intl";
5
6
  import { useRouter } from "next/navigation";
6
7
  import { useWhitelabel, useIsDefaultWhitelabel } from "../../providers/whitelabel.provider";
7
8
  import { Button } from "../ui/buttons/Button";
@@ -18,6 +19,7 @@ function GreatAppsLogo() {
18
19
  }
19
20
 
20
21
  export function NotFoundPage() {
22
+ const translate = useTranslations();
21
23
  const router = useRouter();
22
24
 
23
25
  useEffect(() => {
@@ -47,11 +49,11 @@ export function NotFoundPage() {
47
49
  </div>
48
50
 
49
51
  <h1 className="max-w-[554px] px-6 font-[family-name:var(--font-outfit)] text-[32px] leading-[36px] font-semibold text-zinc-950 sm:px-0 sm:text-[56px] sm:leading-[56px]">
50
- Página não foi encontrada!
52
+ {translate('common.notFound.title')}
51
53
  </h1>
52
54
 
53
55
  <p className="max-w-[496px] px-6 paragraph-medium-medium text-zinc-950 sm:px-0">
54
- A página que você está procurando não existe. Verifique se digitou corretamente o link que está procurando.
56
+ {translate('common.notFound.description')}
55
57
  </p>
56
58
 
57
59
  <Button
@@ -65,7 +67,7 @@ export function NotFoundPage() {
65
67
  }
66
68
  }}
67
69
  >
68
- Voltar para o início
70
+ {translate('common.notFound.backToHome')}
69
71
  </Button>
70
72
  </div>
71
73
  </div>
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { IconBell, IconCheck, IconLoader2 } from '@tabler/icons-react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import { Button } from "../../ui/buttons/Button";
5
6
  import {
6
7
  Tabs,
@@ -34,6 +35,7 @@ function NotificationsList({
34
35
  hasNextPage: boolean;
35
36
  fetchNextPage: () => void;
36
37
  }) {
38
+ const translate = useTranslations();
37
39
  const notifications = pages?.pages.flatMap((p) => p.data) ?? [];
38
40
 
39
41
  const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
@@ -50,7 +52,7 @@ function NotificationsList({
50
52
  return (
51
53
  <div className="rounded-lg border border-zinc-200 bg-white">
52
54
  <div className="flex flex-col items-center justify-center py-16 gap-2">
53
- <span className="paragraph-medium-regular text-zinc-500">Nenhuma notificação encontrada.</span>
55
+ <span className="paragraph-medium-regular text-zinc-500">{translate('common.notifications.page.empty')}</span>
54
56
  </div>
55
57
  </div>
56
58
  );
@@ -80,7 +82,7 @@ function NotificationsList({
80
82
  actions={
81
83
  detailsUrl ? (
82
84
  <Button asChild variant="secondary" className="h-8! px-3! paragraph-small-semibold">
83
- <Link href={detailsUrl}>Detalhes</Link>
85
+ <Link href={detailsUrl}>{translate('common.notifications.page.details')}</Link>
84
86
  </Button>
85
87
  ) : undefined
86
88
  }
@@ -97,6 +99,7 @@ function NotificationsList({
97
99
  }
98
100
 
99
101
  export function NotificationPageContent() {
102
+ const translate = useTranslations();
100
103
  const unreadNotifications = useInfiniteMessages({
101
104
  readed: false,
102
105
  type: "notification",
@@ -116,12 +119,12 @@ export function NotificationPageContent() {
116
119
  markAllAsRead(undefined, {
117
120
  onSuccess: () => {
118
121
  toast.custom((t) => (
119
- <Toast variant="success" message="Todas as notificações foram marcadas como lidas" toastId={t} />
122
+ <Toast variant="success" message={translate('common.notifications.page.markAllSuccess')} toastId={t} />
120
123
  ));
121
124
  },
122
125
  onError: () => {
123
126
  toast.custom((t) => (
124
- <Toast variant="error" message="Erro ao marcar notificações como lidas" toastId={t} />
127
+ <Toast variant="error" message={translate('common.notifications.page.markAllError')} toastId={t} />
125
128
  ));
126
129
  },
127
130
  });
@@ -132,10 +135,10 @@ export function NotificationPageContent() {
132
135
  <div className="flex items-center justify-between gap-4">
133
136
  <div className="flex flex-col gap-2">
134
137
  <h1 className="title-large-semibold text-gray-950">
135
- Notificações
138
+ {translate('common.notifications.page.title')}
136
139
  </h1>
137
140
  <p className="paragraph-small-regular text-gray-600 hidden lg:block">
138
- Tenha controle total sobre páginas, experiências e conversões.
141
+ {translate('common.notifications.page.subtitle')}
139
142
  </p>
140
143
  </div>
141
144
  <Button
@@ -145,8 +148,8 @@ export function NotificationPageContent() {
145
148
  disabled={isMarkingAll || totalUnread === 0}
146
149
  >
147
150
  {isMarkingAll ? <IconLoader2 size={20} className="animate-spin" /> : <IconCheck size={20} />}
148
- <span className="lg:block hidden">Marcar todos como lidos</span>
149
- <span className="lg:hidden">Marcar como lidos</span>
151
+ <span className="lg:block hidden">{translate('common.notifications.page.markAllReadDesktop')}</span>
152
+ <span className="lg:hidden">{translate('common.notifications.page.markAllReadMobile')}</span>
150
153
  </Button>
151
154
  </div>
152
155
  <Tabs
@@ -155,11 +158,11 @@ export function NotificationPageContent() {
155
158
  >
156
159
  <TabsList className="h-auto pb-0 bg-transparent">
157
160
  <TabsTrigger className="gap-2" value="all">
158
- Todas as notificações{" "}
161
+ {translate('common.notifications.page.tabAll')}{" "}
159
162
  <span className="text-gray-500">({totalAll})</span>
160
163
  </TabsTrigger>
161
164
  <TabsTrigger className="gap-2" value="unreads" disabled={totalUnread === 0}>
162
- Não lidas{" "}
165
+ {translate('common.notifications.page.tabUnread')}{" "}
163
166
  <span className="text-gray-500">({totalUnread})</span>
164
167
  </TabsTrigger>
165
168
  </TabsList>
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { useTranslations } from 'next-intl';
3
4
  import { IconCheck, IconCopy } from '@tabler/icons-react';
4
5
  import { toast } from "sonner";
5
6
  import { Toast } from "../feedback/Toast";
@@ -19,18 +20,22 @@ export function CopyButton({
19
20
  textToCopy,
20
21
  size = 14,
21
22
  className,
22
- tooltipText = "Copiar link",
23
- successMessage = "Link copiado",
23
+ tooltipText,
24
+ successMessage,
24
25
  }: CopyButtonProps) {
26
+ const translate = useTranslations();
27
+ const resolvedTooltipText = tooltipText ?? translate('common.forms.copyButton.tooltipDefault');
28
+ const resolvedSuccessMessage = successMessage ?? translate('common.forms.copyButton.successDefault');
29
+
25
30
  const { isCopied, copy } = useCopyToClipboard({
26
31
  onSuccess: () => {
27
32
  toast.custom((t) => (
28
- <Toast variant="success" message={successMessage} toastId={t} />
33
+ <Toast variant="success" message={resolvedSuccessMessage} toastId={t} />
29
34
  ));
30
35
  },
31
36
  onError: () => {
32
37
  toast.custom((t) => (
33
- <Toast variant="error" message="Não foi possível copiar o link" toastId={t} />
38
+ <Toast variant="error" message={translate('common.forms.copyButton.errorMessage')} toastId={t} />
34
39
  ));
35
40
  },
36
41
  });
@@ -48,7 +53,7 @@ export function CopyButton({
48
53
  {isCopied ? <IconCheck size={size} /> : <IconCopy size={size} />}
49
54
  </span>
50
55
  </TooltipTrigger>
51
- <TooltipContent>{isCopied ? "Copiado!" : tooltipText}</TooltipContent>
56
+ <TooltipContent>{isCopied ? translate('common.actions.copied') : resolvedTooltipText}</TooltipContent>
52
57
  </Tooltip>
53
58
  );
54
59
  }
@@ -10,6 +10,7 @@ import { CardBrandIcon } from './CardBrandIcons';
10
10
  import { useSetDefaultCard } from '../../../modules/cards/hooks/set-default-card.hook';
11
11
  import { useHandleDeleteCard } from '../../../modules/cards/hooks/handle-delete-card.hook';
12
12
  import type { Card } from '../../../modules/cards/types';
13
+ import { useTranslations } from 'next-intl';
13
14
 
14
15
  type CardItemProps = {
15
16
  card: Card;
@@ -32,6 +33,7 @@ export function CardItem({
32
33
  onAfterDelete,
33
34
  onAfterSetDefault,
34
35
  }: CardItemProps) {
36
+ const translate = useTranslations();
35
37
  const [popoverOpen, setPopoverOpen] = useState(false);
36
38
 
37
39
  const setDefaultCardMutation = useSetDefaultCard();
@@ -44,7 +46,7 @@ export function CardItem({
44
46
  toast.custom((t) => (
45
47
  <Toast
46
48
  variant="success"
47
- message="Cartão definido como padrão"
49
+ message={translate('common.cards.item.setDefaultSuccess')}
48
50
  toastId={t}
49
51
  />
50
52
  ));
@@ -54,7 +56,7 @@ export function CardItem({
54
56
  toast.custom((t) => (
55
57
  <Toast
56
58
  variant="error"
57
- message="Erro ao definir cartão padrão"
59
+ message={translate('common.cards.item.setDefaultError')}
58
60
  toastId={t}
59
61
  />
60
62
  ));
@@ -86,7 +88,7 @@ export function CardItem({
86
88
  </div>
87
89
  {card.is_default && (
88
90
  <span className="paragraph-xsmall-semibold text-zinc-600 bg-zinc-100 px-2 py-0.5 rounded-lg whitespace-nowrap">
89
- Padrão
91
+ {translate('common.cards.item.defaultBadge')}
90
92
  </span>
91
93
  )}
92
94
  <CardBrandIcon brand={card.brand} />
@@ -108,7 +110,7 @@ export function CardItem({
108
110
  {menuHasSetDefault && (
109
111
  <NavBarItem
110
112
  icon={<IconStar size={18} className="text-zinc-400" />}
111
- label="Tornar padrão"
113
+ label={translate('common.cards.item.makeDefault')}
112
114
  onClick={() => {
113
115
  setPopoverOpen(false);
114
116
  handleSetDefault();
@@ -118,7 +120,7 @@ export function CardItem({
118
120
  {menuHasDelete && (
119
121
  <NavBarItem
120
122
  icon={<IconTrash size={18} className="text-zinc-400" />}
121
- label="Excluir"
123
+ label={translate('common.actions.delete')}
122
124
  onClick={() => {
123
125
  setPopoverOpen(false);
124
126
  handleDeleteCard(card);
@@ -9,6 +9,7 @@ import { useCards } from '../../../modules/cards/hooks/cards.hook';
9
9
  import { useModalManager } from '../../../store/useModalManager';
10
10
  import { useIsDefaultWhitelabel } from '../../../providers/whitelabel.provider';
11
11
  import type { Card } from '../../../modules/cards/types';
12
+ import { useTranslations } from 'next-intl';
12
13
 
13
14
  type PaymentInfoCardProps = {
14
15
  email?: string;
@@ -29,6 +30,7 @@ export function PaymentInfoCard({
29
30
  hideDelete,
30
31
  hideSetDefault,
31
32
  }: PaymentInfoCardProps) {
33
+ const translate = useTranslations();
32
34
  const isMobile = useIsMobile();
33
35
  const { openModal } = useModalManager();
34
36
  const isDefaultWl = useIsDefaultWhitelabel();
@@ -83,19 +85,19 @@ export function PaymentInfoCard({
83
85
  <div className="border border-zinc-200 rounded-lg overflow-hidden">
84
86
  {email && (
85
87
  <div className="flex items-center h-[60px] px-4 border-b border-zinc-200 gap-3">
86
- <span className="paragraph-small-medium text-zinc-500 w-[100px]">E-mail</span>
88
+ <span className="paragraph-small-medium text-zinc-500 w-[100px]">{translate('common.cards.paymentInfo.email')}</span>
87
89
  <span className="paragraph-small-medium text-zinc-950 flex-1">{email}</span>
88
90
  </div>
89
91
  )}
90
92
  <div className="flex items-center h-[60px] px-4 gap-3">
91
- <span className="paragraph-small-medium text-zinc-500 w-[100px]">Pagamento</span>
93
+ <span className="paragraph-small-medium text-zinc-500 w-[100px]">{translate('common.cards.paymentInfo.payment')}</span>
92
94
  <button
93
95
  type="button"
94
96
  onClick={handleOpenAddCardModal}
95
97
  className="flex items-center h-8 px-2 paragraph-small-semibold text-zinc-950 border border-zinc-200 rounded-md hover:border-zinc-400 transition-colors cursor-pointer w-fit"
96
98
  >
97
99
  <IconPlus className="size-[18px]" />
98
- <span className="px-1.5">Adicionar cartão</span>
100
+ <span className="px-1.5">{translate('common.cards.paymentInfo.addCard')}</span>
99
101
  </button>
100
102
  </div>
101
103
  </div>
@@ -107,7 +109,7 @@ export function PaymentInfoCard({
107
109
  <div className="border border-zinc-200 rounded-lg overflow-hidden">
108
110
  {email && (
109
111
  <div className="flex items-center h-[60px] px-4 border-b border-zinc-200 gap-3">
110
- <span className="paragraph-small-medium text-zinc-500 w-[100px]">E-mail</span>
112
+ <span className="paragraph-small-medium text-zinc-500 w-[100px]">{translate('common.cards.paymentInfo.email')}</span>
111
113
  <span className="paragraph-small-medium text-zinc-950 flex-1">{email}</span>
112
114
  </div>
113
115
  )}
@@ -118,7 +120,7 @@ export function PaymentInfoCard({
118
120
  >
119
121
  {!isExpanded ? (
120
122
  <>
121
- <span className="paragraph-small-medium text-zinc-500 w-[100px]">Pagamento</span>
123
+ <span className="paragraph-small-medium text-zinc-500 w-[100px]">{translate('common.cards.paymentInfo.payment')}</span>
122
124
  <div className="flex flex-col gap-0.5 flex-1 min-w-0">
123
125
  <span className="paragraph-small-semibold text-zinc-950 truncate">
124
126
  {selectedCard?.name}
@@ -135,7 +137,7 @@ export function PaymentInfoCard({
135
137
  ) : (
136
138
  <>
137
139
  <span className="paragraph-small-semibold text-zinc-950 flex-1">
138
- Selecione a forma de pagamento
140
+ {translate('common.cards.paymentInfo.selectPaymentMethod')}
139
141
  </span>
140
142
  <div className="size-8 flex items-center justify-center rounded-lg">
141
143
  <IconChevronDown className="size-[18px] text-zinc-500 rotate-180" />
@@ -170,7 +172,7 @@ export function PaymentInfoCard({
170
172
  : 'text-zinc-600 border border-zinc-200 hover:bg-zinc-50'
171
173
  }`}
172
174
  >
173
- Gerenciar cartões
175
+ {translate('common.cards.paymentInfo.manageCards')}
174
176
  </button>
175
177
  )}
176
178
  </div>
@@ -183,7 +185,7 @@ export function PaymentInfoCard({
183
185
  >
184
186
  <IconPlus className="size-[18px]" />
185
187
  <span className="px-1.5">
186
- {isExpanded ? 'Adicionar cartão' : 'Nova forma de pagamento'}
188
+ {isExpanded ? translate('common.cards.paymentInfo.addCard') : translate('common.cards.paymentInfo.newPaymentMethod')}
187
189
  </span>
188
190
  </button>
189
191
  </div>
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { useTranslations } from 'next-intl';
3
4
  import {
4
5
  calculateRadius,
5
6
  calculateCircumference,
@@ -21,6 +22,7 @@ export default function DefaultCircularProgress({
21
22
  size = 62,
22
23
  strokeWidth = 3,
23
24
  }: CircularProgressProps) {
25
+ const translate = useTranslations();
24
26
  const isDefaultWl = useIsDefaultWhitelabel();
25
27
  const radius = calculateRadius(size, strokeWidth);
26
28
  const circumference = calculateCircumference(radius);
@@ -35,7 +37,7 @@ export default function DefaultCircularProgress({
35
37
  aria-valuenow={current}
36
38
  aria-valuemin={0}
37
39
  aria-valuemax={total}
38
- aria-label="Progresso de exportação"
40
+ aria-label={translate('common.forms.circularProgress.ariaLabel')}
39
41
  >
40
42
  <svg width={size} height={size} className="absolute">
41
43
  <circle
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useMemo } from 'react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import { IconCheck, IconChevronDown, IconSearch } from '@tabler/icons-react';
5
6
  import { Popover, PopoverContent, PopoverTrigger } from '../overlay/Popover';
6
7
  import { cn } from '../../../infra/utils/clsx';
@@ -28,9 +29,9 @@ interface ComboboxFieldProps {
28
29
  export function ComboboxField({
29
30
  options,
30
31
  label,
31
- placeholder = 'Selecione uma opção',
32
- searchPlaceholder = 'Buscar...',
33
- emptyMessage = 'Nenhuma opção encontrada.',
32
+ placeholder,
33
+ searchPlaceholder,
34
+ emptyMessage,
34
35
  value,
35
36
  onChange,
36
37
  disabled = false,
@@ -38,6 +39,10 @@ export function ComboboxField({
38
39
  className,
39
40
  containerClassName,
40
41
  }: ComboboxFieldProps) {
42
+ const translate = useTranslations();
43
+ const resolvedPlaceholder = placeholder ?? translate('common.forms.combobox.placeholder');
44
+ const resolvedSearchPlaceholder = searchPlaceholder ?? translate('common.forms.combobox.searchPlaceholder');
45
+ const resolvedEmptyMessage = emptyMessage ?? translate('common.forms.combobox.emptyMessage');
41
46
  const [open, setOpen] = useState(false);
42
47
  const [search, setSearch] = useState('');
43
48
 
@@ -90,7 +95,7 @@ export function ComboboxField({
90
95
  <span className={cn('truncate text-left', !selectedOption && 'text-gray-400')}>
91
96
  {selectedOption
92
97
  ? (selectedOption.displayValue ?? selectedOption.label)
93
- : placeholder}
98
+ : resolvedPlaceholder}
94
99
  </span>
95
100
  {icon ?? (
96
101
  <IconChevronDown
@@ -114,7 +119,7 @@ export function ComboboxField({
114
119
  <IconSearch className="size-4 shrink-0 opacity-50 text-gray-500" />
115
120
  <input
116
121
  autoFocus
117
- placeholder={searchPlaceholder}
122
+ placeholder={resolvedSearchPlaceholder}
118
123
  value={search}
119
124
  onChange={(e) => setSearch(e.target.value)}
120
125
  className="flex h-full w-full bg-transparent text-sm outline-none placeholder:text-gray-400 paragraph-small-medium text-gray-950"
@@ -123,7 +128,7 @@ export function ComboboxField({
123
128
 
124
129
  <div className="overflow-y-auto max-h-[280px] p-1">
125
130
  {filteredOptions.length === 0 ? (
126
- <p className="py-6 text-center paragraph-small-medium text-gray-500">{emptyMessage}</p>
131
+ <p className="py-6 text-center paragraph-small-medium text-gray-500">{resolvedEmptyMessage}</p>
127
132
  ) : (
128
133
  filteredOptions.map((option) => {
129
134
  const isSelected = value === option.value;