@greatapps/common 1.1.219 → 1.1.221

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 (217) hide show
  1. package/dist/components/account/ConfigurationsMyAccountModal.mjs +1 -1
  2. package/dist/components/account/ConfigurationsMyAccountModal.mjs.map +1 -1
  3. package/dist/components/account/ConfirmDeleteAccountModal.mjs.map +1 -1
  4. package/dist/components/account/ConfirmGlobalPreferencesModal.mjs.map +1 -1
  5. package/dist/components/account/DeleteAccountModal.mjs.map +1 -1
  6. package/dist/components/account/DisableTwoFactorAuthModal.mjs.map +1 -1
  7. package/dist/components/account/IsntPossibleDeleteAccountModal.mjs.map +1 -1
  8. package/dist/components/account/TwoFactorAuthModal.mjs.map +1 -1
  9. package/dist/components/account/hooks/useChangePhoneForm.mjs.map +1 -1
  10. package/dist/components/account/hooks/useTwoFactorAuthForm.mjs.map +1 -1
  11. package/dist/components/account/sections/ChangeEmailModal.mjs.map +1 -1
  12. package/dist/components/account/sections/ChangePasswordSection.mjs.map +1 -1
  13. package/dist/components/account/sections/ChangePhoneModal.mjs.map +1 -1
  14. package/dist/components/account/sections/MyProfileSection.mjs.map +1 -1
  15. package/dist/components/account/sections/PreferencesSection.mjs.map +1 -1
  16. package/dist/components/account/sections/SecuritySection.mjs.map +1 -1
  17. package/dist/components/embeds/CrispEmbed.mjs.map +1 -1
  18. package/dist/components/embeds/FrillEmbed.mjs.map +1 -1
  19. package/dist/components/layouts/AppMobileNavBar.mjs +0 -1
  20. package/dist/components/layouts/AppMobileNavBar.mjs.map +1 -1
  21. package/dist/components/layouts/AppNavBar.mjs.map +1 -1
  22. package/dist/components/layouts/MdSideBarNavigation.mjs.map +1 -1
  23. package/dist/components/layouts/NavBar.mjs.map +1 -1
  24. package/dist/components/layouts/NotificationsPopover.mjs.map +1 -1
  25. package/dist/components/layouts/ProfilePopover.mjs +7 -10
  26. package/dist/components/layouts/ProfilePopover.mjs.map +1 -1
  27. package/dist/components/layouts/UsersSelectorPopover.mjs.map +1 -1
  28. package/dist/components/navigation/CancelledSubscriptionBanner.mjs.map +1 -1
  29. package/dist/components/navigation/NavItemsContext.mjs.map +1 -1
  30. package/dist/components/navigation/ProjectSelectorPopover.mjs.map +1 -1
  31. package/dist/components/navigation/shells/TabsMobileShell.mjs.map +1 -1
  32. package/dist/components/navigation/subcomponents/ProjectSelector.mjs.map +1 -1
  33. package/dist/components/pages/notifications/Notifications.mjs.map +1 -1
  34. package/dist/components/ui/buttons/Button.mjs.map +1 -1
  35. package/dist/components/ui/buttons/CancelButton.mjs.map +1 -1
  36. package/dist/components/ui/buttons/CopyButton.mjs.map +1 -1
  37. package/dist/components/ui/data-display/Accordion.mjs.map +1 -1
  38. package/dist/components/ui/data-display/Badge.mjs.map +1 -1
  39. package/dist/components/ui/data-display/Card.mjs.map +1 -1
  40. package/dist/components/ui/data-display/Pagination.mjs.map +1 -1
  41. package/dist/components/ui/data-display/ScrollArea.mjs.map +1 -1
  42. package/dist/components/ui/data-display/Separator.mjs.map +1 -1
  43. package/dist/components/ui/data-display/Table.mjs.map +1 -1
  44. package/dist/components/ui/data-display/Tabs.mjs.map +1 -1
  45. package/dist/components/ui/feedback/CircularProgress.mjs.map +1 -1
  46. package/dist/components/ui/feedback/DefaultCircularProgress.mjs.map +1 -1
  47. package/dist/components/ui/feedback/LoadingOverlay.mjs.map +1 -1
  48. package/dist/components/ui/feedback/Progress.mjs.map +1 -1
  49. package/dist/components/ui/feedback/Toast.mjs.map +1 -1
  50. package/dist/components/ui/form/Calendar.mjs.map +1 -1
  51. package/dist/components/ui/form/Checkbox.mjs.map +1 -1
  52. package/dist/components/ui/form/ComboboxField.mjs.map +1 -1
  53. package/dist/components/ui/form/DatePicker.mjs.map +1 -1
  54. package/dist/components/ui/form/DateRangePicker.mjs.map +1 -1
  55. package/dist/components/ui/form/FormField.mjs.map +1 -1
  56. package/dist/components/ui/form/Input.mjs.map +1 -1
  57. package/dist/components/ui/form/InputOtp.mjs.map +1 -1
  58. package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
  59. package/dist/components/ui/form/RadioGroup.mjs.map +1 -1
  60. package/dist/components/ui/form/Select.mjs +1 -1
  61. package/dist/components/ui/form/Select.mjs.map +1 -1
  62. package/dist/components/ui/form/Switch.mjs.map +1 -1
  63. package/dist/components/ui/form/Textarea.mjs.map +1 -1
  64. package/dist/components/ui/overlay/Command.mjs.map +1 -1
  65. package/dist/components/ui/overlay/Dialog.mjs.map +1 -1
  66. package/dist/components/ui/overlay/Popover.mjs.map +1 -1
  67. package/dist/components/ui/overlay/Sheet.mjs.map +1 -1
  68. package/dist/components/ui/overlay/Tooltip.mjs.map +1 -1
  69. package/dist/hooks/copy-to-clipboard.hook.mjs.map +1 -1
  70. package/dist/index.mjs.map +1 -1
  71. package/dist/middleware.mjs.map +1 -1
  72. package/dist/modules/accounts/actions/account-management.action.mjs.map +1 -1
  73. package/dist/modules/accounts/actions/find-current-account.action.mjs.map +1 -1
  74. package/dist/modules/accounts/actions/list-currencies.action.mjs.map +1 -1
  75. package/dist/modules/accounts/actions/list-timezones.action.mjs.map +1 -1
  76. package/dist/modules/accounts/hooks/current-account.hook.mjs.map +1 -1
  77. package/dist/modules/accounts/hooks/useAccountManagement.mjs.map +1 -1
  78. package/dist/modules/accounts/hooks/useCurrencies.mjs.map +1 -1
  79. package/dist/modules/accounts/hooks/useTimezones.mjs.map +1 -1
  80. package/dist/modules/accounts/services/account.service.mjs.map +1 -1
  81. package/dist/modules/auth/hooks/login.hook.mjs.map +1 -1
  82. package/dist/modules/auth/hooks/logout.hook.mjs.map +1 -1
  83. package/dist/modules/auth/hooks/register.hook.mjs.map +1 -1
  84. package/dist/modules/ia-credits/actions/list-ia-credits.action.mjs.map +1 -1
  85. package/dist/modules/projects/actions/add-project-user.action.mjs.map +1 -1
  86. package/dist/modules/projects/actions/list-all-account-users.action.mjs.map +1 -1
  87. package/dist/modules/projects/actions/list-available-users.action.mjs.map +1 -1
  88. package/dist/modules/projects/actions/list-project-users.action.mjs.map +1 -1
  89. package/dist/modules/projects/actions/remove-project-user.action.mjs.map +1 -1
  90. package/dist/modules/projects/hooks/add-project-user.hook.mjs.map +1 -1
  91. package/dist/modules/projects/hooks/list-all-account-users.hook.mjs.map +1 -1
  92. package/dist/modules/projects/hooks/list-available-users.hook.mjs.map +1 -1
  93. package/dist/modules/projects/hooks/list-project-users.hook.mjs.map +1 -1
  94. package/dist/modules/projects/hooks/remove-project-user.hook.mjs.map +1 -1
  95. package/dist/modules/subscriptions/actions/list-subscriptions.action.mjs.map +1 -1
  96. package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs.map +1 -1
  97. package/dist/modules/subscriptions/hooks/list-subscriptions.hook.mjs.map +1 -1
  98. package/dist/modules/users/action/find-user-by-id.action.mjs.map +1 -1
  99. package/dist/modules/users/action/list-messages.action.mjs.map +1 -1
  100. package/dist/modules/users/action/mark-all-messages-as-read.action.mjs.map +1 -1
  101. package/dist/modules/users/action/mark-message-as-read.action.mjs.map +1 -1
  102. package/dist/modules/users/hooks/messages.hook.mjs.map +1 -1
  103. package/dist/modules/whitelabel/actions/find-whitelabel-styles.action.mjs.map +1 -1
  104. package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs.map +1 -1
  105. package/dist/modules/whitelabel/services/whitelabel-styles.service.mjs.map +1 -1
  106. package/dist/providers/whitelabel.provider.mjs.map +1 -1
  107. package/dist/utils/countries.mjs.map +1 -1
  108. package/dist/utils/safeServerAction.mjs.map +1 -1
  109. package/dist/utils/withAction.mjs.map +1 -1
  110. package/package.json +89 -89
  111. package/src/components/account/ConfigurationsMyAccountModal.tsx +150 -150
  112. package/src/components/account/ConfirmDeleteAccountModal.tsx +55 -55
  113. package/src/components/account/ConfirmGlobalPreferencesModal.tsx +66 -66
  114. package/src/components/account/DeleteAccountModal.tsx +130 -130
  115. package/src/components/account/DisableTwoFactorAuthModal.tsx +86 -86
  116. package/src/components/account/IsntPossibleDeleteAccountModal.tsx +49 -49
  117. package/src/components/account/TwoFactorAuthModal.tsx +223 -223
  118. package/src/components/account/hooks/useChangePhoneForm.tsx +81 -81
  119. package/src/components/account/hooks/useTwoFactorAuthForm.tsx +101 -101
  120. package/src/components/account/sections/ChangeEmailModal.tsx +225 -225
  121. package/src/components/account/sections/ChangePasswordSection.tsx +64 -64
  122. package/src/components/account/sections/ChangePhoneModal.tsx +231 -231
  123. package/src/components/account/sections/MyProfileSection.tsx +327 -327
  124. package/src/components/account/sections/PreferencesSection.tsx +273 -273
  125. package/src/components/account/sections/SecuritySection.tsx +160 -160
  126. package/src/components/embeds/CrispEmbed.tsx +101 -101
  127. package/src/components/embeds/FrillEmbed.tsx +119 -119
  128. package/src/components/layouts/AppMobileNavBar.tsx +110 -111
  129. package/src/components/layouts/AppNavBar.tsx +57 -57
  130. package/src/components/layouts/MdSideBarNavigation.tsx +56 -56
  131. package/src/components/layouts/NavBar.tsx +120 -120
  132. package/src/components/layouts/NotificationsPopover.tsx +115 -115
  133. package/src/components/layouts/ProfilePopover.tsx +179 -184
  134. package/src/components/layouts/UsersSelectorPopover.tsx +238 -238
  135. package/src/components/navigation/CancelledSubscriptionBanner.tsx +69 -69
  136. package/src/components/navigation/NavItemsContext.ts +20 -20
  137. package/src/components/navigation/ProjectSelectorPopover.tsx +151 -151
  138. package/src/components/navigation/shells/TabsMobileShell.tsx +72 -72
  139. package/src/components/navigation/subcomponents/ProjectSelector.tsx +149 -149
  140. package/src/components/pages/notifications/Notifications.tsx +160 -160
  141. package/src/components/ui/buttons/Button.tsx +57 -57
  142. package/src/components/ui/buttons/CancelButton.tsx +14 -14
  143. package/src/components/ui/buttons/CopyButton.tsx +54 -54
  144. package/src/components/ui/data-display/Accordion.tsx +81 -81
  145. package/src/components/ui/data-display/Badge.tsx +47 -47
  146. package/src/components/ui/data-display/Card.tsx +93 -93
  147. package/src/components/ui/data-display/Pagination.tsx +126 -126
  148. package/src/components/ui/data-display/ScrollArea.tsx +59 -59
  149. package/src/components/ui/data-display/Separator.tsx +29 -29
  150. package/src/components/ui/data-display/Table.tsx +117 -117
  151. package/src/components/ui/data-display/Tabs.tsx +67 -67
  152. package/src/components/ui/feedback/CircularProgress.tsx +75 -75
  153. package/src/components/ui/feedback/DefaultCircularProgress.tsx +61 -61
  154. package/src/components/ui/feedback/LoadingOverlay.tsx +31 -31
  155. package/src/components/ui/feedback/Progress.tsx +40 -40
  156. package/src/components/ui/feedback/Toast.tsx +101 -101
  157. package/src/components/ui/form/Calendar.tsx +228 -228
  158. package/src/components/ui/form/Checkbox.tsx +31 -31
  159. package/src/components/ui/form/ComboboxField.tsx +154 -154
  160. package/src/components/ui/form/DatePicker.tsx +97 -97
  161. package/src/components/ui/form/DateRangePicker.tsx +108 -108
  162. package/src/components/ui/form/FormField.tsx +131 -131
  163. package/src/components/ui/form/Input.tsx +20 -20
  164. package/src/components/ui/form/InputOtp.tsx +80 -80
  165. package/src/components/ui/form/PhoneInput.tsx +205 -205
  166. package/src/components/ui/form/RadioGroup.tsx +45 -45
  167. package/src/components/ui/form/Select.tsx +200 -200
  168. package/src/components/ui/form/Switch.tsx +32 -32
  169. package/src/components/ui/form/Textarea.tsx +19 -19
  170. package/src/components/ui/overlay/Command.tsx +185 -185
  171. package/src/components/ui/overlay/Dialog.tsx +151 -151
  172. package/src/components/ui/overlay/Popover.tsx +49 -49
  173. package/src/components/ui/overlay/Sheet.tsx +166 -166
  174. package/src/components/ui/overlay/Tooltip.tsx +62 -62
  175. package/src/hooks/copy-to-clipboard.hook.tsx +67 -67
  176. package/src/index.ts +355 -355
  177. package/src/middleware.ts +4 -4
  178. package/src/modules/accounts/actions/account-management.action.ts +90 -90
  179. package/src/modules/accounts/actions/find-current-account.action.ts +9 -9
  180. package/src/modules/accounts/actions/list-currencies.action.ts +8 -8
  181. package/src/modules/accounts/actions/list-timezones.action.ts +8 -8
  182. package/src/modules/accounts/hooks/current-account.hook.tsx +22 -22
  183. package/src/modules/accounts/hooks/useAccountManagement.ts +85 -85
  184. package/src/modules/accounts/hooks/useCurrencies.ts +15 -15
  185. package/src/modules/accounts/hooks/useTimezones.ts +15 -15
  186. package/src/modules/accounts/services/account.service.ts +236 -236
  187. package/src/modules/accounts/types.ts +214 -214
  188. package/src/modules/auth/hooks/login.hook.tsx +13 -13
  189. package/src/modules/auth/hooks/logout.hook.tsx +11 -11
  190. package/src/modules/auth/hooks/register.hook.tsx +11 -11
  191. package/src/modules/ia-credits/actions/list-ia-credits.action.ts +8 -8
  192. package/src/modules/projects/actions/add-project-user.action.ts +9 -9
  193. package/src/modules/projects/actions/list-all-account-users.action.ts +9 -9
  194. package/src/modules/projects/actions/list-available-users.action.ts +9 -9
  195. package/src/modules/projects/actions/list-project-users.action.ts +9 -9
  196. package/src/modules/projects/actions/remove-project-user.action.ts +9 -9
  197. package/src/modules/projects/hooks/add-project-user.hook.ts +69 -69
  198. package/src/modules/projects/hooks/list-all-account-users.hook.ts +34 -34
  199. package/src/modules/projects/hooks/list-available-users.hook.ts +33 -33
  200. package/src/modules/projects/hooks/list-project-users.hook.ts +33 -33
  201. package/src/modules/projects/hooks/remove-project-user.hook.ts +69 -69
  202. package/src/modules/subscriptions/actions/list-subscriptions.action.ts +9 -9
  203. package/src/modules/subscriptions/hooks/find-active-subscription.hook.ts +19 -19
  204. package/src/modules/subscriptions/hooks/list-subscriptions.hook.ts +17 -17
  205. package/src/modules/users/action/find-user-by-id.action.ts +8 -8
  206. package/src/modules/users/action/list-messages.action.ts +12 -12
  207. package/src/modules/users/action/mark-all-messages-as-read.action.ts +8 -8
  208. package/src/modules/users/action/mark-message-as-read.action.ts +8 -8
  209. package/src/modules/users/hooks/messages.hook.ts +60 -60
  210. package/src/modules/whitelabel/actions/find-whitelabel-styles.action.ts +14 -14
  211. package/src/modules/whitelabel/actions/find-whitelabel.action.ts +23 -23
  212. package/src/modules/whitelabel/services/whitelabel-styles.service.ts +98 -98
  213. package/src/modules/whitelabel/styles-schema.ts +37 -37
  214. package/src/providers/whitelabel.provider.tsx +46 -46
  215. package/src/utils/countries.ts +224 -224
  216. package/src/utils/safeServerAction.ts +21 -21
  217. package/src/utils/withAction.ts +13 -13
@@ -1,273 +1,273 @@
1
- 'use client';
2
-
3
- import { useState, useEffect } from 'react';
4
- import { useForm } from 'react-hook-form';
5
- import { toast } from 'sonner';
6
- import { IconLock, IconMail, IconMessageCircle } from '@tabler/icons-react';
7
- import { Button } from '../../ui/buttons/Button';
8
- import { Separator } from '../../ui/data-display/Separator';
9
- import { Toast } from '../../ui/feedback/Toast';
10
- import { ComboboxField } from '../../ui/form/ComboboxField';
11
- import { SelectField } from '../../ui/form/SelectField';
12
- import SwitchOptionFieldWithIcon from '../../ui/form/SwitchOptionFieldWithIcon';
13
- import ConfirmGlobalPreferencesModal from '../ConfirmGlobalPreferencesModal';
14
- import { useAuth } from '../../../providers/auth.provider';
15
- import { UserProfile } from '../../../modules/users/schema';
16
- import type { UpdateAccountRequest } from '../../../modules/accounts/types';
17
- import {
18
- useUpdateAccountUser,
19
- useUpdateAccount,
20
- } from '../../../modules/accounts/hooks/useAccountManagement';
21
- import {
22
- LANGUAGE_OPTIONS,
23
- TIME_FORMAT_OPTIONS,
24
- NOTIFICATION_TYPES,
25
- } from '../constants';
26
- import { useTimezones } from '../../../modules/accounts/hooks/useTimezones';
27
- import { useCurrencies } from '../../../modules/accounts/hooks/useCurrencies';
28
-
29
- interface PreferencesFormValues {
30
- language: string;
31
- currency: string;
32
- timezone: string;
33
- timeFormat: string;
34
- receive_sms: boolean;
35
- receive_email: boolean;
36
- }
37
-
38
- interface PreferencesSectionProps {
39
- onClose: () => void;
40
- }
41
-
42
- export function PreferencesSection({ onClose }: PreferencesSectionProps) {
43
- const { data: timezones = [] } = useTimezones();
44
- const { data: currencies = [] } = useCurrencies();
45
- const { user, account } = useAuth();
46
- const canEditPreferences =
47
- user?.profile === UserProfile.owner || user?.profile === UserProfile.admin;
48
- const updateAccountUser = useUpdateAccountUser();
49
- const updateAccount = useUpdateAccount();
50
- const [confirmGlobalOpen, setConfirmGlobalOpen] = useState(false);
51
- const [pendingSubmit, setPendingSubmit] = useState<{
52
- data: PreferencesFormValues;
53
- accountFields: UpdateAccountRequest;
54
- } | null>(null);
55
- const [isSaving, setIsSaving] = useState(false);
56
-
57
- const { watch, setValue, handleSubmit, reset, formState: { dirtyFields } } =
58
- useForm<PreferencesFormValues>({
59
- defaultValues: {
60
- language: '',
61
- currency: '',
62
- timezone: '',
63
- timeFormat: '24h',
64
- receive_sms: false,
65
- receive_email: false,
66
- },
67
- });
68
-
69
- useEffect(() => {
70
- if (!user || !account) return;
71
- reset({
72
- language: LANGUAGE_OPTIONS.find(
73
- (o) => o.apiValue === user.language?.toLowerCase()
74
- )?.value ?? 'pt-BR',
75
- currency: account.currency,
76
- timezone: account.timezone,
77
- timeFormat: '24h',
78
- receive_sms: user.receive_sms ?? false,
79
- receive_email: user.receive_email ?? false,
80
- });
81
- }, [user, account, reset]);
82
-
83
- const receiveSms = watch('receive_sms');
84
- const receiveEmail = watch('receive_email');
85
-
86
- const doSave = async (data: PreferencesFormValues, accountFields: UpdateAccountRequest) => {
87
- setIsSaving(true);
88
- try {
89
- const apiLanguage = LANGUAGE_OPTIONS.find((o) => o.value === data.language)?.apiValue ?? data.language.toLowerCase();
90
- await updateAccountUser.mutateAsync({
91
- language: apiLanguage,
92
- receive_sms: data.receive_sms,
93
- receive_email: data.receive_email,
94
- });
95
-
96
- if (canEditPreferences && Object.keys(accountFields).length > 0) {
97
- await updateAccount.mutateAsync(accountFields);
98
- }
99
-
100
- reset(data);
101
- toast.custom((t) => (
102
- <Toast variant="success" message="Preferências salvas com sucesso" toastId={t} />
103
- ));
104
- onClose();
105
- } catch (error) {
106
- toast.custom((t) => (
107
- <Toast
108
- variant="error"
109
- message={error instanceof Error ? error.message : 'Erro ao salvar preferências'}
110
- toastId={t}
111
- />
112
- ));
113
- } finally {
114
- setIsSaving(false);
115
- }
116
- };
117
-
118
- const onSubmit = (data: PreferencesFormValues) => {
119
- const accountFields: UpdateAccountRequest = {
120
- ...(canEditPreferences && dirtyFields.currency && { currency: data.currency }),
121
- ...(canEditPreferences && dirtyFields.timezone && { timezone: data.timezone }),
122
- };
123
- const globalFieldChanged = Object.keys(accountFields).length > 0;
124
-
125
- if (globalFieldChanged) {
126
- setPendingSubmit({ data, accountFields });
127
- setConfirmGlobalOpen(true);
128
- } else {
129
- doSave(data, accountFields);
130
- }
131
- };
132
-
133
- const handleConfirmGlobal = () => {
134
- setConfirmGlobalOpen(false);
135
- if (pendingSubmit) {
136
- doSave(pendingSubmit.data, pendingSubmit.accountFields);
137
- setPendingSubmit(null);
138
- }
139
- };
140
-
141
- const handleCancelGlobal = () => {
142
- setConfirmGlobalOpen(false);
143
- setPendingSubmit(null);
144
- };
145
-
146
- return (
147
- <>
148
- <form onSubmit={handleSubmit(onSubmit)} className="lg:h-full lg:flex lg:flex-col">
149
- <div className="absolute inset-0 overflow-y-auto overscroll-contain px-4 pb-36 lg:p-5 lg:static lg:flex-1 lg:min-h-0 lg:overscroll-auto flex flex-col gap-5 lg:gap-6">
150
- <span className="paragraph-medium-semibold text-gray-950">Preferências</span>
151
- <div className="flex flex-col gap-8">
152
- <div className="flex flex-col lg:flex-row items-center gap-3">
153
- <ComboboxField
154
- label="Idioma"
155
- placeholder="Selecione o idioma"
156
- searchPlaceholder="Buscar idioma..."
157
- containerClassName="w-full"
158
- options={LANGUAGE_OPTIONS}
159
- value={watch('language')}
160
- onChange={(value) => setValue('language', value, { shouldDirty: true })}
161
- />
162
- <ComboboxField
163
- label="Moeda"
164
- placeholder="Selecione a moeda"
165
- searchPlaceholder="Buscar moeda..."
166
- containerClassName="w-full"
167
- options={currencies}
168
- value={watch('currency')}
169
- onChange={(value) => setValue('currency', value, { shouldDirty: true })}
170
- icon={!canEditPreferences ? <IconLock className="size-4 text-gray-400" /> : undefined}
171
- disabled={!canEditPreferences}
172
- />
173
- </div>
174
-
175
- <Separator />
176
-
177
- <div className="flex flex-col gap-4">
178
- <span className="paragraph-medium-semibold text-gray-600">Exibição hora</span>
179
- <div className="flex flex-col lg:flex-row items-center gap-3">
180
- <ComboboxField
181
- label="Fuso horário"
182
- placeholder="Selecione o fuso horário"
183
- searchPlaceholder="Buscar fuso horário..."
184
- containerClassName="w-full"
185
- options={timezones}
186
- value={watch('timezone')}
187
- onChange={(value) => setValue('timezone', value, { shouldDirty: true })}
188
- icon={!canEditPreferences ? <IconLock className="size-4 text-gray-400" /> : undefined}
189
- disabled={!canEditPreferences}
190
- />
191
- <SelectField
192
- label="Formato hora"
193
- placeholder="Selecione o formato"
194
- className="h-10!"
195
- containerClassName="w-full"
196
- options={TIME_FORMAT_OPTIONS}
197
- value={watch('timeFormat')}
198
- onChange={(value) =>
199
- setValue('timeFormat', value as string, { shouldDirty: true })
200
- }
201
- icon={<IconLock className="size-4 text-gray-400" />}
202
- disabled
203
- />
204
- </div>
205
- </div>
206
-
207
- <Separator />
208
-
209
- <div className="flex flex-col gap-4">
210
- <span className="paragraph-medium-semibold text-gray-600">Notificações</span>
211
- <div className="flex flex-col gap-3">
212
- <div className="flex flex-col border border-gray-200 rounded-lg">
213
- <SwitchOptionFieldWithIcon
214
- icon={IconMessageCircle}
215
- label="WhatsApp"
216
- description="Receber notificações importantes por WhatsApp"
217
- checked={receiveSms}
218
- onCheckedChange={(val: boolean) => setValue('receive_sms', val, { shouldDirty: true })}
219
- />
220
- <SwitchOptionFieldWithIcon
221
- icon={IconMail}
222
- label="E-mail"
223
- description="Receber notificações e atualizações por email"
224
- checked={receiveEmail}
225
- onCheckedChange={(val: boolean) => setValue('receive_email', val, { shouldDirty: true })}
226
- hideBorderBottom={true}
227
- />
228
- </div>
229
- <div className="flex flex-col gap-4 p-5 pb-6 rounded-lg bg-gray-50">
230
- <span className="paragraph-small-semibold text-gray-950">
231
- Tipos de notificações incluem:
232
- </span>
233
- <div className="flex flex-col">
234
- {NOTIFICATION_TYPES.map((type) => (
235
- <div key={type} className="flex items-center gap-2">
236
- <div className="size-1 rounded-full bg-gray-600" />
237
- <span className="paragraph-small-medium text-gray-600">{type}</span>
238
- </div>
239
- ))}
240
- </div>
241
- </div>
242
- </div>
243
- </div>
244
- </div>
245
- </div>
246
-
247
- <div className="fixed bottom-0 left-0 right-0 lg:static flex items-center h-20 px-4 lg:px-5 border-t border-gray-200 justify-between bg-white z-10">
248
- <Button
249
- variant="secondary"
250
- className="h-10!"
251
- type="button"
252
- disabled={isSaving}
253
- onClick={() => {
254
- reset();
255
- onClose();
256
- }}
257
- >
258
- Cancelar
259
- </Button>
260
- <Button className="h-10!" type="submit" disabled={isSaving}>
261
- {isSaving ? 'Salvando...' : 'Salvar alterações'}
262
- </Button>
263
- </div>
264
- </form>
265
-
266
- <ConfirmGlobalPreferencesModal
267
- open={confirmGlobalOpen}
268
- onConfirm={handleConfirmGlobal}
269
- onCancel={handleCancelGlobal}
270
- />
271
- </>
272
- );
273
- }
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import { useForm } from 'react-hook-form';
5
+ import { toast } from 'sonner';
6
+ import { IconLock, IconMail, IconMessageCircle } from '@tabler/icons-react';
7
+ import { Button } from '../../ui/buttons/Button';
8
+ import { Separator } from '../../ui/data-display/Separator';
9
+ import { Toast } from '../../ui/feedback/Toast';
10
+ import { ComboboxField } from '../../ui/form/ComboboxField';
11
+ import { SelectField } from '../../ui/form/SelectField';
12
+ import SwitchOptionFieldWithIcon from '../../ui/form/SwitchOptionFieldWithIcon';
13
+ import ConfirmGlobalPreferencesModal from '../ConfirmGlobalPreferencesModal';
14
+ import { useAuth } from '../../../providers/auth.provider';
15
+ import { UserProfile } from '../../../modules/users/schema';
16
+ import type { UpdateAccountRequest } from '../../../modules/accounts/types';
17
+ import {
18
+ useUpdateAccountUser,
19
+ useUpdateAccount,
20
+ } from '../../../modules/accounts/hooks/useAccountManagement';
21
+ import {
22
+ LANGUAGE_OPTIONS,
23
+ TIME_FORMAT_OPTIONS,
24
+ NOTIFICATION_TYPES,
25
+ } from '../constants';
26
+ import { useTimezones } from '../../../modules/accounts/hooks/useTimezones';
27
+ import { useCurrencies } from '../../../modules/accounts/hooks/useCurrencies';
28
+
29
+ interface PreferencesFormValues {
30
+ language: string;
31
+ currency: string;
32
+ timezone: string;
33
+ timeFormat: string;
34
+ receive_sms: boolean;
35
+ receive_email: boolean;
36
+ }
37
+
38
+ interface PreferencesSectionProps {
39
+ onClose: () => void;
40
+ }
41
+
42
+ export function PreferencesSection({ onClose }: PreferencesSectionProps) {
43
+ const { data: timezones = [] } = useTimezones();
44
+ const { data: currencies = [] } = useCurrencies();
45
+ const { user, account } = useAuth();
46
+ const canEditPreferences =
47
+ user?.profile === UserProfile.owner || user?.profile === UserProfile.admin;
48
+ const updateAccountUser = useUpdateAccountUser();
49
+ const updateAccount = useUpdateAccount();
50
+ const [confirmGlobalOpen, setConfirmGlobalOpen] = useState(false);
51
+ const [pendingSubmit, setPendingSubmit] = useState<{
52
+ data: PreferencesFormValues;
53
+ accountFields: UpdateAccountRequest;
54
+ } | null>(null);
55
+ const [isSaving, setIsSaving] = useState(false);
56
+
57
+ const { watch, setValue, handleSubmit, reset, formState: { dirtyFields } } =
58
+ useForm<PreferencesFormValues>({
59
+ defaultValues: {
60
+ language: '',
61
+ currency: '',
62
+ timezone: '',
63
+ timeFormat: '24h',
64
+ receive_sms: false,
65
+ receive_email: false,
66
+ },
67
+ });
68
+
69
+ useEffect(() => {
70
+ if (!user || !account) return;
71
+ reset({
72
+ language: LANGUAGE_OPTIONS.find(
73
+ (o) => o.apiValue === user.language?.toLowerCase()
74
+ )?.value ?? 'pt-BR',
75
+ currency: account.currency,
76
+ timezone: account.timezone,
77
+ timeFormat: '24h',
78
+ receive_sms: user.receive_sms ?? false,
79
+ receive_email: user.receive_email ?? false,
80
+ });
81
+ }, [user, account, reset]);
82
+
83
+ const receiveSms = watch('receive_sms');
84
+ const receiveEmail = watch('receive_email');
85
+
86
+ const doSave = async (data: PreferencesFormValues, accountFields: UpdateAccountRequest) => {
87
+ setIsSaving(true);
88
+ try {
89
+ const apiLanguage = LANGUAGE_OPTIONS.find((o) => o.value === data.language)?.apiValue ?? data.language.toLowerCase();
90
+ await updateAccountUser.mutateAsync({
91
+ language: apiLanguage,
92
+ receive_sms: data.receive_sms,
93
+ receive_email: data.receive_email,
94
+ });
95
+
96
+ if (canEditPreferences && Object.keys(accountFields).length > 0) {
97
+ await updateAccount.mutateAsync(accountFields);
98
+ }
99
+
100
+ reset(data);
101
+ toast.custom((t) => (
102
+ <Toast variant="success" message="Preferências salvas com sucesso" toastId={t} />
103
+ ));
104
+ onClose();
105
+ } catch (error) {
106
+ toast.custom((t) => (
107
+ <Toast
108
+ variant="error"
109
+ message={error instanceof Error ? error.message : 'Erro ao salvar preferências'}
110
+ toastId={t}
111
+ />
112
+ ));
113
+ } finally {
114
+ setIsSaving(false);
115
+ }
116
+ };
117
+
118
+ const onSubmit = (data: PreferencesFormValues) => {
119
+ const accountFields: UpdateAccountRequest = {
120
+ ...(canEditPreferences && dirtyFields.currency && { currency: data.currency }),
121
+ ...(canEditPreferences && dirtyFields.timezone && { timezone: data.timezone }),
122
+ };
123
+ const globalFieldChanged = Object.keys(accountFields).length > 0;
124
+
125
+ if (globalFieldChanged) {
126
+ setPendingSubmit({ data, accountFields });
127
+ setConfirmGlobalOpen(true);
128
+ } else {
129
+ doSave(data, accountFields);
130
+ }
131
+ };
132
+
133
+ const handleConfirmGlobal = () => {
134
+ setConfirmGlobalOpen(false);
135
+ if (pendingSubmit) {
136
+ doSave(pendingSubmit.data, pendingSubmit.accountFields);
137
+ setPendingSubmit(null);
138
+ }
139
+ };
140
+
141
+ const handleCancelGlobal = () => {
142
+ setConfirmGlobalOpen(false);
143
+ setPendingSubmit(null);
144
+ };
145
+
146
+ return (
147
+ <>
148
+ <form onSubmit={handleSubmit(onSubmit)} className="lg:h-full lg:flex lg:flex-col">
149
+ <div className="absolute inset-0 overflow-y-auto overscroll-contain px-4 pb-36 lg:p-5 lg:static lg:flex-1 lg:min-h-0 lg:overscroll-auto flex flex-col gap-5 lg:gap-6">
150
+ <span className="paragraph-medium-semibold text-gray-950">Preferências</span>
151
+ <div className="flex flex-col gap-8">
152
+ <div className="flex flex-col lg:flex-row items-center gap-3">
153
+ <ComboboxField
154
+ label="Idioma"
155
+ placeholder="Selecione o idioma"
156
+ searchPlaceholder="Buscar idioma..."
157
+ containerClassName="w-full"
158
+ options={LANGUAGE_OPTIONS}
159
+ value={watch('language')}
160
+ onChange={(value) => setValue('language', value, { shouldDirty: true })}
161
+ />
162
+ <ComboboxField
163
+ label="Moeda"
164
+ placeholder="Selecione a moeda"
165
+ searchPlaceholder="Buscar moeda..."
166
+ containerClassName="w-full"
167
+ options={currencies}
168
+ value={watch('currency')}
169
+ onChange={(value) => setValue('currency', value, { shouldDirty: true })}
170
+ icon={!canEditPreferences ? <IconLock className="size-4 text-gray-400" /> : undefined}
171
+ disabled={!canEditPreferences}
172
+ />
173
+ </div>
174
+
175
+ <Separator />
176
+
177
+ <div className="flex flex-col gap-4">
178
+ <span className="paragraph-medium-semibold text-gray-600">Exibição hora</span>
179
+ <div className="flex flex-col lg:flex-row items-center gap-3">
180
+ <ComboboxField
181
+ label="Fuso horário"
182
+ placeholder="Selecione o fuso horário"
183
+ searchPlaceholder="Buscar fuso horário..."
184
+ containerClassName="w-full"
185
+ options={timezones}
186
+ value={watch('timezone')}
187
+ onChange={(value) => setValue('timezone', value, { shouldDirty: true })}
188
+ icon={!canEditPreferences ? <IconLock className="size-4 text-gray-400" /> : undefined}
189
+ disabled={!canEditPreferences}
190
+ />
191
+ <SelectField
192
+ label="Formato hora"
193
+ placeholder="Selecione o formato"
194
+ className="h-10!"
195
+ containerClassName="w-full"
196
+ options={TIME_FORMAT_OPTIONS}
197
+ value={watch('timeFormat')}
198
+ onChange={(value) =>
199
+ setValue('timeFormat', value as string, { shouldDirty: true })
200
+ }
201
+ icon={<IconLock className="size-4 text-gray-400" />}
202
+ disabled
203
+ />
204
+ </div>
205
+ </div>
206
+
207
+ <Separator />
208
+
209
+ <div className="flex flex-col gap-4">
210
+ <span className="paragraph-medium-semibold text-gray-600">Notificações</span>
211
+ <div className="flex flex-col gap-3">
212
+ <div className="flex flex-col border border-gray-200 rounded-lg">
213
+ <SwitchOptionFieldWithIcon
214
+ icon={IconMessageCircle}
215
+ label="WhatsApp"
216
+ description="Receber notificações importantes por WhatsApp"
217
+ checked={receiveSms}
218
+ onCheckedChange={(val: boolean) => setValue('receive_sms', val, { shouldDirty: true })}
219
+ />
220
+ <SwitchOptionFieldWithIcon
221
+ icon={IconMail}
222
+ label="E-mail"
223
+ description="Receber notificações e atualizações por email"
224
+ checked={receiveEmail}
225
+ onCheckedChange={(val: boolean) => setValue('receive_email', val, { shouldDirty: true })}
226
+ hideBorderBottom={true}
227
+ />
228
+ </div>
229
+ <div className="flex flex-col gap-4 p-5 pb-6 rounded-lg bg-gray-50">
230
+ <span className="paragraph-small-semibold text-gray-950">
231
+ Tipos de notificações incluem:
232
+ </span>
233
+ <div className="flex flex-col">
234
+ {NOTIFICATION_TYPES.map((type) => (
235
+ <div key={type} className="flex items-center gap-2">
236
+ <div className="size-1 rounded-full bg-gray-600" />
237
+ <span className="paragraph-small-medium text-gray-600">{type}</span>
238
+ </div>
239
+ ))}
240
+ </div>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ </div>
245
+ </div>
246
+
247
+ <div className="fixed bottom-0 left-0 right-0 lg:static flex items-center h-20 px-4 lg:px-5 border-t border-gray-200 justify-between bg-white z-10">
248
+ <Button
249
+ variant="secondary"
250
+ className="h-10!"
251
+ type="button"
252
+ disabled={isSaving}
253
+ onClick={() => {
254
+ reset();
255
+ onClose();
256
+ }}
257
+ >
258
+ Cancelar
259
+ </Button>
260
+ <Button className="h-10!" type="submit" disabled={isSaving}>
261
+ {isSaving ? 'Salvando...' : 'Salvar alterações'}
262
+ </Button>
263
+ </div>
264
+ </form>
265
+
266
+ <ConfirmGlobalPreferencesModal
267
+ open={confirmGlobalOpen}
268
+ onConfirm={handleConfirmGlobal}
269
+ onCancel={handleCancelGlobal}
270
+ />
271
+ </>
272
+ );
273
+ }