@greatapps/common 1.1.675 → 1.1.677

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 (240) hide show
  1. package/dist/components/account/ConfigurationsMyAccountModal.mjs +8 -6
  2. package/dist/components/account/ConfigurationsMyAccountModal.mjs.map +1 -1
  3. package/dist/components/account/ConfirmDeleteAccountModal.mjs +20 -19
  4. package/dist/components/account/ConfirmDeleteAccountModal.mjs.map +1 -1
  5. package/dist/components/account/ConfirmGlobalPreferencesModal.mjs +10 -11
  6. package/dist/components/account/ConfirmGlobalPreferencesModal.mjs.map +1 -1
  7. package/dist/components/account/DeleteAccountModal.mjs +34 -28
  8. package/dist/components/account/DeleteAccountModal.mjs.map +1 -1
  9. package/dist/components/account/DisableTwoFactorAuthModal.mjs +7 -5
  10. package/dist/components/account/DisableTwoFactorAuthModal.mjs.map +1 -1
  11. package/dist/components/account/IsntPossibleDeleteAccountModal.mjs +8 -9
  12. package/dist/components/account/IsntPossibleDeleteAccountModal.mjs.map +1 -1
  13. package/dist/components/account/TwoFactorAuthModal.mjs +22 -23
  14. package/dist/components/account/TwoFactorAuthModal.mjs.map +1 -1
  15. package/dist/components/account/constants.mjs +4 -4
  16. package/dist/components/account/constants.mjs.map +1 -1
  17. package/dist/components/account/hooks/useChangeEmailForm.mjs +5 -3
  18. package/dist/components/account/hooks/useChangeEmailForm.mjs.map +1 -1
  19. package/dist/components/account/hooks/useChangePasswordForm.mjs +26 -3
  20. package/dist/components/account/hooks/useChangePasswordForm.mjs.map +1 -1
  21. package/dist/components/account/hooks/useChangePhoneForm.mjs +18 -9
  22. package/dist/components/account/hooks/useChangePhoneForm.mjs.map +1 -1
  23. package/dist/components/account/hooks/useDisableTwoFactorAuthForm.mjs +26 -3
  24. package/dist/components/account/hooks/useDisableTwoFactorAuthForm.mjs.map +1 -1
  25. package/dist/components/account/hooks/useOtpVerification.mjs +19 -3
  26. package/dist/components/account/hooks/useOtpVerification.mjs.map +1 -1
  27. package/dist/components/account/hooks/useTwoFactorAuthForm.mjs +27 -4
  28. package/dist/components/account/hooks/useTwoFactorAuthForm.mjs.map +1 -1
  29. package/dist/components/account/sections/ChangeEmailModal.mjs +25 -28
  30. package/dist/components/account/sections/ChangeEmailModal.mjs.map +1 -1
  31. package/dist/components/account/sections/ChangePasswordSection.mjs +8 -6
  32. package/dist/components/account/sections/ChangePasswordSection.mjs.map +1 -1
  33. package/dist/components/account/sections/ChangePhoneModal.mjs +24 -27
  34. package/dist/components/account/sections/ChangePhoneModal.mjs.map +1 -1
  35. package/dist/components/account/sections/MyProfileSection.mjs +37 -22
  36. package/dist/components/account/sections/MyProfileSection.mjs.map +1 -1
  37. package/dist/components/account/sections/PreferencesSection.mjs +48 -24
  38. package/dist/components/account/sections/PreferencesSection.mjs.map +1 -1
  39. package/dist/components/account/sections/SecuritySection.mjs +11 -9
  40. package/dist/components/account/sections/SecuritySection.mjs.map +1 -1
  41. package/dist/components/account/sections/TokenSection.mjs +24 -8
  42. package/dist/components/account/sections/TokenSection.mjs.map +1 -1
  43. package/dist/components/layouts/AppMobileNavBar.mjs +2 -148
  44. package/dist/components/layouts/AppMobileNavBar.mjs.map +1 -1
  45. package/dist/components/layouts/NavBar.mjs +5 -3
  46. package/dist/components/layouts/NavBar.mjs.map +1 -1
  47. package/dist/components/layouts/NotificationsPopover.mjs +5 -3
  48. package/dist/components/layouts/NotificationsPopover.mjs.map +1 -1
  49. package/dist/components/layouts/ProfilePopover.mjs +6 -4
  50. package/dist/components/layouts/ProfilePopover.mjs.map +1 -1
  51. package/dist/components/layouts/UsersSelectorPopover.mjs +9 -7
  52. package/dist/components/layouts/UsersSelectorPopover.mjs.map +1 -1
  53. package/dist/components/modals/BuyCreditsModal.mjs +17 -18
  54. package/dist/components/modals/BuyCreditsModal.mjs.map +1 -1
  55. package/dist/components/modals/CreditsDisabledModal.mjs +6 -4
  56. package/dist/components/modals/CreditsDisabledModal.mjs.map +1 -1
  57. package/dist/components/modals/PaidPlanRequiredModal.mjs +5 -3
  58. package/dist/components/modals/PaidPlanRequiredModal.mjs.map +1 -1
  59. package/dist/components/modals/cards/AddCardModal.mjs +64 -46
  60. package/dist/components/modals/cards/AddCardModal.mjs.map +1 -1
  61. package/dist/components/modals/cards/CannotDeleteCardModal.mjs +9 -7
  62. package/dist/components/modals/cards/CannotDeleteCardModal.mjs.map +1 -1
  63. package/dist/components/modals/cards/CardFormFields.mjs +65 -51
  64. package/dist/components/modals/cards/CardFormFields.mjs.map +1 -1
  65. package/dist/components/modals/cards/DeleteCardModal.mjs +10 -8
  66. package/dist/components/modals/cards/DeleteCardModal.mjs.map +1 -1
  67. package/dist/components/navigation/CancelledSubscriptionBanner.mjs +4 -2
  68. package/dist/components/navigation/CancelledSubscriptionBanner.mjs.map +1 -1
  69. package/dist/components/navigation/OverdueInvoiceBanner.mjs +4 -2
  70. package/dist/components/navigation/OverdueInvoiceBanner.mjs.map +1 -1
  71. package/dist/components/navigation/PendingCancellationBanner.mjs +4 -6
  72. package/dist/components/navigation/PendingCancellationBanner.mjs.map +1 -1
  73. package/dist/components/navigation/ProjectSelectorPopover.mjs +8 -6
  74. package/dist/components/navigation/ProjectSelectorPopover.mjs.map +1 -1
  75. package/dist/components/navigation/UpcomingInvoiceBanner.mjs +4 -6
  76. package/dist/components/navigation/UpcomingInvoiceBanner.mjs.map +1 -1
  77. package/dist/components/navigation/subcomponents/BottomLinks.mjs +5 -3
  78. package/dist/components/navigation/subcomponents/BottomLinks.mjs.map +1 -1
  79. package/dist/components/navigation/subcomponents/CreditsCard.mjs +3 -1
  80. package/dist/components/navigation/subcomponents/CreditsCard.mjs.map +1 -1
  81. package/dist/components/navigation/subcomponents/PlanCard.mjs +3 -1
  82. package/dist/components/navigation/subcomponents/PlanCard.mjs.map +1 -1
  83. package/dist/components/navigation/subcomponents/ProjectSelector.mjs +5 -3
  84. package/dist/components/navigation/subcomponents/ProjectSelector.mjs.map +1 -1
  85. package/dist/components/pages/NotFoundPage.mjs +5 -3
  86. package/dist/components/pages/NotFoundPage.mjs.map +1 -1
  87. package/dist/components/pages/notifications/Notifications.mjs +13 -10
  88. package/dist/components/pages/notifications/Notifications.mjs.map +1 -1
  89. package/dist/components/ui/buttons/CopyButton.mjs +9 -5
  90. package/dist/components/ui/buttons/CopyButton.mjs.map +1 -1
  91. package/dist/components/ui/data-display/CardItem.mjs +7 -5
  92. package/dist/components/ui/data-display/CardItem.mjs.map +1 -1
  93. package/dist/components/ui/data-display/PaymentInfoCard.mjs +10 -8
  94. package/dist/components/ui/data-display/PaymentInfoCard.mjs.map +1 -1
  95. package/dist/components/ui/feedback/CircularProgress.mjs.map +1 -1
  96. package/dist/components/ui/feedback/DefaultCircularProgress.mjs +3 -1
  97. package/dist/components/ui/feedback/DefaultCircularProgress.mjs.map +1 -1
  98. package/dist/components/ui/form/ComboboxField.mjs +11 -6
  99. package/dist/components/ui/form/ComboboxField.mjs.map +1 -1
  100. package/dist/components/ui/form/DatePicker.mjs +5 -2
  101. package/dist/components/ui/form/DatePicker.mjs.map +1 -1
  102. package/dist/components/ui/form/DateRangePicker.mjs +5 -2
  103. package/dist/components/ui/form/DateRangePicker.mjs.map +1 -1
  104. package/dist/components/ui/form/PhoneInput.mjs +5 -3
  105. package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
  106. package/dist/components/ui/form/SelectField.mjs +6 -3
  107. package/dist/components/ui/form/SelectField.mjs.map +1 -1
  108. package/dist/components/widgets/ImageUpload/ImageCropModal.mjs +11 -18
  109. package/dist/components/widgets/ImageUpload/ImageCropModal.mjs.map +1 -1
  110. package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs +6 -15
  111. package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs.map +1 -1
  112. package/dist/i18n/config.mjs +13 -0
  113. package/dist/i18n/config.mjs.map +1 -0
  114. package/dist/i18n/country-language.mjs +74 -0
  115. package/dist/i18n/country-language.mjs.map +1 -0
  116. package/dist/i18n/messages/en-us.mjs +494 -0
  117. package/dist/i18n/messages/en-us.mjs.map +1 -0
  118. package/dist/i18n/messages/es-es.mjs +494 -0
  119. package/dist/i18n/messages/es-es.mjs.map +1 -0
  120. package/dist/i18n/messages/index.mjs +16 -0
  121. package/dist/i18n/messages/index.mjs.map +1 -0
  122. package/dist/i18n/messages/pt-br.mjs +507 -0
  123. package/dist/i18n/messages/pt-br.mjs.map +1 -0
  124. package/dist/i18n/normalize.mjs +12 -0
  125. package/dist/i18n/normalize.mjs.map +1 -0
  126. package/dist/i18n/resolve-locale.mjs +42 -0
  127. package/dist/i18n/resolve-locale.mjs.map +1 -0
  128. package/dist/i18n.mjs +14 -0
  129. package/dist/i18n.mjs.map +1 -0
  130. package/dist/index.mjs +19 -3
  131. package/dist/index.mjs.map +1 -1
  132. package/dist/modules/accounts/hooks/use-currency-formatter.hook.mjs +20 -0
  133. package/dist/modules/accounts/hooks/use-currency-formatter.hook.mjs.map +1 -0
  134. package/dist/modules/cards/hooks/handle-delete-card.hook.mjs +2 -1
  135. package/dist/modules/cards/hooks/handle-delete-card.hook.mjs.map +1 -1
  136. package/dist/modules/images/utils/validate-image.mjs +8 -8
  137. package/dist/modules/images/utils/validate-image.mjs.map +1 -1
  138. package/dist/modules/plans/hooks/list-plans.hook.mjs +2 -2
  139. package/dist/modules/plans/hooks/list-plans.hook.mjs.map +1 -1
  140. package/dist/modules/plans/types/plan.type.mjs +5 -1
  141. package/dist/modules/plans/types/plan.type.mjs.map +1 -1
  142. package/dist/modules/plans/utils/map-api-plan-to-ui.mjs +61 -35
  143. package/dist/modules/plans/utils/map-api-plan-to-ui.mjs.map +1 -1
  144. package/dist/modules/projects/services/projects.service.mjs +9 -0
  145. package/dist/modules/projects/services/projects.service.mjs.map +1 -1
  146. package/dist/modules/subscriptions/utils/periodicity.mjs +20 -14
  147. package/dist/modules/subscriptions/utils/periodicity.mjs.map +1 -1
  148. package/dist/providers/auth.provider.mjs +9 -1
  149. package/dist/providers/auth.provider.mjs.map +1 -1
  150. package/dist/server.mjs +2 -0
  151. package/dist/server.mjs.map +1 -1
  152. package/dist/utils/format/currency.mjs +32 -4
  153. package/dist/utils/format/currency.mjs.map +1 -1
  154. package/dist/utils/intl/cookie-domain.mjs +17 -0
  155. package/dist/utils/intl/cookie-domain.mjs.map +1 -0
  156. package/dist/utils/intl/locale-cookie.mjs +13 -0
  157. package/dist/utils/intl/locale-cookie.mjs.map +1 -0
  158. package/dist/utils/intl/locales.mjs +3 -2
  159. package/dist/utils/intl/locales.mjs.map +1 -1
  160. package/package.json +7 -1
  161. package/src/components/account/ConfigurationsMyAccountModal.tsx +10 -6
  162. package/src/components/account/ConfirmDeleteAccountModal.tsx +22 -13
  163. package/src/components/account/ConfirmGlobalPreferencesModal.tsx +12 -10
  164. package/src/components/account/DeleteAccountModal.tsx +34 -24
  165. package/src/components/account/DisableTwoFactorAuthModal.tsx +11 -6
  166. package/src/components/account/IsntPossibleDeleteAccountModal.tsx +8 -6
  167. package/src/components/account/TwoFactorAuthModal.tsx +33 -29
  168. package/src/components/account/constants.ts +8 -5
  169. package/src/components/account/hooks/useChangeEmailForm.tsx +5 -3
  170. package/src/components/account/hooks/useChangePasswordForm.tsx +20 -6
  171. package/src/components/account/hooks/useChangePhoneForm.tsx +16 -10
  172. package/src/components/account/hooks/useDisableTwoFactorAuthForm.tsx +20 -6
  173. package/src/components/account/hooks/useOtpVerification.tsx +16 -4
  174. package/src/components/account/hooks/useTwoFactorAuthForm.tsx +21 -7
  175. package/src/components/account/sections/ChangeEmailModal.tsx +35 -21
  176. package/src/components/account/sections/ChangePasswordSection.tsx +10 -6
  177. package/src/components/account/sections/ChangePhoneModal.tsx +50 -22
  178. package/src/components/account/sections/MyProfileSection.tsx +37 -23
  179. package/src/components/account/sections/PreferencesSection.tsx +52 -25
  180. package/src/components/account/sections/SecuritySection.tsx +19 -10
  181. package/src/components/account/sections/TokenSection.tsx +22 -10
  182. package/src/components/layouts/AppMobileNavBar.tsx +1 -154
  183. package/src/components/layouts/NavBar.tsx +5 -3
  184. package/src/components/layouts/NotificationsPopover.tsx +5 -3
  185. package/src/components/layouts/ProfilePopover.tsx +6 -4
  186. package/src/components/layouts/UsersSelectorPopover.tsx +9 -7
  187. package/src/components/modals/BuyCreditsModal.tsx +18 -16
  188. package/src/components/modals/CreditsDisabledModal.tsx +6 -6
  189. package/src/components/modals/PaidPlanRequiredModal.tsx +5 -3
  190. package/src/components/modals/cards/AddCardModal.tsx +89 -62
  191. package/src/components/modals/cards/CannotDeleteCardModal.tsx +9 -8
  192. package/src/components/modals/cards/CardFormFields.tsx +93 -75
  193. package/src/components/modals/cards/DeleteCardModal.tsx +10 -8
  194. package/src/components/navigation/CancelledSubscriptionBanner.tsx +4 -2
  195. package/src/components/navigation/OverdueInvoiceBanner.tsx +4 -2
  196. package/src/components/navigation/PendingCancellationBanner.tsx +4 -2
  197. package/src/components/navigation/ProjectSelectorPopover.tsx +8 -6
  198. package/src/components/navigation/UpcomingInvoiceBanner.tsx +4 -2
  199. package/src/components/navigation/subcomponents/BottomLinks.tsx +5 -3
  200. package/src/components/navigation/subcomponents/CreditsCard.tsx +3 -1
  201. package/src/components/navigation/subcomponents/PlanCard.tsx +3 -1
  202. package/src/components/navigation/subcomponents/ProjectSelector.tsx +5 -3
  203. package/src/components/pages/NotFoundPage.tsx +5 -3
  204. package/src/components/pages/notifications/Notifications.tsx +13 -10
  205. package/src/components/ui/buttons/CopyButton.tsx +10 -5
  206. package/src/components/ui/data-display/CardItem.tsx +7 -5
  207. package/src/components/ui/data-display/PaymentInfoCard.tsx +10 -8
  208. package/src/components/ui/feedback/CircularProgress.tsx +4 -0
  209. package/src/components/ui/feedback/DefaultCircularProgress.tsx +3 -1
  210. package/src/components/ui/form/ComboboxField.tsx +11 -6
  211. package/src/components/ui/form/DatePicker.tsx +5 -2
  212. package/src/components/ui/form/DateRangePicker.tsx +5 -2
  213. package/src/components/ui/form/PhoneInput.tsx +5 -3
  214. package/src/components/ui/form/SelectField.tsx +6 -3
  215. package/src/components/widgets/ImageUpload/ImageCropModal.tsx +19 -11
  216. package/src/components/widgets/ImageUpload/ImageTooSmallModal.tsx +6 -5
  217. package/src/i18n/config.ts +17 -0
  218. package/src/i18n/country-language.ts +60 -0
  219. package/src/i18n/messages/en-us.ts +514 -0
  220. package/src/i18n/messages/es-es.ts +515 -0
  221. package/src/i18n/messages/index.ts +24 -0
  222. package/src/i18n/messages/pt-br.ts +538 -0
  223. package/src/i18n/normalize.ts +15 -0
  224. package/src/i18n/resolve-locale.ts +77 -0
  225. package/src/i18n.ts +10 -0
  226. package/src/index.ts +13 -2
  227. package/src/modules/accounts/hooks/use-currency-formatter.hook.tsx +32 -0
  228. package/src/modules/cards/hooks/handle-delete-card.hook.ts +8 -3
  229. package/src/modules/images/utils/validate-image.ts +23 -8
  230. package/src/modules/plans/hooks/list-plans.hook.ts +2 -2
  231. package/src/modules/plans/types/plan.type.ts +4 -0
  232. package/src/modules/plans/utils/map-api-plan-to-ui.ts +109 -47
  233. package/src/modules/projects/services/projects.service.ts +12 -0
  234. package/src/modules/subscriptions/utils/periodicity.ts +29 -12
  235. package/src/providers/auth.provider.tsx +13 -1
  236. package/src/server.ts +2 -0
  237. package/src/utils/format/currency.ts +62 -7
  238. package/src/utils/intl/cookie-domain.ts +29 -0
  239. package/src/utils/intl/locale-cookie.ts +18 -0
  240. package/src/utils/intl/locales.ts +3 -2
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useState, useRef } from 'react';
4
4
  import Image from 'next/image';
5
+ import { useTranslations } from 'next-intl';
5
6
  import { useForm } from 'react-hook-form';
6
7
  import { zodResolver } from '@hookform/resolvers/zod';
7
8
  import { toast } from 'sonner';
@@ -26,6 +27,13 @@ interface MyProfileSectionProps {
26
27
  }
27
28
 
28
29
  export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps) {
30
+ const translate = useTranslations();
31
+ const genderOptions = GENDER_OPTIONS.map((option) => ({
32
+ ...option,
33
+ label: translate(
34
+ `common.account.profile.genderOptions.${option.value === 'masculino' ? 'male' : 'female'}`,
35
+ ),
36
+ }));
29
37
  const { user } = useAuth();
30
38
  const updateAccountUser = useUpdateAccountUser();
31
39
  const fileInputRef = useRef<HTMLInputElement>(null);
@@ -86,16 +94,20 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
86
94
  form.reset(data);
87
95
  setSelectedPhoto(null);
88
96
  setPhotoRemoved(false);
89
- toast.custom((t) => (
90
- <Toast variant="success" message="Perfil atualizado com sucesso" toastId={t} />
97
+ toast.custom((toastId) => (
98
+ <Toast
99
+ variant="success"
100
+ message={translate('common.account.profile.savedSuccess')}
101
+ toastId={toastId}
102
+ />
91
103
  ));
92
104
  onClose();
93
105
  } catch (error) {
94
- toast.custom((t) => (
106
+ toast.custom((toastId) => (
95
107
  <Toast
96
108
  variant="error"
97
- message={error instanceof Error ? error.message : 'Erro ao atualizar perfil'}
98
- toastId={t}
109
+ message={error instanceof Error ? error.message : translate('common.account.profile.saveError')}
110
+ toastId={toastId}
99
111
  />
100
112
  ));
101
113
  }
@@ -113,14 +125,14 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
113
125
  />
114
126
  <div className="absolute inset-0 overflow-y-auto overscroll-contain px-4 pb-20 lg:p-5 lg:static lg:flex-1 lg:min-h-0 lg:overscroll-auto flex flex-col gap-5 lg:gap-6">
115
127
  <span className="paragraph-medium-semibold text-gray-950 hidden lg:block">
116
- Meu perfil
128
+ {translate('common.account.profile.title')}
117
129
  </span>
118
130
  <div className="flex flex-col gap-5">
119
131
  <div className="flex items-center gap-4">
120
132
  {avatarPreview ? (
121
133
  <Image
122
134
  src={avatarPreview}
123
- alt="Foto do usuário"
135
+ alt={translate('common.account.profile.avatarAlt')}
124
136
  width={48}
125
137
  height={48}
126
138
  className="size-12 rounded-full object-cover cursor-pointer"
@@ -142,7 +154,7 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
142
154
  type="button"
143
155
  onClick={() => fileInputRef.current?.click()}
144
156
  >
145
- Alterar foto
157
+ {translate('common.account.profile.changePhoto')}
146
158
  </Button>
147
159
  <Button
148
160
  type="button"
@@ -160,7 +172,7 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
160
172
  type="button"
161
173
  onClick={() => fileInputRef.current?.click()}
162
174
  >
163
- Alterar foto
175
+ {translate('common.account.profile.changePhoto')}
164
176
  </Button>
165
177
  )}
166
178
  </div>
@@ -169,31 +181,31 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
169
181
  <div className="flex flex-col gap-4">
170
182
  <div className="flex gap-4">
171
183
  <FormField
172
- label="Nome"
173
- placeholder="Digite seu nome"
184
+ label={translate('common.account.profile.name')}
185
+ placeholder={translate('common.account.profile.namePlaceholder')}
174
186
  error={!!errors.name}
175
187
  errorMessage={errors.name?.message}
176
188
  {...register('name')}
177
189
  />
178
190
  <FormField
179
- label="Sobrenome"
180
- placeholder="Digite seu sobrenome"
191
+ label={translate('common.account.profile.lastName')}
192
+ placeholder={translate('common.account.profile.lastNamePlaceholder')}
181
193
  error={!!errors.last_name}
182
194
  errorMessage={errors.last_name?.message}
183
195
  {...register('last_name')}
184
196
  />
185
197
  </div>
186
198
  <SelectField
187
- label="Gênero"
188
- placeholder="Selecione seu gênero"
189
- options={GENDER_OPTIONS}
199
+ label={translate('common.account.profile.gender')}
200
+ placeholder={translate('common.account.profile.genderPlaceholder')}
201
+ options={genderOptions}
190
202
  value={watch('gender')}
191
203
  className="h-10!"
192
204
  onChange={(value) => setValue('gender', value as string)}
193
205
  />
194
206
  <div className="relative">
195
207
  <PhoneInput
196
- label="Telefone"
208
+ label={translate('common.account.profile.phone')}
197
209
  value={user?.phone ?? ''}
198
210
  disabled
199
211
  classnameContainer="bg-gray-50 border-0!"
@@ -203,7 +215,7 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
203
215
  className="absolute right-3 top-5.5 h-10 flex items-center paragraph-xsmall-semibold text-gray-950 underline cursor-pointer"
204
216
  onClick={() => setChangePhoneOpen(true)}
205
217
  >
206
- Alterar
218
+ {translate('common.account.profile.change')}
207
219
  </button>
208
220
  </div>
209
221
  </div>
@@ -211,11 +223,13 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
211
223
  <Separator />
212
224
 
213
225
  <div className="flex flex-col gap-4">
214
- <span className="paragraph-medium-semibold text-gray-600">Dados de acesso</span>
226
+ <span className="paragraph-medium-semibold text-gray-600">
227
+ {translate('common.account.profile.accessData')}
228
+ </span>
215
229
  <div className="relative">
216
230
  <div className='cursor-not-allowed'>
217
231
  <FormField
218
- label="E-mail"
232
+ label={translate('common.account.profile.email')}
219
233
  value={user?.email ?? ''}
220
234
  disabled
221
235
  classnameContainer="bg-gray-50 border-0!"
@@ -226,7 +240,7 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
226
240
  className="absolute right-3 top-5.5 h-10 flex items-center paragraph-xsmall-semibold text-gray-950 underline cursor-pointer"
227
241
  onClick={() => setChangeEmailOpen(true)}
228
242
  >
229
- Alterar
243
+ {translate('common.account.profile.change')}
230
244
  </button>
231
245
  </div>
232
246
  </div>
@@ -247,10 +261,10 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
247
261
  onClose();
248
262
  }}
249
263
  >
250
- Cancelar
264
+ {translate('common.actions.cancel')}
251
265
  </Button>
252
266
  <Button className="h-10! cursor-pointer" type="submit" disabled={isSubmitting}>
253
- {isSubmitting ? 'Salvando...' : 'Salvar alterações'}
267
+ {isSubmitting ? translate('common.actions.saving') : translate('common.actions.saveChanges')}
254
268
  </Button>
255
269
  </div>
256
270
  </form>
@@ -2,9 +2,13 @@
2
2
 
3
3
  import { useState, useEffect } from 'react';
4
4
  import { useForm } from 'react-hook-form';
5
+ import { useTranslations, useLocale } from 'next-intl';
6
+ import { useRouter } from 'next/navigation';
5
7
  import { toast } from 'sonner';
6
8
  import { IconLock } from '@tabler/icons-react';
7
9
  import { cn } from '../../../infra/utils/clsx';
10
+ import type { Locale } from '../../../i18n/config';
11
+ import { setLocaleCookie } from '../../../utils/intl/locale-cookie';
8
12
  import { Button } from '../../ui/buttons/Button';
9
13
  import { Separator } from '../../ui/data-display/Separator';
10
14
  import { Toast } from '../../ui/feedback/Toast';
@@ -13,6 +17,7 @@ import { ComboboxField } from '../../ui/form/ComboboxField';
13
17
  import { SelectField } from '../../ui/form/SelectField';
14
18
  import ConfirmGlobalPreferencesModal from '../ConfirmGlobalPreferencesModal';
15
19
  import { useAuth } from '../../../providers/auth.provider';
20
+ import { useWhitelabel } from '../../../providers/whitelabel.provider';
16
21
  import { UserProfile } from '../../../modules/users/schema';
17
22
  import type { UpdateAccountRequest } from '../../../modules/accounts/types';
18
23
  import {
@@ -41,9 +46,27 @@ interface PreferencesSectionProps {
41
46
  }
42
47
 
43
48
  export function PreferencesSection({ onClose }: PreferencesSectionProps) {
49
+ // Padrão raiz: um único `translate` chamado pela chave inteira (`common.<modulo>.<chave>`).
50
+ const translate = useTranslations();
51
+ // Exceção: chave dinâmica (option.value) numa lista → namespace escopado.
52
+ const translateLanguage = useTranslations('common.languages');
53
+ const currentLocale = useLocale();
54
+ const router = useRouter();
44
55
  const { data: timezones = [] } = useTimezones();
45
56
  const { data: currencies = [] } = useCurrencies();
46
57
  const { user, account } = useAuth();
58
+ const { whitelabel } = useWhitelabel();
59
+ const languageOptions = LANGUAGE_OPTIONS.map((option) => ({
60
+ ...option,
61
+ label: translateLanguage(option.value as Locale),
62
+ displayValue: translateLanguage(option.value as Locale),
63
+ }));
64
+ const timeFormatOptions = TIME_FORMAT_OPTIONS.map((option) => ({
65
+ ...option,
66
+ label: translate(
67
+ `common.preferences.timeFormatOptions.${option.value === '24h' ? 'h24' : 'h12'}`,
68
+ ),
69
+ }));
47
70
  const isOwner = user?.profile === UserProfile.owner;
48
71
  const canEditPreferences = isOwner || user?.profile === UserProfile.admin;
49
72
  const updateAccountUser = useUpdateAccountUser();
@@ -74,7 +97,7 @@ export function PreferencesSection({ onClose }: PreferencesSectionProps) {
74
97
  companyName: account.name ?? '',
75
98
  language: LANGUAGE_OPTIONS.find(
76
99
  (o) => o.apiValue === user.language?.toLowerCase()
77
- )?.value ?? 'pt-BR',
100
+ )?.value ?? 'pt-br',
78
101
  currency: account.currency,
79
102
  timezone: account.timezone,
80
103
  timeFormat: '24h',
@@ -98,16 +121,20 @@ export function PreferencesSection({ onClose }: PreferencesSectionProps) {
98
121
  }
99
122
 
100
123
  reset(data);
101
- toast.custom((t) => (
102
- <Toast variant="success" message="Preferências salvas com sucesso" toastId={t} />
124
+ toast.custom((toastId) => (
125
+ <Toast variant="success" message={translate('common.preferences.savedSuccess')} toastId={toastId} />
103
126
  ));
104
127
  onClose();
128
+ if (data.language !== currentLocale) {
129
+ setLocaleCookie(data.language, whitelabel?.domain);
130
+ router.refresh();
131
+ }
105
132
  } catch (error) {
106
- toast.custom((t) => (
133
+ toast.custom((toastId) => (
107
134
  <Toast
108
135
  variant="error"
109
- message={error instanceof Error ? error.message : 'Erro ao salvar preferências'}
110
- toastId={t}
136
+ message={error instanceof Error ? error.message : translate('common.preferences.saveError')}
137
+ toastId={toastId}
111
138
  />
112
139
  ));
113
140
  } finally {
@@ -148,12 +175,12 @@ export function PreferencesSection({ onClose }: PreferencesSectionProps) {
148
175
  <>
149
176
  <form onSubmit={handleSubmit(onSubmit)} className="flex flex-col h-full">
150
177
  <div className="overflow-y-auto overscroll-contain px-4 pb-4 lg:p-5 flex flex-col gap-5 lg:gap-6 flex-1">
151
- <span className="paragraph-medium-semibold text-gray-950">Preferências</span>
178
+ <span className="paragraph-medium-semibold text-gray-950">{translate('common.preferences.title')}</span>
152
179
  <div className="flex flex-col gap-8">
153
180
  <div className="flex flex-col lg:flex-row items-center gap-3">
154
181
  <FormField
155
- label="Nome da empresa"
156
- placeholder="Digite o nome da empresa"
182
+ label={translate('common.preferences.companyName')}
183
+ placeholder={translate('common.preferences.companyNamePlaceholder')}
157
184
  classnameContainer={cn('w-full', !isOwner && 'cursor-not-allowed opacity-50')}
158
185
  value={watch('companyName')}
159
186
  onChange={(e) => setValue('companyName', e.target.value, { shouldDirty: true })}
@@ -163,18 +190,18 @@ export function PreferencesSection({ onClose }: PreferencesSectionProps) {
163
190
 
164
191
  <div className="flex flex-col lg:flex-row items-center gap-3">
165
192
  <ComboboxField
166
- label="Idioma"
167
- placeholder="Selecione o idioma"
168
- searchPlaceholder="Buscar idioma..."
193
+ label={translate('common.preferences.language')}
194
+ placeholder={translate('common.preferences.languagePlaceholder')}
195
+ searchPlaceholder={translate('common.preferences.languageSearch')}
169
196
  containerClassName="w-full"
170
- options={LANGUAGE_OPTIONS}
197
+ options={languageOptions}
171
198
  value={watch('language')}
172
199
  onChange={(value) => setValue('language', value, { shouldDirty: true })}
173
200
  />
174
201
  <ComboboxField
175
- label="Moeda"
176
- placeholder="Selecione a moeda"
177
- searchPlaceholder="Buscar moeda..."
202
+ label={translate('common.preferences.currency')}
203
+ placeholder={translate('common.preferences.currencyPlaceholder')}
204
+ searchPlaceholder={translate('common.preferences.currencySearch')}
178
205
  containerClassName="w-full"
179
206
  options={currencies}
180
207
  value={watch('currency')}
@@ -187,12 +214,12 @@ export function PreferencesSection({ onClose }: PreferencesSectionProps) {
187
214
  <Separator />
188
215
 
189
216
  <div className="flex flex-col gap-4">
190
- <span className="paragraph-medium-semibold text-gray-950">Exibição hora</span>
217
+ <span className="paragraph-medium-semibold text-gray-950">{translate('common.preferences.timeDisplay')}</span>
191
218
  <div className="flex flex-col lg:flex-row items-center gap-3">
192
219
  <ComboboxField
193
- label="Fuso horário"
194
- placeholder="Selecione o fuso horário"
195
- searchPlaceholder="Buscar fuso horário..."
220
+ label={translate('common.preferences.timezone')}
221
+ placeholder={translate('common.preferences.timezonePlaceholder')}
222
+ searchPlaceholder={translate('common.preferences.timezoneSearch')}
196
223
  containerClassName="w-full"
197
224
  options={timezones}
198
225
  value={watch('timezone')}
@@ -201,11 +228,11 @@ export function PreferencesSection({ onClose }: PreferencesSectionProps) {
201
228
  disabled={!canEditPreferences}
202
229
  />
203
230
  <SelectField
204
- label="Formato hora"
205
- placeholder="Selecione o formato"
231
+ label={translate('common.preferences.timeFormat')}
232
+ placeholder={translate('common.preferences.timeFormatPlaceholder')}
206
233
  className="h-10!"
207
234
  containerClassName="w-full"
208
- options={TIME_FORMAT_OPTIONS}
235
+ options={timeFormatOptions}
209
236
  value={watch('timeFormat')}
210
237
  onChange={(value) =>
211
238
  setValue('timeFormat', value as string, { shouldDirty: true })
@@ -230,10 +257,10 @@ export function PreferencesSection({ onClose }: PreferencesSectionProps) {
230
257
  onClose();
231
258
  }}
232
259
  >
233
- Cancelar
260
+ {translate('common.actions.cancel')}
234
261
  </Button>
235
262
  <Button className="h-10!" type="submit" disabled={isSaving}>
236
- {isSaving ? 'Salvando...' : 'Salvar alterações'}
263
+ {isSaving ? translate('common.actions.saving') : translate('common.actions.saveChanges')}
237
264
  </Button>
238
265
  </div>
239
266
  </form>
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { IconAlertTriangle, IconBrandGoogleFilled } from '@tabler/icons-react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import { toast } from 'sonner';
5
6
  import { useQueryClient } from '@tanstack/react-query';
6
7
  import { useAuth } from '../../../providers/auth.provider';
@@ -18,6 +19,7 @@ interface SecuritySectionProps {
18
19
  export function SecuritySection({
19
20
  onDeleteAccount,
20
21
  }: SecuritySectionProps) {
22
+ const translate = useTranslations();
21
23
  const { user } = useAuth();
22
24
  const queryClient = useQueryClient();
23
25
  const { mutateAsync: unlinkGoogle, isPending: isUnlinking } = useUnlinkGoogle();
@@ -32,14 +34,16 @@ export function SecuritySection({
32
34
  queryClient.invalidateQueries({ queryKey: USER_QUERY_KEY });
33
35
  } catch (error) {
34
36
  const message =
35
- error instanceof Error ? error.message : 'Erro ao desvincular conta Google.';
36
- toast.custom((t) => <Toast variant="error" message={message} toastId={t} />);
37
+ error instanceof Error ? error.message : translate('common.account.security.unlinkError');
38
+ toast.custom((toastId) => <Toast variant="error" message={message} toastId={toastId} />);
37
39
  }
38
40
  };
39
41
 
40
42
  return (
41
43
  <div className="h-full overflow-y-auto overscroll-contain px-4 pb-20 lg:p-5 lg:pt-5 flex flex-col gap-5 lg:gap-6 lg:pb-31 lg:overscroll-auto">
42
- <span className="paragraph-medium-semibold text-gray-950 hidden lg:block">Segurança</span>
44
+ <span className="paragraph-medium-semibold text-gray-950 hidden lg:block">
45
+ {translate('common.account.security.title')}
46
+ </span>
43
47
 
44
48
  {hasGoogleLinked && (
45
49
  <div className="flex flex-col items-center lg:items-start gap-4">
@@ -48,10 +52,11 @@ export function SecuritySection({
48
52
  </div>
49
53
 
50
54
  <div className="flex flex-col gap-2">
51
- <span className="paragraph-medium-semibold text-gray-950">Conta Google vinculada</span>
55
+ <span className="paragraph-medium-semibold text-gray-950">
56
+ {translate('common.account.security.googleLinkedTitle')}
57
+ </span>
52
58
  <span className="paragraph-small-regular text-gray-600">
53
- Sua conta está vinculada ao Google. Se desvincular, use &quot;Esqueci minha senha&quot; para
54
- redefinir uma senha de acesso por e-mail.
59
+ {translate('common.account.security.googleLinkedDescription')}
55
60
  </span>
56
61
  </div>
57
62
 
@@ -61,7 +66,9 @@ export function SecuritySection({
61
66
  className="paragraph-small-semibold text-zinc-700 bg-zinc-100 rounded-lg px-3 py-2 cursor-pointer hover:bg-zinc-200 transition-colors w-fit disabled:opacity-50 disabled:cursor-not-allowed"
62
67
  onClick={handleUnlinkGoogle}
63
68
  >
64
- {isUnlinking ? 'Desvinculando...' : 'Desvincular Google'}
69
+ {isUnlinking
70
+ ? translate('common.account.security.unlinking')
71
+ : translate('common.account.security.unlinkGoogle')}
65
72
  </button>
66
73
  </div>
67
74
  )}
@@ -72,9 +79,11 @@ export function SecuritySection({
72
79
  </div>
73
80
 
74
81
  <div className="flex flex-col gap-2">
75
- <span className="paragraph-medium-semibold text-gray-950">Excluir conta</span>
82
+ <span className="paragraph-medium-semibold text-gray-950">
83
+ {translate('common.account.deleteAccount.title')}
84
+ </span>
76
85
  <span className="paragraph-small-regular text-gray-600">
77
- Esta ação é permanente e não pode ser desfeita. Todos os seus dados, projetos e configurações serão removidos definitivamente.
86
+ {translate('common.account.security.deleteAccountDescription')}
78
87
  </span>
79
88
  </div>
80
89
 
@@ -84,7 +93,7 @@ export function SecuritySection({
84
93
  className="paragraph-small-semibold text-red-600 bg-red-50 rounded-lg px-3 py-2 cursor-pointer hover:bg-red-100 transition-colors w-fit"
85
94
  onClick={onDeleteAccount}
86
95
  >
87
- Excluir minha conta
96
+ {translate('common.account.security.deleteMyAccount')}
88
97
  </button>
89
98
  )}
90
99
  </div>
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { IconCheck, IconCopy, IconInfoCircle } from '@tabler/icons-react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import { toast } from 'sonner';
5
6
  import { Toast } from '../../ui/feedback/Toast';
6
7
  import { Button } from '../../ui/buttons/Button';
@@ -10,31 +11,42 @@ import { useAccountToken } from '../../../modules/accounts/hooks/use-account-tok
10
11
  import { useIsDefaultWhitelabel } from '../../../providers/whitelabel.provider';
11
12
 
12
13
  export function TokenSection() {
14
+ const translate = useTranslations();
13
15
  const { data: token = '', isLoading } = useAccountToken();
14
16
  const isDefaultWl = useIsDefaultWhitelabel();
15
17
 
16
18
  const { isCopied, copy } = useCopyToClipboard({
17
19
  onSuccess: () => {
18
- toast.custom((t) => (
19
- <Toast variant="success" message="Token copiado com sucesso" toastId={t} />
20
+ toast.custom((toastId) => (
21
+ <Toast
22
+ variant="success"
23
+ message={translate('common.account.token.copySuccess')}
24
+ toastId={toastId}
25
+ />
20
26
  ));
21
27
  },
22
28
  onError: () => {
23
- toast.custom((t) => (
24
- <Toast variant="error" message="Não foi possível copiar o token" toastId={t} />
29
+ toast.custom((toastId) => (
30
+ <Toast
31
+ variant="error"
32
+ message={translate('common.account.token.copyError')}
33
+ toastId={toastId}
34
+ />
25
35
  ));
26
36
  },
27
37
  });
28
38
 
29
39
  return (
30
40
  <div className="h-full overflow-y-auto overscroll-contain px-4 pb-20 lg:p-5 lg:pt-5 flex flex-col gap-5 lg:gap-6 lg:pb-31 lg:overscroll-auto">
31
- <span className="paragraph-medium-semibold text-gray-950">Token da conta</span>
41
+ <span className="paragraph-medium-semibold text-gray-950">
42
+ {translate('common.account.token.title')}
43
+ </span>
32
44
 
33
45
  <div className="flex flex-col gap-3">
34
46
  <div className="flex items-end gap-3">
35
47
  <FormField
36
- label="Token"
37
- value={isLoading ? 'Carregando...' : token}
48
+ label={translate('common.account.token.label')}
49
+ value={isLoading ? translate('common.actions.loading') : token}
38
50
  disabled
39
51
  readOnly
40
52
  classnameContainer="bg-gray-50 cursor-default opacity-100!"
@@ -48,14 +60,14 @@ export function TokenSection() {
48
60
  disabled={isLoading || !token}
49
61
  >
50
62
  {isCopied ? <IconCheck size={16} /> : <IconCopy size={16} />}
51
- {isCopied ? 'Copiado' : 'Copiar'}
63
+ {isCopied ? translate('common.actions.copied') : translate('common.actions.copy')}
52
64
  </Button>
53
65
  </div>
54
66
 
55
67
  <div className={`flex items-center gap-3 p-4 rounded-lg ${isDefaultWl ? 'bg-cyan-50' : 'bg-zinc-50'}`}>
56
68
  <IconInfoCircle className="size-4 text-zinc-950 shrink-0" />
57
69
  <span className="paragraph-small-regular text-zinc-950 max-w-[420px]">
58
- Este token é exclusivo da sua conta e permite acesso à API. Não compartilhe com terceiros.
70
+ {translate('common.account.token.info')}
59
71
  </span>
60
72
  </div>
61
73
 
@@ -65,7 +77,7 @@ export function TokenSection() {
65
77
  rel="noopener noreferrer"
66
78
  className="paragraph-small-medium text-gray-500 underline hover:text-gray-700 transition-colors w-fit"
67
79
  >
68
- Acessar a documentação
80
+ {translate('common.account.token.docsLink')}
69
81
  </a>
70
82
  </div>
71
83
  </div>