@flusys/ng-iam 3.0.0 → 4.0.0-lts

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 (25) hide show
  1. package/README.md +2 -2
  2. package/fesm2022/{flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs → flusys-ng-iam-action-form-page.component-BQx9yset.mjs} +62 -50
  3. package/fesm2022/flusys-ng-iam-action-form-page.component-BQx9yset.mjs.map +1 -0
  4. package/fesm2022/{flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs → flusys-ng-iam-action-list-page.component-BrpZujxk.mjs} +51 -42
  5. package/fesm2022/flusys-ng-iam-action-list-page.component-BrpZujxk.mjs.map +1 -0
  6. package/fesm2022/{flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs → flusys-ng-iam-flusys-ng-iam-Co4ot9My.mjs} +641 -341
  7. package/fesm2022/flusys-ng-iam-flusys-ng-iam-Co4ot9My.mjs.map +1 -0
  8. package/fesm2022/{flusys-ng-iam-iam-container.component-UYJjqYV9.mjs → flusys-ng-iam-iam-container.component-CQA2B6cU.mjs} +14 -13
  9. package/fesm2022/flusys-ng-iam-iam-container.component-CQA2B6cU.mjs.map +1 -0
  10. package/fesm2022/{flusys-ng-iam-permission-page.component-DcgT7L3_.mjs → flusys-ng-iam-permission-page.component-Dpk90y72.mjs} +14 -13
  11. package/fesm2022/flusys-ng-iam-permission-page.component-Dpk90y72.mjs.map +1 -0
  12. package/fesm2022/{flusys-ng-iam-role-form-page.component-D_AAEay2.mjs → flusys-ng-iam-role-form-page.component-CVfRQpoa.mjs} +38 -35
  13. package/fesm2022/flusys-ng-iam-role-form-page.component-CVfRQpoa.mjs.map +1 -0
  14. package/fesm2022/{flusys-ng-iam-role-list-page.component-D4J1by6Q.mjs → flusys-ng-iam-role-list-page.component-BHB8X5r7.mjs} +41 -38
  15. package/fesm2022/flusys-ng-iam-role-list-page.component-BHB8X5r7.mjs.map +1 -0
  16. package/fesm2022/flusys-ng-iam.mjs +1 -1
  17. package/package.json +7 -5
  18. package/types/flusys-ng-iam.d.ts +13 -5
  19. package/fesm2022/flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs.map +0 -1
  20. package/fesm2022/flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs.map +0 -1
  21. package/fesm2022/flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs.map +0 -1
  22. package/fesm2022/flusys-ng-iam-iam-container.component-UYJjqYV9.mjs.map +0 -1
  23. package/fesm2022/flusys-ng-iam-permission-page.component-DcgT7L3_.mjs.map +0 -1
  24. package/fesm2022/flusys-ng-iam-role-form-page.component-D_AAEay2.mjs.map +0 -1
  25. package/fesm2022/flusys-ng-iam-role-list-page.component-D4J1by6Q.mjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { HttpClient } from '@angular/common/http';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, Injectable, signal, input, output, effect, ChangeDetectionStrategy, Component, DestroyRef, computed } from '@angular/core';
4
- import { ApiResourceService, PermissionValidatorService, AngularModule, PrimeModule, ROLE_ACTION_PERMISSIONS, HasPermissionDirective, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, USER_ROLE_PERMISSIONS, USER_PERMISSION_PROVIDER, UserSelectComponent, USER_ACTION_PERMISSIONS, PROFILE_PERMISSION_PROVIDER, permissionGuard, ACTION_PERMISSIONS, ROLE_PERMISSIONS, anyPermissionGuard } from '@flusys/ng-shared';
5
- import { APP_CONFIG, getServiceUrl, BaseApiService, isCompanyFeatureEnabled } from '@flusys/ng-core';
3
+ import { inject, Injectable, signal, input, output, effect, Component, DestroyRef, computed } from '@angular/core';
4
+ import { ApiResourceService, PermissionValidatorService, AngularModule, PrimeModule, TranslatePipe, ROLE_ACTION_PERMISSIONS, HasPermissionDirective, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, USER_ROLE_PERMISSIONS, USER_PERMISSION_PROVIDER, UserSelectComponent, USER_ACTION_PERMISSIONS, PROFILE_PERMISSION_PROVIDER, permissionGuard, ACTION_PERMISSIONS, ROLE_PERMISSIONS, anyPermissionGuard, resolveTranslationModule, SHARED_MESSAGES } from '@flusys/ng-shared';
5
+ import { APP_CONFIG, getServiceUrl, TRANSLATE_ADAPTER, BaseApiService, isCompanyFeatureEnabled } from '@flusys/ng-core';
6
6
  import { ConfirmationService, MessageService } from 'primeng/api';
7
7
  import { of, firstValueFrom, map as map$1 } from 'rxjs';
8
8
  import { tap, catchError, map } from 'rxjs/operators';
@@ -32,6 +32,322 @@ var ActionType;
32
32
  /** Maximum items for dropdown lists (companies, roles, users, branches) */
33
33
  const MAX_DROPDOWN_ITEMS = 100;
34
34
 
35
+ const IAM_MESSAGES = {
36
+ // Container page
37
+ 'iam.title': 'Identity & Access Management',
38
+ 'iam.subtitle': 'Manage roles, permissions, and access control',
39
+ // Tabs
40
+ 'iam.tabs.roles': 'Roles',
41
+ 'iam.tabs.actions': 'Actions',
42
+ 'iam.tabs.permissions': 'Permissions',
43
+ // Administration
44
+ 'iam.administration.title': 'Administration',
45
+ 'iam.administration.subtitle': 'Manage users, companies, and branches',
46
+ // Users
47
+ 'iam.user.title': 'Users',
48
+ 'iam.user.add': 'Add User',
49
+ 'iam.user.new': 'New User',
50
+ 'iam.user.create': 'Create User',
51
+ 'iam.user.edit': 'Edit User',
52
+ 'iam.user.name': 'Name',
53
+ 'iam.user.email': 'Email',
54
+ 'iam.user.phone': 'Phone',
55
+ 'iam.user.password': 'Password',
56
+ 'iam.user.password.optional': 'Password (leave blank to keep current)',
57
+ 'iam.user.password.placeholder': 'Enter password',
58
+ 'iam.user.name.placeholder': 'Enter full name',
59
+ 'iam.user.email.placeholder': 'Enter email address',
60
+ 'iam.user.phone.placeholder': 'Enter phone number',
61
+ 'iam.user.email.verified': 'Email Verified',
62
+ 'iam.user.no.users': 'No users found',
63
+ 'iam.user.details': 'User Details',
64
+ 'iam.user.auto.assign.to': 'Auto-assign to',
65
+ 'iam.user.assign.role': 'Assign Role',
66
+ 'iam.user.remove.role': 'Remove Role',
67
+ 'iam.user.activate': 'Activate User',
68
+ 'iam.user.deactivate': 'Deactivate User',
69
+ 'iam.user.reset.password': 'Reset Password',
70
+ 'iam.user.assign.branch': 'Assign Branch',
71
+ 'iam.user.delete.title': 'Delete User',
72
+ 'iam.user.delete.success': 'User deleted successfully',
73
+ // Roles
74
+ 'iam.role.title': 'Roles',
75
+ 'iam.role.new': 'New Role',
76
+ 'iam.role.create': 'Create Role',
77
+ 'iam.role.edit': 'Edit Role',
78
+ 'iam.role.name': 'Role Name',
79
+ 'iam.role.name.placeholder': 'Enter role name',
80
+ 'iam.role.description': 'Role Description',
81
+ 'iam.role.description.placeholder': 'Enter role description',
82
+ 'iam.role.permissions': 'Permissions',
83
+ 'iam.role.assigned.users': 'Assigned Users',
84
+ 'iam.role.no.roles': 'No roles found',
85
+ 'iam.role.no.roles.assigned': 'No roles assigned',
86
+ 'iam.role.delete.title': 'Delete Role',
87
+ 'iam.role.delete.success': 'Role deleted successfully',
88
+ 'iam.role.create.success': 'Role created successfully',
89
+ 'iam.role.update.success': 'Role updated successfully',
90
+ 'role.create.success': 'Role created successfully',
91
+ 'role.update.success': 'Role updated successfully',
92
+ 'role.delete.success': 'Role deleted successfully',
93
+ 'role.get.success': 'Role retrieved successfully',
94
+ 'role.get.all.success': 'Roles retrieved successfully',
95
+ // Actions
96
+ 'iam.action.title': 'Actions',
97
+ 'iam.action.new': 'New Action',
98
+ 'iam.action.create': 'Create Action',
99
+ 'iam.action.edit': 'Edit Action',
100
+ 'iam.action.name': 'Action Name',
101
+ 'iam.action.name.placeholder': 'Enter action name',
102
+ 'iam.action.code': 'Action Code',
103
+ 'iam.action.code.placeholder': 'Enter action code',
104
+ 'iam.action.type': 'Type',
105
+ 'iam.action.no.actions': 'No actions found',
106
+ 'iam.action.no.actions.assigned': 'No actions assigned',
107
+ 'iam.action.direct.actions': 'Direct Actions',
108
+ 'iam.action.type.backend': 'Backend',
109
+ 'iam.action.type.frontend': 'Frontend',
110
+ 'iam.action.type.both': 'Both',
111
+ 'iam.action.type.backend.label': 'Backend Only',
112
+ 'iam.action.type.frontend.label': 'Frontend Only',
113
+ 'iam.action.type.both.label': 'Backend & Frontend',
114
+ 'iam.action.delete.title': 'Delete Action',
115
+ 'iam.action.delete.success': 'Action deleted successfully',
116
+ 'iam.action.create.success': 'Action created successfully',
117
+ 'iam.action.update.success': 'Action updated successfully',
118
+ 'iam.action.parent': 'Parent Action',
119
+ 'iam.action.select.parent': 'Select Parent Action',
120
+ // Permissions
121
+ 'iam.permission.title': 'Permissions',
122
+ 'iam.permission.create': 'Create Permission',
123
+ 'iam.permission.edit': 'Edit Permission',
124
+ 'iam.permission.name': 'Permission Name',
125
+ 'iam.permission.code': 'Permission Code',
126
+ 'iam.permission.module': 'Module',
127
+ 'iam.permission.action': 'Action',
128
+ 'iam.permission.description': 'Description',
129
+ 'iam.permission.role.actions': 'Role Actions',
130
+ 'iam.permission.user.roles': 'User Roles',
131
+ 'iam.permission.user.actions': 'User Actions',
132
+ 'iam.permission.company.actions': 'Company Actions',
133
+ 'iam.permission.company.permissions': 'Company Permissions',
134
+ 'iam.permission.branch.permissions': 'Branch Permissions',
135
+ 'iam.permission.my.permissions': 'My Permissions',
136
+ 'iam.permission.select.company': 'Select Company',
137
+ 'iam.permission.select.company.placeholder': 'Search and select a company',
138
+ 'iam.permission.select.role': 'Select Role',
139
+ 'iam.permission.select.role.placeholder': 'Search and select a role',
140
+ 'iam.permission.select.user': 'Select User',
141
+ 'iam.permission.select.user.placeholder': 'Search and select a user',
142
+ 'iam.permission.select.branch': 'Select Branch',
143
+ 'iam.permission.select.branch.placeholder': 'Search and select a branch',
144
+ 'iam.permission.action.whitelist': 'Action Whitelist',
145
+ 'iam.permission.action.permissions': 'Action Permissions',
146
+ 'iam.permission.direct.action.permissions': 'Direct Action Permissions',
147
+ 'iam.permission.role.assignments': 'Role Assignments',
148
+ 'iam.permission.actions.available': '{{count}} actions available',
149
+ 'iam.permission.roles.available': '{{count}} roles available',
150
+ 'iam.permission.no.actions.available': 'No actions available',
151
+ 'iam.permission.no.roles.available': 'No roles available',
152
+ 'iam.permission.no.actions.for.company': 'No actions available for this company',
153
+ 'iam.permission.no.actions.for.role': 'No actions available for this role',
154
+ 'iam.permission.no.actions.for.user': 'No actions available for this user',
155
+ 'iam.permission.no.roles.for.user': 'No roles available for this user',
156
+ 'iam.permission.company.required': 'Please select a company first',
157
+ 'iam.permission.has.prerequisites': 'Has prerequisites',
158
+ 'iam.permission.requirements': 'Requirements',
159
+ 'iam.permission.company.actions.updated': 'Company actions updated successfully',
160
+ 'iam.permission.role.permissions.updated': 'Role permissions updated successfully',
161
+ 'iam.permission.user.actions.updated': 'User actions updated successfully',
162
+ 'iam.permission.user.roles.updated': 'User roles updated successfully',
163
+ 'iam.permission.requires': 'requires',
164
+ 'iam.permission.prerequisite.validation.failed': 'Prerequisite Validation Failed',
165
+ 'iam.permission.prerequisite.error.message': 'The following actions have unmet prerequisites:',
166
+ 'iam.permission.auto.select.prompt': 'Would you like to auto-select the required actions?',
167
+ 'iam.permission.auto.select.required': 'Auto-select Required',
168
+ 'iam.permission.actions.selected': 'Actions Selected',
169
+ 'iam.permission.auto.selected.prerequisites': 'Required prerequisites have been auto-selected. Click Save to apply changes.',
170
+ 'iam.permission.changes.reverted': 'Changes Reverted',
171
+ 'iam.permission.selection.reverted': 'Selection has been reverted to the initial state.',
172
+ 'permission.create.success': 'Permission created successfully',
173
+ 'permission.update.success': 'Permission updated successfully',
174
+ 'permission.delete.success': 'Permission deleted successfully',
175
+ 'permission.get.success': 'Permission retrieved successfully',
176
+ 'permission.get.all.success': 'Permissions retrieved successfully',
177
+ 'permission.process.success': 'Successfully processed {{total}} items: {{added}} added, {{removed}} removed',
178
+ 'permission.user.required': 'User is required for {{method}}',
179
+ 'permission.already.exists': 'Permission already exists',
180
+ // Access control
181
+ 'iam.access.denied': 'Access Denied',
182
+ 'iam.no.permission': 'You do not have permission to perform this action.',
183
+ 'iam.contact.admin': 'Please contact your administrator for access.',
184
+ 'iam.insufficient.permissions': 'Insufficient permissions',
185
+ // Company
186
+ 'iam.company.title': 'Companies',
187
+ 'iam.company.add': 'Add Company',
188
+ 'iam.company.new': 'New Company',
189
+ 'iam.company.create': 'Create Company',
190
+ 'iam.company.edit': 'Edit Company',
191
+ 'iam.company.select': 'Select Company',
192
+ 'iam.company.name': 'Company Name',
193
+ 'iam.company.name.placeholder': 'Enter company name',
194
+ 'iam.company.slug': 'Company Slug',
195
+ 'iam.company.slug.placeholder': 'Enter company slug',
196
+ 'iam.company.code': 'Company Code',
197
+ 'iam.company.code.placeholder': 'Enter company code',
198
+ 'iam.company.details': 'Company Details',
199
+ 'iam.company.address': 'Address',
200
+ 'iam.company.address.placeholder': 'Enter company address',
201
+ 'iam.company.phone': 'Phone',
202
+ 'iam.company.phone.placeholder': 'Enter phone number',
203
+ 'iam.company.email': 'Email',
204
+ 'iam.company.email.placeholder': 'Enter email address',
205
+ 'iam.company.website': 'Website',
206
+ 'iam.company.website.placeholder': 'Enter website URL',
207
+ 'iam.company.no.companies': 'No companies found',
208
+ 'iam.company.delete.title': 'Delete Company',
209
+ 'iam.company.delete.success': 'Company deleted successfully',
210
+ 'iam.company.slug.required': 'Company slug is required',
211
+ // Branch
212
+ 'iam.branch.title': 'Branches',
213
+ 'iam.branch.add': 'Add Branch',
214
+ 'iam.branch.new': 'New Branch',
215
+ 'iam.branch.create': 'Create Branch',
216
+ 'iam.branch.edit': 'Edit Branch',
217
+ 'iam.branch.name': 'Branch Name',
218
+ 'iam.branch.name.placeholder': 'Enter branch name',
219
+ 'iam.branch.slug': 'Branch Slug',
220
+ 'iam.branch.slug.placeholder': 'Enter branch slug',
221
+ 'iam.branch.code': 'Branch Code',
222
+ 'iam.branch.code.placeholder': 'Enter branch code',
223
+ 'iam.branch.company': 'Company',
224
+ 'iam.branch.no.branches': 'No branches found',
225
+ 'iam.branch.select.company.first': 'Please select a company first',
226
+ 'iam.branch.view.branches': 'View Branches',
227
+ 'iam.branch.delete.title': 'Delete Branch',
228
+ 'iam.branch.delete.success': 'Branch deleted successfully',
229
+ 'iam.branch.slug.required': 'Branch slug is required',
230
+ // Action CRUD messages (API keys)
231
+ 'action.create.success': 'Action created successfully',
232
+ 'action.create.many.success': '{{count}} actions created successfully',
233
+ 'action.get.success': 'Action retrieved successfully',
234
+ 'action.get.all.success': 'Actions retrieved successfully',
235
+ 'action.update.success': 'Action updated successfully',
236
+ 'action.update.many.success': '{{count}} actions updated successfully',
237
+ 'action.delete.success': 'Action deleted successfully',
238
+ 'action.restore.success': 'Action restored successfully',
239
+ 'action.not.found': 'Action not found',
240
+ // Role CRUD messages (API keys)
241
+ 'role.create.many.success': '{{count}} roles created successfully',
242
+ 'role.update.many.success': '{{count}} roles updated successfully',
243
+ 'role.restore.success': 'Role restored successfully',
244
+ 'role.not.found': 'Role not found',
245
+ // Role permission messages (API keys)
246
+ 'role.permission.get.success': 'Role permissions retrieved successfully',
247
+ 'role.permission.assign.success': 'Role permissions assigned successfully',
248
+ 'role.permission.actions.success': 'Role actions retrieved successfully',
249
+ 'role.permission.users.success': 'Role users retrieved successfully',
250
+ 'role.permission.user.roles.success': 'User roles retrieved successfully',
251
+ // User action permission messages (API keys)
252
+ 'user.action.permission.get.success': 'User action permissions retrieved successfully',
253
+ 'user.action.permission.assign.success': 'User action permissions assigned successfully',
254
+ 'user.action.permission.revoke.success': 'User action permissions revoked successfully',
255
+ // Company action permission messages (API keys)
256
+ 'company.action.permission.get.success': 'Company action permissions retrieved successfully',
257
+ 'company.action.permission.assign.success': 'Company action permissions assigned successfully',
258
+ 'company.action.permission.revoke.success': 'Company action permissions revoked successfully',
259
+ // My permission messages (API keys)
260
+ 'my.permission.get.success': 'Permissions loaded successfully',
261
+ // IAM mode messages (API keys)
262
+ 'iam.direct.mode.unavailable': 'Direct permission assignment not available in RBAC-only mode',
263
+ 'iam.rbac.mode.unavailable': 'Role-based permission assignment not available in direct-only mode',
264
+ 'iam.role.assignment.unavailable': 'Role assignment not available in direct-only mode',
265
+ // Validation warnings
266
+ 'iam.validation.warning.title': 'Validation Warning',
267
+ 'iam.validation.unmet.prerequisites.singular': '{{count}} selected action has unmet prerequisites. Fix before saving or use auto-fix on save.',
268
+ 'iam.validation.unmet.prerequisites.plural': '{{count}} selected actions have unmet prerequisites. Fix before saving or use auto-fix on save.',
269
+ 'iam.validation.unmet.prerequisites.tooltip': 'This action has unmet prerequisites and will fail validation on save',
270
+ // Branch selector
271
+ 'iam.branch.permitted.count': '{{count}} permitted branch in current company',
272
+ 'iam.branch.permitted.count.plural': '{{count}} permitted branches in current company',
273
+ // Tooltips
274
+ 'iam.tooltip.remove.action': 'Click to remove',
275
+ 'iam.tooltip.add.action': 'Click to add (auto-selects required)',
276
+ 'iam.tooltip.selected': 'Selected',
277
+ 'iam.tooltip.click.to.remove': 'Click to remove from company whitelist',
278
+ 'iam.tooltip.click.to.add': 'Click to add to company whitelist',
279
+ 'iam.tooltip.assigned.to.role': 'Assigned to role',
280
+ 'iam.tooltip.click.to.remove.role': 'Click to remove',
281
+ 'iam.tooltip.click.to.assign.role': 'Click to assign to role',
282
+ 'iam.tooltip.assigned.to.user': 'Assigned to user',
283
+ 'iam.tooltip.click.to.remove.user': 'Click to remove direct permission',
284
+ 'iam.tooltip.click.to.assign.user': 'Click to assign direct permission',
285
+ 'iam.tooltip.click.to.remove.role.from.user': 'Click to remove role',
286
+ 'iam.tooltip.click.to.assign.role.to.user': 'Click to assign role to user',
287
+ // Logic builder
288
+ 'iam.logic.title': 'Permission Logic',
289
+ 'iam.logic.add.logic': 'Add Logic',
290
+ 'iam.logic.clear.logic': 'Clear Logic',
291
+ 'iam.logic.description': 'Define permission requirements using AND/OR logic with actions',
292
+ 'iam.logic.conditions': '{{count}} conditions',
293
+ 'iam.logic.select.action': 'Select Action...',
294
+ 'iam.logic.actions.available': '{{count}} available',
295
+ 'iam.logic.remove': 'Remove',
296
+ 'iam.logic.add.condition': 'Add Condition:',
297
+ 'iam.logic.group': 'Group',
298
+ 'iam.logic.action': 'Action',
299
+ // Permission logic dialogs
300
+ 'iam.logic.validation.failed': 'Validation Failed',
301
+ 'iam.logic.validation.failed.message': 'The following actions have unmet prerequisites:',
302
+ 'iam.logic.validation.failed.prompt': 'Would you like to:',
303
+ 'iam.logic.auto.fix': 'Auto-fix (Remove Invalid)',
304
+ 'iam.logic.invalid.actions.removed': 'Invalid Actions Removed',
305
+ 'iam.logic.removed.actions.detail': 'Removed {{count}} action(s) with unmet prerequisites. You can now save.',
306
+ 'iam.logic.save.cancelled': 'Save Cancelled',
307
+ 'iam.logic.fix.prerequisites.manually': 'Please fix the prerequisites manually before saving',
308
+ 'iam.logic.missing.prerequisites': 'Missing Prerequisites',
309
+ 'iam.logic.requires.conditions': 'requires the following conditions to be satisfied:',
310
+ 'iam.logic.auto.select.prompt': 'Auto-select will choose {{count}} action(s){{suffix}}.',
311
+ 'iam.logic.minimum.required': ' (minimum required)',
312
+ 'iam.logic.would.you.continue': 'Would you like to continue?',
313
+ 'iam.logic.select.action.label': 'Select Action',
314
+ 'iam.logic.auto.select.actions': 'Auto-select Actions',
315
+ 'iam.logic.actions.selected.summary': 'Actions Selected',
316
+ 'iam.logic.action.selected.detail': 'Action selected successfully (prerequisites already satisfied)',
317
+ 'iam.logic.auto.selected.detail': 'Automatically selected {{count}} action(s) including prerequisites',
318
+ 'iam.logic.selection.cancelled': 'Selection Cancelled',
319
+ 'iam.logic.action.not.added': 'Action not added to whitelist',
320
+ 'iam.logic.dependency.warning': 'Dependency Warning',
321
+ 'iam.logic.required.by.actions': 'is required by the following action(s):',
322
+ 'iam.logic.alternatives.available': 'Alternative options available:',
323
+ 'iam.logic.switch.to.alternatives': 'Would you like to automatically switch to alternatives?',
324
+ 'iam.logic.remove.dependents': 'Removing this will also remove the dependent action(s).',
325
+ 'iam.logic.use.alternatives': 'Use Alternatives',
326
+ 'iam.logic.remove.all': 'Remove All',
327
+ 'iam.logic.alternatives.applied': 'Alternatives Applied',
328
+ 'iam.logic.switched.to.alternatives': 'Switched to alternative actions to maintain dependencies',
329
+ 'iam.logic.actions.removed': 'Actions Removed',
330
+ 'iam.logic.removed.with.dependents': 'Removed {{name}} and {{count}} dependent action(s)',
331
+ 'iam.logic.cancelled': 'Cancelled',
332
+ 'iam.logic.no.changes.made': 'No changes made',
333
+ 'iam.logic.prerequisites.satisfied': '[OK] Prerequisites Satisfied',
334
+ 'iam.logic.all.required.selected': 'All required actions are already selected.\nYou can safely add this action.',
335
+ 'iam.logic.prerequisites.required': 'Prerequisites Required ({{count}} missing)',
336
+ 'iam.logic.click.to.auto.select': 'Click to auto-select required actions',
337
+ 'iam.logic.satisfied': '(satisfied)',
338
+ 'iam.logic.missing': '(missing)',
339
+ 'iam.logic.unknown.action': 'Unknown Action',
340
+ 'iam.logic.invalid.node': 'Invalid logic node',
341
+ 'iam.logic.invalid': '(invalid)',
342
+ 'iam.logic.click.to.toggle': 'Click to toggle',
343
+ // Pagination templates
344
+ 'iam.pagination.roles.template': 'Showing {first} to {last} of {totalRecords} roles',
345
+ 'iam.pagination.actions.template': 'Showing {first} to {last} of {totalRecords} actions',
346
+ 'iam.pagination.users.template': 'Showing {first} to {last} of {totalRecords} users',
347
+ 'iam.pagination.companies.template': 'Showing {first} to {last} of {totalRecords} companies',
348
+ 'iam.pagination.branches.template': 'Showing {first} to {last} of {totalRecords} branches',
349
+ };
350
+
35
351
  class RoleApiService extends ApiResourceService {
36
352
  constructor() {
37
353
  super('roles', inject(HttpClient), 'iam');
@@ -136,6 +452,10 @@ function evaluateLogicNodeWithMissing(node, selectedActionIds) {
136
452
  class ActionPermissionLogicService {
137
453
  confirmationService = inject(ConfirmationService);
138
454
  messageService = inject(MessageService);
455
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
456
+ t(key, variables) {
457
+ return this.translateAdapter?.translate(key, variables) ?? key;
458
+ }
139
459
  /** Handle checking an action with prerequisite validation (recursive deep scan) */
140
460
  handleCheck(action, currentSelection, allActions, onUpdate, onCancel) {
141
461
  // Validate prerequisites with RECURSIVE DEEP SCAN
@@ -196,19 +516,19 @@ class ActionPermissionLogicService {
196
516
  const selectedActionIds = this.getSelectedIds(currentSelection);
197
517
  selectedActionIds.delete(action.id);
198
518
  const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
199
- const sanitizedActionName = this.sanitizeHtml(action.name ?? 'Unknown');
519
+ const sanitizedActionName = this.sanitizeHtml(action.name ?? this.t('iam.logic.unknown.action'));
200
520
  const missingNames = validationResult.missingActions
201
- .map((a) => this.sanitizeHtml(a.name ?? 'Unknown'))
521
+ .map((a) => this.sanitizeHtml(a.name ?? this.t('iam.logic.unknown.action')))
202
522
  .join(', ');
203
523
  return `<li><strong>${sanitizedActionName}</strong> requires: ${missingNames}</li>`;
204
524
  })
205
525
  .join('');
206
526
  this.confirmationService.confirm({
207
- header: 'Validation Failed',
208
- message: `The following actions have unmet prerequisites:<br/><br/><ul class="pl-4">${errorList}</ul><br/>Would you like to:`,
527
+ header: this.t('iam.logic.validation.failed'),
528
+ message: `${this.t('iam.logic.validation.failed.message')}<br/><br/><ul class="pl-4">${errorList}</ul><br/>${this.t('iam.logic.validation.failed.prompt')}`,
209
529
  icon: 'pi pi-exclamation-triangle',
210
- acceptLabel: 'Auto-fix (Remove Invalid)',
211
- rejectLabel: 'Cancel',
530
+ acceptLabel: this.t('iam.logic.auto.fix'),
531
+ rejectLabel: this.t('shared.cancel'),
212
532
  acceptIcon: 'pi pi-trash',
213
533
  rejectIcon: 'pi pi-times',
214
534
  acceptButtonStyleClass: 'p-button-warning',
@@ -223,15 +543,15 @@ class ActionPermissionLogicService {
223
543
  onUpdate(selMap);
224
544
  this.messageService.add({
225
545
  severity: 'warning',
226
- summary: 'Invalid Actions Removed',
227
- detail: `Removed ${invalidActions.length} action${invalidActions.length > 1 ? 's' : ''} with unmet prerequisites. You can now save.`,
546
+ summary: this.t('iam.logic.invalid.actions.removed'),
547
+ detail: this.t('iam.logic.removed.actions.detail', { count: invalidActions.length }),
228
548
  });
229
549
  },
230
550
  reject: () => {
231
551
  this.messageService.add({
232
552
  severity: 'info',
233
- summary: 'Save Cancelled',
234
- detail: 'Please fix the prerequisites manually before saving',
553
+ summary: this.t('iam.logic.save.cancelled'),
554
+ detail: this.t('iam.logic.fix.prerequisites.manually'),
235
555
  });
236
556
  },
237
557
  });
@@ -244,13 +564,13 @@ class ActionPermissionLogicService {
244
564
  const selectedActionIds = this.getSelectedIds(currentSelection);
245
565
  const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
246
566
  if (validationResult.valid) {
247
- return '[OK] Prerequisites Satisfied\n\nAll required actions are already selected.\nYou can safely add this action.';
567
+ return `${this.t('iam.logic.prerequisites.satisfied')}\n\n${this.t('iam.logic.all.required.selected')}`;
248
568
  }
249
569
  const allMissingActions = this.getAllMissingPrerequisitesRecursive(action, currentSelection, allActions);
250
570
  const logicTree = this.buildTooltipLogicTree(action.permissionLogic, currentSelection, allActions, 0);
251
571
  const missingCount = allMissingActions.length;
252
- const header = `⚠ Prerequisites Required (${missingCount} missing)`;
253
- const hint = '\n\n💡 Click to auto-select required actions';
572
+ const header = `⚠ ${this.t('iam.logic.prerequisites.required', { count: missingCount })}`;
573
+ const hint = `\n\n💡 ${this.t('iam.logic.click.to.auto.select')}`;
254
574
  return `${header}\n\n${logicTree}${hint}`;
255
575
  }
256
576
  /** Build dynamic logic tree message with AND/OR operators and nesting */
@@ -314,17 +634,19 @@ class ActionPermissionLogicService {
314
634
  ? this.calculateSmartSelection(action.permissionLogic, missingActions, currentSelection, allActions)
315
635
  : missingActions;
316
636
  const selectionCount = actionsToSelect.length;
317
- const sanitizedActionName = this.sanitizeHtml(action.name ?? 'Unknown Action');
318
- const acceptLabel = selectionCount === 0 ? 'Select Action' : 'Auto-select Actions';
637
+ const sanitizedActionName = this.sanitizeHtml(action.name ?? this.t('iam.logic.unknown.action'));
638
+ const acceptLabel = selectionCount === 0 ? this.t('iam.logic.select.action.label') : this.t('iam.logic.auto.select.actions');
319
639
  const detailMessage = selectionCount === 0
320
- ? 'Action selected successfully (prerequisites already satisfied)'
321
- : `Automatically selected ${selectionCount} action${selectionCount !== 1 ? 's' : ''} including prerequisites`;
640
+ ? this.t('iam.logic.action.selected.detail')
641
+ : this.t('iam.logic.auto.selected.detail', { count: selectionCount });
642
+ const suffix = selectionCount < missingActions.length ? this.t('iam.logic.minimum.required') : '';
643
+ const autoSelectPrompt = this.t('iam.logic.auto.select.prompt', { count: selectionCount, suffix });
322
644
  this.confirmationService.confirm({
323
- header: 'Missing Prerequisites',
324
- message: `<strong>${sanitizedActionName}</strong> requires the following conditions to be satisfied:<br/><br/>${logicMessage}<br/><br/><strong>Auto-select will choose ${selectionCount} action${selectionCount !== 1 ? 's' : ''}${selectionCount < missingActions.length ? ' (minimum required)' : ''}.</strong><br/>Would you like to continue?`,
645
+ header: this.t('iam.logic.missing.prerequisites'),
646
+ message: `<strong>${sanitizedActionName}</strong> ${this.t('iam.logic.requires.conditions')}<br/><br/>${logicMessage}<br/><br/><strong>${autoSelectPrompt}</strong><br/>${this.t('iam.logic.would.you.continue')}`,
325
647
  icon: 'pi pi-exclamation-triangle',
326
648
  acceptLabel,
327
- rejectLabel: 'Cancel',
649
+ rejectLabel: this.t('shared.cancel'),
328
650
  acceptIcon: 'pi pi-check',
329
651
  rejectIcon: 'pi pi-times',
330
652
  closeOnEscape: false,
@@ -338,7 +660,7 @@ class ActionPermissionLogicService {
338
660
  onUpdate(selMap);
339
661
  this.messageService.add({
340
662
  severity: 'info',
341
- summary: 'Actions Selected',
663
+ summary: this.t('iam.logic.actions.selected.summary'),
342
664
  detail: detailMessage,
343
665
  });
344
666
  },
@@ -346,8 +668,8 @@ class ActionPermissionLogicService {
346
668
  onCancel(previousState);
347
669
  this.messageService.add({
348
670
  severity: 'info',
349
- summary: 'Selection Cancelled',
350
- detail: 'Action not added to whitelist',
671
+ summary: this.t('iam.logic.selection.cancelled'),
672
+ detail: this.t('iam.logic.action.not.added'),
351
673
  });
352
674
  },
353
675
  });
@@ -356,29 +678,27 @@ class ActionPermissionLogicService {
356
678
  if (!action.id)
357
679
  return;
358
680
  const alternatives = this.findAlternatives(action.id, affectedActions, currentSelection);
359
- const sanitizedActionName = this.sanitizeHtml(action.name ?? 'Unknown Action');
360
- let message = `<strong>${sanitizedActionName}</strong> is required by the following action${affectedActions.length > 1 ? 's' : ''}:<br/><br/><ul class="pl-4">`;
681
+ const sanitizedActionName = this.sanitizeHtml(action.name ?? this.t('iam.logic.unknown.action'));
682
+ let message = `<strong>${sanitizedActionName}</strong> ${this.t('iam.logic.required.by.actions')}<br/><br/><ul class="pl-4">`;
361
683
  affectedActions.forEach((a) => {
362
- const sanitizedName = this.sanitizeHtml(a.name ?? 'Unknown');
684
+ const sanitizedName = this.sanitizeHtml(a.name ?? this.t('iam.logic.unknown.action'));
363
685
  message += `<li>${sanitizedName}</li>`;
364
686
  });
365
687
  message += '</ul><br/>';
366
688
  if (alternatives.length > 0) {
367
- message += '<strong>Alternative options available:</strong><br/><br/>';
368
- message +=
369
- '<em>Would you like to automatically switch to alternatives?</em>';
689
+ message += `<strong>${this.t('iam.logic.alternatives.available')}</strong><br/><br/>`;
690
+ message += `<em>${this.t('iam.logic.switch.to.alternatives')}</em>`;
370
691
  }
371
692
  else {
372
- message +=
373
- '<em>Removing this will also remove the dependent action(s).</em><br/><br/>';
374
- message += 'Would you like to continue?';
693
+ message += `<em>${this.t('iam.logic.remove.dependents')}</em><br/><br/>`;
694
+ message += this.t('iam.logic.would.you.continue');
375
695
  }
376
696
  this.confirmationService.confirm({
377
- header: 'Dependency Warning',
697
+ header: this.t('iam.logic.dependency.warning'),
378
698
  message,
379
699
  icon: 'pi pi-exclamation-triangle',
380
- acceptLabel: alternatives.length > 0 ? 'Use Alternatives' : 'Remove All',
381
- rejectLabel: 'Cancel',
700
+ acceptLabel: alternatives.length > 0 ? this.t('iam.logic.use.alternatives') : this.t('iam.logic.remove.all'),
701
+ rejectLabel: this.t('shared.cancel'),
382
702
  acceptIcon: 'pi pi-check',
383
703
  rejectIcon: 'pi pi-times',
384
704
  closeOnEscape: false,
@@ -393,8 +713,8 @@ class ActionPermissionLogicService {
393
713
  selMap[action.id] = false;
394
714
  this.messageService.add({
395
715
  severity: 'success',
396
- summary: 'Alternatives Applied',
397
- detail: 'Switched to alternative actions to maintain dependencies',
716
+ summary: this.t('iam.logic.alternatives.applied'),
717
+ detail: this.t('iam.logic.switched.to.alternatives'),
398
718
  });
399
719
  }
400
720
  else {
@@ -405,8 +725,8 @@ class ActionPermissionLogicService {
405
725
  });
406
726
  this.messageService.add({
407
727
  severity: 'warn',
408
- summary: 'Actions Removed',
409
- detail: `Removed ${action.name} and ${affectedActions.length} dependent action${affectedActions.length > 1 ? 's' : ''}`,
728
+ summary: this.t('iam.logic.actions.removed'),
729
+ detail: this.t('iam.logic.removed.with.dependents', { name: action.name ?? '', count: affectedActions.length }),
410
730
  });
411
731
  }
412
732
  onUpdate(selMap);
@@ -414,8 +734,8 @@ class ActionPermissionLogicService {
414
734
  reject: () => {
415
735
  this.messageService.add({
416
736
  severity: 'info',
417
- summary: 'Cancelled',
418
- detail: 'No changes made',
737
+ summary: this.t('iam.logic.cancelled'),
738
+ detail: this.t('iam.logic.no.changes.made'),
419
739
  });
420
740
  },
421
741
  });
@@ -517,19 +837,19 @@ class ActionPermissionLogicService {
517
837
  return new Set();
518
838
  }
519
839
  buildSimpleMessage(missingActions) {
520
- return `<ul class="pl-4">${missingActions.map((a) => `<li>${this.sanitizeHtml(a.name ?? 'Unknown')}</li>`).join('')}</ul>`;
840
+ return `<ul class="pl-4">${missingActions.map((a) => `<li>${this.sanitizeHtml(a.name ?? this.t('iam.logic.unknown.action'))}</li>`).join('')}</ul>`;
521
841
  }
522
842
  formatLogicNode(node, selectedIds, actionMap, depth) {
523
843
  const indent = '&nbsp;&nbsp;&nbsp;&nbsp;'.repeat(depth);
524
844
  if (node.type === 'action' && node.actionId) {
525
845
  const action = actionMap.get(node.actionId);
526
- const actionName = this.sanitizeHtml(action?.name || 'Unknown Action');
846
+ const actionName = this.sanitizeHtml(action?.name || this.t('iam.logic.unknown.action'));
527
847
  const isSelected = selectedIds.has(node.actionId);
528
848
  if (isSelected) {
529
- return `${indent}<span style="color: #10b981; font-weight: 500;">✓ ${actionName}</span> <em style="color: #059669;">(satisfied)</em>`;
849
+ return `${indent}<span style="color: #10b981; font-weight: 500;">✓ ${actionName}</span> <em style="color: #059669;">${this.t('iam.logic.satisfied')}</em>`;
530
850
  }
531
851
  else {
532
- return `${indent}<span style="color: #ef4444;">✗ ${actionName}</span> <em style="color: #dc2626;">(missing)</em>`;
852
+ return `${indent}<span style="color: #ef4444;">✗ ${actionName}</span> <em style="color: #dc2626;">${this.t('iam.logic.missing')}</em>`;
533
853
  }
534
854
  }
535
855
  if (node.type === 'group' && node.children) {
@@ -546,7 +866,7 @@ class ActionPermissionLogicService {
546
866
  });
547
867
  return lines.join('<br/>');
548
868
  }
549
- return `${indent}<em style="color: #9ca3af;">Invalid logic node</em>`;
869
+ return `${indent}<em style="color: #9ca3af;">${this.t('iam.logic.invalid.node')}</em>`;
550
870
  }
551
871
  /** Build clean text-based logic tree for tooltips */
552
872
  buildTooltipLogicTree(node, currentSelection, allActions, depth) {
@@ -555,7 +875,7 @@ class ActionPermissionLogicService {
555
875
  const actionMap = new Map(allActions.map((a) => [a.id, a]));
556
876
  if (node.type === 'action' && node.actionId) {
557
877
  const action = actionMap.get(node.actionId);
558
- const actionName = action?.name || 'Unknown';
878
+ const actionName = action?.name || this.t('iam.logic.unknown.action');
559
879
  const isSelected = selectedIds.has(node.actionId);
560
880
  if (isSelected) {
561
881
  return `${indent}✓ ${actionName}`;
@@ -579,7 +899,7 @@ class ActionPermissionLogicService {
579
899
  });
580
900
  return lines.join('\n');
581
901
  }
582
- return `${indent}(invalid)`;
902
+ return `${indent}${this.t('iam.logic.invalid')}`;
583
903
  }
584
904
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ActionPermissionLogicService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
585
905
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ActionPermissionLogicService, providedIn: 'root' });
@@ -801,16 +1121,16 @@ class LogicBuilderComponent {
801
1121
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: LogicBuilderComponent, isStandalone: true, selector: "lib-logic-builder", inputs: { logic: { classPropertyName: "logic", publicName: "logic", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { logicChange: "logicChange" }, ngImport: i0, template: `
802
1122
  <div class="logic-builder">
803
1123
  <div class="flex justify-between items-center mb-3">
804
- <h4 class="text-sm font-semibold m-0">Permission Logic</h4>
1124
+ <h4 class="text-sm font-semibold m-0">{{ 'iam.logic.title' | translate }}</h4>
805
1125
  @if (!builderLogic()) {
806
1126
  <p-button
807
- label="Add Logic"
1127
+ [label]="'iam.logic.add.logic' | translate"
808
1128
  icon="pi pi-plus"
809
1129
  size="small"
810
1130
  (onClick)="initializeLogic()" />
811
1131
  } @else {
812
1132
  <p-button
813
- label="Clear Logic"
1133
+ [label]="'iam.logic.clear.logic' | translate"
814
1134
  icon="pi pi-trash"
815
1135
  size="small"
816
1136
  severity="danger"
@@ -822,7 +1142,7 @@ class LogicBuilderComponent {
822
1142
  @if (builderLogic()) {
823
1143
  <div class="border border-surface rounded p-3 bg-surface-50">
824
1144
  <div class="mb-3 text-sm text-muted-color">
825
- Define permission requirements using AND/OR logic with actions
1145
+ {{ 'iam.logic.description' | translate }}
826
1146
  </div>
827
1147
 
828
1148
  <!-- Root Node -->
@@ -846,10 +1166,10 @@ class LogicBuilderComponent {
846
1166
  class="operator-badge"
847
1167
  [ngClass]="node.operator === 'AND' ? 'and' : 'or'"
848
1168
  (click)="toggleOperator(node.id)"
849
- title="Click to toggle">
1169
+ [title]="'iam.logic.click.to.toggle' | translate">
850
1170
  {{ node.operator }}
851
1171
  </span>
852
- <span class="text-muted-color text-xs">({{ node.children?.length || 0 }} conditions)</span>
1172
+ <span class="text-muted-color text-xs">({{ 'iam.logic.conditions' | translate: { count: node.children?.length || 0 } }})</span>
853
1173
  }
854
1174
 
855
1175
  @if (node.type === 'action') {
@@ -858,7 +1178,7 @@ class LogicBuilderComponent {
858
1178
  class="action-select flex-1 p-2 border border-surface rounded text-sm"
859
1179
  [ngModel]="node.actionId"
860
1180
  (ngModelChange)="updateActionId(node.id, $event)">
861
- <option [value]="null">Select Action... ({{ actions().length }} available)</option>
1181
+ <option [value]="null">{{ 'iam.logic.select.action' | translate }} ({{ 'iam.logic.actions.available' | translate: { count: actions().length } }})</option>
862
1182
  @for (action of actions(); track action.id) {
863
1183
  <option [ngValue]="action.id">{{ action.name }}</option>
864
1184
  }
@@ -873,7 +1193,7 @@ class LogicBuilderComponent {
873
1193
  severity="danger"
874
1194
  size="small"
875
1195
  (onClick)="removeNode(node.id)"
876
- [pTooltip]="'Remove'" />
1196
+ [pTooltip]="'iam.logic.remove' | translate" />
877
1197
  </div>
878
1198
  </div>
879
1199
 
@@ -889,16 +1209,16 @@ class LogicBuilderComponent {
889
1209
  <!-- Add child buttons for group nodes -->
890
1210
  @if (node.type === 'group') {
891
1211
  <div class="mt-3 p-3 bg-surface-50 rounded border border-dashed border-surface">
892
- <div class="text-xs font-semibold text-muted-color mb-2">Add Condition:</div>
1212
+ <div class="text-xs font-semibold text-muted-color mb-2">{{ 'iam.logic.add.condition' | translate }}</div>
893
1213
  <div class="flex gap-2 flex-wrap">
894
1214
  <p-button
895
- label="Group"
1215
+ [label]="'iam.logic.group' | translate"
896
1216
  icon="pi pi-sitemap"
897
1217
  size="small"
898
1218
  [outlined]="true"
899
1219
  (onClick)="addChildNode(node.id, 'group')" />
900
1220
  <p-button
901
- label="Action"
1221
+ [label]="'iam.logic.action' | translate"
902
1222
  icon="pi pi-bolt"
903
1223
  size="small"
904
1224
  severity="success"
@@ -909,23 +1229,23 @@ class LogicBuilderComponent {
909
1229
  }
910
1230
  </div>
911
1231
  </ng-template>
912
- `, isInline: true, styles: [":host{display:block}.node-type{display:inline-flex;align-items:center;padding:.25rem .5rem;border-radius:.25rem;font-weight:600;font-size:.75rem;text-transform:uppercase}.node-type.group{background-color:var(--p-blue-100, #dbeafe);color:var(--p-blue-700, #1e40af)}.node-type.action{background-color:var(--p-green-100, #d1fae5);color:var(--p-green-700, #065f46)}:host-context(.p-dark) .node-type.group{background-color:#3b82f633;color:var(--p-blue-400, #60a5fa)}:host-context(.p-dark) .node-type.action{background-color:#22c55e33;color:var(--p-green-400, #4ade80)}.operator-badge{display:inline-flex;align-items:center;padding:.125rem .5rem;border-radius:9999px;font-weight:600;font-size:.75rem;cursor:pointer;transition:opacity .2s}.operator-badge.and{background-color:var(--p-yellow-100, #fef3c7);color:var(--p-yellow-700, #92400e)}.operator-badge.or{background-color:var(--p-indigo-100, #e0e7ff);color:var(--p-indigo-700, #3730a3)}:host-context(.p-dark) .operator-badge.and{background-color:#eab30833;color:var(--p-yellow-400, #facc15)}:host-context(.p-dark) .operator-badge.or{background-color:#6366f133;color:var(--p-indigo-400, #818cf8)}.operator-badge:hover{opacity:.8}.action-select{background-color:var(--p-surface-0, #ffffff);color:var(--p-text-color, #1f2937)}:host-context(.p-dark) .action-select{background-color:var(--p-surface-900, #1f2937);color:var(--p-text-color, #f9fafb)}.action-select:focus{outline:none;border-color:var(--p-primary-color, #3b82f6)}\n"], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1232
+ `, isInline: true, styles: [":host{display:block}.node-type{display:inline-flex;align-items:center;padding:.25rem .5rem;border-radius:.25rem;font-weight:600;font-size:.75rem;text-transform:uppercase}.node-type.group{background-color:var(--p-blue-100, #dbeafe);color:var(--p-blue-700, #1e40af)}.node-type.action{background-color:var(--p-green-100, #d1fae5);color:var(--p-green-700, #065f46)}:host-context(.p-dark) .node-type.group{background-color:#3b82f633;color:var(--p-blue-400, #60a5fa)}:host-context(.p-dark) .node-type.action{background-color:#22c55e33;color:var(--p-green-400, #4ade80)}.operator-badge{display:inline-flex;align-items:center;padding:.125rem .5rem;border-radius:9999px;font-weight:600;font-size:.75rem;cursor:pointer;transition:opacity .2s}.operator-badge.and{background-color:var(--p-yellow-100, #fef3c7);color:var(--p-yellow-700, #92400e)}.operator-badge.or{background-color:var(--p-indigo-100, #e0e7ff);color:var(--p-indigo-700, #3730a3)}:host-context(.p-dark) .operator-badge.and{background-color:#eab30833;color:var(--p-yellow-400, #facc15)}:host-context(.p-dark) .operator-badge.or{background-color:#6366f133;color:var(--p-indigo-400, #818cf8)}.operator-badge:hover{opacity:.8}.action-select{background-color:var(--p-surface-0, #ffffff);color:var(--p-text-color, #1f2937)}:host-context(.p-dark) .action-select{background-color:var(--p-surface-900, #1f2937);color:var(--p-text-color, #f9fafb)}.action-select:focus{outline:none;border-color:var(--p-primary-color, #3b82f6)}\n"], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
913
1233
  }
914
1234
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LogicBuilderComponent, decorators: [{
915
1235
  type: Component,
916
- args: [{ selector: 'lib-logic-builder', imports: [AngularModule, PrimeModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
1236
+ args: [{ selector: 'lib-logic-builder', imports: [AngularModule, PrimeModule, TranslatePipe], template: `
917
1237
  <div class="logic-builder">
918
1238
  <div class="flex justify-between items-center mb-3">
919
- <h4 class="text-sm font-semibold m-0">Permission Logic</h4>
1239
+ <h4 class="text-sm font-semibold m-0">{{ 'iam.logic.title' | translate }}</h4>
920
1240
  @if (!builderLogic()) {
921
1241
  <p-button
922
- label="Add Logic"
1242
+ [label]="'iam.logic.add.logic' | translate"
923
1243
  icon="pi pi-plus"
924
1244
  size="small"
925
1245
  (onClick)="initializeLogic()" />
926
1246
  } @else {
927
1247
  <p-button
928
- label="Clear Logic"
1248
+ [label]="'iam.logic.clear.logic' | translate"
929
1249
  icon="pi pi-trash"
930
1250
  size="small"
931
1251
  severity="danger"
@@ -937,7 +1257,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
937
1257
  @if (builderLogic()) {
938
1258
  <div class="border border-surface rounded p-3 bg-surface-50">
939
1259
  <div class="mb-3 text-sm text-muted-color">
940
- Define permission requirements using AND/OR logic with actions
1260
+ {{ 'iam.logic.description' | translate }}
941
1261
  </div>
942
1262
 
943
1263
  <!-- Root Node -->
@@ -961,10 +1281,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
961
1281
  class="operator-badge"
962
1282
  [ngClass]="node.operator === 'AND' ? 'and' : 'or'"
963
1283
  (click)="toggleOperator(node.id)"
964
- title="Click to toggle">
1284
+ [title]="'iam.logic.click.to.toggle' | translate">
965
1285
  {{ node.operator }}
966
1286
  </span>
967
- <span class="text-muted-color text-xs">({{ node.children?.length || 0 }} conditions)</span>
1287
+ <span class="text-muted-color text-xs">({{ 'iam.logic.conditions' | translate: { count: node.children?.length || 0 } }})</span>
968
1288
  }
969
1289
 
970
1290
  @if (node.type === 'action') {
@@ -973,7 +1293,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
973
1293
  class="action-select flex-1 p-2 border border-surface rounded text-sm"
974
1294
  [ngModel]="node.actionId"
975
1295
  (ngModelChange)="updateActionId(node.id, $event)">
976
- <option [value]="null">Select Action... ({{ actions().length }} available)</option>
1296
+ <option [value]="null">{{ 'iam.logic.select.action' | translate }} ({{ 'iam.logic.actions.available' | translate: { count: actions().length } }})</option>
977
1297
  @for (action of actions(); track action.id) {
978
1298
  <option [ngValue]="action.id">{{ action.name }}</option>
979
1299
  }
@@ -988,7 +1308,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
988
1308
  severity="danger"
989
1309
  size="small"
990
1310
  (onClick)="removeNode(node.id)"
991
- [pTooltip]="'Remove'" />
1311
+ [pTooltip]="'iam.logic.remove' | translate" />
992
1312
  </div>
993
1313
  </div>
994
1314
 
@@ -1004,16 +1324,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1004
1324
  <!-- Add child buttons for group nodes -->
1005
1325
  @if (node.type === 'group') {
1006
1326
  <div class="mt-3 p-3 bg-surface-50 rounded border border-dashed border-surface">
1007
- <div class="text-xs font-semibold text-muted-color mb-2">Add Condition:</div>
1327
+ <div class="text-xs font-semibold text-muted-color mb-2">{{ 'iam.logic.add.condition' | translate }}</div>
1008
1328
  <div class="flex gap-2 flex-wrap">
1009
1329
  <p-button
1010
- label="Group"
1330
+ [label]="'iam.logic.group' | translate"
1011
1331
  icon="pi pi-sitemap"
1012
1332
  size="small"
1013
1333
  [outlined]="true"
1014
1334
  (onClick)="addChildNode(node.id, 'group')" />
1015
1335
  <p-button
1016
- label="Action"
1336
+ [label]="'iam.logic.action' | translate"
1017
1337
  icon="pi pi-bolt"
1018
1338
  size="small"
1019
1339
  severity="success"
@@ -1110,7 +1430,6 @@ function convertActionToTreeNode(actions) {
1110
1430
  * - Pre-save validation with auto-fix
1111
1431
  *
1112
1432
  * **Performance:**
1113
- * - OnPush change detection
1114
1433
  * - Computed signals for reactive updates
1115
1434
  * - Memoized prerequisite validation
1116
1435
  * - AbortController for request cancellation
@@ -1130,6 +1449,10 @@ class RoleActionSelectorComponent {
1130
1449
  permissionApi = inject(PermissionApiService);
1131
1450
  messageService = inject(MessageService);
1132
1451
  permissionLogic = inject(ActionPermissionLogicService);
1452
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
1453
+ translate(key, vars) {
1454
+ return this.translateAdapter?.translate(key, vars) ?? key;
1455
+ }
1133
1456
  // State - Role Selection
1134
1457
  selectedRoleId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedRoleId" }] : []));
1135
1458
  roles = signal([], ...(ngDevMode ? [{ debugName: "roles" }] : []));
@@ -1288,15 +1611,15 @@ class RoleActionSelectorComponent {
1288
1611
  const prerequisiteInfo = this.permissionLogic.getPrerequisiteTooltip(action, selMap, this.actions());
1289
1612
  if (prerequisiteInfo) {
1290
1613
  const actionHint = isCurrentlySelected
1291
- ? '\n\n---\n[Remove] Click to remove'
1292
- : '\n\n---\n[Add] Click to add (auto-selects required)';
1614
+ ? `\n\n---\n[${this.translate('shared.remove')}] ${this.translate('iam.tooltip.remove.action')}`
1615
+ : `\n\n---\n[${this.translate('shared.add')}] ${this.translate('iam.tooltip.add.action')}`;
1293
1616
  return `${prerequisiteInfo}${actionHint}`;
1294
1617
  }
1295
1618
  }
1296
1619
  if (isCurrentlySelected) {
1297
- return '[Assigned] Assigned to role\n\n[Remove] Click to remove';
1620
+ return `[${this.translate('shared.assigned')}] ${this.translate('iam.tooltip.assigned.to.role')}\n\n[${this.translate('shared.remove')}] ${this.translate('iam.tooltip.click.to.remove.role')}`;
1298
1621
  }
1299
- return '[Add] Click to assign to role';
1622
+ return `[${this.translate('shared.add')}] ${this.translate('iam.tooltip.click.to.assign.role')}`;
1300
1623
  }
1301
1624
  /**
1302
1625
  * Check if action has unmet prerequisites
@@ -1360,8 +1683,8 @@ class RoleActionSelectorComponent {
1360
1683
  }));
1361
1684
  this.messageService.add({
1362
1685
  severity: 'success',
1363
- summary: 'Success',
1364
- detail: response?.data?.message || 'Role permissions updated successfully',
1686
+ summary: this.translate('shared.success'),
1687
+ detail: response?.data?.message || this.translate('iam.permission.role.permissions.updated'),
1365
1688
  });
1366
1689
  // Update baseline
1367
1690
  this._initialSelection.set({ ...this.selectionMap() });
@@ -1400,14 +1723,14 @@ class RoleActionSelectorComponent {
1400
1723
  <div class="mb-4">
1401
1724
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
1402
1725
  <div>
1403
- <label class="block font-semibold mb-2">Select Role</label>
1726
+ <label class="block font-semibold mb-2">{{ 'iam.permission.select.role' | translate }}</label>
1404
1727
  <p-select
1405
1728
  [ngModel]="selectedRoleId()"
1406
1729
  (ngModelChange)="selectedRoleId.set($event)"
1407
1730
  [options]="roles()"
1408
1731
  optionLabel="name"
1409
1732
  optionValue="id"
1410
- placeholder="Select a role"
1733
+ [placeholder]="'iam.permission.select.role.placeholder' | translate"
1411
1734
  [showClear]="true"
1412
1735
  [filter]="true"
1413
1736
  styleClass="w-full"
@@ -1433,14 +1756,14 @@ class RoleActionSelectorComponent {
1433
1756
  class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4"
1434
1757
  >
1435
1758
  <div>
1436
- <h5 class="m-0 mb-1">Action Permissions</h5>
1759
+ <h5 class="m-0 mb-1">{{ 'iam.permission.action.permissions' | translate }}</h5>
1437
1760
  <p class="text-sm text-muted-color m-0">
1438
- {{ actions().length }} actions available
1761
+ {{ 'iam.permission.actions.available' | translate: { count: actions().length } }}
1439
1762
  </p>
1440
1763
  </div>
1441
1764
  <div class="flex flex-wrap gap-2">
1442
1765
  <p-button
1443
- label="Select All"
1766
+ [label]="'shared.select.all' | translate"
1444
1767
  icon="pi pi-check"
1445
1768
  [outlined]="true"
1446
1769
  size="small"
@@ -1448,7 +1771,7 @@ class RoleActionSelectorComponent {
1448
1771
  (onClick)="selectAll()"
1449
1772
  />
1450
1773
  <p-button
1451
- label="Deselect All"
1774
+ [label]="'shared.deselect.all' | translate"
1452
1775
  icon="pi pi-times"
1453
1776
  [outlined]="true"
1454
1777
  size="small"
@@ -1457,7 +1780,7 @@ class RoleActionSelectorComponent {
1457
1780
  />
1458
1781
  <p-button
1459
1782
  *hasPermission="ROLE_ACTION_PERMISSIONS.ASSIGN"
1460
- label="Save Changes"
1783
+ [label]="'shared.save.changes' | translate"
1461
1784
  icon="pi pi-save"
1462
1785
  [disabled]="!canSave()"
1463
1786
  [loading]="saving()"
@@ -1474,12 +1797,9 @@ class RoleActionSelectorComponent {
1474
1797
  <div class="flex items-start gap-2">
1475
1798
  <i class="pi pi-exclamation-triangle text-xl"></i>
1476
1799
  <div class="flex-1">
1477
- <span class="font-semibold">Validation Warning:</span>
1800
+ <span class="font-semibold">{{ 'iam.validation.warning.title' | translate }}:</span>
1478
1801
  <p class="text-sm mt-1 mb-0">
1479
- {{ invalidActionsCount() }} selected
1480
- action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
1481
- unmet prerequisites. Fix before saving or use auto-fix on
1482
- save.
1802
+ {{ (invalidActionsCount() > 1 ? 'iam.validation.unmet.prerequisites.plural' : 'iam.validation.unmet.prerequisites.singular') | translate: { count: invalidActionsCount() } }}
1483
1803
  </p>
1484
1804
  </div>
1485
1805
  </div>
@@ -1501,14 +1821,14 @@ class RoleActionSelectorComponent {
1501
1821
  [ngModel]="allSelected()"
1502
1822
  [binary]="true"
1503
1823
  (ngModelChange)="toggleAll()"
1504
- pTooltip="Select/Deselect All"
1824
+ [pTooltip]="'shared.select.deselect.all' | translate"
1505
1825
  tooltipPosition="top"
1506
1826
  />
1507
1827
  </th>
1508
- <th>Name</th>
1509
- <th class="hidden md:table-cell">Code</th>
1510
- <th>Type</th>
1511
- <th class="hidden lg:table-cell">Requirements</th>
1828
+ <th>{{ 'shared.name' | translate }}</th>
1829
+ <th class="hidden md:table-cell">{{ 'shared.code' | translate }}</th>
1830
+ <th>{{ 'iam.action.type' | translate }}</th>
1831
+ <th class="hidden lg:table-cell">{{ 'iam.permission.requirements' | translate }}</th>
1512
1832
  </tr>
1513
1833
  </ng-template>
1514
1834
  <ng-template #body let-rowNode let-rowData="rowData">
@@ -1529,7 +1849,7 @@ class RoleActionSelectorComponent {
1529
1849
  @if (hasUnmetPrerequisites(rowData)) {
1530
1850
  <i
1531
1851
  class="pi pi-exclamation-triangle text-orange-500"
1532
- pTooltip="This action has unmet prerequisites and will fail validation on save"
1852
+ [pTooltip]="'iam.validation.unmet.prerequisites.tooltip' | translate"
1533
1853
  tooltipPosition="top"
1534
1854
  ></i>
1535
1855
  }
@@ -1546,7 +1866,7 @@ class RoleActionSelectorComponent {
1546
1866
  </td>
1547
1867
  <td class="hidden lg:table-cell">
1548
1868
  @if (rowData.permissionLogic) {
1549
- <span class="text-sm text-muted-color">Has prerequisites</span>
1869
+ <span class="text-sm text-muted-color">{{ 'iam.permission.has.prerequisites' | translate }}</span>
1550
1870
  } @else {
1551
1871
  <span class="text-muted-color">-</span>
1552
1872
  }
@@ -1557,8 +1877,8 @@ class RoleActionSelectorComponent {
1557
1877
  <tr>
1558
1878
  <td colspan="5" class="text-center p-4 text-muted-color">
1559
1879
  @if (loading()) {
1560
- <i class="pi pi-spin pi-spinner"></i> Loading actions...
1561
- } @else { No actions available. }
1880
+ <i class="pi pi-spin pi-spinner"></i> {{ 'shared.loading.actions' | translate }}
1881
+ } @else { {{ 'iam.permission.no.actions.available' | translate }} }
1562
1882
  </td>
1563
1883
  </tr>
1564
1884
  </ng-template>
@@ -1571,14 +1891,14 @@ class RoleActionSelectorComponent {
1571
1891
  <div class="border border-surface rounded-border p-3 mt-4">
1572
1892
  <div class="flex items-center gap-2 mb-3">
1573
1893
  <i class="pi pi-info-circle text-primary"></i>
1574
- <span class="font-bold">Pending Changes</span>
1894
+ <span class="font-bold">{{ 'shared.pending.changes' | translate }}</span>
1575
1895
  </div>
1576
1896
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
1577
1897
  @if (pendingAdd().length > 0) {
1578
1898
  <div>
1579
1899
  <div class="flex items-center gap-2 mb-2">
1580
1900
  <i class="pi pi-plus-circle text-green-500"></i>
1581
- <strong class="text-sm">To Add ({{ pendingAdd().length }})</strong>
1901
+ <strong class="text-sm">{{ 'shared.to.add' | translate }} ({{ pendingAdd().length }})</strong>
1582
1902
  </div>
1583
1903
  <ul class="list-none p-0 m-0 pl-4">
1584
1904
  @for (action of pendingAdd(); track action.id) {
@@ -1591,7 +1911,7 @@ class RoleActionSelectorComponent {
1591
1911
  <div>
1592
1912
  <div class="flex items-center gap-2 mb-2">
1593
1913
  <i class="pi pi-minus-circle text-red-500"></i>
1594
- <strong class="text-sm">To Remove ({{ pendingRemove().length }})</strong>
1914
+ <strong class="text-sm">{{ 'shared.to.remove' | translate }} ({{ pendingRemove().length }})</strong>
1595
1915
  </div>
1596
1916
  <ul class="list-none p-0 m-0 pl-4">
1597
1917
  @for (action of pendingRemove(); track action.id) {
@@ -1609,30 +1929,30 @@ class RoleActionSelectorComponent {
1609
1929
  <div class="surface-card p-5 rounded-border shadow-sm text-center">
1610
1930
  <i class="pi pi-info-circle text-muted-color mb-3 block text-5xl"></i>
1611
1931
  <p class="text-muted-color m-0">
1612
- No actions available for this role.
1932
+ {{ 'iam.permission.no.actions.for.role' | translate }}
1613
1933
  </p>
1614
1934
  </div>
1615
1935
  }
1616
1936
  }
1617
1937
  </div>
1618
- `, isInline: true, styles: [":host{display:block}.validation-warning{background-color:var(--p-yellow-50, #fefce8);border-left:4px solid var(--p-yellow-500, #eab308);color:var(--p-yellow-700, #a16207)}:host-context(.p-dark) .validation-warning{background-color:#eab3081a;color:var(--p-yellow-400, #facc15)}:host ::ng-deep tr.highlight-warning{background-color:var(--p-red-50, rgba(239, 68, 68, .1))!important}:host-context(.p-dark) ::ng-deep tr.highlight-warning{background-color:#ef444426!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i7.TreeTable, selector: "p-treeTable, p-treetable, p-tree-table", inputs: ["columns", "styleClass", "tableStyle", "tableStyleClass", "autoLayout", "lazy", "lazyLoadOnInit", "paginator", "rows", "first", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "customSort", "selectionMode", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "compareSelectionBy", "rowHover", "loading", "loadingIcon", "showLoader", "scrollable", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "frozenColumns", "resizableColumns", "columnResizeMode", "reorderableColumns", "contextMenu", "rowTrackBy", "filters", "globalFilterFields", "filterDelay", "filterMode", "filterLocale", "paginatorLocale", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "value", "virtualRowHeight", "selectionKeys", "showGridlines"], outputs: ["selectionChange", "contextMenuSelectionChange", "onFilter", "onNodeExpand", "onNodeCollapse", "onPage", "onSort", "onLazyLoad", "sortFunction", "onColResize", "onColReorder", "onNodeSelect", "onNodeUnselect", "onContextMenuSelect", "onHeaderCheckboxToggle", "onEditInit", "onEditComplete", "onEditCancel", "selectionKeysChange"] }, { kind: "component", type: i7.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1938
+ `, isInline: true, styles: [":host{display:block}.validation-warning{background-color:var(--p-yellow-50, #fefce8);border-left:4px solid var(--p-yellow-500, #eab308);color:var(--p-yellow-700, #a16207)}:host-context(.p-dark) .validation-warning{background-color:#eab3081a;color:var(--p-yellow-400, #facc15)}:host ::ng-deep tr.highlight-warning{background-color:var(--p-red-50, rgba(239, 68, 68, .1))!important}:host-context(.p-dark) ::ng-deep tr.highlight-warning{background-color:#ef444426!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i7.TreeTable, selector: "p-treeTable, p-treetable, p-tree-table", inputs: ["columns", "styleClass", "tableStyle", "tableStyleClass", "autoLayout", "lazy", "lazyLoadOnInit", "paginator", "rows", "first", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "customSort", "selectionMode", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "compareSelectionBy", "rowHover", "loading", "loadingIcon", "showLoader", "scrollable", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "frozenColumns", "resizableColumns", "columnResizeMode", "reorderableColumns", "contextMenu", "rowTrackBy", "filters", "globalFilterFields", "filterDelay", "filterMode", "filterLocale", "paginatorLocale", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "value", "virtualRowHeight", "selectionKeys", "showGridlines"], outputs: ["selectionChange", "contextMenuSelectionChange", "onFilter", "onNodeExpand", "onNodeCollapse", "onPage", "onSort", "onLazyLoad", "sortFunction", "onColResize", "onColReorder", "onNodeSelect", "onNodeUnselect", "onContextMenuSelect", "onHeaderCheckboxToggle", "onEditInit", "onEditComplete", "onEditCancel", "selectionKeysChange"] }, { kind: "component", type: i7.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1619
1939
  }
1620
1940
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: RoleActionSelectorComponent, decorators: [{
1621
1941
  type: Component,
1622
- args: [{ selector: 'flusys-role-action-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective], template: `
1942
+ args: [{ selector: 'flusys-role-action-selector', imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, TranslatePipe], template: `
1623
1943
  <div class="role-action-selector">
1624
1944
  <!-- Role Selector -->
1625
1945
  <div class="mb-4">
1626
1946
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
1627
1947
  <div>
1628
- <label class="block font-semibold mb-2">Select Role</label>
1948
+ <label class="block font-semibold mb-2">{{ 'iam.permission.select.role' | translate }}</label>
1629
1949
  <p-select
1630
1950
  [ngModel]="selectedRoleId()"
1631
1951
  (ngModelChange)="selectedRoleId.set($event)"
1632
1952
  [options]="roles()"
1633
1953
  optionLabel="name"
1634
1954
  optionValue="id"
1635
- placeholder="Select a role"
1955
+ [placeholder]="'iam.permission.select.role.placeholder' | translate"
1636
1956
  [showClear]="true"
1637
1957
  [filter]="true"
1638
1958
  styleClass="w-full"
@@ -1658,14 +1978,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1658
1978
  class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4"
1659
1979
  >
1660
1980
  <div>
1661
- <h5 class="m-0 mb-1">Action Permissions</h5>
1981
+ <h5 class="m-0 mb-1">{{ 'iam.permission.action.permissions' | translate }}</h5>
1662
1982
  <p class="text-sm text-muted-color m-0">
1663
- {{ actions().length }} actions available
1983
+ {{ 'iam.permission.actions.available' | translate: { count: actions().length } }}
1664
1984
  </p>
1665
1985
  </div>
1666
1986
  <div class="flex flex-wrap gap-2">
1667
1987
  <p-button
1668
- label="Select All"
1988
+ [label]="'shared.select.all' | translate"
1669
1989
  icon="pi pi-check"
1670
1990
  [outlined]="true"
1671
1991
  size="small"
@@ -1673,7 +1993,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1673
1993
  (onClick)="selectAll()"
1674
1994
  />
1675
1995
  <p-button
1676
- label="Deselect All"
1996
+ [label]="'shared.deselect.all' | translate"
1677
1997
  icon="pi pi-times"
1678
1998
  [outlined]="true"
1679
1999
  size="small"
@@ -1682,7 +2002,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1682
2002
  />
1683
2003
  <p-button
1684
2004
  *hasPermission="ROLE_ACTION_PERMISSIONS.ASSIGN"
1685
- label="Save Changes"
2005
+ [label]="'shared.save.changes' | translate"
1686
2006
  icon="pi pi-save"
1687
2007
  [disabled]="!canSave()"
1688
2008
  [loading]="saving()"
@@ -1699,12 +2019,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1699
2019
  <div class="flex items-start gap-2">
1700
2020
  <i class="pi pi-exclamation-triangle text-xl"></i>
1701
2021
  <div class="flex-1">
1702
- <span class="font-semibold">Validation Warning:</span>
2022
+ <span class="font-semibold">{{ 'iam.validation.warning.title' | translate }}:</span>
1703
2023
  <p class="text-sm mt-1 mb-0">
1704
- {{ invalidActionsCount() }} selected
1705
- action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
1706
- unmet prerequisites. Fix before saving or use auto-fix on
1707
- save.
2024
+ {{ (invalidActionsCount() > 1 ? 'iam.validation.unmet.prerequisites.plural' : 'iam.validation.unmet.prerequisites.singular') | translate: { count: invalidActionsCount() } }}
1708
2025
  </p>
1709
2026
  </div>
1710
2027
  </div>
@@ -1726,14 +2043,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1726
2043
  [ngModel]="allSelected()"
1727
2044
  [binary]="true"
1728
2045
  (ngModelChange)="toggleAll()"
1729
- pTooltip="Select/Deselect All"
2046
+ [pTooltip]="'shared.select.deselect.all' | translate"
1730
2047
  tooltipPosition="top"
1731
2048
  />
1732
2049
  </th>
1733
- <th>Name</th>
1734
- <th class="hidden md:table-cell">Code</th>
1735
- <th>Type</th>
1736
- <th class="hidden lg:table-cell">Requirements</th>
2050
+ <th>{{ 'shared.name' | translate }}</th>
2051
+ <th class="hidden md:table-cell">{{ 'shared.code' | translate }}</th>
2052
+ <th>{{ 'iam.action.type' | translate }}</th>
2053
+ <th class="hidden lg:table-cell">{{ 'iam.permission.requirements' | translate }}</th>
1737
2054
  </tr>
1738
2055
  </ng-template>
1739
2056
  <ng-template #body let-rowNode let-rowData="rowData">
@@ -1754,7 +2071,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1754
2071
  @if (hasUnmetPrerequisites(rowData)) {
1755
2072
  <i
1756
2073
  class="pi pi-exclamation-triangle text-orange-500"
1757
- pTooltip="This action has unmet prerequisites and will fail validation on save"
2074
+ [pTooltip]="'iam.validation.unmet.prerequisites.tooltip' | translate"
1758
2075
  tooltipPosition="top"
1759
2076
  ></i>
1760
2077
  }
@@ -1771,7 +2088,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1771
2088
  </td>
1772
2089
  <td class="hidden lg:table-cell">
1773
2090
  @if (rowData.permissionLogic) {
1774
- <span class="text-sm text-muted-color">Has prerequisites</span>
2091
+ <span class="text-sm text-muted-color">{{ 'iam.permission.has.prerequisites' | translate }}</span>
1775
2092
  } @else {
1776
2093
  <span class="text-muted-color">-</span>
1777
2094
  }
@@ -1782,8 +2099,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1782
2099
  <tr>
1783
2100
  <td colspan="5" class="text-center p-4 text-muted-color">
1784
2101
  @if (loading()) {
1785
- <i class="pi pi-spin pi-spinner"></i> Loading actions...
1786
- } @else { No actions available. }
2102
+ <i class="pi pi-spin pi-spinner"></i> {{ 'shared.loading.actions' | translate }}
2103
+ } @else { {{ 'iam.permission.no.actions.available' | translate }} }
1787
2104
  </td>
1788
2105
  </tr>
1789
2106
  </ng-template>
@@ -1796,14 +2113,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1796
2113
  <div class="border border-surface rounded-border p-3 mt-4">
1797
2114
  <div class="flex items-center gap-2 mb-3">
1798
2115
  <i class="pi pi-info-circle text-primary"></i>
1799
- <span class="font-bold">Pending Changes</span>
2116
+ <span class="font-bold">{{ 'shared.pending.changes' | translate }}</span>
1800
2117
  </div>
1801
2118
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
1802
2119
  @if (pendingAdd().length > 0) {
1803
2120
  <div>
1804
2121
  <div class="flex items-center gap-2 mb-2">
1805
2122
  <i class="pi pi-plus-circle text-green-500"></i>
1806
- <strong class="text-sm">To Add ({{ pendingAdd().length }})</strong>
2123
+ <strong class="text-sm">{{ 'shared.to.add' | translate }} ({{ pendingAdd().length }})</strong>
1807
2124
  </div>
1808
2125
  <ul class="list-none p-0 m-0 pl-4">
1809
2126
  @for (action of pendingAdd(); track action.id) {
@@ -1816,7 +2133,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1816
2133
  <div>
1817
2134
  <div class="flex items-center gap-2 mb-2">
1818
2135
  <i class="pi pi-minus-circle text-red-500"></i>
1819
- <strong class="text-sm">To Remove ({{ pendingRemove().length }})</strong>
2136
+ <strong class="text-sm">{{ 'shared.to.remove' | translate }} ({{ pendingRemove().length }})</strong>
1820
2137
  </div>
1821
2138
  <ul class="list-none p-0 m-0 pl-4">
1822
2139
  @for (action of pendingRemove(); track action.id) {
@@ -1834,7 +2151,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1834
2151
  <div class="surface-card p-5 rounded-border shadow-sm text-center">
1835
2152
  <i class="pi pi-info-circle text-muted-color mb-3 block text-5xl"></i>
1836
2153
  <p class="text-muted-color m-0">
1837
- No actions available for this role.
2154
+ {{ 'iam.permission.no.actions.for.role' | translate }}
1838
2155
  </p>
1839
2156
  </div>
1840
2157
  }
@@ -1867,7 +2184,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1867
2184
  * - Pre-save validation with auto-fix
1868
2185
  *
1869
2186
  * **Performance:**
1870
- * - OnPush change detection
1871
2187
  * - Computed signals for reactive updates
1872
2188
  * - Memoized prerequisite validation
1873
2189
  * - AbortController for request cancellation
@@ -1888,6 +2204,10 @@ class CompanyActionSelectorComponent {
1888
2204
  confirmationService = inject(ConfirmationService);
1889
2205
  permissionLogic = inject(ActionPermissionLogicService);
1890
2206
  destroyRef = inject(DestroyRef);
2207
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
2208
+ translate(key, vars) {
2209
+ return this.translateAdapter?.translate(key, vars) ?? key;
2210
+ }
1891
2211
  // State - Company Selection
1892
2212
  selectedCompanyId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedCompanyId" }] : []));
1893
2213
  companies = signal([], ...(ngDevMode ? [{ debugName: "companies" }] : []));
@@ -2050,15 +2370,15 @@ class CompanyActionSelectorComponent {
2050
2370
  const prerequisiteInfo = this.permissionLogic.getPrerequisiteTooltip(action, selMap, this.actions());
2051
2371
  if (prerequisiteInfo) {
2052
2372
  const actionHint = isCurrentlySelected
2053
- ? '\n\n---\n[Remove] Click to remove'
2054
- : '\n\n---\n[Add] Click to add (auto-selects required)';
2373
+ ? `\n\n---\n[${this.translate('shared.remove')}] ${this.translate('iam.tooltip.remove.action')}`
2374
+ : `\n\n---\n[${this.translate('shared.add')}] ${this.translate('iam.tooltip.add.action')}`;
2055
2375
  return `${prerequisiteInfo}${actionHint}`;
2056
2376
  }
2057
2377
  }
2058
2378
  if (isCurrentlySelected) {
2059
- return '[Selected] Selected\n\n[Remove] Click to remove from company whitelist';
2379
+ return `[${this.translate('iam.tooltip.selected')}] ${this.translate('iam.tooltip.selected')}\n\n[${this.translate('shared.remove')}] ${this.translate('iam.tooltip.click.to.remove')}`;
2060
2380
  }
2061
- return '[Add] Click to add to company whitelist';
2381
+ return `[${this.translate('shared.add')}] ${this.translate('iam.tooltip.click.to.add')}`;
2062
2382
  }
2063
2383
  /**
2064
2384
  * Handle action checkbox toggle
@@ -2115,9 +2435,8 @@ class CompanyActionSelectorComponent {
2115
2435
  }
2116
2436
  this.messageService.add({
2117
2437
  severity: 'success',
2118
- summary: 'Success',
2119
- detail: response?.data?.message ||
2120
- 'Company action whitelist updated successfully',
2438
+ summary: this.translate('shared.success'),
2439
+ detail: response?.data?.message || this.translate('iam.permission.company.actions.updated'),
2121
2440
  });
2122
2441
  // Update baseline
2123
2442
  this._initialSelection.set({ ...this.selectionMap() });
@@ -2139,15 +2458,15 @@ class CompanyActionSelectorComponent {
2139
2458
  const requiredNames = error.requiredActions
2140
2459
  .map((a) => a.actionName)
2141
2460
  .join(', ');
2142
- return `• ${error.actionName} requires: ${requiredNames}`;
2461
+ return `${error.actionName} ${this.translate('iam.permission.requires')}: ${requiredNames}`;
2143
2462
  })
2144
2463
  .join('\n');
2145
2464
  this.confirmationService.confirm({
2146
- header: 'Prerequisite Validation Failed',
2147
- message: `The following actions cannot be added because they have missing prerequisites:\n\n${errorList}\n\nWould you like to automatically select all required actions?`,
2465
+ header: this.translate('iam.permission.prerequisite.validation.failed'),
2466
+ message: `${this.translate('iam.permission.prerequisite.error.message')}\n\n${errorList}\n\n${this.translate('iam.permission.auto.select.prompt')}`,
2148
2467
  icon: 'pi pi-exclamation-triangle',
2149
- acceptLabel: 'Auto-select Required Actions',
2150
- rejectLabel: 'Cancel',
2468
+ acceptLabel: this.translate('iam.permission.auto.select.required'),
2469
+ rejectLabel: this.translate('shared.cancel'),
2151
2470
  acceptIcon: 'pi pi-check',
2152
2471
  rejectIcon: 'pi pi-times',
2153
2472
  accept: () => {
@@ -2161,8 +2480,8 @@ class CompanyActionSelectorComponent {
2161
2480
  this._selectionMap.set(selMap);
2162
2481
  this.messageService.add({
2163
2482
  severity: 'info',
2164
- summary: 'Actions Selected',
2165
- detail: 'Automatically selected required prerequisite actions. Please save again.',
2483
+ summary: this.translate('iam.permission.actions.selected'),
2484
+ detail: this.translate('iam.permission.auto.selected.prerequisites'),
2166
2485
  });
2167
2486
  },
2168
2487
  reject: () => {
@@ -2170,8 +2489,8 @@ class CompanyActionSelectorComponent {
2170
2489
  this._selectionMap.set({ ...this.initialSelection() });
2171
2490
  this.messageService.add({
2172
2491
  severity: 'info',
2173
- summary: 'Changes Reverted',
2174
- detail: 'Selection reverted to saved state',
2492
+ summary: this.translate('iam.permission.changes.reverted'),
2493
+ detail: this.translate('iam.permission.selection.reverted'),
2175
2494
  });
2176
2495
  },
2177
2496
  });
@@ -2206,14 +2525,14 @@ class CompanyActionSelectorComponent {
2206
2525
  <div class="mb-4">
2207
2526
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
2208
2527
  <div>
2209
- <label class="block font-semibold mb-2">Select Company</label>
2528
+ <label class="block font-semibold mb-2">{{ 'iam.permission.select.company' | translate }}</label>
2210
2529
  <p-select
2211
2530
  [ngModel]="selectedCompanyId()"
2212
2531
  (ngModelChange)="selectedCompanyId.set($event)"
2213
2532
  [options]="companies()"
2214
2533
  optionLabel="name"
2215
2534
  optionValue="id"
2216
- placeholder="Select a company"
2535
+ [placeholder]="'iam.permission.select.company.placeholder' | translate"
2217
2536
  [showClear]="true"
2218
2537
  [filter]="true"
2219
2538
  styleClass="w-full"
@@ -2239,14 +2558,14 @@ class CompanyActionSelectorComponent {
2239
2558
  class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4"
2240
2559
  >
2241
2560
  <div>
2242
- <h5 class="m-0 mb-1">Action Whitelist</h5>
2561
+ <h5 class="m-0 mb-1">{{ 'iam.permission.action.whitelist' | translate }}</h5>
2243
2562
  <p class="text-sm text-muted-color m-0">
2244
- {{ actions().length }} actions available
2563
+ {{ 'iam.permission.actions.available' | translate: { count: actions().length } }}
2245
2564
  </p>
2246
2565
  </div>
2247
2566
  <div class="flex flex-wrap gap-2">
2248
2567
  <p-button
2249
- label="Select All"
2568
+ [label]="'shared.select.all' | translate"
2250
2569
  icon="pi pi-check"
2251
2570
  [outlined]="true"
2252
2571
  size="small"
@@ -2254,7 +2573,7 @@ class CompanyActionSelectorComponent {
2254
2573
  (onClick)="selectAll()"
2255
2574
  />
2256
2575
  <p-button
2257
- label="Deselect All"
2576
+ [label]="'shared.deselect.all' | translate"
2258
2577
  icon="pi pi-times"
2259
2578
  [outlined]="true"
2260
2579
  size="small"
@@ -2263,7 +2582,7 @@ class CompanyActionSelectorComponent {
2263
2582
  />
2264
2583
  <p-button
2265
2584
  *hasPermission="COMPANY_ACTION_PERMISSIONS.ASSIGN"
2266
- label="Save Changes"
2585
+ [label]="'shared.save.changes' | translate"
2267
2586
  icon="pi pi-save"
2268
2587
  [disabled]="!canSave()"
2269
2588
  [loading]="saving()"
@@ -2280,12 +2599,9 @@ class CompanyActionSelectorComponent {
2280
2599
  <div class="flex items-start gap-2">
2281
2600
  <i class="pi pi-exclamation-triangle text-xl"></i>
2282
2601
  <div class="flex-1">
2283
- <span class="font-semibold">Validation Warning:</span>
2602
+ <span class="font-semibold">{{ 'iam.validation.warning.title' | translate }}:</span>
2284
2603
  <p class="text-sm mt-1 mb-0">
2285
- {{ invalidActionsCount() }} selected
2286
- action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
2287
- unmet prerequisites. Fix before saving or use auto-fix on
2288
- save.
2604
+ {{ (invalidActionsCount() > 1 ? 'iam.validation.unmet.prerequisites.plural' : 'iam.validation.unmet.prerequisites.singular') | translate: { count: invalidActionsCount() } }}
2289
2605
  </p>
2290
2606
  </div>
2291
2607
  </div>
@@ -2307,14 +2623,14 @@ class CompanyActionSelectorComponent {
2307
2623
  [ngModel]="allSelected()"
2308
2624
  [binary]="true"
2309
2625
  (ngModelChange)="toggleAll()"
2310
- pTooltip="Select/Deselect All"
2626
+ [pTooltip]="'shared.select.deselect.all' | translate"
2311
2627
  tooltipPosition="top"
2312
2628
  />
2313
2629
  </th>
2314
- <th>Name</th>
2315
- <th class="hidden md:table-cell">Code</th>
2316
- <th>Type</th>
2317
- <th class="hidden lg:table-cell">Description</th>
2630
+ <th>{{ 'shared.name' | translate }}</th>
2631
+ <th class="hidden md:table-cell">{{ 'shared.code' | translate }}</th>
2632
+ <th>{{ 'iam.action.type' | translate }}</th>
2633
+ <th class="hidden lg:table-cell">{{ 'shared.description' | translate }}</th>
2318
2634
  </tr>
2319
2635
  </ng-template>
2320
2636
  <ng-template #body let-rowNode let-rowData="rowData">
@@ -2335,7 +2651,7 @@ class CompanyActionSelectorComponent {
2335
2651
  @if (hasUnmetPrerequisites(rowData)) {
2336
2652
  <i
2337
2653
  class="pi pi-exclamation-triangle text-orange-500"
2338
- pTooltip="This action has unmet prerequisites and will fail validation on save"
2654
+ [pTooltip]="'iam.validation.unmet.prerequisites.tooltip' | translate"
2339
2655
  tooltipPosition="top"
2340
2656
  ></i>
2341
2657
  }
@@ -2357,8 +2673,8 @@ class CompanyActionSelectorComponent {
2357
2673
  <tr>
2358
2674
  <td colspan="5" class="text-center p-4 text-muted-color">
2359
2675
  @if (loading()) {
2360
- <i class="pi pi-spin pi-spinner"></i> Loading actions...
2361
- } @else { No actions available. }
2676
+ <i class="pi pi-spin pi-spinner"></i> {{ 'shared.loading.actions' | translate }}
2677
+ } @else { {{ 'iam.permission.no.actions.available' | translate }} }
2362
2678
  </td>
2363
2679
  </tr>
2364
2680
  </ng-template>
@@ -2371,14 +2687,14 @@ class CompanyActionSelectorComponent {
2371
2687
  <div class="border border-surface rounded-border p-3 mt-4">
2372
2688
  <div class="flex items-center gap-2 mb-3">
2373
2689
  <i class="pi pi-info-circle text-primary"></i>
2374
- <span class="font-bold">Pending Changes</span>
2690
+ <span class="font-bold">{{ 'shared.pending.changes' | translate }}</span>
2375
2691
  </div>
2376
2692
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
2377
2693
  @if (pendingAdd().length > 0) {
2378
2694
  <div>
2379
2695
  <div class="flex items-center gap-2 mb-2">
2380
2696
  <i class="pi pi-plus-circle text-green-500"></i>
2381
- <strong class="text-sm">To Whitelist ({{ pendingAdd().length }})</strong>
2697
+ <strong class="text-sm">{{ 'shared.to.whitelist' | translate }} ({{ pendingAdd().length }})</strong>
2382
2698
  </div>
2383
2699
  <ul class="list-none p-0 m-0 pl-4">
2384
2700
  @for (action of pendingAdd(); track action.id) {
@@ -2391,7 +2707,7 @@ class CompanyActionSelectorComponent {
2391
2707
  <div>
2392
2708
  <div class="flex items-center gap-2 mb-2">
2393
2709
  <i class="pi pi-minus-circle text-red-500"></i>
2394
- <strong class="text-sm">To Remove ({{ pendingRemove().length }})</strong>
2710
+ <strong class="text-sm">{{ 'shared.to.remove' | translate }} ({{ pendingRemove().length }})</strong>
2395
2711
  </div>
2396
2712
  <ul class="list-none p-0 m-0 pl-4">
2397
2713
  @for (action of pendingRemove(); track action.id) {
@@ -2409,30 +2725,30 @@ class CompanyActionSelectorComponent {
2409
2725
  <div class="surface-card p-5 rounded-border shadow-sm text-center">
2410
2726
  <i class="pi pi-info-circle text-muted-color mb-3 block text-5xl"></i>
2411
2727
  <p class="text-muted-color m-0">
2412
- No actions available for this company.
2728
+ {{ 'iam.permission.no.actions.for.company' | translate }}
2413
2729
  </p>
2414
2730
  </div>
2415
2731
  }
2416
2732
  }
2417
2733
  </div>
2418
- `, isInline: true, styles: [":host{display:block}.validation-warning{background-color:var(--p-yellow-50, #fefce8);border-left:4px solid var(--p-yellow-500, #eab308);color:var(--p-yellow-700, #a16207)}:host-context(.p-dark) .validation-warning{background-color:#eab3081a;color:var(--p-yellow-400, #facc15)}:host ::ng-deep tr.highlight-warning{background-color:var(--p-red-50, rgba(239, 68, 68, .1))!important}:host-context(.p-dark) ::ng-deep tr.highlight-warning{background-color:#ef444426!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i7.TreeTable, selector: "p-treeTable, p-treetable, p-tree-table", inputs: ["columns", "styleClass", "tableStyle", "tableStyleClass", "autoLayout", "lazy", "lazyLoadOnInit", "paginator", "rows", "first", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "customSort", "selectionMode", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "compareSelectionBy", "rowHover", "loading", "loadingIcon", "showLoader", "scrollable", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "frozenColumns", "resizableColumns", "columnResizeMode", "reorderableColumns", "contextMenu", "rowTrackBy", "filters", "globalFilterFields", "filterDelay", "filterMode", "filterLocale", "paginatorLocale", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "value", "virtualRowHeight", "selectionKeys", "showGridlines"], outputs: ["selectionChange", "contextMenuSelectionChange", "onFilter", "onNodeExpand", "onNodeCollapse", "onPage", "onSort", "onLazyLoad", "sortFunction", "onColResize", "onColReorder", "onNodeSelect", "onNodeUnselect", "onContextMenuSelect", "onHeaderCheckboxToggle", "onEditInit", "onEditComplete", "onEditCancel", "selectionKeysChange"] }, { kind: "component", type: i7.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2734
+ `, isInline: true, styles: [":host{display:block}.validation-warning{background-color:var(--p-yellow-50, #fefce8);border-left:4px solid var(--p-yellow-500, #eab308);color:var(--p-yellow-700, #a16207)}:host-context(.p-dark) .validation-warning{background-color:#eab3081a;color:var(--p-yellow-400, #facc15)}:host ::ng-deep tr.highlight-warning{background-color:var(--p-red-50, rgba(239, 68, 68, .1))!important}:host-context(.p-dark) ::ng-deep tr.highlight-warning{background-color:#ef444426!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i7.TreeTable, selector: "p-treeTable, p-treetable, p-tree-table", inputs: ["columns", "styleClass", "tableStyle", "tableStyleClass", "autoLayout", "lazy", "lazyLoadOnInit", "paginator", "rows", "first", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "customSort", "selectionMode", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "compareSelectionBy", "rowHover", "loading", "loadingIcon", "showLoader", "scrollable", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "frozenColumns", "resizableColumns", "columnResizeMode", "reorderableColumns", "contextMenu", "rowTrackBy", "filters", "globalFilterFields", "filterDelay", "filterMode", "filterLocale", "paginatorLocale", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "value", "virtualRowHeight", "selectionKeys", "showGridlines"], outputs: ["selectionChange", "contextMenuSelectionChange", "onFilter", "onNodeExpand", "onNodeCollapse", "onPage", "onSort", "onLazyLoad", "sortFunction", "onColResize", "onColReorder", "onNodeSelect", "onNodeUnselect", "onContextMenuSelect", "onHeaderCheckboxToggle", "onEditInit", "onEditComplete", "onEditCancel", "selectionKeysChange"] }, { kind: "component", type: i7.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2419
2735
  }
2420
2736
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyActionSelectorComponent, decorators: [{
2421
2737
  type: Component,
2422
- args: [{ selector: 'flusys-company-action-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective], template: `
2738
+ args: [{ selector: 'flusys-company-action-selector', imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, TranslatePipe], template: `
2423
2739
  <div class="company-action-selector">
2424
2740
  <!-- Company Selector -->
2425
2741
  <div class="mb-4">
2426
2742
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
2427
2743
  <div>
2428
- <label class="block font-semibold mb-2">Select Company</label>
2744
+ <label class="block font-semibold mb-2">{{ 'iam.permission.select.company' | translate }}</label>
2429
2745
  <p-select
2430
2746
  [ngModel]="selectedCompanyId()"
2431
2747
  (ngModelChange)="selectedCompanyId.set($event)"
2432
2748
  [options]="companies()"
2433
2749
  optionLabel="name"
2434
2750
  optionValue="id"
2435
- placeholder="Select a company"
2751
+ [placeholder]="'iam.permission.select.company.placeholder' | translate"
2436
2752
  [showClear]="true"
2437
2753
  [filter]="true"
2438
2754
  styleClass="w-full"
@@ -2458,14 +2774,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2458
2774
  class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4"
2459
2775
  >
2460
2776
  <div>
2461
- <h5 class="m-0 mb-1">Action Whitelist</h5>
2777
+ <h5 class="m-0 mb-1">{{ 'iam.permission.action.whitelist' | translate }}</h5>
2462
2778
  <p class="text-sm text-muted-color m-0">
2463
- {{ actions().length }} actions available
2779
+ {{ 'iam.permission.actions.available' | translate: { count: actions().length } }}
2464
2780
  </p>
2465
2781
  </div>
2466
2782
  <div class="flex flex-wrap gap-2">
2467
2783
  <p-button
2468
- label="Select All"
2784
+ [label]="'shared.select.all' | translate"
2469
2785
  icon="pi pi-check"
2470
2786
  [outlined]="true"
2471
2787
  size="small"
@@ -2473,7 +2789,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2473
2789
  (onClick)="selectAll()"
2474
2790
  />
2475
2791
  <p-button
2476
- label="Deselect All"
2792
+ [label]="'shared.deselect.all' | translate"
2477
2793
  icon="pi pi-times"
2478
2794
  [outlined]="true"
2479
2795
  size="small"
@@ -2482,7 +2798,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2482
2798
  />
2483
2799
  <p-button
2484
2800
  *hasPermission="COMPANY_ACTION_PERMISSIONS.ASSIGN"
2485
- label="Save Changes"
2801
+ [label]="'shared.save.changes' | translate"
2486
2802
  icon="pi pi-save"
2487
2803
  [disabled]="!canSave()"
2488
2804
  [loading]="saving()"
@@ -2499,12 +2815,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2499
2815
  <div class="flex items-start gap-2">
2500
2816
  <i class="pi pi-exclamation-triangle text-xl"></i>
2501
2817
  <div class="flex-1">
2502
- <span class="font-semibold">Validation Warning:</span>
2818
+ <span class="font-semibold">{{ 'iam.validation.warning.title' | translate }}:</span>
2503
2819
  <p class="text-sm mt-1 mb-0">
2504
- {{ invalidActionsCount() }} selected
2505
- action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
2506
- unmet prerequisites. Fix before saving or use auto-fix on
2507
- save.
2820
+ {{ (invalidActionsCount() > 1 ? 'iam.validation.unmet.prerequisites.plural' : 'iam.validation.unmet.prerequisites.singular') | translate: { count: invalidActionsCount() } }}
2508
2821
  </p>
2509
2822
  </div>
2510
2823
  </div>
@@ -2526,14 +2839,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2526
2839
  [ngModel]="allSelected()"
2527
2840
  [binary]="true"
2528
2841
  (ngModelChange)="toggleAll()"
2529
- pTooltip="Select/Deselect All"
2842
+ [pTooltip]="'shared.select.deselect.all' | translate"
2530
2843
  tooltipPosition="top"
2531
2844
  />
2532
2845
  </th>
2533
- <th>Name</th>
2534
- <th class="hidden md:table-cell">Code</th>
2535
- <th>Type</th>
2536
- <th class="hidden lg:table-cell">Description</th>
2846
+ <th>{{ 'shared.name' | translate }}</th>
2847
+ <th class="hidden md:table-cell">{{ 'shared.code' | translate }}</th>
2848
+ <th>{{ 'iam.action.type' | translate }}</th>
2849
+ <th class="hidden lg:table-cell">{{ 'shared.description' | translate }}</th>
2537
2850
  </tr>
2538
2851
  </ng-template>
2539
2852
  <ng-template #body let-rowNode let-rowData="rowData">
@@ -2554,7 +2867,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2554
2867
  @if (hasUnmetPrerequisites(rowData)) {
2555
2868
  <i
2556
2869
  class="pi pi-exclamation-triangle text-orange-500"
2557
- pTooltip="This action has unmet prerequisites and will fail validation on save"
2870
+ [pTooltip]="'iam.validation.unmet.prerequisites.tooltip' | translate"
2558
2871
  tooltipPosition="top"
2559
2872
  ></i>
2560
2873
  }
@@ -2576,8 +2889,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2576
2889
  <tr>
2577
2890
  <td colspan="5" class="text-center p-4 text-muted-color">
2578
2891
  @if (loading()) {
2579
- <i class="pi pi-spin pi-spinner"></i> Loading actions...
2580
- } @else { No actions available. }
2892
+ <i class="pi pi-spin pi-spinner"></i> {{ 'shared.loading.actions' | translate }}
2893
+ } @else { {{ 'iam.permission.no.actions.available' | translate }} }
2581
2894
  </td>
2582
2895
  </tr>
2583
2896
  </ng-template>
@@ -2590,14 +2903,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2590
2903
  <div class="border border-surface rounded-border p-3 mt-4">
2591
2904
  <div class="flex items-center gap-2 mb-3">
2592
2905
  <i class="pi pi-info-circle text-primary"></i>
2593
- <span class="font-bold">Pending Changes</span>
2906
+ <span class="font-bold">{{ 'shared.pending.changes' | translate }}</span>
2594
2907
  </div>
2595
2908
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
2596
2909
  @if (pendingAdd().length > 0) {
2597
2910
  <div>
2598
2911
  <div class="flex items-center gap-2 mb-2">
2599
2912
  <i class="pi pi-plus-circle text-green-500"></i>
2600
- <strong class="text-sm">To Whitelist ({{ pendingAdd().length }})</strong>
2913
+ <strong class="text-sm">{{ 'shared.to.whitelist' | translate }} ({{ pendingAdd().length }})</strong>
2601
2914
  </div>
2602
2915
  <ul class="list-none p-0 m-0 pl-4">
2603
2916
  @for (action of pendingAdd(); track action.id) {
@@ -2610,7 +2923,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2610
2923
  <div>
2611
2924
  <div class="flex items-center gap-2 mb-2">
2612
2925
  <i class="pi pi-minus-circle text-red-500"></i>
2613
- <strong class="text-sm">To Remove ({{ pendingRemove().length }})</strong>
2926
+ <strong class="text-sm">{{ 'shared.to.remove' | translate }} ({{ pendingRemove().length }})</strong>
2614
2927
  </div>
2615
2928
  <ul class="list-none p-0 m-0 pl-4">
2616
2929
  @for (action of pendingRemove(); track action.id) {
@@ -2628,7 +2941,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2628
2941
  <div class="surface-card p-5 rounded-border shadow-sm text-center">
2629
2942
  <i class="pi pi-info-circle text-muted-color mb-3 block text-5xl"></i>
2630
2943
  <p class="text-muted-color m-0">
2631
- No actions available for this company.
2944
+ {{ 'iam.permission.no.actions.for.company' | translate }}
2632
2945
  </p>
2633
2946
  </div>
2634
2947
  }
@@ -2658,7 +2971,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2658
2971
  * - Branch-specific: branchId = selected value
2659
2972
  *
2660
2973
  * **Performance:**
2661
- * - OnPush change detection
2662
2974
  * - Computed signals for reactive updates
2663
2975
  * - Branch filtering on company context change
2664
2976
  *
@@ -2677,6 +2989,10 @@ class UserRoleSelectorComponent {
2677
2989
  roleApi = inject(RoleApiService);
2678
2990
  permissionApi = inject(PermissionApiService);
2679
2991
  messageService = inject(MessageService);
2992
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
2993
+ translate(key, vars) {
2994
+ return this.translateAdapter?.translate(key, vars) ?? key;
2995
+ }
2680
2996
  // State - User/Branch Selection
2681
2997
  selectedUserId = signal(null, ...(ngDevMode ? [{ debugName: "selectedUserId" }] : []));
2682
2998
  selectedBranchId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedBranchId" }] : []));
@@ -2829,9 +3145,9 @@ class UserRoleSelectorComponent {
2829
3145
  const selMap = this.selectionMap();
2830
3146
  const isCurrentlySelected = selMap[role.id];
2831
3147
  if (isCurrentlySelected) {
2832
- return '[Assigned] Assigned to user\n\n[Remove] Click to remove role';
3148
+ return `[${this.translate('shared.assigned')}] ${this.translate('iam.tooltip.assigned.to.user')}\n\n[${this.translate('shared.remove')}] ${this.translate('iam.tooltip.click.to.remove.role.from.user')}`;
2833
3149
  }
2834
- return '[Add] Click to assign role to user';
3150
+ return `[${this.translate('shared.add')}] ${this.translate('iam.tooltip.click.to.assign.role.to.user')}`;
2835
3151
  }
2836
3152
  /**
2837
3153
  * Handle role checkbox toggle
@@ -2882,8 +3198,8 @@ class UserRoleSelectorComponent {
2882
3198
  if (isCompanyFeatureEnabled(this.appConfig) && !companyId) {
2883
3199
  this.messageService.add({
2884
3200
  severity: 'warn',
2885
- summary: 'Company Required',
2886
- detail: 'Please select a company',
3201
+ summary: this.translate('shared.warning'),
3202
+ detail: this.translate('iam.permission.company.required'),
2887
3203
  });
2888
3204
  return;
2889
3205
  }
@@ -2911,9 +3227,8 @@ class UserRoleSelectorComponent {
2911
3227
  const response = await firstValueFrom(this.permissionApi.assignUserRoles(payload));
2912
3228
  this.messageService.add({
2913
3229
  severity: 'success',
2914
- summary: 'Success',
2915
- detail: response?.data?.message ||
2916
- 'User role assignments updated successfully',
3230
+ summary: this.translate('shared.success'),
3231
+ detail: response?.data?.message || this.translate('iam.permission.user.roles.updated'),
2917
3232
  });
2918
3233
  // Update baseline
2919
3234
  this._initialSelection.set({ ...this.selectionMap() });
@@ -2942,13 +3257,13 @@ class UserRoleSelectorComponent {
2942
3257
  <div>
2943
3258
  <label class="block font-semibold mb-2">
2944
3259
  <i class="pi pi-user mr-2 text-primary"></i>
2945
- Select User
3260
+ {{ 'iam.permission.select.user' | translate }}
2946
3261
  </label>
2947
3262
  <lib-user-select
2948
3263
  [value]="selectedUserId()"
2949
3264
  (valueChange)="selectedUserId.set($event)"
2950
3265
  [isEditMode]="true"
2951
- placeHolder="Select a user"
3266
+ [placeHolder]="'iam.permission.select.user.placeholder' | translate"
2952
3267
  />
2953
3268
  </div>
2954
3269
 
@@ -2956,7 +3271,7 @@ class UserRoleSelectorComponent {
2956
3271
  <div>
2957
3272
  <label class="block font-semibold mb-2">
2958
3273
  <i class="pi pi-building mr-2 text-primary"></i>
2959
- Select Branch
3274
+ {{ 'iam.permission.select.branch' | translate }}
2960
3275
  </label>
2961
3276
  <p-select
2962
3277
  [ngModel]="selectedBranchId()"
@@ -2964,7 +3279,7 @@ class UserRoleSelectorComponent {
2964
3279
  [options]="filteredBranches()"
2965
3280
  optionLabel="name"
2966
3281
  optionValue="id"
2967
- placeholder="Select a branch"
3282
+ [placeholder]="'iam.permission.select.branch.placeholder' | translate"
2968
3283
  [showClear]="true"
2969
3284
  [filter]="true"
2970
3285
  styleClass="w-full"
@@ -2985,10 +3300,7 @@ class UserRoleSelectorComponent {
2985
3300
  </ng-template>
2986
3301
  </p-select>
2987
3302
  <small class="text-muted-color block mt-1">
2988
- {{ filteredBranches().length }} permitted branch{{
2989
- filteredBranches().length !== 1 ? 'es' : ''
2990
- }}
2991
- in current company
3303
+ {{ (filteredBranches().length !== 1 ? 'iam.branch.permitted.count.plural' : 'iam.branch.permitted.count') | translate: { count: filteredBranches().length } }}
2992
3304
  </small>
2993
3305
  </div>
2994
3306
  }
@@ -3012,14 +3324,14 @@ class UserRoleSelectorComponent {
3012
3324
  class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4"
3013
3325
  >
3014
3326
  <div>
3015
- <h5 class="m-0 mb-1">Role Assignments</h5>
3327
+ <h5 class="m-0 mb-1">{{ 'iam.permission.role.assignments' | translate }}</h5>
3016
3328
  <p class="text-sm text-muted-color m-0">
3017
- {{ roles().length }} roles available
3329
+ {{ 'iam.permission.roles.available' | translate: { count: roles().length } }}
3018
3330
  </p>
3019
3331
  </div>
3020
3332
  <div class="flex flex-wrap gap-2">
3021
3333
  <p-button
3022
- label="Select All"
3334
+ [label]="'shared.select.all' | translate"
3023
3335
  icon="pi pi-check"
3024
3336
  [outlined]="true"
3025
3337
  size="small"
@@ -3027,7 +3339,7 @@ class UserRoleSelectorComponent {
3027
3339
  (onClick)="selectAll()"
3028
3340
  />
3029
3341
  <p-button
3030
- label="Deselect All"
3342
+ [label]="'shared.deselect.all' | translate"
3031
3343
  icon="pi pi-times"
3032
3344
  [outlined]="true"
3033
3345
  size="small"
@@ -3036,7 +3348,7 @@ class UserRoleSelectorComponent {
3036
3348
  />
3037
3349
  <p-button
3038
3350
  *hasPermission="USER_ROLE_PERMISSIONS.ASSIGN"
3039
- label="Save Changes"
3351
+ [label]="'shared.save.changes' | translate"
3040
3352
  icon="pi pi-save"
3041
3353
  [disabled]="!canSave()"
3042
3354
  [loading]="saving()"
@@ -3056,7 +3368,7 @@ class UserRoleSelectorComponent {
3056
3368
  [rowsPerPageOptions]="[10, 20, 50]"
3057
3369
  [globalFilterFields]="['name', 'code', 'description']"
3058
3370
  [showCurrentPageReport]="true"
3059
- currentPageReportTemplate="Showing {first} to {last} of {totalRecords} roles"
3371
+ [currentPageReportTemplate]="'iam.pagination.roles.template' | translate"
3060
3372
  styleClass="p-datatable-sm"
3061
3373
  [tableStyle]="{ 'min-width': '35rem' }"
3062
3374
  >
@@ -3067,13 +3379,13 @@ class UserRoleSelectorComponent {
3067
3379
  [ngModel]="allSelected()"
3068
3380
  [binary]="true"
3069
3381
  (ngModelChange)="toggleAll()"
3070
- pTooltip="Select/Deselect All"
3382
+ [pTooltip]="'shared.select.deselect.all' | translate"
3071
3383
  tooltipPosition="top"
3072
3384
  />
3073
3385
  </th>
3074
- <th>Name</th>
3075
- <th class="hidden sm:table-cell">Code</th>
3076
- <th class="hidden md:table-cell">Description</th>
3386
+ <th>{{ 'shared.name' | translate }}</th>
3387
+ <th class="hidden sm:table-cell">{{ 'shared.code' | translate }}</th>
3388
+ <th class="hidden md:table-cell">{{ 'shared.description' | translate }}</th>
3077
3389
  </tr>
3078
3390
  </ng-template>
3079
3391
  <ng-template #body let-role>
@@ -3096,9 +3408,9 @@ class UserRoleSelectorComponent {
3096
3408
  <tr>
3097
3409
  <td colspan="4" class="text-center p-4 text-muted-color">
3098
3410
  @if (loading()) {
3099
- <i class="pi pi-spin pi-spinner"></i> Loading roles...
3411
+ <i class="pi pi-spin pi-spinner"></i> {{ 'shared.loading.roles' | translate }}
3100
3412
  } @else {
3101
- No roles available.
3413
+ {{ 'iam.permission.no.roles.available' | translate }}
3102
3414
  }
3103
3415
  </td>
3104
3416
  </tr>
@@ -3112,14 +3424,14 @@ class UserRoleSelectorComponent {
3112
3424
  <div class="border border-surface rounded-border p-3 mt-4">
3113
3425
  <div class="flex items-center gap-2 mb-3">
3114
3426
  <i class="pi pi-info-circle text-primary"></i>
3115
- <span class="font-bold">Pending Changes</span>
3427
+ <span class="font-bold">{{ 'shared.pending.changes' | translate }}</span>
3116
3428
  </div>
3117
3429
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
3118
3430
  @if (pendingAdd().length > 0) {
3119
3431
  <div>
3120
3432
  <div class="flex items-center gap-2 mb-2">
3121
3433
  <i class="pi pi-plus-circle text-green-500"></i>
3122
- <strong class="text-sm">To Assign ({{ pendingAdd().length }})</strong>
3434
+ <strong class="text-sm">{{ 'shared.to.assign' | translate }} ({{ pendingAdd().length }})</strong>
3123
3435
  </div>
3124
3436
  <ul class="list-none p-0 m-0 pl-4">
3125
3437
  @for (role of pendingAdd(); track role.id) {
@@ -3132,7 +3444,7 @@ class UserRoleSelectorComponent {
3132
3444
  <div>
3133
3445
  <div class="flex items-center gap-2 mb-2">
3134
3446
  <i class="pi pi-minus-circle text-red-500"></i>
3135
- <strong class="text-sm">To Remove ({{ pendingRemove().length }})</strong>
3447
+ <strong class="text-sm">{{ 'shared.to.remove' | translate }} ({{ pendingRemove().length }})</strong>
3136
3448
  </div>
3137
3449
  <ul class="list-none p-0 m-0 pl-4">
3138
3450
  @for (role of pendingRemove(); track role.id) {
@@ -3150,17 +3462,17 @@ class UserRoleSelectorComponent {
3150
3462
  <div class="surface-card p-5 rounded-border shadow-sm text-center">
3151
3463
  <i class="pi pi-info-circle text-muted-color mb-3 block text-5xl"></i>
3152
3464
  <p class="text-muted-color m-0">
3153
- No roles available for this user.
3465
+ {{ 'iam.permission.no.roles.for.user' | translate }}
3154
3466
  </p>
3155
3467
  </div>
3156
3468
  }
3157
3469
  }
3158
3470
  </div>
3159
- `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "component", type: UserSelectComponent, selector: "lib-user-select", inputs: ["value"], outputs: ["valueChange", "userSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3471
+ `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "component", type: UserSelectComponent, selector: "lib-user-select", inputs: ["value"], outputs: ["valueChange", "userSelected"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
3160
3472
  }
3161
3473
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserRoleSelectorComponent, decorators: [{
3162
3474
  type: Component,
3163
- args: [{ selector: 'flusys-user-role-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, UserSelectComponent], template: `
3475
+ args: [{ selector: 'flusys-user-role-selector', imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, UserSelectComponent, TranslatePipe], template: `
3164
3476
  <div class="user-role-selector">
3165
3477
  <!-- User and Branch Selectors -->
3166
3478
  <div class="surface-card p-4 rounded-border mb-4 shadow-sm">
@@ -3168,13 +3480,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3168
3480
  <div>
3169
3481
  <label class="block font-semibold mb-2">
3170
3482
  <i class="pi pi-user mr-2 text-primary"></i>
3171
- Select User
3483
+ {{ 'iam.permission.select.user' | translate }}
3172
3484
  </label>
3173
3485
  <lib-user-select
3174
3486
  [value]="selectedUserId()"
3175
3487
  (valueChange)="selectedUserId.set($event)"
3176
3488
  [isEditMode]="true"
3177
- placeHolder="Select a user"
3489
+ [placeHolder]="'iam.permission.select.user.placeholder' | translate"
3178
3490
  />
3179
3491
  </div>
3180
3492
 
@@ -3182,7 +3494,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3182
3494
  <div>
3183
3495
  <label class="block font-semibold mb-2">
3184
3496
  <i class="pi pi-building mr-2 text-primary"></i>
3185
- Select Branch
3497
+ {{ 'iam.permission.select.branch' | translate }}
3186
3498
  </label>
3187
3499
  <p-select
3188
3500
  [ngModel]="selectedBranchId()"
@@ -3190,7 +3502,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3190
3502
  [options]="filteredBranches()"
3191
3503
  optionLabel="name"
3192
3504
  optionValue="id"
3193
- placeholder="Select a branch"
3505
+ [placeholder]="'iam.permission.select.branch.placeholder' | translate"
3194
3506
  [showClear]="true"
3195
3507
  [filter]="true"
3196
3508
  styleClass="w-full"
@@ -3211,10 +3523,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3211
3523
  </ng-template>
3212
3524
  </p-select>
3213
3525
  <small class="text-muted-color block mt-1">
3214
- {{ filteredBranches().length }} permitted branch{{
3215
- filteredBranches().length !== 1 ? 'es' : ''
3216
- }}
3217
- in current company
3526
+ {{ (filteredBranches().length !== 1 ? 'iam.branch.permitted.count.plural' : 'iam.branch.permitted.count') | translate: { count: filteredBranches().length } }}
3218
3527
  </small>
3219
3528
  </div>
3220
3529
  }
@@ -3238,14 +3547,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3238
3547
  class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4"
3239
3548
  >
3240
3549
  <div>
3241
- <h5 class="m-0 mb-1">Role Assignments</h5>
3550
+ <h5 class="m-0 mb-1">{{ 'iam.permission.role.assignments' | translate }}</h5>
3242
3551
  <p class="text-sm text-muted-color m-0">
3243
- {{ roles().length }} roles available
3552
+ {{ 'iam.permission.roles.available' | translate: { count: roles().length } }}
3244
3553
  </p>
3245
3554
  </div>
3246
3555
  <div class="flex flex-wrap gap-2">
3247
3556
  <p-button
3248
- label="Select All"
3557
+ [label]="'shared.select.all' | translate"
3249
3558
  icon="pi pi-check"
3250
3559
  [outlined]="true"
3251
3560
  size="small"
@@ -3253,7 +3562,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3253
3562
  (onClick)="selectAll()"
3254
3563
  />
3255
3564
  <p-button
3256
- label="Deselect All"
3565
+ [label]="'shared.deselect.all' | translate"
3257
3566
  icon="pi pi-times"
3258
3567
  [outlined]="true"
3259
3568
  size="small"
@@ -3262,7 +3571,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3262
3571
  />
3263
3572
  <p-button
3264
3573
  *hasPermission="USER_ROLE_PERMISSIONS.ASSIGN"
3265
- label="Save Changes"
3574
+ [label]="'shared.save.changes' | translate"
3266
3575
  icon="pi pi-save"
3267
3576
  [disabled]="!canSave()"
3268
3577
  [loading]="saving()"
@@ -3282,7 +3591,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3282
3591
  [rowsPerPageOptions]="[10, 20, 50]"
3283
3592
  [globalFilterFields]="['name', 'code', 'description']"
3284
3593
  [showCurrentPageReport]="true"
3285
- currentPageReportTemplate="Showing {first} to {last} of {totalRecords} roles"
3594
+ [currentPageReportTemplate]="'iam.pagination.roles.template' | translate"
3286
3595
  styleClass="p-datatable-sm"
3287
3596
  [tableStyle]="{ 'min-width': '35rem' }"
3288
3597
  >
@@ -3293,13 +3602,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3293
3602
  [ngModel]="allSelected()"
3294
3603
  [binary]="true"
3295
3604
  (ngModelChange)="toggleAll()"
3296
- pTooltip="Select/Deselect All"
3605
+ [pTooltip]="'shared.select.deselect.all' | translate"
3297
3606
  tooltipPosition="top"
3298
3607
  />
3299
3608
  </th>
3300
- <th>Name</th>
3301
- <th class="hidden sm:table-cell">Code</th>
3302
- <th class="hidden md:table-cell">Description</th>
3609
+ <th>{{ 'shared.name' | translate }}</th>
3610
+ <th class="hidden sm:table-cell">{{ 'shared.code' | translate }}</th>
3611
+ <th class="hidden md:table-cell">{{ 'shared.description' | translate }}</th>
3303
3612
  </tr>
3304
3613
  </ng-template>
3305
3614
  <ng-template #body let-role>
@@ -3322,9 +3631,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3322
3631
  <tr>
3323
3632
  <td colspan="4" class="text-center p-4 text-muted-color">
3324
3633
  @if (loading()) {
3325
- <i class="pi pi-spin pi-spinner"></i> Loading roles...
3634
+ <i class="pi pi-spin pi-spinner"></i> {{ 'shared.loading.roles' | translate }}
3326
3635
  } @else {
3327
- No roles available.
3636
+ {{ 'iam.permission.no.roles.available' | translate }}
3328
3637
  }
3329
3638
  </td>
3330
3639
  </tr>
@@ -3338,14 +3647,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3338
3647
  <div class="border border-surface rounded-border p-3 mt-4">
3339
3648
  <div class="flex items-center gap-2 mb-3">
3340
3649
  <i class="pi pi-info-circle text-primary"></i>
3341
- <span class="font-bold">Pending Changes</span>
3650
+ <span class="font-bold">{{ 'shared.pending.changes' | translate }}</span>
3342
3651
  </div>
3343
3652
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
3344
3653
  @if (pendingAdd().length > 0) {
3345
3654
  <div>
3346
3655
  <div class="flex items-center gap-2 mb-2">
3347
3656
  <i class="pi pi-plus-circle text-green-500"></i>
3348
- <strong class="text-sm">To Assign ({{ pendingAdd().length }})</strong>
3657
+ <strong class="text-sm">{{ 'shared.to.assign' | translate }} ({{ pendingAdd().length }})</strong>
3349
3658
  </div>
3350
3659
  <ul class="list-none p-0 m-0 pl-4">
3351
3660
  @for (role of pendingAdd(); track role.id) {
@@ -3358,7 +3667,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3358
3667
  <div>
3359
3668
  <div class="flex items-center gap-2 mb-2">
3360
3669
  <i class="pi pi-minus-circle text-red-500"></i>
3361
- <strong class="text-sm">To Remove ({{ pendingRemove().length }})</strong>
3670
+ <strong class="text-sm">{{ 'shared.to.remove' | translate }} ({{ pendingRemove().length }})</strong>
3362
3671
  </div>
3363
3672
  <ul class="list-none p-0 m-0 pl-4">
3364
3673
  @for (role of pendingRemove(); track role.id) {
@@ -3376,7 +3685,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3376
3685
  <div class="surface-card p-5 rounded-border shadow-sm text-center">
3377
3686
  <i class="pi pi-info-circle text-muted-color mb-3 block text-5xl"></i>
3378
3687
  <p class="text-muted-color m-0">
3379
- No roles available for this user.
3688
+ {{ 'iam.permission.no.roles.for.user' | translate }}
3380
3689
  </p>
3381
3690
  </div>
3382
3691
  }
@@ -3407,7 +3716,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3407
3716
  * - Branch-specific: branchId = selected value
3408
3717
  *
3409
3718
  * **Performance:**
3410
- * - OnPush change detection
3411
3719
  * - Computed signals for reactive updates
3412
3720
  * - Branch filtering on company context change
3413
3721
  *
@@ -3427,6 +3735,10 @@ class UserActionSelectorComponent {
3427
3735
  permissionApi = inject(PermissionApiService);
3428
3736
  permissionLogic = inject(ActionPermissionLogicService);
3429
3737
  messageService = inject(MessageService);
3738
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
3739
+ translate(key, vars) {
3740
+ return this.translateAdapter?.translate(key, vars) ?? key;
3741
+ }
3430
3742
  // State - User/Branch Selection
3431
3743
  selectedUserId = signal(null, ...(ngDevMode ? [{ debugName: "selectedUserId" }] : []));
3432
3744
  selectedBranchId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedBranchId" }] : []));
@@ -3595,15 +3907,15 @@ class UserActionSelectorComponent {
3595
3907
  const prerequisiteInfo = this.permissionLogic.getPrerequisiteTooltip(action, selMap, this.actions());
3596
3908
  if (prerequisiteInfo) {
3597
3909
  const actionHint = isCurrentlySelected
3598
- ? '\n\n---\n[Remove] Click to remove'
3599
- : '\n\n---\n[Add] Click to add (auto-selects required)';
3910
+ ? `\n\n---\n[${this.translate('shared.remove')}] ${this.translate('iam.tooltip.remove.action')}`
3911
+ : `\n\n---\n[${this.translate('shared.add')}] ${this.translate('iam.tooltip.add.action')}`;
3600
3912
  return `${prerequisiteInfo}${actionHint}`;
3601
3913
  }
3602
3914
  }
3603
3915
  if (isCurrentlySelected) {
3604
- return '[Assigned] Assigned to user\n\n[Remove] Click to remove direct permission';
3916
+ return `[${this.translate('shared.assigned')}] ${this.translate('iam.tooltip.assigned.to.user')}\n\n[${this.translate('shared.remove')}] ${this.translate('iam.tooltip.click.to.remove.user')}`;
3605
3917
  }
3606
- return '[Add] Click to assign direct permission';
3918
+ return `[${this.translate('shared.add')}] ${this.translate('iam.tooltip.click.to.assign.user')}`;
3607
3919
  }
3608
3920
  /**
3609
3921
  * Check if action has unmet prerequisites
@@ -3647,8 +3959,8 @@ class UserActionSelectorComponent {
3647
3959
  if (this.isCompanyFeatureActive() && !companyId) {
3648
3960
  this.messageService.add({
3649
3961
  severity: 'warn',
3650
- summary: 'Company Required',
3651
- detail: 'Please select a company',
3962
+ summary: this.translate('shared.warning'),
3963
+ detail: this.translate('iam.permission.company.required'),
3652
3964
  });
3653
3965
  return;
3654
3966
  }
@@ -3672,9 +3984,8 @@ class UserActionSelectorComponent {
3672
3984
  const response = await firstValueFrom(this.permissionApi.assignUserActions(payload));
3673
3985
  this.messageService.add({
3674
3986
  severity: 'success',
3675
- summary: 'Success',
3676
- detail: response?.data?.message ||
3677
- 'User action permissions updated successfully',
3987
+ summary: this.translate('shared.success'),
3988
+ detail: response?.data?.message || this.translate('iam.permission.user.actions.updated'),
3678
3989
  });
3679
3990
  this._initialSelection.set({ ...this.selectionMap() });
3680
3991
  }
@@ -3717,13 +4028,13 @@ class UserActionSelectorComponent {
3717
4028
  <div>
3718
4029
  <label class="block font-semibold mb-2">
3719
4030
  <i class="pi pi-user mr-2 text-primary"></i>
3720
- Select User
4031
+ {{ 'iam.permission.select.user' | translate }}
3721
4032
  </label>
3722
4033
  <lib-user-select
3723
4034
  [value]="selectedUserId()"
3724
4035
  (valueChange)="selectedUserId.set($event)"
3725
4036
  [isEditMode]="true"
3726
- placeHolder="Select a user"
4037
+ [placeHolder]="'iam.permission.select.user.placeholder' | translate"
3727
4038
  />
3728
4039
  </div>
3729
4040
 
@@ -3731,7 +4042,7 @@ class UserActionSelectorComponent {
3731
4042
  <div>
3732
4043
  <label class="block font-semibold mb-2">
3733
4044
  <i class="pi pi-building mr-2 text-primary"></i>
3734
- Select Branch
4045
+ {{ 'iam.permission.select.branch' | translate }}
3735
4046
  </label>
3736
4047
  <p-select
3737
4048
  [ngModel]="selectedBranchId()"
@@ -3739,7 +4050,7 @@ class UserActionSelectorComponent {
3739
4050
  [options]="filteredBranches()"
3740
4051
  optionLabel="name"
3741
4052
  optionValue="id"
3742
- placeholder="Select a branch"
4053
+ [placeholder]="'iam.permission.select.branch.placeholder' | translate"
3743
4054
  [showClear]="true"
3744
4055
  [filter]="true"
3745
4056
  styleClass="w-full"
@@ -3760,10 +4071,7 @@ class UserActionSelectorComponent {
3760
4071
  </ng-template>
3761
4072
  </p-select>
3762
4073
  <small class="text-muted-color block mt-1">
3763
- {{ filteredBranches().length }} permitted branch{{
3764
- filteredBranches().length !== 1 ? 'es' : ''
3765
- }}
3766
- in current company
4074
+ {{ (filteredBranches().length !== 1 ? 'iam.branch.permitted.count.plural' : 'iam.branch.permitted.count') | translate: { count: filteredBranches().length } }}
3767
4075
  </small>
3768
4076
  </div>
3769
4077
  }
@@ -3785,14 +4093,14 @@ class UserActionSelectorComponent {
3785
4093
  class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4"
3786
4094
  >
3787
4095
  <div>
3788
- <h5 class="m-0 mb-1">Direct Action Permissions</h5>
4096
+ <h5 class="m-0 mb-1">{{ 'iam.permission.direct.action.permissions' | translate }}</h5>
3789
4097
  <p class="text-sm text-muted-color m-0">
3790
- {{ actions().length }} actions available
4098
+ {{ 'iam.permission.actions.available' | translate: { count: actions().length } }}
3791
4099
  </p>
3792
4100
  </div>
3793
4101
  <div class="flex flex-wrap gap-2">
3794
4102
  <p-button
3795
- label="Select All"
4103
+ [label]="'shared.select.all' | translate"
3796
4104
  icon="pi pi-check"
3797
4105
  [outlined]="true"
3798
4106
  size="small"
@@ -3800,7 +4108,7 @@ class UserActionSelectorComponent {
3800
4108
  (onClick)="selectAll()"
3801
4109
  />
3802
4110
  <p-button
3803
- label="Deselect All"
4111
+ [label]="'shared.deselect.all' | translate"
3804
4112
  icon="pi pi-times"
3805
4113
  [outlined]="true"
3806
4114
  size="small"
@@ -3809,7 +4117,7 @@ class UserActionSelectorComponent {
3809
4117
  />
3810
4118
  <p-button
3811
4119
  *hasPermission="USER_ACTION_PERMISSIONS.ASSIGN"
3812
- label="Save Changes"
4120
+ [label]="'shared.save.changes' | translate"
3813
4121
  icon="pi pi-save"
3814
4122
  [disabled]="!canSave()"
3815
4123
  [loading]="saving()"
@@ -3826,12 +4134,9 @@ class UserActionSelectorComponent {
3826
4134
  <div class="flex items-start gap-2">
3827
4135
  <i class="pi pi-exclamation-triangle text-xl"></i>
3828
4136
  <div class="flex-1">
3829
- <span class="font-semibold">Validation Warning:</span>
4137
+ <span class="font-semibold">{{ 'iam.validation.warning.title' | translate }}:</span>
3830
4138
  <p class="text-sm mt-1 mb-0">
3831
- {{ invalidActionsCount() }} selected
3832
- action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
3833
- unmet prerequisites. Fix before saving or use auto-fix on
3834
- save.
4139
+ {{ (invalidActionsCount() > 1 ? 'iam.validation.unmet.prerequisites.plural' : 'iam.validation.unmet.prerequisites.singular') | translate: { count: invalidActionsCount() } }}
3835
4140
  </p>
3836
4141
  </div>
3837
4142
  </div>
@@ -3853,14 +4158,14 @@ class UserActionSelectorComponent {
3853
4158
  [ngModel]="allSelected()"
3854
4159
  [binary]="true"
3855
4160
  (ngModelChange)="toggleAll()"
3856
- pTooltip="Select/Deselect All"
4161
+ [pTooltip]="'shared.select.deselect.all' | translate"
3857
4162
  tooltipPosition="top"
3858
4163
  />
3859
4164
  </th>
3860
- <th>Name</th>
3861
- <th class="hidden md:table-cell">Code</th>
3862
- <th>Type</th>
3863
- <th class="hidden lg:table-cell">Description</th>
4165
+ <th>{{ 'shared.name' | translate }}</th>
4166
+ <th class="hidden md:table-cell">{{ 'shared.code' | translate }}</th>
4167
+ <th>{{ 'iam.action.type' | translate }}</th>
4168
+ <th class="hidden lg:table-cell">{{ 'shared.description' | translate }}</th>
3864
4169
  </tr>
3865
4170
  </ng-template>
3866
4171
  <ng-template #body let-rowNode let-rowData="rowData">
@@ -3881,7 +4186,7 @@ class UserActionSelectorComponent {
3881
4186
  @if (hasUnmetPrerequisites(rowData)) {
3882
4187
  <i
3883
4188
  class="pi pi-exclamation-triangle text-orange-500"
3884
- pTooltip="This action has unmet prerequisites and will fail validation on save"
4189
+ [pTooltip]="'iam.validation.unmet.prerequisites.tooltip' | translate"
3885
4190
  tooltipPosition="top"
3886
4191
  ></i>
3887
4192
  }
@@ -3903,9 +4208,9 @@ class UserActionSelectorComponent {
3903
4208
  <tr>
3904
4209
  <td colspan="5" class="text-center p-4 text-muted-color">
3905
4210
  @if (loading()) {
3906
- <i class="pi pi-spin pi-spinner"></i> Loading actions...
4211
+ <i class="pi pi-spin pi-spinner"></i> {{ 'shared.loading.actions' | translate }}
3907
4212
  } @else {
3908
- No actions available.
4213
+ {{ 'iam.permission.no.actions.available' | translate }}
3909
4214
  }
3910
4215
  </td>
3911
4216
  </tr>
@@ -3919,14 +4224,14 @@ class UserActionSelectorComponent {
3919
4224
  <div class="border border-surface rounded-border p-3 mt-4">
3920
4225
  <div class="flex items-center gap-2 mb-3">
3921
4226
  <i class="pi pi-info-circle text-primary"></i>
3922
- <span class="font-bold">Pending Changes</span>
4227
+ <span class="font-bold">{{ 'shared.pending.changes' | translate }}</span>
3923
4228
  </div>
3924
4229
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
3925
4230
  @if (pendingAdd().length > 0) {
3926
4231
  <div>
3927
4232
  <div class="flex items-center gap-2 mb-2">
3928
4233
  <i class="pi pi-plus-circle text-green-500"></i>
3929
- <strong class="text-sm">To Assign ({{ pendingAdd().length }})</strong>
4234
+ <strong class="text-sm">{{ 'shared.to.assign' | translate }} ({{ pendingAdd().length }})</strong>
3930
4235
  </div>
3931
4236
  <ul class="list-none p-0 m-0 pl-4">
3932
4237
  @for (action of pendingAdd(); track action.id) {
@@ -3939,7 +4244,7 @@ class UserActionSelectorComponent {
3939
4244
  <div>
3940
4245
  <div class="flex items-center gap-2 mb-2">
3941
4246
  <i class="pi pi-minus-circle text-red-500"></i>
3942
- <strong class="text-sm">To Remove ({{ pendingRemove().length }})</strong>
4247
+ <strong class="text-sm">{{ 'shared.to.remove' | translate }} ({{ pendingRemove().length }})</strong>
3943
4248
  </div>
3944
4249
  <ul class="list-none p-0 m-0 pl-4">
3945
4250
  @for (action of pendingRemove(); track action.id) {
@@ -3957,17 +4262,17 @@ class UserActionSelectorComponent {
3957
4262
  <div class="surface-card p-5 rounded-border shadow-sm text-center">
3958
4263
  <i class="pi pi-info-circle text-muted-color mb-3 block text-5xl"></i>
3959
4264
  <p class="text-muted-color m-0">
3960
- No actions available for this user.
4265
+ {{ 'iam.permission.no.actions.for.user' | translate }}
3961
4266
  </p>
3962
4267
  </div>
3963
4268
  }
3964
4269
  }
3965
4270
  </div>
3966
- `, isInline: true, styles: [":host{display:block}.validation-warning{background-color:var(--p-yellow-50, #fefce8);border-left:4px solid var(--p-yellow-500, #eab308);color:var(--p-yellow-700, #a16207)}:host-context(.p-dark) .validation-warning{background-color:#eab3081a;color:var(--p-yellow-400, #facc15)}:host ::ng-deep tr.highlight-warning{background-color:var(--p-red-50, rgba(239, 68, 68, .1))!important}:host-context(.p-dark) ::ng-deep tr.highlight-warning{background-color:#ef444426!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i7.TreeTable, selector: "p-treeTable, p-treetable, p-tree-table", inputs: ["columns", "styleClass", "tableStyle", "tableStyleClass", "autoLayout", "lazy", "lazyLoadOnInit", "paginator", "rows", "first", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "customSort", "selectionMode", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "compareSelectionBy", "rowHover", "loading", "loadingIcon", "showLoader", "scrollable", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "frozenColumns", "resizableColumns", "columnResizeMode", "reorderableColumns", "contextMenu", "rowTrackBy", "filters", "globalFilterFields", "filterDelay", "filterMode", "filterLocale", "paginatorLocale", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "value", "virtualRowHeight", "selectionKeys", "showGridlines"], outputs: ["selectionChange", "contextMenuSelectionChange", "onFilter", "onNodeExpand", "onNodeCollapse", "onPage", "onSort", "onLazyLoad", "sortFunction", "onColResize", "onColReorder", "onNodeSelect", "onNodeUnselect", "onContextMenuSelect", "onHeaderCheckboxToggle", "onEditInit", "onEditComplete", "onEditCancel", "selectionKeysChange"] }, { kind: "component", type: i7.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "component", type: UserSelectComponent, selector: "lib-user-select", inputs: ["value"], outputs: ["valueChange", "userSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4271
+ `, isInline: true, styles: [":host{display:block}.validation-warning{background-color:var(--p-yellow-50, #fefce8);border-left:4px solid var(--p-yellow-500, #eab308);color:var(--p-yellow-700, #a16207)}:host-context(.p-dark) .validation-warning{background-color:#eab3081a;color:var(--p-yellow-400, #facc15)}:host ::ng-deep tr.highlight-warning{background-color:var(--p-red-50, rgba(239, 68, 68, .1))!important}:host-context(.p-dark) ::ng-deep tr.highlight-warning{background-color:#ef444426!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i7.TreeTable, selector: "p-treeTable, p-treetable, p-tree-table", inputs: ["columns", "styleClass", "tableStyle", "tableStyleClass", "autoLayout", "lazy", "lazyLoadOnInit", "paginator", "rows", "first", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "customSort", "selectionMode", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "compareSelectionBy", "rowHover", "loading", "loadingIcon", "showLoader", "scrollable", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "frozenColumns", "resizableColumns", "columnResizeMode", "reorderableColumns", "contextMenu", "rowTrackBy", "filters", "globalFilterFields", "filterDelay", "filterMode", "filterLocale", "paginatorLocale", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "value", "virtualRowHeight", "selectionKeys", "showGridlines"], outputs: ["selectionChange", "contextMenuSelectionChange", "onFilter", "onNodeExpand", "onNodeCollapse", "onPage", "onSort", "onLazyLoad", "sortFunction", "onColResize", "onColReorder", "onNodeSelect", "onNodeUnselect", "onContextMenuSelect", "onHeaderCheckboxToggle", "onEditInit", "onEditComplete", "onEditCancel", "selectionKeysChange"] }, { kind: "component", type: i7.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "component", type: UserSelectComponent, selector: "lib-user-select", inputs: ["value"], outputs: ["valueChange", "userSelected"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
3967
4272
  }
3968
4273
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserActionSelectorComponent, decorators: [{
3969
4274
  type: Component,
3970
- args: [{ selector: 'flusys-user-action-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, UserSelectComponent], template: `
4275
+ args: [{ selector: 'flusys-user-action-selector', imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, UserSelectComponent, TranslatePipe], template: `
3971
4276
  <div class="user-action-selector">
3972
4277
  <!-- User and Branch Selectors -->
3973
4278
  <div class="surface-card p-4 rounded-border mb-4 shadow-sm">
@@ -3975,13 +4280,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3975
4280
  <div>
3976
4281
  <label class="block font-semibold mb-2">
3977
4282
  <i class="pi pi-user mr-2 text-primary"></i>
3978
- Select User
4283
+ {{ 'iam.permission.select.user' | translate }}
3979
4284
  </label>
3980
4285
  <lib-user-select
3981
4286
  [value]="selectedUserId()"
3982
4287
  (valueChange)="selectedUserId.set($event)"
3983
4288
  [isEditMode]="true"
3984
- placeHolder="Select a user"
4289
+ [placeHolder]="'iam.permission.select.user.placeholder' | translate"
3985
4290
  />
3986
4291
  </div>
3987
4292
 
@@ -3989,7 +4294,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3989
4294
  <div>
3990
4295
  <label class="block font-semibold mb-2">
3991
4296
  <i class="pi pi-building mr-2 text-primary"></i>
3992
- Select Branch
4297
+ {{ 'iam.permission.select.branch' | translate }}
3993
4298
  </label>
3994
4299
  <p-select
3995
4300
  [ngModel]="selectedBranchId()"
@@ -3997,7 +4302,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3997
4302
  [options]="filteredBranches()"
3998
4303
  optionLabel="name"
3999
4304
  optionValue="id"
4000
- placeholder="Select a branch"
4305
+ [placeholder]="'iam.permission.select.branch.placeholder' | translate"
4001
4306
  [showClear]="true"
4002
4307
  [filter]="true"
4003
4308
  styleClass="w-full"
@@ -4018,10 +4323,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4018
4323
  </ng-template>
4019
4324
  </p-select>
4020
4325
  <small class="text-muted-color block mt-1">
4021
- {{ filteredBranches().length }} permitted branch{{
4022
- filteredBranches().length !== 1 ? 'es' : ''
4023
- }}
4024
- in current company
4326
+ {{ (filteredBranches().length !== 1 ? 'iam.branch.permitted.count.plural' : 'iam.branch.permitted.count') | translate: { count: filteredBranches().length } }}
4025
4327
  </small>
4026
4328
  </div>
4027
4329
  }
@@ -4043,14 +4345,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4043
4345
  class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4"
4044
4346
  >
4045
4347
  <div>
4046
- <h5 class="m-0 mb-1">Direct Action Permissions</h5>
4348
+ <h5 class="m-0 mb-1">{{ 'iam.permission.direct.action.permissions' | translate }}</h5>
4047
4349
  <p class="text-sm text-muted-color m-0">
4048
- {{ actions().length }} actions available
4350
+ {{ 'iam.permission.actions.available' | translate: { count: actions().length } }}
4049
4351
  </p>
4050
4352
  </div>
4051
4353
  <div class="flex flex-wrap gap-2">
4052
4354
  <p-button
4053
- label="Select All"
4355
+ [label]="'shared.select.all' | translate"
4054
4356
  icon="pi pi-check"
4055
4357
  [outlined]="true"
4056
4358
  size="small"
@@ -4058,7 +4360,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4058
4360
  (onClick)="selectAll()"
4059
4361
  />
4060
4362
  <p-button
4061
- label="Deselect All"
4363
+ [label]="'shared.deselect.all' | translate"
4062
4364
  icon="pi pi-times"
4063
4365
  [outlined]="true"
4064
4366
  size="small"
@@ -4067,7 +4369,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4067
4369
  />
4068
4370
  <p-button
4069
4371
  *hasPermission="USER_ACTION_PERMISSIONS.ASSIGN"
4070
- label="Save Changes"
4372
+ [label]="'shared.save.changes' | translate"
4071
4373
  icon="pi pi-save"
4072
4374
  [disabled]="!canSave()"
4073
4375
  [loading]="saving()"
@@ -4084,12 +4386,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4084
4386
  <div class="flex items-start gap-2">
4085
4387
  <i class="pi pi-exclamation-triangle text-xl"></i>
4086
4388
  <div class="flex-1">
4087
- <span class="font-semibold">Validation Warning:</span>
4389
+ <span class="font-semibold">{{ 'iam.validation.warning.title' | translate }}:</span>
4088
4390
  <p class="text-sm mt-1 mb-0">
4089
- {{ invalidActionsCount() }} selected
4090
- action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
4091
- unmet prerequisites. Fix before saving or use auto-fix on
4092
- save.
4391
+ {{ (invalidActionsCount() > 1 ? 'iam.validation.unmet.prerequisites.plural' : 'iam.validation.unmet.prerequisites.singular') | translate: { count: invalidActionsCount() } }}
4093
4392
  </p>
4094
4393
  </div>
4095
4394
  </div>
@@ -4111,14 +4410,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4111
4410
  [ngModel]="allSelected()"
4112
4411
  [binary]="true"
4113
4412
  (ngModelChange)="toggleAll()"
4114
- pTooltip="Select/Deselect All"
4413
+ [pTooltip]="'shared.select.deselect.all' | translate"
4115
4414
  tooltipPosition="top"
4116
4415
  />
4117
4416
  </th>
4118
- <th>Name</th>
4119
- <th class="hidden md:table-cell">Code</th>
4120
- <th>Type</th>
4121
- <th class="hidden lg:table-cell">Description</th>
4417
+ <th>{{ 'shared.name' | translate }}</th>
4418
+ <th class="hidden md:table-cell">{{ 'shared.code' | translate }}</th>
4419
+ <th>{{ 'iam.action.type' | translate }}</th>
4420
+ <th class="hidden lg:table-cell">{{ 'shared.description' | translate }}</th>
4122
4421
  </tr>
4123
4422
  </ng-template>
4124
4423
  <ng-template #body let-rowNode let-rowData="rowData">
@@ -4139,7 +4438,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4139
4438
  @if (hasUnmetPrerequisites(rowData)) {
4140
4439
  <i
4141
4440
  class="pi pi-exclamation-triangle text-orange-500"
4142
- pTooltip="This action has unmet prerequisites and will fail validation on save"
4441
+ [pTooltip]="'iam.validation.unmet.prerequisites.tooltip' | translate"
4143
4442
  tooltipPosition="top"
4144
4443
  ></i>
4145
4444
  }
@@ -4161,9 +4460,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4161
4460
  <tr>
4162
4461
  <td colspan="5" class="text-center p-4 text-muted-color">
4163
4462
  @if (loading()) {
4164
- <i class="pi pi-spin pi-spinner"></i> Loading actions...
4463
+ <i class="pi pi-spin pi-spinner"></i> {{ 'shared.loading.actions' | translate }}
4165
4464
  } @else {
4166
- No actions available.
4465
+ {{ 'iam.permission.no.actions.available' | translate }}
4167
4466
  }
4168
4467
  </td>
4169
4468
  </tr>
@@ -4177,14 +4476,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4177
4476
  <div class="border border-surface rounded-border p-3 mt-4">
4178
4477
  <div class="flex items-center gap-2 mb-3">
4179
4478
  <i class="pi pi-info-circle text-primary"></i>
4180
- <span class="font-bold">Pending Changes</span>
4479
+ <span class="font-bold">{{ 'shared.pending.changes' | translate }}</span>
4181
4480
  </div>
4182
4481
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
4183
4482
  @if (pendingAdd().length > 0) {
4184
4483
  <div>
4185
4484
  <div class="flex items-center gap-2 mb-2">
4186
4485
  <i class="pi pi-plus-circle text-green-500"></i>
4187
- <strong class="text-sm">To Assign ({{ pendingAdd().length }})</strong>
4486
+ <strong class="text-sm">{{ 'shared.to.assign' | translate }} ({{ pendingAdd().length }})</strong>
4188
4487
  </div>
4189
4488
  <ul class="list-none p-0 m-0 pl-4">
4190
4489
  @for (action of pendingAdd(); track action.id) {
@@ -4197,7 +4496,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4197
4496
  <div>
4198
4497
  <div class="flex items-center gap-2 mb-2">
4199
4498
  <i class="pi pi-minus-circle text-red-500"></i>
4200
- <strong class="text-sm">To Remove ({{ pendingRemove().length }})</strong>
4499
+ <strong class="text-sm">{{ 'shared.to.remove' | translate }} ({{ pendingRemove().length }})</strong>
4201
4500
  </div>
4202
4501
  <ul class="list-none p-0 m-0 pl-4">
4203
4502
  @for (action of pendingRemove(); track action.id) {
@@ -4215,7 +4514,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
4215
4514
  <div class="surface-card p-5 rounded-border shadow-sm text-center">
4216
4515
  <i class="pi pi-info-circle text-muted-color mb-3 block text-5xl"></i>
4217
4516
  <p class="text-muted-color m-0">
4218
- No actions available for this user.
4517
+ {{ 'iam.permission.no.actions.for.user' | translate }}
4219
4518
  </p>
4220
4519
  </div>
4221
4520
  }
@@ -4296,7 +4595,8 @@ function provideIamProviders() {
4296
4595
  const IAM_ROUTES = [
4297
4596
  {
4298
4597
  path: '',
4299
- loadComponent: () => import('./flusys-ng-iam-iam-container.component-UYJjqYV9.mjs').then((m) => m.IamContainerComponent),
4598
+ resolve: { translations: resolveTranslationModule({ modules: ['iam'], fallbackMessages: { ...IAM_MESSAGES, ...SHARED_MESSAGES } }) },
4599
+ loadComponent: () => import('./flusys-ng-iam-iam-container.component-CQA2B6cU.mjs').then((m) => m.IamContainerComponent),
4300
4600
  children: [
4301
4601
  // Actions Management
4302
4602
  {
@@ -4305,15 +4605,15 @@ const IAM_ROUTES = [
4305
4605
  children: [
4306
4606
  {
4307
4607
  path: '',
4308
- loadComponent: () => import('./flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs').then((m) => m.ActionListPageComponent),
4608
+ loadComponent: () => import('./flusys-ng-iam-action-list-page.component-BrpZujxk.mjs').then((m) => m.ActionListPageComponent),
4309
4609
  },
4310
4610
  {
4311
4611
  path: 'new',
4312
- loadComponent: () => import('./flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs').then((m) => m.ActionFormPageComponent),
4612
+ loadComponent: () => import('./flusys-ng-iam-action-form-page.component-BQx9yset.mjs').then((m) => m.ActionFormPageComponent),
4313
4613
  },
4314
4614
  {
4315
4615
  path: ':id',
4316
- loadComponent: () => import('./flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs').then((m) => m.ActionFormPageComponent),
4616
+ loadComponent: () => import('./flusys-ng-iam-action-form-page.component-BQx9yset.mjs').then((m) => m.ActionFormPageComponent),
4317
4617
  },
4318
4618
  ],
4319
4619
  },
@@ -4324,15 +4624,15 @@ const IAM_ROUTES = [
4324
4624
  children: [
4325
4625
  {
4326
4626
  path: '',
4327
- loadComponent: () => import('./flusys-ng-iam-role-list-page.component-D4J1by6Q.mjs').then((m) => m.RoleListPageComponent),
4627
+ loadComponent: () => import('./flusys-ng-iam-role-list-page.component-BHB8X5r7.mjs').then((m) => m.RoleListPageComponent),
4328
4628
  },
4329
4629
  {
4330
4630
  path: 'new',
4331
- loadComponent: () => import('./flusys-ng-iam-role-form-page.component-D_AAEay2.mjs').then((m) => m.RoleFormPageComponent),
4631
+ loadComponent: () => import('./flusys-ng-iam-role-form-page.component-CVfRQpoa.mjs').then((m) => m.RoleFormPageComponent),
4332
4632
  },
4333
4633
  {
4334
4634
  path: ':id',
4335
- loadComponent: () => import('./flusys-ng-iam-role-form-page.component-D_AAEay2.mjs').then((m) => m.RoleFormPageComponent),
4635
+ loadComponent: () => import('./flusys-ng-iam-role-form-page.component-CVfRQpoa.mjs').then((m) => m.RoleFormPageComponent),
4336
4636
  },
4337
4637
  ],
4338
4638
  },
@@ -4347,7 +4647,7 @@ const IAM_ROUTES = [
4347
4647
  COMPANY_ACTION_PERMISSIONS.READ,
4348
4648
  ]),
4349
4649
  ],
4350
- loadComponent: () => import('./flusys-ng-iam-permission-page.component-DcgT7L3_.mjs').then((m) => m.PermissionPageComponent),
4650
+ loadComponent: () => import('./flusys-ng-iam-permission-page.component-Dpk90y72.mjs').then((m) => m.PermissionPageComponent),
4351
4651
  },
4352
4652
  // Default redirect to actions
4353
4653
  {
@@ -4365,5 +4665,5 @@ const IAM_ROUTES = [
4365
4665
  * Generated bundle index. Do not edit.
4366
4666
  */
4367
4667
 
4368
- export { ActionApiService as A, CompanyActionSelectorComponent as C, IAM_ROUTES as I, LogicBuilderComponent as L, MAX_DROPDOWN_ITEMS as M, PermissionApiService as P, RoleApiService as R, UserRoleSelectorComponent as U, ActionType as a, RoleActionSelectorComponent as b, convertActionToTreeNode as c, UserActionSelectorComponent as d, ActionPermissionLogicService as e, MyPermissionsApiService as f, PermissionStateService as g, ProfilePermissionProviderAdapter as h, provideIamProviders as p };
4369
- //# sourceMappingURL=flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs.map
4668
+ export { ActionApiService as A, CompanyActionSelectorComponent as C, IAM_MESSAGES as I, LogicBuilderComponent as L, MAX_DROPDOWN_ITEMS as M, PermissionApiService as P, RoleApiService as R, UserRoleSelectorComponent as U, ActionType as a, RoleActionSelectorComponent as b, convertActionToTreeNode as c, UserActionSelectorComponent as d, ActionPermissionLogicService as e, IAM_ROUTES as f, MyPermissionsApiService as g, PermissionStateService as h, ProfilePermissionProviderAdapter as i, provideIamProviders as p };
4669
+ //# sourceMappingURL=flusys-ng-iam-flusys-ng-iam-Co4ot9My.mjs.map