@meshmakers/octo-ui 3.3.970 → 3.3.1000
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/fesm2022/meshmakers-octo-ui.mjs +2606 -272
- package/fesm2022/meshmakers-octo-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/meshmakers-octo-ui.d.ts +744 -87
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Component, Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, ElementRef, forwardRef, ViewChild, signal, computed,
|
|
2
|
+
import { inject, Component, Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, ElementRef, forwardRef, ViewChild, signal, computed, input, model, output, effect, untracked, Directive, InjectionToken, HostListener, makeEnvironmentProviders } from '@angular/core';
|
|
3
3
|
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
|
4
|
-
import { AttributeSelectorService, AttributeValueTypeDto as AttributeValueTypeDto$1, CkTypeSelectorService, CkTypeAttributeService, GetEntitiesByCkTypeDtoGQL, RuntimeEntitySelectDataSource, RuntimeEntityDialogDataSource, SearchFilterTypesDto, SortOrdersDto, FieldFilterOperatorsDto, GetCkTypesDtoGQL, GetCkModelByIdDtoGQL, LevelMetaData, RtAssociationMetaData, CkTypeMetaData, GraphDirectionDto, AssociationModOptionsDto, GraphQL, DeleteStrategiesDto, provideOctoServices } from '@meshmakers/octo-services';
|
|
4
|
+
import { AttributeSelectorService, AttributeValueTypeDto as AttributeValueTypeDto$1, CkTypeSelectorService, CkTypeAttributeService, GetEntitiesByCkTypeDtoGQL, RuntimeEntitySelectDataSource, RuntimeEntityDialogDataSource, SearchFilterTypesDto, SortOrdersDto, FieldFilterOperatorsDto, GetCkTypesDtoGQL, GetCkModelByIdDtoGQL, LevelMetaData, RtAssociationMetaData, CkTypeMetaData, GraphDirectionDto, AssociationModOptionsDto, GraphQL, DeleteStrategiesDto, CommunicationService, provideOctoServices } from '@meshmakers/octo-services';
|
|
5
5
|
import { Roles, provideMmSharedAuth } from '@meshmakers/shared-auth';
|
|
6
|
-
import { WindowStateService, EntitySelectInputComponent, DataSourceTyped, HierarchyDataSourceBase, NotificationDisplayService, TreeComponent, ListViewComponent, FetchResultTyped, DataSourceBase, InputService, BaseTreeDetailComponent, provideMmSharedUi } from '@meshmakers/shared-ui';
|
|
6
|
+
import { WindowStateService, EntitySelectInputComponent, DataSourceTyped, HierarchyDataSourceBase, NotificationDisplayService, TreeComponent, ListViewComponent, FetchResultTyped, DataSourceBase, InputService, BaseTreeDetailComponent, ConfirmationService, provideMmSharedUi } from '@meshmakers/shared-ui';
|
|
7
7
|
import * as i1$2 from '@angular/common';
|
|
8
8
|
import { CommonModule, Location } from '@angular/common';
|
|
9
9
|
import * as i1 from '@angular/forms';
|
|
10
10
|
import { FormsModule, FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormArray, Validators, FormGroup, FormBuilder } from '@angular/forms';
|
|
11
11
|
import * as i1$1 from '@progress/kendo-angular-buttons';
|
|
12
12
|
import { ButtonsModule, ButtonModule, KENDO_BUTTONS, ButtonComponent } from '@progress/kendo-angular-buttons';
|
|
13
|
-
import { WindowRef, WindowModule, WindowService, WindowCloseResult
|
|
14
|
-
import * as
|
|
15
|
-
import { DropDownListModule, DropDownsModule, AutoCompleteModule, KENDO_DROPDOWNS } from '@progress/kendo-angular-dropdowns';
|
|
13
|
+
import { WindowRef, WindowModule, WindowService, WindowCloseResult } from '@progress/kendo-angular-dialog';
|
|
14
|
+
import * as i3$1 from '@progress/kendo-angular-dropdowns';
|
|
15
|
+
import { DropDownListModule, DropDownsModule, AutoCompleteModule, ComboBoxModule, KENDO_DROPDOWNS } from '@progress/kendo-angular-dropdowns';
|
|
16
16
|
import * as i3 from '@progress/kendo-angular-grid';
|
|
17
17
|
import { GridModule } from '@progress/kendo-angular-grid';
|
|
18
18
|
import * as i5$1 from '@progress/kendo-angular-icons';
|
|
19
19
|
import { IconsModule, SVGIconModule, SVGIconComponent } from '@progress/kendo-angular-icons';
|
|
20
20
|
import * as i5 from '@progress/kendo-angular-inputs';
|
|
21
21
|
import { InputsModule, TextBoxModule, KENDO_INPUTS, SwitchModule } from '@progress/kendo-angular-inputs';
|
|
22
|
-
import { searchIcon, sortAscSmallIcon, sortDescSmallIcon, filterClearIcon, fileIcon, folderIcon, calendarIcon, checkboxCheckedIcon, listUnorderedIcon, chevronRightIcon, chevronDownIcon, downloadIcon, windowIcon, arrowRightIcon, arrowLeftIcon, chevronDoubleRightIcon, chevronDoubleLeftIcon, arrowUpIcon, arrowDownIcon, pencilIcon, trashIcon, copyIcon, folderMoreIcon, folderOpenIcon, gearIcon, plusIcon, minusIcon, dollarIcon, hyperlinkOpenIcon, infoCircleIcon, eyeIcon, arrowRotateCcwIcon, locationsIcon, arrowRotateCwIcon, xIcon, checkCircleIcon, exclamationCircleIcon, xCircleIcon } from '@progress/kendo-svg-icons';
|
|
23
|
-
import { Subject, firstValueFrom, Subscription, of, forkJoin,
|
|
22
|
+
import { searchIcon, sortAscSmallIcon, sortDescSmallIcon, filterClearIcon, fileIcon, folderIcon, calendarIcon, checkboxCheckedIcon, listUnorderedIcon, chevronRightIcon, chevronDownIcon, downloadIcon, windowIcon, arrowRightIcon, arrowLeftIcon, chevronDoubleRightIcon, chevronDoubleLeftIcon, arrowUpIcon, arrowDownIcon, pencilIcon, trashIcon, copyIcon, folderMoreIcon, folderOpenIcon, gearIcon, plusIcon, minusIcon, dollarIcon, hyperlinkOpenIcon, infoCircleIcon, eyeIcon, arrowRotateCcwIcon, locationsIcon, arrowRotateCwIcon, xIcon, checkCircleIcon, exclamationCircleIcon, xCircleIcon, warningCircleIcon, gridIcon, linkIcon } from '@progress/kendo-svg-icons';
|
|
23
|
+
import { Subject, firstValueFrom, Subscription, of, forkJoin, map as map$1, takeUntil as takeUntil$1, catchError as catchError$1, defer, from, finalize, switchMap as switchMap$1, startWith } from 'rxjs';
|
|
24
24
|
import { debounceTime, distinctUntilChanged, map, switchMap, tap, catchError, takeUntil } from 'rxjs/operators';
|
|
25
25
|
import { LoaderModule, BadgeModule } from '@progress/kendo-angular-indicators';
|
|
26
|
-
import { isCompositeFilterDescriptor } from '@progress/kendo-data-query';
|
|
27
|
-
import { TreeItemDataTyped } from '@meshmakers/shared-services';
|
|
28
26
|
import * as i1$3 from 'apollo-angular';
|
|
29
27
|
import { gql } from 'apollo-angular';
|
|
28
|
+
import { isCompositeFilterDescriptor } from '@progress/kendo-data-query';
|
|
29
|
+
import { TreeItemDataTyped } from '@meshmakers/shared-services';
|
|
30
30
|
import * as i7 from '@progress/kendo-angular-dateinputs';
|
|
31
31
|
import { DateInputsModule, KENDO_DATEINPUTS } from '@progress/kendo-angular-dateinputs';
|
|
32
32
|
import { IntlModule } from '@progress/kendo-angular-intl';
|
|
@@ -34,7 +34,7 @@ import { PopupModule, PopupComponent } from '@progress/kendo-angular-popup';
|
|
|
34
34
|
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
|
35
35
|
import { NotificationService } from '@progress/kendo-angular-notification';
|
|
36
36
|
import * as i1$4 from '@progress/kendo-angular-layout';
|
|
37
|
-
import { TabStripModule, CardModule, KENDO_LAYOUT } from '@progress/kendo-angular-layout';
|
|
37
|
+
import { TabStripModule, CardModule, KENDO_LAYOUT, LayoutModule } from '@progress/kendo-angular-layout';
|
|
38
38
|
import { rxResource, toSignal, toObservable } from '@angular/core/rxjs-interop';
|
|
39
39
|
import * as i1$5 from '@progress/kendo-angular-label';
|
|
40
40
|
import { KENDO_LABEL } from '@progress/kendo-angular-label';
|
|
@@ -419,7 +419,7 @@ class AttributeSortSelectorDialogComponent {
|
|
|
419
419
|
<button kendoButton themeColor="primary" (click)="onOk()">Apply</button>
|
|
420
420
|
</div>
|
|
421
421
|
</div>
|
|
422
|
-
`, isInline: true, styles: [":host{display:block;height:100%}.attribute-sort-selector-container{display:flex;flex-direction:column;height:100%;padding:16px 20px;box-sizing:border-box;gap:16px}.filter-container{display:flex;gap:12px;flex-shrink:0}.search-input{flex:1}.type-filter-dropdown{width:160px;flex-shrink:0}.lists-container{display:flex;gap:16px;flex:1;min-height:0}.list-section{flex:1;display:flex;flex-direction:column;min-height:0}.list-section h4,.sort-options-section h4{margin:0 0 10px;font-size:.85rem;font-weight:600;flex-shrink:0}.attribute-grid{border-radius:4px;flex:1;min-height:200px}.attribute-grid ::ng-deep .k-grid-table tbody tr{cursor:pointer}.sort-options-section{width:120px;flex-shrink:0;display:flex;flex-direction:column;padding-top:32px}.sort-buttons{display:flex;flex-direction:row;gap:4px}.sort-button{flex:1;min-width:36px;height:36px;padding:0;display:flex;align-items:center;justify-content:center}.add-button{width:100%;margin-top:16px}.sort-display{display:flex;align-items:center;gap:6px}.sort-indicator{font-size:14px;font-weight:700;color:var(--kendo-color-primary, #ff6358)}.action-bar{display:flex;justify-content:flex-end;gap:8px;flex-shrink:0;padding-top:8px;border-top:1px solid var(--kendo-color-border, #dee2e6)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: i3.SelectionDirective, selector: "[kendoGridSelectBy]" }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: i5.TextBoxSuffixTemplateDirective, selector: "[kendoTextBoxSuffixTemplate]", inputs: ["showSeparator"] }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type:
|
|
422
|
+
`, isInline: true, styles: [":host{display:block;height:100%}.attribute-sort-selector-container{display:flex;flex-direction:column;height:100%;padding:16px 20px;box-sizing:border-box;gap:16px}.filter-container{display:flex;gap:12px;flex-shrink:0}.search-input{flex:1}.type-filter-dropdown{width:160px;flex-shrink:0}.lists-container{display:flex;gap:16px;flex:1;min-height:0}.list-section{flex:1;display:flex;flex-direction:column;min-height:0}.list-section h4,.sort-options-section h4{margin:0 0 10px;font-size:.85rem;font-weight:600;flex-shrink:0}.attribute-grid{border-radius:4px;flex:1;min-height:200px}.attribute-grid ::ng-deep .k-grid-table tbody tr{cursor:pointer}.sort-options-section{width:120px;flex-shrink:0;display:flex;flex-direction:column;padding-top:32px}.sort-buttons{display:flex;flex-direction:row;gap:4px}.sort-button{flex:1;min-width:36px;height:36px;padding:0;display:flex;align-items:center;justify-content:center}.add-button{width:100%;margin-top:16px}.sort-display{display:flex;align-items:center;gap:6px}.sort-indicator{font-size:14px;font-weight:700;color:var(--kendo-color-primary, #ff6358)}.action-bar{display:flex;justify-content:flex-end;gap:8px;flex-shrink:0;padding-top:8px;border-top:1px solid var(--kendo-color-border, #dee2e6)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: i3.SelectionDirective, selector: "[kendoGridSelectBy]" }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: i5.TextBoxSuffixTemplateDirective, selector: "[kendoTextBoxSuffixTemplate]", inputs: ["showSeparator"] }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i3$1.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: IconsModule }, { kind: "ngmodule", type: WindowModule }] });
|
|
423
423
|
}
|
|
424
424
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AttributeSortSelectorDialogComponent, decorators: [{
|
|
425
425
|
type: Component,
|
|
@@ -938,7 +938,7 @@ class CkTypeSelectorDialogComponent {
|
|
|
938
938
|
<button kendoButton themeColor="primary" [disabled]="!selectedType || (selectedType.isAbstract && !allowAbstract)" (click)="onConfirm()">OK</button>
|
|
939
939
|
</div>
|
|
940
940
|
</div>
|
|
941
|
-
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%}.ck-type-selector-container{display:flex;flex-direction:column;flex:1;min-height:0;padding:20px;box-sizing:border-box;gap:12px}.filter-container{margin-bottom:16px;flex-shrink:0}.filter-row{display:flex;gap:16px;align-items:flex-end}.filter-item{display:flex;flex-direction:column;gap:4px}.filter-item label{font-size:12px;font-weight:500}.filter-item.flex-grow{flex:1}.filter-item.filter-actions{flex-shrink:0}.filter-input{min-width:180px}.grid-container{flex:1;min-height:0;display:flex;flex-direction:column}.grid-container kendo-grid,.grid-container .type-grid{flex:1;min-height:200px}.type-grid{border-radius:4px}.type-grid ::ng-deep .k-grid-table tbody tr{cursor:pointer}.abstract-type{font-style:italic;opacity:.7}.final-type{font-weight:600}.type-badge{display:inline-block;font-size:10px;padding:1px 6px;border-radius:10px;margin-left:8px;text-transform:uppercase}.type-badge.abstract{background-color:var(--kendo-color-warning, #ffc107);color:var(--kendo-color-on-warning, #000);opacity:.8}.type-badge.final{background-color:var(--kendo-color-success, #28a745);color:var(--kendo-color-on-success, #fff);opacity:.8}.selection-info{margin-top:12px;padding:8px 12px;background:var(--kendo-color-success-subtle, #d4edda);border:1px solid var(--kendo-color-success, #28a745);border-radius:4px;font-size:14px;flex-shrink:0}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;padding:8px 20px 0;flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: i3.SelectionDirective, selector: "[kendoGridSelectBy]" }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: i5.TextBoxSuffixTemplateDirective, selector: "[kendoTextBoxSuffixTemplate]", inputs: ["showSeparator"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "component", type:
|
|
941
|
+
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%}.ck-type-selector-container{display:flex;flex-direction:column;flex:1;min-height:0;padding:20px;box-sizing:border-box;gap:12px}.filter-container{margin-bottom:16px;flex-shrink:0}.filter-row{display:flex;gap:16px;align-items:flex-end}.filter-item{display:flex;flex-direction:column;gap:4px}.filter-item label{font-size:12px;font-weight:500}.filter-item.flex-grow{flex:1}.filter-item.filter-actions{flex-shrink:0}.filter-input{min-width:180px}.grid-container{flex:1;min-height:0;display:flex;flex-direction:column}.grid-container kendo-grid,.grid-container .type-grid{flex:1;min-height:200px}.type-grid{border-radius:4px}.type-grid ::ng-deep .k-grid-table tbody tr{cursor:pointer}.abstract-type{font-style:italic;opacity:.7}.final-type{font-weight:600}.type-badge{display:inline-block;font-size:10px;padding:1px 6px;border-radius:10px;margin-left:8px;text-transform:uppercase}.type-badge.abstract{background-color:var(--kendo-color-warning, #ffc107);color:var(--kendo-color-on-warning, #000);opacity:.8}.type-badge.final{background-color:var(--kendo-color-success, #28a745);color:var(--kendo-color-on-success, #fff);opacity:.8}.selection-info{margin-top:12px;padding:8px 12px;background:var(--kendo-color-success-subtle, #d4edda);border:1px solid var(--kendo-color-success, #28a745);border-radius:4px;font-size:14px;flex-shrink:0}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;padding:8px 20px 0;flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: i3.SelectionDirective, selector: "[kendoGridSelectBy]" }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: i5.TextBoxSuffixTemplateDirective, selector: "[kendoTextBoxSuffixTemplate]", inputs: ["showSeparator"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "component", type: i3$1.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "ngmodule", type: IconsModule }, { kind: "ngmodule", type: LoaderModule }, { kind: "ngmodule", type: WindowModule }] });
|
|
942
942
|
}
|
|
943
943
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CkTypeSelectorDialogComponent, decorators: [{
|
|
944
944
|
type: Component,
|
|
@@ -3393,7 +3393,7 @@ class CkTypeSelectorInputComponent {
|
|
|
3393
3393
|
(click)="openDialog()">
|
|
3394
3394
|
</button>
|
|
3395
3395
|
</div>
|
|
3396
|
-
`, isInline: true, styles: [":host{display:block;width:100%}.ck-type-select-wrapper{position:relative;display:flex;align-items:center;width:100%;gap:4px}.ck-type-select-wrapper.disabled{opacity:.6;pointer-events:none}.ck-type-autocomplete{flex:1;min-width:0}.dialog-button{flex-shrink:0;height:30px;width:30px;padding:0;display:flex;align-items:center;justify-content:center}.ck-type-item{padding:4px 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.no-data-message{padding:8px 12px;color:var(--kendo-color-subtle);font-style:italic;text-align:center}.advanced-search-footer{display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;color:var(--kendo-color-primary);border-top:1px solid var(--kendo-color-border);background:var(--kendo-color-surface-alt);transition:background-color .2s}.advanced-search-footer:hover{background:var(--kendo-color-base-hover)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type:
|
|
3396
|
+
`, isInline: true, styles: [":host{display:block;width:100%}.ck-type-select-wrapper{position:relative;display:flex;align-items:center;width:100%;gap:4px}.ck-type-select-wrapper.disabled{opacity:.6;pointer-events:none}.ck-type-autocomplete{flex:1;min-width:0}.dialog-button{flex-shrink:0;height:30px;width:30px;padding:0;display:flex;align-items:center;justify-content:center}.ck-type-item{padding:4px 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.no-data-message{padding:8px 12px;color:var(--kendo-color-subtle);font-style:italic;text-align:center}.advanced-search-footer{display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;color:var(--kendo-color-primary);border-top:1px solid var(--kendo-color-border);background:var(--kendo-color-surface-alt);transition:background-color .2s}.advanced-search-footer:hover{background:var(--kendo-color-base-hover)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i3$1.AutoCompleteComponent, selector: "kendo-autocomplete", inputs: ["highlightFirst", "showStickyHeader", "focusableId", "data", "value", "valueField", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "clearButton", "suggest", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoAutoComplete"] }, { kind: "directive", type: i3$1.FooterTemplateDirective, selector: "[kendoDropDownListFooterTemplate],[kendoComboBoxFooterTemplate],[kendoDropDownTreeFooterTemplate],[kendoMultiColumnComboBoxFooterTemplate],[kendoAutoCompleteFooterTemplate],[kendoMultiSelectFooterTemplate],[kendoMultiSelectTreeFooterTemplate]" }, { kind: "directive", type: i3$1.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "directive", type: i3$1.NoDataTemplateDirective, selector: "[kendoDropDownListNoDataTemplate],[kendoDropDownTreeNoDataTemplate],[kendoComboBoxNoDataTemplate],[kendoMultiColumnComboBoxNoDataTemplate],[kendoAutoCompleteNoDataTemplate],[kendoMultiSelectNoDataTemplate],[kendoMultiSelectTreeNoDataTemplate]" }, { kind: "ngmodule", type: LoaderModule }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: IconsModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "ngmodule", type: SVGIconModule }] });
|
|
3397
3397
|
}
|
|
3398
3398
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CkTypeSelectorInputComponent, decorators: [{
|
|
3399
3399
|
type: Component,
|
|
@@ -4059,7 +4059,7 @@ class AttributeSelectorDialogComponent {
|
|
|
4059
4059
|
</button>
|
|
4060
4060
|
</div>
|
|
4061
4061
|
</div>
|
|
4062
|
-
`, isInline: true, styles: [":host{display:block;height:100%}.attribute-selector-container{display:flex;flex-direction:column;height:100%;padding:16px 20px;box-sizing:border-box;gap:16px}.filter-container{display:flex;gap:12px;flex-shrink:0}.search-input{flex:1}.type-filter-dropdown{width:160px;flex-shrink:0}.options-container{display:flex;align-items:center;gap:8px;font-size:.85rem;flex-shrink:0}.option-label{cursor:pointer}.depth-input{width:90px}.lists-container{display:flex;gap:16px;flex:1;min-height:0}.list-section{flex:1;display:flex;flex-direction:column;min-height:0}.list-section h4{margin:0 0 10px;font-size:.85rem;font-weight:600;flex-shrink:0}.attribute-grid{border-radius:4px;flex:1;min-height:200px}.attribute-grid ::ng-deep .k-grid-table tbody tr{cursor:pointer}.action-buttons{display:flex;flex-direction:column;gap:8px;justify-content:center;padding:32px 8px 0}.separator{height:1px;background-color:var(--kendo-color-border, #dee2e6);margin:8px 0}.order-buttons{display:flex;flex-direction:column;gap:8px;justify-content:center;padding:32px 8px 0}.order-number{color:var(--kendo-color-primary, #ff6358);font-weight:600;margin-right:8px;min-width:24px;display:inline-block}.single-select-container{flex:1;min-height:0;display:flex;flex-direction:column}.single-select-container .attribute-grid{flex:1}.action-bar{display:flex;justify-content:flex-end;gap:8px;flex-shrink:0;padding-top:8px;border-top:1px solid var(--kendo-color-border, #dee2e6)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: i3.SelectionDirective, selector: "[kendoGridSelectBy]" }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: i5.TextBoxSuffixTemplateDirective, selector: "[kendoTextBoxSuffixTemplate]", inputs: ["showSeparator"] }, { kind: "component", type: i5.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i5.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type:
|
|
4062
|
+
`, isInline: true, styles: [":host{display:block;height:100%}.attribute-selector-container{display:flex;flex-direction:column;height:100%;padding:16px 20px;box-sizing:border-box;gap:16px}.filter-container{display:flex;gap:12px;flex-shrink:0}.search-input{flex:1}.type-filter-dropdown{width:160px;flex-shrink:0}.options-container{display:flex;align-items:center;gap:8px;font-size:.85rem;flex-shrink:0}.option-label{cursor:pointer}.depth-input{width:90px}.lists-container{display:flex;gap:16px;flex:1;min-height:0}.list-section{flex:1;display:flex;flex-direction:column;min-height:0}.list-section h4{margin:0 0 10px;font-size:.85rem;font-weight:600;flex-shrink:0}.attribute-grid{border-radius:4px;flex:1;min-height:200px}.attribute-grid ::ng-deep .k-grid-table tbody tr{cursor:pointer}.action-buttons{display:flex;flex-direction:column;gap:8px;justify-content:center;padding:32px 8px 0}.separator{height:1px;background-color:var(--kendo-color-border, #dee2e6);margin:8px 0}.order-buttons{display:flex;flex-direction:column;gap:8px;justify-content:center;padding:32px 8px 0}.order-number{color:var(--kendo-color-primary, #ff6358);font-weight:600;margin-right:8px;min-width:24px;display:inline-block}.single-select-container{flex:1;min-height:0;display:flex;flex-direction:column}.single-select-container .attribute-grid{flex:1}.action-bar{display:flex;justify-content:flex-end;gap:8px;flex-shrink:0;padding-top:8px;border-top:1px solid var(--kendo-color-border, #dee2e6)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: i3.SelectionDirective, selector: "[kendoGridSelectBy]" }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: i5.TextBoxSuffixTemplateDirective, selector: "[kendoTextBoxSuffixTemplate]", inputs: ["showSeparator"] }, { kind: "component", type: i5.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i5.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i3$1.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: IconsModule }, { kind: "ngmodule", type: WindowModule }] });
|
|
4063
4063
|
}
|
|
4064
4064
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AttributeSelectorDialogComponent, decorators: [{
|
|
4065
4065
|
type: Component,
|
|
@@ -4735,6 +4735,323 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
4735
4735
|
}]
|
|
4736
4736
|
}] });
|
|
4737
4737
|
|
|
4738
|
+
const GetRuntimeEntityByIdDocumentDto = gql `
|
|
4739
|
+
query getRuntimeEntityById($rtId: OctoObjectId!, $ckTypeId: String!) {
|
|
4740
|
+
runtime {
|
|
4741
|
+
runtimeEntities(ckId: $ckTypeId, rtId: $rtId) {
|
|
4742
|
+
items {
|
|
4743
|
+
rtId
|
|
4744
|
+
ckTypeId
|
|
4745
|
+
rtWellKnownName
|
|
4746
|
+
rtCreationDateTime
|
|
4747
|
+
rtChangedDateTime
|
|
4748
|
+
attributes(resolveEnumValuesToNames: true) {
|
|
4749
|
+
items {
|
|
4750
|
+
attributeName
|
|
4751
|
+
value
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
associations {
|
|
4755
|
+
definitions(direction: ANY, first: 0) {
|
|
4756
|
+
totalCount
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
}
|
|
4762
|
+
}
|
|
4763
|
+
`;
|
|
4764
|
+
class GetRuntimeEntityByIdDtoGQL extends i1$3.Query {
|
|
4765
|
+
document = GetRuntimeEntityByIdDocumentDto;
|
|
4766
|
+
constructor(apollo) {
|
|
4767
|
+
super(apollo);
|
|
4768
|
+
}
|
|
4769
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetRuntimeEntityByIdDtoGQL, deps: [{ token: i1$3.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4770
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetRuntimeEntityByIdDtoGQL, providedIn: 'root' });
|
|
4771
|
+
}
|
|
4772
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetRuntimeEntityByIdDtoGQL, decorators: [{
|
|
4773
|
+
type: Injectable,
|
|
4774
|
+
args: [{
|
|
4775
|
+
providedIn: 'root'
|
|
4776
|
+
}]
|
|
4777
|
+
}], ctorParameters: () => [{ type: i1$3.Apollo }] });
|
|
4778
|
+
|
|
4779
|
+
/**
|
|
4780
|
+
* Pure helpers for extracting runtime data point names from a CK entity. Kept
|
|
4781
|
+
* Angular-free so unit tests can exercise the record-shape branches without
|
|
4782
|
+
* spinning up a TestBed, and so the same logic can be called both from the
|
|
4783
|
+
* service (which fetches the entity via GraphQL) and from any caller that
|
|
4784
|
+
* already has the entity in hand (e.g. the runtime-browser detail pane).
|
|
4785
|
+
*
|
|
4786
|
+
* A "data point" here is one entry of an entity's `States` or `DataPoints`
|
|
4787
|
+
* RecordArray attribute — for Loxone Controls these are the state names
|
|
4788
|
+
* (`tempActual`, `co2`, `humidityActual`, …); for adapters that don't model
|
|
4789
|
+
* sub-states the array is absent and the only available data point is the
|
|
4790
|
+
* default `currentValue` constant the runtime adapter exposes for
|
|
4791
|
+
* single-state polling.
|
|
4792
|
+
*/
|
|
4793
|
+
/**
|
|
4794
|
+
* Default data-point name for entities without a States/DataPoints RecordArray.
|
|
4795
|
+
* Matches the constant the runtime adapters (Loxone, MQTT, OPC-UA) use as the
|
|
4796
|
+
* `sourceAttributePath` fallback when no specific state is configured.
|
|
4797
|
+
*/
|
|
4798
|
+
const DEFAULT_DATA_POINT = 'currentValue';
|
|
4799
|
+
/**
|
|
4800
|
+
* Extracts the list of available data point names from an entity's attribute
|
|
4801
|
+
* list. Always includes {@link DEFAULT_DATA_POINT} first; named state entries
|
|
4802
|
+
* follow in alphabetical order.
|
|
4803
|
+
*
|
|
4804
|
+
* Tolerates the three record shapes the platform produces for a RecordArray:
|
|
4805
|
+
*
|
|
4806
|
+
* 1. GraphQL: each record is `{ ckRecordId, attributes: [{attributeName, value}, …] }`.
|
|
4807
|
+
* 2. Pipeline / MongoDB: each record is `{ attributes: { Name: "…", ExternalId: "…" } }`.
|
|
4808
|
+
* 3. Flat: each record is `{ Name: "…", ExternalId: "…" }` (older dumps / sample data).
|
|
4809
|
+
*
|
|
4810
|
+
* Returns `[DEFAULT_DATA_POINT]` for null/undefined input, attributes without a
|
|
4811
|
+
* States/DataPoints entry, or malformed RecordArrays.
|
|
4812
|
+
*/
|
|
4813
|
+
function extractDataPointNames(attributes) {
|
|
4814
|
+
if (!attributes)
|
|
4815
|
+
return [DEFAULT_DATA_POINT];
|
|
4816
|
+
const statesAttr = attributes.find(a => {
|
|
4817
|
+
const name = a?.attributeName?.toLowerCase();
|
|
4818
|
+
return name === 'states' || name === 'datapoints';
|
|
4819
|
+
});
|
|
4820
|
+
if (!statesAttr?.value)
|
|
4821
|
+
return [DEFAULT_DATA_POINT];
|
|
4822
|
+
const records = coerceToRecordArray(statesAttr.value);
|
|
4823
|
+
if (!records)
|
|
4824
|
+
return [DEFAULT_DATA_POINT];
|
|
4825
|
+
const names = [];
|
|
4826
|
+
for (const record of records) {
|
|
4827
|
+
const name = extractRecordName(record);
|
|
4828
|
+
if (name)
|
|
4829
|
+
names.push(name);
|
|
4830
|
+
}
|
|
4831
|
+
if (names.length === 0)
|
|
4832
|
+
return [DEFAULT_DATA_POINT];
|
|
4833
|
+
return [DEFAULT_DATA_POINT, ...[...names].sort((a, b) => a.localeCompare(b))];
|
|
4834
|
+
}
|
|
4835
|
+
/**
|
|
4836
|
+
* Accepts either an array (GraphQL or in-memory) or a JSON-stringified array
|
|
4837
|
+
* (the wire format some pipelines emit). Returns the parsed array or null
|
|
4838
|
+
* when the value can't be coerced.
|
|
4839
|
+
*/
|
|
4840
|
+
function coerceToRecordArray(value) {
|
|
4841
|
+
if (Array.isArray(value))
|
|
4842
|
+
return value;
|
|
4843
|
+
if (typeof value === 'string') {
|
|
4844
|
+
try {
|
|
4845
|
+
const parsed = JSON.parse(value);
|
|
4846
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
4847
|
+
}
|
|
4848
|
+
catch {
|
|
4849
|
+
return null;
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
return null;
|
|
4853
|
+
}
|
|
4854
|
+
/** Walks the known record shapes and returns the first non-empty name found. */
|
|
4855
|
+
function extractRecordName(record) {
|
|
4856
|
+
if (!record || typeof record !== 'object')
|
|
4857
|
+
return null;
|
|
4858
|
+
const r = record;
|
|
4859
|
+
// 1. GraphQL shape: attributes = [{attributeName, value}, …]
|
|
4860
|
+
const attrsAny = r['attributes'];
|
|
4861
|
+
if (Array.isArray(attrsAny)) {
|
|
4862
|
+
const nameEntry = attrsAny.find(a => a?.attributeName === 'name' || a?.attributeName === 'Name');
|
|
4863
|
+
const value = nameEntry?.value;
|
|
4864
|
+
if (typeof value === 'string' && value.length > 0)
|
|
4865
|
+
return value;
|
|
4866
|
+
}
|
|
4867
|
+
// 2. Pipeline / MongoDB shape: attributes = { Name, ExternalId, … }
|
|
4868
|
+
if (attrsAny && typeof attrsAny === 'object' && !Array.isArray(attrsAny)) {
|
|
4869
|
+
const attrObj = attrsAny;
|
|
4870
|
+
const value = attrObj['Name'] ?? attrObj['name'] ?? attrObj['stateName'];
|
|
4871
|
+
if (typeof value === 'string' && value.length > 0)
|
|
4872
|
+
return value;
|
|
4873
|
+
}
|
|
4874
|
+
// 3. Flat shape: { Name, ExternalId, … } at the record root
|
|
4875
|
+
const flatName = r['Name'] ?? r['name'];
|
|
4876
|
+
if (typeof flatName === 'string' && flatName.length > 0)
|
|
4877
|
+
return flatName;
|
|
4878
|
+
return null;
|
|
4879
|
+
}
|
|
4880
|
+
|
|
4881
|
+
/**
|
|
4882
|
+
* Resolves the list of runtime data points available on a source entity.
|
|
4883
|
+
* Single source of truth shared by the runtime-browser detail pane (which
|
|
4884
|
+
* already has the entity loaded) and the mapping-edit dialog (which only
|
|
4885
|
+
* has rtId + ckTypeId and has to fetch). Picks the right path automatically:
|
|
4886
|
+
*
|
|
4887
|
+
* - {@link extractFromEntity} is sync — pass a pre-loaded entity.
|
|
4888
|
+
* - {@link load} is async — fetches via `getRuntimeEntityById` then extracts.
|
|
4889
|
+
*
|
|
4890
|
+
* Both paths share the same pure helper so a Loxone Control's state list
|
|
4891
|
+
* (`tempActual`, `co2`, …) is reported identically regardless of who's asking.
|
|
4892
|
+
*/
|
|
4893
|
+
class DataPointResolverService {
|
|
4894
|
+
getRuntimeEntityByIdGQL = inject(GetRuntimeEntityByIdDtoGQL);
|
|
4895
|
+
extractFromEntity(entity) {
|
|
4896
|
+
return extractDataPointNames(entity?.attributes?.items);
|
|
4897
|
+
}
|
|
4898
|
+
async load(rtId, ckTypeId) {
|
|
4899
|
+
if (!rtId || !ckTypeId)
|
|
4900
|
+
return [DEFAULT_DATA_POINT];
|
|
4901
|
+
try {
|
|
4902
|
+
const entity = await firstValueFrom(this.getRuntimeEntityByIdGQL
|
|
4903
|
+
.fetch({
|
|
4904
|
+
variables: { rtId, ckTypeId },
|
|
4905
|
+
fetchPolicy: 'network-only',
|
|
4906
|
+
})
|
|
4907
|
+
.pipe(map$1(r => r.data?.runtime?.runtimeEntities?.items?.[0] ?? null)));
|
|
4908
|
+
return this.extractFromEntity(entity);
|
|
4909
|
+
}
|
|
4910
|
+
catch (error) {
|
|
4911
|
+
console.error(`DataPointResolverService.load failed for ${ckTypeId}@${rtId}:`, error);
|
|
4912
|
+
return [DEFAULT_DATA_POINT];
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataPointResolverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4916
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataPointResolverService, providedIn: 'root' });
|
|
4917
|
+
}
|
|
4918
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataPointResolverService, decorators: [{
|
|
4919
|
+
type: Injectable,
|
|
4920
|
+
args: [{ providedIn: 'root' }]
|
|
4921
|
+
}] });
|
|
4922
|
+
|
|
4923
|
+
/**
|
|
4924
|
+
* Source data-point picker for DataPointMapping `sourceAttributePath`.
|
|
4925
|
+
*
|
|
4926
|
+
* Renders a Kendo combobox of the data points exposed by a source entity —
|
|
4927
|
+
* a Loxone Control's State names (`tempActual`, `co2`, …), an MQTT topic
|
|
4928
|
+
* sub-key, an OPC-UA node attribute, etc. — with `currentValue` as the
|
|
4929
|
+
* always-available default for single-state sources.
|
|
4930
|
+
*
|
|
4931
|
+
* Two ways to feed the picker:
|
|
4932
|
+
* - `entity` — pass a pre-loaded RtEntityDto; the picker reads the
|
|
4933
|
+
* States/DataPoints RecordArray straight off it (no GraphQL roundtrip).
|
|
4934
|
+
* Used by the runtime-browser detail pane.
|
|
4935
|
+
* - `entityRtId` + `entityCkTypeId` — the picker fetches the entity via
|
|
4936
|
+
* `getRuntimeEntityById` and extracts data points itself. Used by the
|
|
4937
|
+
* mapping-edit dialog where only the IDs are known.
|
|
4938
|
+
*
|
|
4939
|
+
* `allowCustom: true` keeps the picker useful when the user needs to escape
|
|
4940
|
+
* the catalogue — e.g. typing a path the entity hasn't published yet, or a
|
|
4941
|
+
* field a future state will expose.
|
|
4942
|
+
*/
|
|
4943
|
+
class DataPointPickerComponent {
|
|
4944
|
+
resolver = inject(DataPointResolverService);
|
|
4945
|
+
/** Pre-loaded source entity. When set, no GraphQL call is made. */
|
|
4946
|
+
entity = input(null, ...(ngDevMode ? [{ debugName: "entity" }] : /* istanbul ignore next */ []));
|
|
4947
|
+
/** Source entity rtId. Required (together with ckTypeId) when `entity` is not provided. */
|
|
4948
|
+
entityRtId = input(null, ...(ngDevMode ? [{ debugName: "entityRtId" }] : /* istanbul ignore next */ []));
|
|
4949
|
+
/** Source entity CK type id. */
|
|
4950
|
+
entityCkTypeId = input(null, ...(ngDevMode ? [{ debugName: "entityCkTypeId" }] : /* istanbul ignore next */ []));
|
|
4951
|
+
/** Two-way bound data-point name. */
|
|
4952
|
+
value = model('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
4953
|
+
/** Optional placeholder shown when the combobox is empty. */
|
|
4954
|
+
placeholder = input('e.g. tempActual, currentValue', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
4955
|
+
/** Disables the combobox without hiding it. */
|
|
4956
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
4957
|
+
/** Emits whenever the user enters a custom filter — useful for callers that
|
|
4958
|
+
* want to refine a backing catalogue independently of the value selection. */
|
|
4959
|
+
filterChange = output();
|
|
4960
|
+
options = signal([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
4961
|
+
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
4962
|
+
/**
|
|
4963
|
+
* Current filter text entered into the combobox. Drives the case-insensitive
|
|
4964
|
+
* *contains* match below. Kendo's combobox by default does not filter the
|
|
4965
|
+
* `[data]` it is given — when `filterable` is true it just emits
|
|
4966
|
+
* `filterChange` events and expects the consumer to refilter. Without this,
|
|
4967
|
+
* typing "co2" would show every data point regardless of name.
|
|
4968
|
+
*/
|
|
4969
|
+
filter = signal('', ...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
|
|
4970
|
+
filteredOptions = computed(() => {
|
|
4971
|
+
const all = this.options();
|
|
4972
|
+
const needle = this.filter().trim().toLowerCase();
|
|
4973
|
+
if (!needle)
|
|
4974
|
+
return all;
|
|
4975
|
+
return all.filter(o => o.toLowerCase().includes(needle));
|
|
4976
|
+
}, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
|
|
4977
|
+
constructor() {
|
|
4978
|
+
// Re-resolve whenever the entity or its identifying ids change. We use
|
|
4979
|
+
// `untracked` around the actual mutation/IO to keep the effect's read
|
|
4980
|
+
// dependencies limited to the inputs that should trigger a refetch.
|
|
4981
|
+
effect(() => {
|
|
4982
|
+
const ent = this.entity();
|
|
4983
|
+
const rtId = this.entityRtId();
|
|
4984
|
+
const ckTypeId = this.entityCkTypeId();
|
|
4985
|
+
untracked(() => {
|
|
4986
|
+
if (ent) {
|
|
4987
|
+
this.options.set(this.resolver.extractFromEntity(ent));
|
|
4988
|
+
this.loading.set(false);
|
|
4989
|
+
return;
|
|
4990
|
+
}
|
|
4991
|
+
if (!rtId || !ckTypeId) {
|
|
4992
|
+
this.options.set([]);
|
|
4993
|
+
this.loading.set(false);
|
|
4994
|
+
return;
|
|
4995
|
+
}
|
|
4996
|
+
this.loading.set(true);
|
|
4997
|
+
void this.resolver.load(rtId, ckTypeId).then(opts => {
|
|
4998
|
+
// Only commit if the inputs haven't changed underneath us. We compare
|
|
4999
|
+
// against the current input snapshot to avoid stomping a fresher load.
|
|
5000
|
+
if (this.entityRtId() === rtId && this.entityCkTypeId() === ckTypeId) {
|
|
5001
|
+
this.options.set(opts);
|
|
5002
|
+
this.loading.set(false);
|
|
5003
|
+
}
|
|
5004
|
+
});
|
|
5005
|
+
});
|
|
5006
|
+
});
|
|
5007
|
+
}
|
|
5008
|
+
onValueChange(v) {
|
|
5009
|
+
this.value.set(v ?? '');
|
|
5010
|
+
}
|
|
5011
|
+
onFilterChange(filter) {
|
|
5012
|
+
this.filter.set(filter);
|
|
5013
|
+
this.filterChange.emit(filter);
|
|
5014
|
+
}
|
|
5015
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataPointPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5016
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataPointPickerComponent, isStandalone: true, selector: "mm-data-point-picker", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, entityRtId: { classPropertyName: "entityRtId", publicName: "entityRtId", isSignal: true, isRequired: false, transformFunction: null }, entityCkTypeId: { classPropertyName: "entityCkTypeId", publicName: "entityCkTypeId", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", filterChange: "filterChange" }, ngImport: i0, template: `
|
|
5017
|
+
<kendo-combobox
|
|
5018
|
+
[data]="filteredOptions()"
|
|
5019
|
+
[value]="value()"
|
|
5020
|
+
[valuePrimitive]="true"
|
|
5021
|
+
[allowCustom]="true"
|
|
5022
|
+
[filterable]="true"
|
|
5023
|
+
[disabled]="disabled() || loading()"
|
|
5024
|
+
[popupSettings]="{ appendTo: 'root', animate: true }"
|
|
5025
|
+
[placeholder]="placeholder()"
|
|
5026
|
+
(valueChange)="onValueChange($event)"
|
|
5027
|
+
(filterChange)="onFilterChange($event)">
|
|
5028
|
+
</kendo-combobox>
|
|
5029
|
+
@if (loading()) {
|
|
5030
|
+
<span class="dpp-loading">loading…</span>
|
|
5031
|
+
}
|
|
5032
|
+
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;gap:6px;width:100%}kendo-combobox{flex:1;min-width:0}.dpp-loading{font-size:.7rem;padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--kendo-color-info, #0dcaf0) 18%,transparent);color:var(--kendo-color-info, #0dcaf0);font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: ComboBoxModule }, { kind: "component", type: i3$1.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5033
|
+
}
|
|
5034
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataPointPickerComponent, decorators: [{
|
|
5035
|
+
type: Component,
|
|
5036
|
+
args: [{ selector: 'mm-data-point-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ComboBoxModule], template: `
|
|
5037
|
+
<kendo-combobox
|
|
5038
|
+
[data]="filteredOptions()"
|
|
5039
|
+
[value]="value()"
|
|
5040
|
+
[valuePrimitive]="true"
|
|
5041
|
+
[allowCustom]="true"
|
|
5042
|
+
[filterable]="true"
|
|
5043
|
+
[disabled]="disabled() || loading()"
|
|
5044
|
+
[popupSettings]="{ appendTo: 'root', animate: true }"
|
|
5045
|
+
[placeholder]="placeholder()"
|
|
5046
|
+
(valueChange)="onValueChange($event)"
|
|
5047
|
+
(filterChange)="onFilterChange($event)">
|
|
5048
|
+
</kendo-combobox>
|
|
5049
|
+
@if (loading()) {
|
|
5050
|
+
<span class="dpp-loading">loading…</span>
|
|
5051
|
+
}
|
|
5052
|
+
`, styles: [":host{display:inline-flex;align-items:center;gap:6px;width:100%}kendo-combobox{flex:1;min-width:0}.dpp-loading{font-size:.7rem;padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--kendo-color-info, #0dcaf0) 18%,transparent);color:var(--kendo-color-info, #0dcaf0);font-style:italic}\n"] }]
|
|
5053
|
+
}], ctorParameters: () => [], propDecorators: { entity: [{ type: i0.Input, args: [{ isSignal: true, alias: "entity", required: false }] }], entityRtId: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityRtId", required: false }] }], entityCkTypeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityCkTypeId", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }] } });
|
|
5054
|
+
|
|
4738
5055
|
// noinspection JSUnusedGlobalSymbols
|
|
4739
5056
|
class OctoGraphQlDataSource extends DataSourceTyped {
|
|
4740
5057
|
_searchFilterAttributePaths = [];
|
|
@@ -6021,7 +6338,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
6021
6338
|
}] });
|
|
6022
6339
|
|
|
6023
6340
|
class EntitySelectorDialogComponent {
|
|
6024
|
-
|
|
6341
|
+
windowRef = inject(WindowRef);
|
|
6025
6342
|
treeDataSource = inject(RuntimeBrowserDataSource);
|
|
6026
6343
|
data = {};
|
|
6027
6344
|
selectedEntity = null;
|
|
@@ -6047,55 +6364,53 @@ class EntitySelectorDialogComponent {
|
|
|
6047
6364
|
ckTypeId: this.selectedEntity.ckTypeId,
|
|
6048
6365
|
name: this.selectedEntity.name,
|
|
6049
6366
|
};
|
|
6050
|
-
this.
|
|
6367
|
+
this.windowRef.close(result);
|
|
6051
6368
|
}
|
|
6052
6369
|
}
|
|
6053
6370
|
onCancel() {
|
|
6054
|
-
this.
|
|
6371
|
+
this.windowRef.close();
|
|
6055
6372
|
}
|
|
6056
6373
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EntitySelectorDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6057
6374
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: EntitySelectorDialogComponent, isStandalone: true, selector: "mm-entity-selector-dialog", providers: [RuntimeBrowserDataSource], ngImport: i0, template: `
|
|
6058
6375
|
<div class="entity-selector">
|
|
6059
|
-
<div class="
|
|
6060
|
-
<
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6376
|
+
<div class="entity-selector-body">
|
|
6377
|
+
<div class="tree-section">
|
|
6378
|
+
<mm-tree-view
|
|
6379
|
+
[dataSource]="treeDataSource"
|
|
6380
|
+
(nodeSelected)="onNodeSelected($event)"
|
|
6381
|
+
></mm-tree-view>
|
|
6382
|
+
</div>
|
|
6065
6383
|
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6384
|
+
@if (selectedEntity) {
|
|
6385
|
+
<div class="selection-preview">
|
|
6386
|
+
<div class="preview-row">
|
|
6387
|
+
<span class="preview-label">Name:</span>
|
|
6388
|
+
<span class="preview-value">{{ selectedEntity.name || '\u2014' }}</span>
|
|
6389
|
+
</div>
|
|
6390
|
+
<div class="preview-row">
|
|
6391
|
+
<span class="preview-label">Type:</span>
|
|
6392
|
+
<span class="preview-value monospace">{{ selectedEntity.ckTypeId }}</span>
|
|
6393
|
+
</div>
|
|
6394
|
+
<div class="preview-row">
|
|
6395
|
+
<span class="preview-label">RtId:</span>
|
|
6396
|
+
<span class="preview-value monospace">{{ selectedEntity.rtId }}</span>
|
|
6397
|
+
</div>
|
|
6075
6398
|
</div>
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6399
|
+
} @else {
|
|
6400
|
+
<div class="selection-hint">
|
|
6401
|
+
Select an entity from the tree above.
|
|
6079
6402
|
</div>
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
@if (!selectedEntity) {
|
|
6084
|
-
<div class="selection-hint">
|
|
6085
|
-
Select an entity from the tree above.
|
|
6086
|
-
</div>
|
|
6087
|
-
}
|
|
6403
|
+
}
|
|
6404
|
+
</div>
|
|
6088
6405
|
|
|
6089
6406
|
<div class="dialog-actions">
|
|
6407
|
+
<button kendoButton (click)="onCancel()">Cancel</button>
|
|
6090
6408
|
<button kendoButton themeColor="primary" [disabled]="!selectedEntity" (click)="onConfirm()">
|
|
6091
6409
|
Select
|
|
6092
6410
|
</button>
|
|
6093
|
-
<button kendoButton (click)="onCancel()">
|
|
6094
|
-
Cancel
|
|
6095
|
-
</button>
|
|
6096
6411
|
</div>
|
|
6097
6412
|
</div>
|
|
6098
|
-
`, isInline: true, styles: [".entity-selector{display:flex;flex-direction:column;height:
|
|
6413
|
+
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.entity-selector{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;box-sizing:border-box}.entity-selector-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.tree-section{flex:1;min-height:0;overflow:auto;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface, #ffffff)}.selection-preview{padding:10px 12px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);display:flex;flex-direction:column;gap:4px;flex-shrink:0}.preview-row{display:flex;gap:8px}.preview-row .preview-label{font-weight:600;min-width:45px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.preview-row .preview-value{font-size:.85rem}.preview-row .monospace{font-family:monospace}.selection-hint{text-align:center;padding:8px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem;flex-shrink:0}.dialog-actions{flex:0 0 auto;display:flex;gap:8px;justify-content:flex-end;padding:10px 14px;border-top:1px solid var(--kendo-color-border, #dee2e6);background:var(--kendo-color-surface, transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: TreeComponent, selector: "mm-tree-view", inputs: ["dataSource"], outputs: ["nodeSelected", "nodeClick", "nodeDoubleClick", "nodeDrop", "expand", "collapse"] }] });
|
|
6099
6414
|
}
|
|
6100
6415
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EntitySelectorDialogComponent, decorators: [{
|
|
6101
6416
|
type: Component,
|
|
@@ -6105,66 +6420,77 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
6105
6420
|
TreeComponent,
|
|
6106
6421
|
], providers: [RuntimeBrowserDataSource], template: `
|
|
6107
6422
|
<div class="entity-selector">
|
|
6108
|
-
<div class="
|
|
6109
|
-
<
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6423
|
+
<div class="entity-selector-body">
|
|
6424
|
+
<div class="tree-section">
|
|
6425
|
+
<mm-tree-view
|
|
6426
|
+
[dataSource]="treeDataSource"
|
|
6427
|
+
(nodeSelected)="onNodeSelected($event)"
|
|
6428
|
+
></mm-tree-view>
|
|
6429
|
+
</div>
|
|
6114
6430
|
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6431
|
+
@if (selectedEntity) {
|
|
6432
|
+
<div class="selection-preview">
|
|
6433
|
+
<div class="preview-row">
|
|
6434
|
+
<span class="preview-label">Name:</span>
|
|
6435
|
+
<span class="preview-value">{{ selectedEntity.name || '\u2014' }}</span>
|
|
6436
|
+
</div>
|
|
6437
|
+
<div class="preview-row">
|
|
6438
|
+
<span class="preview-label">Type:</span>
|
|
6439
|
+
<span class="preview-value monospace">{{ selectedEntity.ckTypeId }}</span>
|
|
6440
|
+
</div>
|
|
6441
|
+
<div class="preview-row">
|
|
6442
|
+
<span class="preview-label">RtId:</span>
|
|
6443
|
+
<span class="preview-value monospace">{{ selectedEntity.rtId }}</span>
|
|
6444
|
+
</div>
|
|
6124
6445
|
</div>
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6446
|
+
} @else {
|
|
6447
|
+
<div class="selection-hint">
|
|
6448
|
+
Select an entity from the tree above.
|
|
6128
6449
|
</div>
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
@if (!selectedEntity) {
|
|
6133
|
-
<div class="selection-hint">
|
|
6134
|
-
Select an entity from the tree above.
|
|
6135
|
-
</div>
|
|
6136
|
-
}
|
|
6450
|
+
}
|
|
6451
|
+
</div>
|
|
6137
6452
|
|
|
6138
6453
|
<div class="dialog-actions">
|
|
6454
|
+
<button kendoButton (click)="onCancel()">Cancel</button>
|
|
6139
6455
|
<button kendoButton themeColor="primary" [disabled]="!selectedEntity" (click)="onConfirm()">
|
|
6140
6456
|
Select
|
|
6141
6457
|
</button>
|
|
6142
|
-
<button kendoButton (click)="onCancel()">
|
|
6143
|
-
Cancel
|
|
6144
|
-
</button>
|
|
6145
6458
|
</div>
|
|
6146
6459
|
</div>
|
|
6147
|
-
`, styles: [".entity-selector{display:flex;flex-direction:column;height:
|
|
6460
|
+
`, styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.entity-selector{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;box-sizing:border-box}.entity-selector-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.tree-section{flex:1;min-height:0;overflow:auto;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface, #ffffff)}.selection-preview{padding:10px 12px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);display:flex;flex-direction:column;gap:4px;flex-shrink:0}.preview-row{display:flex;gap:8px}.preview-row .preview-label{font-weight:600;min-width:45px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.preview-row .preview-value{font-size:.85rem}.preview-row .monospace{font-family:monospace}.selection-hint{text-align:center;padding:8px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem;flex-shrink:0}.dialog-actions{flex:0 0 auto;display:flex;gap:8px;justify-content:flex-end;padding:10px 14px;border-top:1px solid var(--kendo-color-border, #dee2e6);background:var(--kendo-color-surface, transparent)}\n"] }]
|
|
6148
6461
|
}] });
|
|
6149
6462
|
|
|
6463
|
+
const DIALOG_KEY$1 = 'entity-selector';
|
|
6464
|
+
const DEFAULT_SIZE$1 = { width: 600, height: 680 };
|
|
6465
|
+
const MIN_SIZE = { width: 480, height: 540 };
|
|
6150
6466
|
class EntitySelectorDialogService {
|
|
6151
|
-
|
|
6467
|
+
windowService = inject(WindowService);
|
|
6468
|
+
windowStateService = inject(WindowStateService);
|
|
6469
|
+
/**
|
|
6470
|
+
* Opens the entity selector as a Kendo Window so it stacks correctly above
|
|
6471
|
+
* other Kendo Windows (e.g. the Mapping-Edit dialog calls this from its
|
|
6472
|
+
* own Window — Kendo Dialogs would land underneath because Window and
|
|
6473
|
+
* Dialog use different z-index ranges).
|
|
6474
|
+
*/
|
|
6152
6475
|
async openEntitySelector(data) {
|
|
6153
|
-
const
|
|
6476
|
+
const size = this.windowStateService.resolveWindowSize(DIALOG_KEY$1, DEFAULT_SIZE$1, MIN_SIZE);
|
|
6477
|
+
const windowRef = this.windowService.open({
|
|
6154
6478
|
content: EntitySelectorDialogComponent,
|
|
6155
|
-
width:
|
|
6156
|
-
height:
|
|
6157
|
-
minWidth:
|
|
6158
|
-
minHeight:
|
|
6479
|
+
width: size.width,
|
|
6480
|
+
height: size.height,
|
|
6481
|
+
minWidth: MIN_SIZE.width,
|
|
6482
|
+
minHeight: MIN_SIZE.height,
|
|
6483
|
+
resizable: true,
|
|
6159
6484
|
title: data?.title ?? 'Select Target Entity',
|
|
6160
6485
|
});
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6486
|
+
this.windowStateService.applyModalBehavior(DIALOG_KEY$1, windowRef);
|
|
6487
|
+
const contentRef = windowRef.content;
|
|
6488
|
+
if (data && contentRef?.instance) {
|
|
6489
|
+
contentRef.instance.data = data;
|
|
6164
6490
|
}
|
|
6165
6491
|
try {
|
|
6166
|
-
const result = await firstValueFrom(
|
|
6167
|
-
if (result instanceof
|
|
6492
|
+
const result = await firstValueFrom(windowRef.result);
|
|
6493
|
+
if (result instanceof WindowCloseResult) {
|
|
6168
6494
|
return { confirmed: false };
|
|
6169
6495
|
}
|
|
6170
6496
|
if (result && typeof result === 'object' && 'rtId' in result) {
|
|
@@ -6980,7 +7306,7 @@ class FieldFilterEditorComponent {
|
|
|
6980
7306
|
</div>
|
|
6981
7307
|
}
|
|
6982
7308
|
</div>
|
|
6983
|
-
`, isInline: true, styles: [".field-filter-editor{display:flex;flex-direction:column;gap:10px}.attribute-options{display:flex;align-items:center;gap:16px;font-size:.85rem}.inline-checkbox{display:flex;align-items:center;gap:6px;cursor:pointer;font-weight:400}.inline-field{display:flex;align-items:center;gap:6px;font-weight:400}.depth-input{width:80px}.loading-hint{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.toolbar{display:flex;gap:10px;margin-bottom:5px}.filter-grid{border:1px solid #d5d5d5}.filter-grid ::ng-deep .k-grid-header .k-header{font-weight:600}.checkbox-column{text-align:center}.attribute-dropdown,.operator-dropdown,.attribute-input,.value-input{width:100%}.attribute-item{display:flex;justify-content:space-between;align-items:center;width:100%}.attribute-path{flex:1}.attribute-type{font-size:11px;color:#888;margin-left:8px;padding:2px 6px;background:#f0f0f0;border-radius:3px}.empty-state{padding:40px;text-align:center;border:1px dashed;border-radius:8px;font-family:Montserrat,sans-serif;font-size:.9rem}.empty-state p{margin:0}.value-cell{display:flex;gap:4px;align-items:center}.value-cell .value-input{flex:1}.variable-toggle{flex-shrink:0}.variable-item{display:flex;flex-direction:column;gap:2px}.variable-name{font-family:monospace;font-weight:500}.variable-label{font-size:11px;color:var(--kendo-color-subtle, #888)}.variable-value{font-family:monospace;color:var(--kendo-color-primary, #0d6efd)}.variable-placeholder{color:var(--kendo-color-subtle, #888)}.variable-dropdown ::ng-deep .k-input-value-text{font-family:monospace}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: i3.SelectionDirective, selector: "[kendoGridSelectBy]" }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "directive", type: i3.HeaderTemplateDirective, selector: "[kendoGridHeaderTemplate]" }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type:
|
|
7309
|
+
`, isInline: true, styles: [".field-filter-editor{display:flex;flex-direction:column;gap:10px}.attribute-options{display:flex;align-items:center;gap:16px;font-size:.85rem}.inline-checkbox{display:flex;align-items:center;gap:6px;cursor:pointer;font-weight:400}.inline-field{display:flex;align-items:center;gap:6px;font-weight:400}.depth-input{width:80px}.loading-hint{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.toolbar{display:flex;gap:10px;margin-bottom:5px}.filter-grid{border:1px solid #d5d5d5}.filter-grid ::ng-deep .k-grid-header .k-header{font-weight:600}.checkbox-column{text-align:center}.attribute-dropdown,.operator-dropdown,.attribute-input,.value-input{width:100%}.attribute-item{display:flex;justify-content:space-between;align-items:center;width:100%}.attribute-path{flex:1}.attribute-type{font-size:11px;color:#888;margin-left:8px;padding:2px 6px;background:#f0f0f0;border-radius:3px}.empty-state{padding:40px;text-align:center;border:1px dashed;border-radius:8px;font-family:Montserrat,sans-serif;font-size:.9rem}.empty-state p{margin:0}.value-cell{display:flex;gap:4px;align-items:center}.value-cell .value-input{flex:1}.variable-toggle{flex-shrink:0}.variable-item{display:flex;flex-direction:column;gap:2px}.variable-name{font-family:monospace;font-weight:500}.variable-label{font-size:11px;color:var(--kendo-color-subtle, #888)}.variable-value{font-family:monospace;color:var(--kendo-color-primary, #0d6efd)}.variable-placeholder{color:var(--kendo-color-subtle, #888)}.variable-dropdown ::ng-deep .k-input-value-text{font-family:monospace}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: i3.SelectionDirective, selector: "[kendoGridSelectBy]" }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "directive", type: i3.HeaderTemplateDirective, selector: "[kendoGridHeaderTemplate]" }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i3$1.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i3$1.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i3$1.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "directive", type: i3$1.ValueTemplateDirective, selector: "[kendoDropDownListValueTemplate],[kendoDropDownTreeValueTemplate]" }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: i5.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i5.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "ngmodule", type: DateInputsModule }, { kind: "component", type: i7.DateTimePickerComponent, selector: "kendo-datetimepicker", inputs: ["focusableId", "weekDaysFormat", "showOtherMonthDays", "value", "format", "twoDigitYearMax", "tabindex", "disabledDates", "popupSettings", "adaptiveTitle", "adaptiveSubtitle", "disabled", "readonly", "readOnlyInput", "cancelButton", "formatPlaceholder", "placeholder", "steps", "focusedDate", "calendarType", "animateCalendarNavigation", "weekNumber", "min", "max", "rangeValidation", "disabledDatesValidation", "incompleteDateValidation", "autoCorrectParts", "autoSwitchParts", "autoSwitchKeys", "enableMouseWheel", "allowCaretMode", "clearButton", "autoFill", "adaptiveMode", "inputAttributes", "defaultTab", "size", "rounded", "fillMode", "headerTemplate", "footerTemplate", "footer"], outputs: ["valueChange", "open", "close", "focus", "blur", "escape"], exportAs: ["kendo-datetimepicker"] }, { kind: "ngmodule", type: IconsModule }, { kind: "ngmodule", type: PopupModule }, { kind: "ngmodule", type: IntlModule }] });
|
|
6984
7310
|
}
|
|
6985
7311
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldFilterEditorComponent, decorators: [{
|
|
6986
7312
|
type: Component,
|
|
@@ -7541,47 +7867,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
7541
7867
|
}]
|
|
7542
7868
|
}], ctorParameters: () => [{ type: i1$3.Apollo }] });
|
|
7543
7869
|
|
|
7544
|
-
const GetRuntimeEntityByIdDocumentDto = gql `
|
|
7545
|
-
query getRuntimeEntityById($rtId: OctoObjectId!, $ckTypeId: String!) {
|
|
7546
|
-
runtime {
|
|
7547
|
-
runtimeEntities(ckId: $ckTypeId, rtId: $rtId) {
|
|
7548
|
-
items {
|
|
7549
|
-
rtId
|
|
7550
|
-
ckTypeId
|
|
7551
|
-
rtWellKnownName
|
|
7552
|
-
rtCreationDateTime
|
|
7553
|
-
rtChangedDateTime
|
|
7554
|
-
attributes(resolveEnumValuesToNames: true) {
|
|
7555
|
-
items {
|
|
7556
|
-
attributeName
|
|
7557
|
-
value
|
|
7558
|
-
}
|
|
7559
|
-
}
|
|
7560
|
-
associations {
|
|
7561
|
-
definitions(direction: ANY, first: 0) {
|
|
7562
|
-
totalCount
|
|
7563
|
-
}
|
|
7564
|
-
}
|
|
7565
|
-
}
|
|
7566
|
-
}
|
|
7567
|
-
}
|
|
7568
|
-
}
|
|
7569
|
-
`;
|
|
7570
|
-
class GetRuntimeEntityByIdDtoGQL extends i1$3.Query {
|
|
7571
|
-
document = GetRuntimeEntityByIdDocumentDto;
|
|
7572
|
-
constructor(apollo) {
|
|
7573
|
-
super(apollo);
|
|
7574
|
-
}
|
|
7575
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetRuntimeEntityByIdDtoGQL, deps: [{ token: i1$3.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7576
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetRuntimeEntityByIdDtoGQL, providedIn: 'root' });
|
|
7577
|
-
}
|
|
7578
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetRuntimeEntityByIdDtoGQL, decorators: [{
|
|
7579
|
-
type: Injectable,
|
|
7580
|
-
args: [{
|
|
7581
|
-
providedIn: 'root'
|
|
7582
|
-
}]
|
|
7583
|
-
}], ctorParameters: () => [{ type: i1$3.Apollo }] });
|
|
7584
|
-
|
|
7585
7870
|
const GetBinaryInfoDocumentDto = gql `
|
|
7586
7871
|
query getBinaryInfo($rtId: OctoObjectId!) {
|
|
7587
7872
|
runtime {
|
|
@@ -7816,7 +8101,14 @@ const DEFAULT_RUNTIME_BROWSER_MESSAGES = {
|
|
|
7816
8101
|
|
|
7817
8102
|
class DataMappingListComponent {
|
|
7818
8103
|
mappings = [];
|
|
7819
|
-
|
|
8104
|
+
/**
|
|
8105
|
+
* The runtime entity whose data points feed the Source Data Point picker.
|
|
8106
|
+
* Typically the entity displayed in the surrounding detail pane (each mapping
|
|
8107
|
+
* row shares the same source — the entity being inspected). The picker reads
|
|
8108
|
+
* the States/DataPoints RecordArray off this entity to populate its options.
|
|
8109
|
+
* When null, the picker falls back to the {@link DEFAULT_DATA_POINT} default.
|
|
8110
|
+
*/
|
|
8111
|
+
sourceEntity = null;
|
|
7820
8112
|
/**
|
|
7821
8113
|
* Optional expression validator function. When provided, expressions are validated
|
|
7822
8114
|
* on change and feedback (error or preview) is shown below the expression field.
|
|
@@ -7827,6 +8119,11 @@ class DataMappingListComponent {
|
|
|
7827
8119
|
addMapping = new EventEmitter();
|
|
7828
8120
|
removeMapping = new EventEmitter();
|
|
7829
8121
|
selectTarget = new EventEmitter();
|
|
8122
|
+
/**
|
|
8123
|
+
* @deprecated Source attribute selection is now handled inline by
|
|
8124
|
+
* {@link DataPointPickerComponent}; this output is no longer emitted.
|
|
8125
|
+
* Kept on the public surface so existing host bindings keep compiling.
|
|
8126
|
+
*/
|
|
7830
8127
|
selectSourceAttribute = new EventEmitter();
|
|
7831
8128
|
selectTargetAttribute = new EventEmitter();
|
|
7832
8129
|
mappingChanged = new EventEmitter();
|
|
@@ -7855,7 +8152,7 @@ class DataMappingListComponent {
|
|
|
7855
8152
|
trashIcon = trashIcon;
|
|
7856
8153
|
linkIcon = hyperlinkOpenIcon;
|
|
7857
8154
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataMappingListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7858
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataMappingListComponent, isStandalone: true, selector: "mm-data-mapping-list", inputs: { mappings: "mappings",
|
|
8155
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataMappingListComponent, isStandalone: true, selector: "mm-data-mapping-list", inputs: { mappings: "mappings", sourceEntity: "sourceEntity", expressionValidator: "expressionValidator" }, outputs: { addMapping: "addMapping", removeMapping: "removeMapping", selectTarget: "selectTarget", selectSourceAttribute: "selectSourceAttribute", selectTargetAttribute: "selectTargetAttribute", mappingChanged: "mappingChanged", navigateToTarget: "navigateToTarget", saveAll: "saveAll" }, ngImport: i0, template: `
|
|
7859
8156
|
<div class="mapping-list">
|
|
7860
8157
|
<div class="mapping-toolbar">
|
|
7861
8158
|
<button kendoButton themeColor="primary" size="small" [svgIcon]="plusIcon"
|
|
@@ -7878,20 +8175,11 @@ class DataMappingListComponent {
|
|
|
7878
8175
|
<div class="mapping-card-body">
|
|
7879
8176
|
<div class="mapping-row">
|
|
7880
8177
|
<label>Source Data Point</label>
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
(valueChange)="onSourceDataPointChange(mapping, $event)">
|
|
7887
|
-
</kendo-dropdownlist>
|
|
7888
|
-
} @else {
|
|
7889
|
-
<div class="target-display">
|
|
7890
|
-
<span class="target-info">{{ mapping.sourceAttributePath || '(not set)' }}</span>
|
|
7891
|
-
<button kendoButton fillMode="flat" size="small"
|
|
7892
|
-
(click)="selectSourceAttribute.emit(mapping)">Select...</button>
|
|
7893
|
-
</div>
|
|
7894
|
-
}
|
|
8178
|
+
<mm-data-point-picker
|
|
8179
|
+
[entity]="sourceEntity"
|
|
8180
|
+
[value]="mapping.sourceAttributePath || 'currentValue'"
|
|
8181
|
+
(valueChange)="onSourceDataPointChange(mapping, $event)">
|
|
8182
|
+
</mm-data-point-picker>
|
|
7895
8183
|
</div>
|
|
7896
8184
|
<div class="mapping-row">
|
|
7897
8185
|
<label>Expression</label>
|
|
@@ -7956,7 +8244,7 @@ class DataMappingListComponent {
|
|
|
7956
8244
|
</div>
|
|
7957
8245
|
}
|
|
7958
8246
|
</div>
|
|
7959
|
-
`, isInline: true, styles: [".mapping-list{display:flex;flex-direction:column;gap:12px}.mapping-toolbar{display:flex;justify-content:flex-end}.mapping-empty-hint{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.mapping-card{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.mapping-card-header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.mapping-card-header .mapping-name{flex:1}.mapping-card-body{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.mapping-row{display:flex;flex-direction:column;gap:3px}.mapping-row label{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.target-display{display:flex;align-items:center;gap:8px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);min-height:30px}.target-display .target-info{flex:1;font-size:.85rem;font-family:monospace}.mapping-actions{display:flex;justify-content:flex-end;padding-top:4px}.entity-info-display{display:flex;flex-direction:column;gap:2px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa)}.entity-info-main{display:flex;align-items:center;gap:4px}.entity-name{flex:1;font-size:.85rem;font-weight:600}.entity-info-details{display:flex;align-items:center;gap:2px;font-size:.7rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d)}.entity-detail-separator{color:var(--kendo-color-subtle, #6c757d)}.expression-feedback{font-size:.75rem;padding:2px 0;font-family:monospace}.expression-error{color:var(--kendo-color-error, #dc3545)}.expression-success{color:var(--kendo-color-success, #28a745)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type:
|
|
8247
|
+
`, isInline: true, styles: [".mapping-list{display:flex;flex-direction:column;gap:12px}.mapping-toolbar{display:flex;justify-content:flex-end}.mapping-empty-hint{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.mapping-card{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.mapping-card-header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.mapping-card-header .mapping-name{flex:1}.mapping-card-body{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.mapping-row{display:flex;flex-direction:column;gap:3px}.mapping-row label{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.target-display{display:flex;align-items:center;gap:8px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);min-height:30px}.target-display .target-info{flex:1;font-size:.85rem;font-family:monospace}.mapping-actions{display:flex;justify-content:flex-end;padding-top:4px}.entity-info-display{display:flex;flex-direction:column;gap:2px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa)}.entity-info-main{display:flex;align-items:center;gap:4px}.entity-name{flex:1;font-size:.85rem;font-weight:600}.entity-info-details{display:flex;align-items:center;gap:2px;font-size:.7rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d)}.entity-detail-separator{color:var(--kendo-color-subtle, #6c757d)}.expression-feedback{font-size:.75rem;padding:2px 0;font-family:monospace}.expression-error{color:var(--kendo-color-error, #dc3545)}.expression-success{color:var(--kendo-color-success, #28a745)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: TextBoxModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: DataPointPickerComponent, selector: "mm-data-point-picker", inputs: ["entity", "entityRtId", "entityCkTypeId", "value", "placeholder", "disabled"], outputs: ["valueChange", "filterChange"] }] });
|
|
7960
8248
|
}
|
|
7961
8249
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataMappingListComponent, decorators: [{
|
|
7962
8250
|
type: Component,
|
|
@@ -7964,9 +8252,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
7964
8252
|
CommonModule,
|
|
7965
8253
|
FormsModule,
|
|
7966
8254
|
ButtonModule,
|
|
7967
|
-
DropDownListModule,
|
|
7968
8255
|
TextBoxModule,
|
|
7969
8256
|
SVGIconModule,
|
|
8257
|
+
DataPointPickerComponent,
|
|
7970
8258
|
], template: `
|
|
7971
8259
|
<div class="mapping-list">
|
|
7972
8260
|
<div class="mapping-toolbar">
|
|
@@ -7990,20 +8278,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
7990
8278
|
<div class="mapping-card-body">
|
|
7991
8279
|
<div class="mapping-row">
|
|
7992
8280
|
<label>Source Data Point</label>
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
(valueChange)="onSourceDataPointChange(mapping, $event)">
|
|
7999
|
-
</kendo-dropdownlist>
|
|
8000
|
-
} @else {
|
|
8001
|
-
<div class="target-display">
|
|
8002
|
-
<span class="target-info">{{ mapping.sourceAttributePath || '(not set)' }}</span>
|
|
8003
|
-
<button kendoButton fillMode="flat" size="small"
|
|
8004
|
-
(click)="selectSourceAttribute.emit(mapping)">Select...</button>
|
|
8005
|
-
</div>
|
|
8006
|
-
}
|
|
8281
|
+
<mm-data-point-picker
|
|
8282
|
+
[entity]="sourceEntity"
|
|
8283
|
+
[value]="mapping.sourceAttributePath || 'currentValue'"
|
|
8284
|
+
(valueChange)="onSourceDataPointChange(mapping, $event)">
|
|
8285
|
+
</mm-data-point-picker>
|
|
8007
8286
|
</div>
|
|
8008
8287
|
<div class="mapping-row">
|
|
8009
8288
|
<label>Expression</label>
|
|
@@ -8071,7 +8350,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
8071
8350
|
`, styles: [".mapping-list{display:flex;flex-direction:column;gap:12px}.mapping-toolbar{display:flex;justify-content:flex-end}.mapping-empty-hint{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.mapping-card{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.mapping-card-header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.mapping-card-header .mapping-name{flex:1}.mapping-card-body{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.mapping-row{display:flex;flex-direction:column;gap:3px}.mapping-row label{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.target-display{display:flex;align-items:center;gap:8px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);min-height:30px}.target-display .target-info{flex:1;font-size:.85rem;font-family:monospace}.mapping-actions{display:flex;justify-content:flex-end;padding-top:4px}.entity-info-display{display:flex;flex-direction:column;gap:2px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa)}.entity-info-main{display:flex;align-items:center;gap:4px}.entity-name{flex:1;font-size:.85rem;font-weight:600}.entity-info-details{display:flex;align-items:center;gap:2px;font-size:.7rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d)}.entity-detail-separator{color:var(--kendo-color-subtle, #6c757d)}.expression-feedback{font-size:.75rem;padding:2px 0;font-family:monospace}.expression-error{color:var(--kendo-color-error, #dc3545)}.expression-success{color:var(--kendo-color-success, #28a745)}\n"] }]
|
|
8072
8351
|
}], propDecorators: { mappings: [{
|
|
8073
8352
|
type: Input
|
|
8074
|
-
}],
|
|
8353
|
+
}], sourceEntity: [{
|
|
8075
8354
|
type: Input
|
|
8076
8355
|
}], expressionValidator: [{
|
|
8077
8356
|
type: Input
|
|
@@ -8103,7 +8382,6 @@ class EntityDetailViewComponent {
|
|
|
8103
8382
|
}
|
|
8104
8383
|
showDataMapping = true;
|
|
8105
8384
|
dataMappings = [];
|
|
8106
|
-
sourceDataPoints = [];
|
|
8107
8385
|
/**
|
|
8108
8386
|
* Optional expression validator function passed through to DataMappingListComponent.
|
|
8109
8387
|
* When provided, mapping expressions are validated on change with visual feedback.
|
|
@@ -8115,6 +8393,11 @@ class EntityDetailViewComponent {
|
|
|
8115
8393
|
addMappingRequested = new EventEmitter();
|
|
8116
8394
|
removeMappingRequested = new EventEmitter();
|
|
8117
8395
|
selectMappingTarget = new EventEmitter();
|
|
8396
|
+
/**
|
|
8397
|
+
* @deprecated The source attribute is now picked inline by the
|
|
8398
|
+
* DataPointPickerComponent; this output is no longer emitted.
|
|
8399
|
+
* Kept for binding compatibility with existing hosts.
|
|
8400
|
+
*/
|
|
8118
8401
|
selectSourceAttributeRequested = new EventEmitter();
|
|
8119
8402
|
selectTargetAttributeRequested = new EventEmitter();
|
|
8120
8403
|
mappingChanged = new EventEmitter();
|
|
@@ -8377,7 +8660,7 @@ class EntityDetailViewComponent {
|
|
|
8377
8660
|
}
|
|
8378
8661
|
}
|
|
8379
8662
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EntityDetailViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8380
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: EntityDetailViewComponent, isStandalone: true, selector: "mm-entity-detail-view", inputs: { entity: "entity", loading: "loading", error: "error", showHeader: "showHeader", messages: "messages", showDataMapping: "showDataMapping", dataMappings: "dataMappings",
|
|
8663
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: EntityDetailViewComponent, isStandalone: true, selector: "mm-entity-detail-view", inputs: { entity: "entity", loading: "loading", error: "error", showHeader: "showHeader", messages: "messages", showDataMapping: "showDataMapping", dataMappings: "dataMappings", expressionValidator: "expressionValidator" }, outputs: { retry: "retry", propertyChange: "propertyChange", navigateToEntity: "navigateToEntity", addMappingRequested: "addMappingRequested", removeMappingRequested: "removeMappingRequested", selectMappingTarget: "selectMappingTarget", selectSourceAttributeRequested: "selectSourceAttributeRequested", selectTargetAttributeRequested: "selectTargetAttributeRequested", mappingChanged: "mappingChanged", saveAllMappingsRequested: "saveAllMappingsRequested" }, viewQueries: [{ propertyName: "associationsDataSource", first: true, predicate: ["associationsDir"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
8381
8664
|
@if (loading) {
|
|
8382
8665
|
<div class="loading-state">
|
|
8383
8666
|
<p>{{ _messages.loadingEntityDetails }}</p>
|
|
@@ -8593,12 +8876,11 @@ class EntityDetailViewComponent {
|
|
|
8593
8876
|
<div class="tab-content mapping-tab">
|
|
8594
8877
|
<mm-data-mapping-list
|
|
8595
8878
|
[mappings]="dataMappings"
|
|
8596
|
-
[
|
|
8879
|
+
[sourceEntity]="entity"
|
|
8597
8880
|
[expressionValidator]="expressionValidator"
|
|
8598
8881
|
(addMapping)="addMappingRequested.emit()"
|
|
8599
8882
|
(removeMapping)="removeMappingRequested.emit($event)"
|
|
8600
8883
|
(selectTarget)="selectMappingTarget.emit($event)"
|
|
8601
|
-
(selectSourceAttribute)="selectSourceAttributeRequested.emit($event)"
|
|
8602
8884
|
(selectTargetAttribute)="selectTargetAttributeRequested.emit($event)"
|
|
8603
8885
|
(mappingChanged)="mappingChanged.emit($event)"
|
|
8604
8886
|
(navigateToTarget)="onNavigateToMappingTarget($event)"
|
|
@@ -8611,7 +8893,7 @@ class EntityDetailViewComponent {
|
|
|
8611
8893
|
</kendo-tabstrip>
|
|
8612
8894
|
</div>
|
|
8613
8895
|
}
|
|
8614
|
-
`, isInline: true, styles: [".loading-state,.error-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;text-align:center;min-height:200px}.loading-state .error-message,.error-state .error-message{margin-bottom:16px;font-family:Roboto,sans-serif;color:#e74c3c}.entity-content{flex:1;display:flex;flex-direction:column;gap:24px}.entity-content .basic-info-card{padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.entity-content .basic-info-card:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.entity-content .basic-info-card .basic-info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px}.entity-content .basic-info-card .basic-info-grid .info-item{display:flex;flex-direction:column;gap:8px}.entity-content .basic-info-card .basic-info-grid .info-item label{font-family:Montserrat,sans-serif;font-weight:600;font-size:.75rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80)}.entity-content .basic-info-card .basic-info-grid .info-item .value{font-family:Roboto Mono,monospace;font-size:.875rem;color:rgba(var(--octo-text-color),.9);word-break:break-word;background:var(--deep-sea-40);padding:8px 12px;border-radius:4px;border:1px solid var(--octo-mint-15)}.entity-content .basic-info-card .basic-info-grid .info-item.with-action .value-with-action{display:flex;align-items:center}.entity-content .basic-info-card .basic-info-grid .info-item.with-action .value-with-action .value{flex:1;font-family:Roboto Mono,monospace;font-size:.875rem;color:rgba(var(--octo-text-color),.9);word-break:break-word}.entity-content .entity-tabs{flex:1;min-height:400px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;overflow:hidden;position:relative}.entity-content .entity-tabs:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);z-index:1}.entity-content .entity-tabs ::ng-deep .k-tabstrip{background:transparent}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{background:linear-gradient(90deg,var(--octo-mint-10),transparent);border-bottom:1px solid var(--octo-mint-20);padding-left:20px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item{background:transparent;border:none;color:rgba(var(--octo-text-color),.7);font-family:Montserrat,sans-serif;font-weight:600;font-size:.8rem;letter-spacing:.5px;text-transform:uppercase;padding:12px 20px;margin-right:4px;border-radius:4px 4px 0 0;transition:all .2s ease}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item:hover{background:var(--octo-mint-10);color:var(--octo-mint)}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item.k-active{background:linear-gradient(180deg,var(--octo-mint-20),transparent);color:var(--octo-mint);border-bottom:2px solid var(--octo-mint);box-shadow:0 0 10px var(--octo-mint-30)}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-content{background:transparent;border:none;padding:0}.entity-content .entity-tabs .tab-content{padding:20px 24px;height:100%;overflow-y:auto}.entity-content .entity-tabs .tab-content .empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 40px;text-align:center}.entity-content .entity-tabs .tab-content .empty-state kendo-svgicon{font-size:64px;margin-bottom:20px;color:var(--octo-mint-40);text-shadow:0 0 20px var(--octo-mint-30)}.entity-content .entity-tabs .tab-content .empty-state p{margin:0;font-family:Montserrat,sans-serif;font-size:.9rem;color:rgba(var(--octo-text-color),.5);letter-spacing:.5px}.entity-content .entity-tabs .tab-content.properties-tab{padding:0;height:100%}.entity-content .entity-tabs .tab-content.properties-tab mm-property-grid{display:block;height:100%;width:100%}.entity-content .entity-tabs .tab-content.associations-tab{display:flex;flex-direction:column;gap:16px;padding:0;height:100%}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:16px;padding:16px 20px;background:linear-gradient(90deg,var(--octo-mint-05),transparent);border-bottom:1px solid var(--octo-mint-20)}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group{display:flex;align-items:center;gap:10px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group label{font-family:Montserrat,sans-serif;font-weight:600;font-size:.75rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80);white-space:nowrap}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-dropdownlist{width:160px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-textbox{width:220px}.entity-content .entity-tabs .tab-content.associations-tab mm-list-view{flex:1;display:block;height:calc(100% - 70px)}.entity-content .entity-tabs .tab-content.mapping-tab{padding:12px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty{display:flex;flex-direction:column;align-items:center;padding:24px 20px;text-align:center}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty kendo-svgicon{font-size:36px;margin-bottom:10px;opacity:.5}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty p{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-config{display:flex;flex-direction:column;gap:20px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section .section-header{padding:8px 14px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section .section-body{padding:12px 14px;display:flex;flex-direction:column;gap:10px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-field{display:flex;flex-direction:column;gap:4px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-field label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display{display:flex;align-items:center;gap:8px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display .target-type{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace;padding:2px 6px;border-radius:3px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display .target-name{font-weight:600;flex:1}.entity-content .entity-tabs .tab-content.mapping-tab .field-hint{font-size:.7rem;color:var(--kendo-color-subtle, #6c757d);font-style:italic;line-height:1.3}.entity-content .entity-tabs .tab-content.mapping-tab .attribute-picker{display:flex;align-items:center;gap:8px;padding:4px 10px;border-radius:4px;border:1px solid var(--kendo-color-border, #dee2e6);background:var(--kendo-color-surface-alt, #f8f9fa);min-height:32px}.entity-content .entity-tabs .tab-content.mapping-tab .attribute-picker .attribute-value{flex:1;font-family:monospace;font-size:.85rem}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-actions{display:flex;gap:8px;justify-content:flex-end;padding-top:4px}@media(max-width:768px){.entity-content{gap:16px}.entity-content .basic-info-card{padding:16px 20px}.entity-content .basic-info-card .basic-info-grid{grid-template-columns:1fr;gap:16px}.entity-content .entity-tabs{min-height:300px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{padding-left:12px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item{padding:10px 14px;font-size:.75rem}.entity-content .entity-tabs .tab-content{padding:16px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar{flex-direction:column;align-items:flex-start;gap:12px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group{width:100%}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-dropdownlist,.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-textbox{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TabStripModule }, { kind: "component", type: i1$4.TabStripComponent, selector: "kendo-tabstrip", inputs: ["height", "animate", "tabAlignment", "tabPosition", "keepTabContent", "closable", "scrollable", "size", "closeIcon", "closeIconClass", "closeSVGIcon", "showContentArea"], outputs: ["tabSelect", "tabClose", "tabScroll"], exportAs: ["kendoTabStrip"] }, { kind: "component", type: i1$4.TabStripTabComponent, selector: "kendo-tabstrip-tab", inputs: ["title", "disabled", "cssClass", "cssStyle", "selected", "closable", "closeIcon", "closeIconClass", "closeSVGIcon"], exportAs: ["kendoTabStripTab"] }, { kind: "directive", type: i1$4.TabContentDirective, selector: "[kendoTabContent]" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$4.CardComponent, selector: "kendo-card", inputs: ["orientation", "width"] }, { kind: "component", type: i1$4.CardBodyComponent, selector: "kendo-card-body" }, { kind: "component", type: i1$4.CardHeaderComponent, selector: "kendo-card-header" }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: TextBoxModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: PropertyGridComponent, selector: "mm-property-grid", inputs: ["data", "config", "showTypeColumn"], outputs: ["propertyChange", "saveRequested", "binaryDownload"] }, { kind: "component", type: ListViewComponent, selector: "mm-list-view", inputs: ["pageSize", "skip", "rowIsClickable", "showRowCheckBoxes", "showRowSelectAllCheckBox", "contextMenuType", "leftToolbarActions", "rightToolbarActions", "actionCommandItems", "contextMenuCommandItems", "excelExportFileName", "pdfExportFileName", "pageable", "sortable", "rowFilterEnabled", "searchTextBoxEnabled", "rowClass", "messages", "selectable", "columns"], outputs: ["rowClicked"] }, { kind: "directive", type: EntityAssociationsDataSourceDirective, selector: "[mmEntityAssociationsDataSource]", exportAs: ["mmEntityAssociationsDataSource"] }, { kind: "component", type: DataMappingListComponent, selector: "mm-data-mapping-list", inputs: ["mappings", "sourceDataPoints", "expressionValidator"], outputs: ["addMapping", "removeMapping", "selectTarget", "selectSourceAttribute", "selectTargetAttribute", "mappingChanged", "navigateToTarget", "saveAll"] }] });
|
|
8896
|
+
`, isInline: true, styles: [".loading-state,.error-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;text-align:center;min-height:200px}.loading-state .error-message,.error-state .error-message{margin-bottom:16px;font-family:Roboto,sans-serif;color:#e74c3c}.entity-content{flex:1;display:flex;flex-direction:column;gap:24px}.entity-content .basic-info-card{padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.entity-content .basic-info-card:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.entity-content .basic-info-card .basic-info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px}.entity-content .basic-info-card .basic-info-grid .info-item{display:flex;flex-direction:column;gap:8px}.entity-content .basic-info-card .basic-info-grid .info-item label{font-family:Montserrat,sans-serif;font-weight:600;font-size:.75rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80)}.entity-content .basic-info-card .basic-info-grid .info-item .value{font-family:Roboto Mono,monospace;font-size:.875rem;color:rgba(var(--octo-text-color),.9);word-break:break-word;background:var(--deep-sea-40);padding:8px 12px;border-radius:4px;border:1px solid var(--octo-mint-15)}.entity-content .basic-info-card .basic-info-grid .info-item.with-action .value-with-action{display:flex;align-items:center}.entity-content .basic-info-card .basic-info-grid .info-item.with-action .value-with-action .value{flex:1;font-family:Roboto Mono,monospace;font-size:.875rem;color:rgba(var(--octo-text-color),.9);word-break:break-word}.entity-content .entity-tabs{flex:1;min-height:400px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;overflow:hidden;position:relative}.entity-content .entity-tabs:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);z-index:1}.entity-content .entity-tabs ::ng-deep .k-tabstrip{background:transparent}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{background:linear-gradient(90deg,var(--octo-mint-10),transparent);border-bottom:1px solid var(--octo-mint-20);padding-left:20px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item{background:transparent;border:none;color:rgba(var(--octo-text-color),.7);font-family:Montserrat,sans-serif;font-weight:600;font-size:.8rem;letter-spacing:.5px;text-transform:uppercase;padding:12px 20px;margin-right:4px;border-radius:4px 4px 0 0;transition:all .2s ease}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item:hover{background:var(--octo-mint-10);color:var(--octo-mint)}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item.k-active{background:linear-gradient(180deg,var(--octo-mint-20),transparent);color:var(--octo-mint);border-bottom:2px solid var(--octo-mint);box-shadow:0 0 10px var(--octo-mint-30)}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-content{background:transparent;border:none;padding:0}.entity-content .entity-tabs .tab-content{padding:20px 24px;height:100%;overflow-y:auto}.entity-content .entity-tabs .tab-content .empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 40px;text-align:center}.entity-content .entity-tabs .tab-content .empty-state kendo-svgicon{font-size:64px;margin-bottom:20px;color:var(--octo-mint-40);text-shadow:0 0 20px var(--octo-mint-30)}.entity-content .entity-tabs .tab-content .empty-state p{margin:0;font-family:Montserrat,sans-serif;font-size:.9rem;color:rgba(var(--octo-text-color),.5);letter-spacing:.5px}.entity-content .entity-tabs .tab-content.properties-tab{padding:0;height:100%}.entity-content .entity-tabs .tab-content.properties-tab mm-property-grid{display:block;height:100%;width:100%}.entity-content .entity-tabs .tab-content.associations-tab{display:flex;flex-direction:column;gap:16px;padding:0;height:100%}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:16px;padding:16px 20px;background:linear-gradient(90deg,var(--octo-mint-05),transparent);border-bottom:1px solid var(--octo-mint-20)}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group{display:flex;align-items:center;gap:10px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group label{font-family:Montserrat,sans-serif;font-weight:600;font-size:.75rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80);white-space:nowrap}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-dropdownlist{width:160px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-textbox{width:220px}.entity-content .entity-tabs .tab-content.associations-tab mm-list-view{flex:1;display:block;height:calc(100% - 70px)}.entity-content .entity-tabs .tab-content.mapping-tab{padding:12px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty{display:flex;flex-direction:column;align-items:center;padding:24px 20px;text-align:center}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty kendo-svgicon{font-size:36px;margin-bottom:10px;opacity:.5}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty p{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-config{display:flex;flex-direction:column;gap:20px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section .section-header{padding:8px 14px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section .section-body{padding:12px 14px;display:flex;flex-direction:column;gap:10px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-field{display:flex;flex-direction:column;gap:4px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-field label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display{display:flex;align-items:center;gap:8px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display .target-type{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace;padding:2px 6px;border-radius:3px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display .target-name{font-weight:600;flex:1}.entity-content .entity-tabs .tab-content.mapping-tab .field-hint{font-size:.7rem;color:var(--kendo-color-subtle, #6c757d);font-style:italic;line-height:1.3}.entity-content .entity-tabs .tab-content.mapping-tab .attribute-picker{display:flex;align-items:center;gap:8px;padding:4px 10px;border-radius:4px;border:1px solid var(--kendo-color-border, #dee2e6);background:var(--kendo-color-surface-alt, #f8f9fa);min-height:32px}.entity-content .entity-tabs .tab-content.mapping-tab .attribute-picker .attribute-value{flex:1;font-family:monospace;font-size:.85rem}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-actions{display:flex;gap:8px;justify-content:flex-end;padding-top:4px}@media(max-width:768px){.entity-content{gap:16px}.entity-content .basic-info-card{padding:16px 20px}.entity-content .basic-info-card .basic-info-grid{grid-template-columns:1fr;gap:16px}.entity-content .entity-tabs{min-height:300px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{padding-left:12px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item{padding:10px 14px;font-size:.75rem}.entity-content .entity-tabs .tab-content{padding:16px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar{flex-direction:column;align-items:flex-start;gap:12px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group{width:100%}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-dropdownlist,.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-textbox{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TabStripModule }, { kind: "component", type: i1$4.TabStripComponent, selector: "kendo-tabstrip", inputs: ["height", "animate", "tabAlignment", "tabPosition", "keepTabContent", "closable", "scrollable", "size", "closeIcon", "closeIconClass", "closeSVGIcon", "showContentArea"], outputs: ["tabSelect", "tabClose", "tabScroll"], exportAs: ["kendoTabStrip"] }, { kind: "component", type: i1$4.TabStripTabComponent, selector: "kendo-tabstrip-tab", inputs: ["title", "disabled", "cssClass", "cssStyle", "selected", "closable", "closeIcon", "closeIconClass", "closeSVGIcon"], exportAs: ["kendoTabStripTab"] }, { kind: "directive", type: i1$4.TabContentDirective, selector: "[kendoTabContent]" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$4.CardComponent, selector: "kendo-card", inputs: ["orientation", "width"] }, { kind: "component", type: i1$4.CardBodyComponent, selector: "kendo-card-body" }, { kind: "component", type: i1$4.CardHeaderComponent, selector: "kendo-card-header" }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i3$1.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: TextBoxModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: PropertyGridComponent, selector: "mm-property-grid", inputs: ["data", "config", "showTypeColumn"], outputs: ["propertyChange", "saveRequested", "binaryDownload"] }, { kind: "component", type: ListViewComponent, selector: "mm-list-view", inputs: ["pageSize", "skip", "rowIsClickable", "showRowCheckBoxes", "showRowSelectAllCheckBox", "contextMenuType", "leftToolbarActions", "rightToolbarActions", "actionCommandItems", "contextMenuCommandItems", "excelExportFileName", "pdfExportFileName", "pageable", "sortable", "rowFilterEnabled", "searchTextBoxEnabled", "rowClass", "messages", "selectable", "columns"], outputs: ["rowClicked"] }, { kind: "directive", type: EntityAssociationsDataSourceDirective, selector: "[mmEntityAssociationsDataSource]", exportAs: ["mmEntityAssociationsDataSource"] }, { kind: "component", type: DataMappingListComponent, selector: "mm-data-mapping-list", inputs: ["mappings", "sourceEntity", "expressionValidator"], outputs: ["addMapping", "removeMapping", "selectTarget", "selectSourceAttribute", "selectTargetAttribute", "mappingChanged", "navigateToTarget", "saveAll"] }] });
|
|
8615
8897
|
}
|
|
8616
8898
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EntityDetailViewComponent, decorators: [{
|
|
8617
8899
|
type: Component,
|
|
@@ -8843,12 +9125,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
8843
9125
|
<div class="tab-content mapping-tab">
|
|
8844
9126
|
<mm-data-mapping-list
|
|
8845
9127
|
[mappings]="dataMappings"
|
|
8846
|
-
[
|
|
9128
|
+
[sourceEntity]="entity"
|
|
8847
9129
|
[expressionValidator]="expressionValidator"
|
|
8848
9130
|
(addMapping)="addMappingRequested.emit()"
|
|
8849
9131
|
(removeMapping)="removeMappingRequested.emit($event)"
|
|
8850
9132
|
(selectTarget)="selectMappingTarget.emit($event)"
|
|
8851
|
-
(selectSourceAttribute)="selectSourceAttributeRequested.emit($event)"
|
|
8852
9133
|
(selectTargetAttribute)="selectTargetAttributeRequested.emit($event)"
|
|
8853
9134
|
(mappingChanged)="mappingChanged.emit($event)"
|
|
8854
9135
|
(navigateToTarget)="onNavigateToMappingTarget($event)"
|
|
@@ -8876,8 +9157,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
8876
9157
|
type: Input
|
|
8877
9158
|
}], dataMappings: [{
|
|
8878
9159
|
type: Input
|
|
8879
|
-
}], sourceDataPoints: [{
|
|
8880
|
-
type: Input
|
|
8881
9160
|
}], expressionValidator: [{
|
|
8882
9161
|
type: Input
|
|
8883
9162
|
}], retry: [{
|
|
@@ -9399,7 +9678,7 @@ class EntityDetailComponent {
|
|
|
9399
9678
|
>
|
|
9400
9679
|
</mm-entity-detail-view>
|
|
9401
9680
|
</div>
|
|
9402
|
-
`, isInline: true, styles: [":host{display:block;height:100%;width:100%}.entity-detail{height:100%;display:flex;flex-direction:column;padding:24px;box-sizing:border-box;overflow-y:auto}.entity-detail .entity-detail-header{display:flex;align-items:center;gap:20px;margin-bottom:24px;padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.entity-detail .entity-detail-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.entity-detail .entity-detail-header .header-info{flex:1}.entity-detail .entity-detail-header .header-info .entity-title h2{margin:0 0 8px;font-family:Montserrat,sans-serif;font-size:1.4rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color);text-shadow:0 0 10px rgba(0,0,0,.3)}.entity-detail .entity-detail-header .header-info .entity-type{margin:0;font-family:Roboto Mono,monospace;font-size:.8rem;background:var(--deep-sea-60);padding:6px 12px;border-radius:4px;border:1px solid var(--neo-cyan-30);display:inline-block;color:var(--neo-cyan)}.entity-detail mm-entity-detail-view{flex:1;display:flex;flex-direction:column;overflow-y:auto}@media(max-width:768px){.entity-detail{padding:16px}.entity-detail .entity-detail-header{flex-direction:column;align-items:flex-start;gap:12px;padding:16px 20px}.entity-detail .entity-detail-header .header-info .entity-title h2{font-size:1.1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: EntityDetailViewComponent, selector: "mm-entity-detail-view", inputs: ["entity", "loading", "error", "showHeader", "messages", "showDataMapping", "dataMappings", "
|
|
9681
|
+
`, isInline: true, styles: [":host{display:block;height:100%;width:100%}.entity-detail{height:100%;display:flex;flex-direction:column;padding:24px;box-sizing:border-box;overflow-y:auto}.entity-detail .entity-detail-header{display:flex;align-items:center;gap:20px;margin-bottom:24px;padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.entity-detail .entity-detail-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.entity-detail .entity-detail-header .header-info{flex:1}.entity-detail .entity-detail-header .header-info .entity-title h2{margin:0 0 8px;font-family:Montserrat,sans-serif;font-size:1.4rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color);text-shadow:0 0 10px rgba(0,0,0,.3)}.entity-detail .entity-detail-header .header-info .entity-type{margin:0;font-family:Roboto Mono,monospace;font-size:.8rem;background:var(--deep-sea-60);padding:6px 12px;border-radius:4px;border:1px solid var(--neo-cyan-30);display:inline-block;color:var(--neo-cyan)}.entity-detail mm-entity-detail-view{flex:1;display:flex;flex-direction:column;overflow-y:auto}@media(max-width:768px){.entity-detail{padding:16px}.entity-detail .entity-detail-header{flex-direction:column;align-items:flex-start;gap:12px;padding:16px 20px}.entity-detail .entity-detail-header .header-info .entity-title h2{font-size:1.1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: EntityDetailViewComponent, selector: "mm-entity-detail-view", inputs: ["entity", "loading", "error", "showHeader", "messages", "showDataMapping", "dataMappings", "expressionValidator"], outputs: ["retry", "propertyChange", "navigateToEntity", "addMappingRequested", "removeMappingRequested", "selectMappingTarget", "selectSourceAttributeRequested", "selectTargetAttributeRequested", "mappingChanged", "saveAllMappingsRequested"] }] });
|
|
9403
9682
|
}
|
|
9404
9683
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EntityDetailComponent, decorators: [{
|
|
9405
9684
|
type: Component,
|
|
@@ -11707,7 +11986,7 @@ class AttributesGroupComponent {
|
|
|
11707
11986
|
</kendo-card-body>
|
|
11708
11987
|
</kendo-card>
|
|
11709
11988
|
</div>
|
|
11710
|
-
`, isInline: true, styles: [".attributes-form-container ::ng-deep kendo-card kendo-card-body{display:flex;flex-direction:column;gap:16px}.attributes-form-container ::ng-deep kendo-label{display:flex;flex-direction:column;gap:4px}.attributes-form-container ::ng-deep kendo-label label{text-transform:uppercase}.header-content{display:flex;flex-direction:column;gap:4px}.record-array-content,.tab-content-wrapper{display:flex;flex-direction:column;gap:16px}.record-actions-info{display:flex;flex-direction:column;gap:12px;padding:16px;background-color:var(--kendo-color-surface-alt, rgba(0, 0, 0, .05));border:1px solid var(--kendo-color-border, rgba(0, 0, 0, .1));border-radius:4px;margin-bottom:16px}.record-actions-description{margin:0;font-size:13px;color:var(--kendo-color-subtle, rgba(0, 0, 0, .6));line-height:1.5}.record-array-actions{display:flex;gap:8px;justify-content:flex-start;padding-top:0}.record-actions{margin-top:16px;display:flex;gap:8px;justify-content:flex-start;padding-top:0}.binary-linked-wrap{display:flex;flex-direction:column;gap:6px}.binary-linked-reference-hint{font-size:.75rem;color:var(--kendo-color-primary, #0f6dff);font-style:italic}\n"], dependencies: [{ kind: "component", type: i0.forwardRef(() => AttributesGroupComponent), selector: "mm-attributes-group", inputs: ["ckId", "parentFormGroup", "isRecord", "initialValues"] }, { kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "ngmodule", type: i0.forwardRef(() => ReactiveFormsModule) }, { kind: "directive", type: i0.forwardRef(() => i1.NgControlStatus), selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i0.forwardRef(() => i1.NgControlStatusGroup), selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i0.forwardRef(() => i1.FormGroupDirective), selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i0.forwardRef(() => i1.FormControlName), selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i0.forwardRef(() => i1.FormGroupName), selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: i0.forwardRef(() => i1$4.CardComponent), selector: "kendo-card", inputs: ["orientation", "width"] }, { kind: "component", type: i0.forwardRef(() => i1$4.CardBodyComponent), selector: "kendo-card-body" }, { kind: "component", type: i0.forwardRef(() => i1$4.CardHeaderComponent), selector: "kendo-card-header" }, { kind: "component", type: i0.forwardRef(() => i1$4.ExpansionPanelComponent), selector: "kendo-expansionpanel", inputs: ["title", "subtitle", "disabled", "expanded", "svgExpandIcon", "svgCollapseIcon", "expandIcon", "collapseIcon", "animation"], outputs: ["expandedChange", "action", "expand", "collapse"], exportAs: ["kendoExpansionPanel"] }, { kind: "directive", type: i0.forwardRef(() => i1$4.ExpansionPanelTitleDirective), selector: "[kendoExpansionPanelTitleDirective]" }, { kind: "component", type: i0.forwardRef(() => i1$4.TabStripComponent), selector: "kendo-tabstrip", inputs: ["height", "animate", "tabAlignment", "tabPosition", "keepTabContent", "closable", "scrollable", "size", "closeIcon", "closeIconClass", "closeSVGIcon", "showContentArea"], outputs: ["tabSelect", "tabClose", "tabScroll"], exportAs: ["kendoTabStrip"] }, { kind: "component", type: i0.forwardRef(() => i1$4.TabStripTabComponent), selector: "kendo-tabstrip-tab", inputs: ["title", "disabled", "cssClass", "cssStyle", "selected", "closable", "closeIcon", "closeIconClass", "closeSVGIcon"], exportAs: ["kendoTabStripTab"] }, { kind: "directive", type: i0.forwardRef(() => i1$4.TabContentDirective), selector: "[kendoTabContent]" }, { kind: "component", type: i0.forwardRef(() => i5.TextBoxComponent), selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: i0.forwardRef(() => i5.NumericTextBoxComponent), selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "component", type: i0.forwardRef(() => i5.SwitchComponent), selector: "kendo-switch", inputs: ["focusableId", "onLabel", "offLabel", "checked", "disabled", "readonly", "tabindex", "size", "thumbRounded", "trackRounded", "tabIndex"], outputs: ["focus", "blur", "valueChange"], exportAs: ["kendoSwitch"] }, { kind: "component", type: i0.forwardRef(() => i1$5.LabelComponent), selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }, { kind: "component", type: i0.forwardRef(() => i1$1.ButtonComponent), selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: i0.forwardRef(() =>
|
|
11989
|
+
`, isInline: true, styles: [".attributes-form-container ::ng-deep kendo-card kendo-card-body{display:flex;flex-direction:column;gap:16px}.attributes-form-container ::ng-deep kendo-label{display:flex;flex-direction:column;gap:4px}.attributes-form-container ::ng-deep kendo-label label{text-transform:uppercase}.header-content{display:flex;flex-direction:column;gap:4px}.record-array-content,.tab-content-wrapper{display:flex;flex-direction:column;gap:16px}.record-actions-info{display:flex;flex-direction:column;gap:12px;padding:16px;background-color:var(--kendo-color-surface-alt, rgba(0, 0, 0, .05));border:1px solid var(--kendo-color-border, rgba(0, 0, 0, .1));border-radius:4px;margin-bottom:16px}.record-actions-description{margin:0;font-size:13px;color:var(--kendo-color-subtle, rgba(0, 0, 0, .6));line-height:1.5}.record-array-actions{display:flex;gap:8px;justify-content:flex-start;padding-top:0}.record-actions{margin-top:16px;display:flex;gap:8px;justify-content:flex-start;padding-top:0}.binary-linked-wrap{display:flex;flex-direction:column;gap:6px}.binary-linked-reference-hint{font-size:.75rem;color:var(--kendo-color-primary, #0f6dff);font-style:italic}\n"], dependencies: [{ kind: "component", type: i0.forwardRef(() => AttributesGroupComponent), selector: "mm-attributes-group", inputs: ["ckId", "parentFormGroup", "isRecord", "initialValues"] }, { kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "ngmodule", type: i0.forwardRef(() => ReactiveFormsModule) }, { kind: "directive", type: i0.forwardRef(() => i1.NgControlStatus), selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i0.forwardRef(() => i1.NgControlStatusGroup), selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i0.forwardRef(() => i1.FormGroupDirective), selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i0.forwardRef(() => i1.FormControlName), selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i0.forwardRef(() => i1.FormGroupName), selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: i0.forwardRef(() => i1$4.CardComponent), selector: "kendo-card", inputs: ["orientation", "width"] }, { kind: "component", type: i0.forwardRef(() => i1$4.CardBodyComponent), selector: "kendo-card-body" }, { kind: "component", type: i0.forwardRef(() => i1$4.CardHeaderComponent), selector: "kendo-card-header" }, { kind: "component", type: i0.forwardRef(() => i1$4.ExpansionPanelComponent), selector: "kendo-expansionpanel", inputs: ["title", "subtitle", "disabled", "expanded", "svgExpandIcon", "svgCollapseIcon", "expandIcon", "collapseIcon", "animation"], outputs: ["expandedChange", "action", "expand", "collapse"], exportAs: ["kendoExpansionPanel"] }, { kind: "directive", type: i0.forwardRef(() => i1$4.ExpansionPanelTitleDirective), selector: "[kendoExpansionPanelTitleDirective]" }, { kind: "component", type: i0.forwardRef(() => i1$4.TabStripComponent), selector: "kendo-tabstrip", inputs: ["height", "animate", "tabAlignment", "tabPosition", "keepTabContent", "closable", "scrollable", "size", "closeIcon", "closeIconClass", "closeSVGIcon", "showContentArea"], outputs: ["tabSelect", "tabClose", "tabScroll"], exportAs: ["kendoTabStrip"] }, { kind: "component", type: i0.forwardRef(() => i1$4.TabStripTabComponent), selector: "kendo-tabstrip-tab", inputs: ["title", "disabled", "cssClass", "cssStyle", "selected", "closable", "closeIcon", "closeIconClass", "closeSVGIcon"], exportAs: ["kendoTabStripTab"] }, { kind: "directive", type: i0.forwardRef(() => i1$4.TabContentDirective), selector: "[kendoTabContent]" }, { kind: "component", type: i0.forwardRef(() => i5.TextBoxComponent), selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: i0.forwardRef(() => i5.NumericTextBoxComponent), selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "component", type: i0.forwardRef(() => i5.SwitchComponent), selector: "kendo-switch", inputs: ["focusableId", "onLabel", "offLabel", "checked", "disabled", "readonly", "tabindex", "size", "thumbRounded", "trackRounded", "tabIndex"], outputs: ["focus", "blur", "valueChange"], exportAs: ["kendoSwitch"] }, { kind: "component", type: i0.forwardRef(() => i1$5.LabelComponent), selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }, { kind: "component", type: i0.forwardRef(() => i1$1.ButtonComponent), selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: i0.forwardRef(() => i3$1.DropDownListComponent), selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "component", type: i0.forwardRef(() => i3$1.MultiSelectComponent), selector: "kendo-multiselect", inputs: ["showStickyHeader", "focusableId", "autoClose", "loading", "data", "value", "valueField", "textField", "tabindex", "tabIndex", "size", "rounded", "fillMode", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "disabled", "itemDisabled", "checkboxes", "readonly", "filterable", "virtual", "popupSettings", "listHeight", "valuePrimitive", "clearButton", "tagMapper", "allowCustom", "valueNormalizer", "inputAttributes"], outputs: ["filterChange", "valueChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "removeTag"], exportAs: ["kendoMultiSelect"] }, { kind: "component", type: i0.forwardRef(() => i7.DateTimePickerComponent), selector: "kendo-datetimepicker", inputs: ["focusableId", "weekDaysFormat", "showOtherMonthDays", "value", "format", "twoDigitYearMax", "tabindex", "disabledDates", "popupSettings", "adaptiveTitle", "adaptiveSubtitle", "disabled", "readonly", "readOnlyInput", "cancelButton", "formatPlaceholder", "placeholder", "steps", "focusedDate", "calendarType", "animateCalendarNavigation", "weekNumber", "min", "max", "rangeValidation", "disabledDatesValidation", "incompleteDateValidation", "autoCorrectParts", "autoSwitchParts", "autoSwitchKeys", "enableMouseWheel", "allowCaretMode", "clearButton", "autoFill", "adaptiveMode", "inputAttributes", "defaultTab", "size", "rounded", "fillMode", "headerTemplate", "footerTemplate", "footer"], outputs: ["valueChange", "open", "close", "focus", "blur", "escape"], exportAs: ["kendo-datetimepicker"] }, { kind: "component", type: i0.forwardRef(() => i7.TimePickerComponent), selector: "kendo-timepicker", inputs: ["focusableId", "disabled", "readonly", "readOnlyInput", "clearButton", "format", "formatPlaceholder", "placeholder", "min", "max", "incompleteDateValidation", "autoSwitchParts", "autoSwitchKeys", "enableMouseWheel", "allowCaretMode", "cancelButton", "nowButton", "steps", "popupSettings", "tabindex", "tabIndex", "adaptiveTitle", "adaptiveSubtitle", "rangeValidation", "adaptiveMode", "value", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "open", "close", "escape"], exportAs: ["kendo-timepicker"] }, { kind: "component", type: i0.forwardRef(() => i8.FileSelectComponent), selector: "kendo-fileselect", inputs: ["name"], outputs: ["valueChange"], exportAs: ["kendoFileSelect"] }, { kind: "component", type: i0.forwardRef(() => AttributeFieldComponent), selector: "mm-attribute-field", inputs: ["attribute", "control", "baselineValue", "fieldId", "overrideLabelText", "showUndoButton", "undoButtonSize", "errorMessage", "hintText"], outputs: ["undo"] }] });
|
|
11711
11990
|
}
|
|
11712
11991
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AttributesGroupComponent, decorators: [{
|
|
11713
11992
|
type: Component,
|
|
@@ -12762,9 +13041,10 @@ class RuntimeBrowserDetailsComponent {
|
|
|
12762
13041
|
deleteEntitiesGQL = inject(DeleteEntitiesDtoGQL);
|
|
12763
13042
|
entitySelectorDialog = inject(EntitySelectorDialogService);
|
|
12764
13043
|
attributeSelectorDialog = inject(AttributeSelectorDialogService);
|
|
12765
|
-
// Data Mapping state (list of DataPointMapping entities)
|
|
13044
|
+
// Data Mapping state (list of DataPointMapping entities). Source data points
|
|
13045
|
+
// for the mapping rows are now resolved inline by DataPointPickerComponent
|
|
13046
|
+
// from the displayed RtEntity — no precomputed string list maintained here.
|
|
12766
13047
|
dataMappings = [];
|
|
12767
|
-
sourceDataPoints = [];
|
|
12768
13048
|
/**
|
|
12769
13049
|
* Optional expression validator function passed through to EntityDetailViewComponent → DataMappingListComponent.
|
|
12770
13050
|
*/
|
|
@@ -12885,7 +13165,6 @@ class RuntimeBrowserDetailsComponent {
|
|
|
12885
13165
|
this.error = this._messages.couldNotLoadEntityDetails;
|
|
12886
13166
|
}
|
|
12887
13167
|
else {
|
|
12888
|
-
this.extractSourceDataPoints();
|
|
12889
13168
|
await this.loadDataMappings();
|
|
12890
13169
|
}
|
|
12891
13170
|
}
|
|
@@ -13291,100 +13570,28 @@ class RuntimeBrowserDetailsComponent {
|
|
|
13291
13570
|
// Actual persistence happens on "Save All".
|
|
13292
13571
|
}
|
|
13293
13572
|
/**
|
|
13294
|
-
*
|
|
13295
|
-
* Provides them as dropdown options for sourceAttributePath selection.
|
|
13573
|
+
* Opens attribute selector for the source entity's attributes.
|
|
13296
13574
|
*/
|
|
13297
|
-
|
|
13298
|
-
this.sourceDataPoints = ['currentValue'];
|
|
13575
|
+
async onSelectSourceAttribute(mapping) {
|
|
13299
13576
|
const entity = this.getEntityForDisplay();
|
|
13300
|
-
if (!entity?.
|
|
13577
|
+
if (!entity?.ckTypeId)
|
|
13301
13578
|
return;
|
|
13302
|
-
|
|
13303
|
-
|
|
13304
|
-
|
|
13305
|
-
|
|
13306
|
-
}
|
|
13307
|
-
|
|
13579
|
+
const result = await this.attributeSelectorDialog.openAttributeSelector(entity.ckTypeId, mapping.sourceAttributePath ? [mapping.sourceAttributePath] : undefined, 'Select Source Attribute', true, undefined, false, undefined, true);
|
|
13580
|
+
if (result.confirmed && result.selectedAttributes.length > 0) {
|
|
13581
|
+
mapping.sourceAttributePath = result.selectedAttributes[0].attributePath;
|
|
13582
|
+
this.dataMappings = [...this.dataMappings];
|
|
13583
|
+
}
|
|
13584
|
+
}
|
|
13585
|
+
/**
|
|
13586
|
+
* Opens attribute selector for the target entity's attributes.
|
|
13587
|
+
*/
|
|
13588
|
+
async onSelectTargetAttribute(mapping) {
|
|
13589
|
+
if (!mapping.targetCkTypeId)
|
|
13308
13590
|
return;
|
|
13309
|
-
|
|
13310
|
-
if (
|
|
13311
|
-
|
|
13312
|
-
|
|
13313
|
-
else if (typeof statesAttr.value === 'string') {
|
|
13314
|
-
// RecordArray might come as JSON string from GraphQL
|
|
13315
|
-
try {
|
|
13316
|
-
const parsed = JSON.parse(statesAttr.value);
|
|
13317
|
-
if (Array.isArray(parsed)) {
|
|
13318
|
-
records = parsed;
|
|
13319
|
-
}
|
|
13320
|
-
else {
|
|
13321
|
-
return;
|
|
13322
|
-
}
|
|
13323
|
-
}
|
|
13324
|
-
catch {
|
|
13325
|
-
return;
|
|
13326
|
-
}
|
|
13327
|
-
}
|
|
13328
|
-
else {
|
|
13329
|
-
return;
|
|
13330
|
-
}
|
|
13331
|
-
// Extract Name from each record.
|
|
13332
|
-
// GraphQL returns records as: { ckRecordId, attributes: [{attributeName, value}, ...] }
|
|
13333
|
-
const names = [];
|
|
13334
|
-
for (const record of records) {
|
|
13335
|
-
if (record && typeof record === 'object') {
|
|
13336
|
-
const r = record;
|
|
13337
|
-
const attrs = r['attributes'];
|
|
13338
|
-
let name;
|
|
13339
|
-
if (Array.isArray(attrs)) {
|
|
13340
|
-
// GraphQL format: attributes is array of {attributeName, value}
|
|
13341
|
-
const nameEntry = attrs
|
|
13342
|
-
.find((a) => a.attributeName === 'name' || a.attributeName === 'Name');
|
|
13343
|
-
if (nameEntry?.value && typeof nameEntry.value === 'string') {
|
|
13344
|
-
name = nameEntry.value;
|
|
13345
|
-
}
|
|
13346
|
-
}
|
|
13347
|
-
else if (attrs && typeof attrs === 'object') {
|
|
13348
|
-
// Pipeline/MongoDB format: attributes is object {Name: "...", ...}
|
|
13349
|
-
const attrObj = attrs;
|
|
13350
|
-
name = (attrObj['Name'] ?? attrObj['name'] ?? attrObj['stateName']);
|
|
13351
|
-
}
|
|
13352
|
-
if (!name) {
|
|
13353
|
-
// Flat format: {name: "...", ...}
|
|
13354
|
-
name = (r['Name'] ?? r['name']);
|
|
13355
|
-
}
|
|
13356
|
-
if (typeof name === 'string' && name.length > 0) {
|
|
13357
|
-
names.push(name);
|
|
13358
|
-
}
|
|
13359
|
-
}
|
|
13360
|
-
}
|
|
13361
|
-
if (names.length > 0) {
|
|
13362
|
-
this.sourceDataPoints = ['currentValue', ...names.sort()];
|
|
13363
|
-
}
|
|
13364
|
-
}
|
|
13365
|
-
/**
|
|
13366
|
-
* Opens attribute selector for the source entity's attributes.
|
|
13367
|
-
*/
|
|
13368
|
-
async onSelectSourceAttribute(mapping) {
|
|
13369
|
-
const entity = this.getEntityForDisplay();
|
|
13370
|
-
if (!entity?.ckTypeId)
|
|
13371
|
-
return;
|
|
13372
|
-
const result = await this.attributeSelectorDialog.openAttributeSelector(entity.ckTypeId, mapping.sourceAttributePath ? [mapping.sourceAttributePath] : undefined, 'Select Source Attribute', true, undefined, false, undefined, true);
|
|
13373
|
-
if (result.confirmed && result.selectedAttributes.length > 0) {
|
|
13374
|
-
mapping.sourceAttributePath = result.selectedAttributes[0].attributePath;
|
|
13375
|
-
this.dataMappings = [...this.dataMappings];
|
|
13376
|
-
}
|
|
13377
|
-
}
|
|
13378
|
-
/**
|
|
13379
|
-
* Opens attribute selector for the target entity's attributes.
|
|
13380
|
-
*/
|
|
13381
|
-
async onSelectTargetAttribute(mapping) {
|
|
13382
|
-
if (!mapping.targetCkTypeId)
|
|
13383
|
-
return;
|
|
13384
|
-
const result = await this.attributeSelectorDialog.openAttributeSelector(mapping.targetCkTypeId, mapping.targetAttributePath ? [mapping.targetAttributePath] : undefined, 'Select Target Attribute', true, undefined, false, undefined, true);
|
|
13385
|
-
if (result.confirmed && result.selectedAttributes.length > 0) {
|
|
13386
|
-
mapping.targetAttributePath = result.selectedAttributes[0].attributePath;
|
|
13387
|
-
this.dataMappings = [...this.dataMappings];
|
|
13591
|
+
const result = await this.attributeSelectorDialog.openAttributeSelector(mapping.targetCkTypeId, mapping.targetAttributePath ? [mapping.targetAttributePath] : undefined, 'Select Target Attribute', true, undefined, false, undefined, true);
|
|
13592
|
+
if (result.confirmed && result.selectedAttributes.length > 0) {
|
|
13593
|
+
mapping.targetAttributePath = result.selectedAttributes[0].attributePath;
|
|
13594
|
+
this.dataMappings = [...this.dataMappings];
|
|
13388
13595
|
}
|
|
13389
13596
|
}
|
|
13390
13597
|
/**
|
|
@@ -13489,7 +13696,6 @@ class RuntimeBrowserDetailsComponent {
|
|
|
13489
13696
|
[messages]="_messages"
|
|
13490
13697
|
[showDataMapping]="showDataMapping"
|
|
13491
13698
|
[dataMappings]="dataMappings"
|
|
13492
|
-
[sourceDataPoints]="sourceDataPoints"
|
|
13493
13699
|
[expressionValidator]="expressionValidator"
|
|
13494
13700
|
(retry)="loadFullEntityDetails()"
|
|
13495
13701
|
(navigateToEntity)="
|
|
@@ -13589,7 +13795,7 @@ class RuntimeBrowserDetailsComponent {
|
|
|
13589
13795
|
}
|
|
13590
13796
|
}
|
|
13591
13797
|
</div>
|
|
13592
|
-
`, isInline: true, styles: [":host{display:block;height:100%;width:100%}.runtime-browser-details{display:flex;flex-direction:column;flex:1;min-height:0;padding:24px;box-sizing:border-box;overflow-y:auto;background:linear-gradient(180deg,var(--surface-elevated) 0%,var(--deep-sea) 100%)}.runtime-browser-details .no-selection{display:flex;align-items:center;justify-content:center;flex:1;min-height:0}.runtime-browser-details .no-selection .placeholder-content{text-align:center;max-width:400px;padding:40px;background:linear-gradient(135deg,var(--iron-navy-80),var(--surface-elevated-90));border:1px solid var(--octo-mint-30);border-radius:8px 24px;box-shadow:0 0 30px var(--octo-mint-10),inset 0 0 40px var(--octo-mint-03)}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon{margin-bottom:24px}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon .k-icon{font-size:64px;color:var(--octo-mint-50);text-shadow:0 0 20px var(--octo-mint-50);animation:lcars-icon-pulse 3s ease-in-out infinite}.runtime-browser-details .no-selection .placeholder-content h3{margin:0 0 12px;font-family:Montserrat,sans-serif;font-size:1.4rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--octo-mint);text-shadow:0 0 15px var(--octo-mint-50)}.runtime-browser-details .no-selection .placeholder-content p{margin:0;font-family:Montserrat,sans-serif;font-size:.9rem;line-height:1.6;color:rgba(var(--octo-text-color),.7);letter-spacing:.5px}.runtime-browser-details .entity-details{height:100%;display:flex;flex-direction:column;overflow:hidden}.runtime-browser-details .entity-details .details-header{display:flex;align-items:center;gap:16px;padding:16px 20px 16px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;margin-bottom:20px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.runtime-browser-details .entity-details .details-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.runtime-browser-details .entity-details .details-header .entity-icon{font-size:28px;color:var(--octo-mint);text-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .details-header .entity-title{flex:1}.runtime-browser-details .entity-details .details-header .entity-title h3{margin:0 0 6px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .details-header .entity-title .entity-type{margin:0;font-family:Roboto Mono,monospace;font-size:.8rem;color:var(--neo-cyan);background:var(--deep-sea-60);padding:4px 10px;border-radius:4px;border:1px solid var(--neo-cyan-30);display:inline-block}.runtime-browser-details .entity-details mm-entity-detail-view{flex:1;display:flex;flex-direction:column;overflow-y:auto}.runtime-browser-details .entity-details .ck-model-details,.runtime-browser-details .entity-details .ck-models-root{padding:24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative}.runtime-browser-details .entity-details .ck-model-details:before,.runtime-browser-details .entity-details .ck-models-root:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .ck-model-details h3,.runtime-browser-details .entity-details .ck-models-root h3{margin:0 0 20px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .ck-model-details p,.runtime-browser-details .entity-details .ck-models-root p{margin:0 0 12px;font-family:Roboto,sans-serif;font-size:.9rem;color:rgba(var(--octo-text-color),.85)}.runtime-browser-details .entity-details .ck-model-details p strong,.runtime-browser-details .entity-details .ck-models-root p strong{font-family:Montserrat,sans-serif;font-weight:600;font-size:.8rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80);margin-right:12px}.runtime-browser-details .entity-details .ck-model-details p.info-text,.runtime-browser-details .entity-details .ck-models-root p.info-text{color:rgba(var(--octo-text-color),.6);font-style:italic;margin-top:24px;padding:16px 20px;background:linear-gradient(135deg,var(--neo-cyan-10),var(--neo-cyan-05));border:1px solid var(--neo-cyan-30);border-radius:4px 12px}.runtime-browser-details .entity-details .ck-type-details{display:flex;flex-direction:column;height:100%;padding:0}.runtime-browser-details .entity-details .ck-type-details .type-header{margin-bottom:20px;padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative}.runtime-browser-details .entity-details .ck-type-details .type-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .ck-type-details .type-header h3{margin:0 0 12px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata{display:flex;gap:10px;align-items:center}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge{padding:6px 14px;border-radius:4px;font-family:Montserrat,sans-serif;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge.abstract{background:linear-gradient(135deg,var(--royal-violet-30),var(--royal-violet-15));border:1px solid var(--royal-violet-50);color:var(--royal-violet-light-20);box-shadow:0 0 10px var(--royal-violet-30)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge.final{background:linear-gradient(135deg,var(--toffee-30),var(--toffee-15));border:1px solid var(--toffee-50);color:var(--toffee);box-shadow:0 0 10px var(--toffee-30)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .base-type{font-family:Roboto Mono,monospace;font-size:.8rem;color:var(--neo-cyan);background:var(--deep-sea-60);padding:4px 10px;border-radius:4px;border:1px solid var(--neo-cyan-30)}.runtime-browser-details .entity-details .ck-type-details .entities-table{flex:1;display:flex;flex-direction:column;overflow:hidden;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;padding:20px}.runtime-browser-details .entity-details .ck-type-details .entities-table h4{margin:0 0 16px;font-family:Montserrat,sans-serif;font-size:1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-mint)}.runtime-browser-details .entity-details .ck-type-details .entities-table mm-list-view{flex:1;display:flex;flex-direction:column;overflow:hidden}@keyframes lcars-icon-pulse{0%,to{opacity:.5;transform:scale(1)}50%{opacity:.8;transform:scale(1.05)}}@media(max-width:768px){.runtime-browser-details{padding:16px}.runtime-browser-details .no-selection .placeholder-content{padding:24px}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon .k-icon{font-size:48px}.runtime-browser-details .no-selection .placeholder-content h3{font-size:1.1rem}.runtime-browser-details .entity-details .details-header{flex-direction:column;align-items:flex-start;gap:12px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: EntityDetailViewComponent, selector: "mm-entity-detail-view", inputs: ["entity", "loading", "error", "showHeader", "messages", "showDataMapping", "dataMappings", "
|
|
13798
|
+
`, isInline: true, styles: [":host{display:block;height:100%;width:100%}.runtime-browser-details{display:flex;flex-direction:column;flex:1;min-height:0;padding:24px;box-sizing:border-box;overflow-y:auto;background:linear-gradient(180deg,var(--surface-elevated) 0%,var(--deep-sea) 100%)}.runtime-browser-details .no-selection{display:flex;align-items:center;justify-content:center;flex:1;min-height:0}.runtime-browser-details .no-selection .placeholder-content{text-align:center;max-width:400px;padding:40px;background:linear-gradient(135deg,var(--iron-navy-80),var(--surface-elevated-90));border:1px solid var(--octo-mint-30);border-radius:8px 24px;box-shadow:0 0 30px var(--octo-mint-10),inset 0 0 40px var(--octo-mint-03)}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon{margin-bottom:24px}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon .k-icon{font-size:64px;color:var(--octo-mint-50);text-shadow:0 0 20px var(--octo-mint-50);animation:lcars-icon-pulse 3s ease-in-out infinite}.runtime-browser-details .no-selection .placeholder-content h3{margin:0 0 12px;font-family:Montserrat,sans-serif;font-size:1.4rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--octo-mint);text-shadow:0 0 15px var(--octo-mint-50)}.runtime-browser-details .no-selection .placeholder-content p{margin:0;font-family:Montserrat,sans-serif;font-size:.9rem;line-height:1.6;color:rgba(var(--octo-text-color),.7);letter-spacing:.5px}.runtime-browser-details .entity-details{height:100%;display:flex;flex-direction:column;overflow:hidden}.runtime-browser-details .entity-details .details-header{display:flex;align-items:center;gap:16px;padding:16px 20px 16px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;margin-bottom:20px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.runtime-browser-details .entity-details .details-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.runtime-browser-details .entity-details .details-header .entity-icon{font-size:28px;color:var(--octo-mint);text-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .details-header .entity-title{flex:1}.runtime-browser-details .entity-details .details-header .entity-title h3{margin:0 0 6px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .details-header .entity-title .entity-type{margin:0;font-family:Roboto Mono,monospace;font-size:.8rem;color:var(--neo-cyan);background:var(--deep-sea-60);padding:4px 10px;border-radius:4px;border:1px solid var(--neo-cyan-30);display:inline-block}.runtime-browser-details .entity-details mm-entity-detail-view{flex:1;display:flex;flex-direction:column;overflow-y:auto}.runtime-browser-details .entity-details .ck-model-details,.runtime-browser-details .entity-details .ck-models-root{padding:24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative}.runtime-browser-details .entity-details .ck-model-details:before,.runtime-browser-details .entity-details .ck-models-root:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .ck-model-details h3,.runtime-browser-details .entity-details .ck-models-root h3{margin:0 0 20px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .ck-model-details p,.runtime-browser-details .entity-details .ck-models-root p{margin:0 0 12px;font-family:Roboto,sans-serif;font-size:.9rem;color:rgba(var(--octo-text-color),.85)}.runtime-browser-details .entity-details .ck-model-details p strong,.runtime-browser-details .entity-details .ck-models-root p strong{font-family:Montserrat,sans-serif;font-weight:600;font-size:.8rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80);margin-right:12px}.runtime-browser-details .entity-details .ck-model-details p.info-text,.runtime-browser-details .entity-details .ck-models-root p.info-text{color:rgba(var(--octo-text-color),.6);font-style:italic;margin-top:24px;padding:16px 20px;background:linear-gradient(135deg,var(--neo-cyan-10),var(--neo-cyan-05));border:1px solid var(--neo-cyan-30);border-radius:4px 12px}.runtime-browser-details .entity-details .ck-type-details{display:flex;flex-direction:column;height:100%;padding:0}.runtime-browser-details .entity-details .ck-type-details .type-header{margin-bottom:20px;padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative}.runtime-browser-details .entity-details .ck-type-details .type-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .ck-type-details .type-header h3{margin:0 0 12px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata{display:flex;gap:10px;align-items:center}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge{padding:6px 14px;border-radius:4px;font-family:Montserrat,sans-serif;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge.abstract{background:linear-gradient(135deg,var(--royal-violet-30),var(--royal-violet-15));border:1px solid var(--royal-violet-50);color:var(--royal-violet-light-20);box-shadow:0 0 10px var(--royal-violet-30)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge.final{background:linear-gradient(135deg,var(--toffee-30),var(--toffee-15));border:1px solid var(--toffee-50);color:var(--toffee);box-shadow:0 0 10px var(--toffee-30)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .base-type{font-family:Roboto Mono,monospace;font-size:.8rem;color:var(--neo-cyan);background:var(--deep-sea-60);padding:4px 10px;border-radius:4px;border:1px solid var(--neo-cyan-30)}.runtime-browser-details .entity-details .ck-type-details .entities-table{flex:1;display:flex;flex-direction:column;overflow:hidden;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;padding:20px}.runtime-browser-details .entity-details .ck-type-details .entities-table h4{margin:0 0 16px;font-family:Montserrat,sans-serif;font-size:1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-mint)}.runtime-browser-details .entity-details .ck-type-details .entities-table mm-list-view{flex:1;display:flex;flex-direction:column;overflow:hidden}@keyframes lcars-icon-pulse{0%,to{opacity:.5;transform:scale(1)}50%{opacity:.8;transform:scale(1.05)}}@media(max-width:768px){.runtime-browser-details{padding:16px}.runtime-browser-details .no-selection .placeholder-content{padding:24px}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon .k-icon{font-size:48px}.runtime-browser-details .no-selection .placeholder-content h3{font-size:1.1rem}.runtime-browser-details .entity-details .details-header{flex-direction:column;align-items:flex-start;gap:12px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: EntityDetailViewComponent, selector: "mm-entity-detail-view", inputs: ["entity", "loading", "error", "showHeader", "messages", "showDataMapping", "dataMappings", "expressionValidator"], outputs: ["retry", "propertyChange", "navigateToEntity", "addMappingRequested", "removeMappingRequested", "selectMappingTarget", "selectSourceAttributeRequested", "selectTargetAttributeRequested", "mappingChanged", "saveAllMappingsRequested"] }, { kind: "component", type: ListViewComponent, selector: "mm-list-view", inputs: ["pageSize", "skip", "rowIsClickable", "showRowCheckBoxes", "showRowSelectAllCheckBox", "contextMenuType", "leftToolbarActions", "rightToolbarActions", "actionCommandItems", "contextMenuCommandItems", "excelExportFileName", "pdfExportFileName", "pageable", "sortable", "rowFilterEnabled", "searchTextBoxEnabled", "rowClass", "messages", "selectable", "columns"], outputs: ["rowClicked"] }, { kind: "directive", type: CkTypeEntitiesDataSourceDirective, selector: "[mmCkTypeEntitiesDataSource]", exportAs: ["mmCkTypeEntitiesDataSource"] }, { kind: "component", type: CreateEditorComponent, selector: "mm-create-editor-component", inputs: ["createInput", "messages"], outputs: ["createOutput", "cancelRequested"] }, { kind: "component", type: UpdateEditorComponent, selector: "mm-update-editor-component", inputs: ["updateInput", "messages"], outputs: ["updateOutput", "cancelRequested"] }] });
|
|
13593
13799
|
}
|
|
13594
13800
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RuntimeBrowserDetailsComponent, decorators: [{
|
|
13595
13801
|
type: Component,
|
|
@@ -13643,7 +13849,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
13643
13849
|
[messages]="_messages"
|
|
13644
13850
|
[showDataMapping]="showDataMapping"
|
|
13645
13851
|
[dataMappings]="dataMappings"
|
|
13646
|
-
[sourceDataPoints]="sourceDataPoints"
|
|
13647
13852
|
[expressionValidator]="expressionValidator"
|
|
13648
13853
|
(retry)="loadFullEntityDetails()"
|
|
13649
13854
|
(navigateToEntity)="
|
|
@@ -15207,6 +15412,2135 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
15207
15412
|
type: Output
|
|
15208
15413
|
}] } });
|
|
15209
15414
|
|
|
15415
|
+
const GetLatestValidationExecutionDocumentDto = gql `
|
|
15416
|
+
query getLatestValidationExecution($pipelineRtId: OctoObjectId!, $pipelineCkTypeId: String!, $executesRoleId: String!, $executionCkTypeId: String!) {
|
|
15417
|
+
runtime {
|
|
15418
|
+
runtimeEntities(rtId: $pipelineRtId, ckId: $pipelineCkTypeId) {
|
|
15419
|
+
items {
|
|
15420
|
+
rtId
|
|
15421
|
+
ckTypeId
|
|
15422
|
+
associations {
|
|
15423
|
+
executions: targets(
|
|
15424
|
+
roleId: $executesRoleId
|
|
15425
|
+
ckId: $executionCkTypeId
|
|
15426
|
+
direction: INBOUND
|
|
15427
|
+
first: 1
|
|
15428
|
+
sortOrder: [{attributePath: "CompletedAt", sortOrder: DESCENDING}]
|
|
15429
|
+
) {
|
|
15430
|
+
items {
|
|
15431
|
+
rtId
|
|
15432
|
+
ckTypeId
|
|
15433
|
+
attributes(resolveEnumValuesToNames: true) {
|
|
15434
|
+
items {
|
|
15435
|
+
attributeName
|
|
15436
|
+
value
|
|
15437
|
+
}
|
|
15438
|
+
}
|
|
15439
|
+
}
|
|
15440
|
+
}
|
|
15441
|
+
}
|
|
15442
|
+
}
|
|
15443
|
+
}
|
|
15444
|
+
}
|
|
15445
|
+
}
|
|
15446
|
+
`;
|
|
15447
|
+
class GetLatestValidationExecutionDtoGQL extends i1$3.Query {
|
|
15448
|
+
document = GetLatestValidationExecutionDocumentDto;
|
|
15449
|
+
constructor(apollo) {
|
|
15450
|
+
super(apollo);
|
|
15451
|
+
}
|
|
15452
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetLatestValidationExecutionDtoGQL, deps: [{ token: i1$3.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
15453
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetLatestValidationExecutionDtoGQL, providedIn: 'root' });
|
|
15454
|
+
}
|
|
15455
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetLatestValidationExecutionDtoGQL, decorators: [{
|
|
15456
|
+
type: Injectable,
|
|
15457
|
+
args: [{
|
|
15458
|
+
providedIn: 'root'
|
|
15459
|
+
}]
|
|
15460
|
+
}], ctorParameters: () => [{ type: i1$3.Apollo }] });
|
|
15461
|
+
|
|
15462
|
+
const GetNodeMappingsDocumentDto = gql `
|
|
15463
|
+
query getNodeMappings($rtId: OctoObjectId!, $ckTypeId: String!, $mapsToRoleId: String!, $mapsFromRoleId: String!, $mappingCkTypeId: String!) {
|
|
15464
|
+
runtime {
|
|
15465
|
+
runtimeEntities(rtId: $rtId, ckId: $ckTypeId) {
|
|
15466
|
+
items {
|
|
15467
|
+
rtId
|
|
15468
|
+
ckTypeId
|
|
15469
|
+
associations {
|
|
15470
|
+
mappings: targets(
|
|
15471
|
+
roleId: $mapsToRoleId
|
|
15472
|
+
ckId: $mappingCkTypeId
|
|
15473
|
+
direction: INBOUND
|
|
15474
|
+
) {
|
|
15475
|
+
totalCount
|
|
15476
|
+
items {
|
|
15477
|
+
rtId
|
|
15478
|
+
ckTypeId
|
|
15479
|
+
attributes(resolveEnumValuesToNames: true) {
|
|
15480
|
+
items {
|
|
15481
|
+
attributeName
|
|
15482
|
+
value
|
|
15483
|
+
}
|
|
15484
|
+
}
|
|
15485
|
+
associations {
|
|
15486
|
+
sources: definitions(roleId: $mapsFromRoleId, direction: OUTBOUND, first: 1) {
|
|
15487
|
+
items {
|
|
15488
|
+
targetRtId
|
|
15489
|
+
targetCkTypeId
|
|
15490
|
+
}
|
|
15491
|
+
}
|
|
15492
|
+
}
|
|
15493
|
+
}
|
|
15494
|
+
}
|
|
15495
|
+
}
|
|
15496
|
+
}
|
|
15497
|
+
}
|
|
15498
|
+
}
|
|
15499
|
+
}
|
|
15500
|
+
`;
|
|
15501
|
+
class GetNodeMappingsDtoGQL extends i1$3.Query {
|
|
15502
|
+
document = GetNodeMappingsDocumentDto;
|
|
15503
|
+
constructor(apollo) {
|
|
15504
|
+
super(apollo);
|
|
15505
|
+
}
|
|
15506
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetNodeMappingsDtoGQL, deps: [{ token: i1$3.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
15507
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetNodeMappingsDtoGQL, providedIn: 'root' });
|
|
15508
|
+
}
|
|
15509
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetNodeMappingsDtoGQL, decorators: [{
|
|
15510
|
+
type: Injectable,
|
|
15511
|
+
args: [{
|
|
15512
|
+
providedIn: 'root'
|
|
15513
|
+
}]
|
|
15514
|
+
}], ctorParameters: () => [{ type: i1$3.Apollo }] });
|
|
15515
|
+
|
|
15516
|
+
const GetOrphanCandidatesDocumentDto = gql `
|
|
15517
|
+
query getOrphanCandidates($ckTypeId: String!, $mapsFromRoleId: String!, $mappingCkTypeId: String!, $childRoleId: String!, $childCkTypeId: String!, $first: Int, $after: String, $searchFilter: SearchFilter) {
|
|
15518
|
+
runtime {
|
|
15519
|
+
runtimeEntities(
|
|
15520
|
+
ckId: $ckTypeId
|
|
15521
|
+
first: $first
|
|
15522
|
+
after: $after
|
|
15523
|
+
searchFilter: $searchFilter
|
|
15524
|
+
) {
|
|
15525
|
+
totalCount
|
|
15526
|
+
items {
|
|
15527
|
+
rtId
|
|
15528
|
+
ckTypeId
|
|
15529
|
+
rtWellKnownName
|
|
15530
|
+
attributes(attributeNames: ["name", "description"]) {
|
|
15531
|
+
items {
|
|
15532
|
+
attributeName
|
|
15533
|
+
value
|
|
15534
|
+
}
|
|
15535
|
+
}
|
|
15536
|
+
associations {
|
|
15537
|
+
mappings: targets(
|
|
15538
|
+
roleId: $mapsFromRoleId
|
|
15539
|
+
ckId: $mappingCkTypeId
|
|
15540
|
+
direction: INBOUND
|
|
15541
|
+
first: 1
|
|
15542
|
+
) {
|
|
15543
|
+
totalCount
|
|
15544
|
+
}
|
|
15545
|
+
parent: targets(
|
|
15546
|
+
roleId: $childRoleId
|
|
15547
|
+
ckId: $childCkTypeId
|
|
15548
|
+
direction: OUTBOUND
|
|
15549
|
+
first: 1
|
|
15550
|
+
) {
|
|
15551
|
+
items {
|
|
15552
|
+
rtId
|
|
15553
|
+
ckTypeId
|
|
15554
|
+
rtWellKnownName
|
|
15555
|
+
attributes(attributeNames: ["name"]) {
|
|
15556
|
+
items {
|
|
15557
|
+
attributeName
|
|
15558
|
+
value
|
|
15559
|
+
}
|
|
15560
|
+
}
|
|
15561
|
+
associations {
|
|
15562
|
+
parent: targets(
|
|
15563
|
+
roleId: $childRoleId
|
|
15564
|
+
ckId: $childCkTypeId
|
|
15565
|
+
direction: OUTBOUND
|
|
15566
|
+
first: 1
|
|
15567
|
+
) {
|
|
15568
|
+
items {
|
|
15569
|
+
rtId
|
|
15570
|
+
ckTypeId
|
|
15571
|
+
rtWellKnownName
|
|
15572
|
+
attributes(attributeNames: ["name"]) {
|
|
15573
|
+
items {
|
|
15574
|
+
attributeName
|
|
15575
|
+
value
|
|
15576
|
+
}
|
|
15577
|
+
}
|
|
15578
|
+
associations {
|
|
15579
|
+
parent: targets(
|
|
15580
|
+
roleId: $childRoleId
|
|
15581
|
+
ckId: $childCkTypeId
|
|
15582
|
+
direction: OUTBOUND
|
|
15583
|
+
first: 1
|
|
15584
|
+
) {
|
|
15585
|
+
items {
|
|
15586
|
+
rtId
|
|
15587
|
+
ckTypeId
|
|
15588
|
+
rtWellKnownName
|
|
15589
|
+
attributes(attributeNames: ["name"]) {
|
|
15590
|
+
items {
|
|
15591
|
+
attributeName
|
|
15592
|
+
value
|
|
15593
|
+
}
|
|
15594
|
+
}
|
|
15595
|
+
}
|
|
15596
|
+
}
|
|
15597
|
+
}
|
|
15598
|
+
}
|
|
15599
|
+
}
|
|
15600
|
+
}
|
|
15601
|
+
}
|
|
15602
|
+
}
|
|
15603
|
+
}
|
|
15604
|
+
}
|
|
15605
|
+
}
|
|
15606
|
+
}
|
|
15607
|
+
}
|
|
15608
|
+
`;
|
|
15609
|
+
class GetOrphanCandidatesDtoGQL extends i1$3.Query {
|
|
15610
|
+
document = GetOrphanCandidatesDocumentDto;
|
|
15611
|
+
constructor(apollo) {
|
|
15612
|
+
super(apollo);
|
|
15613
|
+
}
|
|
15614
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetOrphanCandidatesDtoGQL, deps: [{ token: i1$3.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
15615
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetOrphanCandidatesDtoGQL, providedIn: 'root' });
|
|
15616
|
+
}
|
|
15617
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetOrphanCandidatesDtoGQL, decorators: [{
|
|
15618
|
+
type: Injectable,
|
|
15619
|
+
args: [{
|
|
15620
|
+
providedIn: 'root'
|
|
15621
|
+
}]
|
|
15622
|
+
}], ctorParameters: () => [{ type: i1$3.Apollo }] });
|
|
15623
|
+
|
|
15624
|
+
const GetMappingCoverageNodeDocumentDto = gql `
|
|
15625
|
+
query getMappingCoverageNode($rtId: OctoObjectId!, $ckTypeId: String!, $childRoleId: String!, $childCkTypeId: String!, $mappingRoleId: String!, $mappingCkTypeId: String!) {
|
|
15626
|
+
runtime {
|
|
15627
|
+
runtimeEntities(rtId: $rtId, ckId: $ckTypeId) {
|
|
15628
|
+
items {
|
|
15629
|
+
rtId
|
|
15630
|
+
ckTypeId
|
|
15631
|
+
rtWellKnownName
|
|
15632
|
+
attributes(attributeNames: ["name", "description"]) {
|
|
15633
|
+
items {
|
|
15634
|
+
attributeName
|
|
15635
|
+
value
|
|
15636
|
+
}
|
|
15637
|
+
}
|
|
15638
|
+
associations {
|
|
15639
|
+
ownMappings: targets(
|
|
15640
|
+
roleId: $mappingRoleId
|
|
15641
|
+
ckId: $mappingCkTypeId
|
|
15642
|
+
direction: INBOUND
|
|
15643
|
+
) {
|
|
15644
|
+
totalCount
|
|
15645
|
+
}
|
|
15646
|
+
children: targets(
|
|
15647
|
+
roleId: $childRoleId
|
|
15648
|
+
ckId: $childCkTypeId
|
|
15649
|
+
direction: INBOUND
|
|
15650
|
+
) {
|
|
15651
|
+
totalCount
|
|
15652
|
+
items {
|
|
15653
|
+
rtId
|
|
15654
|
+
ckTypeId
|
|
15655
|
+
attributes(attributeNames: ["name", "description"]) {
|
|
15656
|
+
items {
|
|
15657
|
+
attributeName
|
|
15658
|
+
value
|
|
15659
|
+
}
|
|
15660
|
+
}
|
|
15661
|
+
associations {
|
|
15662
|
+
grandChildren: targets(
|
|
15663
|
+
roleId: $childRoleId
|
|
15664
|
+
ckId: $childCkTypeId
|
|
15665
|
+
direction: INBOUND
|
|
15666
|
+
) {
|
|
15667
|
+
totalCount
|
|
15668
|
+
}
|
|
15669
|
+
mappings: targets(
|
|
15670
|
+
roleId: $mappingRoleId
|
|
15671
|
+
ckId: $mappingCkTypeId
|
|
15672
|
+
direction: INBOUND
|
|
15673
|
+
) {
|
|
15674
|
+
totalCount
|
|
15675
|
+
}
|
|
15676
|
+
}
|
|
15677
|
+
}
|
|
15678
|
+
}
|
|
15679
|
+
}
|
|
15680
|
+
}
|
|
15681
|
+
}
|
|
15682
|
+
}
|
|
15683
|
+
}
|
|
15684
|
+
`;
|
|
15685
|
+
class GetMappingCoverageNodeDtoGQL extends i1$3.Query {
|
|
15686
|
+
document = GetMappingCoverageNodeDocumentDto;
|
|
15687
|
+
constructor(apollo) {
|
|
15688
|
+
super(apollo);
|
|
15689
|
+
}
|
|
15690
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetMappingCoverageNodeDtoGQL, deps: [{ token: i1$3.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
15691
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetMappingCoverageNodeDtoGQL, providedIn: 'root' });
|
|
15692
|
+
}
|
|
15693
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetMappingCoverageNodeDtoGQL, decorators: [{
|
|
15694
|
+
type: Injectable,
|
|
15695
|
+
args: [{
|
|
15696
|
+
providedIn: 'root'
|
|
15697
|
+
}]
|
|
15698
|
+
}], ctorParameters: () => [{ type: i1$3.Apollo }] });
|
|
15699
|
+
|
|
15700
|
+
/**
|
|
15701
|
+
* Generic hierarchy data source for the Mapping Coverage Tree.
|
|
15702
|
+
*
|
|
15703
|
+
* Configure with `setRoot(...)` and `setConfig(...)` before passing to a
|
|
15704
|
+
* `<mm-tree-view>` instance. The data source resolves child nodes through the
|
|
15705
|
+
* configured `childRoleId` / `childCkTypeId` and decorates each item with the
|
|
15706
|
+
* number of inbound mappings (via `mappingRoleId`).
|
|
15707
|
+
*/
|
|
15708
|
+
class MappingCoverageTreeDataSource extends HierarchyDataSourceBase {
|
|
15709
|
+
getCoverageNodeGQL = inject(GetMappingCoverageNodeDtoGQL);
|
|
15710
|
+
_root = null;
|
|
15711
|
+
_config = null;
|
|
15712
|
+
_rootMappingCount = 0;
|
|
15713
|
+
_validationMap = new Map();
|
|
15714
|
+
setRoot(root) {
|
|
15715
|
+
this._root = root;
|
|
15716
|
+
}
|
|
15717
|
+
setConfig(config) {
|
|
15718
|
+
this._config = config;
|
|
15719
|
+
}
|
|
15720
|
+
/**
|
|
15721
|
+
* Inject the validation report (rtId → status detail) loaded from the
|
|
15722
|
+
* latest PipelineExecution. Pass an empty map to clear the overlay.
|
|
15723
|
+
* The caller must trigger a tree refresh after updating the map.
|
|
15724
|
+
*/
|
|
15725
|
+
setValidationMap(map) {
|
|
15726
|
+
this._validationMap = map;
|
|
15727
|
+
}
|
|
15728
|
+
getValidationMap() {
|
|
15729
|
+
return this._validationMap;
|
|
15730
|
+
}
|
|
15731
|
+
getRoot() {
|
|
15732
|
+
return this._root;
|
|
15733
|
+
}
|
|
15734
|
+
getRootMappingCount() {
|
|
15735
|
+
return this._rootMappingCount;
|
|
15736
|
+
}
|
|
15737
|
+
async fetchRootNodes() {
|
|
15738
|
+
if (!this._root || !this._config) {
|
|
15739
|
+
return [];
|
|
15740
|
+
}
|
|
15741
|
+
const result = await this.queryNode(this._root.rtId, this._root.ckTypeId);
|
|
15742
|
+
if (!result) {
|
|
15743
|
+
// Fall back: show the root even if we could not load its children counts.
|
|
15744
|
+
return [this.buildItem(this._root, true, 0, true)];
|
|
15745
|
+
}
|
|
15746
|
+
this._rootMappingCount = result.ownMappingCount;
|
|
15747
|
+
const hasChildren = result.children.length > 0;
|
|
15748
|
+
return [this.buildItem(result.entity, true, result.ownMappingCount, hasChildren)];
|
|
15749
|
+
}
|
|
15750
|
+
async fetchChildren(item) {
|
|
15751
|
+
if (!this._config) {
|
|
15752
|
+
return [];
|
|
15753
|
+
}
|
|
15754
|
+
const result = await this.queryNode(item.item.rtId, item.item.ckTypeId);
|
|
15755
|
+
if (!result) {
|
|
15756
|
+
return [];
|
|
15757
|
+
}
|
|
15758
|
+
return result.children.map(child => this.buildItem({ rtId: child.rtId, ckTypeId: child.ckTypeId, name: child.name, description: child.description }, false, child.mappingCount, child.hasGrandChildren));
|
|
15759
|
+
}
|
|
15760
|
+
/**
|
|
15761
|
+
* Reloads the coverage payload (mapping count / hasChildren flag) for a single
|
|
15762
|
+
* entity. Used after CRUD operations on mappings so the badge updates without
|
|
15763
|
+
* collapsing the surrounding subtree.
|
|
15764
|
+
*/
|
|
15765
|
+
async refreshNode(rtId, ckTypeId) {
|
|
15766
|
+
const result = await this.queryNode(rtId, ckTypeId);
|
|
15767
|
+
if (!result) {
|
|
15768
|
+
return null;
|
|
15769
|
+
}
|
|
15770
|
+
const detail = this._validationMap.get(rtId) ?? null;
|
|
15771
|
+
return {
|
|
15772
|
+
...result.entity,
|
|
15773
|
+
mappingCount: result.ownMappingCount,
|
|
15774
|
+
hasChildren: result.children.length > 0,
|
|
15775
|
+
isRoot: this._root?.rtId === rtId,
|
|
15776
|
+
validationStatus: detail?.status ?? null,
|
|
15777
|
+
validationDetail: detail,
|
|
15778
|
+
};
|
|
15779
|
+
}
|
|
15780
|
+
buildItem(entity, isRoot, mappingCount, hasChildren) {
|
|
15781
|
+
const detail = this._validationMap.get(entity.rtId) ?? null;
|
|
15782
|
+
const payload = {
|
|
15783
|
+
...entity,
|
|
15784
|
+
mappingCount,
|
|
15785
|
+
hasChildren,
|
|
15786
|
+
isRoot,
|
|
15787
|
+
validationStatus: detail?.status ?? null,
|
|
15788
|
+
validationDetail: detail,
|
|
15789
|
+
};
|
|
15790
|
+
const text = formatNodeLabel(entity, mappingCount, detail);
|
|
15791
|
+
const tooltip = buildTooltip(entity, detail);
|
|
15792
|
+
// Use the subtree rollup status so info/ok parents reveal hidden warnings
|
|
15793
|
+
// or errors below them. Falls back to own status when subtree info isn't
|
|
15794
|
+
// in the report (older backend) or no detail at all.
|
|
15795
|
+
const iconStatus = detail?.subtreeStatus ?? detail?.status ?? null;
|
|
15796
|
+
const icon = resolveIcon(iconStatus, isRoot);
|
|
15797
|
+
return new TreeItemDataTyped(entity.rtId, text, tooltip, payload, icon, hasChildren, false);
|
|
15798
|
+
}
|
|
15799
|
+
async queryNode(rtId, ckTypeId) {
|
|
15800
|
+
if (!this._config)
|
|
15801
|
+
return null;
|
|
15802
|
+
try {
|
|
15803
|
+
const data = await firstValueFrom(this.getCoverageNodeGQL
|
|
15804
|
+
.fetch({
|
|
15805
|
+
variables: {
|
|
15806
|
+
rtId,
|
|
15807
|
+
ckTypeId,
|
|
15808
|
+
childRoleId: this._config.childRoleId,
|
|
15809
|
+
childCkTypeId: this._config.childCkTypeId,
|
|
15810
|
+
mappingRoleId: this._config.mappingRoleId,
|
|
15811
|
+
mappingCkTypeId: this._config.mappingCkTypeId,
|
|
15812
|
+
},
|
|
15813
|
+
fetchPolicy: 'network-only',
|
|
15814
|
+
})
|
|
15815
|
+
.pipe(map(r => r.data?.runtime?.runtimeEntities?.items?.[0])));
|
|
15816
|
+
if (!data?.rtId || !data.ckTypeId) {
|
|
15817
|
+
return null;
|
|
15818
|
+
}
|
|
15819
|
+
const entity = {
|
|
15820
|
+
rtId: data.rtId,
|
|
15821
|
+
ckTypeId: data.ckTypeId,
|
|
15822
|
+
name: readAttr$1(data.attributes?.items, 'name') ?? data.rtWellKnownName ?? data.rtId,
|
|
15823
|
+
description: readAttr$1(data.attributes?.items, 'description') ?? undefined,
|
|
15824
|
+
};
|
|
15825
|
+
const ownMappingCount = data.associations?.ownMappings?.totalCount ?? 0;
|
|
15826
|
+
const childItems = data.associations?.children?.items ?? [];
|
|
15827
|
+
const children = childItems
|
|
15828
|
+
.filter((c) => !!c && !!c.rtId && !!c.ckTypeId)
|
|
15829
|
+
.map(c => ({
|
|
15830
|
+
rtId: c.rtId,
|
|
15831
|
+
ckTypeId: c.ckTypeId,
|
|
15832
|
+
name: readAttr$1(c.attributes?.items, 'name') ?? c.rtId,
|
|
15833
|
+
description: readAttr$1(c.attributes?.items, 'description') ?? undefined,
|
|
15834
|
+
mappingCount: c.associations?.mappings?.totalCount ?? 0,
|
|
15835
|
+
hasGrandChildren: (c.associations?.grandChildren?.totalCount ?? 0) > 0,
|
|
15836
|
+
}));
|
|
15837
|
+
return { entity, ownMappingCount, children };
|
|
15838
|
+
}
|
|
15839
|
+
catch (error) {
|
|
15840
|
+
console.error('Failed to load coverage node', error);
|
|
15841
|
+
return null;
|
|
15842
|
+
}
|
|
15843
|
+
}
|
|
15844
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingCoverageTreeDataSource, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
15845
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingCoverageTreeDataSource });
|
|
15846
|
+
}
|
|
15847
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingCoverageTreeDataSource, decorators: [{
|
|
15848
|
+
type: Injectable
|
|
15849
|
+
}] });
|
|
15850
|
+
function readAttr$1(items, name) {
|
|
15851
|
+
if (!items)
|
|
15852
|
+
return null;
|
|
15853
|
+
const target = name.toLowerCase();
|
|
15854
|
+
for (const item of items) {
|
|
15855
|
+
if (item?.attributeName != null && item.attributeName.toLowerCase() === target && item.value != null) {
|
|
15856
|
+
return String(item.value);
|
|
15857
|
+
}
|
|
15858
|
+
}
|
|
15859
|
+
return null;
|
|
15860
|
+
}
|
|
15861
|
+
function formatNodeLabel(entity, mappingCount, detail) {
|
|
15862
|
+
const base = entity.name || entity.rtId;
|
|
15863
|
+
const countSuffix = mappingCount > 0 ? ` [${mappingCount}]` : '';
|
|
15864
|
+
if (detail && (detail.missingRequired.length > 0 || detail.missingRecommended.length > 0)) {
|
|
15865
|
+
const missing = [...detail.missingRequired, ...detail.missingRecommended].join(', ');
|
|
15866
|
+
return `${base}${countSuffix} — missing: ${missing}`;
|
|
15867
|
+
}
|
|
15868
|
+
return `${base}${countSuffix}`;
|
|
15869
|
+
}
|
|
15870
|
+
function buildTooltip(entity, detail) {
|
|
15871
|
+
const head = entity.description ?? `${entity.ckTypeId}@${entity.rtId}`;
|
|
15872
|
+
if (!detail)
|
|
15873
|
+
return head;
|
|
15874
|
+
const lines = [head, `Status: ${detail.status}`];
|
|
15875
|
+
// Flag subtree problems on `info` / `ok` parents so the user knows to drill
|
|
15876
|
+
// in. Only show when the subtree is strictly worse than the node itself.
|
|
15877
|
+
const counts = detail.subtreeCounts;
|
|
15878
|
+
if (counts) {
|
|
15879
|
+
const errBelow = counts.error - (detail.status === 'error' ? 1 : 0);
|
|
15880
|
+
const warnBelow = counts.warning - (detail.status === 'warning' ? 1 : 0);
|
|
15881
|
+
if (errBelow > 0 || warnBelow > 0) {
|
|
15882
|
+
const parts = [];
|
|
15883
|
+
if (errBelow > 0)
|
|
15884
|
+
parts.push(`${errBelow} error${errBelow === 1 ? '' : 's'}`);
|
|
15885
|
+
if (warnBelow > 0)
|
|
15886
|
+
parts.push(`${warnBelow} warning${warnBelow === 1 ? '' : 's'}`);
|
|
15887
|
+
lines.push(`Below: ${parts.join(', ')}`);
|
|
15888
|
+
}
|
|
15889
|
+
}
|
|
15890
|
+
if (detail.missingRequired.length > 0) {
|
|
15891
|
+
lines.push(`Missing required: ${detail.missingRequired.join(', ')}`);
|
|
15892
|
+
}
|
|
15893
|
+
if (detail.missingRecommended.length > 0) {
|
|
15894
|
+
lines.push(`Missing recommended: ${detail.missingRecommended.join(', ')}`);
|
|
15895
|
+
}
|
|
15896
|
+
if (detail.present.length > 0) {
|
|
15897
|
+
lines.push(`Present: ${detail.present.join(', ')}`);
|
|
15898
|
+
}
|
|
15899
|
+
return lines.join('\n');
|
|
15900
|
+
}
|
|
15901
|
+
/**
|
|
15902
|
+
* Wraps a Kendo SVG icon's <path> markup in a coloured group so the rendered
|
|
15903
|
+
* icon picks up a semantic colour at the SVG layer — no CSS hooks needed.
|
|
15904
|
+
*
|
|
15905
|
+
* Kendo's `kendo-svgicon` writes the icon content as-is inside an outer
|
|
15906
|
+
* `<svg>`, and the path has no fill of its own (so it inherits currentColor).
|
|
15907
|
+
* Wrapping in `<g fill="…">` forces a literal colour and survives the
|
|
15908
|
+
* surrounding `color: inherit` in the tree template.
|
|
15909
|
+
*
|
|
15910
|
+
* We embed the hex value rather than a CSS variable because SVG attributes
|
|
15911
|
+
* inside the content string aren't resolved by CSS — they're literal.
|
|
15912
|
+
*/
|
|
15913
|
+
function colorize(icon, color) {
|
|
15914
|
+
return {
|
|
15915
|
+
name: `${icon.name}-${color.replace('#', '')}`,
|
|
15916
|
+
viewBox: icon.viewBox,
|
|
15917
|
+
content: `<g fill="${color}">${icon.content}</g>`,
|
|
15918
|
+
};
|
|
15919
|
+
}
|
|
15920
|
+
// Semantic palette — matches the badge colours used in the toolbar summary
|
|
15921
|
+
// (badge-error/-warning/-ok/-info) so the legend and the tree agree.
|
|
15922
|
+
const STATUS_COLORS = {
|
|
15923
|
+
ok: '#28a745', // success green
|
|
15924
|
+
warning: '#ffc107', // amber
|
|
15925
|
+
error: '#dc3545', // red
|
|
15926
|
+
info: '#0dcaf0', // info cyan
|
|
15927
|
+
};
|
|
15928
|
+
const COLORED_STATUS_ICONS = {
|
|
15929
|
+
ok: colorize(checkCircleIcon, STATUS_COLORS.ok),
|
|
15930
|
+
warning: colorize(warningCircleIcon, STATUS_COLORS.warning),
|
|
15931
|
+
error: colorize(exclamationCircleIcon, STATUS_COLORS.error),
|
|
15932
|
+
info: colorize(infoCircleIcon, STATUS_COLORS.info),
|
|
15933
|
+
};
|
|
15934
|
+
function resolveIcon(status, isRoot) {
|
|
15935
|
+
if (status && COLORED_STATUS_ICONS[status]) {
|
|
15936
|
+
return COLORED_STATUS_ICONS[status];
|
|
15937
|
+
}
|
|
15938
|
+
return isRoot ? folderIcon : gridIcon;
|
|
15939
|
+
}
|
|
15940
|
+
|
|
15941
|
+
/**
|
|
15942
|
+
* Focused single-mapping editor dialog. The host renders this via
|
|
15943
|
+
* `MappingEditDialogService.open(...)` and awaits the resulting promise:
|
|
15944
|
+
* `confirmed=true` means the user saved (caller persists), `confirmed=false`
|
|
15945
|
+
* means cancel.
|
|
15946
|
+
*/
|
|
15947
|
+
class MappingEditDialogComponent {
|
|
15948
|
+
windowRef = inject(WindowRef);
|
|
15949
|
+
entitySelector = inject(EntitySelectorDialogService);
|
|
15950
|
+
attributeSelectorDialog = inject(AttributeSelectorDialogService);
|
|
15951
|
+
attributeService = inject(AttributeSelectorService);
|
|
15952
|
+
icons = {
|
|
15953
|
+
link: hyperlinkOpenIcon,
|
|
15954
|
+
browse: folderOpenIcon,
|
|
15955
|
+
regenerate: arrowRotateCwIcon,
|
|
15956
|
+
};
|
|
15957
|
+
/** Set by the service before the dialog content is shown. */
|
|
15958
|
+
data = {
|
|
15959
|
+
mapping: this.emptyValue(),
|
|
15960
|
+
};
|
|
15961
|
+
/** Reactive working copy — the form binds to this. */
|
|
15962
|
+
model = signal(this.emptyValue(), ...(ngDevMode ? [{ debugName: "model" }] : /* istanbul ignore next */ []));
|
|
15963
|
+
/** Target CK-type attribute catalogue. The source side is now handled by
|
|
15964
|
+
* the dedicated {@link DataPointPickerComponent}, which loads runtime state
|
|
15965
|
+
* names from the entity itself instead of CK schema attributes. */
|
|
15966
|
+
targetAttributes = signal([], ...(ngDevMode ? [{ debugName: "targetAttributes" }] : /* istanbul ignore next */ []));
|
|
15967
|
+
targetFilter = signal('', ...(ngDevMode ? [{ debugName: "targetFilter" }] : /* istanbul ignore next */ []));
|
|
15968
|
+
targetAttributesLoading = signal(false, ...(ngDevMode ? [{ debugName: "targetAttributesLoading" }] : /* istanbul ignore next */ []));
|
|
15969
|
+
targetAttributeList = computed(() => filterAttributes(this.targetAttributes(), this.targetFilter()), ...(ngDevMode ? [{ debugName: "targetAttributeList" }] : /* istanbul ignore next */ []));
|
|
15970
|
+
/**
|
|
15971
|
+
* Effective target CK type id — prefers the value picked by the user via
|
|
15972
|
+
* {@link pickTarget}; falls back to the optional {@link MappingEditDialogData.targetCkTypeId}
|
|
15973
|
+
* legacy default. Drives the Target Attribute Path autocomplete dropdown.
|
|
15974
|
+
*/
|
|
15975
|
+
effectiveTargetCkTypeId = computed(() => this.model().targetCkTypeId ?? this.data.targetCkTypeId, ...(ngDevMode ? [{ debugName: "effectiveTargetCkTypeId" }] : /* istanbul ignore next */ []));
|
|
15976
|
+
initialise(data) {
|
|
15977
|
+
this.data = data;
|
|
15978
|
+
// Seed mapping.targetCkTypeId from the legacy data field when the caller
|
|
15979
|
+
// hasn't set it yet — covers the tree-context flow where the dialog was
|
|
15980
|
+
// opened for a fixed target node and the value was passed via data.
|
|
15981
|
+
const seededTargetCkTypeId = data.mapping.targetCkTypeId ?? data.targetCkTypeId;
|
|
15982
|
+
this.model.set({
|
|
15983
|
+
...data.mapping,
|
|
15984
|
+
targetCkTypeId: seededTargetCkTypeId,
|
|
15985
|
+
_originalSourceRtId: data.mapping.sourceRtId,
|
|
15986
|
+
_originalSourceCkTypeId: data.mapping.sourceCkTypeId,
|
|
15987
|
+
_originalTargetRtId: data.mapping.targetRtId,
|
|
15988
|
+
_originalTargetCkTypeId: data.mapping.targetCkTypeId,
|
|
15989
|
+
});
|
|
15990
|
+
if (seededTargetCkTypeId) {
|
|
15991
|
+
void this.loadTargetAttributes(seededTargetCkTypeId);
|
|
15992
|
+
}
|
|
15993
|
+
}
|
|
15994
|
+
/**
|
|
15995
|
+
* Enabled when at least one labelled side of the mapping is filled enough
|
|
15996
|
+
* to produce a meaningful name. We require *some* identifying info on each
|
|
15997
|
+
* end so the generated name isn't just "(unset) → (unset)".
|
|
15998
|
+
*/
|
|
15999
|
+
canGenerateName() {
|
|
16000
|
+
const m = this.model();
|
|
16001
|
+
const sourceLabel = m.sourceName || m.sourceRtId;
|
|
16002
|
+
const targetLabel = m.targetName || m.targetRtId;
|
|
16003
|
+
return !!(sourceLabel && targetLabel);
|
|
16004
|
+
}
|
|
16005
|
+
/**
|
|
16006
|
+
* Writes a deterministic, human-readable name into the mapping based on the
|
|
16007
|
+
* current source/target selection. Format:
|
|
16008
|
+
* `{sourceName} {sourcePath} → {targetName} {targetPath}`
|
|
16009
|
+
* Falls back to rtId fragments when names are missing. The user can still
|
|
16010
|
+
* edit the result freely — this just gives them a sensible starting point
|
|
16011
|
+
* so they don't have to invent a name from scratch when finishing an
|
|
16012
|
+
* orphan-tab mapping.
|
|
16013
|
+
*/
|
|
16014
|
+
generateName() {
|
|
16015
|
+
const m = this.model();
|
|
16016
|
+
const source = describeEnd(m.sourceName, m.sourceRtId, m.sourceAttributePath);
|
|
16017
|
+
const target = describeEnd(m.targetName, m.targetRtId, m.targetAttributePath);
|
|
16018
|
+
if (!source && !target)
|
|
16019
|
+
return;
|
|
16020
|
+
const generated = `${source || '(no source)'} → ${target || '(no target)'}`;
|
|
16021
|
+
this.model.update(curr => ({ ...curr, name: generated }));
|
|
16022
|
+
}
|
|
16023
|
+
isValid() {
|
|
16024
|
+
const m = this.model();
|
|
16025
|
+
// Both ends must be linked and the target attribute path filled — a
|
|
16026
|
+
// mapping without these can't actually fire on the runtime side.
|
|
16027
|
+
if (!m.sourceRtId || !m.sourceCkTypeId)
|
|
16028
|
+
return false;
|
|
16029
|
+
if (!m.targetRtId || !m.targetCkTypeId)
|
|
16030
|
+
return false;
|
|
16031
|
+
return !!m.targetAttributePath && m.targetAttributePath.trim().length > 0;
|
|
16032
|
+
}
|
|
16033
|
+
async pickSource() {
|
|
16034
|
+
const current = this.model();
|
|
16035
|
+
const result = await this.entitySelector.openEntitySelector({
|
|
16036
|
+
title: 'Select Source Entity',
|
|
16037
|
+
currentTargetRtId: current.sourceRtId,
|
|
16038
|
+
currentTargetCkTypeId: current.sourceCkTypeId,
|
|
16039
|
+
});
|
|
16040
|
+
if (!result.confirmed || !result.entity)
|
|
16041
|
+
return;
|
|
16042
|
+
const changedType = result.entity.ckTypeId !== current.sourceCkTypeId;
|
|
16043
|
+
this.model.update(m => ({
|
|
16044
|
+
...m,
|
|
16045
|
+
sourceRtId: result.entity.rtId,
|
|
16046
|
+
sourceCkTypeId: result.entity.ckTypeId,
|
|
16047
|
+
sourceName: result.entity.name ?? result.entity.rtId,
|
|
16048
|
+
// Reset the path when the type changes — the previous attribute likely
|
|
16049
|
+
// doesn't exist on the new type.
|
|
16050
|
+
sourceAttributePath: changedType ? '' : m.sourceAttributePath,
|
|
16051
|
+
}));
|
|
16052
|
+
// The data-point picker watches sourceCkTypeId/sourceRtId and refreshes
|
|
16053
|
+
// its option list on its own — no extra wiring needed here.
|
|
16054
|
+
}
|
|
16055
|
+
async pickTarget() {
|
|
16056
|
+
const current = this.model();
|
|
16057
|
+
const result = await this.entitySelector.openEntitySelector({
|
|
16058
|
+
title: 'Select Target Entity',
|
|
16059
|
+
currentTargetRtId: current.targetRtId,
|
|
16060
|
+
currentTargetCkTypeId: current.targetCkTypeId,
|
|
16061
|
+
});
|
|
16062
|
+
if (!result.confirmed || !result.entity)
|
|
16063
|
+
return;
|
|
16064
|
+
const changedType = result.entity.ckTypeId !== current.targetCkTypeId;
|
|
16065
|
+
this.model.update(m => ({
|
|
16066
|
+
...m,
|
|
16067
|
+
targetRtId: result.entity.rtId,
|
|
16068
|
+
targetCkTypeId: result.entity.ckTypeId,
|
|
16069
|
+
targetName: result.entity.name ?? result.entity.rtId,
|
|
16070
|
+
targetAttributePath: changedType ? '' : m.targetAttributePath,
|
|
16071
|
+
}));
|
|
16072
|
+
if (changedType) {
|
|
16073
|
+
this.targetFilter.set('');
|
|
16074
|
+
void this.loadTargetAttributes(result.entity.ckTypeId);
|
|
16075
|
+
}
|
|
16076
|
+
}
|
|
16077
|
+
onSourceAttributePathChange(value) {
|
|
16078
|
+
this.model.update(m => ({ ...m, sourceAttributePath: value }));
|
|
16079
|
+
}
|
|
16080
|
+
onTargetAttributePathChange(value) {
|
|
16081
|
+
this.model.update(m => ({ ...m, targetAttributePath: value ?? '' }));
|
|
16082
|
+
}
|
|
16083
|
+
onTargetAttributeFilter(filter) {
|
|
16084
|
+
this.targetFilter.set(filter);
|
|
16085
|
+
}
|
|
16086
|
+
onSave() {
|
|
16087
|
+
if (!this.isValid())
|
|
16088
|
+
return;
|
|
16089
|
+
const result = { confirmed: true, mapping: this.model() };
|
|
16090
|
+
this.windowRef.close(result);
|
|
16091
|
+
}
|
|
16092
|
+
onCancel() {
|
|
16093
|
+
const result = { confirmed: false };
|
|
16094
|
+
this.windowRef.close(result);
|
|
16095
|
+
}
|
|
16096
|
+
/**
|
|
16097
|
+
* Fetches the CK-schema attribute catalogue for the target side and stores
|
|
16098
|
+
* it for the target combobox dropdown. Navigation properties are excluded —
|
|
16099
|
+
* they drown out the direct attributes; the browse button below opens the
|
|
16100
|
+
* full {@link AttributeSelectorDialog} for users who need a deep path.
|
|
16101
|
+
*
|
|
16102
|
+
* (The source side now uses {@link DataPointPickerComponent}, which queries
|
|
16103
|
+
* the runtime entity's States RecordArray directly — runtime data points,
|
|
16104
|
+
* not CK schema attributes, are what the runtime engine matches against.)
|
|
16105
|
+
*/
|
|
16106
|
+
async loadTargetAttributes(ckTypeId) {
|
|
16107
|
+
this.targetAttributesLoading.set(true);
|
|
16108
|
+
try {
|
|
16109
|
+
const result = await firstValueFrom(this.attributeService.getAvailableAttributes(ckTypeId, undefined, // filter
|
|
16110
|
+
500, // first
|
|
16111
|
+
undefined, // after
|
|
16112
|
+
undefined, // attributeValueType
|
|
16113
|
+
undefined, // searchTerm
|
|
16114
|
+
false, // includeNavigationProperties — keep the list focused
|
|
16115
|
+
0));
|
|
16116
|
+
this.targetAttributes.set(result.items);
|
|
16117
|
+
}
|
|
16118
|
+
catch (err) {
|
|
16119
|
+
console.error(`Failed to load target attributes for ${ckTypeId}:`, err);
|
|
16120
|
+
this.targetAttributes.set([]);
|
|
16121
|
+
}
|
|
16122
|
+
finally {
|
|
16123
|
+
this.targetAttributesLoading.set(false);
|
|
16124
|
+
}
|
|
16125
|
+
}
|
|
16126
|
+
async browseTargetAttribute() {
|
|
16127
|
+
const ckTypeId = this.effectiveTargetCkTypeId();
|
|
16128
|
+
if (!ckTypeId)
|
|
16129
|
+
return;
|
|
16130
|
+
const current = this.model().targetAttributePath;
|
|
16131
|
+
const result = await this.attributeSelectorDialog.openAttributeSelector(ckTypeId, current ? [current] : undefined, 'Select Target Attribute Path', true);
|
|
16132
|
+
if (result.confirmed && result.selectedAttributes.length > 0) {
|
|
16133
|
+
this.model.update(m => ({
|
|
16134
|
+
...m,
|
|
16135
|
+
targetAttributePath: result.selectedAttributes[0].attributePath,
|
|
16136
|
+
}));
|
|
16137
|
+
}
|
|
16138
|
+
}
|
|
16139
|
+
emptyValue() {
|
|
16140
|
+
return {
|
|
16141
|
+
rtId: '',
|
|
16142
|
+
ckTypeId: '',
|
|
16143
|
+
name: '',
|
|
16144
|
+
enabled: true,
|
|
16145
|
+
sourceAttributePath: '',
|
|
16146
|
+
mappingExpression: '',
|
|
16147
|
+
targetAttributePath: '',
|
|
16148
|
+
};
|
|
16149
|
+
}
|
|
16150
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingEditDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
16151
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MappingEditDialogComponent, isStandalone: true, selector: "mm-mapping-edit-dialog", ngImport: i0, template: `
|
|
16152
|
+
<div class="mapping-edit">
|
|
16153
|
+
<div class="mapping-edit-body">
|
|
16154
|
+
<div class="field-row">
|
|
16155
|
+
<label>Name</label>
|
|
16156
|
+
<div class="combo-row">
|
|
16157
|
+
<kendo-textbox [(value)]="model().name"
|
|
16158
|
+
placeholder="Mapping name">
|
|
16159
|
+
</kendo-textbox>
|
|
16160
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16161
|
+
[svgIcon]="icons.regenerate"
|
|
16162
|
+
[disabled]="!canGenerateName()"
|
|
16163
|
+
(click)="generateName()"
|
|
16164
|
+
title="Generate name from source + target"></button>
|
|
16165
|
+
</div>
|
|
16166
|
+
</div>
|
|
16167
|
+
|
|
16168
|
+
<div class="field-row inline">
|
|
16169
|
+
<label>Enabled</label>
|
|
16170
|
+
<kendo-switch [(ngModel)]="model().enabled" size="small"></kendo-switch>
|
|
16171
|
+
@if (!model().enabled) {
|
|
16172
|
+
<span class="hint">Mapping is disabled — it will be skipped by data acquisition.</span>
|
|
16173
|
+
}
|
|
16174
|
+
</div>
|
|
16175
|
+
|
|
16176
|
+
<div class="field-row">
|
|
16177
|
+
<label>Source Entity</label>
|
|
16178
|
+
@if (model().sourceRtId) {
|
|
16179
|
+
<div class="entity-display">
|
|
16180
|
+
<div class="entity-main">
|
|
16181
|
+
<span class="entity-name">{{ model().sourceName || model().sourceRtId }}</span>
|
|
16182
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16183
|
+
(click)="pickSource()">Change…</button>
|
|
16184
|
+
</div>
|
|
16185
|
+
<div class="entity-meta">
|
|
16186
|
+
<span>{{ model().sourceCkTypeId }}</span>
|
|
16187
|
+
<span class="sep">@</span>
|
|
16188
|
+
<span>{{ model().sourceRtId }}</span>
|
|
16189
|
+
</div>
|
|
16190
|
+
</div>
|
|
16191
|
+
} @else {
|
|
16192
|
+
<div class="entity-display empty">
|
|
16193
|
+
<span class="hint">No source entity linked.</span>
|
|
16194
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16195
|
+
(click)="pickSource()">Select…</button>
|
|
16196
|
+
</div>
|
|
16197
|
+
}
|
|
16198
|
+
</div>
|
|
16199
|
+
|
|
16200
|
+
<div class="field-row">
|
|
16201
|
+
<label>Source Data Point</label>
|
|
16202
|
+
<mm-data-point-picker
|
|
16203
|
+
[entityRtId]="model().sourceRtId"
|
|
16204
|
+
[entityCkTypeId]="model().sourceCkTypeId"
|
|
16205
|
+
[value]="model().sourceAttributePath"
|
|
16206
|
+
[disabled]="!model().sourceRtId || !model().sourceCkTypeId"
|
|
16207
|
+
(valueChange)="onSourceAttributePathChange($event)">
|
|
16208
|
+
</mm-data-point-picker>
|
|
16209
|
+
<span class="hint">
|
|
16210
|
+
@if (model().sourceRtId && model().sourceCkTypeId) {
|
|
16211
|
+
Data points exposed by the source entity's
|
|
16212
|
+
<code>States</code> / <code>DataPoints</code> array, with
|
|
16213
|
+
<code>currentValue</code> as the default for single-state controls.
|
|
16214
|
+
Free text allowed.
|
|
16215
|
+
} @else {
|
|
16216
|
+
Pick a source entity above to load its available data points.
|
|
16217
|
+
}
|
|
16218
|
+
</span>
|
|
16219
|
+
</div>
|
|
16220
|
+
|
|
16221
|
+
<div class="field-row">
|
|
16222
|
+
<label>Mapping Expression</label>
|
|
16223
|
+
<kendo-textbox [(value)]="model().mappingExpression"
|
|
16224
|
+
placeholder="e.g. value, value / 100, value > 50 ? 1 : 0">
|
|
16225
|
+
</kendo-textbox>
|
|
16226
|
+
<span class="hint">Optional. mXparser expression. <code>value</code> = raw source value.</span>
|
|
16227
|
+
</div>
|
|
16228
|
+
|
|
16229
|
+
<div class="field-row">
|
|
16230
|
+
<label>Target Entity</label>
|
|
16231
|
+
@if (model().targetRtId) {
|
|
16232
|
+
<div class="entity-display">
|
|
16233
|
+
<div class="entity-main">
|
|
16234
|
+
<span class="entity-name">{{ model().targetName || model().targetRtId }}</span>
|
|
16235
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16236
|
+
(click)="pickTarget()">Change…</button>
|
|
16237
|
+
</div>
|
|
16238
|
+
<div class="entity-meta">
|
|
16239
|
+
<span>{{ model().targetCkTypeId }}</span>
|
|
16240
|
+
<span class="sep">@</span>
|
|
16241
|
+
<span>{{ model().targetRtId }}</span>
|
|
16242
|
+
</div>
|
|
16243
|
+
</div>
|
|
16244
|
+
} @else {
|
|
16245
|
+
<div class="entity-display empty">
|
|
16246
|
+
<span class="hint">No target entity linked.</span>
|
|
16247
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16248
|
+
(click)="pickTarget()">Select…</button>
|
|
16249
|
+
</div>
|
|
16250
|
+
}
|
|
16251
|
+
</div>
|
|
16252
|
+
|
|
16253
|
+
<div class="field-row">
|
|
16254
|
+
<label>
|
|
16255
|
+
Target Attribute Path
|
|
16256
|
+
@if (targetAttributesLoading()) { <span class="loading-pill">loading…</span> }
|
|
16257
|
+
</label>
|
|
16258
|
+
<div class="combo-row">
|
|
16259
|
+
<kendo-combobox
|
|
16260
|
+
[data]="targetAttributeList()"
|
|
16261
|
+
[value]="model().targetAttributePath"
|
|
16262
|
+
(valueChange)="onTargetAttributePathChange($event)"
|
|
16263
|
+
[textField]="'attributePath'"
|
|
16264
|
+
[valueField]="'attributePath'"
|
|
16265
|
+
[valuePrimitive]="true"
|
|
16266
|
+
[allowCustom]="true"
|
|
16267
|
+
[filterable]="true"
|
|
16268
|
+
(filterChange)="onTargetAttributeFilter($event)"
|
|
16269
|
+
[popupSettings]="{ appendTo: 'root', animate: true }"
|
|
16270
|
+
placeholder="e.g. Temperature, CO2Level">
|
|
16271
|
+
<ng-template kendoComboBoxItemTemplate let-item>
|
|
16272
|
+
<div class="attribute-option">
|
|
16273
|
+
<span class="attribute-path">{{ item.attributePath }}</span>
|
|
16274
|
+
<span class="attribute-type">{{ item.attributeValueType }}</span>
|
|
16275
|
+
</div>
|
|
16276
|
+
</ng-template>
|
|
16277
|
+
</kendo-combobox>
|
|
16278
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16279
|
+
[svgIcon]="icons.browse"
|
|
16280
|
+
[disabled]="!effectiveTargetCkTypeId()"
|
|
16281
|
+
(click)="browseTargetAttribute()"
|
|
16282
|
+
title="Browse all attributes (incl. navigation properties)"></button>
|
|
16283
|
+
</div>
|
|
16284
|
+
<span class="hint">
|
|
16285
|
+
@if (effectiveTargetCkTypeId(); as t) {
|
|
16286
|
+
Direct attributes on <code>{{ t }}</code>. Click the browse button
|
|
16287
|
+
for navigation properties or to escape autocomplete.
|
|
16288
|
+
} @else {
|
|
16289
|
+
Pick a target entity above to see available attribute paths.
|
|
16290
|
+
}
|
|
16291
|
+
</span>
|
|
16292
|
+
</div>
|
|
16293
|
+
|
|
16294
|
+
</div>
|
|
16295
|
+
<div class="dialog-actions">
|
|
16296
|
+
<button kendoButton (click)="onCancel()">Cancel</button>
|
|
16297
|
+
<button kendoButton themeColor="primary" (click)="onSave()"
|
|
16298
|
+
[disabled]="!isValid()">Save</button>
|
|
16299
|
+
</div>
|
|
16300
|
+
</div>
|
|
16301
|
+
`, isInline: true, styles: [".mapping-edit{display:flex;flex-direction:column;height:100%;box-sizing:border-box}.mapping-edit-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:14px 18px;display:flex;flex-direction:column;gap:14px}.field-row{display:flex;flex-direction:column;gap:4px}.field-row label{font-size:.72rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.field-row .hint{font-size:.7rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));font-style:italic}.field-row.inline{flex-direction:row;align-items:center;gap:8px}.entity-display{display:flex;flex-direction:column;gap:2px;padding:6px 10px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:4px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 65%,transparent)}.entity-display.empty{flex-direction:row;align-items:center;justify-content:space-between}.entity-main{display:flex;align-items:center;gap:8px}.entity-main .entity-name{flex:1;font-weight:600;font-size:.9rem}.entity-meta{display:flex;align-items:center;gap:2px;font-size:.7rem;font-family:monospace;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.entity-meta .sep{opacity:.7}.dialog-actions{flex:0 0 auto;display:flex;gap:8px;justify-content:flex-end;padding:10px 18px;border-top:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 70%,transparent)}.combo-row{display:flex;align-items:stretch;gap:4px}.combo-row kendo-combobox{flex:1}.attribute-option{display:flex;justify-content:space-between;align-items:center;width:100%;gap:8px}.attribute-option .attribute-path{flex:1;font-family:monospace;font-size:.85rem}.attribute-option .attribute-type{font-size:.7rem;padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f0f0f0)) 80%,transparent);color:var(--theme-text-secondary, var(--kendo-color-subtle, #888))}.loading-pill{margin-left:6px;font-size:.65rem;padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--kendo-color-info, #0dcaf0) 18%,transparent);color:var(--kendo-color-info, #0dcaf0);text-transform:none;letter-spacing:0;font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: ComboBoxModule }, { kind: "component", type: i3$1.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "directive", type: i3$1.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "ngmodule", type: SVGIconModule }, { kind: "ngmodule", type: SwitchModule }, { kind: "component", type: i5.SwitchComponent, selector: "kendo-switch", inputs: ["focusableId", "onLabel", "offLabel", "checked", "disabled", "readonly", "tabindex", "size", "thumbRounded", "trackRounded", "tabIndex"], outputs: ["focus", "blur", "valueChange"], exportAs: ["kendoSwitch"] }, { kind: "ngmodule", type: TextBoxModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: DataPointPickerComponent, selector: "mm-data-point-picker", inputs: ["entity", "entityRtId", "entityCkTypeId", "value", "placeholder", "disabled"], outputs: ["valueChange", "filterChange"] }] });
|
|
16302
|
+
}
|
|
16303
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingEditDialogComponent, decorators: [{
|
|
16304
|
+
type: Component,
|
|
16305
|
+
args: [{ selector: 'mm-mapping-edit-dialog', standalone: true, imports: [
|
|
16306
|
+
CommonModule,
|
|
16307
|
+
FormsModule,
|
|
16308
|
+
ButtonModule,
|
|
16309
|
+
ComboBoxModule,
|
|
16310
|
+
SVGIconModule,
|
|
16311
|
+
SwitchModule,
|
|
16312
|
+
TextBoxModule,
|
|
16313
|
+
DataPointPickerComponent,
|
|
16314
|
+
], template: `
|
|
16315
|
+
<div class="mapping-edit">
|
|
16316
|
+
<div class="mapping-edit-body">
|
|
16317
|
+
<div class="field-row">
|
|
16318
|
+
<label>Name</label>
|
|
16319
|
+
<div class="combo-row">
|
|
16320
|
+
<kendo-textbox [(value)]="model().name"
|
|
16321
|
+
placeholder="Mapping name">
|
|
16322
|
+
</kendo-textbox>
|
|
16323
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16324
|
+
[svgIcon]="icons.regenerate"
|
|
16325
|
+
[disabled]="!canGenerateName()"
|
|
16326
|
+
(click)="generateName()"
|
|
16327
|
+
title="Generate name from source + target"></button>
|
|
16328
|
+
</div>
|
|
16329
|
+
</div>
|
|
16330
|
+
|
|
16331
|
+
<div class="field-row inline">
|
|
16332
|
+
<label>Enabled</label>
|
|
16333
|
+
<kendo-switch [(ngModel)]="model().enabled" size="small"></kendo-switch>
|
|
16334
|
+
@if (!model().enabled) {
|
|
16335
|
+
<span class="hint">Mapping is disabled — it will be skipped by data acquisition.</span>
|
|
16336
|
+
}
|
|
16337
|
+
</div>
|
|
16338
|
+
|
|
16339
|
+
<div class="field-row">
|
|
16340
|
+
<label>Source Entity</label>
|
|
16341
|
+
@if (model().sourceRtId) {
|
|
16342
|
+
<div class="entity-display">
|
|
16343
|
+
<div class="entity-main">
|
|
16344
|
+
<span class="entity-name">{{ model().sourceName || model().sourceRtId }}</span>
|
|
16345
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16346
|
+
(click)="pickSource()">Change…</button>
|
|
16347
|
+
</div>
|
|
16348
|
+
<div class="entity-meta">
|
|
16349
|
+
<span>{{ model().sourceCkTypeId }}</span>
|
|
16350
|
+
<span class="sep">@</span>
|
|
16351
|
+
<span>{{ model().sourceRtId }}</span>
|
|
16352
|
+
</div>
|
|
16353
|
+
</div>
|
|
16354
|
+
} @else {
|
|
16355
|
+
<div class="entity-display empty">
|
|
16356
|
+
<span class="hint">No source entity linked.</span>
|
|
16357
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16358
|
+
(click)="pickSource()">Select…</button>
|
|
16359
|
+
</div>
|
|
16360
|
+
}
|
|
16361
|
+
</div>
|
|
16362
|
+
|
|
16363
|
+
<div class="field-row">
|
|
16364
|
+
<label>Source Data Point</label>
|
|
16365
|
+
<mm-data-point-picker
|
|
16366
|
+
[entityRtId]="model().sourceRtId"
|
|
16367
|
+
[entityCkTypeId]="model().sourceCkTypeId"
|
|
16368
|
+
[value]="model().sourceAttributePath"
|
|
16369
|
+
[disabled]="!model().sourceRtId || !model().sourceCkTypeId"
|
|
16370
|
+
(valueChange)="onSourceAttributePathChange($event)">
|
|
16371
|
+
</mm-data-point-picker>
|
|
16372
|
+
<span class="hint">
|
|
16373
|
+
@if (model().sourceRtId && model().sourceCkTypeId) {
|
|
16374
|
+
Data points exposed by the source entity's
|
|
16375
|
+
<code>States</code> / <code>DataPoints</code> array, with
|
|
16376
|
+
<code>currentValue</code> as the default for single-state controls.
|
|
16377
|
+
Free text allowed.
|
|
16378
|
+
} @else {
|
|
16379
|
+
Pick a source entity above to load its available data points.
|
|
16380
|
+
}
|
|
16381
|
+
</span>
|
|
16382
|
+
</div>
|
|
16383
|
+
|
|
16384
|
+
<div class="field-row">
|
|
16385
|
+
<label>Mapping Expression</label>
|
|
16386
|
+
<kendo-textbox [(value)]="model().mappingExpression"
|
|
16387
|
+
placeholder="e.g. value, value / 100, value > 50 ? 1 : 0">
|
|
16388
|
+
</kendo-textbox>
|
|
16389
|
+
<span class="hint">Optional. mXparser expression. <code>value</code> = raw source value.</span>
|
|
16390
|
+
</div>
|
|
16391
|
+
|
|
16392
|
+
<div class="field-row">
|
|
16393
|
+
<label>Target Entity</label>
|
|
16394
|
+
@if (model().targetRtId) {
|
|
16395
|
+
<div class="entity-display">
|
|
16396
|
+
<div class="entity-main">
|
|
16397
|
+
<span class="entity-name">{{ model().targetName || model().targetRtId }}</span>
|
|
16398
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16399
|
+
(click)="pickTarget()">Change…</button>
|
|
16400
|
+
</div>
|
|
16401
|
+
<div class="entity-meta">
|
|
16402
|
+
<span>{{ model().targetCkTypeId }}</span>
|
|
16403
|
+
<span class="sep">@</span>
|
|
16404
|
+
<span>{{ model().targetRtId }}</span>
|
|
16405
|
+
</div>
|
|
16406
|
+
</div>
|
|
16407
|
+
} @else {
|
|
16408
|
+
<div class="entity-display empty">
|
|
16409
|
+
<span class="hint">No target entity linked.</span>
|
|
16410
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16411
|
+
(click)="pickTarget()">Select…</button>
|
|
16412
|
+
</div>
|
|
16413
|
+
}
|
|
16414
|
+
</div>
|
|
16415
|
+
|
|
16416
|
+
<div class="field-row">
|
|
16417
|
+
<label>
|
|
16418
|
+
Target Attribute Path
|
|
16419
|
+
@if (targetAttributesLoading()) { <span class="loading-pill">loading…</span> }
|
|
16420
|
+
</label>
|
|
16421
|
+
<div class="combo-row">
|
|
16422
|
+
<kendo-combobox
|
|
16423
|
+
[data]="targetAttributeList()"
|
|
16424
|
+
[value]="model().targetAttributePath"
|
|
16425
|
+
(valueChange)="onTargetAttributePathChange($event)"
|
|
16426
|
+
[textField]="'attributePath'"
|
|
16427
|
+
[valueField]="'attributePath'"
|
|
16428
|
+
[valuePrimitive]="true"
|
|
16429
|
+
[allowCustom]="true"
|
|
16430
|
+
[filterable]="true"
|
|
16431
|
+
(filterChange)="onTargetAttributeFilter($event)"
|
|
16432
|
+
[popupSettings]="{ appendTo: 'root', animate: true }"
|
|
16433
|
+
placeholder="e.g. Temperature, CO2Level">
|
|
16434
|
+
<ng-template kendoComboBoxItemTemplate let-item>
|
|
16435
|
+
<div class="attribute-option">
|
|
16436
|
+
<span class="attribute-path">{{ item.attributePath }}</span>
|
|
16437
|
+
<span class="attribute-type">{{ item.attributeValueType }}</span>
|
|
16438
|
+
</div>
|
|
16439
|
+
</ng-template>
|
|
16440
|
+
</kendo-combobox>
|
|
16441
|
+
<button kendoButton fillMode="flat" size="small"
|
|
16442
|
+
[svgIcon]="icons.browse"
|
|
16443
|
+
[disabled]="!effectiveTargetCkTypeId()"
|
|
16444
|
+
(click)="browseTargetAttribute()"
|
|
16445
|
+
title="Browse all attributes (incl. navigation properties)"></button>
|
|
16446
|
+
</div>
|
|
16447
|
+
<span class="hint">
|
|
16448
|
+
@if (effectiveTargetCkTypeId(); as t) {
|
|
16449
|
+
Direct attributes on <code>{{ t }}</code>. Click the browse button
|
|
16450
|
+
for navigation properties or to escape autocomplete.
|
|
16451
|
+
} @else {
|
|
16452
|
+
Pick a target entity above to see available attribute paths.
|
|
16453
|
+
}
|
|
16454
|
+
</span>
|
|
16455
|
+
</div>
|
|
16456
|
+
|
|
16457
|
+
</div>
|
|
16458
|
+
<div class="dialog-actions">
|
|
16459
|
+
<button kendoButton (click)="onCancel()">Cancel</button>
|
|
16460
|
+
<button kendoButton themeColor="primary" (click)="onSave()"
|
|
16461
|
+
[disabled]="!isValid()">Save</button>
|
|
16462
|
+
</div>
|
|
16463
|
+
</div>
|
|
16464
|
+
`, styles: [".mapping-edit{display:flex;flex-direction:column;height:100%;box-sizing:border-box}.mapping-edit-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:14px 18px;display:flex;flex-direction:column;gap:14px}.field-row{display:flex;flex-direction:column;gap:4px}.field-row label{font-size:.72rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.field-row .hint{font-size:.7rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));font-style:italic}.field-row.inline{flex-direction:row;align-items:center;gap:8px}.entity-display{display:flex;flex-direction:column;gap:2px;padding:6px 10px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:4px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 65%,transparent)}.entity-display.empty{flex-direction:row;align-items:center;justify-content:space-between}.entity-main{display:flex;align-items:center;gap:8px}.entity-main .entity-name{flex:1;font-weight:600;font-size:.9rem}.entity-meta{display:flex;align-items:center;gap:2px;font-size:.7rem;font-family:monospace;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.entity-meta .sep{opacity:.7}.dialog-actions{flex:0 0 auto;display:flex;gap:8px;justify-content:flex-end;padding:10px 18px;border-top:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 70%,transparent)}.combo-row{display:flex;align-items:stretch;gap:4px}.combo-row kendo-combobox{flex:1}.attribute-option{display:flex;justify-content:space-between;align-items:center;width:100%;gap:8px}.attribute-option .attribute-path{flex:1;font-family:monospace;font-size:.85rem}.attribute-option .attribute-type{font-size:.7rem;padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f0f0f0)) 80%,transparent);color:var(--theme-text-secondary, var(--kendo-color-subtle, #888))}.loading-pill{margin-left:6px;font-size:.65rem;padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--kendo-color-info, #0dcaf0) 18%,transparent);color:var(--kendo-color-info, #0dcaf0);text-transform:none;letter-spacing:0;font-style:italic}\n"] }]
|
|
16465
|
+
}] });
|
|
16466
|
+
/**
|
|
16467
|
+
* Client-side filter mirroring the substring search Kendo's combobox does
|
|
16468
|
+
* internally. Centralised here so the dropdown still shows a reasonable list
|
|
16469
|
+
* when the user types a path that hasn't been seen on this CK type yet.
|
|
16470
|
+
*/
|
|
16471
|
+
function filterAttributes(all, filter) {
|
|
16472
|
+
if (!filter || filter.trim().length === 0)
|
|
16473
|
+
return all;
|
|
16474
|
+
const needle = filter.toLowerCase();
|
|
16475
|
+
return all.filter(a => a.attributePath.toLowerCase().includes(needle));
|
|
16476
|
+
}
|
|
16477
|
+
/**
|
|
16478
|
+
* Produces the "{label} {path}" fragment used on either side of the generated
|
|
16479
|
+
* mapping name. Uses the entity name when present; otherwise a shortened rtId
|
|
16480
|
+
* suffix (last 6 hex chars) keeps the name compact while staying identifiable.
|
|
16481
|
+
* Returns an empty string when nothing useful is available.
|
|
16482
|
+
*/
|
|
16483
|
+
function describeEnd(entityName, rtId, attributePath) {
|
|
16484
|
+
const label = entityName?.trim() || (rtId ? `…${rtId.slice(-6)}` : '');
|
|
16485
|
+
const path = attributePath?.trim();
|
|
16486
|
+
if (label && path)
|
|
16487
|
+
return `${label} ${path}`;
|
|
16488
|
+
return label || path || '';
|
|
16489
|
+
}
|
|
16490
|
+
|
|
16491
|
+
const DIALOG_KEY = 'mapping-edit-dialog';
|
|
16492
|
+
const DEFAULT_SIZE = { width: 760, height: 760 };
|
|
16493
|
+
class MappingEditDialogService {
|
|
16494
|
+
windowService = inject(WindowService);
|
|
16495
|
+
windowStateService = inject(WindowStateService);
|
|
16496
|
+
/**
|
|
16497
|
+
* Opens the focused mapping editor as a resizable Kendo Window and returns
|
|
16498
|
+
* the user's choice. The window's size is persisted across sessions via
|
|
16499
|
+
* `WindowStateService` (same pattern as the AttributeSelectorDialog and the
|
|
16500
|
+
* EntitySelectDialog).
|
|
16501
|
+
*
|
|
16502
|
+
* The host is responsible for persisting changes when `confirmed=true` — the
|
|
16503
|
+
* dialog itself only mutates a local copy.
|
|
16504
|
+
*/
|
|
16505
|
+
async open(data) {
|
|
16506
|
+
const size = this.windowStateService.resolveWindowSize(DIALOG_KEY, DEFAULT_SIZE);
|
|
16507
|
+
const windowRef = this.windowService.open({
|
|
16508
|
+
title: data.title ?? 'Edit Mapping',
|
|
16509
|
+
content: MappingEditDialogComponent,
|
|
16510
|
+
width: size.width,
|
|
16511
|
+
height: size.height,
|
|
16512
|
+
minWidth: 540,
|
|
16513
|
+
minHeight: 480,
|
|
16514
|
+
resizable: true,
|
|
16515
|
+
});
|
|
16516
|
+
this.windowStateService.applyModalBehavior(DIALOG_KEY, windowRef);
|
|
16517
|
+
const contentRef = windowRef.content;
|
|
16518
|
+
if (contentRef?.instance) {
|
|
16519
|
+
contentRef.instance.initialise(data);
|
|
16520
|
+
}
|
|
16521
|
+
try {
|
|
16522
|
+
const result = await firstValueFrom(windowRef.result);
|
|
16523
|
+
if (result instanceof WindowCloseResult) {
|
|
16524
|
+
return { confirmed: false };
|
|
16525
|
+
}
|
|
16526
|
+
if (result && typeof result === 'object' && 'confirmed' in result) {
|
|
16527
|
+
return result;
|
|
16528
|
+
}
|
|
16529
|
+
return { confirmed: false };
|
|
16530
|
+
}
|
|
16531
|
+
catch {
|
|
16532
|
+
return { confirmed: false };
|
|
16533
|
+
}
|
|
16534
|
+
}
|
|
16535
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingEditDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
16536
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingEditDialogService, providedIn: 'root' });
|
|
16537
|
+
}
|
|
16538
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingEditDialogService, decorators: [{
|
|
16539
|
+
type: Injectable,
|
|
16540
|
+
args: [{ providedIn: 'root' }]
|
|
16541
|
+
}] });
|
|
16542
|
+
|
|
16543
|
+
/**
|
|
16544
|
+
* Default configuration: Basic/Tree + Basic/TreeNode driven by System/ParentChild,
|
|
16545
|
+
* mappings via System.Communication/MapsTo.
|
|
16546
|
+
*/
|
|
16547
|
+
const DEFAULT_MAPPING_COVERAGE_TREE_CONFIG = {
|
|
16548
|
+
rootCkTypeId: 'Basic/Tree',
|
|
16549
|
+
childCkTypeId: 'Basic/TreeNode',
|
|
16550
|
+
childRoleId: 'System/ParentChild',
|
|
16551
|
+
mappingRoleId: 'System.Communication/MapsTo',
|
|
16552
|
+
mappingSourceRoleId: 'System.Communication/MapsFrom',
|
|
16553
|
+
mappingCkTypeId: 'System.Communication/DataPointMapping',
|
|
16554
|
+
mappingTargetOutboundRoleName: 'mappedAsTarget',
|
|
16555
|
+
mappingSourceOutboundRoleName: 'mappedAsSource',
|
|
16556
|
+
validationPipelineCkTypeId: 'System.Communication/Pipeline',
|
|
16557
|
+
validationExecutionCkTypeId: 'System.Communication/PipelineExecution',
|
|
16558
|
+
validationExecutesRoleId: 'System.Communication/ExecutedPipeline',
|
|
16559
|
+
sourceCandidateCkTypeIds: [],
|
|
16560
|
+
};
|
|
16561
|
+
|
|
16562
|
+
/**
|
|
16563
|
+
* Master-detail component that visualises mapping coverage on a generic entity
|
|
16564
|
+
* hierarchy (defaults: Basic/Tree + Basic/TreeNode, mappings via
|
|
16565
|
+
* System.Communication/MapsTo). The user picks a root, browses the hierarchy on
|
|
16566
|
+
* the left and inspects / edits DataPointMappings on the right.
|
|
16567
|
+
*
|
|
16568
|
+
* Phase 1: tree + counts + read-only mapping list + CRUD (add new mapping,
|
|
16569
|
+
* relink source via {@link EntitySelectorDialogService}, delete).
|
|
16570
|
+
*/
|
|
16571
|
+
class MappingCoverageTreeComponent {
|
|
16572
|
+
entitySelector = inject(EntitySelectorDialogService);
|
|
16573
|
+
editDialog = inject(MappingEditDialogService);
|
|
16574
|
+
confirmation = inject(ConfirmationService);
|
|
16575
|
+
communicationService = inject(CommunicationService);
|
|
16576
|
+
getEntitiesByCkType = inject(GetEntitiesByCkTypeDtoGQL);
|
|
16577
|
+
getNodeMappingsGQL = inject(GetNodeMappingsDtoGQL);
|
|
16578
|
+
getRuntimeEntityByIdGQL = inject(GetRuntimeEntityByIdDtoGQL);
|
|
16579
|
+
getLatestValidationGQL = inject(GetLatestValidationExecutionDtoGQL);
|
|
16580
|
+
getOrphanCandidatesGQL = inject(GetOrphanCandidatesDtoGQL);
|
|
16581
|
+
createEntitiesGQL = inject(CreateEntitiesDtoGQL);
|
|
16582
|
+
deleteEntitiesGQL = inject(DeleteEntitiesDtoGQL);
|
|
16583
|
+
updateEntitiesGQL = inject(UpdateRuntimeEntitiesDtoGQL);
|
|
16584
|
+
dataSource = inject(MappingCoverageTreeDataSource);
|
|
16585
|
+
treeView;
|
|
16586
|
+
/** Optional override for non-default hierarchies / mapping roles. */
|
|
16587
|
+
config = DEFAULT_MAPPING_COVERAGE_TREE_CONFIG;
|
|
16588
|
+
/** Pre-select a root on first show (e.g. via route param). */
|
|
16589
|
+
initialRoot = null;
|
|
16590
|
+
/**
|
|
16591
|
+
* Current tenant ID. Required for the "Run Validation" pipeline trigger
|
|
16592
|
+
* which calls a tenant-scoped REST endpoint on the Communication Controller.
|
|
16593
|
+
* When not provided, the Run button is hidden and the user has to trigger
|
|
16594
|
+
* the pipeline externally.
|
|
16595
|
+
*/
|
|
16596
|
+
tenantId = null;
|
|
16597
|
+
entitySelected = new EventEmitter();
|
|
16598
|
+
icons = {
|
|
16599
|
+
refresh: arrowRotateCwIcon,
|
|
16600
|
+
folderOpen: folderOpenIcon,
|
|
16601
|
+
plus: plusIcon,
|
|
16602
|
+
pencil: pencilIcon,
|
|
16603
|
+
trash: trashIcon,
|
|
16604
|
+
link: linkIcon,
|
|
16605
|
+
};
|
|
16606
|
+
rootCandidates = signal([], ...(ngDevMode ? [{ debugName: "rootCandidates" }] : /* istanbul ignore next */ []));
|
|
16607
|
+
selectedRoot = signal(null, ...(ngDevMode ? [{ debugName: "selectedRoot" }] : /* istanbul ignore next */ []));
|
|
16608
|
+
selectedNode = signal(null, ...(ngDevMode ? [{ debugName: "selectedNode" }] : /* istanbul ignore next */ []));
|
|
16609
|
+
mappings = signal([], ...(ngDevMode ? [{ debugName: "mappings" }] : /* istanbul ignore next */ []));
|
|
16610
|
+
mappingsLoading = signal(false, ...(ngDevMode ? [{ debugName: "mappingsLoading" }] : /* istanbul ignore next */ []));
|
|
16611
|
+
mappingsError = signal(null, ...(ngDevMode ? [{ debugName: "mappingsError" }] : /* istanbul ignore next */ []));
|
|
16612
|
+
validationPipelines = signal([], ...(ngDevMode ? [{ debugName: "validationPipelines" }] : /* istanbul ignore next */ []));
|
|
16613
|
+
selectedPipeline = signal(null, ...(ngDevMode ? [{ debugName: "selectedPipeline" }] : /* istanbul ignore next */ []));
|
|
16614
|
+
validationSummary = signal(null, ...(ngDevMode ? [{ debugName: "validationSummary" }] : /* istanbul ignore next */ []));
|
|
16615
|
+
validationExecutedAt = signal(null, ...(ngDevMode ? [{ debugName: "validationExecutedAt" }] : /* istanbul ignore next */ []));
|
|
16616
|
+
validationLoading = signal(false, ...(ngDevMode ? [{ debugName: "validationLoading" }] : /* istanbul ignore next */ []));
|
|
16617
|
+
validationError = signal(null, ...(ngDevMode ? [{ debugName: "validationError" }] : /* istanbul ignore next */ []));
|
|
16618
|
+
validationRunning = signal(false, ...(ngDevMode ? [{ debugName: "validationRunning" }] : /* istanbul ignore next */ []));
|
|
16619
|
+
/** Active tab: 'coverage' shows the tree, 'orphans' shows the unmapped sources. */
|
|
16620
|
+
activeTab = signal('coverage', ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
|
|
16621
|
+
/** Source CK type currently inspected for orphans. */
|
|
16622
|
+
orphanCkType = signal(null, ...(ngDevMode ? [{ debugName: "orphanCkType" }] : /* istanbul ignore next */ []));
|
|
16623
|
+
orphanCandidates = signal([], ...(ngDevMode ? [{ debugName: "orphanCandidates" }] : /* istanbul ignore next */ []));
|
|
16624
|
+
orphanLoading = signal(false, ...(ngDevMode ? [{ debugName: "orphanLoading" }] : /* istanbul ignore next */ []));
|
|
16625
|
+
orphanError = signal(null, ...(ngDevMode ? [{ debugName: "orphanError" }] : /* istanbul ignore next */ []));
|
|
16626
|
+
orphanHideMapped = signal(true, ...(ngDevMode ? [{ debugName: "orphanHideMapped" }] : /* istanbul ignore next */ []));
|
|
16627
|
+
orphanFilteredList = computed(() => {
|
|
16628
|
+
const all = this.orphanCandidates();
|
|
16629
|
+
return this.orphanHideMapped() ? all.filter(c => c.mappingCount === 0) : all;
|
|
16630
|
+
}, ...(ngDevMode ? [{ debugName: "orphanFilteredList" }] : /* istanbul ignore next */ []));
|
|
16631
|
+
orphanStats = computed(() => {
|
|
16632
|
+
const all = this.orphanCandidates();
|
|
16633
|
+
const unmapped = all.filter(c => c.mappingCount === 0).length;
|
|
16634
|
+
return { total: all.length, unmapped, mapped: all.length - unmapped };
|
|
16635
|
+
}, ...(ngDevMode ? [{ debugName: "orphanStats" }] : /* istanbul ignore next */ []));
|
|
16636
|
+
/**
|
|
16637
|
+
* Which parent CK type to group by, or null for a flat list. We let the user
|
|
16638
|
+
* pick the type instead of just "immediate parent" because Loxone-style trees
|
|
16639
|
+
* include intermediate buckets (Loxone/Category) where each parent rtId is
|
|
16640
|
+
* unique per room — grouping by Category produces dozens of look-alike
|
|
16641
|
+
* sections ("Stellantrieb" appears N times, once per room). The user almost
|
|
16642
|
+
* always wants Loxone/Room or whichever level genuinely partitions the data,
|
|
16643
|
+
* so we expose all parent types seen in the loaded data and let them choose.
|
|
16644
|
+
*/
|
|
16645
|
+
orphanGroupParentType = signal(null, ...(ngDevMode ? [{ debugName: "orphanGroupParentType" }] : /* istanbul ignore next */ []));
|
|
16646
|
+
/**
|
|
16647
|
+
* Distinct parent CK type ids found in the loaded candidates, sorted so the
|
|
16648
|
+
* deepest type (root-most ancestor) comes first. For Loxone-Controls this is
|
|
16649
|
+
* Loxone/Room first, then Loxone/Category — usually the deeper one is also
|
|
16650
|
+
* the more meaningful grouping context.
|
|
16651
|
+
*/
|
|
16652
|
+
orphanAvailableParentTypes = computed(() => {
|
|
16653
|
+
const maxDepthByType = new Map();
|
|
16654
|
+
for (const item of this.orphanCandidates()) {
|
|
16655
|
+
item.parentPath.forEach((p, idx) => {
|
|
16656
|
+
const prev = maxDepthByType.get(p.ckTypeId) ?? -1;
|
|
16657
|
+
if (idx > prev)
|
|
16658
|
+
maxDepthByType.set(p.ckTypeId, idx);
|
|
16659
|
+
});
|
|
16660
|
+
}
|
|
16661
|
+
return Array.from(maxDepthByType.entries())
|
|
16662
|
+
.sort((a, b) => b[1] - a[1])
|
|
16663
|
+
.map(([ckTypeId]) => ckTypeId);
|
|
16664
|
+
}, ...(ngDevMode ? [{ debugName: "orphanAvailableParentTypes" }] : /* istanbul ignore next */ []));
|
|
16665
|
+
orphanGroupedList = computed(() => {
|
|
16666
|
+
const groupBy = this.orphanGroupParentType();
|
|
16667
|
+
if (!groupBy)
|
|
16668
|
+
return [];
|
|
16669
|
+
const groups = new Map();
|
|
16670
|
+
for (const item of this.orphanFilteredList()) {
|
|
16671
|
+
// First ancestor of the chosen type (closest to leaf wins). Falls back to
|
|
16672
|
+
// the catch-all bucket when no ancestor of that type is in the chain.
|
|
16673
|
+
const ancestor = item.parentPath.find(p => p.ckTypeId === groupBy);
|
|
16674
|
+
const key = ancestor ? `${ancestor.ckTypeId}@${ancestor.rtId}` : '__no_parent__';
|
|
16675
|
+
const label = ancestor?.name ?? '(no parent of this type)';
|
|
16676
|
+
let group = groups.get(key);
|
|
16677
|
+
if (!group) {
|
|
16678
|
+
group = { key, label, items: [] };
|
|
16679
|
+
groups.set(key, group);
|
|
16680
|
+
}
|
|
16681
|
+
group.items.push(item);
|
|
16682
|
+
}
|
|
16683
|
+
// Sort groups by their (locale-aware) label; "(no parent…)" lands last.
|
|
16684
|
+
return Array.from(groups.values()).sort((a, b) => {
|
|
16685
|
+
if (a.key === '__no_parent__')
|
|
16686
|
+
return 1;
|
|
16687
|
+
if (b.key === '__no_parent__')
|
|
16688
|
+
return -1;
|
|
16689
|
+
return a.label.localeCompare(b.label);
|
|
16690
|
+
});
|
|
16691
|
+
}, ...(ngDevMode ? [{ debugName: "orphanGroupedList" }] : /* istanbul ignore next */ []));
|
|
16692
|
+
summaryLine = computed(() => {
|
|
16693
|
+
const node = this.selectedNode();
|
|
16694
|
+
if (!node)
|
|
16695
|
+
return null;
|
|
16696
|
+
const items = this.mappings();
|
|
16697
|
+
const enabled = items.filter(m => m.enabled).length;
|
|
16698
|
+
return `${items.length} mapping(s), ${enabled} enabled`;
|
|
16699
|
+
}, ...(ngDevMode ? [{ debugName: "summaryLine" }] : /* istanbul ignore next */ []));
|
|
16700
|
+
async ngOnInit() {
|
|
16701
|
+
this.dataSource.setConfig(this.config);
|
|
16702
|
+
if (this.config.sourceCandidateCkTypeIds.length > 0) {
|
|
16703
|
+
this.orphanCkType.set(this.config.sourceCandidateCkTypeIds[0]);
|
|
16704
|
+
}
|
|
16705
|
+
await Promise.all([this.loadRootCandidates(), this.loadValidationPipelines()]);
|
|
16706
|
+
if (this.initialRoot) {
|
|
16707
|
+
const match = this.rootCandidates().find(r => r.rtId === this.initialRoot?.rtId);
|
|
16708
|
+
if (match) {
|
|
16709
|
+
await this.selectRoot(match);
|
|
16710
|
+
}
|
|
16711
|
+
}
|
|
16712
|
+
}
|
|
16713
|
+
onPipelineSelectChange(rtId) {
|
|
16714
|
+
const match = this.validationPipelines().find(p => p.rtId === rtId);
|
|
16715
|
+
this.selectedPipeline.set(match ?? null);
|
|
16716
|
+
}
|
|
16717
|
+
async refreshValidation() {
|
|
16718
|
+
const pipeline = this.selectedPipeline();
|
|
16719
|
+
if (!pipeline) {
|
|
16720
|
+
this.validationError.set('Pick a validation pipeline first.');
|
|
16721
|
+
return;
|
|
16722
|
+
}
|
|
16723
|
+
this.validationLoading.set(true);
|
|
16724
|
+
this.validationError.set(null);
|
|
16725
|
+
try {
|
|
16726
|
+
const data = await firstValueFrom(this.getLatestValidationGQL
|
|
16727
|
+
.fetch({
|
|
16728
|
+
variables: {
|
|
16729
|
+
pipelineRtId: pipeline.rtId,
|
|
16730
|
+
pipelineCkTypeId: pipeline.ckTypeId,
|
|
16731
|
+
executesRoleId: this.config.validationExecutesRoleId,
|
|
16732
|
+
executionCkTypeId: this.config.validationExecutionCkTypeId,
|
|
16733
|
+
},
|
|
16734
|
+
fetchPolicy: 'network-only',
|
|
16735
|
+
})
|
|
16736
|
+
.pipe(map(r => r.data?.runtime?.runtimeEntities?.items?.[0])));
|
|
16737
|
+
const execution = data?.associations?.executions?.items?.[0];
|
|
16738
|
+
const attrs = execution?.attributes?.items;
|
|
16739
|
+
// Case-insensitive lookup: the engine normalises attribute names to
|
|
16740
|
+
// camelCase in the response (`outputData`, `completedAt`) even though
|
|
16741
|
+
// the CK YAML declares them as PascalCase. The Strict reader was a
|
|
16742
|
+
// mistake — it matched nothing in practice and the user always saw
|
|
16743
|
+
// "no validation output yet" no matter how often they ran the pipeline.
|
|
16744
|
+
const outputData = readAttr(attrs, 'OutputData');
|
|
16745
|
+
const completedAt = readAttr(attrs, 'CompletedAt');
|
|
16746
|
+
if (!outputData) {
|
|
16747
|
+
this.dataSource.setValidationMap(new Map());
|
|
16748
|
+
this.validationSummary.set(null);
|
|
16749
|
+
this.validationExecutedAt.set(null);
|
|
16750
|
+
this.validationError.set('Pipeline has no validation output yet — run the pipeline first.');
|
|
16751
|
+
await this.refreshTreeOverlay();
|
|
16752
|
+
return;
|
|
16753
|
+
}
|
|
16754
|
+
const { map: detailMap, summary } = parseValidationReport(outputData);
|
|
16755
|
+
this.dataSource.setValidationMap(detailMap);
|
|
16756
|
+
this.validationSummary.set(summary);
|
|
16757
|
+
this.validationExecutedAt.set(completedAt);
|
|
16758
|
+
await this.refreshTreeOverlay();
|
|
16759
|
+
}
|
|
16760
|
+
catch (error) {
|
|
16761
|
+
console.error('Failed to load validation report:', error);
|
|
16762
|
+
this.validationError.set('Failed to load validation report.');
|
|
16763
|
+
}
|
|
16764
|
+
finally {
|
|
16765
|
+
this.validationLoading.set(false);
|
|
16766
|
+
}
|
|
16767
|
+
}
|
|
16768
|
+
clearValidation() {
|
|
16769
|
+
this.dataSource.setValidationMap(new Map());
|
|
16770
|
+
this.validationSummary.set(null);
|
|
16771
|
+
this.validationExecutedAt.set(null);
|
|
16772
|
+
this.validationError.set(null);
|
|
16773
|
+
void this.refreshTreeOverlay();
|
|
16774
|
+
}
|
|
16775
|
+
// ─── Orphan-Sources Tab ────────────────────────────────────────────────────
|
|
16776
|
+
selectTab(tab) {
|
|
16777
|
+
this.activeTab.set(tab);
|
|
16778
|
+
if (tab === 'orphans' && this.orphanCkType() && this.orphanCandidates().length === 0) {
|
|
16779
|
+
void this.loadOrphanCandidates();
|
|
16780
|
+
}
|
|
16781
|
+
}
|
|
16782
|
+
onOrphanCkTypeChange(ckTypeId) {
|
|
16783
|
+
this.orphanCkType.set(ckTypeId || null);
|
|
16784
|
+
this.orphanCandidates.set([]);
|
|
16785
|
+
if (ckTypeId)
|
|
16786
|
+
void this.loadOrphanCandidates();
|
|
16787
|
+
}
|
|
16788
|
+
async refreshOrphans() {
|
|
16789
|
+
await this.loadOrphanCandidates();
|
|
16790
|
+
}
|
|
16791
|
+
toggleOrphanHideMapped() {
|
|
16792
|
+
this.orphanHideMapped.update(v => !v);
|
|
16793
|
+
}
|
|
16794
|
+
onOrphanGroupParentTypeChange(value) {
|
|
16795
|
+
this.orphanGroupParentType.set(value ? value : null);
|
|
16796
|
+
}
|
|
16797
|
+
/**
|
|
16798
|
+
* Returns the parent chain ordered for breadcrumb display: root-most ancestor
|
|
16799
|
+
* first, immediate parent last. `parentPath` itself is stored immediate-first
|
|
16800
|
+
* (so `parentPath[0]` cheaply reports the grouping key), but humans read
|
|
16801
|
+
* breadcrumbs from outside in.
|
|
16802
|
+
*/
|
|
16803
|
+
breadcrumbFor(item) {
|
|
16804
|
+
return [...item.parentPath].reverse();
|
|
16805
|
+
}
|
|
16806
|
+
/**
|
|
16807
|
+
* Fetches all entities of the selected source CK type and tags each with
|
|
16808
|
+
* its inbound MapsFrom DataPointMapping count. The view filters the list
|
|
16809
|
+
* down to mappingCount === 0 by default, but the user can flip the toggle
|
|
16810
|
+
* to see all candidates (mapped + unmapped) for verification.
|
|
16811
|
+
*/
|
|
16812
|
+
async loadOrphanCandidates() {
|
|
16813
|
+
const ckTypeId = this.orphanCkType();
|
|
16814
|
+
if (!ckTypeId)
|
|
16815
|
+
return;
|
|
16816
|
+
this.orphanLoading.set(true);
|
|
16817
|
+
this.orphanError.set(null);
|
|
16818
|
+
try {
|
|
16819
|
+
const result = await firstValueFrom(this.getOrphanCandidatesGQL
|
|
16820
|
+
.fetch({
|
|
16821
|
+
variables: {
|
|
16822
|
+
ckTypeId,
|
|
16823
|
+
mapsFromRoleId: this.config.mappingSourceRoleId,
|
|
16824
|
+
mappingCkTypeId: this.config.mappingCkTypeId,
|
|
16825
|
+
childRoleId: this.config.childRoleId,
|
|
16826
|
+
childCkTypeId: this.config.childCkTypeId,
|
|
16827
|
+
first: 1000,
|
|
16828
|
+
after: GraphQL.offsetToCursor(0),
|
|
16829
|
+
},
|
|
16830
|
+
fetchPolicy: 'network-only',
|
|
16831
|
+
})
|
|
16832
|
+
.pipe(map(r => r.data?.runtime?.runtimeEntities?.items ?? [])));
|
|
16833
|
+
const candidates = (result ?? [])
|
|
16834
|
+
.filter((e) => !!e && !!e.rtId && !!e.ckTypeId)
|
|
16835
|
+
.map(e => ({
|
|
16836
|
+
rtId: e.rtId,
|
|
16837
|
+
ckTypeId: e.ckTypeId,
|
|
16838
|
+
name: readAttr(e.attributes?.items, 'name') ?? e.rtWellKnownName ?? e.rtId,
|
|
16839
|
+
description: readAttr(e.attributes?.items, 'description') ?? undefined,
|
|
16840
|
+
mappingCount: e.associations?.mappings?.totalCount ?? 0,
|
|
16841
|
+
parentPath: extractParentPath(e.associations?.parent?.items?.[0]),
|
|
16842
|
+
}));
|
|
16843
|
+
candidates.sort((a, b) => {
|
|
16844
|
+
// Show unmapped first, then alphabetical.
|
|
16845
|
+
if (a.mappingCount === 0 && b.mappingCount > 0)
|
|
16846
|
+
return -1;
|
|
16847
|
+
if (a.mappingCount > 0 && b.mappingCount === 0)
|
|
16848
|
+
return 1;
|
|
16849
|
+
return a.name.localeCompare(b.name);
|
|
16850
|
+
});
|
|
16851
|
+
this.orphanCandidates.set(candidates);
|
|
16852
|
+
}
|
|
16853
|
+
catch (error) {
|
|
16854
|
+
console.error('Failed to load orphan candidates:', error);
|
|
16855
|
+
this.orphanError.set('Failed to load source candidates.');
|
|
16856
|
+
this.orphanCandidates.set([]);
|
|
16857
|
+
}
|
|
16858
|
+
finally {
|
|
16859
|
+
this.orphanLoading.set(false);
|
|
16860
|
+
}
|
|
16861
|
+
}
|
|
16862
|
+
/**
|
|
16863
|
+
* Opens the mapping editor pre-populated with the orphan as source and lets
|
|
16864
|
+
* the user pick the target + attribute paths. Creates the DataPointMapping
|
|
16865
|
+
* entity atomically on save (both MapsFrom and MapsTo wired up in one
|
|
16866
|
+
* mutation). Cancel leaves nothing behind.
|
|
16867
|
+
*/
|
|
16868
|
+
async createMappingFromOrphan(orphan) {
|
|
16869
|
+
const skeleton = {
|
|
16870
|
+
// Empty rtId flags this as a not-yet-persisted mapping; saveEditedMapping
|
|
16871
|
+
// routes it through CreateEntities instead of UpdateRuntimeEntities.
|
|
16872
|
+
rtId: '',
|
|
16873
|
+
ckTypeId: this.config.mappingCkTypeId,
|
|
16874
|
+
name: `${orphan.name} mapping`,
|
|
16875
|
+
enabled: true,
|
|
16876
|
+
sourceRtId: orphan.rtId,
|
|
16877
|
+
sourceCkTypeId: orphan.ckTypeId,
|
|
16878
|
+
sourceName: orphan.name,
|
|
16879
|
+
sourceAttributePath: '',
|
|
16880
|
+
mappingExpression: '',
|
|
16881
|
+
targetAttributePath: '',
|
|
16882
|
+
};
|
|
16883
|
+
const result = await this.editDialog.open({ mapping: skeleton });
|
|
16884
|
+
if (!result.confirmed)
|
|
16885
|
+
return;
|
|
16886
|
+
try {
|
|
16887
|
+
await this.saveEditedMapping(result.mapping);
|
|
16888
|
+
await this.loadOrphanCandidates();
|
|
16889
|
+
}
|
|
16890
|
+
catch (error) {
|
|
16891
|
+
console.error('Failed to create mapping for orphan:', error);
|
|
16892
|
+
this.orphanError.set('Failed to create mapping.');
|
|
16893
|
+
}
|
|
16894
|
+
}
|
|
16895
|
+
trackOrphanByRtId(_index, item) {
|
|
16896
|
+
return item.rtId;
|
|
16897
|
+
}
|
|
16898
|
+
/**
|
|
16899
|
+
* Triggers the selected validation pipeline on the Communication Controller
|
|
16900
|
+
* and, when it completes, automatically refreshes the coverage report so the
|
|
16901
|
+
* tree colour-codes update. Requires {@link tenantId} to be set.
|
|
16902
|
+
*
|
|
16903
|
+
* Polling strategy: every 1.5 s, fetch the latest execution metadata for the
|
|
16904
|
+
* pipeline. When `dateTime` differs from the snapshot taken before the run,
|
|
16905
|
+
* we know a new execution finished — refresh and stop. Aborts after 60 s.
|
|
16906
|
+
*/
|
|
16907
|
+
async runValidation() {
|
|
16908
|
+
const pipeline = this.selectedPipeline();
|
|
16909
|
+
const tenant = this.tenantId;
|
|
16910
|
+
if (!pipeline || !tenant) {
|
|
16911
|
+
this.validationError.set(!pipeline ? 'Pick a validation pipeline first.' : 'Tenant context missing — Run is unavailable.');
|
|
16912
|
+
return;
|
|
16913
|
+
}
|
|
16914
|
+
this.validationRunning.set(true);
|
|
16915
|
+
this.validationError.set(null);
|
|
16916
|
+
// Snapshot the latest execution's id so we can detect the new one finishing.
|
|
16917
|
+
let previousExecutionId = null;
|
|
16918
|
+
try {
|
|
16919
|
+
const previous = await this.communicationService.getLatestPipelineExecution(tenant, pipeline.rtId, pipeline.ckTypeId);
|
|
16920
|
+
previousExecutionId = previous?.id ?? null;
|
|
16921
|
+
}
|
|
16922
|
+
catch {
|
|
16923
|
+
// Non-fatal: we'll still detect completion by polling and falling back
|
|
16924
|
+
// to "any latest execution" being non-Running.
|
|
16925
|
+
}
|
|
16926
|
+
try {
|
|
16927
|
+
await this.communicationService.executePipeline(tenant, pipeline.rtId);
|
|
16928
|
+
}
|
|
16929
|
+
catch (error) {
|
|
16930
|
+
console.error('Failed to start validation pipeline:', error);
|
|
16931
|
+
this.validationError.set('Failed to start validation pipeline.');
|
|
16932
|
+
this.validationRunning.set(false);
|
|
16933
|
+
return;
|
|
16934
|
+
}
|
|
16935
|
+
// Poll for completion. We accept that the new execution id may equal
|
|
16936
|
+
// previousExecutionId for a brief moment until the controller flushes it
|
|
16937
|
+
// — that's why we also accept any latest execution whose status leaves
|
|
16938
|
+
// Running (Completed / Failed / Interrupted / Cancelled).
|
|
16939
|
+
const startedAt = Date.now();
|
|
16940
|
+
const timeoutMs = 60_000;
|
|
16941
|
+
const pollIntervalMs = 1500;
|
|
16942
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
16943
|
+
await sleep(pollIntervalMs);
|
|
16944
|
+
try {
|
|
16945
|
+
const latest = await this.communicationService.getLatestPipelineExecution(tenant, pipeline.rtId, pipeline.ckTypeId);
|
|
16946
|
+
if (!latest)
|
|
16947
|
+
continue;
|
|
16948
|
+
const idChanged = latest.id !== previousExecutionId;
|
|
16949
|
+
// Treat absent status as "done". The in-memory debug-cache branch of
|
|
16950
|
+
// GET /pipelineDebug/.../latest doesn't populate Status (only the
|
|
16951
|
+
// MongoDB fallback does), so a sub-second pipeline that hits the
|
|
16952
|
+
// cache path before the DB row is visible would never satisfy a
|
|
16953
|
+
// strict `status === 'Completed'` check and the button would freeze.
|
|
16954
|
+
// We only keep polling while the controller *explicitly* reports
|
|
16955
|
+
// "Running" (case-insensitive for safety).
|
|
16956
|
+
const stillRunning = typeof latest.status === 'string' && latest.status.toLowerCase() === 'running';
|
|
16957
|
+
if (idChanged && !stillRunning) {
|
|
16958
|
+
this.validationRunning.set(false);
|
|
16959
|
+
await this.refreshValidation();
|
|
16960
|
+
return;
|
|
16961
|
+
}
|
|
16962
|
+
}
|
|
16963
|
+
catch (error) {
|
|
16964
|
+
console.warn('Polling validation execution failed:', error);
|
|
16965
|
+
}
|
|
16966
|
+
}
|
|
16967
|
+
// Timeout: leave running flag off so the button is usable again; user can
|
|
16968
|
+
// hit Load Report manually if the pipeline is just slow.
|
|
16969
|
+
this.validationRunning.set(false);
|
|
16970
|
+
this.validationError.set('Validation is still running — use Load Report to refresh later.');
|
|
16971
|
+
}
|
|
16972
|
+
async refreshTreeOverlay() {
|
|
16973
|
+
if (this.treeView?.isReady) {
|
|
16974
|
+
const expanded = this.treeView.getExpandedKeys();
|
|
16975
|
+
await this.treeView.refreshTree();
|
|
16976
|
+
this.treeView.setExpandedKeys(expanded);
|
|
16977
|
+
}
|
|
16978
|
+
// Re-evaluate the currently selected node's payload so the detail panel
|
|
16979
|
+
// shows the new validation status.
|
|
16980
|
+
const current = this.selectedNode();
|
|
16981
|
+
if (current) {
|
|
16982
|
+
const refreshed = await this.dataSource.refreshNode(current.rtId, current.ckTypeId);
|
|
16983
|
+
if (refreshed)
|
|
16984
|
+
this.selectedNode.set(refreshed);
|
|
16985
|
+
}
|
|
16986
|
+
}
|
|
16987
|
+
async loadValidationPipelines() {
|
|
16988
|
+
try {
|
|
16989
|
+
const result = await firstValueFrom(this.getEntitiesByCkType
|
|
16990
|
+
.fetch({
|
|
16991
|
+
variables: {
|
|
16992
|
+
ckTypeId: this.config.validationPipelineCkTypeId,
|
|
16993
|
+
first: 200,
|
|
16994
|
+
after: GraphQL.offsetToCursor(0),
|
|
16995
|
+
},
|
|
16996
|
+
fetchPolicy: 'network-only',
|
|
16997
|
+
})
|
|
16998
|
+
.pipe(map(r => r.data?.runtime?.runtimeEntities?.items ?? [])));
|
|
16999
|
+
const candidates = (result ?? [])
|
|
17000
|
+
.filter((e) => !!e && !!e.rtId && !!e.ckTypeId)
|
|
17001
|
+
.map(e => ({
|
|
17002
|
+
rtId: e.rtId,
|
|
17003
|
+
ckTypeId: e.ckTypeId,
|
|
17004
|
+
name: readAttr(e.attributes?.items, 'name') ?? e.rtWellKnownName ?? e.rtId,
|
|
17005
|
+
}));
|
|
17006
|
+
candidates.sort((a, b) => a.name.localeCompare(b.name));
|
|
17007
|
+
this.validationPipelines.set(candidates);
|
|
17008
|
+
}
|
|
17009
|
+
catch (error) {
|
|
17010
|
+
console.error('Failed to load validation pipelines:', error);
|
|
17011
|
+
this.validationPipelines.set([]);
|
|
17012
|
+
}
|
|
17013
|
+
}
|
|
17014
|
+
async refreshRoots() {
|
|
17015
|
+
await this.loadRootCandidates();
|
|
17016
|
+
}
|
|
17017
|
+
async pickRoot() {
|
|
17018
|
+
const result = await this.entitySelector.openEntitySelector({
|
|
17019
|
+
title: `Select ${this.config.rootCkTypeId}`,
|
|
17020
|
+
});
|
|
17021
|
+
if (!result.confirmed || !result.entity)
|
|
17022
|
+
return;
|
|
17023
|
+
const candidate = {
|
|
17024
|
+
rtId: result.entity.rtId,
|
|
17025
|
+
ckTypeId: result.entity.ckTypeId,
|
|
17026
|
+
name: result.entity.name ?? result.entity.rtId,
|
|
17027
|
+
description: '',
|
|
17028
|
+
};
|
|
17029
|
+
const known = this.rootCandidates().find(r => r.rtId === candidate.rtId);
|
|
17030
|
+
if (!known) {
|
|
17031
|
+
this.rootCandidates.update(list => [...list, candidate]);
|
|
17032
|
+
}
|
|
17033
|
+
await this.selectRoot(candidate);
|
|
17034
|
+
}
|
|
17035
|
+
async selectRoot(root) {
|
|
17036
|
+
this.selectedRoot.set(root);
|
|
17037
|
+
this.selectedNode.set(null);
|
|
17038
|
+
this.mappings.set([]);
|
|
17039
|
+
this.dataSource.setRoot({
|
|
17040
|
+
rtId: root.rtId,
|
|
17041
|
+
ckTypeId: root.ckTypeId,
|
|
17042
|
+
name: root.name,
|
|
17043
|
+
description: root.description,
|
|
17044
|
+
});
|
|
17045
|
+
if (this.treeView?.isReady) {
|
|
17046
|
+
await this.treeView.refreshTree();
|
|
17047
|
+
}
|
|
17048
|
+
}
|
|
17049
|
+
onRootSelectChange(rtId) {
|
|
17050
|
+
const match = this.rootCandidates().find(r => r.rtId === rtId);
|
|
17051
|
+
if (match) {
|
|
17052
|
+
void this.selectRoot(match);
|
|
17053
|
+
}
|
|
17054
|
+
}
|
|
17055
|
+
async onNodeSelected(item) {
|
|
17056
|
+
const payload = item.item;
|
|
17057
|
+
this.selectedNode.set(payload);
|
|
17058
|
+
this.entitySelected.emit({
|
|
17059
|
+
rtId: payload.rtId,
|
|
17060
|
+
ckTypeId: payload.ckTypeId,
|
|
17061
|
+
name: payload.name,
|
|
17062
|
+
description: payload.description,
|
|
17063
|
+
});
|
|
17064
|
+
await this.loadMappingsForSelected();
|
|
17065
|
+
}
|
|
17066
|
+
async refreshSelected() {
|
|
17067
|
+
const node = this.selectedNode();
|
|
17068
|
+
if (!node)
|
|
17069
|
+
return;
|
|
17070
|
+
const updated = await this.dataSource.refreshNode(node.rtId, node.ckTypeId);
|
|
17071
|
+
if (updated) {
|
|
17072
|
+
this.selectedNode.set(updated);
|
|
17073
|
+
}
|
|
17074
|
+
await this.loadMappingsForSelected();
|
|
17075
|
+
}
|
|
17076
|
+
async addMapping() {
|
|
17077
|
+
const node = this.selectedNode();
|
|
17078
|
+
if (!node)
|
|
17079
|
+
return;
|
|
17080
|
+
try {
|
|
17081
|
+
await firstValueFrom(this.createEntitiesGQL.mutate({
|
|
17082
|
+
variables: {
|
|
17083
|
+
entities: [
|
|
17084
|
+
{
|
|
17085
|
+
ckTypeId: this.config.mappingCkTypeId,
|
|
17086
|
+
attributes: [
|
|
17087
|
+
{ attributeName: 'Name', value: `Mapping ${this.mappings().length + 1}` },
|
|
17088
|
+
{ attributeName: 'Enabled', value: true },
|
|
17089
|
+
],
|
|
17090
|
+
associations: [
|
|
17091
|
+
{
|
|
17092
|
+
roleName: this.config.mappingTargetOutboundRoleName,
|
|
17093
|
+
targets: [
|
|
17094
|
+
{
|
|
17095
|
+
modOption: AssociationModOptionsDto.CreateDto,
|
|
17096
|
+
target: { rtId: node.rtId, ckTypeId: node.ckTypeId },
|
|
17097
|
+
},
|
|
17098
|
+
],
|
|
17099
|
+
},
|
|
17100
|
+
],
|
|
17101
|
+
},
|
|
17102
|
+
],
|
|
17103
|
+
},
|
|
17104
|
+
}));
|
|
17105
|
+
await this.refreshSelected();
|
|
17106
|
+
}
|
|
17107
|
+
catch (error) {
|
|
17108
|
+
console.error('Failed to create mapping:', error);
|
|
17109
|
+
this.mappingsError.set('Failed to create mapping.');
|
|
17110
|
+
}
|
|
17111
|
+
}
|
|
17112
|
+
/**
|
|
17113
|
+
* Opens the focused edit dialog for one mapping and, on save, persists
|
|
17114
|
+
* attribute and (if changed) MapsFrom- / MapsTo-association updates in a
|
|
17115
|
+
* single UpdateRuntimeEntities mutation.
|
|
17116
|
+
*/
|
|
17117
|
+
async editMapping(mapping) {
|
|
17118
|
+
// In the coverage-tree context the mapping's MapsTo target IS the selected
|
|
17119
|
+
// node — we pre-fill the dialog with that so the user sees it immediately
|
|
17120
|
+
// and can retarget if needed via the new Target Entity picker.
|
|
17121
|
+
const node = this.selectedNode();
|
|
17122
|
+
const initial = {
|
|
17123
|
+
rtId: mapping.rtId,
|
|
17124
|
+
ckTypeId: mapping.ckTypeId,
|
|
17125
|
+
name: mapping.name,
|
|
17126
|
+
enabled: mapping.enabled,
|
|
17127
|
+
sourceRtId: mapping.sourceRtId,
|
|
17128
|
+
sourceCkTypeId: mapping.sourceCkTypeId,
|
|
17129
|
+
sourceName: mapping.sourceName,
|
|
17130
|
+
sourceAttributePath: mapping.sourceAttributePath,
|
|
17131
|
+
mappingExpression: mapping.mappingExpression,
|
|
17132
|
+
targetRtId: node?.rtId,
|
|
17133
|
+
targetCkTypeId: node?.ckTypeId,
|
|
17134
|
+
targetName: node?.name,
|
|
17135
|
+
targetAttributePath: mapping.targetAttributePath,
|
|
17136
|
+
};
|
|
17137
|
+
const result = await this.editDialog.open({ mapping: initial });
|
|
17138
|
+
if (!result.confirmed)
|
|
17139
|
+
return;
|
|
17140
|
+
await this.saveEditedMapping(result.mapping);
|
|
17141
|
+
}
|
|
17142
|
+
async saveEditedMapping(edited) {
|
|
17143
|
+
const attributeUpdates = [
|
|
17144
|
+
{ attributeName: 'Name', value: edited.name ?? '' },
|
|
17145
|
+
{ attributeName: 'Enabled', value: edited.enabled },
|
|
17146
|
+
{ attributeName: 'SourceAttributePath', value: edited.sourceAttributePath ?? '' },
|
|
17147
|
+
{ attributeName: 'MappingExpression', value: edited.mappingExpression ?? '' },
|
|
17148
|
+
{ attributeName: 'TargetAttributePath', value: edited.targetAttributePath ?? '' },
|
|
17149
|
+
];
|
|
17150
|
+
const associations = [];
|
|
17151
|
+
const buildAssocChange = (originalRtId, originalCkTypeId, newRtId, newCkTypeId, roleName) => {
|
|
17152
|
+
if (newRtId === originalRtId && newCkTypeId === originalCkTypeId)
|
|
17153
|
+
return;
|
|
17154
|
+
const targets = [];
|
|
17155
|
+
if (originalRtId && originalCkTypeId) {
|
|
17156
|
+
targets.push({
|
|
17157
|
+
modOption: AssociationModOptionsDto.DeleteDto,
|
|
17158
|
+
target: { rtId: originalRtId, ckTypeId: originalCkTypeId },
|
|
17159
|
+
});
|
|
17160
|
+
}
|
|
17161
|
+
if (newRtId && newCkTypeId) {
|
|
17162
|
+
targets.push({
|
|
17163
|
+
modOption: AssociationModOptionsDto.CreateDto,
|
|
17164
|
+
target: { rtId: newRtId, ckTypeId: newCkTypeId },
|
|
17165
|
+
});
|
|
17166
|
+
}
|
|
17167
|
+
if (targets.length > 0) {
|
|
17168
|
+
associations.push({ roleName, targets });
|
|
17169
|
+
}
|
|
17170
|
+
};
|
|
17171
|
+
buildAssocChange(edited._originalSourceRtId, edited._originalSourceCkTypeId, edited.sourceRtId, edited.sourceCkTypeId, this.config.mappingSourceOutboundRoleName);
|
|
17172
|
+
buildAssocChange(edited._originalTargetRtId, edited._originalTargetCkTypeId, edited.targetRtId, edited.targetCkTypeId, this.config.mappingTargetOutboundRoleName);
|
|
17173
|
+
try {
|
|
17174
|
+
if (!edited.rtId) {
|
|
17175
|
+
// New mapping (orphan-flow): atomic create with both MapsFrom and
|
|
17176
|
+
// MapsTo associations. We translate the assoc-change list (built for
|
|
17177
|
+
// the update path's modOptions) into a create-only assoc list — the
|
|
17178
|
+
// entity doesn't exist yet, so any deletes are no-ops.
|
|
17179
|
+
const createAssociations = associations
|
|
17180
|
+
.map(a => ({
|
|
17181
|
+
roleName: a.roleName,
|
|
17182
|
+
targets: a.targets
|
|
17183
|
+
.filter(t => t.modOption !== AssociationModOptionsDto.DeleteDto)
|
|
17184
|
+
.map(t => ({ modOption: AssociationModOptionsDto.CreateDto, target: t.target })),
|
|
17185
|
+
}))
|
|
17186
|
+
.filter(a => a.targets.length > 0);
|
|
17187
|
+
await firstValueFrom(this.createEntitiesGQL.mutate({
|
|
17188
|
+
variables: {
|
|
17189
|
+
entities: [
|
|
17190
|
+
{
|
|
17191
|
+
ckTypeId: edited.ckTypeId,
|
|
17192
|
+
attributes: attributeUpdates,
|
|
17193
|
+
associations: createAssociations,
|
|
17194
|
+
},
|
|
17195
|
+
],
|
|
17196
|
+
},
|
|
17197
|
+
}));
|
|
17198
|
+
}
|
|
17199
|
+
else {
|
|
17200
|
+
await firstValueFrom(this.updateEntitiesGQL.mutate({
|
|
17201
|
+
variables: {
|
|
17202
|
+
entities: [
|
|
17203
|
+
{
|
|
17204
|
+
rtId: edited.rtId,
|
|
17205
|
+
item: {
|
|
17206
|
+
ckTypeId: edited.ckTypeId,
|
|
17207
|
+
attributes: attributeUpdates,
|
|
17208
|
+
associations,
|
|
17209
|
+
},
|
|
17210
|
+
},
|
|
17211
|
+
],
|
|
17212
|
+
},
|
|
17213
|
+
}));
|
|
17214
|
+
}
|
|
17215
|
+
await this.refreshSelected();
|
|
17216
|
+
}
|
|
17217
|
+
catch (error) {
|
|
17218
|
+
console.error('Failed to save mapping:', error);
|
|
17219
|
+
this.mappingsError.set('Failed to save mapping changes.');
|
|
17220
|
+
}
|
|
17221
|
+
}
|
|
17222
|
+
async deleteMapping(mapping) {
|
|
17223
|
+
const confirmed = await this.confirmation.showYesNoConfirmationDialog('Delete Mapping', `Delete mapping '${mapping.name || mapping.rtId}'? This cannot be undone.`);
|
|
17224
|
+
if (!confirmed)
|
|
17225
|
+
return;
|
|
17226
|
+
try {
|
|
17227
|
+
await firstValueFrom(this.deleteEntitiesGQL.mutate({
|
|
17228
|
+
variables: {
|
|
17229
|
+
rtEntityIds: [{ rtId: mapping.rtId, ckTypeId: mapping.ckTypeId }],
|
|
17230
|
+
deleteStrategy: DeleteStrategiesDto.EraseDto,
|
|
17231
|
+
},
|
|
17232
|
+
}));
|
|
17233
|
+
await this.refreshSelected();
|
|
17234
|
+
}
|
|
17235
|
+
catch (error) {
|
|
17236
|
+
console.error('Failed to delete mapping:', error);
|
|
17237
|
+
this.mappingsError.set('Failed to delete mapping.');
|
|
17238
|
+
}
|
|
17239
|
+
}
|
|
17240
|
+
trackByRtId(_index, item) {
|
|
17241
|
+
return item.rtId;
|
|
17242
|
+
}
|
|
17243
|
+
async loadRootCandidates() {
|
|
17244
|
+
try {
|
|
17245
|
+
const result = await firstValueFrom(this.getEntitiesByCkType
|
|
17246
|
+
.fetch({
|
|
17247
|
+
variables: {
|
|
17248
|
+
ckTypeId: this.config.rootCkTypeId,
|
|
17249
|
+
first: 200,
|
|
17250
|
+
after: GraphQL.offsetToCursor(0),
|
|
17251
|
+
},
|
|
17252
|
+
fetchPolicy: 'network-only',
|
|
17253
|
+
})
|
|
17254
|
+
.pipe(map(r => r.data?.runtime?.runtimeEntities?.items ?? [])));
|
|
17255
|
+
const candidates = (result ?? [])
|
|
17256
|
+
.filter((e) => !!e && !!e.rtId && !!e.ckTypeId)
|
|
17257
|
+
.map(e => {
|
|
17258
|
+
const name = readAttr(e.attributes?.items, 'name') ?? e.rtWellKnownName ?? e.rtId;
|
|
17259
|
+
const description = readAttr(e.attributes?.items, 'description') ?? '';
|
|
17260
|
+
return {
|
|
17261
|
+
rtId: e.rtId,
|
|
17262
|
+
ckTypeId: e.ckTypeId,
|
|
17263
|
+
name,
|
|
17264
|
+
description,
|
|
17265
|
+
};
|
|
17266
|
+
});
|
|
17267
|
+
candidates.sort((a, b) => a.name.localeCompare(b.name));
|
|
17268
|
+
this.rootCandidates.set(candidates);
|
|
17269
|
+
}
|
|
17270
|
+
catch (error) {
|
|
17271
|
+
console.error('Failed to load root candidates:', error);
|
|
17272
|
+
this.rootCandidates.set([]);
|
|
17273
|
+
}
|
|
17274
|
+
}
|
|
17275
|
+
async loadMappingsForSelected() {
|
|
17276
|
+
const node = this.selectedNode();
|
|
17277
|
+
if (!node) {
|
|
17278
|
+
this.mappings.set([]);
|
|
17279
|
+
return;
|
|
17280
|
+
}
|
|
17281
|
+
this.mappingsLoading.set(true);
|
|
17282
|
+
this.mappingsError.set(null);
|
|
17283
|
+
try {
|
|
17284
|
+
const data = await firstValueFrom(this.getNodeMappingsGQL
|
|
17285
|
+
.fetch({
|
|
17286
|
+
variables: {
|
|
17287
|
+
rtId: node.rtId,
|
|
17288
|
+
ckTypeId: node.ckTypeId,
|
|
17289
|
+
mapsToRoleId: this.config.mappingRoleId,
|
|
17290
|
+
mapsFromRoleId: this.config.mappingSourceRoleId,
|
|
17291
|
+
mappingCkTypeId: this.config.mappingCkTypeId,
|
|
17292
|
+
},
|
|
17293
|
+
fetchPolicy: 'network-only',
|
|
17294
|
+
})
|
|
17295
|
+
.pipe(map(r => r.data?.runtime?.runtimeEntities?.items?.[0])));
|
|
17296
|
+
const items = data?.associations?.mappings?.items ?? [];
|
|
17297
|
+
// First pass: map mapping attributes + source rtId/ckTypeId from association
|
|
17298
|
+
// definitions. Source NAMES are resolved in a second pass below because the
|
|
17299
|
+
// generic `targets(ckId: ...)` projection requires a concrete CK type with
|
|
17300
|
+
// its own MongoDB collection — `System/Entity` is abstract and would error.
|
|
17301
|
+
const mapped = items
|
|
17302
|
+
.filter((m) => !!m && !!m.rtId && !!m.ckTypeId)
|
|
17303
|
+
.map(m => {
|
|
17304
|
+
const attrs = m.attributes?.items ?? [];
|
|
17305
|
+
const sourceDef = m.associations?.sources?.items?.[0] ?? null;
|
|
17306
|
+
return {
|
|
17307
|
+
rtId: m.rtId,
|
|
17308
|
+
ckTypeId: m.ckTypeId,
|
|
17309
|
+
name: readAttr(attrs, 'name') ?? '',
|
|
17310
|
+
enabled: readAttr(attrs, 'enabled') !== 'false',
|
|
17311
|
+
sourceAttributePath: readAttr(attrs, 'sourceAttributePath') ?? '',
|
|
17312
|
+
targetAttributePath: readAttr(attrs, 'targetAttributePath') ?? '',
|
|
17313
|
+
mappingExpression: readAttr(attrs, 'mappingExpression') ?? '',
|
|
17314
|
+
sourceRtId: sourceDef?.targetRtId ? String(sourceDef.targetRtId) : undefined,
|
|
17315
|
+
sourceCkTypeId: sourceDef?.targetCkTypeId ? String(sourceDef.targetCkTypeId) : undefined,
|
|
17316
|
+
sourceName: undefined,
|
|
17317
|
+
};
|
|
17318
|
+
});
|
|
17319
|
+
this.mappings.set(mapped);
|
|
17320
|
+
// Fire-and-forget the source-name resolution in parallel; the list renders
|
|
17321
|
+
// immediately and names patch in as they arrive.
|
|
17322
|
+
void this.resolveSourceNames(mapped);
|
|
17323
|
+
}
|
|
17324
|
+
catch (error) {
|
|
17325
|
+
console.error('Failed to load mappings:', error);
|
|
17326
|
+
this.mappingsError.set('Failed to load mappings.');
|
|
17327
|
+
this.mappings.set([]);
|
|
17328
|
+
}
|
|
17329
|
+
finally {
|
|
17330
|
+
this.mappingsLoading.set(false);
|
|
17331
|
+
}
|
|
17332
|
+
}
|
|
17333
|
+
/**
|
|
17334
|
+
* Resolves source entity names by issuing a parallel `getRuntimeEntityById`
|
|
17335
|
+
* per unique (rtId, ckTypeId) pair. Updates the mappings signal in-place so
|
|
17336
|
+
* the detail panel re-renders with names. Failures are silent — the row
|
|
17337
|
+
* still falls back to displaying the source rtId.
|
|
17338
|
+
*/
|
|
17339
|
+
async resolveSourceNames(mappings) {
|
|
17340
|
+
const byKey = new Map();
|
|
17341
|
+
for (const m of mappings) {
|
|
17342
|
+
if (m.sourceRtId && m.sourceCkTypeId) {
|
|
17343
|
+
byKey.set(`${m.sourceCkTypeId}@${m.sourceRtId}`, {
|
|
17344
|
+
rtId: m.sourceRtId,
|
|
17345
|
+
ckTypeId: m.sourceCkTypeId,
|
|
17346
|
+
});
|
|
17347
|
+
}
|
|
17348
|
+
}
|
|
17349
|
+
if (byKey.size === 0)
|
|
17350
|
+
return;
|
|
17351
|
+
const lookups = await Promise.all(Array.from(byKey.values()).map(async (ref) => {
|
|
17352
|
+
try {
|
|
17353
|
+
const data = await firstValueFrom(this.getRuntimeEntityByIdGQL
|
|
17354
|
+
.fetch({
|
|
17355
|
+
variables: { rtId: ref.rtId, ckTypeId: ref.ckTypeId },
|
|
17356
|
+
fetchPolicy: 'cache-first',
|
|
17357
|
+
})
|
|
17358
|
+
.pipe(map(r => r.data?.runtime?.runtimeEntities?.items?.[0])));
|
|
17359
|
+
const name = readAttr(data?.attributes?.items, 'name') ?? data?.rtWellKnownName ?? null;
|
|
17360
|
+
return { key: `${ref.ckTypeId}@${ref.rtId}`, name };
|
|
17361
|
+
}
|
|
17362
|
+
catch {
|
|
17363
|
+
return { key: `${ref.ckTypeId}@${ref.rtId}`, name: null };
|
|
17364
|
+
}
|
|
17365
|
+
}));
|
|
17366
|
+
const nameByKey = new Map(lookups.map(l => [l.key, l.name]));
|
|
17367
|
+
const node = this.selectedNode();
|
|
17368
|
+
if (!node)
|
|
17369
|
+
return; // user changed selection meanwhile
|
|
17370
|
+
const next = this.mappings().map(m => {
|
|
17371
|
+
if (!m.sourceRtId || !m.sourceCkTypeId)
|
|
17372
|
+
return m;
|
|
17373
|
+
const key = `${m.sourceCkTypeId}@${m.sourceRtId}`;
|
|
17374
|
+
const name = nameByKey.get(key);
|
|
17375
|
+
return name ? { ...m, sourceName: name } : m;
|
|
17376
|
+
});
|
|
17377
|
+
this.mappings.set(next);
|
|
17378
|
+
}
|
|
17379
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingCoverageTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17380
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MappingCoverageTreeComponent, isStandalone: true, selector: "mm-mapping-coverage-tree", inputs: { config: "config", initialRoot: "initialRoot", tenantId: "tenantId" }, outputs: { entitySelected: "entitySelected" }, providers: [MappingCoverageTreeDataSource], viewQueries: [{ propertyName: "treeView", first: true, predicate: TreeComponent, descendants: true }], ngImport: i0, template: "<div class=\"coverage-root\">\n @if (config.sourceCandidateCkTypeIds.length > 0) {\n <div class=\"coverage-tabs\" role=\"tablist\">\n <button class=\"coverage-tab\"\n [class.active]=\"activeTab() === 'coverage'\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === 'coverage'\"\n (click)=\"selectTab('coverage')\">Coverage Tree</button>\n <button class=\"coverage-tab\"\n [class.active]=\"activeTab() === 'orphans'\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === 'orphans'\"\n (click)=\"selectTab('orphans')\">\n Orphan Sources\n @if (orphanStats(); as s) {\n @if (s.unmapped > 0) { <span class=\"tab-badge\">{{ s.unmapped }}</span> }\n }\n </button>\n </div>\n }\n\n @if (activeTab() === 'coverage') {\n <div class=\"coverage-toolbar\">\n <label class=\"toolbar-label\">Tree:</label>\n <select class=\"root-select\"\n [value]=\"selectedRoot()?.rtId ?? ''\"\n (change)=\"onRootSelectChange($any($event.target).value)\">\n <option value=\"\" disabled>\u2014 Pick a {{ config.rootCkTypeId }} \u2014</option>\n @for (root of rootCandidates(); track root.rtId) {\n <option [value]=\"root.rtId\" [title]=\"root.description\">{{ root.name }}</option>\n }\n </select>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.refresh\"\n (click)=\"refreshRoots()\" title=\"Refresh tree list\"></button>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.folderOpen\"\n (click)=\"pickRoot()\" title=\"Browse for entity\">Browse...</button>\n\n @if (selectedRoot(); as root) {\n <div class=\"toolbar-spacer\"></div>\n <span class=\"toolbar-meta\">\n <span class=\"meta-name\">{{ root.name }}</span>\n <span class=\"meta-type\">{{ root.ckTypeId }}</span>\n </span>\n }\n </div>\n\n <div class=\"coverage-toolbar coverage-toolbar-validation\">\n <label class=\"toolbar-label\">Validation:</label>\n <select class=\"root-select\"\n [value]=\"selectedPipeline()?.rtId ?? ''\"\n (change)=\"onPipelineSelectChange($any($event.target).value)\">\n <option value=\"\" disabled>\u2014 Pick a {{ config.validationPipelineCkTypeId }} \u2014</option>\n @for (p of validationPipelines(); track p.rtId) {\n <option [value]=\"p.rtId\">{{ p.name }}</option>\n }\n </select>\n @if (tenantId) {\n <button kendoButton themeColor=\"primary\" size=\"small\"\n [disabled]=\"!selectedPipeline() || validationRunning() || validationLoading()\"\n (click)=\"runValidation()\"\n [title]=\"validationRunning() ? 'Validation running\u2026' : 'Run pipeline + load report'\">\n @if (validationRunning()) { Running\u2026 } @else { Run }\n </button>\n }\n <button kendoButton fillMode=\"solid\" size=\"small\" [svgIcon]=\"icons.refresh\"\n [disabled]=\"!selectedPipeline() || validationLoading() || validationRunning()\"\n (click)=\"refreshValidation()\">Load Report</button>\n @if (validationSummary()) {\n <button kendoButton fillMode=\"flat\" size=\"small\"\n (click)=\"clearValidation()\">Clear</button>\n }\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (validationLoading()) {\n <span class=\"toolbar-meta\">Loading\u2026</span>\n } @else if (validationError(); as err) {\n <span class=\"toolbar-meta validation-error-text\">{{ err }}</span>\n } @else if (validationSummary(); as s) {\n <span class=\"validation-summary\">\n <span class=\"badge badge-error\" title=\"error\">{{ s.error }} err</span>\n <span class=\"badge badge-warning\" title=\"warning\">{{ s.warning }} warn</span>\n <span class=\"badge badge-ok\" title=\"ok\">{{ s.ok }} ok</span>\n <span class=\"badge badge-info\" title=\"info / unscoped\">{{ s.info }} info</span>\n <span class=\"badge\">{{ s.total }} total</span>\n @if (validationExecutedAt(); as ts) {\n <span class=\"validation-timestamp\">{{ ts }}</span>\n }\n </span>\n }\n </div>\n\n <div class=\"coverage-body\">\n <div class=\"coverage-tree-pane\">\n @if (selectedRoot()) {\n <mm-tree-view\n [dataSource]=\"dataSource\"\n (nodeSelected)=\"onNodeSelected($event)\">\n </mm-tree-view>\n } @else {\n <div class=\"empty-hint\">\n Select a tree to start. Counts in brackets show how many mappings\n point to that node.\n </div>\n }\n </div>\n\n <div class=\"coverage-detail-pane\">\n @if (selectedNode(); as node) {\n <div class=\"detail-header\">\n <div class=\"detail-titles\">\n <h3 class=\"detail-name\">{{ node.name }}</h3>\n <div class=\"detail-subtitle\">{{ node.ckTypeId }}@{{ node.rtId }}</div>\n </div>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.refresh\"\n (click)=\"refreshSelected()\" title=\"Refresh\"></button>\n </div>\n\n <div class=\"detail-section-title\">\n <span>Mappings</span>\n @if (summaryLine(); as s) {\n <span class=\"detail-summary\">{{ s }}</span>\n }\n <span class=\"toolbar-spacer\"></span>\n <button kendoButton themeColor=\"primary\" size=\"small\" [svgIcon]=\"icons.plus\"\n (click)=\"addMapping()\">Add</button>\n </div>\n\n @if (mappingsLoading()) {\n <div class=\"empty-hint\">Loading mappings\u2026</div>\n } @else if (mappingsError(); as err) {\n <div class=\"error-hint\">{{ err }}</div>\n } @else if (mappings().length === 0) {\n <div class=\"empty-hint\">\n No mappings reference this node yet. Add one using the button above.\n </div>\n } @else {\n <ul class=\"mapping-list\">\n @for (mapping of mappings(); track trackByRtId($index, mapping)) {\n <li class=\"mapping-row\" [class.mapping-disabled]=\"!mapping.enabled\">\n <div class=\"mapping-line\">\n <span class=\"mapping-name\">\n {{ mapping.name || 'Mapping ' + ($index + 1) }}\n </span>\n @if (!mapping.enabled) {\n <span class=\"mapping-badge mapping-badge-off\">disabled</span>\n }\n </div>\n <div class=\"mapping-details\">\n <span class=\"kv\">\n <span class=\"kv-key\">Source:</span>\n <span class=\"kv-val\">\n {{ mapping.sourceName || mapping.sourceRtId || '\u2014 not linked \u2014' }}\n </span>\n </span>\n @if (mapping.sourceAttributePath) {\n <span class=\"kv\">\n <span class=\"kv-key\">Attr:</span>\n <span class=\"kv-val\">{{ mapping.sourceAttributePath }}</span>\n </span>\n }\n @if (mapping.mappingExpression) {\n <span class=\"kv\">\n <span class=\"kv-key\">Expr:</span>\n <code class=\"kv-val\">{{ mapping.mappingExpression }}</code>\n </span>\n }\n @if (mapping.targetAttributePath) {\n <span class=\"kv\">\n <span class=\"kv-key\">\u2192</span>\n <span class=\"kv-val\">{{ mapping.targetAttributePath }}</span>\n </span>\n }\n </div>\n <div class=\"mapping-actions\">\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\" size=\"small\"\n [svgIcon]=\"icons.pencil\"\n (click)=\"editMapping(mapping)\"\n title=\"Edit mapping\">Edit\u2026</button>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.trash\"\n (click)=\"deleteMapping(mapping)\"\n title=\"Delete mapping\"></button>\n </div>\n </li>\n }\n </ul>\n }\n } @else {\n <div class=\"empty-hint\">\n Select a node from the tree to view and edit its mappings.\n </div>\n }\n </div>\n </div>\n }\n\n @if (activeTab() === 'orphans') {\n <div class=\"coverage-toolbar\">\n <label class=\"toolbar-label\">Source type:</label>\n <select class=\"root-select\"\n [value]=\"orphanCkType() ?? ''\"\n (change)=\"onOrphanCkTypeChange($any($event.target).value)\">\n @for (ckType of config.sourceCandidateCkTypeIds; track ckType) {\n <option [value]=\"ckType\">{{ ckType }}</option>\n }\n </select>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.refresh\"\n [disabled]=\"!orphanCkType() || orphanLoading()\"\n (click)=\"refreshOrphans()\" title=\"Reload\"></button>\n <label class=\"orphan-toggle\">\n <input type=\"checkbox\" [checked]=\"orphanHideMapped()\" (change)=\"toggleOrphanHideMapped()\" />\n Hide already-mapped\n </label>\n <label class=\"toolbar-label\">Group by:</label>\n <select class=\"root-select\"\n [value]=\"orphanGroupParentType() ?? ''\"\n (change)=\"onOrphanGroupParentTypeChange($any($event.target).value)\">\n <option value=\"\">(no grouping)</option>\n @for (t of orphanAvailableParentTypes(); track t) {\n <option [value]=\"t\">{{ t }}</option>\n }\n </select>\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (orphanStats(); as s) {\n <span class=\"validation-summary\">\n <span class=\"badge badge-error\" title=\"unmapped \u2014 no DataPointMapping yet\">{{ s.unmapped }} unmapped</span>\n <span class=\"badge badge-ok\" title=\"already covered by at least one mapping\">{{ s.mapped }} mapped</span>\n <span class=\"badge\">{{ s.total }} total</span>\n </span>\n }\n </div>\n\n <div class=\"orphan-body\">\n @if (orphanLoading()) {\n <div class=\"empty-hint\">Loading source candidates\u2026</div>\n } @else if (orphanError(); as err) {\n <div class=\"error-hint\">{{ err }}</div>\n } @else if (orphanFilteredList().length === 0) {\n <div class=\"empty-hint\">\n @if (orphanHideMapped()) {\n All {{ orphanCkType() }} entities are mapped at least once. \uD83C\uDF89\n } @else {\n No entities of type {{ orphanCkType() }} found.\n }\n </div>\n } @else if (orphanGroupParentType()) {\n @for (group of orphanGroupedList(); track group.key) {\n <div class=\"orphan-group\">\n <div class=\"orphan-group-header\">\n <span class=\"orphan-group-label\">{{ group.label }}</span>\n <span class=\"orphan-group-count\">{{ group.items.length }}</span>\n </div>\n <ul class=\"orphan-list\">\n @for (item of group.items; track trackOrphanByRtId($index, item)) {\n <ng-container *ngTemplateOutlet=\"orphanRowTpl; context: { $implicit: item }\"></ng-container>\n }\n </ul>\n </div>\n }\n } @else {\n <ul class=\"orphan-list\">\n @for (item of orphanFilteredList(); track trackOrphanByRtId($index, item)) {\n <ng-container *ngTemplateOutlet=\"orphanRowTpl; context: { $implicit: item }\"></ng-container>\n }\n </ul>\n }\n\n <ng-template #orphanRowTpl let-item>\n <li class=\"orphan-row\" [class.orphan-mapped]=\"item.mappingCount > 0\">\n <div class=\"orphan-line\">\n <span class=\"orphan-name\">{{ item.name }}</span>\n @if (item.mappingCount > 0) {\n <span class=\"orphan-badge orphan-badge-mapped\">\n {{ item.mappingCount }} mapping{{ item.mappingCount === 1 ? '' : 's' }}\n </span>\n } @else {\n <span class=\"orphan-badge orphan-badge-unmapped\">unmapped</span>\n }\n </div>\n @if (item.parentPath.length > 0) {\n <div class=\"orphan-breadcrumb\" [attr.title]=\"'Parent chain (root-first)'\">\n @for (parent of breadcrumbFor(item); track parent.rtId; let last = $last) {\n <span class=\"crumb\">{{ parent.name }}</span>\n @if (!last) { <span class=\"crumb-sep\">\u203A</span> }\n }\n </div>\n }\n <div class=\"orphan-meta\">\n <span class=\"kv-val\">{{ item.ckTypeId }}@{{ item.rtId }}</span>\n </div>\n @if (item.description) {\n <div class=\"orphan-description\">{{ item.description }}</div>\n }\n <div class=\"orphan-actions\">\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\" size=\"small\"\n [svgIcon]=\"icons.plus\"\n (click)=\"createMappingFromOrphan(item)\"\n title=\"Create mapping pre-linked to this source\">\n Map\u2026\n </button>\n </div>\n </li>\n </ng-template>\n </div>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.coverage-root{display:flex;flex-direction:column;flex:1;min-height:0;gap:12px}.coverage-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 12px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface-alt, #f8f9fa)) 70%,transparent)}.coverage-toolbar .toolbar-label{flex:0 0 96px;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.coverage-toolbar .root-select{flex:0 0 280px;min-width:0;padding:4px 8px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:4px;background:var(--theme-bg-elevated, var(--kendo-color-surface, #fff));color:var(--theme-text-primary, inherit);font-size:.9rem;height:30px}.coverage-toolbar .toolbar-spacer{flex:1}.coverage-toolbar .toolbar-meta{display:flex;flex-direction:column;align-items:flex-end;line-height:1.1}.coverage-toolbar .toolbar-meta .meta-name{font-size:.85rem;font-weight:600}.coverage-toolbar .toolbar-meta .meta-type{font-size:.7rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d)}.coverage-body{flex:1;display:flex;gap:12px;min-height:0}.coverage-tree-pane{flex:0 0 40%;min-width:240px;display:flex;flex-direction:column;min-height:0;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface, #fff)) 60%,transparent);color:var(--theme-text-primary, inherit);overflow:auto}.coverage-detail-pane{flex:1;min-width:0;display:flex;flex-direction:column;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface, #fff)) 60%,transparent);color:var(--theme-text-primary, inherit);padding:12px 14px;gap:12px;overflow:auto}.detail-header{display:flex;align-items:flex-start;gap:8px}.detail-header .detail-titles{flex:1;min-width:0}.detail-header .detail-name{margin:0;font-size:1.05rem;font-weight:600;word-break:break-word}.detail-header .detail-subtitle{font-size:.72rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d);word-break:break-all}.detail-section-title{display:flex;align-items:center;gap:10px;font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.detail-section-title .toolbar-spacer{flex:1}.detail-section-title .detail-summary{text-transform:none;letter-spacing:0;font-weight:500;color:var(--kendo-color-on-app-surface, inherit);font-size:.78rem}.mapping-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.mapping-row{border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;padding:8px 10px;display:flex;flex-direction:column;gap:6px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 65%,transparent)}.mapping-row.mapping-disabled{opacity:.55}.mapping-line{display:flex;align-items:center;gap:8px}.mapping-line .mapping-name{font-weight:600;font-size:.9rem}.mapping-badge{font-size:.65rem;text-transform:uppercase;letter-spacing:.5px;padding:1px 6px;border-radius:8px;border:1px solid var(--kendo-color-border, #dee2e6)}.mapping-badge.mapping-badge-off{color:var(--kendo-color-subtle, #6c757d)}.mapping-details{display:flex;flex-wrap:wrap;gap:10px;font-size:.8rem}.mapping-details .kv{display:inline-flex;align-items:baseline;gap:4px}.mapping-details .kv-key{font-weight:600;color:var(--kendo-color-subtle, #6c757d)}.mapping-details code.kv-val{font-family:monospace;background:var(--kendo-color-surface, transparent);padding:1px 4px;border-radius:3px}.mapping-actions{display:flex;gap:6px;justify-content:flex-end}.empty-hint,.error-hint{padding:12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d);text-align:center}.error-hint{color:var(--kendo-color-error, #dc3545)}.coverage-tabs{display:flex;gap:0;border-bottom:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6))}.coverage-tab{padding:8px 16px;border:none;background:transparent;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));font-size:.85rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;display:inline-flex;align-items:center;gap:6px}.coverage-tab:hover{color:var(--theme-text-primary, inherit)}.coverage-tab.active{color:var(--theme-text-accent, var(--kendo-color-primary, #ff6358));border-bottom-color:var(--theme-text-accent, var(--kendo-color-primary, #ff6358))}.coverage-tab .tab-badge{background:color-mix(in srgb,var(--kendo-color-error, #dc3545) 22%,transparent);color:var(--kendo-color-error, #dc3545);border-radius:10px;padding:1px 8px;font-size:.7rem;font-weight:700}.orphan-toggle{display:inline-flex;align-items:center;gap:6px;font-size:.8rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));cursor:pointer}.orphan-body{flex:1;min-height:0;overflow:auto;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface, #fff)) 60%,transparent);padding:8px}.orphan-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.orphan-row{display:flex;flex-direction:column;gap:4px;padding:8px 10px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-left-width:3px;border-left-color:var(--kendo-color-error, #dc3545);border-radius:6px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 65%,transparent)}.orphan-row.orphan-mapped{border-left-color:var(--kendo-color-success, #28a745);opacity:.7}.orphan-row .orphan-line{display:flex;align-items:center;gap:8px}.orphan-row .orphan-line .orphan-name{flex:1;font-weight:600;font-size:.9rem}.orphan-row .orphan-badge{font-size:.7rem;padding:1px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.5px;font-weight:600}.orphan-row .orphan-badge-unmapped{background:color-mix(in srgb,var(--kendo-color-error, #dc3545) 22%,transparent);color:var(--kendo-color-error, #dc3545);border:1px solid var(--kendo-color-error, #dc3545)}.orphan-row .orphan-badge-mapped{background:color-mix(in srgb,var(--kendo-color-success, #28a745) 18%,transparent);color:var(--kendo-color-success, #28a745);border:1px solid var(--kendo-color-success, #28a745)}.orphan-row .orphan-meta{font-family:monospace;font-size:.7rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));word-break:break-all}.orphan-row .orphan-breadcrumb{display:flex;flex-wrap:wrap;align-items:center;gap:4px;font-size:.75rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.orphan-row .orphan-breadcrumb .crumb{line-height:1.3}.orphan-row .orphan-breadcrumb .crumb-sep{opacity:.55;font-weight:600}.orphan-row .orphan-description{font-size:.78rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));font-style:italic}.orphan-row .orphan-actions{display:flex;gap:6px;justify-content:flex-end}.orphan-group{display:flex;flex-direction:column;gap:6px}.orphan-group+.orphan-group{margin-top:14px}.orphan-group .orphan-group-header{display:flex;align-items:baseline;gap:8px;padding:4px 8px;border-bottom:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));font-size:.85rem;font-weight:600;color:var(--theme-text-primary, inherit)}.orphan-group .orphan-group-header .orphan-group-label{flex:1}.orphan-group .orphan-group-header .orphan-group-count{font-size:.7rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f1f3f5)) 80%,transparent);border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:10px;padding:0 8px}.validation-summary{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}.badge{font-size:.7rem;padding:2px 8px;border-radius:10px;background:var(--kendo-color-surface, #f1f3f5);border:1px solid var(--kendo-color-border, #dee2e6)}.badge-error{background:color-mix(in srgb,var(--kendo-color-error, #dc3545) 18%,transparent);border-color:var(--kendo-color-error, #dc3545);color:var(--kendo-color-error, #dc3545);font-weight:600}.badge-warning{background:color-mix(in srgb,var(--kendo-color-warning, #ffc107) 22%,transparent);border-color:var(--kendo-color-warning, #ffc107);color:var(--kendo-color-warning-on, #6b5500);font-weight:600}.badge-ok{background:color-mix(in srgb,var(--kendo-color-success, #28a745) 18%,transparent);border-color:var(--kendo-color-success, #28a745);color:var(--kendo-color-success, #28a745);font-weight:600}.badge-info{background:color-mix(in srgb,var(--kendo-color-info, #0dcaf0) 18%,transparent);border-color:var(--kendo-color-info, #0dcaf0);color:var(--kendo-color-info, #0dcaf0)}.validation-error-text{color:var(--kendo-color-error, #dc3545);font-size:.8rem}.validation-timestamp{font-size:.7rem;color:var(--kendo-color-subtle, #6c757d);margin-left:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: LayoutModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: TreeComponent, selector: "mm-tree-view", inputs: ["dataSource"], outputs: ["nodeSelected", "nodeClick", "nodeDoubleClick", "nodeDrop", "expand", "collapse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
17381
|
+
}
|
|
17382
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MappingCoverageTreeComponent, decorators: [{
|
|
17383
|
+
type: Component,
|
|
17384
|
+
args: [{ selector: 'mm-mapping-coverage-tree', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
17385
|
+
CommonModule,
|
|
17386
|
+
FormsModule,
|
|
17387
|
+
ButtonModule,
|
|
17388
|
+
LayoutModule,
|
|
17389
|
+
SVGIconModule,
|
|
17390
|
+
TreeComponent,
|
|
17391
|
+
], providers: [MappingCoverageTreeDataSource], template: "<div class=\"coverage-root\">\n @if (config.sourceCandidateCkTypeIds.length > 0) {\n <div class=\"coverage-tabs\" role=\"tablist\">\n <button class=\"coverage-tab\"\n [class.active]=\"activeTab() === 'coverage'\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === 'coverage'\"\n (click)=\"selectTab('coverage')\">Coverage Tree</button>\n <button class=\"coverage-tab\"\n [class.active]=\"activeTab() === 'orphans'\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === 'orphans'\"\n (click)=\"selectTab('orphans')\">\n Orphan Sources\n @if (orphanStats(); as s) {\n @if (s.unmapped > 0) { <span class=\"tab-badge\">{{ s.unmapped }}</span> }\n }\n </button>\n </div>\n }\n\n @if (activeTab() === 'coverage') {\n <div class=\"coverage-toolbar\">\n <label class=\"toolbar-label\">Tree:</label>\n <select class=\"root-select\"\n [value]=\"selectedRoot()?.rtId ?? ''\"\n (change)=\"onRootSelectChange($any($event.target).value)\">\n <option value=\"\" disabled>\u2014 Pick a {{ config.rootCkTypeId }} \u2014</option>\n @for (root of rootCandidates(); track root.rtId) {\n <option [value]=\"root.rtId\" [title]=\"root.description\">{{ root.name }}</option>\n }\n </select>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.refresh\"\n (click)=\"refreshRoots()\" title=\"Refresh tree list\"></button>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.folderOpen\"\n (click)=\"pickRoot()\" title=\"Browse for entity\">Browse...</button>\n\n @if (selectedRoot(); as root) {\n <div class=\"toolbar-spacer\"></div>\n <span class=\"toolbar-meta\">\n <span class=\"meta-name\">{{ root.name }}</span>\n <span class=\"meta-type\">{{ root.ckTypeId }}</span>\n </span>\n }\n </div>\n\n <div class=\"coverage-toolbar coverage-toolbar-validation\">\n <label class=\"toolbar-label\">Validation:</label>\n <select class=\"root-select\"\n [value]=\"selectedPipeline()?.rtId ?? ''\"\n (change)=\"onPipelineSelectChange($any($event.target).value)\">\n <option value=\"\" disabled>\u2014 Pick a {{ config.validationPipelineCkTypeId }} \u2014</option>\n @for (p of validationPipelines(); track p.rtId) {\n <option [value]=\"p.rtId\">{{ p.name }}</option>\n }\n </select>\n @if (tenantId) {\n <button kendoButton themeColor=\"primary\" size=\"small\"\n [disabled]=\"!selectedPipeline() || validationRunning() || validationLoading()\"\n (click)=\"runValidation()\"\n [title]=\"validationRunning() ? 'Validation running\u2026' : 'Run pipeline + load report'\">\n @if (validationRunning()) { Running\u2026 } @else { Run }\n </button>\n }\n <button kendoButton fillMode=\"solid\" size=\"small\" [svgIcon]=\"icons.refresh\"\n [disabled]=\"!selectedPipeline() || validationLoading() || validationRunning()\"\n (click)=\"refreshValidation()\">Load Report</button>\n @if (validationSummary()) {\n <button kendoButton fillMode=\"flat\" size=\"small\"\n (click)=\"clearValidation()\">Clear</button>\n }\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (validationLoading()) {\n <span class=\"toolbar-meta\">Loading\u2026</span>\n } @else if (validationError(); as err) {\n <span class=\"toolbar-meta validation-error-text\">{{ err }}</span>\n } @else if (validationSummary(); as s) {\n <span class=\"validation-summary\">\n <span class=\"badge badge-error\" title=\"error\">{{ s.error }} err</span>\n <span class=\"badge badge-warning\" title=\"warning\">{{ s.warning }} warn</span>\n <span class=\"badge badge-ok\" title=\"ok\">{{ s.ok }} ok</span>\n <span class=\"badge badge-info\" title=\"info / unscoped\">{{ s.info }} info</span>\n <span class=\"badge\">{{ s.total }} total</span>\n @if (validationExecutedAt(); as ts) {\n <span class=\"validation-timestamp\">{{ ts }}</span>\n }\n </span>\n }\n </div>\n\n <div class=\"coverage-body\">\n <div class=\"coverage-tree-pane\">\n @if (selectedRoot()) {\n <mm-tree-view\n [dataSource]=\"dataSource\"\n (nodeSelected)=\"onNodeSelected($event)\">\n </mm-tree-view>\n } @else {\n <div class=\"empty-hint\">\n Select a tree to start. Counts in brackets show how many mappings\n point to that node.\n </div>\n }\n </div>\n\n <div class=\"coverage-detail-pane\">\n @if (selectedNode(); as node) {\n <div class=\"detail-header\">\n <div class=\"detail-titles\">\n <h3 class=\"detail-name\">{{ node.name }}</h3>\n <div class=\"detail-subtitle\">{{ node.ckTypeId }}@{{ node.rtId }}</div>\n </div>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.refresh\"\n (click)=\"refreshSelected()\" title=\"Refresh\"></button>\n </div>\n\n <div class=\"detail-section-title\">\n <span>Mappings</span>\n @if (summaryLine(); as s) {\n <span class=\"detail-summary\">{{ s }}</span>\n }\n <span class=\"toolbar-spacer\"></span>\n <button kendoButton themeColor=\"primary\" size=\"small\" [svgIcon]=\"icons.plus\"\n (click)=\"addMapping()\">Add</button>\n </div>\n\n @if (mappingsLoading()) {\n <div class=\"empty-hint\">Loading mappings\u2026</div>\n } @else if (mappingsError(); as err) {\n <div class=\"error-hint\">{{ err }}</div>\n } @else if (mappings().length === 0) {\n <div class=\"empty-hint\">\n No mappings reference this node yet. Add one using the button above.\n </div>\n } @else {\n <ul class=\"mapping-list\">\n @for (mapping of mappings(); track trackByRtId($index, mapping)) {\n <li class=\"mapping-row\" [class.mapping-disabled]=\"!mapping.enabled\">\n <div class=\"mapping-line\">\n <span class=\"mapping-name\">\n {{ mapping.name || 'Mapping ' + ($index + 1) }}\n </span>\n @if (!mapping.enabled) {\n <span class=\"mapping-badge mapping-badge-off\">disabled</span>\n }\n </div>\n <div class=\"mapping-details\">\n <span class=\"kv\">\n <span class=\"kv-key\">Source:</span>\n <span class=\"kv-val\">\n {{ mapping.sourceName || mapping.sourceRtId || '\u2014 not linked \u2014' }}\n </span>\n </span>\n @if (mapping.sourceAttributePath) {\n <span class=\"kv\">\n <span class=\"kv-key\">Attr:</span>\n <span class=\"kv-val\">{{ mapping.sourceAttributePath }}</span>\n </span>\n }\n @if (mapping.mappingExpression) {\n <span class=\"kv\">\n <span class=\"kv-key\">Expr:</span>\n <code class=\"kv-val\">{{ mapping.mappingExpression }}</code>\n </span>\n }\n @if (mapping.targetAttributePath) {\n <span class=\"kv\">\n <span class=\"kv-key\">\u2192</span>\n <span class=\"kv-val\">{{ mapping.targetAttributePath }}</span>\n </span>\n }\n </div>\n <div class=\"mapping-actions\">\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\" size=\"small\"\n [svgIcon]=\"icons.pencil\"\n (click)=\"editMapping(mapping)\"\n title=\"Edit mapping\">Edit\u2026</button>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.trash\"\n (click)=\"deleteMapping(mapping)\"\n title=\"Delete mapping\"></button>\n </div>\n </li>\n }\n </ul>\n }\n } @else {\n <div class=\"empty-hint\">\n Select a node from the tree to view and edit its mappings.\n </div>\n }\n </div>\n </div>\n }\n\n @if (activeTab() === 'orphans') {\n <div class=\"coverage-toolbar\">\n <label class=\"toolbar-label\">Source type:</label>\n <select class=\"root-select\"\n [value]=\"orphanCkType() ?? ''\"\n (change)=\"onOrphanCkTypeChange($any($event.target).value)\">\n @for (ckType of config.sourceCandidateCkTypeIds; track ckType) {\n <option [value]=\"ckType\">{{ ckType }}</option>\n }\n </select>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.refresh\"\n [disabled]=\"!orphanCkType() || orphanLoading()\"\n (click)=\"refreshOrphans()\" title=\"Reload\"></button>\n <label class=\"orphan-toggle\">\n <input type=\"checkbox\" [checked]=\"orphanHideMapped()\" (change)=\"toggleOrphanHideMapped()\" />\n Hide already-mapped\n </label>\n <label class=\"toolbar-label\">Group by:</label>\n <select class=\"root-select\"\n [value]=\"orphanGroupParentType() ?? ''\"\n (change)=\"onOrphanGroupParentTypeChange($any($event.target).value)\">\n <option value=\"\">(no grouping)</option>\n @for (t of orphanAvailableParentTypes(); track t) {\n <option [value]=\"t\">{{ t }}</option>\n }\n </select>\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (orphanStats(); as s) {\n <span class=\"validation-summary\">\n <span class=\"badge badge-error\" title=\"unmapped \u2014 no DataPointMapping yet\">{{ s.unmapped }} unmapped</span>\n <span class=\"badge badge-ok\" title=\"already covered by at least one mapping\">{{ s.mapped }} mapped</span>\n <span class=\"badge\">{{ s.total }} total</span>\n </span>\n }\n </div>\n\n <div class=\"orphan-body\">\n @if (orphanLoading()) {\n <div class=\"empty-hint\">Loading source candidates\u2026</div>\n } @else if (orphanError(); as err) {\n <div class=\"error-hint\">{{ err }}</div>\n } @else if (orphanFilteredList().length === 0) {\n <div class=\"empty-hint\">\n @if (orphanHideMapped()) {\n All {{ orphanCkType() }} entities are mapped at least once. \uD83C\uDF89\n } @else {\n No entities of type {{ orphanCkType() }} found.\n }\n </div>\n } @else if (orphanGroupParentType()) {\n @for (group of orphanGroupedList(); track group.key) {\n <div class=\"orphan-group\">\n <div class=\"orphan-group-header\">\n <span class=\"orphan-group-label\">{{ group.label }}</span>\n <span class=\"orphan-group-count\">{{ group.items.length }}</span>\n </div>\n <ul class=\"orphan-list\">\n @for (item of group.items; track trackOrphanByRtId($index, item)) {\n <ng-container *ngTemplateOutlet=\"orphanRowTpl; context: { $implicit: item }\"></ng-container>\n }\n </ul>\n </div>\n }\n } @else {\n <ul class=\"orphan-list\">\n @for (item of orphanFilteredList(); track trackOrphanByRtId($index, item)) {\n <ng-container *ngTemplateOutlet=\"orphanRowTpl; context: { $implicit: item }\"></ng-container>\n }\n </ul>\n }\n\n <ng-template #orphanRowTpl let-item>\n <li class=\"orphan-row\" [class.orphan-mapped]=\"item.mappingCount > 0\">\n <div class=\"orphan-line\">\n <span class=\"orphan-name\">{{ item.name }}</span>\n @if (item.mappingCount > 0) {\n <span class=\"orphan-badge orphan-badge-mapped\">\n {{ item.mappingCount }} mapping{{ item.mappingCount === 1 ? '' : 's' }}\n </span>\n } @else {\n <span class=\"orphan-badge orphan-badge-unmapped\">unmapped</span>\n }\n </div>\n @if (item.parentPath.length > 0) {\n <div class=\"orphan-breadcrumb\" [attr.title]=\"'Parent chain (root-first)'\">\n @for (parent of breadcrumbFor(item); track parent.rtId; let last = $last) {\n <span class=\"crumb\">{{ parent.name }}</span>\n @if (!last) { <span class=\"crumb-sep\">\u203A</span> }\n }\n </div>\n }\n <div class=\"orphan-meta\">\n <span class=\"kv-val\">{{ item.ckTypeId }}@{{ item.rtId }}</span>\n </div>\n @if (item.description) {\n <div class=\"orphan-description\">{{ item.description }}</div>\n }\n <div class=\"orphan-actions\">\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\" size=\"small\"\n [svgIcon]=\"icons.plus\"\n (click)=\"createMappingFromOrphan(item)\"\n title=\"Create mapping pre-linked to this source\">\n Map\u2026\n </button>\n </div>\n </li>\n </ng-template>\n </div>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.coverage-root{display:flex;flex-direction:column;flex:1;min-height:0;gap:12px}.coverage-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 12px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface-alt, #f8f9fa)) 70%,transparent)}.coverage-toolbar .toolbar-label{flex:0 0 96px;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.coverage-toolbar .root-select{flex:0 0 280px;min-width:0;padding:4px 8px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:4px;background:var(--theme-bg-elevated, var(--kendo-color-surface, #fff));color:var(--theme-text-primary, inherit);font-size:.9rem;height:30px}.coverage-toolbar .toolbar-spacer{flex:1}.coverage-toolbar .toolbar-meta{display:flex;flex-direction:column;align-items:flex-end;line-height:1.1}.coverage-toolbar .toolbar-meta .meta-name{font-size:.85rem;font-weight:600}.coverage-toolbar .toolbar-meta .meta-type{font-size:.7rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d)}.coverage-body{flex:1;display:flex;gap:12px;min-height:0}.coverage-tree-pane{flex:0 0 40%;min-width:240px;display:flex;flex-direction:column;min-height:0;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface, #fff)) 60%,transparent);color:var(--theme-text-primary, inherit);overflow:auto}.coverage-detail-pane{flex:1;min-width:0;display:flex;flex-direction:column;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface, #fff)) 60%,transparent);color:var(--theme-text-primary, inherit);padding:12px 14px;gap:12px;overflow:auto}.detail-header{display:flex;align-items:flex-start;gap:8px}.detail-header .detail-titles{flex:1;min-width:0}.detail-header .detail-name{margin:0;font-size:1.05rem;font-weight:600;word-break:break-word}.detail-header .detail-subtitle{font-size:.72rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d);word-break:break-all}.detail-section-title{display:flex;align-items:center;gap:10px;font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.detail-section-title .toolbar-spacer{flex:1}.detail-section-title .detail-summary{text-transform:none;letter-spacing:0;font-weight:500;color:var(--kendo-color-on-app-surface, inherit);font-size:.78rem}.mapping-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.mapping-row{border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;padding:8px 10px;display:flex;flex-direction:column;gap:6px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 65%,transparent)}.mapping-row.mapping-disabled{opacity:.55}.mapping-line{display:flex;align-items:center;gap:8px}.mapping-line .mapping-name{font-weight:600;font-size:.9rem}.mapping-badge{font-size:.65rem;text-transform:uppercase;letter-spacing:.5px;padding:1px 6px;border-radius:8px;border:1px solid var(--kendo-color-border, #dee2e6)}.mapping-badge.mapping-badge-off{color:var(--kendo-color-subtle, #6c757d)}.mapping-details{display:flex;flex-wrap:wrap;gap:10px;font-size:.8rem}.mapping-details .kv{display:inline-flex;align-items:baseline;gap:4px}.mapping-details .kv-key{font-weight:600;color:var(--kendo-color-subtle, #6c757d)}.mapping-details code.kv-val{font-family:monospace;background:var(--kendo-color-surface, transparent);padding:1px 4px;border-radius:3px}.mapping-actions{display:flex;gap:6px;justify-content:flex-end}.empty-hint,.error-hint{padding:12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d);text-align:center}.error-hint{color:var(--kendo-color-error, #dc3545)}.coverage-tabs{display:flex;gap:0;border-bottom:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6))}.coverage-tab{padding:8px 16px;border:none;background:transparent;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));font-size:.85rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;display:inline-flex;align-items:center;gap:6px}.coverage-tab:hover{color:var(--theme-text-primary, inherit)}.coverage-tab.active{color:var(--theme-text-accent, var(--kendo-color-primary, #ff6358));border-bottom-color:var(--theme-text-accent, var(--kendo-color-primary, #ff6358))}.coverage-tab .tab-badge{background:color-mix(in srgb,var(--kendo-color-error, #dc3545) 22%,transparent);color:var(--kendo-color-error, #dc3545);border-radius:10px;padding:1px 8px;font-size:.7rem;font-weight:700}.orphan-toggle{display:inline-flex;align-items:center;gap:6px;font-size:.8rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));cursor:pointer}.orphan-body{flex:1;min-height:0;overflow:auto;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface, #fff)) 60%,transparent);padding:8px}.orphan-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.orphan-row{display:flex;flex-direction:column;gap:4px;padding:8px 10px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-left-width:3px;border-left-color:var(--kendo-color-error, #dc3545);border-radius:6px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 65%,transparent)}.orphan-row.orphan-mapped{border-left-color:var(--kendo-color-success, #28a745);opacity:.7}.orphan-row .orphan-line{display:flex;align-items:center;gap:8px}.orphan-row .orphan-line .orphan-name{flex:1;font-weight:600;font-size:.9rem}.orphan-row .orphan-badge{font-size:.7rem;padding:1px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.5px;font-weight:600}.orphan-row .orphan-badge-unmapped{background:color-mix(in srgb,var(--kendo-color-error, #dc3545) 22%,transparent);color:var(--kendo-color-error, #dc3545);border:1px solid var(--kendo-color-error, #dc3545)}.orphan-row .orphan-badge-mapped{background:color-mix(in srgb,var(--kendo-color-success, #28a745) 18%,transparent);color:var(--kendo-color-success, #28a745);border:1px solid var(--kendo-color-success, #28a745)}.orphan-row .orphan-meta{font-family:monospace;font-size:.7rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));word-break:break-all}.orphan-row .orphan-breadcrumb{display:flex;flex-wrap:wrap;align-items:center;gap:4px;font-size:.75rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.orphan-row .orphan-breadcrumb .crumb{line-height:1.3}.orphan-row .orphan-breadcrumb .crumb-sep{opacity:.55;font-weight:600}.orphan-row .orphan-description{font-size:.78rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));font-style:italic}.orphan-row .orphan-actions{display:flex;gap:6px;justify-content:flex-end}.orphan-group{display:flex;flex-direction:column;gap:6px}.orphan-group+.orphan-group{margin-top:14px}.orphan-group .orphan-group-header{display:flex;align-items:baseline;gap:8px;padding:4px 8px;border-bottom:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));font-size:.85rem;font-weight:600;color:var(--theme-text-primary, inherit)}.orphan-group .orphan-group-header .orphan-group-label{flex:1}.orphan-group .orphan-group-header .orphan-group-count{font-size:.7rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f1f3f5)) 80%,transparent);border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:10px;padding:0 8px}.validation-summary{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}.badge{font-size:.7rem;padding:2px 8px;border-radius:10px;background:var(--kendo-color-surface, #f1f3f5);border:1px solid var(--kendo-color-border, #dee2e6)}.badge-error{background:color-mix(in srgb,var(--kendo-color-error, #dc3545) 18%,transparent);border-color:var(--kendo-color-error, #dc3545);color:var(--kendo-color-error, #dc3545);font-weight:600}.badge-warning{background:color-mix(in srgb,var(--kendo-color-warning, #ffc107) 22%,transparent);border-color:var(--kendo-color-warning, #ffc107);color:var(--kendo-color-warning-on, #6b5500);font-weight:600}.badge-ok{background:color-mix(in srgb,var(--kendo-color-success, #28a745) 18%,transparent);border-color:var(--kendo-color-success, #28a745);color:var(--kendo-color-success, #28a745);font-weight:600}.badge-info{background:color-mix(in srgb,var(--kendo-color-info, #0dcaf0) 18%,transparent);border-color:var(--kendo-color-info, #0dcaf0);color:var(--kendo-color-info, #0dcaf0)}.validation-error-text{color:var(--kendo-color-error, #dc3545);font-size:.8rem}.validation-timestamp{font-size:.7rem;color:var(--kendo-color-subtle, #6c757d);margin-left:4px}\n"] }]
|
|
17392
|
+
}], propDecorators: { treeView: [{
|
|
17393
|
+
type: ViewChild,
|
|
17394
|
+
args: [TreeComponent, { static: false }]
|
|
17395
|
+
}], config: [{
|
|
17396
|
+
type: Input
|
|
17397
|
+
}], initialRoot: [{
|
|
17398
|
+
type: Input
|
|
17399
|
+
}], tenantId: [{
|
|
17400
|
+
type: Input
|
|
17401
|
+
}], entitySelected: [{
|
|
17402
|
+
type: Output
|
|
17403
|
+
}] } });
|
|
17404
|
+
function readAttr(items, name) {
|
|
17405
|
+
if (!items)
|
|
17406
|
+
return null;
|
|
17407
|
+
const target = name.toLowerCase();
|
|
17408
|
+
for (const item of items) {
|
|
17409
|
+
if (item?.attributeName != null && item.attributeName.toLowerCase() === target && item.value != null) {
|
|
17410
|
+
return String(item.value);
|
|
17411
|
+
}
|
|
17412
|
+
}
|
|
17413
|
+
return null;
|
|
17414
|
+
}
|
|
17415
|
+
/**
|
|
17416
|
+
* Parses the JSON report emitted by ValidateDataPointCoverage@1 into a lookup
|
|
17417
|
+
* map keyed by entity rtId, plus aggregate summary counters and a per-node
|
|
17418
|
+
* subtree rollup (worst status + aggregate counts) so the tree can colour
|
|
17419
|
+
* `info` ancestors red when there's an error somewhere below them.
|
|
17420
|
+
*
|
|
17421
|
+
* Tolerant of partial/malformed payloads: unknown statuses fall back to "info"
|
|
17422
|
+
* and missing arrays default to empty. Reports from an older backend that
|
|
17423
|
+
* doesn't emit `parentRtId` degrade gracefully — every node's subtreeStatus
|
|
17424
|
+
* collapses to its own status.
|
|
17425
|
+
*/
|
|
17426
|
+
function parseValidationReport(serialised) {
|
|
17427
|
+
const result = new Map();
|
|
17428
|
+
const empty = { ok: 0, warning: 0, error: 0, info: 0, total: 0 };
|
|
17429
|
+
try {
|
|
17430
|
+
const parsed = JSON.parse(serialised);
|
|
17431
|
+
const summary = parsed?.summary ?? empty;
|
|
17432
|
+
const nodes = (parsed?.nodes ?? []).filter((n) => !!n?.rtId);
|
|
17433
|
+
// Index by rtId + build parent → children map for the rollup pass.
|
|
17434
|
+
const childrenByParent = new Map();
|
|
17435
|
+
const ownStatus = new Map();
|
|
17436
|
+
for (const n of nodes) {
|
|
17437
|
+
ownStatus.set(n.rtId, normaliseStatus(n.status));
|
|
17438
|
+
if (n.parentRtId) {
|
|
17439
|
+
const bucket = childrenByParent.get(n.parentRtId);
|
|
17440
|
+
if (bucket)
|
|
17441
|
+
bucket.push(n.rtId);
|
|
17442
|
+
else
|
|
17443
|
+
childrenByParent.set(n.parentRtId, [n.rtId]);
|
|
17444
|
+
}
|
|
17445
|
+
}
|
|
17446
|
+
// Memoised post-order walk: each node's subtree rollup includes itself
|
|
17447
|
+
// plus the recursive rollup of all descendants.
|
|
17448
|
+
const memo = new Map();
|
|
17449
|
+
const rollup = (rtId) => {
|
|
17450
|
+
const cached = memo.get(rtId);
|
|
17451
|
+
if (cached)
|
|
17452
|
+
return cached;
|
|
17453
|
+
const self = ownStatus.get(rtId) ?? 'info';
|
|
17454
|
+
const counts = { ok: 0, warning: 0, error: 0, info: 0 };
|
|
17455
|
+
counts[self] += 1;
|
|
17456
|
+
let worst = self;
|
|
17457
|
+
for (const childId of childrenByParent.get(rtId) ?? []) {
|
|
17458
|
+
const sub = rollup(childId);
|
|
17459
|
+
counts.ok += sub.counts.ok;
|
|
17460
|
+
counts.warning += sub.counts.warning;
|
|
17461
|
+
counts.error += sub.counts.error;
|
|
17462
|
+
counts.info += sub.counts.info;
|
|
17463
|
+
if (statusSeverity(sub.worst) > statusSeverity(worst))
|
|
17464
|
+
worst = sub.worst;
|
|
17465
|
+
}
|
|
17466
|
+
const out = { worst, counts };
|
|
17467
|
+
memo.set(rtId, out);
|
|
17468
|
+
return out;
|
|
17469
|
+
};
|
|
17470
|
+
for (const n of nodes) {
|
|
17471
|
+
const status = ownStatus.get(n.rtId) ?? 'info';
|
|
17472
|
+
const sub = rollup(n.rtId);
|
|
17473
|
+
result.set(n.rtId, {
|
|
17474
|
+
status,
|
|
17475
|
+
subtreeStatus: sub.worst,
|
|
17476
|
+
subtreeCounts: sub.counts,
|
|
17477
|
+
required: n.required ?? [],
|
|
17478
|
+
recommended: n.recommended ?? [],
|
|
17479
|
+
present: n.present ?? [],
|
|
17480
|
+
missingRequired: n.missingRequired ?? [],
|
|
17481
|
+
missingRecommended: n.missingRecommended ?? [],
|
|
17482
|
+
});
|
|
17483
|
+
}
|
|
17484
|
+
return { map: result, summary };
|
|
17485
|
+
}
|
|
17486
|
+
catch (err) {
|
|
17487
|
+
console.warn('Failed to parse validation report:', err);
|
|
17488
|
+
return { map: result, summary: empty };
|
|
17489
|
+
}
|
|
17490
|
+
}
|
|
17491
|
+
/**
|
|
17492
|
+
* Ordering used to compute "worst status" in a subtree rollup. Higher number
|
|
17493
|
+
* = more severe; ties are broken by the first occurrence in the traversal.
|
|
17494
|
+
*/
|
|
17495
|
+
function statusSeverity(s) {
|
|
17496
|
+
switch (s) {
|
|
17497
|
+
case 'error': return 3;
|
|
17498
|
+
case 'warning': return 2;
|
|
17499
|
+
case 'ok': return 1;
|
|
17500
|
+
case 'info':
|
|
17501
|
+
default: return 0;
|
|
17502
|
+
}
|
|
17503
|
+
}
|
|
17504
|
+
function normaliseStatus(value) {
|
|
17505
|
+
switch (value) {
|
|
17506
|
+
case 'ok':
|
|
17507
|
+
case 'warning':
|
|
17508
|
+
case 'error':
|
|
17509
|
+
case 'info':
|
|
17510
|
+
return value;
|
|
17511
|
+
default:
|
|
17512
|
+
return 'info';
|
|
17513
|
+
}
|
|
17514
|
+
}
|
|
17515
|
+
function sleep(ms) {
|
|
17516
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
17517
|
+
}
|
|
17518
|
+
/**
|
|
17519
|
+
* Walks the up-to-3-hop parent chain produced by `getOrphanCandidates` and
|
|
17520
|
+
* flattens it into an array ordered from immediate parent (index 0) to
|
|
17521
|
+
* root-most known ancestor. Stops at the first hop with no parent items —
|
|
17522
|
+
* a Loxone Control whose Category is the topmost reachable ancestor in 3
|
|
17523
|
+
* hops yields a 1-item path; an OPC-UA node deep in a tree yields 3.
|
|
17524
|
+
*/
|
|
17525
|
+
function extractParentPath(first) {
|
|
17526
|
+
const path = [];
|
|
17527
|
+
let cursor = first;
|
|
17528
|
+
while (cursor) {
|
|
17529
|
+
if (cursor.rtId == null || cursor.ckTypeId == null)
|
|
17530
|
+
break;
|
|
17531
|
+
path.push({
|
|
17532
|
+
rtId: String(cursor.rtId),
|
|
17533
|
+
ckTypeId: String(cursor.ckTypeId),
|
|
17534
|
+
name: readAttr(cursor.attributes?.items, 'name')
|
|
17535
|
+
?? cursor.rtWellKnownName
|
|
17536
|
+
?? String(cursor.rtId),
|
|
17537
|
+
});
|
|
17538
|
+
const next = cursor.associations?.parent?.items?.[0];
|
|
17539
|
+
cursor = next ?? null;
|
|
17540
|
+
}
|
|
17541
|
+
return path;
|
|
17542
|
+
}
|
|
17543
|
+
|
|
15210
17544
|
class TenantSwitcherComponent {
|
|
15211
17545
|
currentTenantId = null;
|
|
15212
17546
|
allowedTenants = [];
|
|
@@ -15384,5 +17718,5 @@ function provideOctoUi() {
|
|
|
15384
17718
|
* Generated bundle index. Do not edit.
|
|
15385
17719
|
*/
|
|
15386
17720
|
|
|
15387
|
-
export { AssociationValidationService, AttributeSelectorDialogComponent, AttributeSelectorDialogService, AttributeSortSelectorDialogComponent, AttributeSortSelectorDialogService, AttributeValueTypeDto, CkTypeSelectorDialogComponent, CkTypeSelectorDialogService, CkTypeSelectorInputComponent, DEFAULT_RUNTIME_BROWSER_MESSAGES, DataMappingOverviewComponent, DefaultPropertyCategory, EntityDetailComponent, EntityIdInfoComponent, EntitySelectorDialogComponent, EntitySelectorDialogService, FieldFilterEditorComponent, OctoGraphQlDataSource, OctoGraphQlHierarchyDataSource, OctoLoaderComponent, PropertyConverterService, PropertyDisplayMode, PropertyGridComponent, PropertyValueDisplayComponent, RUNTIME_BROWSER_MESSAGES, RecordDetailDialogComponent, RtEntityIdHelper, RuntimeBrowserComponent, RuntimeBrowserOutletComponent, RuntimeBrowserPageComponent, RuntimeBrowserStateService, RuntimeEntityVariableDialogComponent, RuntimeEntityVariableDialogService, TenantSwitcherComponent, account_tree, add, analytics, app_registration, article, botService, category, chat, checklist, code, component_exchange, computer, createRuntimeBrowserRoutes, customer, dashboard, event_list, graphic_eq, group, identityService, insert_link, manage_accounts, more_time, notifications, page_info, pages, person_search, playlist_add_check, pool, power, provideOctoUi, publicIcon, query_builder, schedule_send, settings, sort, storage, swagger, swagger_asset, swagger_bot, swagger_communication, swagger_identity, team_dashboard, tenancy, text_snippet, travel_explore, user_diagnostics, webhook, work };
|
|
17721
|
+
export { AssociationValidationService, AttributeSelectorDialogComponent, AttributeSelectorDialogService, AttributeSortSelectorDialogComponent, AttributeSortSelectorDialogService, AttributeValueTypeDto, CkTypeSelectorDialogComponent, CkTypeSelectorDialogService, CkTypeSelectorInputComponent, DEFAULT_DATA_POINT, DEFAULT_MAPPING_COVERAGE_TREE_CONFIG, DEFAULT_RUNTIME_BROWSER_MESSAGES, DataMappingOverviewComponent, DataPointPickerComponent, DataPointResolverService, DefaultPropertyCategory, EntityDetailComponent, EntityIdInfoComponent, EntitySelectorDialogComponent, EntitySelectorDialogService, FieldFilterEditorComponent, MappingCoverageTreeComponent, MappingCoverageTreeDataSource, MappingEditDialogComponent, MappingEditDialogService, OctoGraphQlDataSource, OctoGraphQlHierarchyDataSource, OctoLoaderComponent, PropertyConverterService, PropertyDisplayMode, PropertyGridComponent, PropertyValueDisplayComponent, RUNTIME_BROWSER_MESSAGES, RecordDetailDialogComponent, RtEntityIdHelper, RuntimeBrowserComponent, RuntimeBrowserOutletComponent, RuntimeBrowserPageComponent, RuntimeBrowserStateService, RuntimeEntityVariableDialogComponent, RuntimeEntityVariableDialogService, TenantSwitcherComponent, account_tree, add, analytics, app_registration, article, botService, category, chat, checklist, code, component_exchange, computer, createRuntimeBrowserRoutes, customer, dashboard, event_list, extractDataPointNames, graphic_eq, group, identityService, insert_link, manage_accounts, more_time, notifications, page_info, pages, person_search, playlist_add_check, pool, power, provideOctoUi, publicIcon, query_builder, schedule_send, settings, sort, storage, swagger, swagger_asset, swagger_bot, swagger_communication, swagger_identity, team_dashboard, tenancy, text_snippet, travel_explore, user_diagnostics, webhook, work };
|
|
15388
17722
|
//# sourceMappingURL=meshmakers-octo-ui.mjs.map
|