@cccteam/ccc-lib 0.0.15 → 0.0.16

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 (221) hide show
  1. package/README.md +38 -13
  2. package/fesm2022/cccteam-ccc-lib-src-auth-authentication-guard.mjs +36 -0
  3. package/fesm2022/cccteam-ccc-lib-src-auth-authentication-guard.mjs.map +1 -0
  4. package/fesm2022/cccteam-ccc-lib-src-auth-authorization-guard.mjs +25 -0
  5. package/fesm2022/cccteam-ccc-lib-src-auth-authorization-guard.mjs.map +1 -0
  6. package/fesm2022/cccteam-ccc-lib-src-auth-forms.mjs +83 -0
  7. package/fesm2022/cccteam-ccc-lib-src-auth-forms.mjs.map +1 -0
  8. package/fesm2022/cccteam-ccc-lib-src-auth-has-permission.mjs +44 -0
  9. package/fesm2022/cccteam-ccc-lib-src-auth-has-permission.mjs.map +1 -0
  10. package/fesm2022/cccteam-ccc-lib-src-auth-service.mjs +82 -0
  11. package/fesm2022/cccteam-ccc-lib-src-auth-service.mjs.map +1 -0
  12. package/fesm2022/cccteam-ccc-lib-src-ccc-camel-case-to-title.mjs +33 -0
  13. package/fesm2022/cccteam-ccc-lib-src-ccc-camel-case-to-title.mjs.map +1 -0
  14. package/fesm2022/cccteam-ccc-lib-src-ccc-grid.mjs +256 -0
  15. package/fesm2022/cccteam-ccc-lib-src-ccc-grid.mjs.map +1 -0
  16. package/fesm2022/cccteam-ccc-lib-src-ccc-resource.mjs +3129 -0
  17. package/fesm2022/cccteam-ccc-lib-src-ccc-resource.mjs.map +1 -0
  18. package/fesm2022/cccteam-ccc-lib-src-forms.mjs +79 -0
  19. package/fesm2022/cccteam-ccc-lib-src-forms.mjs.map +1 -0
  20. package/fesm2022/cccteam-ccc-lib-src-internal-types.mjs +6 -0
  21. package/fesm2022/cccteam-ccc-lib-src-internal-types.mjs.map +1 -0
  22. package/fesm2022/cccteam-ccc-lib-src-types.mjs +431 -0
  23. package/fesm2022/cccteam-ccc-lib-src-types.mjs.map +1 -0
  24. package/fesm2022/cccteam-ccc-lib-src-ui-alert.mjs +48 -0
  25. package/fesm2022/cccteam-ccc-lib-src-ui-alert.mjs.map +1 -0
  26. package/fesm2022/cccteam-ccc-lib-src-ui-core-service.mjs +41 -0
  27. package/fesm2022/cccteam-ccc-lib-src-ui-core-service.mjs.map +1 -0
  28. package/fesm2022/cccteam-ccc-lib-src-ui-idle-service.mjs +157 -0
  29. package/fesm2022/cccteam-ccc-lib-src-ui-idle-service.mjs.map +1 -0
  30. package/fesm2022/cccteam-ccc-lib-src-ui-interceptor.mjs +50 -0
  31. package/fesm2022/cccteam-ccc-lib-src-ui-interceptor.mjs.map +1 -0
  32. package/fesm2022/cccteam-ccc-lib-src-ui-notification-service.mjs +63 -0
  33. package/fesm2022/cccteam-ccc-lib-src-ui-notification-service.mjs.map +1 -0
  34. package/fesm2022/cccteam-ccc-lib-src-ui-sidenav.mjs +60 -0
  35. package/fesm2022/cccteam-ccc-lib-src-ui-sidenav.mjs.map +1 -0
  36. package/fesm2022/cccteam-ccc-lib-src-util-request-options.mjs +19 -0
  37. package/fesm2022/cccteam-ccc-lib-src-util-request-options.mjs.map +1 -0
  38. package/fesm2022/cccteam-ccc-lib.mjs +4444 -0
  39. package/fesm2022/cccteam-ccc-lib.mjs.map +1 -0
  40. package/package.json +88 -5
  41. package/types/cccteam-ccc-lib-src-auth-authentication-guard.d.ts +6 -0
  42. package/types/cccteam-ccc-lib-src-auth-authorization-guard.d.ts +6 -0
  43. package/types/cccteam-ccc-lib-src-auth-forms.d.ts +28 -0
  44. package/types/cccteam-ccc-lib-src-auth-has-permission.d.ts +15 -0
  45. package/types/cccteam-ccc-lib-src-auth-service.d.ts +38 -0
  46. package/types/cccteam-ccc-lib-src-ccc-camel-case-to-title.d.ts +10 -0
  47. package/types/cccteam-ccc-lib-src-ccc-grid.d.ts +35 -0
  48. package/types/cccteam-ccc-lib-src-ccc-resource.d.ts +674 -0
  49. package/types/cccteam-ccc-lib-src-forms.d.ts +27 -0
  50. package/types/cccteam-ccc-lib-src-types.d.ts +934 -0
  51. package/types/cccteam-ccc-lib-src-ui-alert.d.ts +16 -0
  52. package/types/cccteam-ccc-lib-src-ui-core-service.d.ts +20 -0
  53. package/types/cccteam-ccc-lib-src-ui-idle-service.d.ts +49 -0
  54. package/types/cccteam-ccc-lib-src-ui-interceptor.d.ts +16 -0
  55. package/types/cccteam-ccc-lib-src-ui-notification-service.d.ts +33 -0
  56. package/types/cccteam-ccc-lib-src-ui-sidenav.d.ts +33 -0
  57. package/types/cccteam-ccc-lib-src-util-request-options.d.ts +12 -0
  58. package/types/cccteam-ccc-lib.d.ts +1877 -0
  59. package/eslint.config.js +0 -32
  60. package/ng-package.json +0 -11
  61. package/src/auth-authentication-guard/authentication.guard.ts +0 -40
  62. package/src/auth-authentication-guard/index.ts +0 -1
  63. package/src/auth-authentication-guard/ng-package.json +0 -6
  64. package/src/auth-authorization-guard/authorization.guard.ts +0 -17
  65. package/src/auth-authorization-guard/index.ts +0 -1
  66. package/src/auth-authorization-guard/ng-package.json +0 -6
  67. package/src/auth-forms/ccc-field/ccc-field.component.html +0 -1
  68. package/src/auth-forms/ccc-field/ccc-field.component.scss +0 -0
  69. package/src/auth-forms/ccc-field/ccc-field.component.spec.ts +0 -22
  70. package/src/auth-forms/ccc-field/ccc-field.component.ts +0 -74
  71. package/src/auth-forms/form-helpers.ts +0 -39
  72. package/src/auth-forms/index.ts +0 -3
  73. package/src/auth-forms/ng-package.json +0 -6
  74. package/src/auth-has-permission/has-permission.directive.ts +0 -34
  75. package/src/auth-has-permission/index.ts +0 -1
  76. package/src/auth-has-permission/ng-package.json +0 -6
  77. package/src/auth-service/auth.service.ts +0 -92
  78. package/src/auth-service/index.ts +0 -1
  79. package/src/auth-service/ng-package.json +0 -6
  80. package/src/ccc-camel-case-to-title/camel-case-to-title.pipe.ts +0 -23
  81. package/src/ccc-camel-case-to-title/index.ts +0 -1
  82. package/src/ccc-camel-case-to-title/ng-package.json +0 -6
  83. package/src/ccc-grid/ccc-grid.component.ts +0 -155
  84. package/src/ccc-grid/index.ts +0 -3
  85. package/src/ccc-grid/ng-package.json +0 -6
  86. package/src/ccc-grid/table-button/table-button.component.html +0 -16
  87. package/src/ccc-grid/table-button/table-button.component.scss +0 -5
  88. package/src/ccc-grid/table-button/table-button.component.spec.ts +0 -22
  89. package/src/ccc-grid/table-button/table-button.component.ts +0 -49
  90. package/src/ccc-resource/can-deactivate.guard.ts +0 -41
  91. package/src/ccc-resource/compound-resource/compound-resource.component.html +0 -57
  92. package/src/ccc-resource/compound-resource/compound-resource.component.scss +0 -86
  93. package/src/ccc-resource/compound-resource/compound-resource.component.spec.ts +0 -22
  94. package/src/ccc-resource/compound-resource/compound-resource.component.ts +0 -158
  95. package/src/ccc-resource/concat-fns.ts +0 -162
  96. package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.html +0 -12
  97. package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.scss +0 -0
  98. package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.spec.ts +0 -23
  99. package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.ts +0 -17
  100. package/src/ccc-resource/form-state.service.ts +0 -24
  101. package/src/ccc-resource/format-fns.ts +0 -49
  102. package/src/ccc-resource/gui-constants.ts +0 -88
  103. package/src/ccc-resource/index.ts +0 -23
  104. package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.html +0 -8
  105. package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.scss +0 -0
  106. package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.spec.ts +0 -22
  107. package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.ts +0 -12
  108. package/src/ccc-resource/ng-package.json +0 -6
  109. package/src/ccc-resource/operation-types.ts +0 -19
  110. package/src/ccc-resource/padding-element/padding-element.component.html +0 -1
  111. package/src/ccc-resource/padding-element/padding-element.component.scss +0 -3
  112. package/src/ccc-resource/padding-element/padding-element.component.spec.ts +0 -22
  113. package/src/ccc-resource/padding-element/padding-element.component.ts +0 -20
  114. package/src/ccc-resource/resource-array-view/resource-array-view.component.html +0 -81
  115. package/src/ccc-resource/resource-array-view/resource-array-view.component.scss +0 -21
  116. package/src/ccc-resource/resource-array-view/resource-array-view.component.spec.ts +0 -22
  117. package/src/ccc-resource/resource-array-view/resource-array-view.component.ts +0 -143
  118. package/src/ccc-resource/resource-base/resource-base.component.spec.ts +0 -22
  119. package/src/ccc-resource/resource-base/resource-base.component.ts +0 -11
  120. package/src/ccc-resource/resource-cache.service.ts +0 -232
  121. package/src/ccc-resource/resource-create/resource-create.component.html +0 -31
  122. package/src/ccc-resource/resource-create/resource-create.component.scss +0 -130
  123. package/src/ccc-resource/resource-create/resource-create.component.spec.ts +0 -22
  124. package/src/ccc-resource/resource-create/resource-create.component.ts +0 -303
  125. package/src/ccc-resource/resource-field/base-field.directive.ts +0 -102
  126. package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.html +0 -16
  127. package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.scss +0 -0
  128. package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.spec.ts +0 -22
  129. package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.ts +0 -15
  130. package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.html +0 -13
  131. package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.scss +0 -0
  132. package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.spec.ts +0 -23
  133. package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.ts +0 -50
  134. package/src/ccc-resource/resource-field/fields/date-field/date-field.component.html +0 -22
  135. package/src/ccc-resource/resource-field/fields/date-field/date-field.component.scss +0 -0
  136. package/src/ccc-resource/resource-field/fields/date-field/date-field.component.spec.ts +0 -22
  137. package/src/ccc-resource/resource-field/fields/date-field/date-field.component.ts +0 -14
  138. package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.html +0 -71
  139. package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.scss +0 -9
  140. package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.spec.ts +0 -22
  141. package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.ts +0 -207
  142. package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.html +0 -38
  143. package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.scss +0 -3
  144. package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.spec.ts +0 -22
  145. package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.ts +0 -87
  146. package/src/ccc-resource/resource-field/fields/number-field/number-field.component.html +0 -23
  147. package/src/ccc-resource/resource-field/fields/number-field/number-field.component.scss +0 -6
  148. package/src/ccc-resource/resource-field/fields/number-field/number-field.component.spec.ts +0 -22
  149. package/src/ccc-resource/resource-field/fields/number-field/number-field.component.ts +0 -14
  150. package/src/ccc-resource/resource-field/fields/text-field/text-field.component.html +0 -29
  151. package/src/ccc-resource/resource-field/fields/text-field/text-field.component.scss +0 -6
  152. package/src/ccc-resource/resource-field/fields/text-field/text-field.component.spec.ts +0 -22
  153. package/src/ccc-resource/resource-field/fields/text-field/text-field.component.ts +0 -23
  154. package/src/ccc-resource/resource-field/resource-field.component.html +0 -112
  155. package/src/ccc-resource/resource-field/resource-field.component.scss +0 -7
  156. package/src/ccc-resource/resource-field/resource-field.component.spec.ts +0 -22
  157. package/src/ccc-resource/resource-field/resource-field.component.ts +0 -214
  158. package/src/ccc-resource/resource-layout/resource-layout.component.html +0 -73
  159. package/src/ccc-resource/resource-layout/resource-layout.component.scss +0 -26
  160. package/src/ccc-resource/resource-layout/resource-layout.component.spec.ts +0 -22
  161. package/src/ccc-resource/resource-layout/resource-layout.component.ts +0 -176
  162. package/src/ccc-resource/resource-list/ resource-list.component.spec.ts +0 -22
  163. package/src/ccc-resource/resource-list/resource-list.component.html +0 -27
  164. package/src/ccc-resource/resource-list/resource-list.component.scss +0 -67
  165. package/src/ccc-resource/resource-list/resource-list.component.ts +0 -376
  166. package/src/ccc-resource/resource-list-create/resource-list-create.component.html +0 -71
  167. package/src/ccc-resource/resource-list-create/resource-list-create.component.scss +0 -9
  168. package/src/ccc-resource/resource-list-create/resource-list-create.component.spec.ts +0 -22
  169. package/src/ccc-resource/resource-list-create/resource-list-create.component.ts +0 -103
  170. package/src/ccc-resource/resource-resolver/resource-resolver.component.html +0 -1
  171. package/src/ccc-resource/resource-resolver/resource-resolver.component.scss +0 -0
  172. package/src/ccc-resource/resource-resolver/resource-resolver.component.spec.ts +0 -22
  173. package/src/ccc-resource/resource-resolver/resource-resolver.component.ts +0 -69
  174. package/src/ccc-resource/resource-store.service.ts +0 -93
  175. package/src/ccc-resource/resource-view/resource-view.component.html +0 -133
  176. package/src/ccc-resource/resource-view/resource-view.component.scss +0 -150
  177. package/src/ccc-resource/resource-view/resource-view.component.spec.ts +0 -22
  178. package/src/ccc-resource/resource-view/resource-view.component.ts +0 -354
  179. package/src/ccc-resource/resources-helpers.ts +0 -262
  180. package/src/ccc-resource/utils/validator-utils.ts +0 -6
  181. package/src/index.ts +0 -44
  182. package/src/internal-types/ng-package.json +0 -6
  183. package/src/types/auth.actions.ts +0 -46
  184. package/src/types/configs.ts +0 -952
  185. package/src/types/constants.ts +0 -1
  186. package/src/types/core.actions.ts +0 -33
  187. package/src/types/index.ts +0 -9
  188. package/src/types/ng-package.json +0 -6
  189. package/src/types/notification-message.ts +0 -20
  190. package/src/types/permissions.ts +0 -17
  191. package/src/types/session-info.ts +0 -10
  192. package/src/types/tokens.ts +0 -20
  193. package/src/ui-alert/alert.component.html +0 -13
  194. package/src/ui-alert/alert.component.scss +0 -48
  195. package/src/ui-alert/alert.component.spec.ts +0 -22
  196. package/src/ui-alert/alert.component.ts +0 -35
  197. package/src/ui-alert/index.ts +0 -1
  198. package/src/ui-alert/ng-package.json +0 -6
  199. package/src/ui-core-service/index.ts +0 -1
  200. package/src/ui-core-service/ng-package.json +0 -6
  201. package/src/ui-core-service/ui-core.service.ts +0 -34
  202. package/src/ui-interceptor/api.interceptor.spec.ts +0 -16
  203. package/src/ui-interceptor/api.interceptor.ts +0 -45
  204. package/src/ui-interceptor/index.ts +0 -1
  205. package/src/ui-interceptor/ng-package.json +0 -6
  206. package/src/ui-notification-service/index.ts +0 -1
  207. package/src/ui-notification-service/ng-package.json +0 -6
  208. package/src/ui-notification-service/notification.service.ts +0 -59
  209. package/src/ui-sidenav/index.ts +0 -1
  210. package/src/ui-sidenav/ng-package.json +0 -6
  211. package/src/ui-sidenav/sidenav.component.html +0 -60
  212. package/src/ui-sidenav/sidenav.component.scss +0 -99
  213. package/src/ui-sidenav/sidenav.component.spec.ts +0 -22
  214. package/src/ui-sidenav/sidenav.component.ts +0 -64
  215. package/src/util-request-options/index.ts +0 -1
  216. package/src/util-request-options/ng-package.json +0 -6
  217. package/src/util-request-options/request-options.ts +0 -17
  218. package/tsconfig.lib.json +0 -13
  219. package/tsconfig.lib.prod.json +0 -11
  220. package/tsconfig.spec.json +0 -15
  221. /package/{src/internal-types/index.ts → types/cccteam-ccc-lib-src-internal-types.d.ts} +0 -0
@@ -0,0 +1,3129 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, Component, HostBinding, effect, signal, inject, Injector, untracked, Injectable, Directive, ChangeDetectionStrategy, model, DestroyRef, output, viewChild, runInInjectionContext, ViewContainerRef } from '@angular/core';
3
+ import * as i2$1 from '@angular/material/button';
4
+ import { MatButtonModule } from '@angular/material/button';
5
+ import * as i1$1 from '@angular/material/dialog';
6
+ import { MatDialogModule, MatDialogActions, MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
7
+ import * as i9 from '@angular/router';
8
+ import { Router, ActivatedRoute, RouterModule } from '@angular/router';
9
+ import { createResourceValidator, defaultEmptyFieldValue, API_URL, METHOD_META, AlertLevel, RESOURCE_META, validatorsPresent, rpcConfigDefaults } from '@cccteam/ccc-lib/src/types';
10
+ import { NotificationService } from '@cccteam/ccc-lib/src/ui-notification-service';
11
+ import { tap, of, firstValueFrom, filter } from 'rxjs';
12
+ import * as i3 from '@angular/forms';
13
+ import { Validators, FormGroup, FormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
14
+ import * as i2 from '@angular/material/input';
15
+ import { MatInputModule } from '@angular/material/input';
16
+ import { isDate, isValid, format, parseISO } from 'date-fns';
17
+ import { formatInTimeZone } from 'date-fns-tz';
18
+ import { isNumber, camelCase } from 'lodash-es';
19
+ import * as i1 from '@angular/material/form-field';
20
+ import { MatFormFieldModule } from '@angular/material/form-field';
21
+ import * as i4$1 from '@angular/common';
22
+ import { NgTemplateOutlet, NgComponentOutlet, Location, CommonModule } from '@angular/common';
23
+ import * as i2$2 from '@angular/material/checkbox';
24
+ import { MatCheckboxModule } from '@angular/material/checkbox';
25
+ import * as i3$1 from '@angular/material/datepicker';
26
+ import { MatDatepickerModule } from '@angular/material/datepicker';
27
+ import { HttpClient, HttpParams } from '@angular/common/http';
28
+ import { rxResource, takeUntilDestroyed } from '@angular/core/rxjs-interop';
29
+ import { AuthService } from '@cccteam/ccc-lib/src/auth-service';
30
+ import * as i10 from '@angular/material/autocomplete';
31
+ import { MatAutocompleteModule } from '@angular/material/autocomplete';
32
+ import * as i4 from '@angular/material/core';
33
+ import { MatOptionModule } from '@angular/material/core';
34
+ import * as i3$2 from '@angular/material/icon';
35
+ import { MatIconModule } from '@angular/material/icon';
36
+ import * as i5 from '@angular/material/select';
37
+ import { MatSelectModule } from '@angular/material/select';
38
+ import * as i8 from '@angular/material/tooltip';
39
+ import { MatTooltipModule } from '@angular/material/tooltip';
40
+ import * as i2$3 from '@angular/material/expansion';
41
+ import { MatExpansionPanel, MatExpansionModule, MatExpansionPanelHeader, MatExpansionPanelTitle } from '@angular/material/expansion';
42
+ import { MatDividerModule } from '@angular/material/divider';
43
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
44
+ import { CamelCaseToTitlePipe } from '@cccteam/ccc-lib/src/ccc-camel-case-to-title';
45
+ import { cleanStringForm, sparseFormData } from '@cccteam/ccc-lib/src/forms';
46
+ import { AppGridComponent } from '@cccteam/ccc-lib/src/ccc-grid';
47
+
48
+ class ActionAccessControlWrapperComponent {
49
+ actionContext = input(undefined, { ...(ngDevMode ? { debugName: "actionContext" } : {}) });
50
+ showAction = computed(() => {
51
+ const context = this.actionContext();
52
+ let isActionPermittedForUser = true;
53
+ if (context === undefined) {
54
+ return true;
55
+ }
56
+ const actionType = context.actionType;
57
+ if (actionType === 'rpc') {
58
+ //TODO: When ABAC permissions is completed, update this logic
59
+ isActionPermittedForUser = true;
60
+ }
61
+ if (actionType === 'create') {
62
+ isActionPermittedForUser = !context.meta.createDisabled;
63
+ }
64
+ if (actionType === 'edit') {
65
+ isActionPermittedForUser = !context.meta.updateDisabled;
66
+ }
67
+ if (actionType === 'delete') {
68
+ isActionPermittedForUser = !context.meta.deleteDisabled;
69
+ }
70
+ if (!isActionPermittedForUser) {
71
+ console.debug('ACCESS CONTROL | ', actionType, ' action not permitted for user in context: ', context);
72
+ return false;
73
+ }
74
+ let showAction = true;
75
+ try {
76
+ showAction = context.shouldRender(context.resourceData);
77
+ }
78
+ catch (e) {
79
+ console.error('Failed to calculate value for should Render function for action: ', actionType);
80
+ console.error(e);
81
+ }
82
+ return showAction;
83
+ }, { ...(ngDevMode ? { debugName: "showAction" } : {}) });
84
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ActionAccessControlWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
85
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: ActionAccessControlWrapperComponent, isStandalone: true, selector: "action-access-control-wrapper", inputs: { actionContext: { classPropertyName: "actionContext", publicName: "actionContext", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `@if (showAction()) {
86
+ <ng-content />
87
+ }`, isInline: true, styles: [":host{margin-top:auto;margin-bottom:auto}\n"] });
88
+ }
89
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ActionAccessControlWrapperComponent, decorators: [{
90
+ type: Component,
91
+ args: [{ selector: 'action-access-control-wrapper', template: `@if (showAction()) {
92
+ <ng-content />
93
+ }`, styles: [":host{margin-top:auto;margin-bottom:auto}\n"] }]
94
+ }], propDecorators: { actionContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionContext", required: false }] }] } });
95
+
96
+ const maxConfigElementRecursionDepth = 240;
97
+ const maxLayoutNestingDepth = 48;
98
+ /** Returns a flat array of nested elements by recursively traversing
99
+ * through the elements graph */
100
+ const flattenElements = (elements, depth = 0) => {
101
+ depth++;
102
+ if (!elements || elements.length === 0 || depth > maxConfigElementRecursionDepth) {
103
+ return [];
104
+ }
105
+ return elements.reduce((acc, element) => {
106
+ if (element.type === 'section') {
107
+ return acc.concat(element, flattenElements(element.children, depth));
108
+ }
109
+ return acc.concat(element);
110
+ }, []);
111
+ };
112
+ const civildateCoercion = (value) => {
113
+ if (value === undefined || value === '') {
114
+ return new Date(value);
115
+ }
116
+ return new Date(formatInTimeZone(new Date(value), 'UTC', 'yyyy-MM-dd HH:mm:ss'));
117
+ };
118
+ const currentOrFutureDateValidator = (control) => {
119
+ if (!control.value) {
120
+ return null; // Let "REQUIRED" validator handle empty values
121
+ }
122
+ if (!isDate(control.value)) {
123
+ return { errorMsg: 'Value must be a valid date' };
124
+ }
125
+ const selectedDate = new Date(control.value);
126
+ const today = new Date();
127
+ today.setHours(0, 0, 0, 0);
128
+ if (selectedDate >= today) {
129
+ return null;
130
+ }
131
+ return { errorMsg: 'Past date not allowed' };
132
+ };
133
+ const positiveNumberValidator = (control) => {
134
+ if (!control.value && control.value !== 0) {
135
+ return null; // Let "REQUIRED" validator handle "empty" values
136
+ }
137
+ if (isNumber(control.value) && control.value > 0) {
138
+ return null;
139
+ }
140
+ return { errorMsg: 'Value must be a positive number' };
141
+ };
142
+ const notFutureDateValidator = (control) => {
143
+ if (!control.value) {
144
+ return null; // Let "REQUIRED" validator handle empty values
145
+ }
146
+ if (!isDate(control.value)) {
147
+ return { errorMsg: 'Value must be a valid date' };
148
+ }
149
+ const selectedDate = new Date(control.value);
150
+ const today = new Date();
151
+ today.setHours(0, 0, 0, 0);
152
+ if (selectedDate <= today) {
153
+ return null;
154
+ }
155
+ return { errorMsg: 'Future date not allowed' };
156
+ };
157
+ const valueInRangeInclusive = (min, max) => {
158
+ return (control) => {
159
+ if (!control.value && control.value !== 0) {
160
+ return null; // Let "REQUIRED" validator handle "empty" values
161
+ }
162
+ if (isNumber(control.value) && control.value >= min && control.value <= max) {
163
+ return null;
164
+ }
165
+ return { errorMsg: `Value must be in the range [${min}, ${max}]` };
166
+ };
167
+ };
168
+ /**
169
+ * This object stores every possible validator used across the application
170
+ * Add all validators that are used in configs to this object
171
+ *
172
+ * Available validators that may be added: min, max, required, requiredTrue,
173
+ * email, minLength, maxLength, pattern, nullValidator
174
+ */
175
+ const resourceValidators = Object.freeze({
176
+ REQUIRED: createResourceValidator(Validators.required),
177
+ EMAIL: createResourceValidator(Validators.email),
178
+ CURRENT_OR_FUTURE_DATE: createResourceValidator(currentOrFutureDateValidator),
179
+ POSITIVE_NUMBER: createResourceValidator(positiveNumberValidator),
180
+ NOT_FUTURE_DATE: createResourceValidator(notFutureDateValidator),
181
+ VALUE_IN_RANGE_INCLUSIVE: (min, max) => createResourceValidator(valueInRangeInclusive(min, max)),
182
+ MAX_LENGTH: (maxLength) => createResourceValidator(Validators.maxLength(maxLength)),
183
+ // Add any additional validators here. They may be defined in
184
+ // a different file if they are too large. E.g.:
185
+ // MIN_LENGTH_3: createResourceValidator(Validators.minLength(3)),
186
+ });
187
+
188
+ class PaddingElementComponent {
189
+ paddingElement = input.required({ ...(ngDevMode ? { debugName: "paddingElement" } : {}) });
190
+ class = 'col-6';
191
+ ngOnInit() {
192
+ if (this.paddingElement().cols) {
193
+ this.class = 'col-' + this.paddingElement()?.cols;
194
+ }
195
+ }
196
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: PaddingElementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
197
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.2", type: PaddingElementComponent, isStandalone: true, selector: "ccc-padding-element", inputs: { paddingElement: { classPropertyName: "paddingElement", publicName: "paddingElement", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class": "this.class" } }, ngImport: i0, template: "<div class=\"padding-container\"></div>\n", styles: [".padding-container{height:52px}\n"] });
198
+ }
199
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: PaddingElementComponent, decorators: [{
200
+ type: Component,
201
+ args: [{ selector: 'ccc-padding-element', imports: [], template: "<div class=\"padding-container\"></div>\n", styles: [".padding-container{height:52px}\n"] }]
202
+ }], propDecorators: { paddingElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "paddingElement", required: true }] }], class: [{
203
+ type: HostBinding,
204
+ args: ['class']
205
+ }] } });
206
+
207
+ class ComputedFieldComponent {
208
+ fieldConfig = input.required({ ...(ngDevMode ? { debugName: "fieldConfig" } : {}) });
209
+ fieldClass = input(undefined, { ...(ngDevMode ? { debugName: "fieldClass" } : {}) });
210
+ formDataState = input(undefined, { ...(ngDevMode ? { debugName: "formDataState" } : {}) });
211
+ computedValue = computed(() => {
212
+ try {
213
+ return this.fieldConfig().calculatedValue(this.formDataState());
214
+ }
215
+ catch (e) {
216
+ console.error('Failed to calculate value for computed field: ', this.fieldConfig().label);
217
+ console.error(e);
218
+ return '';
219
+ }
220
+ }, { ...(ngDevMode ? { debugName: "computedValue" } : {}) });
221
+ showField = computed(() => {
222
+ const shouldRender = this.fieldConfig().shouldRender;
223
+ if (typeof shouldRender === 'boolean') {
224
+ return shouldRender;
225
+ }
226
+ try {
227
+ return shouldRender(this.formDataState());
228
+ }
229
+ catch (e) {
230
+ console.error('Failed to calculate value for should Render function for field: ', this.fieldConfig().label);
231
+ console.error(e);
232
+ return true;
233
+ }
234
+ }, { ...(ngDevMode ? { debugName: "showField" } : {}) });
235
+ class = '';
236
+ constructor() {
237
+ effect(() => {
238
+ this.class = this.showField() ? 'col-' + this.fieldConfig()?.cols : 'hidden-field';
239
+ });
240
+ }
241
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ComputedFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
242
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: ComputedFieldComponent, isStandalone: true, selector: "ccc-computed-field", inputs: { fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: true, isRequired: true, transformFunction: null }, fieldClass: { classPropertyName: "fieldClass", publicName: "fieldClass", isSignal: true, isRequired: false, transformFunction: null }, formDataState: { classPropertyName: "formDataState", publicName: "formDataState", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.class" } }, ngImport: i0, template: "@if (showField()) {\n <div class=\"readonly-field\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"true\"\n type=\"text\"\n [value]=\"computedValue()\" />\n </mat-form-field>\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] });
243
+ }
244
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ComputedFieldComponent, decorators: [{
245
+ type: Component,
246
+ args: [{ selector: 'ccc-computed-field', imports: [MatFormFieldModule, MatButtonModule, MatInputModule], template: "@if (showField()) {\n <div class=\"readonly-field\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"true\"\n type=\"text\"\n [value]=\"computedValue()\" />\n </mat-form-field>\n </div>\n}\n" }]
247
+ }], ctorParameters: () => [], propDecorators: { fieldConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldConfig", required: true }] }], fieldClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldClass", required: false }] }], formDataState: [{ type: i0.Input, args: [{ isSignal: true, alias: "formDataState", required: false }] }], class: [{
248
+ type: HostBinding,
249
+ args: ['class']
250
+ }] } });
251
+
252
+ class EmptyReadonlyFieldComponent {
253
+ label = input.required({ ...(ngDevMode ? { debugName: "label" } : {}) });
254
+ displayValue = defaultEmptyFieldValue;
255
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: EmptyReadonlyFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
256
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.2", type: EmptyReadonlyFieldComponent, isStandalone: true, selector: "ccc-empty-readonly-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"readonly-field\">\n <mat-form-field class=\"field\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ label() }}</mat-label>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"true\"\n type=\"text\"\n [value]=\"displayValue\"\n id=\"{{ label() }}-readonly-input-text\" />\n </mat-form-field>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] });
257
+ }
258
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: EmptyReadonlyFieldComponent, decorators: [{
259
+ type: Component,
260
+ args: [{ selector: 'ccc-empty-readonly-field', imports: [MatFormFieldModule, MatButtonModule, MatInputModule], template: "<div class=\"readonly-field\">\n <mat-form-field class=\"field\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ label() }}</mat-label>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"true\"\n type=\"text\"\n [value]=\"displayValue\"\n id=\"{{ label() }}-readonly-input-text\" />\n </mat-form-field>\n</div>\n" }]
261
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }] } });
262
+
263
+ class ResourceStore {
264
+ resourceMeta = signal({}, { ...(ngDevMode ? { debugName: "resourceMeta" } : {}) });
265
+ resourceName = signal('', { ...(ngDevMode ? { debugName: "resourceName" } : {}) });
266
+ filter = signal('', { ...(ngDevMode ? { debugName: "filter" } : {}) });
267
+ disableCacheForFilterPii = signal(false, { ...(ngDevMode ? { debugName: "disableCacheForFilterPii" } : {}) });
268
+ searchTokens = signal('', { ...(ngDevMode ? { debugName: "searchTokens" } : {}) });
269
+ sorts = signal([], { ...(ngDevMode ? { debugName: "sorts" } : {}) });
270
+ listColumns = signal([], { ...(ngDevMode ? { debugName: "listColumns" } : {}) });
271
+ requireSearchToDisplayResults = signal(false, { ...(ngDevMode ? { debugName: "requireSearchToDisplayResults" } : {}) });
272
+ uuid = signal('', { ...(ngDevMode ? { debugName: "uuid" } : {}) });
273
+ error = signal('', { ...(ngDevMode ? { debugName: "error" } : {}) });
274
+ notifications = inject(NotificationService);
275
+ http = inject(HttpClient);
276
+ router = inject(Router);
277
+ injector = inject(Injector);
278
+ apiUrl = inject(API_URL);
279
+ methodMeta = inject(METHOD_META);
280
+ resourceListRef = signal(undefined, { ...(ngDevMode ? { debugName: "resourceListRef" } : {}) });
281
+ listData = computed(() => {
282
+ const ref = this.resourceListRef();
283
+ if (ref && ref.status() === 'resolved') {
284
+ return ref.value();
285
+ }
286
+ return [];
287
+ }, { ...(ngDevMode ? { debugName: "listData" } : {}) });
288
+ listStatus = computed(() => {
289
+ return this.resourceListRef()?.status();
290
+ }, { ...(ngDevMode ? { debugName: "listStatus" } : {}) });
291
+ resourceViewRef = signal(undefined, { ...(ngDevMode ? { debugName: "resourceViewRef" } : {}) });
292
+ viewData = computed(() => {
293
+ const ref = this.resourceViewRef();
294
+ if (ref && ref.status() === 'resolved') {
295
+ return ref.value();
296
+ }
297
+ return {};
298
+ }, { ...(ngDevMode ? { debugName: "viewData" } : {}) });
299
+ viewStatus = computed(() => {
300
+ return this.resourceViewRef()?.status();
301
+ }, { ...(ngDevMode ? { debugName: "viewStatus" } : {}) });
302
+ overrideRoute = signal('', { ...(ngDevMode ? { debugName: "overrideRoute" } : {}) });
303
+ resourceRoute = computed(() => this.resourceMeta()?.route, { ...(ngDevMode ? { debugName: "resourceRoute" } : {}) });
304
+ route = computed(() => {
305
+ if (this.overrideRoute()) {
306
+ return this.overrideRoute();
307
+ }
308
+ const route = this.resourceRoute();
309
+ if (route) {
310
+ return route;
311
+ }
312
+ return '';
313
+ }, { ...(ngDevMode ? { debugName: "route" } : {}) });
314
+ reloadViewData() {
315
+ this.resourceViewRef()?.reload();
316
+ }
317
+ reloadListData() {
318
+ this.resourceListRef()?.reload();
319
+ }
320
+ buildStoreListData() {
321
+ const route = this.route();
322
+ const name = this.resourceName();
323
+ if (!route || name === '') {
324
+ return;
325
+ }
326
+ const columnIds = this.listColumns().flatMap((col) => {
327
+ return [col.id];
328
+ });
329
+ const resourceMeta = this.resourceMeta();
330
+ if (resourceMeta && resourceMeta.fields.some((field) => field.fieldName === 'id')) {
331
+ columnIds.push('id');
332
+ }
333
+ const uniqueColumns = signal([...new Set([...columnIds])], { ...(ngDevMode ? { debugName: "uniqueColumns" } : {}) });
334
+ const ref = this.resourceList(this.route, this.filter, uniqueColumns, this.disableCacheForFilterPii, this.searchTokens, this.sorts);
335
+ this.resourceListRef.set(ref);
336
+ this.reloadListData();
337
+ }
338
+ buildStoreViewData() {
339
+ const route = this.route();
340
+ const uuid = this.uuid();
341
+ if (!route || !uuid || uuid === 'undefined') {
342
+ return;
343
+ }
344
+ const ref = this.resourceView(this.route, this.uuid);
345
+ this.resourceViewRef.set(ref);
346
+ this.reloadListData();
347
+ }
348
+ routes = {
349
+ resources: (rootUrl, resources) => `${rootUrl}/${resources}`,
350
+ resource: (rootUrl, resources, uuid) => `${rootUrl}/${resources}/${uuid}`,
351
+ method: (rootUrl, method) => `${rootUrl}/${method}`,
352
+ };
353
+ makePatches(operations, route, resource) {
354
+ return this.patchMultiple(String(route), operations).pipe(tap(() => {
355
+ this.notifications.addGlobalNotification({
356
+ message: `${resource} updated successfully`,
357
+ level: AlertLevel.SUCCESS,
358
+ duration: 5000,
359
+ link: '',
360
+ });
361
+ }));
362
+ }
363
+ createPatch(operation, route, resource) {
364
+ return this.patchMultiple(String(route), [operation]).pipe(tap(() => {
365
+ this.notifications.addGlobalNotification({
366
+ message: `${resource} created successfully`,
367
+ level: AlertLevel.SUCCESS,
368
+ duration: 5000,
369
+ link: '',
370
+ });
371
+ }));
372
+ }
373
+ patchMultiple(resourceRoute, data) {
374
+ return this.http.patch(this.routes.resources(this.apiUrl, resourceRoute), data);
375
+ }
376
+ resourceView(route, uuid) {
377
+ return untracked(() => rxResource({
378
+ injector: this.injector,
379
+ params: () => ({
380
+ route: route,
381
+ uuid: uuid,
382
+ }),
383
+ stream: ({ params }) => {
384
+ if (!params.route() || !params.uuid() || params.uuid() === 'undefined')
385
+ return of({});
386
+ return this.http.get(this.routes.resource(this.apiUrl, String(params.route()), params.uuid() || ''));
387
+ },
388
+ }));
389
+ }
390
+ resourceList(route, filter = signal(''), columns = signal([]), disableCacheForFilterPii = signal(false), searchTokens = signal(''), sorts = signal([]), defaultEmpty = false) {
391
+ return untracked(() => {
392
+ return rxResource({
393
+ defaultValue: [],
394
+ injector: this.injector,
395
+ params: () => ({
396
+ route: route(),
397
+ filter: filter(),
398
+ columns: columns(),
399
+ searchTokens: searchTokens(),
400
+ sorts: sorts(),
401
+ }),
402
+ stream: ({ params }) => {
403
+ if (!params.route)
404
+ return of([]);
405
+ if (defaultEmpty && (!params.searchTokens || params.searchTokens.trim() === '')) {
406
+ return of([]);
407
+ }
408
+ return this.list(String(params.route), params.filter, disableCacheForFilterPii(), params.columns, params.searchTokens, params.sorts);
409
+ },
410
+ });
411
+ });
412
+ }
413
+ list(resourceRoute, filter, disableCacheForFilterPii, columns, searchTokens, sort) {
414
+ const paramsObj = {};
415
+ if (filter && filter.trim() !== '')
416
+ paramsObj['filter'] = filter;
417
+ if (columns && columns.length > 0)
418
+ paramsObj['columns'] = columns.join(',');
419
+ if (searchTokens && searchTokens.trim() !== '')
420
+ paramsObj['SearchTokens'] = searchTokens;
421
+ if (sort && sort.length > 0) {
422
+ paramsObj['sort'] = sort.map((s) => `${s.field}:${s.direction}`).join(',');
423
+ }
424
+ const params = new HttpParams({ fromObject: paramsObj });
425
+ if (disableCacheForFilterPii) {
426
+ const paramsObjWithoutFilter = { ...paramsObj };
427
+ delete paramsObjWithoutFilter['filter'];
428
+ return this.http.post(this.routes.resources(this.apiUrl, resourceRoute), { filter: filter }, paramsObjWithoutFilter);
429
+ }
430
+ return this.http.get(this.routes.resources(this.apiUrl, resourceRoute), { params });
431
+ }
432
+ rpcCall(rpcConfig, body) {
433
+ const methodData = this.methodMeta(rpcConfig.method);
434
+ if (!methodData) {
435
+ console.error('Method not found in methodMap:', rpcConfig.method);
436
+ return of({});
437
+ }
438
+ return this.http.post(this.routes.method(this.apiUrl, methodData.route), body).pipe(tap(() => {
439
+ this.notifications.addGlobalNotification({
440
+ message: rpcConfig.successMessage ? rpcConfig.successMessage : `${rpcConfig.method} called successfully`,
441
+ level: AlertLevel.SUCCESS,
442
+ duration: 5000,
443
+ link: '',
444
+ });
445
+ if (rpcConfig.afterMethodRedirect) {
446
+ if (typeof rpcConfig.afterMethodRedirect === 'string') {
447
+ this.router.navigate([rpcConfig.afterMethodRedirect]);
448
+ }
449
+ else {
450
+ this.router.navigate(rpcConfig.afterMethodRedirect);
451
+ }
452
+ }
453
+ }));
454
+ }
455
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
456
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceStore });
457
+ }
458
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceStore, decorators: [{
459
+ type: Injectable
460
+ }] });
461
+
462
+ class FormStateService {
463
+ dirtyForms = signal(0, { ...(ngDevMode ? { debugName: "dirtyForms" } : {}) });
464
+ incrementDirtyForms() {
465
+ this.dirtyForms.set(this.dirtyForms() + 1);
466
+ }
467
+ decrementDirtyForms() {
468
+ this.dirtyForms.set(this.dirtyForms() - 1);
469
+ }
470
+ resetDirtyForms() {
471
+ this.dirtyForms.set(0);
472
+ }
473
+ isDirty = computed(() => {
474
+ return this.dirtyForms() > 0;
475
+ }, { ...(ngDevMode ? { debugName: "isDirty" } : {}) });
476
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: FormStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
477
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: FormStateService, providedIn: 'root' });
478
+ }
479
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: FormStateService, decorators: [{
480
+ type: Injectable,
481
+ args: [{
482
+ providedIn: 'root',
483
+ }]
484
+ }] });
485
+
486
+ class LeavePageConfirmationModalComponent {
487
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: LeavePageConfirmationModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
488
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.2", type: LeavePageConfirmationModalComponent, isStandalone: true, selector: "ccc-leave-page-confirmation-modal", ngImport: i0, template: "<div mat-dialog-title>You have unsaved changes, are you sure you want to leave?</div>\n<mat-dialog-content>\n <p>Any unsaved changes will be lost.</p>\n</mat-dialog-content>\n<mat-dialog-actions align=\"end\">\n <button mat-raised-button color=\"warn\" [mat-dialog-close]=\"false\">Cancel</button>\n <button mat-raised-button color=\"accent\" [mat-dialog-close]=\"true\">Confirm</button>\n</mat-dialog-actions>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }] });
489
+ }
490
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: LeavePageConfirmationModalComponent, decorators: [{
491
+ type: Component,
492
+ args: [{ selector: 'ccc-leave-page-confirmation-modal', imports: [MatDialogModule, MatFormFieldModule, MatDialogActions, MatButtonModule], template: "<div mat-dialog-title>You have unsaved changes, are you sure you want to leave?</div>\n<mat-dialog-content>\n <p>Any unsaved changes will be lost.</p>\n</mat-dialog-content>\n<mat-dialog-actions align=\"end\">\n <button mat-raised-button color=\"warn\" [mat-dialog-close]=\"false\">Cancel</button>\n <button mat-raised-button color=\"accent\" [mat-dialog-close]=\"true\">Confirm</button>\n</mat-dialog-actions>\n" }]
493
+ }] });
494
+
495
+ const canDeactivateGuard = async (_, __, ___, nextState) => {
496
+ const auth = inject(AuthService);
497
+ const dialog = inject(MatDialog);
498
+ const formStateService = inject(FormStateService);
499
+ if (nextState?.url.includes(auth.loginRoute())) {
500
+ return true;
501
+ }
502
+ if (!formStateService.isDirty()) {
503
+ return true;
504
+ }
505
+ const existingDialog = dialog.openDialogs.find((d) => d.componentInstance instanceof LeavePageConfirmationModalComponent);
506
+ const dialogRef = existingDialog ?? dialog.open(LeavePageConfirmationModalComponent, { delayFocusTrap: false });
507
+ const result = dialogRef.afterClosed().pipe(tap((value) => {
508
+ if (value === true) {
509
+ formStateService.resetDirtyForms();
510
+ }
511
+ }));
512
+ return firstValueFrom(result);
513
+ };
514
+
515
+ const generatedNavItems = [];
516
+ const generatedNavGroups = [];
517
+ const createFormGroup = (meta, resourceData, config, formDataState) => {
518
+ const fg = new FormGroup({});
519
+ const pristineValues = {};
520
+ const allElements = flattenElements(config.elements);
521
+ for (const field of meta.fields || []) {
522
+ const isFieldNameRegistered = fg.get(field.fieldName) !== null;
523
+ if (isFieldNameRegistered) {
524
+ continue;
525
+ }
526
+ const findConfig = allElements.find((element) => element.type === 'field' && element.name === field.fieldName);
527
+ const fieldConfig = findConfig;
528
+ if (!fieldConfig) {
529
+ continue;
530
+ }
531
+ let value = null;
532
+ const stringValue = resourceData?.[field.fieldName] ? String(resourceData[field.fieldName]) : '';
533
+ if (field.displayType === 'civildate' && stringValue) {
534
+ value = civildateCoercion(stringValue);
535
+ }
536
+ else if (resourceData[field.fieldName] !== undefined) {
537
+ value = resourceData[field.fieldName];
538
+ }
539
+ const control = new FormControl(value);
540
+ if (fieldConfig.validators.length > 0) {
541
+ control.setValidators(fieldConfig.validators);
542
+ }
543
+ fg.addControl(field.fieldName, control);
544
+ pristineValues[field.fieldName] = value;
545
+ }
546
+ // todo: swap this with a manual subscription where the form data is subscribed to and
547
+ // is dstroyed once the form is destroyed, similar to gui/src/app/components/Resource/resource-view/resource-view.component.ts
548
+ // constructor effect
549
+ console.debug(formDataState);
550
+ // formDataState && formDataState.set(pristineValues);
551
+ return {
552
+ formGroup: fg,
553
+ pristineFormValues: pristineValues,
554
+ };
555
+ };
556
+ const resourceRoutes = (config, resourceMeta) => {
557
+ const meta = resourceMeta(config.parentConfig.primaryResource);
558
+ if (!meta) {
559
+ return {};
560
+ }
561
+ if (config.nav.group) {
562
+ if (config.routeData.route) {
563
+ addToNavItems(config.nav, config.routeData.route);
564
+ }
565
+ else {
566
+ addToNavItems(config.nav, meta.route);
567
+ }
568
+ }
569
+ if (config.routeData.route) {
570
+ const baseRoute = {
571
+ path: config.routeData.route,
572
+ data: { config: config },
573
+ children: [
574
+ {
575
+ path: '',
576
+ loadComponent: () => Promise.resolve().then(function () { return resourceListCreate_component; }).then((mod) => mod.ResourceListCreateComponent),
577
+ canDeactivate: [canDeactivateGuard],
578
+ },
579
+ ],
580
+ };
581
+ if (config.routeData.hasViewRoute !== false) {
582
+ baseRoute.children?.push({
583
+ path: ':uuid',
584
+ loadComponent: () => Promise.resolve().then(function () { return compoundResource_component; }).then((mod) => mod.CompoundResourceComponent),
585
+ canDeactivate: [canDeactivateGuard],
586
+ });
587
+ return baseRoute;
588
+ }
589
+ }
590
+ return {
591
+ path: meta.route,
592
+ data: { config: config },
593
+ children: [
594
+ {
595
+ path: ':uuid',
596
+ loadComponent: () => Promise.resolve().then(function () { return compoundResource_component; }).then((mod) => mod.CompoundResourceComponent),
597
+ canDeactivate: [canDeactivateGuard],
598
+ },
599
+ {
600
+ path: '',
601
+ loadComponent: () => Promise.resolve().then(function () { return resourceListCreate_component; }).then((mod) => mod.ResourceListCreateComponent),
602
+ canDeactivate: [canDeactivateGuard],
603
+ },
604
+ ],
605
+ };
606
+ };
607
+ function addToNavItems(nav, route) {
608
+ nav.navItem.route = [route];
609
+ if (!nav.group) {
610
+ generatedNavItems.push(nav.navItem);
611
+ return;
612
+ }
613
+ if (!generatedNavGroups.includes(nav.group)) {
614
+ generatedNavGroups.push(nav.group);
615
+ }
616
+ let groupItem = generatedNavItems.find((item) => item.label === nav.group);
617
+ if (!groupItem) {
618
+ groupItem = { label: nav.group, children: [] };
619
+ generatedNavItems.push(groupItem);
620
+ }
621
+ groupItem.children = groupItem.children || [];
622
+ groupItem.children.push(nav.navItem);
623
+ generatedNavItems.sort((a, b) => (a.label > b.label ? -1 : 1));
624
+ }
625
+ /**
626
+ * Recursive function to extract nested field names from a config.
627
+ * @param elements - The elements to extract field names from.
628
+ * @returns - An array of field names.
629
+ */
630
+ function extractFieldNames(elements) {
631
+ const fields = [];
632
+ for (const element of elements) {
633
+ if (element.type === 'section') {
634
+ fields.push(...extractFieldNames(element.children));
635
+ }
636
+ else if (element.type === 'field') {
637
+ fields.push(element.name);
638
+ }
639
+ }
640
+ return fields;
641
+ }
642
+ /**
643
+ * Checks if a string is a valid UUID (versions 1-5) according to RFC 4122.
644
+ *
645
+ * @param str - The string to validate.
646
+ * @returns `true` if the string is a valid UUID, otherwise `false`.
647
+ */
648
+ function isUUID(str) {
649
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
650
+ return uuidRegex.test(str);
651
+ }
652
+ function metadataTypeCoercion(record, meta) {
653
+ if (!meta?.fields)
654
+ return record;
655
+ const displayTypeByField = new Map(meta.fields.map((f) => [f.fieldName, f.displayType]));
656
+ for (const [key, value] of Object.entries(record)) {
657
+ const displayType = displayTypeByField.get(key);
658
+ if (displayType === 'civildate' && value && isValid(value)) {
659
+ const date = value;
660
+ record[key] = format(date, 'yyyy-MM-dd');
661
+ }
662
+ }
663
+ return record;
664
+ }
665
+
666
+ /*
667
+ * Base class for all resource input components.
668
+ */
669
+ class BaseInputComponent {
670
+ resourceMeta = inject(RESOURCE_META);
671
+ injector = inject(Injector);
672
+ store = inject(ResourceStore);
673
+ meta = input.required({ ...(ngDevMode ? { debugName: "meta" } : {}) });
674
+ pristineValue = input(undefined, { ...(ngDevMode ? { debugName: "pristineValue" } : {}) });
675
+ editMode = input.required({ ...(ngDevMode ? { debugName: "editMode" } : {}) });
676
+ showField = input(undefined, { ...(ngDevMode ? { debugName: "showField" } : {}) });
677
+ fieldConfig = input.required({ ...(ngDevMode ? { debugName: "fieldConfig" } : {}) });
678
+ fieldClass = input(undefined, { ...(ngDevMode ? { debugName: "fieldClass" } : {}) });
679
+ fieldMeta = input.required({ ...(ngDevMode ? { debugName: "fieldMeta" } : {}) });
680
+ form = input.required({ ...(ngDevMode ? { debugName: "form" } : {}) });
681
+ relatedData = input(undefined, { ...(ngDevMode ? { debugName: "relatedData" } : {}) });
682
+ /**
683
+ * Resets the field back to its pristine value.
684
+ */
685
+ reset() {
686
+ const control = this.form().get(this.fieldConfig().name);
687
+ if (control) {
688
+ control.setValue(this.pristineValue());
689
+ control.markAsPristine();
690
+ }
691
+ }
692
+ affixResources = new Map();
693
+ prefixString = computed(() => {
694
+ const relatedData = this.relatedData();
695
+ let builtPrefix = '';
696
+ this.fieldConfig().prefixes.forEach((prefix) => {
697
+ if (typeof prefix === 'string') {
698
+ builtPrefix += prefix;
699
+ }
700
+ else if ('resource' in prefix) {
701
+ if (!relatedData || !relatedData[prefix.id]) {
702
+ return;
703
+ }
704
+ const id = relatedData[prefix.id];
705
+ if (typeof id !== 'string' || !id || !isUUID(id)) {
706
+ return;
707
+ }
708
+ const cacheKey = `${prefix.resource}-${id}`;
709
+ let resourceRef = this.affixResources.get(cacheKey);
710
+ if (!resourceRef) {
711
+ const prefixResourceMeta = this.resourceMeta(prefix.resource);
712
+ resourceRef = this.store.resourceView(signal(prefixResourceMeta.route), signal(id));
713
+ this.affixResources.set(cacheKey, resourceRef);
714
+ }
715
+ const resource = resourceRef.value();
716
+ if (resource && prefix.field in resource) {
717
+ builtPrefix += resource[String(prefix.field)];
718
+ }
719
+ }
720
+ else if ('field' in prefix) {
721
+ if (relatedData) {
722
+ const fieldValue = relatedData[prefix.field];
723
+ builtPrefix += fieldValue ? fieldValue : '';
724
+ }
725
+ }
726
+ });
727
+ return builtPrefix;
728
+ }, { ...(ngDevMode ? { debugName: "prefixString" } : {}) });
729
+ suffixString = computed(() => {
730
+ const relatedData = this.relatedData();
731
+ let builtSuffix = '';
732
+ this.fieldConfig().suffixes.forEach((suffix) => {
733
+ if (typeof suffix === 'string') {
734
+ builtSuffix += suffix;
735
+ }
736
+ else if ('resource' in suffix) {
737
+ if (!relatedData || !relatedData[suffix.id]) {
738
+ return;
739
+ }
740
+ const id = relatedData[suffix.id];
741
+ if (typeof id !== 'string' || !id || !isUUID(id)) {
742
+ return;
743
+ }
744
+ const cacheKey = `${suffix.resource}-${id}`;
745
+ let resourceRef = this.affixResources.get(cacheKey);
746
+ if (!resourceRef) {
747
+ const suffixResourceMeta = this.resourceMeta(suffix.resource);
748
+ resourceRef = this.store.resourceView(signal(suffixResourceMeta.route), signal(id));
749
+ this.affixResources.set(cacheKey, resourceRef);
750
+ }
751
+ const resource = resourceRef.value();
752
+ if (resource && suffix.field in resource) {
753
+ builtSuffix += resource[suffix.field];
754
+ }
755
+ }
756
+ else if ('field' in suffix) {
757
+ if (relatedData) {
758
+ const fieldValue = relatedData[suffix.field];
759
+ builtSuffix += fieldValue ? fieldValue : '';
760
+ }
761
+ }
762
+ });
763
+ return builtSuffix;
764
+ }, { ...(ngDevMode ? { debugName: "suffixString" } : {}) });
765
+ floatLabel = computed(() => {
766
+ if (this.fieldConfig().prefixes.length > 0 || this.fieldConfig().suffixes.length > 0) {
767
+ return 'always';
768
+ }
769
+ else {
770
+ return 'auto';
771
+ }
772
+ }, { ...(ngDevMode ? { debugName: "floatLabel" } : {}) });
773
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: BaseInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
774
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.2", type: BaseInputComponent, isStandalone: true, inputs: { meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: true, transformFunction: null }, pristineValue: { classPropertyName: "pristineValue", publicName: "pristineValue", isSignal: true, isRequired: false, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: true, transformFunction: null }, showField: { classPropertyName: "showField", publicName: "showField", isSignal: true, isRequired: false, transformFunction: null }, fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: true, isRequired: true, transformFunction: null }, fieldClass: { classPropertyName: "fieldClass", publicName: "fieldClass", isSignal: true, isRequired: false, transformFunction: null }, fieldMeta: { classPropertyName: "fieldMeta", publicName: "fieldMeta", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, relatedData: { classPropertyName: "relatedData", publicName: "relatedData", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
775
+ }
776
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: BaseInputComponent, decorators: [{
777
+ type: Directive
778
+ }], propDecorators: { meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: true }] }], pristineValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "pristineValue", required: false }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: true }] }], showField: [{ type: i0.Input, args: [{ isSignal: true, alias: "showField", required: false }] }], fieldConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldConfig", required: true }] }], fieldClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldClass", required: false }] }], fieldMeta: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldMeta", required: true }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], relatedData: [{ type: i0.Input, args: [{ isSignal: true, alias: "relatedData", required: false }] }] } });
779
+
780
+ class BooleanFieldComponent extends BaseInputComponent {
781
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: BooleanFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
782
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: BooleanFieldComponent, isStandalone: true, selector: "ccc-boolean-field", usesInheritance: true, ngImport: i0, template: "@if (showField()) {\n <ng-container [formGroup]=\"form()\">\n <div [class.readonly-field]=\"editMode() === 'view'\">\n <mat-checkbox\n class=\"field checkbox {{ fieldClass() }}\"\n [formControlName]=\"fieldConfig().name\"\n (keydown.enter)=\"$event.preventDefault()\">\n {{ fieldConfig().label }}\n </mat-checkbox>\n </div>\n </ng-container>\n} @else {\n <ng-container [formGroup]=\"form()\">\n <input type=\"hidden\" [formControlName]=\"fieldConfig().name\" />\n </ng-container>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i2$2.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }] });
783
+ }
784
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: BooleanFieldComponent, decorators: [{
785
+ type: Component,
786
+ args: [{ selector: 'ccc-boolean-field', imports: [MatFormFieldModule, MatInputModule, MatDatepickerModule, ReactiveFormsModule, MatCheckboxModule], template: "@if (showField()) {\n <ng-container [formGroup]=\"form()\">\n <div [class.readonly-field]=\"editMode() === 'view'\">\n <mat-checkbox\n class=\"field checkbox {{ fieldClass() }}\"\n [formControlName]=\"fieldConfig().name\"\n (keydown.enter)=\"$event.preventDefault()\">\n {{ fieldConfig().label }}\n </mat-checkbox>\n </div>\n </ng-container>\n} @else {\n <ng-container [formGroup]=\"form()\">\n <input type=\"hidden\" [formControlName]=\"fieldConfig().name\" />\n </ng-container>\n}\n" }]
787
+ }] });
788
+
789
+ class DateFieldComponent extends BaseInputComponent {
790
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: DateFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
791
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: DateFieldComponent, isStandalone: true, selector: "ccc-date-field", usesInheritance: true, ngImport: i0, template: "@if (showField()) {\n <div [class.readonly-field]=\"editMode() === 'view'\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [formGroup]=\"form()\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <input\n matInput\n [readonly]=\"editMode() === 'view'\"\n [matDatepicker]=\"picker\"\n type=\"text\"\n (keydown.enter)=\"$event.preventDefault()\"\n [formControlName]=\"fieldConfig().name\" />\n @if (editMode() === 'edit') {\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n }\n <mat-datepicker #picker></mat-datepicker>\n </mat-form-field>\n </div>\n} @else {\n <ng-container [formGroup]=\"form()\">\n <input type=\"hidden\" [formControlName]=\"fieldConfig().name\" />\n </ng-container>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i3$1.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i3$1.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i3$1.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
792
+ }
793
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: DateFieldComponent, decorators: [{
794
+ type: Component,
795
+ args: [{ selector: 'ccc-date-field', imports: [MatFormFieldModule, MatInputModule, MatDatepickerModule, ReactiveFormsModule], template: "@if (showField()) {\n <div [class.readonly-field]=\"editMode() === 'view'\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [formGroup]=\"form()\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <input\n matInput\n [readonly]=\"editMode() === 'view'\"\n [matDatepicker]=\"picker\"\n type=\"text\"\n (keydown.enter)=\"$event.preventDefault()\"\n [formControlName]=\"fieldConfig().name\" />\n @if (editMode() === 'edit') {\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n }\n <mat-datepicker #picker></mat-datepicker>\n </mat-form-field>\n </div>\n} @else {\n <ng-container [formGroup]=\"form()\">\n <input type=\"hidden\" [formControlName]=\"fieldConfig().name\" />\n </ng-container>\n}\n" }]
796
+ }] });
797
+
798
+ const concatFunctions = {
799
+ 'space-concat': spaceConcat,
800
+ 'hyphen-concat': hyphenConcat,
801
+ 'space-hyphen-concat': spaceHyphenConcat,
802
+ 'hyphen-space-concat': hyphenSpaceConcat,
803
+ };
804
+ /**
805
+ * Concatenates the strings corresponding to the given keys in the resource,
806
+ * using a space separator.
807
+ * @param resource A record mapping keys to string values.
808
+ * @param args Keys of the resource to concatenate.
809
+ * @returns Concatenated string.
810
+ * @example
811
+ * const resource = { foo: 'foo', bar: 'bar', baz: 'baz' };
812
+ * spaceConcat(resource, 'foo', 'bar', 'baz'); // returns 'foo bar baz'
813
+ */
814
+ function spaceConcat(resource, ...args) {
815
+ if (args.length === 0)
816
+ return '';
817
+ if (args.length === 1)
818
+ return resource[args[0] ?? ''] ?? '';
819
+ return args.map((arg) => resource[arg] ?? '').join(' ');
820
+ }
821
+ /**
822
+ * Concatenates the strings corresponding to the given keys in the resource,
823
+ * using a hyphen separator.
824
+ * @param resource A record mapping keys to string values.
825
+ * @param args Keys of the resource to concatenate.
826
+ * @returns Concatenated string.
827
+ * @example
828
+ * const resource = { foo: 'foo', bar: 'bar', baz: 'baz' };
829
+ * hyphenConcat(resource, 'foo', 'bar', 'baz'); // returns 'foo - bar - baz'
830
+ */
831
+ function hyphenConcat(resource, ...args) {
832
+ if (args.length === 0)
833
+ return '';
834
+ if (args.length === 1)
835
+ return resource[args[0] ?? ''] ?? '';
836
+ return args.map((arg) => resource[arg] ?? '').join(' - ');
837
+ }
838
+ /**
839
+ * Concatenates the given strings so that all but the last are joined with a space,
840
+ * and the last string is appended with a " - " separator.
841
+ * @param resource A mapping of keys to string values.
842
+ * @param args Keys of strings to concatenate.
843
+ * @returns Concatenated string.
844
+ * @example spaceHyphenConcat(resource, 'foo', 'bar', 'baz') => 'foo bar - baz'
845
+ */
846
+ function spaceHyphenConcat(resource, ...args) {
847
+ if (args.length === 0)
848
+ return '';
849
+ if (args.length === 1)
850
+ return resource[args[0] || ''] || '';
851
+ const initialPart = args
852
+ .slice(0, -1)
853
+ .map((arg) => resource[arg] || '')
854
+ .join(' ');
855
+ const lastPart = resource[args[args.length - 1] || ''] || '';
856
+ return `${initialPart} - ${lastPart}`;
857
+ }
858
+ /**
859
+ * Concatenates the given strings so that all but the first are joined with a space,
860
+ * and the first string is appended with a " - " separator.
861
+ * @param resource A mapping of keys to string values.
862
+ * @param args Keys of strings to concatenate.
863
+ * @returns Concatenated string.
864
+ * @example spaceHyphenConcat(resource, 'foo', 'bar', 'baz') => 'foo - bar baz'
865
+ */
866
+ function hyphenSpaceConcat(resource, ...args) {
867
+ if (args.length === 0)
868
+ return '';
869
+ if (args.length === 1)
870
+ return resource[args[0] || ''] || '';
871
+ const initialPart = args[0] !== undefined ? resource[args[0]] || '' : '';
872
+ const lastPart = args
873
+ .slice(1, args.length)
874
+ .map((arg) => resource[arg] || '')
875
+ .join(' ');
876
+ return `${initialPart} - ${lastPart}`;
877
+ }
878
+ /**
879
+ * Concatenates the strings using a space separator.
880
+ * @param args Strings to concatenate.
881
+ * @returns Concatenated string.
882
+ * @example
883
+ * spaceConcatWithoutResource(['foo', 'bar', 'baz']); // returns 'foo bar baz'
884
+ */
885
+ function spaceConcatWithoutResource(args) {
886
+ if (args.length === 0)
887
+ return '';
888
+ if (args.length === 1)
889
+ return args[0] ?? '';
890
+ return args.map((arg) => arg ?? '').join(' ');
891
+ }
892
+ /**
893
+ * Concatenates the strings using a hyphen separator.
894
+ * @param args Strings to concatenate.
895
+ * @returns Concatenated string.
896
+ * @example
897
+ * hyphenConcatWithoutResource(['foo', 'bar', 'baz']); // returns 'foo - bar - baz'
898
+ */
899
+ function hyphenConcatWithoutResource(args) {
900
+ if (args.length === 0)
901
+ return '';
902
+ if (args.length === 1)
903
+ return args[0] || '';
904
+ return args.join(' - ');
905
+ }
906
+ /**
907
+ * Concatenates the given strings so that all but the last are joined with a space,
908
+ * and the last string is appended with a " - " separator.
909
+ * @param args Strings to concatenate.
910
+ * @returns Concatenated string.
911
+ * @example spaceHyphenConcatWithoutResource(['foo', 'bar', 'baz']) => 'foo bar - baz'
912
+ */
913
+ function spaceHyphenConcatWithoutResource(args) {
914
+ if (args.length === 0)
915
+ return '';
916
+ if (args.length === 1)
917
+ return args[0] || '';
918
+ const initialPart = args
919
+ .slice(0, -1)
920
+ .map((arg) => arg || '')
921
+ .join(' ');
922
+ const lastPart = args[args.length - 1] || '';
923
+ return `${initialPart} - ${lastPart}`;
924
+ }
925
+ /**
926
+ * Concatenates the given strings so that all but the first are joined with a space,
927
+ * and the first string is appended with a " - " separator.
928
+ * @param args Strings to concatenate.
929
+ * @returns Concatenated string.
930
+ * @example hyphenSpaceConcatWithoutResource(['foo', 'bar', 'baz']) => 'foo - bar baz'
931
+ */
932
+ function hyphenSpaceConcatWithoutResource(args) {
933
+ if (args.length === 0)
934
+ return '';
935
+ if (args.length === 1)
936
+ return args[0] || '';
937
+ const initialPart = args[0] || '';
938
+ const lastPart = args
939
+ .slice(1, args.length)
940
+ .map((arg) => arg || '')
941
+ .join(' ');
942
+ return `${initialPart} - ${lastPart}`;
943
+ }
944
+ /**
945
+ * Concatenates the given strings without any space,
946
+ * @param args Strings to concatenate.
947
+ * @returns Concatenated string.
948
+ * @example noSpaceConcatWithoutResource(['foo', 'bar', 'baz']) => 'foobarbaz'
949
+ */
950
+ function noSpaceConcatWithoutResource(args) {
951
+ if (args.length === 0)
952
+ return '';
953
+ if (args.length === 1)
954
+ return args[0] || '';
955
+ return args.join('');
956
+ }
957
+
958
+ class EnumeratedFieldComponent extends BaseInputComponent {
959
+ activatedRoute = inject(ActivatedRoute);
960
+ router = inject(Router);
961
+ reloadSignal = signal(false, { ...(ngDevMode ? { debugName: "reloadSignal" } : {}) });
962
+ query = signal('', { ...(ngDevMode ? { debugName: "query" } : {}) });
963
+ resource = computed(() => {
964
+ if (this.fieldConfig()?.enumeratedConfig?.overrideResource) {
965
+ return this.fieldConfig()?.enumeratedConfig?.overrideResource;
966
+ }
967
+ return this.fieldMeta()?.enumeratedResource;
968
+ }, { ...(ngDevMode ? { debugName: "resource" } : {}) });
969
+ tooltipMessage = computed(() => {
970
+ const label = this.fieldConfig().label;
971
+ return 'View ' + label + ' details';
972
+ }, { ...(ngDevMode ? { debugName: "tooltipMessage" } : {}) });
973
+ route = computed(() => {
974
+ const resource = this.fieldMeta()?.enumeratedResource;
975
+ return this.resourceMeta(resource)?.route;
976
+ }, { ...(ngDevMode ? { debugName: "route" } : {}) });
977
+ viewDetails = computed(() => {
978
+ return this.editMode() === 'view' && this.fieldConfig().enumeratedConfig.viewDetails === true;
979
+ }, { ...(ngDevMode ? { debugName: "viewDetails" } : {}) });
980
+ sorts = computed(() => {
981
+ return this.fieldConfig().enumeratedConfig.sorts;
982
+ }, { ...(ngDevMode ? { debugName: "sorts" } : {}) });
983
+ singleEnumResourceRef = computed(() => {
984
+ this.editMode();
985
+ if (this.showField() === false) {
986
+ return undefined;
987
+ }
988
+ const route = this.route();
989
+ const resource = this.resource();
990
+ this.reloadSignal();
991
+ const fieldValue = this.form().get(this.fieldConfig().name)?.value;
992
+ if (fieldValue && route && resource) {
993
+ return untracked(() => this.store.resourceView(this.route, signal(fieldValue)));
994
+ }
995
+ return undefined;
996
+ }, { ...(ngDevMode ? { debugName: "singleEnumResourceRef" } : {}) });
997
+ rootResourceRef = computed(() => {
998
+ const rootConfig = this.activatedRoute.snapshot.data['config'];
999
+ const uuid = (this.activatedRoute.snapshot.params['uuid'] || '');
1000
+ const rootMeta = this.resourceMeta(rootConfig.parentConfig.primaryResource);
1001
+ return this.store.resourceView(signal(rootMeta.route), signal(uuid));
1002
+ }, { ...(ngDevMode ? { debugName: "rootResourceRef" } : {}) });
1003
+ enumResourceRef = computed(() => {
1004
+ if (this.showField() === false)
1005
+ return undefined;
1006
+ const enumeratedMeta = this.resourceMeta(this.resource());
1007
+ const config = this.fieldConfig().enumeratedConfig;
1008
+ const filter = config.filterType === 'rootResource'
1009
+ ? config?.filter?.(this.rootResourceRef()?.value() || {})
1010
+ : config?.filter?.(this.relatedData() || {});
1011
+ return this.store.resourceList(signal(enumeratedMeta.route), signal(filter), signal([]), signal(config.disableCacheForFilterPii), this.query, this.sorts);
1012
+ }, { ...(ngDevMode ? { debugName: "enumResourceRef" } : {}) });
1013
+ singleEnumDisplayText = computed(() => {
1014
+ const showField = this.showField();
1015
+ if (showField === false) {
1016
+ return undefined;
1017
+ }
1018
+ const form = this.form();
1019
+ const fieldConfig = this.fieldConfig();
1020
+ const singleEnumResourceRef = this.singleEnumResourceRef();
1021
+ if (form === undefined || fieldConfig === undefined || singleEnumResourceRef === undefined) {
1022
+ return defaultEmptyFieldValue;
1023
+ }
1024
+ const record = singleEnumResourceRef.value();
1025
+ if (record === undefined) {
1026
+ return defaultEmptyFieldValue;
1027
+ }
1028
+ const enumeratedValues = this.toEnumerated(record, fieldConfig);
1029
+ return enumeratedValues.display;
1030
+ }, { ...(ngDevMode ? { debugName: "singleEnumDisplayText" } : {}) });
1031
+ hasRequiredValidator = computed(() => {
1032
+ const form = this.form();
1033
+ const fieldConfig = this.fieldConfig();
1034
+ if (form === undefined || fieldConfig === undefined) {
1035
+ return false;
1036
+ }
1037
+ const control = form.get(fieldConfig.name);
1038
+ if (!control) {
1039
+ return false;
1040
+ }
1041
+ return control.hasValidator(Validators.required);
1042
+ }, { ...(ngDevMode ? { debugName: "hasRequiredValidator" } : {}) });
1043
+ singleEnumValue = computed(() => {
1044
+ if (this.showField() === false)
1045
+ return undefined;
1046
+ const currentValue = this.form().get(this.fieldConfig().name)?.value;
1047
+ const record = this.singleEnumResourceRef()?.value();
1048
+ if (!currentValue)
1049
+ return [];
1050
+ if (!record)
1051
+ return [];
1052
+ return [this.toEnumerated(record, this.fieldConfig())];
1053
+ }, { ...(ngDevMode ? { debugName: "singleEnumValue" } : {}) });
1054
+ listEnumValues = computed(() => {
1055
+ if (this.showField() === false)
1056
+ return [];
1057
+ const currentValue = this.singleEnumValue();
1058
+ const records = this.enumResourceRef()?.value();
1059
+ if (!records || !records.length)
1060
+ return currentValue || [];
1061
+ return records.map((record) => this.toEnumerated(record, this.fieldConfig()));
1062
+ }, { ...(ngDevMode ? { debugName: "listEnumValues" } : {}) });
1063
+ availableEnumOptions = computed(() => {
1064
+ if (this.editMode() === 'edit') {
1065
+ return this.listEnumValues();
1066
+ }
1067
+ return this.singleEnumValue();
1068
+ }, { ...(ngDevMode ? { debugName: "availableEnumOptions" } : {}) });
1069
+ toEnumerated(resource, element) {
1070
+ const enumeratedConfig = element.enumeratedConfig;
1071
+ const displayFields = this.editMode() === 'edit' && enumeratedConfig.listDisplay.length > 0
1072
+ ? enumeratedConfig.listDisplay
1073
+ : enumeratedConfig.viewDisplay;
1074
+ const concat = this.editMode() === 'edit'
1075
+ ? enumeratedConfig.listConcatFn || enumeratedConfig.viewConcatFn
1076
+ : enumeratedConfig.viewConcatFn;
1077
+ const concatFunction = concatFunctions[concat] || hyphenConcat;
1078
+ return {
1079
+ id: resource['id'] ?? '',
1080
+ display: concatFunction(resource, ...displayFields),
1081
+ };
1082
+ }
1083
+ getDisplayText = (value) => {
1084
+ if (!value)
1085
+ return '';
1086
+ if (typeof value === 'string') {
1087
+ const option = this.availableEnumOptions()?.find((o) => o.id === value);
1088
+ return option ? option['display'] || this.formatDisplay(option) : '';
1089
+ }
1090
+ return value['display'] || this.formatDisplay(value);
1091
+ };
1092
+ formatDisplay(resource) {
1093
+ return this.toEnumerated(resource, this.fieldConfig()).display;
1094
+ }
1095
+ search(value) {
1096
+ this.query.set(value);
1097
+ }
1098
+ select(value) {
1099
+ this.form().patchValue({ [this.fieldConfig().name]: value });
1100
+ this.form().markAsDirty();
1101
+ this.form().markAsTouched();
1102
+ this.reloadSignal.update((prev) => !prev);
1103
+ }
1104
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: EnumeratedFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1105
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: EnumeratedFieldComponent, isStandalone: true, selector: "ccc-enumerated-field", usesInheritance: true, ngImport: i0, template: "<ng-container [formGroup]=\"form()\">\n @if (showField()) {\n @let options = availableEnumOptions();\n @if (editMode() === 'edit') {\n <div class=\"enumerated-field-container\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n @if (fieldConfig().enumeratedConfig.searchable) {\n <input\n #input\n type=\"text\"\n placeholder=\"Search\"\n matInput\n [matAutocomplete]=\"auto\"\n [formControlName]=\"fieldConfig().name\"\n (input)=\"search(input.value)\" />\n <mat-autocomplete\n (optionSelected)=\"select($event.option.value)\"\n #auto=\"matAutocomplete\"\n [displayWith]=\"getDisplayText\">\n @for (option of options; track option.id) {\n <mat-option [value]=\"option.id\">{{ option?.display }}</mat-option>\n }\n </mat-autocomplete>\n } @else {\n <mat-select\n [formControlName]=\"fieldConfig().name\"\n (keydown.enter)=\"$event.preventDefault()\"\n (selectionChange)=\"select($event.value)\"\n [ariaReadOnly]=\"editMode() === 'view'\">\n @for (option of options; track option.id) {\n @if (option?.id === form().get(this.fieldConfig().name)?.value) {\n <mat-option [value]=\"option?.id\" selected>{{ option?.display }}</mat-option>\n } @else {\n <mat-option [value]=\"option?.id\">{{ option?.display }}</mat-option>\n }\n }\n </mat-select>\n }\n </mat-form-field>\n </div>\n } @else {\n <div class=\"enumerated-field-container readonly-field\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <input\n id=\"{{ fieldConfig().name }}-readonly-input-text\"\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"true\"\n type=\"text\"\n [required]=\"hasRequiredValidator()\"\n [value]=\"singleEnumDisplayText()\" />\n </mat-form-field>\n @if (viewDetails()) {\n <a\n mat-icon-button\n color=\"accent\"\n [matTooltip]=\"tooltipMessage()\"\n matTooltipPosition=\"below\"\n [routerLink]=\"['/', route(), form().get(this.fieldConfig().name)?.value]\">\n <mat-icon>arrow_forward</mat-icon>\n </a>\n }\n </div>\n <input class=\"display-none\" [formControlName]=\"fieldConfig().name\" />\n }\n } @else {\n <input class=\"display-none\" [formControlName]=\"fieldConfig().name\" />\n }\n</ng-container>\n", styles: [".enumerated-field-container{display:flex;flex-direction:row;gap:10px}.display-none{display:none}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i9.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i10.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i10.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1106
+ }
1107
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: EnumeratedFieldComponent, decorators: [{
1108
+ type: Component,
1109
+ args: [{ selector: 'ccc-enumerated-field', imports: [
1110
+ MatFormFieldModule,
1111
+ MatInputModule,
1112
+ MatDatepickerModule,
1113
+ ReactiveFormsModule,
1114
+ MatOptionModule,
1115
+ MatSelectModule,
1116
+ MatIconModule,
1117
+ MatButtonModule,
1118
+ MatTooltipModule,
1119
+ RouterModule,
1120
+ MatAutocompleteModule,
1121
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container [formGroup]=\"form()\">\n @if (showField()) {\n @let options = availableEnumOptions();\n @if (editMode() === 'edit') {\n <div class=\"enumerated-field-container\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n @if (fieldConfig().enumeratedConfig.searchable) {\n <input\n #input\n type=\"text\"\n placeholder=\"Search\"\n matInput\n [matAutocomplete]=\"auto\"\n [formControlName]=\"fieldConfig().name\"\n (input)=\"search(input.value)\" />\n <mat-autocomplete\n (optionSelected)=\"select($event.option.value)\"\n #auto=\"matAutocomplete\"\n [displayWith]=\"getDisplayText\">\n @for (option of options; track option.id) {\n <mat-option [value]=\"option.id\">{{ option?.display }}</mat-option>\n }\n </mat-autocomplete>\n } @else {\n <mat-select\n [formControlName]=\"fieldConfig().name\"\n (keydown.enter)=\"$event.preventDefault()\"\n (selectionChange)=\"select($event.value)\"\n [ariaReadOnly]=\"editMode() === 'view'\">\n @for (option of options; track option.id) {\n @if (option?.id === form().get(this.fieldConfig().name)?.value) {\n <mat-option [value]=\"option?.id\" selected>{{ option?.display }}</mat-option>\n } @else {\n <mat-option [value]=\"option?.id\">{{ option?.display }}</mat-option>\n }\n }\n </mat-select>\n }\n </mat-form-field>\n </div>\n } @else {\n <div class=\"enumerated-field-container readonly-field\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <input\n id=\"{{ fieldConfig().name }}-readonly-input-text\"\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"true\"\n type=\"text\"\n [required]=\"hasRequiredValidator()\"\n [value]=\"singleEnumDisplayText()\" />\n </mat-form-field>\n @if (viewDetails()) {\n <a\n mat-icon-button\n color=\"accent\"\n [matTooltip]=\"tooltipMessage()\"\n matTooltipPosition=\"below\"\n [routerLink]=\"['/', route(), form().get(this.fieldConfig().name)?.value]\">\n <mat-icon>arrow_forward</mat-icon>\n </a>\n }\n </div>\n <input class=\"display-none\" [formControlName]=\"fieldConfig().name\" />\n }\n } @else {\n <input class=\"display-none\" [formControlName]=\"fieldConfig().name\" />\n }\n</ng-container>\n", styles: [".enumerated-field-container{display:flex;flex-direction:row;gap:10px}.display-none{display:none}\n"] }]
1122
+ }] });
1123
+
1124
+ class NullBooleanFieldComponent extends BaseInputComponent {
1125
+ availableEnumOptions = computed(() => {
1126
+ const fieldConfig = this.fieldConfig();
1127
+ if (fieldConfig === undefined) {
1128
+ return;
1129
+ }
1130
+ const valueMap = new Map();
1131
+ Object.values(fieldConfig.nullBooleanConfig.displayValues).forEach((value) => {
1132
+ valueMap.set(value.value, value.label);
1133
+ });
1134
+ return valueMap;
1135
+ }, { ...(ngDevMode ? { debugName: "availableEnumOptions" } : {}) });
1136
+ currentValue = computed(() => {
1137
+ const form = this.form();
1138
+ const fieldConfig = this.fieldConfig();
1139
+ if (form === undefined || fieldConfig === undefined) {
1140
+ return;
1141
+ }
1142
+ return form.get(fieldConfig.name)?.value;
1143
+ }, { ...(ngDevMode ? { debugName: "currentValue" } : {}) });
1144
+ currentDisplayText = computed(() => {
1145
+ const value = this.currentValue();
1146
+ const availableOptions = this.availableEnumOptions();
1147
+ if (availableOptions === undefined || value === undefined) {
1148
+ return;
1149
+ }
1150
+ return availableOptions.get(value);
1151
+ }, { ...(ngDevMode ? { debugName: "currentDisplayText" } : {}) });
1152
+ hasRequiredValidator = computed(() => {
1153
+ const form = this.form();
1154
+ const fieldConfig = this.fieldConfig();
1155
+ if (form === undefined || fieldConfig === undefined) {
1156
+ return false;
1157
+ }
1158
+ const control = form.get(fieldConfig.name);
1159
+ if (control === null) {
1160
+ return false;
1161
+ }
1162
+ return control.hasValidator(Validators.required);
1163
+ }, { ...(ngDevMode ? { debugName: "hasRequiredValidator" } : {}) });
1164
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: NullBooleanFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1165
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: NullBooleanFieldComponent, isStandalone: true, selector: "ccc-nullboolean-field", usesInheritance: true, ngImport: i0, template: "<ng-container [formGroup]=\"form()\">\n @if (showField()) {\n @let options = availableEnumOptions();\n @if (editMode() === 'edit') {\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <mat-select\n [formControlName]=\"fieldConfig().name\"\n (keydown.enter)=\"$event.preventDefault()\"\n [canSelectNullableOptions]=\"true\"\n [ariaReadOnly]=\"editMode() === 'view'\">\n @for (option of options; track option[0]) {\n @if (option[0] === currentValue()) {\n <mat-option [value]=\"option[0]\" selected>{{ option[1] }}</mat-option>\n } @else {\n <mat-option [value]=\"option[0]\">{{ option[1] }}</mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n } @else {\n <div class=\"readonly-field\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"true\"\n type=\"text\"\n [required]=\"hasRequiredValidator()\"\n [value]=\"currentDisplayText()\" />\n </mat-form-field>\n </div>\n }\n } @else {\n <input class=\"display-none\" [formControlName]=\"fieldConfig().name\" />\n }\n</ng-container>\n", styles: [".display-none{display:none}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: MatAutocompleteModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1166
+ }
1167
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: NullBooleanFieldComponent, decorators: [{
1168
+ type: Component,
1169
+ args: [{ selector: 'ccc-nullboolean-field', imports: [
1170
+ MatFormFieldModule,
1171
+ MatInputModule,
1172
+ MatDatepickerModule,
1173
+ ReactiveFormsModule,
1174
+ MatOptionModule,
1175
+ MatSelectModule,
1176
+ MatIconModule,
1177
+ MatButtonModule,
1178
+ MatTooltipModule,
1179
+ RouterModule,
1180
+ MatAutocompleteModule,
1181
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container [formGroup]=\"form()\">\n @if (showField()) {\n @let options = availableEnumOptions();\n @if (editMode() === 'edit') {\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <mat-select\n [formControlName]=\"fieldConfig().name\"\n (keydown.enter)=\"$event.preventDefault()\"\n [canSelectNullableOptions]=\"true\"\n [ariaReadOnly]=\"editMode() === 'view'\">\n @for (option of options; track option[0]) {\n @if (option[0] === currentValue()) {\n <mat-option [value]=\"option[0]\" selected>{{ option[1] }}</mat-option>\n } @else {\n <mat-option [value]=\"option[0]\">{{ option[1] }}</mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n } @else {\n <div class=\"readonly-field\">\n <mat-form-field class=\"field {{ fieldClass() }}\" [subscriptSizing]=\"'dynamic'\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"true\"\n type=\"text\"\n [required]=\"hasRequiredValidator()\"\n [value]=\"currentDisplayText()\" />\n </mat-form-field>\n </div>\n }\n } @else {\n <input class=\"display-none\" [formControlName]=\"fieldConfig().name\" />\n }\n</ng-container>\n", styles: [".display-none{display:none}\n"] }]
1182
+ }] });
1183
+
1184
+ class NumberFieldComponent extends BaseInputComponent {
1185
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: NumberFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1186
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: NumberFieldComponent, isStandalone: true, selector: "ccc-number-field", usesInheritance: true, ngImport: i0, template: "@if (showField()) {\n <div [class.readonly-field]=\"editMode() === 'view'\">\n <mat-form-field\n class=\"field {{ fieldClass() }}\"\n [formGroup]=\"form()\"\n [subscriptSizing]=\"'dynamic'\"\n [floatLabel]=\"floatLabel()\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <span matTextPrefix class=\"prefix\">{{ prefixString() }}</span>\n <span matTextSuffix class=\"suffix\">{{ suffixString() }}</span>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"editMode() === 'view'\"\n type=\"number\"\n [formControlName]=\"fieldConfig().name\" />\n </mat-form-field>\n </div>\n} @else {\n <ng-container [formGroup]=\"form()\">\n <input type=\"hidden\" [formControlName]=\"fieldConfig().name\" />\n </ng-container>\n}\n", styles: [".prefix,.suffix{font-style:italic;color:gray;padding-right:5px}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
1187
+ }
1188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: NumberFieldComponent, decorators: [{
1189
+ type: Component,
1190
+ args: [{ selector: 'ccc-number-field', imports: [MatFormFieldModule, MatInputModule, MatDatepickerModule, ReactiveFormsModule], template: "@if (showField()) {\n <div [class.readonly-field]=\"editMode() === 'view'\">\n <mat-form-field\n class=\"field {{ fieldClass() }}\"\n [formGroup]=\"form()\"\n [subscriptSizing]=\"'dynamic'\"\n [floatLabel]=\"floatLabel()\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <span matTextPrefix class=\"prefix\">{{ prefixString() }}</span>\n <span matTextSuffix class=\"suffix\">{{ suffixString() }}</span>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"editMode() === 'view'\"\n type=\"number\"\n [formControlName]=\"fieldConfig().name\" />\n </mat-form-field>\n </div>\n} @else {\n <ng-container [formGroup]=\"form()\">\n <input type=\"hidden\" [formControlName]=\"fieldConfig().name\" />\n </ng-container>\n}\n", styles: [".prefix,.suffix{font-style:italic;color:gray;padding-right:5px}\n"] }]
1191
+ }] });
1192
+
1193
+ class TextFieldComponent extends BaseInputComponent {
1194
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: TextFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1195
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: TextFieldComponent, isStandalone: true, selector: "ccc-text-field", usesInheritance: true, ngImport: i0, template: "@if (showField()) {\n <div [class.readonly-field]=\"editMode() === 'view'\">\n <mat-form-field\n class=\"field {{ fieldClass() }}\"\n [formGroup]=\"form()\"\n [subscriptSizing]=\"'dynamic'\"\n [floatLabel]=\"floatLabel()\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <span matTextPrefix class=\"prefix\">{{ prefixString() }}</span>\n <span matTextSuffix class=\"suffix\">{{ suffixString() }}</span>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"editMode() === 'view'\"\n type=\"text\"\n [formControlName]=\"fieldConfig().name\"\n [class.dirty-input]=\"form().get(fieldConfig().name)?.dirty\" />\n @if (editMode() === 'edit' && form().get(fieldConfig().name)?.dirty) {\n <button matSuffix mat-icon-button (click)=\"reset()\" tabindex=\"-1\">\n <mat-icon>refresh</mat-icon>\n </button>\n }\n </mat-form-field>\n </div>\n} @else {\n <ng-container [formGroup]=\"form()\">\n <input type=\"hidden\" [formControlName]=\"fieldConfig().name\" />\n </ng-container>\n}\n", styles: [".prefix,.suffix{font-style:italic;color:gray;padding-right:5px}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }] });
1196
+ }
1197
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: TextFieldComponent, decorators: [{
1198
+ type: Component,
1199
+ args: [{ selector: 'ccc-text-field', imports: [
1200
+ MatFormFieldModule,
1201
+ MatInputModule,
1202
+ MatDatepickerModule,
1203
+ ReactiveFormsModule,
1204
+ MatIconModule,
1205
+ MatButtonModule,
1206
+ ], template: "@if (showField()) {\n <div [class.readonly-field]=\"editMode() === 'view'\">\n <mat-form-field\n class=\"field {{ fieldClass() }}\"\n [formGroup]=\"form()\"\n [subscriptSizing]=\"'dynamic'\"\n [floatLabel]=\"floatLabel()\">\n <mat-label>{{ fieldConfig().label }}</mat-label>\n <span matTextPrefix class=\"prefix\">{{ prefixString() }}</span>\n <span matTextSuffix class=\"suffix\">{{ suffixString() }}</span>\n <input\n matInput\n (keydown.enter)=\"$event.preventDefault()\"\n [readonly]=\"editMode() === 'view'\"\n type=\"text\"\n [formControlName]=\"fieldConfig().name\"\n [class.dirty-input]=\"form().get(fieldConfig().name)?.dirty\" />\n @if (editMode() === 'edit' && form().get(fieldConfig().name)?.dirty) {\n <button matSuffix mat-icon-button (click)=\"reset()\" tabindex=\"-1\">\n <mat-icon>refresh</mat-icon>\n </button>\n }\n </mat-form-field>\n </div>\n} @else {\n <ng-container [formGroup]=\"form()\">\n <input type=\"hidden\" [formControlName]=\"fieldConfig().name\" />\n </ng-container>\n}\n", styles: [".prefix,.suffix{font-style:italic;color:gray;padding-right:5px}\n"] }]
1207
+ }] });
1208
+
1209
+ class ResourceFieldComponent {
1210
+ fieldConfig = input.required({ ...(ngDevMode ? { debugName: "fieldConfig" } : {}) });
1211
+ meta = input.required({ ...(ngDevMode ? { debugName: "meta" } : {}) });
1212
+ fieldClass = input(undefined, { ...(ngDevMode ? { debugName: "fieldClass" } : {}) });
1213
+ editMode = input('edit', { ...(ngDevMode ? { debugName: "editMode" } : {}) });
1214
+ form = input.required({ ...(ngDevMode ? { debugName: "form" } : {}) });
1215
+ formDataState = input(undefined, { ...(ngDevMode ? { debugName: "formDataState" } : {}) });
1216
+ pristineValue = input(undefined, { ...(ngDevMode ? { debugName: "pristineValue" } : {}) });
1217
+ data = input(undefined, { ...(ngDevMode ? { debugName: "data" } : {}) });
1218
+ previouslyNulled = null;
1219
+ fieldMeta = computed(() => {
1220
+ return (this.meta().fields?.find((field) => field.fieldName === this.fieldConfig().name) ||
1221
+ {});
1222
+ }, { ...(ngDevMode ? { debugName: "fieldMeta" } : {}) });
1223
+ mode = computed(() => {
1224
+ if (this.fieldConfig().readOnly) {
1225
+ return 'view';
1226
+ }
1227
+ return this.editMode();
1228
+ }, { ...(ngDevMode ? { debugName: "mode" } : {}) });
1229
+ showField = computed(() => {
1230
+ const shouldRender = this.fieldConfig().shouldRender;
1231
+ const conditionallyNull = this.fieldConfig().nullIfConditionallyHidden;
1232
+ const isForeignKeyDefault = this.fieldConfig().default?.type === 'foreignKey';
1233
+ if (typeof shouldRender === 'boolean') {
1234
+ return shouldRender && !isForeignKeyDefault;
1235
+ }
1236
+ const formValues = this.formDataState();
1237
+ if (!formValues) {
1238
+ return true;
1239
+ }
1240
+ console.debug('Field: ', this.fieldConfig().name, ' | Values to be used in showField calculation ', formValues);
1241
+ try {
1242
+ const showField = shouldRender(formValues) && !isForeignKeyDefault;
1243
+ if (!conditionallyNull) {
1244
+ return showField;
1245
+ }
1246
+ const formControlName = this.fieldConfig()?.name;
1247
+ const control = this.form()?.controls[formControlName];
1248
+ const pristineFieldValue = this.data()?.[formControlName];
1249
+ const previouslyNulled = this.previouslyNulled;
1250
+ untracked(() => {
1251
+ if (!showField && !previouslyNulled) {
1252
+ this.previouslyNulled = true;
1253
+ control?.setValue(null);
1254
+ }
1255
+ if (showField && previouslyNulled) {
1256
+ this.previouslyNulled = false;
1257
+ control?.setValue(pristineFieldValue);
1258
+ }
1259
+ });
1260
+ return showField;
1261
+ }
1262
+ catch (e) {
1263
+ console.error('Failed to calculate value for should Render function for field: ', this.fieldConfig().name);
1264
+ console.error(e);
1265
+ return true;
1266
+ }
1267
+ }, { ...(ngDevMode ? { debugName: "showField" } : {}) });
1268
+ showEmptyField = computed(() => {
1269
+ const editMode = this.mode();
1270
+ const showField = this.showField();
1271
+ if (!showField || editMode === 'edit') {
1272
+ return false;
1273
+ }
1274
+ const form = this.form();
1275
+ const config = this.fieldConfig();
1276
+ const meta = this.fieldMeta();
1277
+ if (form === undefined || config === undefined || meta === undefined) {
1278
+ return false;
1279
+ }
1280
+ if (meta.displayType === 'nullboolean') {
1281
+ return false;
1282
+ }
1283
+ const value = form.get(config.name)?.value;
1284
+ if (value === null || value === '') {
1285
+ return true;
1286
+ }
1287
+ return false;
1288
+ }, { ...(ngDevMode ? { debugName: "showEmptyField" } : {}) });
1289
+ class = '';
1290
+ booleanEditDisplayType = computed(() => {
1291
+ const fieldConfig = this.fieldConfig();
1292
+ const fieldMeta = this.fieldMeta();
1293
+ const form = this.form();
1294
+ if (fieldConfig === undefined || fieldMeta === undefined || form === undefined) {
1295
+ return 'boolean';
1296
+ }
1297
+ const control = form.get(fieldConfig.name);
1298
+ if (control === null) {
1299
+ console.warn("Unable to find control for field '" + fieldConfig.name + "' to determine boolean display type");
1300
+ return 'boolean';
1301
+ }
1302
+ // Frontend forcing users to make a choice displays nullboolean to
1303
+ // trigger form validation - users must interact and explicitly
1304
+ // choose a true or false value
1305
+ if (control.hasValidator(Validators.required) || fieldMeta.displayType === 'nullboolean') {
1306
+ return 'nullboolean';
1307
+ }
1308
+ return 'boolean';
1309
+ }, { ...(ngDevMode ? { debugName: "booleanEditDisplayType" } : {}) });
1310
+ previousValidatorCount = 0;
1311
+ constructor() {
1312
+ effect(() => {
1313
+ this.class = this.showField() ? 'col-' + this.fieldConfig()?.cols : 'hidden-field';
1314
+ });
1315
+ effect(() => {
1316
+ const getValidators = this.fieldConfig().validators;
1317
+ if (typeof getValidators !== 'function') {
1318
+ return;
1319
+ }
1320
+ const formValues = this.formDataState();
1321
+ if (!formValues) {
1322
+ return;
1323
+ }
1324
+ console.debug('Field: ', this.fieldConfig().name, ' | Values to be used in validators calculation ', formValues);
1325
+ try {
1326
+ const newValidators = getValidators(formValues);
1327
+ const formControlName = this.fieldConfig().name;
1328
+ const control = this.form().get(formControlName);
1329
+ if (control === null) {
1330
+ throw new Error(`Control with name ${this.fieldConfig().name} not found during forceRequired calculation`);
1331
+ }
1332
+ const addValidators = !validatorsPresent(control, newValidators, this.previousValidatorCount);
1333
+ if (addValidators) {
1334
+ control.setValidators(newValidators);
1335
+ control.updateValueAndValidity();
1336
+ }
1337
+ this.previousValidatorCount = newValidators.length;
1338
+ }
1339
+ catch (e) {
1340
+ console.error('Failed to calculate value for forceRequired function for field: ', this.fieldConfig().name);
1341
+ console.error(e);
1342
+ return;
1343
+ }
1344
+ });
1345
+ }
1346
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1347
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: ResourceFieldComponent, isStandalone: true, selector: "ccc-resource-field", inputs: { fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: true, isRequired: true, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: true, transformFunction: null }, fieldClass: { classPropertyName: "fieldClass", publicName: "fieldClass", isSignal: true, isRequired: false, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, formDataState: { classPropertyName: "formDataState", publicName: "formDataState", isSignal: true, isRequired: false, transformFunction: null }, pristineValue: { classPropertyName: "pristineValue", publicName: "pristineValue", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.class" } }, ngImport: i0, template: "<div [class.hidden-field]=\"!showField()\">\n @if (showEmptyField()) {\n <ccc-empty-readonly-field [label]=\"fieldConfig().label\"></ccc-empty-readonly-field>\n } @else {\n @switch (fieldMeta().displayType) {\n @case ('date') {\n <ccc-date-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-date-field>\n }\n\n @case ('civildate') {\n <ccc-date-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-date-field>\n }\n\n @case ('number') {\n <ccc-number-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-number-field>\n }\n\n @case ('boolean') {\n <ng-container *ngTemplateOutlet=\"booleanFragment\"></ng-container>\n }\n @case ('nullboolean') {\n <ng-container *ngTemplateOutlet=\"booleanFragment\"></ng-container>\n }\n\n @case ('enumerated') {\n <ccc-enumerated-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-enumerated-field>\n }\n\n @default {\n <ccc-text-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-text-field>\n }\n }\n }\n</div>\n\n<ng-template #booleanFragment>\n @if (booleanEditDisplayType() === 'boolean') {\n <ccc-boolean-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-boolean-field>\n } @else if (booleanEditDisplayType() === 'nullboolean') {\n <ccc-nullboolean-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-nullboolean-field>\n }\n</ng-template>\n", styles: [".checkbox{margin-top:10px}.dirty-input{background-color:#00617f26}\n"], dependencies: [{ kind: "component", type: DateFieldComponent, selector: "ccc-date-field" }, { kind: "component", type: BooleanFieldComponent, selector: "ccc-boolean-field" }, { kind: "component", type: TextFieldComponent, selector: "ccc-text-field" }, { kind: "component", type: NumberFieldComponent, selector: "ccc-number-field" }, { kind: "component", type: EnumeratedFieldComponent, selector: "ccc-enumerated-field" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: EmptyReadonlyFieldComponent, selector: "ccc-empty-readonly-field", inputs: ["label"] }, { kind: "component", type: NullBooleanFieldComponent, selector: "ccc-nullboolean-field" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1348
+ }
1349
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceFieldComponent, decorators: [{
1350
+ type: Component,
1351
+ args: [{ selector: 'ccc-resource-field', imports: [
1352
+ DateFieldComponent,
1353
+ BooleanFieldComponent,
1354
+ TextFieldComponent,
1355
+ NumberFieldComponent,
1356
+ EnumeratedFieldComponent,
1357
+ MatFormFieldModule,
1358
+ EmptyReadonlyFieldComponent,
1359
+ NullBooleanFieldComponent,
1360
+ NgTemplateOutlet,
1361
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class.hidden-field]=\"!showField()\">\n @if (showEmptyField()) {\n <ccc-empty-readonly-field [label]=\"fieldConfig().label\"></ccc-empty-readonly-field>\n } @else {\n @switch (fieldMeta().displayType) {\n @case ('date') {\n <ccc-date-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-date-field>\n }\n\n @case ('civildate') {\n <ccc-date-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-date-field>\n }\n\n @case ('number') {\n <ccc-number-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-number-field>\n }\n\n @case ('boolean') {\n <ng-container *ngTemplateOutlet=\"booleanFragment\"></ng-container>\n }\n @case ('nullboolean') {\n <ng-container *ngTemplateOutlet=\"booleanFragment\"></ng-container>\n }\n\n @case ('enumerated') {\n <ccc-enumerated-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-enumerated-field>\n }\n\n @default {\n <ccc-text-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-text-field>\n }\n }\n }\n</div>\n\n<ng-template #booleanFragment>\n @if (booleanEditDisplayType() === 'boolean') {\n <ccc-boolean-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-boolean-field>\n } @else if (booleanEditDisplayType() === 'nullboolean') {\n <ccc-nullboolean-field\n [form]=\"form()\"\n [fieldConfig]=\"fieldConfig()\"\n [editMode]=\"mode()\"\n [fieldClass]=\"fieldClass()\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"data()\"\n [fieldMeta]=\"fieldMeta()\"\n [showField]=\"showField()\">\n </ccc-nullboolean-field>\n }\n</ng-template>\n", styles: [".checkbox{margin-top:10px}.dirty-input{background-color:#00617f26}\n"] }]
1362
+ }], ctorParameters: () => [], propDecorators: { fieldConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldConfig", required: true }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: true }] }], fieldClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldClass", required: false }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], formDataState: [{ type: i0.Input, args: [{ isSignal: true, alias: "formDataState", required: false }] }], pristineValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "pristineValue", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], class: [{
1363
+ type: HostBinding,
1364
+ args: ['class']
1365
+ }] } });
1366
+
1367
+ class ResourceLayoutComponent {
1368
+ element = input.required({ ...(ngDevMode ? { debugName: "element" } : {}) });
1369
+ meta = input.required({ ...(ngDevMode ? { debugName: "meta" } : {}) });
1370
+ fieldClass = input(undefined, { ...(ngDevMode ? { debugName: "fieldClass" } : {}) });
1371
+ editMode = input.required({ ...(ngDevMode ? { debugName: "editMode" } : {}) });
1372
+ form = input.required({ ...(ngDevMode ? { debugName: "form" } : {}) });
1373
+ formDataState = model(undefined, { ...(ngDevMode ? { debugName: "formDataState" } : {}) });
1374
+ pristineValue = input('', { ...(ngDevMode ? { debugName: "pristineValue" } : {}) });
1375
+ relatedData = input(undefined, { ...(ngDevMode ? { debugName: "relatedData" } : {}) });
1376
+ parentClass = input(undefined, { ...(ngDevMode ? { debugName: "parentClass" } : {}) });
1377
+ layoutNestingDepth = input(1, { ...(ngDevMode ? { debugName: "layoutNestingDepth" } : {}) });
1378
+ maxLayoutNestingDepth = maxLayoutNestingDepth;
1379
+ children = computed(() => {
1380
+ const element = this.element();
1381
+ if (element.type === 'section') {
1382
+ return element.children;
1383
+ }
1384
+ return [];
1385
+ }, { ...(ngDevMode ? { debugName: "children" } : {}) });
1386
+ layoutChildrenConfig = computed(() => {
1387
+ return flattenElements(this.children());
1388
+ }, { ...(ngDevMode ? { debugName: "layoutChildrenConfig" } : {}) });
1389
+ previouslyNulled = false;
1390
+ nullIfConditionallyHidden = computed(() => {
1391
+ const element = this.element();
1392
+ if (element.type === 'field') {
1393
+ return element.nullIfConditionallyHidden;
1394
+ }
1395
+ if (element.type === 'section') {
1396
+ return element.nullAllChildrenIfConditionallyHidden;
1397
+ }
1398
+ return false;
1399
+ }, { ...(ngDevMode ? { debugName: "nullIfConditionallyHidden" } : {}) });
1400
+ showLayout = computed(() => {
1401
+ const element = this.element();
1402
+ const shouldRender = element.shouldRender;
1403
+ const conditionallyNull = this.nullIfConditionallyHidden();
1404
+ const form = this.form();
1405
+ const label = 'label' in element ? element.label : 'padding';
1406
+ const isForeignKeyDefault = element.type === 'field' && element.default?.type === 'foreignKey';
1407
+ if (typeof shouldRender === 'boolean') {
1408
+ return shouldRender && !isForeignKeyDefault;
1409
+ }
1410
+ const formValues = this.formDataState();
1411
+ if (!formValues) {
1412
+ return true;
1413
+ }
1414
+ console.debug('Layout: ', label, ' | Values to be used in showLayout calculation ', formValues);
1415
+ try {
1416
+ const showLayout = shouldRender(formValues) && !isForeignKeyDefault;
1417
+ if (!conditionallyNull) {
1418
+ return showLayout;
1419
+ }
1420
+ const previouslyNulled = this.previouslyNulled;
1421
+ untracked(() => {
1422
+ this.layoutChildrenConfig().forEach((configElement) => {
1423
+ const isFormControl = configElement.type === 'field';
1424
+ if (!isFormControl) {
1425
+ return;
1426
+ }
1427
+ const control = form?.controls[configElement.name];
1428
+ const pristineFieldValue = this.relatedData()?.[configElement.name];
1429
+ if (!showLayout && !previouslyNulled) {
1430
+ this.previouslyNulled = true;
1431
+ control?.setValue(null);
1432
+ }
1433
+ if (showLayout && previouslyNulled) {
1434
+ this.previouslyNulled = false;
1435
+ control?.setValue(pristineFieldValue);
1436
+ }
1437
+ });
1438
+ });
1439
+ return showLayout;
1440
+ }
1441
+ catch (e) {
1442
+ console.error('Failed to calculate value for should Render function for layout: ', label);
1443
+ console.error(e);
1444
+ return true;
1445
+ }
1446
+ }, { ...(ngDevMode ? { debugName: "showLayout" } : {}) });
1447
+ class = '';
1448
+ constructor() {
1449
+ // TODO: Remove this effect once Angular supports signals in Reactive forms to obtain changes to form values.
1450
+ // This effect has been necessary because translating from Observables to Signals, specifically via an input signal
1451
+ // doesn't have a clean solution
1452
+ effect((onCleanup) => {
1453
+ const element = this.element();
1454
+ if (element === undefined) {
1455
+ return;
1456
+ }
1457
+ const currentForm = this.form();
1458
+ const shouldRender = element.shouldRender;
1459
+ const nestingDepth = this.layoutNestingDepth();
1460
+ const layoutChildren = this.layoutChildrenConfig();
1461
+ const label = 'label' in element ? element.label : 'padding';
1462
+ if (!this.shouldInitializeFormData(currentForm, nestingDepth, shouldRender, layoutChildren)) {
1463
+ return;
1464
+ }
1465
+ console.debug('USAGE | New subscription for formValuesSignal created for layout: ', label);
1466
+ this.formDataState.set(currentForm.getRawValue());
1467
+ const subscription = currentForm.valueChanges.subscribe((value) => {
1468
+ this.formDataState.set(value);
1469
+ });
1470
+ onCleanup(() => {
1471
+ console.debug('USAGE | Unsubscribing from valueChanges for layout: ', label);
1472
+ subscription.unsubscribe();
1473
+ });
1474
+ });
1475
+ }
1476
+ ngOnInit() {
1477
+ this.class = 'col-' + this.element().cols;
1478
+ }
1479
+ shouldInitializeFormData(form, layoutNestingDepth, shouldRender, layoutChildren) {
1480
+ const childrenNeedFormDataState = layoutChildren.some((config) => {
1481
+ const shouldRenderIsFunction = typeof config.shouldRender === 'function';
1482
+ const isComputedField = config.type === 'computedDisplayField';
1483
+ let validatorsIsFunction = false;
1484
+ const castConfig = config;
1485
+ if (castConfig.validators && typeof castConfig.validators === 'function') {
1486
+ validatorsIsFunction = true;
1487
+ }
1488
+ return shouldRenderIsFunction || isComputedField || validatorsIsFunction;
1489
+ });
1490
+ if (!form || layoutNestingDepth > 1) {
1491
+ return false;
1492
+ }
1493
+ if (!childrenNeedFormDataState && typeof shouldRender !== 'function') {
1494
+ return false;
1495
+ }
1496
+ return true;
1497
+ }
1498
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1499
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: ResourceLayoutComponent, isStandalone: true, selector: "ccc-resource-layout-template", inputs: { element: { classPropertyName: "element", publicName: "element", isSignal: true, isRequired: true, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: true, transformFunction: null }, fieldClass: { classPropertyName: "fieldClass", publicName: "fieldClass", isSignal: true, isRequired: false, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, formDataState: { classPropertyName: "formDataState", publicName: "formDataState", isSignal: true, isRequired: false, transformFunction: null }, pristineValue: { classPropertyName: "pristineValue", publicName: "pristineValue", isSignal: true, isRequired: false, transformFunction: null }, relatedData: { classPropertyName: "relatedData", publicName: "relatedData", isSignal: true, isRequired: false, transformFunction: null }, parentClass: { classPropertyName: "parentClass", publicName: "parentClass", isSignal: true, isRequired: false, transformFunction: null }, layoutNestingDepth: { classPropertyName: "layoutNestingDepth", publicName: "layoutNestingDepth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formDataState: "formDataStateChange" }, host: { properties: { "class": "this.class" } }, ngImport: i0, template: "<div class=\"section {{ parentClass() }}\" [class.hidden-field]=\"!showLayout()\">\n @let parentElement = element();\n @if (children().length > 0) {\n <div\n class=\"section-elements dense-1 resource-layout-container\"\n [class]=\"layoutNestingDepth() % 2 === 0 ? 'resource-layout-container-even' : 'resource-layout-container-odd'\">\n @if (parentElement.type !== 'padding' && parentElement.label !== '') {\n <div class=\"label\">{{ parentElement.label }}</div>\n }\n @for (elementField of children(); track elementField) {\n @let childElement = elementField;\n @if (childElement.type === 'section') {\n @if (layoutNestingDepth() < maxLayoutNestingDepth) {\n <ccc-resource-layout-template\n [layoutNestingDepth]=\"layoutNestingDepth() + 1\"\n [element]=\"childElement\"\n [meta]=\"meta()\"\n [fieldClass]=\"fieldClass()\"\n [editMode]=\"editMode()\"\n [form]=\"form()\"\n [formDataState]=\"formDataState()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"relatedData()\"\n [parentClass]=\"parentClass()\">\n </ccc-resource-layout-template>\n }\n } @else if (childElement.type === 'field') {\n <ccc-resource-field\n [meta]=\"meta()\"\n [fieldClass]=\"fieldClass()\"\n [fieldConfig]=\"childElement\"\n [editMode]=\"editMode()\"\n [form]=\"form()\"\n [formDataState]=\"formDataState()\"\n [pristineValue]=\"pristineValue()\"\n [data]=\"relatedData()\">\n </ccc-resource-field>\n } @else if (childElement.type === 'computedDisplayField') {\n <ccc-computed-field\n [fieldClass]=\"fieldClass()\"\n [fieldConfig]=\"childElement\"\n [formDataState]=\"formDataState()\"></ccc-computed-field>\n } @else if (childElement.type === 'padding') {\n <ccc-padding-element [paddingElement]=\"childElement\"></ccc-padding-element>\n }\n }\n </div>\n } @else {\n @if (parentElement.type === 'field') {\n <div\n class=\"section-elements dense-1 resource-layout-container\"\n [class]=\"layoutNestingDepth() % 2 === 0 ? 'resource-layout-container-even' : 'resource-layout-container-odd'\">\n <ccc-resource-field\n [meta]=\"meta()\"\n [fieldClass]=\"fieldClass()\"\n [fieldConfig]=\"parentElement\"\n [editMode]=\"editMode()\"\n [form]=\"form()\"\n [formDataState]=\"formDataState()\"\n [pristineValue]=\"pristineValue()\"\n [data]=\"relatedData()\">\n </ccc-resource-field>\n </div>\n } @else if (parentElement.type === 'computedDisplayField') {\n <ccc-computed-field\n [fieldClass]=\"fieldClass()\"\n [fieldConfig]=\"parentElement\"\n [formDataState]=\"formDataState()\"></ccc-computed-field>\n } @else if (parentElement.type === 'padding') {\n <ccc-padding-element [paddingElement]=\"parentElement\"></ccc-padding-element>\n }\n }\n</div>\n", styles: [".section{margin-top:15px}.section-elements{display:flex;flex-wrap:wrap;padding-bottom:4px}.label{position:absolute;top:-15px;left:20px;height:20px;background-color:var(--layout-label-background-color);border-radius:8px;margin:0 10px;padding:0 10px;text-align:center;z-index:10;font-weight:500;font:18px Roboto,sans-serif}\n"], dependencies: [{ kind: "component", type: ResourceLayoutComponent, selector: "ccc-resource-layout-template", inputs: ["element", "meta", "fieldClass", "editMode", "form", "formDataState", "pristineValue", "relatedData", "parentClass", "layoutNestingDepth"], outputs: ["formDataStateChange"] }, { kind: "component", type: PaddingElementComponent, selector: "ccc-padding-element", inputs: ["paddingElement"] }, { kind: "component", type: ResourceFieldComponent, selector: "ccc-resource-field", inputs: ["fieldConfig", "meta", "fieldClass", "editMode", "form", "formDataState", "pristineValue", "data"] }, { kind: "component", type: ComputedFieldComponent, selector: "ccc-computed-field", inputs: ["fieldConfig", "fieldClass", "formDataState"] }] });
1500
+ }
1501
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceLayoutComponent, decorators: [{
1502
+ type: Component,
1503
+ args: [{ selector: 'ccc-resource-layout-template', imports: [PaddingElementComponent, ResourceFieldComponent, ComputedFieldComponent], template: "<div class=\"section {{ parentClass() }}\" [class.hidden-field]=\"!showLayout()\">\n @let parentElement = element();\n @if (children().length > 0) {\n <div\n class=\"section-elements dense-1 resource-layout-container\"\n [class]=\"layoutNestingDepth() % 2 === 0 ? 'resource-layout-container-even' : 'resource-layout-container-odd'\">\n @if (parentElement.type !== 'padding' && parentElement.label !== '') {\n <div class=\"label\">{{ parentElement.label }}</div>\n }\n @for (elementField of children(); track elementField) {\n @let childElement = elementField;\n @if (childElement.type === 'section') {\n @if (layoutNestingDepth() < maxLayoutNestingDepth) {\n <ccc-resource-layout-template\n [layoutNestingDepth]=\"layoutNestingDepth() + 1\"\n [element]=\"childElement\"\n [meta]=\"meta()\"\n [fieldClass]=\"fieldClass()\"\n [editMode]=\"editMode()\"\n [form]=\"form()\"\n [formDataState]=\"formDataState()\"\n [pristineValue]=\"pristineValue()\"\n [relatedData]=\"relatedData()\"\n [parentClass]=\"parentClass()\">\n </ccc-resource-layout-template>\n }\n } @else if (childElement.type === 'field') {\n <ccc-resource-field\n [meta]=\"meta()\"\n [fieldClass]=\"fieldClass()\"\n [fieldConfig]=\"childElement\"\n [editMode]=\"editMode()\"\n [form]=\"form()\"\n [formDataState]=\"formDataState()\"\n [pristineValue]=\"pristineValue()\"\n [data]=\"relatedData()\">\n </ccc-resource-field>\n } @else if (childElement.type === 'computedDisplayField') {\n <ccc-computed-field\n [fieldClass]=\"fieldClass()\"\n [fieldConfig]=\"childElement\"\n [formDataState]=\"formDataState()\"></ccc-computed-field>\n } @else if (childElement.type === 'padding') {\n <ccc-padding-element [paddingElement]=\"childElement\"></ccc-padding-element>\n }\n }\n </div>\n } @else {\n @if (parentElement.type === 'field') {\n <div\n class=\"section-elements dense-1 resource-layout-container\"\n [class]=\"layoutNestingDepth() % 2 === 0 ? 'resource-layout-container-even' : 'resource-layout-container-odd'\">\n <ccc-resource-field\n [meta]=\"meta()\"\n [fieldClass]=\"fieldClass()\"\n [fieldConfig]=\"parentElement\"\n [editMode]=\"editMode()\"\n [form]=\"form()\"\n [formDataState]=\"formDataState()\"\n [pristineValue]=\"pristineValue()\"\n [data]=\"relatedData()\">\n </ccc-resource-field>\n </div>\n } @else if (parentElement.type === 'computedDisplayField') {\n <ccc-computed-field\n [fieldClass]=\"fieldClass()\"\n [fieldConfig]=\"parentElement\"\n [formDataState]=\"formDataState()\"></ccc-computed-field>\n } @else if (parentElement.type === 'padding') {\n <ccc-padding-element [paddingElement]=\"parentElement\"></ccc-padding-element>\n }\n }\n</div>\n", styles: [".section{margin-top:15px}.section-elements{display:flex;flex-wrap:wrap;padding-bottom:4px}.label{position:absolute;top:-15px;left:20px;height:20px;background-color:var(--layout-label-background-color);border-radius:8px;margin:0 10px;padding:0 10px;text-align:center;z-index:10;font-weight:500;font:18px Roboto,sans-serif}\n"] }]
1504
+ }], ctorParameters: () => [], propDecorators: { element: [{ type: i0.Input, args: [{ isSignal: true, alias: "element", required: true }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: true }] }], fieldClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldClass", required: false }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: true }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], formDataState: [{ type: i0.Input, args: [{ isSignal: true, alias: "formDataState", required: false }] }, { type: i0.Output, args: ["formDataStateChange"] }], pristineValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "pristineValue", required: false }] }], relatedData: [{ type: i0.Input, args: [{ isSignal: true, alias: "relatedData", required: false }] }], parentClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentClass", required: false }] }], layoutNestingDepth: [{ type: i0.Input, args: [{ isSignal: true, alias: "layoutNestingDepth", required: false }] }], class: [{
1505
+ type: HostBinding,
1506
+ args: ['class']
1507
+ }] } });
1508
+
1509
+ class BaseRPCModalComponent {
1510
+ methodMeta = inject(METHOD_META);
1511
+ dialogRef = inject(MatDialogRef);
1512
+ data = inject(MAT_DIALOG_DATA);
1513
+ formData = signal({}, { ...(ngDevMode ? { debugName: "formData" } : {}) });
1514
+ meta = computed(() => {
1515
+ return this.methodMeta(this.formData().method);
1516
+ }, { ...(ngDevMode ? { debugName: "meta" } : {}) });
1517
+ constructor() {
1518
+ this.formData.set(this.data);
1519
+ if (this.data.width) {
1520
+ this.dialogRef.updateSize(this.data.width);
1521
+ }
1522
+ }
1523
+ onConfirm() {
1524
+ this.dialogRef.close(this.form());
1525
+ }
1526
+ form = computed(() => {
1527
+ const fg = new FormGroup({});
1528
+ this.formData();
1529
+ const meta = this.meta();
1530
+ const allElements = flattenElements(this.data.elements);
1531
+ if (meta) {
1532
+ for (const field of meta.fields || []) {
1533
+ const value = '';
1534
+ const control = new FormControl(value);
1535
+ const findConfig = allElements.find((element) => element.type === 'field' && element.name === field.fieldName);
1536
+ const fieldConfig = findConfig;
1537
+ if (!fieldConfig) {
1538
+ continue;
1539
+ }
1540
+ if (fieldConfig.validators.length > 0) {
1541
+ control.setValidators(fieldConfig.validators);
1542
+ }
1543
+ fg.addControl(field.fieldName, control);
1544
+ this.pristineForm[field.fieldName] = value;
1545
+ }
1546
+ }
1547
+ return fg;
1548
+ }, { ...(ngDevMode ? { debugName: "form" } : {}) });
1549
+ pristineForm = {};
1550
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: BaseRPCModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1551
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: BaseRPCModalComponent, isStandalone: true, selector: "ccc-base-rpc-modal", providers: [ResourceStore], ngImport: i0, template: "<div mat-dialog-title>{{ formData().label }}</div>\n<mat-dialog-content>\n <div class=\"resource-container\">\n <form [formGroup]=\"form()\">\n <div class=\"resource row\">\n @for (element of formData().elements; track element) {\n <ccc-resource-layout-template\n [element]=\"element\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineForm[element.name]\"\n editMode=\"edit\"\n [form]=\"form()\"></ccc-resource-layout-template>\n }\n </div>\n </form>\n </div>\n</mat-dialog-content>\n<mat-dialog-actions align=\"end\">\n <button mat-raised-button color=\"warn\" [mat-dialog-close]=\"false\">Cancel</button>\n <button mat-raised-button color=\"accent\" [mat-dialog-close]=\"form().getRawValue()\" [disabled]=\"!form().valid\">\n Complete\n </button>\n</mat-dialog-actions>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ResourceLayoutComponent, selector: "ccc-resource-layout-template", inputs: ["element", "meta", "fieldClass", "editMode", "form", "formDataState", "pristineValue", "relatedData", "parentClass", "layoutNestingDepth"], outputs: ["formDataStateChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
1552
+ }
1553
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: BaseRPCModalComponent, decorators: [{
1554
+ type: Component,
1555
+ args: [{ selector: 'ccc-base-rpc-modal', imports: [
1556
+ MatDialogModule,
1557
+ MatDialogActions,
1558
+ MatButtonModule,
1559
+ ResourceLayoutComponent,
1560
+ FormsModule,
1561
+ MatInputModule,
1562
+ ReactiveFormsModule,
1563
+ ], providers: [ResourceStore], template: "<div mat-dialog-title>{{ formData().label }}</div>\n<mat-dialog-content>\n <div class=\"resource-container\">\n <form [formGroup]=\"form()\">\n <div class=\"resource row\">\n @for (element of formData().elements; track element) {\n <ccc-resource-layout-template\n [element]=\"element\"\n [meta]=\"meta()\"\n [pristineValue]=\"pristineForm[element.name]\"\n editMode=\"edit\"\n [form]=\"form()\"></ccc-resource-layout-template>\n }\n </div>\n </form>\n </div>\n</mat-dialog-content>\n<mat-dialog-actions align=\"end\">\n <button mat-raised-button color=\"warn\" [mat-dialog-close]=\"false\">Cancel</button>\n <button mat-raised-button color=\"accent\" [mat-dialog-close]=\"form().getRawValue()\" [disabled]=\"!form().valid\">\n Complete\n </button>\n</mat-dialog-actions>\n" }]
1564
+ }], ctorParameters: () => [] });
1565
+
1566
+ class RpcButtonComponent {
1567
+ activatedRoute = inject(ActivatedRoute);
1568
+ router = inject(Router);
1569
+ notifications = inject(NotificationService);
1570
+ formState = inject(FormStateService);
1571
+ dialog = inject(MatDialog);
1572
+ store = inject(ResourceStore);
1573
+ methodMeta = inject(METHOD_META);
1574
+ relatedData = input.required({ ...(ngDevMode ? { debugName: "relatedData" } : {}) });
1575
+ rpcConfig = input.required({ ...(ngDevMode ? { debugName: "rpcConfig" } : {}) });
1576
+ primaryResource = input.required({ ...(ngDevMode ? { debugName: "primaryResource" } : {}) });
1577
+ dependentResources = input([], { ...(ngDevMode ? { debugName: "dependentResources" } : {}) });
1578
+ resourceConfigRouteSnapshot = computed(() => {
1579
+ return (this.rpcConfig() ||
1580
+ this.activatedRoute.snapshot.data['config']?.parentConfig ||
1581
+ {});
1582
+ }, { ...(ngDevMode ? { debugName: "resourceConfigRouteSnapshot" } : {}) });
1583
+ showRPCButton = computed(() => {
1584
+ const rpcConfig = this.rpcConfig();
1585
+ if (!rpcConfig) {
1586
+ return false;
1587
+ }
1588
+ let canView = true;
1589
+ for (const condition of rpcConfig.conditions || []) {
1590
+ const filterField = condition.field;
1591
+ const matchValues = condition.matchValues;
1592
+ const parentFieldData = this.relatedData()[filterField ? filterField : ''];
1593
+ for (const matchValue of matchValues) {
1594
+ if (parentFieldData === matchValue) {
1595
+ canView = true;
1596
+ break;
1597
+ }
1598
+ canView = false;
1599
+ }
1600
+ if (!canView) {
1601
+ break;
1602
+ }
1603
+ }
1604
+ return canView;
1605
+ }, { ...(ngDevMode ? { debugName: "showRPCButton" } : {}) });
1606
+ submitRPC() {
1607
+ const rpcConfig = this.rpcConfig();
1608
+ if (!rpcConfig) {
1609
+ console.error('RPC config is not defined');
1610
+ return;
1611
+ }
1612
+ const submitBody = {};
1613
+ let dialogRef;
1614
+ if (rpcConfig.customComponent !== rpcConfigDefaults.customComponent) {
1615
+ let componentToOpen;
1616
+ if (!componentToOpen) {
1617
+ console.error('Custom RPC component ' + rpcConfig.customComponent.component + ' not found.');
1618
+ return;
1619
+ }
1620
+ if (!dialogRef) {
1621
+ console.error('Dialog reference is undefined.');
1622
+ return;
1623
+ }
1624
+ }
1625
+ else if (rpcConfig.elements.length > 0) {
1626
+ dialogRef = this.dialog.open(BaseRPCModalComponent, {
1627
+ data: {
1628
+ label: rpcConfig.label,
1629
+ elements: rpcConfig.elements,
1630
+ method: rpcConfig.method,
1631
+ width: rpcConfig.defaultModalWidth,
1632
+ },
1633
+ });
1634
+ }
1635
+ else {
1636
+ this.buildRpcBody(rpcConfig, submitBody);
1637
+ return;
1638
+ }
1639
+ const meta = this.methodMeta(rpcConfig.method);
1640
+ dialogRef
1641
+ .afterClosed()
1642
+ .pipe(filter((data) => !!data), tap((data) => {
1643
+ const coercedData = metadataTypeCoercion(data, meta);
1644
+ Object.keys(coercedData).forEach((key) => {
1645
+ const value = coercedData[key];
1646
+ if (value) {
1647
+ submitBody[key] = value;
1648
+ }
1649
+ });
1650
+ this.buildRpcBody(rpcConfig, submitBody);
1651
+ }), tap(() => {
1652
+ this.reloadDependentResources();
1653
+ }))
1654
+ .subscribe();
1655
+ }
1656
+ buildRpcBody(rpcConfig, submitBody) {
1657
+ let canSubmitRPC = true;
1658
+ const method = this.methodMeta(rpcConfig.method);
1659
+ if (method.fields) {
1660
+ method.fields.forEach((methodField) => {
1661
+ const key = methodField.fieldName;
1662
+ if (submitBody[key] === undefined || submitBody[key] === null || submitBody[key] === '') {
1663
+ if (!method.fields) {
1664
+ return;
1665
+ }
1666
+ const value = rpcConfig.methodBodyTemplate[key];
1667
+ if (value == undefined) {
1668
+ return;
1669
+ }
1670
+ if (typeof value !== 'string') {
1671
+ const returnValue = this.relatedData()[value.field];
1672
+ if (returnValue === undefined) {
1673
+ this.notifications.addGlobalNotification({
1674
+ message: 'Value not found for field: ' + value.field,
1675
+ link: '',
1676
+ level: AlertLevel.ERROR,
1677
+ });
1678
+ canSubmitRPC = false;
1679
+ return;
1680
+ }
1681
+ if (returnValue)
1682
+ submitBody[key] = returnValue;
1683
+ }
1684
+ else {
1685
+ submitBody[key] = value;
1686
+ }
1687
+ }
1688
+ });
1689
+ }
1690
+ if (!canSubmitRPC) {
1691
+ return;
1692
+ }
1693
+ this.callRpc(rpcConfig, submitBody);
1694
+ }
1695
+ callRpc(rpc, submitBody) {
1696
+ this.store
1697
+ .rpcCall(rpc, submitBody)
1698
+ .pipe(tap(() => {
1699
+ this.reloadDependentResources();
1700
+ }))
1701
+ .subscribe();
1702
+ }
1703
+ reloadDependentResources() {
1704
+ // TODO: this does not work. We need to invalidate the cache via resource names.
1705
+ for (const ref of this.dependentResources()) {
1706
+ ref.reload();
1707
+ }
1708
+ }
1709
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: RpcButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1710
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: RpcButtonComponent, isStandalone: true, selector: "ccc-rpc-button", inputs: { relatedData: { classPropertyName: "relatedData", publicName: "relatedData", isSignal: true, isRequired: true, transformFunction: null }, rpcConfig: { classPropertyName: "rpcConfig", publicName: "rpcConfig", isSignal: true, isRequired: true, transformFunction: null }, primaryResource: { classPropertyName: "primaryResource", publicName: "primaryResource", isSignal: true, isRequired: true, transformFunction: null }, dependentResources: { classPropertyName: "dependentResources", publicName: "dependentResources", isSignal: true, isRequired: false, transformFunction: null } }, providers: [ResourceStore], ngImport: i0, template: "@if (showRPCButton()) {\n <div class=\"button-container\">\n <button mat-raised-button color=\"accent\" (click)=\"submitRPC()\" [disabled]=\"formState.isDirty()\">\n {{ rpcConfig().label }}\n </button>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:row;margin-top:auto;margin-bottom:auto;justify-content:right;flex-grow:1;gap:20px;margin-right:20px}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }] });
1711
+ }
1712
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: RpcButtonComponent, decorators: [{
1713
+ type: Component,
1714
+ args: [{ selector: 'ccc-rpc-button', imports: [MatButtonModule], providers: [ResourceStore], template: "@if (showRPCButton()) {\n <div class=\"button-container\">\n <button mat-raised-button color=\"accent\" (click)=\"submitRPC()\" [disabled]=\"formState.isDirty()\">\n {{ rpcConfig().label }}\n </button>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:row;margin-top:auto;margin-bottom:auto;justify-content:right;flex-grow:1;gap:20px;margin-right:20px}\n"] }]
1715
+ }], propDecorators: { relatedData: [{ type: i0.Input, args: [{ isSignal: true, alias: "relatedData", required: true }] }], rpcConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "rpcConfig", required: true }] }], primaryResource: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryResource", required: true }] }], dependentResources: [{ type: i0.Input, args: [{ isSignal: true, alias: "dependentResources", required: false }] }] } });
1716
+
1717
+ class ResourceCreateComponent {
1718
+ resourceMeta = inject(RESOURCE_META);
1719
+ activatedRoute = inject(ActivatedRoute);
1720
+ notifications = inject(NotificationService);
1721
+ store = inject(ResourceStore);
1722
+ router = inject(Router);
1723
+ destroyRef = inject(DestroyRef);
1724
+ formState = inject(FormStateService);
1725
+ isDirty = signal(false, { ...(ngDevMode ? { debugName: "isDirty" } : {}) });
1726
+ submitted = signal(false, { ...(ngDevMode ? { debugName: "submitted" } : {}) });
1727
+ complete = output();
1728
+ resourceConfig = input(undefined, { ...(ngDevMode ? { debugName: "resourceConfig" } : {}) });
1729
+ parentData = input({}, { ...(ngDevMode ? { debugName: "parentData" } : {}) });
1730
+ loadCreatedResource = input(false, { ...(ngDevMode ? { debugName: "loadCreatedResource" } : {}) });
1731
+ rootConfig = computed(() => {
1732
+ return this.activatedRoute.snapshot.data['config'];
1733
+ }, { ...(ngDevMode ? { debugName: "rootConfig" } : {}) });
1734
+ config = computed(() => {
1735
+ const inputConfig = this.resourceConfig();
1736
+ if (inputConfig !== undefined) {
1737
+ return inputConfig;
1738
+ }
1739
+ return this.rootConfig().parentConfig;
1740
+ }, { ...(ngDevMode ? { debugName: "config" } : {}) });
1741
+ indentTitle = computed(() => {
1742
+ if (this.config().collapsible || this.resourceConfig() === undefined) {
1743
+ return false;
1744
+ }
1745
+ return true;
1746
+ }, { ...(ngDevMode ? { debugName: "indentTitle" } : {}) });
1747
+ form = computed(() => {
1748
+ const meta = this.store.resourceMeta();
1749
+ const fg = new FormGroup({});
1750
+ const allElements = flattenElements(this.config().elements);
1751
+ for (const field of meta.fields || []) {
1752
+ if (fg.get(field.fieldName)) {
1753
+ continue;
1754
+ }
1755
+ let control = new FormControl('');
1756
+ if (field.displayType === 'boolean') {
1757
+ control = new FormControl(false);
1758
+ }
1759
+ const findElement = allElements.find((element) => element.type === 'field' && element.name === field.fieldName);
1760
+ const fieldConfig = findElement;
1761
+ if (!fieldConfig) {
1762
+ continue;
1763
+ }
1764
+ const fieldDefault = fieldConfig.default;
1765
+ if (field.displayType === 'boolean') {
1766
+ const booleanDefaultValue = fieldDefault?.type == 'static' && typeof fieldDefault?.value === 'boolean' ? fieldDefault.value : null;
1767
+ if (booleanDefaultValue === null) {
1768
+ console.error(`Default value for boolean field, ${field.fieldName}, is null, add a default value to the config`);
1769
+ }
1770
+ control = new FormControl(booleanDefaultValue);
1771
+ }
1772
+ if (fieldDefault?.type === 'foreignKey' && this.parentData()) {
1773
+ const parentValue = this.parentData()[fieldDefault.parentId];
1774
+ if (parentValue !== undefined) {
1775
+ control.setValue(parentValue);
1776
+ }
1777
+ }
1778
+ else if (fieldDefault?.type == 'static') {
1779
+ const staticDefault = fieldDefault;
1780
+ if (staticDefault.value) {
1781
+ control.setValue(staticDefault.value);
1782
+ }
1783
+ }
1784
+ control.setValidators([]);
1785
+ if (fieldConfig.validators.length > 0) {
1786
+ control.addValidators(fieldConfig.validators);
1787
+ }
1788
+ if (field.required && !control.hasValidator(Validators.required)) {
1789
+ control.addValidators(Validators.required);
1790
+ }
1791
+ fg.addControl(field.fieldName, control);
1792
+ }
1793
+ return fg;
1794
+ }, { ...(ngDevMode ? { debugName: "form" } : {}) });
1795
+ route = computed(() => {
1796
+ const meta = this.store.resourceMeta();
1797
+ if (!meta)
1798
+ return '';
1799
+ return meta.consolidatedRoute || meta.route;
1800
+ }, { ...(ngDevMode ? { debugName: "route" } : {}) });
1801
+ primaryKeys = computed(() => {
1802
+ const meta = this.store.resourceMeta();
1803
+ if (!meta)
1804
+ return [];
1805
+ return meta.fields
1806
+ .filter((field) => field.primaryKey)
1807
+ .sort((a, b) => a.primaryKey.ordinalPosition - b.primaryKey.ordinalPosition);
1808
+ }, { ...(ngDevMode ? { debugName: "primaryKeys" } : {}) });
1809
+ hasRequiredPrimaryKey = computed(() => {
1810
+ const meta = this.store.resourceMeta();
1811
+ if (!meta) {
1812
+ return false;
1813
+ }
1814
+ return meta.fields.some((field) => field.primaryKey && field.required);
1815
+ }, { ...(ngDevMode ? { debugName: "hasRequiredPrimaryKey" } : {}) });
1816
+ primaryKeyPath = computed(() => {
1817
+ const meta = this.store.resourceMeta();
1818
+ const isConsolidated = meta.consolidatedRoute !== undefined;
1819
+ const pathPrefix = isConsolidated ? '/' + meta.route : '';
1820
+ const keyPath = this.primaryKeys()
1821
+ .map((field) => this.form().get(field.fieldName)?.value)
1822
+ .join('/');
1823
+ if (keyPath === '' && pathPrefix === '') {
1824
+ return '/';
1825
+ }
1826
+ if (keyPath === '') {
1827
+ return pathPrefix;
1828
+ }
1829
+ return pathPrefix + '/' + keyPath;
1830
+ }, { ...(ngDevMode ? { debugName: "primaryKeyPath" } : {}) });
1831
+ camelCaseToTitlePipe = new CamelCaseToTitlePipe();
1832
+ ngOnInit() {
1833
+ if (this.resourceMeta(this.config().primaryResource)) {
1834
+ this.store.resourceName.set(this.config().primaryResource);
1835
+ this.store.resourceMeta.set(this.resourceMeta(this.config().primaryResource));
1836
+ }
1837
+ }
1838
+ saveForm() {
1839
+ if (!this.form().valid) {
1840
+ this.submitted.set(true);
1841
+ this.form().markAllAsTouched();
1842
+ const formElement = document.getElementById('resource-form');
1843
+ const invalidField = formElement?.querySelector('.mdc-text-field--invalid');
1844
+ if (invalidField) {
1845
+ invalidField.scrollIntoView({ behavior: 'smooth' });
1846
+ }
1847
+ return;
1848
+ }
1849
+ const resourceMeta = this.store.resourceMeta();
1850
+ if (!resourceMeta) {
1851
+ return;
1852
+ }
1853
+ const cleanedForm = cleanStringForm(this.form());
1854
+ if (cleanedForm === undefined || cleanedForm === null) {
1855
+ return;
1856
+ }
1857
+ const coercedCleanedData = metadataTypeCoercion(cleanedForm, this.store.resourceMeta());
1858
+ const cleanedDataWithoutPrimaryKeys = Object.fromEntries(Object.entries(coercedCleanedData).filter(([key]) => !this.primaryKeys().some((field) => field.fieldName === key)));
1859
+ const createPatch = {
1860
+ op: 'add',
1861
+ value: cleanedDataWithoutPrimaryKeys,
1862
+ path: this.primaryKeyPath(),
1863
+ };
1864
+ this.store
1865
+ .createPatch(createPatch, this.route(), this.store.resourceName())
1866
+ .pipe(tap((response) => {
1867
+ this.formState.decrementDirtyForms();
1868
+ if (!response) {
1869
+ this.complete.emit(true);
1870
+ return;
1871
+ }
1872
+ let createIds = [];
1873
+ if (resourceMeta.consolidatedRoute) {
1874
+ const resourceName = camelCase(this.store.resourceName());
1875
+ createIds = response[resourceName] || [];
1876
+ }
1877
+ else {
1878
+ createIds = (response['iDs'] || []);
1879
+ }
1880
+ if (this.loadCreatedResource() && createIds.length === 1) {
1881
+ let route = this.rootConfig().routeData.route;
1882
+ if (this.parentData() || !route) {
1883
+ route = resourceMeta.route;
1884
+ }
1885
+ const navigationRoutes = this.config().createNavigation;
1886
+ if (navigationRoutes.length === 0) {
1887
+ this.router.navigate([route, createIds[0]]);
1888
+ }
1889
+ else {
1890
+ if (createIds[0]) {
1891
+ navigationRoutes.push(createIds[0]);
1892
+ }
1893
+ this.router.navigate(navigationRoutes);
1894
+ }
1895
+ }
1896
+ else {
1897
+ this.complete.emit(true);
1898
+ }
1899
+ }))
1900
+ .subscribe();
1901
+ }
1902
+ cancelForm() {
1903
+ if (this.form().dirty) {
1904
+ this.formState.decrementDirtyForms();
1905
+ }
1906
+ this.complete.emit(true);
1907
+ }
1908
+ constructor() {
1909
+ effect(() => {
1910
+ console.debug('USAGE | New FormGroup subscription for: ', this.config().title);
1911
+ this.form()
1912
+ .valueChanges.pipe(tap(() => {
1913
+ const dirty = this.form().dirty;
1914
+ if (dirty !== this.isDirty()) {
1915
+ this.isDirty.set(dirty);
1916
+ if (dirty) {
1917
+ this.formState.incrementDirtyForms();
1918
+ }
1919
+ else {
1920
+ this.formState.decrementDirtyForms();
1921
+ }
1922
+ }
1923
+ }), takeUntilDestroyed(this.destroyRef))
1924
+ .subscribe();
1925
+ });
1926
+ }
1927
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceCreateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1928
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: ResourceCreateComponent, isStandalone: true, selector: "ccc-resource-create", inputs: { resourceConfig: { classPropertyName: "resourceConfig", publicName: "resourceConfig", isSignal: true, isRequired: false, transformFunction: null }, parentData: { classPropertyName: "parentData", publicName: "parentData", isSignal: true, isRequired: false, transformFunction: null }, loadCreatedResource: { classPropertyName: "loadCreatedResource", publicName: "loadCreatedResource", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { complete: "complete" }, providers: [ResourceStore], ngImport: i0, template: "<div class=\"page-container\">\n <div class=\"header-container\">\n <div class=\"title\" [class.indent]=\"indentTitle()\">\n <h1>Create {{ config().title }}</h1>\n </div>\n\n <div class=\"state-buttons\">\n @if (!form().valid && submitted()) {\n <div class=\"invalid message\">Please complete or fix required fields.</div>\n }\n <button mat-raised-button color=\"primary\" (click)=\"cancelForm()\">Cancel</button>\n <button mat-raised-button color=\"accent\" (click)=\"saveForm()\" [disabled]=\"form().pristine\">Create</button>\n </div>\n </div>\n\n <div class=\"resource-container\">\n <form [formGroup]=\"form()\">\n <div class=\"resource row\">\n @for (element of config().elements; track element) {\n <ccc-resource-layout-template\n [element]=\"element\"\n [meta]=\"store.resourceMeta()\"\n [fieldClass]=\"config().fieldClass\"\n [relatedData]=\"parentData()\"\n editMode=\"edit\"\n [form]=\"form()\"></ccc-resource-layout-template>\n }\n </div>\n </form>\n </div>\n</div>\n", styles: [".container{border:1px solid #8e8e8e;border-radius:5px;position:relative;margin-bottom:20px;padding-top:10px}.label{position:absolute;top:-15px;left:20px;height:20px;background-color:#fff;margin:0 10px;padding:0 10px;text-align:center;z-index:10;font-weight:500;font:18px Roboto,sans-serif}.page-container{position:relative}.header-container{margin:20px 20px 0;display:flex;flex-direction:row;top:0;z-index:11;background:transparent}.title{display:flex;align-items:center;flex-shrink:0}.indent{padding-left:20px}.message{margin-top:auto;margin-bottom:auto}.state-buttons{display:flex;flex-direction:row;margin-top:auto;margin-bottom:auto;justify-content:right;flex-grow:1;gap:20px}.mode-text{margin-top:auto;margin-bottom:auto}.resource{margin:10px}.section h2{font-size:1.5em;border-bottom:2px solid #ccc;padding-bottom:8px;margin-bottom:100px}.section{margin-top:15px}.divider{margin:16px 0}.section-elements{display:flex;flex-wrap:wrap}.mat-input-element:disabled[readonly]{color:currentColor}.unsaved{color:#f44336}.edit-button{margin-top:auto;margin-bottom:auto;padding-left:10px}.button-text{display:flex;flex-direction:row;align-items:center}.button-text .edit{color:#003b49}.button-text .mat-icon{margin-right:8px}.shaded{background-color:#e9e9e9}.rounded{border-radius:8px}.dotted-outline{border:1px dotted #ccc;padding:8px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: ResourceLayoutComponent, selector: "ccc-resource-layout-template", inputs: ["element", "meta", "fieldClass", "editMode", "form", "formDataState", "pristineValue", "relatedData", "parentClass", "layoutNestingDepth"], outputs: ["formDataStateChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1929
+ }
1930
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceCreateComponent, decorators: [{
1931
+ type: Component,
1932
+ args: [{ selector: 'ccc-resource-create', imports: [
1933
+ FormsModule,
1934
+ ReactiveFormsModule,
1935
+ MatInputModule,
1936
+ RouterModule,
1937
+ MatButtonModule,
1938
+ MatIconModule,
1939
+ MatProgressSpinnerModule,
1940
+ MatDividerModule,
1941
+ ResourceLayoutComponent,
1942
+ ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ResourceStore], template: "<div class=\"page-container\">\n <div class=\"header-container\">\n <div class=\"title\" [class.indent]=\"indentTitle()\">\n <h1>Create {{ config().title }}</h1>\n </div>\n\n <div class=\"state-buttons\">\n @if (!form().valid && submitted()) {\n <div class=\"invalid message\">Please complete or fix required fields.</div>\n }\n <button mat-raised-button color=\"primary\" (click)=\"cancelForm()\">Cancel</button>\n <button mat-raised-button color=\"accent\" (click)=\"saveForm()\" [disabled]=\"form().pristine\">Create</button>\n </div>\n </div>\n\n <div class=\"resource-container\">\n <form [formGroup]=\"form()\">\n <div class=\"resource row\">\n @for (element of config().elements; track element) {\n <ccc-resource-layout-template\n [element]=\"element\"\n [meta]=\"store.resourceMeta()\"\n [fieldClass]=\"config().fieldClass\"\n [relatedData]=\"parentData()\"\n editMode=\"edit\"\n [form]=\"form()\"></ccc-resource-layout-template>\n }\n </div>\n </form>\n </div>\n</div>\n", styles: [".container{border:1px solid #8e8e8e;border-radius:5px;position:relative;margin-bottom:20px;padding-top:10px}.label{position:absolute;top:-15px;left:20px;height:20px;background-color:#fff;margin:0 10px;padding:0 10px;text-align:center;z-index:10;font-weight:500;font:18px Roboto,sans-serif}.page-container{position:relative}.header-container{margin:20px 20px 0;display:flex;flex-direction:row;top:0;z-index:11;background:transparent}.title{display:flex;align-items:center;flex-shrink:0}.indent{padding-left:20px}.message{margin-top:auto;margin-bottom:auto}.state-buttons{display:flex;flex-direction:row;margin-top:auto;margin-bottom:auto;justify-content:right;flex-grow:1;gap:20px}.mode-text{margin-top:auto;margin-bottom:auto}.resource{margin:10px}.section h2{font-size:1.5em;border-bottom:2px solid #ccc;padding-bottom:8px;margin-bottom:100px}.section{margin-top:15px}.divider{margin:16px 0}.section-elements{display:flex;flex-wrap:wrap}.mat-input-element:disabled[readonly]{color:currentColor}.unsaved{color:#f44336}.edit-button{margin-top:auto;margin-bottom:auto;padding-left:10px}.button-text{display:flex;flex-direction:row;align-items:center}.button-text .edit{color:#003b49}.button-text .mat-icon{margin-right:8px}.shaded{background-color:#e9e9e9}.rounded{border-radius:8px}.dotted-outline{border:1px dotted #ccc;padding:8px}\n"] }]
1943
+ }], ctorParameters: () => [], propDecorators: { complete: [{ type: i0.Output, args: ["complete"] }], resourceConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "resourceConfig", required: false }] }], parentData: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentData", required: false }] }], loadCreatedResource: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadCreatedResource", required: false }] }] } });
1944
+
1945
+ class ResourceArrayViewComponent {
1946
+ resourceMeta = inject(RESOURCE_META);
1947
+ store = inject(ResourceStore);
1948
+ injector = inject(Injector);
1949
+ resourceConfig = input.required({ ...(ngDevMode ? { debugName: "resourceConfig" } : {}) });
1950
+ parentData = input({}, { ...(ngDevMode ? { debugName: "parentData" } : {}) });
1951
+ expPanel = viewChild('expPanel', { ...(ngDevMode ? { debugName: "expPanel" } : {}), read: MatExpansionPanel });
1952
+ emptyOneToOne = output();
1953
+ createMode = signal(false, { ...(ngDevMode ? { debugName: "createMode" } : {}) });
1954
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1955
+ compoundResourceComponent = input.required({ ...(ngDevMode ? { debugName: "compoundResourceComponent" } : {}) });
1956
+ showCreateButton = computed(() => {
1957
+ const list = this.store.listData();
1958
+ const resourceConfig = this.resourceConfig();
1959
+ const iteratedConfig = resourceConfig.iteratedConfig;
1960
+ if (list && resourceConfig?.viewType === 'OneToOne') {
1961
+ this.emptyOneToOne.emit(list.length === 0);
1962
+ return list.length === 0;
1963
+ }
1964
+ if ('createTitle' in iteratedConfig && iteratedConfig.createTitle !== '') {
1965
+ return true;
1966
+ }
1967
+ return false;
1968
+ }, { ...(ngDevMode ? { debugName: "showCreateButton" } : {}) });
1969
+ createActionConfig = computed(() => {
1970
+ const config = this.resourceConfig();
1971
+ const showCreate = this.showCreateButton();
1972
+ const meta = this.store.resourceMeta();
1973
+ if (config === undefined || showCreate === undefined || meta === undefined) {
1974
+ return undefined;
1975
+ }
1976
+ return {
1977
+ actionType: 'create',
1978
+ meta: this.store.resourceMeta(),
1979
+ shouldRender: () => (showCreate && config.shouldRenderActions?.create?.(this.parentData())) ?? false,
1980
+ resourceData: this.parentData(),
1981
+ };
1982
+ }, { ...(ngDevMode ? { debugName: "createActionConfig" } : {}) });
1983
+ createConfig = computed(() => {
1984
+ const config = this.resourceConfig();
1985
+ if (config.createConfig && Object.keys(config.createConfig).length !== 0) {
1986
+ return config.createConfig;
1987
+ }
1988
+ if (config.iteratedConfig && Object.keys(config.iteratedConfig).length !== 0) {
1989
+ return config.iteratedConfig;
1990
+ }
1991
+ return config;
1992
+ }, { ...(ngDevMode ? { debugName: "createConfig" } : {}) });
1993
+ resourceListRoute = computed(() => {
1994
+ const config = this.resourceConfig();
1995
+ const meta = this.resourceMeta(config.connectorResource || config.primaryResource);
1996
+ return meta.route;
1997
+ }, { ...(ngDevMode ? { debugName: "resourceListRoute" } : {}) });
1998
+ setCreateMode(value) {
1999
+ this.createMode.set(value);
2000
+ if (value && this.expPanel && this.expPanel()?.closed) {
2001
+ this.expPanel()?.open();
2002
+ }
2003
+ }
2004
+ ngOnInit() {
2005
+ this.store.resourceName.set(this.resourceConfig().primaryResource);
2006
+ const meta = this.resourceMeta(this.resourceConfig().primaryResource);
2007
+ this.store.resourceMeta.set(meta);
2008
+ this.store.overrideRoute.set(this.resourceListRoute());
2009
+ const columnArray = [];
2010
+ if (this.resourceConfig().connectorField !== '') {
2011
+ columnArray.push({ id: this.resourceConfig().connectorField });
2012
+ }
2013
+ else {
2014
+ columnArray.push({ id: 'id' });
2015
+ }
2016
+ this.store.listColumns.set(columnArray);
2017
+ this.store.sorts.set(this.resourceConfig().sorts);
2018
+ }
2019
+ constructor() {
2020
+ effect(() => {
2021
+ const parentData = this.parentData();
2022
+ const resourceConfig = this.resourceConfig();
2023
+ if (resourceConfig && 'listFilter' in resourceConfig && parentData) {
2024
+ const filter = resourceConfig.listFilter(parentData);
2025
+ this.store.filter.set(filter);
2026
+ this.store.disableCacheForFilterPii.set(resourceConfig.disableCacheForFilterPii);
2027
+ this.store.buildStoreListData();
2028
+ }
2029
+ });
2030
+ }
2031
+ createResource(event) {
2032
+ event.stopPropagation();
2033
+ const expPanel = this.expPanel();
2034
+ if (!expPanel) {
2035
+ this.setCreateMode(true);
2036
+ return;
2037
+ }
2038
+ this.setCreateMode(true);
2039
+ if (expPanel.closed) {
2040
+ expPanel.open();
2041
+ }
2042
+ }
2043
+ onResourceDeleted() {
2044
+ this.setCreateMode(false);
2045
+ }
2046
+ onCreateCompleted() {
2047
+ this.store.reloadListData();
2048
+ this.setCreateMode(false);
2049
+ }
2050
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceArrayViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2051
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: ResourceArrayViewComponent, isStandalone: true, selector: "ccc-resource-array-view", inputs: { resourceConfig: { classPropertyName: "resourceConfig", publicName: "resourceConfig", isSignal: true, isRequired: true, transformFunction: null }, parentData: { classPropertyName: "parentData", publicName: "parentData", isSignal: true, isRequired: false, transformFunction: null }, compoundResourceComponent: { classPropertyName: "compoundResourceComponent", publicName: "compoundResourceComponent", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { emptyOneToOne: "emptyOneToOne" }, providers: [ResourceStore], viewQueries: [{ propertyName: "expPanel", first: true, predicate: ["expPanel"], descendants: true, read: MatExpansionPanel, isSignal: true }], ngImport: i0, template: "@if (resourceConfig().title !== '') {\n <div class=\"resource-array-view\">\n <mat-expansion-panel\n #expPanel\n [@.disabled]=\"true\"\n [disabled]=\"!resourceConfig().collapsible\"\n [expanded]=\"!resourceConfig().collapsible\">\n <mat-expansion-panel-header>\n @if (resourceConfig().title) {\n <mat-panel-title>\n <h1 class=\"title\">\n {{ resourceConfig().title }}\n </h1>\n </mat-panel-title>\n }\n <action-access-control-wrapper [actionContext]=\"createActionConfig()\">\n <button\n mat-icon-button\n class=\"exp-panel-add-element-btn\"\n color=\"accent\"\n (click)=\"createResource($event)\"\n [matTooltip]=\"resourceConfig().createButtonLabel\"\n matTooltipPosition=\"above\">\n <mat-icon color=\"accent\">add_circle</mat-icon>\n </button>\n </action-access-control-wrapper>\n </mat-expansion-panel-header>\n\n @if (!resourceConfig().collapsible && !resourceConfig().title) {\n <div class=\"no-title-exp-panel-padding\"></div>\n }\n\n @if (createMode()) {\n <ccc-resource-create\n [resourceConfig]=\"createConfig()\"\n [parentData]=\"parentData()\"\n (complete)=\"onCreateCompleted()\">\n </ccc-resource-create>\n }\n @if (store.listStatus() === 'resolved') {\n @for (resource of store.listData(); track resource['id']; let i = $index) {\n @let resourceId = (resource[resourceConfig().connectorField] || resource['id']) + '';\n\n @if (parentData() && resourceConfig().iteratedConfig && resourceId && i + 1 <= resourceConfig().limit) {\n <ng-container *ngComponentOutlet=\"compoundResourceComponent();\n inputs: { \n uuid: resourceId, \n parentData: parentData(), \n isArrayChild: true, \n resourceConfig: resourceConfig().iteratedConfig \n }\">\n </ng-container>\n }\n }\n } @else {\n <div class=\"resource-container empty\">\n <div class=\"resource row\"></div>\n </div>\n }\n\n @if (store.listData().length === 0) {\n <div>No records found</div>\n }\n </mat-expansion-panel>\n </div>\n} @else {\n @if (createMode()) {\n <ccc-resource-create [resourceConfig]=\"createConfig()\" [parentData]=\"parentData()\" (complete)=\"onCreateCompleted()\">\n </ccc-resource-create>\n }\n @if (store.listStatus() === 'resolved') {\n @for (resource of store.listData(); track resource['id']; let i = $index) {\n @if (parentData() && resourceConfig().iteratedConfig && resource['id'] && i + 1 <= resourceConfig().limit) {\n <ng-container *ngComponentOutlet=\"compoundResourceComponent();\n inputs: { \n uuid: resource['id'] + '', \n parentData: parentData(), \n isArrayChild: true, \n resourceConfig: resourceConfig().iteratedConfig \n }\">\n </ng-container>\n }\n }\n } @else {\n <div class=\"resource-container empty\">\n <div class=\"resource row\"></div>\n </div>\n }\n\n @if (store.listData().length === 0) {\n <div>No records found</div>\n }\n}\n", styles: [".resource-array-view{display:flex;flex-direction:column;min-height:36px;padding:10px 0 0}.title{display:flex;align-items:center;flex-shrink:0}.exp-panel-add-element-btn{margin-right:10px;transform:scale(1.2)}.no-title-exp-panel-padding{padding:10px}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i2$3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i2$3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i2$3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ResourceCreateComponent, selector: "ccc-resource-create", inputs: ["resourceConfig", "parentData", "loadCreatedResource"], outputs: ["complete"] }, { kind: "component", type: ActionAccessControlWrapperComponent, selector: "action-access-control-wrapper", inputs: ["actionContext"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }] });
2052
+ }
2053
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceArrayViewComponent, decorators: [{
2054
+ type: Component,
2055
+ args: [{ selector: 'ccc-resource-array-view', imports: [
2056
+ MatButtonModule,
2057
+ MatExpansionModule,
2058
+ MatIconModule,
2059
+ MatTooltipModule,
2060
+ ResourceCreateComponent,
2061
+ ActionAccessControlWrapperComponent,
2062
+ NgComponentOutlet,
2063
+ ], providers: [ResourceStore], template: "@if (resourceConfig().title !== '') {\n <div class=\"resource-array-view\">\n <mat-expansion-panel\n #expPanel\n [@.disabled]=\"true\"\n [disabled]=\"!resourceConfig().collapsible\"\n [expanded]=\"!resourceConfig().collapsible\">\n <mat-expansion-panel-header>\n @if (resourceConfig().title) {\n <mat-panel-title>\n <h1 class=\"title\">\n {{ resourceConfig().title }}\n </h1>\n </mat-panel-title>\n }\n <action-access-control-wrapper [actionContext]=\"createActionConfig()\">\n <button\n mat-icon-button\n class=\"exp-panel-add-element-btn\"\n color=\"accent\"\n (click)=\"createResource($event)\"\n [matTooltip]=\"resourceConfig().createButtonLabel\"\n matTooltipPosition=\"above\">\n <mat-icon color=\"accent\">add_circle</mat-icon>\n </button>\n </action-access-control-wrapper>\n </mat-expansion-panel-header>\n\n @if (!resourceConfig().collapsible && !resourceConfig().title) {\n <div class=\"no-title-exp-panel-padding\"></div>\n }\n\n @if (createMode()) {\n <ccc-resource-create\n [resourceConfig]=\"createConfig()\"\n [parentData]=\"parentData()\"\n (complete)=\"onCreateCompleted()\">\n </ccc-resource-create>\n }\n @if (store.listStatus() === 'resolved') {\n @for (resource of store.listData(); track resource['id']; let i = $index) {\n @let resourceId = (resource[resourceConfig().connectorField] || resource['id']) + '';\n\n @if (parentData() && resourceConfig().iteratedConfig && resourceId && i + 1 <= resourceConfig().limit) {\n <ng-container *ngComponentOutlet=\"compoundResourceComponent();\n inputs: { \n uuid: resourceId, \n parentData: parentData(), \n isArrayChild: true, \n resourceConfig: resourceConfig().iteratedConfig \n }\">\n </ng-container>\n }\n }\n } @else {\n <div class=\"resource-container empty\">\n <div class=\"resource row\"></div>\n </div>\n }\n\n @if (store.listData().length === 0) {\n <div>No records found</div>\n }\n </mat-expansion-panel>\n </div>\n} @else {\n @if (createMode()) {\n <ccc-resource-create [resourceConfig]=\"createConfig()\" [parentData]=\"parentData()\" (complete)=\"onCreateCompleted()\">\n </ccc-resource-create>\n }\n @if (store.listStatus() === 'resolved') {\n @for (resource of store.listData(); track resource['id']; let i = $index) {\n @if (parentData() && resourceConfig().iteratedConfig && resource['id'] && i + 1 <= resourceConfig().limit) {\n <ng-container *ngComponentOutlet=\"compoundResourceComponent();\n inputs: { \n uuid: resource['id'] + '', \n parentData: parentData(), \n isArrayChild: true, \n resourceConfig: resourceConfig().iteratedConfig \n }\">\n </ng-container>\n }\n }\n } @else {\n <div class=\"resource-container empty\">\n <div class=\"resource row\"></div>\n </div>\n }\n\n @if (store.listData().length === 0) {\n <div>No records found</div>\n }\n}\n", styles: [".resource-array-view{display:flex;flex-direction:column;min-height:36px;padding:10px 0 0}.title{display:flex;align-items:center;flex-shrink:0}.exp-panel-add-element-btn{margin-right:10px;transform:scale(1.2)}.no-title-exp-panel-padding{padding:10px}\n"] }]
2064
+ }], ctorParameters: () => [], propDecorators: { resourceConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "resourceConfig", required: true }] }], parentData: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentData", required: false }] }], expPanel: [{ type: i0.ViewChild, args: ['expPanel', { ...{ read: MatExpansionPanel }, isSignal: true }] }], emptyOneToOne: [{ type: i0.Output, args: ["emptyOneToOne"] }], compoundResourceComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "compoundResourceComponent", required: true }] }] } });
2065
+
2066
+ const simpleSlashDateFormatter = (value) => {
2067
+ if (!value)
2068
+ return '';
2069
+ const parsedDate = parseISO(value.toString());
2070
+ if (isValid(parsedDate)) {
2071
+ return format(parsedDate, 'M/d/yyyy');
2072
+ }
2073
+ console.error('Applying simpleSlashDateFormatter to invalid date value:', value);
2074
+ return value.toString();
2075
+ };
2076
+ const ValueFormatters = {
2077
+ ['simpleSlashDateFormat']: simpleSlashDateFormatter,
2078
+ };
2079
+ function applyFormatting(formatString, value) {
2080
+ if (!value)
2081
+ return '';
2082
+ if (formatString in ValueFormatters) {
2083
+ return ValueFormatters[formatString](value);
2084
+ }
2085
+ //default to formatting as a date with provided format string
2086
+ const parsedDate = parseISO(value.toString());
2087
+ if (isValid(parsedDate)) {
2088
+ return format(parsedDate, formatString);
2089
+ }
2090
+ return value.toString();
2091
+ }
2092
+ function formatDateString(formatString, value) {
2093
+ if (!value)
2094
+ return '';
2095
+ //default to formatting as a date with provided format string
2096
+ const parsedDate = parseISO(value.toString());
2097
+ if (isValid(parsedDate)) {
2098
+ return format(parsedDate, formatString);
2099
+ }
2100
+ return value.toString();
2101
+ }
2102
+
2103
+ class ResourceListComponent {
2104
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2105
+ compoundResourceComponent = input.required({ ...(ngDevMode ? { debugName: "compoundResourceComponent" } : {}) });
2106
+ resourceMeta = inject(RESOURCE_META);
2107
+ router = inject(Router);
2108
+ store = inject(ResourceStore);
2109
+ injector = inject(Injector);
2110
+ activatedRoute = inject(ActivatedRoute);
2111
+ hideCreateButton = input(true, { ...(ngDevMode ? { debugName: "hideCreateButton" } : {}) });
2112
+ createMode = output();
2113
+ resourceConfig = input(undefined, { ...(ngDevMode ? { debugName: "resourceConfig" } : {}) });
2114
+ viewRoute = input(undefined, { ...(ngDevMode ? { debugName: "viewRoute" } : {}) });
2115
+ filter = input('', { ...(ngDevMode ? { debugName: "filter" } : {}) });
2116
+ linkCreateType = input(false, { ...(ngDevMode ? { debugName: "linkCreateType" } : {}) });
2117
+ isRootList = input(true, { ...(ngDevMode ? { debugName: "isRootList" } : {}) });
2118
+ showCreateButton = computed(() => {
2119
+ const config = this.config();
2120
+ if (config.createTitle !== '' && !config.collapsible && !this.hideCreateButton()) {
2121
+ return true;
2122
+ }
2123
+ if (config.createConfig && Object.keys(config.createConfig).length !== 0) {
2124
+ return true;
2125
+ }
2126
+ return false;
2127
+ }, { ...(ngDevMode ? { debugName: "showCreateButton" } : {}) });
2128
+ createButtonContext = computed(() => {
2129
+ const config = this.resourceConfig();
2130
+ const showCreate = this.showCreateButton();
2131
+ if (config === undefined || showCreate === undefined) {
2132
+ return undefined;
2133
+ }
2134
+ if (config.shouldRenderActions === undefined) {
2135
+ return undefined;
2136
+ }
2137
+ return {
2138
+ actionType: 'create',
2139
+ meta: this.meta(),
2140
+ shouldRender: (data) => showCreate && config.shouldRenderActions.create(data),
2141
+ resourceData: this.relatedData() ?? {},
2142
+ };
2143
+ }, { ...(ngDevMode ? { debugName: "createButtonContext" } : {}) });
2144
+ searchableFields = computed(() => {
2145
+ const meta = this.meta();
2146
+ if (!meta)
2147
+ return '';
2148
+ return `over fields of ${meta.route}`;
2149
+ // TODO: implement searchable fields based on resource meta
2150
+ // return meta.searchableFields.join(', ') || '';
2151
+ }, { ...(ngDevMode ? { debugName: "searchableFields" } : {}) });
2152
+ relatedData = input(undefined, { ...(ngDevMode ? { debugName: "relatedData" } : {}) });
2153
+ parentId = input(undefined, { ...(ngDevMode ? { debugName: "parentId" } : {}) });
2154
+ routeConfig = computed(() => {
2155
+ return this.activatedRoute.snapshot.data['config'];
2156
+ }, { ...(ngDevMode ? { debugName: "routeConfig" } : {}) });
2157
+ config = computed(() => {
2158
+ return this.resourceConfig();
2159
+ }, { ...(ngDevMode ? { debugName: "config" } : {}) });
2160
+ expansionConfig = computed(() => {
2161
+ const config = this.config().rowExpansionConfig;
2162
+ if (config.type !== 'Component' && config.type !== 'Array') {
2163
+ config.showBackButton = false;
2164
+ }
2165
+ return config;
2166
+ }, { ...(ngDevMode ? { debugName: "expansionConfig" } : {}) });
2167
+ viewRouteFallback = computed(() => {
2168
+ if (this.viewRoute()) {
2169
+ return this.viewRoute();
2170
+ }
2171
+ return this.resourceMeta(this.config().primaryResource).route;
2172
+ }, { ...(ngDevMode ? { debugName: "viewRouteFallback" } : {}) });
2173
+ listTitle = computed(() => {
2174
+ if (this.config()?.collapsible)
2175
+ return '';
2176
+ return this.config().title || '';
2177
+ }, { ...(ngDevMode ? { debugName: "listTitle" } : {}) });
2178
+ indentTitle = computed(() => {
2179
+ if (this.config() === undefined) {
2180
+ return false;
2181
+ }
2182
+ return true;
2183
+ }, { ...(ngDevMode ? { debugName: "indentTitle" } : {}) });
2184
+ createButtonLabel = computed(() => {
2185
+ return this.config().createButtonLabel || 'Create';
2186
+ }, { ...(ngDevMode ? { debugName: "createButtonLabel" } : {}) });
2187
+ meta = computed(() => {
2188
+ const config = this.config();
2189
+ return this.resourceMeta(config.overrideResource || config.primaryResource);
2190
+ }, { ...(ngDevMode ? { debugName: "meta" } : {}) });
2191
+ resourceRefMap = signal(new Map(), { ...(ngDevMode ? { debugName: "resourceRefMap" } : {}) });
2192
+ primaryKeys = computed(() => {
2193
+ const meta = this.meta();
2194
+ if (!meta) {
2195
+ return [];
2196
+ }
2197
+ return meta.fields.filter((field) => field.primaryKey !== undefined);
2198
+ }, { ...(ngDevMode ? { debugName: "primaryKeys" } : {}) });
2199
+ rootColumns = computed(() => {
2200
+ const config = this.config();
2201
+ const idCols = [];
2202
+ for (const pk of this.primaryKeys()) {
2203
+ if (pk.required) {
2204
+ continue;
2205
+ }
2206
+ idCols.push({
2207
+ id: pk.fieldName,
2208
+ hidden: true,
2209
+ });
2210
+ }
2211
+ for (const col of config.listColumns) {
2212
+ if (!('additionalIds' in col))
2213
+ continue;
2214
+ for (const additionalCol of col.additionalIds) {
2215
+ if (additionalCol.resource !== undefined)
2216
+ continue;
2217
+ idCols.push({
2218
+ id: additionalCol.id,
2219
+ hidden: true,
2220
+ });
2221
+ }
2222
+ }
2223
+ return [...new Set([...idCols, ...config.listColumns])];
2224
+ }, { ...(ngDevMode ? { debugName: "rootColumns" } : {}) });
2225
+ columns = computed(() => {
2226
+ const refmap = this.resourceRefMap();
2227
+ const cols = this.rootColumns();
2228
+ const columns = [];
2229
+ const usedIds = new Set();
2230
+ for (const col of cols) {
2231
+ const indexId = this.getUniqueId(col.id, usedIds);
2232
+ usedIds.add(indexId);
2233
+ if (col.hidden) {
2234
+ continue;
2235
+ }
2236
+ if ('additionalIds' in col) {
2237
+ columns.push({
2238
+ id: indexId,
2239
+ header: col.header,
2240
+ width: col.width,
2241
+ resizable: col.resizable,
2242
+ valueGetter: (data) => {
2243
+ const concatArray = [];
2244
+ col.additionalIds.forEach((additionalCol) => {
2245
+ if (!additionalCol.resource) {
2246
+ concatArray.push(data[additionalCol.id]);
2247
+ }
2248
+ else {
2249
+ const resource = signal(additionalCol.resource);
2250
+ const resourceRef = refmap.get(resource());
2251
+ const resData = resourceRef?.value();
2252
+ if (!resData) {
2253
+ return;
2254
+ }
2255
+ for (const res of resData) {
2256
+ if (res[additionalCol.id] === data[col.id] && additionalCol.field) {
2257
+ const value = res[additionalCol.field];
2258
+ if (col.formatType && typeof value === 'string') {
2259
+ concatArray.push(formatDateString(col.formatType, value));
2260
+ }
2261
+ else if (value) {
2262
+ concatArray.push(value);
2263
+ }
2264
+ }
2265
+ }
2266
+ }
2267
+ });
2268
+ if (concatArray.length === 0) {
2269
+ return col.emptyDataValue;
2270
+ }
2271
+ switch (col.concatFn) {
2272
+ case 'space-concat':
2273
+ return spaceConcatWithoutResource(concatArray);
2274
+ case 'space-hyphen-concat':
2275
+ return spaceHyphenConcatWithoutResource(concatArray);
2276
+ case 'hyphen-space-concat':
2277
+ return hyphenSpaceConcatWithoutResource(concatArray);
2278
+ case 'no-space-concat':
2279
+ return noSpaceConcatWithoutResource(concatArray);
2280
+ default: {
2281
+ // default case is hyphen-concat
2282
+ return hyphenConcatWithoutResource(concatArray);
2283
+ }
2284
+ }
2285
+ },
2286
+ });
2287
+ }
2288
+ else {
2289
+ columns.push({
2290
+ id: indexId,
2291
+ header: col.header,
2292
+ width: col.width,
2293
+ resizable: col.resizable,
2294
+ valueFormatter: (params) => {
2295
+ if (col.formatType) {
2296
+ const retValue = applyFormatting(col.formatType, params);
2297
+ return retValue || col.emptyDataValue;
2298
+ }
2299
+ return params || col.emptyDataValue;
2300
+ },
2301
+ });
2302
+ }
2303
+ }
2304
+ if (this.config().showViewButton) {
2305
+ let route = '';
2306
+ const isRootList = this.isRootList() === undefined;
2307
+ if (this.viewRouteFallback() && isRootList) {
2308
+ route = this.viewRouteFallback() || '';
2309
+ }
2310
+ else {
2311
+ let viewResource = this.config().viewResource;
2312
+ if (!viewResource || viewResource === '') {
2313
+ viewResource = this.config().primaryResource;
2314
+ }
2315
+ const meta = this.resourceMeta(viewResource);
2316
+ if (meta !== undefined) {
2317
+ route = meta.route;
2318
+ }
2319
+ }
2320
+ columns.push({
2321
+ id: 'view',
2322
+ header: 'View',
2323
+ hideHeader: true,
2324
+ buttonConfig: {
2325
+ label: 'View',
2326
+ icon: 'arrow_forward',
2327
+ viewRoute: route,
2328
+ actionType: 'link',
2329
+ },
2330
+ });
2331
+ }
2332
+ return columns;
2333
+ }, { ...(ngDevMode ? { debugName: "columns" } : {}) });
2334
+ reloadListData() {
2335
+ this.store.reloadListData();
2336
+ }
2337
+ processedRowData = computed(() => {
2338
+ this.filter();
2339
+ this.relatedData();
2340
+ const data = this.store.listData();
2341
+ if (!data)
2342
+ return [];
2343
+ const columns = this.columns();
2344
+ return data.map((row) => {
2345
+ const updatedRow = { ...row };
2346
+ for (const col of columns) {
2347
+ if (col.valueGetter) {
2348
+ updatedRow[col.id] = col.valueGetter(row);
2349
+ }
2350
+ else {
2351
+ updatedRow[col.id] = row[col.id];
2352
+ }
2353
+ if (typeof col.valueFormatter === 'function') {
2354
+ updatedRow[col.id] = col.valueFormatter(updatedRow[col.id]);
2355
+ }
2356
+ }
2357
+ return updatedRow;
2358
+ });
2359
+ }, { ...(ngDevMode ? { debugName: "processedRowData" } : {}) });
2360
+ filters = computed(() => {
2361
+ const configFilter = this.config().filter?.(this.relatedData()) || '';
2362
+ const inputFilter = this.filter();
2363
+ if (inputFilter && configFilter) {
2364
+ return `(${inputFilter}),(${configFilter})`;
2365
+ }
2366
+ if (inputFilter) {
2367
+ return `(${inputFilter})`;
2368
+ }
2369
+ return configFilter || '';
2370
+ }, { ...(ngDevMode ? { debugName: "filters" } : {}) });
2371
+ createResource(event) {
2372
+ event.stopPropagation();
2373
+ this.createMode.emit(true);
2374
+ }
2375
+ parentKey = computed(() => {
2376
+ if (this.relatedData() === undefined || this.childKey() === undefined) {
2377
+ return '';
2378
+ }
2379
+ const parent = this.config().parentRelation?.parentKey;
2380
+ return this.relatedData()?.[parent] || '';
2381
+ }, { ...(ngDevMode ? { debugName: "parentKey" } : {}) });
2382
+ childKey = computed(() => {
2383
+ if (!this.config().parentRelation) {
2384
+ return '';
2385
+ }
2386
+ return this.config().parentRelation?.childKey;
2387
+ }, { ...(ngDevMode ? { debugName: "childKey" } : {}) });
2388
+ getUniqueId(baseId, usedIds) {
2389
+ if (!usedIds.has(baseId)) {
2390
+ return baseId;
2391
+ }
2392
+ let counter = 1;
2393
+ let newId = `${baseId}_${counter}`;
2394
+ while (usedIds.has(newId)) {
2395
+ counter++;
2396
+ newId = `${baseId}_${counter}`;
2397
+ }
2398
+ return newId;
2399
+ }
2400
+ ngOnInit() {
2401
+ const primaryResource = this.config().primaryResource;
2402
+ if (this.meta()) {
2403
+ this.store.resourceName.set(primaryResource);
2404
+ this.store.resourceMeta.set(this.meta());
2405
+ this.store.listColumns.set(this.config().listColumns || []);
2406
+ this.store.requireSearchToDisplayResults.set(this.config().requireSearchToDisplayResults || false);
2407
+ this.store.sorts.set(this.config().sorts || []);
2408
+ }
2409
+ this.store.filter.set(this.filters());
2410
+ this.store.disableCacheForFilterPii.set(this.config().disableCacheForFilterPii);
2411
+ runInInjectionContext(this.injector, () => {
2412
+ this.config().listColumns.forEach((element) => {
2413
+ if (!('additionalIds' in element)) {
2414
+ return;
2415
+ }
2416
+ element.additionalIds.forEach((id) => {
2417
+ if (id.resource === undefined) {
2418
+ return;
2419
+ }
2420
+ const meta = this.resourceMeta(id.resource);
2421
+ if (meta === undefined) {
2422
+ return;
2423
+ }
2424
+ const route = signal(meta.route, { ...(ngDevMode ? { debugName: "route" } : {}) });
2425
+ const resource = signal(id.resource, { ...(ngDevMode ? { debugName: "resource" } : {}) });
2426
+ if (!this.resourceRefMap().has(resource())) {
2427
+ const ref = this.store.resourceList(route);
2428
+ if (ref === undefined) {
2429
+ return;
2430
+ }
2431
+ this.resourceRefMap.set(this.resourceRefMap().set(resource(), ref));
2432
+ }
2433
+ });
2434
+ });
2435
+ effect(() => {
2436
+ this.filter();
2437
+ this.relatedData();
2438
+ this.store.filter.set(this.filters());
2439
+ this.store.disableCacheForFilterPii.set(this.config().disableCacheForFilterPii);
2440
+ this.store.buildStoreListData();
2441
+ });
2442
+ });
2443
+ }
2444
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2445
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: ResourceListComponent, isStandalone: true, selector: "ccc-resource-list", inputs: { compoundResourceComponent: { classPropertyName: "compoundResourceComponent", publicName: "compoundResourceComponent", isSignal: true, isRequired: true, transformFunction: null }, hideCreateButton: { classPropertyName: "hideCreateButton", publicName: "hideCreateButton", isSignal: true, isRequired: false, transformFunction: null }, resourceConfig: { classPropertyName: "resourceConfig", publicName: "resourceConfig", isSignal: true, isRequired: false, transformFunction: null }, viewRoute: { classPropertyName: "viewRoute", publicName: "viewRoute", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, linkCreateType: { classPropertyName: "linkCreateType", publicName: "linkCreateType", isSignal: true, isRequired: false, transformFunction: null }, isRootList: { classPropertyName: "isRootList", publicName: "isRootList", isSignal: true, isRequired: false, transformFunction: null }, relatedData: { classPropertyName: "relatedData", publicName: "relatedData", isSignal: true, isRequired: false, transformFunction: null }, parentId: { classPropertyName: "parentId", publicName: "parentId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { createMode: "createMode" }, providers: [ResourceStore], ngImport: i0, template: "<div [class]=\"linkCreateType() ? 'page-container hide' : 'page-container'\">\n <div class=\"header-container\" [class.show-background]=\"!config().collapsible\" [class.indent]=\"indentTitle()\">\n <h1 class=\"title\">{{ listTitle() }}</h1>\n <action-access-control-wrapper [actionContext]=\"createButtonContext()\">\n <div class=\"create-button\">\n <button mat-raised-button color=\"accent\" (click)=\"createResource($event)\">\n <div class=\"edit-text\">\n <mat-icon>create</mat-icon>\n <span class=\"edit-button-text\"> {{ createButtonLabel() }} </span>\n </div>\n </button>\n </div>\n </action-access-control-wrapper>\n </div>\n <div class=\"resource-container\">\n @if (config().searchable) {\n <mat-form-field class=\"search\">\n <input matInput #qry (keyup)=\"store.searchTokens.set(qry.value)\" placeholder=\"Search\" aria-label=\"Search\" />\n @if (searchableFields() !== '') {\n <mat-hint>Search {{ searchableFields() }}</mat-hint>\n }\n <mat-icon matPrefix>search</mat-icon>\n </mat-form-field>\n }\n <ccc-grid\n [rowData]=\"processedRowData()\"\n [columnDefs]=\"columns()\"\n [enableRowExpansion]=\"config().enableRowExpansion\"\n [detailTemplate]=\"expandedRowTemplate\">\n </ccc-grid>\n </div>\n</div>\n\n<ng-template #expandedRowTemplate let-rowData>\n <ng-container\n *ngComponentOutlet=\"\n compoundResourceComponent();\n inputs: {\n uuid: rowData.id,\n navAfterDelete: false,\n deleted: store.reloadListData.bind(store),\n resourceConfig: expansionConfig(),\n }\n \">\n </ng-container>\n</ng-template>\n", styles: [".page-container{position:relative;margin:10px 0 0}.header-container{margin-top:10px;display:flex;flex-direction:row;justify-content:space-between;top:0;z-index:10;width:100%}.title{display:flex;align-items:center;flex-shrink:0}.create-button{margin-top:auto;margin-bottom:auto;padding-left:10px}.edit-text{display:flex;flex-direction:row;align-items:center}.edit-text .edit{color:#003b49}.edit-text .mat-icon{margin-right:8px}.edit-button-text{margin-top:auto;margin-bottom:auto}.exp-panel-add-element-btn{margin-right:10px;transform:scale(1.2)}.hide{display:none}.show-background{background-color:#fff;padding-bottom:20px}.indent{padding-left:20px}.search{width:100%}\n"], dependencies: [{ kind: "component", type: AppGridComponent, selector: "ccc-grid", inputs: ["rowData", "columnDefs", "enableRowExpansion", "detailTemplate", "selectionType"], outputs: ["selectedRows"] }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "component", type: ActionAccessControlWrapperComponent, selector: "action-access-control-wrapper", inputs: ["actionContext"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }] });
2446
+ }
2447
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceListComponent, decorators: [{
2448
+ type: Component,
2449
+ args: [{ standalone: true, imports: [
2450
+ AppGridComponent,
2451
+ RouterModule,
2452
+ MatIconModule,
2453
+ MatButtonModule,
2454
+ MatExpansionModule,
2455
+ MatTooltipModule,
2456
+ ReactiveFormsModule,
2457
+ MatInputModule,
2458
+ ActionAccessControlWrapperComponent,
2459
+ NgComponentOutlet,
2460
+ ], selector: 'ccc-resource-list', providers: [ResourceStore], template: "<div [class]=\"linkCreateType() ? 'page-container hide' : 'page-container'\">\n <div class=\"header-container\" [class.show-background]=\"!config().collapsible\" [class.indent]=\"indentTitle()\">\n <h1 class=\"title\">{{ listTitle() }}</h1>\n <action-access-control-wrapper [actionContext]=\"createButtonContext()\">\n <div class=\"create-button\">\n <button mat-raised-button color=\"accent\" (click)=\"createResource($event)\">\n <div class=\"edit-text\">\n <mat-icon>create</mat-icon>\n <span class=\"edit-button-text\"> {{ createButtonLabel() }} </span>\n </div>\n </button>\n </div>\n </action-access-control-wrapper>\n </div>\n <div class=\"resource-container\">\n @if (config().searchable) {\n <mat-form-field class=\"search\">\n <input matInput #qry (keyup)=\"store.searchTokens.set(qry.value)\" placeholder=\"Search\" aria-label=\"Search\" />\n @if (searchableFields() !== '') {\n <mat-hint>Search {{ searchableFields() }}</mat-hint>\n }\n <mat-icon matPrefix>search</mat-icon>\n </mat-form-field>\n }\n <ccc-grid\n [rowData]=\"processedRowData()\"\n [columnDefs]=\"columns()\"\n [enableRowExpansion]=\"config().enableRowExpansion\"\n [detailTemplate]=\"expandedRowTemplate\">\n </ccc-grid>\n </div>\n</div>\n\n<ng-template #expandedRowTemplate let-rowData>\n <ng-container\n *ngComponentOutlet=\"\n compoundResourceComponent();\n inputs: {\n uuid: rowData.id,\n navAfterDelete: false,\n deleted: store.reloadListData.bind(store),\n resourceConfig: expansionConfig(),\n }\n \">\n </ng-container>\n</ng-template>\n", styles: [".page-container{position:relative;margin:10px 0 0}.header-container{margin-top:10px;display:flex;flex-direction:row;justify-content:space-between;top:0;z-index:10;width:100%}.title{display:flex;align-items:center;flex-shrink:0}.create-button{margin-top:auto;margin-bottom:auto;padding-left:10px}.edit-text{display:flex;flex-direction:row;align-items:center}.edit-text .edit{color:#003b49}.edit-text .mat-icon{margin-right:8px}.edit-button-text{margin-top:auto;margin-bottom:auto}.exp-panel-add-element-btn{margin-right:10px;transform:scale(1.2)}.hide{display:none}.show-background{background-color:#fff;padding-bottom:20px}.indent{padding-left:20px}.search{width:100%}\n"] }]
2461
+ }], propDecorators: { compoundResourceComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "compoundResourceComponent", required: true }] }], hideCreateButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideCreateButton", required: false }] }], createMode: [{ type: i0.Output, args: ["createMode"] }], resourceConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "resourceConfig", required: false }] }], viewRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewRoute", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], linkCreateType: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkCreateType", required: false }] }], isRootList: [{ type: i0.Input, args: [{ isSignal: true, alias: "isRootList", required: false }] }], relatedData: [{ type: i0.Input, args: [{ isSignal: true, alias: "relatedData", required: false }] }], parentId: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentId", required: false }] }] } });
2462
+
2463
+ class ResourceListCreateComponent {
2464
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2465
+ compoundResourceComponent = input.required({ ...(ngDevMode ? { debugName: "compoundResourceComponent" } : {}) });
2466
+ injector = inject(Injector);
2467
+ store = inject(ResourceStore);
2468
+ listChild = viewChild('list', { ...(ngDevMode ? { debugName: "listChild" } : {}), read: ResourceListComponent });
2469
+ route = inject(ActivatedRoute);
2470
+ router = inject(Router);
2471
+ create = signal(false, { ...(ngDevMode ? { debugName: "create" } : {}) });
2472
+ parentId = input(undefined, { ...(ngDevMode ? { debugName: "parentId" } : {}) });
2473
+ parentData = input({}, { ...(ngDevMode ? { debugName: "parentData" } : {}) });
2474
+ searchParams = input([], { ...(ngDevMode ? { debugName: "searchParams" } : {}) });
2475
+ resourceConfig = input(undefined, { ...(ngDevMode ? { debugName: "resourceConfig" } : {}) });
2476
+ isRootList = input(true, { ...(ngDevMode ? { debugName: "isRootList" } : {}) });
2477
+ childKey = computed(() => {
2478
+ if (!this.config().parentRelation) {
2479
+ return '';
2480
+ }
2481
+ return this.config().parentRelation?.childKey;
2482
+ }, { ...(ngDevMode ? { debugName: "childKey" } : {}) });
2483
+ filter = computed(() => {
2484
+ const filters = [];
2485
+ if (this.childKey() !== '' && this.parentKey() !== '') {
2486
+ filters.push(`${this.childKey()}:eq:${this.parentKey()}`);
2487
+ }
2488
+ return filters.join(',');
2489
+ }, { ...(ngDevMode ? { debugName: "filter" } : {}) });
2490
+ parentKey = computed(() => {
2491
+ if (this.parentData() === undefined || this.childKey() === undefined) {
2492
+ return '';
2493
+ }
2494
+ const parent = this.config().parentRelation?.parentKey;
2495
+ return this.parentData()?.[parent] || '';
2496
+ }, { ...(ngDevMode ? { debugName: "parentKey" } : {}) });
2497
+ config = computed(() => {
2498
+ const inputConfig = this.resourceConfig();
2499
+ if (inputConfig) {
2500
+ return inputConfig;
2501
+ }
2502
+ return this.rootConfig().parentConfig;
2503
+ }, { ...(ngDevMode ? { debugName: "config" } : {}) });
2504
+ expPanel = viewChild('expPanel', { ...(ngDevMode ? { debugName: "expPanel" } : {}), read: MatExpansionPanel });
2505
+ rootConfig = computed(() => {
2506
+ return this.route.snapshot.data['config'];
2507
+ }, { ...(ngDevMode ? { debugName: "rootConfig" } : {}) });
2508
+ createLinkType = computed(() => {
2509
+ return this.config().loadCreatedResource && this.create();
2510
+ }, { ...(ngDevMode ? { debugName: "createLinkType" } : {}) });
2511
+ createConfig = computed(() => {
2512
+ const config = this.config();
2513
+ if (Object.keys(config.createConfig).length !== 0) {
2514
+ return config.createConfig;
2515
+ }
2516
+ return config;
2517
+ }, { ...(ngDevMode ? { debugName: "createConfig" } : {}) });
2518
+ createResource(event) {
2519
+ event.stopPropagation();
2520
+ this.create.set(true);
2521
+ if (this.expPanel() && this.expPanel()?.closed) {
2522
+ this.expPanel()?.open();
2523
+ }
2524
+ }
2525
+ makeCreatePatches() {
2526
+ if (this.create()) {
2527
+ this.listChild()?.reloadListData();
2528
+ this.create.set(false);
2529
+ }
2530
+ }
2531
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceListCreateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2532
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: ResourceListCreateComponent, isStandalone: true, selector: "ccc-resource-list-create", inputs: { compoundResourceComponent: { classPropertyName: "compoundResourceComponent", publicName: "compoundResourceComponent", isSignal: true, isRequired: true, transformFunction: null }, parentId: { classPropertyName: "parentId", publicName: "parentId", isSignal: true, isRequired: false, transformFunction: null }, parentData: { classPropertyName: "parentData", publicName: "parentData", isSignal: true, isRequired: false, transformFunction: null }, searchParams: { classPropertyName: "searchParams", publicName: "searchParams", isSignal: true, isRequired: false, transformFunction: null }, resourceConfig: { classPropertyName: "resourceConfig", publicName: "resourceConfig", isSignal: true, isRequired: false, transformFunction: null }, isRootList: { classPropertyName: "isRootList", publicName: "isRootList", isSignal: true, isRequired: false, transformFunction: null } }, providers: [ResourceStore], viewQueries: [{ propertyName: "listChild", first: true, predicate: ["list"], descendants: true, read: ResourceListComponent, isSignal: true }, { propertyName: "expPanel", first: true, predicate: ["expPanel"], descendants: true, read: MatExpansionPanel, isSignal: true }], ngImport: i0, template: "@if (config().collapsible) {\n <div class=\"page-container\">\n <mat-expansion-panel\n #expPanel\n [@.disabled]=\"true\"\n disabled=\"{{ !config().collapsible }}\"\n expanded=\"{{ !config().collapsible }}\">\n @if (config().collapsible || config().title) {\n <mat-expansion-panel-header>\n <mat-panel-title>\n <h1 class=\"title\">\n {{ config().title }}\n </h1>\n </mat-panel-title>\n @if (config().createTitle !== '') {\n <button\n mat-icon-button\n class=\"exp-panel-add-element-btn\"\n color=\"accent\"\n (click)=\"createResource($event)\"\n matTooltip=\"{{ config().createButtonLabel }}\"\n matTooltipPosition=\"above\">\n <mat-icon color=\"accent\">add_circle</mat-icon>\n </button>\n }\n </mat-expansion-panel-header>\n } @else {\n <div class=\"no-title-exp-panel-padding\"></div>\n }\n\n <ng-container *ngTemplateOutlet=\"resourceComponents\"></ng-container>\n </mat-expansion-panel>\n </div>\n} @else {\n <ng-container *ngTemplateOutlet=\"resourceComponents\"></ng-container>\n}\n\n<ng-template #resourceComponents>\n @if (create()) {\n <ccc-resource-create\n [resourceConfig]=\"createConfig()\"\n [parentData]=\"parentData()\"\n [loadCreatedResource]=\"config().loadCreatedResource\"\n (complete)=\"makeCreatePatches()\">\n </ccc-resource-create>\n }\n\n <ccc-resource-list\n #list\n [viewRoute]=\"rootConfig().routeData.route\"\n [resourceConfig]=\"config()\"\n [filter]=\"filter()\"\n [hideCreateButton]=\"create()\"\n [relatedData]=\"parentData()\"\n [parentId]=\"parentId()\"\n [linkCreateType]=\"createLinkType()\"\n [isRootList]=\"isRootList()\"\n (createMode)=\"create.set($event)\"\n [compoundResourceComponent]=\"compoundResourceComponent()\">\n </ccc-resource-list>\n</ng-template>\n", styles: [".page-container{position:relative;margin:10px 0 0}.exp-panel-add-element-btn{margin-right:10px;transform:scale(1.2)}\n"], dependencies: [{ kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i2$3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i2$3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i2$3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ResourceCreateComponent, selector: "ccc-resource-create", inputs: ["resourceConfig", "parentData", "loadCreatedResource"], outputs: ["complete"] }, { kind: "component", type: ResourceListComponent, selector: "ccc-resource-list", inputs: ["compoundResourceComponent", "hideCreateButton", "resourceConfig", "viewRoute", "filter", "linkCreateType", "isRootList", "relatedData", "parentId"], outputs: ["createMode"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
2533
+ }
2534
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceListCreateComponent, decorators: [{
2535
+ type: Component,
2536
+ args: [{ standalone: true, selector: 'ccc-resource-list-create', imports: [
2537
+ MatExpansionModule,
2538
+ MatIconModule,
2539
+ MatTooltipModule,
2540
+ MatButtonModule,
2541
+ ResourceCreateComponent,
2542
+ ResourceListComponent,
2543
+ NgTemplateOutlet,
2544
+ ], providers: [ResourceStore], template: "@if (config().collapsible) {\n <div class=\"page-container\">\n <mat-expansion-panel\n #expPanel\n [@.disabled]=\"true\"\n disabled=\"{{ !config().collapsible }}\"\n expanded=\"{{ !config().collapsible }}\">\n @if (config().collapsible || config().title) {\n <mat-expansion-panel-header>\n <mat-panel-title>\n <h1 class=\"title\">\n {{ config().title }}\n </h1>\n </mat-panel-title>\n @if (config().createTitle !== '') {\n <button\n mat-icon-button\n class=\"exp-panel-add-element-btn\"\n color=\"accent\"\n (click)=\"createResource($event)\"\n matTooltip=\"{{ config().createButtonLabel }}\"\n matTooltipPosition=\"above\">\n <mat-icon color=\"accent\">add_circle</mat-icon>\n </button>\n }\n </mat-expansion-panel-header>\n } @else {\n <div class=\"no-title-exp-panel-padding\"></div>\n }\n\n <ng-container *ngTemplateOutlet=\"resourceComponents\"></ng-container>\n </mat-expansion-panel>\n </div>\n} @else {\n <ng-container *ngTemplateOutlet=\"resourceComponents\"></ng-container>\n}\n\n<ng-template #resourceComponents>\n @if (create()) {\n <ccc-resource-create\n [resourceConfig]=\"createConfig()\"\n [parentData]=\"parentData()\"\n [loadCreatedResource]=\"config().loadCreatedResource\"\n (complete)=\"makeCreatePatches()\">\n </ccc-resource-create>\n }\n\n <ccc-resource-list\n #list\n [viewRoute]=\"rootConfig().routeData.route\"\n [resourceConfig]=\"config()\"\n [filter]=\"filter()\"\n [hideCreateButton]=\"create()\"\n [relatedData]=\"parentData()\"\n [parentId]=\"parentId()\"\n [linkCreateType]=\"createLinkType()\"\n [isRootList]=\"isRootList()\"\n (createMode)=\"create.set($event)\"\n [compoundResourceComponent]=\"compoundResourceComponent()\">\n </ccc-resource-list>\n</ng-template>\n", styles: [".page-container{position:relative;margin:10px 0 0}.exp-panel-add-element-btn{margin-right:10px;transform:scale(1.2)}\n"] }]
2545
+ }], propDecorators: { compoundResourceComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "compoundResourceComponent", required: true }] }], listChild: [{ type: i0.ViewChild, args: ['list', { ...{ read: ResourceListComponent }, isSignal: true }] }], parentId: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentId", required: false }] }], parentData: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentData", required: false }] }], searchParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchParams", required: false }] }], resourceConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "resourceConfig", required: false }] }], isRootList: [{ type: i0.Input, args: [{ isSignal: true, alias: "isRootList", required: false }] }], expPanel: [{ type: i0.ViewChild, args: ['expPanel', { ...{ read: MatExpansionPanel }, isSignal: true }] }] } });
2546
+
2547
+ var resourceListCreate_component = /*#__PURE__*/Object.freeze({
2548
+ __proto__: null,
2549
+ ResourceListCreateComponent: ResourceListCreateComponent
2550
+ });
2551
+
2552
+ class ResourceResolverComponent {
2553
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2554
+ compoundResourceComponent = input.required({ ...(ngDevMode ? { debugName: "compoundResourceComponent" } : {}) });
2555
+ resourceConfig = input(undefined, { ...(ngDevMode ? { debugName: "resourceConfig" } : {}) });
2556
+ config = computed(() => {
2557
+ return this.resourceConfig();
2558
+ }, { ...(ngDevMode ? { debugName: "config" } : {}) });
2559
+ dynamicSlot = viewChild.required('dynamicSlot', { read: ViewContainerRef });
2560
+ parentData = input.required({ ...(ngDevMode ? { debugName: "parentData" } : {}) });
2561
+ constructor() {
2562
+ effect(() => {
2563
+ const parentData = this.parentData();
2564
+ const config = this.config();
2565
+ const params = config?.params;
2566
+ if (params === undefined || config === undefined) {
2567
+ return;
2568
+ }
2569
+ untracked(() => {
2570
+ this.dynamicSlot().clear();
2571
+ const component = config.component;
2572
+ switch (component) {
2573
+ case 'SwitchResolver': {
2574
+ let uuid = '';
2575
+ let caseConfig = {};
2576
+ for (const c of params.cases) {
2577
+ if (parentData) {
2578
+ if (parentData[c.parentField] === c.caseId) {
2579
+ uuid = String(parentData[c.childId]);
2580
+ caseConfig = c.config;
2581
+ if ('parentRelation' in caseConfig) {
2582
+ caseConfig.parentRelation.childKey = '';
2583
+ caseConfig.parentRelation.parentKey = '';
2584
+ }
2585
+ break;
2586
+ }
2587
+ }
2588
+ }
2589
+ if (uuid == '') {
2590
+ return;
2591
+ }
2592
+ // Use dynamic import to avoid circular dependency
2593
+ const primaryComponentRef = this.dynamicSlot().createComponent(this.compoundResourceComponent());
2594
+ primaryComponentRef.setInput('resourceConfig', caseConfig);
2595
+ primaryComponentRef.setInput('uuid', uuid);
2596
+ primaryComponentRef.setInput('parentData', parentData);
2597
+ break;
2598
+ }
2599
+ default:
2600
+ console.warn('Component not found', component);
2601
+ // add default component to dynamic slot
2602
+ break;
2603
+ }
2604
+ });
2605
+ });
2606
+ }
2607
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceResolverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2608
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.2", type: ResourceResolverComponent, isStandalone: true, selector: "ccc-resource-resolver", inputs: { compoundResourceComponent: { classPropertyName: "compoundResourceComponent", publicName: "compoundResourceComponent", isSignal: true, isRequired: true, transformFunction: null }, resourceConfig: { classPropertyName: "resourceConfig", publicName: "resourceConfig", isSignal: true, isRequired: false, transformFunction: null }, parentData: { classPropertyName: "parentData", publicName: "parentData", isSignal: true, isRequired: true, transformFunction: null } }, providers: [ResourceStore], viewQueries: [{ propertyName: "dynamicSlot", first: true, predicate: ["dynamicSlot"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "<ng-template #dynamicSlot></ng-template>", styles: [""] });
2609
+ }
2610
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceResolverComponent, decorators: [{
2611
+ type: Component,
2612
+ args: [{ selector: 'ccc-resource-resolver', imports: [], providers: [ResourceStore], template: "<ng-template #dynamicSlot></ng-template>" }]
2613
+ }], ctorParameters: () => [], propDecorators: { compoundResourceComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "compoundResourceComponent", required: true }] }], resourceConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "resourceConfig", required: false }] }], dynamicSlot: [{ type: i0.ViewChild, args: ['dynamicSlot', { ...{ read: ViewContainerRef }, isSignal: true }] }], parentData: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentData", required: true }] }] } });
2614
+
2615
+ class DeleteResourceConfirmationModalComponent {
2616
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: DeleteResourceConfirmationModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2617
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.2", type: DeleteResourceConfirmationModalComponent, isStandalone: true, selector: "ccc-delete-resource-confirmation-modal", ngImport: i0, template: "<div mat-dialog-title>Are you sure you want to delete this resource?</div>\n<mat-dialog-actions align=\"end\">\n <button mat-raised-button color=\"warn\" [mat-dialog-close]=\"false\">Cancel</button>\n <button mat-raised-button color=\"accent\" [mat-dialog-close]=\"true\">Confirm</button>\n</mat-dialog-actions>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatInputModule }] });
2618
+ }
2619
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: DeleteResourceConfirmationModalComponent, decorators: [{
2620
+ type: Component,
2621
+ args: [{ selector: 'ccc-delete-resource-confirmation-modal', imports: [MatDialogModule, MatFormFieldModule, MatDialogActions, MatButtonModule, MatInputModule], template: "<div mat-dialog-title>Are you sure you want to delete this resource?</div>\n<mat-dialog-actions align=\"end\">\n <button mat-raised-button color=\"warn\" [mat-dialog-close]=\"false\">Cancel</button>\n <button mat-raised-button color=\"accent\" [mat-dialog-close]=\"true\">Confirm</button>\n</mat-dialog-actions>\n" }]
2622
+ }] });
2623
+
2624
+ class ResourceViewComponent {
2625
+ resourceMeta = inject(RESOURCE_META);
2626
+ location = inject(Location);
2627
+ router = inject(Router);
2628
+ activatedRoute = inject(ActivatedRoute);
2629
+ notifications = inject(NotificationService);
2630
+ store = inject(ResourceStore);
2631
+ injector = inject(Injector);
2632
+ destroyRef = inject(DestroyRef);
2633
+ formState = inject(FormStateService);
2634
+ dialog = inject(MatDialog);
2635
+ editMode = signal('view', { ...(ngDevMode ? { debugName: "editMode" } : {}) });
2636
+ uuid = input.required({ ...(ngDevMode ? { debugName: "uuid" } : {}) });
2637
+ config = input.required({ ...(ngDevMode ? { debugName: "config" } : {}) });
2638
+ relatedData = input({}, { ...(ngDevMode ? { debugName: "relatedData" } : {}) });
2639
+ compoundResourceView = input(false, { ...(ngDevMode ? { debugName: "compoundResourceView" } : {}) });
2640
+ isDirty = signal(false, { ...(ngDevMode ? { debugName: "isDirty" } : {}) });
2641
+ displayFormInvalidMessage = signal(false, { ...(ngDevMode ? { debugName: "displayFormInvalidMessage" } : {}) });
2642
+ deleted = output();
2643
+ navAfterDelete = input(true, { ...(ngDevMode ? { debugName: "navAfterDelete" } : {}) });
2644
+ showCreateForm = model(false, { ...(ngDevMode ? { debugName: "showCreateForm" } : {}) });
2645
+ createConfig = computed(() => {
2646
+ if (Object.keys(this.config().createConfig || {}).length > 0) {
2647
+ return this.config().createConfig;
2648
+ }
2649
+ return this.config();
2650
+ }, { ...(ngDevMode ? { debugName: "createConfig" } : {}) });
2651
+ rootConfig = computed(() => {
2652
+ return this.activatedRoute.snapshot.data['config'];
2653
+ }, { ...(ngDevMode ? { debugName: "rootConfig" } : {}) });
2654
+ showCloseButton = computed(() => {
2655
+ const config = this.config();
2656
+ const showBackButton = config.showBackButton ?? true;
2657
+ if (config) {
2658
+ return showBackButton && config.primaryResource === this.rootConfig().parentConfig.primaryResource;
2659
+ }
2660
+ return showBackButton;
2661
+ }, { ...(ngDevMode ? { debugName: "showCloseButton" } : {}) });
2662
+ commonButtonConfig = computed(() => {
2663
+ const config = this.config();
2664
+ if (config === undefined) {
2665
+ return undefined;
2666
+ }
2667
+ if (config.shouldRenderActions === undefined) {
2668
+ return undefined;
2669
+ }
2670
+ return {
2671
+ meta: this.store.resourceMeta(),
2672
+ resourceData: this.relatedData(),
2673
+ config,
2674
+ };
2675
+ }, { ...(ngDevMode ? { debugName: "commonButtonConfig" } : {}) });
2676
+ editButtonContext = computed(() => {
2677
+ const commonConfig = this.commonButtonConfig();
2678
+ if (commonConfig === undefined) {
2679
+ return undefined;
2680
+ }
2681
+ return {
2682
+ actionType: 'edit',
2683
+ meta: commonConfig.meta,
2684
+ resourceData: commonConfig.resourceData,
2685
+ shouldRender: commonConfig.config.shouldRenderActions.edit,
2686
+ };
2687
+ }, { ...(ngDevMode ? { debugName: "editButtonContext" } : {}) });
2688
+ deleteButtonContext = computed(() => {
2689
+ const commonConfig = this.commonButtonConfig();
2690
+ if (commonConfig === undefined) {
2691
+ return undefined;
2692
+ }
2693
+ return {
2694
+ actionType: 'delete',
2695
+ meta: commonConfig.meta,
2696
+ resourceData: commonConfig.resourceData,
2697
+ shouldRender: (data) => commonConfig.config.shouldRenderActions.delete(data),
2698
+ };
2699
+ }, { ...(ngDevMode ? { debugName: "deleteButtonContext" } : {}) });
2700
+ inlineRpcConfigs = computed(() => {
2701
+ const config = this.config();
2702
+ if (config === undefined) {
2703
+ return [];
2704
+ }
2705
+ const rpcConfigs = config.rpcConfigs?.filter((rpc) => rpc.placement === 'inline');
2706
+ return rpcConfigs?.map((config) => {
2707
+ return {
2708
+ config,
2709
+ context: {
2710
+ actionType: 'rpc',
2711
+ shouldRender: config.shouldRender,
2712
+ resourceData: this.relatedData(),
2713
+ },
2714
+ };
2715
+ });
2716
+ }, { ...(ngDevMode ? { debugName: "inlineRpcConfigs" } : {}) });
2717
+ endRpcConfigs = computed(() => {
2718
+ const config = this.config();
2719
+ if (config === undefined) {
2720
+ return [];
2721
+ }
2722
+ const rpcConfigs = config.rpcConfigs?.filter((rpc) => rpc.placement === 'end');
2723
+ return rpcConfigs?.map((config) => {
2724
+ return {
2725
+ config,
2726
+ context: {
2727
+ actionType: 'rpc',
2728
+ shouldRender: config.shouldRender,
2729
+ resourceData: this.relatedData(),
2730
+ },
2731
+ };
2732
+ });
2733
+ }, { ...(ngDevMode ? { debugName: "endRpcConfigs" } : {}) });
2734
+ useExpansionPanel = computed(() => {
2735
+ const config = this.config();
2736
+ // TODO: Investigate why we're doing this check, it's weird because viewType is not part of the input configs
2737
+ if (config && 'viewType' in config && config.viewType !== 'View') {
2738
+ return false;
2739
+ }
2740
+ return config.collapsible || !this.compoundResourceView();
2741
+ }, { ...(ngDevMode ? { debugName: "useExpansionPanel" } : {}) });
2742
+ resourceConfigRouteSnapshot = computed(() => {
2743
+ return (this.config() ||
2744
+ this.activatedRoute.snapshot.data['config']?.parentConfig ||
2745
+ {});
2746
+ }, { ...(ngDevMode ? { debugName: "resourceConfigRouteSnapshot" } : {}) });
2747
+ emptyFormGroup = new FormGroup({});
2748
+ form = computed(() => {
2749
+ const meta = this.store.resourceMeta();
2750
+ const resourceData = this.store.viewData();
2751
+ const config = this.config();
2752
+ if (meta === undefined || resourceData === undefined || config === undefined) {
2753
+ return this.emptyFormGroup;
2754
+ }
2755
+ const fg = new FormGroup({});
2756
+ const pristineValues = {};
2757
+ const allElements = flattenElements(config.elements);
2758
+ for (const field of meta.fields || []) {
2759
+ const isFieldNameRegistered = fg.get(field.fieldName) !== null;
2760
+ if (isFieldNameRegistered) {
2761
+ continue;
2762
+ }
2763
+ const findConfig = allElements.find((element) => element.type === 'field' && element.name === field.fieldName);
2764
+ const fieldConfig = findConfig;
2765
+ if (!fieldConfig) {
2766
+ continue;
2767
+ }
2768
+ let value = null;
2769
+ const stringValue = resourceData?.[field.fieldName] ? String(resourceData[field.fieldName]) : '';
2770
+ if (field.displayType === 'civildate' && stringValue) {
2771
+ value = civildateCoercion(stringValue);
2772
+ }
2773
+ else if (resourceData[field.fieldName] !== undefined) {
2774
+ value = resourceData[field.fieldName];
2775
+ }
2776
+ const control = new FormControl(value);
2777
+ if (fieldConfig.validators.length > 0) {
2778
+ control.setValidators(fieldConfig.validators);
2779
+ }
2780
+ fg.addControl(field.fieldName, control);
2781
+ pristineValues[field.fieldName] = value;
2782
+ }
2783
+ this.pristineFormValues = pristineValues;
2784
+ return fg;
2785
+ }, { ...(ngDevMode ? { debugName: "form" } : {}) });
2786
+ pristineFormValues = {};
2787
+ route = computed(() => {
2788
+ const meta = this.store.resourceMeta();
2789
+ if (!meta)
2790
+ return '';
2791
+ return meta.consolidatedRoute || meta.route;
2792
+ }, { ...(ngDevMode ? { debugName: "route" } : {}) });
2793
+ primaryKeys = computed(() => {
2794
+ const meta = this.store.resourceMeta();
2795
+ if (!meta)
2796
+ return [];
2797
+ return meta.fields
2798
+ .filter((field) => field.primaryKey)
2799
+ .sort((a, b) => a.primaryKey.ordinalPosition - b.primaryKey.ordinalPosition);
2800
+ }, { ...(ngDevMode ? { debugName: "primaryKeys" } : {}) });
2801
+ primaryKeyPath = computed(() => {
2802
+ const meta = this.store.resourceMeta();
2803
+ const isConsolidated = !!meta.consolidatedRoute;
2804
+ let resourceIdentifier = '';
2805
+ if (isConsolidated) {
2806
+ resourceIdentifier = '/' + String(meta.route);
2807
+ }
2808
+ return (resourceIdentifier +
2809
+ '/' +
2810
+ this.primaryKeys()
2811
+ .map((field) => this.store.viewData()[field.fieldName])
2812
+ .join('/'));
2813
+ }, { ...(ngDevMode ? { debugName: "primaryKeyPath" } : {}) });
2814
+ ngOnInit() {
2815
+ if (this.resourceMeta(this.config().primaryResource)) {
2816
+ this.store.resourceName.set(this.config().primaryResource);
2817
+ this.store.resourceMeta.set(this.resourceMeta(this.config().primaryResource));
2818
+ }
2819
+ this.inlineRpcConfigs();
2820
+ this.endRpcConfigs();
2821
+ }
2822
+ setEditMode(mode) {
2823
+ if (mode === 'view') {
2824
+ const changeData = sparseFormData(this.form(), this.pristineFormValues);
2825
+ if (Object.keys(changeData).length !== 0) {
2826
+ this.notifications.addGlobalNotification({
2827
+ message: 'You have unsaved changes.',
2828
+ link: '',
2829
+ level: AlertLevel.ERROR,
2830
+ });
2831
+ return;
2832
+ }
2833
+ this.editMode.set('view');
2834
+ }
2835
+ else if (mode === 'edit') {
2836
+ this.editMode.set('edit');
2837
+ }
2838
+ }
2839
+ saveForm() {
2840
+ if (!this.form().valid) {
2841
+ this.displayFormInvalidMessage.set(true);
2842
+ this.form().markAllAsTouched();
2843
+ return;
2844
+ }
2845
+ this.displayFormInvalidMessage.set(false);
2846
+ const resourceMeta = this.store.resourceMeta();
2847
+ if (!resourceMeta) {
2848
+ return;
2849
+ }
2850
+ const sparseData = sparseFormData(this.form(), this.pristineFormValues);
2851
+ const coercedSparseData = metadataTypeCoercion(sparseData, resourceMeta);
2852
+ const updatePatch = {
2853
+ op: 'patch',
2854
+ value: coercedSparseData,
2855
+ path: this.primaryKeyPath(),
2856
+ };
2857
+ this.pristineFormValues = this.form().getRawValue();
2858
+ if (Object.keys(coercedSparseData).length === 0) {
2859
+ return;
2860
+ }
2861
+ this.store
2862
+ .makePatches([updatePatch], this.route(), this.store.resourceName())
2863
+ .pipe(tap(() => {
2864
+ this.form().markAsPristine();
2865
+ this.setEditMode('view');
2866
+ this.formState.decrementDirtyForms();
2867
+ this.store.reloadViewData();
2868
+ }))
2869
+ .subscribe();
2870
+ }
2871
+ resetForm() {
2872
+ this.form().reset();
2873
+ this.form().patchValue(this.pristineFormValues);
2874
+ this.setEditMode('view');
2875
+ if (this.form().dirty) {
2876
+ this.formState.decrementDirtyForms();
2877
+ }
2878
+ }
2879
+ confirmDeleteResource() {
2880
+ const existingDialog = this.dialog.openDialogs.find((d) => d.componentInstance instanceof DeleteResourceConfirmationModalComponent);
2881
+ const dialogRef = existingDialog ?? this.dialog.open(DeleteResourceConfirmationModalComponent, { delayFocusTrap: false });
2882
+ const result = dialogRef.afterClosed().pipe(tap((value) => {
2883
+ if (value === true) {
2884
+ console.debug('Deleting resource | ', this.config().title, '|', this.relatedId());
2885
+ this.deleteResource();
2886
+ }
2887
+ }), takeUntilDestroyed(this.destroyRef));
2888
+ result.subscribe();
2889
+ }
2890
+ deleteResource() {
2891
+ const deletePatch = {
2892
+ op: 'remove',
2893
+ value: {},
2894
+ path: this.primaryKeyPath(),
2895
+ };
2896
+ this.store
2897
+ .makePatches([deletePatch], this.route(), this.store.resourceName())
2898
+ .pipe(tap(() => {
2899
+ this.deleted.emit(true);
2900
+ }), filter(() => this.compoundResourceView()), tap(() => {
2901
+ const navAfterDelete = this.navAfterDelete() !== false;
2902
+ if (navAfterDelete) {
2903
+ this.location.back();
2904
+ }
2905
+ else {
2906
+ this.showCreateForm.set(true);
2907
+ }
2908
+ }))
2909
+ .subscribe();
2910
+ }
2911
+ relatedId() {
2912
+ const uuid = this.uuid();
2913
+ const relatedData = this.relatedData();
2914
+ const config = this.config();
2915
+ if (config.parentRelation?.parentKey) {
2916
+ return String(relatedData[config.parentRelation.parentKey]);
2917
+ }
2918
+ return uuid;
2919
+ }
2920
+ constructor() {
2921
+ effect(() => {
2922
+ console.debug('USAGE | New FormGroup subscription for: ', this.config().title);
2923
+ this.form()
2924
+ .valueChanges.pipe(tap(() => {
2925
+ const dirty = this.form().dirty;
2926
+ if (dirty !== this.isDirty()) {
2927
+ this.isDirty.set(dirty);
2928
+ if (dirty) {
2929
+ this.formState.incrementDirtyForms();
2930
+ }
2931
+ else {
2932
+ this.formState.decrementDirtyForms();
2933
+ }
2934
+ }
2935
+ }), takeUntilDestroyed(this.destroyRef))
2936
+ .subscribe();
2937
+ });
2938
+ effect(() => {
2939
+ const id = this.relatedId();
2940
+ const create = this.showCreateForm();
2941
+ this.store.uuid.set(id);
2942
+ if (!create) {
2943
+ this.store.buildStoreViewData();
2944
+ }
2945
+ });
2946
+ }
2947
+ createResource() {
2948
+ this.showCreateForm.set(false);
2949
+ this.store.buildStoreViewData();
2950
+ }
2951
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2952
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: ResourceViewComponent, isStandalone: true, selector: "ccc-resource-view", inputs: { uuid: { classPropertyName: "uuid", publicName: "uuid", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, relatedData: { classPropertyName: "relatedData", publicName: "relatedData", isSignal: true, isRequired: false, transformFunction: null }, compoundResourceView: { classPropertyName: "compoundResourceView", publicName: "compoundResourceView", isSignal: true, isRequired: false, transformFunction: null }, navAfterDelete: { classPropertyName: "navAfterDelete", publicName: "navAfterDelete", isSignal: true, isRequired: false, transformFunction: null }, showCreateForm: { classPropertyName: "showCreateForm", publicName: "showCreateForm", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { deleted: "deleted", showCreateForm: "showCreateFormChange" }, providers: [ResourceStore], ngImport: i0, template: "@if (useExpansionPanel()) {\n <div class=\"resource-view\">\n <mat-expansion-panel [@.disabled]=\"true\" [disabled]=\"!config().collapsible\" [expanded]=\"!config().collapsible\">\n @if (config().title) {\n <mat-expansion-panel-header>\n <mat-panel-title>\n <h1 class=\"title\">{{ showCreateForm() ? 'Create' : '' }}{{ config().title }}</h1>\n </mat-panel-title>\n </mat-expansion-panel-header>\n } @else {\n <div class=\"no-title-exp-panel-padding\"></div>\n }\n <ng-container *ngTemplateOutlet=\"headerSlot\"></ng-container>\n @if (store.viewStatus() === 'resolved') {\n <div class=\"resource-container\" [class.form-edit-mode]=\"editMode() === 'edit'\">\n <form [formGroup]=\"form()\">\n <div class=\"resource row\">\n @if (showCreateForm()) {\n <ccc-resource-create\n [resourceConfig]=\"createConfig()\"\n [parentData]=\"relatedData()\"\n (complete)=\"createResource()\">\n </ccc-resource-create>\n } @else {\n <ng-container *ngTemplateOutlet=\"formSlot\"></ng-container>\n }\n </div>\n </form>\n <ng-container *ngTemplateOutlet=\"footerSlot\"></ng-container>\n </div>\n } @else {\n <div class=\"resource-container empty\">\n <div class=\"resource row\"></div>\n </div>\n }\n </mat-expansion-panel>\n </div>\n} @else {\n <div class=\"page-container\">\n @if (!showCreateForm()) {\n <ng-container *ngTemplateOutlet=\"headerSlot\"></ng-container>\n }\n @if (store.viewStatus() === 'resolved') {\n <div class=\"resource-container base-container-bg\" [class.form-edit-mode]=\"editMode() === 'edit'\">\n <form [formGroup]=\"form()\">\n <div class=\"resource row\">\n @if (showCreateForm()) {\n <ccc-resource-create\n [resourceConfig]=\"createConfig()\"\n [parentData]=\"relatedData()\"\n (complete)=\"createResource()\">\n </ccc-resource-create>\n } @else {\n <ng-container *ngTemplateOutlet=\"formSlot\"></ng-container>\n }\n </div>\n </form>\n </div>\n } @else {\n <div class=\"resource-container\">\n <div class=\"resource row empty\"></div>\n </div>\n }\n <ng-container *ngTemplateOutlet=\"footerSlot\"></ng-container>\n </div>\n}\n\n<ng-template #formSlot>\n @for (element of config().elements; track element) {\n @if (element.type === 'section' || element.type === 'computedDisplayField' || element.type === 'padding') {\n <ccc-resource-layout-template\n [element]=\"element\"\n [meta]=\"store.resourceMeta()\"\n [fieldClass]=\"config().fieldClass\"\n [editMode]=\"editMode()\"\n [relatedData]=\"relatedData()\"\n [form]=\"form()\">\n </ccc-resource-layout-template>\n } @else {\n <ccc-resource-layout-template\n [element]=\"element\"\n [meta]=\"store.resourceMeta()\"\n [fieldClass]=\"config().fieldClass\"\n [pristineValue]=\"pristineFormValues[element.name]\"\n [editMode]=\"editMode()\"\n [relatedData]=\"relatedData()\"\n [form]=\"form()\">\n </ccc-resource-layout-template>\n }\n }\n</ng-template>\n\n<ng-template #headerSlot>\n <div class=\"header-container\">\n <div class=\"state-buttons\">\n @if (store.viewStatus() === 'resolved') {\n @for (rpc of inlineRpcConfigs(); track rpc.context) {\n <action-access-control-wrapper [actionContext]=\"rpc.context\">\n <ccc-rpc-button\n [rpcConfig]=\"rpc.config\"\n [relatedData]=\"relatedData()\"\n [primaryResource]=\"config().primaryResource\">\n </ccc-rpc-button>\n </action-access-control-wrapper>\n }\n @if (editMode() === 'view') {\n <action-access-control-wrapper [actionContext]=\"editButtonContext()\">\n <div class=\"edit-button\">\n <button mat-raised-button color=\"accent\" (click)=\"setEditMode('edit')\">\n <div class=\"button-text\">\n <mat-icon>edit</mat-icon>\n <span class=\"mode-text\"> Edit </span>\n </div>\n </button>\n </div>\n </action-access-control-wrapper>\n\n <action-access-control-wrapper [actionContext]=\"deleteButtonContext()\">\n <button mat-raised-button color=\"primary\" (click)=\"confirmDeleteResource()\">Delete</button>\n </action-access-control-wrapper>\n }\n\n @if (editMode() === 'edit') {\n @if (form().dirty) {\n <div class=\"unsaved message\">You have unsaved changes!</div>\n }\n @if (!form().valid && displayFormInvalidMessage()) {\n <div class=\"invalid message\">Please complete or fix required fields.</div>\n }\n <button mat-raised-button color=\"primary\" (click)=\"resetForm()\">Cancel</button>\n <button mat-raised-button color=\"accent\" (click)=\"saveForm()\" [disabled]=\"form().pristine\">Save</button>\n }\n\n @if (showCloseButton()) {\n <div class=\"close-button\">\n <button mat-icon-button color=\"warn\" (click)=\"location.back()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n }\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #footerSlot>\n @for (rpc of endRpcConfigs(); track rpc) {\n <action-access-control-wrapper [actionContext]=\"rpc.context\">\n <ccc-rpc-button\n [rpcConfig]=\"rpc.config\"\n [relatedData]=\"relatedData()\"\n [primaryResource]=\"config().primaryResource\">\n </ccc-rpc-button>\n </action-access-control-wrapper>\n }\n</ng-template>\n", styles: [".container{border:1px solid #8e8e8e;border-radius:5px;position:relative;margin-bottom:20px;padding-top:10px}.label{position:absolute;top:-15px;left:20px;height:20px;background-color:#fff;margin:0 10px;padding:0 10px;text-align:center;z-index:10;font-weight:500;font:18px Roboto,sans-serif}.padding{width:100px}.page-container{position:relative}.header-container{display:flex;flex-direction:row;top:0;z-index:11;background:transparent}.title{display:flex;align-items:center;flex-shrink:0}.message{margin-top:auto;margin-bottom:auto}.state-buttons{display:flex;flex-direction:row;margin-top:auto;margin-bottom:auto;justify-content:right;flex-grow:1;gap:20px;margin-right:20px;min-height:36px}.action-button,.mode-text{margin-top:auto;margin-bottom:auto}.resource{margin:10px}.section h2{font-size:1.5em;border-bottom:2px solid #ccc;padding-bottom:8px;margin-bottom:100px}.section{margin-top:15px}.divider{margin:16px 0}.section-elements{display:flex;flex-wrap:wrap}.mat-input-element:disabled[readonly]{color:currentColor}.unsaved{color:#f44336}.edit-button{margin-top:auto;margin-bottom:auto;padding-left:10px}.button-text{display:flex;flex-direction:row;align-items:center}.button-text .edit{color:#003b49}.button-text .mat-icon{margin-right:8px}.shaded{background-color:#e9e9e9}.rounded{border-radius:8px}.dotted-outline{border:1px dotted #ccc;padding:8px}.margin-top{margin-top:50px}.resource-view{display:flex;flex-direction:column;min-height:36px;padding:10px 0 0}.no-title-exp-panel-padding{padding:10px}.base-container-bg{background-color:var(--app-background-color)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: ResourceLayoutComponent, selector: "ccc-resource-layout-template", inputs: ["element", "meta", "fieldClass", "editMode", "form", "formDataState", "pristineValue", "relatedData", "parentClass", "layoutNestingDepth"], outputs: ["formDataStateChange"] }, { kind: "component", type: MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "component", type: RpcButtonComponent, selector: "ccc-rpc-button", inputs: ["relatedData", "rpcConfig", "primaryResource", "dependentResources"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ActionAccessControlWrapperComponent, selector: "action-access-control-wrapper", inputs: ["actionContext"] }, { kind: "component", type: ResourceCreateComponent, selector: "ccc-resource-create", inputs: ["resourceConfig", "parentData", "loadCreatedResource"], outputs: ["complete"] }] });
2953
+ }
2954
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: ResourceViewComponent, decorators: [{
2955
+ type: Component,
2956
+ args: [{ selector: 'ccc-resource-view', imports: [
2957
+ FormsModule,
2958
+ ReactiveFormsModule,
2959
+ MatInputModule,
2960
+ RouterModule,
2961
+ MatButtonModule,
2962
+ MatIconModule,
2963
+ MatProgressSpinnerModule,
2964
+ MatDividerModule,
2965
+ ResourceLayoutComponent,
2966
+ MatExpansionPanel,
2967
+ MatExpansionPanelHeader,
2968
+ MatExpansionPanelTitle,
2969
+ RpcButtonComponent,
2970
+ CommonModule,
2971
+ ActionAccessControlWrapperComponent,
2972
+ ResourceCreateComponent,
2973
+ ], providers: [ResourceStore], template: "@if (useExpansionPanel()) {\n <div class=\"resource-view\">\n <mat-expansion-panel [@.disabled]=\"true\" [disabled]=\"!config().collapsible\" [expanded]=\"!config().collapsible\">\n @if (config().title) {\n <mat-expansion-panel-header>\n <mat-panel-title>\n <h1 class=\"title\">{{ showCreateForm() ? 'Create' : '' }}{{ config().title }}</h1>\n </mat-panel-title>\n </mat-expansion-panel-header>\n } @else {\n <div class=\"no-title-exp-panel-padding\"></div>\n }\n <ng-container *ngTemplateOutlet=\"headerSlot\"></ng-container>\n @if (store.viewStatus() === 'resolved') {\n <div class=\"resource-container\" [class.form-edit-mode]=\"editMode() === 'edit'\">\n <form [formGroup]=\"form()\">\n <div class=\"resource row\">\n @if (showCreateForm()) {\n <ccc-resource-create\n [resourceConfig]=\"createConfig()\"\n [parentData]=\"relatedData()\"\n (complete)=\"createResource()\">\n </ccc-resource-create>\n } @else {\n <ng-container *ngTemplateOutlet=\"formSlot\"></ng-container>\n }\n </div>\n </form>\n <ng-container *ngTemplateOutlet=\"footerSlot\"></ng-container>\n </div>\n } @else {\n <div class=\"resource-container empty\">\n <div class=\"resource row\"></div>\n </div>\n }\n </mat-expansion-panel>\n </div>\n} @else {\n <div class=\"page-container\">\n @if (!showCreateForm()) {\n <ng-container *ngTemplateOutlet=\"headerSlot\"></ng-container>\n }\n @if (store.viewStatus() === 'resolved') {\n <div class=\"resource-container base-container-bg\" [class.form-edit-mode]=\"editMode() === 'edit'\">\n <form [formGroup]=\"form()\">\n <div class=\"resource row\">\n @if (showCreateForm()) {\n <ccc-resource-create\n [resourceConfig]=\"createConfig()\"\n [parentData]=\"relatedData()\"\n (complete)=\"createResource()\">\n </ccc-resource-create>\n } @else {\n <ng-container *ngTemplateOutlet=\"formSlot\"></ng-container>\n }\n </div>\n </form>\n </div>\n } @else {\n <div class=\"resource-container\">\n <div class=\"resource row empty\"></div>\n </div>\n }\n <ng-container *ngTemplateOutlet=\"footerSlot\"></ng-container>\n </div>\n}\n\n<ng-template #formSlot>\n @for (element of config().elements; track element) {\n @if (element.type === 'section' || element.type === 'computedDisplayField' || element.type === 'padding') {\n <ccc-resource-layout-template\n [element]=\"element\"\n [meta]=\"store.resourceMeta()\"\n [fieldClass]=\"config().fieldClass\"\n [editMode]=\"editMode()\"\n [relatedData]=\"relatedData()\"\n [form]=\"form()\">\n </ccc-resource-layout-template>\n } @else {\n <ccc-resource-layout-template\n [element]=\"element\"\n [meta]=\"store.resourceMeta()\"\n [fieldClass]=\"config().fieldClass\"\n [pristineValue]=\"pristineFormValues[element.name]\"\n [editMode]=\"editMode()\"\n [relatedData]=\"relatedData()\"\n [form]=\"form()\">\n </ccc-resource-layout-template>\n }\n }\n</ng-template>\n\n<ng-template #headerSlot>\n <div class=\"header-container\">\n <div class=\"state-buttons\">\n @if (store.viewStatus() === 'resolved') {\n @for (rpc of inlineRpcConfigs(); track rpc.context) {\n <action-access-control-wrapper [actionContext]=\"rpc.context\">\n <ccc-rpc-button\n [rpcConfig]=\"rpc.config\"\n [relatedData]=\"relatedData()\"\n [primaryResource]=\"config().primaryResource\">\n </ccc-rpc-button>\n </action-access-control-wrapper>\n }\n @if (editMode() === 'view') {\n <action-access-control-wrapper [actionContext]=\"editButtonContext()\">\n <div class=\"edit-button\">\n <button mat-raised-button color=\"accent\" (click)=\"setEditMode('edit')\">\n <div class=\"button-text\">\n <mat-icon>edit</mat-icon>\n <span class=\"mode-text\"> Edit </span>\n </div>\n </button>\n </div>\n </action-access-control-wrapper>\n\n <action-access-control-wrapper [actionContext]=\"deleteButtonContext()\">\n <button mat-raised-button color=\"primary\" (click)=\"confirmDeleteResource()\">Delete</button>\n </action-access-control-wrapper>\n }\n\n @if (editMode() === 'edit') {\n @if (form().dirty) {\n <div class=\"unsaved message\">You have unsaved changes!</div>\n }\n @if (!form().valid && displayFormInvalidMessage()) {\n <div class=\"invalid message\">Please complete or fix required fields.</div>\n }\n <button mat-raised-button color=\"primary\" (click)=\"resetForm()\">Cancel</button>\n <button mat-raised-button color=\"accent\" (click)=\"saveForm()\" [disabled]=\"form().pristine\">Save</button>\n }\n\n @if (showCloseButton()) {\n <div class=\"close-button\">\n <button mat-icon-button color=\"warn\" (click)=\"location.back()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n }\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #footerSlot>\n @for (rpc of endRpcConfigs(); track rpc) {\n <action-access-control-wrapper [actionContext]=\"rpc.context\">\n <ccc-rpc-button\n [rpcConfig]=\"rpc.config\"\n [relatedData]=\"relatedData()\"\n [primaryResource]=\"config().primaryResource\">\n </ccc-rpc-button>\n </action-access-control-wrapper>\n }\n</ng-template>\n", styles: [".container{border:1px solid #8e8e8e;border-radius:5px;position:relative;margin-bottom:20px;padding-top:10px}.label{position:absolute;top:-15px;left:20px;height:20px;background-color:#fff;margin:0 10px;padding:0 10px;text-align:center;z-index:10;font-weight:500;font:18px Roboto,sans-serif}.padding{width:100px}.page-container{position:relative}.header-container{display:flex;flex-direction:row;top:0;z-index:11;background:transparent}.title{display:flex;align-items:center;flex-shrink:0}.message{margin-top:auto;margin-bottom:auto}.state-buttons{display:flex;flex-direction:row;margin-top:auto;margin-bottom:auto;justify-content:right;flex-grow:1;gap:20px;margin-right:20px;min-height:36px}.action-button,.mode-text{margin-top:auto;margin-bottom:auto}.resource{margin:10px}.section h2{font-size:1.5em;border-bottom:2px solid #ccc;padding-bottom:8px;margin-bottom:100px}.section{margin-top:15px}.divider{margin:16px 0}.section-elements{display:flex;flex-wrap:wrap}.mat-input-element:disabled[readonly]{color:currentColor}.unsaved{color:#f44336}.edit-button{margin-top:auto;margin-bottom:auto;padding-left:10px}.button-text{display:flex;flex-direction:row;align-items:center}.button-text .edit{color:#003b49}.button-text .mat-icon{margin-right:8px}.shaded{background-color:#e9e9e9}.rounded{border-radius:8px}.dotted-outline{border:1px dotted #ccc;padding:8px}.margin-top{margin-top:50px}.resource-view{display:flex;flex-direction:column;min-height:36px;padding:10px 0 0}.no-title-exp-panel-padding{padding:10px}.base-container-bg{background-color:var(--app-background-color)}\n"] }]
2974
+ }], ctorParameters: () => [], propDecorators: { uuid: [{ type: i0.Input, args: [{ isSignal: true, alias: "uuid", required: true }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], relatedData: [{ type: i0.Input, args: [{ isSignal: true, alias: "relatedData", required: false }] }], compoundResourceView: [{ type: i0.Input, args: [{ isSignal: true, alias: "compoundResourceView", required: false }] }], deleted: [{ type: i0.Output, args: ["deleted"] }], navAfterDelete: [{ type: i0.Input, args: [{ isSignal: true, alias: "navAfterDelete", required: false }] }], showCreateForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCreateForm", required: false }] }, { type: i0.Output, args: ["showCreateFormChange"] }] } });
2975
+
2976
+ class CompoundResourceComponent {
2977
+ location = inject(Location);
2978
+ route = inject(ActivatedRoute);
2979
+ store = inject(ResourceStore);
2980
+ injector = inject(Injector);
2981
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2982
+ componentRef = CompoundResourceComponent;
2983
+ resourceMeta = inject(RESOURCE_META);
2984
+ resourceConfig = input(undefined, { ...(ngDevMode ? { debugName: "resourceConfig" } : {}) });
2985
+ isArrayChild = input(false, { ...(ngDevMode ? { debugName: "isArrayChild" } : {}) });
2986
+ uuid = input.required({ ...(ngDevMode ? { debugName: "uuid" } : {}) });
2987
+ parentData = input(undefined, { ...(ngDevMode ? { debugName: "parentData" } : {}) });
2988
+ rootConfig = computed(() => this.route.snapshot.data['config'], { ...(ngDevMode ? { debugName: "rootConfig" } : {}) });
2989
+ emptyOneToOne = signal(false, { ...(ngDevMode ? { debugName: "emptyOneToOne" } : {}) });
2990
+ missingRoot = input(false, { ...(ngDevMode ? { debugName: "missingRoot" } : {}) });
2991
+ resourceCreate = output();
2992
+ deleted = output();
2993
+ navAfterDelete = input(true, { ...(ngDevMode ? { debugName: "navAfterDelete" } : {}) });
2994
+ navAfterDeleteConsideringRoot = computed(() => {
2995
+ const navAfterDeleteInput = this.navAfterDelete();
2996
+ if (navAfterDeleteInput === undefined)
2997
+ return true;
2998
+ return false;
2999
+ }, { ...(ngDevMode ? { debugName: "navAfterDeleteConsideringRoot" } : {}) });
3000
+ hasElements = computed(() => {
3001
+ const config = this.primaryConfig();
3002
+ return config && (config.type === 'ListView' || config.type === 'View') && config.elements.length > 0;
3003
+ }, { ...(ngDevMode ? { debugName: "hasElements" } : {}) });
3004
+ primaryConfigParentId = computed(() => {
3005
+ const config = this.primaryConfig();
3006
+ const data = this.resolvedData();
3007
+ if (config.type === 'View' || config.type === 'ListView') {
3008
+ const parentKey = config.parentRelation?.parentKey;
3009
+ if (parentKey !== '') {
3010
+ return String(data[parentKey]);
3011
+ }
3012
+ }
3013
+ return this.uuid();
3014
+ }, { ...(ngDevMode ? { debugName: "primaryConfigParentId" } : {}) });
3015
+ primaryConfig = computed(() => {
3016
+ const config = this.resourceConfig();
3017
+ if (config) {
3018
+ return config;
3019
+ }
3020
+ return this.rootConfig().parentConfig;
3021
+ }, { ...(ngDevMode ? { debugName: "primaryConfig" } : {}) });
3022
+ title = computed(() => {
3023
+ if (this.isArrayChild()) {
3024
+ return '';
3025
+ }
3026
+ const config = this.primaryConfig();
3027
+ if (config.type !== 'Component') {
3028
+ return config.title;
3029
+ }
3030
+ return '';
3031
+ }, { ...(ngDevMode ? { debugName: "title" } : {}) });
3032
+ isRootConfig = computed(() => {
3033
+ return this.resourceConfig() === undefined;
3034
+ }, { ...(ngDevMode ? { debugName: "isRootConfig" } : {}) });
3035
+ configs = computed(() => {
3036
+ if (this.hasElements() && this.emptyOneToOne()) {
3037
+ return [];
3038
+ }
3039
+ if (this.isRootConfig()) {
3040
+ return this.rootConfig().relatedConfigs;
3041
+ }
3042
+ const config = this.primaryConfig();
3043
+ if (config.type === 'ListView' || config.type === 'View') {
3044
+ return config.relatedConfigs;
3045
+ }
3046
+ return [];
3047
+ }, { ...(ngDevMode ? { debugName: "configs" } : {}) });
3048
+ rpcConfigs = computed(() => {
3049
+ const isRootConfig = this.isRootConfig();
3050
+ const rootConfig = this.rootConfig();
3051
+ if (isRootConfig === undefined || rootConfig === undefined) {
3052
+ return [];
3053
+ }
3054
+ if (!isRootConfig || rootConfig.rpcConfigs === undefined) {
3055
+ return [];
3056
+ }
3057
+ return rootConfig.rpcConfigs?.map((config) => ({
3058
+ config,
3059
+ context: {
3060
+ actionType: 'rpc',
3061
+ shouldRender: config.shouldRender,
3062
+ resourceData: this.resolvedData(),
3063
+ },
3064
+ }));
3065
+ }, { ...(ngDevMode ? { debugName: "rpcConfigs" } : {}) });
3066
+ hasRpcConfigs = computed(() => !!this.rpcConfigs() && this.rpcConfigs().length > 0, { ...(ngDevMode ? { debugName: "hasRpcConfigs" } : {}) });
3067
+ resolvedData = computed(() => {
3068
+ if (Object.keys(this.store.viewData()).length > 0) {
3069
+ return this.store.viewData();
3070
+ }
3071
+ return this.parentData() || {};
3072
+ }, { ...(ngDevMode ? { debugName: "resolvedData" } : {}) });
3073
+ ngOnInit() {
3074
+ const resource = this.primaryConfig().primaryResource;
3075
+ const meta = this.resourceMeta(resource);
3076
+ if (meta) {
3077
+ this.store.resourceName.set(resource);
3078
+ this.store.resourceMeta.set(meta);
3079
+ }
3080
+ }
3081
+ constructor() {
3082
+ effect(() => {
3083
+ this.store.uuid.set(this.primaryConfigParentId());
3084
+ const c = this.primaryConfig();
3085
+ if (this.missingRoot()) {
3086
+ return;
3087
+ }
3088
+ if (c.type === 'View') {
3089
+ this.store.buildStoreViewData();
3090
+ }
3091
+ else if (c.type === 'ListView') {
3092
+ this.store.buildStoreListData();
3093
+ this.store.buildStoreViewData();
3094
+ }
3095
+ });
3096
+ }
3097
+ goBack() {
3098
+ this.location.back();
3099
+ }
3100
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: CompoundResourceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3101
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: CompoundResourceComponent, isStandalone: true, selector: "compound-resource", inputs: { resourceConfig: { classPropertyName: "resourceConfig", publicName: "resourceConfig", isSignal: true, isRequired: false, transformFunction: null }, isArrayChild: { classPropertyName: "isArrayChild", publicName: "isArrayChild", isSignal: true, isRequired: false, transformFunction: null }, uuid: { classPropertyName: "uuid", publicName: "uuid", isSignal: true, isRequired: true, transformFunction: null }, parentData: { classPropertyName: "parentData", publicName: "parentData", isSignal: true, isRequired: false, transformFunction: null }, missingRoot: { classPropertyName: "missingRoot", publicName: "missingRoot", isSignal: true, isRequired: false, transformFunction: null }, navAfterDelete: { classPropertyName: "navAfterDelete", publicName: "navAfterDelete", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { resourceCreate: "resourceCreate", deleted: "deleted" }, providers: [ResourceStore], ngImport: i0, template: "<div class=\"page-container\">\n <div class=\"header-container\">\n <div class=\"title\" [class.title-without-back]=\"title()\">\n @if (title() !== '') {\n <h1>{{ title() }}</h1>\n }\n </div>\n </div>\n\n @if (hasElements() || configs().length > 0) {\n <div class=\"resource-container\" [class.empty-elements]=\"!hasElements()\">\n @let primaryConfigRef = primaryConfig();\n @if (primaryConfigRef.type === 'View' || primaryConfigRef.type === 'ListView') {\n <ccc-resource-view\n [uuid]=\"uuid()\"\n [config]=\"primaryConfigRef\"\n [relatedData]=\"resolvedData()\"\n [showCreateForm]=\"missingRoot()\"\n [compoundResourceView]=\"true\"\n [navAfterDelete]=\"navAfterDeleteConsideringRoot()\"\n (createChange)=\"resourceCreate.emit()\"\n (deleted)=\"deleted.emit(true)\">\n </ccc-resource-view>\n }\n\n @for (config of configs(); track config) {\n @let hasrelatedConfigs = 'relatedConfigs' in config && config.relatedConfigs.length > 0;\n @if (hasrelatedConfigs) {\n <compound-resource [uuid]=\"primaryConfigParentId()\" [parentData]=\"resolvedData()\" [resourceConfig]=\"config\">\n </compound-resource>\n } @else if (config.type === 'ListView') {\n <ccc-resource-list-create\n [resourceConfig]=\"config\"\n [parentData]=\"resolvedData()\"\n [isRootList]=\"false\"\n [parentId]=\"primaryConfigParentId()\"\n [compoundResourceComponent]=\"componentRef\">\n </ccc-resource-list-create>\n } @else if (config.type === 'Array') {\n <ccc-resource-array-view\n [resourceConfig]=\"config\"\n [parentData]=\"store.viewData()\"\n [compoundResourceComponent]=\"componentRef\">\n </ccc-resource-array-view>\n } @else if (config.type === 'View') {\n @let relatedId = resolvedData()[config.parentRelation.parentKey] || primaryConfigParentId();\n @if (relatedId) {\n <ccc-resource-view\n [navAfterDelete]=\"navAfterDeleteConsideringRoot()\"\n [uuid]=\"relatedId + ''\"\n [config]=\"config\"\n [relatedData]=\"resolvedData()\"></ccc-resource-view>\n }\n } @else if (config.type === 'Component') {\n <ccc-resource-resolver\n [parentData]=\"resolvedData()\"\n [resourceConfig]=\"config\"\n [compoundResourceComponent]=\"componentRef\">\n </ccc-resource-resolver>\n }\n }\n @if (hasRpcConfigs()) {\n <div class=\"rpc-buttons-container\">\n @for (rpc of rpcConfigs(); track rpc.config.label) {\n <action-access-control-wrapper [actionContext]=\"rpc.context\">\n <ccc-rpc-button\n [rpcConfig]=\"rpc.config\"\n [relatedData]=\"resolvedData()\"\n [primaryResource]=\"primaryConfig().primaryResource\">\n </ccc-rpc-button>\n </action-access-control-wrapper>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [".page-container{position:relative}.header-container{display:flex;flex-direction:row;position:absolute;top:0;z-index:11;background:transparent}.title{display:flex;align-items:center;flex-shrink:0}.title-without-back{margin-left:48px;height:48px}.message{margin-top:auto;margin-bottom:auto}.state-buttons{display:flex;flex-direction:row;margin-bottom:auto;justify-content:right;flex-grow:1;gap:20px;margin-right:32px}.mode-text{margin-top:auto;margin-bottom:auto}.resource{margin:10px}.mat-input-element:disabled[readonly]{color:currentColor}.unsaved{color:#f44336}.edit-button{margin-top:auto;margin-bottom:auto;padding-left:10px}.button-text{display:flex;flex-direction:row;align-items:center}.button-text .edit{color:#003b49}.button-text .mat-icon{margin-right:8px}.empty-elements{padding-top:30px}.rpc-buttons-container{display:flex;flex-direction:row;justify-content:flex-end;padding-bottom:10px}\n"], dependencies: [{ kind: "component", type: CompoundResourceComponent, selector: "compound-resource", inputs: ["resourceConfig", "isArrayChild", "uuid", "parentData", "missingRoot", "navAfterDelete"], outputs: ["resourceCreate", "deleted"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: RpcButtonComponent, selector: "ccc-rpc-button", inputs: ["relatedData", "rpcConfig", "primaryResource", "dependentResources"] }, { kind: "component", type: ResourceViewComponent, selector: "ccc-resource-view", inputs: ["uuid", "config", "relatedData", "compoundResourceView", "navAfterDelete", "showCreateForm"], outputs: ["deleted", "showCreateFormChange"] }, { kind: "component", type: ResourceListCreateComponent, selector: "ccc-resource-list-create", inputs: ["compoundResourceComponent", "parentId", "parentData", "searchParams", "resourceConfig", "isRootList"] }, { kind: "component", type: ResourceArrayViewComponent, selector: "ccc-resource-array-view", inputs: ["resourceConfig", "parentData", "compoundResourceComponent"], outputs: ["emptyOneToOne"] }, { kind: "component", type: ResourceResolverComponent, selector: "ccc-resource-resolver", inputs: ["compoundResourceComponent", "resourceConfig", "parentData"] }, { kind: "component", type: ActionAccessControlWrapperComponent, selector: "action-access-control-wrapper", inputs: ["actionContext"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3102
+ }
3103
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: CompoundResourceComponent, decorators: [{
3104
+ type: Component,
3105
+ args: [{ selector: 'compound-resource', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
3106
+ MatIconModule,
3107
+ RouterModule,
3108
+ MatButtonModule,
3109
+ RpcButtonComponent,
3110
+ ResourceViewComponent,
3111
+ ResourceListCreateComponent,
3112
+ ResourceArrayViewComponent,
3113
+ ResourceResolverComponent,
3114
+ ActionAccessControlWrapperComponent,
3115
+ RouterModule,
3116
+ ], providers: [ResourceStore], template: "<div class=\"page-container\">\n <div class=\"header-container\">\n <div class=\"title\" [class.title-without-back]=\"title()\">\n @if (title() !== '') {\n <h1>{{ title() }}</h1>\n }\n </div>\n </div>\n\n @if (hasElements() || configs().length > 0) {\n <div class=\"resource-container\" [class.empty-elements]=\"!hasElements()\">\n @let primaryConfigRef = primaryConfig();\n @if (primaryConfigRef.type === 'View' || primaryConfigRef.type === 'ListView') {\n <ccc-resource-view\n [uuid]=\"uuid()\"\n [config]=\"primaryConfigRef\"\n [relatedData]=\"resolvedData()\"\n [showCreateForm]=\"missingRoot()\"\n [compoundResourceView]=\"true\"\n [navAfterDelete]=\"navAfterDeleteConsideringRoot()\"\n (createChange)=\"resourceCreate.emit()\"\n (deleted)=\"deleted.emit(true)\">\n </ccc-resource-view>\n }\n\n @for (config of configs(); track config) {\n @let hasrelatedConfigs = 'relatedConfigs' in config && config.relatedConfigs.length > 0;\n @if (hasrelatedConfigs) {\n <compound-resource [uuid]=\"primaryConfigParentId()\" [parentData]=\"resolvedData()\" [resourceConfig]=\"config\">\n </compound-resource>\n } @else if (config.type === 'ListView') {\n <ccc-resource-list-create\n [resourceConfig]=\"config\"\n [parentData]=\"resolvedData()\"\n [isRootList]=\"false\"\n [parentId]=\"primaryConfigParentId()\"\n [compoundResourceComponent]=\"componentRef\">\n </ccc-resource-list-create>\n } @else if (config.type === 'Array') {\n <ccc-resource-array-view\n [resourceConfig]=\"config\"\n [parentData]=\"store.viewData()\"\n [compoundResourceComponent]=\"componentRef\">\n </ccc-resource-array-view>\n } @else if (config.type === 'View') {\n @let relatedId = resolvedData()[config.parentRelation.parentKey] || primaryConfigParentId();\n @if (relatedId) {\n <ccc-resource-view\n [navAfterDelete]=\"navAfterDeleteConsideringRoot()\"\n [uuid]=\"relatedId + ''\"\n [config]=\"config\"\n [relatedData]=\"resolvedData()\"></ccc-resource-view>\n }\n } @else if (config.type === 'Component') {\n <ccc-resource-resolver\n [parentData]=\"resolvedData()\"\n [resourceConfig]=\"config\"\n [compoundResourceComponent]=\"componentRef\">\n </ccc-resource-resolver>\n }\n }\n @if (hasRpcConfigs()) {\n <div class=\"rpc-buttons-container\">\n @for (rpc of rpcConfigs(); track rpc.config.label) {\n <action-access-control-wrapper [actionContext]=\"rpc.context\">\n <ccc-rpc-button\n [rpcConfig]=\"rpc.config\"\n [relatedData]=\"resolvedData()\"\n [primaryResource]=\"primaryConfig().primaryResource\">\n </ccc-rpc-button>\n </action-access-control-wrapper>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [".page-container{position:relative}.header-container{display:flex;flex-direction:row;position:absolute;top:0;z-index:11;background:transparent}.title{display:flex;align-items:center;flex-shrink:0}.title-without-back{margin-left:48px;height:48px}.message{margin-top:auto;margin-bottom:auto}.state-buttons{display:flex;flex-direction:row;margin-bottom:auto;justify-content:right;flex-grow:1;gap:20px;margin-right:32px}.mode-text{margin-top:auto;margin-bottom:auto}.resource{margin:10px}.mat-input-element:disabled[readonly]{color:currentColor}.unsaved{color:#f44336}.edit-button{margin-top:auto;margin-bottom:auto;padding-left:10px}.button-text{display:flex;flex-direction:row;align-items:center}.button-text .edit{color:#003b49}.button-text .mat-icon{margin-right:8px}.empty-elements{padding-top:30px}.rpc-buttons-container{display:flex;flex-direction:row;justify-content:flex-end;padding-bottom:10px}\n"] }]
3117
+ }], ctorParameters: () => [], propDecorators: { resourceConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "resourceConfig", required: false }] }], isArrayChild: [{ type: i0.Input, args: [{ isSignal: true, alias: "isArrayChild", required: false }] }], uuid: [{ type: i0.Input, args: [{ isSignal: true, alias: "uuid", required: true }] }], parentData: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentData", required: false }] }], missingRoot: [{ type: i0.Input, args: [{ isSignal: true, alias: "missingRoot", required: false }] }], resourceCreate: [{ type: i0.Output, args: ["resourceCreate"] }], deleted: [{ type: i0.Output, args: ["deleted"] }], navAfterDelete: [{ type: i0.Input, args: [{ isSignal: true, alias: "navAfterDelete", required: false }] }] } });
3118
+
3119
+ var compoundResource_component = /*#__PURE__*/Object.freeze({
3120
+ __proto__: null,
3121
+ CompoundResourceComponent: CompoundResourceComponent
3122
+ });
3123
+
3124
+ /**
3125
+ * Generated bundle index. Do not edit.
3126
+ */
3127
+
3128
+ export { ActionAccessControlWrapperComponent, BaseInputComponent, BaseRPCModalComponent, CompoundResourceComponent, DeleteResourceConfirmationModalComponent, EmptyReadonlyFieldComponent, FormStateService, LeavePageConfirmationModalComponent, PaddingElementComponent, ResourceArrayViewComponent, ResourceCreateComponent, ResourceFieldComponent, ResourceLayoutComponent, ResourceListComponent, ResourceListCreateComponent, ResourceResolverComponent, ResourceStore, ResourceViewComponent, RpcButtonComponent, ValueFormatters, applyFormatting, canDeactivateGuard, civildateCoercion, concatFunctions, createFormGroup, extractFieldNames, flattenElements, formatDateString, generatedNavGroups, generatedNavItems, hyphenConcat, hyphenConcatWithoutResource, hyphenSpaceConcat, hyphenSpaceConcatWithoutResource, isUUID, maxConfigElementRecursionDepth, maxLayoutNestingDepth, metadataTypeCoercion, noSpaceConcatWithoutResource, resourceRoutes, resourceValidators, simpleSlashDateFormatter, spaceConcat, spaceConcatWithoutResource, spaceHyphenConcat, spaceHyphenConcatWithoutResource };
3129
+ //# sourceMappingURL=cccteam-ccc-lib-src-ccc-resource.mjs.map