@greatapps/common 1.1.23 → 1.1.25

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 (127) hide show
  1. package/dist/components/account/ConfigurationsMyAccountModal.mjs +121 -0
  2. package/dist/components/account/ConfigurationsMyAccountModal.mjs.map +1 -0
  3. package/dist/components/account/ConfirmGlobalPreferencesModal.mjs +45 -0
  4. package/dist/components/account/ConfirmGlobalPreferencesModal.mjs.map +1 -0
  5. package/dist/components/account/DisableTwoFactorAuthModal.mjs +67 -0
  6. package/dist/components/account/DisableTwoFactorAuthModal.mjs.map +1 -0
  7. package/dist/components/account/TwoFactorAuthModal.mjs +190 -0
  8. package/dist/components/account/TwoFactorAuthModal.mjs.map +1 -0
  9. package/dist/components/account/constants.mjs +32 -0
  10. package/dist/components/account/constants.mjs.map +1 -0
  11. package/dist/components/account/hooks/useChangeEmailForm.mjs +53 -0
  12. package/dist/components/account/hooks/useChangeEmailForm.mjs.map +1 -0
  13. package/dist/components/account/hooks/useChangePasswordForm.mjs +47 -0
  14. package/dist/components/account/hooks/useChangePasswordForm.mjs.map +1 -0
  15. package/dist/components/account/hooks/useChangePhoneForm.mjs +64 -0
  16. package/dist/components/account/hooks/useChangePhoneForm.mjs.map +1 -0
  17. package/dist/components/account/hooks/useConfigurationsModal.mjs +33 -0
  18. package/dist/components/account/hooks/useConfigurationsModal.mjs.map +1 -0
  19. package/dist/components/account/hooks/useDisableTwoFactorAuthForm.mjs +54 -0
  20. package/dist/components/account/hooks/useDisableTwoFactorAuthForm.mjs.map +1 -0
  21. package/dist/components/account/hooks/useOtpVerification.mjs +94 -0
  22. package/dist/components/account/hooks/useOtpVerification.mjs.map +1 -0
  23. package/dist/components/account/hooks/useTwoFactorAuthForm.mjs +88 -0
  24. package/dist/components/account/hooks/useTwoFactorAuthForm.mjs.map +1 -0
  25. package/dist/components/account/sections/ChangeEmailModal.mjs +180 -0
  26. package/dist/components/account/sections/ChangeEmailModal.mjs.map +1 -0
  27. package/dist/components/account/sections/ChangePasswordSection.mjs +64 -0
  28. package/dist/components/account/sections/ChangePasswordSection.mjs.map +1 -0
  29. package/dist/components/account/sections/ChangePhoneModal.mjs +182 -0
  30. package/dist/components/account/sections/ChangePhoneModal.mjs.map +1 -0
  31. package/dist/components/account/sections/MyProfileSection.mjs +312 -0
  32. package/dist/components/account/sections/MyProfileSection.mjs.map +1 -0
  33. package/dist/components/account/sections/PreferencesSection.mjs +246 -0
  34. package/dist/components/account/sections/PreferencesSection.mjs.map +1 -0
  35. package/dist/components/account/sections/SecuritySection.mjs +147 -0
  36. package/dist/components/account/sections/SecuritySection.mjs.map +1 -0
  37. package/dist/components/ui/form/ComboboxField.mjs +130 -0
  38. package/dist/components/ui/form/ComboboxField.mjs.map +1 -0
  39. package/dist/components/ui/form/FormField.mjs +103 -0
  40. package/dist/components/ui/form/FormField.mjs.map +1 -0
  41. package/dist/components/ui/form/PhoneInput.mjs +78 -0
  42. package/dist/components/ui/form/PhoneInput.mjs.map +1 -0
  43. package/dist/components/ui/form/Select.mjs.map +1 -1
  44. package/dist/components/ui/form/SelectField.mjs +80 -0
  45. package/dist/components/ui/form/SelectField.mjs.map +1 -0
  46. package/dist/components/ui/form/SwitchOptionFieldWithIcon.mjs +40 -0
  47. package/dist/components/ui/form/SwitchOptionFieldWithIcon.mjs.map +1 -0
  48. package/dist/components/ui/form/TextAreaField.mjs +56 -0
  49. package/dist/components/ui/form/TextAreaField.mjs.map +1 -0
  50. package/dist/enums/AccountSectionType.mjs +11 -0
  51. package/dist/enums/AccountSectionType.mjs.map +1 -0
  52. package/dist/hooks/useCountdownTimer.mjs +29 -0
  53. package/dist/hooks/useCountdownTimer.mjs.map +1 -0
  54. package/dist/hooks/useIsMobile.mjs +18 -0
  55. package/dist/hooks/useIsMobile.mjs.map +1 -0
  56. package/dist/hooks/usePasswordVisibility.mjs +13 -0
  57. package/dist/hooks/usePasswordVisibility.mjs.map +1 -0
  58. package/dist/index.mjs +80 -0
  59. package/dist/index.mjs.map +1 -1
  60. package/dist/modules/accounts/actions/account-management.action.mjs +163 -0
  61. package/dist/modules/accounts/actions/account-management.action.mjs.map +1 -0
  62. package/dist/modules/accounts/hooks/useAccountManagement.mjs +64 -0
  63. package/dist/modules/accounts/hooks/useAccountManagement.mjs.map +1 -0
  64. package/dist/modules/accounts/services/account.service.mjs +162 -25
  65. package/dist/modules/accounts/services/account.service.mjs.map +1 -1
  66. package/dist/modules/accounts/services/two-factor.service.mjs +77 -0
  67. package/dist/modules/accounts/services/two-factor.service.mjs.map +1 -0
  68. package/dist/server.mjs +36 -0
  69. package/dist/server.mjs.map +1 -1
  70. package/dist/utils/browser/clipboard.mjs +36 -0
  71. package/dist/utils/browser/clipboard.mjs.map +1 -0
  72. package/dist/utils/file/index.mjs +9 -0
  73. package/dist/utils/file/index.mjs.map +1 -0
  74. package/dist/utils/format/masks.mjs +56 -0
  75. package/dist/utils/format/masks.mjs.map +1 -0
  76. package/dist/utils/intl/locales.mjs +12 -0
  77. package/dist/utils/intl/locales.mjs.map +1 -0
  78. package/dist/utils/intl/timezones.mjs +41 -0
  79. package/dist/utils/intl/timezones.mjs.map +1 -0
  80. package/dist/utils/validators/account.mjs +26 -0
  81. package/dist/utils/validators/account.mjs.map +1 -0
  82. package/dist/utils/validators/common.mjs +47 -0
  83. package/dist/utils/validators/common.mjs.map +1 -0
  84. package/package.json +6 -1
  85. package/src/components/account/ConfigurationsMyAccountModal.tsx +147 -0
  86. package/src/components/account/ConfirmGlobalPreferencesModal.tsx +66 -0
  87. package/src/components/account/DisableTwoFactorAuthModal.tsx +86 -0
  88. package/src/components/account/TwoFactorAuthModal.tsx +221 -0
  89. package/src/components/account/constants.ts +29 -0
  90. package/src/components/account/hooks/useChangeEmailForm.tsx +56 -0
  91. package/src/components/account/hooks/useChangePasswordForm.tsx +59 -0
  92. package/src/components/account/hooks/useChangePhoneForm.tsx +69 -0
  93. package/src/components/account/hooks/useConfigurationsModal.ts +40 -0
  94. package/src/components/account/hooks/useDisableTwoFactorAuthForm.tsx +69 -0
  95. package/src/components/account/hooks/useOtpVerification.tsx +116 -0
  96. package/src/components/account/hooks/useTwoFactorAuthForm.tsx +114 -0
  97. package/src/components/account/sections/ChangeEmailModal.tsx +224 -0
  98. package/src/components/account/sections/ChangePasswordSection.tsx +64 -0
  99. package/src/components/account/sections/ChangePhoneModal.tsx +229 -0
  100. package/src/components/account/sections/MyProfileSection.tsx +337 -0
  101. package/src/components/account/sections/PreferencesSection.tsx +277 -0
  102. package/src/components/account/sections/SecuritySection.tsx +160 -0
  103. package/src/components/ui/form/ComboboxField.tsx +154 -0
  104. package/src/components/ui/form/FormField.tsx +130 -0
  105. package/src/components/ui/form/PhoneInput.tsx +98 -0
  106. package/src/components/ui/form/Select.tsx +1 -0
  107. package/src/components/ui/form/SelectField.tsx +118 -0
  108. package/src/components/ui/form/SwitchOptionFieldWithIcon.tsx +47 -0
  109. package/src/components/ui/form/TextAreaField.tsx +70 -0
  110. package/src/enums/AccountSectionType.ts +6 -0
  111. package/src/hooks/useCountdownTimer.ts +41 -0
  112. package/src/hooks/useIsMobile.ts +19 -0
  113. package/src/hooks/usePasswordVisibility.ts +13 -0
  114. package/src/index.ts +70 -0
  115. package/src/modules/accounts/actions/account-management.action.ts +197 -0
  116. package/src/modules/accounts/hooks/useAccountManagement.ts +79 -0
  117. package/src/modules/accounts/services/account.service.ts +229 -50
  118. package/src/modules/accounts/services/two-factor.service.ts +98 -0
  119. package/src/modules/accounts/types.ts +157 -0
  120. package/src/server.ts +21 -0
  121. package/src/utils/browser/clipboard.ts +34 -0
  122. package/src/utils/file/index.ts +5 -0
  123. package/src/utils/format/masks.ts +52 -0
  124. package/src/utils/intl/locales.ts +15 -0
  125. package/src/utils/intl/timezones.ts +53 -0
  126. package/src/utils/validators/account.ts +36 -0
  127. package/src/utils/validators/common.ts +43 -0
@@ -0,0 +1,47 @@
1
+ import { LucideIcon } from 'lucide-react';
2
+ import { Switch } from './Switch';
3
+
4
+ type SwitchOptionFieldWithIconProps = {
5
+ icon: LucideIcon;
6
+ label: string;
7
+ description: string;
8
+ checked: boolean;
9
+ onCheckedChange: (checked: boolean) => void;
10
+ disabled?: boolean;
11
+ hideBorderBottom?: boolean;
12
+ id?: string;
13
+ };
14
+
15
+ export default function SwitchOptionFieldWithIcon({
16
+ icon: Icon,
17
+ label,
18
+ description,
19
+ checked,
20
+ onCheckedChange,
21
+ disabled = false,
22
+ hideBorderBottom = false,
23
+ id,
24
+ }: SwitchOptionFieldWithIconProps) {
25
+ return (
26
+ <label
27
+ className={`flex justify-between items-center gap-3 p-5 ${
28
+ !hideBorderBottom ? 'border-b border-gray-200' : ''
29
+ } ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}`}
30
+ >
31
+ <div className="size-10 rounded-lg bg-gray-50 flex items-center justify-center text-gray-950">
32
+ <Icon size={20} />
33
+ </div>
34
+ <div className="flex flex-col gap-2 flex-1">
35
+ <span className="paragraph-small-semibold text-gray-950">{label}</span>
36
+ <span className="paragraph-small-regular text-gray-600">{description}</span>
37
+ </div>
38
+ <Switch
39
+ id={id}
40
+ checked={disabled ? true : checked}
41
+ onCheckedChange={onCheckedChange}
42
+ disabled={disabled}
43
+ className={disabled ? 'opacity-20!' : ''}
44
+ />
45
+ </label>
46
+ );
47
+ }
@@ -0,0 +1,70 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, type ComponentPropsWithoutRef } from 'react';
4
+ import { Textarea } from './Textarea';
5
+ import { cn } from '../../../infra/utils/clsx';
6
+
7
+ interface TextAreaFieldProps extends ComponentPropsWithoutRef<'textarea'> {
8
+ label?: string;
9
+ optional?: boolean;
10
+ required?: boolean;
11
+ hintMessage?: string;
12
+ error?: boolean;
13
+ errorMessage?: string;
14
+ }
15
+
16
+ export const TextAreaField = forwardRef<HTMLTextAreaElement, TextAreaFieldProps>(
17
+ (
18
+ {
19
+ label,
20
+ optional = false,
21
+ required = false,
22
+ hintMessage,
23
+ error = false,
24
+ errorMessage,
25
+ className,
26
+ ...textareaProps
27
+ },
28
+ ref
29
+ ) => {
30
+ const finalMessage = error && errorMessage ? errorMessage : hintMessage;
31
+ const messageColor = error ? 'text-red-600' : 'text-gray-600';
32
+
33
+ return (
34
+ <div className="flex flex-col gap-1.5">
35
+ <div className="flex items-center gap-1 paragraph-xsmall-semibold">
36
+ {label && <span className="text-gray-600">{label}</span>}
37
+ {optional && <span className="text-gray-500">(Opcional)</span>}
38
+ {required && <span className="text-red-600">*</span>}
39
+ </div>
40
+
41
+ <div
42
+ className={cn(
43
+ 'flex px-4 py-3 border rounded-lg transition-colors overflow-hidden',
44
+ error
45
+ ? 'border-red-600 hover:border-red-700'
46
+ : 'border-gray-200 hover:border-gray-400 focus-within:border-gray-950'
47
+ )}
48
+ >
49
+ <Textarea
50
+ ref={ref}
51
+ className={cn(
52
+ 'paragraph-small-medium w-full text-gray-950 border-none shadow-none focus-visible:ring-0 p-0 resize-none overflow-y-auto field-sizing-fixed! min-h-0! break-words',
53
+ error && 'placeholder:text-red-400',
54
+ className
55
+ )}
56
+ {...textareaProps}
57
+ />
58
+ </div>
59
+
60
+ {finalMessage && (
61
+ <span className={cn('paragraph-xsmall-medium', messageColor)}>{finalMessage}</span>
62
+ )}
63
+ </div>
64
+ );
65
+ }
66
+ );
67
+
68
+ TextAreaField.displayName = 'TextAreaField';
69
+
70
+ export default TextAreaField;
@@ -0,0 +1,6 @@
1
+ export enum AccountSectionType {
2
+ MY_PROFILE = 'my-profile',
3
+ SECURITY = 'security',
4
+ PREFERENCES = 'preferences',
5
+ CHANGE_PASSWORD = 'change-password',
6
+ }
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState, useCallback } from 'react';
4
+
5
+ interface UseCountdownTimerOptions {
6
+ initialSeconds?: number;
7
+ onExpire?: () => void;
8
+ }
9
+
10
+ interface UseCountdownTimerReturn {
11
+ seconds: number;
12
+ isExpired: boolean;
13
+ reset: () => void;
14
+ }
15
+
16
+ export default function useCountdownTimer({
17
+ initialSeconds = 59,
18
+ onExpire,
19
+ }: UseCountdownTimerOptions = {}): UseCountdownTimerReturn {
20
+ const [seconds, setSeconds] = useState(initialSeconds);
21
+ const [isExpired, setIsExpired] = useState(false);
22
+
23
+ useEffect(() => {
24
+ if (seconds > 0) {
25
+ const interval = setInterval(() => {
26
+ setSeconds((prev) => prev - 1);
27
+ }, 1000);
28
+ return () => clearInterval(interval);
29
+ } else {
30
+ setIsExpired(true);
31
+ onExpire?.();
32
+ }
33
+ }, [seconds, onExpire]);
34
+
35
+ const reset = useCallback(() => {
36
+ setSeconds(initialSeconds);
37
+ setIsExpired(false);
38
+ }, [initialSeconds]);
39
+
40
+ return { seconds, isExpired, reset };
41
+ }
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+
5
+ export default function useIsMobile() {
6
+ const [isMobile, setIsMobile] = useState(false);
7
+
8
+ useEffect(() => {
9
+ const checkMobile = () => {
10
+ setIsMobile(window.innerWidth < 1024);
11
+ };
12
+
13
+ checkMobile();
14
+ window.addEventListener('resize', checkMobile);
15
+ return () => window.removeEventListener('resize', checkMobile);
16
+ }, []);
17
+
18
+ return isMobile;
19
+ }
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+
3
+ import { useState, useCallback } from 'react';
4
+
5
+ export default function usePasswordVisibility() {
6
+ const [showPassword, setShowPassword] = useState(false);
7
+
8
+ const togglePassword = useCallback(() => {
9
+ setShowPassword((prev) => !prev);
10
+ }, []);
11
+
12
+ return { showPassword, togglePassword };
13
+ }
package/src/index.ts CHANGED
@@ -96,3 +96,73 @@ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './comp
96
96
 
97
97
  // Store
98
98
  export { useMdSidebarStore } from './store/useMdSidebarStore';
99
+
100
+ // Enums
101
+ export { AccountSectionType } from './enums/AccountSectionType';
102
+
103
+ // Hooks
104
+ export { default as usePasswordVisibility } from './hooks/usePasswordVisibility';
105
+ export { default as useCountdownTimer } from './hooks/useCountdownTimer';
106
+ export { default as useIsMobile } from './hooks/useIsMobile';
107
+
108
+ // Utils
109
+ export { formatPhone, formatTimer, formatFullName, formatCPF, formatCardNumber } from './utils/format/masks';
110
+ export { buildTimezoneOptions } from './utils/intl/timezones';
111
+ export type { TimezoneOption } from './utils/intl/timezones';
112
+ export { buildLocaleOptions, LANGUAGE_OPTIONS as LOCALE_LANGUAGE_OPTIONS } from './utils/intl/locales';
113
+ export type { LocaleOption } from './utils/intl/locales';
114
+ export { copyToClipboard, readFromClipboard } from './utils/browser/clipboard';
115
+
116
+ // UI - Form (new widgets)
117
+ export { FormField } from './components/ui/form/FormField';
118
+ export { SelectField } from './components/ui/form/SelectField';
119
+ export { ComboboxField } from './components/ui/form/ComboboxField';
120
+ export type { ComboboxOption } from './components/ui/form/ComboboxField';
121
+ export { default as PhoneInput } from './components/ui/form/PhoneInput';
122
+ export { default as SwitchOptionFieldWithIcon } from './components/ui/form/SwitchOptionFieldWithIcon';
123
+ export { TextAreaField } from './components/ui/form/TextAreaField';
124
+
125
+ // Account Module - Hooks
126
+ export {
127
+ useUpdateAccount,
128
+ useUpdateAccountUser,
129
+ useUpdateAccountUserById,
130
+ useDeleteAccountUser,
131
+ useDeleteAccount,
132
+ } from './modules/accounts/hooks/useAccountManagement';
133
+
134
+ // Account Types
135
+ export type {
136
+ UpdateAccountRequest,
137
+ UpdateAccountUserRequest,
138
+ ChangePasswordRequest,
139
+ DeleteAccountActionResult,
140
+ DeleteUserActionResult,
141
+ UpdateAccountActionResult,
142
+ UpdateUserActionResult,
143
+ TwoFactorGenerateResult,
144
+ TwoFactorActionResult,
145
+ ContactResetResult,
146
+ } from './modules/accounts/types';
147
+
148
+ // Account Components
149
+ export { default as ConfigurationsMyAccountModal } from './components/account/ConfigurationsMyAccountModal';
150
+ export { default as TwoFactorAuthModal } from './components/account/TwoFactorAuthModal';
151
+ export { default as DisableTwoFactorAuthModal } from './components/account/DisableTwoFactorAuthModal';
152
+ export { default as ConfirmGlobalPreferencesModal } from './components/account/ConfirmGlobalPreferencesModal';
153
+ export { MyProfileSection } from './components/account/sections/MyProfileSection';
154
+ export { PreferencesSection } from './components/account/sections/PreferencesSection';
155
+ export { SecuritySection } from './components/account/sections/SecuritySection';
156
+ export { ChangePasswordSection } from './components/account/sections/ChangePasswordSection';
157
+ export { ChangeEmailModal } from './components/account/sections/ChangeEmailModal';
158
+ export { ChangePhoneModal } from './components/account/sections/ChangePhoneModal';
159
+
160
+ // Account Constants
161
+ export {
162
+ GENDER_OPTIONS,
163
+ CURRENCY_OPTIONS,
164
+ TIMEZONE_OPTIONS,
165
+ TIME_FORMAT_OPTIONS,
166
+ NOTIFICATION_TYPES,
167
+ LANGUAGE_OPTIONS,
168
+ } from './components/account/constants';
@@ -0,0 +1,197 @@
1
+ 'use server';
2
+
3
+ import { accountService } from '../services/account.service';
4
+ import { ApiError } from '../../../infra/api/types';
5
+ import type {
6
+ AccountUsersPaginationParams,
7
+ AccountUsersActionResult,
8
+ ChangePasswordActionResult,
9
+ ChangePasswordRequest,
10
+ ContactResetResult,
11
+ CreateAccountUserRequest,
12
+ DeleteAccountActionResult,
13
+ DeleteUserActionResult,
14
+ TwoFactorActionResult,
15
+ TwoFactorGenerateResult,
16
+ UpdateAccountActionResult,
17
+ UpdateAccountRequest,
18
+ UpdateAccountUserRequest,
19
+ UpdateUserActionResult,
20
+ } from '../types';
21
+ import { twoFactorService } from '../services/two-factor.service';
22
+
23
+ export async function listAccountUsersAction(
24
+ params?: AccountUsersPaginationParams
25
+ ): Promise<AccountUsersActionResult> {
26
+ try {
27
+ const { users, total } = await accountService.listAccountUsers(params);
28
+ return { success: true, data: users, total };
29
+ } catch (error) {
30
+ if (error instanceof ApiError) return { success: false, error: error.message };
31
+ return { success: false, error: 'Erro ao listar usuários. Tente novamente.' };
32
+ }
33
+ }
34
+
35
+ export async function updateUserAction(
36
+ user: UpdateAccountUserRequest
37
+ ): Promise<UpdateUserActionResult> {
38
+ try {
39
+ const result = await accountService.updateAccountUser(user);
40
+ return { success: true, data: result };
41
+ } catch (error) {
42
+ if (error instanceof ApiError) return { success: false, error: error.message };
43
+ return { success: false, error: 'Erro ao atualizar usuário. Tente novamente.' };
44
+ }
45
+ }
46
+
47
+ export async function updateAccountUserByIdAction(
48
+ userId: number,
49
+ user: UpdateAccountUserRequest
50
+ ): Promise<UpdateUserActionResult> {
51
+ try {
52
+ const result = await accountService.updateAccountUserById(userId, user);
53
+ return { success: true, data: result };
54
+ } catch (error) {
55
+ if (error instanceof ApiError) return { success: false, error: error.message };
56
+ return { success: false, error: 'Erro ao atualizar usuário. Tente novamente.' };
57
+ }
58
+ }
59
+
60
+ export async function updateAccountAction(
61
+ data: UpdateAccountRequest
62
+ ): Promise<UpdateAccountActionResult> {
63
+ try {
64
+ await accountService.updateAccount(data);
65
+ return { success: true };
66
+ } catch (error) {
67
+ if (error instanceof ApiError) return { success: false, error: error.message };
68
+ return { success: false, error: 'Erro ao atualizar conta. Tente novamente.' };
69
+ }
70
+ }
71
+
72
+ export async function deleteAccountUserAction(userId: number): Promise<DeleteUserActionResult> {
73
+ try {
74
+ await accountService.deleteAccountUser(userId);
75
+ return { success: true };
76
+ } catch (error) {
77
+ if (error instanceof ApiError) return { success: false, error: error.message };
78
+ return { success: false, error: 'Erro ao remover usuário. Tente novamente.' };
79
+ }
80
+ }
81
+
82
+ export async function deleteAccountAction(): Promise<DeleteAccountActionResult> {
83
+ try {
84
+ await accountService.deleteAccount();
85
+ return { success: true };
86
+ } catch (error) {
87
+ if (error instanceof ApiError) return { success: false, error: error.message };
88
+ return { success: false, error: 'Erro ao excluir conta. Tente novamente.' };
89
+ }
90
+ }
91
+
92
+ export async function createAccountUserAction(
93
+ user: CreateAccountUserRequest
94
+ ): Promise<AccountUsersActionResult> {
95
+ try {
96
+ const result = await accountService.createAccountUser(user);
97
+ return { success: true, data: result.data };
98
+ } catch (error) {
99
+ if (error instanceof ApiError) return { success: false, error: error.message };
100
+ return { success: false, error: 'Erro ao criar usuário. Tente novamente.' };
101
+ }
102
+ }
103
+
104
+ export async function changePasswordAction(
105
+ data: ChangePasswordRequest
106
+ ): Promise<ChangePasswordActionResult> {
107
+ try {
108
+ await accountService.changePassword(data);
109
+ return { success: true };
110
+ } catch (error) {
111
+ if (error instanceof ApiError) return { success: false, error: error.message };
112
+ return { success: false, error: 'Erro ao alterar senha. Tente novamente.' };
113
+ }
114
+ }
115
+
116
+ export async function requestEmailChangeAction(newEmail: string): Promise<ContactResetResult> {
117
+ try {
118
+ await accountService.requestContactReset('email', newEmail);
119
+ return { success: true };
120
+ } catch (error) {
121
+ if (error instanceof ApiError) return { success: false, error: error.message };
122
+ return { success: false, error: 'E-mail já existente ou ocorreu um erro. Tente novamente mais tarde.' };
123
+ }
124
+ }
125
+
126
+ export async function confirmEmailChangeAction(
127
+ token: string,
128
+ newEmail: string
129
+ ): Promise<ContactResetResult> {
130
+ try {
131
+ await accountService.confirmContactReset('email', token, newEmail);
132
+ return { success: true };
133
+ } catch (error) {
134
+ if (error instanceof ApiError) return { success: false, error: error.message };
135
+ return { success: false, error: 'Código inválido ou expirado. Tente novamente.' };
136
+ }
137
+ }
138
+
139
+ export async function requestPhoneChangeAction(newPhone: string): Promise<ContactResetResult> {
140
+ try {
141
+ await accountService.requestContactReset('phone', newPhone);
142
+ return { success: true };
143
+ } catch (error) {
144
+ if (error instanceof ApiError) return { success: false, error: error.message };
145
+ return { success: false, error: 'Número já existente ou ocorreu um erro. Tente novamente mais tarde.' };
146
+ }
147
+ }
148
+
149
+ export async function confirmPhoneChangeAction(
150
+ token: string,
151
+ newPhone: string
152
+ ): Promise<ContactResetResult> {
153
+ try {
154
+ await accountService.confirmContactReset('phone', token, newPhone);
155
+ return { success: true };
156
+ } catch (error) {
157
+ if (error instanceof ApiError) return { success: false, error: error.message };
158
+ return { success: false, error: 'Código inválido ou expirado. Tente novamente.' };
159
+ }
160
+ }
161
+
162
+ export async function generateTwoFactorAction(): Promise<TwoFactorGenerateResult> {
163
+ try {
164
+ const result = await twoFactorService.generate();
165
+ return { success: true, qrcode: result.data.qrcode, backupCodes: result.data.backup_codes };
166
+ } catch (error) {
167
+ if (error instanceof ApiError) return { success: false, error: error.message };
168
+ return { success: false, error: 'Erro ao gerar QR Code. Tente novamente.' };
169
+ }
170
+ }
171
+
172
+ export async function confirmTwoFactorAction(code: string): Promise<TwoFactorActionResult> {
173
+ try {
174
+ await twoFactorService.confirm(code);
175
+ return { success: true };
176
+ } catch (error) {
177
+ if (error instanceof ApiError) return { success: false, error: error.message };
178
+ return { success: false, error: 'Código inválido. Tente novamente.' };
179
+ }
180
+ }
181
+
182
+ export async function disableTwoFactorAction(code: string): Promise<TwoFactorActionResult> {
183
+ try {
184
+ await twoFactorService.verify(code);
185
+ } catch (error) {
186
+ if (error instanceof ApiError) return { success: false, error: error.message };
187
+ return { success: false, error: 'Código inválido. Tente novamente.' };
188
+ }
189
+
190
+ try {
191
+ await twoFactorService.disable();
192
+ return { success: true };
193
+ } catch (error) {
194
+ if (error instanceof ApiError) return { success: false, error: error.message };
195
+ return { success: false, error: 'Erro ao desabilitar 2FA. Tente novamente.' };
196
+ }
197
+ }
@@ -0,0 +1,79 @@
1
+ 'use client';
2
+
3
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
4
+ import { USER_QUERY_KEY } from '../../auth/hooks/useUserQuery';
5
+ import type {
6
+ UpdateAccountRequest,
7
+ UpdateAccountUserRequest,
8
+ DeleteUserActionResult,
9
+ DeleteAccountActionResult,
10
+ } from '../types';
11
+ import {
12
+ updateUserAction,
13
+ updateAccountAction,
14
+ updateAccountUserByIdAction,
15
+ deleteAccountUserAction,
16
+ deleteAccountAction,
17
+ } from '../actions/account-management.action';
18
+ import type { UseMutationOptions } from '@tanstack/react-query';
19
+
20
+ const ACCOUNT_USERS_QUERY_KEY = ['account-users'];
21
+
22
+ export function useUpdateAccount() {
23
+ const queryClient = useQueryClient();
24
+
25
+ return useMutation({
26
+ mutationFn: (data: UpdateAccountRequest) => updateAccountAction(data),
27
+ onSuccess: () => {
28
+ queryClient.invalidateQueries({ queryKey: ACCOUNT_USERS_QUERY_KEY });
29
+ },
30
+ });
31
+ }
32
+
33
+ export function useUpdateAccountUser() {
34
+ const queryClient = useQueryClient();
35
+
36
+ return useMutation({
37
+ mutationFn: updateUserAction,
38
+ onSuccess: () => {
39
+ queryClient.invalidateQueries({ queryKey: ACCOUNT_USERS_QUERY_KEY });
40
+ queryClient.invalidateQueries({ queryKey: USER_QUERY_KEY });
41
+ },
42
+ });
43
+ }
44
+
45
+ export function useUpdateAccountUserById() {
46
+ const queryClient = useQueryClient();
47
+
48
+ return useMutation({
49
+ mutationFn: ({ userId, ...user }: UpdateAccountUserRequest & { userId: number }) =>
50
+ updateAccountUserByIdAction(userId, user),
51
+ onSuccess: () => {
52
+ queryClient.invalidateQueries({ queryKey: ACCOUNT_USERS_QUERY_KEY });
53
+ },
54
+ });
55
+ }
56
+
57
+ export function useDeleteAccountUser(
58
+ options?: UseMutationOptions<DeleteUserActionResult, Error, number>
59
+ ) {
60
+ const queryClient = useQueryClient();
61
+
62
+ return useMutation({
63
+ mutationFn: (userId: number) => deleteAccountUserAction(userId),
64
+ ...options,
65
+ onSuccess: (...params) => {
66
+ options?.onSuccess?.(...params);
67
+ queryClient.invalidateQueries({ queryKey: ACCOUNT_USERS_QUERY_KEY });
68
+ },
69
+ });
70
+ }
71
+
72
+ export function useDeleteAccount(
73
+ options?: UseMutationOptions<DeleteAccountActionResult, Error, void>
74
+ ) {
75
+ return useMutation({
76
+ mutationFn: () => deleteAccountAction(),
77
+ ...options,
78
+ });
79
+ }