verben-authentication-ui 1.0.37 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +23 -1
  2. package/esm2022/src/lib/base/core/base-data-page.facade.mjs +215 -0
  3. package/esm2022/src/lib/base/core/base-data-page.service.mjs +103 -0
  4. package/esm2022/src/lib/base/core/base-data-page.state.mjs +203 -0
  5. package/esm2022/src/lib/base/handlers/filter-handler.service.mjs +53 -0
  6. package/esm2022/src/lib/base/handlers/search-handler.service.mjs +31 -0
  7. package/esm2022/src/lib/base/handlers/sort-handler.service.mjs +49 -0
  8. package/esm2022/src/lib/base/models/api-payload.mjs +4 -0
  9. package/esm2022/src/lib/base/models/page-config.interface.mjs +2 -0
  10. package/esm2022/src/lib/base/public-api.mjs +14 -6
  11. package/esm2022/src/lib/base/utils/column-helpers.mjs +45 -0
  12. package/esm2022/src/lib/components/button/button.component.mjs +3 -3
  13. package/esm2022/src/lib/components/forgot-password/forgot-password.component.mjs +2 -2
  14. package/esm2022/src/lib/components/mail/mail.component.mjs +3 -3
  15. package/esm2022/src/lib/components/mail-validation/mail-validation.component.mjs +5 -5
  16. package/esm2022/src/lib/components/o-auth/o-auth.component.mjs +3 -3
  17. package/esm2022/src/lib/components/otp-input/otp-input.component.mjs +2 -2
  18. package/esm2022/src/lib/components/reset-password/reset-password.component.mjs +2 -2
  19. package/esm2022/src/lib/components/role-control/base-table-style.mjs +6 -6
  20. package/esm2022/src/lib/components/role-control/role-control.component.mjs +3 -3
  21. package/esm2022/src/lib/components/sign-in/sign-in.component.mjs +4 -4
  22. package/esm2022/src/lib/components/sign-up/sign-up.component.mjs +6 -6
  23. package/esm2022/src/lib/components/sso/base-table-style.mjs +6 -6
  24. package/esm2022/src/lib/components/sso/sso-form/sso-form.component.mjs +3 -3
  25. package/esm2022/src/lib/components/sso/sso.component.mjs +3 -3
  26. package/esm2022/src/lib/components/two-factor-auth-otp/two-factor-auth-otp.component.mjs +4 -4
  27. package/esm2022/src/lib/components/two-factor-auth-setup/two-factor-auth-setup.component.mjs +5 -5
  28. package/esm2022/src/lib/components/user-management/public-api.mjs +2 -1
  29. package/esm2022/src/lib/components/user-management/user-management-card/user-management-card.component.mjs +23 -0
  30. package/esm2022/src/lib/components/user-management/user-management-form/user-management-form.component.mjs +53 -91
  31. package/esm2022/src/lib/components/user-management/user-management-table/user-management-table.component.mjs +68 -0
  32. package/esm2022/src/lib/components/user-management/user-management.component.mjs +98 -509
  33. package/esm2022/src/lib/components/user-management/user-management.module.mjs +11 -80
  34. package/esm2022/src/lib/components/user-management/user-status-badge/user-status-badge.component.mjs +8 -6
  35. package/esm2022/src/lib/components/user-management/user.config.mjs +94 -0
  36. package/esm2022/src/lib/components/user-management/user.facade.mjs +118 -0
  37. package/esm2022/src/lib/components/user-management/user.service.mjs +60 -0
  38. package/esm2022/src/lib/components/user-management/user.state.mjs +33 -0
  39. package/esm2022/src/lib/components/user-request/user-request.component.mjs +8 -8
  40. package/esm2022/src/lib/components/user-request-approval/public-api.mjs +2 -2
  41. package/esm2022/src/lib/components/user-request-approval/user-access-request-status-badge/user-access-request-status-badge.component.mjs +8 -6
  42. package/esm2022/src/lib/components/user-request-approval/user-access-request.config.mjs +79 -0
  43. package/esm2022/src/lib/components/user-request-approval/user-access-request.facade.mjs +123 -0
  44. package/esm2022/src/lib/components/user-request-approval/user-access-request.service.mjs +74 -0
  45. package/esm2022/src/lib/components/user-request-approval/user-access-request.state.mjs +26 -0
  46. package/esm2022/src/lib/components/user-request-approval/user-request-approval-card/user-request-approval-card.component.mjs +23 -0
  47. package/esm2022/src/lib/components/user-request-approval/user-request-approval-form/user-request-approval-form.component.mjs +124 -0
  48. package/esm2022/src/lib/components/user-request-approval/user-request-approval-table/user-request-approval-table.component.mjs +44 -0
  49. package/esm2022/src/lib/components/user-request-approval/user-request-approval.component.mjs +101 -273
  50. package/esm2022/src/lib/components/user-request-approval/user-request-approval.module.mjs +10 -74
  51. package/fesm2022/verben-authentication-ui-src-lib-base.mjs +596 -214
  52. package/fesm2022/verben-authentication-ui-src-lib-base.mjs.map +1 -1
  53. package/fesm2022/verben-authentication-ui-src-lib-components-button.mjs +2 -2
  54. package/fesm2022/verben-authentication-ui-src-lib-components-button.mjs.map +1 -1
  55. package/fesm2022/verben-authentication-ui-src-lib-components-forgot-password.mjs +2 -2
  56. package/fesm2022/verben-authentication-ui-src-lib-components-forgot-password.mjs.map +1 -1
  57. package/fesm2022/verben-authentication-ui-src-lib-components-mail-validation.mjs +4 -4
  58. package/fesm2022/verben-authentication-ui-src-lib-components-mail-validation.mjs.map +1 -1
  59. package/fesm2022/verben-authentication-ui-src-lib-components-mail.mjs +2 -2
  60. package/fesm2022/verben-authentication-ui-src-lib-components-mail.mjs.map +1 -1
  61. package/fesm2022/verben-authentication-ui-src-lib-components-o-auth.mjs +2 -2
  62. package/fesm2022/verben-authentication-ui-src-lib-components-o-auth.mjs.map +1 -1
  63. package/fesm2022/verben-authentication-ui-src-lib-components-otp-input.mjs +2 -2
  64. package/fesm2022/verben-authentication-ui-src-lib-components-otp-input.mjs.map +1 -1
  65. package/fesm2022/verben-authentication-ui-src-lib-components-reset-password.mjs +2 -2
  66. package/fesm2022/verben-authentication-ui-src-lib-components-reset-password.mjs.map +1 -1
  67. package/fesm2022/verben-authentication-ui-src-lib-components-role-control.mjs +7 -7
  68. package/fesm2022/verben-authentication-ui-src-lib-components-role-control.mjs.map +1 -1
  69. package/fesm2022/verben-authentication-ui-src-lib-components-sign-in.mjs +3 -3
  70. package/fesm2022/verben-authentication-ui-src-lib-components-sign-in.mjs.map +1 -1
  71. package/fesm2022/verben-authentication-ui-src-lib-components-sign-up.mjs +5 -5
  72. package/fesm2022/verben-authentication-ui-src-lib-components-sign-up.mjs.map +1 -1
  73. package/fesm2022/verben-authentication-ui-src-lib-components-sso.mjs +9 -9
  74. package/fesm2022/verben-authentication-ui-src-lib-components-sso.mjs.map +1 -1
  75. package/fesm2022/verben-authentication-ui-src-lib-components-two-factor-auth-otp.mjs +3 -3
  76. package/fesm2022/verben-authentication-ui-src-lib-components-two-factor-auth-otp.mjs.map +1 -1
  77. package/fesm2022/verben-authentication-ui-src-lib-components-two-factor-auth-setup.mjs +4 -4
  78. package/fesm2022/verben-authentication-ui-src-lib-components-two-factor-auth-setup.mjs.map +1 -1
  79. package/fesm2022/verben-authentication-ui-src-lib-components-user-management.mjs +468 -831
  80. package/fesm2022/verben-authentication-ui-src-lib-components-user-management.mjs.map +1 -1
  81. package/fesm2022/verben-authentication-ui-src-lib-components-user-request-approval.mjs +509 -952
  82. package/fesm2022/verben-authentication-ui-src-lib-components-user-request-approval.mjs.map +1 -1
  83. package/fesm2022/verben-authentication-ui-src-lib-components-user-request.mjs +7 -7
  84. package/fesm2022/verben-authentication-ui-src-lib-components-user-request.mjs.map +1 -1
  85. package/package.json +36 -42
  86. package/src/lib/base/README.md +99 -0
  87. package/src/lib/base/core/base-data-page.facade.d.ts +65 -0
  88. package/src/lib/base/core/base-data-page.service.d.ts +43 -0
  89. package/src/lib/base/core/base-data-page.state.d.ts +68 -0
  90. package/src/lib/base/handlers/filter-handler.service.d.ts +13 -0
  91. package/src/lib/base/handlers/search-handler.service.d.ts +20 -0
  92. package/src/lib/base/handlers/sort-handler.service.d.ts +13 -0
  93. package/src/lib/base/models/api-payload.d.ts +2 -0
  94. package/src/lib/base/models/page-config.interface.d.ts +21 -0
  95. package/src/lib/base/public-api.d.ts +9 -5
  96. package/src/lib/base/utils/column-helpers.d.ts +16 -0
  97. package/src/lib/components/user-management/public-api.d.ts +1 -0
  98. package/src/lib/components/user-management/user-management-card/user-management-card.component.d.ts +11 -0
  99. package/src/lib/components/user-management/user-management-form/user-management-form.component.d.ts +12 -14
  100. package/src/lib/components/user-management/user-management-table/user-management-table.component.d.ts +30 -0
  101. package/src/lib/components/user-management/user-management.component.d.ts +28 -124
  102. package/src/lib/components/user-management/user-management.module.d.ts +8 -8
  103. package/src/lib/components/user-management/user-status-badge/user-status-badge.component.d.ts +1 -1
  104. package/src/lib/components/user-management/user.config.d.ts +13 -0
  105. package/src/lib/components/user-management/user.facade.d.ts +42 -0
  106. package/src/lib/components/user-management/user.service.d.ts +26 -0
  107. package/src/lib/components/user-management/user.state.d.ts +19 -0
  108. package/src/lib/components/user-request-approval/public-api.d.ts +1 -1
  109. package/src/lib/components/user-request-approval/user-access-request-status-badge/user-access-request-status-badge.component.d.ts +1 -1
  110. package/src/lib/components/user-request-approval/user-access-request.config.d.ts +13 -0
  111. package/src/lib/components/user-request-approval/user-access-request.facade.d.ts +36 -0
  112. package/src/lib/components/user-request-approval/user-access-request.service.d.ts +30 -0
  113. package/src/lib/components/user-request-approval/user-access-request.state.d.ts +16 -0
  114. package/src/lib/components/user-request-approval/user-request-approval-card/user-request-approval-card.component.d.ts +11 -0
  115. package/src/lib/components/user-request-approval/user-request-approval-form/user-request-approval-form.component.d.ts +47 -0
  116. package/src/lib/components/user-request-approval/user-request-approval-table/user-request-approval-table.component.d.ts +20 -0
  117. package/src/lib/components/user-request-approval/user-request-approval.component.d.ts +29 -68
  118. package/src/lib/components/user-request-approval/user-request-approval.module.d.ts +7 -6
  119. package/esm2022/src/lib/base/base-data.component.mjs +0 -80
  120. package/esm2022/src/lib/base/base-data.facade.mjs +0 -130
  121. package/esm2022/src/lib/base/base-data.service.mjs +0 -21
  122. package/esm2022/src/lib/base/base-data.state.mjs +0 -74
  123. package/esm2022/src/lib/base/models.mjs +0 -2
  124. package/esm2022/src/lib/components/user-management/base-table-style.mjs +0 -53
  125. package/esm2022/src/lib/components/user-management/services/user-management.service.mjs +0 -110
  126. package/esm2022/src/lib/components/user-management/user-management.columns.mjs +0 -53
  127. package/esm2022/src/lib/components/user-request-approval/access-request.columns.mjs +0 -29
  128. package/esm2022/src/lib/components/user-request-approval/base-table-style.mjs +0 -53
  129. package/esm2022/src/lib/components/user-request-approval/facades/user-access-request.facade.mjs +0 -260
  130. package/esm2022/src/lib/components/user-request-approval/helper.mjs +0 -56
  131. package/esm2022/src/lib/components/user-request-approval/services/user-access-request.service.mjs +0 -92
  132. package/esm2022/src/lib/components/user-request-approval/states/user-access-request.state.mjs +0 -92
  133. package/esm2022/src/lib/components/user-request-approval/user-request-form/user-request-form.component.mjs +0 -105
  134. package/esm2022/src/lib/components/user-request-process/index.mjs +0 -2
  135. package/esm2022/src/lib/components/user-request-process/public-api.mjs +0 -3
  136. package/esm2022/src/lib/components/user-request-process/user-request-process.component.mjs +0 -11
  137. package/esm2022/src/lib/components/user-request-process/user-request-process.module.mjs +0 -18
  138. package/esm2022/src/lib/components/user-request-process/verben-authentication-ui-src-lib-components-user-request-process.mjs +0 -5
  139. package/fesm2022/verben-authentication-ui-src-lib-components-user-request-process.mjs +0 -34
  140. package/fesm2022/verben-authentication-ui-src-lib-components-user-request-process.mjs.map +0 -1
  141. package/src/lib/base/base-data.component.d.ts +0 -32
  142. package/src/lib/base/base-data.facade.d.ts +0 -27
  143. package/src/lib/base/base-data.service.d.ts +0 -14
  144. package/src/lib/base/base-data.state.d.ts +0 -28
  145. package/src/lib/base/models.d.ts +0 -11
  146. package/src/lib/components/user-management/base-table-style.d.ts +0 -1
  147. package/src/lib/components/user-management/services/user-management.service.d.ts +0 -44
  148. package/src/lib/components/user-management/user-management.columns.d.ts +0 -3
  149. package/src/lib/components/user-request-approval/access-request.columns.d.ts +0 -3
  150. package/src/lib/components/user-request-approval/base-table-style.d.ts +0 -1
  151. package/src/lib/components/user-request-approval/facades/user-access-request.facade.d.ts +0 -39
  152. package/src/lib/components/user-request-approval/helper.d.ts +0 -7
  153. package/src/lib/components/user-request-approval/services/user-access-request.service.d.ts +0 -64
  154. package/src/lib/components/user-request-approval/states/user-access-request.state.d.ts +0 -35
  155. package/src/lib/components/user-request-approval/user-request-form/user-request-form.component.d.ts +0 -34
  156. package/src/lib/components/user-request-process/index.d.ts +0 -1
  157. package/src/lib/components/user-request-process/public-api.d.ts +0 -2
  158. package/src/lib/components/user-request-process/user-request-process.component.d.ts +0 -5
  159. package/src/lib/components/user-request-process/user-request-process.module.d.ts +0 -7
@@ -1,16 +1,69 @@
1
- import * as i0 from '@angular/core';
2
- import { Injectable, EventEmitter, Component, Input, Output, signal, computed, ChangeDetectionStrategy, ViewChild, NgModule } from '@angular/core';
3
- import { SearchPropertyValueType, SortDirection, SearchOperator, SearchPropertySign, UserAccessRequestStatus, ErrorResponse } from 'verben-authentication-ui/src/lib/models';
4
- import * as i4 from 'verben-ng-ui';
5
- import { DataFilterType, VerbenDialogueModule, DataTableModule, CardModule, SvgModule, DataViewModule, CardDataViewModule, SortTableModule, VisibleColumnModule, TableFilterModule, DataExportModule, DataXportModule, VerbenaInputModule, VerbenaButtonModule, VerbenaBadgeModule, DropDownModule } from 'verben-ng-ui';
6
- import * as i1 from 'verben-authentication-ui/src/lib/services';
7
- import { BehaviorSubject, Subject, debounceTime, distinctUntilChanged, takeUntil, lastValueFrom } from 'rxjs';
8
- import * as i3 from '@angular/common';
1
+ import * as i1 from '@angular/common';
9
2
  import { CommonModule } from '@angular/common';
3
+ import * as i0 from '@angular/core';
4
+ import { Component, Input, input, EventEmitter, effect, Output, Injectable, inject, DestroyRef, ViewChild, NgModule } from '@angular/core';
5
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
+ import * as i2 from 'verben-authentication-ui/src/lib/base';
7
+ import { BaseDataPageState, BaseDataPageService, BaseDataPageFacade, SearchHandlerService } from 'verben-authentication-ui/src/lib/base';
8
+ import * as i3 from 'verben-ng-ui';
9
+ import { VerbenaBadgeModule, VerbenaInputModule, VerbenaButtonModule, VerbenaTextareaModule, DropDownModule, VerbenDialogueModule, DataTableModule, SvgModule, DataViewModule, DataColumnsModule, DataFilterModule, DataSortModule, DataXportModule, CardDataViewModule } from 'verben-ng-ui';
10
+ import { UserAccessRequestStatus, ErrorResponse, ObjectState, Status, OAuthType } from 'verben-authentication-ui/src/lib/models';
10
11
  import * as i1$1 from '@angular/forms';
11
- import { Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
12
+ import { FormBuilder, Validators, FormControl, ReactiveFormsModule } from '@angular/forms';
13
+ import { BehaviorSubject } from 'rxjs';
14
+ import * as i1$2 from 'verben-authentication-ui/src/lib/services';
15
+
16
+ class UserAccessRequestStatusBadgeComponent {
17
+ status = UserAccessRequestStatus.Pending;
18
+ badgeConfig() {
19
+ switch (this.status) {
20
+ case UserAccessRequestStatus.Approved:
21
+ return { bgColor: 'var(--vbn-color-success-bg)', textColor: 'var(--vbn-color-success)', label: 'Approved' };
22
+ case UserAccessRequestStatus.Declined:
23
+ return { bgColor: 'var(--vbn-color-error-bg)', textColor: 'var(--vbn-color-error)', label: 'Rejected' };
24
+ default:
25
+ return { bgColor: 'var(--vbn-color-text-muted)', textColor: 'var(--vbn-color-border)', label: 'Pending' };
26
+ }
27
+ }
28
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestStatusBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
29
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UserAccessRequestStatusBadgeComponent, isStandalone: true, selector: "lib-user-access-request-status-badge", inputs: { status: "status" }, ngImport: i0, template: "<verbena-badge\n borderRadius=\"9px\"\n [bgColor]=\"badgeConfig().bgColor\"\n [textColor]=\"badgeConfig().textColor\"\n fontSize=\"12px\"\n [text]=\"badgeConfig().label\"\n [pd]=\"'2px 6px'\"\n></verbena-badge>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: VerbenaBadgeModule }, { kind: "component", type: i3.VerbenaBadgeComponent, selector: "verbena-badge", inputs: ["text", "bgColor", "textColor", "borderRadius", "pd", "fontSize", "width", "height"] }] });
30
+ }
31
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestStatusBadgeComponent, decorators: [{
32
+ type: Component,
33
+ args: [{ selector: 'lib-user-access-request-status-badge', standalone: true, imports: [CommonModule, VerbenaBadgeModule], template: "<verbena-badge\n borderRadius=\"9px\"\n [bgColor]=\"badgeConfig().bgColor\"\n [textColor]=\"badgeConfig().textColor\"\n fontSize=\"12px\"\n [text]=\"badgeConfig().label\"\n [pd]=\"'2px 6px'\"\n></verbena-badge>\n" }]
34
+ }], propDecorators: { status: [{
35
+ type: Input
36
+ }] } });
12
37
 
13
- const columns = [
38
+ /**
39
+ * Presentational card for a single access request in the card view.
40
+ */
41
+ class UserRequestApprovalCardComponent {
42
+ request;
43
+ selected = false;
44
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestApprovalCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
45
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UserRequestApprovalCardComponent, isStandalone: true, selector: "lib-user-request-approval-card", inputs: { request: "request", selected: "selected" }, ngImport: i0, template: "<div class=\"flex cursor-pointer w-full bg-secondary rounded-xl\">\n <div class=\"w-3 rounded-xl rounded-tr-none rounded-br-none\"\n [ngClass]=\"selected ? 'bg-primary' : 'bg-secondary-200'\"></div>\n <div class=\"flex-1 flex items-end gap-y-2 gap-x-4 p-4\">\n <div class=\"flex-1 space-y-2\">\n <span class=\"font-semibold text-[var(--vbn-color-text)] flex-1 truncate\">\n {{ request.FirstName }} {{ request.LastName }}\n </span>\n <p class=\"flex items-center my-1 gap-1\">\n <label class=\"text-[10px] font-light\">Medium:</label>\n <span class=\"text-sm truncate\">{{ request.SignupType }}</span>\n </p>\n </div>\n <div class=\"grid gap-y-1\">\n <lib-user-access-request-status-badge [status]=\"request.RequestStatus\"></lib-user-access-request-status-badge>\n </div>\n </div>\n</div>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: UserAccessRequestStatusBadgeComponent, selector: "lib-user-access-request-status-badge", inputs: ["status"] }] });
46
+ }
47
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestApprovalCardComponent, decorators: [{
48
+ type: Component,
49
+ args: [{ selector: 'lib-user-request-approval-card', standalone: true, imports: [CommonModule, UserAccessRequestStatusBadgeComponent], template: "<div class=\"flex cursor-pointer w-full bg-secondary rounded-xl\">\n <div class=\"w-3 rounded-xl rounded-tr-none rounded-br-none\"\n [ngClass]=\"selected ? 'bg-primary' : 'bg-secondary-200'\"></div>\n <div class=\"flex-1 flex items-end gap-y-2 gap-x-4 p-4\">\n <div class=\"flex-1 space-y-2\">\n <span class=\"font-semibold text-[var(--vbn-color-text)] flex-1 truncate\">\n {{ request.FirstName }} {{ request.LastName }}\n </span>\n <p class=\"flex items-center my-1 gap-1\">\n <label class=\"text-[10px] font-light\">Medium:</label>\n <span class=\"text-sm truncate\">{{ request.SignupType }}</span>\n </p>\n </div>\n <div class=\"grid gap-y-1\">\n <lib-user-access-request-status-badge [status]=\"request.RequestStatus\"></lib-user-access-request-status-badge>\n </div>\n </div>\n</div>\n", styles: [":host{display:block}\n"] }]
50
+ }], propDecorators: { request: [{
51
+ type: Input
52
+ }], selected: [{
53
+ type: Input
54
+ }] } });
55
+
56
+ const fb = new FormBuilder();
57
+ /**
58
+ * Column definitions for the access-request table.
59
+ * Single source of truth for table, filter, sort, and export.
60
+ */
61
+ const USER_ACCESS_REQUEST_COLUMNS = [
62
+ {
63
+ id: 'select',
64
+ header: '',
65
+ canExport: false,
66
+ },
14
67
  {
15
68
  id: 'createdAt',
16
69
  header: 'CREATED',
@@ -29,1030 +82,534 @@ const columns = [
29
82
  {
30
83
  id: 'role',
31
84
  header: 'ROLE',
32
- accessorFn: (row) => row.Role?.Name,
85
+ accessorFn: (row) => row.Role?.Name ?? '',
33
86
  },
34
87
  {
35
88
  id: 'status',
36
89
  header: 'STATUS',
37
90
  accessorKey: 'RequestStatus',
38
91
  },
39
- { id: 'actions', header: 'Actions', accessorKey: 'MailAddress' },
92
+ {
93
+ id: 'actions',
94
+ header: 'ACTIONS',
95
+ canExport: false,
96
+ },
40
97
  ];
41
-
42
- const baseStyle = {
43
- width: '100%',
44
- fontFamily: '"Fraunces", serif',
45
- fontSize: '125%',
46
- whiteSpace: 'nowrap',
47
- margin: '0',
48
- border: '1px solid #D4A007',
49
- borderRadius: '16px',
50
- borderCollapse: 'separate',
51
- borderSpacing: '0',
52
- // tableLayout: 'fixed',
53
- overflow: 'hidden',
54
- rows: {
55
- even: {
56
- backgroundColor: '#FDFDFD',
57
- },
58
- odd: {
59
- backgroundColor: '#F1F4FB',
98
+ /**
99
+ * Page configuration for access-request approval.
100
+ * Create/import are disabled — this is an approval workflow, not CRUD create.
101
+ */
102
+ const USER_ACCESS_REQUEST_CONFIG = {
103
+ endpoint: 'UserAccessRequest',
104
+ pageSize: 20,
105
+ columns: USER_ACCESS_REQUEST_COLUMNS,
106
+ cardDataMapper: (request) => ({
107
+ selected: false,
108
+ title: `${request.FirstName} ${request.LastName}`,
109
+ data: request,
110
+ body: [{ title: 'Role', value: request.Role?.Name ?? '' }],
111
+ children: [],
112
+ }),
113
+ searchDebounceTime: 500,
114
+ formGroupConfig: {
115
+ controls: {
116
+ MailAddress: fb.control(''),
117
+ FirstName: fb.control(''),
118
+ LastName: fb.control(''),
119
+ OtherName: fb.control(''),
120
+ PhoneNumber: fb.control(''),
121
+ RoleID: fb.control('', [Validators.required]),
60
122
  },
61
- height: '68px',
62
- },
63
- cells: {
64
- padding: '0.5rem 1rem',
65
- },
66
- header: {
67
- // stickyTop: true,
68
- // zIndex: 2,
69
- backgroundColor: '#EFF2FB',
70
- fontWeight: 'bold',
71
- textAlign: 'left',
72
- padding: '12px 16px',
73
- height: '40px',
74
- },
75
- footer: {
76
- // stickyBottom: true,
77
- // zIndex: 2,
78
- padding: '3px',
79
- },
80
- // body: {
81
- // background: '#fff',
82
- // padding: '4px 5px',
83
- // textAlign: 'center',
84
- // },
85
- // firstColumn: {
86
- // stickyLeft: true,
87
- // zIndex: 1,
88
- // },
89
- lastColumn: {
90
- // stickyRight: true,
91
- // zIndex: 1,
92
123
  },
124
+ enableCreate: false,
125
+ enableImport: false,
126
+ enableExport: true,
127
+ enableFilter: true,
128
+ enableSort: true,
129
+ enableSearch: true,
130
+ enableCardView: true,
131
+ enableTableRowEdit: false,
93
132
  };
94
133
 
95
- const roles = [
96
- { id: 'admin', name: 'Admin' },
97
- { id: 'manager', name: 'Manager' },
98
- { id: 'staff', name: 'Staff' },
99
- ];
100
- // export const mockData: UserAccessRequest[] = new Array(10)
101
- // .fill(1)
102
- // .map((id, i) => ({
103
- // FirstName: `First ${id + i}`,
104
- // LastName: `Last ${id + i}`,
105
- // OtherName: `Other ${id + i}`,
106
- // Id: id + i,
107
- // id: (id + i).toString(),
108
- // MailAddress: `user${id + i}@mail.com`,
109
- // PhoneNumber: `+23480000000${id + i}`,
110
- // RequestStatus: randomEnum(UserAccessRequestStatus),
111
- // RoleID: roles[Math.floor(Math.random() * roles.length)].id,
112
- // Password: '1234567',
113
- // ExpireOn: new Date(),
114
- // IsSeeded: false,
115
- // OTPExpireOn: new Date(),
116
- // Tenants: [],
117
- // SignupType: OAuthType.InApp,
118
- // CreatedAt: new Date(),
119
- // UpdatedAt: new Date(),
120
- // DataState: ObjectState.New,
121
- // TenantId: `Tenant${id + i}`,
122
- // Code: '',
123
- // ServiceName: '',
124
- // }));
125
- function randomEnum(anEnum) {
126
- const enumValues = Object.values(anEnum);
127
- const randomIndex = Math.floor(Math.random() * enumValues.length);
128
- return enumValues[randomIndex];
129
- }
130
- function convertDataFilterTypeToSearchPropertyValueType(type) {
131
- switch (type) {
132
- case DataFilterType.String:
133
- return SearchPropertyValueType.String;
134
- case DataFilterType.Integer:
135
- return SearchPropertyValueType.Int;
136
- case DataFilterType.Decimal:
137
- return SearchPropertyValueType.Decimal;
138
- case DataFilterType.Credit:
139
- return SearchPropertyValueType.Float;
140
- case DataFilterType.Date:
141
- return SearchPropertyValueType.Date;
142
- case DataFilterType.Bool:
143
- return SearchPropertyValueType.Bool;
144
- case DataFilterType.Number:
145
- return SearchPropertyValueType.Int;
146
- }
147
- }
148
-
149
- class UserAccessRequestService {
150
- httpService;
151
- constructor(httpService) {
152
- this.httpService = httpService;
153
- }
154
- /**
155
- * Get user access requests with pagination and sorting
156
- * @param skip Number of records to skip
157
- * @param limit Number of records to take
158
- * @param sortParam Sort parameter
159
- * @param sortOrder Sort order ('asc' or 'desc')
160
- * @returns Promise containing the user access requests
161
- */
162
- getUserAccessRequests(skip, limit, sortParam, sortOrder) {
163
- const url = `GetUserAccessRequests/${skip}/${limit}/${sortParam}/${sortOrder}`;
164
- return this.httpService.get(url);
165
- }
166
- getRoles() {
167
- const url = `GetRoles/0/0`;
168
- return this.httpService.get(url);
169
- }
170
- getTags() {
171
- const url = `GetTags/0/0`;
172
- return this.httpService.get(url);
173
- }
174
- /**
175
- * Get user access requests with additional parameter, pagination and sorting
176
- * @param param Search parameter
177
- * @param skip Number of records to skip
178
- * @param limit Number of records to take
179
- * @param sortParam Sort parameter
180
- * @param sortOrder Sort order ('asc' or 'desc')
181
- * @returns Promise containing the filtered user access requests
182
- */
183
- getUserAccessRequestWithParam(param, skip, limit, sortParam, sortOrder) {
184
- const url = `GetUserAccessRequestWithParam/${param}/${skip}/${limit}/${sortParam}/${sortOrder}`;
185
- return this.httpService.get(url);
186
- }
187
- /**
188
- * Get user access requests with additional parameter, pagination and sorting
189
- * @param params Filter parameters
190
- * @param skip Number of records to skip
191
- * @param limit Number of records to take
192
- * @param sortParam Sort parameter
193
- * @param sortOrder Sort order ('asc' or 'desc')
194
- * @returns Promise containing the filtered user access requests
195
- */
196
- getUserAccessRequestByFilterParams(params, skip, limit, sortParam, sortOrder) {
197
- const url = `SearchUserAccessRequest/${skip}/${limit}/${sortParam}/${sortOrder}`;
198
- return this.httpService.post(url, params);
199
- }
200
- /**
201
- * Save multiple user access requests
202
- * @param requests Array of user access requests to save
203
- * @returns Promise containing the save operation result
204
- */
205
- saveUserAccessRequests(requests) {
206
- const url = `SaveUserAccessRequests`;
207
- return this.httpService.post(url, requests);
208
- }
209
- /**
210
- * Approve or reject multiple user access requests
211
- * @param requests Array of user access requests to save
212
- * @param isApproval True for approval, false for decline
213
- * @returns Promise containing the save operation result
214
- */
215
- processUserAccessRequests(requests, isApproval) {
216
- const url = `UserAccessRequestAction/${isApproval}`;
217
- return this.httpService.post(url, requests);
218
- }
134
+ /**
135
+ * Form for approving/rejecting a single access request.
136
+ * Identity fields are read-only; the role must be chosen before approval.
137
+ */
138
+ class UserRequestApprovalFormComponent {
139
+ fb;
140
+ request = input();
141
+ roles = input([]);
142
+ showDelete = input(false);
143
+ approve = new EventEmitter();
144
+ reject = new EventEmitter();
145
+ deleteRequest = new EventEmitter();
146
+ switchToTable = new EventEmitter();
147
+ form;
148
+ showDeleteDialogue = false;
149
+ showRejectDialogue = false;
219
150
  /**
220
- * Delete multiple user access requests
221
- * @param requestIds Array of request IDs to delete
222
- * @returns Observable containing the delete operation result
151
+ * Rejection reason. Deliberately outside `form` — that group's validity gates
152
+ * Approve, and a required comment there would gate Approve too.
223
153
  */
224
- deleteUserAccessRequests(requestIds) {
225
- const url = `DeleteUserAccessRequests`;
226
- return this.httpService.delete(`${url}?data=${requestIds.join(',')}`);
227
- }
228
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestService, deps: [{ token: i1.HttpWebRequestService }], target: i0.ɵɵFactoryTarget.Injectable });
229
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestService, providedIn: 'root' });
230
- }
231
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestService, decorators: [{
232
- type: Injectable,
233
- args: [{
234
- providedIn: 'root',
235
- }]
236
- }], ctorParameters: () => [{ type: i1.HttpWebRequestService }] });
237
-
238
- class UserAccessRequestState {
239
- updating$ = new BehaviorSubject(false);
240
- requests$ = new BehaviorSubject([]);
241
- roles$ = new BehaviorSubject([]);
242
- searchTerm$ = new BehaviorSubject('');
243
- queryParams$ = new BehaviorSubject({
244
- skip: 0,
245
- limit: 20,
246
- sortParam: 'CreatedAt',
247
- sortOrder: SortDirection.Asc,
248
- });
249
- filterParams$ = new BehaviorSubject([]);
250
- isUpdating$() {
251
- return this.updating$.asObservable();
252
- }
253
- setUpdating(isUpdating) {
254
- this.updating$.next(isUpdating);
255
- }
256
- getRequests$() {
257
- return this.requests$.asObservable();
258
- }
259
- setRequests(requests) {
260
- this.requests$.next(requests);
261
- }
262
- getFilterParams$() {
263
- return this.filterParams$.asObservable();
264
- }
265
- setFilterParams(params) {
266
- this.filterParams$.next(params);
267
- }
268
- getRoles$() {
269
- return this.roles$.asObservable();
270
- }
271
- setRoles(roles) {
272
- this.roles$.next(roles);
273
- }
274
- appendRequests(newRequests) {
275
- const currentRequests = this.requests$.getValue();
276
- this.requests$.next([...currentRequests, ...newRequests]);
277
- }
278
- getSearchTerm$() {
279
- return this.searchTerm$.asObservable();
280
- }
281
- getSearchTermValue() {
282
- return this.searchTerm$.getValue();
283
- }
284
- setSearchTerm(term) {
285
- this.searchTerm$.next(term);
286
- }
287
- getQueryParams$() {
288
- return this.queryParams$.asObservable();
289
- }
290
- getQueryParamsValue() {
291
- return this.queryParams$.getValue();
292
- }
293
- updateQueryParams(params) {
294
- this.queryParams$.next({ ...this.queryParams$.getValue(), ...params });
295
- }
296
- resetPagination() {
297
- const currentParams = this.queryParams$.getValue();
298
- this.queryParams$.next({ ...currentParams, skip: 0 });
299
- }
300
- addRequest(request) {
301
- const currentValue = this.requests$.getValue();
302
- this.requests$.next([...currentValue, request]);
303
- }
304
- updateRequest(updatedRequest) {
305
- const requests = this.requests$.getValue();
306
- const index = requests.findIndex((r) => r.Code === updatedRequest.Code);
307
- if (index !== -1) {
308
- requests[index] = updatedRequest;
309
- this.requests$.next([...requests]);
310
- }
311
- }
312
- removeRequest(requestId) {
313
- const currentValue = this.requests$.getValue();
314
- this.requests$.next(currentValue.filter((r) => r.Id !== requestId));
315
- }
316
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestState, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
317
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestState, providedIn: 'root' });
318
- }
319
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestState, decorators: [{
320
- type: Injectable,
321
- args: [{
322
- providedIn: 'root',
323
- }]
324
- }] });
325
-
326
- class UserAccessRequestFacade {
327
- userAccessRequestService;
328
- userAccessRequestState;
329
- utilService;
330
- searchSubject = new Subject();
331
- destroy$ = new Subject();
332
- constructor(userAccessRequestService, userAccessRequestState, utilService) {
333
- this.userAccessRequestService = userAccessRequestService;
334
- this.userAccessRequestState = userAccessRequestState;
335
- this.utilService = utilService;
336
- // Set up debounced search handling
337
- this.searchSubject
338
- .pipe(debounceTime(1000), distinctUntilChanged(), takeUntil(this.destroy$))
339
- .subscribe((searchTerm) => {
340
- this.handleSearch(searchTerm);
341
- });
342
- this.isUpdating$().subscribe((loading) => {
343
- this.utilService.sendBI(loading);
344
- });
345
- this.getFilterParams$().subscribe((params) => {
346
- if (params.length) {
347
- }
348
- else {
349
- this.loadRequests();
154
+ rejectComment = new FormControl('', [Validators.required]);
155
+ constructor(fb) {
156
+ this.fb = fb;
157
+ this.form = this.fb.group(USER_ACCESS_REQUEST_CONFIG.formGroupConfig.controls);
158
+ effect(() => {
159
+ const request = this.request();
160
+ if (request && this.form) {
161
+ this.form.patchValue({
162
+ MailAddress: request.MailAddress || '',
163
+ FirstName: request.FirstName || '',
164
+ LastName: request.LastName || '',
165
+ OtherName: request.OtherName || '',
166
+ PhoneNumber: request.PhoneNumber || '',
167
+ RoleID: request.RoleID || '',
168
+ });
350
169
  }
351
170
  });
352
171
  }
353
- destroy() {
354
- this.destroy$.next();
355
- this.destroy$.complete();
172
+ isPending() {
173
+ return this.request()?.RequestStatus === UserAccessRequestStatus.Pending;
356
174
  }
357
- isUpdating$() {
358
- return this.userAccessRequestState.isUpdating$();
175
+ canProcess() {
176
+ return this.isPending() && this.form.valid;
359
177
  }
360
- getRequests$() {
361
- return this.userAccessRequestState.getRequests$();
178
+ canReject() {
179
+ return this.isPending();
362
180
  }
363
- getFilterParams$() {
364
- return this.userAccessRequestState.getFilterParams$();
365
- }
366
- getRoles$() {
367
- return this.userAccessRequestState.getRoles$();
368
- }
369
- getQueryParams$() {
370
- return this.userAccessRequestState.getQueryParams$();
371
- }
372
- getSearchTerm$() {
373
- return this.userAccessRequestState.getSearchTerm$();
374
- }
375
- // Search handling
376
- search(term) {
377
- this.searchSubject.next(term);
378
- }
379
- async handleSearch(searchTerm) {
380
- this.userAccessRequestState.setSearchTerm(searchTerm);
381
- this.userAccessRequestState.resetPagination();
382
- try {
383
- this.userAccessRequestState.setUpdating(true);
384
- if (searchTerm.trim()) {
385
- await this.loadRequestsWithParam(searchTerm);
386
- }
387
- else {
388
- await this.loadRequests();
389
- }
390
- }
391
- catch (error) {
392
- console.error('Search failed:', error);
393
- throw error;
394
- }
395
- finally {
396
- this.userAccessRequestState.setUpdating(false);
397
- }
398
- }
399
- async filter(filterParams) {
400
- this.userAccessRequestState.resetPagination();
401
- this.userAccessRequestState.setFilterParams(filterParams);
402
- }
403
- // Load more handling
404
- async loadMore() {
405
- try {
406
- this.userAccessRequestState.setUpdating(true);
407
- const currentParams = this.userAccessRequestState.getQueryParamsValue();
408
- const searchTerm = this.userAccessRequestState.getSearchTermValue();
409
- // Update skip value
410
- const newSkip = currentParams.skip + currentParams.limit;
411
- this.userAccessRequestState.updateQueryParams({ skip: newSkip });
412
- // Load and append data
413
- const response = searchTerm
414
- ? await this.userAccessRequestService.getUserAccessRequestWithParam(searchTerm, newSkip, currentParams.limit, currentParams.sortParam || 'createdDate', currentParams.sortOrder)
415
- : await this.userAccessRequestService.getUserAccessRequests(newSkip, currentParams.limit, currentParams.sortParam || 'createdDate', currentParams.sortOrder);
416
- this.userAccessRequestState.appendRequests(response.Result);
417
- }
418
- catch (error) {
419
- console.error('Load more failed:', error);
420
- throw error;
421
- }
422
- finally {
423
- this.userAccessRequestState.setUpdating(false);
424
- }
425
- }
426
- async loadRequests() {
427
- try {
428
- this.userAccessRequestState.setUpdating(true);
429
- const params = this.userAccessRequestState.getQueryParamsValue();
430
- const requests = await this.userAccessRequestService.getUserAccessRequests(params.skip, params.limit, params.sortParam || 'createdDate', params.sortOrder);
431
- this.userAccessRequestState.setRequests(requests.Result);
432
- }
433
- catch (error) {
434
- console.error('Failed to load requests:', error);
435
- throw error;
436
- }
437
- finally {
438
- this.userAccessRequestState.setUpdating(false);
439
- }
181
+ canDelete() {
182
+ return this.showDelete() && this.isPending();
440
183
  }
441
- async loadRoles() {
442
- try {
443
- // this.userAccessRequestState.setUpdating(true);
444
- // const params = this.userAccessRequestState.getQueryParamsValue();
445
- const requests = await this.userAccessRequestService.getRoles();
446
- this.userAccessRequestState.setRoles(requests.Result);
447
- }
448
- catch (error) {
449
- console.error('Failed to load requests:', error);
450
- throw error;
451
- }
452
- finally {
453
- // this.userAccessRequestState.setUpdating(false);
184
+ onApprove() {
185
+ const request = this.request();
186
+ if (request && this.canProcess()) {
187
+ this.approve.emit({ request, roleId: this.form.get('RoleID')?.value });
454
188
  }
455
189
  }
456
- async loadRequestsWithParam(param) {
457
- try {
458
- this.userAccessRequestState.setUpdating(true);
459
- const params = this.userAccessRequestState.getQueryParamsValue();
460
- const requests = await this.userAccessRequestService.getUserAccessRequestWithParam(param, params.skip, params.limit, params.sortParam || 'createdDate', params.sortOrder);
461
- this.userAccessRequestState.setRequests(requests.Result);
462
- }
463
- catch (error) {
464
- console.error('Failed to load requests with param:', error);
465
- throw error;
466
- }
467
- finally {
468
- this.userAccessRequestState.setUpdating(false);
190
+ onDelete() {
191
+ const request = this.request();
192
+ if (request) {
193
+ this.deleteRequest.emit(request);
469
194
  }
195
+ this.closeDeleteDialogue();
470
196
  }
471
- async loadRequestsByFilterParams(filterParams) {
472
- try {
473
- this.userAccessRequestState.setUpdating(true);
474
- const params = this.userAccessRequestState.getQueryParamsValue();
475
- const requests = await this.userAccessRequestService.getUserAccessRequestByFilterParams(filterParams.map((filter) => ({
476
- PropertyName: filter.name,
477
- EntityValue: filter.value,
478
- type: convertDataFilterTypeToSearchPropertyValueType(filter.type),
479
- Operator: SearchOperator.And,
480
- Sign: SearchPropertySign.EQ,
481
- })), params.skip, params.limit, params.sortParam || 'createdDate', params.sortOrder);
482
- this.userAccessRequestState.setRequests(requests.Result);
483
- }
484
- catch (error) {
485
- console.error('Failed to load requests with param:', error);
486
- throw error;
487
- }
488
- finally {
489
- this.userAccessRequestState.setUpdating(false);
490
- }
197
+ toggleDeleteDialogue() {
198
+ this.showDeleteDialogue = !this.showDeleteDialogue;
491
199
  }
492
- updateQueryParams(params) {
493
- this.userAccessRequestState.updateQueryParams(params);
494
- this.loadRequests();
200
+ closeDeleteDialogue() {
201
+ this.showDeleteDialogue = false;
495
202
  }
496
- async addRequest(request) {
497
- try {
498
- this.userAccessRequestState.setUpdating(true);
499
- const newRequest = { ...request };
500
- this.userAccessRequestState.addRequest(newRequest);
501
- await this.userAccessRequestService.saveUserAccessRequests([newRequest]);
502
- await this.loadRequests(); // Reload to get server-side changes
503
- }
504
- catch (error) {
505
- console.error('Failed to add request:', error);
506
- this.userAccessRequestState.removeRequest(request.Id);
507
- throw error;
508
- }
509
- finally {
510
- this.userAccessRequestState.setUpdating(false);
511
- }
203
+ // ===== Reject (requires a reason) =====
204
+ openRejectDialogue() {
205
+ if (!this.canReject())
206
+ return;
207
+ this.rejectComment.setValue('');
208
+ this.rejectComment.markAsUntouched();
209
+ this.showRejectDialogue = true;
512
210
  }
513
- async updateRequest(request) {
514
- try {
515
- this.userAccessRequestState.setUpdating(true);
516
- await this.userAccessRequestService.saveUserAccessRequests([request]);
517
- this.userAccessRequestState.updateRequest(request);
518
- this.utilService.showSuccess('Request updated successfully');
519
- }
520
- catch (error) {
521
- console.error('Failed to update request:', error);
522
- throw error;
523
- }
524
- finally {
525
- this.userAccessRequestState.setUpdating(false);
526
- }
211
+ closeRejectDialogue() {
212
+ this.showRejectDialogue = false;
527
213
  }
528
- async deleteRequest(requestId) {
529
- try {
530
- this.userAccessRequestState.setUpdating(true);
531
- await lastValueFrom(this.userAccessRequestService.deleteUserAccessRequests([requestId]));
532
- this.userAccessRequestState.removeRequest(requestId);
533
- }
534
- catch (error) {
535
- console.error('Failed to delete request:', error);
536
- throw error;
537
- }
538
- finally {
539
- this.userAccessRequestState.setUpdating(false);
540
- }
214
+ canConfirmReject() {
215
+ return !!this.rejectComment.value?.trim();
541
216
  }
542
- async approveRequest(request) {
543
- try {
544
- this.userAccessRequestState.setUpdating(true);
545
- const response = await this.userAccessRequestService.processUserAccessRequests([request], true);
546
- // console.log(response);
547
- const data = response?.[0];
548
- if (data) {
549
- this.userAccessRequestState.updateRequest(data);
550
- this.utilService.showSuccess('Request approved successfully');
551
- }
552
- }
553
- catch (error) {
554
- console.error('Failed to update request:', error);
555
- throw error;
556
- }
557
- finally {
558
- this.userAccessRequestState.setUpdating(false);
559
- }
217
+ confirmReject() {
218
+ const request = this.request();
219
+ if (!request || !this.canReject() || !this.canConfirmReject())
220
+ return;
221
+ this.reject.emit({ request, comment: this.rejectComment.value.trim() });
222
+ this.closeRejectDialogue();
560
223
  }
561
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestFacade, deps: [{ token: UserAccessRequestService }, { token: UserAccessRequestState }, { token: i1.UtilService }], target: i0.ɵɵFactoryTarget.Injectable });
562
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestFacade, providedIn: 'root' });
224
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestApprovalFormComponent, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component });
225
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: UserRequestApprovalFormComponent, isStandalone: true, selector: "lib-user-request-approval-form", inputs: { request: { classPropertyName: "request", publicName: "request", isSignal: true, isRequired: false, transformFunction: null }, roles: { classPropertyName: "roles", publicName: "roles", isSignal: true, isRequired: false, transformFunction: null }, showDelete: { classPropertyName: "showDelete", publicName: "showDelete", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { approve: "approve", reject: "reject", deleteRequest: "deleteRequest", switchToTable: "switchToTable" }, ngImport: i0, template: "<form [formGroup]=\"form\" class=\"flex flex-col px-3 pb-3 rounded-xl h-full w-full relative space-y-4\">\n <verbena-input name=\"MailAddress\" label=\"Mail Address:\" formControlName=\"MailAddress\" [readOnly]=\"true\" />\n <verbena-input label=\"First Name:\" name=\"FirstName\" type=\"text\" formControlName=\"FirstName\" [readOnly]=\"true\" />\n <verbena-input label=\"Last Name:\" name=\"LastName\" type=\"text\" formControlName=\"LastName\" [readOnly]=\"true\" />\n <verbena-input label=\"Other Name:\" name=\"OtherName\" type=\"text\" formControlName=\"OtherName\" [readOnly]=\"true\" />\n\n <div class=\"space-y-1\">\n <label for=\"role\">Role</label>\n <verben-drop-down styleClass=\"w-full\" width=\"100%\" [multiselect]=\"false\" [filter]=\"true\" [showClear]=\"true\"\n placeholder=\"\" [options]=\"roles()\" id=\"role\" optionLabel=\"Name\" optionValue=\"Code\" formControlName=\"RoleID\"\n class=\"form-control\">\n </verben-drop-down>\n </div>\n\n <!-- buttons -->\n <div class=\"flex justify-between my-2 overflow-auto\">\n <div class=\"flex gap-2\">\n <verbena-button (click)=\"switchToTable.emit()\" text=\"Switch To Table\" styleType=\"ylw-outline\"></verbena-button>\n\n <verbena-button *ngIf=\"showDelete()\" width=\"114px\" height=\"39px\" (click)=\"toggleDeleteDialogue()\" text=\"Delete\"\n styleType=\"danger\" borderRadius=\"10px\" [disable]=\"!canDelete()\"></verbena-button>\n </div>\n\n <div class=\"flex gap-2\">\n <verbena-button width=\"114px\" height=\"39px\" text=\"Reject\" styleType=\"danger\" borderRadius=\"10px\"\n (click)=\"openRejectDialogue()\" [disable]=\"!canReject()\"></verbena-button>\n\n <verbena-button (click)=\"onApprove()\" text=\"Approve\" bgColor=\"var(--vbn-color-success)\" textColor=\"white\"\n border=\"1px solid var(--vbn-color-success)\" borderRadius=\"10px\" pd=\"10px 20px\" width=\"114px\" height=\"39px\"\n [disable]=\"!canProcess()\"></verbena-button>\n </div>\n </div>\n</form>\n\n<verben-dialogue [showCloseIcon]=\"false\" [dismissOutsideClick]=\"false\" [closeOnEscape]=\"true\" [size]=\"'medium'\"\n [mode]=\"'dialogue'\" [customClass]=\"'show-revert-dialogue'\" [disableFooter]=\"true\" [isVisible]=\"showDeleteDialogue\"\n [bodyTemplate]=\"deleteBodyTemplate\" (closeModal)=\"closeDeleteDialogue()\">\n <ng-template #deleteBodyTemplate>\n <div class=\"dialog-body flex flex-col gap-4\">\n <div class=\"header font-bold text-2xl\">Delete User Request</div>\n <div class=\"form-container-dialogue flex flex-col gap-2\">\n Are you sure you want to proceed?\n </div>\n <div class=\"dialog-button-container flex gap-2\">\n <div class=\"flex-1\">\n <verbena-button width=\"100%\" [fontWeight]=\"'bold'\" [bgColor]=\"'var(--vbn-color-surface)'\"\n [borderRadius]=\"'4px'\" [textColor]=\"'var(--vbn-color-text)'\" [border]=\"'1px solid var(--vbn-color-text)'\"\n [text]=\"'Close'\" (click)=\"closeDeleteDialogue()\">\n </verbena-button>\n </div>\n <div class=\"flex-1\">\n <verbena-button width=\"100%\" class=\"verben-primary-button\" [text]=\"'Proceed'\" (click)=\"onDelete()\">\n </verbena-button>\n </div>\n </div>\n </div>\n </ng-template>\n</verben-dialogue>\n\n<verben-dialogue [showCloseIcon]=\"false\" [dismissOutsideClick]=\"false\" [closeOnEscape]=\"true\" [size]=\"'medium'\"\n [mode]=\"'dialogue'\" [customClass]=\"'show-revert-dialogue'\" [disableFooter]=\"true\" [isVisible]=\"showRejectDialogue\"\n [bodyTemplate]=\"rejectBodyTemplate\" (closeModal)=\"closeRejectDialogue()\">\n <ng-template #rejectBodyTemplate>\n <div class=\"dialog-body flex flex-col gap-4\">\n <div class=\"header font-bold text-2xl\">Reject User Request</div>\n <div class=\"form-container-dialogue flex flex-col gap-2\">\n Please provide a reason for rejecting this request.\n <verbena-textarea label=\"Reason\" [required]=\"true\" [rows]=\"4\" width=\"100%\" [formControl]=\"rejectComment\"\n errorMessageColor=\"var(--vbn-color-danger)\">\n </verbena-textarea>\n </div>\n <div class=\"dialog-button-container flex gap-2\">\n <div class=\"flex-1\">\n <verbena-button width=\"100%\" [fontWeight]=\"'bold'\" [bgColor]=\"'var(--vbn-color-surface)'\"\n [borderRadius]=\"'4px'\" [textColor]=\"'var(--vbn-color-text)'\" [border]=\"'1px solid var(--vbn-color-text)'\"\n [text]=\"'Close'\" (click)=\"closeRejectDialogue()\">\n </verbena-button>\n </div>\n <div class=\"flex-1\">\n <verbena-button width=\"100%\" class=\"verben-primary-button\" [text]=\"'Proceed'\" (click)=\"confirmReject()\"\n [disable]=\"!canConfirmReject()\">\n </verbena-button>\n </div>\n </div>\n </div>\n </ng-template>\n</verben-dialogue>\n", styles: [":host{display:block;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: VerbenaInputModule }, { kind: "component", type: i3.VerbenaInputComponent, selector: "verbena-input", inputs: ["disabled", "label", "placeHolder", "required", "svgPosition", "minLength", "maxLength", "type", "bgColor", "border", "borderRadius", "textColor", "value", "labelPosition", "labelColor", "disable", "readOnly", "min", "max", "showBorder", "showErrorMessage", "errorMessageColor", "errorBorderColor", "errorPosition", "svg", "fontSize", "svgWidth", "svgHeight", "svgColor", "capitalization", "inputContainerClass", "inputFieldClass", "passLength", "inputWrapperClass", "passwordToggle", "customErrorMessages", "icon", "textPass"], outputs: ["valueChange"] }, { kind: "ngmodule", type: VerbenaButtonModule }, { kind: "component", type: i3.VerbenaButtonComponent, selector: "verbena-button", inputs: ["type", "text", "icon", "useIcon", "svgPosition", "iconPosition", "bgColor", "textColor", "border", "borderRadius", "pd", "width", "height", "fontSize", "fontWeight", "disable", "svgSize", "weight", "variant", "styleType", "svg", "svgWidth", "svgHeight", "iconColor", "svgColor", "buttonClass", "buttonTextClass", "isLoading", "spinnerSize", "spinnerColor"] }, { kind: "ngmodule", type: VerbenaTextareaModule }, { kind: "component", type: i3.VerbenaTextareaComponent, selector: "verbena-textarea", inputs: ["disabled", "label", "required", "rows", "cols", "bgColor", "textColor", "border", "borderRadius", "pd", "width", "height", "value", "errorMessageColor"], outputs: ["valueChange"] }, { kind: "ngmodule", type: DropDownModule }, { kind: "component", type: i3.DropDownComponent, selector: "verben-drop-down", inputs: ["options", "width", "overlayWidth", "showHorizontalLine", "horizontalLineColor", "optionLabel", "optionSubLabel", "optionValue", "placeholder", "invalidMessage", "errorPosition", "loadMoreCaption", "display", "showClear", "lazyLoad", "selectKey", "styleClass", "group", "multiselect", "filter", "avoidDuplication", "filterBy", "debounceTime", "minChar", "refPageSize", "disabled", "required", "load", "asyncLabel", "search"], outputs: ["optionsChange", "onChange", "onClick", "onClear"] }, { kind: "ngmodule", type: VerbenDialogueModule }, { kind: "component", type: i3.VerbenDialogueComponent, selector: "verben-dialogue", inputs: ["dialogueWidth", "headerTemplate", "bodyTemplate", "footerTemplate", "showCloseIcon", "dismissOutsideClick", "closeOnEscape", "isVisible", "size", "backdropColor", "customClass", "disableFooter", "margin", "padding", "borderRadius", "dialogueBgColor", "width", "closeIconClass", "boxShadow", "enableTransition", "modalData", "mode", "position", "drawerWidth"], outputs: ["openModal", "closeModal"] }] });
563
226
  }
564
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestFacade, decorators: [{
565
- type: Injectable,
566
- args: [{
567
- providedIn: 'root',
568
- }]
569
- }], ctorParameters: () => [{ type: UserAccessRequestService }, { type: UserAccessRequestState }, { type: i1.UtilService }] });
570
- var DateFilterTypes;
571
- (function (DateFilterTypes) {
572
- DateFilterTypes["Before"] = "LT";
573
- DateFilterTypes["On"] = "EQ";
574
- DateFilterTypes["After"] = "GT";
575
- })(DateFilterTypes || (DateFilterTypes = {}));
227
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestApprovalFormComponent, decorators: [{
228
+ type: Component,
229
+ args: [{ selector: 'lib-user-request-approval-form', standalone: true, imports: [
230
+ CommonModule,
231
+ ReactiveFormsModule,
232
+ VerbenaInputModule,
233
+ VerbenaButtonModule,
234
+ VerbenaTextareaModule,
235
+ DropDownModule,
236
+ VerbenDialogueModule,
237
+ ], template: "<form [formGroup]=\"form\" class=\"flex flex-col px-3 pb-3 rounded-xl h-full w-full relative space-y-4\">\n <verbena-input name=\"MailAddress\" label=\"Mail Address:\" formControlName=\"MailAddress\" [readOnly]=\"true\" />\n <verbena-input label=\"First Name:\" name=\"FirstName\" type=\"text\" formControlName=\"FirstName\" [readOnly]=\"true\" />\n <verbena-input label=\"Last Name:\" name=\"LastName\" type=\"text\" formControlName=\"LastName\" [readOnly]=\"true\" />\n <verbena-input label=\"Other Name:\" name=\"OtherName\" type=\"text\" formControlName=\"OtherName\" [readOnly]=\"true\" />\n\n <div class=\"space-y-1\">\n <label for=\"role\">Role</label>\n <verben-drop-down styleClass=\"w-full\" width=\"100%\" [multiselect]=\"false\" [filter]=\"true\" [showClear]=\"true\"\n placeholder=\"\" [options]=\"roles()\" id=\"role\" optionLabel=\"Name\" optionValue=\"Code\" formControlName=\"RoleID\"\n class=\"form-control\">\n </verben-drop-down>\n </div>\n\n <!-- buttons -->\n <div class=\"flex justify-between my-2 overflow-auto\">\n <div class=\"flex gap-2\">\n <verbena-button (click)=\"switchToTable.emit()\" text=\"Switch To Table\" styleType=\"ylw-outline\"></verbena-button>\n\n <verbena-button *ngIf=\"showDelete()\" width=\"114px\" height=\"39px\" (click)=\"toggleDeleteDialogue()\" text=\"Delete\"\n styleType=\"danger\" borderRadius=\"10px\" [disable]=\"!canDelete()\"></verbena-button>\n </div>\n\n <div class=\"flex gap-2\">\n <verbena-button width=\"114px\" height=\"39px\" text=\"Reject\" styleType=\"danger\" borderRadius=\"10px\"\n (click)=\"openRejectDialogue()\" [disable]=\"!canReject()\"></verbena-button>\n\n <verbena-button (click)=\"onApprove()\" text=\"Approve\" bgColor=\"var(--vbn-color-success)\" textColor=\"white\"\n border=\"1px solid var(--vbn-color-success)\" borderRadius=\"10px\" pd=\"10px 20px\" width=\"114px\" height=\"39px\"\n [disable]=\"!canProcess()\"></verbena-button>\n </div>\n </div>\n</form>\n\n<verben-dialogue [showCloseIcon]=\"false\" [dismissOutsideClick]=\"false\" [closeOnEscape]=\"true\" [size]=\"'medium'\"\n [mode]=\"'dialogue'\" [customClass]=\"'show-revert-dialogue'\" [disableFooter]=\"true\" [isVisible]=\"showDeleteDialogue\"\n [bodyTemplate]=\"deleteBodyTemplate\" (closeModal)=\"closeDeleteDialogue()\">\n <ng-template #deleteBodyTemplate>\n <div class=\"dialog-body flex flex-col gap-4\">\n <div class=\"header font-bold text-2xl\">Delete User Request</div>\n <div class=\"form-container-dialogue flex flex-col gap-2\">\n Are you sure you want to proceed?\n </div>\n <div class=\"dialog-button-container flex gap-2\">\n <div class=\"flex-1\">\n <verbena-button width=\"100%\" [fontWeight]=\"'bold'\" [bgColor]=\"'var(--vbn-color-surface)'\"\n [borderRadius]=\"'4px'\" [textColor]=\"'var(--vbn-color-text)'\" [border]=\"'1px solid var(--vbn-color-text)'\"\n [text]=\"'Close'\" (click)=\"closeDeleteDialogue()\">\n </verbena-button>\n </div>\n <div class=\"flex-1\">\n <verbena-button width=\"100%\" class=\"verben-primary-button\" [text]=\"'Proceed'\" (click)=\"onDelete()\">\n </verbena-button>\n </div>\n </div>\n </div>\n </ng-template>\n</verben-dialogue>\n\n<verben-dialogue [showCloseIcon]=\"false\" [dismissOutsideClick]=\"false\" [closeOnEscape]=\"true\" [size]=\"'medium'\"\n [mode]=\"'dialogue'\" [customClass]=\"'show-revert-dialogue'\" [disableFooter]=\"true\" [isVisible]=\"showRejectDialogue\"\n [bodyTemplate]=\"rejectBodyTemplate\" (closeModal)=\"closeRejectDialogue()\">\n <ng-template #rejectBodyTemplate>\n <div class=\"dialog-body flex flex-col gap-4\">\n <div class=\"header font-bold text-2xl\">Reject User Request</div>\n <div class=\"form-container-dialogue flex flex-col gap-2\">\n Please provide a reason for rejecting this request.\n <verbena-textarea label=\"Reason\" [required]=\"true\" [rows]=\"4\" width=\"100%\" [formControl]=\"rejectComment\"\n errorMessageColor=\"var(--vbn-color-danger)\">\n </verbena-textarea>\n </div>\n <div class=\"dialog-button-container flex gap-2\">\n <div class=\"flex-1\">\n <verbena-button width=\"100%\" [fontWeight]=\"'bold'\" [bgColor]=\"'var(--vbn-color-surface)'\"\n [borderRadius]=\"'4px'\" [textColor]=\"'var(--vbn-color-text)'\" [border]=\"'1px solid var(--vbn-color-text)'\"\n [text]=\"'Close'\" (click)=\"closeRejectDialogue()\">\n </verbena-button>\n </div>\n <div class=\"flex-1\">\n <verbena-button width=\"100%\" class=\"verben-primary-button\" [text]=\"'Proceed'\" (click)=\"confirmReject()\"\n [disable]=\"!canConfirmReject()\">\n </verbena-button>\n </div>\n </div>\n </div>\n </ng-template>\n</verben-dialogue>\n", styles: [":host{display:block;width:100%;height:100%}\n"] }]
238
+ }], ctorParameters: () => [{ type: i1$1.FormBuilder }], propDecorators: { approve: [{
239
+ type: Output
240
+ }], reject: [{
241
+ type: Output
242
+ }], deleteRequest: [{
243
+ type: Output
244
+ }], switchToTable: [{
245
+ type: Output
246
+ }] } });
576
247
 
577
- class UserRequestFormComponent {
578
- fb;
579
- roles = [];
248
+ /**
249
+ * Table view for access requests. Row actions open the form (to approve/reject
250
+ * with a role) or delete the request.
251
+ */
252
+ class UserRequestApprovalTableComponent {
253
+ requests = [];
254
+ columns = [];
255
+ isLoading = false;
580
256
  showDelete = false;
581
- get currentData() {
582
- return this._currentData;
257
+ view = new EventEmitter();
258
+ delete = new EventEmitter();
259
+ onView(request) {
260
+ this.view.emit(request);
583
261
  }
584
- set currentData(value) {
585
- this._currentData = value;
586
- console.log(value?.data);
587
- if (value?.data) {
588
- this.form.patchValue({
589
- MailAddress: value.data.MailAddress || '',
590
- FirstName: value.data.FirstName || '',
591
- LastName: value.data.LastName || '',
592
- OtherName: value.data.OtherName || '',
593
- PhoneNumber: value.data.PhoneNumber || '',
594
- RoleID: value.data.RoleID || '',
595
- });
596
- }
597
- else {
598
- this.form.reset();
599
- }
262
+ onDelete(request) {
263
+ this.delete.emit(request);
600
264
  }
601
- showDialogue = false;
602
- _currentData = null;
603
- switchView = new EventEmitter();
604
- onApproval = new EventEmitter();
605
- onDeleteRequest = new EventEmitter();
606
- form;
607
- // roles: WritableSignal<Role[]> = signal([]);
608
- constructor(fb) {
609
- this.fb = fb;
610
- this.form = this.fb.group({
611
- MailAddress: ['', { readonly: true }],
612
- FirstName: ['', { readonly: true }],
613
- LastName: ['', { readonly: true }],
614
- OtherName: ['', { readonly: true }],
615
- PhoneNumber: ['', { readonly: true }],
616
- RoleID: ['', { validators: [Validators.required] }],
617
- });
618
- }
619
- ngOnInit() {
620
- // this.userAccessRequestFacade.getRoles$().subscribe((data) => {
621
- // this.roles.set(data);
622
- // });
623
- // try {
624
- // this.userAccessRequestFacade.loadRoles();
625
- // } catch (error) {
626
- // // Handle error
627
- // }
628
- console.log(this.currentData?.data);
629
- }
630
- onSave() {
631
- if (this.form.valid) {
632
- // Emit save event with form value
633
- console.log('Form saved:', this.form.value);
634
- }
635
- }
636
- onDelete() {
637
- // Emit delete event
638
- this.onDeleteRequest.emit({ email: this.form.get('MailAddress')?.value });
639
- this.onModalClose('');
640
- }
641
- canDelete() {
642
- return (this.currentData?.data?.RequestStatus == UserAccessRequestStatus.Pending);
643
- }
644
- disableProcessing() {
645
- return (this.currentData?.data?.RequestStatus !==
646
- UserAccessRequestStatus.Pending || !this.form.valid);
647
- }
648
- toggleModal() {
649
- this.showDialogue = !this.showDialogue;
650
- }
651
- onModalOpen(event) { }
652
- onModalClose(event) {
653
- this.showDialogue = false;
654
- }
655
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestFormComponent, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component });
656
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UserRequestFormComponent, selector: "lib-user-request-form", inputs: { roles: "roles", showDelete: "showDelete", currentData: "currentData" }, outputs: { switchView: "switchView", onApproval: "onApproval", onDeleteRequest: "onDeleteRequest" }, ngImport: i0, template: "<form\n [formGroup]=\"form\"\n class=\"flex flex-col px-3 pb-3 rounded-xl h-full w-full relative space-y-4\"\n>\n <verbena-input\n name=\"MailAddress\"\n label=\"Mail Address:\"\n formControlName=\"MailAddress\"\n [readOnly]=\"true\"\n />\n <verbena-input\n label=\"First Name:\"\n name=\"FirstName\"\n type=\"text\"\n formControlName=\"FirstName\"\n [readOnly]=\"true\"\n />\n <verbena-input\n label=\"Last Name:\"\n name=\"LastName\"\n type=\"text\"\n formControlName=\"LastName\"\n [readOnly]=\"true\"\n />\n <verbena-input\n label=\"OtherName:\"\n name=\"OtherName\"\n type=\"text\"\n formControlName=\"OtherName\"\n [readOnly]=\"true\"\n />\n <!-- <verbena-input\n label=\"Phone Number:\"\n name=\"PhoneNumber\"\n type=\"tel\"\n formControlName=\"PhoneNumber\"\n [readOnly]=\"true\"\n /> -->\n\n <div class=\"space-y-1\">\n <label for=\"role\">Role</label>\n\n <verben-drop-down\n label=\"Role\"\n styleClass=\"w-full\"\n width=\"100%\"\n [multiselect]=\"false\"\n [filter]=\"true\"\n [showClear]=\"true\"\n placeholder=\"\"\n [(options)]=\"roles\"\n id=\"role\"\n optionLabel=\"Name\"\n optionValue=\"Code\"\n formControlName=\"RoleID\"\n class=\"form-control\"\n >\n </verben-drop-down>\n </div>\n\n <!-- buttons -->\n <div class=\"flex justify-between my-2 overflow-auto\">\n <div class=\"flex gap-2\">\n <verbena-button\n (click)=\"switchView.emit()\"\n text=\"Switch To Table\"\n styleType=\"ylw-outline\"\n ></verbena-button>\n\n <verbena-button\n *ngIf=\"showDelete\"\n width=\"114px\"\n height=\"39px\"\n (click)=\"toggleModal()\"\n text=\"Delete\"\n styleType=\"danger\"\n borderRadius=\"10px\"\n ></verbena-button>\n </div>\n\n <div class=\"flex gap-2\">\n <verbena-button\n width=\"114px\"\n height=\"39px\"\n text=\"Reject\"\n styleType=\"danger\"\n borderRadius=\"10px\"\n [disable]=\"disableProcessing()\"\n ></verbena-button>\n\n <verbena-button\n (click)=\"\n onApproval.emit({\n email: form.get('MailAddress')?.value,\n role: form.get('RoleID')?.value,\n })\n \"\n text=\"Approve\"\n bgColor=\"#28a745\"\n textColor=\"white\"\n border=\"1px solid #28a745\"\n borderRadius=\"10px\"\n pd=\"10px 20px\"\n width=\"114px\"\n height=\"39px\"\n [disable]=\"disableProcessing()\"\n ></verbena-button>\n </div>\n </div>\n</form>\n\n<verben-dialogue\n [showCloseIcon]=\"false\"\n [dismissOutsideClick]=\"false\"\n [closeOnEscape]=\"true\"\n [size]=\"'medium'\"\n [mode]=\"'dialogue'\"\n [customClass]=\"'show-revert-dialogue'\"\n [disableFooter]=\"true\"\n (openModal)=\"onModalOpen($event)\"\n [isVisible]=\"showDialogue\"\n [bodyTemplate]=\"bodyTemplate\"\n (closeModal)=\"onModalClose($event)\"\n>\n <ng-template #bodyTemplate>\n <div class=\"dialog-body flex flex-col gap-4\">\n <div class=\"header font-bold text-2xl\">Delete User Request</div>\n <div class=\"form-container-dialogue flex flex-col gap-2\">\n Are you sure you want to proceed ?\n </div>\n <div class=\"dialog-button-container flex gap-2\">\n <div class=\"flex-1\">\n <verbena-button\n width=\"100%\"\n [fontWeight]=\"'bold'\"\n [bgColor]=\"'#ffffff'\"\n [borderRadius]=\"'4px'\"\n [textColor]=\"'#000'\"\n [border]=\"'1px solid #000'\"\n [text]=\"'Close'\"\n (click)=\"onModalClose($event)\"\n >\n </verbena-button>\n </div>\n <div class=\"flex-1\">\n <verbena-button\n width=\"100%\"\n class=\"verben-primary-button\"\n [text]=\"'Proceed'\"\n (click)=\"onDelete()\"\n >\n </verbena-button>\n </div>\n </div>\n </div>\n </ng-template>\n</verben-dialogue>\n", styles: [".form-container-dialogue{overflow:auto;max-height:60vh}.dialog-body{padding:15px;height:100%}.dialog-button-container{flex:1 1 0;align-items:end}\n"], dependencies: [{ kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.VerbenDialogueComponent, selector: "verben-dialogue", inputs: ["dialogueWidth", "headerTemplate", "bodyTemplate", "footerTemplate", "showCloseIcon", "dismissOutsideClick", "closeOnEscape", "isVisible", "size", "backdropColor", "customClass", "disableFooter", "margin", "padding", "borderRadius", "dialogueBgColor", "width", "closeIconClass", "boxShadow", "enableTransition", "modalData", "mode", "position", "drawerWidth"], outputs: ["openModal", "closeModal"] }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i4.VerbenaInputComponent, selector: "verbena-input", inputs: ["disabled", "label", "placeHolder", "required", "svgPosition", "minLength", "maxLength", "type", "bgColor", "border", "borderRadius", "textColor", "value", "labelPosition", "labelColor", "disable", "readOnly", "min", "max", "showBorder", "showErrorMessage", "errorMessageColor", "errorBorderColor", "errorPosition", "svg", "fontSize", "svgWidth", "svgHeight", "svgColor", "capitalization", "inputContainerClass", "inputFieldClass", "passLength", "inputWrapperClass", "passwordToggle", "customErrorMessages", "icon", "textPass"], outputs: ["valueChange"] }, { kind: "component", type: i4.VerbenaButtonComponent, selector: "verbena-button", inputs: ["type", "text", "icon", "useIcon", "svgPosition", "iconPosition", "bgColor", "textColor", "border", "borderRadius", "pd", "width", "height", "fontSize", "fontWeight", "disable", "svgSize", "weight", "variant", "styleType", "svg", "svgWidth", "svgHeight", "iconColor", "svgColor", "buttonClass", "buttonTextClass", "isLoading", "spinnerSize", "spinnerColor"] }, { kind: "component", type: i4.DropDownComponent, selector: "verben-drop-down", inputs: ["options", "width", "overlayWidth", "showHorizontalLine", "horizontalLineColor", "optionLabel", "optionSubLabel", "optionValue", "placeholder", "invalidMessage", "errorPosition", "loadMoreCaption", "display", "showClear", "lazyLoad", "selectKey", "styleClass", "group", "multiselect", "filter", "avoidDuplication", "filterBy", "debounceTime", "minChar", "refPageSize", "disabled", "required", "load", "asyncLabel", "search"], outputs: ["optionsChange", "onChange", "onClick", "onClear"] }] });
265
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestApprovalTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
266
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UserRequestApprovalTableComponent, isStandalone: true, selector: "lib-user-request-approval-table", inputs: { requests: "requests", columns: "columns", isLoading: "isLoading", showDelete: "showDelete" }, outputs: { view: "view", delete: "delete" }, ngImport: i0, template: "<div class=\"table-container\" [class.loading]=\"isLoading\">\n <lib-data-table [data]=\"requests\" [columns]=\"columns\" dataKey=\"MailAddress\">\n <!-- Select -->\n <ng-container libColumn=\"select\">\n <ng-template #cell let-isSelected=\"isSelected\" let-toggleRowSelection=\"toggleRowSelection\">\n <input type=\"checkbox\" [checked]=\"isSelected\" (change)=\"toggleRowSelection()\" />\n </ng-template>\n </ng-container>\n\n <!-- Created -->\n <ng-container libColumn=\"createdAt\">\n <ng-template #cell let-value>{{ value | date }}</ng-template>\n </ng-container>\n\n <!-- Status -->\n <ng-container libColumn=\"status\">\n <ng-template #cell let-value>\n <lib-user-access-request-status-badge [status]=\"value\"></lib-user-access-request-status-badge>\n </ng-template>\n </ng-container>\n\n <!-- Actions -->\n <ng-container libColumn=\"actions\">\n <ng-template #cell let-row=\"row\">\n <div class=\"flex gap-6\">\n <verben-svg (click)=\"onView(row)\" icon=\"edit\" [width]=\"15\" [height]=\"15\" class=\"cursor-pointer\"></verben-svg>\n <verben-svg *ngIf=\"showDelete\" (click)=\"onDelete(row)\" icon=\"delete\" [width]=\"15\" [height]=\"15\"\n class=\"cursor-pointer\"></verben-svg>\n </div>\n </ng-template>\n </ng-container>\n </lib-data-table>\n\n <div *ngIf=\"requests.length === 0 && !isLoading\" class=\"empty-state\">\n <p>No access requests found</p>\n </div>\n</div>\n", styles: [".table-container{width:100%}.table-container.loading{opacity:.6;pointer-events:none}.empty-state{padding:2rem;text-align:center;color:var(--vbn-color-text-muted)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "ngmodule", type: DataTableModule }, { kind: "component", type: i3.DataTableComponent, selector: "lib-data-table", inputs: ["data", "columns", "dataKey", "formGroupConfig", "groupBy", "useVirtualScroll", "virtualScrollItemSize", "styleConfig"], outputs: ["rowEdit", "rowSave", "rowRevert", "rowDelete", "selectionChange"] }, { kind: "directive", type: i3.ColumnDirective, selector: "[libColumn]", inputs: ["libColumn"] }, { kind: "ngmodule", type: SvgModule }, { kind: "component", type: i3.SvgComponent, selector: "verben-svg", inputs: ["icon", "width", "height", "fill", "stroke", "type", "size"] }, { kind: "component", type: UserAccessRequestStatusBadgeComponent, selector: "lib-user-access-request-status-badge", inputs: ["status"] }] });
657
267
  }
658
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestFormComponent, decorators: [{
268
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestApprovalTableComponent, decorators: [{
659
269
  type: Component,
660
- args: [{ selector: 'lib-user-request-form', template: "<form\n [formGroup]=\"form\"\n class=\"flex flex-col px-3 pb-3 rounded-xl h-full w-full relative space-y-4\"\n>\n <verbena-input\n name=\"MailAddress\"\n label=\"Mail Address:\"\n formControlName=\"MailAddress\"\n [readOnly]=\"true\"\n />\n <verbena-input\n label=\"First Name:\"\n name=\"FirstName\"\n type=\"text\"\n formControlName=\"FirstName\"\n [readOnly]=\"true\"\n />\n <verbena-input\n label=\"Last Name:\"\n name=\"LastName\"\n type=\"text\"\n formControlName=\"LastName\"\n [readOnly]=\"true\"\n />\n <verbena-input\n label=\"OtherName:\"\n name=\"OtherName\"\n type=\"text\"\n formControlName=\"OtherName\"\n [readOnly]=\"true\"\n />\n <!-- <verbena-input\n label=\"Phone Number:\"\n name=\"PhoneNumber\"\n type=\"tel\"\n formControlName=\"PhoneNumber\"\n [readOnly]=\"true\"\n /> -->\n\n <div class=\"space-y-1\">\n <label for=\"role\">Role</label>\n\n <verben-drop-down\n label=\"Role\"\n styleClass=\"w-full\"\n width=\"100%\"\n [multiselect]=\"false\"\n [filter]=\"true\"\n [showClear]=\"true\"\n placeholder=\"\"\n [(options)]=\"roles\"\n id=\"role\"\n optionLabel=\"Name\"\n optionValue=\"Code\"\n formControlName=\"RoleID\"\n class=\"form-control\"\n >\n </verben-drop-down>\n </div>\n\n <!-- buttons -->\n <div class=\"flex justify-between my-2 overflow-auto\">\n <div class=\"flex gap-2\">\n <verbena-button\n (click)=\"switchView.emit()\"\n text=\"Switch To Table\"\n styleType=\"ylw-outline\"\n ></verbena-button>\n\n <verbena-button\n *ngIf=\"showDelete\"\n width=\"114px\"\n height=\"39px\"\n (click)=\"toggleModal()\"\n text=\"Delete\"\n styleType=\"danger\"\n borderRadius=\"10px\"\n ></verbena-button>\n </div>\n\n <div class=\"flex gap-2\">\n <verbena-button\n width=\"114px\"\n height=\"39px\"\n text=\"Reject\"\n styleType=\"danger\"\n borderRadius=\"10px\"\n [disable]=\"disableProcessing()\"\n ></verbena-button>\n\n <verbena-button\n (click)=\"\n onApproval.emit({\n email: form.get('MailAddress')?.value,\n role: form.get('RoleID')?.value,\n })\n \"\n text=\"Approve\"\n bgColor=\"#28a745\"\n textColor=\"white\"\n border=\"1px solid #28a745\"\n borderRadius=\"10px\"\n pd=\"10px 20px\"\n width=\"114px\"\n height=\"39px\"\n [disable]=\"disableProcessing()\"\n ></verbena-button>\n </div>\n </div>\n</form>\n\n<verben-dialogue\n [showCloseIcon]=\"false\"\n [dismissOutsideClick]=\"false\"\n [closeOnEscape]=\"true\"\n [size]=\"'medium'\"\n [mode]=\"'dialogue'\"\n [customClass]=\"'show-revert-dialogue'\"\n [disableFooter]=\"true\"\n (openModal)=\"onModalOpen($event)\"\n [isVisible]=\"showDialogue\"\n [bodyTemplate]=\"bodyTemplate\"\n (closeModal)=\"onModalClose($event)\"\n>\n <ng-template #bodyTemplate>\n <div class=\"dialog-body flex flex-col gap-4\">\n <div class=\"header font-bold text-2xl\">Delete User Request</div>\n <div class=\"form-container-dialogue flex flex-col gap-2\">\n Are you sure you want to proceed ?\n </div>\n <div class=\"dialog-button-container flex gap-2\">\n <div class=\"flex-1\">\n <verbena-button\n width=\"100%\"\n [fontWeight]=\"'bold'\"\n [bgColor]=\"'#ffffff'\"\n [borderRadius]=\"'4px'\"\n [textColor]=\"'#000'\"\n [border]=\"'1px solid #000'\"\n [text]=\"'Close'\"\n (click)=\"onModalClose($event)\"\n >\n </verbena-button>\n </div>\n <div class=\"flex-1\">\n <verbena-button\n width=\"100%\"\n class=\"verben-primary-button\"\n [text]=\"'Proceed'\"\n (click)=\"onDelete()\"\n >\n </verbena-button>\n </div>\n </div>\n </div>\n </ng-template>\n</verben-dialogue>\n", styles: [".form-container-dialogue{overflow:auto;max-height:60vh}.dialog-body{padding:15px;height:100%}.dialog-button-container{flex:1 1 0;align-items:end}\n"] }]
661
- }], ctorParameters: () => [{ type: i1$1.FormBuilder }], propDecorators: { roles: [{
270
+ args: [{ selector: 'lib-user-request-approval-table', standalone: true, imports: [CommonModule, DataTableModule, SvgModule, UserAccessRequestStatusBadgeComponent], template: "<div class=\"table-container\" [class.loading]=\"isLoading\">\n <lib-data-table [data]=\"requests\" [columns]=\"columns\" dataKey=\"MailAddress\">\n <!-- Select -->\n <ng-container libColumn=\"select\">\n <ng-template #cell let-isSelected=\"isSelected\" let-toggleRowSelection=\"toggleRowSelection\">\n <input type=\"checkbox\" [checked]=\"isSelected\" (change)=\"toggleRowSelection()\" />\n </ng-template>\n </ng-container>\n\n <!-- Created -->\n <ng-container libColumn=\"createdAt\">\n <ng-template #cell let-value>{{ value | date }}</ng-template>\n </ng-container>\n\n <!-- Status -->\n <ng-container libColumn=\"status\">\n <ng-template #cell let-value>\n <lib-user-access-request-status-badge [status]=\"value\"></lib-user-access-request-status-badge>\n </ng-template>\n </ng-container>\n\n <!-- Actions -->\n <ng-container libColumn=\"actions\">\n <ng-template #cell let-row=\"row\">\n <div class=\"flex gap-6\">\n <verben-svg (click)=\"onView(row)\" icon=\"edit\" [width]=\"15\" [height]=\"15\" class=\"cursor-pointer\"></verben-svg>\n <verben-svg *ngIf=\"showDelete\" (click)=\"onDelete(row)\" icon=\"delete\" [width]=\"15\" [height]=\"15\"\n class=\"cursor-pointer\"></verben-svg>\n </div>\n </ng-template>\n </ng-container>\n </lib-data-table>\n\n <div *ngIf=\"requests.length === 0 && !isLoading\" class=\"empty-state\">\n <p>No access requests found</p>\n </div>\n</div>\n", styles: [".table-container{width:100%}.table-container.loading{opacity:.6;pointer-events:none}.empty-state{padding:2rem;text-align:center;color:var(--vbn-color-text-muted)}\n"] }]
271
+ }], propDecorators: { requests: [{
662
272
  type: Input
663
- }], showDelete: [{
273
+ }], columns: [{
664
274
  type: Input
665
- }], currentData: [{
275
+ }], isLoading: [{
666
276
  type: Input
667
- }], switchView: [{
668
- type: Output
669
- }], onApproval: [{
277
+ }], showDelete: [{
278
+ type: Input
279
+ }], view: [{
670
280
  type: Output
671
- }], onDeleteRequest: [{
281
+ }], delete: [{
672
282
  type: Output
673
283
  }] } });
674
284
 
675
- class UserAccessRequestStatusBadgeComponent {
676
- status = UserAccessRequestStatus.Pending;
677
- badgeConfig() {
678
- switch (this.status) {
679
- case UserAccessRequestStatus.Approved:
680
- return { bgColor: '#CAE1CC', textColor: '#4ABB54', label: 'Approved' };
681
- case UserAccessRequestStatus.Declined:
682
- return { bgColor: '#FFDFDF', textColor: '#EB5757', label: 'Rejected' };
683
- default:
684
- return { bgColor: '#827C7C', textColor: '#C3C3C3', label: 'Pending' };
685
- }
285
+ /**
286
+ * State for the access-request approval page.
287
+ * Adds the roles list used by the form's role dropdown.
288
+ */
289
+ class UserAccessRequestState extends BaseDataPageState {
290
+ roles$ = new BehaviorSubject([]);
291
+ getRoles$() {
292
+ return this.roles$.asObservable();
686
293
  }
687
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestStatusBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
688
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UserAccessRequestStatusBadgeComponent, selector: "lib-user-access-request-status-badge", inputs: { status: "status" }, ngImport: i0, template: "<verbena-badge\n borderRadius=\"9px\"\n [bgColor]=\"badgeConfig().bgColor\"\n [textColor]=\"badgeConfig().textColor\"\n fontSize=\"12px\"\n [text]=\"badgeConfig().label\"\n [pd]=\"'2px 6px'\"\n></verbena-badge>\n", styles: [""], dependencies: [{ kind: "component", type: i4.VerbenaBadgeComponent, selector: "verbena-badge", inputs: ["text", "bgColor", "textColor", "borderRadius", "pd", "fontSize", "width", "height"] }] });
294
+ getRolesValue() {
295
+ return this.roles$.getValue();
296
+ }
297
+ setRoles(roles) {
298
+ this.roles$.next(roles);
299
+ }
300
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestState, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
301
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestState });
689
302
  }
690
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestStatusBadgeComponent, decorators: [{
691
- type: Component,
692
- args: [{ selector: 'lib-user-access-request-status-badge', template: "<verbena-badge\n borderRadius=\"9px\"\n [bgColor]=\"badgeConfig().bgColor\"\n [textColor]=\"badgeConfig().textColor\"\n fontSize=\"12px\"\n [text]=\"badgeConfig().label\"\n [pd]=\"'2px 6px'\"\n></verbena-badge>\n" }]
693
- }], propDecorators: { status: [{
694
- type: Input
695
- }] } });
303
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestState, decorators: [{
304
+ type: Injectable
305
+ }] });
696
306
 
697
- class UserRequestApprovalComponent {
698
- cdr;
699
- utilService;
700
- envService;
701
- server;
702
- userAccessRequestFacade;
703
- showDelete = false;
704
- cardDataView;
705
- dataView;
706
- columns = columns;
707
- // visibleColumnDef: WritableSignal<ColumnDefinition<UserAccessRequest>[]> =
708
- // signal(columns);
709
- visibleColumnDef = this.columns;
710
- data = signal([]);
711
- styles = baseStyle;
712
- currentData = null;
713
- isGridView = true;
714
- selected = null;
715
- cardData = computed(() => this.data()?.map(({ FirstName, LastName, MailAddress, PhoneNumber, RoleID, RequestStatus, Role, SignupType, }) => ({
716
- selected: false,
717
- title: `${FirstName} ${LastName}`,
718
- data: {
719
- FirstName,
720
- LastName,
721
- MailAddress,
722
- PhoneNumber,
723
- RoleID,
724
- RequestStatus,
725
- Role,
726
- SignupType,
727
- },
728
- body: [{ title: 'Role', value: Role?.Name ?? '' }],
729
- children: [],
730
- })));
731
- roles = signal([]);
732
- constructor(cdr,
733
- // private service: UserRequestApprovalService,
734
- utilService, envService, server, userAccessRequestFacade) {
735
- this.cdr = cdr;
736
- this.utilService = utilService;
737
- this.envService = envService;
738
- this.server = server;
739
- this.userAccessRequestFacade = userAccessRequestFacade;
307
+ /**
308
+ * Service for access-request API operations.
309
+ *
310
+ * The auth API pluralises names and appends a `/{sortParam}/{sortOrder}` suffix
311
+ * to every read, so the base URL seams are overridden accordingly. It also
312
+ * carries an approve/reject workflow and a bespoke delete-by-email endpoint that
313
+ * have no analog in the generic CRUD base.
314
+ *
315
+ * Kept publicly exported under its original name for backward compatibility.
316
+ */
317
+ class UserAccessRequestService extends BaseDataPageService {
318
+ defaultSort = 'CreatedAt/Asc';
319
+ constructor(server, filterHandler, sortHandler) {
320
+ super(server, filterHandler, sortHandler);
740
321
  }
741
- async ngOnInit() {
742
- // this.data = this.service.getMockUserRequests();
743
- this.userAccessRequestFacade.getRequests$().subscribe((data) => {
744
- this.data.set(data);
745
- });
746
- this.userAccessRequestFacade.getRoles$().subscribe((data) => {
747
- this.roles.set(data);
748
- });
749
- try {
750
- this.userAccessRequestFacade.loadRequests();
751
- this.userAccessRequestFacade.loadRoles();
752
- }
753
- catch (error) {
754
- // Handle error
755
- }
322
+ getEndpoint() {
323
+ return 'UserAccessRequest';
756
324
  }
757
- ngOnDestroy() {
758
- this.userAccessRequestFacade.destroy();
325
+ // ===== URL seams =====
326
+ listUrl(skip, limit) {
327
+ return `GetUserAccessRequests/${skip}/${limit}/${this.defaultSort}`;
759
328
  }
760
- handleSearch(event) {
761
- this.userAccessRequestFacade.search(event.value);
329
+ searchTermUrl(term, skip, limit) {
330
+ return `GetUserAccessRequestWithParam/${term}/${skip}/${limit}/${this.defaultSort}`;
762
331
  }
763
- async loadMore() {
764
- try {
765
- await this.userAccessRequestFacade.loadMore();
766
- }
767
- catch (error) {
768
- // Handle error
769
- }
332
+ filterSearchUrl(skip, limit) {
333
+ return `SearchUserAccessRequest/${skip}/${limit}/${this.defaultSort}`;
770
334
  }
771
- // To search with parameter:
772
- async searchRequests(param) {
773
- try {
774
- await this.userAccessRequestFacade.loadRequestsWithParam(param);
775
- }
776
- catch (error) {
777
- // Handle error
778
- }
335
+ saveUrl() {
336
+ return 'SaveUserAccessRequests';
779
337
  }
780
- async onApprove(approvalData) {
781
- const data = this.data().find(({ MailAddress }) => MailAddress === approvalData.email);
782
- if (data) {
783
- await this.userAccessRequestFacade.approveRequest({
784
- ...data,
785
- RoleID: approvalData.role,
786
- });
787
- }
788
- }
789
- async onDelete(deletionData) {
790
- const data = this.data().find(({ MailAddress }) => MailAddress === deletionData.email);
791
- this.utilService.sendBI(true);
792
- const res = await this.server.get(`DeleteUserRequestWithUser/${deletionData.email}`, this.envService.environment.AuthenticationAPI);
793
- this.utilService.sendBI(false);
794
- if (res instanceof ErrorResponse) {
795
- }
796
- else {
797
- if (data) {
798
- this.data.update((items) => items.filter((x) => x.Code !== data.Code));
338
+ // ===== Delete (bespoke: by email, also removes the associated signup) =====
339
+ async deleteItems(emails) {
340
+ for (const email of emails) {
341
+ const res = await this.server.get(`DeleteUserRequestWithUser/${email}`, this.baseUrl);
342
+ if (res instanceof ErrorResponse) {
343
+ return res;
799
344
  }
800
- this.cardDataView.clearData();
801
- }
802
- }
803
- openDetailView(mailAddress) {
804
- const cardItem = this.getCardDataByMailAddress(mailAddress);
805
- if (cardItem && this.cardDataView) {
806
- this.dataView.toggleView();
807
- // First reset all selections
808
- this.cardData().forEach((item) => {
809
- item.selected = false;
810
- if (item.children) {
811
- item.children.forEach((child) => (child.selected = false));
812
- }
813
- });
814
- // Set the selected item
815
- cardItem.selected = true;
816
- this.currentData = this.cardDataView.onItemClick(cardItem);
817
- // Force change detection if needed
818
- // this.changeDetectorRef.detectChanges();
819
345
  }
346
+ return true;
820
347
  }
821
- getCardDataByMailAddress(mailAddress) {
822
- return this.cardData().find(({ data }) => data.MailAddress === mailAddress);
348
+ // ===== Workflow =====
349
+ /** Approve (isApproval=true) or reject (false) the given requests. */
350
+ processRequests(requests, isApproval) {
351
+ return this.server.post(`UserAccessRequestAction/${isApproval}`, requests);
823
352
  }
824
- onViewChange(isGridView) {
825
- this.isGridView = isGridView;
826
- // Reset selection when switching views
827
- if (this.currentData) {
828
- this.cardDataView?.clearData();
829
- this.currentData = null;
830
- }
831
- console.log('SELECTED', this.selected);
832
- if (this.selected) {
833
- this.cardDataView.onItemClick(this.selected);
353
+ // ===== Lookups =====
354
+ async getRoles() {
355
+ const res = await this.server.get('GetRoles/0/0');
356
+ if (res instanceof ErrorResponse) {
357
+ return [];
834
358
  }
359
+ return res.Result ?? [];
835
360
  }
836
- openFormView(index) {
837
- const item = this.cardData()[index];
838
- console.log('ITEM = ', item);
839
- }
840
- handleExport(exportedData) {
841
- console.log('Exported data:', exportedData);
842
- this.downloadCSV(exportedData);
843
- }
844
- /**
845
- * @param data Simple csv export for testing
846
- */
847
- downloadCSV(data) {
848
- const headers = Object.keys(data[0]);
849
- const csvContent = [
850
- headers.join(','),
851
- ...data.map((row) => headers.map((header) => row[header]).join(',')),
852
- ].join('\n');
853
- const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
854
- const link = document.createElement('a');
855
- if (link.download !== undefined) {
856
- const url = URL.createObjectURL(blob);
857
- link.setAttribute('href', url);
858
- link.setAttribute('download', 'export.csv');
859
- link.style.visibility = 'hidden';
860
- document.body.appendChild(link);
861
- link.click();
862
- document.body.removeChild(link);
361
+ async getTags() {
362
+ const res = await this.server.get('GetTags/0/0');
363
+ if (res instanceof ErrorResponse) {
364
+ return [];
863
365
  }
366
+ return res.Result ?? [];
864
367
  }
865
- onSelectionChange(selectedRows) {
866
- console.log('Selection changed:', selectedRows);
867
- // Handle the selection change
868
- }
368
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestService, deps: [{ token: i1$2.HttpWebRequestService }, { token: i2.FilterHandlerService }, { token: i2.SortHandlerService }], target: i0.ɵɵFactoryTarget.Injectable });
369
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestService });
370
+ }
371
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestService, decorators: [{
372
+ type: Injectable
373
+ }], ctorParameters: () => [{ type: i1$2.HttpWebRequestService }, { type: i2.FilterHandlerService }, { type: i2.SortHandlerService }] });
374
+
375
+ /**
376
+ * Facade for the access-request approval page.
377
+ * Adds the approve/reject workflow on top of the generic base facade.
378
+ */
379
+ class UserAccessRequestFacade extends BaseDataPageFacade {
380
+ requestService;
381
+ blankRequest;
382
+ constructor(state, service, searchHandler, utilService) {
383
+ super(state, service, searchHandler, utilService, USER_ACCESS_REQUEST_CONFIG);
384
+ this.requestService = service;
385
+ }
386
+ // ===== Custom observables =====
387
+ get roles$() {
388
+ return this.state.getRoles$();
389
+ }
390
+ // ===== View management =====
869
391
  /**
870
- * Copied over from card view doc until I understand usage
392
+ * Select the request to show in the form. The form template binds to
393
+ * currentCardData(), so route selection through selectCard (reusing the
394
+ * matching card from the loaded list when present) rather than a separate
395
+ * selected-request stream that nothing reads.
871
396
  */
872
- selectedColumnCount = 0;
873
- selectedFilterTableCount = 0;
874
- isOPen = true;
875
- selectedSortCount = 0;
876
- showColumn = false;
877
- showSort = false;
878
- selectedAll = false;
879
- visibleColumns = columns.map((col) => ({
880
- checked: true,
881
- name: typeof col.header === 'string' ? col.header : col.id,
882
- type: DataFilterType.Bool,
883
- }));
884
- filterArray = [
885
- {
886
- name: 'Name',
887
- type: DataFilterType.String,
888
- checked: false,
889
- },
890
- {
891
- name: 'MailAddress',
892
- type: DataFilterType.String,
893
- checked: false,
894
- },
895
- {
896
- name: 'PhoneNumber',
897
- type: DataFilterType.String,
898
- checked: false,
899
- },
900
- {
901
- name: 'CreatedAt',
902
- type: DataFilterType.Date,
903
- checked: false,
904
- },
905
- ];
906
- sortOptions = [
907
- {
908
- name: 'Name',
909
- type: DataFilterType.String,
910
- checked: false,
911
- },
912
- {
913
- name: 'MailAddress',
914
- type: DataFilterType.String,
915
- checked: false,
916
- },
917
- {
918
- name: 'PhoneNumber',
919
- type: DataFilterType.String,
920
- checked: false,
921
- },
922
- {
923
- name: 'CreatedAt',
924
- type: DataFilterType.Date,
925
- checked: true,
926
- },
927
- ];
928
- clearData() {
929
- this.currentData = {};
930
- }
931
- onColumnChange(event) {
932
- this.showColumn = event;
933
- }
934
- onSortChange(event) {
935
- this.showSort = event;
936
- console.log(event);
937
- }
938
- onColumnsUpdated(updatedColumns) {
939
- console.log(updatedColumns);
940
- const updatedColumnDef = [];
941
- updatedColumns.forEach((col) => {
942
- const matchingCol = this.columns.find((column) => column.header === col.name || column.id === col.name);
943
- if (matchingCol) {
944
- updatedColumnDef.push(matchingCol);
945
- }
946
- });
947
- console.log(updatedColumnDef);
948
- this.visibleColumnDef = [...updatedColumnDef];
949
- this.cdr.markForCheck();
950
- this.onColumnChange(false);
951
- this.selectedColumnCount = updatedColumns.length;
397
+ openForm(request) {
398
+ if (!request) {
399
+ this.selectCard(null);
400
+ return;
401
+ }
402
+ const existing = this.cardData().find((card) => card.data?.MailAddress === request.MailAddress);
403
+ this.selectCard(existing ?? this.getConfig().cardDataMapper(request));
952
404
  }
953
- onSortUpdated(updatedSorts) {
954
- this.onSortChange(false);
955
- this.selectedSortCount = updatedSorts.length;
956
- console.log(updatedSorts);
405
+ closeForm() {
406
+ this.selectCard(null);
957
407
  }
958
- onStateChange(event) {
959
- console.log(`State changed for ${event.key}:`, event.value);
408
+ handleColumnsUpdated(columns) {
409
+ this.updateVisibleColumns(columns);
960
410
  }
961
- onFilterSet(event) {
962
- console.log(event);
963
- this.userAccessRequestFacade.filter(event);
411
+ // ===== Roles =====
412
+ async loadRoles() {
413
+ const roles = await this.requestService.getRoles();
414
+ this.state.setRoles(roles);
415
+ }
416
+ // ===== Approval workflow =====
417
+ async approveRequest(request, roleId) {
418
+ return this.process({ ...request, RoleID: roleId }, true, 'Request approved');
419
+ }
420
+ async rejectRequest(request, comment) {
421
+ return this.process({ ...request, Comment: comment }, false, 'Request rejected');
422
+ }
423
+ async process(request, isApproval, successMessage) {
424
+ this.state.setUpdating(true);
425
+ const result = await this.requestService.processRequests([request], isApproval);
426
+ this.state.setUpdating(false);
427
+ if (result instanceof ErrorResponse) {
428
+ this.utilService.showError(result.ErrorMsg);
429
+ return false;
430
+ }
431
+ this.utilService.showSuccess(successMessage);
432
+ const updated = result[0] ?? {
433
+ ...request,
434
+ RequestStatus: isApproval
435
+ ? UserAccessRequestStatus.Approved
436
+ : UserAccessRequestStatus.Declined,
437
+ };
438
+ this.state.updateItem(updated);
439
+ this.closeForm();
440
+ return true;
441
+ }
442
+ async deleteRequest(request) {
443
+ // Delete is keyed by email (DeleteUserRequestWithUser/{email}), which does
444
+ // not match the list's Id/Code key, so call the service directly and let
445
+ // refresh() reconcile the list rather than the base facade's key-based
446
+ // removeItem (which would be a no-op for an email).
447
+ this.state.setUpdating(true);
448
+ const result = await this.requestService.deleteItems([request.MailAddress]);
449
+ this.state.setUpdating(false);
450
+ if (result instanceof ErrorResponse) {
451
+ this.utilService.showError(result.ErrorMsg);
452
+ return false;
453
+ }
454
+ this.closeForm();
455
+ await this.refresh();
456
+ return true;
457
+ }
458
+ // ===== Empty placeholder for the form when nothing is selected =====
459
+ get emptyRequest() {
460
+ return (this.blankRequest ??= this.createEmptyRequest());
461
+ }
462
+ createEmptyRequest() {
463
+ return {
464
+ id: Date.now().toString(),
465
+ Id: '',
466
+ Code: '',
467
+ TenantId: '',
468
+ ServiceName: '',
469
+ CreatedAt: new Date(),
470
+ UpdatedAt: new Date(),
471
+ DataState: ObjectState.New,
472
+ Password: '',
473
+ FirstName: '',
474
+ LastName: '',
475
+ MailAddress: '',
476
+ IsSeeded: false,
477
+ Status: Status.Active,
478
+ SignupType: OAuthType.InApp,
479
+ RequestStatus: UserAccessRequestStatus.Pending,
480
+ };
481
+ }
482
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestFacade, deps: [{ token: UserAccessRequestState }, { token: UserAccessRequestService }, { token: i2.SearchHandlerService }, { token: i1$2.UtilService }], target: i0.ɵɵFactoryTarget.Injectable });
483
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestFacade });
484
+ }
485
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserAccessRequestFacade, decorators: [{
486
+ type: Injectable
487
+ }], ctorParameters: () => [{ type: UserAccessRequestState }, { type: UserAccessRequestService }, { type: i2.SearchHandlerService }, { type: i1$2.UtilService }] });
488
+
489
+ /**
490
+ * Access-request approval page. Lists requests in table/card views and drives
491
+ * the approve/reject/delete workflow through the facade.
492
+ */
493
+ class UserRequestApprovalComponent {
494
+ facade;
495
+ showDelete = false;
496
+ vdv;
497
+ cardDataView;
498
+ destroyRef = inject(DestroyRef);
499
+ constructor(facade) {
500
+ this.facade = facade;
964
501
  }
965
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestApprovalComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.UtilService }, { token: i1.EnvironmentService }, { token: i1.HttpWebRequestService }, { token: UserAccessRequestFacade }], target: i0.ɵɵFactoryTarget.Component });
966
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UserRequestApprovalComponent, selector: "lib-user-request-approval", inputs: { showDelete: "showDelete" }, viewQueries: [{ propertyName: "cardDataView", first: true, predicate: ["vdcv"], descendants: true }, { propertyName: "dataView", first: true, predicate: ["vdv"], descendants: true }], ngImport: i0, template: "<div class=\"space-y-8\">\n <verben-data-view\n #vdv\n [viewState]=\"{\n isSearch: true,\n isColumn: true,\n isFilter: true,\n isSort: true,\n isExport: true,\n isSelect: true,\n isCreate: false,\n isToggle: true,\n }\"\n [buttonClass]=\"'my-custom-button-class'\"\n [iconClass]=\"'my-icon-class'\"\n [activeIconClass]=\"'my-active-icon-class'\"\n [selectedColumnCount]=\"0\"\n [selectedSortCount]=\"0\"\n [selectedFilterTableCount]=\"0\"\n (viewChange)=\"onViewChange($event)\"\n (stateChange)=\"onStateChange($event)\"\n (onSearchChange)=\"handleSearch($event)\"\n >\n <div table-content>\n <lib-data-table\n [data]=\"data()\"\n [columns]=\"visibleColumnDef\"\n [styleConfig]=\"styles\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n <ng-container libColumn=\"createdAt\">\n <ng-template #cell let-value>\n {{ value | date }}\n </ng-template>\n </ng-container>\n <ng-container libColumn=\"status\">\n <ng-template #cell let-value>\n <lib-user-access-request-status-badge\n [status]=\"value\"\n ></lib-user-access-request-status-badge>\n </ng-template>\n </ng-container>\n\n <ng-container libColumn=\"actions\">\n <ng-template #cell let-value let-deleteRow>\n <div class=\"flex gap-6\">\n <verben-svg\n (click)=\"openDetailView(value)\"\n icon=\"edit\"\n [width]=\"15\"\n [height]=\"15\"\n class=\"cursor-pointer\"\n ></verben-svg>\n <!-- <button (click)=\"openDetailView(value)\">Open</button>\n <button (click)=\"selected = cardData[rowIndex]; vdv.toggleView()\">\n Switch\n </button> -->\n <verben-svg\n icon=\"delete\"\n [width]=\"15\"\n [height]=\"15\"\n (click)=\"deleteRow()\"\n class=\"cursor-pointer\"\n ></verben-svg>\n </div>\n </ng-template>\n <!-- <ng-template #header>\n <strong>Actions</strong>\n </ng-template> -->\n </ng-container>\n </lib-data-table>\n </div>\n <div card-content>\n <verben-card-data-view\n borderRadius=\"12px\"\n (loadMoreClick)=\"loadMore()\"\n #vdcv\n dataId=\"MailAddress\"\n border=\"5px\"\n [cardDataList]=\"cardData()\"\n rbgColor=\"#f5f6f9\"\n mg=\"20px\"\n >\n <verben-left-card-data-view class=\"space-y-7\">\n <verben-left-card-data\n #vlcd\n [parent]=\"vdcv\"\n dataId=\"MailAddress\"\n class=\"bg-secondary-100 rounded-xl border-primary border-[1px]\"\n [cardDataList]=\"cardData()\"\n >\n <ng-template #card let-item>\n <div (click)=\"currentData = vdcv.onItemClick(item)\" class=\"flex\">\n <div\n (click)=\"currentData = vdcv.onItemClick(item)\"\n class=\"flex cursor-pointer w-full bg-secondary rounded-xl\"\n >\n <div\n class=\"w-3 rounded-xl rounded-tr-none rounded-br-none\"\n [ngClass]=\"\n item.selected ? 'bg-primary' : 'bg-secondary-200'\n \"\n ></div>\n <div class=\"flex-1 flex items-end gap-y-2 gap-x-4 p-4\">\n <div class=\"flex-1 space-y-2\">\n <span\n class=\"font-semibold text-[#404040] flex-1 truncate\"\n >{{ item.title }}</span\n >\n\n <p class=\"flex items-center my-1 gap-1\">\n <label for=\"phone\" class=\"text-[10px] font-light\"\n >Medium:</label\n >\n <span id=\"phone\" class=\"text-sm truncate\">{{\n item.data.SignupType\n }}</span>\n </p>\n </div>\n\n <div class=\"grid gap-y-1\">\n <!-- <p class=\"grid\">\n <label\n for=\"role\"\n class=\"text-[10px] font-light text-[#404040]\"\n >Medium:</label\n >\n <span id=\"role\" class=\"text-sm font-medium\">{{\n item.data?.SignupType\n }}</span>\n </p> -->\n\n <lib-user-access-request-status-badge\n [status]=\"item.data.RequestStatus\"\n ></lib-user-access-request-status-badge>\n </div>\n </div>\n </div>\n </div>\n </ng-template>\n </verben-left-card-data>\n </verben-left-card-data-view>\n\n <verben-right-card-data-view>\n <ng-template #parent>\n <lib-user-request-form\n [roles]=\"roles()\"\n [showDelete]=\"showDelete\"\n [currentData]=\"currentData\"\n (switchView)=\"vdv.toggleView()\"\n (onDeleteRequest)=\"onDelete($event)\"\n (onApproval)=\"onApprove($event)\"\n ></lib-user-request-form>\n </ng-template>\n </verben-right-card-data-view>\n\n <verben-card-data-view-footer class=\"w-full\">\n <div\n [ngClass]=\"{\n 'flex gap-2 items-center': true,\n 'justify-end': true,\n }\"\n >\n <!-- <verbena-button\n *ngIf=\"!vdcv.hasCurrentItem()\"\n [fontWeight]=\"'bold'\"\n [bgColor]=\"'#8E8D87'\"\n [borderRadius]=\"'4px'\"\n [textColor]=\"'#fff'\"\n [pd]=\"'9.79px 37.28px'\"\n [text]=\"'Delete'\"\n >\n </verbena-button> -->\n <div\n class=\"flex {{\n vdcv.hasCurrentItem() ? 'flex-1' : ''\n }} justify-end items-center gap-5\"\n >\n <span class=\"paginator-text\"\n >{{ cardData().length }} records loaded</span\n >\n <button\n (click)=\"loadMore()\"\n class=\"load-more view-links text-[#3479E9] underline\"\n >\n Load more\n </button>\n </div>\n </div>\n </verben-card-data-view-footer>\n </verben-card-data-view>\n </div>\n <div column-content>\n <verben-visible-column\n (columnsUpdated)=\"onColumnsUpdated($event)\"\n [border]=\"'1px solid rgba(212, 160, 7, 1)'\"\n borderRadius=\"10px\"\n boxShadow=\"2px 2px 2px 0px silver\"\n bgColor=\"white\"\n width=\"400px\"\n textColor=\"black\"\n pd=\"1rem\"\n primaryColor=\"#FFE681\"\n secondaryColor=\"#3479E9\"\n [columns]=\"visibleColumns\"\n [displayedColumns]=\"5\"\n ></verben-visible-column>\n </div>\n <div filter-content>\n <verben-table-filter\n (filtersApplied)=\"onFilterSet($event)\"\n [border]=\"'1px solid rgba(212, 160, 7, 1)'\"\n borderRadius=\"10px\"\n boxShadow=\"2px 2px 2px 0px silver\"\n bgColor=\"white\"\n width=\"420px\"\n textColor=\"black\"\n pd=\"1rem\"\n primaryColor=\"#FFE681\"\n secondaryColor=\"#3479E9\"\n tertiaryColor=\"#404040\"\n [filterOptions]=\"filterArray\"\n [maxFilterLength]=\"3\"\n ></verben-table-filter>\n </div>\n <div sort-content>\n <verben-sort-table\n (selectedOptions)=\"onSortUpdated($event)\"\n [border]=\"'1px solid rgba(212, 160, 7, 1)'\"\n borderRadius=\"10px\"\n boxShadow=\"2px 2px 2px 0px silver\"\n bgColor=\"white\"\n width=\"400px\"\n textColor=\"black\"\n pd=\"1rem\"\n primaryColor=\"#FFE681\"\n secondaryColor=\"#3479E9\"\n tertiaryColor=\"#404040\"\n [enableDragAndDrop]=\"true\"\n [sortOptions]=\"sortOptions\"\n ></verben-sort-table>\n </div>\n <div export-content>\n <lib-data-xport\n [columns]=\"visibleColumnDef\"\n [data]=\"data()\"\n (exportDataEvent)=\"handleExport($event)\"\n >\n </lib-data-xport>\n </div>\n </verben-data-view>\n\n <div *ngIf=\"vdv.isTableView\" class=\"flex gap-2 justify-end\">\n <!-- <div class=\"message-log-button-container flex justify-between\">\n <verbena-button\n [fontWeight]=\"'bold'\"\n [bgColor]=\"'#8E8D87'\"\n [borderRadius]=\"'4px'\"\n [textColor]=\"'#fff'\"\n [pd]=\"'9.79px 37.28px'\"\n [text]=\"'Delete'\"\n >\n </verbena-button>\n </div> -->\n <div class=\"flex justify-end items-center gap-5\">\n <span class=\"paginator-text\">{{ cardData().length }} records loaded</span>\n <button\n (click)=\"loadMore()\"\n class=\"load-more view-links text-[#3479E9] underline\"\n >\n Load more\n </button>\n </div>\n </div>\n</div>\n", styles: ["*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}:before,:after{--tw-content: \"\"}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.visible{visibility:visible}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.bottom-8{bottom:2rem}.left-0{left:0}.right-0{right:0}.float-right{float:right}.m-0{margin:0}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-0{margin-bottom:0}.mb-2{margin-bottom:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-\\[100\\%\\]{height:100%}.h-\\[max-content\\]{height:-moz-max-content;height:max-content}.h-full{height:100%}.w-3{width:.75rem}.w-full{width:100%}.flex-1{flex:1 1 0%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-20{gap:5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-1{row-gap:.25rem}.gap-y-2{row-gap:.5rem}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-7>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.75rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-all{word-break:break-all}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-br-none{border-bottom-right-radius:0}.rounded-tr-none{border-top-right-radius:0}.border,.border-\\[1px\\]{border-width:1px}.border-primary{--tw-border-opacity: 1;border-color:rgb(212 160 7 / var(--tw-border-opacity, 1))}.border-secondary-200{border-color:#d7dbe6}.bg-primary{--tw-bg-opacity: 1;background-color:rgb(212 160 7 / var(--tw-bg-opacity, 1))}.bg-secondary{--tw-bg-opacity: 1;background-color:rgb(232 234 241 / var(--tw-bg-opacity, 1))}.bg-secondary-100{--tw-bg-opacity: 1;background-color:rgb(245 246 249 / var(--tw-bg-opacity, 1))}.bg-secondary-200{background-color:#d7dbe6}.p-3{padding:.75rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pr-1{padding-right:.25rem}.\\!text-\\[10px\\]{font-size:10px!important}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\\[10px\\]{font-size:10px}.text-\\[12px\\]{font-size:12px}.text-\\[22px\\]{font-size:22px}.text-\\[24px\\]{font-size:24px}.text-\\[2rem\\]{font-size:2rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.lowercase{text-transform:lowercase}.leading-6{line-height:1.5rem}.leading-\\[12\\.1px\\]{line-height:12.1px}.leading-\\[16\\.6px\\]{line-height:16.6px}.leading-\\[19\\.5px\\]{line-height:19.5px}.leading-\\[29\\.05px\\]{line-height:29.05px}.leading-\\[33px\\]{line-height:33px}.leading-\\[48px\\]{line-height:48px}.text-\\[\\#333\\]{--tw-text-opacity: 1;color:rgb(51 51 51 / var(--tw-text-opacity, 1))}.text-\\[\\#3479E9\\]{--tw-text-opacity: 1;color:rgb(52 121 233 / var(--tw-text-opacity, 1))}.text-\\[\\#404040\\]{--tw-text-opacity: 1;color:rgb(64 64 64 / var(--tw-text-opacity, 1))}.text-\\[black\\]{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-muted{color:#0009}.underline{text-decoration-line:underline}.outline-none{outline:2px solid transparent;outline-offset:2px}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.view-links{color:#3479e9;cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.DataTableComponent, selector: "lib-data-table", inputs: ["data", "columns", "dataKey", "formGroupConfig", "groupBy", "useVirtualScroll", "virtualScrollItemSize", "styleConfig"], outputs: ["rowEdit", "rowSave", "rowRevert", "rowDelete", "selectionChange"] }, { kind: "directive", type: i4.ColumnDirective, selector: "[libColumn]", inputs: ["libColumn"] }, { kind: "component", type: i4.SvgComponent, selector: "verben-svg", inputs: ["icon", "width", "height", "fill", "stroke", "type", "size"] }, { kind: "component", type: i4.DataViewComponent, selector: "verben-data-view", inputs: ["buttonClass", "iconClass", "activeIconClass", "columnCustomClass", "filterCustomClass", "sortCustomClass", "extendCustomClass", "exportCustomClass", "selectCustomClass", "importCustomClass", "zIndex", "createCustomClass", "tableIcon", "cardIcon", "cardClass", "tableClass", "searchKey", "searchValue", "viewState", "searchTemplate", "importTemplate", "columnTemplate", "extendTemplate", "filterTemplate", "sortTemplate", "children", "exportTemplate", "createTemplate", "selectedColumnCount", "selectedSortCount", "selectedFilterTableCount", "inputWidth", "milliseconds", "showColumnChild", "showSortChild", "showFilterChild", "showImportChild", "showExportChild", "showExtendChild", "create", "showSelected", "isTableView"], outputs: ["viewChange", "stateChange", "onSearchChange"] }, { kind: "component", type: i4.CardDataViewComponent, selector: "verben-card-data-view", inputs: ["pd", "mg", "lHeight", "rHeight", "rWidth", "lWidth", "textColor", "lbgColor", "rbgColor", "border", "display", "borderRadius", "activeCss", "inActiveCss", "displayAsRow", "cardDataList", "dataId", "totalRecords", "footer", "noOfVisibleChildren", "showEditIcon", "useVirtualScroll", "virtualScrollItemSize", "onItemClick", "onCardChildClick"], outputs: ["editClicked", "loadMoreClick"] }, { kind: "component", type: i4.LeftCardDataComponent, selector: "verben-left-card-data", inputs: ["pd", "mg", "height", "weight", "activeCss", "inActiveCss", "cardDataList", "iconCollapse", "iconExpanded", "parent", "useVirtualScroll", "virtualScrollItemSize", "dataId"] }, { kind: "component", type: i4.LeftCardDataViewComponent, selector: "verben-left-card-data-view", inputs: ["cardDataList"] }, { kind: "component", type: i4.RightCardDataViewComponent, selector: "verben-right-card-data-view", inputs: ["parentData", "chilData", "meth"] }, { kind: "component", type: i4.CardDataViewFooterComponent, selector: "verben-card-data-view-footer" }, { kind: "component", type: i4.SortTableComponent, selector: "verben-sort-table", inputs: ["enableDragAndDrop", "sortOptions", "resetText", "displayedOptions", "propertyText", "showMoreText", "sortButtonText", "pd", "mg", "height", "width", "bgColor", "boxShadow", "textColor", "primaryColor", "secondaryColor", "tertiaryColor", "border", "borderRadius", "selectWidth", "containerHeight"], outputs: ["selectedOptions", "resetSortData"] }, { kind: "component", type: i4.VisibleColumnComponent, selector: "verben-visible-column", inputs: ["columns", "items", "enableDragAndDrop", "displayedColumns", "showMore", "pd", "mg", "height", "width", "bgColor", "boxShadow", "textColor", "primaryColor", "secondaryColor", "tertiaryColor", "border", "borderRadius", "selectWidth", "closeColumn"], outputs: ["columnsUpdated", "resetFilter"] }, { kind: "component", type: i4.TableFilterComponent, selector: "verben-table-filter", inputs: ["filterOptions", "pd", "mg", "height", "width", "bgColor", "boxShadow", "textColor", "primaryColor", "secondaryColor", "tertiaryColor", "border", "borderRadius", "selectWidth", "maxFilterLength", "tooltip"], outputs: ["filtersApplied", "resetSortData"] }, { kind: "component", type: i4.DataXportComponent, selector: "lib-data-xport", inputs: ["data", "columns", "useImportKey", "dataFetchUrl", "dataQueryParameters", "dataQueryFunction"], outputs: ["exportDataEvent", "exportDataRangeEvent"] }, { kind: "component", type: UserRequestFormComponent, selector: "lib-user-request-form", inputs: ["roles", "showDelete", "currentData"], outputs: ["switchView", "onApproval", "onDeleteRequest"] }, { kind: "component", type: UserAccessRequestStatusBadgeComponent, selector: "lib-user-access-request-status-badge", inputs: ["status"] }, { kind: "pipe", type: i3.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
502
+ ngOnInit() {
503
+ this.facade.cardSelectionCleared$
504
+ .pipe(takeUntilDestroyed(this.destroyRef))
505
+ .subscribe(() => this.clearCardView());
506
+ this.facade.loadData();
507
+ this.facade.loadRoles();
508
+ }
509
+ clearCardView() {
510
+ const view = this.cardDataView;
511
+ if (!view)
512
+ return;
513
+ view.clearData();
514
+ if (view.rightContent) {
515
+ view.rightContent.parentData = null;
516
+ }
517
+ }
518
+ // ===== Filter / Sort / Search =====
519
+ onFilterApplied(filters) {
520
+ this.facade.applyFilters(filters);
521
+ }
522
+ onSortApplied(sorts) {
523
+ this.facade.applySorts(sorts);
524
+ }
525
+ onSearch(term) {
526
+ this.facade.onSearch(term);
527
+ }
528
+ onColumnsUpdated(columns) {
529
+ this.facade.handleColumnsUpdated(columns);
530
+ }
531
+ onLoadMore() {
532
+ this.facade.loadMore();
533
+ }
534
+ // ===== Table events =====
535
+ onViewRequest(request) {
536
+ this.facade.openForm(request);
537
+ this.vdv.toggleView();
538
+ }
539
+ requestCardClicked(selectedCard) {
540
+ this.facade.selectCard(selectedCard);
541
+ }
542
+ onDeleteFromTable(request) {
543
+ const name = `${request.FirstName} ${request.LastName}`.trim() || request.MailAddress;
544
+ if (confirm(`Delete request from ${name}?`)) {
545
+ this.facade.deleteRequest(request);
546
+ }
547
+ }
548
+ // ===== Form events =====
549
+ onApprove(event) {
550
+ this.facade.approveRequest(event.request, event.roleId);
551
+ this.vdv.toggleView();
552
+ }
553
+ onReject(event) {
554
+ this.facade.rejectRequest(event.request, event.comment);
555
+ this.vdv.toggleView();
556
+ }
557
+ onDeleteRequest(request) {
558
+ this.facade.deleteRequest(request);
559
+ this.vdv.toggleView();
560
+ }
561
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestApprovalComponent, deps: [{ token: UserAccessRequestFacade }], target: i0.ɵɵFactoryTarget.Component });
562
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UserRequestApprovalComponent, isStandalone: true, selector: "lib-user-request-approval", inputs: { showDelete: "showDelete" }, providers: [
563
+ UserAccessRequestFacade,
564
+ UserAccessRequestState,
565
+ UserAccessRequestService,
566
+ SearchHandlerService,
567
+ ], viewQueries: [{ propertyName: "vdv", first: true, predicate: ["vdv"], descendants: true }, { propertyName: "cardDataView", first: true, predicate: ["vdcv"], descendants: true }], ngImport: i0, template: "<div class=\"user-request-approval-page space-y-8\">\n <verben-data-view #vdv [viewState]=\"{\n isSearch: true,\n isColumn: true,\n isFilter: true,\n isSort: true,\n isExport: true,\n isCreate: false,\n isToggle: true,\n isImport: false,\n }\" [selectedColumnCount]=\"(facade.visibleColumns$ | async)?.length ?? 0\"\n [selectedSortCount]=\"(facade.sorts$ | async)?.length ?? 0\"\n [selectedFilterTableCount]=\"(facade.filters$ | async)?.length ?? 0\"\n (onSearchChange)=\"onSearch($event.value)\">\n\n <!-- Table View -->\n <div table-content>\n <lib-user-request-approval-table [requests]=\"(facade.items$ | async) ?? []\"\n [columns]=\"(facade.visibleColumns$ | async) ?? []\" [isLoading]=\"(facade.isLoading$ | async) ?? false\"\n [showDelete]=\"showDelete\" (view)=\"onViewRequest($event)\" (delete)=\"onDeleteFromTable($event)\">\n </lib-user-request-approval-table>\n </div>\n\n <!-- Card View -->\n <div card-content>\n <verben-card-data-view #vdcv [cardDataList]=\"facade.cardData()\" borderRadius=\"12px\" dataId=\"MailAddress\"\n [border]=\"'5px'\" rbgColor=\"var(--vbn-color-surface-alt)\" mg=\"20px\">\n\n <verben-left-card-data-view class=\"space-y-7\">\n <verben-left-card-data #vlcd [parent]=\"vdcv\" dataId=\"MailAddress\"\n class=\"bg-secondary-100 rounded-xl border-primary border-[1px]\" [cardDataList]=\"facade.cardData()\">\n <ng-template #card let-item>\n <div (click)=\"requestCardClicked(vdcv.onItemClick(item))\" class=\"cursor-pointer h-[max-content]\">\n <lib-user-request-approval-card [request]=\"item.data\" [selected]=\"item.selected\">\n </lib-user-request-approval-card>\n </div>\n </ng-template>\n </verben-left-card-data>\n </verben-left-card-data-view>\n\n <verben-right-card-data-view>\n <ng-template #parent>\n <lib-user-request-approval-form [request]=\"facade.currentCardData()?.data ?? facade.emptyRequest\"\n [roles]=\"(facade.roles$ | async) ?? []\" [showDelete]=\"showDelete\" (approve)=\"onApprove($event)\"\n (reject)=\"onReject($event)\" (deleteRequest)=\"onDeleteRequest($event)\"\n (switchToTable)=\"vdv.toggleView()\">\n </lib-user-request-approval-form>\n </ng-template>\n </verben-right-card-data-view>\n\n <verben-card-data-view-footer class=\"w-full\">\n <div class=\"flex gap-2 items-center justify-end\">\n <div class=\"flex justify-end items-center gap-5\">\n <span class=\"paginator-text\">{{ (facade.items$ | async)?.length }} records loaded</span>\n <button class=\"load-more view-links text-[var(--vbn-color-border-focus)] underline\"\n [disabled]=\"(facade.isLoading$ | async) ?? false\" (click)=\"onLoadMore()\">Load More</button>\n </div>\n </div>\n </verben-card-data-view-footer>\n </verben-card-data-view>\n </div>\n\n <!-- Column Selector -->\n <div column-content>\n <lib-data-columns [columns]=\"facade.getColumns()\" (columnsUpdated)=\"onColumnsUpdated($event)\"></lib-data-columns>\n </div>\n\n <!-- Filter -->\n <div filter-content>\n <lib-data-filter [columns]=\"facade.getColumns()\" [data]=\"(facade.items$ | async) ?? []\"\n (filterApplied)=\"onFilterApplied($event)\"></lib-data-filter>\n </div>\n\n <!-- Sort -->\n <div sort-content>\n <lib-data-sort [columns]=\"facade.getColumns()\" [data]=\"(facade.items$ | async) ?? []\"\n (sortApplied)=\"onSortApplied($event)\"></lib-data-sort>\n </div>\n\n <!-- Export -->\n <div export-content>\n <lib-data-xport [columns]=\"facade.getColumns()\" [data]=\"(facade.items$ | async) ?? []\"></lib-data-xport>\n </div>\n </verben-data-view>\n\n <!-- Table View Load More -->\n <div *ngIf=\"vdv.isTableView\" class=\"flex gap-2 justify-end mt-4 px-4\">\n <div class=\"flex justify-end items-center gap-5\">\n <span class=\"paginator-text\">{{ (facade.items$ | async)?.length }} records loaded</span>\n <button class=\"load-more view-links text-[var(--vbn-color-border-focus)] underline\"\n [disabled]=\"(facade.isLoading$ | async) ?? false\" (click)=\"onLoadMore()\">Load More</button>\n </div>\n </div>\n</div>\n", styles: [".user-request-approval-page{width:100%}.paginator-text{color:var(--vbn-color-text-muted);font-size:.875rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: DataViewModule }, { kind: "component", type: i3.DataViewComponent, selector: "verben-data-view", inputs: ["buttonClass", "iconClass", "activeIconClass", "columnCustomClass", "filterCustomClass", "sortCustomClass", "extendCustomClass", "exportCustomClass", "selectCustomClass", "importCustomClass", "zIndex", "createCustomClass", "tableIcon", "cardIcon", "cardClass", "tableClass", "searchKey", "searchValue", "viewState", "searchTemplate", "importTemplate", "columnTemplate", "extendTemplate", "filterTemplate", "sortTemplate", "children", "exportTemplate", "createTemplate", "selectedColumnCount", "selectedSortCount", "selectedFilterTableCount", "inputWidth", "milliseconds", "showColumnChild", "showSortChild", "showFilterChild", "showImportChild", "showExportChild", "showExtendChild", "create", "showSelected", "isTableView"], outputs: ["viewChange", "stateChange", "onSearchChange"] }, { kind: "ngmodule", type: DataColumnsModule }, { kind: "component", type: i3.DataColumnsComponent, selector: "lib-data-columns", inputs: ["columns", "enableDragAndDrop", "maxVisibleItems"], outputs: ["columnsUpdated"] }, { kind: "ngmodule", type: DataFilterModule }, { kind: "component", type: i3.DataFilterComponent, selector: "lib-data-filter", inputs: ["columns", "data"], outputs: ["filterApplied", "resetFilter"] }, { kind: "ngmodule", type: DataSortModule }, { kind: "component", type: i3.DataSortComponent, selector: "lib-data-sort", inputs: ["columns", "data", "enableDragAndDrop"], outputs: ["sortApplied", "resetFilter"] }, { kind: "ngmodule", type: DataXportModule }, { kind: "component", type: i3.DataXportComponent, selector: "lib-data-xport", inputs: ["data", "columns", "useImportKey", "dataFetchUrl", "dataQueryParameters", "dataQueryFunction"], outputs: ["exportDataEvent", "exportDataRangeEvent"] }, { kind: "ngmodule", type: CardDataViewModule }, { kind: "component", type: i3.CardDataViewComponent, selector: "verben-card-data-view", inputs: ["pd", "mg", "lHeight", "rHeight", "rWidth", "lWidth", "textColor", "lbgColor", "rbgColor", "border", "display", "borderRadius", "activeCss", "inActiveCss", "displayAsRow", "cardDataList", "dataId", "totalRecords", "footer", "noOfVisibleChildren", "showEditIcon", "useVirtualScroll", "virtualScrollItemSize", "onItemClick", "onCardChildClick"], outputs: ["editClicked", "loadMoreClick"] }, { kind: "component", type: i3.LeftCardDataComponent, selector: "verben-left-card-data", inputs: ["pd", "mg", "height", "weight", "activeCss", "inActiveCss", "cardDataList", "iconCollapse", "iconExpanded", "parent", "useVirtualScroll", "virtualScrollItemSize", "dataId"] }, { kind: "component", type: i3.LeftCardDataViewComponent, selector: "verben-left-card-data-view", inputs: ["cardDataList"] }, { kind: "component", type: i3.RightCardDataViewComponent, selector: "verben-right-card-data-view", inputs: ["parentData", "chilData", "meth"] }, { kind: "component", type: i3.CardDataViewFooterComponent, selector: "verben-card-data-view-footer" }, { kind: "component", type: UserRequestApprovalTableComponent, selector: "lib-user-request-approval-table", inputs: ["requests", "columns", "isLoading", "showDelete"], outputs: ["view", "delete"] }, { kind: "component", type: UserRequestApprovalCardComponent, selector: "lib-user-request-approval-card", inputs: ["request", "selected"] }, { kind: "component", type: UserRequestApprovalFormComponent, selector: "lib-user-request-approval-form", inputs: ["request", "roles", "showDelete"], outputs: ["approve", "reject", "deleteRequest", "switchToTable"] }] });
967
568
  }
968
569
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UserRequestApprovalComponent, decorators: [{
969
570
  type: Component,
970
- args: [{ selector: 'lib-user-request-approval', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"space-y-8\">\n <verben-data-view\n #vdv\n [viewState]=\"{\n isSearch: true,\n isColumn: true,\n isFilter: true,\n isSort: true,\n isExport: true,\n isSelect: true,\n isCreate: false,\n isToggle: true,\n }\"\n [buttonClass]=\"'my-custom-button-class'\"\n [iconClass]=\"'my-icon-class'\"\n [activeIconClass]=\"'my-active-icon-class'\"\n [selectedColumnCount]=\"0\"\n [selectedSortCount]=\"0\"\n [selectedFilterTableCount]=\"0\"\n (viewChange)=\"onViewChange($event)\"\n (stateChange)=\"onStateChange($event)\"\n (onSearchChange)=\"handleSearch($event)\"\n >\n <div table-content>\n <lib-data-table\n [data]=\"data()\"\n [columns]=\"visibleColumnDef\"\n [styleConfig]=\"styles\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n <ng-container libColumn=\"createdAt\">\n <ng-template #cell let-value>\n {{ value | date }}\n </ng-template>\n </ng-container>\n <ng-container libColumn=\"status\">\n <ng-template #cell let-value>\n <lib-user-access-request-status-badge\n [status]=\"value\"\n ></lib-user-access-request-status-badge>\n </ng-template>\n </ng-container>\n\n <ng-container libColumn=\"actions\">\n <ng-template #cell let-value let-deleteRow>\n <div class=\"flex gap-6\">\n <verben-svg\n (click)=\"openDetailView(value)\"\n icon=\"edit\"\n [width]=\"15\"\n [height]=\"15\"\n class=\"cursor-pointer\"\n ></verben-svg>\n <!-- <button (click)=\"openDetailView(value)\">Open</button>\n <button (click)=\"selected = cardData[rowIndex]; vdv.toggleView()\">\n Switch\n </button> -->\n <verben-svg\n icon=\"delete\"\n [width]=\"15\"\n [height]=\"15\"\n (click)=\"deleteRow()\"\n class=\"cursor-pointer\"\n ></verben-svg>\n </div>\n </ng-template>\n <!-- <ng-template #header>\n <strong>Actions</strong>\n </ng-template> -->\n </ng-container>\n </lib-data-table>\n </div>\n <div card-content>\n <verben-card-data-view\n borderRadius=\"12px\"\n (loadMoreClick)=\"loadMore()\"\n #vdcv\n dataId=\"MailAddress\"\n border=\"5px\"\n [cardDataList]=\"cardData()\"\n rbgColor=\"#f5f6f9\"\n mg=\"20px\"\n >\n <verben-left-card-data-view class=\"space-y-7\">\n <verben-left-card-data\n #vlcd\n [parent]=\"vdcv\"\n dataId=\"MailAddress\"\n class=\"bg-secondary-100 rounded-xl border-primary border-[1px]\"\n [cardDataList]=\"cardData()\"\n >\n <ng-template #card let-item>\n <div (click)=\"currentData = vdcv.onItemClick(item)\" class=\"flex\">\n <div\n (click)=\"currentData = vdcv.onItemClick(item)\"\n class=\"flex cursor-pointer w-full bg-secondary rounded-xl\"\n >\n <div\n class=\"w-3 rounded-xl rounded-tr-none rounded-br-none\"\n [ngClass]=\"\n item.selected ? 'bg-primary' : 'bg-secondary-200'\n \"\n ></div>\n <div class=\"flex-1 flex items-end gap-y-2 gap-x-4 p-4\">\n <div class=\"flex-1 space-y-2\">\n <span\n class=\"font-semibold text-[#404040] flex-1 truncate\"\n >{{ item.title }}</span\n >\n\n <p class=\"flex items-center my-1 gap-1\">\n <label for=\"phone\" class=\"text-[10px] font-light\"\n >Medium:</label\n >\n <span id=\"phone\" class=\"text-sm truncate\">{{\n item.data.SignupType\n }}</span>\n </p>\n </div>\n\n <div class=\"grid gap-y-1\">\n <!-- <p class=\"grid\">\n <label\n for=\"role\"\n class=\"text-[10px] font-light text-[#404040]\"\n >Medium:</label\n >\n <span id=\"role\" class=\"text-sm font-medium\">{{\n item.data?.SignupType\n }}</span>\n </p> -->\n\n <lib-user-access-request-status-badge\n [status]=\"item.data.RequestStatus\"\n ></lib-user-access-request-status-badge>\n </div>\n </div>\n </div>\n </div>\n </ng-template>\n </verben-left-card-data>\n </verben-left-card-data-view>\n\n <verben-right-card-data-view>\n <ng-template #parent>\n <lib-user-request-form\n [roles]=\"roles()\"\n [showDelete]=\"showDelete\"\n [currentData]=\"currentData\"\n (switchView)=\"vdv.toggleView()\"\n (onDeleteRequest)=\"onDelete($event)\"\n (onApproval)=\"onApprove($event)\"\n ></lib-user-request-form>\n </ng-template>\n </verben-right-card-data-view>\n\n <verben-card-data-view-footer class=\"w-full\">\n <div\n [ngClass]=\"{\n 'flex gap-2 items-center': true,\n 'justify-end': true,\n }\"\n >\n <!-- <verbena-button\n *ngIf=\"!vdcv.hasCurrentItem()\"\n [fontWeight]=\"'bold'\"\n [bgColor]=\"'#8E8D87'\"\n [borderRadius]=\"'4px'\"\n [textColor]=\"'#fff'\"\n [pd]=\"'9.79px 37.28px'\"\n [text]=\"'Delete'\"\n >\n </verbena-button> -->\n <div\n class=\"flex {{\n vdcv.hasCurrentItem() ? 'flex-1' : ''\n }} justify-end items-center gap-5\"\n >\n <span class=\"paginator-text\"\n >{{ cardData().length }} records loaded</span\n >\n <button\n (click)=\"loadMore()\"\n class=\"load-more view-links text-[#3479E9] underline\"\n >\n Load more\n </button>\n </div>\n </div>\n </verben-card-data-view-footer>\n </verben-card-data-view>\n </div>\n <div column-content>\n <verben-visible-column\n (columnsUpdated)=\"onColumnsUpdated($event)\"\n [border]=\"'1px solid rgba(212, 160, 7, 1)'\"\n borderRadius=\"10px\"\n boxShadow=\"2px 2px 2px 0px silver\"\n bgColor=\"white\"\n width=\"400px\"\n textColor=\"black\"\n pd=\"1rem\"\n primaryColor=\"#FFE681\"\n secondaryColor=\"#3479E9\"\n [columns]=\"visibleColumns\"\n [displayedColumns]=\"5\"\n ></verben-visible-column>\n </div>\n <div filter-content>\n <verben-table-filter\n (filtersApplied)=\"onFilterSet($event)\"\n [border]=\"'1px solid rgba(212, 160, 7, 1)'\"\n borderRadius=\"10px\"\n boxShadow=\"2px 2px 2px 0px silver\"\n bgColor=\"white\"\n width=\"420px\"\n textColor=\"black\"\n pd=\"1rem\"\n primaryColor=\"#FFE681\"\n secondaryColor=\"#3479E9\"\n tertiaryColor=\"#404040\"\n [filterOptions]=\"filterArray\"\n [maxFilterLength]=\"3\"\n ></verben-table-filter>\n </div>\n <div sort-content>\n <verben-sort-table\n (selectedOptions)=\"onSortUpdated($event)\"\n [border]=\"'1px solid rgba(212, 160, 7, 1)'\"\n borderRadius=\"10px\"\n boxShadow=\"2px 2px 2px 0px silver\"\n bgColor=\"white\"\n width=\"400px\"\n textColor=\"black\"\n pd=\"1rem\"\n primaryColor=\"#FFE681\"\n secondaryColor=\"#3479E9\"\n tertiaryColor=\"#404040\"\n [enableDragAndDrop]=\"true\"\n [sortOptions]=\"sortOptions\"\n ></verben-sort-table>\n </div>\n <div export-content>\n <lib-data-xport\n [columns]=\"visibleColumnDef\"\n [data]=\"data()\"\n (exportDataEvent)=\"handleExport($event)\"\n >\n </lib-data-xport>\n </div>\n </verben-data-view>\n\n <div *ngIf=\"vdv.isTableView\" class=\"flex gap-2 justify-end\">\n <!-- <div class=\"message-log-button-container flex justify-between\">\n <verbena-button\n [fontWeight]=\"'bold'\"\n [bgColor]=\"'#8E8D87'\"\n [borderRadius]=\"'4px'\"\n [textColor]=\"'#fff'\"\n [pd]=\"'9.79px 37.28px'\"\n [text]=\"'Delete'\"\n >\n </verbena-button>\n </div> -->\n <div class=\"flex justify-end items-center gap-5\">\n <span class=\"paginator-text\">{{ cardData().length }} records loaded</span>\n <button\n (click)=\"loadMore()\"\n class=\"load-more view-links text-[#3479E9] underline\"\n >\n Load more\n </button>\n </div>\n </div>\n</div>\n", styles: ["*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}:before,:after{--tw-content: \"\"}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.visible{visibility:visible}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.bottom-8{bottom:2rem}.left-0{left:0}.right-0{right:0}.float-right{float:right}.m-0{margin:0}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-0{margin-bottom:0}.mb-2{margin-bottom:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-\\[100\\%\\]{height:100%}.h-\\[max-content\\]{height:-moz-max-content;height:max-content}.h-full{height:100%}.w-3{width:.75rem}.w-full{width:100%}.flex-1{flex:1 1 0%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-20{gap:5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-1{row-gap:.25rem}.gap-y-2{row-gap:.5rem}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-7>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.75rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-all{word-break:break-all}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-br-none{border-bottom-right-radius:0}.rounded-tr-none{border-top-right-radius:0}.border,.border-\\[1px\\]{border-width:1px}.border-primary{--tw-border-opacity: 1;border-color:rgb(212 160 7 / var(--tw-border-opacity, 1))}.border-secondary-200{border-color:#d7dbe6}.bg-primary{--tw-bg-opacity: 1;background-color:rgb(212 160 7 / var(--tw-bg-opacity, 1))}.bg-secondary{--tw-bg-opacity: 1;background-color:rgb(232 234 241 / var(--tw-bg-opacity, 1))}.bg-secondary-100{--tw-bg-opacity: 1;background-color:rgb(245 246 249 / var(--tw-bg-opacity, 1))}.bg-secondary-200{background-color:#d7dbe6}.p-3{padding:.75rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pr-1{padding-right:.25rem}.\\!text-\\[10px\\]{font-size:10px!important}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\\[10px\\]{font-size:10px}.text-\\[12px\\]{font-size:12px}.text-\\[22px\\]{font-size:22px}.text-\\[24px\\]{font-size:24px}.text-\\[2rem\\]{font-size:2rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.lowercase{text-transform:lowercase}.leading-6{line-height:1.5rem}.leading-\\[12\\.1px\\]{line-height:12.1px}.leading-\\[16\\.6px\\]{line-height:16.6px}.leading-\\[19\\.5px\\]{line-height:19.5px}.leading-\\[29\\.05px\\]{line-height:29.05px}.leading-\\[33px\\]{line-height:33px}.leading-\\[48px\\]{line-height:48px}.text-\\[\\#333\\]{--tw-text-opacity: 1;color:rgb(51 51 51 / var(--tw-text-opacity, 1))}.text-\\[\\#3479E9\\]{--tw-text-opacity: 1;color:rgb(52 121 233 / var(--tw-text-opacity, 1))}.text-\\[\\#404040\\]{--tw-text-opacity: 1;color:rgb(64 64 64 / var(--tw-text-opacity, 1))}.text-\\[black\\]{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-muted{color:#0009}.underline{text-decoration-line:underline}.outline-none{outline:2px solid transparent;outline-offset:2px}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.view-links{color:#3479e9;cursor:pointer}\n"] }]
971
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.UtilService }, { type: i1.EnvironmentService }, { type: i1.HttpWebRequestService }, { type: UserAccessRequestFacade }], propDecorators: { showDelete: [{
571
+ args: [{ selector: 'lib-user-request-approval', standalone: true, imports: [
572
+ CommonModule,
573
+ DataViewModule,
574
+ DataColumnsModule,
575
+ DataFilterModule,
576
+ DataSortModule,
577
+ DataXportModule,
578
+ CardDataViewModule,
579
+ UserRequestApprovalTableComponent,
580
+ UserRequestApprovalCardComponent,
581
+ UserRequestApprovalFormComponent,
582
+ ], providers: [
583
+ UserAccessRequestFacade,
584
+ UserAccessRequestState,
585
+ UserAccessRequestService,
586
+ SearchHandlerService,
587
+ ], template: "<div class=\"user-request-approval-page space-y-8\">\n <verben-data-view #vdv [viewState]=\"{\n isSearch: true,\n isColumn: true,\n isFilter: true,\n isSort: true,\n isExport: true,\n isCreate: false,\n isToggle: true,\n isImport: false,\n }\" [selectedColumnCount]=\"(facade.visibleColumns$ | async)?.length ?? 0\"\n [selectedSortCount]=\"(facade.sorts$ | async)?.length ?? 0\"\n [selectedFilterTableCount]=\"(facade.filters$ | async)?.length ?? 0\"\n (onSearchChange)=\"onSearch($event.value)\">\n\n <!-- Table View -->\n <div table-content>\n <lib-user-request-approval-table [requests]=\"(facade.items$ | async) ?? []\"\n [columns]=\"(facade.visibleColumns$ | async) ?? []\" [isLoading]=\"(facade.isLoading$ | async) ?? false\"\n [showDelete]=\"showDelete\" (view)=\"onViewRequest($event)\" (delete)=\"onDeleteFromTable($event)\">\n </lib-user-request-approval-table>\n </div>\n\n <!-- Card View -->\n <div card-content>\n <verben-card-data-view #vdcv [cardDataList]=\"facade.cardData()\" borderRadius=\"12px\" dataId=\"MailAddress\"\n [border]=\"'5px'\" rbgColor=\"var(--vbn-color-surface-alt)\" mg=\"20px\">\n\n <verben-left-card-data-view class=\"space-y-7\">\n <verben-left-card-data #vlcd [parent]=\"vdcv\" dataId=\"MailAddress\"\n class=\"bg-secondary-100 rounded-xl border-primary border-[1px]\" [cardDataList]=\"facade.cardData()\">\n <ng-template #card let-item>\n <div (click)=\"requestCardClicked(vdcv.onItemClick(item))\" class=\"cursor-pointer h-[max-content]\">\n <lib-user-request-approval-card [request]=\"item.data\" [selected]=\"item.selected\">\n </lib-user-request-approval-card>\n </div>\n </ng-template>\n </verben-left-card-data>\n </verben-left-card-data-view>\n\n <verben-right-card-data-view>\n <ng-template #parent>\n <lib-user-request-approval-form [request]=\"facade.currentCardData()?.data ?? facade.emptyRequest\"\n [roles]=\"(facade.roles$ | async) ?? []\" [showDelete]=\"showDelete\" (approve)=\"onApprove($event)\"\n (reject)=\"onReject($event)\" (deleteRequest)=\"onDeleteRequest($event)\"\n (switchToTable)=\"vdv.toggleView()\">\n </lib-user-request-approval-form>\n </ng-template>\n </verben-right-card-data-view>\n\n <verben-card-data-view-footer class=\"w-full\">\n <div class=\"flex gap-2 items-center justify-end\">\n <div class=\"flex justify-end items-center gap-5\">\n <span class=\"paginator-text\">{{ (facade.items$ | async)?.length }} records loaded</span>\n <button class=\"load-more view-links text-[var(--vbn-color-border-focus)] underline\"\n [disabled]=\"(facade.isLoading$ | async) ?? false\" (click)=\"onLoadMore()\">Load More</button>\n </div>\n </div>\n </verben-card-data-view-footer>\n </verben-card-data-view>\n </div>\n\n <!-- Column Selector -->\n <div column-content>\n <lib-data-columns [columns]=\"facade.getColumns()\" (columnsUpdated)=\"onColumnsUpdated($event)\"></lib-data-columns>\n </div>\n\n <!-- Filter -->\n <div filter-content>\n <lib-data-filter [columns]=\"facade.getColumns()\" [data]=\"(facade.items$ | async) ?? []\"\n (filterApplied)=\"onFilterApplied($event)\"></lib-data-filter>\n </div>\n\n <!-- Sort -->\n <div sort-content>\n <lib-data-sort [columns]=\"facade.getColumns()\" [data]=\"(facade.items$ | async) ?? []\"\n (sortApplied)=\"onSortApplied($event)\"></lib-data-sort>\n </div>\n\n <!-- Export -->\n <div export-content>\n <lib-data-xport [columns]=\"facade.getColumns()\" [data]=\"(facade.items$ | async) ?? []\"></lib-data-xport>\n </div>\n </verben-data-view>\n\n <!-- Table View Load More -->\n <div *ngIf=\"vdv.isTableView\" class=\"flex gap-2 justify-end mt-4 px-4\">\n <div class=\"flex justify-end items-center gap-5\">\n <span class=\"paginator-text\">{{ (facade.items$ | async)?.length }} records loaded</span>\n <button class=\"load-more view-links text-[var(--vbn-color-border-focus)] underline\"\n [disabled]=\"(facade.isLoading$ | async) ?? false\" (click)=\"onLoadMore()\">Load More</button>\n </div>\n </div>\n</div>\n", styles: [".user-request-approval-page{width:100%}.paginator-text{color:var(--vbn-color-text-muted);font-size:.875rem}\n"] }]
588
+ }], ctorParameters: () => [{ type: UserAccessRequestFacade }], propDecorators: { showDelete: [{
972
589
  type: Input
590
+ }], vdv: [{
591
+ type: ViewChild,
592
+ args: ['vdv']
973
593
  }], cardDataView: [{
974
594
  type: ViewChild,
975
595
  args: ['vdcv']
976
- }], dataView: [{
977
- type: ViewChild,
978
- args: ['vdv']
979
596
  }] } });
980
597
 
598
+ /**
599
+ * Backward-compatibility shim. The components are standalone; this module keeps
600
+ * the historical `LibUserRequestApprovalModule` name so existing NgModule-based
601
+ * consumers keep working after the rebuild.
602
+ */
981
603
  class LibUserRequestApprovalModule {
982
604
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibUserRequestApprovalModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
983
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: LibUserRequestApprovalModule, declarations: [UserRequestApprovalComponent,
984
- UserRequestFormComponent,
985
- UserAccessRequestStatusBadgeComponent], imports: [CommonModule,
986
- VerbenDialogueModule,
987
- DataTableModule,
988
- CardModule,
989
- SvgModule,
990
- DataViewModule,
991
- CardDataViewModule,
992
- SortTableModule,
993
- VisibleColumnModule,
994
- TableFilterModule,
995
- DataExportModule,
996
- DataXportModule,
997
- FormsModule,
998
- ReactiveFormsModule,
999
- VerbenaInputModule,
1000
- VerbenaButtonModule,
1001
- VerbenaBadgeModule,
1002
- DropDownModule], exports: [UserRequestApprovalComponent,
1003
- UserAccessRequestStatusBadgeComponent] });
1004
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibUserRequestApprovalModule, imports: [CommonModule,
1005
- VerbenDialogueModule,
1006
- DataTableModule,
1007
- CardModule,
1008
- SvgModule,
1009
- DataViewModule,
1010
- CardDataViewModule,
1011
- SortTableModule,
1012
- VisibleColumnModule,
1013
- TableFilterModule,
1014
- DataExportModule,
1015
- DataXportModule,
1016
- FormsModule,
1017
- ReactiveFormsModule,
1018
- VerbenaInputModule,
1019
- VerbenaButtonModule,
1020
- VerbenaBadgeModule,
1021
- DropDownModule] });
605
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: LibUserRequestApprovalModule, imports: [UserRequestApprovalComponent, UserAccessRequestStatusBadgeComponent], exports: [UserRequestApprovalComponent, UserAccessRequestStatusBadgeComponent] });
606
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibUserRequestApprovalModule, imports: [UserRequestApprovalComponent, UserAccessRequestStatusBadgeComponent] });
1022
607
  }
1023
608
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibUserRequestApprovalModule, decorators: [{
1024
609
  type: NgModule,
1025
610
  args: [{
1026
- declarations: [
1027
- UserRequestApprovalComponent,
1028
- UserRequestFormComponent,
1029
- UserAccessRequestStatusBadgeComponent,
1030
- ],
1031
- imports: [
1032
- CommonModule,
1033
- VerbenDialogueModule,
1034
- DataTableModule,
1035
- CardModule,
1036
- SvgModule,
1037
- DataViewModule,
1038
- CardDataViewModule,
1039
- SortTableModule,
1040
- VisibleColumnModule,
1041
- TableFilterModule,
1042
- DataExportModule,
1043
- DataXportModule,
1044
- FormsModule,
1045
- ReactiveFormsModule,
1046
- VerbenaInputModule,
1047
- VerbenaButtonModule,
1048
- VerbenaBadgeModule,
1049
- DropDownModule,
1050
- ],
1051
- providers: [],
1052
- exports: [
1053
- UserRequestApprovalComponent,
1054
- UserAccessRequestStatusBadgeComponent,
1055
- ],
611
+ imports: [UserRequestApprovalComponent, UserAccessRequestStatusBadgeComponent],
612
+ exports: [UserRequestApprovalComponent, UserAccessRequestStatusBadgeComponent],
1056
613
  }]
1057
614
  }] });
1058
615