@flusys/ng-iam 1.1.1-beta → 3.0.0-rc

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 (30) hide show
  1. package/README.md +175 -24
  2. package/fesm2022/flusys-ng-iam-action-form-page.component-CVN8sV-c.mjs +389 -0
  3. package/fesm2022/flusys-ng-iam-action-form-page.component-CVN8sV-c.mjs.map +1 -0
  4. package/fesm2022/flusys-ng-iam-action-list-page.component-CQ6RazN0.mjs +262 -0
  5. package/fesm2022/flusys-ng-iam-action-list-page.component-CQ6RazN0.mjs.map +1 -0
  6. package/fesm2022/{flusys-ng-iam-flusys-ng-iam-DISrddPh.mjs → flusys-ng-iam-flusys-ng-iam-DrGHlTiz.mjs} +1016 -1585
  7. package/fesm2022/flusys-ng-iam-flusys-ng-iam-DrGHlTiz.mjs.map +1 -0
  8. package/fesm2022/flusys-ng-iam-iam-container.component-BToYxEej.mjs +92 -0
  9. package/fesm2022/flusys-ng-iam-iam-container.component-BToYxEej.mjs.map +1 -0
  10. package/fesm2022/flusys-ng-iam-permission-page.component-BS7xXmsn.mjs +137 -0
  11. package/fesm2022/flusys-ng-iam-permission-page.component-BS7xXmsn.mjs.map +1 -0
  12. package/fesm2022/{flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs → flusys-ng-iam-role-form-page.component-BjPwXkip.mjs} +106 -148
  13. package/fesm2022/flusys-ng-iam-role-form-page.component-BjPwXkip.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-iam-role-list-page.component-Cz-jk-R_.mjs +299 -0
  15. package/fesm2022/flusys-ng-iam-role-list-page.component-Cz-jk-R_.mjs.map +1 -0
  16. package/fesm2022/flusys-ng-iam.mjs +1 -1
  17. package/package.json +5 -5
  18. package/types/flusys-ng-iam.d.ts +75 -454
  19. package/fesm2022/flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs +0 -467
  20. package/fesm2022/flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs.map +0 -1
  21. package/fesm2022/flusys-ng-iam-action-list-page.component-BCzSardO.mjs +0 -281
  22. package/fesm2022/flusys-ng-iam-action-list-page.component-BCzSardO.mjs.map +0 -1
  23. package/fesm2022/flusys-ng-iam-flusys-ng-iam-DISrddPh.mjs.map +0 -1
  24. package/fesm2022/flusys-ng-iam-iam-container.component-BkhqmzLi.mjs +0 -97
  25. package/fesm2022/flusys-ng-iam-iam-container.component-BkhqmzLi.mjs.map +0 -1
  26. package/fesm2022/flusys-ng-iam-permission-page.component-BSQFPt_N.mjs +0 -143
  27. package/fesm2022/flusys-ng-iam-permission-page.component-BSQFPt_N.mjs.map +0 -1
  28. package/fesm2022/flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs.map +0 -1
  29. package/fesm2022/flusys-ng-iam-role-list-page.component-BObCxHiB.mjs +0 -266
  30. package/fesm2022/flusys-ng-iam-role-list-page.component-BObCxHiB.mjs.map +0 -1
package/README.md CHANGED
@@ -14,6 +14,7 @@
14
14
  | Version | 1.0.0 |
15
15
  | Dependencies | ng-core, ng-shared, ng-layout (IMenuItem only) |
16
16
  | NO dependency on | ng-auth (fully independent via Provider Pattern) |
17
+ | CSS Framework | Tailwind CSS (not PrimeFlex) |
17
18
  | Build | `npm run build:ng-iam` |
18
19
  | Entry point | `public-api.ts` |
19
20
 
@@ -64,21 +65,34 @@ ng-iam requires these provider interfaces:
64
65
 
65
66
  ### How Components Use Providers
66
67
 
68
+ User selection uses `lib-user-select` component from ng-shared (encapsulates USER_PROVIDER):
69
+
70
+ ```html
71
+ <!-- UserActionSelectorComponent / UserRoleSelectorComponent -->
72
+ <lib-user-select
73
+ [(value)]="selectedUserId"
74
+ [isEditMode]="true"
75
+ placeHolder="Select a user"
76
+ />
77
+ ```
78
+
79
+ Company context is accessed via `LAYOUT_AUTH_STATE`:
80
+
67
81
  ```typescript
68
- // UserActionSelectorComponent / UserRoleSelectorComponent
69
- private readonly userProvider = inject(USER_PROVIDER);
70
82
  private readonly companyContext = inject(LAYOUT_AUTH_STATE);
71
83
 
72
- async loadUsers() {
73
- const response = await firstValueFrom(
74
- this.userProvider.getUsers({
75
- page: 0,
76
- pageSize: MAX_DROPDOWN_ITEMS,
77
- companyId: this.companyContext.currentCompanyInfo()?.id || undefined,
78
- })
79
- );
80
- this.users.set(response?.data ?? []);
81
- }
84
+ // Get current company
85
+ const companyId = this.companyContext.currentCompanyInfo()?.id;
86
+ ```
87
+
88
+ Branch permissions loaded from `USER_PERMISSION_PROVIDER`:
89
+
90
+ ```typescript
91
+ private readonly userPermissionProvider = inject(USER_PERMISSION_PROVIDER);
92
+
93
+ const response = await this.userPermissionProvider
94
+ .getUserBranchPermissions(userId)
95
+ .toPromise();
82
96
  ```
83
97
 
84
98
  ---
@@ -363,12 +377,14 @@ Assigns actions directly to users (DIRECT/FULL mode).
363
377
  | Standalone | Yes |
364
378
  | Change Detection | OnPush |
365
379
 
366
- **Dependencies:** `APP_CONFIG`, `LAYOUT_AUTH_STATE`, `USER_PROVIDER`, `USER_PERMISSION_PROVIDER`, `ActionApiService`, `ActionPermissionLogicService`, `PermissionApiService`, `MessageService`
380
+ **Dependencies:** `APP_CONFIG`, `LAYOUT_AUTH_STATE`, `USER_PERMISSION_PROVIDER`, `ActionApiService`, `ActionPermissionLogicService`, `PermissionApiService`, `MessageService`
367
381
 
368
382
  **Features:**
369
- - User dropdown (loaded from USER_PROVIDER)
383
+ - User dropdown (`lib-user-select` component from ng-shared)
370
384
  - Branch selector (conditional, loaded from USER_PERMISSION_PROVIDER)
371
385
  - Hierarchical TreeTable with checkboxes
386
+ - Responsive table with `overflow-x-auto` container
387
+ - Responsive columns (Code hidden on mobile, Description hidden until lg)
372
388
  - Action type tags (backend/frontend/both)
373
389
  - Prerequisite validation with tooltips
374
390
  - Change tracking with pending add/remove summary
@@ -387,11 +403,13 @@ Assigns roles to users (RBAC/FULL mode).
387
403
  | Standalone | Yes |
388
404
  | Change Detection | OnPush |
389
405
 
390
- **Dependencies:** `APP_CONFIG`, `LAYOUT_AUTH_STATE`, `USER_PROVIDER`, `USER_PERMISSION_PROVIDER`, `RoleApiService`, `PermissionApiService`, `MessageService`
406
+ **Dependencies:** `APP_CONFIG`, `LAYOUT_AUTH_STATE`, `USER_PERMISSION_PROVIDER`, `RoleApiService`, `PermissionApiService`, `MessageService`
391
407
 
392
408
  **Features:**
393
- - User dropdown with branch selector
394
- - DataTable with pagination
409
+ - User dropdown (`lib-user-select` component from ng-shared)
410
+ - Branch selector (conditional)
411
+ - DataTable with pagination and `overflow-x-auto` container
412
+ - Responsive columns (Code hidden on mobile, Description hidden until md)
395
413
  - Change tracking with pending add/remove summary
396
414
  - Select/deselect all
397
415
  - No prerequisite validation (roles are simple assignments)
@@ -411,12 +429,13 @@ Assigns actions to roles (RBAC/FULL mode).
411
429
  **Dependencies:** `RoleApiService`, `ActionApiService`, `PermissionApiService`, `MessageService`, `ActionPermissionLogicService`
412
430
 
413
431
  **Features:**
414
- - Role dropdown
415
- - Hierarchical TreeTable with checkboxes
432
+ - Role dropdown with filter
433
+ - Hierarchical TreeTable with checkboxes and `overflow-x-auto` container
434
+ - Responsive columns (Code hidden until md, Requirements hidden until lg)
416
435
  - Prerequisite validation with smart dependency handling
417
436
  - Request cancellation with AbortController
418
437
  - Pre-save validation with auto-fix dialog
419
- - Change tracking
438
+ - Change tracking with pending add/remove summary
420
439
 
421
440
  ---
422
441
 
@@ -433,11 +452,14 @@ Manages company action whitelisting (when company feature enabled).
433
452
  **Dependencies:** `COMPANY_API_PROVIDER`, `ActionApiService`, `PermissionApiService`, `MessageService`, `ConfirmationService`, `ActionPermissionLogicService`
434
453
 
435
454
  **Features:**
436
- - Company dropdown (loaded from COMPANY_API_PROVIDER)
455
+ - Company dropdown with filter (loaded from COMPANY_API_PROVIDER)
437
456
  - Full action tree (not filtered by company whitelist)
438
- - Prerequisite validation
457
+ - Hierarchical TreeTable with `overflow-x-auto` container
458
+ - Responsive columns (Code hidden until md, Description hidden until lg)
459
+ - Prerequisite validation with tooltips
439
460
  - Backend prerequisite error handling with auto-fix
440
461
  - Request cancellation with AbortController
462
+ - Change tracking with pending whitelist/remove summary
441
463
 
442
464
  ---
443
465
 
@@ -453,8 +475,34 @@ Tabbed navigation container for IAM pages.
453
475
  |----------|-------|
454
476
  | Selector | `lib-iam-container` |
455
477
  | Tabs | Actions (always), Roles (RBAC/FULL), Permissions (always) |
478
+ | Change Detection | OnPush |
479
+
480
+ **Features:**
481
+ - Responsive page title (`text-xl sm:text-2xl`)
482
+ - Horizontally scrollable tabs on mobile (hidden scrollbar)
483
+ - Uses `RouterLink` and `RouterLinkActive` for navigation
484
+ - Tab visibility based on permission mode
456
485
 
457
- Uses `RouterLink` and `RouterLinkActive` for navigation. Tab visibility based on permission mode.
486
+ **Responsive Patterns:**
487
+
488
+ ```html
489
+ <!-- Page title -->
490
+ <h1 class="text-xl sm:text-2xl font-bold m-0">Identity & Access Management</h1>
491
+
492
+ <!-- Horizontally scrollable tabs (hidden scrollbar) -->
493
+ <div class="scrollbar-hide flex gap-1 overflow-x-auto flex-nowrap">
494
+ <!-- tabs -->
495
+ </div>
496
+ ```
497
+
498
+ **Scrollbar hide CSS:**
499
+ ```css
500
+ .scrollbar-hide {
501
+ -ms-overflow-style: none;
502
+ scrollbar-width: none;
503
+ &::-webkit-scrollbar { display: none; }
504
+ }
505
+ ```
458
506
 
459
507
  ---
460
508
 
@@ -467,6 +515,11 @@ Hierarchical action list with CRUD operations.
467
515
  | Selector | `lib-action-list-page` |
468
516
  | Features | TreeTable display, create/edit/delete, read-only indicators, company context awareness |
469
517
 
518
+ **Responsive features:**
519
+ - TreeTable with `overflow-x-auto -mx-4 sm:mx-0` container
520
+ - Responsive columns: Code (hidden until md), Active (hidden until sm), Read Only (hidden until lg)
521
+ - Responsive header with stacked layout on mobile
522
+
470
523
  Reloads actions when company context changes via effect.
471
524
 
472
525
  ---
@@ -493,6 +546,11 @@ Paginated role list with CRUD operations.
493
546
  | Selector | `lib-role-list-page` |
494
547
  | Features | Paginated table (10/25/50), create/edit/delete, company context filtering, read-only indicators |
495
548
 
549
+ **Responsive features:**
550
+ - Table with `overflow-x-auto -mx-4 sm:mx-0` container
551
+ - Responsive columns: Description (hidden until md), Read Only (hidden until sm)
552
+ - Conditional paginator (only shown when data exists)
553
+
496
554
  ---
497
555
 
498
556
  ### RoleFormPageComponent
@@ -883,6 +941,96 @@ GET /iam/permissions/company-actions/:id # Get company's whitelisted action
883
941
 
884
942
  ---
885
943
 
944
+ ## Responsive Table Patterns
945
+
946
+ All IAM tables and tree tables use consistent responsive patterns with Tailwind CSS.
947
+
948
+ ### Table Container Pattern
949
+
950
+ ```html
951
+ <!-- Wrapper enables horizontal scroll on mobile, full-width on desktop -->
952
+ <div class="overflow-x-auto -mx-4 sm:mx-0">
953
+ <p-treeTable
954
+ [value]="treeNodes()"
955
+ dataKey="id"
956
+ styleClass="p-treetable-sm"
957
+ [tableStyle]="{ 'min-width': '50rem' }"
958
+ >
959
+ <!-- ... -->
960
+ </p-treeTable>
961
+ </div>
962
+ ```
963
+
964
+ **Key properties:**
965
+ - `overflow-x-auto` - Enables horizontal scroll when content exceeds container
966
+ - `-mx-4 sm:mx-0` - Edge-to-edge on mobile, normal margins on desktop
967
+ - `[tableStyle]="{ 'min-width': '50rem' }"` - Minimum width ensures consistent layout
968
+
969
+ ### Responsive Column Visibility
970
+
971
+ ```html
972
+ <ng-template #header>
973
+ <tr>
974
+ <th class="w-12"><!-- checkbox --></th>
975
+ <th>Name</th> <!-- Always visible -->
976
+ <th class="hidden sm:table-cell">Code</th> <!-- Hidden on xs -->
977
+ <th>Type</th> <!-- Always visible -->
978
+ <th class="hidden md:table-cell">Status</th> <!-- Hidden until md -->
979
+ <th class="hidden lg:table-cell">Description</th> <!-- Hidden until lg -->
980
+ </tr>
981
+ </ng-template>
982
+ ```
983
+
984
+ **Breakpoint classes:**
985
+ | Class | Visibility |
986
+ |-------|------------|
987
+ | (none) | Always visible |
988
+ | `hidden sm:table-cell` | Visible at 640px+ |
989
+ | `hidden md:table-cell` | Visible at 768px+ |
990
+ | `hidden lg:table-cell` | Visible at 1024px+ |
991
+
992
+ ### Header Layout Pattern
993
+
994
+ ```html
995
+ <div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4">
996
+ <div>
997
+ <h5 class="m-0 mb-1">Section Title</h5>
998
+ <p class="text-sm text-muted-color m-0">{{ count }} items available</p>
999
+ </div>
1000
+ <div class="flex flex-wrap gap-2">
1001
+ <!-- Action buttons -->
1002
+ </div>
1003
+ </div>
1004
+ ```
1005
+
1006
+ ### Dark Mode Styling
1007
+
1008
+ ```css
1009
+ /* Light mode */
1010
+ .validation-warning {
1011
+ background-color: var(--p-yellow-50, #fefce8);
1012
+ border-left: 4px solid var(--p-yellow-500, #eab308);
1013
+ color: var(--p-yellow-700, #a16207);
1014
+ }
1015
+
1016
+ /* Dark mode */
1017
+ :host-context(.p-dark) .validation-warning {
1018
+ background-color: rgba(234, 179, 8, 0.1);
1019
+ color: var(--p-yellow-400, #facc15);
1020
+ }
1021
+ ```
1022
+
1023
+ **Color classes (Tailwind):**
1024
+ | Purpose | Class |
1025
+ |---------|-------|
1026
+ | Muted text | `text-muted-color` |
1027
+ | Success | `text-green-500` |
1028
+ | Danger | `text-red-500` |
1029
+ | Warning | `text-orange-500` |
1030
+ | Primary | `text-primary` |
1031
+
1032
+ ---
1033
+
886
1034
  ## Best Practices
887
1035
 
888
1036
  ### Permission Loading
@@ -986,6 +1134,9 @@ const logic: ILogicNode = {
986
1134
  | 401 on IAM endpoints with external auth | Auth token not forwarded | Ensure your auth interceptor adds `Authorization` header to IAM requests |
987
1135
  | Provider not configured error | Missing DI registration | Add required providers to `app.config.ts` (USER_PROVIDER, LAYOUT_AUTH_STATE) |
988
1136
  | Empty user/company dropdowns | API response format mismatch | Ensure adapters return `IListResponse<T>` format with `data` array |
1137
+ | TreeTable has excessive whitespace | Using `scrollHeight="flex"` without fixed-height parent | Remove `scrollHeight="flex"` and `[scrollable]="true"`, use `overflow-x-auto` container instead |
1138
+ | Table not horizontally scrollable on mobile | Missing responsive wrapper | Add `<div class="overflow-x-auto -mx-4 sm:mx-0">` around table |
1139
+ | Columns visible on mobile causing overflow | Missing responsive column classes | Add `hidden sm:table-cell` or `hidden md:table-cell` to non-essential columns |
989
1140
 
990
1141
  ---
991
1142
 
@@ -1074,5 +1225,5 @@ if (this.permissionProvider) {
1074
1225
 
1075
1226
  ---
1076
1227
 
1077
- **Last Updated:** 2026-02-16
1228
+ **Last Updated:** 2026-02-21
1078
1229
  **Angular Version:** 21
@@ -0,0 +1,389 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, signal, computed, effect, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { toSignal } from '@angular/core/rxjs-interop';
4
+ import { form, required, FormField } from '@angular/forms/signals';
5
+ import { ActivatedRoute, Router } from '@angular/router';
6
+ import { AngularModule, PrimeModule } from '@flusys/ng-shared';
7
+ import { MessageService } from 'primeng/api';
8
+ import { firstValueFrom } from 'rxjs';
9
+ import { A as ActionApiService, a as ActionType, L as LogicBuilderComponent } from './flusys-ng-iam-flusys-ng-iam-DrGHlTiz.mjs';
10
+ import * as i1 from '@angular/forms';
11
+ import * as i2 from 'primeng/button';
12
+ import * as i3 from 'primeng/checkbox';
13
+ import * as i4 from 'primeng/inputtext';
14
+
15
+ class ActionFormPageComponent {
16
+ route = inject(ActivatedRoute);
17
+ router = inject(Router);
18
+ actionApi = inject(ActionApiService);
19
+ messageService = inject(MessageService);
20
+ routeParams = toSignal(this.route.paramMap);
21
+ initialized = false;
22
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
23
+ existingAction = signal(null, ...(ngDevMode ? [{ debugName: "existingAction" }] : []));
24
+ isEditMode = computed(() => !!this.existingAction(), ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
25
+ allActionsForLogic = signal([], ...(ngDevMode ? [{ debugName: "allActionsForLogic" }] : []));
26
+ allActions = signal([], ...(ngDevMode ? [{ debugName: "allActions" }] : []));
27
+ availableActions = computed(() => {
28
+ const actions = this.allActions();
29
+ const currentId = this.existingAction()?.id;
30
+ return currentId ? actions.filter((a) => a.id !== currentId) : actions;
31
+ }, ...(ngDevMode ? [{ debugName: "availableActions" }] : []));
32
+ formModel = signal({
33
+ id: '',
34
+ name: '',
35
+ description: '',
36
+ code: '',
37
+ actionType: ActionType.BACKEND,
38
+ permissionLogic: null,
39
+ parentId: '',
40
+ serial: '',
41
+ isActive: true,
42
+ metadata: null,
43
+ }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
44
+ actionTypes = [
45
+ { label: 'Backend (API Endpoints)', value: ActionType.BACKEND },
46
+ { label: 'Frontend (UI Features)', value: ActionType.FRONTEND },
47
+ { label: 'Both (Backend + Frontend)', value: ActionType.BOTH },
48
+ ];
49
+ actionForm = form(this.formModel, (f) => {
50
+ required(f.name, { message: 'Name is required' });
51
+ });
52
+ isFormValid = computed(() => {
53
+ const model = this.formModel();
54
+ return model.name.trim().length > 0;
55
+ }, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
56
+ constructor() {
57
+ effect(() => {
58
+ const params = this.routeParams();
59
+ if (!params || this.initialized)
60
+ return;
61
+ this.initialized = true;
62
+ this.initializeForm(params.get('id'));
63
+ });
64
+ }
65
+ async initializeForm(id) {
66
+ try {
67
+ const response = await firstValueFrom(this.actionApi.getAll('', {
68
+ pagination: { currentPage: 0, pageSize: 10000 },
69
+ select: ['id', 'name', 'code', 'actionType', 'permissionLogic'],
70
+ }));
71
+ if (response?.success && response.data) {
72
+ this.allActionsForLogic.set(response.data.map((a) => ({ id: a.id, name: a.name ?? 'Unnamed' })));
73
+ this.allActions.set(response.data);
74
+ }
75
+ }
76
+ catch {
77
+ // Ignored - form will show empty parent dropdown
78
+ }
79
+ if (id && id !== 'new') {
80
+ await this.loadAction(id);
81
+ }
82
+ }
83
+ async loadAction(id) {
84
+ this.isLoading.set(true);
85
+ try {
86
+ const response = await this.actionApi.findByIdAsync(id, [
87
+ 'id', 'name', 'description', 'code', 'actionType',
88
+ 'permissionLogic', 'parentId', 'serial', 'isActive', 'metadata',
89
+ ]);
90
+ if (response?.success && response.data) {
91
+ const action = response.data;
92
+ this.existingAction.set(action);
93
+ this.formModel.set({
94
+ id: action.id ?? '',
95
+ name: action.name ?? '',
96
+ description: action.description ?? '',
97
+ code: action.code ?? '',
98
+ actionType: action.actionType ?? ActionType.BACKEND,
99
+ permissionLogic: action.permissionLogic ?? null,
100
+ parentId: action.parentId ?? '',
101
+ serial: action.serial?.toString() ?? '',
102
+ isActive: action.isActive ?? true,
103
+ metadata: action.metadata ?? null,
104
+ });
105
+ }
106
+ else {
107
+ this.router.navigate(['/iam/actions']);
108
+ }
109
+ }
110
+ catch {
111
+ this.router.navigate(['/iam/actions']);
112
+ }
113
+ finally {
114
+ this.isLoading.set(false);
115
+ }
116
+ }
117
+ async onSubmit() {
118
+ if (!this.isFormValid()) {
119
+ this.messageService.add({
120
+ severity: 'error',
121
+ summary: 'Validation Error',
122
+ detail: 'Please fill in all required fields.',
123
+ });
124
+ return;
125
+ }
126
+ this.isLoading.set(true);
127
+ try {
128
+ const formValue = this.formModel();
129
+ const dto = {
130
+ ...formValue,
131
+ description: formValue.description || undefined,
132
+ code: formValue.code || undefined,
133
+ parentId: formValue.parentId || undefined,
134
+ serial: formValue.serial ? parseInt(formValue.serial, 10) : undefined,
135
+ metadata: formValue.metadata ?? undefined,
136
+ permissionLogic: formValue.permissionLogic ?? undefined,
137
+ };
138
+ if (this.isEditMode()) {
139
+ await this.actionApi.updateAsync(dto);
140
+ this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Action updated successfully.' });
141
+ }
142
+ else {
143
+ await this.actionApi.insertAsync(dto);
144
+ this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Action created successfully.' });
145
+ }
146
+ this.router.navigate(['/iam/actions']);
147
+ }
148
+ catch {
149
+ // Handled by global interceptor
150
+ }
151
+ finally {
152
+ this.isLoading.set(false);
153
+ }
154
+ }
155
+ onBack() {
156
+ this.router.navigate(['/iam/actions']);
157
+ }
158
+ onLogicChange(logic) {
159
+ this.formModel.update((model) => ({ ...model, permissionLogic: logic }));
160
+ }
161
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ActionFormPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
162
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ActionFormPageComponent, isStandalone: true, selector: "lib-action-form-page", ngImport: i0, template: `
163
+ <div class="card">
164
+ <h3 class="text-lg sm:text-xl font-semibold mb-4">
165
+ {{ isEditMode() ? 'Edit Action' : 'New Action' }}
166
+ </h3>
167
+
168
+ <form (ngSubmit)="onSubmit()" class="grid grid-cols-1 md:grid-cols-2 gap-4">
169
+ <!-- Name -->
170
+ <div class="flex flex-col gap-2">
171
+ <label for="name" class="font-medium">Name *</label>
172
+ <input
173
+ pInputText
174
+ id="name"
175
+ [formField]="actionForm.name"
176
+ placeholder="Enter action name" />
177
+ </div>
178
+
179
+ <!-- Code -->
180
+ <div class="flex flex-col gap-2">
181
+ <label for="code" class="font-medium">Code</label>
182
+ <input
183
+ pInputText
184
+ id="code"
185
+ [formField]="actionForm.code"
186
+ placeholder="Enter action code" />
187
+ </div>
188
+
189
+ <!-- Description -->
190
+ <div class="flex flex-col gap-2">
191
+ <label for="description" class="font-medium">Description</label>
192
+ <input
193
+ pInputText
194
+ id="description"
195
+ [formField]="actionForm.description"
196
+ placeholder="Enter description" />
197
+ </div>
198
+
199
+ <!-- Action Type -->
200
+ <div class="flex flex-col gap-2">
201
+ <label for="actionType" class="font-medium">Action Type *</label>
202
+ <select
203
+ id="actionType"
204
+ class="p-inputtext w-full"
205
+ [formField]="actionForm.actionType">
206
+ @for (type of actionTypes; track type.value) {
207
+ <option [value]="type.value">{{ type.label }}</option>
208
+ }
209
+ </select>
210
+ </div>
211
+
212
+ <!-- Parent Action -->
213
+ <div class="flex flex-col gap-2">
214
+ <label for="parentId" class="font-medium">Parent Action</label>
215
+ <select
216
+ id="parentId"
217
+ class="p-inputtext w-full"
218
+ [formField]="actionForm.parentId">
219
+ <option value="">Select parent action</option>
220
+ @for (action of availableActions(); track action.id) {
221
+ <option [value]="action.id">{{ action.name }}</option>
222
+ }
223
+ </select>
224
+ </div>
225
+
226
+ <!-- Order -->
227
+ <div class="flex flex-col gap-2">
228
+ <label for="serial" class="font-medium">Display Order</label>
229
+ <input
230
+ pInputText
231
+ id="serial"
232
+ type="number"
233
+ [formField]="actionForm.serial"
234
+ placeholder="Enter display order" />
235
+ </div>
236
+
237
+ <!-- Is Active -->
238
+ <div class="flex items-end gap-2 pb-1 md:col-span-2">
239
+ <p-checkbox
240
+ [formField]="actionForm.isActive"
241
+ [binary]="true"
242
+ inputId="isActive" />
243
+ <label for="isActive">Active</label>
244
+ </div>
245
+
246
+ <!-- Permission Logic Builder -->
247
+ <div class="md:col-span-2">
248
+ <lib-logic-builder
249
+ [logic]="formModel().permissionLogic"
250
+ [actions]="allActionsForLogic()"
251
+ (logicChange)="onLogicChange($event)" />
252
+ </div>
253
+
254
+ <!-- Actions -->
255
+ <div class="flex justify-end gap-2 md:col-span-2 pt-4">
256
+ <p-button
257
+ label="Cancel"
258
+ severity="secondary"
259
+ [outlined]="true"
260
+ (onClick)="onBack()" />
261
+ <p-button
262
+ [label]="isEditMode() ? 'Update' : 'Create'"
263
+ type="submit"
264
+ [loading]="isLoading()"
265
+ [disabled]="!isFormValid() || isLoading()" />
266
+ </div>
267
+ </form>
268
+ </div>
269
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { 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: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "component", type: LogicBuilderComponent, selector: "lib-logic-builder", inputs: ["logic", "actions"], outputs: ["logicChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
270
+ }
271
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ActionFormPageComponent, decorators: [{
272
+ type: Component,
273
+ args: [{
274
+ selector: 'lib-action-form-page',
275
+ imports: [AngularModule, PrimeModule, FormField, LogicBuilderComponent],
276
+ changeDetection: ChangeDetectionStrategy.OnPush,
277
+ template: `
278
+ <div class="card">
279
+ <h3 class="text-lg sm:text-xl font-semibold mb-4">
280
+ {{ isEditMode() ? 'Edit Action' : 'New Action' }}
281
+ </h3>
282
+
283
+ <form (ngSubmit)="onSubmit()" class="grid grid-cols-1 md:grid-cols-2 gap-4">
284
+ <!-- Name -->
285
+ <div class="flex flex-col gap-2">
286
+ <label for="name" class="font-medium">Name *</label>
287
+ <input
288
+ pInputText
289
+ id="name"
290
+ [formField]="actionForm.name"
291
+ placeholder="Enter action name" />
292
+ </div>
293
+
294
+ <!-- Code -->
295
+ <div class="flex flex-col gap-2">
296
+ <label for="code" class="font-medium">Code</label>
297
+ <input
298
+ pInputText
299
+ id="code"
300
+ [formField]="actionForm.code"
301
+ placeholder="Enter action code" />
302
+ </div>
303
+
304
+ <!-- Description -->
305
+ <div class="flex flex-col gap-2">
306
+ <label for="description" class="font-medium">Description</label>
307
+ <input
308
+ pInputText
309
+ id="description"
310
+ [formField]="actionForm.description"
311
+ placeholder="Enter description" />
312
+ </div>
313
+
314
+ <!-- Action Type -->
315
+ <div class="flex flex-col gap-2">
316
+ <label for="actionType" class="font-medium">Action Type *</label>
317
+ <select
318
+ id="actionType"
319
+ class="p-inputtext w-full"
320
+ [formField]="actionForm.actionType">
321
+ @for (type of actionTypes; track type.value) {
322
+ <option [value]="type.value">{{ type.label }}</option>
323
+ }
324
+ </select>
325
+ </div>
326
+
327
+ <!-- Parent Action -->
328
+ <div class="flex flex-col gap-2">
329
+ <label for="parentId" class="font-medium">Parent Action</label>
330
+ <select
331
+ id="parentId"
332
+ class="p-inputtext w-full"
333
+ [formField]="actionForm.parentId">
334
+ <option value="">Select parent action</option>
335
+ @for (action of availableActions(); track action.id) {
336
+ <option [value]="action.id">{{ action.name }}</option>
337
+ }
338
+ </select>
339
+ </div>
340
+
341
+ <!-- Order -->
342
+ <div class="flex flex-col gap-2">
343
+ <label for="serial" class="font-medium">Display Order</label>
344
+ <input
345
+ pInputText
346
+ id="serial"
347
+ type="number"
348
+ [formField]="actionForm.serial"
349
+ placeholder="Enter display order" />
350
+ </div>
351
+
352
+ <!-- Is Active -->
353
+ <div class="flex items-end gap-2 pb-1 md:col-span-2">
354
+ <p-checkbox
355
+ [formField]="actionForm.isActive"
356
+ [binary]="true"
357
+ inputId="isActive" />
358
+ <label for="isActive">Active</label>
359
+ </div>
360
+
361
+ <!-- Permission Logic Builder -->
362
+ <div class="md:col-span-2">
363
+ <lib-logic-builder
364
+ [logic]="formModel().permissionLogic"
365
+ [actions]="allActionsForLogic()"
366
+ (logicChange)="onLogicChange($event)" />
367
+ </div>
368
+
369
+ <!-- Actions -->
370
+ <div class="flex justify-end gap-2 md:col-span-2 pt-4">
371
+ <p-button
372
+ label="Cancel"
373
+ severity="secondary"
374
+ [outlined]="true"
375
+ (onClick)="onBack()" />
376
+ <p-button
377
+ [label]="isEditMode() ? 'Update' : 'Create'"
378
+ type="submit"
379
+ [loading]="isLoading()"
380
+ [disabled]="!isFormValid() || isLoading()" />
381
+ </div>
382
+ </form>
383
+ </div>
384
+ `,
385
+ }]
386
+ }], ctorParameters: () => [] });
387
+
388
+ export { ActionFormPageComponent };
389
+ //# sourceMappingURL=flusys-ng-iam-action-form-page.component-CVN8sV-c.mjs.map