@greatapps/common 1.1.44 → 1.1.48

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 (35) hide show
  1. package/dist/components/layouts/UsersSelectorPopover.mjs +92 -21
  2. package/dist/components/layouts/UsersSelectorPopover.mjs.map +1 -1
  3. package/dist/index.mjs +3 -0
  4. package/dist/index.mjs.map +1 -1
  5. package/dist/modules/accounts/actions/account-management.action.mjs +80 -29
  6. package/dist/modules/accounts/actions/account-management.action.mjs.map +1 -1
  7. package/dist/modules/projects/actions/add-project-user.action.mjs +9 -0
  8. package/dist/modules/projects/actions/add-project-user.action.mjs.map +1 -0
  9. package/dist/modules/projects/actions/list-available-users.action.mjs +9 -0
  10. package/dist/modules/projects/actions/list-available-users.action.mjs.map +1 -0
  11. package/dist/modules/projects/actions/list-project-users.action.mjs +9 -0
  12. package/dist/modules/projects/actions/list-project-users.action.mjs.map +1 -0
  13. package/dist/modules/projects/actions/remove-project-user.action.mjs +9 -0
  14. package/dist/modules/projects/actions/remove-project-user.action.mjs.map +1 -0
  15. package/dist/modules/projects/hooks/project-users.hook.mjs +70 -0
  16. package/dist/modules/projects/hooks/project-users.hook.mjs.map +1 -0
  17. package/dist/modules/projects/services/project-users.service.mjs +67 -0
  18. package/dist/modules/projects/services/project-users.service.mjs.map +1 -0
  19. package/dist/modules/projects/types.mjs +22 -0
  20. package/dist/modules/projects/types.mjs.map +1 -0
  21. package/dist/server.mjs +10 -0
  22. package/dist/server.mjs.map +1 -1
  23. package/package.json +1 -1
  24. package/src/components/layouts/UsersSelectorPopover.tsx +163 -63
  25. package/src/index.ts +2 -0
  26. package/src/modules/accounts/actions/account-management.action.ts +112 -48
  27. package/src/modules/accounts/types.ts +1 -0
  28. package/src/modules/projects/actions/add-project-user.action.ts +8 -0
  29. package/src/modules/projects/actions/list-available-users.action.ts +8 -0
  30. package/src/modules/projects/actions/list-project-users.action.ts +8 -0
  31. package/src/modules/projects/actions/remove-project-user.action.ts +8 -0
  32. package/src/modules/projects/hooks/project-users.hook.ts +81 -0
  33. package/src/modules/projects/services/project-users.service.ts +94 -0
  34. package/src/modules/projects/types.ts +28 -0
  35. package/src/server.ts +7 -0
@@ -1,7 +1,7 @@
1
- 'use server';
1
+ "use server";
2
2
 
3
- import { accountService } from '../services/account.service';
4
- import { ApiError } from '../../../infra/api/types';
3
+ import { accountService } from "../services/account.service";
4
+ import { ApiError } from "../../../infra/api/types";
5
5
  import type {
6
6
  AccountUsersPaginationParams,
7
7
  AccountUsersActionResult,
@@ -17,8 +17,8 @@ import type {
17
17
  UpdateAccountRequest,
18
18
  UpdateAccountUserRequest,
19
19
  UpdateUserActionResult,
20
- } from '../types';
21
- import { twoFactorService } from '../services/two-factor.service';
20
+ } from "../types";
21
+ import { twoFactorService } from "../services/two-factor.service";
22
22
 
23
23
  export async function listAccountUsersAction(
24
24
  params?: AccountUsersPaginationParams
@@ -27,8 +27,12 @@ export async function listAccountUsersAction(
27
27
  const { users, total } = await accountService.listAccountUsers(params);
28
28
  return { success: true, data: users, total };
29
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.' };
30
+ if (error instanceof ApiError)
31
+ return { success: false, error: error.message };
32
+ return {
33
+ success: false,
34
+ error: "Erro ao listar usuários. Tente novamente.",
35
+ };
32
36
  }
33
37
  }
34
38
 
@@ -39,8 +43,12 @@ export async function updateUserAction(
39
43
  const result = await accountService.updateAccountUser(user);
40
44
  return { success: true, data: result };
41
45
  } 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.' };
46
+ if (error instanceof ApiError)
47
+ return { success: false, error: error.message };
48
+ return {
49
+ success: false,
50
+ error: "Erro ao atualizar usuário. Tente novamente.",
51
+ };
44
52
  }
45
53
  }
46
54
 
@@ -52,8 +60,12 @@ export async function updateAccountUserByIdAction(
52
60
  const result = await accountService.updateAccountUserById(userId, user);
53
61
  return { success: true, data: result };
54
62
  } 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.' };
63
+ if (error instanceof ApiError)
64
+ return { success: false, error: error.message };
65
+ return {
66
+ success: false,
67
+ error: "Erro ao atualizar usuário. Tente novamente.",
68
+ };
57
69
  }
58
70
  }
59
71
 
@@ -64,18 +76,28 @@ export async function updateAccountAction(
64
76
  await accountService.updateAccount(data);
65
77
  return { success: true };
66
78
  } catch (error) {
67
- if (error instanceof ApiError) return { success: false, error: error.message };
68
- return { success: false, error: 'Erro ao atualizar conta. Tente novamente.' };
79
+ if (error instanceof ApiError)
80
+ return { success: false, error: error.message };
81
+ return {
82
+ success: false,
83
+ error: "Erro ao atualizar conta. Tente novamente.",
84
+ };
69
85
  }
70
86
  }
71
87
 
72
- export async function deleteAccountUserAction(userId: number): Promise<DeleteUserActionResult> {
88
+ export async function deleteAccountUserAction(
89
+ userId: number
90
+ ): Promise<DeleteUserActionResult> {
73
91
  try {
74
92
  await accountService.deleteAccountUser(userId);
75
93
  return { success: true };
76
94
  } 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.' };
95
+ if (error instanceof ApiError)
96
+ return { success: false, error: error.message };
97
+ return {
98
+ success: false,
99
+ error: "Erro ao remover usuário. Tente novamente.",
100
+ };
79
101
  }
80
102
  }
81
103
 
@@ -84,8 +106,9 @@ export async function deleteAccountAction(): Promise<DeleteAccountActionResult>
84
106
  await accountService.deleteAccount();
85
107
  return { success: true };
86
108
  } catch (error) {
87
- if (error instanceof ApiError) return { success: false, error: error.message };
88
- return { success: false, error: 'Erro ao excluir conta. Tente novamente.' };
109
+ if (error instanceof ApiError)
110
+ return { success: false, error: error.message };
111
+ return { success: false, error: "Erro ao excluir conta. Tente novamente." };
89
112
  }
90
113
  }
91
114
 
@@ -96,8 +119,9 @@ export async function createAccountUserAction(
96
119
  const result = await accountService.createAccountUser(user);
97
120
  return { success: true, data: result.data };
98
121
  } 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.' };
122
+ if (error instanceof ApiError)
123
+ return { success: false, error: error.message };
124
+ return { success: false, error: "Erro ao criar usuário. Tente novamente." };
101
125
  }
102
126
  }
103
127
 
@@ -108,18 +132,26 @@ export async function changePasswordAction(
108
132
  await accountService.changePassword(data);
109
133
  return { success: true };
110
134
  } catch (error) {
111
- if (error instanceof ApiError) return { success: false, error: error.message };
112
- return { success: false, error: 'Erro ao alterar senha. Tente novamente.' };
135
+ if (error instanceof ApiError)
136
+ return { success: false, error: error.message };
137
+ return { success: false, error: "Erro ao alterar senha. Tente novamente." };
113
138
  }
114
139
  }
115
140
 
116
- export async function requestEmailChangeAction(newEmail: string): Promise<ContactResetResult> {
141
+ export async function requestEmailChangeAction(
142
+ newEmail: string
143
+ ): Promise<ContactResetResult> {
117
144
  try {
118
- await accountService.requestContactReset('email', newEmail);
145
+ await accountService.requestContactReset("email", newEmail);
119
146
  return { success: true };
120
147
  } 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.' };
148
+ if (error instanceof ApiError)
149
+ return { success: false, error: error.message };
150
+ return {
151
+ success: false,
152
+ error:
153
+ "E-mail já existente ou ocorreu um erro. Tente novamente mais tarde.",
154
+ };
123
155
  }
124
156
  }
125
157
 
@@ -128,21 +160,32 @@ export async function confirmEmailChangeAction(
128
160
  newEmail: string
129
161
  ): Promise<ContactResetResult> {
130
162
  try {
131
- await accountService.confirmContactReset('email', token, newEmail);
163
+ await accountService.confirmContactReset("email", token, newEmail);
132
164
  return { success: true };
133
165
  } 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.' };
166
+ if (error instanceof ApiError)
167
+ return { success: false, error: error.message };
168
+ return {
169
+ success: false,
170
+ error: "Código inválido ou expirado. Tente novamente.",
171
+ };
136
172
  }
137
173
  }
138
174
 
139
- export async function requestPhoneChangeAction(newPhone: string): Promise<ContactResetResult> {
175
+ export async function requestPhoneChangeAction(
176
+ newPhone: string
177
+ ): Promise<ContactResetResult> {
140
178
  try {
141
- await accountService.requestContactReset('phone', newPhone);
179
+ await accountService.requestContactReset("phone", newPhone);
142
180
  return { success: true };
143
181
  } 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.' };
182
+ if (error instanceof ApiError)
183
+ return { success: false, error: error.message };
184
+ return {
185
+ success: false,
186
+ error:
187
+ "Número já existente ou ocorreu um erro. Tente novamente mais tarde.",
188
+ };
146
189
  }
147
190
  }
148
191
 
@@ -151,47 +194,68 @@ export async function confirmPhoneChangeAction(
151
194
  newPhone: string
152
195
  ): Promise<ContactResetResult> {
153
196
  try {
154
- await accountService.confirmContactReset('phone', token, newPhone);
197
+ await accountService.confirmContactReset("phone", token, newPhone);
155
198
  return { success: true };
156
199
  } 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.' };
200
+ if (error instanceof ApiError)
201
+ return { success: false, error: error.message };
202
+ return {
203
+ success: false,
204
+ error: "Código inválido ou expirado. Tente novamente.",
205
+ };
159
206
  }
160
207
  }
161
208
 
162
209
  export async function generateTwoFactorAction(): Promise<TwoFactorGenerateResult> {
163
210
  try {
164
211
  const result = await twoFactorService.generate();
165
- return { success: true, qrcode: result.data.qrcode, backupCodes: result.data.backup_codes };
212
+ return {
213
+ success: true,
214
+ qrcode: result.data.qrcode,
215
+ backupCodes: result.data.backup_codes,
216
+ };
166
217
  } 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.' };
218
+ if (error instanceof ApiError)
219
+ return { success: false, error: error.message };
220
+ return { success: false, error: "Erro ao gerar QR Code. Tente novamente." };
169
221
  }
170
222
  }
171
223
 
172
- export async function confirmTwoFactorAction(code: string): Promise<TwoFactorActionResult> {
224
+ export async function confirmTwoFactorAction(
225
+ code: string
226
+ ): Promise<TwoFactorActionResult> {
173
227
  try {
174
228
  await twoFactorService.confirm(code);
175
- return { success: true };
229
+ await accountService.updateAccountUser({ two_factor_authentication: true });
176
230
  } catch (error) {
177
- if (error instanceof ApiError) return { success: false, error: error.message };
178
- return { success: false, error: 'Código inválido. Tente novamente.' };
231
+ if (error instanceof ApiError)
232
+ return { success: false, error: error.message };
233
+ return { success: false, error: "Código inválido. Tente novamente." };
179
234
  }
235
+
236
+ return { success: true };
180
237
  }
181
238
 
182
- export async function disableTwoFactorAction(code: string): Promise<TwoFactorActionResult> {
239
+ export async function disableTwoFactorAction(
240
+ code: string
241
+ ): Promise<TwoFactorActionResult> {
183
242
  try {
184
243
  await twoFactorService.verify(code);
185
244
  } catch (error) {
186
- if (error instanceof ApiError) return { success: false, error: error.message };
187
- return { success: false, error: 'Código inválido. Tente novamente.' };
245
+ if (error instanceof ApiError)
246
+ return { success: false, error: error.message };
247
+ return { success: false, error: "Código inválido. Tente novamente." };
188
248
  }
189
249
 
190
250
  try {
191
251
  await twoFactorService.disable();
192
252
  return { success: true };
193
253
  } catch (error) {
194
- if (error instanceof ApiError) return { success: false, error: error.message };
195
- return { success: false, error: 'Erro ao desabilitar 2FA. Tente novamente.' };
254
+ if (error instanceof ApiError)
255
+ return { success: false, error: error.message };
256
+ return {
257
+ success: false,
258
+ error: "Erro ao desabilitar 2FA. Tente novamente.",
259
+ };
196
260
  }
197
261
  }
@@ -108,6 +108,7 @@ export interface UpdateAccountUserRequest {
108
108
  receive_email?: boolean;
109
109
  photo?: File | string | null;
110
110
  projects?: number[];
111
+ two_factor_authentication?: boolean;
111
112
  }
112
113
 
113
114
  export interface UpdateAccountUserResponse {
@@ -0,0 +1,8 @@
1
+ 'use server';
2
+
3
+ import { projectUsersService } from '../services/project-users.service';
4
+ import type { AddRemoveProjectUsersParams } from '../types';
5
+
6
+ export async function addProjectUserAction(params: AddRemoveProjectUsersParams): Promise<void> {
7
+ return projectUsersService.addToProject(params);
8
+ }
@@ -0,0 +1,8 @@
1
+ 'use server';
2
+
3
+ import { projectUsersService } from '../services/project-users.service';
4
+ import type { AccountUser } from '../types';
5
+
6
+ export async function listAvailableUsersAction(id_project: number): Promise<AccountUser[]> {
7
+ return projectUsersService.listNotInProject(id_project);
8
+ }
@@ -0,0 +1,8 @@
1
+ 'use server';
2
+
3
+ import { projectUsersService } from '../services/project-users.service';
4
+ import type { ProjectUser } from '../types';
5
+
6
+ export async function listProjectUsersAction(id_project: number): Promise<ProjectUser[]> {
7
+ return projectUsersService.listInProject(id_project);
8
+ }
@@ -0,0 +1,8 @@
1
+ 'use server';
2
+
3
+ import { projectUsersService } from '../services/project-users.service';
4
+ import type { AddRemoveProjectUsersParams } from '../types';
5
+
6
+ export async function removeProjectUserAction(params: AddRemoveProjectUsersParams): Promise<void> {
7
+ return projectUsersService.removeFromProject(params);
8
+ }
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+
3
+ import { useMemo } from 'react';
4
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
5
+
6
+ import type { UserSelectorItem } from '../../../components/layouts/UsersSelectorPopover';
7
+ import { addProjectUserAction } from '../actions/add-project-user.action';
8
+ import { listAvailableUsersAction } from '../actions/list-available-users.action';
9
+ import { listProjectUsersAction } from '../actions/list-project-users.action';
10
+ import { removeProjectUserAction } from '../actions/remove-project-user.action';
11
+
12
+ const DEFAULT_AVATAR = '/images/Logomark.png';
13
+
14
+ export const projectUsersQueryKey = (id_project: number) => ['project-users', id_project];
15
+
16
+ export function useProjectUsers(id_project: number) {
17
+ const queryClient = useQueryClient();
18
+
19
+ const { data: inProject = [], isLoading: isLoadingIn } = useQuery({
20
+ queryKey: [...projectUsersQueryKey(id_project), 'in'],
21
+ queryFn: () => listProjectUsersAction(id_project),
22
+ enabled: !!id_project,
23
+ });
24
+
25
+ const { data: notInProject = [], isLoading: isLoadingOut } = useQuery({
26
+ queryKey: [...projectUsersQueryKey(id_project), 'out'],
27
+ queryFn: () => listAvailableUsersAction(id_project),
28
+ enabled: !!id_project,
29
+ });
30
+
31
+ const invalidate = () => {
32
+ queryClient.invalidateQueries({ queryKey: projectUsersQueryKey(id_project) });
33
+ };
34
+
35
+ const addMutation = useMutation({
36
+ mutationFn: (user_id: number) =>
37
+ addProjectUserAction({ id_project, users: [user_id] }),
38
+ onSuccess: invalidate,
39
+ });
40
+
41
+ const removeMutation = useMutation({
42
+ mutationFn: (user_id: number) =>
43
+ removeProjectUserAction({ id_project, users: [user_id] }),
44
+ onSuccess: invalidate,
45
+ });
46
+
47
+ const users: UserSelectorItem[] = useMemo(
48
+ () => [
49
+ ...inProject.map((u) => ({
50
+ id: u.id_user,
51
+ name: u.name,
52
+ avatar: u.photo ?? DEFAULT_AVATAR,
53
+ checked: true,
54
+ })),
55
+ ...notInProject.map((u) => ({
56
+ id: u.id,
57
+ name: u.name,
58
+ avatar: u.photo ?? DEFAULT_AVATAR,
59
+ checked: false,
60
+ })),
61
+ ],
62
+ [inProject, notInProject]
63
+ );
64
+
65
+ const toggleUser = (id: number) => {
66
+ const user = users.find((u) => u.id === id);
67
+ if (!user) return;
68
+ if (user.checked) {
69
+ removeMutation.mutate(id);
70
+ } else {
71
+ addMutation.mutate(id);
72
+ }
73
+ };
74
+
75
+ return {
76
+ users,
77
+ isLoading: isLoadingIn || isLoadingOut,
78
+ userCount: inProject.length,
79
+ toggleUser,
80
+ };
81
+ }
@@ -0,0 +1,94 @@
1
+ import 'server-only';
2
+
3
+ import { api } from '../../../infra/api/client';
4
+ import { ApiError } from '../../../infra/api/types';
5
+ import { getUserContext } from '../../auth/utils/get-user-context';
6
+ import {
7
+ AccountUser,
8
+ AccountUserSchema,
9
+ AddRemoveProjectUsersParams,
10
+ ProjectUser,
11
+ ProjectUserSchema,
12
+ } from '../types';
13
+
14
+ interface ApiListResponse<T> {
15
+ data: T[];
16
+ total: number;
17
+ status: number;
18
+ message?: string;
19
+ }
20
+
21
+ interface ApiActionResponse {
22
+ status: number;
23
+ message?: string;
24
+ }
25
+
26
+ class ProjectUsersService {
27
+ async listInProject(id_project: number): Promise<ProjectUser[]> {
28
+ const { id_account, id_user } = await getUserContext();
29
+ const url = `/account/${id_account}/user/${id_user}/projects/${id_project}/users`;
30
+
31
+ const response = await api.pages.get<ApiListResponse<ProjectUser>>(url);
32
+
33
+ if (response.status === 0) {
34
+ throw new ApiError(
35
+ response.message || 'Erro ao listar usuários do projeto',
36
+ 'LIST_PROJECT_USERS_FAILED',
37
+ 400
38
+ );
39
+ }
40
+
41
+ return response.data.map((item) => ProjectUserSchema.parse(item));
42
+ }
43
+
44
+ async listNotInProject(id_project: number): Promise<AccountUser[]> {
45
+ const { id_account } = await getUserContext();
46
+ const url = `/accounts/${id_account}/users?notInProject=${id_project}`;
47
+
48
+ const response = await api.apps.get<ApiListResponse<AccountUser>>(url);
49
+
50
+ if (response.status === 0) {
51
+ throw new ApiError(
52
+ response.message || 'Erro ao listar usuários disponíveis',
53
+ 'LIST_AVAILABLE_USERS_FAILED',
54
+ 400
55
+ );
56
+ }
57
+
58
+ return response.data.map((item) => AccountUserSchema.parse(item));
59
+ }
60
+
61
+ async addToProject({ id_project, users }: AddRemoveProjectUsersParams): Promise<void> {
62
+ const { id_account, id_user } = await getUserContext();
63
+ const url = `/account/${id_account}/user/${id_user}/projects/${id_project}/users`;
64
+
65
+ const response = await api.pages.post<ApiActionResponse>(url, { users });
66
+
67
+ if (response.status === 0) {
68
+ throw new ApiError(
69
+ response.message || 'Erro ao adicionar usuário ao projeto',
70
+ 'ADD_PROJECT_USER_FAILED',
71
+ 400
72
+ );
73
+ }
74
+ }
75
+
76
+ async removeFromProject({ id_project, users }: AddRemoveProjectUsersParams): Promise<void> {
77
+ const { id_account, id_user } = await getUserContext();
78
+ const url = `/account/${id_account}/user/${id_user}/projects/${id_project}/users`;
79
+
80
+ const response = await api.pages.delete<ApiActionResponse>(url, {
81
+ body: JSON.stringify({ users }),
82
+ });
83
+
84
+ if (response.status === 0) {
85
+ throw new ApiError(
86
+ response.message || 'Erro ao remover usuário do projeto',
87
+ 'REMOVE_PROJECT_USER_FAILED',
88
+ 400
89
+ );
90
+ }
91
+ }
92
+ }
93
+
94
+ export const projectUsersService = new ProjectUsersService();
@@ -0,0 +1,28 @@
1
+ import { z } from 'zod';
2
+
3
+ export const ProjectUserSchema = z.object({
4
+ id_user: z.union([z.number(), z.string()]).transform(Number),
5
+ id_account: z.union([z.number(), z.string()]).transform(Number),
6
+ id_project: z.union([z.number(), z.string()]).transform(Number),
7
+ name: z.string(),
8
+ email: z.string(),
9
+ profile: z.string().optional(),
10
+ photo: z.string().nullable().optional(),
11
+ });
12
+
13
+ export type ProjectUser = z.infer<typeof ProjectUserSchema>;
14
+
15
+ export const AccountUserSchema = z.object({
16
+ id: z.union([z.number(), z.string()]).transform(Number),
17
+ name: z.string(),
18
+ email: z.string(),
19
+ profile: z.string().optional(),
20
+ photo: z.string().nullable().optional(),
21
+ });
22
+
23
+ export type AccountUser = z.infer<typeof AccountUserSchema>;
24
+
25
+ export interface AddRemoveProjectUsersParams {
26
+ id_project: number;
27
+ users: number[];
28
+ }
package/src/server.ts CHANGED
@@ -15,6 +15,13 @@ export { validateSessionAction } from './modules/auth/actions/validate-session.a
15
15
  export { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';
16
16
  export { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';
17
17
 
18
+ // Project Users Services & Actions (server-only)
19
+ export { projectUsersService } from './modules/projects/services/project-users.service';
20
+ export { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';
21
+ export { listAvailableUsersAction } from './modules/projects/actions/list-available-users.action';
22
+ export { addProjectUserAction } from './modules/projects/actions/add-project-user.action';
23
+ export { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';
24
+
18
25
  // Account Services & Actions (server-only)
19
26
  export { accountService } from './modules/accounts/services/account.service';
20
27
  export { twoFactorService } from './modules/accounts/services/two-factor.service';