@memberjunction/ng-explorer-settings 5.42.0 → 5.44.0

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 (55) hide show
  1. package/dist/__tests__/role-management-agent-context.test.d.ts +2 -0
  2. package/dist/__tests__/role-management-agent-context.test.d.ts.map +1 -0
  3. package/dist/__tests__/role-management-agent-context.test.js +107 -0
  4. package/dist/__tests__/role-management-agent-context.test.js.map +1 -0
  5. package/dist/__tests__/user-management-agent-context.test.d.ts +2 -0
  6. package/dist/__tests__/user-management-agent-context.test.d.ts.map +1 -0
  7. package/dist/__tests__/user-management-agent-context.test.js +134 -0
  8. package/dist/__tests__/user-management-agent-context.test.js.map +1 -0
  9. package/dist/lib/application-management/application-dialog/application-dialog.component.js +176 -200
  10. package/dist/lib/application-management/application-dialog/application-dialog.component.js.map +1 -1
  11. package/dist/lib/application-management/application-management.component.d.ts +7 -0
  12. package/dist/lib/application-management/application-management.component.d.ts.map +1 -1
  13. package/dist/lib/application-management/application-management.component.js +49 -39
  14. package/dist/lib/application-management/application-management.component.js.map +1 -1
  15. package/dist/lib/application-settings/application-settings.component.js +58 -70
  16. package/dist/lib/application-settings/application-settings.component.js.map +1 -1
  17. package/dist/lib/entity-permissions/entity-permissions.component.d.ts +10 -0
  18. package/dist/lib/entity-permissions/entity-permissions.component.d.ts.map +1 -1
  19. package/dist/lib/entity-permissions/entity-permissions.component.js +32 -16
  20. package/dist/lib/entity-permissions/entity-permissions.component.js.map +1 -1
  21. package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.js +32 -33
  22. package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.js.map +1 -1
  23. package/dist/lib/module.d.ts +1 -1
  24. package/dist/lib/module.d.ts.map +1 -1
  25. package/dist/lib/module.js +13 -4
  26. package/dist/lib/module.js.map +1 -1
  27. package/dist/lib/notification-preferences/notification-preferences.component.js +30 -35
  28. package/dist/lib/notification-preferences/notification-preferences.component.js.map +1 -1
  29. package/dist/lib/role-management/role-dialog/role-dialog.component.js +26 -46
  30. package/dist/lib/role-management/role-dialog/role-dialog.component.js.map +1 -1
  31. package/dist/lib/role-management/role-management-agent-context.d.ts +96 -0
  32. package/dist/lib/role-management/role-management-agent-context.d.ts.map +1 -0
  33. package/dist/lib/role-management/role-management-agent-context.js +85 -0
  34. package/dist/lib/role-management/role-management-agent-context.js.map +1 -0
  35. package/dist/lib/role-management/role-management.component.d.ts +32 -0
  36. package/dist/lib/role-management/role-management.component.d.ts.map +1 -1
  37. package/dist/lib/role-management/role-management.component.js +236 -41
  38. package/dist/lib/role-management/role-management.component.js.map +1 -1
  39. package/dist/lib/sql-logging/sql-logging.component.js +151 -184
  40. package/dist/lib/sql-logging/sql-logging.component.js.map +1 -1
  41. package/dist/lib/user-app-config/user-app-config.component.js +80 -93
  42. package/dist/lib/user-app-config/user-app-config.component.js.map +1 -1
  43. package/dist/lib/user-management/user-dialog/user-dialog.component.js +20 -21
  44. package/dist/lib/user-management/user-dialog/user-dialog.component.js.map +1 -1
  45. package/dist/lib/user-management/user-management-agent-context.d.ts +107 -0
  46. package/dist/lib/user-management/user-management-agent-context.d.ts.map +1 -0
  47. package/dist/lib/user-management/user-management-agent-context.js +132 -0
  48. package/dist/lib/user-management/user-management-agent-context.js.map +1 -0
  49. package/dist/lib/user-management/user-management.component.d.ts +35 -0
  50. package/dist/lib/user-management/user-management.component.d.ts.map +1 -1
  51. package/dist/lib/user-management/user-management.component.js +311 -71
  52. package/dist/lib/user-management/user-management.component.js.map +1 -1
  53. package/dist/lib/user-profile-settings/user-profile-settings.component.js +52 -70
  54. package/dist/lib/user-profile-settings/user-profile-settings.component.js.map +1 -1
  55. package/package.json +19 -19
@@ -3,16 +3,15 @@ import { FormBuilder, Validators } from '@angular/forms';
3
3
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
4
4
  import * as i0 from "@angular/core";
5
5
  import * as i1 from "@angular/forms";
6
+ import * as i2 from "@memberjunction/ng-ui-components";
6
7
  function RoleDialogComponent_Conditional_0_Conditional_13_Template(rf, ctx) { if (rf & 1) {
7
- i0.ɵɵelementStart(0, "div", 12)(1, "div", 31);
8
- i0.ɵɵelement(2, "i", 32);
9
- i0.ɵɵelementStart(3, "div");
10
- i0.ɵɵtext(4);
11
- i0.ɵɵelementEnd()()();
8
+ i0.ɵɵelementStart(0, "div", 12);
9
+ i0.ɵɵelement(1, "mj-alert", 31);
10
+ i0.ɵɵelementEnd();
12
11
  } if (rf & 2) {
13
12
  const ctx_r1 = i0.ɵɵnextContext(2);
14
- i0.ɵɵadvance(4);
15
- i0.ɵɵtextInterpolate(ctx_r1.error);
13
+ i0.ɵɵadvance();
14
+ i0.ɵɵproperty("Message", ctx_r1.error);
16
15
  } }
17
16
  function RoleDialogComponent_Conditional_0_Conditional_27_Conditional_2_Template(rf, ctx) { if (rf & 1) {
18
17
  i0.ɵɵtext(0, " Role name is required ");
@@ -22,7 +21,7 @@ function RoleDialogComponent_Conditional_0_Conditional_27_Conditional_3_Template
22
21
  } }
23
22
  function RoleDialogComponent_Conditional_0_Conditional_27_Template(rf, ctx) { if (rf & 1) {
24
23
  i0.ɵɵelementStart(0, "div", 21);
25
- i0.ɵɵelement(1, "i", 33);
24
+ i0.ɵɵelement(1, "i", 32);
26
25
  i0.ɵɵconditionalCreate(2, RoleDialogComponent_Conditional_0_Conditional_27_Conditional_2_Template, 1, 0);
27
26
  i0.ɵɵconditionalCreate(3, RoleDialogComponent_Conditional_0_Conditional_27_Conditional_3_Template, 1, 0);
28
27
  i0.ɵɵelementEnd();
@@ -36,61 +35,42 @@ function RoleDialogComponent_Conditional_0_Conditional_27_Template(rf, ctx) { if
36
35
  i0.ɵɵconditional(((tmp_3_0 = ctx_r1.roleForm.get("name")) == null ? null : tmp_3_0.errors == null ? null : tmp_3_0.errors["maxlength"]) ? 3 : -1);
37
36
  } }
38
37
  function RoleDialogComponent_Conditional_0_Conditional_34_Template(rf, ctx) { if (rf & 1) {
39
- i0.ɵɵelementStart(0, "div", 18)(1, "label", 34);
38
+ i0.ɵɵelementStart(0, "div", 18)(1, "label", 33);
40
39
  i0.ɵɵtext(2, "Directory ID");
41
40
  i0.ɵɵelementEnd();
42
- i0.ɵɵelement(3, "input", 35);
41
+ i0.ɵɵelement(3, "input", 34);
43
42
  i0.ɵɵelementStart(4, "div", 24);
44
43
  i0.ɵɵtext(5, " External directory identifier for syncing with Active Directory or other systems. ");
45
44
  i0.ɵɵelementEnd()();
46
45
  } }
47
46
  function RoleDialogComponent_Conditional_0_Conditional_44_Template(rf, ctx) { if (rf & 1) {
48
- i0.ɵɵelementStart(0, "div", 26);
49
- i0.ɵɵelement(1, "i", 6);
50
- i0.ɵɵelementStart(2, "div")(3, "strong");
51
- i0.ɵɵtext(4, "System Role");
52
- i0.ɵɵelementEnd();
53
- i0.ɵɵelementStart(5, "p");
54
- i0.ɵɵtext(6, "This is a system-defined role. Some properties may be limited for editing to maintain system integrity.");
55
- i0.ɵɵelementEnd()()();
47
+ i0.ɵɵelement(0, "mj-alert", 26);
56
48
  } }
57
49
  function RoleDialogComponent_Conditional_0_Conditional_45_Template(rf, ctx) { if (rf & 1) {
58
- i0.ɵɵelementStart(0, "div", 27);
59
- i0.ɵɵelement(1, "i", 36);
60
- i0.ɵɵelementStart(2, "div")(3, "strong");
61
- i0.ɵɵtext(4, "Custom Role");
62
- i0.ɵɵelementEnd();
63
- i0.ɵɵelementStart(5, "p");
64
- i0.ɵɵtext(6, "This is a custom role that can be fully configured and modified.");
65
- i0.ɵɵelementEnd()()();
50
+ i0.ɵɵelement(0, "mj-alert", 27);
66
51
  } }
67
52
  function RoleDialogComponent_Conditional_0_Conditional_46_Template(rf, ctx) { if (rf & 1) {
68
53
  i0.ɵɵelementStart(0, "div", 12)(1, "div", 13)(2, "div", 14)(3, "h3", 15);
69
- i0.ɵɵelement(4, "i", 37);
54
+ i0.ɵɵelement(4, "i", 35);
70
55
  i0.ɵɵtext(5, " Permissions ");
71
56
  i0.ɵɵelementEnd();
72
57
  i0.ɵɵelementStart(6, "p", 16);
73
58
  i0.ɵɵtext(7, "Manage role permissions and access rights");
74
59
  i0.ɵɵelementEnd()();
75
- i0.ɵɵelementStart(8, "div", 17)(9, "div", 27);
76
- i0.ɵɵelement(10, "i", 38);
77
- i0.ɵɵelementStart(11, "div")(12, "strong");
78
- i0.ɵɵtext(13, "Permission Management");
60
+ i0.ɵɵelementStart(8, "div", 17)(9, "mj-alert", 36);
61
+ i0.ɵɵtext(10, " Role permissions can be managed in the ");
62
+ i0.ɵɵelementStart(11, "strong");
63
+ i0.ɵɵtext(12, "Permissions");
79
64
  i0.ɵɵelementEnd();
80
- i0.ɵɵelementStart(14, "p");
81
- i0.ɵɵtext(15, "Role permissions can be managed in the ");
82
- i0.ɵɵelementStart(16, "strong");
83
- i0.ɵɵtext(17, "Permissions");
84
- i0.ɵɵelementEnd();
85
- i0.ɵɵtext(18, " tab of the settings dashboard. Use that interface to configure specific access rights for this role.");
86
- i0.ɵɵelementEnd()()()()()();
65
+ i0.ɵɵtext(13, " tab of the settings dashboard. Use that interface to configure specific access rights for this role. ");
66
+ i0.ɵɵelementEnd()()()();
87
67
  } }
88
68
  function RoleDialogComponent_Conditional_0_Conditional_49_Template(rf, ctx) { if (rf & 1) {
89
- i0.ɵɵelement(0, "i", 39);
69
+ i0.ɵɵelement(0, "i", 37);
90
70
  i0.ɵɵtext(1, " Saving... ");
91
71
  } }
92
72
  function RoleDialogComponent_Conditional_0_Conditional_50_Template(rf, ctx) { if (rf & 1) {
93
- i0.ɵɵelement(0, "i", 40);
73
+ i0.ɵɵelement(0, "i", 38);
94
74
  i0.ɵɵtext(1);
95
75
  } if (rf & 2) {
96
76
  const ctx_r1 = i0.ɵɵnextContext(2);
@@ -116,7 +96,7 @@ function RoleDialogComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
116
96
  i0.ɵɵelementEnd()();
117
97
  i0.ɵɵelementStart(11, "div", 10)(12, "form", 11);
118
98
  i0.ɵɵlistener("ngSubmit", function RoleDialogComponent_Conditional_0_Template_form_ngSubmit_12_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSubmit()); });
119
- i0.ɵɵconditionalCreate(13, RoleDialogComponent_Conditional_0_Conditional_13_Template, 5, 1, "div", 12);
99
+ i0.ɵɵconditionalCreate(13, RoleDialogComponent_Conditional_0_Conditional_13_Template, 2, 1, "div", 12);
120
100
  i0.ɵɵelementStart(14, "div", 12)(15, "div", 13)(16, "div", 14)(17, "h3", 15);
121
101
  i0.ɵɵelement(18, "i", 6);
122
102
  i0.ɵɵtext(19, " Role Information ");
@@ -147,9 +127,9 @@ function RoleDialogComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
147
127
  i0.ɵɵtext(42, "System or custom role classification");
148
128
  i0.ɵɵelementEnd()();
149
129
  i0.ɵɵelementStart(43, "div", 17);
150
- i0.ɵɵconditionalCreate(44, RoleDialogComponent_Conditional_0_Conditional_44_Template, 7, 0, "div", 26)(45, RoleDialogComponent_Conditional_0_Conditional_45_Template, 7, 0, "div", 27);
130
+ i0.ɵɵconditionalCreate(44, RoleDialogComponent_Conditional_0_Conditional_44_Template, 1, 0, "mj-alert", 26)(45, RoleDialogComponent_Conditional_0_Conditional_45_Template, 1, 0, "mj-alert", 27);
151
131
  i0.ɵɵelementEnd()()();
152
- i0.ɵɵconditionalCreate(46, RoleDialogComponent_Conditional_0_Conditional_46_Template, 19, 0, "div", 12);
132
+ i0.ɵɵconditionalCreate(46, RoleDialogComponent_Conditional_0_Conditional_46_Template, 14, 0, "div", 12);
153
133
  i0.ɵɵelementStart(47, "div", 28)(48, "button", 29);
154
134
  i0.ɵɵconditionalCreate(49, RoleDialogComponent_Conditional_0_Conditional_49_Template, 2, 0)(50, RoleDialogComponent_Conditional_0_Conditional_50_Template, 2, 1);
155
135
  i0.ɵɵelementEnd();
@@ -317,15 +297,15 @@ export class RoleDialogComponent extends BaseAngularComponent {
317
297
  static ɵfac = function RoleDialogComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || RoleDialogComponent)(); };
318
298
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RoleDialogComponent, selectors: [["mj-role-dialog"]], hostBindings: function RoleDialogComponent_HostBindings(rf, ctx) { if (rf & 1) {
319
299
  i0.ɵɵlistener("keydown.escape", function RoleDialogComponent_keydown_escape_HostBindingHandler($event) { return ctx.onEscapeKey($event); }, i0.ɵɵresolveDocument);
320
- } }, inputs: { data: "data", visible: "visible" }, outputs: { result: "result" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 1, vars: 1, consts: [[1, "modal-backdrop"], [1, "modal-backdrop", 3, "click"], [1, "modal-dialog", 3, "click"], [1, "modal-header"], [1, "dialog-header"], [1, "dialog-title"], [1, "fa-solid", "fa-shield-halved"], [1, "dialog-subtitle"], ["type", "button", "aria-label", "Close dialog", 1, "modal-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "modal-body"], [3, "ngSubmit", "formGroup"], [1, "content-section"], [1, "form-section"], [1, "section-header"], [1, "section-title"], [1, "section-description"], [1, "form-grid"], [1, "form-field"], ["for", "name", 1, "field-label", "required"], ["id", "name", "type", "text", "formControlName", "name", "placeholder", "Sales Manager", 1, "field-input"], [1, "field-error"], ["for", "description", 1, "field-label"], ["id", "description", "formControlName", "description", "placeholder", "Describe the role's purpose and responsibilities...", "rows", "4", 1, "field-textarea"], [1, "field-hint"], [1, "fa-solid", "fa-cog"], [1, "alert", "alert-warning"], [1, "alert", "alert-info"], [1, "modal-footer"], ["type", "submit", 1, "btn", "btn-primary", 3, "disabled"], ["type", "button", 1, "btn", "btn-secondary", 3, "click"], [1, "alert", "alert-error"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "fa-solid", "fa-exclamation-circle"], ["for", "directoryId", 1, "field-label"], ["id", "directoryId", "type", "text", "formControlName", "directoryId", "placeholder", "External directory identifier", 1, "field-input"], [1, "fa-solid", "fa-user-tag"], [1, "fa-solid", "fa-key"], [1, "fa-solid", "fa-info-circle"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-save"]], template: function RoleDialogComponent_Template(rf, ctx) { if (rf & 1) {
300
+ } }, inputs: { data: "data", visible: "visible" }, outputs: { result: "result" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 1, vars: 1, consts: [[1, "modal-backdrop"], [1, "modal-backdrop", 3, "click"], [1, "modal-dialog", 3, "click"], [1, "modal-header"], [1, "dialog-header"], [1, "dialog-title"], [1, "fa-solid", "fa-shield-halved"], [1, "dialog-subtitle"], ["type", "button", "aria-label", "Close dialog", 1, "modal-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "modal-body"], [3, "ngSubmit", "formGroup"], [1, "content-section"], [1, "form-section"], [1, "section-header"], [1, "section-title"], [1, "section-description"], [1, "form-grid"], [1, "form-field"], ["for", "name", 1, "field-label", "required"], ["id", "name", "type", "text", "formControlName", "name", "placeholder", "Sales Manager", 1, "field-input"], [1, "field-error"], ["for", "description", 1, "field-label"], ["id", "description", "formControlName", "description", "placeholder", "Describe the role's purpose and responsibilities...", "rows", "4", 1, "field-textarea"], [1, "field-hint"], [1, "fa-solid", "fa-cog"], ["Variant", "warning", "Title", "System Role", "Message", "This is a system-defined role. Some properties may be limited for editing to maintain system integrity."], ["Variant", "info", "Title", "Custom Role", "Message", "This is a custom role that can be fully configured and modified."], [1, "modal-footer"], ["type", "submit", 1, "btn", "btn-primary", 3, "disabled"], ["type", "button", 1, "btn", "btn-secondary", 3, "click"], ["Variant", "error", 3, "Message"], [1, "fa-solid", "fa-exclamation-circle"], ["for", "directoryId", 1, "field-label"], ["id", "directoryId", "type", "text", "formControlName", "directoryId", "placeholder", "External directory identifier", 1, "field-input"], [1, "fa-solid", "fa-key"], ["Variant", "info", "Title", "Permission Management"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-save"]], template: function RoleDialogComponent_Template(rf, ctx) { if (rf & 1) {
321
301
  i0.ɵɵconditionalCreate(0, RoleDialogComponent_Conditional_0_Template, 54, 12, "div", 0);
322
302
  } if (rf & 2) {
323
303
  i0.ɵɵconditional(ctx.visible ? 0 : -1);
324
- } }, dependencies: [i1.ɵNgNoValidate, i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgControlStatusGroup, i1.FormGroupDirective, i1.FormControlName], styles: ["/* ============================================\n Role Dialog Component\n Uses MJ Design Tokens (--mj-*)\n ============================================ */\n\n/* ============================================\n Modal Backdrop & Container\n ============================================ */\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1050;\n padding: 1rem;\n animation: fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.modal-dialog {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n width: 100%;\n max-width: 850px;\n max-height: calc(100vh - 2rem);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: scale(0.95) translateY(20px);\n }\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n\n/* ============================================\n Modal Header (MJ Blue)\n ============================================ */\n\n.modal-header {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n padding: 24px 32px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1rem;\n border-radius: var(--mj-radius-2xl) var(--mj-radius-2xl) 0 0;\n flex-shrink: 0;\n}\n\n.dialog-header {\n flex: 1;\n min-width: 0;\n}\n\n.dialog-title {\n margin: 0;\n font-size: 1.5rem;\n font-weight: 600;\n line-height: 1.3;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n color: var(--mj-brand-on-primary);\n}\n\n.dialog-title i {\n font-size: 1.25rem;\n opacity: 0.95;\n}\n\n.dialog-subtitle {\n margin: 0.5rem 0 0 0;\n font-size: 0.875rem;\n opacity: 0.9;\n font-weight: 400;\n line-height: 1.4;\n}\n\n.modal-close {\n background: transparent;\n border: none;\n color: var(--mj-brand-on-primary);\n cursor: pointer;\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 40px;\n height: 40px;\n flex-shrink: 0;\n}\n\n.modal-close:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.modal-close:active {\n background: rgba(255, 255, 255, 0.2);\n transform: scale(0.95);\n}\n\n.modal-close i {\n font-size: 1.25rem;\n}\n\n/* ============================================\n Modal Body\n ============================================ */\n\n.modal-body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 0;\n background: var(--mj-bg-page);\n}\n\n.modal-body::-webkit-scrollbar {\n width: 8px;\n}\n\n.modal-body::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-sunken);\n}\n\n.modal-body::-webkit-scrollbar-thumb {\n background: var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n}\n\n.modal-body::-webkit-scrollbar-thumb:hover {\n background: var(--mj-color-neutral-400);\n}\n\n/* ============================================\n Form Structure\n ============================================ */\n\n.content-section {\n padding: 1.5rem 2rem;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.content-section:last-child {\n border-bottom: none;\n}\n\n.form-section {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.section-header {\n margin-bottom: 1rem;\n}\n\n.section-title {\n margin: 0 0 0.5rem 0;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.75rem;\n}\n\n.section-title i {\n font-size: 1.25rem;\n color: var(--mj-brand-primary);\n}\n\n.section-description {\n margin: 0;\n font-size: 0.875rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n/* ============================================\n Form Grid Layout\n ============================================ */\n\n.form-grid {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n/* ============================================\n Form Fields\n ============================================ */\n\n.form-field {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.field-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.field-label.required::after {\n content: '*';\n color: var(--mj-status-error);\n margin-left: 0.25rem;\n}\n\n.field-input,\n.field-textarea {\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 0.875rem 1rem;\n font-size: 1rem;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: var(--mj-bg-page);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 100%;\n box-sizing: border-box;\n}\n\n.field-input:hover:not(:disabled),\n.field-textarea:hover:not(:disabled) {\n border-color: var(--mj-color-neutral-400);\n}\n\n.field-input:focus,\n.field-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n}\n\n.field-input:disabled,\n.field-textarea:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.field-input.error,\n.field-textarea.error {\n border-color: var(--mj-status-error);\n}\n\n.field-input.error:focus,\n.field-textarea.error:focus {\n box-shadow: 0 0 0 3px rgba(211, 47, 47, 0.2);\n}\n\n.field-textarea {\n resize: vertical;\n min-height: 100px;\n line-height: 1.5;\n}\n\n.field-hint {\n font-size: 0.75rem;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n margin-top: 0.25rem;\n}\n\n.field-error {\n font-size: 0.75rem;\n color: var(--mj-status-error);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n}\n\n.field-error i {\n font-size: 0.875rem;\n}\n\n/* ============================================\n Alert Messages\n ============================================ */\n\n.alert {\n display: flex;\n align-items: flex-start;\n gap: 1rem;\n padding: 1rem 1.25rem;\n border-radius: var(--mj-radius-md);\n border: 1px solid;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.alert i {\n font-size: 1.25rem;\n flex-shrink: 0;\n margin-top: 0.125rem;\n}\n\n.alert > div {\n flex: 1;\n min-width: 0;\n}\n\n.alert strong {\n display: block;\n font-weight: 600;\n margin-bottom: 0.25rem;\n}\n\n.alert p {\n margin: 0;\n}\n\n/* Error Alert (Red) */\n.alert-error {\n background: var(--mj-color-error-100);\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n.alert-error i {\n color: var(--mj-status-error);\n}\n\n/* Warning Alert (Orange) */\n.alert-warning {\n background: var(--mj-color-warning-50);\n border-color: var(--mj-color-warning-500);\n color: var(--mj-color-warning-800);\n}\n\n.alert-warning i {\n color: var(--mj-color-warning-500);\n}\n\n/* Info Alert (Blue) */\n.alert-info {\n background: var(--mj-brand-accent-subtle);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-primary);\n}\n\n.alert-info i {\n color: var(--mj-brand-primary);\n}\n\n/* ============================================\n Modal Footer (Buttons)\n ============================================ */\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 1rem;\n padding: 1.5rem 2rem;\n background: var(--mj-bg-surface-sunken);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n/* ============================================\n Buttons\n ============================================ */\n\n.btn {\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 500;\n border-radius: var(--mj-radius-full);\n border: none;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n font-family: inherit;\n text-transform: none;\n letter-spacing: 0.01em;\n}\n\n.btn:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.btn:active:not(:disabled) {\n transform: scale(0.98);\n}\n\n.btn i {\n font-size: 1rem;\n}\n\n.btn i.fa-spinner {\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Primary Button (MJ Blue - Dark Button) */\n.btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-light);\n box-shadow: var(--mj-shadow-md);\n}\n\n.btn-primary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n box-shadow: none;\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Secondary Button (Light Button) */\n.btn-secondary {\n background: var(--mj-bg-page);\n color: var(--mj-brand-primary);\n border: 1px solid var(--mj-color-neutral-400);\n box-shadow: none;\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-secondary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-color: var(--mj-border-default);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* ============================================\n Responsive Design\n ============================================ */\n\n@media (max-width: 768px) {\n .modal-backdrop {\n padding: 0;\n }\n\n .modal-dialog {\n max-width: 100%;\n max-height: 100vh;\n border-radius: 0;\n animation: slideInMobile 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n @keyframes slideInMobile {\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .modal-header {\n padding: 20px 16px;\n border-radius: 0;\n }\n\n .dialog-title {\n font-size: 1.25rem;\n }\n\n .dialog-subtitle {\n font-size: 0.8125rem;\n }\n\n .content-section {\n padding: 1.25rem 1rem;\n }\n\n .section-title {\n font-size: 1.125rem;\n }\n\n .modal-footer {\n padding: 1rem;\n flex-direction: column;\n }\n\n .btn {\n width: 100%;\n justify-content: center;\n }\n}\n\n@media (max-width: 480px) {\n .modal-header {\n padding: 16px 12px;\n }\n\n .dialog-title {\n font-size: 1.125rem;\n gap: 0.5rem;\n }\n\n .dialog-title i {\n font-size: 1rem;\n }\n\n .dialog-subtitle {\n font-size: 0.75rem;\n margin-top: 0.25rem;\n }\n\n .content-section {\n padding: 1rem 0.75rem;\n }\n\n .section-title {\n font-size: 1rem;\n gap: 0.5rem;\n }\n\n .field-input,\n .field-textarea {\n padding: 0.75rem;\n font-size: 0.875rem;\n }\n\n .modal-footer {\n padding: 0.75rem;\n }\n\n .btn {\n padding: 0.625rem 1.25rem;\n font-size: 0.8125rem;\n }\n}\n\n/* ============================================\n Accessibility\n ============================================ */\n\n/* Focus Visible Styles */\n*:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n/* Reduced Motion Support */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n/* High Contrast Mode Support */\n@media (prefers-contrast: high) {\n .modal-dialog {\n border: 2px solid var(--mj-border-default);\n }\n\n .btn {\n border: 2px solid currentColor;\n }\n\n .field-input,\n .field-textarea {\n border-width: 3px;\n }\n}\n\n/* ============================================\n Print Styles\n ============================================ */\n\n@media print {\n .modal-backdrop {\n display: none;\n }\n}\n"], encapsulation: 2 });
304
+ } }, dependencies: [i1.ɵNgNoValidate, i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgControlStatusGroup, i1.FormGroupDirective, i1.FormControlName, i2.MJAlertComponent], styles: ["/* ============================================\n Role Dialog Component\n Uses MJ Design Tokens (--mj-*)\n ============================================ */\n\n/* ============================================\n Modal Backdrop & Container\n ============================================ */\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1050;\n padding: 1rem;\n animation: fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.modal-dialog {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n width: 100%;\n max-width: 850px;\n max-height: calc(100vh - 2rem);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: scale(0.95) translateY(20px);\n }\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n\n/* ============================================\n Modal Header (MJ Blue)\n ============================================ */\n\n.modal-header {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n padding: 24px 32px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1rem;\n border-radius: var(--mj-radius-2xl) var(--mj-radius-2xl) 0 0;\n flex-shrink: 0;\n}\n\n.dialog-header {\n flex: 1;\n min-width: 0;\n}\n\n.dialog-title {\n margin: 0;\n font-size: 1.5rem;\n font-weight: 600;\n line-height: 1.3;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n color: var(--mj-brand-on-primary);\n}\n\n.dialog-title i {\n font-size: 1.25rem;\n opacity: 0.95;\n}\n\n.dialog-subtitle {\n margin: 0.5rem 0 0 0;\n font-size: 0.875rem;\n opacity: 0.9;\n font-weight: 400;\n line-height: 1.4;\n}\n\n.modal-close {\n background: transparent;\n border: none;\n color: var(--mj-brand-on-primary);\n cursor: pointer;\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 40px;\n height: 40px;\n flex-shrink: 0;\n}\n\n.modal-close:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.modal-close:active {\n background: rgba(255, 255, 255, 0.2);\n transform: scale(0.95);\n}\n\n.modal-close i {\n font-size: 1.25rem;\n}\n\n/* ============================================\n Modal Body\n ============================================ */\n\n.modal-body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 0;\n background: var(--mj-bg-page);\n}\n\n.modal-body::-webkit-scrollbar {\n width: 8px;\n}\n\n.modal-body::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-sunken);\n}\n\n.modal-body::-webkit-scrollbar-thumb {\n background: var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n}\n\n.modal-body::-webkit-scrollbar-thumb:hover {\n background: var(--mj-color-neutral-400);\n}\n\n/* ============================================\n Form Structure\n ============================================ */\n\n.content-section {\n padding: 1.5rem 2rem;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.content-section:last-child {\n border-bottom: none;\n}\n\n.form-section {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.section-header {\n margin-bottom: 1rem;\n}\n\n.section-title {\n margin: 0 0 0.5rem 0;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.75rem;\n}\n\n.section-title i {\n font-size: 1.25rem;\n color: var(--mj-brand-primary);\n}\n\n.section-description {\n margin: 0;\n font-size: 0.875rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n/* ============================================\n Form Grid Layout\n ============================================ */\n\n.form-grid {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n/* ============================================\n Form Fields\n ============================================ */\n\n.form-field {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.field-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.field-label.required::after {\n content: '*';\n color: var(--mj-status-error);\n margin-left: 0.25rem;\n}\n\n.field-input,\n.field-textarea {\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 0.875rem 1rem;\n font-size: 1rem;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: var(--mj-bg-page);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 100%;\n box-sizing: border-box;\n}\n\n.field-input:hover:not(:disabled),\n.field-textarea:hover:not(:disabled) {\n border-color: var(--mj-color-neutral-400);\n}\n\n.field-input:focus,\n.field-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-focus-ring);\n}\n\n.field-input:disabled,\n.field-textarea:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.field-input.error,\n.field-textarea.error {\n border-color: var(--mj-status-error);\n}\n\n.field-input.error:focus,\n.field-textarea.error:focus {\n --mj-ring-color: var(--mj-status-error);\n box-shadow: var(--mj-focus-ring);\n}\n\n.field-textarea {\n resize: vertical;\n min-height: 100px;\n line-height: 1.5;\n}\n\n.field-hint {\n font-size: 0.75rem;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n margin-top: 0.25rem;\n}\n\n.field-error {\n font-size: 0.75rem;\n color: var(--mj-status-error);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n}\n\n.field-error i {\n font-size: 0.875rem;\n}\n\n/* ============================================\n Modal Footer (Buttons)\n ============================================ */\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 1rem;\n padding: 1.5rem 2rem;\n background: var(--mj-bg-surface-sunken);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n/* ============================================\n Buttons\n ============================================ */\n\n.btn {\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 500;\n border-radius: var(--mj-radius-full);\n border: none;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n font-family: inherit;\n text-transform: none;\n letter-spacing: 0.01em;\n}\n\n.btn:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.btn:active:not(:disabled) {\n transform: scale(0.98);\n}\n\n.btn i {\n font-size: 1rem;\n}\n\n.btn i.fa-spinner {\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Primary Button (MJ Blue - Dark Button) */\n.btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-light);\n box-shadow: var(--mj-shadow-md);\n}\n\n.btn-primary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n box-shadow: none;\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Secondary Button (Light Button) */\n.btn-secondary {\n background: var(--mj-bg-page);\n color: var(--mj-brand-primary);\n border: 1px solid var(--mj-color-neutral-400);\n box-shadow: none;\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-secondary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-color: var(--mj-border-default);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* ============================================\n Responsive Design\n ============================================ */\n\n@media (max-width: 768px) {\n .modal-backdrop {\n padding: 0;\n }\n\n .modal-dialog {\n max-width: 100%;\n max-height: 100vh;\n border-radius: 0;\n animation: slideInMobile 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n @keyframes slideInMobile {\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .modal-header {\n padding: 20px 16px;\n border-radius: 0;\n }\n\n .dialog-title {\n font-size: 1.25rem;\n }\n\n .dialog-subtitle {\n font-size: 0.8125rem;\n }\n\n .content-section {\n padding: 1.25rem 1rem;\n }\n\n .section-title {\n font-size: 1.125rem;\n }\n\n .modal-footer {\n padding: 1rem;\n flex-direction: column;\n }\n\n .btn {\n width: 100%;\n justify-content: center;\n }\n}\n\n@media (max-width: 480px) {\n .modal-header {\n padding: 16px 12px;\n }\n\n .dialog-title {\n font-size: 1.125rem;\n gap: 0.5rem;\n }\n\n .dialog-title i {\n font-size: 1rem;\n }\n\n .dialog-subtitle {\n font-size: 0.75rem;\n margin-top: 0.25rem;\n }\n\n .content-section {\n padding: 1rem 0.75rem;\n }\n\n .section-title {\n font-size: 1rem;\n gap: 0.5rem;\n }\n\n .field-input,\n .field-textarea {\n padding: 0.75rem;\n font-size: 0.875rem;\n }\n\n .modal-footer {\n padding: 0.75rem;\n }\n\n .btn {\n padding: 0.625rem 1.25rem;\n font-size: 0.8125rem;\n }\n}\n\n/* ============================================\n Accessibility\n ============================================ */\n\n/* Focus Visible Styles */\n*:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n/* Reduced Motion Support */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n/* High Contrast Mode Support */\n@media (prefers-contrast: high) {\n .modal-dialog {\n border: 2px solid var(--mj-border-default);\n }\n\n .btn {\n border: 2px solid currentColor;\n }\n\n .field-input,\n .field-textarea {\n border-width: 3px;\n }\n}\n\n/* ============================================\n Print Styles\n ============================================ */\n\n@media print {\n .modal-backdrop {\n display: none;\n }\n}\n"], encapsulation: 2 });
325
305
  }
326
306
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RoleDialogComponent, [{
327
307
  type: Component,
328
- args: [{ standalone: false, selector: 'mj-role-dialog', encapsulation: ViewEncapsulation.None, template: "<!-- MD3 Modal Dialog -->\n@if (visible) {\n <div class=\"modal-backdrop\" (click)=\"onCancel()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Modal Header with MJ Blue -->\n <div class=\"modal-header\">\n <div class=\"dialog-header\">\n <h2 class=\"dialog-title\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n {{ isEditMode ? 'Edit Role' : 'Create New Role' }}\n </h2>\n <p class=\"dialog-subtitle\">\n {{ isEditMode ? 'Update role information and permissions' : 'Add a new role to the system' }}\n </p>\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"onCancel()\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Modal Body -->\n <div class=\"modal-body\">\n <form [formGroup]=\"roleForm\" (ngSubmit)=\"onSubmit()\">\n @if (error) {\n <div class=\"content-section\">\n <div class=\"alert alert-error\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <div>{{ error }}</div>\n </div>\n </div>\n }\n <!-- Basic Information Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n Role Information\n </h3>\n <p class=\"section-description\">Define the role's basic properties and purpose</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"name\">Role Name</label>\n <input\n id=\"name\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"name\"\n placeholder=\"Sales Manager\"\n [class.error]=\"roleForm.get('name')?.invalid && roleForm.get('name')?.touched\"\n />\n @if (roleForm.get('name')?.invalid && roleForm.get('name')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (roleForm.get('name')?.errors?.['required']) {\n Role name is required\n }\n @if (roleForm.get('name')?.errors?.['maxlength']) {\n Role name cannot exceed 50 characters\n }\n </div>\n }\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"description\">Description</label>\n <textarea\n id=\"description\"\n class=\"field-textarea\"\n formControlName=\"description\"\n placeholder=\"Describe the role's purpose and responsibilities...\"\n rows=\"4\"\n ></textarea>\n <div class=\"field-hint\">\n Provide a clear description of what this role is for and what permissions it should have.\n </div>\n </div>\n @if (isEditMode) {\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"directoryId\">Directory ID</label>\n <input\n id=\"directoryId\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"directoryId\"\n placeholder=\"External directory identifier\"\n />\n <div class=\"field-hint\">\n External directory identifier for syncing with Active Directory or other systems.\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- Role Type Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-cog\"></i>\n Role Type\n </h3>\n <p class=\"section-description\">System or custom role classification</p>\n </div>\n <div class=\"form-grid\">\n @if (isSystemRole) {\n <div class=\"alert alert-warning\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n <div>\n <strong>System Role</strong>\n <p>This is a system-defined role. Some properties may be limited for editing to maintain system integrity.</p>\n </div>\n </div>\n } @else {\n <div class=\"alert alert-info\">\n <i class=\"fa-solid fa-user-tag\"></i>\n <div>\n <strong>Custom Role</strong>\n <p>This is a custom role that can be fully configured and modified.</p>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- Permissions Preview Section -->\n @if (isEditMode) {\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-key\"></i>\n Permissions\n </h3>\n <p class=\"section-description\">Manage role permissions and access rights</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"alert alert-info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <div>\n <strong>Permission Management</strong>\n <p>Role permissions can be managed in the <strong>Permissions</strong> tab of the settings dashboard. Use that interface to configure specific access rights for this role.</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n <!-- Modal Footer -->\n <div class=\"modal-footer\">\n <button\n type=\"submit\"\n class=\"btn btn-primary\"\n [disabled]=\"roleForm.invalid || isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n {{ isEditMode ? 'Update Role' : 'Create Role' }}\n }\n </button>\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n}\n", styles: ["/* ============================================\n Role Dialog Component\n Uses MJ Design Tokens (--mj-*)\n ============================================ */\n\n/* ============================================\n Modal Backdrop & Container\n ============================================ */\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1050;\n padding: 1rem;\n animation: fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.modal-dialog {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n width: 100%;\n max-width: 850px;\n max-height: calc(100vh - 2rem);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: scale(0.95) translateY(20px);\n }\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n\n/* ============================================\n Modal Header (MJ Blue)\n ============================================ */\n\n.modal-header {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n padding: 24px 32px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1rem;\n border-radius: var(--mj-radius-2xl) var(--mj-radius-2xl) 0 0;\n flex-shrink: 0;\n}\n\n.dialog-header {\n flex: 1;\n min-width: 0;\n}\n\n.dialog-title {\n margin: 0;\n font-size: 1.5rem;\n font-weight: 600;\n line-height: 1.3;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n color: var(--mj-brand-on-primary);\n}\n\n.dialog-title i {\n font-size: 1.25rem;\n opacity: 0.95;\n}\n\n.dialog-subtitle {\n margin: 0.5rem 0 0 0;\n font-size: 0.875rem;\n opacity: 0.9;\n font-weight: 400;\n line-height: 1.4;\n}\n\n.modal-close {\n background: transparent;\n border: none;\n color: var(--mj-brand-on-primary);\n cursor: pointer;\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 40px;\n height: 40px;\n flex-shrink: 0;\n}\n\n.modal-close:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.modal-close:active {\n background: rgba(255, 255, 255, 0.2);\n transform: scale(0.95);\n}\n\n.modal-close i {\n font-size: 1.25rem;\n}\n\n/* ============================================\n Modal Body\n ============================================ */\n\n.modal-body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 0;\n background: var(--mj-bg-page);\n}\n\n.modal-body::-webkit-scrollbar {\n width: 8px;\n}\n\n.modal-body::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-sunken);\n}\n\n.modal-body::-webkit-scrollbar-thumb {\n background: var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n}\n\n.modal-body::-webkit-scrollbar-thumb:hover {\n background: var(--mj-color-neutral-400);\n}\n\n/* ============================================\n Form Structure\n ============================================ */\n\n.content-section {\n padding: 1.5rem 2rem;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.content-section:last-child {\n border-bottom: none;\n}\n\n.form-section {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.section-header {\n margin-bottom: 1rem;\n}\n\n.section-title {\n margin: 0 0 0.5rem 0;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.75rem;\n}\n\n.section-title i {\n font-size: 1.25rem;\n color: var(--mj-brand-primary);\n}\n\n.section-description {\n margin: 0;\n font-size: 0.875rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n/* ============================================\n Form Grid Layout\n ============================================ */\n\n.form-grid {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n/* ============================================\n Form Fields\n ============================================ */\n\n.form-field {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.field-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.field-label.required::after {\n content: '*';\n color: var(--mj-status-error);\n margin-left: 0.25rem;\n}\n\n.field-input,\n.field-textarea {\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 0.875rem 1rem;\n font-size: 1rem;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: var(--mj-bg-page);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 100%;\n box-sizing: border-box;\n}\n\n.field-input:hover:not(:disabled),\n.field-textarea:hover:not(:disabled) {\n border-color: var(--mj-color-neutral-400);\n}\n\n.field-input:focus,\n.field-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n}\n\n.field-input:disabled,\n.field-textarea:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.field-input.error,\n.field-textarea.error {\n border-color: var(--mj-status-error);\n}\n\n.field-input.error:focus,\n.field-textarea.error:focus {\n box-shadow: 0 0 0 3px rgba(211, 47, 47, 0.2);\n}\n\n.field-textarea {\n resize: vertical;\n min-height: 100px;\n line-height: 1.5;\n}\n\n.field-hint {\n font-size: 0.75rem;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n margin-top: 0.25rem;\n}\n\n.field-error {\n font-size: 0.75rem;\n color: var(--mj-status-error);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n}\n\n.field-error i {\n font-size: 0.875rem;\n}\n\n/* ============================================\n Alert Messages\n ============================================ */\n\n.alert {\n display: flex;\n align-items: flex-start;\n gap: 1rem;\n padding: 1rem 1.25rem;\n border-radius: var(--mj-radius-md);\n border: 1px solid;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.alert i {\n font-size: 1.25rem;\n flex-shrink: 0;\n margin-top: 0.125rem;\n}\n\n.alert > div {\n flex: 1;\n min-width: 0;\n}\n\n.alert strong {\n display: block;\n font-weight: 600;\n margin-bottom: 0.25rem;\n}\n\n.alert p {\n margin: 0;\n}\n\n/* Error Alert (Red) */\n.alert-error {\n background: var(--mj-color-error-100);\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n.alert-error i {\n color: var(--mj-status-error);\n}\n\n/* Warning Alert (Orange) */\n.alert-warning {\n background: var(--mj-color-warning-50);\n border-color: var(--mj-color-warning-500);\n color: var(--mj-color-warning-800);\n}\n\n.alert-warning i {\n color: var(--mj-color-warning-500);\n}\n\n/* Info Alert (Blue) */\n.alert-info {\n background: var(--mj-brand-accent-subtle);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-primary);\n}\n\n.alert-info i {\n color: var(--mj-brand-primary);\n}\n\n/* ============================================\n Modal Footer (Buttons)\n ============================================ */\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 1rem;\n padding: 1.5rem 2rem;\n background: var(--mj-bg-surface-sunken);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n/* ============================================\n Buttons\n ============================================ */\n\n.btn {\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 500;\n border-radius: var(--mj-radius-full);\n border: none;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n font-family: inherit;\n text-transform: none;\n letter-spacing: 0.01em;\n}\n\n.btn:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.btn:active:not(:disabled) {\n transform: scale(0.98);\n}\n\n.btn i {\n font-size: 1rem;\n}\n\n.btn i.fa-spinner {\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Primary Button (MJ Blue - Dark Button) */\n.btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-light);\n box-shadow: var(--mj-shadow-md);\n}\n\n.btn-primary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n box-shadow: none;\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Secondary Button (Light Button) */\n.btn-secondary {\n background: var(--mj-bg-page);\n color: var(--mj-brand-primary);\n border: 1px solid var(--mj-color-neutral-400);\n box-shadow: none;\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-secondary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-color: var(--mj-border-default);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* ============================================\n Responsive Design\n ============================================ */\n\n@media (max-width: 768px) {\n .modal-backdrop {\n padding: 0;\n }\n\n .modal-dialog {\n max-width: 100%;\n max-height: 100vh;\n border-radius: 0;\n animation: slideInMobile 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n @keyframes slideInMobile {\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .modal-header {\n padding: 20px 16px;\n border-radius: 0;\n }\n\n .dialog-title {\n font-size: 1.25rem;\n }\n\n .dialog-subtitle {\n font-size: 0.8125rem;\n }\n\n .content-section {\n padding: 1.25rem 1rem;\n }\n\n .section-title {\n font-size: 1.125rem;\n }\n\n .modal-footer {\n padding: 1rem;\n flex-direction: column;\n }\n\n .btn {\n width: 100%;\n justify-content: center;\n }\n}\n\n@media (max-width: 480px) {\n .modal-header {\n padding: 16px 12px;\n }\n\n .dialog-title {\n font-size: 1.125rem;\n gap: 0.5rem;\n }\n\n .dialog-title i {\n font-size: 1rem;\n }\n\n .dialog-subtitle {\n font-size: 0.75rem;\n margin-top: 0.25rem;\n }\n\n .content-section {\n padding: 1rem 0.75rem;\n }\n\n .section-title {\n font-size: 1rem;\n gap: 0.5rem;\n }\n\n .field-input,\n .field-textarea {\n padding: 0.75rem;\n font-size: 0.875rem;\n }\n\n .modal-footer {\n padding: 0.75rem;\n }\n\n .btn {\n padding: 0.625rem 1.25rem;\n font-size: 0.8125rem;\n }\n}\n\n/* ============================================\n Accessibility\n ============================================ */\n\n/* Focus Visible Styles */\n*:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n/* Reduced Motion Support */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n/* High Contrast Mode Support */\n@media (prefers-contrast: high) {\n .modal-dialog {\n border: 2px solid var(--mj-border-default);\n }\n\n .btn {\n border: 2px solid currentColor;\n }\n\n .field-input,\n .field-textarea {\n border-width: 3px;\n }\n}\n\n/* ============================================\n Print Styles\n ============================================ */\n\n@media print {\n .modal-backdrop {\n display: none;\n }\n}\n"] }]
308
+ args: [{ standalone: false, selector: 'mj-role-dialog', encapsulation: ViewEncapsulation.None, template: "<!-- MD3 Modal Dialog -->\n@if (visible) {\n <div class=\"modal-backdrop\" (click)=\"onCancel()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Modal Header with MJ Blue -->\n <div class=\"modal-header\">\n <div class=\"dialog-header\">\n <h2 class=\"dialog-title\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n {{ isEditMode ? 'Edit Role' : 'Create New Role' }}\n </h2>\n <p class=\"dialog-subtitle\">\n {{ isEditMode ? 'Update role information and permissions' : 'Add a new role to the system' }}\n </p>\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"onCancel()\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Modal Body -->\n <div class=\"modal-body\">\n <form [formGroup]=\"roleForm\" (ngSubmit)=\"onSubmit()\">\n @if (error) {\n <div class=\"content-section\">\n <mj-alert Variant=\"error\" [Message]=\"error\" />\n </div>\n }\n <!-- Basic Information Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n Role Information\n </h3>\n <p class=\"section-description\">Define the role's basic properties and purpose</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"name\">Role Name</label>\n <input\n id=\"name\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"name\"\n placeholder=\"Sales Manager\"\n [class.error]=\"roleForm.get('name')?.invalid && roleForm.get('name')?.touched\"\n />\n @if (roleForm.get('name')?.invalid && roleForm.get('name')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (roleForm.get('name')?.errors?.['required']) {\n Role name is required\n }\n @if (roleForm.get('name')?.errors?.['maxlength']) {\n Role name cannot exceed 50 characters\n }\n </div>\n }\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"description\">Description</label>\n <textarea\n id=\"description\"\n class=\"field-textarea\"\n formControlName=\"description\"\n placeholder=\"Describe the role's purpose and responsibilities...\"\n rows=\"4\"\n ></textarea>\n <div class=\"field-hint\">\n Provide a clear description of what this role is for and what permissions it should have.\n </div>\n </div>\n @if (isEditMode) {\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"directoryId\">Directory ID</label>\n <input\n id=\"directoryId\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"directoryId\"\n placeholder=\"External directory identifier\"\n />\n <div class=\"field-hint\">\n External directory identifier for syncing with Active Directory or other systems.\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- Role Type Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-cog\"></i>\n Role Type\n </h3>\n <p class=\"section-description\">System or custom role classification</p>\n </div>\n <div class=\"form-grid\">\n @if (isSystemRole) {\n <mj-alert Variant=\"warning\" Title=\"System Role\" Message=\"This is a system-defined role. Some properties may be limited for editing to maintain system integrity.\" />\n } @else {\n <mj-alert Variant=\"info\" Title=\"Custom Role\" Message=\"This is a custom role that can be fully configured and modified.\" />\n }\n </div>\n </div>\n </div>\n <!-- Permissions Preview Section -->\n @if (isEditMode) {\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-key\"></i>\n Permissions\n </h3>\n <p class=\"section-description\">Manage role permissions and access rights</p>\n </div>\n <div class=\"form-grid\">\n <mj-alert Variant=\"info\" Title=\"Permission Management\">\n Role permissions can be managed in the <strong>Permissions</strong> tab of the settings dashboard. Use that interface to configure specific access rights for this role.\n </mj-alert>\n </div>\n </div>\n </div>\n }\n <!-- Modal Footer -->\n <div class=\"modal-footer\">\n <button\n type=\"submit\"\n class=\"btn btn-primary\"\n [disabled]=\"roleForm.invalid || isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n {{ isEditMode ? 'Update Role' : 'Create Role' }}\n }\n </button>\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n}\n", styles: ["/* ============================================\n Role Dialog Component\n Uses MJ Design Tokens (--mj-*)\n ============================================ */\n\n/* ============================================\n Modal Backdrop & Container\n ============================================ */\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1050;\n padding: 1rem;\n animation: fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.modal-dialog {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n width: 100%;\n max-width: 850px;\n max-height: calc(100vh - 2rem);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: scale(0.95) translateY(20px);\n }\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n\n/* ============================================\n Modal Header (MJ Blue)\n ============================================ */\n\n.modal-header {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n padding: 24px 32px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1rem;\n border-radius: var(--mj-radius-2xl) var(--mj-radius-2xl) 0 0;\n flex-shrink: 0;\n}\n\n.dialog-header {\n flex: 1;\n min-width: 0;\n}\n\n.dialog-title {\n margin: 0;\n font-size: 1.5rem;\n font-weight: 600;\n line-height: 1.3;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n color: var(--mj-brand-on-primary);\n}\n\n.dialog-title i {\n font-size: 1.25rem;\n opacity: 0.95;\n}\n\n.dialog-subtitle {\n margin: 0.5rem 0 0 0;\n font-size: 0.875rem;\n opacity: 0.9;\n font-weight: 400;\n line-height: 1.4;\n}\n\n.modal-close {\n background: transparent;\n border: none;\n color: var(--mj-brand-on-primary);\n cursor: pointer;\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 40px;\n height: 40px;\n flex-shrink: 0;\n}\n\n.modal-close:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.modal-close:active {\n background: rgba(255, 255, 255, 0.2);\n transform: scale(0.95);\n}\n\n.modal-close i {\n font-size: 1.25rem;\n}\n\n/* ============================================\n Modal Body\n ============================================ */\n\n.modal-body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 0;\n background: var(--mj-bg-page);\n}\n\n.modal-body::-webkit-scrollbar {\n width: 8px;\n}\n\n.modal-body::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-sunken);\n}\n\n.modal-body::-webkit-scrollbar-thumb {\n background: var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n}\n\n.modal-body::-webkit-scrollbar-thumb:hover {\n background: var(--mj-color-neutral-400);\n}\n\n/* ============================================\n Form Structure\n ============================================ */\n\n.content-section {\n padding: 1.5rem 2rem;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.content-section:last-child {\n border-bottom: none;\n}\n\n.form-section {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.section-header {\n margin-bottom: 1rem;\n}\n\n.section-title {\n margin: 0 0 0.5rem 0;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.75rem;\n}\n\n.section-title i {\n font-size: 1.25rem;\n color: var(--mj-brand-primary);\n}\n\n.section-description {\n margin: 0;\n font-size: 0.875rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n/* ============================================\n Form Grid Layout\n ============================================ */\n\n.form-grid {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n/* ============================================\n Form Fields\n ============================================ */\n\n.form-field {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.field-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.field-label.required::after {\n content: '*';\n color: var(--mj-status-error);\n margin-left: 0.25rem;\n}\n\n.field-input,\n.field-textarea {\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 0.875rem 1rem;\n font-size: 1rem;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: var(--mj-bg-page);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 100%;\n box-sizing: border-box;\n}\n\n.field-input:hover:not(:disabled),\n.field-textarea:hover:not(:disabled) {\n border-color: var(--mj-color-neutral-400);\n}\n\n.field-input:focus,\n.field-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-focus-ring);\n}\n\n.field-input:disabled,\n.field-textarea:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.field-input.error,\n.field-textarea.error {\n border-color: var(--mj-status-error);\n}\n\n.field-input.error:focus,\n.field-textarea.error:focus {\n --mj-ring-color: var(--mj-status-error);\n box-shadow: var(--mj-focus-ring);\n}\n\n.field-textarea {\n resize: vertical;\n min-height: 100px;\n line-height: 1.5;\n}\n\n.field-hint {\n font-size: 0.75rem;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n margin-top: 0.25rem;\n}\n\n.field-error {\n font-size: 0.75rem;\n color: var(--mj-status-error);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n}\n\n.field-error i {\n font-size: 0.875rem;\n}\n\n/* ============================================\n Modal Footer (Buttons)\n ============================================ */\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 1rem;\n padding: 1.5rem 2rem;\n background: var(--mj-bg-surface-sunken);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n/* ============================================\n Buttons\n ============================================ */\n\n.btn {\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 500;\n border-radius: var(--mj-radius-full);\n border: none;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n font-family: inherit;\n text-transform: none;\n letter-spacing: 0.01em;\n}\n\n.btn:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.btn:active:not(:disabled) {\n transform: scale(0.98);\n}\n\n.btn i {\n font-size: 1rem;\n}\n\n.btn i.fa-spinner {\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Primary Button (MJ Blue - Dark Button) */\n.btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-light);\n box-shadow: var(--mj-shadow-md);\n}\n\n.btn-primary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n box-shadow: none;\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Secondary Button (Light Button) */\n.btn-secondary {\n background: var(--mj-bg-page);\n color: var(--mj-brand-primary);\n border: 1px solid var(--mj-color-neutral-400);\n box-shadow: none;\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-secondary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-color: var(--mj-border-default);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* ============================================\n Responsive Design\n ============================================ */\n\n@media (max-width: 768px) {\n .modal-backdrop {\n padding: 0;\n }\n\n .modal-dialog {\n max-width: 100%;\n max-height: 100vh;\n border-radius: 0;\n animation: slideInMobile 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n @keyframes slideInMobile {\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .modal-header {\n padding: 20px 16px;\n border-radius: 0;\n }\n\n .dialog-title {\n font-size: 1.25rem;\n }\n\n .dialog-subtitle {\n font-size: 0.8125rem;\n }\n\n .content-section {\n padding: 1.25rem 1rem;\n }\n\n .section-title {\n font-size: 1.125rem;\n }\n\n .modal-footer {\n padding: 1rem;\n flex-direction: column;\n }\n\n .btn {\n width: 100%;\n justify-content: center;\n }\n}\n\n@media (max-width: 480px) {\n .modal-header {\n padding: 16px 12px;\n }\n\n .dialog-title {\n font-size: 1.125rem;\n gap: 0.5rem;\n }\n\n .dialog-title i {\n font-size: 1rem;\n }\n\n .dialog-subtitle {\n font-size: 0.75rem;\n margin-top: 0.25rem;\n }\n\n .content-section {\n padding: 1rem 0.75rem;\n }\n\n .section-title {\n font-size: 1rem;\n gap: 0.5rem;\n }\n\n .field-input,\n .field-textarea {\n padding: 0.75rem;\n font-size: 0.875rem;\n }\n\n .modal-footer {\n padding: 0.75rem;\n }\n\n .btn {\n padding: 0.625rem 1.25rem;\n font-size: 0.8125rem;\n }\n}\n\n/* ============================================\n Accessibility\n ============================================ */\n\n/* Focus Visible Styles */\n*:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n/* Reduced Motion Support */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n/* High Contrast Mode Support */\n@media (prefers-contrast: high) {\n .modal-dialog {\n border: 2px solid var(--mj-border-default);\n }\n\n .btn {\n border: 2px solid currentColor;\n }\n\n .field-input,\n .field-textarea {\n border-width: 3px;\n }\n}\n\n/* ============================================\n Print Styles\n ============================================ */\n\n@media print {\n .modal-backdrop {\n display: none;\n }\n}\n"] }]
329
309
  }], () => [], { data: [{
330
310
  type: Input
331
311
  }], visible: [{
@@ -1 +1 @@
1
- {"version":3,"file":"role-dialog.component.js","sourceRoot":"","sources":["../../../../src/lib/role-management/role-dialog/role-dialog.component.ts","../../../../src/lib/role-management/role-dialog/role-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, SimpleChanges, inject, HostListener, ViewEncapsulation, ChangeDetectorRef, NgZone } from '@angular/core';\n\nimport { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';\nimport { Metadata } from '@memberjunction/core';\nimport { MJRoleEntity } from '@memberjunction/core-entities';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nexport interface RoleDialogData {\n role?: MJRoleEntity;\n mode: 'create' | 'edit';\n}\n\nexport interface RoleDialogResult {\n action: 'save' | 'cancel';\n role?: MJRoleEntity;\n}\n\n@Component({\n standalone: false,\n selector: 'mj-role-dialog',\n encapsulation: ViewEncapsulation.None,\n templateUrl: './role-dialog.component.html',\n styleUrls: ['./role-dialog.component.css']\n})\nexport class RoleDialogComponent extends BaseAngularComponent implements OnInit, OnDestroy, OnChanges {\n @Input() data: RoleDialogData | null = null;\n @Input() visible = false;\n @Output() result = new EventEmitter<RoleDialogResult>();\n\n private fb = inject(FormBuilder);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n private get metadata() { return this.ProviderToUse; }\n public roleForm: FormGroup;\n public isLoading = false;\n public error: string | null = null;\n\n constructor() {\n super();\n this.roleForm = this.fb.group({\n name: ['', [Validators.required, Validators.maxLength(50)]],\n description: [''],\n directoryId: ['']\n });\n }\n\n ngOnInit(): void {\n // Initial setup\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['data'] && this.data?.role && this.isEditMode) {\n this.loadRoleData();\n }\n \n // Reset form if switching to create mode or dialog becomes visible\n if (changes['visible'] && this.visible && !this.isEditMode) {\n this.resetForm();\n }\n }\n\n ngOnDestroy(): void {\n // Cleanup if needed\n }\n\n private resetForm(): void {\n this.roleForm.reset({\n name: '',\n description: '',\n directoryId: ''\n });\n this.error = null;\n }\n\n @HostListener('document:keydown.escape', ['$event'])\n onEscapeKey(event: Event): void {\n if (this.visible) {\n this.onCancel();\n }\n }\n\n public get windowTitle(): string {\n return this.isEditMode ? 'Edit Role' : 'Create New Role';\n }\n\n public get isEditMode(): boolean {\n return this.data?.mode === 'edit';\n }\n\n public get isSystemRole(): boolean {\n if (!this.data?.role) return false;\n const systemRoleNames = ['Administrator', 'User', 'Guest', 'Developer'];\n return systemRoleNames.includes(this.data.role.Name || '');\n }\n\n private loadRoleData(): void {\n if (!this.data?.role) return;\n\n const role = this.data.role;\n this.roleForm.patchValue({\n name: role.Name,\n description: role.Description,\n directoryId: role.DirectoryID\n });\n\n // Disable name editing for system roles\n if (this.isSystemRole) {\n this.roleForm.get('name')?.disable();\n }\n }\n\n public async onSubmit(): Promise<void> {\n if (this.roleForm.invalid) {\n this.markFormGroupTouched(this.roleForm);\n return;\n }\n\n this.isLoading = true;\n this.error = null;\n\n try {\n let role: MJRoleEntity;\n\n if (this.isEditMode && this.data?.role) {\n // Edit existing role\n role = this.data.role;\n } else {\n // Create new role\n role = await this.metadata.GetEntityObject<MJRoleEntity>('MJ: Roles');\n role.NewRecord();\n }\n\n // Update role properties\n const formValue = this.roleForm.value;\n \n // Only update name if not a system role\n if (!this.isSystemRole) {\n role.Name = formValue.name;\n }\n \n role.Description = formValue.description;\n role.DirectoryID = formValue.directoryId || null;\n\n // Save role\n const saveResult = await role.Save();\n if (!saveResult) {\n throw new Error(role.LatestResult?.Message || 'Failed to save role');\n }\n\n this.result.emit({ action: 'save', role });\n\n } catch (error: unknown) {\n console.error('Error saving role:', error);\n this.ngZone.run(() => {\n this.error = error instanceof Error ? error.message : 'An unexpected error occurred';\n this.cdr.markForCheck();\n });\n } finally {\n this.ngZone.run(() => {\n this.isLoading = false;\n this.cdr.markForCheck();\n });\n }\n }\n\n public onCancel(): void {\n this.result.emit({ action: 'cancel' });\n }\n\n private markFormGroupTouched(formGroup: FormGroup): void {\n Object.keys(formGroup.controls).forEach(key => {\n const control = formGroup.get(key);\n control?.markAsTouched();\n });\n }\n}","<!-- MD3 Modal Dialog -->\n@if (visible) {\n <div class=\"modal-backdrop\" (click)=\"onCancel()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Modal Header with MJ Blue -->\n <div class=\"modal-header\">\n <div class=\"dialog-header\">\n <h2 class=\"dialog-title\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n {{ isEditMode ? 'Edit Role' : 'Create New Role' }}\n </h2>\n <p class=\"dialog-subtitle\">\n {{ isEditMode ? 'Update role information and permissions' : 'Add a new role to the system' }}\n </p>\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"onCancel()\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Modal Body -->\n <div class=\"modal-body\">\n <form [formGroup]=\"roleForm\" (ngSubmit)=\"onSubmit()\">\n @if (error) {\n <div class=\"content-section\">\n <div class=\"alert alert-error\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <div>{{ error }}</div>\n </div>\n </div>\n }\n <!-- Basic Information Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n Role Information\n </h3>\n <p class=\"section-description\">Define the role's basic properties and purpose</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"name\">Role Name</label>\n <input\n id=\"name\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"name\"\n placeholder=\"Sales Manager\"\n [class.error]=\"roleForm.get('name')?.invalid && roleForm.get('name')?.touched\"\n />\n @if (roleForm.get('name')?.invalid && roleForm.get('name')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (roleForm.get('name')?.errors?.['required']) {\n Role name is required\n }\n @if (roleForm.get('name')?.errors?.['maxlength']) {\n Role name cannot exceed 50 characters\n }\n </div>\n }\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"description\">Description</label>\n <textarea\n id=\"description\"\n class=\"field-textarea\"\n formControlName=\"description\"\n placeholder=\"Describe the role's purpose and responsibilities...\"\n rows=\"4\"\n ></textarea>\n <div class=\"field-hint\">\n Provide a clear description of what this role is for and what permissions it should have.\n </div>\n </div>\n @if (isEditMode) {\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"directoryId\">Directory ID</label>\n <input\n id=\"directoryId\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"directoryId\"\n placeholder=\"External directory identifier\"\n />\n <div class=\"field-hint\">\n External directory identifier for syncing with Active Directory or other systems.\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- Role Type Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-cog\"></i>\n Role Type\n </h3>\n <p class=\"section-description\">System or custom role classification</p>\n </div>\n <div class=\"form-grid\">\n @if (isSystemRole) {\n <div class=\"alert alert-warning\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n <div>\n <strong>System Role</strong>\n <p>This is a system-defined role. Some properties may be limited for editing to maintain system integrity.</p>\n </div>\n </div>\n } @else {\n <div class=\"alert alert-info\">\n <i class=\"fa-solid fa-user-tag\"></i>\n <div>\n <strong>Custom Role</strong>\n <p>This is a custom role that can be fully configured and modified.</p>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- Permissions Preview Section -->\n @if (isEditMode) {\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-key\"></i>\n Permissions\n </h3>\n <p class=\"section-description\">Manage role permissions and access rights</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"alert alert-info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <div>\n <strong>Permission Management</strong>\n <p>Role permissions can be managed in the <strong>Permissions</strong> tab of the settings dashboard. Use that interface to configure specific access rights for this role.</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n <!-- Modal Footer -->\n <div class=\"modal-footer\">\n <button\n type=\"submit\"\n class=\"btn btn-primary\"\n [disabled]=\"roleForm.invalid || isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n {{ isEditMode ? 'Update Role' : 'Create Role' }}\n }\n </button>\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n}\n"]}
1
+ {"version":3,"file":"role-dialog.component.js","sourceRoot":"","sources":["../../../../src/lib/role-management/role-dialog/role-dialog.component.ts","../../../../src/lib/role-management/role-dialog/role-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, SimpleChanges, inject, HostListener, ViewEncapsulation, ChangeDetectorRef, NgZone } from '@angular/core';\n\nimport { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';\nimport { Metadata } from '@memberjunction/core';\nimport { MJRoleEntity } from '@memberjunction/core-entities';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nexport interface RoleDialogData {\n role?: MJRoleEntity;\n mode: 'create' | 'edit';\n}\n\nexport interface RoleDialogResult {\n action: 'save' | 'cancel';\n role?: MJRoleEntity;\n}\n\n@Component({\n standalone: false,\n selector: 'mj-role-dialog',\n encapsulation: ViewEncapsulation.None,\n templateUrl: './role-dialog.component.html',\n styleUrls: ['./role-dialog.component.css']\n})\nexport class RoleDialogComponent extends BaseAngularComponent implements OnInit, OnDestroy, OnChanges {\n @Input() data: RoleDialogData | null = null;\n @Input() visible = false;\n @Output() result = new EventEmitter<RoleDialogResult>();\n\n private fb = inject(FormBuilder);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n private get metadata() { return this.ProviderToUse; }\n public roleForm: FormGroup;\n public isLoading = false;\n public error: string | null = null;\n\n constructor() {\n super();\n this.roleForm = this.fb.group({\n name: ['', [Validators.required, Validators.maxLength(50)]],\n description: [''],\n directoryId: ['']\n });\n }\n\n ngOnInit(): void {\n // Initial setup\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['data'] && this.data?.role && this.isEditMode) {\n this.loadRoleData();\n }\n \n // Reset form if switching to create mode or dialog becomes visible\n if (changes['visible'] && this.visible && !this.isEditMode) {\n this.resetForm();\n }\n }\n\n ngOnDestroy(): void {\n // Cleanup if needed\n }\n\n private resetForm(): void {\n this.roleForm.reset({\n name: '',\n description: '',\n directoryId: ''\n });\n this.error = null;\n }\n\n @HostListener('document:keydown.escape', ['$event'])\n onEscapeKey(event: Event): void {\n if (this.visible) {\n this.onCancel();\n }\n }\n\n public get windowTitle(): string {\n return this.isEditMode ? 'Edit Role' : 'Create New Role';\n }\n\n public get isEditMode(): boolean {\n return this.data?.mode === 'edit';\n }\n\n public get isSystemRole(): boolean {\n if (!this.data?.role) return false;\n const systemRoleNames = ['Administrator', 'User', 'Guest', 'Developer'];\n return systemRoleNames.includes(this.data.role.Name || '');\n }\n\n private loadRoleData(): void {\n if (!this.data?.role) return;\n\n const role = this.data.role;\n this.roleForm.patchValue({\n name: role.Name,\n description: role.Description,\n directoryId: role.DirectoryID\n });\n\n // Disable name editing for system roles\n if (this.isSystemRole) {\n this.roleForm.get('name')?.disable();\n }\n }\n\n public async onSubmit(): Promise<void> {\n if (this.roleForm.invalid) {\n this.markFormGroupTouched(this.roleForm);\n return;\n }\n\n this.isLoading = true;\n this.error = null;\n\n try {\n let role: MJRoleEntity;\n\n if (this.isEditMode && this.data?.role) {\n // Edit existing role\n role = this.data.role;\n } else {\n // Create new role\n role = await this.metadata.GetEntityObject<MJRoleEntity>('MJ: Roles');\n role.NewRecord();\n }\n\n // Update role properties\n const formValue = this.roleForm.value;\n \n // Only update name if not a system role\n if (!this.isSystemRole) {\n role.Name = formValue.name;\n }\n \n role.Description = formValue.description;\n role.DirectoryID = formValue.directoryId || null;\n\n // Save role\n const saveResult = await role.Save();\n if (!saveResult) {\n throw new Error(role.LatestResult?.Message || 'Failed to save role');\n }\n\n this.result.emit({ action: 'save', role });\n\n } catch (error: unknown) {\n console.error('Error saving role:', error);\n this.ngZone.run(() => {\n this.error = error instanceof Error ? error.message : 'An unexpected error occurred';\n this.cdr.markForCheck();\n });\n } finally {\n this.ngZone.run(() => {\n this.isLoading = false;\n this.cdr.markForCheck();\n });\n }\n }\n\n public onCancel(): void {\n this.result.emit({ action: 'cancel' });\n }\n\n private markFormGroupTouched(formGroup: FormGroup): void {\n Object.keys(formGroup.controls).forEach(key => {\n const control = formGroup.get(key);\n control?.markAsTouched();\n });\n }\n}","<!-- MD3 Modal Dialog -->\n@if (visible) {\n <div class=\"modal-backdrop\" (click)=\"onCancel()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Modal Header with MJ Blue -->\n <div class=\"modal-header\">\n <div class=\"dialog-header\">\n <h2 class=\"dialog-title\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n {{ isEditMode ? 'Edit Role' : 'Create New Role' }}\n </h2>\n <p class=\"dialog-subtitle\">\n {{ isEditMode ? 'Update role information and permissions' : 'Add a new role to the system' }}\n </p>\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"onCancel()\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Modal Body -->\n <div class=\"modal-body\">\n <form [formGroup]=\"roleForm\" (ngSubmit)=\"onSubmit()\">\n @if (error) {\n <div class=\"content-section\">\n <mj-alert Variant=\"error\" [Message]=\"error\" />\n </div>\n }\n <!-- Basic Information Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n Role Information\n </h3>\n <p class=\"section-description\">Define the role's basic properties and purpose</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"name\">Role Name</label>\n <input\n id=\"name\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"name\"\n placeholder=\"Sales Manager\"\n [class.error]=\"roleForm.get('name')?.invalid && roleForm.get('name')?.touched\"\n />\n @if (roleForm.get('name')?.invalid && roleForm.get('name')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (roleForm.get('name')?.errors?.['required']) {\n Role name is required\n }\n @if (roleForm.get('name')?.errors?.['maxlength']) {\n Role name cannot exceed 50 characters\n }\n </div>\n }\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"description\">Description</label>\n <textarea\n id=\"description\"\n class=\"field-textarea\"\n formControlName=\"description\"\n placeholder=\"Describe the role's purpose and responsibilities...\"\n rows=\"4\"\n ></textarea>\n <div class=\"field-hint\">\n Provide a clear description of what this role is for and what permissions it should have.\n </div>\n </div>\n @if (isEditMode) {\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"directoryId\">Directory ID</label>\n <input\n id=\"directoryId\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"directoryId\"\n placeholder=\"External directory identifier\"\n />\n <div class=\"field-hint\">\n External directory identifier for syncing with Active Directory or other systems.\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- Role Type Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-cog\"></i>\n Role Type\n </h3>\n <p class=\"section-description\">System or custom role classification</p>\n </div>\n <div class=\"form-grid\">\n @if (isSystemRole) {\n <mj-alert Variant=\"warning\" Title=\"System Role\" Message=\"This is a system-defined role. Some properties may be limited for editing to maintain system integrity.\" />\n } @else {\n <mj-alert Variant=\"info\" Title=\"Custom Role\" Message=\"This is a custom role that can be fully configured and modified.\" />\n }\n </div>\n </div>\n </div>\n <!-- Permissions Preview Section -->\n @if (isEditMode) {\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-key\"></i>\n Permissions\n </h3>\n <p class=\"section-description\">Manage role permissions and access rights</p>\n </div>\n <div class=\"form-grid\">\n <mj-alert Variant=\"info\" Title=\"Permission Management\">\n Role permissions can be managed in the <strong>Permissions</strong> tab of the settings dashboard. Use that interface to configure specific access rights for this role.\n </mj-alert>\n </div>\n </div>\n </div>\n }\n <!-- Modal Footer -->\n <div class=\"modal-footer\">\n <button\n type=\"submit\"\n class=\"btn btn-primary\"\n [disabled]=\"roleForm.invalid || isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n {{ isEditMode ? 'Update Role' : 'Create Role' }}\n }\n </button>\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n}\n"]}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * @fileoverview Pure helpers for the Role Management admin surface's AI-agent integration.
3
+ *
4
+ * 🔒 READ-ONLY / GOVERNANCE SURFACE. These helpers shape a non-sensitive snapshot of the
5
+ * role-management screen into the key-value `AgentContext` object and back agent-supplied
6
+ * role references with tolerant id→name→contains resolution. They are intentionally free of
7
+ * Angular / component dependencies so they can be unit-tested in isolation (mirrors the
8
+ * Data Explorer agent-context pattern). Permission information is surfaced as a READ-ONLY
9
+ * summary (counts only) — no grant/revoke surface, no secrets.
10
+ */
11
+ /** The three role-type filter values the surface supports. */
12
+ export declare const VALID_ROLE_TYPE_FILTERS: readonly ["all", "system", "custom"];
13
+ export type RoleTypeFilter = (typeof VALID_ROLE_TYPE_FILTERS)[number];
14
+ /**
15
+ * Upper bound on how many role names we publish in a name-list context field
16
+ * (VisibleRoleNames). Keeps the streamed note bounded; a companion total-count field reports
17
+ * the true number when the list is truncated.
18
+ */
19
+ export declare const ROLE_AGENT_CONTEXT_NAME_LIST_CAP = 25;
20
+ /** Type-guard for a role-type filter string. Keeps the FilterRolesByType tool tolerant. */
21
+ export declare function isValidRoleTypeFilter(value: unknown): value is RoleTypeFilter;
22
+ /** Cap a list of names to {@link ROLE_AGENT_CONTEXT_NAME_LIST_CAP}. Pure + deterministic. */
23
+ export declare function capRoleNames(names: readonly string[]): string[];
24
+ /** A minimal descriptor of a role (id + display name) supplied by the component. */
25
+ export interface RoleNameCandidate {
26
+ ID: string;
27
+ Name: string;
28
+ }
29
+ /** Outcome of a tolerant id→name→contains role resolution. */
30
+ export type RoleLookupResult = {
31
+ ok: true;
32
+ match: RoleNameCandidate;
33
+ } | {
34
+ ok: false;
35
+ error: string;
36
+ };
37
+ /**
38
+ * Resolve an agent-supplied role reference against the loaded roles, tolerantly:
39
+ * 1. exact ID match (case-insensitive)
40
+ * 2. exact display-name match (case-insensitive)
41
+ * 3. first case-insensitive *contains* match on name
42
+ *
43
+ * Pure + deterministic. On a miss, returns a clear error listing a sample of available role
44
+ * names so the agent can correct itself.
45
+ */
46
+ export declare function resolveRoleByIDOrName(input: string, candidates: readonly RoleNameCandidate[]): RoleLookupResult;
47
+ /**
48
+ * Read-only permission summary for the selected role, derived from the role's
49
+ * entity-permission rows. Counts only — describes the breadth of access the role has been
50
+ * granted without exposing any mutation surface.
51
+ */
52
+ export interface RolePermissionSummary {
53
+ /** Number of entities for which the role has at least one permission row. */
54
+ EntityCount: number;
55
+ /** Number of entities the role can read. */
56
+ ReadCount: number;
57
+ /** Number of entities the role can create. */
58
+ CreateCount: number;
59
+ /** Number of entities the role can update. */
60
+ UpdateCount: number;
61
+ /** Number of entities the role can delete. */
62
+ DeleteCount: number;
63
+ }
64
+ /**
65
+ * The plain, component-supplied snapshot used to build the role-management agent context.
66
+ * NON-SENSITIVE by construction — counts, filter state, and on-screen display values only.
67
+ */
68
+ export interface RoleManagementAgentContextInput {
69
+ /** Total roles loaded (unfiltered). */
70
+ TotalRoleCount: number;
71
+ /** Roles currently visible after type/search filters. */
72
+ FilteredRoleCount: number;
73
+ /** Count of system roles (across the unfiltered set). */
74
+ SystemRoleCount: number;
75
+ /** Count of custom roles (across the unfiltered set). */
76
+ CustomRoleCount: number;
77
+ /** Active role-type filter. */
78
+ TypeFilter: RoleTypeFilter;
79
+ /** Current free-text search string. */
80
+ SearchText: string;
81
+ /** ID of the currently selected (expanded) role, or null. */
82
+ SelectedRoleId: string | null;
83
+ /** Display name of the currently selected role, or null. */
84
+ SelectedRoleName: string | null;
85
+ /** Display names of the currently visible (filtered) roles, in list order. */
86
+ VisibleRoleNames: string[];
87
+ /** Read-only permission summary for the selected role, or null when none selected / not loaded. */
88
+ SelectedRolePermissions: RolePermissionSummary | null;
89
+ }
90
+ /**
91
+ * Build the agent-visible context object for Role Management. Pure (no `this`). Bounds the
92
+ * role-name list and emits a companion total-count when truncated; surfaces the read-only
93
+ * permission summary only when a role is selected and its permissions have been loaded.
94
+ */
95
+ export declare function buildRoleManagementAgentContext(input: RoleManagementAgentContextInput): Record<string, unknown>;
96
+ //# sourceMappingURL=role-management-agent-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-management-agent-context.d.ts","sourceRoot":"","sources":["../../../src/lib/role-management/role-management-agent-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,8DAA8D;AAC9D,eAAO,MAAM,uBAAuB,sCAAuC,CAAC;AAC5E,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,2FAA2F;AAC3F,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAE7E;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAE/D;AAED,oFAAoF;AACpF,MAAM,WAAW,iBAAiB;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,8DAA8D;AAC9D,MAAM,MAAM,gBAAgB,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,GACtC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,iBAAiB,EAAE,GAAG,gBAAgB,CAmB/G;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IAClC,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,+BAA+B;IAC5C,uCAAuC;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,eAAe,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,eAAe,EAAE,MAAM,CAAC;IACxB,+BAA+B;IAC/B,UAAU,EAAE,cAAc,CAAC;IAC3B,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mGAAmG;IACnG,uBAAuB,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACzD;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,+BAA+B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA0B/G"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @fileoverview Pure helpers for the Role Management admin surface's AI-agent integration.
3
+ *
4
+ * 🔒 READ-ONLY / GOVERNANCE SURFACE. These helpers shape a non-sensitive snapshot of the
5
+ * role-management screen into the key-value `AgentContext` object and back agent-supplied
6
+ * role references with tolerant id→name→contains resolution. They are intentionally free of
7
+ * Angular / component dependencies so they can be unit-tested in isolation (mirrors the
8
+ * Data Explorer agent-context pattern). Permission information is surfaced as a READ-ONLY
9
+ * summary (counts only) — no grant/revoke surface, no secrets.
10
+ */
11
+ /** The three role-type filter values the surface supports. */
12
+ export const VALID_ROLE_TYPE_FILTERS = ['all', 'system', 'custom'];
13
+ /**
14
+ * Upper bound on how many role names we publish in a name-list context field
15
+ * (VisibleRoleNames). Keeps the streamed note bounded; a companion total-count field reports
16
+ * the true number when the list is truncated.
17
+ */
18
+ export const ROLE_AGENT_CONTEXT_NAME_LIST_CAP = 25;
19
+ /** Type-guard for a role-type filter string. Keeps the FilterRolesByType tool tolerant. */
20
+ export function isValidRoleTypeFilter(value) {
21
+ return typeof value === 'string' && VALID_ROLE_TYPE_FILTERS.includes(value);
22
+ }
23
+ /** Cap a list of names to {@link ROLE_AGENT_CONTEXT_NAME_LIST_CAP}. Pure + deterministic. */
24
+ export function capRoleNames(names) {
25
+ return names.slice(0, ROLE_AGENT_CONTEXT_NAME_LIST_CAP);
26
+ }
27
+ /**
28
+ * Resolve an agent-supplied role reference against the loaded roles, tolerantly:
29
+ * 1. exact ID match (case-insensitive)
30
+ * 2. exact display-name match (case-insensitive)
31
+ * 3. first case-insensitive *contains* match on name
32
+ *
33
+ * Pure + deterministic. On a miss, returns a clear error listing a sample of available role
34
+ * names so the agent can correct itself.
35
+ */
36
+ export function resolveRoleByIDOrName(input, candidates) {
37
+ const needle = input.trim().toLowerCase();
38
+ if (!needle) {
39
+ return { ok: false, error: 'Provide a role ID or name to select.' };
40
+ }
41
+ const byId = candidates.find(c => c.ID.toLowerCase() === needle);
42
+ if (byId) {
43
+ return { ok: true, match: byId };
44
+ }
45
+ const exact = candidates.find(c => c.Name.toLowerCase() === needle);
46
+ if (exact) {
47
+ return { ok: true, match: exact };
48
+ }
49
+ const contains = candidates.find(c => c.Name.toLowerCase().includes(needle));
50
+ if (contains) {
51
+ return { ok: true, match: contains };
52
+ }
53
+ const sample = candidates.slice(0, 8).map(c => c.Name).join(', ');
54
+ return { ok: false, error: `No role matches "${input}". Available roles: ${sample || '(none loaded)'}.` };
55
+ }
56
+ /**
57
+ * Build the agent-visible context object for Role Management. Pure (no `this`). Bounds the
58
+ * role-name list and emits a companion total-count when truncated; surfaces the read-only
59
+ * permission summary only when a role is selected and its permissions have been loaded.
60
+ */
61
+ export function buildRoleManagementAgentContext(input) {
62
+ const context = {
63
+ TotalRoleCount: input.TotalRoleCount,
64
+ FilteredRoleCount: input.FilteredRoleCount,
65
+ SystemRoleCount: input.SystemRoleCount,
66
+ CustomRoleCount: input.CustomRoleCount,
67
+ TypeFilter: input.TypeFilter,
68
+ SearchText: input.SearchText,
69
+ SelectedRoleId: input.SelectedRoleId,
70
+ SelectedRoleName: input.SelectedRoleName,
71
+ };
72
+ if (input.VisibleRoleNames.length > 0) {
73
+ context['VisibleRoleNames'] = capRoleNames(input.VisibleRoleNames);
74
+ if (input.VisibleRoleNames.length > ROLE_AGENT_CONTEXT_NAME_LIST_CAP) {
75
+ context['VisibleRoleCount'] = input.VisibleRoleNames.length;
76
+ }
77
+ }
78
+ // Read-only permission summary — surfaced ONLY when a role is selected AND its
79
+ // permissions have been loaded (never fabricated zeros for an unselected role).
80
+ if (input.SelectedRolePermissions) {
81
+ context['SelectedRolePermissions'] = { ...input.SelectedRolePermissions };
82
+ }
83
+ return context;
84
+ }
85
+ //# sourceMappingURL=role-management-agent-context.js.map