@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.
- package/README.md +38 -13
- package/fesm2022/cccteam-ccc-lib-src-auth-authentication-guard.mjs +36 -0
- package/fesm2022/cccteam-ccc-lib-src-auth-authentication-guard.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-auth-authorization-guard.mjs +25 -0
- package/fesm2022/cccteam-ccc-lib-src-auth-authorization-guard.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-auth-forms.mjs +83 -0
- package/fesm2022/cccteam-ccc-lib-src-auth-forms.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-auth-has-permission.mjs +44 -0
- package/fesm2022/cccteam-ccc-lib-src-auth-has-permission.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-auth-service.mjs +82 -0
- package/fesm2022/cccteam-ccc-lib-src-auth-service.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-ccc-camel-case-to-title.mjs +33 -0
- package/fesm2022/cccteam-ccc-lib-src-ccc-camel-case-to-title.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-ccc-grid.mjs +256 -0
- package/fesm2022/cccteam-ccc-lib-src-ccc-grid.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-ccc-resource.mjs +3129 -0
- package/fesm2022/cccteam-ccc-lib-src-ccc-resource.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-forms.mjs +79 -0
- package/fesm2022/cccteam-ccc-lib-src-forms.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-internal-types.mjs +6 -0
- package/fesm2022/cccteam-ccc-lib-src-internal-types.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-types.mjs +431 -0
- package/fesm2022/cccteam-ccc-lib-src-types.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-alert.mjs +48 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-alert.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-core-service.mjs +41 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-core-service.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-idle-service.mjs +157 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-idle-service.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-interceptor.mjs +50 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-interceptor.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-notification-service.mjs +63 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-notification-service.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-sidenav.mjs +60 -0
- package/fesm2022/cccteam-ccc-lib-src-ui-sidenav.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib-src-util-request-options.mjs +19 -0
- package/fesm2022/cccteam-ccc-lib-src-util-request-options.mjs.map +1 -0
- package/fesm2022/cccteam-ccc-lib.mjs +4444 -0
- package/fesm2022/cccteam-ccc-lib.mjs.map +1 -0
- package/package.json +88 -5
- package/types/cccteam-ccc-lib-src-auth-authentication-guard.d.ts +6 -0
- package/types/cccteam-ccc-lib-src-auth-authorization-guard.d.ts +6 -0
- package/types/cccteam-ccc-lib-src-auth-forms.d.ts +28 -0
- package/types/cccteam-ccc-lib-src-auth-has-permission.d.ts +15 -0
- package/types/cccteam-ccc-lib-src-auth-service.d.ts +38 -0
- package/types/cccteam-ccc-lib-src-ccc-camel-case-to-title.d.ts +10 -0
- package/types/cccteam-ccc-lib-src-ccc-grid.d.ts +35 -0
- package/types/cccteam-ccc-lib-src-ccc-resource.d.ts +674 -0
- package/types/cccteam-ccc-lib-src-forms.d.ts +27 -0
- package/types/cccteam-ccc-lib-src-types.d.ts +934 -0
- package/types/cccteam-ccc-lib-src-ui-alert.d.ts +16 -0
- package/types/cccteam-ccc-lib-src-ui-core-service.d.ts +20 -0
- package/types/cccteam-ccc-lib-src-ui-idle-service.d.ts +49 -0
- package/types/cccteam-ccc-lib-src-ui-interceptor.d.ts +16 -0
- package/types/cccteam-ccc-lib-src-ui-notification-service.d.ts +33 -0
- package/types/cccteam-ccc-lib-src-ui-sidenav.d.ts +33 -0
- package/types/cccteam-ccc-lib-src-util-request-options.d.ts +12 -0
- package/types/cccteam-ccc-lib.d.ts +1877 -0
- package/eslint.config.js +0 -32
- package/ng-package.json +0 -11
- package/src/auth-authentication-guard/authentication.guard.ts +0 -40
- package/src/auth-authentication-guard/index.ts +0 -1
- package/src/auth-authentication-guard/ng-package.json +0 -6
- package/src/auth-authorization-guard/authorization.guard.ts +0 -17
- package/src/auth-authorization-guard/index.ts +0 -1
- package/src/auth-authorization-guard/ng-package.json +0 -6
- package/src/auth-forms/ccc-field/ccc-field.component.html +0 -1
- package/src/auth-forms/ccc-field/ccc-field.component.scss +0 -0
- package/src/auth-forms/ccc-field/ccc-field.component.spec.ts +0 -22
- package/src/auth-forms/ccc-field/ccc-field.component.ts +0 -74
- package/src/auth-forms/form-helpers.ts +0 -39
- package/src/auth-forms/index.ts +0 -3
- package/src/auth-forms/ng-package.json +0 -6
- package/src/auth-has-permission/has-permission.directive.ts +0 -34
- package/src/auth-has-permission/index.ts +0 -1
- package/src/auth-has-permission/ng-package.json +0 -6
- package/src/auth-service/auth.service.ts +0 -92
- package/src/auth-service/index.ts +0 -1
- package/src/auth-service/ng-package.json +0 -6
- package/src/ccc-camel-case-to-title/camel-case-to-title.pipe.ts +0 -23
- package/src/ccc-camel-case-to-title/index.ts +0 -1
- package/src/ccc-camel-case-to-title/ng-package.json +0 -6
- package/src/ccc-grid/ccc-grid.component.ts +0 -155
- package/src/ccc-grid/index.ts +0 -3
- package/src/ccc-grid/ng-package.json +0 -6
- package/src/ccc-grid/table-button/table-button.component.html +0 -16
- package/src/ccc-grid/table-button/table-button.component.scss +0 -5
- package/src/ccc-grid/table-button/table-button.component.spec.ts +0 -22
- package/src/ccc-grid/table-button/table-button.component.ts +0 -49
- package/src/ccc-resource/can-deactivate.guard.ts +0 -41
- package/src/ccc-resource/compound-resource/compound-resource.component.html +0 -57
- package/src/ccc-resource/compound-resource/compound-resource.component.scss +0 -86
- package/src/ccc-resource/compound-resource/compound-resource.component.spec.ts +0 -22
- package/src/ccc-resource/compound-resource/compound-resource.component.ts +0 -158
- package/src/ccc-resource/concat-fns.ts +0 -162
- package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.html +0 -12
- package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.scss +0 -0
- package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.spec.ts +0 -23
- package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.ts +0 -17
- package/src/ccc-resource/form-state.service.ts +0 -24
- package/src/ccc-resource/format-fns.ts +0 -49
- package/src/ccc-resource/gui-constants.ts +0 -88
- package/src/ccc-resource/index.ts +0 -23
- package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.html +0 -8
- package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.scss +0 -0
- package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.spec.ts +0 -22
- package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.ts +0 -12
- package/src/ccc-resource/ng-package.json +0 -6
- package/src/ccc-resource/operation-types.ts +0 -19
- package/src/ccc-resource/padding-element/padding-element.component.html +0 -1
- package/src/ccc-resource/padding-element/padding-element.component.scss +0 -3
- package/src/ccc-resource/padding-element/padding-element.component.spec.ts +0 -22
- package/src/ccc-resource/padding-element/padding-element.component.ts +0 -20
- package/src/ccc-resource/resource-array-view/resource-array-view.component.html +0 -81
- package/src/ccc-resource/resource-array-view/resource-array-view.component.scss +0 -21
- package/src/ccc-resource/resource-array-view/resource-array-view.component.spec.ts +0 -22
- package/src/ccc-resource/resource-array-view/resource-array-view.component.ts +0 -143
- package/src/ccc-resource/resource-base/resource-base.component.spec.ts +0 -22
- package/src/ccc-resource/resource-base/resource-base.component.ts +0 -11
- package/src/ccc-resource/resource-cache.service.ts +0 -232
- package/src/ccc-resource/resource-create/resource-create.component.html +0 -31
- package/src/ccc-resource/resource-create/resource-create.component.scss +0 -130
- package/src/ccc-resource/resource-create/resource-create.component.spec.ts +0 -22
- package/src/ccc-resource/resource-create/resource-create.component.ts +0 -303
- package/src/ccc-resource/resource-field/base-field.directive.ts +0 -102
- package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.html +0 -16
- package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.scss +0 -0
- package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.spec.ts +0 -22
- package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.ts +0 -15
- package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.html +0 -13
- package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.scss +0 -0
- package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.spec.ts +0 -23
- package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.ts +0 -50
- package/src/ccc-resource/resource-field/fields/date-field/date-field.component.html +0 -22
- package/src/ccc-resource/resource-field/fields/date-field/date-field.component.scss +0 -0
- package/src/ccc-resource/resource-field/fields/date-field/date-field.component.spec.ts +0 -22
- package/src/ccc-resource/resource-field/fields/date-field/date-field.component.ts +0 -14
- package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.html +0 -71
- package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.scss +0 -9
- package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.spec.ts +0 -22
- package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.ts +0 -207
- package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.html +0 -38
- package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.scss +0 -3
- package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.spec.ts +0 -22
- package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.ts +0 -87
- package/src/ccc-resource/resource-field/fields/number-field/number-field.component.html +0 -23
- package/src/ccc-resource/resource-field/fields/number-field/number-field.component.scss +0 -6
- package/src/ccc-resource/resource-field/fields/number-field/number-field.component.spec.ts +0 -22
- package/src/ccc-resource/resource-field/fields/number-field/number-field.component.ts +0 -14
- package/src/ccc-resource/resource-field/fields/text-field/text-field.component.html +0 -29
- package/src/ccc-resource/resource-field/fields/text-field/text-field.component.scss +0 -6
- package/src/ccc-resource/resource-field/fields/text-field/text-field.component.spec.ts +0 -22
- package/src/ccc-resource/resource-field/fields/text-field/text-field.component.ts +0 -23
- package/src/ccc-resource/resource-field/resource-field.component.html +0 -112
- package/src/ccc-resource/resource-field/resource-field.component.scss +0 -7
- package/src/ccc-resource/resource-field/resource-field.component.spec.ts +0 -22
- package/src/ccc-resource/resource-field/resource-field.component.ts +0 -214
- package/src/ccc-resource/resource-layout/resource-layout.component.html +0 -73
- package/src/ccc-resource/resource-layout/resource-layout.component.scss +0 -26
- package/src/ccc-resource/resource-layout/resource-layout.component.spec.ts +0 -22
- package/src/ccc-resource/resource-layout/resource-layout.component.ts +0 -176
- package/src/ccc-resource/resource-list/ resource-list.component.spec.ts +0 -22
- package/src/ccc-resource/resource-list/resource-list.component.html +0 -27
- package/src/ccc-resource/resource-list/resource-list.component.scss +0 -67
- package/src/ccc-resource/resource-list/resource-list.component.ts +0 -376
- package/src/ccc-resource/resource-list-create/resource-list-create.component.html +0 -71
- package/src/ccc-resource/resource-list-create/resource-list-create.component.scss +0 -9
- package/src/ccc-resource/resource-list-create/resource-list-create.component.spec.ts +0 -22
- package/src/ccc-resource/resource-list-create/resource-list-create.component.ts +0 -103
- package/src/ccc-resource/resource-resolver/resource-resolver.component.html +0 -1
- package/src/ccc-resource/resource-resolver/resource-resolver.component.scss +0 -0
- package/src/ccc-resource/resource-resolver/resource-resolver.component.spec.ts +0 -22
- package/src/ccc-resource/resource-resolver/resource-resolver.component.ts +0 -69
- package/src/ccc-resource/resource-store.service.ts +0 -93
- package/src/ccc-resource/resource-view/resource-view.component.html +0 -133
- package/src/ccc-resource/resource-view/resource-view.component.scss +0 -150
- package/src/ccc-resource/resource-view/resource-view.component.spec.ts +0 -22
- package/src/ccc-resource/resource-view/resource-view.component.ts +0 -354
- package/src/ccc-resource/resources-helpers.ts +0 -262
- package/src/ccc-resource/utils/validator-utils.ts +0 -6
- package/src/index.ts +0 -44
- package/src/internal-types/ng-package.json +0 -6
- package/src/types/auth.actions.ts +0 -46
- package/src/types/configs.ts +0 -952
- package/src/types/constants.ts +0 -1
- package/src/types/core.actions.ts +0 -33
- package/src/types/index.ts +0 -9
- package/src/types/ng-package.json +0 -6
- package/src/types/notification-message.ts +0 -20
- package/src/types/permissions.ts +0 -17
- package/src/types/session-info.ts +0 -10
- package/src/types/tokens.ts +0 -20
- package/src/ui-alert/alert.component.html +0 -13
- package/src/ui-alert/alert.component.scss +0 -48
- package/src/ui-alert/alert.component.spec.ts +0 -22
- package/src/ui-alert/alert.component.ts +0 -35
- package/src/ui-alert/index.ts +0 -1
- package/src/ui-alert/ng-package.json +0 -6
- package/src/ui-core-service/index.ts +0 -1
- package/src/ui-core-service/ng-package.json +0 -6
- package/src/ui-core-service/ui-core.service.ts +0 -34
- package/src/ui-interceptor/api.interceptor.spec.ts +0 -16
- package/src/ui-interceptor/api.interceptor.ts +0 -45
- package/src/ui-interceptor/index.ts +0 -1
- package/src/ui-interceptor/ng-package.json +0 -6
- package/src/ui-notification-service/index.ts +0 -1
- package/src/ui-notification-service/ng-package.json +0 -6
- package/src/ui-notification-service/notification.service.ts +0 -59
- package/src/ui-sidenav/index.ts +0 -1
- package/src/ui-sidenav/ng-package.json +0 -6
- package/src/ui-sidenav/sidenav.component.html +0 -60
- package/src/ui-sidenav/sidenav.component.scss +0 -99
- package/src/ui-sidenav/sidenav.component.spec.ts +0 -22
- package/src/ui-sidenav/sidenav.component.ts +0 -64
- package/src/util-request-options/index.ts +0 -1
- package/src/util-request-options/ng-package.json +0 -6
- package/src/util-request-options/request-options.ts +0 -17
- package/tsconfig.lib.json +0 -13
- package/tsconfig.lib.prod.json +0 -11
- package/tsconfig.spec.json +0 -15
- /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
|