@seedgrid/fe-security 2026.6.2 → 2026.7.1

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 (124) hide show
  1. package/dist/client/index.d.ts +4 -0
  2. package/dist/client/index.d.ts.map +1 -0
  3. package/dist/client/index.js +5 -0
  4. package/dist/client/permission-provider.d.ts +18 -0
  5. package/dist/client/permission-provider.d.ts.map +1 -0
  6. package/dist/client/permission-provider.js +57 -0
  7. package/dist/client/pick-list-field.d.ts +15 -0
  8. package/dist/client/pick-list-field.d.ts.map +1 -0
  9. package/dist/client/pick-list-field.js +30 -0
  10. package/dist/client/status-badge.d.ts +6 -0
  11. package/dist/client/status-badge.d.ts.map +1 -0
  12. package/dist/client/status-badge.js +6 -0
  13. package/dist/config.d.ts +34 -0
  14. package/dist/config.d.ts.map +1 -0
  15. package/dist/config.js +4 -0
  16. package/dist/dtos.d.ts +88 -0
  17. package/dist/dtos.d.ts.map +1 -0
  18. package/dist/dtos.js +3 -0
  19. package/dist/index.d.ts +8 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +10 -0
  22. package/dist/menu.d.ts +19 -0
  23. package/dist/menu.d.ts.map +1 -0
  24. package/dist/menu.js +41 -0
  25. package/dist/messages.d.ts +1858 -0
  26. package/dist/messages.d.ts.map +1 -0
  27. package/dist/messages.js +990 -0
  28. package/dist/module.d.ts +3 -0
  29. package/dist/module.d.ts.map +1 -0
  30. package/dist/module.js +37 -0
  31. package/dist/module.json +21 -0
  32. package/dist/paths.d.ts +1 -0
  33. package/dist/paths.d.ts.map +1 -1
  34. package/dist/paths.js +3 -0
  35. package/dist/permissions.d.ts +14 -0
  36. package/dist/permissions.d.ts.map +1 -0
  37. package/dist/permissions.js +39 -0
  38. package/dist/server/action.d.ts +3 -0
  39. package/dist/server/action.d.ts.map +1 -0
  40. package/dist/server/action.js +21 -0
  41. package/dist/server/create-security-server.d.ts +4 -0
  42. package/dist/server/create-security-server.d.ts.map +1 -0
  43. package/dist/server/create-security-server.js +166 -0
  44. package/dist/server/errors.d.ts +13 -0
  45. package/dist/server/errors.d.ts.map +1 -0
  46. package/dist/server/errors.js +26 -0
  47. package/dist/server/index.d.ts +8 -0
  48. package/dist/server/index.d.ts.map +1 -0
  49. package/dist/server/index.js +8 -0
  50. package/dist/server/problem-response.d.ts +3 -0
  51. package/dist/server/problem-response.d.ts.map +1 -0
  52. package/dist/server/problem-response.js +28 -0
  53. package/dist/server/route.d.ts +3 -0
  54. package/dist/server/route.d.ts.map +1 -0
  55. package/dist/server/route.js +22 -0
  56. package/dist/server/to-problem.d.ts +3 -0
  57. package/dist/server/to-problem.d.ts.map +1 -0
  58. package/dist/server/to-problem.js +22 -0
  59. package/dist/server-contract.d.ts +23 -0
  60. package/dist/server-contract.d.ts.map +1 -0
  61. package/dist/server-contract.js +4 -0
  62. package/dist/session-contract.d.ts +36 -0
  63. package/dist/session-contract.d.ts.map +1 -0
  64. package/dist/session-contract.js +8 -0
  65. package/dist/session-result.d.ts +22 -0
  66. package/dist/session-result.d.ts.map +1 -0
  67. package/dist/session-result.js +14 -0
  68. package/package.json +38 -3
  69. package/scaffold/README.md +97 -0
  70. package/scaffold/src/app/api/security/session/2fa/resend/route.ts +8 -0
  71. package/scaffold/src/app/api/security/session/2fa/verify/route.ts +8 -0
  72. package/scaffold/src/app/api/security/session/login/route.ts +7 -0
  73. package/scaffold/src/app/api/security/session/logout/route.ts +5 -0
  74. package/scaffold/src/app/api/security/session/refresh/route.ts +7 -0
  75. package/scaffold/src/app/api/security/users/[publicId]/avatar/route.ts +15 -0
  76. package/scaffold/src/app/auth/2fa/verify/page.tsx +286 -0
  77. package/scaffold/src/app/auth/forgot-password/confirm/page.tsx +96 -0
  78. package/scaffold/src/app/auth/forgot-password/page.tsx +165 -0
  79. package/scaffold/src/app/auth/forgot-password/success/page.tsx +31 -0
  80. package/scaffold/src/app/auth/login/page.tsx +386 -0
  81. package/scaffold/src/app/auth/welcome/page.tsx +90 -0
  82. package/scaffold/src/app/login/page.tsx +7 -0
  83. package/scaffold/src/app/page.tsx +12 -0
  84. package/scaffold/src/app/security/2fa/page.tsx +370 -0
  85. package/scaffold/src/app/security/companies/(create)/new/page.tsx +12 -0
  86. package/scaffold/src/app/security/companies/(edit)/[publicId]/page.tsx +18 -0
  87. package/scaffold/src/app/security/companies/(list)/page.tsx +298 -0
  88. package/scaffold/src/app/security/companies/editors/CompanyEditor.tsx +754 -0
  89. package/scaffold/src/app/security/profile/page.tsx +492 -0
  90. package/scaffold/src/app/security/roles/(create)/new/page.tsx +11 -0
  91. package/scaffold/src/app/security/roles/(edit)/[publicId]/page.tsx +17 -0
  92. package/scaffold/src/app/security/roles/(list)/page.tsx +269 -0
  93. package/scaffold/src/app/security/roles/editors/RoleEditor.tsx +328 -0
  94. package/scaffold/src/app/security/users/(create)/new/page.tsx +10 -0
  95. package/scaffold/src/app/security/users/(edit)/[publicId]/page.tsx +16 -0
  96. package/scaffold/src/app/security/users/(list)/page.tsx +289 -0
  97. package/scaffold/src/app/security/users/editors/UserEditor.tsx +478 -0
  98. package/scaffold/src/app/users/change-password/page.tsx +172 -0
  99. package/scaffold/src/modules/security/AuthProvider.tsx +405 -0
  100. package/scaffold/src/modules/security/components/AppShell.tsx.hbs +889 -0
  101. package/scaffold/src/modules/security/components/CompanyManagementGuard.tsx +59 -0
  102. package/scaffold/src/modules/security/components/PageFrame.tsx +105 -0
  103. package/scaffold/src/modules/security/components/RouteGuard.tsx +114 -0
  104. package/scaffold/src/modules/security/components/SecurityDashboard.tsx.hbs +315 -0
  105. package/scaffold/src/modules/security/config.ts +55 -0
  106. package/scaffold/src/modules/security/hooks/useAuth.ts +7 -0
  107. package/scaffold/src/modules/security/hooks/useDebouncedSearch.ts +13 -0
  108. package/scaffold/src/modules/security/hooks/usePermission.ts +35 -0
  109. package/scaffold/src/modules/security/host-access.ts +59 -0
  110. package/scaffold/src/modules/security/index.ts +44 -0
  111. package/scaffold/src/modules/security/navigation.ts +41 -0
  112. package/scaffold/src/modules/security/server/avatar-routes.ts +86 -0
  113. package/scaffold/src/modules/security/server/public-api-base-url.ts +26 -0
  114. package/scaffold/src/modules/security/server/session-routes.ts.hbs +322 -0
  115. package/scaffold/src/modules/security/server/session-server.ts.hbs +30 -0
  116. package/scaffold/src/modules/security/services/auth-service.ts +332 -0
  117. package/scaffold/src/modules/security/services/security-api.test.ts +178 -0
  118. package/scaffold/src/modules/security/services/security-api.ts +281 -0
  119. package/scaffold/src/modules/security/services/security-service.test.ts +71 -0
  120. package/scaffold/src/modules/security/services/security-service.ts +727 -0
  121. package/scaffold/src/modules/security/services/session-storage.ts +80 -0
  122. package/scaffold/src/modules/security/types.ts +295 -0
  123. package/scaffold/src/modules/security/useSecurityAppShellSections.tsx.hbs +180 -0
  124. package/scaffold/src/modules/security/utils.ts +194 -0
@@ -0,0 +1,80 @@
1
+ import { securityConfig } from "../config";
2
+ import type {
3
+ AuthenticatedSession,
4
+ PendingTwoFactorSession,
5
+ } from "../types";
6
+
7
+ function readStorageValue<T>(key: string): T | null {
8
+ if (typeof window === "undefined") {
9
+ return null;
10
+ }
11
+
12
+ try {
13
+ const rawValue = window.localStorage.getItem(key);
14
+
15
+ if (!rawValue) {
16
+ return null;
17
+ }
18
+
19
+ return JSON.parse(rawValue) as T;
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+
25
+ function writeStorageValue<T>(key: string, value: T) {
26
+ if (typeof window === "undefined") {
27
+ return;
28
+ }
29
+
30
+ window.localStorage.setItem(key, JSON.stringify(value));
31
+ }
32
+
33
+ function removeStorageValue(key: string) {
34
+ if (typeof window === "undefined") {
35
+ return;
36
+ }
37
+
38
+ window.localStorage.removeItem(key);
39
+ }
40
+
41
+ export function readStoredSession() {
42
+ return readStorageValue<AuthenticatedSession>(securityConfig.storage.sessionKey);
43
+ }
44
+
45
+ export function writeStoredSession(session: AuthenticatedSession) {
46
+ writeStorageValue(securityConfig.storage.sessionKey, session);
47
+ }
48
+
49
+ export function clearStoredSession() {
50
+ removeStorageValue(securityConfig.storage.sessionKey);
51
+ }
52
+
53
+ export function readPendingTwoFactorSession() {
54
+ const session = readStorageValue<PendingTwoFactorSession>(
55
+ securityConfig.storage.pendingTwoFactorKey
56
+ );
57
+
58
+ if (!session) {
59
+ return null;
60
+ }
61
+
62
+ if (!session.tempToken && session.twoFactorToken) {
63
+ return {
64
+ ...session,
65
+ tempToken: session.twoFactorToken,
66
+ };
67
+ }
68
+
69
+ return session;
70
+ }
71
+
72
+ export function writePendingTwoFactorSession(
73
+ session: PendingTwoFactorSession
74
+ ) {
75
+ writeStorageValue(securityConfig.storage.pendingTwoFactorKey, session);
76
+ }
77
+
78
+ export function clearPendingTwoFactorSession() {
79
+ removeStorageValue(securityConfig.storage.pendingTwoFactorKey);
80
+ }
@@ -0,0 +1,295 @@
1
+ export type PermissionValue = string;
2
+
3
+ export type SortOrder = 1 | -1 | 0;
4
+
5
+ export type SortState = {
6
+ sortField: string | null;
7
+ sortOrder: SortOrder;
8
+ };
9
+
10
+ export type PagedResult<T> = {
11
+ items: T[];
12
+ total: number;
13
+ page: number;
14
+ size: number;
15
+ fallback?: boolean;
16
+ };
17
+
18
+ export type PermissionEnvelope = {
19
+ permissions: PermissionValue[];
20
+ roles: string[];
21
+ login?: string;
22
+ publicId?: string;
23
+ name?: string;
24
+ };
25
+
26
+ export type AuthTokens = {
27
+ accessToken: string;
28
+ refreshToken?: string | null;
29
+ tokenType: string;
30
+ expiresIn?: number | null;
31
+ expiresAt?: number | null;
32
+ allowedCompanies: number[];
33
+ publicId?: string;
34
+ name?: string;
35
+ changePasswordRequired?: boolean;
36
+ };
37
+
38
+ export type AuthenticatedSession = AuthTokens &
39
+ PermissionEnvelope & {
40
+ login?: string;
41
+ };
42
+
43
+ export type LoginCredentials = {
44
+ login: string;
45
+ password: string;
46
+ };
47
+
48
+ export type PendingTwoFactorSession = {
49
+ login: string;
50
+ tempToken: string;
51
+ twoFactorToken?: string;
52
+ method?: string | null;
53
+ };
54
+
55
+ export type ForgotPasswordPayload = {
56
+ email: string;
57
+ newPassword: string;
58
+ };
59
+
60
+ export type ChangePasswordPayload = {
61
+ currentPassword: string;
62
+ newPassword: string;
63
+ confirmNewPassword: string;
64
+ };
65
+
66
+ export type PermissionSummary = {
67
+ id?: number;
68
+ publicId: string;
69
+ name: string;
70
+ description?: string | null;
71
+ createdAt?: string | null;
72
+ updatedAt?: string | null;
73
+ };
74
+
75
+ export type RoleSummary = {
76
+ id?: number;
77
+ publicId: string;
78
+ name: string;
79
+ description?: string | null;
80
+ forIntegration?: boolean;
81
+ permissions?: PermissionSummary[] | null;
82
+ createdAt?: string | null;
83
+ updatedAt?: string | null;
84
+ };
85
+
86
+ export type RoleMutationPayload = {
87
+ name: string;
88
+ description?: string;
89
+ forIntegration?: boolean;
90
+ permissionIds: Array<string | number>;
91
+ };
92
+
93
+ export type CompanySummary = {
94
+ id?: number;
95
+ publicId: string;
96
+ cnpj?: string | null;
97
+ corporateName?: string | null;
98
+ tradeName?: string | null;
99
+ active?: boolean;
100
+ addrStreet?: string | null;
101
+ addrNumber?: string | null;
102
+ addrComplement?: string | null;
103
+ addrPostalCode?: string | null;
104
+ addrCity?: string | null;
105
+ addrState?: string | null;
106
+ addrDistrict?: string | null;
107
+ addrPhone?: string | null;
108
+ repFirstName?: string | null;
109
+ repLastName?: string | null;
110
+ repCpf?: string | null;
111
+ repPhone?: string | null;
112
+ repWhatsapp?: string | null;
113
+ repEmail?: string | null;
114
+ createdAt?: string | null;
115
+ updatedAt?: string | null;
116
+ };
117
+
118
+ export type UserSummary = {
119
+ id?: number;
120
+ publicId: string;
121
+ email: string;
122
+ name?: string | null;
123
+ whatsApp?: string | null;
124
+ avatarStorageKey?: string | null;
125
+ roles?: RoleSummary[];
126
+ companies?: CompanySummary[];
127
+ companyIds?: Array<number | string>;
128
+ companyPublicIds?: string[];
129
+ root?: boolean;
130
+ active?: boolean;
131
+ changePasswordRequired?: boolean;
132
+ passwordExpiresAt?: string | null;
133
+ emailConfirmedAt?: string | null;
134
+ twoFactorEnabled?: boolean;
135
+ twoFactorPrimaryMethod?: string | null;
136
+ twoFactorEnabledAt?: string | null;
137
+ twoFactorLastUsedAt?: string | null;
138
+ twoFactorHasBackupCodes?: boolean;
139
+ createdAt?: string | null;
140
+ updatedAt?: string | null;
141
+ };
142
+
143
+ export type UserMutationPayload = {
144
+ email: string;
145
+ name?: string;
146
+ password?: string;
147
+ whatsApp?: string;
148
+ roleIds: number[];
149
+ companyIds: number[];
150
+ active: boolean;
151
+ changePasswordRequired: boolean;
152
+ };
153
+
154
+ export type CompanyMutationPayload = {
155
+ cnpj?: string;
156
+ corporateName: string;
157
+ tradeName: string;
158
+ active: boolean;
159
+ addrStreet?: string;
160
+ addrNumber?: string;
161
+ addrComplement?: string;
162
+ addrPostalCode?: string;
163
+ addrCity?: string;
164
+ addrState?: string;
165
+ addrDistrict?: string;
166
+ addrPhone?: string;
167
+ repFirstName?: string;
168
+ repLastName?: string;
169
+ repCpf?: string;
170
+ repPhone?: string;
171
+ repWhatsapp?: string;
172
+ repEmail?: string;
173
+ };
174
+
175
+ export type TwoFactorMethod = "EMAIL" | "TOTP" | "BACKUP_CODE" | string;
176
+
177
+ export type TwoFactorStatus = {
178
+ enabled: boolean;
179
+ primaryMethod?: TwoFactorMethod | null;
180
+ availableMethods: TwoFactorMethod[];
181
+ hasBackupCodes?: boolean;
182
+ unusedBackupCodesCount?: number;
183
+ };
184
+
185
+ export type TwoFactorEnableResponse = {
186
+ status?: string;
187
+ method?: TwoFactorMethod | null;
188
+ primaryMethod?: TwoFactorMethod | null;
189
+ backupCodes?: string[];
190
+ secret?: string | null;
191
+ otpauthUrl?: string | null;
192
+ qrCode?: {
193
+ format?: string | null;
194
+ width?: number | null;
195
+ height?: number | null;
196
+ base64?: string | null;
197
+ } | null;
198
+ };
199
+
200
+ export type DisableTwoFactorPayload = {
201
+ twoFactorCode?: string;
202
+ password?: string;
203
+ backupCode?: string;
204
+ };
205
+
206
+ export type EntryRequest = {
207
+ id: number | string;
208
+ name?: string | null;
209
+ email?: string | null;
210
+ whatsApp?: string | null;
211
+ message?: string | null;
212
+ status?: string | null;
213
+ requestedAt?: string | null;
214
+ expiresAt?: string | null;
215
+ approvedBy?: string | null;
216
+ rejectedBy?: string | null;
217
+ createdAt?: string | null;
218
+ approvedAt?: string | null;
219
+ rejectedAt?: string | null;
220
+ confirmedAt?: string | null;
221
+ };
222
+
223
+ export type SelfEnterRequestPayload = {
224
+ name: string;
225
+ email: string;
226
+ password: string;
227
+ };
228
+
229
+ export type SelfEnterRequestEmailConfirmationPayload = {
230
+ code: string;
231
+ };
232
+
233
+ export type SelfEnterInvitation = {
234
+ id?: number | string;
235
+ email: string;
236
+ name?: string | null;
237
+ status?: string | null;
238
+ expiresAt?: string | null;
239
+ confirmedAt?: string | null;
240
+ consumedAt?: string | null;
241
+ requestedAt?: string | null;
242
+ createdAt?: string | null;
243
+ };
244
+
245
+ export type SelfEnterInvitationCompletionPayload = {
246
+ name: string;
247
+ email: string;
248
+ password: string;
249
+ };
250
+
251
+ export type SelfEnterInvitationCreatePayload = {
252
+ email: string;
253
+ name?: string;
254
+ };
255
+
256
+ export type SelfEnterInvitationAdminError =
257
+ | "duplicate_email"
258
+ | "unknown";
259
+
260
+ export type ApiKeyListItem = {
261
+ publicId: string;
262
+ name: string;
263
+ roleName?: string | null;
264
+ active?: boolean;
265
+ prefix?: string | null;
266
+ masked?: string | null;
267
+ expiresAt?: string | null;
268
+ revokedAt?: string | null;
269
+ lastUsedAt?: string | null;
270
+ createdAt?: string | null;
271
+ };
272
+
273
+ export type ApiKeyCreatePayload = {
274
+ roleName: string;
275
+ name: string;
276
+ expiresDays?: number | null;
277
+ };
278
+
279
+ export type ApiKeyCreateResponse = {
280
+ publicId: string;
281
+ name: string;
282
+ key?: string | null;
283
+ roleName?: string | null;
284
+ expiresAt?: string | null;
285
+ createdAt?: string | null;
286
+ };
287
+
288
+ export type PickListOption = {
289
+ label: string;
290
+ value: string | number;
291
+ disabled?: boolean;
292
+ data?: unknown;
293
+ };
294
+
295
+ export type FormMode = "create" | "edit" | "view";
@@ -0,0 +1,180 @@
1
+ "use client";
2
+
3
+ import { Building2, KeySquare, Mail, Shield, ShieldCheck, UserCog, Users } from "lucide-react";
4
+
5
+ import type {
6
+ AppShellNavItem,
7
+ AppShellSection,
8
+ } from "@/components/layout/app-shell-navigation";
9
+ import { useI18n } from "@/i18n";
10
+
11
+ import { securityConfig } from "./config";
12
+ import { useCompanyManagementEnabledForCurrentHost } from "./host-access";
13
+ import { useCrudPermission, usePermission } from "./hooks/usePermission";
14
+
15
+ export function useSecurityAppShellSections(): AppShellSection[] {
16
+ const { t } = useI18n();
17
+ const companiesEnabled = useCompanyManagementEnabledForCurrentHost();
18
+ const userPermissions = useCrudPermission("USER");
19
+ const companyPermissions = useCrudPermission("COMPANY");
20
+ const rolePermissions = useCrudPermission("ROLE");
21
+ {{#if security_api_keys_enabled}}
22
+ const canReadApiKeys = usePermission("APIKEY_READ");
23
+ const canCreateApiKeys = usePermission("APIKEY_CREATE");
24
+ const canRevokeApiKeys = usePermission("APIKEY_REVOKE");
25
+ {{/if}}
26
+ {{#if security_self_enter_enabled}}
27
+ const canApproveSelfEnter = usePermission("SELFENTER_APPROVE");
28
+ const canRejectSelfEnter = usePermission("SELFENTER_REJECT");
29
+ const canInviteSelfEnter = usePermission("SELFENTER_INVITE");
30
+ {{/if}}
31
+
32
+ const accessItems: AppShellNavItem[] = [];
33
+ {{#if security_self_enter_enabled}}
34
+ const selfEnterItems: AppShellNavItem[] = [];
35
+ {{/if}}
36
+
37
+ if (userPermissions.canRead) {
38
+ accessItems.push({
39
+ id: "users",
40
+ label: t("security.users.list.title"),
41
+ href: securityConfig.routes.users,
42
+ badge: userPermissions.isReadOnly ? t("security.common.readonly") : undefined,
43
+ order: 10,
44
+ });
45
+ }
46
+
47
+ if (companiesEnabled && companyPermissions.canRead) {
48
+ accessItems.push({
49
+ id: "companies",
50
+ label: t("security.companies.list.title"),
51
+ href: securityConfig.routes.companies,
52
+ badge: companyPermissions.isReadOnly ? t("security.common.readonly") : undefined,
53
+ order: 20,
54
+ });
55
+ }
56
+
57
+ if (rolePermissions.canRead) {
58
+ accessItems.push({
59
+ id: "roles",
60
+ label: t("security.roles.list.title"),
61
+ href: securityConfig.routes.roles,
62
+ badge: rolePermissions.isReadOnly ? t("security.common.readonly") : undefined,
63
+ order: 30,
64
+ });
65
+ }
66
+
67
+ {{#if security_api_keys_enabled}}
68
+ if (canReadApiKeys) {
69
+ accessItems.push({
70
+ id: "api-keys",
71
+ label: t("security.api_keys.title"),
72
+ href: securityConfig.routes.apiKeys,
73
+ badge:
74
+ !canCreateApiKeys && !canRevokeApiKeys
75
+ ? t("security.common.readonly")
76
+ : undefined,
77
+ order: 40,
78
+ });
79
+ }
80
+ {{/if}}
81
+
82
+ {{#if security_self_enter_enabled}}
83
+ if (canApproveSelfEnter || canRejectSelfEnter) {
84
+ selfEnterItems.push({
85
+ id: "self-enter-approval",
86
+ label: t("security.self_enter.approval.title"),
87
+ href: securityConfig.routes.selfEnterApproval,
88
+ badge:
89
+ !canApproveSelfEnter || !canRejectSelfEnter
90
+ ? t("security.common.readonly")
91
+ : undefined,
92
+ order: 50,
93
+ });
94
+ }
95
+
96
+ if (canInviteSelfEnter) {
97
+ selfEnterItems.push({
98
+ id: "self-enter-invitations",
99
+ label: t("security.self_enter.invitation_admin.nav_title"),
100
+ href: securityConfig.routes.selfEnterInvitations,
101
+ order: 60,
102
+ });
103
+ }
104
+ {{/if}}
105
+
106
+ const sections: AppShellSection[] = [];
107
+
108
+ if (
109
+ accessItems.length
110
+ {{#if security_self_enter_enabled}}
111
+ || selfEnterItems.length
112
+ {{/if}}
113
+ ) {
114
+ sections.push({
115
+ id: "application",
116
+ label: t("security.dashboard.menu.group.access"),
117
+ order: 20,
118
+ items: [
119
+ {
120
+ id: "access-management",
121
+ label: t("security.dashboard.menu.group.access"),
122
+ icon: <Shield size={18} />,
123
+ order: 10,
124
+ children: accessItems.map((item) => {
125
+ if (item.id === "users") {
126
+ return { ...item, icon: <Users size={16} /> };
127
+ }
128
+
129
+ if (item.id === "companies") {
130
+ return { ...item, icon: <Building2 size={16} /> };
131
+ }
132
+
133
+ if (item.id === "roles") {
134
+ return { ...item, icon: <ShieldCheck size={16} /> };
135
+ }
136
+
137
+ {{#if security_api_keys_enabled}}
138
+ if (item.id === "api-keys") {
139
+ return { ...item, icon: <KeySquare size={16} /> };
140
+ }
141
+ {{/if}}
142
+
143
+ {{#if security_self_enter_enabled}}
144
+ if (item.id === "self-enter-approval") {
145
+ return { ...item, icon: <UserCog size={16} /> };
146
+ }
147
+ {{/if}}
148
+
149
+ return item;
150
+ }),
151
+ },
152
+ {{#if security_self_enter_enabled}}
153
+ ...(selfEnterItems.length
154
+ ? [
155
+ {
156
+ id: "self-enter",
157
+ label: t("security.self_enter.menu.title"),
158
+ icon: <UserCog size={18} />,
159
+ order: 20,
160
+ children: selfEnterItems.map((item) => {
161
+ if (item.id === "self-enter-approval") {
162
+ return { ...item, icon: <ShieldCheck size={16} /> };
163
+ }
164
+
165
+ if (item.id === "self-enter-invitations") {
166
+ return { ...item, icon: <Mail size={16} /> };
167
+ }
168
+
169
+ return item;
170
+ }),
171
+ },
172
+ ]
173
+ : []),
174
+ {{/if}}
175
+ ],
176
+ });
177
+ }
178
+
179
+ return sections;
180
+ }