@meshmakers/octo-meshboard 3.3.690 → 3.3.710
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.
|
@@ -5,14 +5,14 @@ import { Injectable, inject, EventEmitter, forwardRef, Output, Input, ViewChild,
|
|
|
5
5
|
import * as i1$1 from '@angular/forms';
|
|
6
6
|
import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
|
|
7
7
|
import { firstValueFrom, from, map, Observable, of, catchError, interval, filter } from 'rxjs';
|
|
8
|
-
import * as i1$
|
|
8
|
+
import * as i1$8 from '@meshmakers/shared-ui';
|
|
9
9
|
import { EntitySelectInputComponent, WindowStateService, ListViewComponent, FetchResultTyped, DataSourceBase, TimeRangePickerComponent, ImportStrategyDialogService, TimeRangeUtils, HAS_UNSAVED_CHANGES, UnsavedChangesDirective } from '@meshmakers/shared-ui';
|
|
10
10
|
import * as i1 from 'apollo-angular';
|
|
11
11
|
import { gql, Apollo } from 'apollo-angular';
|
|
12
12
|
import * as i1$3 from '@angular/common';
|
|
13
13
|
import { CommonModule } from '@angular/common';
|
|
14
14
|
import { CkTypeSelectorInputComponent, FieldFilterEditorComponent, AttributeValueTypeDto, PropertyGridComponent, OctoGraphQlDataSource, AttributeSelectorDialogService, AttributeSortSelectorDialogService } from '@meshmakers/octo-ui';
|
|
15
|
-
import * as i1$
|
|
15
|
+
import * as i1$5 from '@progress/kendo-angular-dialog';
|
|
16
16
|
import { WindowService, WindowCloseResult, WindowRef, DialogsModule, DialogRef, DialogModule, DialogService } from '@progress/kendo-angular-dialog';
|
|
17
17
|
import * as i2 from '@progress/kendo-angular-buttons';
|
|
18
18
|
import { ButtonsModule, ButtonModule } from '@progress/kendo-angular-buttons';
|
|
@@ -20,27 +20,27 @@ import * as i3 from '@progress/kendo-angular-inputs';
|
|
|
20
20
|
import { InputsModule, CheckBoxModule, NumericTextBoxModule, TextBoxModule, FormFieldModule } from '@progress/kendo-angular-inputs';
|
|
21
21
|
import * as i1$2 from '@progress/kendo-angular-indicators';
|
|
22
22
|
import { LoaderModule } from '@progress/kendo-angular-indicators';
|
|
23
|
-
import * as
|
|
23
|
+
import * as i1$4 from '@progress/kendo-angular-icons';
|
|
24
24
|
import { SVGIconModule } from '@progress/kendo-angular-icons';
|
|
25
|
-
import { arrowUpIcon, arrowDownIcon, minusIcon, arrowRightIcon, arrowLeftIcon, linkIcon, chevronDownIcon, chevronRightIcon, columnsIcon, sortAscIcon, filterIcon, searchIcon, chartPieIcon, plusIcon, trashIcon, pencilIcon,
|
|
25
|
+
import { arrowUpIcon, arrowDownIcon, minusIcon, arrowRightIcon, arrowLeftIcon, linkIcon, chevronDownIcon, chevronRightIcon, circleIcon, questionCircleIcon, minusCircleIcon, warningTriangleIcon, exclamationCircleIcon, xCircleIcon, checkCircleIcon, columnsIcon, sortAscIcon, filterIcon, searchIcon, chartPieIcon, infoCircleIcon, plusIcon, trashIcon, pencilIcon, chartLineIcon, gearsIcon, clipboardMarkdownIcon, copyIcon, gridIcon, heartIcon, gridLayoutIcon, chartLineMarkersIcon, chartColumnStackedIcon, chartDoughnutIcon, tableIcon, shareIcon, fileTxtIcon, checkIcon, xIcon, downloadIcon, uploadIcon, gearIcon, saveIcon, arrowRotateCwIcon, undoIcon } from '@progress/kendo-svg-icons';
|
|
26
26
|
import * as i4 from '@progress/kendo-angular-dropdowns';
|
|
27
27
|
import { DropDownsModule, DropDownListModule } from '@progress/kendo-angular-dropdowns';
|
|
28
28
|
import { map as map$1, catchError as catchError$1 } from 'rxjs/operators';
|
|
29
29
|
import { NotificationService } from '@progress/kendo-angular-notification';
|
|
30
|
-
import * as i1$
|
|
30
|
+
import * as i1$6 from '@progress/kendo-angular-gauges';
|
|
31
31
|
import { CollectionChangesService, KENDO_GAUGES } from '@progress/kendo-angular-gauges';
|
|
32
|
-
import * as i1$
|
|
32
|
+
import * as i1$7 from '@progress/kendo-angular-charts';
|
|
33
33
|
import { ChartsModule } from '@progress/kendo-angular-charts';
|
|
34
34
|
import * as i3$1 from '@angular/router';
|
|
35
35
|
import { Router, ActivatedRoute, NavigationEnd, RouterModule } from '@angular/router';
|
|
36
|
-
import * as i2$
|
|
36
|
+
import * as i2$1 from 'ngx-markdown';
|
|
37
37
|
import { MarkdownModule } from 'ngx-markdown';
|
|
38
|
+
import * as i4$1 from '@progress/kendo-angular-label';
|
|
39
|
+
import { LabelModule, KENDO_LABELS } from '@progress/kendo-angular-label';
|
|
38
40
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
39
41
|
import { GetProcessDiagramDtoGQL, GetProcessDiagramsDtoGQL, CreateProcessDiagramDtoGQL, UpdateProcessDiagramDtoGQL, SymbolLibraryService, ExpressionEvaluatorService, renderAnimations, getFlowParticlesAnimation, renderFlowParticles } from '@meshmakers/octo-process-diagrams';
|
|
40
42
|
import * as i5 from '@progress/kendo-angular-layout';
|
|
41
|
-
import { LayoutModule, TabStripModule, TileLayoutModule } from '@progress/kendo-angular-layout';
|
|
42
|
-
import * as i4$1 from '@progress/kendo-angular-label';
|
|
43
|
-
import { LabelModule, KENDO_LABELS } from '@progress/kendo-angular-label';
|
|
43
|
+
import { LayoutModule, TabStripModule, TileLayoutModule, TileLayoutComponent } from '@progress/kendo-angular-layout';
|
|
44
44
|
import * as i4$2 from '@progress/kendo-angular-dateinputs';
|
|
45
45
|
import { DatePickerModule, DateTimePickerModule } from '@progress/kendo-angular-dateinputs';
|
|
46
46
|
import { BreadCrumbService } from '@meshmakers/shared-services';
|
|
@@ -1120,6 +1120,10 @@ class WidgetRegistryService {
|
|
|
1120
1120
|
* Builds base configuration from persisted data.
|
|
1121
1121
|
*/
|
|
1122
1122
|
buildBaseConfig(data) {
|
|
1123
|
+
const parsedConfig = data.config ? (typeof data.config === 'string' ? JSON.parse(data.config) : data.config) : {};
|
|
1124
|
+
const chromeless = parsedConfig['chromeless'] === true;
|
|
1125
|
+
// Migration: if chromeless is set but no zone, infer zone from chromeless (legacy data)
|
|
1126
|
+
const zone = parsedConfig['zone'] ?? (chromeless ? 'banner' : undefined);
|
|
1123
1127
|
return {
|
|
1124
1128
|
id: data.rtId,
|
|
1125
1129
|
title: data.name,
|
|
@@ -1127,7 +1131,9 @@ class WidgetRegistryService {
|
|
|
1127
1131
|
row: data.row,
|
|
1128
1132
|
colSpan: data.colSpan,
|
|
1129
1133
|
rowSpan: data.rowSpan,
|
|
1130
|
-
configurable: true
|
|
1134
|
+
configurable: true,
|
|
1135
|
+
chromeless: chromeless || undefined,
|
|
1136
|
+
zone
|
|
1131
1137
|
};
|
|
1132
1138
|
}
|
|
1133
1139
|
/**
|
|
@@ -1549,7 +1555,11 @@ class MeshBoardPersistenceService {
|
|
|
1549
1555
|
dataSourceType,
|
|
1550
1556
|
dataSourceCkTypeId: persistenceData.dataSourceCkTypeId ?? '',
|
|
1551
1557
|
dataSourceRtId: persistenceData.dataSourceRtId ?? '',
|
|
1552
|
-
config: JSON.stringify(
|
|
1558
|
+
config: JSON.stringify({
|
|
1559
|
+
...persistenceData.config,
|
|
1560
|
+
...(widget.chromeless ? { chromeless: true } : {}),
|
|
1561
|
+
...(widget.zone && widget.zone !== 'grid' ? { zone: widget.zone } : {})
|
|
1562
|
+
})
|
|
1553
1563
|
};
|
|
1554
1564
|
// Only include parent if there are association changes
|
|
1555
1565
|
if (parentAssociations.length > 0) {
|
|
@@ -1728,7 +1738,7 @@ class MeshBoardGridService {
|
|
|
1728
1738
|
wouldCauseOverlap(widgets, widgetId, col, row, colSpan, rowSpan) {
|
|
1729
1739
|
const widgetCells = this.getCells(col, row, colSpan, rowSpan);
|
|
1730
1740
|
for (const other of widgets) {
|
|
1731
|
-
if (other.id === widgetId)
|
|
1741
|
+
if (other.id === widgetId || other.zone === 'banner')
|
|
1732
1742
|
continue;
|
|
1733
1743
|
const otherCells = this.getCells(other.col, other.row, other.colSpan, other.rowSpan);
|
|
1734
1744
|
for (const cell of widgetCells) {
|
|
@@ -1773,6 +1783,10 @@ class MeshBoardGridService {
|
|
|
1773
1783
|
const movedWidgets = [];
|
|
1774
1784
|
const occupiedCells = new Set();
|
|
1775
1785
|
for (const widget of widgets) {
|
|
1786
|
+
// Skip banner-zone widgets — they are rendered outside the grid
|
|
1787
|
+
// and should not participate in grid overlap detection.
|
|
1788
|
+
if (widget.zone === 'banner')
|
|
1789
|
+
continue;
|
|
1776
1790
|
const cells = this.getCells(widget.col, widget.row, widget.colSpan, widget.rowSpan);
|
|
1777
1791
|
const hasOverlap = cells.some(c => occupiedCells.has(`${c.row},${c.col}`));
|
|
1778
1792
|
if (hasOverlap) {
|
|
@@ -4019,6 +4033,12 @@ class KpiWidgetComponent {
|
|
|
4019
4033
|
default: return 'trend-neutral';
|
|
4020
4034
|
}
|
|
4021
4035
|
}, ...(ngDevMode ? [{ debugName: "trendClass" }] : /* istanbul ignore next */ []));
|
|
4036
|
+
comparisonText = computed(() => {
|
|
4037
|
+
if (!this.config?.comparisonText)
|
|
4038
|
+
return null;
|
|
4039
|
+
const variables = this.stateService.getVariables();
|
|
4040
|
+
return this.variableService.resolveVariables(this.config.comparisonText, variables);
|
|
4041
|
+
}, ...(ngDevMode ? [{ debugName: "comparisonText" }] : /* istanbul ignore next */ []));
|
|
4022
4042
|
ngOnInit() {
|
|
4023
4043
|
this.loadData();
|
|
4024
4044
|
}
|
|
@@ -4265,11 +4285,11 @@ class KpiWidgetComponent {
|
|
|
4265
4285
|
return this.variableService.convertToFieldFilterDto(filters, variables);
|
|
4266
4286
|
}
|
|
4267
4287
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: KpiWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4268
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: KpiWidgetComponent, isStandalone: true, selector: "mm-kpi-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"kpi-widget\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else {\n <div class=\"kpi-content\">\n <div class=\"kpi-value-container\">\n @if (config.prefix) {\n <span class=\"kpi-prefix\">{{ config.prefix }}</span>\n }\n <span class=\"kpi-value\">{{ value() }}</span>\n @if (config.suffix) {\n <span class=\"kpi-suffix\">{{ config.suffix }}</span>\n }\n </div>\n\n @if (label()) {\n <div class=\"kpi-label\">{{ label() }}</div>\n }\n </div>\n\n @if (config.trend) {\n <div class=\"kpi-trend\" [ngClass]=\"trendClass()\">\n <kendo-svg-icon [icon]=\"trendIcon()\" size=\"medium\"></kendo-svg-icon>\n </div>\n }\n }\n</div>\n", styles: [".kpi-widget{display:flex;align-items:center;justify-content:center;height:100%;padding:16px;gap:16px}.kpi-content{display:flex;flex-direction:column;align-items:center;text-align:center}.kpi-value-container{display:flex;align-items:baseline;gap:4px}.kpi-prefix,.kpi-suffix{font-size:1.25rem;font-weight:500;color:var(--kendo-color-subtle, #6c757d)}.kpi-value{font-size:2.5rem;font-weight:700;color:var(--kendo-color-on-surface, #212529);line-height:1}.kpi-label{margin-top:8px;font-size:.875rem;color:var(--kendo-color-subtle, #6c757d)}.kpi-trend{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%}.kpi-trend.trend-up{background:#28a74526;color:var(--kendo-color-success, #28a745)}.kpi-trend.trend-down{background:#dc354526;color:var(--kendo-color-error, #dc3545)}.kpi-trend.trend-neutral{background:#6c757d26;color:var(--kendo-color-subtle, #6c757d)}@media(max-width:768px){.kpi-value{font-size:1.75rem}.kpi-prefix,.kpi-suffix{font-size:1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type:
|
|
4288
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: KpiWidgetComponent, isStandalone: true, selector: "mm-kpi-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"kpi-widget\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else {\n <div class=\"kpi-content\">\n <div class=\"kpi-value-container\">\n @if (config.prefix) {\n <span class=\"kpi-prefix\">{{ config.prefix }}</span>\n }\n <span class=\"kpi-value\">{{ value() }}</span>\n @if (config.suffix) {\n <span class=\"kpi-suffix\">{{ config.suffix }}</span>\n }\n </div>\n\n @if (comparisonText()) {\n <div class=\"kpi-comparison\" [ngClass]=\"trendClass()\">\n @if (config.trend) {\n <kendo-svg-icon [icon]=\"trendIcon()\" size=\"xsmall\"></kendo-svg-icon>\n }\n {{ comparisonText() }}\n </div>\n }\n\n @if (label()) {\n <div class=\"kpi-label\">{{ label() }}</div>\n }\n </div>\n\n @if (config.trend) {\n <div class=\"kpi-trend\" [ngClass]=\"trendClass()\">\n <kendo-svg-icon [icon]=\"trendIcon()\" size=\"medium\"></kendo-svg-icon>\n </div>\n }\n }\n</div>\n", styles: [".kpi-widget{display:flex;align-items:center;justify-content:center;height:100%;padding:16px;gap:16px}.kpi-content{display:flex;flex-direction:column;align-items:center;text-align:center}.kpi-value-container{display:flex;align-items:baseline;gap:4px}.kpi-prefix,.kpi-suffix{font-size:1.25rem;font-weight:500;color:var(--kendo-color-subtle, #6c757d)}.kpi-value{font-size:2.5rem;font-weight:700;color:var(--kendo-color-on-surface, #212529);line-height:1}.kpi-comparison{margin-top:4px;font-size:.75rem;font-weight:500;display:flex;align-items:center;gap:4px;justify-content:center}.kpi-comparison.trend-up{color:var(--kendo-color-success, #28a745)}.kpi-comparison.trend-down{color:var(--kendo-color-error, #dc3545)}.kpi-comparison.trend-neutral{color:var(--kendo-color-subtle, #6c757d)}.kpi-label{margin-top:8px;font-size:.875rem;color:var(--kendo-color-subtle, #6c757d)}.kpi-trend{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%}.kpi-trend.trend-up{background:#28a74526;color:var(--kendo-color-success, #28a745)}.kpi-trend.trend-down{background:#dc354526;color:var(--kendo-color-error, #dc3545)}.kpi-trend.trend-neutral{background:#6c757d26;color:var(--kendo-color-subtle, #6c757d)}@media(max-width:768px){.kpi-value{font-size:1.75rem}.kpi-prefix,.kpi-suffix{font-size:1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
4269
4289
|
}
|
|
4270
4290
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: KpiWidgetComponent, decorators: [{
|
|
4271
4291
|
type: Component,
|
|
4272
|
-
args: [{ selector: 'mm-kpi-widget', standalone: true, imports: [CommonModule, SVGIconModule, WidgetNotConfiguredComponent], template: "<div class=\"kpi-widget\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else {\n <div class=\"kpi-content\">\n <div class=\"kpi-value-container\">\n @if (config.prefix) {\n <span class=\"kpi-prefix\">{{ config.prefix }}</span>\n }\n <span class=\"kpi-value\">{{ value() }}</span>\n @if (config.suffix) {\n <span class=\"kpi-suffix\">{{ config.suffix }}</span>\n }\n </div>\n\n @if (label()) {\n <div class=\"kpi-label\">{{ label() }}</div>\n }\n </div>\n\n @if (config.trend) {\n <div class=\"kpi-trend\" [ngClass]=\"trendClass()\">\n <kendo-svg-icon [icon]=\"trendIcon()\" size=\"medium\"></kendo-svg-icon>\n </div>\n }\n }\n</div>\n", styles: [".kpi-widget{display:flex;align-items:center;justify-content:center;height:100%;padding:16px;gap:16px}.kpi-content{display:flex;flex-direction:column;align-items:center;text-align:center}.kpi-value-container{display:flex;align-items:baseline;gap:4px}.kpi-prefix,.kpi-suffix{font-size:1.25rem;font-weight:500;color:var(--kendo-color-subtle, #6c757d)}.kpi-value{font-size:2.5rem;font-weight:700;color:var(--kendo-color-on-surface, #212529);line-height:1}.kpi-label{margin-top:8px;font-size:.875rem;color:var(--kendo-color-subtle, #6c757d)}.kpi-trend{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%}.kpi-trend.trend-up{background:#28a74526;color:var(--kendo-color-success, #28a745)}.kpi-trend.trend-down{background:#dc354526;color:var(--kendo-color-error, #dc3545)}.kpi-trend.trend-neutral{background:#6c757d26;color:var(--kendo-color-subtle, #6c757d)}@media(max-width:768px){.kpi-value{font-size:1.75rem}.kpi-prefix,.kpi-suffix{font-size:1rem}}\n"] }]
|
|
4292
|
+
args: [{ selector: 'mm-kpi-widget', standalone: true, imports: [CommonModule, SVGIconModule, WidgetNotConfiguredComponent], template: "<div class=\"kpi-widget\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else {\n <div class=\"kpi-content\">\n <div class=\"kpi-value-container\">\n @if (config.prefix) {\n <span class=\"kpi-prefix\">{{ config.prefix }}</span>\n }\n <span class=\"kpi-value\">{{ value() }}</span>\n @if (config.suffix) {\n <span class=\"kpi-suffix\">{{ config.suffix }}</span>\n }\n </div>\n\n @if (comparisonText()) {\n <div class=\"kpi-comparison\" [ngClass]=\"trendClass()\">\n @if (config.trend) {\n <kendo-svg-icon [icon]=\"trendIcon()\" size=\"xsmall\"></kendo-svg-icon>\n }\n {{ comparisonText() }}\n </div>\n }\n\n @if (label()) {\n <div class=\"kpi-label\">{{ label() }}</div>\n }\n </div>\n\n @if (config.trend) {\n <div class=\"kpi-trend\" [ngClass]=\"trendClass()\">\n <kendo-svg-icon [icon]=\"trendIcon()\" size=\"medium\"></kendo-svg-icon>\n </div>\n }\n }\n</div>\n", styles: [".kpi-widget{display:flex;align-items:center;justify-content:center;height:100%;padding:16px;gap:16px}.kpi-content{display:flex;flex-direction:column;align-items:center;text-align:center}.kpi-value-container{display:flex;align-items:baseline;gap:4px}.kpi-prefix,.kpi-suffix{font-size:1.25rem;font-weight:500;color:var(--kendo-color-subtle, #6c757d)}.kpi-value{font-size:2.5rem;font-weight:700;color:var(--kendo-color-on-surface, #212529);line-height:1}.kpi-comparison{margin-top:4px;font-size:.75rem;font-weight:500;display:flex;align-items:center;gap:4px;justify-content:center}.kpi-comparison.trend-up{color:var(--kendo-color-success, #28a745)}.kpi-comparison.trend-down{color:var(--kendo-color-error, #dc3545)}.kpi-comparison.trend-neutral{color:var(--kendo-color-subtle, #6c757d)}.kpi-label{margin-top:8px;font-size:.875rem;color:var(--kendo-color-subtle, #6c757d)}.kpi-trend{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%}.kpi-trend.trend-up{background:#28a74526;color:var(--kendo-color-success, #28a745)}.kpi-trend.trend-down{background:#dc354526;color:var(--kendo-color-error, #dc3545)}.kpi-trend.trend-neutral{background:#6c757d26;color:var(--kendo-color-subtle, #6c757d)}@media(max-width:768px){.kpi-value{font-size:1.75rem}.kpi-prefix,.kpi-suffix{font-size:1rem}}\n"] }]
|
|
4273
4293
|
}], propDecorators: { config: [{
|
|
4274
4294
|
type: Input
|
|
4275
4295
|
}] } });
|
|
@@ -4296,6 +4316,7 @@ class KpiConfigDialogComponent {
|
|
|
4296
4316
|
initialPrefix;
|
|
4297
4317
|
initialSuffix;
|
|
4298
4318
|
initialTrend;
|
|
4319
|
+
initialComparisonText;
|
|
4299
4320
|
// Initial values for editing - Persistent Query
|
|
4300
4321
|
initialDataSourceType;
|
|
4301
4322
|
initialQueryRtId;
|
|
@@ -4338,6 +4359,7 @@ class KpiConfigDialogComponent {
|
|
|
4338
4359
|
prefix: '',
|
|
4339
4360
|
suffix: '',
|
|
4340
4361
|
trend: undefined,
|
|
4362
|
+
comparisonText: '',
|
|
4341
4363
|
// Query-specific form fields
|
|
4342
4364
|
queryValueField: '',
|
|
4343
4365
|
queryCategoryField: '',
|
|
@@ -4395,6 +4417,7 @@ class KpiConfigDialogComponent {
|
|
|
4395
4417
|
this.form.prefix = this.initialPrefix || '';
|
|
4396
4418
|
this.form.suffix = this.initialSuffix || '';
|
|
4397
4419
|
this.form.trend = this.initialTrend;
|
|
4420
|
+
this.form.comparisonText = this.initialComparisonText || '';
|
|
4398
4421
|
this.form.queryValueField = this.initialQueryValueField || '';
|
|
4399
4422
|
this.form.queryCategoryField = this.initialQueryCategoryField || '';
|
|
4400
4423
|
this.form.queryCategoryValue = this.initialQueryCategoryValue || '';
|
|
@@ -4557,7 +4580,8 @@ class KpiConfigDialogComponent {
|
|
|
4557
4580
|
staticValue: this.form.staticValue,
|
|
4558
4581
|
prefix: this.form.prefix || undefined,
|
|
4559
4582
|
suffix: this.form.suffix || undefined,
|
|
4560
|
-
trend: this.form.trend
|
|
4583
|
+
trend: this.form.trend,
|
|
4584
|
+
comparisonText: this.form.comparisonText || undefined
|
|
4561
4585
|
});
|
|
4562
4586
|
return;
|
|
4563
4587
|
}
|
|
@@ -4576,6 +4600,7 @@ class KpiConfigDialogComponent {
|
|
|
4576
4600
|
prefix: this.form.prefix || undefined,
|
|
4577
4601
|
suffix: this.form.suffix || undefined,
|
|
4578
4602
|
trend: this.form.trend,
|
|
4603
|
+
comparisonText: this.form.comparisonText || undefined,
|
|
4579
4604
|
filters: filtersDto
|
|
4580
4605
|
});
|
|
4581
4606
|
}
|
|
@@ -4589,6 +4614,7 @@ class KpiConfigDialogComponent {
|
|
|
4589
4614
|
prefix: this.form.prefix || undefined,
|
|
4590
4615
|
suffix: this.form.suffix || undefined,
|
|
4591
4616
|
trend: this.form.trend,
|
|
4617
|
+
comparisonText: this.form.comparisonText || undefined,
|
|
4592
4618
|
filters: filtersDto
|
|
4593
4619
|
});
|
|
4594
4620
|
}
|
|
@@ -4734,7 +4760,7 @@ class KpiConfigDialogComponent {
|
|
|
4734
4760
|
this.filters = updatedFilters;
|
|
4735
4761
|
}
|
|
4736
4762
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: KpiConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4737
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: KpiConfigDialogComponent, isStandalone: true, selector: "mm-kpi-config-dialog", inputs: { initialCkTypeId: "initialCkTypeId", initialRtId: "initialRtId", initialValueAttribute: "initialValueAttribute", initialLabelAttribute: "initialLabelAttribute", initialPrefix: "initialPrefix", initialSuffix: "initialSuffix", initialTrend: "initialTrend", initialDataSourceType: "initialDataSourceType", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryMode: "initialQueryMode", initialQueryValueField: "initialQueryValueField", initialQueryCategoryField: "initialQueryCategoryField", initialQueryCategoryValue: "initialQueryCategoryValue", initialStaticValue: "initialStaticValue", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "ckTypeSelectorInput", first: true, predicate: ["ckTypeSelector"], descendants: true }, { propertyName: "entitySelectorInput", first: true, predicate: ["entitySelector"], descendants: true }, { propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
|
|
4763
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: KpiConfigDialogComponent, isStandalone: true, selector: "mm-kpi-config-dialog", inputs: { initialCkTypeId: "initialCkTypeId", initialRtId: "initialRtId", initialValueAttribute: "initialValueAttribute", initialLabelAttribute: "initialLabelAttribute", initialPrefix: "initialPrefix", initialSuffix: "initialSuffix", initialTrend: "initialTrend", initialComparisonText: "initialComparisonText", initialDataSourceType: "initialDataSourceType", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryMode: "initialQueryMode", initialQueryValueField: "initialQueryValueField", initialQueryCategoryField: "initialQueryCategoryField", initialQueryCategoryValue: "initialQueryCategoryValue", initialStaticValue: "initialStaticValue", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "ckTypeSelectorInput", first: true, predicate: ["ckTypeSelector"], descendants: true }, { propertyName: "entitySelectorInput", first: true, predicate: ["entitySelector"], descendants: true }, { propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
|
|
4738
4764
|
<div class="config-container">
|
|
4739
4765
|
|
|
4740
4766
|
<div class="config-form" [class.loading]="isLoadingInitial">
|
|
@@ -5070,6 +5096,11 @@ class KpiConfigDialogComponent {
|
|
|
5070
5096
|
[(ngModel)]="form.trend">
|
|
5071
5097
|
</kendo-dropdownlist>
|
|
5072
5098
|
</div>
|
|
5099
|
+
<div class="form-field">
|
|
5100
|
+
<label>Comparison Text</label>
|
|
5101
|
+
<kendo-textbox [(ngModel)]="form.comparisonText" placeholder="e.g. +3,1% vs. last week"></kendo-textbox>
|
|
5102
|
+
<p class="section-hint">Displayed below the value in the trend color. Supports {{ variableSyntaxHint }}</p>
|
|
5103
|
+
</div>
|
|
5073
5104
|
</div>
|
|
5074
5105
|
|
|
5075
5106
|
<!-- Filters Section -->
|
|
@@ -5450,6 +5481,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
5450
5481
|
[(ngModel)]="form.trend">
|
|
5451
5482
|
</kendo-dropdownlist>
|
|
5452
5483
|
</div>
|
|
5484
|
+
<div class="form-field">
|
|
5485
|
+
<label>Comparison Text</label>
|
|
5486
|
+
<kendo-textbox [(ngModel)]="form.comparisonText" placeholder="e.g. +3,1% vs. last week"></kendo-textbox>
|
|
5487
|
+
<p class="section-hint">Displayed below the value in the trend color. Supports {{ variableSyntaxHint }}</p>
|
|
5488
|
+
</div>
|
|
5453
5489
|
</div>
|
|
5454
5490
|
|
|
5455
5491
|
<!-- Filters Section -->
|
|
@@ -5503,6 +5539,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
5503
5539
|
type: Input
|
|
5504
5540
|
}], initialTrend: [{
|
|
5505
5541
|
type: Input
|
|
5542
|
+
}], initialComparisonText: [{
|
|
5543
|
+
type: Input
|
|
5506
5544
|
}], initialDataSourceType: [{
|
|
5507
5545
|
type: Input
|
|
5508
5546
|
}], initialQueryRtId: [{
|
|
@@ -5688,7 +5726,7 @@ class EntityDetailDialogComponent {
|
|
|
5688
5726
|
<button kendoButton (click)="onClose()">Close</button>
|
|
5689
5727
|
</kendo-dialog-actions>
|
|
5690
5728
|
</kendo-dialog>
|
|
5691
|
-
`, isInline: true, styles: [".entity-detail-content{display:flex;flex-direction:column;gap:16px;min-height:400px}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:200px;color:var(--kendo-color-subtle, #6c757d)}.error-message{color:var(--kendo-color-error, #dc3545)}.entity-header{padding:12px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.entity-info{display:flex;flex-direction:column;gap:6px}.info-row{display:flex;gap:8px;font-size:.875rem}.info-row .label{font-weight:600;color:var(--kendo-color-subtle, #6c757d);min-width:80px}.info-row .value{color:var(--kendo-color-on-surface, #212529);word-break:break-all}.attributes-section{flex:1;display:flex;flex-direction:column}.attributes-section h4{margin:0 0 8px;font-size:.9rem;color:var(--kendo-color-primary, #0d6efd)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: DialogsModule }, { kind: "component", type: i1$
|
|
5729
|
+
`, isInline: true, styles: [".entity-detail-content{display:flex;flex-direction:column;gap:16px;min-height:400px}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:200px;color:var(--kendo-color-subtle, #6c757d)}.error-message{color:var(--kendo-color-error, #dc3545)}.entity-header{padding:12px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.entity-info{display:flex;flex-direction:column;gap:6px}.info-row{display:flex;gap:8px;font-size:.875rem}.info-row .label{font-weight:600;color:var(--kendo-color-subtle, #6c757d);min-width:80px}.info-row .value{color:var(--kendo-color-on-surface, #212529);word-break:break-all}.attributes-section{flex:1;display:flex;flex-direction:column}.attributes-section h4{margin:0 0 8px;font-size:.9rem;color:var(--kendo-color-primary, #0d6efd)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: DialogsModule }, { kind: "component", type: i1$5.DialogComponent, selector: "kendo-dialog", inputs: ["actions", "actionsLayout", "autoFocusedElement", "title", "width", "minWidth", "maxWidth", "height", "minHeight", "maxHeight", "animation", "themeColor"], outputs: ["action", "close"], exportAs: ["kendoDialog"] }, { kind: "component", type: i1$5.DialogActionsComponent, selector: "kendo-dialog-actions", inputs: ["actions", "layout"], outputs: ["action"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: PropertyGridComponent, selector: "mm-property-grid", inputs: ["data", "config", "showTypeColumn"], outputs: ["propertyChange", "saveRequested", "binaryDownload"] }] });
|
|
5692
5730
|
}
|
|
5693
5731
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: EntityDetailDialogComponent, decorators: [{
|
|
5694
5732
|
type: Component,
|
|
@@ -6061,7 +6099,7 @@ class EntityAssociationsWidgetComponent {
|
|
|
6061
6099
|
.trim();
|
|
6062
6100
|
}
|
|
6063
6101
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: EntityAssociationsWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6064
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: EntityAssociationsWidgetComponent, isStandalone: true, selector: "mm-entity-associations-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"associations-widget\" [class.no-data]=\"!data()\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>Loading...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else if (data()) {\n <!-- Entity Info Header -->\n <div class=\"entity-header\">\n <div class=\"entity-box\">\n <span class=\"entity-name\">{{ entityName() }}</span>\n </div>\n <div class=\"entity-details\">\n <span class=\"entity-id\" title=\"{{ entityCkTypeId() }}\">{{ entityRtId() }}</span>\n </div>\n </div>\n\n <!-- Entity Attributes (Source) -->\n @if (filteredEntityAttributes().length > 0) {\n <div class=\"entity-attributes\">\n @for (attr of filteredEntityAttributes(); track attr.attributeName) {\n <div class=\"attribute-row\">\n <span class=\"attribute-name\">{{ formatAttributeName(attr.attributeName) }}</span>\n <span class=\"attribute-value\">{{ formatValue(attr.value) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Associations List -->\n <div class=\"associations-container\">\n @for (group of groupedAssociations(); track group.roleId + group.direction) {\n <div class=\"association-group\" [class.expanded]=\"isGroupExpanded(group)\">\n <!-- Group Header -->\n <div class=\"group-header\"\n [class.clickable]=\"displayMode() === 'expandable'\"\n (click)=\"toggleGroup(group)\">\n <div class=\"direction-indicator\">\n @if (group.direction === 'out') {\n <kendo-svg-icon [icon]=\"arrowRightIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"arrowLeftIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n <div class=\"group-info\">\n <span class=\"role-name\">{{ group.roleName }}</span>\n <span class=\"target-type\">{{ group.targetType }}</span>\n <span class=\"count-badge\">{{ group.count }}</span>\n </div>\n @if (displayMode() === 'expandable') {\n <div class=\"expand-icon\">\n @if (isGroupExpanded(group)) {\n <kendo-svg-icon [icon]=\"chevronDownIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"chevronRightIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n }\n </div>\n\n <!-- Expanded Target List -->\n @if (displayMode() === 'expandable' && isGroupExpanded(group)) {\n <div class=\"target-list\">\n @if (isLoadingTargets(group)) {\n <div class=\"target-loading\">Loading attributes...</div>\n }\n @for (target of getTargetEntities(group); track target.rtId) {\n <div class=\"target-entry\">\n <div class=\"target-item\" (click)=\"onTargetClick(target)\">\n <span class=\"target-id\">{{ target.displayName }}</span>\n <span class=\"target-type-hint\" [title]=\"target.ckTypeId\">{{ target.ckTypeId }}</span>\n </div>\n @if (target.attributes?.length) {\n <div class=\"target-attributes\">\n @for (attr of target.attributes; track attr.attributeName) {\n <div class=\"target-attr-row\">\n <span class=\"target-attr-name\">{{ formatAttributeName(attr.attributeName) }}</span>\n <span class=\"target-attr-value\">{{ formatValue(attr.value) }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Summary Footer -->\n <div class=\"associations-summary\">\n <kendo-svg-icon [icon]=\"linkIcon\" size=\"small\"></kendo-svg-icon>\n <span>{{ totalAssociations() }} relationship(s)</span>\n </div>\n } @else {\n <mm-widget-not-configured></mm-widget-not-configured>\n }\n</div>\n\n<!-- Entity Detail Dialog -->\n@if (showDetailDialog) {\n <mm-entity-detail-dialog\n [rtId]=\"detailEntityRtId\"\n [ckTypeId]=\"detailEntityCkTypeId\"\n (closed)=\"onDetailDialogClosed()\">\n </mm-entity-detail-dialog>\n}\n", styles: [".associations-widget{display:flex;flex-direction:column;height:100%}.associations-widget.no-data{justify-content:center;align-items:center;color:var(--kendo-color-subtle, #6c757d)}.entity-header{display:flex;align-items:center;gap:12px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa);border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.entity-header .entity-box{display:flex;align-items:center;justify-content:center;padding:8px 16px;background:var(--kendo-color-primary, #0d6efd);color:var(--kendo-color-on-primary, #fff);border-radius:4px;font-weight:600;font-size:.875rem}.entity-header .entity-details{display:flex;flex-direction:column;gap:2px}.entity-header .entity-id{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace}.entity-attributes{padding:4px 12px 8px;border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.entity-attributes .attribute-row{display:flex;justify-content:space-between;padding:2px 0;font-size:.8125rem}.entity-attributes .attribute-name{color:var(--kendo-color-subtle, #6c757d)}.entity-attributes .attribute-value{font-weight:500;color:var(--kendo-color-on-surface, #212529)}.associations-container{flex:1;overflow:auto;padding:8px;display:flex;flex-direction:column;gap:4px}.association-group{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;overflow:hidden}.association-group.expanded .group-header{border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.group-header{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa)}.group-header.clickable{cursor:pointer}.group-header.clickable:hover{background:var(--kendo-color-surface, #ffffff)}.direction-indicator{color:var(--kendo-color-subtle, #6c757d);display:flex;align-items:center;width:20px}.group-info{display:flex;align-items:center;gap:8px;flex:1;font-size:.8125rem}.group-info .role-name{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.group-info .target-type{font-weight:500;color:var(--kendo-color-on-surface, #212529)}.group-info .count-badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;background:var(--kendo-color-primary, #0d6efd);color:var(--kendo-color-on-primary, #fff);border-radius:10px;font-size:.6875rem;font-weight:600}.expand-icon{color:var(--kendo-color-subtle, #6c757d);display:flex;align-items:center}.target-list{padding:4px 0;max-height:200px;overflow-y:auto}.target-entry+.target-entry{border-top:1px solid var(--kendo-color-border, #dee2e6)}.target-item{display:flex;align-items:center;gap:8px;padding:6px 12px 6px 40px;cursor:pointer;font-size:.8125rem;transition:background .15s ease}.target-item:hover{background:var(--kendo-color-surface-alt, #f8f9fa)}.target-item .target-id{font-family:monospace;color:var(--kendo-color-on-surface, #212529);flex-shrink:0}.target-item .target-type-hint{font-size:.6875rem;color:var(--kendo-color-subtle, #6c757d);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex-shrink:1}.target-attributes{padding:2px 12px 6px 56px}.target-attributes .target-attr-row{display:flex;justify-content:space-between;padding:1px 0;font-size:.75rem}.target-attributes .target-attr-name{color:var(--kendo-color-subtle, #6c757d)}.target-attributes .target-attr-value{color:var(--kendo-color-on-surface, #212529);font-weight:500}.target-loading{padding:6px 12px 6px 40px;font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.associations-summary{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa);border-top:1px solid var(--kendo-color-border, #dee2e6);font-size:.75rem;color:var(--kendo-color-subtle, #6c757d)}.no-data-message{font-size:.875rem}.loading-indicator{display:flex;align-items:center;justify-content:center;height:100%;color:var(--kendo-color-subtle, #6c757d)}.error-message{display:flex;align-items:center;justify-content:center;height:100%;color:var(--kendo-color-error, #dc3545)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type:
|
|
6102
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: EntityAssociationsWidgetComponent, isStandalone: true, selector: "mm-entity-associations-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"associations-widget\" [class.no-data]=\"!data()\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>Loading...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else if (data()) {\n <!-- Entity Info Header -->\n <div class=\"entity-header\">\n <div class=\"entity-box\">\n <span class=\"entity-name\">{{ entityName() }}</span>\n </div>\n <div class=\"entity-details\">\n <span class=\"entity-id\" title=\"{{ entityCkTypeId() }}\">{{ entityRtId() }}</span>\n </div>\n </div>\n\n <!-- Entity Attributes (Source) -->\n @if (filteredEntityAttributes().length > 0) {\n <div class=\"entity-attributes\">\n @for (attr of filteredEntityAttributes(); track attr.attributeName) {\n <div class=\"attribute-row\">\n <span class=\"attribute-name\">{{ formatAttributeName(attr.attributeName) }}</span>\n <span class=\"attribute-value\">{{ formatValue(attr.value) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Associations List -->\n <div class=\"associations-container\">\n @for (group of groupedAssociations(); track group.roleId + group.direction) {\n <div class=\"association-group\" [class.expanded]=\"isGroupExpanded(group)\">\n <!-- Group Header -->\n <div class=\"group-header\"\n [class.clickable]=\"displayMode() === 'expandable'\"\n (click)=\"toggleGroup(group)\">\n <div class=\"direction-indicator\">\n @if (group.direction === 'out') {\n <kendo-svg-icon [icon]=\"arrowRightIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"arrowLeftIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n <div class=\"group-info\">\n <span class=\"role-name\">{{ group.roleName }}</span>\n <span class=\"target-type\">{{ group.targetType }}</span>\n <span class=\"count-badge\">{{ group.count }}</span>\n </div>\n @if (displayMode() === 'expandable') {\n <div class=\"expand-icon\">\n @if (isGroupExpanded(group)) {\n <kendo-svg-icon [icon]=\"chevronDownIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"chevronRightIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n }\n </div>\n\n <!-- Expanded Target List -->\n @if (displayMode() === 'expandable' && isGroupExpanded(group)) {\n <div class=\"target-list\">\n @if (isLoadingTargets(group)) {\n <div class=\"target-loading\">Loading attributes...</div>\n }\n @for (target of getTargetEntities(group); track target.rtId) {\n <div class=\"target-entry\">\n <div class=\"target-item\" (click)=\"onTargetClick(target)\">\n <span class=\"target-id\">{{ target.displayName }}</span>\n <span class=\"target-type-hint\" [title]=\"target.ckTypeId\">{{ target.ckTypeId }}</span>\n </div>\n @if (target.attributes?.length) {\n <div class=\"target-attributes\">\n @for (attr of target.attributes; track attr.attributeName) {\n <div class=\"target-attr-row\">\n <span class=\"target-attr-name\">{{ formatAttributeName(attr.attributeName) }}</span>\n <span class=\"target-attr-value\">{{ formatValue(attr.value) }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Summary Footer -->\n <div class=\"associations-summary\">\n <kendo-svg-icon [icon]=\"linkIcon\" size=\"small\"></kendo-svg-icon>\n <span>{{ totalAssociations() }} relationship(s)</span>\n </div>\n } @else {\n <mm-widget-not-configured></mm-widget-not-configured>\n }\n</div>\n\n<!-- Entity Detail Dialog -->\n@if (showDetailDialog) {\n <mm-entity-detail-dialog\n [rtId]=\"detailEntityRtId\"\n [ckTypeId]=\"detailEntityCkTypeId\"\n (closed)=\"onDetailDialogClosed()\">\n </mm-entity-detail-dialog>\n}\n", styles: [".associations-widget{display:flex;flex-direction:column;height:100%}.associations-widget.no-data{justify-content:center;align-items:center;color:var(--kendo-color-subtle, #6c757d)}.entity-header{display:flex;align-items:center;gap:12px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa);border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.entity-header .entity-box{display:flex;align-items:center;justify-content:center;padding:8px 16px;background:var(--kendo-color-primary, #0d6efd);color:var(--kendo-color-on-primary, #fff);border-radius:4px;font-weight:600;font-size:.875rem}.entity-header .entity-details{display:flex;flex-direction:column;gap:2px}.entity-header .entity-id{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace}.entity-attributes{padding:4px 12px 8px;border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.entity-attributes .attribute-row{display:flex;justify-content:space-between;padding:2px 0;font-size:.8125rem}.entity-attributes .attribute-name{color:var(--kendo-color-subtle, #6c757d)}.entity-attributes .attribute-value{font-weight:500;color:var(--kendo-color-on-surface, #212529)}.associations-container{flex:1;overflow:auto;padding:8px;display:flex;flex-direction:column;gap:4px}.association-group{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;overflow:hidden}.association-group.expanded .group-header{border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.group-header{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa)}.group-header.clickable{cursor:pointer}.group-header.clickable:hover{background:var(--kendo-color-surface, #ffffff)}.direction-indicator{color:var(--kendo-color-subtle, #6c757d);display:flex;align-items:center;width:20px}.group-info{display:flex;align-items:center;gap:8px;flex:1;font-size:.8125rem}.group-info .role-name{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.group-info .target-type{font-weight:500;color:var(--kendo-color-on-surface, #212529)}.group-info .count-badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;background:var(--kendo-color-primary, #0d6efd);color:var(--kendo-color-on-primary, #fff);border-radius:10px;font-size:.6875rem;font-weight:600}.expand-icon{color:var(--kendo-color-subtle, #6c757d);display:flex;align-items:center}.target-list{padding:4px 0;max-height:200px;overflow-y:auto}.target-entry+.target-entry{border-top:1px solid var(--kendo-color-border, #dee2e6)}.target-item{display:flex;align-items:center;gap:8px;padding:6px 12px 6px 40px;cursor:pointer;font-size:.8125rem;transition:background .15s ease}.target-item:hover{background:var(--kendo-color-surface-alt, #f8f9fa)}.target-item .target-id{font-family:monospace;color:var(--kendo-color-on-surface, #212529);flex-shrink:0}.target-item .target-type-hint{font-size:.6875rem;color:var(--kendo-color-subtle, #6c757d);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex-shrink:1}.target-attributes{padding:2px 12px 6px 56px}.target-attributes .target-attr-row{display:flex;justify-content:space-between;padding:1px 0;font-size:.75rem}.target-attributes .target-attr-name{color:var(--kendo-color-subtle, #6c757d)}.target-attributes .target-attr-value{color:var(--kendo-color-on-surface, #212529);font-weight:500}.target-loading{padding:6px 12px 6px 40px;font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.associations-summary{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa);border-top:1px solid var(--kendo-color-border, #dee2e6);font-size:.75rem;color:var(--kendo-color-subtle, #6c757d)}.no-data-message{font-size:.875rem}.loading-indicator{display:flex;align-items:center;justify-content:center;height:100%;color:var(--kendo-color-subtle, #6c757d)}.error-message{display:flex;align-items:center;justify-content:center;height:100%;color:var(--kendo-color-error, #dc3545)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: EntityDetailDialogComponent, selector: "mm-entity-detail-dialog", inputs: ["rtId", "ckTypeId"], outputs: ["closed"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
6065
6103
|
}
|
|
6066
6104
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: EntityAssociationsWidgetComponent, decorators: [{
|
|
6067
6105
|
type: Component,
|
|
@@ -7205,6 +7243,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
7205
7243
|
type: Input
|
|
7206
7244
|
}] } });
|
|
7207
7245
|
|
|
7246
|
+
const ICON_MAP = {
|
|
7247
|
+
'check-circle': checkCircleIcon,
|
|
7248
|
+
'x-circle': xCircleIcon,
|
|
7249
|
+
'exclamation-circle': exclamationCircleIcon,
|
|
7250
|
+
'warning-triangle': warningTriangleIcon,
|
|
7251
|
+
'minus-circle': minusCircleIcon,
|
|
7252
|
+
'question-circle': questionCircleIcon,
|
|
7253
|
+
'circle': circleIcon,
|
|
7254
|
+
};
|
|
7255
|
+
function resolveStatusMapping(config) {
|
|
7256
|
+
return Object.fromEntries(Object.entries(config).map(([key, val]) => [
|
|
7257
|
+
key,
|
|
7258
|
+
{ icon: ICON_MAP[val.icon] ?? questionCircleIcon, tooltip: val.tooltip, color: val.color }
|
|
7259
|
+
]));
|
|
7260
|
+
}
|
|
7208
7261
|
class TableWidgetComponent {
|
|
7209
7262
|
config;
|
|
7210
7263
|
dataSource;
|
|
@@ -7235,25 +7288,25 @@ class TableWidgetComponent {
|
|
|
7235
7288
|
* For persistent queries, uses dynamically derived columns from the query response.
|
|
7236
7289
|
*/
|
|
7237
7290
|
listViewColumns = computed(() => {
|
|
7238
|
-
//
|
|
7291
|
+
// If explicit columns are configured, use them (works for both data source types)
|
|
7292
|
+
if (this.config?.columns && this.config.columns.length > 0) {
|
|
7293
|
+
return this.config.columns.map(col => ({
|
|
7294
|
+
field: col.field,
|
|
7295
|
+
displayName: col.title,
|
|
7296
|
+
dataType: (col.dataType ?? 'text'),
|
|
7297
|
+
width: col.width,
|
|
7298
|
+
...(col.statusMapping ? { statusMapping: resolveStatusMapping(col.statusMapping) } : {})
|
|
7299
|
+
}));
|
|
7300
|
+
}
|
|
7301
|
+
// For persistent query without explicit columns, use derived columns
|
|
7239
7302
|
if (this.config?.dataSource?.type === 'persistentQuery') {
|
|
7240
|
-
// Return query-derived columns if available
|
|
7241
7303
|
const queryColumns = this._queryColumnsForView();
|
|
7242
7304
|
if (queryColumns.length > 0) {
|
|
7243
7305
|
return queryColumns;
|
|
7244
7306
|
}
|
|
7245
|
-
// Return empty array while waiting for first data load
|
|
7246
7307
|
return [];
|
|
7247
7308
|
}
|
|
7248
|
-
|
|
7249
|
-
if (!this.config?.columns)
|
|
7250
|
-
return [];
|
|
7251
|
-
return this.config.columns.map(col => ({
|
|
7252
|
-
field: col.field,
|
|
7253
|
-
displayName: col.title,
|
|
7254
|
-
dataType: 'text',
|
|
7255
|
-
width: col.width
|
|
7256
|
-
}));
|
|
7309
|
+
return [];
|
|
7257
7310
|
}, ...(ngDevMode ? [{ debugName: "listViewColumns" }] : /* istanbul ignore next */ []));
|
|
7258
7311
|
/**
|
|
7259
7312
|
* Checks if the widget has a valid configuration.
|
|
@@ -7847,7 +7900,7 @@ class TableConfigDialogComponent {
|
|
|
7847
7900
|
</button>
|
|
7848
7901
|
</div>
|
|
7849
7902
|
</div>
|
|
7850
|
-
`, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:16px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.form-field{display:flex;flex-direction:column;gap:6px}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.config-card{padding:12px 16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.card-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.card-header kendo-svgicon{color:var(--kendo-color-primary, #0d6efd)}.card-title{font-weight:600;font-size:.95rem;color:var(--kendo-color-primary, #0d6efd)}.card-count{color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.card-content{display:flex;align-items:center;justify-content:space-between;gap:16px}.config-summary{margin:0;font-size:.85rem;color:var(--kendo-color-on-app-surface, #212529);flex:1}.filters-card .card-content{flex-direction:column;align-items:stretch}.filter-content{width:100%}.options-card .options-content{display:flex;gap:24px;align-items:flex-end}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;color:var(--kendo-color-on-app-surface, #212529)}.data-source-type .radio-group{display:flex;gap:24px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400;color:var(--kendo-color-on-app-surface, #212529)}.radio-label span{color:var(--kendo-color-on-app-surface, #212529)}.query-info{flex-direction:column;align-items:stretch;gap:8px}.info-row{display:flex;gap:8px}.info-label{font-weight:600;min-width:100px;color:var(--kendo-color-subtle, #6c757d)}.info-value{flex:1;color:var(--kendo-color-on-app-surface, #212529)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i3.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: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: NumericTextBoxModule }, { kind: "ngmodule", type: DropDownsModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type:
|
|
7903
|
+
`, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:16px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.form-field{display:flex;flex-direction:column;gap:6px}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.config-card{padding:12px 16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.card-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.card-header kendo-svgicon{color:var(--kendo-color-primary, #0d6efd)}.card-title{font-weight:600;font-size:.95rem;color:var(--kendo-color-primary, #0d6efd)}.card-count{color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.card-content{display:flex;align-items:center;justify-content:space-between;gap:16px}.config-summary{margin:0;font-size:.85rem;color:var(--kendo-color-on-app-surface, #212529);flex:1}.filters-card .card-content{flex-direction:column;align-items:stretch}.filter-content{width:100%}.options-card .options-content{display:flex;gap:24px;align-items:flex-end}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;color:var(--kendo-color-on-app-surface, #212529)}.data-source-type .radio-group{display:flex;gap:24px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400;color:var(--kendo-color-on-app-surface, #212529)}.radio-label span{color:var(--kendo-color-on-app-surface, #212529)}.query-info{flex-direction:column;align-items:stretch;gap:8px}.info-row{display:flex;gap:8px}.info-label{font-weight:600;min-width:100px;color:var(--kendo-color-subtle, #6c757d)}.info-value{flex:1;color:var(--kendo-color-on-app-surface, #212529)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i3.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: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: NumericTextBoxModule }, { kind: "ngmodule", type: DropDownsModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: CkTypeSelectorInputComponent, selector: "mm-ck-type-selector-input", inputs: ["placeholder", "minSearchLength", "maxResults", "debounceMs", "ckModelIds", "allowAbstract", "dialogTitle", "advancedSearchLabel", "derivedFromRtCkTypeId", "disabled", "required"], outputs: ["ckTypeSelected", "ckTypeCleared"] }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }] });
|
|
7851
7904
|
}
|
|
7852
7905
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: TableConfigDialogComponent, decorators: [{
|
|
7853
7906
|
type: Component,
|
|
@@ -8521,7 +8574,7 @@ class GaugeWidgetComponent {
|
|
|
8521
8574
|
</div>
|
|
8522
8575
|
}
|
|
8523
8576
|
</div>
|
|
8524
|
-
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.gauge-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.gauge-widget.loading,.gauge-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.gauge-container{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%}.gauge-center-label{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.gauge-center-label .value-text{font-size:1.25rem;font-weight:600;color:var(--kendo-color-on-surface,
|
|
8577
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.gauge-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.gauge-widget.loading,.gauge-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.gauge-container{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%}.gauge-center-label{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.gauge-center-label .value-text{font-size:1.25rem;font-weight:600;color:var(--kendo-color-on-surface, inherit)}.gauge-center-label .label-text{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);margin-top:2px}.gauge-value-label{display:flex;flex-direction:column;align-items:center;margin-top:8px;font-size:1.1rem;font-weight:600;color:var(--kendo-color-on-surface, inherit)}.gauge-value-label .label-text{font-size:.75rem;font-weight:400;color:var(--kendo-color-subtle, #6c757d);margin-top:2px}kendo-arcgauge,kendo-circulargauge,kendo-radialgauge{width:100%;max-width:200px;height:auto}.linear-gauge-wrapper{display:flex;flex-direction:row;align-items:center;justify-content:center;height:100%;width:100%;gap:8px}.linear-gauge-wrapper .gauge-value-label{margin-top:0;writing-mode:horizontal-tb}kendo-lineargauge{height:100%;max-height:180px;width:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: i1$6.ArcGaugeComponent, selector: "kendo-arcgauge", inputs: ["value", "color", "colors", "opacity", "scale"], exportAs: ["kendoArcGauge"] }, { kind: "directive", type: i1$6.ArcCenterTemplateDirective, selector: "[kendoArcGaugeCenterTemplate]" }, { kind: "component", type: i1$6.ArcScaleComponent, selector: "kendo-arcgauge-scale", inputs: ["labels", "rangeDistance", "rangeLineCap", "startAngle", "endAngle"] }, { kind: "component", type: i1$6.ColorsComponent, selector: "kendo-arcgauge-colors" }, { kind: "component", type: i1$6.ColorComponent, selector: "kendo-arcgauge-color", inputs: ["color", "opacity", "from", "to"] }, { kind: "component", type: i1$6.CircularGaugeComponent, selector: "kendo-circulargauge", inputs: ["scale"], exportAs: ["kendoCircularGauge"] }, { kind: "directive", type: i1$6.CircularGaugeCenterTemplateDirective, selector: "[kendoCircularGaugeCenterTemplate]" }, { kind: "component", type: i1$6.CircularGaugeScaleComponent, selector: "kendo-circulargauge-scale" }, { kind: "component", type: i1$6.LinearGaugeComponent, selector: "kendo-lineargauge", inputs: ["pointer", "scale"], exportAs: ["kendoLinearGauge"] }, { kind: "component", type: i1$6.LinearScaleComponent, selector: "kendo-lineargauge-scale", inputs: ["line", "ranges", "mirror", "vertical"] }, { kind: "component", type: i1$6.LinearLabelsComponent, selector: "kendo-lineargauge-scale-labels" }, { kind: "component", type: i1$6.LinearPointersComponent, selector: "kendo-lineargauge-pointers" }, { kind: "component", type: i1$6.LinearPointerComponent, selector: "kendo-lineargauge-pointer", inputs: ["border", "color", "margin", "opacity", "shape", "size", "value"] }, { kind: "component", type: i1$6.LinearRangeComponent, selector: "kendo-lineargauge-scale-range" }, { kind: "component", type: i1$6.LinearRangesComponent, selector: "kendo-lineargauge-scale-ranges" }, { kind: "component", type: i1$6.RadialGaugeComponent, selector: "kendo-radialgauge", inputs: ["pointer", "scale"], exportAs: ["kendoRadialGauge"] }, { kind: "component", type: i1$6.RadialScaleComponent, selector: "kendo-radialgauge-scale", inputs: ["labels", "rangeDistance", "ranges", "startAngle", "endAngle"] }, { kind: "component", type: i1$6.RadialPointerComponent, selector: "kendo-radialgauge-pointer", inputs: ["cap", "color", "length", "value"] }, { kind: "component", type: i1$6.RadialRangeComponent, selector: "kendo-radialgauge-scale-range" }, { kind: "component", type: i1$6.RadialRangesComponent, selector: "kendo-radialgauge-scale-ranges" }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
8525
8578
|
}
|
|
8526
8579
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: GaugeWidgetComponent, decorators: [{
|
|
8527
8580
|
type: Component,
|
|
@@ -8698,7 +8751,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
8698
8751
|
</div>
|
|
8699
8752
|
}
|
|
8700
8753
|
</div>
|
|
8701
|
-
`, styles: [":host{display:block;width:100%;height:100%}.gauge-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.gauge-widget.loading,.gauge-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.gauge-container{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%}.gauge-center-label{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.gauge-center-label .value-text{font-size:1.25rem;font-weight:600;color:var(--kendo-color-on-surface,
|
|
8754
|
+
`, styles: [":host{display:block;width:100%;height:100%}.gauge-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.gauge-widget.loading,.gauge-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.gauge-container{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%}.gauge-center-label{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.gauge-center-label .value-text{font-size:1.25rem;font-weight:600;color:var(--kendo-color-on-surface, inherit)}.gauge-center-label .label-text{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);margin-top:2px}.gauge-value-label{display:flex;flex-direction:column;align-items:center;margin-top:8px;font-size:1.1rem;font-weight:600;color:var(--kendo-color-on-surface, inherit)}.gauge-value-label .label-text{font-size:.75rem;font-weight:400;color:var(--kendo-color-subtle, #6c757d);margin-top:2px}kendo-arcgauge,kendo-circulargauge,kendo-radialgauge{width:100%;max-width:200px;height:auto}.linear-gauge-wrapper{display:flex;flex-direction:row;align-items:center;justify-content:center;height:100%;width:100%;gap:8px}.linear-gauge-wrapper .gauge-value-label{margin-top:0;writing-mode:horizontal-tb}kendo-lineargauge{height:100%;max-height:180px;width:auto}\n"] }]
|
|
8702
8755
|
}], propDecorators: { config: [{
|
|
8703
8756
|
type: Input
|
|
8704
8757
|
}] } });
|
|
@@ -10241,8 +10294,8 @@ class PieChartWidgetComponent {
|
|
|
10241
10294
|
<span>{{ error() }}</span>
|
|
10242
10295
|
</div>
|
|
10243
10296
|
} @else {
|
|
10244
|
-
<kendo-chart class="chart-container">
|
|
10245
|
-
<kendo-chart-
|
|
10297
|
+
<kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: plotAreaMargin }">
|
|
10298
|
+
<kendo-chart-area [background]="'transparent'"></kendo-chart-area>
|
|
10246
10299
|
<kendo-chart-series>
|
|
10247
10300
|
<kendo-chart-series-item
|
|
10248
10301
|
[type]="config.chartType"
|
|
@@ -10266,7 +10319,7 @@ class PieChartWidgetComponent {
|
|
|
10266
10319
|
</kendo-chart>
|
|
10267
10320
|
}
|
|
10268
10321
|
</div>
|
|
10269
|
-
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.pie-chart-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.pie-chart-widget.loading,.pie-chart-widget.error{opacity:.7}.loading-indicator,.error-message,.no-config-overlay{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.no-config-overlay span{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$
|
|
10322
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.pie-chart-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.pie-chart-widget.loading,.pie-chart-widget.error{opacity:.7}.loading-indicator,.error-message,.no-config-overlay{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.no-config-overlay span{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$7.ChartComponent, selector: "kendo-chart", inputs: ["pannable", "renderAs", "seriesColors", "subtitle", "title", "noData", "observeStyles", "transitions", "zoomable", "axisDefaults", "categoryAxis", "chartArea", "legend", "panes", "paneDefaults", "plotArea", "series", "seriesDefaults", "tooltip", "valueAxis", "xAxis", "yAxis", "resizeRateLimit", "popupSettings", "drilldownLevel"], outputs: ["axisLabelClick", "drag", "dragEnd", "dragStart", "legendItemHover", "legendItemLeave", "noteClick", "noteHover", "noteLeave", "paneRender", "plotAreaClick", "plotAreaHover", "plotAreaLeave", "render", "select", "selectEnd", "selectStart", "seriesClick", "drilldown", "seriesHover", "seriesOver", "seriesLeave", "zoom", "zoomEnd", "zoomStart", "legendItemClick", "drilldownLevelChange"], exportAs: ["kendoChart"] }, { kind: "directive", type: i1$7.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$7.ChartAreaComponent, selector: "kendo-chart-area", inputs: ["background", "border", "height", "margin", "opacity", "width"] }, { kind: "component", type: i1$7.LegendComponent, selector: "kendo-chart-legend", inputs: ["align", "background", "border", "height", "labels", "margin", "offsetX", "offsetY", "orientation", "padding", "position", "reverse", "visible", "width", "markers", "spacing", "inactiveItems", "item", "title", "focusHighlight"] }, { kind: "component", type: i1$7.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$7.SeriesItemComponent, selector: "kendo-chart-series-item", inputs: ["aggregate", "autoFit", "axis", "border", "categoryAxis", "categoryField", "closeField", "color", "colorField", "connectors", "currentField", "dashType", "data", "downColor", "downColorField", "drilldownField", "dynamicHeight", "dynamicSlope", "errorHighField", "errorLowField", "explodeField", "field", "fromField", "gap", "highField", "holeSize", "line", "lowField", "lowerField", "margin", "maxSize", "mean", "meanField", "median", "medianField", "minSize", "missingValues", "name", "neckRatio", "negativeColor", "negativeValues", "noteTextField", "opacity", "openField", "outliersField", "overlay", "padding", "q1Field", "q3Field", "segmentSpacing", "size", "sizeField", "spacing", "stack", "startAngle", "style", "summaryField", "target", "toField", "type", "upperField", "visible", "visibleInLegend", "visibleInLegendField", "visual", "width", "whiskers", "xAxis", "xErrorHighField", "xErrorLowField", "xField", "yAxis", "yErrorHighField", "yErrorLowField", "yField", "zIndex", "trendline", "for", "legendItem", "pattern", "patternField", "errorBars", "extremes", "highlight", "labels", "markers", "notes", "outliers", "tooltip"] }, { kind: "component", type: i1$7.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
10270
10323
|
}
|
|
10271
10324
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: PieChartWidgetComponent, decorators: [{
|
|
10272
10325
|
type: Component,
|
|
@@ -10287,8 +10340,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
10287
10340
|
<span>{{ error() }}</span>
|
|
10288
10341
|
</div>
|
|
10289
10342
|
} @else {
|
|
10290
|
-
<kendo-chart class="chart-container">
|
|
10291
|
-
<kendo-chart-
|
|
10343
|
+
<kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: plotAreaMargin }">
|
|
10344
|
+
<kendo-chart-area [background]="'transparent'"></kendo-chart-area>
|
|
10292
10345
|
<kendo-chart-series>
|
|
10293
10346
|
<kendo-chart-series-item
|
|
10294
10347
|
[type]="config.chartType"
|
|
@@ -11195,7 +11248,7 @@ class BarChartWidgetComponent {
|
|
|
11195
11248
|
continue;
|
|
11196
11249
|
const sanitizedPath = this.sanitizeFieldName(cell.attributePath);
|
|
11197
11250
|
if (sanitizedPath === this.sanitizeFieldName(this.config.categoryField)) {
|
|
11198
|
-
categoryValue =
|
|
11251
|
+
categoryValue = this.formatCategoryValue(cell.value);
|
|
11199
11252
|
}
|
|
11200
11253
|
// Check if this cell is one of our series fields
|
|
11201
11254
|
for (const seriesConfig of (this.config.series ?? [])) {
|
|
@@ -11215,11 +11268,14 @@ class BarChartWidgetComponent {
|
|
|
11215
11268
|
}
|
|
11216
11269
|
}
|
|
11217
11270
|
// Convert to series data array
|
|
11218
|
-
const seriesData = (this.config.series ?? []).map(seriesConfig =>
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11271
|
+
const seriesData = (this.config.series ?? []).map(seriesConfig => {
|
|
11272
|
+
const rawData = seriesMap.get(seriesConfig.field) ?? [];
|
|
11273
|
+
return {
|
|
11274
|
+
name: seriesConfig.name ?? seriesConfig.field,
|
|
11275
|
+
data: this.hasColorThresholds() ? this.applyColorThresholds(rawData) : rawData,
|
|
11276
|
+
color: seriesConfig.color
|
|
11277
|
+
};
|
|
11278
|
+
});
|
|
11223
11279
|
this._categories.set(categories);
|
|
11224
11280
|
this._seriesData.set(seriesData);
|
|
11225
11281
|
}
|
|
@@ -11257,7 +11313,7 @@ class BarChartWidgetComponent {
|
|
|
11257
11313
|
continue;
|
|
11258
11314
|
const sanitizedPath = this.sanitizeFieldName(cell.attributePath);
|
|
11259
11315
|
if (sanitizedPath === this.sanitizeFieldName(categoryField)) {
|
|
11260
|
-
categoryValue =
|
|
11316
|
+
categoryValue = this.formatCategoryValue(cell.value);
|
|
11261
11317
|
}
|
|
11262
11318
|
else if (sanitizedPath === this.sanitizeFieldName(seriesGroupField)) {
|
|
11263
11319
|
seriesGroupValue = String(cell.value ?? '');
|
|
@@ -11283,12 +11339,12 @@ class BarChartWidgetComponent {
|
|
|
11283
11339
|
const seriesGroups = Array.from(allSeriesGroups);
|
|
11284
11340
|
// Build series data
|
|
11285
11341
|
const seriesData = seriesGroups.map(seriesGroup => {
|
|
11286
|
-
const
|
|
11342
|
+
const rawData = categories.map(category => {
|
|
11287
11343
|
return dataMap.get(category)?.get(seriesGroup) ?? 0;
|
|
11288
11344
|
});
|
|
11289
11345
|
return {
|
|
11290
11346
|
name: seriesGroup,
|
|
11291
|
-
data
|
|
11347
|
+
data: this.hasColorThresholds() ? this.applyColorThresholds(rawData) : rawData
|
|
11292
11348
|
};
|
|
11293
11349
|
});
|
|
11294
11350
|
this._categories.set(categories);
|
|
@@ -11298,6 +11354,42 @@ class BarChartWidgetComponent {
|
|
|
11298
11354
|
* Sanitizes field names for comparison.
|
|
11299
11355
|
* Replaces dots with underscores (same as table widget).
|
|
11300
11356
|
*/
|
|
11357
|
+
dataLabelFormat = computed(() => {
|
|
11358
|
+
const suffix = this.config?.dataLabelSuffix ?? '';
|
|
11359
|
+
return `{0:n0}${suffix}`;
|
|
11360
|
+
}, ...(ngDevMode ? [{ debugName: "dataLabelFormat" }] : /* istanbul ignore next */ []));
|
|
11361
|
+
hasColorThresholds() {
|
|
11362
|
+
return (this.config?.colorThresholds?.length ?? 0) > 0;
|
|
11363
|
+
}
|
|
11364
|
+
applyColorThresholds(data) {
|
|
11365
|
+
const thresholds = this.config.colorThresholds ?? [];
|
|
11366
|
+
const defaultColor = this.config.defaultBarColor ?? '#6b7280';
|
|
11367
|
+
return data.map(val => ({
|
|
11368
|
+
value: val,
|
|
11369
|
+
_color: this.getColorForValue(val, thresholds, defaultColor)
|
|
11370
|
+
}));
|
|
11371
|
+
}
|
|
11372
|
+
getColorForValue(val, thresholds, defaultColor) {
|
|
11373
|
+
for (const t of thresholds) {
|
|
11374
|
+
if (val <= t.value)
|
|
11375
|
+
return t.color;
|
|
11376
|
+
}
|
|
11377
|
+
return defaultColor;
|
|
11378
|
+
}
|
|
11379
|
+
formatCategoryValue(value) {
|
|
11380
|
+
const str = String(value ?? '');
|
|
11381
|
+
// Detect ISO 8601 timestamps and format as readable date/time
|
|
11382
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(str)) {
|
|
11383
|
+
const date = new Date(str);
|
|
11384
|
+
if (!isNaN(date.getTime())) {
|
|
11385
|
+
return date.toLocaleString('de-AT', {
|
|
11386
|
+
day: '2-digit', month: '2-digit',
|
|
11387
|
+
hour: '2-digit', minute: '2-digit'
|
|
11388
|
+
});
|
|
11389
|
+
}
|
|
11390
|
+
}
|
|
11391
|
+
return str;
|
|
11392
|
+
}
|
|
11301
11393
|
sanitizeFieldName(fieldName) {
|
|
11302
11394
|
return fieldName.replace(/\./g, '_');
|
|
11303
11395
|
}
|
|
@@ -11323,10 +11415,13 @@ class BarChartWidgetComponent {
|
|
|
11323
11415
|
<span>{{ error() }}</span>
|
|
11324
11416
|
</div>
|
|
11325
11417
|
} @else {
|
|
11326
|
-
<kendo-chart class="chart-container">
|
|
11327
|
-
<kendo-chart-
|
|
11418
|
+
<kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: plotAreaMargin() }">
|
|
11419
|
+
<kendo-chart-area [background]="'transparent'"></kendo-chart-area>
|
|
11328
11420
|
<kendo-chart-category-axis>
|
|
11329
|
-
<kendo-chart-category-axis-item
|
|
11421
|
+
<kendo-chart-category-axis-item
|
|
11422
|
+
[categories]="categories()"
|
|
11423
|
+
[line]="{ visible: false }"
|
|
11424
|
+
[majorGridLines]="{ visible: false }">
|
|
11330
11425
|
<kendo-chart-category-axis-item-labels
|
|
11331
11426
|
[rotation]="labelRotation()"
|
|
11332
11427
|
[content]="categoryLabelContent">
|
|
@@ -11334,6 +11429,13 @@ class BarChartWidgetComponent {
|
|
|
11334
11429
|
</kendo-chart-category-axis-item>
|
|
11335
11430
|
</kendo-chart-category-axis>
|
|
11336
11431
|
|
|
11432
|
+
<kendo-chart-value-axis>
|
|
11433
|
+
<kendo-chart-value-axis-item
|
|
11434
|
+
[line]="{ visible: false }"
|
|
11435
|
+
[majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }">
|
|
11436
|
+
</kendo-chart-value-axis-item>
|
|
11437
|
+
</kendo-chart-value-axis>
|
|
11438
|
+
|
|
11337
11439
|
<kendo-chart-series>
|
|
11338
11440
|
@for (series of seriesData(); track series.name) {
|
|
11339
11441
|
<kendo-chart-series-item
|
|
@@ -11341,11 +11443,15 @@ class BarChartWidgetComponent {
|
|
|
11341
11443
|
[data]="series.data"
|
|
11342
11444
|
[name]="series.name"
|
|
11343
11445
|
[color]="series.color"
|
|
11446
|
+
[field]="hasColorThresholds() ? 'value' : ''"
|
|
11447
|
+
[colorField]="hasColorThresholds() ? '_color' : ''"
|
|
11448
|
+
[border]="{ width: 0 }"
|
|
11449
|
+
[gap]="0.8"
|
|
11344
11450
|
[stack]="stackConfig()">
|
|
11345
11451
|
@if (config.showDataLabels) {
|
|
11346
11452
|
<kendo-chart-series-item-labels
|
|
11347
11453
|
[visible]="true"
|
|
11348
|
-
[format]="
|
|
11454
|
+
[format]="dataLabelFormat()">
|
|
11349
11455
|
</kendo-chart-series-item-labels>
|
|
11350
11456
|
}
|
|
11351
11457
|
</kendo-chart-series-item>
|
|
@@ -11368,7 +11474,7 @@ class BarChartWidgetComponent {
|
|
|
11368
11474
|
</kendo-chart>
|
|
11369
11475
|
}
|
|
11370
11476
|
</div>
|
|
11371
|
-
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.bar-chart-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.bar-chart-widget.loading,.bar-chart-widget.error{opacity:.7}.loading-indicator,.error-message,.no-config-overlay{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.no-config-overlay span{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$
|
|
11477
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.bar-chart-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.bar-chart-widget.loading,.bar-chart-widget.error{opacity:.7}.loading-indicator,.error-message,.no-config-overlay{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.no-config-overlay span{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$7.ChartComponent, selector: "kendo-chart", inputs: ["pannable", "renderAs", "seriesColors", "subtitle", "title", "noData", "observeStyles", "transitions", "zoomable", "axisDefaults", "categoryAxis", "chartArea", "legend", "panes", "paneDefaults", "plotArea", "series", "seriesDefaults", "tooltip", "valueAxis", "xAxis", "yAxis", "resizeRateLimit", "popupSettings", "drilldownLevel"], outputs: ["axisLabelClick", "drag", "dragEnd", "dragStart", "legendItemHover", "legendItemLeave", "noteClick", "noteHover", "noteLeave", "paneRender", "plotAreaClick", "plotAreaHover", "plotAreaLeave", "render", "select", "selectEnd", "selectStart", "seriesClick", "drilldown", "seriesHover", "seriesOver", "seriesLeave", "zoom", "zoomEnd", "zoomStart", "legendItemClick", "drilldownLevelChange"], exportAs: ["kendoChart"] }, { kind: "directive", type: i1$7.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$7.CategoryAxisComponent, selector: "kendo-chart-category-axis" }, { kind: "component", type: i1$7.CategoryAxisItemComponent, selector: "kendo-chart-category-axis-item", inputs: ["autoBaseUnitSteps", "axisCrossingValue", "background", "baseUnit", "baseUnitStep", "categories", "color", "justified", "line", "majorGridLines", "majorTicks", "max", "maxDateGroups", "maxDivisions", "min", "minorGridLines", "minorTicks", "name", "pane", "plotBands", "reverse", "roundToBaseUnit", "startAngle", "type", "visible", "weekStartDay", "crosshair", "labels", "notes", "select", "title", "rangeLabels"] }, { kind: "component", type: i1$7.CategoryAxisLabelsComponent, selector: "kendo-chart-category-axis-item-labels", inputs: ["background", "border", "color", "content", "culture", "dateFormats", "font", "format", "margin", "mirror", "padding", "position", "rotation", "skip", "step", "visible", "visual"] }, { kind: "component", type: i1$7.ChartAreaComponent, selector: "kendo-chart-area", inputs: ["background", "border", "height", "margin", "opacity", "width"] }, { kind: "component", type: i1$7.LegendComponent, selector: "kendo-chart-legend", inputs: ["align", "background", "border", "height", "labels", "margin", "offsetX", "offsetY", "orientation", "padding", "position", "reverse", "visible", "width", "markers", "spacing", "inactiveItems", "item", "title", "focusHighlight"] }, { kind: "component", type: i1$7.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$7.SeriesItemComponent, selector: "kendo-chart-series-item", inputs: ["aggregate", "autoFit", "axis", "border", "categoryAxis", "categoryField", "closeField", "color", "colorField", "connectors", "currentField", "dashType", "data", "downColor", "downColorField", "drilldownField", "dynamicHeight", "dynamicSlope", "errorHighField", "errorLowField", "explodeField", "field", "fromField", "gap", "highField", "holeSize", "line", "lowField", "lowerField", "margin", "maxSize", "mean", "meanField", "median", "medianField", "minSize", "missingValues", "name", "neckRatio", "negativeColor", "negativeValues", "noteTextField", "opacity", "openField", "outliersField", "overlay", "padding", "q1Field", "q3Field", "segmentSpacing", "size", "sizeField", "spacing", "stack", "startAngle", "style", "summaryField", "target", "toField", "type", "upperField", "visible", "visibleInLegend", "visibleInLegendField", "visual", "width", "whiskers", "xAxis", "xErrorHighField", "xErrorLowField", "xField", "yAxis", "yErrorHighField", "yErrorLowField", "yField", "zIndex", "trendline", "for", "legendItem", "pattern", "patternField", "errorBars", "extremes", "highlight", "labels", "markers", "notes", "outliers", "tooltip"] }, { kind: "component", type: i1$7.SeriesLabelsComponent, selector: "kendo-chart-series-item-labels", inputs: ["align", "background", "border", "color", "content", "ariaContent", "distance", "font", "format", "margin", "padding", "position", "rotation", "visible", "visual", "from", "to"] }, { kind: "component", type: i1$7.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: i1$7.ValueAxisComponent, selector: "kendo-chart-value-axis" }, { kind: "component", type: i1$7.ValueAxisItemComponent, selector: "kendo-chart-value-axis-item", inputs: ["axisCrossingValue", "background", "color", "line", "majorGridLines", "majorTicks", "majorUnit", "max", "min", "minorGridLines", "minorTicks", "minorUnit", "name", "narrowRange", "pane", "plotBands", "reverse", "type", "visible", "crosshair", "labels", "notes", "title"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
11372
11478
|
}
|
|
11373
11479
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BarChartWidgetComponent, decorators: [{
|
|
11374
11480
|
type: Component,
|
|
@@ -11389,10 +11495,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
11389
11495
|
<span>{{ error() }}</span>
|
|
11390
11496
|
</div>
|
|
11391
11497
|
} @else {
|
|
11392
|
-
<kendo-chart class="chart-container">
|
|
11393
|
-
<kendo-chart-
|
|
11498
|
+
<kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: plotAreaMargin() }">
|
|
11499
|
+
<kendo-chart-area [background]="'transparent'"></kendo-chart-area>
|
|
11394
11500
|
<kendo-chart-category-axis>
|
|
11395
|
-
<kendo-chart-category-axis-item
|
|
11501
|
+
<kendo-chart-category-axis-item
|
|
11502
|
+
[categories]="categories()"
|
|
11503
|
+
[line]="{ visible: false }"
|
|
11504
|
+
[majorGridLines]="{ visible: false }">
|
|
11396
11505
|
<kendo-chart-category-axis-item-labels
|
|
11397
11506
|
[rotation]="labelRotation()"
|
|
11398
11507
|
[content]="categoryLabelContent">
|
|
@@ -11400,6 +11509,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
11400
11509
|
</kendo-chart-category-axis-item>
|
|
11401
11510
|
</kendo-chart-category-axis>
|
|
11402
11511
|
|
|
11512
|
+
<kendo-chart-value-axis>
|
|
11513
|
+
<kendo-chart-value-axis-item
|
|
11514
|
+
[line]="{ visible: false }"
|
|
11515
|
+
[majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }">
|
|
11516
|
+
</kendo-chart-value-axis-item>
|
|
11517
|
+
</kendo-chart-value-axis>
|
|
11518
|
+
|
|
11403
11519
|
<kendo-chart-series>
|
|
11404
11520
|
@for (series of seriesData(); track series.name) {
|
|
11405
11521
|
<kendo-chart-series-item
|
|
@@ -11407,11 +11523,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
11407
11523
|
[data]="series.data"
|
|
11408
11524
|
[name]="series.name"
|
|
11409
11525
|
[color]="series.color"
|
|
11526
|
+
[field]="hasColorThresholds() ? 'value' : ''"
|
|
11527
|
+
[colorField]="hasColorThresholds() ? '_color' : ''"
|
|
11528
|
+
[border]="{ width: 0 }"
|
|
11529
|
+
[gap]="0.8"
|
|
11410
11530
|
[stack]="stackConfig()">
|
|
11411
11531
|
@if (config.showDataLabels) {
|
|
11412
11532
|
<kendo-chart-series-item-labels
|
|
11413
11533
|
[visible]="true"
|
|
11414
|
-
[format]="
|
|
11534
|
+
[format]="dataLabelFormat()">
|
|
11415
11535
|
</kendo-chart-series-item-labels>
|
|
11416
11536
|
}
|
|
11417
11537
|
</kendo-chart-series-item>
|
|
@@ -11459,6 +11579,8 @@ class BarChartConfigDialogComponent {
|
|
|
11459
11579
|
initialShowLegend;
|
|
11460
11580
|
initialLegendPosition;
|
|
11461
11581
|
initialShowDataLabels;
|
|
11582
|
+
initialColorThresholds;
|
|
11583
|
+
initialDefaultBarColor;
|
|
11462
11584
|
initialFilters;
|
|
11463
11585
|
// State
|
|
11464
11586
|
isLoadingInitial = false;
|
|
@@ -11495,7 +11617,9 @@ class BarChartConfigDialogComponent {
|
|
|
11495
11617
|
categoryField: '',
|
|
11496
11618
|
showLegend: true,
|
|
11497
11619
|
legendPosition: 'right',
|
|
11498
|
-
showDataLabels: false
|
|
11620
|
+
showDataLabels: false,
|
|
11621
|
+
colorThresholds: [],
|
|
11622
|
+
defaultBarColor: ''
|
|
11499
11623
|
};
|
|
11500
11624
|
get isValid() {
|
|
11501
11625
|
if (this.selectedPersistentQuery === null || this.form.categoryField === '') {
|
|
@@ -11521,6 +11645,8 @@ class BarChartConfigDialogComponent {
|
|
|
11521
11645
|
this.form.showLegend = this.initialShowLegend ?? true;
|
|
11522
11646
|
this.form.legendPosition = this.initialLegendPosition ?? 'right';
|
|
11523
11647
|
this.form.showDataLabels = this.initialShowDataLabels ?? false;
|
|
11648
|
+
this.form.colorThresholds = this.initialColorThresholds ? [...this.initialColorThresholds] : [];
|
|
11649
|
+
this.form.defaultBarColor = this.initialDefaultBarColor ?? '';
|
|
11524
11650
|
// Determine series mode from initial values
|
|
11525
11651
|
if (this.initialSeriesGroupField && this.initialValueField) {
|
|
11526
11652
|
this.seriesMode = 'dynamic';
|
|
@@ -11664,6 +11790,8 @@ class BarChartConfigDialogComponent {
|
|
|
11664
11790
|
showLegend: this.form.showLegend,
|
|
11665
11791
|
legendPosition: this.form.legendPosition,
|
|
11666
11792
|
showDataLabels: this.form.showDataLabels,
|
|
11793
|
+
colorThresholds: this.form.colorThresholds.length > 0 ? this.form.colorThresholds : undefined,
|
|
11794
|
+
defaultBarColor: this.form.defaultBarColor || undefined,
|
|
11667
11795
|
filters: filtersDto
|
|
11668
11796
|
};
|
|
11669
11797
|
// Add dynamic series fields if in dynamic mode
|
|
@@ -11673,11 +11801,17 @@ class BarChartConfigDialogComponent {
|
|
|
11673
11801
|
}
|
|
11674
11802
|
this.windowRef.close(result);
|
|
11675
11803
|
}
|
|
11804
|
+
addColorThreshold() {
|
|
11805
|
+
this.form.colorThresholds.push({ value: 0, color: '#10b981' });
|
|
11806
|
+
}
|
|
11807
|
+
removeColorThreshold(index) {
|
|
11808
|
+
this.form.colorThresholds.splice(index, 1);
|
|
11809
|
+
}
|
|
11676
11810
|
onCancel() {
|
|
11677
11811
|
this.windowRef.close();
|
|
11678
11812
|
}
|
|
11679
11813
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BarChartConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11680
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: BarChartConfigDialogComponent, isStandalone: true, selector: "mm-bar-chart-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialSeries: "initialSeries", initialSeriesGroupField: "initialSeriesGroupField", initialValueField: "initialValueField", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialShowDataLabels: "initialShowDataLabels", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
|
|
11814
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: BarChartConfigDialogComponent, isStandalone: true, selector: "mm-bar-chart-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialSeries: "initialSeries", initialSeriesGroupField: "initialSeriesGroupField", initialValueField: "initialValueField", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialShowDataLabels: "initialShowDataLabels", initialColorThresholds: "initialColorThresholds", initialDefaultBarColor: "initialDefaultBarColor", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
|
|
11681
11815
|
<div class="config-container">
|
|
11682
11816
|
|
|
11683
11817
|
<div class="config-form" [class.loading]="isLoadingInitial">
|
|
@@ -11907,6 +12041,24 @@ class BarChartConfigDialogComponent {
|
|
|
11907
12041
|
</div>
|
|
11908
12042
|
}
|
|
11909
12043
|
</div>
|
|
12044
|
+
|
|
12045
|
+
<div class="form-section">
|
|
12046
|
+
<h4>Conditional Colors</h4>
|
|
12047
|
+
<p class="section-hint">Color bars based on value thresholds (sorted ascending).</p>
|
|
12048
|
+
@for (threshold of form.colorThresholds; track $index) {
|
|
12049
|
+
<div class="threshold-row">
|
|
12050
|
+
<label>Values ≤</label>
|
|
12051
|
+
<kendo-numerictextbox [(ngModel)]="threshold.value" [format]="'n0'" [spinners]="false" style="width: 100px;"></kendo-numerictextbox>
|
|
12052
|
+
<kendo-textbox [(ngModel)]="threshold.color" [placeholder]="'Color (#10b981)'" style="width: 120px;"></kendo-textbox>
|
|
12053
|
+
<button kendoButton fillMode="flat" (click)="removeColorThreshold($index)">Remove</button>
|
|
12054
|
+
</div>
|
|
12055
|
+
}
|
|
12056
|
+
<div class="threshold-row">
|
|
12057
|
+
<label>Default color</label>
|
|
12058
|
+
<kendo-textbox [(ngModel)]="form.defaultBarColor" [placeholder]="'#ef4444'" style="width: 120px;"></kendo-textbox>
|
|
12059
|
+
</div>
|
|
12060
|
+
<button kendoButton fillMode="flat" (click)="addColorThreshold()">+ Add Threshold</button>
|
|
12061
|
+
</div>
|
|
11910
12062
|
</div>
|
|
11911
12063
|
|
|
11912
12064
|
<div class="action-bar mm-dialog-actions">
|
|
@@ -11920,7 +12072,7 @@ class BarChartConfigDialogComponent {
|
|
|
11920
12072
|
</button>
|
|
11921
12073
|
</div>
|
|
11922
12074
|
</div>
|
|
11923
|
-
`, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.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: 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: "component", type: i4.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: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }] });
|
|
12075
|
+
`, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.threshold-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}.threshold-row label{font-size:.85rem;min-width:70px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i3.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: i3.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: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.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: 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: "component", type: i4.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: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }] });
|
|
11924
12076
|
}
|
|
11925
12077
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BarChartConfigDialogComponent, decorators: [{
|
|
11926
12078
|
type: Component,
|
|
@@ -12164,6 +12316,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
12164
12316
|
</div>
|
|
12165
12317
|
}
|
|
12166
12318
|
</div>
|
|
12319
|
+
|
|
12320
|
+
<div class="form-section">
|
|
12321
|
+
<h4>Conditional Colors</h4>
|
|
12322
|
+
<p class="section-hint">Color bars based on value thresholds (sorted ascending).</p>
|
|
12323
|
+
@for (threshold of form.colorThresholds; track $index) {
|
|
12324
|
+
<div class="threshold-row">
|
|
12325
|
+
<label>Values ≤</label>
|
|
12326
|
+
<kendo-numerictextbox [(ngModel)]="threshold.value" [format]="'n0'" [spinners]="false" style="width: 100px;"></kendo-numerictextbox>
|
|
12327
|
+
<kendo-textbox [(ngModel)]="threshold.color" [placeholder]="'Color (#10b981)'" style="width: 120px;"></kendo-textbox>
|
|
12328
|
+
<button kendoButton fillMode="flat" (click)="removeColorThreshold($index)">Remove</button>
|
|
12329
|
+
</div>
|
|
12330
|
+
}
|
|
12331
|
+
<div class="threshold-row">
|
|
12332
|
+
<label>Default color</label>
|
|
12333
|
+
<kendo-textbox [(ngModel)]="form.defaultBarColor" [placeholder]="'#ef4444'" style="width: 120px;"></kendo-textbox>
|
|
12334
|
+
</div>
|
|
12335
|
+
<button kendoButton fillMode="flat" (click)="addColorThreshold()">+ Add Threshold</button>
|
|
12336
|
+
</div>
|
|
12167
12337
|
</div>
|
|
12168
12338
|
|
|
12169
12339
|
<div class="action-bar mm-dialog-actions">
|
|
@@ -12177,7 +12347,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
12177
12347
|
</button>
|
|
12178
12348
|
</div>
|
|
12179
12349
|
</div>
|
|
12180
|
-
`, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"] }]
|
|
12350
|
+
`, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.threshold-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}.threshold-row label{font-size:.85rem;min-width:70px}\n"] }]
|
|
12181
12351
|
}], propDecorators: { querySelector: [{
|
|
12182
12352
|
type: ViewChild,
|
|
12183
12353
|
args: ['querySelector']
|
|
@@ -12201,6 +12371,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
12201
12371
|
type: Input
|
|
12202
12372
|
}], initialShowDataLabels: [{
|
|
12203
12373
|
type: Input
|
|
12374
|
+
}], initialColorThresholds: [{
|
|
12375
|
+
type: Input
|
|
12376
|
+
}], initialDefaultBarColor: [{
|
|
12377
|
+
type: Input
|
|
12204
12378
|
}], initialFilters: [{
|
|
12205
12379
|
type: Input
|
|
12206
12380
|
}] } });
|
|
@@ -12223,6 +12397,28 @@ class LineChartWidgetComponent {
|
|
|
12223
12397
|
valueAxes = this._valueAxes.asReadonly();
|
|
12224
12398
|
error = this._error.asReadonly();
|
|
12225
12399
|
data = computed(() => this._seriesData(), ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
12400
|
+
plotBands = computed(() => {
|
|
12401
|
+
if (!this.config?.referenceLines?.length)
|
|
12402
|
+
return [];
|
|
12403
|
+
return this.config.referenceLines.map(ref => {
|
|
12404
|
+
const lineColor = ref.color ?? '#ef4444';
|
|
12405
|
+
const bandWidth = ref.value * 0.002 || 1;
|
|
12406
|
+
return {
|
|
12407
|
+
from: ref.value - bandWidth,
|
|
12408
|
+
to: ref.value + bandWidth,
|
|
12409
|
+
color: lineColor,
|
|
12410
|
+
opacity: ref.opacity ?? 0.8,
|
|
12411
|
+
label: ref.label ? {
|
|
12412
|
+
text: ref.label,
|
|
12413
|
+
position: 'top',
|
|
12414
|
+
align: 'right',
|
|
12415
|
+
color: lineColor,
|
|
12416
|
+
font: '500 0.8rem sans-serif',
|
|
12417
|
+
padding: { top: 2, right: 4, bottom: 2, left: 4 }
|
|
12418
|
+
} : undefined
|
|
12419
|
+
};
|
|
12420
|
+
});
|
|
12421
|
+
}, ...(ngDevMode ? [{ debugName: "plotBands" }] : /* istanbul ignore next */ []));
|
|
12226
12422
|
chartType = computed(() => {
|
|
12227
12423
|
return this.config?.chartType ?? 'line';
|
|
12228
12424
|
}, ...(ngDevMode ? [{ debugName: "chartType" }] : /* istanbul ignore next */ []));
|
|
@@ -12482,11 +12678,14 @@ class LineChartWidgetComponent {
|
|
|
12482
12678
|
<span>{{ error() }}</span>
|
|
12483
12679
|
</div>
|
|
12484
12680
|
} @else {
|
|
12485
|
-
<kendo-chart class="chart-container">
|
|
12486
|
-
<kendo-chart-
|
|
12681
|
+
<kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: { top: 0, right: 0, bottom: 0, left: 0 } }">
|
|
12682
|
+
<kendo-chart-area [background]="'transparent'"></kendo-chart-area>
|
|
12487
12683
|
|
|
12488
12684
|
<kendo-chart-category-axis>
|
|
12489
|
-
<kendo-chart-category-axis-item
|
|
12685
|
+
<kendo-chart-category-axis-item
|
|
12686
|
+
[categories]="categories()"
|
|
12687
|
+
[line]="{ visible: false }"
|
|
12688
|
+
[majorGridLines]="{ visible: false }">
|
|
12490
12689
|
<kendo-chart-category-axis-item-labels
|
|
12491
12690
|
[rotation]="labelRotation()"
|
|
12492
12691
|
[step]="labelStep()"
|
|
@@ -12500,19 +12699,36 @@ class LineChartWidgetComponent {
|
|
|
12500
12699
|
@for (axis of valueAxes(); track axis.name) {
|
|
12501
12700
|
<kendo-chart-value-axis-item
|
|
12502
12701
|
[name]="axis.name"
|
|
12503
|
-
[title]="{ text: axis.unit }"
|
|
12702
|
+
[title]="{ text: axis.unit }"
|
|
12703
|
+
[line]="{ visible: false }"
|
|
12704
|
+
[majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }"
|
|
12705
|
+
[plotBands]="plotBands()">
|
|
12504
12706
|
</kendo-chart-value-axis-item>
|
|
12505
12707
|
}
|
|
12506
12708
|
</kendo-chart-value-axis>
|
|
12507
12709
|
}
|
|
12508
12710
|
|
|
12711
|
+
@if (valueAxes().length === 0) {
|
|
12712
|
+
<kendo-chart-value-axis>
|
|
12713
|
+
<kendo-chart-value-axis-item
|
|
12714
|
+
[name]="''"
|
|
12715
|
+
[title]="{ text: config.valueAxisTitle ?? '' }"
|
|
12716
|
+
[line]="{ visible: false }"
|
|
12717
|
+
[majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }"
|
|
12718
|
+
[plotBands]="plotBands()">
|
|
12719
|
+
</kendo-chart-value-axis-item>
|
|
12720
|
+
</kendo-chart-value-axis>
|
|
12721
|
+
}
|
|
12722
|
+
|
|
12509
12723
|
<kendo-chart-series>
|
|
12510
12724
|
@for (series of seriesData(); track series.name) {
|
|
12511
12725
|
<kendo-chart-series-item
|
|
12512
12726
|
[type]="chartType()"
|
|
12727
|
+
[style]="'smooth'"
|
|
12513
12728
|
[data]="series.data"
|
|
12514
12729
|
[name]="series.name"
|
|
12515
12730
|
[axis]="series.axisName ?? ''"
|
|
12731
|
+
[opacity]="0.7"
|
|
12516
12732
|
[markers]="{ visible: config.showMarkers ?? false }">
|
|
12517
12733
|
</kendo-chart-series-item>
|
|
12518
12734
|
}
|
|
@@ -12534,7 +12750,7 @@ class LineChartWidgetComponent {
|
|
|
12534
12750
|
</kendo-chart>
|
|
12535
12751
|
}
|
|
12536
12752
|
</div>
|
|
12537
|
-
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.line-chart-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.line-chart-widget.loading,.line-chart-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$
|
|
12753
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.line-chart-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.line-chart-widget.loading,.line-chart-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$7.ChartComponent, selector: "kendo-chart", inputs: ["pannable", "renderAs", "seriesColors", "subtitle", "title", "noData", "observeStyles", "transitions", "zoomable", "axisDefaults", "categoryAxis", "chartArea", "legend", "panes", "paneDefaults", "plotArea", "series", "seriesDefaults", "tooltip", "valueAxis", "xAxis", "yAxis", "resizeRateLimit", "popupSettings", "drilldownLevel"], outputs: ["axisLabelClick", "drag", "dragEnd", "dragStart", "legendItemHover", "legendItemLeave", "noteClick", "noteHover", "noteLeave", "paneRender", "plotAreaClick", "plotAreaHover", "plotAreaLeave", "render", "select", "selectEnd", "selectStart", "seriesClick", "drilldown", "seriesHover", "seriesOver", "seriesLeave", "zoom", "zoomEnd", "zoomStart", "legendItemClick", "drilldownLevelChange"], exportAs: ["kendoChart"] }, { kind: "directive", type: i1$7.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$7.CategoryAxisComponent, selector: "kendo-chart-category-axis" }, { kind: "component", type: i1$7.CategoryAxisItemComponent, selector: "kendo-chart-category-axis-item", inputs: ["autoBaseUnitSteps", "axisCrossingValue", "background", "baseUnit", "baseUnitStep", "categories", "color", "justified", "line", "majorGridLines", "majorTicks", "max", "maxDateGroups", "maxDivisions", "min", "minorGridLines", "minorTicks", "name", "pane", "plotBands", "reverse", "roundToBaseUnit", "startAngle", "type", "visible", "weekStartDay", "crosshair", "labels", "notes", "select", "title", "rangeLabels"] }, { kind: "component", type: i1$7.CategoryAxisLabelsComponent, selector: "kendo-chart-category-axis-item-labels", inputs: ["background", "border", "color", "content", "culture", "dateFormats", "font", "format", "margin", "mirror", "padding", "position", "rotation", "skip", "step", "visible", "visual"] }, { kind: "component", type: i1$7.ChartAreaComponent, selector: "kendo-chart-area", inputs: ["background", "border", "height", "margin", "opacity", "width"] }, { kind: "component", type: i1$7.LegendComponent, selector: "kendo-chart-legend", inputs: ["align", "background", "border", "height", "labels", "margin", "offsetX", "offsetY", "orientation", "padding", "position", "reverse", "visible", "width", "markers", "spacing", "inactiveItems", "item", "title", "focusHighlight"] }, { kind: "component", type: i1$7.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$7.SeriesItemComponent, selector: "kendo-chart-series-item", inputs: ["aggregate", "autoFit", "axis", "border", "categoryAxis", "categoryField", "closeField", "color", "colorField", "connectors", "currentField", "dashType", "data", "downColor", "downColorField", "drilldownField", "dynamicHeight", "dynamicSlope", "errorHighField", "errorLowField", "explodeField", "field", "fromField", "gap", "highField", "holeSize", "line", "lowField", "lowerField", "margin", "maxSize", "mean", "meanField", "median", "medianField", "minSize", "missingValues", "name", "neckRatio", "negativeColor", "negativeValues", "noteTextField", "opacity", "openField", "outliersField", "overlay", "padding", "q1Field", "q3Field", "segmentSpacing", "size", "sizeField", "spacing", "stack", "startAngle", "style", "summaryField", "target", "toField", "type", "upperField", "visible", "visibleInLegend", "visibleInLegendField", "visual", "width", "whiskers", "xAxis", "xErrorHighField", "xErrorLowField", "xField", "yAxis", "yErrorHighField", "yErrorLowField", "yField", "zIndex", "trendline", "for", "legendItem", "pattern", "patternField", "errorBars", "extremes", "highlight", "labels", "markers", "notes", "outliers", "tooltip"] }, { kind: "component", type: i1$7.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: i1$7.ValueAxisComponent, selector: "kendo-chart-value-axis" }, { kind: "component", type: i1$7.ValueAxisItemComponent, selector: "kendo-chart-value-axis-item", inputs: ["axisCrossingValue", "background", "color", "line", "majorGridLines", "majorTicks", "majorUnit", "max", "min", "minorGridLines", "minorTicks", "minorUnit", "name", "narrowRange", "pane", "plotBands", "reverse", "type", "visible", "crosshair", "labels", "notes", "title"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
12538
12754
|
}
|
|
12539
12755
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: LineChartWidgetComponent, decorators: [{
|
|
12540
12756
|
type: Component,
|
|
@@ -12555,11 +12771,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
12555
12771
|
<span>{{ error() }}</span>
|
|
12556
12772
|
</div>
|
|
12557
12773
|
} @else {
|
|
12558
|
-
<kendo-chart class="chart-container">
|
|
12559
|
-
<kendo-chart-
|
|
12774
|
+
<kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: { top: 0, right: 0, bottom: 0, left: 0 } }">
|
|
12775
|
+
<kendo-chart-area [background]="'transparent'"></kendo-chart-area>
|
|
12560
12776
|
|
|
12561
12777
|
<kendo-chart-category-axis>
|
|
12562
|
-
<kendo-chart-category-axis-item
|
|
12778
|
+
<kendo-chart-category-axis-item
|
|
12779
|
+
[categories]="categories()"
|
|
12780
|
+
[line]="{ visible: false }"
|
|
12781
|
+
[majorGridLines]="{ visible: false }">
|
|
12563
12782
|
<kendo-chart-category-axis-item-labels
|
|
12564
12783
|
[rotation]="labelRotation()"
|
|
12565
12784
|
[step]="labelStep()"
|
|
@@ -12573,19 +12792,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
12573
12792
|
@for (axis of valueAxes(); track axis.name) {
|
|
12574
12793
|
<kendo-chart-value-axis-item
|
|
12575
12794
|
[name]="axis.name"
|
|
12576
|
-
[title]="{ text: axis.unit }"
|
|
12795
|
+
[title]="{ text: axis.unit }"
|
|
12796
|
+
[line]="{ visible: false }"
|
|
12797
|
+
[majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }"
|
|
12798
|
+
[plotBands]="plotBands()">
|
|
12577
12799
|
</kendo-chart-value-axis-item>
|
|
12578
12800
|
}
|
|
12579
12801
|
</kendo-chart-value-axis>
|
|
12580
12802
|
}
|
|
12581
12803
|
|
|
12804
|
+
@if (valueAxes().length === 0) {
|
|
12805
|
+
<kendo-chart-value-axis>
|
|
12806
|
+
<kendo-chart-value-axis-item
|
|
12807
|
+
[name]="''"
|
|
12808
|
+
[title]="{ text: config.valueAxisTitle ?? '' }"
|
|
12809
|
+
[line]="{ visible: false }"
|
|
12810
|
+
[majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }"
|
|
12811
|
+
[plotBands]="plotBands()">
|
|
12812
|
+
</kendo-chart-value-axis-item>
|
|
12813
|
+
</kendo-chart-value-axis>
|
|
12814
|
+
}
|
|
12815
|
+
|
|
12582
12816
|
<kendo-chart-series>
|
|
12583
12817
|
@for (series of seriesData(); track series.name) {
|
|
12584
12818
|
<kendo-chart-series-item
|
|
12585
12819
|
[type]="chartType()"
|
|
12820
|
+
[style]="'smooth'"
|
|
12586
12821
|
[data]="series.data"
|
|
12587
12822
|
[name]="series.name"
|
|
12588
12823
|
[axis]="series.axisName ?? ''"
|
|
12824
|
+
[opacity]="0.7"
|
|
12589
12825
|
[markers]="{ visible: config.showMarkers ?? false }">
|
|
12590
12826
|
</kendo-chart-series-item>
|
|
12591
12827
|
}
|
|
@@ -12632,6 +12868,7 @@ class LineChartConfigDialogComponent {
|
|
|
12632
12868
|
initialShowLegend;
|
|
12633
12869
|
initialLegendPosition;
|
|
12634
12870
|
initialShowMarkers;
|
|
12871
|
+
initialReferenceLines;
|
|
12635
12872
|
initialFilters;
|
|
12636
12873
|
// State
|
|
12637
12874
|
isLoadingInitial = false;
|
|
@@ -12660,7 +12897,8 @@ class LineChartConfigDialogComponent {
|
|
|
12660
12897
|
unitField: '',
|
|
12661
12898
|
showLegend: true,
|
|
12662
12899
|
legendPosition: 'right',
|
|
12663
|
-
showMarkers: false
|
|
12900
|
+
showMarkers: false,
|
|
12901
|
+
referenceLines: []
|
|
12664
12902
|
};
|
|
12665
12903
|
get isValid() {
|
|
12666
12904
|
return this.selectedPersistentQuery !== null
|
|
@@ -12684,6 +12922,7 @@ class LineChartConfigDialogComponent {
|
|
|
12684
12922
|
this.form.showLegend = this.initialShowLegend ?? true;
|
|
12685
12923
|
this.form.legendPosition = this.initialLegendPosition ?? 'right';
|
|
12686
12924
|
this.form.showMarkers = this.initialShowMarkers ?? false;
|
|
12925
|
+
this.form.referenceLines = this.initialReferenceLines ? [...this.initialReferenceLines] : [];
|
|
12687
12926
|
// Initialize filters
|
|
12688
12927
|
if (this.initialFilters && this.initialFilters.length > 0) {
|
|
12689
12928
|
this.filters = this.initialFilters.map((f, index) => ({
|
|
@@ -12784,15 +13023,22 @@ class LineChartConfigDialogComponent {
|
|
|
12784
13023
|
showLegend: this.form.showLegend,
|
|
12785
13024
|
legendPosition: this.form.legendPosition,
|
|
12786
13025
|
showMarkers: this.form.showMarkers,
|
|
13026
|
+
referenceLines: this.form.referenceLines.length > 0 ? this.form.referenceLines : undefined,
|
|
12787
13027
|
filters: filtersDto
|
|
12788
13028
|
};
|
|
12789
13029
|
this.windowRef.close(result);
|
|
12790
13030
|
}
|
|
13031
|
+
addReferenceLine() {
|
|
13032
|
+
this.form.referenceLines.push({ value: 0, label: '', color: '#ef4444' });
|
|
13033
|
+
}
|
|
13034
|
+
removeReferenceLine(index) {
|
|
13035
|
+
this.form.referenceLines.splice(index, 1);
|
|
13036
|
+
}
|
|
12791
13037
|
onCancel() {
|
|
12792
13038
|
this.windowRef.close();
|
|
12793
13039
|
}
|
|
12794
13040
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: LineChartConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12795
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: LineChartConfigDialogComponent, isStandalone: true, selector: "mm-line-chart-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialSeriesGroupField: "initialSeriesGroupField", initialValueField: "initialValueField", initialUnitField: "initialUnitField", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialShowMarkers: "initialShowMarkers", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
|
|
13041
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: LineChartConfigDialogComponent, isStandalone: true, selector: "mm-line-chart-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialSeriesGroupField: "initialSeriesGroupField", initialValueField: "initialValueField", initialUnitField: "initialUnitField", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialShowMarkers: "initialShowMarkers", initialReferenceLines: "initialReferenceLines", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
|
|
12796
13042
|
<div class="config-container">
|
|
12797
13043
|
|
|
12798
13044
|
<div class="config-form" [class.loading]="isLoadingInitial">
|
|
@@ -12968,6 +13214,20 @@ class LineChartConfigDialogComponent {
|
|
|
12968
13214
|
</div>
|
|
12969
13215
|
}
|
|
12970
13216
|
</div>
|
|
13217
|
+
|
|
13218
|
+
<div class="form-section">
|
|
13219
|
+
<h4>Reference Lines</h4>
|
|
13220
|
+
<p class="section-hint">Add horizontal threshold lines to the chart.</p>
|
|
13221
|
+
@for (refLine of form.referenceLines; track $index) {
|
|
13222
|
+
<div class="reference-line-row">
|
|
13223
|
+
<kendo-numerictextbox [(ngModel)]="refLine.value" [format]="'n0'" [placeholder]="'Value'" [spinners]="false" style="width: 100px;"></kendo-numerictextbox>
|
|
13224
|
+
<kendo-textbox [(ngModel)]="refLine.label" [placeholder]="'Label (e.g. Limit)'" style="flex: 1;"></kendo-textbox>
|
|
13225
|
+
<kendo-textbox [(ngModel)]="refLine.color" [placeholder]="'Color (#ef4444)'" style="width: 110px;"></kendo-textbox>
|
|
13226
|
+
<button kendoButton fillMode="flat" (click)="removeReferenceLine($index)">Remove</button>
|
|
13227
|
+
</div>
|
|
13228
|
+
}
|
|
13229
|
+
<button kendoButton fillMode="flat" (click)="addReferenceLine()">+ Add Reference Line</button>
|
|
13230
|
+
</div>
|
|
12971
13231
|
</div>
|
|
12972
13232
|
|
|
12973
13233
|
<div class="action-bar mm-dialog-actions">
|
|
@@ -12981,7 +13241,7 @@ class LineChartConfigDialogComponent {
|
|
|
12981
13241
|
</button>
|
|
12982
13242
|
</div>
|
|
12983
13243
|
</div>
|
|
12984
|
-
`, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px;align-items:center}.checkbox-field{flex-direction:row;align-items:center;margin-bottom:0}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.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: 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: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }] });
|
|
13244
|
+
`, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px;align-items:center}.checkbox-field{flex-direction:row;align-items:center;margin-bottom:0}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.reference-line-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i3.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: i3.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: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.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: 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: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }] });
|
|
12985
13245
|
}
|
|
12986
13246
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: LineChartConfigDialogComponent, decorators: [{
|
|
12987
13247
|
type: Component,
|
|
@@ -13171,6 +13431,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
13171
13431
|
</div>
|
|
13172
13432
|
}
|
|
13173
13433
|
</div>
|
|
13434
|
+
|
|
13435
|
+
<div class="form-section">
|
|
13436
|
+
<h4>Reference Lines</h4>
|
|
13437
|
+
<p class="section-hint">Add horizontal threshold lines to the chart.</p>
|
|
13438
|
+
@for (refLine of form.referenceLines; track $index) {
|
|
13439
|
+
<div class="reference-line-row">
|
|
13440
|
+
<kendo-numerictextbox [(ngModel)]="refLine.value" [format]="'n0'" [placeholder]="'Value'" [spinners]="false" style="width: 100px;"></kendo-numerictextbox>
|
|
13441
|
+
<kendo-textbox [(ngModel)]="refLine.label" [placeholder]="'Label (e.g. Limit)'" style="flex: 1;"></kendo-textbox>
|
|
13442
|
+
<kendo-textbox [(ngModel)]="refLine.color" [placeholder]="'Color (#ef4444)'" style="width: 110px;"></kendo-textbox>
|
|
13443
|
+
<button kendoButton fillMode="flat" (click)="removeReferenceLine($index)">Remove</button>
|
|
13444
|
+
</div>
|
|
13445
|
+
}
|
|
13446
|
+
<button kendoButton fillMode="flat" (click)="addReferenceLine()">+ Add Reference Line</button>
|
|
13447
|
+
</div>
|
|
13174
13448
|
</div>
|
|
13175
13449
|
|
|
13176
13450
|
<div class="action-bar mm-dialog-actions">
|
|
@@ -13184,7 +13458,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
13184
13458
|
</button>
|
|
13185
13459
|
</div>
|
|
13186
13460
|
</div>
|
|
13187
|
-
`, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px;align-items:center}.checkbox-field{flex-direction:row;align-items:center;margin-bottom:0}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"] }]
|
|
13461
|
+
`, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px;align-items:center}.checkbox-field{flex-direction:row;align-items:center;margin-bottom:0}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.reference-line-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}\n"] }]
|
|
13188
13462
|
}], propDecorators: { querySelector: [{
|
|
13189
13463
|
type: ViewChild,
|
|
13190
13464
|
args: ['querySelector']
|
|
@@ -13208,6 +13482,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
13208
13482
|
type: Input
|
|
13209
13483
|
}], initialShowMarkers: [{
|
|
13210
13484
|
type: Input
|
|
13485
|
+
}], initialReferenceLines: [{
|
|
13486
|
+
type: Input
|
|
13211
13487
|
}], initialFilters: [{
|
|
13212
13488
|
type: Input
|
|
13213
13489
|
}] } });
|
|
@@ -13667,7 +13943,7 @@ class HeatmapWidgetComponent {
|
|
|
13667
13943
|
</kendo-chart>
|
|
13668
13944
|
}
|
|
13669
13945
|
</div>
|
|
13670
|
-
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.heatmap-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.heatmap-widget.loading,.heatmap-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px;color:#212529;background:#fff;border-radius:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$
|
|
13946
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.heatmap-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.heatmap-widget.loading,.heatmap-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px;color:#212529;background:#fff;border-radius:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$7.ChartComponent, selector: "kendo-chart", inputs: ["pannable", "renderAs", "seriesColors", "subtitle", "title", "noData", "observeStyles", "transitions", "zoomable", "axisDefaults", "categoryAxis", "chartArea", "legend", "panes", "paneDefaults", "plotArea", "series", "seriesDefaults", "tooltip", "valueAxis", "xAxis", "yAxis", "resizeRateLimit", "popupSettings", "drilldownLevel"], outputs: ["axisLabelClick", "drag", "dragEnd", "dragStart", "legendItemHover", "legendItemLeave", "noteClick", "noteHover", "noteLeave", "paneRender", "plotAreaClick", "plotAreaHover", "plotAreaLeave", "render", "select", "selectEnd", "selectStart", "seriesClick", "drilldown", "seriesHover", "seriesOver", "seriesLeave", "zoom", "zoomEnd", "zoomStart", "legendItemClick", "drilldownLevelChange"], exportAs: ["kendoChart"] }, { kind: "component", type: i1$7.XAxisComponent, selector: "kendo-chart-x-axis" }, { kind: "component", type: i1$7.XAxisItemComponent, selector: "kendo-chart-x-axis-item", inputs: ["axisCrossingValue", "background", "baseUnit", "categories", "color", "line", "majorGridLines", "majorTicks", "majorUnit", "max", "min", "minorGridLines", "minorTicks", "minorUnit", "name", "narrowRange", "pane", "plotBands", "reverse", "startAngle", "type", "visible", "weekStartDay", "crosshair", "labels", "notes", "title"] }, { kind: "component", type: i1$7.XAxisLabelsComponent, selector: "kendo-chart-x-axis-item-labels", inputs: ["background", "border", "color", "content", "culture", "dateFormats", "font", "format", "margin", "mirror", "padding", "position", "rotation", "skip", "step", "visible", "visual"] }, { kind: "component", type: i1$7.YAxisComponent, selector: "kendo-chart-y-axis" }, { kind: "component", type: i1$7.YAxisItemComponent, selector: "kendo-chart-y-axis-item", inputs: ["axisCrossingValue", "background", "baseUnit", "categories", "color", "line", "majorGridLines", "majorTicks", "majorUnit", "max", "min", "minorGridLines", "minorTicks", "minorUnit", "name", "narrowRange", "pane", "plotBands", "reverse", "type", "visible", "crosshair", "labels", "notes", "title"] }, { kind: "directive", type: i1$7.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$7.LegendComponent, selector: "kendo-chart-legend", inputs: ["align", "background", "border", "height", "labels", "margin", "offsetX", "offsetY", "orientation", "padding", "position", "reverse", "visible", "width", "markers", "spacing", "inactiveItems", "item", "title", "focusHighlight"] }, { kind: "component", type: i1$7.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$7.SeriesItemComponent, selector: "kendo-chart-series-item", inputs: ["aggregate", "autoFit", "axis", "border", "categoryAxis", "categoryField", "closeField", "color", "colorField", "connectors", "currentField", "dashType", "data", "downColor", "downColorField", "drilldownField", "dynamicHeight", "dynamicSlope", "errorHighField", "errorLowField", "explodeField", "field", "fromField", "gap", "highField", "holeSize", "line", "lowField", "lowerField", "margin", "maxSize", "mean", "meanField", "median", "medianField", "minSize", "missingValues", "name", "neckRatio", "negativeColor", "negativeValues", "noteTextField", "opacity", "openField", "outliersField", "overlay", "padding", "q1Field", "q3Field", "segmentSpacing", "size", "sizeField", "spacing", "stack", "startAngle", "style", "summaryField", "target", "toField", "type", "upperField", "visible", "visibleInLegend", "visibleInLegendField", "visual", "width", "whiskers", "xAxis", "xErrorHighField", "xErrorLowField", "xField", "yAxis", "yErrorHighField", "yErrorLowField", "yField", "zIndex", "trendline", "for", "legendItem", "pattern", "patternField", "errorBars", "extremes", "highlight", "labels", "markers", "notes", "outliers", "tooltip"] }, { kind: "component", type: i1$7.SeriesLabelsComponent, selector: "kendo-chart-series-item-labels", inputs: ["align", "background", "border", "color", "content", "ariaContent", "distance", "font", "format", "margin", "padding", "position", "rotation", "visible", "visual", "from", "to"] }, { kind: "component", type: i1$7.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
13671
13947
|
}
|
|
13672
13948
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: HeatmapWidgetComponent, decorators: [{
|
|
13673
13949
|
type: Component,
|
|
@@ -17021,7 +17297,7 @@ class MarkdownWidgetComponent {
|
|
|
17021
17297
|
this._data.set(this.config?.content ?? '');
|
|
17022
17298
|
}
|
|
17023
17299
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MarkdownWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17024
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: MarkdownWidgetComponent, isStandalone: true, selector: "mm-markdown-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"markdown-widget\"\n [class.loading]=\"isLoading()\"\n [class.error]=\"error()\"\n [ngStyle]=\"containerStyle()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured\n message=\"No markdown content configured.\">\n </mm-widget-not-configured>\n } @else {\n <div class=\"markdown-content mm-prose\">\n <markdown [data]=\"resolvedContent()\"></markdown>\n </div>\n }\n</div>\n", styles: [".markdown-widget{display:flex;flex-direction:column;height:100%;overflow:auto}.markdown-content{flex:1;overflow:auto}:host{--mm-prose-text: #333333;--mm-prose-heading: #1976d2;--mm-prose-link: #1565c0;--mm-prose-link-hover: #0d47a1;--mm-prose-code-bg: #f5f5f5;--mm-prose-code-text: #d32f2f;--mm-prose-pre-bg: #f5f5f5;--mm-prose-pre-border: #e0e0e0;--mm-prose-blockquote-border: #6c4da8;--mm-prose-blockquote-bg: rgba(108, 77, 168, .05);--mm-prose-table-header-bg: #f5f5f5;--mm-prose-table-header-text: #333333;--mm-prose-table-border: #e0e0e0;--mm-prose-table-row-even: rgba(0, 0, 0, .02);--mm-prose-strong: #111111;--mm-prose-em: #1565c0;--mm-prose-marker-ul: #1976d2;--mm-prose-marker-ol: #1565c0;--mm-prose-hr-start: #1976d2;--mm-prose-hr-end: #1565c0;--mm-prose-img-border: #e0e0e0;--mm-prose-font: \"Roboto\", sans-serif}:host ::ng-deep .mm-prose{color:var(--mm-prose-text);font-family:var(--mm-prose-font);line-height:1.6}:host ::ng-deep .mm-prose h1,:host ::ng-deep .mm-prose h2,:host ::ng-deep .mm-prose h3,:host ::ng-deep .mm-prose h4,:host ::ng-deep .mm-prose h5,:host ::ng-deep .mm-prose h6{color:var(--mm-prose-heading);font-weight:600;margin-top:1.5em;margin-bottom:.5em;letter-spacing:.5px}:host ::ng-deep .mm-prose h1:first-child,:host ::ng-deep .mm-prose h2:first-child,:host ::ng-deep .mm-prose h3:first-child,:host ::ng-deep .mm-prose h4:first-child,:host ::ng-deep .mm-prose h5:first-child,:host ::ng-deep .mm-prose h6:first-child{margin-top:0}:host ::ng-deep .mm-prose h1{font-size:1.75rem;border-bottom:2px solid var(--mm-prose-heading);padding-bottom:.25em}:host ::ng-deep .mm-prose h2{font-size:1.5rem;border-bottom:1px solid var(--mm-prose-table-border);padding-bottom:.2em}:host ::ng-deep .mm-prose h3{font-size:1.25rem}:host ::ng-deep .mm-prose h4{font-size:1.1rem}:host ::ng-deep .mm-prose h5,:host ::ng-deep .mm-prose h6{font-size:1rem}:host ::ng-deep .mm-prose p{margin-bottom:1em}:host ::ng-deep .mm-prose p:last-child{margin-bottom:0}:host ::ng-deep .mm-prose a{color:var(--mm-prose-link);text-decoration:none;border-bottom:1px solid transparent;transition:all .15s ease}:host ::ng-deep .mm-prose a:hover{color:var(--mm-prose-link-hover);border-bottom-color:var(--mm-prose-link-hover)}:host ::ng-deep .mm-prose ul,:host ::ng-deep .mm-prose ol{margin-bottom:1em;padding-left:1.5em}:host ::ng-deep .mm-prose li{margin-bottom:.25em}:host ::ng-deep .mm-prose ul li::marker{color:var(--mm-prose-marker-ul)}:host ::ng-deep .mm-prose ol li::marker{color:var(--mm-prose-marker-ol)}:host ::ng-deep .mm-prose code{font-family:Roboto Mono,monospace;background:var(--mm-prose-code-bg);color:var(--mm-prose-code-text);padding:.2em .4em;border-radius:4px;font-size:.9em}:host ::ng-deep .mm-prose pre{background:var(--mm-prose-pre-bg);border:1px solid var(--mm-prose-pre-border);border-radius:8px;padding:1em;overflow-x:auto;margin-bottom:1em}:host ::ng-deep .mm-prose pre code{background:transparent;padding:0;color:var(--mm-prose-text)}:host ::ng-deep .mm-prose blockquote{border-left:4px solid var(--mm-prose-blockquote-border);background:var(--mm-prose-blockquote-bg);padding:.75em 1em;margin:1em 0;border-radius:0 8px 8px 0}:host ::ng-deep .mm-prose blockquote p:last-child{margin-bottom:0}:host ::ng-deep .mm-prose table{width:100%;border-collapse:collapse;margin-bottom:1em}:host ::ng-deep .mm-prose th,:host ::ng-deep .mm-prose td{padding:.75em;border:1px solid var(--mm-prose-table-border);text-align:left}:host ::ng-deep .mm-prose th{background:var(--mm-prose-table-header-bg);color:var(--mm-prose-table-header-text);font-weight:600;text-transform:uppercase;font-size:.85em;letter-spacing:.5px}:host ::ng-deep .mm-prose tr:nth-child(2n){background:var(--mm-prose-table-row-even)}:host ::ng-deep .mm-prose hr{border:none;height:2px;background:linear-gradient(90deg,var(--mm-prose-hr-start),var(--mm-prose-hr-end),transparent);margin:1.5em 0}:host ::ng-deep .mm-prose img{max-width:100%;height:auto;border-radius:8px;border:1px solid var(--mm-prose-img-border)}:host ::ng-deep .mm-prose strong,:host ::ng-deep .mm-prose b{font-weight:600;color:var(--mm-prose-strong)}:host ::ng-deep .mm-prose em,:host ::ng-deep .mm-prose i{color:var(--mm-prose-em)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MarkdownModule }, { kind: "component", type: i2$
|
|
17300
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: MarkdownWidgetComponent, isStandalone: true, selector: "mm-markdown-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"markdown-widget\"\n [class.loading]=\"isLoading()\"\n [class.error]=\"error()\"\n [ngStyle]=\"containerStyle()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured\n message=\"No markdown content configured.\">\n </mm-widget-not-configured>\n } @else {\n <div class=\"markdown-content mm-prose\">\n <markdown [data]=\"resolvedContent()\"></markdown>\n </div>\n }\n</div>\n", styles: [".markdown-widget{display:flex;flex-direction:column;height:100%;overflow:auto}.markdown-content{flex:1;overflow:auto}:host{--mm-prose-text: #333333;--mm-prose-heading: #1976d2;--mm-prose-link: #1565c0;--mm-prose-link-hover: #0d47a1;--mm-prose-code-bg: #f5f5f5;--mm-prose-code-text: #d32f2f;--mm-prose-pre-bg: #f5f5f5;--mm-prose-pre-border: #e0e0e0;--mm-prose-blockquote-border: #6c4da8;--mm-prose-blockquote-bg: rgba(108, 77, 168, .05);--mm-prose-table-header-bg: #f5f5f5;--mm-prose-table-header-text: #333333;--mm-prose-table-border: #e0e0e0;--mm-prose-table-row-even: rgba(0, 0, 0, .02);--mm-prose-strong: #111111;--mm-prose-em: #1565c0;--mm-prose-marker-ul: #1976d2;--mm-prose-marker-ol: #1565c0;--mm-prose-hr-start: #1976d2;--mm-prose-hr-end: #1565c0;--mm-prose-img-border: #e0e0e0;--mm-prose-font: \"Roboto\", sans-serif}:host ::ng-deep .mm-prose{color:var(--mm-prose-text);font-family:var(--mm-prose-font);line-height:1.6}:host ::ng-deep .mm-prose h1,:host ::ng-deep .mm-prose h2,:host ::ng-deep .mm-prose h3,:host ::ng-deep .mm-prose h4,:host ::ng-deep .mm-prose h5,:host ::ng-deep .mm-prose h6{color:var(--mm-prose-heading);font-weight:600;margin-top:1.5em;margin-bottom:.5em;letter-spacing:.5px}:host ::ng-deep .mm-prose h1:first-child,:host ::ng-deep .mm-prose h2:first-child,:host ::ng-deep .mm-prose h3:first-child,:host ::ng-deep .mm-prose h4:first-child,:host ::ng-deep .mm-prose h5:first-child,:host ::ng-deep .mm-prose h6:first-child{margin-top:0}:host ::ng-deep .mm-prose h1{font-size:1.75rem;border-bottom:2px solid var(--mm-prose-heading);padding-bottom:.25em}:host ::ng-deep .mm-prose h2{font-size:1.5rem;border-bottom:1px solid var(--mm-prose-table-border);padding-bottom:.2em}:host ::ng-deep .mm-prose h3{font-size:1.25rem}:host ::ng-deep .mm-prose h4{font-size:1.1rem}:host ::ng-deep .mm-prose h5,:host ::ng-deep .mm-prose h6{font-size:1rem}:host ::ng-deep .mm-prose p{margin-bottom:1em}:host ::ng-deep .mm-prose p:last-child{margin-bottom:0}:host ::ng-deep .mm-prose a{color:var(--mm-prose-link);text-decoration:none;border-bottom:1px solid transparent;transition:all .15s ease}:host ::ng-deep .mm-prose a:hover{color:var(--mm-prose-link-hover);border-bottom-color:var(--mm-prose-link-hover)}:host ::ng-deep .mm-prose ul,:host ::ng-deep .mm-prose ol{margin-bottom:1em;padding-left:1.5em}:host ::ng-deep .mm-prose li{margin-bottom:.25em}:host ::ng-deep .mm-prose ul li::marker{color:var(--mm-prose-marker-ul)}:host ::ng-deep .mm-prose ol li::marker{color:var(--mm-prose-marker-ol)}:host ::ng-deep .mm-prose code{font-family:Roboto Mono,monospace;background:var(--mm-prose-code-bg);color:var(--mm-prose-code-text);padding:.2em .4em;border-radius:4px;font-size:.9em}:host ::ng-deep .mm-prose pre{background:var(--mm-prose-pre-bg);border:1px solid var(--mm-prose-pre-border);border-radius:8px;padding:1em;overflow-x:auto;margin-bottom:1em}:host ::ng-deep .mm-prose pre code{background:transparent;padding:0;color:var(--mm-prose-text)}:host ::ng-deep .mm-prose blockquote{border-left:4px solid var(--mm-prose-blockquote-border);background:var(--mm-prose-blockquote-bg);padding:.75em 1em;margin:1em 0;border-radius:0 8px 8px 0}:host ::ng-deep .mm-prose blockquote p:last-child{margin-bottom:0}:host ::ng-deep .mm-prose table{width:100%;border-collapse:collapse;margin-bottom:1em}:host ::ng-deep .mm-prose th,:host ::ng-deep .mm-prose td{padding:.75em;border:1px solid var(--mm-prose-table-border);text-align:left}:host ::ng-deep .mm-prose th{background:var(--mm-prose-table-header-bg);color:var(--mm-prose-table-header-text);font-weight:600;text-transform:uppercase;font-size:.85em;letter-spacing:.5px}:host ::ng-deep .mm-prose tr:nth-child(2n){background:var(--mm-prose-table-row-even)}:host ::ng-deep .mm-prose hr{border:none;height:2px;background:linear-gradient(90deg,var(--mm-prose-hr-start),var(--mm-prose-hr-end),transparent);margin:1.5em 0}:host ::ng-deep .mm-prose img{max-width:100%;height:auto;border-radius:8px;border:1px solid var(--mm-prose-img-border)}:host ::ng-deep .mm-prose strong,:host ::ng-deep .mm-prose b{font-weight:600;color:var(--mm-prose-strong)}:host ::ng-deep .mm-prose em,:host ::ng-deep .mm-prose i{color:var(--mm-prose-em)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MarkdownModule }, { kind: "component", type: i2$1.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
17025
17301
|
}
|
|
17026
17302
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MarkdownWidgetComponent, decorators: [{
|
|
17027
17303
|
type: Component,
|
|
@@ -17159,7 +17435,7 @@ Variables: $variableName or \${variableName}">
|
|
|
17159
17435
|
<button kendoButton themeColor="primary" (click)="onSave()">Save</button>
|
|
17160
17436
|
</div>
|
|
17161
17437
|
</div>
|
|
17162
|
-
`, isInline: true, styles: [":host{display:block;height:100%;--mm-prose-editor-bg: #f5f5f5;--mm-prose-editor-text: #333333;--mm-prose-editor-placeholder: #999999;--mm-prose-editor-border: #e0e0e0;--mm-prose-preview-border: #e0e0e0;--mm-prose-form-bg: #f5f5f5;--mm-prose-form-border: #e0e0e0;--mm-prose-label-text: #333333;--mm-prose-hint-text: #666666}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:16px;height:100%;flex:1;overflow-y:auto}.mode-toggle{display:flex;gap:8px}.editor-area{flex:1;min-height:300px;display:flex;flex-direction:column}.markdown-editor{flex:1;width:100%;font-family:Roboto Mono,monospace;font-size:.9rem;padding:12px;border:1px solid var(--mm-prose-editor-border);border-radius:4px;resize:none;background:var(--mm-prose-editor-bg);color:var(--mm-prose-editor-text)}.markdown-editor::placeholder{color:var(--mm-prose-editor-placeholder)}.markdown-preview{flex:1;padding:12px;border:1px solid var(--mm-prose-preview-border);border-radius:4px;overflow:auto;background:var(--mm-prose-editor-bg)}.form-section{padding:12px;background:var(--mm-prose-form-bg);border:1px solid var(--mm-prose-form-border);border-radius:4px}.form-row{display:flex;gap:16px;align-items:flex-start}.form-field{display:flex;flex-direction:column;gap:4px}.form-field label{font-weight:500;display:flex;align-items:center;gap:8px;color:var(--mm-prose-label-text)}.field-hint{margin:0;font-size:.8rem;color:var(--mm-prose-hint-text)}.mm-prose{color:var(--mm-prose-text, #333333);line-height:1.6}.mm-prose h1,.mm-prose h2,.mm-prose h3,.mm-prose h4,.mm-prose h5,.mm-prose h6{color:var(--mm-prose-heading, #1976d2);font-weight:600;margin-top:1em;margin-bottom:.5em}.mm-prose h1{font-size:1.5rem}.mm-prose h2{font-size:1.25rem}.mm-prose h3{font-size:1.1rem}.mm-prose p{margin-bottom:.75em}.mm-prose a{color:var(--mm-prose-link, #1565c0)}.mm-prose code{background:var(--mm-prose-code-bg, #f5f5f5);color:var(--mm-prose-code-text, #d32f2f);padding:.2em .4em;border-radius:4px}.mm-prose pre{background:var(--mm-prose-pre-bg, #f5f5f5);color:var(--mm-prose-text, #333333);padding:1em;border-radius:8px;overflow-x:auto}.mm-prose pre code{background:transparent;color:var(--mm-prose-text, #333333)}.mm-prose blockquote{border-left:4px solid var(--mm-prose-blockquote-border, #6c4da8);background:var(--mm-prose-blockquote-bg, rgba(108, 77, 168, .05));padding:.75em 1em;margin:1em 0;border-radius:0 8px 8px 0}.mm-prose strong,.mm-prose b{color:var(--mm-prose-strong, #111111);font-weight:600}.mm-prose em,.mm-prose i{color:var(--mm-prose-em, #1565c0)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i2.ButtonGroupComponent, selector: "kendo-buttongroup", inputs: ["disabled", "selection", "width", "tabIndex", "navigable"], outputs: ["navigate"], exportAs: ["kendoButtonGroup"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.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: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "ngmodule", type: MarkdownModule }, { kind: "component", type: i2$
|
|
17438
|
+
`, isInline: true, styles: [":host{display:block;height:100%;--mm-prose-editor-bg: #f5f5f5;--mm-prose-editor-text: #333333;--mm-prose-editor-placeholder: #999999;--mm-prose-editor-border: #e0e0e0;--mm-prose-preview-border: #e0e0e0;--mm-prose-form-bg: #f5f5f5;--mm-prose-form-border: #e0e0e0;--mm-prose-label-text: #333333;--mm-prose-hint-text: #666666}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:16px;height:100%;flex:1;overflow-y:auto}.mode-toggle{display:flex;gap:8px}.editor-area{flex:1;min-height:300px;display:flex;flex-direction:column}.markdown-editor{flex:1;width:100%;font-family:Roboto Mono,monospace;font-size:.9rem;padding:12px;border:1px solid var(--mm-prose-editor-border);border-radius:4px;resize:none;background:var(--mm-prose-editor-bg);color:var(--mm-prose-editor-text)}.markdown-editor::placeholder{color:var(--mm-prose-editor-placeholder)}.markdown-preview{flex:1;padding:12px;border:1px solid var(--mm-prose-preview-border);border-radius:4px;overflow:auto;background:var(--mm-prose-editor-bg)}.form-section{padding:12px;background:var(--mm-prose-form-bg);border:1px solid var(--mm-prose-form-border);border-radius:4px}.form-row{display:flex;gap:16px;align-items:flex-start}.form-field{display:flex;flex-direction:column;gap:4px}.form-field label{font-weight:500;display:flex;align-items:center;gap:8px;color:var(--mm-prose-label-text)}.field-hint{margin:0;font-size:.8rem;color:var(--mm-prose-hint-text)}.mm-prose{color:var(--mm-prose-text, #333333);line-height:1.6}.mm-prose h1,.mm-prose h2,.mm-prose h3,.mm-prose h4,.mm-prose h5,.mm-prose h6{color:var(--mm-prose-heading, #1976d2);font-weight:600;margin-top:1em;margin-bottom:.5em}.mm-prose h1{font-size:1.5rem}.mm-prose h2{font-size:1.25rem}.mm-prose h3{font-size:1.1rem}.mm-prose p{margin-bottom:.75em}.mm-prose a{color:var(--mm-prose-link, #1565c0)}.mm-prose code{background:var(--mm-prose-code-bg, #f5f5f5);color:var(--mm-prose-code-text, #d32f2f);padding:.2em .4em;border-radius:4px}.mm-prose pre{background:var(--mm-prose-pre-bg, #f5f5f5);color:var(--mm-prose-text, #333333);padding:1em;border-radius:8px;overflow-x:auto}.mm-prose pre code{background:transparent;color:var(--mm-prose-text, #333333)}.mm-prose blockquote{border-left:4px solid var(--mm-prose-blockquote-border, #6c4da8);background:var(--mm-prose-blockquote-bg, rgba(108, 77, 168, .05));padding:.75em 1em;margin:1em 0;border-radius:0 8px 8px 0}.mm-prose strong,.mm-prose b{color:var(--mm-prose-strong, #111111);font-weight:600}.mm-prose em,.mm-prose i{color:var(--mm-prose-em, #1565c0)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i2.ButtonGroupComponent, selector: "kendo-buttongroup", inputs: ["disabled", "selection", "width", "tabIndex", "navigable"], outputs: ["navigate"], exportAs: ["kendoButtonGroup"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.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: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "ngmodule", type: MarkdownModule }, { kind: "component", type: i2$1.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }] });
|
|
17163
17439
|
}
|
|
17164
17440
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MarkdownConfigDialogComponent, decorators: [{
|
|
17165
17441
|
type: Component,
|
|
@@ -17279,149 +17555,1620 @@ Variables: $variableName or \${variableName}">
|
|
|
17279
17555
|
type: Input
|
|
17280
17556
|
}] } });
|
|
17281
17557
|
|
|
17282
|
-
class
|
|
17283
|
-
|
|
17284
|
-
|
|
17285
|
-
|
|
17286
|
-
|
|
17287
|
-
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
|
|
17291
|
-
|
|
17292
|
-
|
|
17293
|
-
|
|
17294
|
-
* @param searchText - Optional search text to filter diagrams
|
|
17295
|
-
* @returns List of process diagram summaries
|
|
17296
|
-
*/
|
|
17297
|
-
async loadDiagramList(searchText) {
|
|
17298
|
-
try {
|
|
17299
|
-
const result = await firstValueFrom(this.getProcessDiagramsGQL.fetch({
|
|
17300
|
-
first: 100,
|
|
17301
|
-
searchFilter: searchText ? { searchTerm: searchText, language: 'de' } : undefined
|
|
17302
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Apollo fetch requires flexible variable typing
|
|
17303
|
-
}));
|
|
17304
|
-
const items = result.data?.runtime?.systemUIProcessDiagram?.items || [];
|
|
17305
|
-
return items
|
|
17306
|
-
.filter((item) => item !== null)
|
|
17307
|
-
.map(item => ({
|
|
17308
|
-
rtId: item.rtId,
|
|
17309
|
-
name: item.name,
|
|
17310
|
-
description: item.description,
|
|
17311
|
-
version: item.version,
|
|
17312
|
-
canvasWidth: item.canvasWidth,
|
|
17313
|
-
canvasHeight: item.canvasHeight
|
|
17314
|
-
}));
|
|
17315
|
-
}
|
|
17316
|
-
catch (error) {
|
|
17317
|
-
console.error('Error loading process diagrams:', error);
|
|
17318
|
-
return [];
|
|
17319
|
-
}
|
|
17558
|
+
class StatusListWidgetComponent {
|
|
17559
|
+
getEntitiesByCkTypeGQL = inject(GetEntitiesByCkTypeDtoGQL);
|
|
17560
|
+
config;
|
|
17561
|
+
_isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
|
|
17562
|
+
_error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
|
|
17563
|
+
_items = signal([], ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
|
|
17564
|
+
isLoading = this._isLoading.asReadonly();
|
|
17565
|
+
error = this._error.asReadonly();
|
|
17566
|
+
data = this._items.asReadonly();
|
|
17567
|
+
items = this._items.asReadonly();
|
|
17568
|
+
isNotConfigured() {
|
|
17569
|
+
return !this.config?.ckTypeId || !this.config?.labelField || !this.config?.statusField;
|
|
17320
17570
|
}
|
|
17321
|
-
|
|
17322
|
-
|
|
17323
|
-
*
|
|
17324
|
-
* @param rtId - The runtime ID of the process diagram
|
|
17325
|
-
* @returns The process diagram configuration
|
|
17326
|
-
*/
|
|
17327
|
-
async loadDiagram(rtId) {
|
|
17328
|
-
const result = await firstValueFrom(this.getProcessDiagramGQL.fetch({ variables: { rtId } }));
|
|
17329
|
-
const item = result.data?.runtime?.systemUIProcessDiagram?.items?.[0];
|
|
17330
|
-
if (!item) {
|
|
17331
|
-
throw new Error(`Process diagram not found: ${rtId}`);
|
|
17332
|
-
}
|
|
17333
|
-
// Parse JSON fields
|
|
17334
|
-
const elements = this.parseJsonField(item.elements, []);
|
|
17335
|
-
const itemWithExtensions = item;
|
|
17336
|
-
const primitives = itemWithExtensions.primitives
|
|
17337
|
-
? this.parseJsonField(itemWithExtensions.primitives, [])
|
|
17338
|
-
: undefined;
|
|
17339
|
-
const symbolInstances = itemWithExtensions.symbolInstances
|
|
17340
|
-
? this.parseJsonField(itemWithExtensions.symbolInstances, [])
|
|
17341
|
-
: undefined;
|
|
17342
|
-
const connections = this.parseJsonField(item.connections, []);
|
|
17343
|
-
const variables = item.variables ? this.parseJsonField(item.variables, []) : undefined;
|
|
17344
|
-
// Parse diagram-level property fields
|
|
17345
|
-
const transformProperties = itemWithExtensions.transformProperties
|
|
17346
|
-
? this.parseJsonField(itemWithExtensions.transformProperties, [])
|
|
17347
|
-
: undefined;
|
|
17348
|
-
const propertyBindings = itemWithExtensions.propertyBindings
|
|
17349
|
-
? this.parseJsonField(itemWithExtensions.propertyBindings, [])
|
|
17350
|
-
: undefined;
|
|
17351
|
-
const animations = itemWithExtensions.animations
|
|
17352
|
-
? this.parseJsonField(itemWithExtensions.animations, [])
|
|
17353
|
-
: undefined;
|
|
17354
|
-
return {
|
|
17355
|
-
id: item.rtId,
|
|
17356
|
-
name: item.name,
|
|
17357
|
-
description: item.description ?? undefined,
|
|
17358
|
-
version: item.version,
|
|
17359
|
-
canvas: {
|
|
17360
|
-
width: item.canvasWidth,
|
|
17361
|
-
height: item.canvasHeight,
|
|
17362
|
-
backgroundColor: item.canvasBackgroundColor ?? undefined
|
|
17363
|
-
},
|
|
17364
|
-
elements,
|
|
17365
|
-
primitives,
|
|
17366
|
-
symbolInstances,
|
|
17367
|
-
connections,
|
|
17368
|
-
variables,
|
|
17369
|
-
transformProperties,
|
|
17370
|
-
propertyBindings,
|
|
17371
|
-
animations,
|
|
17372
|
-
refreshInterval: item.refreshInterval ?? undefined
|
|
17373
|
-
};
|
|
17571
|
+
ngOnInit() {
|
|
17572
|
+
this.loadData();
|
|
17374
17573
|
}
|
|
17375
|
-
|
|
17376
|
-
|
|
17377
|
-
|
|
17378
|
-
* @param diagram - The process diagram configuration to create
|
|
17379
|
-
* @returns The created diagram with its new rtId
|
|
17380
|
-
*/
|
|
17381
|
-
async createDiagram(diagram) {
|
|
17382
|
-
const input = this.toInputDto(diagram);
|
|
17383
|
-
const result = await firstValueFrom(this.createProcessDiagramGQL.mutate({
|
|
17384
|
-
variables: { entities: [input] }
|
|
17385
|
-
}));
|
|
17386
|
-
const created = result.data?.runtime?.systemUIProcessDiagrams?.create?.[0];
|
|
17387
|
-
if (!created) {
|
|
17388
|
-
throw new Error('Failed to create process diagram');
|
|
17574
|
+
ngOnChanges(changes) {
|
|
17575
|
+
if (changes['config'] && !changes['config'].firstChange) {
|
|
17576
|
+
this.loadData();
|
|
17389
17577
|
}
|
|
17390
|
-
return {
|
|
17391
|
-
...diagram,
|
|
17392
|
-
id: created.rtId
|
|
17393
|
-
};
|
|
17394
17578
|
}
|
|
17395
|
-
|
|
17396
|
-
|
|
17397
|
-
|
|
17398
|
-
|
|
17399
|
-
|
|
17400
|
-
|
|
17401
|
-
|
|
17402
|
-
|
|
17403
|
-
|
|
17579
|
+
refresh() {
|
|
17580
|
+
this.loadData();
|
|
17581
|
+
}
|
|
17582
|
+
async loadData() {
|
|
17583
|
+
if (this.isNotConfigured())
|
|
17584
|
+
return;
|
|
17585
|
+
this._isLoading.set(true);
|
|
17586
|
+
this._error.set(null);
|
|
17587
|
+
try {
|
|
17588
|
+
const result = await firstValueFrom(this.getEntitiesByCkTypeGQL.fetch({
|
|
17589
|
+
variables: {
|
|
17590
|
+
ckTypeId: this.config.ckTypeId,
|
|
17591
|
+
first: 50
|
|
17592
|
+
}
|
|
17593
|
+
}));
|
|
17594
|
+
const entities = result.data?.runtime?.runtimeEntities?.items ?? [];
|
|
17595
|
+
const items = [];
|
|
17596
|
+
for (const entity of entities) {
|
|
17597
|
+
if (!entity)
|
|
17598
|
+
continue;
|
|
17599
|
+
const attrs = (entity.attributes?.items ?? [])
|
|
17600
|
+
.filter((a) => a != null && a.attributeName != null);
|
|
17601
|
+
const label = this.getAttributeValue(attrs, this.config.labelField) ?? '';
|
|
17602
|
+
const status = this.getAttributeValue(attrs, this.config.statusField) ?? '';
|
|
17603
|
+
const colorConfig = this.config.statusColors?.[status];
|
|
17604
|
+
items.push({
|
|
17605
|
+
label,
|
|
17606
|
+
status,
|
|
17607
|
+
color: colorConfig?.color ?? '#6b7280',
|
|
17608
|
+
displayLabel: colorConfig?.label ?? status
|
|
17609
|
+
});
|
|
17610
|
+
}
|
|
17611
|
+
this._items.set(items);
|
|
17404
17612
|
}
|
|
17405
|
-
|
|
17406
|
-
|
|
17407
|
-
|
|
17408
|
-
|
|
17409
|
-
|
|
17410
|
-
variables: { entities: [input] }
|
|
17411
|
-
}));
|
|
17412
|
-
const updated = result.data?.runtime?.systemUIProcessDiagrams?.update?.[0];
|
|
17413
|
-
if (!updated) {
|
|
17414
|
-
throw new Error('Failed to update process diagram');
|
|
17613
|
+
catch (err) {
|
|
17614
|
+
this._error.set(err instanceof Error ? err.message : 'Failed to load data');
|
|
17615
|
+
}
|
|
17616
|
+
finally {
|
|
17617
|
+
this._isLoading.set(false);
|
|
17415
17618
|
}
|
|
17416
|
-
return diagram;
|
|
17417
17619
|
}
|
|
17418
|
-
|
|
17419
|
-
|
|
17420
|
-
|
|
17421
|
-
|
|
17422
|
-
|
|
17423
|
-
|
|
17424
|
-
|
|
17620
|
+
getAttributeValue(attrs, field) {
|
|
17621
|
+
const attr = attrs.find(a => a.attributeName === field);
|
|
17622
|
+
return attr?.value != null ? String(attr.value) : null;
|
|
17623
|
+
}
|
|
17624
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: StatusListWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17625
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: StatusListWidgetComponent, isStandalone: true, selector: "mm-status-list-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: `
|
|
17626
|
+
<div class="status-list-widget">
|
|
17627
|
+
@if (isNotConfigured()) {
|
|
17628
|
+
<mm-widget-not-configured></mm-widget-not-configured>
|
|
17629
|
+
} @else if (error()) {
|
|
17630
|
+
<div class="error-message">{{ error() }}</div>
|
|
17631
|
+
} @else {
|
|
17632
|
+
<div class="status-list">
|
|
17633
|
+
@for (item of items(); track item.label) {
|
|
17634
|
+
<div class="status-list-item">
|
|
17635
|
+
<span class="item-label">{{ item.label }}</span>
|
|
17636
|
+
<span class="item-badge" [style.background-color]="item.color">
|
|
17637
|
+
{{ item.displayLabel }}
|
|
17638
|
+
</span>
|
|
17639
|
+
</div>
|
|
17640
|
+
}
|
|
17641
|
+
@if (!isLoading() && items().length === 0) {
|
|
17642
|
+
<div class="empty-message">No items found</div>
|
|
17643
|
+
}
|
|
17644
|
+
</div>
|
|
17645
|
+
}
|
|
17646
|
+
</div>
|
|
17647
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.status-list-widget{height:100%;display:flex;flex-direction:column;padding:8px 0;overflow-y:auto}.status-list{display:flex;flex-direction:column;gap:6px;padding:0 12px}.status-list-item{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-radius:6px;background:var(--mm-status-list-item-bg, rgba(255, 255, 255, .04));border:1px solid var(--mm-status-list-item-border, rgba(255, 255, 255, .06))}.item-label{font-size:.9rem;font-weight:500;color:var(--kendo-color-on-app-surface, inherit)}.item-badge{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;padding:4px 10px;border-radius:4px;color:#fff;white-space:nowrap}.empty-message{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.error-message{text-align:center;padding:16px;color:var(--kendo-color-error, #dc3545)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
17648
|
+
}
|
|
17649
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: StatusListWidgetComponent, decorators: [{
|
|
17650
|
+
type: Component,
|
|
17651
|
+
args: [{ selector: 'mm-status-list-widget', standalone: true, imports: [CommonModule, WidgetNotConfiguredComponent], template: `
|
|
17652
|
+
<div class="status-list-widget">
|
|
17653
|
+
@if (isNotConfigured()) {
|
|
17654
|
+
<mm-widget-not-configured></mm-widget-not-configured>
|
|
17655
|
+
} @else if (error()) {
|
|
17656
|
+
<div class="error-message">{{ error() }}</div>
|
|
17657
|
+
} @else {
|
|
17658
|
+
<div class="status-list">
|
|
17659
|
+
@for (item of items(); track item.label) {
|
|
17660
|
+
<div class="status-list-item">
|
|
17661
|
+
<span class="item-label">{{ item.label }}</span>
|
|
17662
|
+
<span class="item-badge" [style.background-color]="item.color">
|
|
17663
|
+
{{ item.displayLabel }}
|
|
17664
|
+
</span>
|
|
17665
|
+
</div>
|
|
17666
|
+
}
|
|
17667
|
+
@if (!isLoading() && items().length === 0) {
|
|
17668
|
+
<div class="empty-message">No items found</div>
|
|
17669
|
+
}
|
|
17670
|
+
</div>
|
|
17671
|
+
}
|
|
17672
|
+
</div>
|
|
17673
|
+
`, styles: [":host{display:block;width:100%;height:100%}.status-list-widget{height:100%;display:flex;flex-direction:column;padding:8px 0;overflow-y:auto}.status-list{display:flex;flex-direction:column;gap:6px;padding:0 12px}.status-list-item{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-radius:6px;background:var(--mm-status-list-item-bg, rgba(255, 255, 255, .04));border:1px solid var(--mm-status-list-item-border, rgba(255, 255, 255, .06))}.item-label{font-size:.9rem;font-weight:500;color:var(--kendo-color-on-app-surface, inherit)}.item-badge{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;padding:4px 10px;border-radius:4px;color:#fff;white-space:nowrap}.empty-message{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.error-message{text-align:center;padding:16px;color:var(--kendo-color-error, #dc3545)}\n"] }]
|
|
17674
|
+
}], propDecorators: { config: [{
|
|
17675
|
+
type: Input
|
|
17676
|
+
}] } });
|
|
17677
|
+
|
|
17678
|
+
class StatusListConfigDialogComponent {
|
|
17679
|
+
windowRef = inject(WindowRef);
|
|
17680
|
+
initialCkTypeId;
|
|
17681
|
+
initialLabelField;
|
|
17682
|
+
initialStatusField;
|
|
17683
|
+
initialStatusColors;
|
|
17684
|
+
form = {
|
|
17685
|
+
ckTypeId: '',
|
|
17686
|
+
labelField: '',
|
|
17687
|
+
statusField: ''
|
|
17688
|
+
};
|
|
17689
|
+
statusColorEntries = [];
|
|
17690
|
+
get isValid() {
|
|
17691
|
+
return !!this.form.ckTypeId && !!this.form.labelField && !!this.form.statusField;
|
|
17692
|
+
}
|
|
17693
|
+
ngOnInit() {
|
|
17694
|
+
this.form.ckTypeId = this.initialCkTypeId ?? '';
|
|
17695
|
+
this.form.labelField = this.initialLabelField ?? '';
|
|
17696
|
+
this.form.statusField = this.initialStatusField ?? '';
|
|
17697
|
+
if (this.initialStatusColors) {
|
|
17698
|
+
this.statusColorEntries = Object.entries(this.initialStatusColors).map(([key, val]) => ({
|
|
17699
|
+
key,
|
|
17700
|
+
color: val.color,
|
|
17701
|
+
label: val.label ?? ''
|
|
17702
|
+
}));
|
|
17703
|
+
}
|
|
17704
|
+
}
|
|
17705
|
+
addStatusColor() {
|
|
17706
|
+
this.statusColorEntries.push({ key: '', color: '#10b981', label: '' });
|
|
17707
|
+
}
|
|
17708
|
+
removeStatusColor(index) {
|
|
17709
|
+
this.statusColorEntries.splice(index, 1);
|
|
17710
|
+
}
|
|
17711
|
+
onSave() {
|
|
17712
|
+
const statusColors = {};
|
|
17713
|
+
for (const entry of this.statusColorEntries) {
|
|
17714
|
+
if (entry.key) {
|
|
17715
|
+
statusColors[entry.key] = { color: entry.color, label: entry.label || undefined };
|
|
17716
|
+
}
|
|
17717
|
+
}
|
|
17718
|
+
this.windowRef.close({
|
|
17719
|
+
ckTypeId: this.form.ckTypeId,
|
|
17720
|
+
labelField: this.form.labelField,
|
|
17721
|
+
statusField: this.form.statusField,
|
|
17722
|
+
statusColors: Object.keys(statusColors).length > 0 ? statusColors : undefined
|
|
17723
|
+
});
|
|
17724
|
+
}
|
|
17725
|
+
onCancel() {
|
|
17726
|
+
this.windowRef.close();
|
|
17727
|
+
}
|
|
17728
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: StatusListConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17729
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: StatusListConfigDialogComponent, isStandalone: true, selector: "mm-status-list-config-dialog", inputs: { initialCkTypeId: "initialCkTypeId", initialLabelField: "initialLabelField", initialStatusField: "initialStatusField", initialStatusColors: "initialStatusColors" }, ngImport: i0, template: `
|
|
17730
|
+
<div class="config-container">
|
|
17731
|
+
<div class="config-form">
|
|
17732
|
+
<div class="form-field">
|
|
17733
|
+
<label>Entity Type <span class="required">*</span></label>
|
|
17734
|
+
<kendo-textbox [(ngModel)]="form.ckTypeId" placeholder="e.g. Environment/ComplianceRecord"></kendo-textbox>
|
|
17735
|
+
<p class="field-hint">Full CK type ID</p>
|
|
17736
|
+
</div>
|
|
17737
|
+
|
|
17738
|
+
<div class="form-field">
|
|
17739
|
+
<label>Label Field <span class="required">*</span></label>
|
|
17740
|
+
<kendo-textbox [(ngModel)]="form.labelField" placeholder="e.g. name"></kendo-textbox>
|
|
17741
|
+
<p class="field-hint">Attribute name for the item label</p>
|
|
17742
|
+
</div>
|
|
17743
|
+
|
|
17744
|
+
<div class="form-field">
|
|
17745
|
+
<label>Status Field <span class="required">*</span></label>
|
|
17746
|
+
<kendo-textbox [(ngModel)]="form.statusField" placeholder="e.g. complianceStatus"></kendo-textbox>
|
|
17747
|
+
<p class="field-hint">Attribute name for the status value (enum)</p>
|
|
17748
|
+
</div>
|
|
17749
|
+
|
|
17750
|
+
<div class="form-section">
|
|
17751
|
+
<h4>Status Colors</h4>
|
|
17752
|
+
<p class="field-hint">Map status values to badge colors and labels.</p>
|
|
17753
|
+
@for (entry of statusColorEntries; track $index) {
|
|
17754
|
+
<div class="color-row">
|
|
17755
|
+
<kendo-textbox [(ngModel)]="entry.key" placeholder="Status value" style="width: 120px;"></kendo-textbox>
|
|
17756
|
+
<kendo-textbox [(ngModel)]="entry.color" placeholder="#10b981" style="width: 100px;"></kendo-textbox>
|
|
17757
|
+
<kendo-textbox [(ngModel)]="entry.label" placeholder="Badge label" style="flex: 1;"></kendo-textbox>
|
|
17758
|
+
<button kendoButton fillMode="flat" (click)="removeStatusColor($index)">Remove</button>
|
|
17759
|
+
</div>
|
|
17760
|
+
}
|
|
17761
|
+
<button kendoButton fillMode="flat" (click)="addStatusColor()">+ Add Status Color</button>
|
|
17762
|
+
</div>
|
|
17763
|
+
</div>
|
|
17764
|
+
|
|
17765
|
+
<div class="action-bar mm-dialog-actions">
|
|
17766
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
17767
|
+
<button kendoButton themeColor="primary" [disabled]="!isValid" (click)="onSave()">Save</button>
|
|
17768
|
+
</div>
|
|
17769
|
+
</div>
|
|
17770
|
+
`, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.config-form{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.form-field{display:flex;flex-direction:column;gap:6px}.form-field label{font-weight:600;font-size:.9rem}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.color-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i3.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"] }] });
|
|
17771
|
+
}
|
|
17772
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: StatusListConfigDialogComponent, decorators: [{
|
|
17773
|
+
type: Component,
|
|
17774
|
+
args: [{ selector: 'mm-status-list-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule], template: `
|
|
17775
|
+
<div class="config-container">
|
|
17776
|
+
<div class="config-form">
|
|
17777
|
+
<div class="form-field">
|
|
17778
|
+
<label>Entity Type <span class="required">*</span></label>
|
|
17779
|
+
<kendo-textbox [(ngModel)]="form.ckTypeId" placeholder="e.g. Environment/ComplianceRecord"></kendo-textbox>
|
|
17780
|
+
<p class="field-hint">Full CK type ID</p>
|
|
17781
|
+
</div>
|
|
17782
|
+
|
|
17783
|
+
<div class="form-field">
|
|
17784
|
+
<label>Label Field <span class="required">*</span></label>
|
|
17785
|
+
<kendo-textbox [(ngModel)]="form.labelField" placeholder="e.g. name"></kendo-textbox>
|
|
17786
|
+
<p class="field-hint">Attribute name for the item label</p>
|
|
17787
|
+
</div>
|
|
17788
|
+
|
|
17789
|
+
<div class="form-field">
|
|
17790
|
+
<label>Status Field <span class="required">*</span></label>
|
|
17791
|
+
<kendo-textbox [(ngModel)]="form.statusField" placeholder="e.g. complianceStatus"></kendo-textbox>
|
|
17792
|
+
<p class="field-hint">Attribute name for the status value (enum)</p>
|
|
17793
|
+
</div>
|
|
17794
|
+
|
|
17795
|
+
<div class="form-section">
|
|
17796
|
+
<h4>Status Colors</h4>
|
|
17797
|
+
<p class="field-hint">Map status values to badge colors and labels.</p>
|
|
17798
|
+
@for (entry of statusColorEntries; track $index) {
|
|
17799
|
+
<div class="color-row">
|
|
17800
|
+
<kendo-textbox [(ngModel)]="entry.key" placeholder="Status value" style="width: 120px;"></kendo-textbox>
|
|
17801
|
+
<kendo-textbox [(ngModel)]="entry.color" placeholder="#10b981" style="width: 100px;"></kendo-textbox>
|
|
17802
|
+
<kendo-textbox [(ngModel)]="entry.label" placeholder="Badge label" style="flex: 1;"></kendo-textbox>
|
|
17803
|
+
<button kendoButton fillMode="flat" (click)="removeStatusColor($index)">Remove</button>
|
|
17804
|
+
</div>
|
|
17805
|
+
}
|
|
17806
|
+
<button kendoButton fillMode="flat" (click)="addStatusColor()">+ Add Status Color</button>
|
|
17807
|
+
</div>
|
|
17808
|
+
</div>
|
|
17809
|
+
|
|
17810
|
+
<div class="action-bar mm-dialog-actions">
|
|
17811
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
17812
|
+
<button kendoButton themeColor="primary" [disabled]="!isValid" (click)="onSave()">Save</button>
|
|
17813
|
+
</div>
|
|
17814
|
+
</div>
|
|
17815
|
+
`, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.config-form{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.form-field{display:flex;flex-direction:column;gap:6px}.form-field label{font-weight:600;font-size:.9rem}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.color-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}\n"] }]
|
|
17816
|
+
}], propDecorators: { initialCkTypeId: [{
|
|
17817
|
+
type: Input
|
|
17818
|
+
}], initialLabelField: [{
|
|
17819
|
+
type: Input
|
|
17820
|
+
}], initialStatusField: [{
|
|
17821
|
+
type: Input
|
|
17822
|
+
}], initialStatusColors: [{
|
|
17823
|
+
type: Input
|
|
17824
|
+
}] } });
|
|
17825
|
+
|
|
17826
|
+
class SummaryCardWidgetComponent {
|
|
17827
|
+
entityGQL = inject(GetDashboardEntityDtoGQL);
|
|
17828
|
+
dataService = inject(DashboardDataService);
|
|
17829
|
+
config;
|
|
17830
|
+
_isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
|
|
17831
|
+
_error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
|
|
17832
|
+
_tileValues = signal([], ...(ngDevMode ? [{ debugName: "_tileValues" }] : /* istanbul ignore next */ []));
|
|
17833
|
+
isLoading = this._isLoading.asReadonly();
|
|
17834
|
+
error = this._error.asReadonly();
|
|
17835
|
+
data = this._tileValues.asReadonly();
|
|
17836
|
+
tileValues = this._tileValues.asReadonly();
|
|
17837
|
+
isNotConfigured() {
|
|
17838
|
+
return !this.config?.tiles?.length;
|
|
17839
|
+
}
|
|
17840
|
+
ngOnInit() {
|
|
17841
|
+
this.loadData();
|
|
17842
|
+
}
|
|
17843
|
+
ngOnChanges(changes) {
|
|
17844
|
+
if (changes['config'] && !changes['config'].firstChange) {
|
|
17845
|
+
this.loadData();
|
|
17846
|
+
}
|
|
17847
|
+
}
|
|
17848
|
+
refresh() {
|
|
17849
|
+
this.loadData();
|
|
17850
|
+
}
|
|
17851
|
+
async loadData() {
|
|
17852
|
+
if (this.isNotConfigured())
|
|
17853
|
+
return;
|
|
17854
|
+
this._isLoading.set(true);
|
|
17855
|
+
this._error.set(null);
|
|
17856
|
+
try {
|
|
17857
|
+
// Cache entities by rtId to avoid duplicate fetches
|
|
17858
|
+
const entityCache = new Map();
|
|
17859
|
+
const results = await Promise.all(this.config.tiles.map(tile => this.fetchTileValue(tile, entityCache)));
|
|
17860
|
+
const tileValues = this.config.tiles.map((tile, i) => ({
|
|
17861
|
+
id: tile.id,
|
|
17862
|
+
label: tile.label,
|
|
17863
|
+
value: this.formatValue(results[i]),
|
|
17864
|
+
prefix: tile.prefix ?? '',
|
|
17865
|
+
suffix: tile.suffix ?? '',
|
|
17866
|
+
color: tile.color ?? 'default',
|
|
17867
|
+
fullWidth: tile.size === 'full'
|
|
17868
|
+
}));
|
|
17869
|
+
this._tileValues.set(tileValues);
|
|
17870
|
+
}
|
|
17871
|
+
catch (err) {
|
|
17872
|
+
this._error.set(err instanceof Error ? err.message : 'Failed to load data');
|
|
17873
|
+
}
|
|
17874
|
+
finally {
|
|
17875
|
+
this._isLoading.set(false);
|
|
17876
|
+
}
|
|
17877
|
+
}
|
|
17878
|
+
async fetchTileValue(tile, entityCache) {
|
|
17879
|
+
if (tile.entitySource) {
|
|
17880
|
+
const { rtId, ckTypeId, attributePath } = tile.entitySource;
|
|
17881
|
+
if (!entityCache.has(rtId)) {
|
|
17882
|
+
const attrs = await this.fetchEntityAttributes(rtId, ckTypeId);
|
|
17883
|
+
entityCache.set(rtId, attrs);
|
|
17884
|
+
}
|
|
17885
|
+
return entityCache.get(rtId)?.get(attributePath) ?? null;
|
|
17886
|
+
}
|
|
17887
|
+
if (tile.aggregationSource) {
|
|
17888
|
+
const query = {
|
|
17889
|
+
id: tile.id,
|
|
17890
|
+
ckTypeId: tile.aggregationSource.ckTypeId,
|
|
17891
|
+
aggregation: tile.aggregationSource.aggregation,
|
|
17892
|
+
attribute: tile.aggregationSource.attribute,
|
|
17893
|
+
filters: tile.aggregationSource.filters
|
|
17894
|
+
};
|
|
17895
|
+
const results = await this.dataService.fetchAggregations([query]);
|
|
17896
|
+
return results.get(tile.id) ?? null;
|
|
17897
|
+
}
|
|
17898
|
+
return null;
|
|
17899
|
+
}
|
|
17900
|
+
async fetchEntityAttributes(rtId, ckTypeId) {
|
|
17901
|
+
const result = await firstValueFrom(this.entityGQL.fetch({
|
|
17902
|
+
variables: { rtId, ckTypeId }
|
|
17903
|
+
}));
|
|
17904
|
+
const attrs = new Map();
|
|
17905
|
+
const items = result.data?.runtime?.runtimeEntities?.items?.[0]?.attributes?.items ?? [];
|
|
17906
|
+
for (const item of items) {
|
|
17907
|
+
if (item?.attributeName) {
|
|
17908
|
+
attrs.set(item.attributeName, item.value);
|
|
17909
|
+
}
|
|
17910
|
+
}
|
|
17911
|
+
return attrs;
|
|
17912
|
+
}
|
|
17913
|
+
formatValue(value) {
|
|
17914
|
+
if (value == null)
|
|
17915
|
+
return '-';
|
|
17916
|
+
if (typeof value === 'number') {
|
|
17917
|
+
return value.toLocaleString('de-AT', {
|
|
17918
|
+
minimumFractionDigits: value % 1 !== 0 ? 1 : 0,
|
|
17919
|
+
maximumFractionDigits: 2
|
|
17920
|
+
});
|
|
17921
|
+
}
|
|
17922
|
+
return String(value);
|
|
17923
|
+
}
|
|
17924
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SummaryCardWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17925
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SummaryCardWidgetComponent, isStandalone: true, selector: "mm-summary-card-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: `
|
|
17926
|
+
<div class="summary-card-widget">
|
|
17927
|
+
@if (isNotConfigured()) {
|
|
17928
|
+
<mm-widget-not-configured></mm-widget-not-configured>
|
|
17929
|
+
} @else if (error()) {
|
|
17930
|
+
<div class="error-message">{{ error() }}</div>
|
|
17931
|
+
} @else {
|
|
17932
|
+
<div class="summary-grid" [style.--columns]="config.columns ?? 2">
|
|
17933
|
+
@for (tile of tileValues(); track tile.id) {
|
|
17934
|
+
<div class="tile" [class]="tile.color" [class.full-width]="tile.fullWidth">
|
|
17935
|
+
<div class="tile-value">
|
|
17936
|
+
{{ tile.prefix }}{{ tile.value }}{{ tile.suffix }}
|
|
17937
|
+
</div>
|
|
17938
|
+
<div class="tile-label">{{ tile.label }}</div>
|
|
17939
|
+
</div>
|
|
17940
|
+
}
|
|
17941
|
+
</div>
|
|
17942
|
+
}
|
|
17943
|
+
</div>
|
|
17944
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.summary-card-widget{height:100%;display:flex;align-items:center;justify-content:center}.summary-grid{display:grid;grid-template-columns:repeat(var(--columns, 2),1fr);gap:8px;padding:8px 12px;width:100%}.tile{text-align:center;padding:12px 8px;border-radius:6px;background:var(--mm-summary-tile-bg, rgba(255, 255, 255, .03))}.tile-value{font-size:1.5rem;font-weight:700;color:inherit}.tile-label{font-size:.7rem;text-transform:uppercase;letter-spacing:.5px;margin-top:4px;opacity:.6}.tile.full-width{grid-column:1 / -1}.tile.primary .tile-value{color:var(--kendo-color-primary, #06b6d4)}.tile.success .tile-value{color:var(--kendo-color-success, #10b981)}.tile.warning .tile-value{color:var(--kendo-color-warning, #f59e0b)}.tile.error .tile-value{color:var(--kendo-color-error, #ef4444)}.error-message{text-align:center;padding:16px;color:var(--kendo-color-error, #dc3545)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
17945
|
+
}
|
|
17946
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SummaryCardWidgetComponent, decorators: [{
|
|
17947
|
+
type: Component,
|
|
17948
|
+
args: [{ selector: 'mm-summary-card-widget', standalone: true, imports: [CommonModule, WidgetNotConfiguredComponent], template: `
|
|
17949
|
+
<div class="summary-card-widget">
|
|
17950
|
+
@if (isNotConfigured()) {
|
|
17951
|
+
<mm-widget-not-configured></mm-widget-not-configured>
|
|
17952
|
+
} @else if (error()) {
|
|
17953
|
+
<div class="error-message">{{ error() }}</div>
|
|
17954
|
+
} @else {
|
|
17955
|
+
<div class="summary-grid" [style.--columns]="config.columns ?? 2">
|
|
17956
|
+
@for (tile of tileValues(); track tile.id) {
|
|
17957
|
+
<div class="tile" [class]="tile.color" [class.full-width]="tile.fullWidth">
|
|
17958
|
+
<div class="tile-value">
|
|
17959
|
+
{{ tile.prefix }}{{ tile.value }}{{ tile.suffix }}
|
|
17960
|
+
</div>
|
|
17961
|
+
<div class="tile-label">{{ tile.label }}</div>
|
|
17962
|
+
</div>
|
|
17963
|
+
}
|
|
17964
|
+
</div>
|
|
17965
|
+
}
|
|
17966
|
+
</div>
|
|
17967
|
+
`, styles: [":host{display:block;width:100%;height:100%}.summary-card-widget{height:100%;display:flex;align-items:center;justify-content:center}.summary-grid{display:grid;grid-template-columns:repeat(var(--columns, 2),1fr);gap:8px;padding:8px 12px;width:100%}.tile{text-align:center;padding:12px 8px;border-radius:6px;background:var(--mm-summary-tile-bg, rgba(255, 255, 255, .03))}.tile-value{font-size:1.5rem;font-weight:700;color:inherit}.tile-label{font-size:.7rem;text-transform:uppercase;letter-spacing:.5px;margin-top:4px;opacity:.6}.tile.full-width{grid-column:1 / -1}.tile.primary .tile-value{color:var(--kendo-color-primary, #06b6d4)}.tile.success .tile-value{color:var(--kendo-color-success, #10b981)}.tile.warning .tile-value{color:var(--kendo-color-warning, #f59e0b)}.tile.error .tile-value{color:var(--kendo-color-error, #ef4444)}.error-message{text-align:center;padding:16px;color:var(--kendo-color-error, #dc3545)}\n"] }]
|
|
17968
|
+
}], propDecorators: { config: [{
|
|
17969
|
+
type: Input
|
|
17970
|
+
}] } });
|
|
17971
|
+
|
|
17972
|
+
class SummaryCardConfigDialogComponent {
|
|
17973
|
+
windowRef = inject(WindowRef);
|
|
17974
|
+
initialColumns;
|
|
17975
|
+
initialTiles;
|
|
17976
|
+
form = {
|
|
17977
|
+
columns: 2,
|
|
17978
|
+
tiles: []
|
|
17979
|
+
};
|
|
17980
|
+
colorOptions = ['default', 'primary', 'success', 'warning', 'error'];
|
|
17981
|
+
sizeOptions = ['normal', 'full'];
|
|
17982
|
+
sourceTypeOptions = ['entity', 'aggregation'];
|
|
17983
|
+
aggregationOptions = ['count', 'sum', 'avg', 'min', 'max'];
|
|
17984
|
+
get isValid() {
|
|
17985
|
+
return this.form.tiles.length > 0 && this.form.tiles.every(t => t.label && t.ckTypeId);
|
|
17986
|
+
}
|
|
17987
|
+
ngOnInit() {
|
|
17988
|
+
this.form.columns = this.initialColumns ?? 2;
|
|
17989
|
+
if (this.initialTiles) {
|
|
17990
|
+
this.form.tiles = this.initialTiles.map((t, i) => ({
|
|
17991
|
+
id: t.id || `tile-${i}`,
|
|
17992
|
+
label: t.label,
|
|
17993
|
+
prefix: t.prefix ?? '',
|
|
17994
|
+
suffix: t.suffix ?? '',
|
|
17995
|
+
color: t.color ?? 'default',
|
|
17996
|
+
size: t.size ?? 'normal',
|
|
17997
|
+
sourceType: t.entitySource ? 'entity' : 'aggregation',
|
|
17998
|
+
rtId: t.entitySource?.rtId ?? '',
|
|
17999
|
+
ckTypeId: t.entitySource?.ckTypeId ?? t.aggregationSource?.ckTypeId ?? '',
|
|
18000
|
+
attributePath: t.entitySource?.attributePath ?? '',
|
|
18001
|
+
aggregation: t.aggregationSource?.aggregation ?? 'count',
|
|
18002
|
+
aggAttribute: t.aggregationSource?.attribute ?? ''
|
|
18003
|
+
}));
|
|
18004
|
+
}
|
|
18005
|
+
}
|
|
18006
|
+
addTile() {
|
|
18007
|
+
this.form.tiles.push({
|
|
18008
|
+
id: `tile-${Date.now()}`,
|
|
18009
|
+
label: '', prefix: '', suffix: '',
|
|
18010
|
+
color: 'default', size: 'normal',
|
|
18011
|
+
sourceType: 'entity',
|
|
18012
|
+
rtId: '', ckTypeId: '', attributePath: '',
|
|
18013
|
+
aggregation: 'count', aggAttribute: ''
|
|
18014
|
+
});
|
|
18015
|
+
}
|
|
18016
|
+
removeTile(index) {
|
|
18017
|
+
this.form.tiles.splice(index, 1);
|
|
18018
|
+
}
|
|
18019
|
+
onSave() {
|
|
18020
|
+
const tiles = this.form.tiles.map(t => {
|
|
18021
|
+
const tile = {
|
|
18022
|
+
id: t.id,
|
|
18023
|
+
label: t.label,
|
|
18024
|
+
prefix: t.prefix || undefined,
|
|
18025
|
+
suffix: t.suffix || undefined,
|
|
18026
|
+
color: t.color || undefined,
|
|
18027
|
+
size: t.size || undefined
|
|
18028
|
+
};
|
|
18029
|
+
if (t.sourceType === 'entity') {
|
|
18030
|
+
tile.entitySource = { rtId: t.rtId, ckTypeId: t.ckTypeId, attributePath: t.attributePath };
|
|
18031
|
+
}
|
|
18032
|
+
else {
|
|
18033
|
+
tile.aggregationSource = {
|
|
18034
|
+
ckTypeId: t.ckTypeId,
|
|
18035
|
+
aggregation: t.aggregation,
|
|
18036
|
+
attribute: t.aggAttribute || undefined
|
|
18037
|
+
};
|
|
18038
|
+
}
|
|
18039
|
+
return tile;
|
|
18040
|
+
});
|
|
18041
|
+
this.windowRef.close({
|
|
18042
|
+
ckTypeId: '',
|
|
18043
|
+
columns: this.form.columns,
|
|
18044
|
+
tiles
|
|
18045
|
+
});
|
|
18046
|
+
}
|
|
18047
|
+
onCancel() {
|
|
18048
|
+
this.windowRef.close();
|
|
18049
|
+
}
|
|
18050
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SummaryCardConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18051
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SummaryCardConfigDialogComponent, isStandalone: true, selector: "mm-summary-card-config-dialog", inputs: { initialColumns: "initialColumns", initialTiles: "initialTiles" }, ngImport: i0, template: `
|
|
18052
|
+
<div class="config-container">
|
|
18053
|
+
<div class="config-form">
|
|
18054
|
+
<div class="form-field">
|
|
18055
|
+
<label>Grid Columns</label>
|
|
18056
|
+
<kendo-numerictextbox [(ngModel)]="form.columns" [min]="1" [max]="6" [format]="'n0'" [spinners]="true"></kendo-numerictextbox>
|
|
18057
|
+
</div>
|
|
18058
|
+
|
|
18059
|
+
<h4>Tiles</h4>
|
|
18060
|
+
@for (tile of form.tiles; track $index) {
|
|
18061
|
+
<div class="tile-config">
|
|
18062
|
+
<div class="tile-header">
|
|
18063
|
+
<strong>Tile {{ $index + 1 }}</strong>
|
|
18064
|
+
<button kendoButton fillMode="flat" (click)="removeTile($index)">Remove</button>
|
|
18065
|
+
</div>
|
|
18066
|
+
<div class="tile-row">
|
|
18067
|
+
<kendo-textbox [(ngModel)]="tile.label" placeholder="Label" style="flex: 1;"></kendo-textbox>
|
|
18068
|
+
<kendo-textbox [(ngModel)]="tile.prefix" placeholder="Prefix" style="width: 60px;"></kendo-textbox>
|
|
18069
|
+
<kendo-textbox [(ngModel)]="tile.suffix" placeholder="Suffix" style="width: 80px;"></kendo-textbox>
|
|
18070
|
+
</div>
|
|
18071
|
+
<div class="tile-row">
|
|
18072
|
+
<kendo-dropdownlist [data]="colorOptions" [valuePrimitive]="true" [(ngModel)]="tile.color" style="width: 110px;"></kendo-dropdownlist>
|
|
18073
|
+
<kendo-dropdownlist [data]="sizeOptions" [valuePrimitive]="true" [(ngModel)]="tile.size" style="width: 110px;"></kendo-dropdownlist>
|
|
18074
|
+
<kendo-dropdownlist [data]="sourceTypeOptions" [valuePrimitive]="true" [(ngModel)]="tile.sourceType" style="width: 120px;"></kendo-dropdownlist>
|
|
18075
|
+
</div>
|
|
18076
|
+
@if (tile.sourceType === 'entity') {
|
|
18077
|
+
<div class="tile-row">
|
|
18078
|
+
<kendo-textbox [(ngModel)]="tile.ckTypeId" placeholder="CK Type ID" style="flex: 1;"></kendo-textbox>
|
|
18079
|
+
<kendo-textbox [(ngModel)]="tile.rtId" placeholder="Runtime ID" style="flex: 1;"></kendo-textbox>
|
|
18080
|
+
<kendo-textbox [(ngModel)]="tile.attributePath" placeholder="Attribute" style="width: 120px;"></kendo-textbox>
|
|
18081
|
+
</div>
|
|
18082
|
+
}
|
|
18083
|
+
@if (tile.sourceType === 'aggregation') {
|
|
18084
|
+
<div class="tile-row">
|
|
18085
|
+
<kendo-textbox [(ngModel)]="tile.ckTypeId" placeholder="CK Type ID" style="flex: 1;"></kendo-textbox>
|
|
18086
|
+
<kendo-dropdownlist [data]="aggregationOptions" [valuePrimitive]="true" [(ngModel)]="tile.aggregation" style="width: 100px;"></kendo-dropdownlist>
|
|
18087
|
+
<kendo-textbox [(ngModel)]="tile.aggAttribute" placeholder="Attribute (for sum/avg)" style="flex: 1;"></kendo-textbox>
|
|
18088
|
+
</div>
|
|
18089
|
+
}
|
|
18090
|
+
</div>
|
|
18091
|
+
}
|
|
18092
|
+
<button kendoButton fillMode="flat" (click)="addTile()">+ Add Tile</button>
|
|
18093
|
+
</div>
|
|
18094
|
+
|
|
18095
|
+
<div class="action-bar mm-dialog-actions">
|
|
18096
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
18097
|
+
<button kendoButton themeColor="primary" [disabled]="!isValid" (click)="onSave()">Save</button>
|
|
18098
|
+
</div>
|
|
18099
|
+
</div>
|
|
18100
|
+
`, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.config-form{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.form-field{display:flex;flex-direction:column;gap:6px}.form-field label{font-weight:600;font-size:.9rem}h4{margin:8px 0 4px;font-size:.95rem}.tile-config{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;padding:10px;margin-bottom:8px;display:flex;flex-direction:column;gap:6px}.tile-header{display:flex;justify-content:space-between;align-items:center}.tile-row{display:flex;gap:6px;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i3.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: i3.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: "ngmodule", type: DropDownsModule }, { 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"] }] });
|
|
18101
|
+
}
|
|
18102
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SummaryCardConfigDialogComponent, decorators: [{
|
|
18103
|
+
type: Component,
|
|
18104
|
+
args: [{ selector: 'mm-summary-card-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule, DropDownsModule], template: `
|
|
18105
|
+
<div class="config-container">
|
|
18106
|
+
<div class="config-form">
|
|
18107
|
+
<div class="form-field">
|
|
18108
|
+
<label>Grid Columns</label>
|
|
18109
|
+
<kendo-numerictextbox [(ngModel)]="form.columns" [min]="1" [max]="6" [format]="'n0'" [spinners]="true"></kendo-numerictextbox>
|
|
18110
|
+
</div>
|
|
18111
|
+
|
|
18112
|
+
<h4>Tiles</h4>
|
|
18113
|
+
@for (tile of form.tiles; track $index) {
|
|
18114
|
+
<div class="tile-config">
|
|
18115
|
+
<div class="tile-header">
|
|
18116
|
+
<strong>Tile {{ $index + 1 }}</strong>
|
|
18117
|
+
<button kendoButton fillMode="flat" (click)="removeTile($index)">Remove</button>
|
|
18118
|
+
</div>
|
|
18119
|
+
<div class="tile-row">
|
|
18120
|
+
<kendo-textbox [(ngModel)]="tile.label" placeholder="Label" style="flex: 1;"></kendo-textbox>
|
|
18121
|
+
<kendo-textbox [(ngModel)]="tile.prefix" placeholder="Prefix" style="width: 60px;"></kendo-textbox>
|
|
18122
|
+
<kendo-textbox [(ngModel)]="tile.suffix" placeholder="Suffix" style="width: 80px;"></kendo-textbox>
|
|
18123
|
+
</div>
|
|
18124
|
+
<div class="tile-row">
|
|
18125
|
+
<kendo-dropdownlist [data]="colorOptions" [valuePrimitive]="true" [(ngModel)]="tile.color" style="width: 110px;"></kendo-dropdownlist>
|
|
18126
|
+
<kendo-dropdownlist [data]="sizeOptions" [valuePrimitive]="true" [(ngModel)]="tile.size" style="width: 110px;"></kendo-dropdownlist>
|
|
18127
|
+
<kendo-dropdownlist [data]="sourceTypeOptions" [valuePrimitive]="true" [(ngModel)]="tile.sourceType" style="width: 120px;"></kendo-dropdownlist>
|
|
18128
|
+
</div>
|
|
18129
|
+
@if (tile.sourceType === 'entity') {
|
|
18130
|
+
<div class="tile-row">
|
|
18131
|
+
<kendo-textbox [(ngModel)]="tile.ckTypeId" placeholder="CK Type ID" style="flex: 1;"></kendo-textbox>
|
|
18132
|
+
<kendo-textbox [(ngModel)]="tile.rtId" placeholder="Runtime ID" style="flex: 1;"></kendo-textbox>
|
|
18133
|
+
<kendo-textbox [(ngModel)]="tile.attributePath" placeholder="Attribute" style="width: 120px;"></kendo-textbox>
|
|
18134
|
+
</div>
|
|
18135
|
+
}
|
|
18136
|
+
@if (tile.sourceType === 'aggregation') {
|
|
18137
|
+
<div class="tile-row">
|
|
18138
|
+
<kendo-textbox [(ngModel)]="tile.ckTypeId" placeholder="CK Type ID" style="flex: 1;"></kendo-textbox>
|
|
18139
|
+
<kendo-dropdownlist [data]="aggregationOptions" [valuePrimitive]="true" [(ngModel)]="tile.aggregation" style="width: 100px;"></kendo-dropdownlist>
|
|
18140
|
+
<kendo-textbox [(ngModel)]="tile.aggAttribute" placeholder="Attribute (for sum/avg)" style="flex: 1;"></kendo-textbox>
|
|
18141
|
+
</div>
|
|
18142
|
+
}
|
|
18143
|
+
</div>
|
|
18144
|
+
}
|
|
18145
|
+
<button kendoButton fillMode="flat" (click)="addTile()">+ Add Tile</button>
|
|
18146
|
+
</div>
|
|
18147
|
+
|
|
18148
|
+
<div class="action-bar mm-dialog-actions">
|
|
18149
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
18150
|
+
<button kendoButton themeColor="primary" [disabled]="!isValid" (click)="onSave()">Save</button>
|
|
18151
|
+
</div>
|
|
18152
|
+
</div>
|
|
18153
|
+
`, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.config-form{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.form-field{display:flex;flex-direction:column;gap:6px}.form-field label{font-weight:600;font-size:.9rem}h4{margin:8px 0 4px;font-size:.95rem}.tile-config{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;padding:10px;margin-bottom:8px;display:flex;flex-direction:column;gap:6px}.tile-header{display:flex;justify-content:space-between;align-items:center}.tile-row{display:flex;gap:6px;align-items:center}\n"] }]
|
|
18154
|
+
}], propDecorators: { initialColumns: [{
|
|
18155
|
+
type: Input
|
|
18156
|
+
}], initialTiles: [{
|
|
18157
|
+
type: Input
|
|
18158
|
+
}] } });
|
|
18159
|
+
|
|
18160
|
+
function getAlertSeverityColor(level) {
|
|
18161
|
+
switch (level?.toUpperCase()) {
|
|
18162
|
+
case 'CRITICAL': return 'var(--mm-alert-critical, #dc2626)';
|
|
18163
|
+
case 'ERROR': return 'var(--mm-alert-error, #ef4444)';
|
|
18164
|
+
case 'WARNING': return 'var(--mm-alert-warning, #f59e0b)';
|
|
18165
|
+
case 'INFORMATION': return 'var(--mm-alert-info, #3b82f6)';
|
|
18166
|
+
case 'DEBUG': return 'var(--mm-alert-debug, #6b7280)';
|
|
18167
|
+
default: return 'var(--mm-alert-debug, #6b7280)';
|
|
18168
|
+
}
|
|
18169
|
+
}
|
|
18170
|
+
function getAlertSeverityIcon(level) {
|
|
18171
|
+
switch (level?.toUpperCase()) {
|
|
18172
|
+
case 'CRITICAL':
|
|
18173
|
+
case 'ERROR':
|
|
18174
|
+
return xCircleIcon;
|
|
18175
|
+
case 'WARNING':
|
|
18176
|
+
return exclamationCircleIcon;
|
|
18177
|
+
default:
|
|
18178
|
+
return infoCircleIcon;
|
|
18179
|
+
}
|
|
18180
|
+
}
|
|
18181
|
+
function getAlertSeverityOrder(level) {
|
|
18182
|
+
switch (level?.toUpperCase()) {
|
|
18183
|
+
case 'CRITICAL': return 4;
|
|
18184
|
+
case 'ERROR': return 3;
|
|
18185
|
+
case 'WARNING': return 2;
|
|
18186
|
+
case 'INFORMATION': return 1;
|
|
18187
|
+
case 'DEBUG': return 0;
|
|
18188
|
+
default: return -1;
|
|
18189
|
+
}
|
|
18190
|
+
}
|
|
18191
|
+
const DEFAULT_ALERT_CK_TYPE = 'System.Notification/StatefulEvent';
|
|
18192
|
+
|
|
18193
|
+
class AlertBannerWidgetComponent {
|
|
18194
|
+
getEntitiesByCkTypeGQL = inject(GetEntitiesByCkTypeDtoGQL);
|
|
18195
|
+
rotationTimer = null;
|
|
18196
|
+
config;
|
|
18197
|
+
_isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
|
|
18198
|
+
_error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
|
|
18199
|
+
_items = signal([], ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
|
|
18200
|
+
_currentIndex = signal(0, ...(ngDevMode ? [{ debugName: "_currentIndex" }] : /* istanbul ignore next */ []));
|
|
18201
|
+
isLoading = this._isLoading.asReadonly();
|
|
18202
|
+
error = this._error.asReadonly();
|
|
18203
|
+
data = this._items.asReadonly();
|
|
18204
|
+
items = this._items.asReadonly();
|
|
18205
|
+
currentIndex = this._currentIndex.asReadonly();
|
|
18206
|
+
currentAlert = computed(() => {
|
|
18207
|
+
const items = this._items();
|
|
18208
|
+
if (items.length === 0)
|
|
18209
|
+
return null;
|
|
18210
|
+
return items[this._currentIndex() % items.length];
|
|
18211
|
+
}, ...(ngDevMode ? [{ debugName: "currentAlert" }] : /* istanbul ignore next */ []));
|
|
18212
|
+
currentColor = computed(() => {
|
|
18213
|
+
const alert = this.currentAlert();
|
|
18214
|
+
return alert ? getAlertSeverityColor(alert.level) : '';
|
|
18215
|
+
}, ...(ngDevMode ? [{ debugName: "currentColor" }] : /* istanbul ignore next */ []));
|
|
18216
|
+
currentIcon = computed(() => {
|
|
18217
|
+
const alert = this.currentAlert();
|
|
18218
|
+
return alert ? getAlertSeverityIcon(alert.level) : getAlertSeverityIcon('DEBUG');
|
|
18219
|
+
}, ...(ngDevMode ? [{ debugName: "currentIcon" }] : /* istanbul ignore next */ []));
|
|
18220
|
+
isNotConfigured() {
|
|
18221
|
+
return false;
|
|
18222
|
+
}
|
|
18223
|
+
ngOnInit() {
|
|
18224
|
+
this.loadData();
|
|
18225
|
+
}
|
|
18226
|
+
ngOnChanges(changes) {
|
|
18227
|
+
if (changes['config'] && !changes['config'].firstChange) {
|
|
18228
|
+
this.loadData();
|
|
18229
|
+
}
|
|
18230
|
+
}
|
|
18231
|
+
ngOnDestroy() {
|
|
18232
|
+
this.stopRotation();
|
|
18233
|
+
}
|
|
18234
|
+
refresh() {
|
|
18235
|
+
this.loadData();
|
|
18236
|
+
}
|
|
18237
|
+
startRotation() {
|
|
18238
|
+
this.stopRotation();
|
|
18239
|
+
const interval = this.config?.rotationInterval ?? 5000;
|
|
18240
|
+
if (this._items().length > 1) {
|
|
18241
|
+
this.rotationTimer = setInterval(() => {
|
|
18242
|
+
this._currentIndex.update(i => (i + 1) % this._items().length);
|
|
18243
|
+
}, interval);
|
|
18244
|
+
}
|
|
18245
|
+
}
|
|
18246
|
+
stopRotation() {
|
|
18247
|
+
if (this.rotationTimer) {
|
|
18248
|
+
clearInterval(this.rotationTimer);
|
|
18249
|
+
this.rotationTimer = null;
|
|
18250
|
+
}
|
|
18251
|
+
}
|
|
18252
|
+
async loadData() {
|
|
18253
|
+
this._isLoading.set(true);
|
|
18254
|
+
this._error.set(null);
|
|
18255
|
+
try {
|
|
18256
|
+
const ckTypeId = this.config?.ckTypeId || DEFAULT_ALERT_CK_TYPE;
|
|
18257
|
+
const result = await firstValueFrom(this.getEntitiesByCkTypeGQL.fetch({
|
|
18258
|
+
variables: {
|
|
18259
|
+
ckTypeId,
|
|
18260
|
+
first: this.config?.maxAlerts ?? 20,
|
|
18261
|
+
fieldFilters: [
|
|
18262
|
+
{ attributePath: 'state', operator: FieldFilterOperatorsDto.EqualsDto, comparisonValue: '0' }
|
|
18263
|
+
],
|
|
18264
|
+
sort: [
|
|
18265
|
+
{ attributePath: 'level', sortOrder: SortOrdersDto.DescendingDto }
|
|
18266
|
+
]
|
|
18267
|
+
}
|
|
18268
|
+
}));
|
|
18269
|
+
const entities = result.data?.runtime?.runtimeEntities?.items ?? [];
|
|
18270
|
+
const items = [];
|
|
18271
|
+
for (const entity of entities) {
|
|
18272
|
+
if (!entity)
|
|
18273
|
+
continue;
|
|
18274
|
+
const attrs = (entity.attributes?.items ?? [])
|
|
18275
|
+
.filter((a) => a != null && a.attributeName != null);
|
|
18276
|
+
items.push({
|
|
18277
|
+
rtId: entity.rtId ?? '',
|
|
18278
|
+
message: this.getAttr(attrs, 'message') ?? '',
|
|
18279
|
+
level: this.getAttr(attrs, 'level') ?? 'INFORMATION',
|
|
18280
|
+
state: this.getAttr(attrs, 'state') ?? '',
|
|
18281
|
+
source: this.getAttr(attrs, 'source') ?? '',
|
|
18282
|
+
timestamp: entity.rtCreationDateTime ?? undefined
|
|
18283
|
+
});
|
|
18284
|
+
}
|
|
18285
|
+
items.sort((a, b) => getAlertSeverityOrder(b.level) - getAlertSeverityOrder(a.level));
|
|
18286
|
+
this._items.set(items);
|
|
18287
|
+
this._currentIndex.set(0);
|
|
18288
|
+
this.startRotation();
|
|
18289
|
+
}
|
|
18290
|
+
catch (err) {
|
|
18291
|
+
this._error.set(err instanceof Error ? err.message : 'Failed to load alerts');
|
|
18292
|
+
}
|
|
18293
|
+
finally {
|
|
18294
|
+
this._isLoading.set(false);
|
|
18295
|
+
}
|
|
18296
|
+
}
|
|
18297
|
+
getAttr(attrs, name) {
|
|
18298
|
+
const attr = attrs.find(a => a.attributeName === name);
|
|
18299
|
+
return attr?.value != null ? String(attr.value) : null;
|
|
18300
|
+
}
|
|
18301
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertBannerWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18302
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: AlertBannerWidgetComponent, isStandalone: true, selector: "mm-alert-banner-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: `
|
|
18303
|
+
<div class="alert-banner-widget">
|
|
18304
|
+
@if (isNotConfigured()) {
|
|
18305
|
+
<mm-widget-not-configured></mm-widget-not-configured>
|
|
18306
|
+
} @else if (error()) {
|
|
18307
|
+
<div class="error-message">{{ error() }}</div>
|
|
18308
|
+
} @else if (currentAlert()) {
|
|
18309
|
+
<div class="alert-banner" [class.critical]="currentAlert()!.level === 'CRITICAL'"
|
|
18310
|
+
[style.--alert-color]="currentColor()">
|
|
18311
|
+
@if (config.showIcon !== false) {
|
|
18312
|
+
<kendo-svg-icon [icon]="currentIcon()" class="alert-icon"></kendo-svg-icon>
|
|
18313
|
+
}
|
|
18314
|
+
<span class="alert-severity-badge">{{ currentAlert()!.level }}</span>
|
|
18315
|
+
<span class="alert-message">{{ currentAlert()!.message }}</span>
|
|
18316
|
+
@if (items().length > 1) {
|
|
18317
|
+
<span class="alert-counter">{{ currentIndex() + 1 }}/{{ items().length }}</span>
|
|
18318
|
+
}
|
|
18319
|
+
</div>
|
|
18320
|
+
} @else if (!isLoading()) {
|
|
18321
|
+
<div class="alert-banner no-alerts">
|
|
18322
|
+
<span class="alert-message">No active alerts</span>
|
|
18323
|
+
</div>
|
|
18324
|
+
}
|
|
18325
|
+
</div>
|
|
18326
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.alert-banner-widget{height:100%;display:flex;align-items:center}.alert-banner{display:flex;align-items:center;gap:10px;width:100%;padding:10px 16px;border-left:4px solid var(--alert-color, #6b7280);background:color-mix(in srgb,var(--alert-color, #6b7280) 10%,transparent)}.alert-banner.critical{animation:pulse-bg 1.5s ease-in-out infinite}@keyframes pulse-bg{0%,to{background:color-mix(in srgb,var(--alert-color) 10%,transparent)}50%{background:color-mix(in srgb,var(--alert-color) 25%,transparent)}}.alert-icon{flex-shrink:0;color:var(--alert-color, #6b7280)}.alert-severity-badge{font-size:.65rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 8px;border-radius:3px;background:var(--alert-color, #6b7280);color:#fff;white-space:nowrap;flex-shrink:0}.alert-message{flex:1;font-size:.85rem;color:var(--kendo-color-on-app-surface, inherit);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.alert-counter{font-size:.7rem;opacity:.5;white-space:nowrap;flex-shrink:0}.no-alerts{border-left-color:var(--mm-alert-debug, #6b7280);opacity:.5}.error-message{text-align:center;padding:16px;width:100%;color:var(--kendo-color-error, #dc3545)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
18327
|
+
}
|
|
18328
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertBannerWidgetComponent, decorators: [{
|
|
18329
|
+
type: Component,
|
|
18330
|
+
args: [{ selector: 'mm-alert-banner-widget', standalone: true, imports: [CommonModule, SVGIconModule, WidgetNotConfiguredComponent], template: `
|
|
18331
|
+
<div class="alert-banner-widget">
|
|
18332
|
+
@if (isNotConfigured()) {
|
|
18333
|
+
<mm-widget-not-configured></mm-widget-not-configured>
|
|
18334
|
+
} @else if (error()) {
|
|
18335
|
+
<div class="error-message">{{ error() }}</div>
|
|
18336
|
+
} @else if (currentAlert()) {
|
|
18337
|
+
<div class="alert-banner" [class.critical]="currentAlert()!.level === 'CRITICAL'"
|
|
18338
|
+
[style.--alert-color]="currentColor()">
|
|
18339
|
+
@if (config.showIcon !== false) {
|
|
18340
|
+
<kendo-svg-icon [icon]="currentIcon()" class="alert-icon"></kendo-svg-icon>
|
|
18341
|
+
}
|
|
18342
|
+
<span class="alert-severity-badge">{{ currentAlert()!.level }}</span>
|
|
18343
|
+
<span class="alert-message">{{ currentAlert()!.message }}</span>
|
|
18344
|
+
@if (items().length > 1) {
|
|
18345
|
+
<span class="alert-counter">{{ currentIndex() + 1 }}/{{ items().length }}</span>
|
|
18346
|
+
}
|
|
18347
|
+
</div>
|
|
18348
|
+
} @else if (!isLoading()) {
|
|
18349
|
+
<div class="alert-banner no-alerts">
|
|
18350
|
+
<span class="alert-message">No active alerts</span>
|
|
18351
|
+
</div>
|
|
18352
|
+
}
|
|
18353
|
+
</div>
|
|
18354
|
+
`, styles: [":host{display:block;width:100%;height:100%}.alert-banner-widget{height:100%;display:flex;align-items:center}.alert-banner{display:flex;align-items:center;gap:10px;width:100%;padding:10px 16px;border-left:4px solid var(--alert-color, #6b7280);background:color-mix(in srgb,var(--alert-color, #6b7280) 10%,transparent)}.alert-banner.critical{animation:pulse-bg 1.5s ease-in-out infinite}@keyframes pulse-bg{0%,to{background:color-mix(in srgb,var(--alert-color) 10%,transparent)}50%{background:color-mix(in srgb,var(--alert-color) 25%,transparent)}}.alert-icon{flex-shrink:0;color:var(--alert-color, #6b7280)}.alert-severity-badge{font-size:.65rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 8px;border-radius:3px;background:var(--alert-color, #6b7280);color:#fff;white-space:nowrap;flex-shrink:0}.alert-message{flex:1;font-size:.85rem;color:var(--kendo-color-on-app-surface, inherit);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.alert-counter{font-size:.7rem;opacity:.5;white-space:nowrap;flex-shrink:0}.no-alerts{border-left-color:var(--mm-alert-debug, #6b7280);opacity:.5}.error-message{text-align:center;padding:16px;width:100%;color:var(--kendo-color-error, #dc3545)}\n"] }]
|
|
18355
|
+
}], propDecorators: { config: [{
|
|
18356
|
+
type: Input
|
|
18357
|
+
}] } });
|
|
18358
|
+
|
|
18359
|
+
class AlertBannerConfigDialogComponent {
|
|
18360
|
+
windowRef = inject(WindowRef);
|
|
18361
|
+
ckTypeId = 'System.Notification/StatefulEvent';
|
|
18362
|
+
rotationInterval = 5000;
|
|
18363
|
+
showIcon = true;
|
|
18364
|
+
maxAlerts = 20;
|
|
18365
|
+
onSave() {
|
|
18366
|
+
const result = {
|
|
18367
|
+
ckTypeId: this.ckTypeId,
|
|
18368
|
+
rotationInterval: this.rotationInterval,
|
|
18369
|
+
showIcon: this.showIcon,
|
|
18370
|
+
maxAlerts: this.maxAlerts
|
|
18371
|
+
};
|
|
18372
|
+
this.windowRef.close(result);
|
|
18373
|
+
}
|
|
18374
|
+
onCancel() {
|
|
18375
|
+
this.windowRef.close();
|
|
18376
|
+
}
|
|
18377
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertBannerConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18378
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: AlertBannerConfigDialogComponent, isStandalone: true, selector: "mm-alert-banner-config-dialog", ngImport: i0, template: `
|
|
18379
|
+
<div class="config-form">
|
|
18380
|
+
<div class="form-group">
|
|
18381
|
+
<kendo-label text="CK Type ID">
|
|
18382
|
+
<kendo-textbox [(ngModel)]="ckTypeId"></kendo-textbox>
|
|
18383
|
+
</kendo-label>
|
|
18384
|
+
</div>
|
|
18385
|
+
<div class="form-group">
|
|
18386
|
+
<kendo-label text="Rotation Interval (ms)">
|
|
18387
|
+
<kendo-numerictextbox [(ngModel)]="rotationInterval" [min]="1000" [step]="1000" [format]="'n0'"></kendo-numerictextbox>
|
|
18388
|
+
</kendo-label>
|
|
18389
|
+
</div>
|
|
18390
|
+
<div class="form-group">
|
|
18391
|
+
<kendo-label text="Max Alerts">
|
|
18392
|
+
<kendo-numerictextbox [(ngModel)]="maxAlerts" [min]="1" [max]="100" [format]="'n0'"></kendo-numerictextbox>
|
|
18393
|
+
</kendo-label>
|
|
18394
|
+
</div>
|
|
18395
|
+
<div class="mm-dialog-actions">
|
|
18396
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
18397
|
+
<button kendoButton themeColor="primary" (click)="onSave()">Save</button>
|
|
18398
|
+
</div>
|
|
18399
|
+
</div>
|
|
18400
|
+
`, isInline: true, styles: [".config-form{display:flex;flex-direction:column;gap:12px;padding:16px}.form-group{display:flex;flex-direction:column;gap:4px}.mm-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i3.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: i3.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: "ngmodule", type: LabelModule }, { kind: "component", type: i4$1.LabelComponent, selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }] });
|
|
18401
|
+
}
|
|
18402
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertBannerConfigDialogComponent, decorators: [{
|
|
18403
|
+
type: Component,
|
|
18404
|
+
args: [{ selector: 'mm-alert-banner-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule, LabelModule], template: `
|
|
18405
|
+
<div class="config-form">
|
|
18406
|
+
<div class="form-group">
|
|
18407
|
+
<kendo-label text="CK Type ID">
|
|
18408
|
+
<kendo-textbox [(ngModel)]="ckTypeId"></kendo-textbox>
|
|
18409
|
+
</kendo-label>
|
|
18410
|
+
</div>
|
|
18411
|
+
<div class="form-group">
|
|
18412
|
+
<kendo-label text="Rotation Interval (ms)">
|
|
18413
|
+
<kendo-numerictextbox [(ngModel)]="rotationInterval" [min]="1000" [step]="1000" [format]="'n0'"></kendo-numerictextbox>
|
|
18414
|
+
</kendo-label>
|
|
18415
|
+
</div>
|
|
18416
|
+
<div class="form-group">
|
|
18417
|
+
<kendo-label text="Max Alerts">
|
|
18418
|
+
<kendo-numerictextbox [(ngModel)]="maxAlerts" [min]="1" [max]="100" [format]="'n0'"></kendo-numerictextbox>
|
|
18419
|
+
</kendo-label>
|
|
18420
|
+
</div>
|
|
18421
|
+
<div class="mm-dialog-actions">
|
|
18422
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
18423
|
+
<button kendoButton themeColor="primary" (click)="onSave()">Save</button>
|
|
18424
|
+
</div>
|
|
18425
|
+
</div>
|
|
18426
|
+
`, styles: [".config-form{display:flex;flex-direction:column;gap:12px;padding:16px}.form-group{display:flex;flex-direction:column;gap:4px}.mm-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}\n"] }]
|
|
18427
|
+
}] });
|
|
18428
|
+
|
|
18429
|
+
class AlertListWidgetComponent {
|
|
18430
|
+
getEntitiesByCkTypeGQL = inject(GetEntitiesByCkTypeDtoGQL);
|
|
18431
|
+
config;
|
|
18432
|
+
_isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
|
|
18433
|
+
_error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
|
|
18434
|
+
_items = signal([], ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
|
|
18435
|
+
isLoading = this._isLoading.asReadonly();
|
|
18436
|
+
error = this._error.asReadonly();
|
|
18437
|
+
data = this._items.asReadonly();
|
|
18438
|
+
items = this._items.asReadonly();
|
|
18439
|
+
isNotConfigured() {
|
|
18440
|
+
return false;
|
|
18441
|
+
}
|
|
18442
|
+
ngOnInit() {
|
|
18443
|
+
this.loadData();
|
|
18444
|
+
}
|
|
18445
|
+
ngOnChanges(changes) {
|
|
18446
|
+
if (changes['config'] && !changes['config'].firstChange) {
|
|
18447
|
+
this.loadData();
|
|
18448
|
+
}
|
|
18449
|
+
}
|
|
18450
|
+
refresh() {
|
|
18451
|
+
this.loadData();
|
|
18452
|
+
}
|
|
18453
|
+
getColor(level) {
|
|
18454
|
+
return getAlertSeverityColor(level);
|
|
18455
|
+
}
|
|
18456
|
+
getIcon(level) {
|
|
18457
|
+
return getAlertSeverityIcon(level);
|
|
18458
|
+
}
|
|
18459
|
+
formatTime(timestamp) {
|
|
18460
|
+
try {
|
|
18461
|
+
const date = new Date(timestamp);
|
|
18462
|
+
return date.toLocaleString('de-AT', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
|
|
18463
|
+
}
|
|
18464
|
+
catch {
|
|
18465
|
+
return '';
|
|
18466
|
+
}
|
|
18467
|
+
}
|
|
18468
|
+
async loadData() {
|
|
18469
|
+
this._isLoading.set(true);
|
|
18470
|
+
this._error.set(null);
|
|
18471
|
+
try {
|
|
18472
|
+
const ckTypeId = this.config?.ckTypeId || DEFAULT_ALERT_CK_TYPE;
|
|
18473
|
+
const result = await firstValueFrom(this.getEntitiesByCkTypeGQL.fetch({
|
|
18474
|
+
variables: {
|
|
18475
|
+
ckTypeId,
|
|
18476
|
+
first: this.config?.maxAlerts ?? 50,
|
|
18477
|
+
fieldFilters: [
|
|
18478
|
+
{ attributePath: 'state', operator: FieldFilterOperatorsDto.EqualsDto, comparisonValue: '0' }
|
|
18479
|
+
],
|
|
18480
|
+
sort: [
|
|
18481
|
+
{ attributePath: 'level', sortOrder: SortOrdersDto.DescendingDto }
|
|
18482
|
+
]
|
|
18483
|
+
}
|
|
18484
|
+
}));
|
|
18485
|
+
const entities = result.data?.runtime?.runtimeEntities?.items ?? [];
|
|
18486
|
+
const items = [];
|
|
18487
|
+
for (const entity of entities) {
|
|
18488
|
+
if (!entity)
|
|
18489
|
+
continue;
|
|
18490
|
+
const attrs = (entity.attributes?.items ?? [])
|
|
18491
|
+
.filter((a) => a != null && a.attributeName != null);
|
|
18492
|
+
items.push({
|
|
18493
|
+
rtId: entity.rtId ?? '',
|
|
18494
|
+
message: this.getAttr(attrs, 'message') ?? '',
|
|
18495
|
+
level: this.getAttr(attrs, 'level') ?? 'INFORMATION',
|
|
18496
|
+
state: this.getAttr(attrs, 'state') ?? '',
|
|
18497
|
+
source: this.getAttr(attrs, 'source') ?? '',
|
|
18498
|
+
timestamp: entity.rtCreationDateTime ?? undefined
|
|
18499
|
+
});
|
|
18500
|
+
}
|
|
18501
|
+
if (this.config?.sortBySeverity !== false) {
|
|
18502
|
+
items.sort((a, b) => getAlertSeverityOrder(b.level) - getAlertSeverityOrder(a.level));
|
|
18503
|
+
}
|
|
18504
|
+
this._items.set(items);
|
|
18505
|
+
}
|
|
18506
|
+
catch (err) {
|
|
18507
|
+
this._error.set(err instanceof Error ? err.message : 'Failed to load alerts');
|
|
18508
|
+
}
|
|
18509
|
+
finally {
|
|
18510
|
+
this._isLoading.set(false);
|
|
18511
|
+
}
|
|
18512
|
+
}
|
|
18513
|
+
getAttr(attrs, name) {
|
|
18514
|
+
const attr = attrs.find(a => a.attributeName === name);
|
|
18515
|
+
return attr?.value != null ? String(attr.value) : null;
|
|
18516
|
+
}
|
|
18517
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertListWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18518
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: AlertListWidgetComponent, isStandalone: true, selector: "mm-alert-list-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: `
|
|
18519
|
+
<div class="alert-list-widget">
|
|
18520
|
+
@if (isNotConfigured()) {
|
|
18521
|
+
<mm-widget-not-configured></mm-widget-not-configured>
|
|
18522
|
+
} @else if (error()) {
|
|
18523
|
+
<div class="error-message">{{ error() }}</div>
|
|
18524
|
+
} @else {
|
|
18525
|
+
<div class="alert-list">
|
|
18526
|
+
@for (item of items(); track item.rtId) {
|
|
18527
|
+
<div class="alert-item" [style.--alert-color]="getColor(item.level)">
|
|
18528
|
+
<kendo-svg-icon [icon]="getIcon(item.level)" class="alert-icon"></kendo-svg-icon>
|
|
18529
|
+
<span class="alert-badge" [style.background-color]="getColor(item.level)">
|
|
18530
|
+
{{ item.level }}
|
|
18531
|
+
</span>
|
|
18532
|
+
<span class="alert-message">{{ item.message }}</span>
|
|
18533
|
+
@if (config.showTimestamp !== false && item.timestamp) {
|
|
18534
|
+
<span class="alert-time">{{ formatTime(item.timestamp) }}</span>
|
|
18535
|
+
}
|
|
18536
|
+
</div>
|
|
18537
|
+
}
|
|
18538
|
+
@if (!isLoading() && items().length === 0) {
|
|
18539
|
+
<div class="empty-message">No active alerts</div>
|
|
18540
|
+
}
|
|
18541
|
+
</div>
|
|
18542
|
+
}
|
|
18543
|
+
</div>
|
|
18544
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.alert-list-widget{height:100%;display:flex;flex-direction:column;overflow-y:auto;padding:8px 0}.alert-list{display:flex;flex-direction:column;gap:4px;padding:0 12px}.alert-item{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:6px;border-left:3px solid var(--alert-color, #6b7280);background:var(--mm-alert-list-item-bg, rgba(255, 255, 255, .03))}.alert-icon{flex-shrink:0;color:var(--alert-color, #6b7280)}.alert-badge{font-size:.6rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:3px;color:#fff;white-space:nowrap;flex-shrink:0}.alert-message{flex:1;font-size:.8rem;color:var(--kendo-color-on-app-surface, inherit);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.alert-time{font-size:.7rem;opacity:.5;white-space:nowrap;flex-shrink:0}.empty-message{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.error-message{text-align:center;padding:16px;color:var(--kendo-color-error, #dc3545)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
18545
|
+
}
|
|
18546
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertListWidgetComponent, decorators: [{
|
|
18547
|
+
type: Component,
|
|
18548
|
+
args: [{ selector: 'mm-alert-list-widget', standalone: true, imports: [CommonModule, SVGIconModule, WidgetNotConfiguredComponent], template: `
|
|
18549
|
+
<div class="alert-list-widget">
|
|
18550
|
+
@if (isNotConfigured()) {
|
|
18551
|
+
<mm-widget-not-configured></mm-widget-not-configured>
|
|
18552
|
+
} @else if (error()) {
|
|
18553
|
+
<div class="error-message">{{ error() }}</div>
|
|
18554
|
+
} @else {
|
|
18555
|
+
<div class="alert-list">
|
|
18556
|
+
@for (item of items(); track item.rtId) {
|
|
18557
|
+
<div class="alert-item" [style.--alert-color]="getColor(item.level)">
|
|
18558
|
+
<kendo-svg-icon [icon]="getIcon(item.level)" class="alert-icon"></kendo-svg-icon>
|
|
18559
|
+
<span class="alert-badge" [style.background-color]="getColor(item.level)">
|
|
18560
|
+
{{ item.level }}
|
|
18561
|
+
</span>
|
|
18562
|
+
<span class="alert-message">{{ item.message }}</span>
|
|
18563
|
+
@if (config.showTimestamp !== false && item.timestamp) {
|
|
18564
|
+
<span class="alert-time">{{ formatTime(item.timestamp) }}</span>
|
|
18565
|
+
}
|
|
18566
|
+
</div>
|
|
18567
|
+
}
|
|
18568
|
+
@if (!isLoading() && items().length === 0) {
|
|
18569
|
+
<div class="empty-message">No active alerts</div>
|
|
18570
|
+
}
|
|
18571
|
+
</div>
|
|
18572
|
+
}
|
|
18573
|
+
</div>
|
|
18574
|
+
`, styles: [":host{display:block;width:100%;height:100%}.alert-list-widget{height:100%;display:flex;flex-direction:column;overflow-y:auto;padding:8px 0}.alert-list{display:flex;flex-direction:column;gap:4px;padding:0 12px}.alert-item{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:6px;border-left:3px solid var(--alert-color, #6b7280);background:var(--mm-alert-list-item-bg, rgba(255, 255, 255, .03))}.alert-icon{flex-shrink:0;color:var(--alert-color, #6b7280)}.alert-badge{font-size:.6rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:3px;color:#fff;white-space:nowrap;flex-shrink:0}.alert-message{flex:1;font-size:.8rem;color:var(--kendo-color-on-app-surface, inherit);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.alert-time{font-size:.7rem;opacity:.5;white-space:nowrap;flex-shrink:0}.empty-message{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.error-message{text-align:center;padding:16px;color:var(--kendo-color-error, #dc3545)}\n"] }]
|
|
18575
|
+
}], propDecorators: { config: [{
|
|
18576
|
+
type: Input
|
|
18577
|
+
}] } });
|
|
18578
|
+
|
|
18579
|
+
class AlertListConfigDialogComponent {
|
|
18580
|
+
windowRef = inject(WindowRef);
|
|
18581
|
+
ckTypeId = 'System.Notification/StatefulEvent';
|
|
18582
|
+
showTimestamp = true;
|
|
18583
|
+
sortBySeverity = true;
|
|
18584
|
+
maxAlerts = 50;
|
|
18585
|
+
onSave() {
|
|
18586
|
+
const result = {
|
|
18587
|
+
ckTypeId: this.ckTypeId,
|
|
18588
|
+
showTimestamp: this.showTimestamp,
|
|
18589
|
+
sortBySeverity: this.sortBySeverity,
|
|
18590
|
+
maxAlerts: this.maxAlerts
|
|
18591
|
+
};
|
|
18592
|
+
this.windowRef.close(result);
|
|
18593
|
+
}
|
|
18594
|
+
onCancel() {
|
|
18595
|
+
this.windowRef.close();
|
|
18596
|
+
}
|
|
18597
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertListConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18598
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: AlertListConfigDialogComponent, isStandalone: true, selector: "mm-alert-list-config-dialog", ngImport: i0, template: `
|
|
18599
|
+
<div class="config-form">
|
|
18600
|
+
<div class="form-group">
|
|
18601
|
+
<kendo-label text="CK Type ID">
|
|
18602
|
+
<kendo-textbox [(ngModel)]="ckTypeId"></kendo-textbox>
|
|
18603
|
+
</kendo-label>
|
|
18604
|
+
</div>
|
|
18605
|
+
<div class="form-group">
|
|
18606
|
+
<kendo-label text="Max Alerts">
|
|
18607
|
+
<kendo-numerictextbox [(ngModel)]="maxAlerts" [min]="1" [max]="200" [format]="'n0'"></kendo-numerictextbox>
|
|
18608
|
+
</kendo-label>
|
|
18609
|
+
</div>
|
|
18610
|
+
<div class="mm-dialog-actions">
|
|
18611
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
18612
|
+
<button kendoButton themeColor="primary" (click)="onSave()">Save</button>
|
|
18613
|
+
</div>
|
|
18614
|
+
</div>
|
|
18615
|
+
`, isInline: true, styles: [".config-form{display:flex;flex-direction:column;gap:12px;padding:16px}.form-group{display:flex;flex-direction:column;gap:4px}.mm-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i3.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: i3.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: "ngmodule", type: LabelModule }, { kind: "component", type: i4$1.LabelComponent, selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }] });
|
|
18616
|
+
}
|
|
18617
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertListConfigDialogComponent, decorators: [{
|
|
18618
|
+
type: Component,
|
|
18619
|
+
args: [{ selector: 'mm-alert-list-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule, LabelModule], template: `
|
|
18620
|
+
<div class="config-form">
|
|
18621
|
+
<div class="form-group">
|
|
18622
|
+
<kendo-label text="CK Type ID">
|
|
18623
|
+
<kendo-textbox [(ngModel)]="ckTypeId"></kendo-textbox>
|
|
18624
|
+
</kendo-label>
|
|
18625
|
+
</div>
|
|
18626
|
+
<div class="form-group">
|
|
18627
|
+
<kendo-label text="Max Alerts">
|
|
18628
|
+
<kendo-numerictextbox [(ngModel)]="maxAlerts" [min]="1" [max]="200" [format]="'n0'"></kendo-numerictextbox>
|
|
18629
|
+
</kendo-label>
|
|
18630
|
+
</div>
|
|
18631
|
+
<div class="mm-dialog-actions">
|
|
18632
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
18633
|
+
<button kendoButton themeColor="primary" (click)="onSave()">Save</button>
|
|
18634
|
+
</div>
|
|
18635
|
+
</div>
|
|
18636
|
+
`, styles: [".config-form{display:flex;flex-direction:column;gap:12px;padding:16px}.form-group{display:flex;flex-direction:column;gap:4px}.mm-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}\n"] }]
|
|
18637
|
+
}] });
|
|
18638
|
+
|
|
18639
|
+
class AiInsightsService {
|
|
18640
|
+
async generateInsights(context) {
|
|
18641
|
+
if (context.apiKey) {
|
|
18642
|
+
return this.callClaudeApi(context);
|
|
18643
|
+
}
|
|
18644
|
+
return this.getSimulatedInsights(context);
|
|
18645
|
+
}
|
|
18646
|
+
gatherWidgetContext(widgets) {
|
|
18647
|
+
return widgets
|
|
18648
|
+
.filter(w => w.type !== 'aiInsights')
|
|
18649
|
+
.map(w => this.extractWidgetSummary(w))
|
|
18650
|
+
.filter(s => s.dataPoints.length > 0 || s.type !== 'unknown');
|
|
18651
|
+
}
|
|
18652
|
+
extractWidgetSummary(widget) {
|
|
18653
|
+
const summary = {
|
|
18654
|
+
title: widget.title,
|
|
18655
|
+
type: widget.type,
|
|
18656
|
+
dataPoints: []
|
|
18657
|
+
};
|
|
18658
|
+
if (widget.type === 'kpi') {
|
|
18659
|
+
const kpi = widget;
|
|
18660
|
+
if (kpi.staticValue) {
|
|
18661
|
+
summary.dataPoints.push({ label: 'value', value: `${kpi.staticValue}${kpi.suffix ?? ''}` });
|
|
18662
|
+
}
|
|
18663
|
+
if (kpi.comparisonText) {
|
|
18664
|
+
summary.dataPoints.push({ label: 'comparison', value: kpi.comparisonText });
|
|
18665
|
+
}
|
|
18666
|
+
}
|
|
18667
|
+
if (widget.type === 'gauge') {
|
|
18668
|
+
const gauge = widget;
|
|
18669
|
+
if (gauge.valueAttribute) {
|
|
18670
|
+
summary.dataPoints.push({ label: 'attribute', value: gauge.valueAttribute });
|
|
18671
|
+
}
|
|
18672
|
+
}
|
|
18673
|
+
return summary;
|
|
18674
|
+
}
|
|
18675
|
+
async callClaudeApi(context) {
|
|
18676
|
+
const systemPrompt = context.systemPrompt ?? this.buildDefaultSystemPrompt(context);
|
|
18677
|
+
const userMessage = this.buildUserMessage(context);
|
|
18678
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
18679
|
+
method: 'POST',
|
|
18680
|
+
headers: {
|
|
18681
|
+
'Content-Type': 'application/json',
|
|
18682
|
+
'x-api-key': context.apiKey,
|
|
18683
|
+
'anthropic-version': '2023-06-01',
|
|
18684
|
+
'anthropic-dangerous-direct-browser-access': 'true'
|
|
18685
|
+
},
|
|
18686
|
+
body: JSON.stringify({
|
|
18687
|
+
model: context.model ?? 'claude-sonnet-4-20250514',
|
|
18688
|
+
max_tokens: 1024,
|
|
18689
|
+
system: systemPrompt,
|
|
18690
|
+
messages: [{ role: 'user', content: userMessage }]
|
|
18691
|
+
})
|
|
18692
|
+
});
|
|
18693
|
+
if (!response.ok) {
|
|
18694
|
+
throw new Error(`Claude API error: ${response.status}`);
|
|
18695
|
+
}
|
|
18696
|
+
const data = await response.json();
|
|
18697
|
+
const text = data.content?.[0]?.text ?? '';
|
|
18698
|
+
return this.parseInsightsFromResponse(text, context.maxInsights ?? 4);
|
|
18699
|
+
}
|
|
18700
|
+
buildDefaultSystemPrompt(context) {
|
|
18701
|
+
const domain = context.domainContext ?? 'energy management';
|
|
18702
|
+
return `You are an AI advisor for an industrial ${domain} dashboard.
|
|
18703
|
+
Analyze the provided dashboard data and generate actionable insights.
|
|
18704
|
+
Respond ONLY with a JSON array of insight objects. Each object has:
|
|
18705
|
+
- "title": short headline (max 8 words)
|
|
18706
|
+
- "description": actionable recommendation (1-2 sentences, German language)
|
|
18707
|
+
- "severity": one of "info", "warning", "success", "critical"
|
|
18708
|
+
|
|
18709
|
+
Focus on energy efficiency, cost savings, anomalies, and optimization opportunities.
|
|
18710
|
+
Return at most ${context.maxInsights ?? 4} insights. No markdown, no explanation, only the JSON array.`;
|
|
18711
|
+
}
|
|
18712
|
+
buildUserMessage(context) {
|
|
18713
|
+
const widgetData = context.widgetSummaries.map(w => ({
|
|
18714
|
+
widget: w.title,
|
|
18715
|
+
type: w.type,
|
|
18716
|
+
data: w.dataPoints
|
|
18717
|
+
}));
|
|
18718
|
+
return `Current dashboard state:\n${JSON.stringify(widgetData, null, 2)}`;
|
|
18719
|
+
}
|
|
18720
|
+
parseInsightsFromResponse(text, maxInsights) {
|
|
18721
|
+
try {
|
|
18722
|
+
const jsonMatch = text.match(/\[[\s\S]*\]/);
|
|
18723
|
+
if (!jsonMatch)
|
|
18724
|
+
return this.fallbackInsights();
|
|
18725
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
18726
|
+
return parsed.slice(0, maxInsights).map(i => ({
|
|
18727
|
+
title: i.title ?? 'Insight',
|
|
18728
|
+
description: i.description ?? '',
|
|
18729
|
+
severity: (['info', 'warning', 'success', 'critical'].includes(i.severity) ? i.severity : 'info')
|
|
18730
|
+
}));
|
|
18731
|
+
}
|
|
18732
|
+
catch {
|
|
18733
|
+
return this.fallbackInsights();
|
|
18734
|
+
}
|
|
18735
|
+
}
|
|
18736
|
+
getSimulatedInsights(_context) {
|
|
18737
|
+
const hour = new Date().getHours();
|
|
18738
|
+
const insights = [
|
|
18739
|
+
{
|
|
18740
|
+
title: 'Lastspitze prognostiziert',
|
|
18741
|
+
description: 'Basierend auf dem aktuellen Lastprofil wird die 3.000 kW Grenze voraussichtlich um 14:30 Uhr erreicht. Lastabwurf der HVAC-Anlage um 30% empfohlen.',
|
|
18742
|
+
severity: 'warning'
|
|
18743
|
+
},
|
|
18744
|
+
{
|
|
18745
|
+
title: 'PV-Optimierung möglich',
|
|
18746
|
+
description: hour < 14
|
|
18747
|
+
? 'Wettervorhersage: sonnig bis 16:00 Uhr. Energieintensive Prozesse jetzt starten — erwartete PV-Einspeisung von 810 kW nutzen.'
|
|
18748
|
+
: 'PV-Einspeisung sinkt ab 16:00 Uhr. Batteriespeicher auf 80% laden, solange Überschuss verfügbar ist.',
|
|
18749
|
+
severity: 'success'
|
|
18750
|
+
},
|
|
18751
|
+
{
|
|
18752
|
+
title: 'Kompressoranlage C3 auffällig',
|
|
18753
|
+
description: 'Verbrauch 18% über dem 30-Tage-Durchschnitt bei gleicher Auslastung. Mögliche Ursache: verschlissene Dichtungen oder Druckverlust. Wartung empfohlen.',
|
|
18754
|
+
severity: 'warning'
|
|
18755
|
+
},
|
|
18756
|
+
{
|
|
18757
|
+
title: 'CO2-Budget im Zeitplan',
|
|
18758
|
+
description: 'Mit 489 t von 680 t Budget (72%) und 9 Monaten verbleibend liegt der Verbrauch 4% unter Plan. Zertifikats-Beschaffung kann verschoben werden.',
|
|
18759
|
+
severity: 'success'
|
|
18760
|
+
}
|
|
18761
|
+
];
|
|
18762
|
+
return insights;
|
|
18763
|
+
}
|
|
18764
|
+
fallbackInsights() {
|
|
18765
|
+
return [{
|
|
18766
|
+
title: 'Analyse nicht verfügbar',
|
|
18767
|
+
description: 'Die KI-Analyse konnte nicht durchgeführt werden. Bitte prüfen Sie den API-Schlüssel in den Widget-Einstellungen.',
|
|
18768
|
+
severity: 'info'
|
|
18769
|
+
}];
|
|
18770
|
+
}
|
|
18771
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
18772
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsService, providedIn: 'root' });
|
|
18773
|
+
}
|
|
18774
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsService, decorators: [{
|
|
18775
|
+
type: Injectable,
|
|
18776
|
+
args: [{ providedIn: 'root' }]
|
|
18777
|
+
}] });
|
|
18778
|
+
|
|
18779
|
+
class AiInsightsWidgetComponent {
|
|
18780
|
+
aiService = inject(AiInsightsService);
|
|
18781
|
+
stateService = inject(MeshBoardStateService);
|
|
18782
|
+
refreshTimer = null;
|
|
18783
|
+
infoIcon = infoCircleIcon;
|
|
18784
|
+
warningIcon = exclamationCircleIcon;
|
|
18785
|
+
successIcon = checkCircleIcon;
|
|
18786
|
+
criticalIcon = xCircleIcon;
|
|
18787
|
+
config;
|
|
18788
|
+
_isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
|
|
18789
|
+
_error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
|
|
18790
|
+
_insights = signal(null, ...(ngDevMode ? [{ debugName: "_insights" }] : /* istanbul ignore next */ []));
|
|
18791
|
+
isLoading = this._isLoading.asReadonly();
|
|
18792
|
+
error = this._error.asReadonly();
|
|
18793
|
+
data = this._insights.asReadonly();
|
|
18794
|
+
insights = this._insights.asReadonly();
|
|
18795
|
+
isNotConfigured() {
|
|
18796
|
+
return false;
|
|
18797
|
+
}
|
|
18798
|
+
ngOnInit() {
|
|
18799
|
+
this.loadInsights();
|
|
18800
|
+
this.startAutoRefresh();
|
|
18801
|
+
}
|
|
18802
|
+
ngOnChanges(changes) {
|
|
18803
|
+
if (changes['config'] && !changes['config'].firstChange) {
|
|
18804
|
+
this.stopAutoRefresh();
|
|
18805
|
+
this.loadInsights();
|
|
18806
|
+
this.startAutoRefresh();
|
|
18807
|
+
}
|
|
18808
|
+
}
|
|
18809
|
+
ngOnDestroy() {
|
|
18810
|
+
this.stopAutoRefresh();
|
|
18811
|
+
}
|
|
18812
|
+
refresh() {
|
|
18813
|
+
this.loadInsights();
|
|
18814
|
+
}
|
|
18815
|
+
getIcon(severity) {
|
|
18816
|
+
switch (severity) {
|
|
18817
|
+
case 'warning': return this.warningIcon;
|
|
18818
|
+
case 'success': return this.successIcon;
|
|
18819
|
+
case 'critical': return this.criticalIcon;
|
|
18820
|
+
default: return this.infoIcon;
|
|
18821
|
+
}
|
|
18822
|
+
}
|
|
18823
|
+
async loadInsights() {
|
|
18824
|
+
this._isLoading.set(true);
|
|
18825
|
+
this._error.set(null);
|
|
18826
|
+
try {
|
|
18827
|
+
const widgets = this.stateService.widgets();
|
|
18828
|
+
const widgetSummaries = this.aiService.gatherWidgetContext(widgets);
|
|
18829
|
+
const context = {
|
|
18830
|
+
apiKey: this.config.apiKey,
|
|
18831
|
+
model: this.config.model,
|
|
18832
|
+
systemPrompt: this.config.systemPrompt,
|
|
18833
|
+
maxInsights: this.config.maxInsights ?? 4,
|
|
18834
|
+
domainContext: this.config.domainContext ?? 'energy management',
|
|
18835
|
+
widgetSummaries
|
|
18836
|
+
};
|
|
18837
|
+
const insights = await this.aiService.generateInsights(context);
|
|
18838
|
+
this._insights.set(insights);
|
|
18839
|
+
}
|
|
18840
|
+
catch (err) {
|
|
18841
|
+
this._error.set(err instanceof Error ? err.message : 'Failed to generate insights');
|
|
18842
|
+
}
|
|
18843
|
+
finally {
|
|
18844
|
+
this._isLoading.set(false);
|
|
18845
|
+
}
|
|
18846
|
+
}
|
|
18847
|
+
startAutoRefresh() {
|
|
18848
|
+
this.stopAutoRefresh();
|
|
18849
|
+
const interval = (this.config?.refreshInterval ?? 0) * 1000;
|
|
18850
|
+
if (interval > 0) {
|
|
18851
|
+
this.refreshTimer = setInterval(() => this.loadInsights(), interval);
|
|
18852
|
+
}
|
|
18853
|
+
}
|
|
18854
|
+
stopAutoRefresh() {
|
|
18855
|
+
if (this.refreshTimer) {
|
|
18856
|
+
clearInterval(this.refreshTimer);
|
|
18857
|
+
this.refreshTimer = null;
|
|
18858
|
+
}
|
|
18859
|
+
}
|
|
18860
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18861
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: AiInsightsWidgetComponent, isStandalone: true, selector: "mm-ai-insights-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: `
|
|
18862
|
+
<div class="ai-insights-widget">
|
|
18863
|
+
@if (isLoading() && !insights()) {
|
|
18864
|
+
<div class="loading-state">
|
|
18865
|
+
<div class="loading-dots">
|
|
18866
|
+
<span></span><span></span><span></span>
|
|
18867
|
+
</div>
|
|
18868
|
+
<span class="loading-text">Generating AI insights...</span>
|
|
18869
|
+
</div>
|
|
18870
|
+
} @else if (error()) {
|
|
18871
|
+
<div class="error-state">{{ error() }}</div>
|
|
18872
|
+
} @else if (insights(); as items) {
|
|
18873
|
+
<div class="insights-list">
|
|
18874
|
+
@for (insight of items; track insight.title) {
|
|
18875
|
+
<div class="insight-card" [class]="'severity-' + insight.severity">
|
|
18876
|
+
<div class="insight-icon">
|
|
18877
|
+
<kendo-svg-icon [icon]="getIcon(insight.severity)" size="medium"></kendo-svg-icon>
|
|
18878
|
+
</div>
|
|
18879
|
+
<div class="insight-content">
|
|
18880
|
+
<div class="insight-title">{{ insight.title }}</div>
|
|
18881
|
+
<div class="insight-description">{{ insight.description }}</div>
|
|
18882
|
+
</div>
|
|
18883
|
+
</div>
|
|
18884
|
+
}
|
|
18885
|
+
</div>
|
|
18886
|
+
@if (isLoading()) {
|
|
18887
|
+
<div class="refresh-indicator">Aktualisierung...</div>
|
|
18888
|
+
}
|
|
18889
|
+
}
|
|
18890
|
+
</div>
|
|
18891
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%}.ai-insights-widget{height:100%;overflow-y:auto;padding:8px 12px}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:12px;opacity:.6}.loading-dots{display:flex;gap:6px}.loading-dots span{width:8px;height:8px;border-radius:50%;background:var(--kendo-color-primary, #06b6d4);animation:pulse-dot 1.4s ease-in-out infinite}.loading-dots span:nth-child(2){animation-delay:.2s}.loading-dots span:nth-child(3){animation-delay:.4s}@keyframes pulse-dot{0%,80%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1.2)}}.loading-text{font-size:.8rem}.insights-list{display:flex;flex-direction:column;gap:8px}.insight-card{display:flex;gap:10px;padding:10px 12px;border-radius:6px;border-left:3px solid var(--mm-ai-border-color, #6b7280);background:var(--mm-ai-card-bg, rgba(255, 255, 255, .03))}.insight-icon{flex-shrink:0;padding-top:2px;color:var(--mm-ai-border-color, #6b7280)}.insight-content{flex:1;min-width:0}.insight-title{font-size:.85rem;font-weight:600;margin-bottom:4px;color:var(--kendo-color-on-app-surface, inherit)}.insight-description{font-size:.8rem;line-height:1.4;opacity:.8}.severity-info{--mm-ai-border-color: var(--mm-ai-info, #3b82f6)}.severity-warning{--mm-ai-border-color: var(--mm-ai-warning, #f59e0b)}.severity-success{--mm-ai-border-color: var(--mm-ai-success, #10b981)}.severity-critical{--mm-ai-border-color: var(--mm-ai-critical, #ef4444)}.refresh-indicator{text-align:center;padding:8px;font-size:.7rem;opacity:.5}.error-state{text-align:center;padding:16px;color:var(--kendo-color-error, #dc3545)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }] });
|
|
18892
|
+
}
|
|
18893
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsWidgetComponent, decorators: [{
|
|
18894
|
+
type: Component,
|
|
18895
|
+
args: [{ selector: 'mm-ai-insights-widget', standalone: true, imports: [CommonModule, SVGIconModule, WidgetNotConfiguredComponent], template: `
|
|
18896
|
+
<div class="ai-insights-widget">
|
|
18897
|
+
@if (isLoading() && !insights()) {
|
|
18898
|
+
<div class="loading-state">
|
|
18899
|
+
<div class="loading-dots">
|
|
18900
|
+
<span></span><span></span><span></span>
|
|
18901
|
+
</div>
|
|
18902
|
+
<span class="loading-text">Generating AI insights...</span>
|
|
18903
|
+
</div>
|
|
18904
|
+
} @else if (error()) {
|
|
18905
|
+
<div class="error-state">{{ error() }}</div>
|
|
18906
|
+
} @else if (insights(); as items) {
|
|
18907
|
+
<div class="insights-list">
|
|
18908
|
+
@for (insight of items; track insight.title) {
|
|
18909
|
+
<div class="insight-card" [class]="'severity-' + insight.severity">
|
|
18910
|
+
<div class="insight-icon">
|
|
18911
|
+
<kendo-svg-icon [icon]="getIcon(insight.severity)" size="medium"></kendo-svg-icon>
|
|
18912
|
+
</div>
|
|
18913
|
+
<div class="insight-content">
|
|
18914
|
+
<div class="insight-title">{{ insight.title }}</div>
|
|
18915
|
+
<div class="insight-description">{{ insight.description }}</div>
|
|
18916
|
+
</div>
|
|
18917
|
+
</div>
|
|
18918
|
+
}
|
|
18919
|
+
</div>
|
|
18920
|
+
@if (isLoading()) {
|
|
18921
|
+
<div class="refresh-indicator">Aktualisierung...</div>
|
|
18922
|
+
}
|
|
18923
|
+
}
|
|
18924
|
+
</div>
|
|
18925
|
+
`, styles: [":host{display:block;width:100%;height:100%}.ai-insights-widget{height:100%;overflow-y:auto;padding:8px 12px}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:12px;opacity:.6}.loading-dots{display:flex;gap:6px}.loading-dots span{width:8px;height:8px;border-radius:50%;background:var(--kendo-color-primary, #06b6d4);animation:pulse-dot 1.4s ease-in-out infinite}.loading-dots span:nth-child(2){animation-delay:.2s}.loading-dots span:nth-child(3){animation-delay:.4s}@keyframes pulse-dot{0%,80%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1.2)}}.loading-text{font-size:.8rem}.insights-list{display:flex;flex-direction:column;gap:8px}.insight-card{display:flex;gap:10px;padding:10px 12px;border-radius:6px;border-left:3px solid var(--mm-ai-border-color, #6b7280);background:var(--mm-ai-card-bg, rgba(255, 255, 255, .03))}.insight-icon{flex-shrink:0;padding-top:2px;color:var(--mm-ai-border-color, #6b7280)}.insight-content{flex:1;min-width:0}.insight-title{font-size:.85rem;font-weight:600;margin-bottom:4px;color:var(--kendo-color-on-app-surface, inherit)}.insight-description{font-size:.8rem;line-height:1.4;opacity:.8}.severity-info{--mm-ai-border-color: var(--mm-ai-info, #3b82f6)}.severity-warning{--mm-ai-border-color: var(--mm-ai-warning, #f59e0b)}.severity-success{--mm-ai-border-color: var(--mm-ai-success, #10b981)}.severity-critical{--mm-ai-border-color: var(--mm-ai-critical, #ef4444)}.refresh-indicator{text-align:center;padding:8px;font-size:.7rem;opacity:.5}.error-state{text-align:center;padding:16px;color:var(--kendo-color-error, #dc3545)}\n"] }]
|
|
18926
|
+
}], propDecorators: { config: [{
|
|
18927
|
+
type: Input
|
|
18928
|
+
}] } });
|
|
18929
|
+
|
|
18930
|
+
class AiInsightsConfigDialogComponent {
|
|
18931
|
+
windowRef = inject(WindowRef);
|
|
18932
|
+
apiKey = '';
|
|
18933
|
+
model = 'claude-sonnet-4-20250514';
|
|
18934
|
+
domainContext = 'energy management';
|
|
18935
|
+
refreshInterval = 0;
|
|
18936
|
+
maxInsights = 4;
|
|
18937
|
+
onSave() {
|
|
18938
|
+
const result = {
|
|
18939
|
+
ckTypeId: '',
|
|
18940
|
+
apiKey: this.apiKey || undefined,
|
|
18941
|
+
model: this.model,
|
|
18942
|
+
domainContext: this.domainContext,
|
|
18943
|
+
refreshInterval: this.refreshInterval,
|
|
18944
|
+
maxInsights: this.maxInsights
|
|
18945
|
+
};
|
|
18946
|
+
this.windowRef.close(result);
|
|
18947
|
+
}
|
|
18948
|
+
onCancel() {
|
|
18949
|
+
this.windowRef.close();
|
|
18950
|
+
}
|
|
18951
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18952
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: AiInsightsConfigDialogComponent, isStandalone: true, selector: "mm-ai-insights-config-dialog", ngImport: i0, template: `
|
|
18953
|
+
<div class="config-form">
|
|
18954
|
+
<div class="form-group">
|
|
18955
|
+
<kendo-label text="Anthropic API Key (optional for demo)">
|
|
18956
|
+
<kendo-textbox [(ngModel)]="apiKey" type="password" placeholder="sk-ant-..."></kendo-textbox>
|
|
18957
|
+
</kendo-label>
|
|
18958
|
+
<span class="hint">Without API key, simulated insights are shown.</span>
|
|
18959
|
+
</div>
|
|
18960
|
+
<div class="form-group">
|
|
18961
|
+
<kendo-label text="Model">
|
|
18962
|
+
<kendo-textbox [(ngModel)]="model"></kendo-textbox>
|
|
18963
|
+
</kendo-label>
|
|
18964
|
+
</div>
|
|
18965
|
+
<div class="form-group">
|
|
18966
|
+
<kendo-label text="Domain Context">
|
|
18967
|
+
<kendo-textbox [(ngModel)]="domainContext" placeholder="e.g. energy management"></kendo-textbox>
|
|
18968
|
+
</kendo-label>
|
|
18969
|
+
</div>
|
|
18970
|
+
<div class="form-row">
|
|
18971
|
+
<div class="form-group">
|
|
18972
|
+
<kendo-label text="Refresh Interval (seconds, 0=off)">
|
|
18973
|
+
<kendo-numerictextbox [(ngModel)]="refreshInterval" [min]="0" [step]="10" [format]="'n0'"></kendo-numerictextbox>
|
|
18974
|
+
</kendo-label>
|
|
18975
|
+
</div>
|
|
18976
|
+
<div class="form-group">
|
|
18977
|
+
<kendo-label text="Max Insights">
|
|
18978
|
+
<kendo-numerictextbox [(ngModel)]="maxInsights" [min]="1" [max]="8" [format]="'n0'"></kendo-numerictextbox>
|
|
18979
|
+
</kendo-label>
|
|
18980
|
+
</div>
|
|
18981
|
+
</div>
|
|
18982
|
+
<div class="mm-dialog-actions">
|
|
18983
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
18984
|
+
<button kendoButton themeColor="primary" (click)="onSave()">Save</button>
|
|
18985
|
+
</div>
|
|
18986
|
+
</div>
|
|
18987
|
+
`, isInline: true, styles: [".config-form{display:flex;flex-direction:column;gap:12px;padding:16px}.form-group{display:flex;flex-direction:column;gap:4px}.form-row{display:flex;gap:16px}.form-row .form-group{flex:1}.mm-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}.hint{font-size:.75rem;opacity:.6}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: i3.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: i3.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: "ngmodule", type: LabelModule }, { kind: "component", type: i4$1.LabelComponent, selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }] });
|
|
18988
|
+
}
|
|
18989
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsConfigDialogComponent, decorators: [{
|
|
18990
|
+
type: Component,
|
|
18991
|
+
args: [{ selector: 'mm-ai-insights-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule, LabelModule], template: `
|
|
18992
|
+
<div class="config-form">
|
|
18993
|
+
<div class="form-group">
|
|
18994
|
+
<kendo-label text="Anthropic API Key (optional for demo)">
|
|
18995
|
+
<kendo-textbox [(ngModel)]="apiKey" type="password" placeholder="sk-ant-..."></kendo-textbox>
|
|
18996
|
+
</kendo-label>
|
|
18997
|
+
<span class="hint">Without API key, simulated insights are shown.</span>
|
|
18998
|
+
</div>
|
|
18999
|
+
<div class="form-group">
|
|
19000
|
+
<kendo-label text="Model">
|
|
19001
|
+
<kendo-textbox [(ngModel)]="model"></kendo-textbox>
|
|
19002
|
+
</kendo-label>
|
|
19003
|
+
</div>
|
|
19004
|
+
<div class="form-group">
|
|
19005
|
+
<kendo-label text="Domain Context">
|
|
19006
|
+
<kendo-textbox [(ngModel)]="domainContext" placeholder="e.g. energy management"></kendo-textbox>
|
|
19007
|
+
</kendo-label>
|
|
19008
|
+
</div>
|
|
19009
|
+
<div class="form-row">
|
|
19010
|
+
<div class="form-group">
|
|
19011
|
+
<kendo-label text="Refresh Interval (seconds, 0=off)">
|
|
19012
|
+
<kendo-numerictextbox [(ngModel)]="refreshInterval" [min]="0" [step]="10" [format]="'n0'"></kendo-numerictextbox>
|
|
19013
|
+
</kendo-label>
|
|
19014
|
+
</div>
|
|
19015
|
+
<div class="form-group">
|
|
19016
|
+
<kendo-label text="Max Insights">
|
|
19017
|
+
<kendo-numerictextbox [(ngModel)]="maxInsights" [min]="1" [max]="8" [format]="'n0'"></kendo-numerictextbox>
|
|
19018
|
+
</kendo-label>
|
|
19019
|
+
</div>
|
|
19020
|
+
</div>
|
|
19021
|
+
<div class="mm-dialog-actions">
|
|
19022
|
+
<button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
|
|
19023
|
+
<button kendoButton themeColor="primary" (click)="onSave()">Save</button>
|
|
19024
|
+
</div>
|
|
19025
|
+
</div>
|
|
19026
|
+
`, styles: [".config-form{display:flex;flex-direction:column;gap:12px;padding:16px}.form-group{display:flex;flex-direction:column;gap:4px}.form-row{display:flex;gap:16px}.form-row .form-group{flex:1}.mm-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}.hint{font-size:.75rem;opacity:.6}\n"] }]
|
|
19027
|
+
}] });
|
|
19028
|
+
|
|
19029
|
+
class ProcessDataService {
|
|
19030
|
+
dataService = inject(MeshBoardDataService);
|
|
19031
|
+
variableService = inject(MeshBoardVariableService);
|
|
19032
|
+
stateService = inject(MeshBoardStateService);
|
|
19033
|
+
getProcessDiagramGQL = inject(GetProcessDiagramDtoGQL);
|
|
19034
|
+
getProcessDiagramsGQL = inject(GetProcessDiagramsDtoGQL);
|
|
19035
|
+
createProcessDiagramGQL = inject(CreateProcessDiagramDtoGQL);
|
|
19036
|
+
updateProcessDiagramGQL = inject(UpdateProcessDiagramDtoGQL);
|
|
19037
|
+
executeRuntimeQueryGQL = inject(ExecuteRuntimeQueryDtoGQL);
|
|
19038
|
+
/**
|
|
19039
|
+
* Loads a list of available process diagrams from the backend
|
|
19040
|
+
*
|
|
19041
|
+
* @param searchText - Optional search text to filter diagrams
|
|
19042
|
+
* @returns List of process diagram summaries
|
|
19043
|
+
*/
|
|
19044
|
+
async loadDiagramList(searchText) {
|
|
19045
|
+
try {
|
|
19046
|
+
const result = await firstValueFrom(this.getProcessDiagramsGQL.fetch({
|
|
19047
|
+
first: 100,
|
|
19048
|
+
searchFilter: searchText ? { searchTerm: searchText, language: 'de' } : undefined
|
|
19049
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Apollo fetch requires flexible variable typing
|
|
19050
|
+
}));
|
|
19051
|
+
const items = result.data?.runtime?.systemUIProcessDiagram?.items || [];
|
|
19052
|
+
return items
|
|
19053
|
+
.filter((item) => item !== null)
|
|
19054
|
+
.map(item => ({
|
|
19055
|
+
rtId: item.rtId,
|
|
19056
|
+
name: item.name,
|
|
19057
|
+
description: item.description,
|
|
19058
|
+
version: item.version,
|
|
19059
|
+
canvasWidth: item.canvasWidth,
|
|
19060
|
+
canvasHeight: item.canvasHeight
|
|
19061
|
+
}));
|
|
19062
|
+
}
|
|
19063
|
+
catch (error) {
|
|
19064
|
+
console.error('Error loading process diagrams:', error);
|
|
19065
|
+
return [];
|
|
19066
|
+
}
|
|
19067
|
+
}
|
|
19068
|
+
/**
|
|
19069
|
+
* Loads a process diagram configuration from the backend
|
|
19070
|
+
*
|
|
19071
|
+
* @param rtId - The runtime ID of the process diagram
|
|
19072
|
+
* @returns The process diagram configuration
|
|
19073
|
+
*/
|
|
19074
|
+
async loadDiagram(rtId) {
|
|
19075
|
+
const result = await firstValueFrom(this.getProcessDiagramGQL.fetch({ variables: { rtId } }));
|
|
19076
|
+
const item = result.data?.runtime?.systemUIProcessDiagram?.items?.[0];
|
|
19077
|
+
if (!item) {
|
|
19078
|
+
throw new Error(`Process diagram not found: ${rtId}`);
|
|
19079
|
+
}
|
|
19080
|
+
// Parse JSON fields
|
|
19081
|
+
const elements = this.parseJsonField(item.elements, []);
|
|
19082
|
+
const itemWithExtensions = item;
|
|
19083
|
+
const primitives = itemWithExtensions.primitives
|
|
19084
|
+
? this.parseJsonField(itemWithExtensions.primitives, [])
|
|
19085
|
+
: undefined;
|
|
19086
|
+
const symbolInstances = itemWithExtensions.symbolInstances
|
|
19087
|
+
? this.parseJsonField(itemWithExtensions.symbolInstances, [])
|
|
19088
|
+
: undefined;
|
|
19089
|
+
const connections = this.parseJsonField(item.connections, []);
|
|
19090
|
+
const variables = item.variables ? this.parseJsonField(item.variables, []) : undefined;
|
|
19091
|
+
// Parse diagram-level property fields
|
|
19092
|
+
const transformProperties = itemWithExtensions.transformProperties
|
|
19093
|
+
? this.parseJsonField(itemWithExtensions.transformProperties, [])
|
|
19094
|
+
: undefined;
|
|
19095
|
+
const propertyBindings = itemWithExtensions.propertyBindings
|
|
19096
|
+
? this.parseJsonField(itemWithExtensions.propertyBindings, [])
|
|
19097
|
+
: undefined;
|
|
19098
|
+
const animations = itemWithExtensions.animations
|
|
19099
|
+
? this.parseJsonField(itemWithExtensions.animations, [])
|
|
19100
|
+
: undefined;
|
|
19101
|
+
return {
|
|
19102
|
+
id: item.rtId,
|
|
19103
|
+
name: item.name,
|
|
19104
|
+
description: item.description ?? undefined,
|
|
19105
|
+
version: item.version,
|
|
19106
|
+
canvas: {
|
|
19107
|
+
width: item.canvasWidth,
|
|
19108
|
+
height: item.canvasHeight,
|
|
19109
|
+
backgroundColor: item.canvasBackgroundColor ?? undefined
|
|
19110
|
+
},
|
|
19111
|
+
elements,
|
|
19112
|
+
primitives,
|
|
19113
|
+
symbolInstances,
|
|
19114
|
+
connections,
|
|
19115
|
+
variables,
|
|
19116
|
+
transformProperties,
|
|
19117
|
+
propertyBindings,
|
|
19118
|
+
animations,
|
|
19119
|
+
refreshInterval: item.refreshInterval ?? undefined
|
|
19120
|
+
};
|
|
19121
|
+
}
|
|
19122
|
+
/**
|
|
19123
|
+
* Creates a new process diagram in the backend
|
|
19124
|
+
*
|
|
19125
|
+
* @param diagram - The process diagram configuration to create
|
|
19126
|
+
* @returns The created diagram with its new rtId
|
|
19127
|
+
*/
|
|
19128
|
+
async createDiagram(diagram) {
|
|
19129
|
+
const input = this.toInputDto(diagram);
|
|
19130
|
+
const result = await firstValueFrom(this.createProcessDiagramGQL.mutate({
|
|
19131
|
+
variables: { entities: [input] }
|
|
19132
|
+
}));
|
|
19133
|
+
const created = result.data?.runtime?.systemUIProcessDiagrams?.create?.[0];
|
|
19134
|
+
if (!created) {
|
|
19135
|
+
throw new Error('Failed to create process diagram');
|
|
19136
|
+
}
|
|
19137
|
+
return {
|
|
19138
|
+
...diagram,
|
|
19139
|
+
id: created.rtId
|
|
19140
|
+
};
|
|
19141
|
+
}
|
|
19142
|
+
/**
|
|
19143
|
+
* Updates an existing process diagram in the backend
|
|
19144
|
+
*
|
|
19145
|
+
* @param diagram - The process diagram configuration to update
|
|
19146
|
+
* @returns The updated diagram
|
|
19147
|
+
*/
|
|
19148
|
+
async updateDiagram(diagram) {
|
|
19149
|
+
if (!diagram.id) {
|
|
19150
|
+
throw new Error('Cannot update diagram without rtId');
|
|
19151
|
+
}
|
|
19152
|
+
const input = {
|
|
19153
|
+
rtId: diagram.id,
|
|
19154
|
+
item: this.toInputDto(diagram)
|
|
19155
|
+
};
|
|
19156
|
+
const result = await firstValueFrom(this.updateProcessDiagramGQL.mutate({
|
|
19157
|
+
variables: { entities: [input] }
|
|
19158
|
+
}));
|
|
19159
|
+
const updated = result.data?.runtime?.systemUIProcessDiagrams?.update?.[0];
|
|
19160
|
+
if (!updated) {
|
|
19161
|
+
throw new Error('Failed to update process diagram');
|
|
19162
|
+
}
|
|
19163
|
+
return diagram;
|
|
19164
|
+
}
|
|
19165
|
+
/**
|
|
19166
|
+
* Saves a process diagram (creates new or updates existing)
|
|
19167
|
+
*
|
|
19168
|
+
* @param diagram - The process diagram configuration to save
|
|
19169
|
+
* @param isNew - Whether this is a new diagram (default: false)
|
|
19170
|
+
* @returns The saved diagram
|
|
19171
|
+
*/
|
|
17425
19172
|
async saveDiagram(diagram, isNew = false) {
|
|
17426
19173
|
if (isNew || !diagram.id) {
|
|
17427
19174
|
return this.createDiagram(diagram);
|
|
@@ -23011,6 +24758,7 @@ function registerDefaultWidgets(registry) {
|
|
|
23011
24758
|
initialPrefix: kpiWidget.prefix,
|
|
23012
24759
|
initialSuffix: kpiWidget.suffix,
|
|
23013
24760
|
initialTrend: kpiWidget.trend,
|
|
24761
|
+
initialComparisonText: kpiWidget.comparisonText,
|
|
23014
24762
|
// Filters
|
|
23015
24763
|
initialFilters: kpiWidget.filters
|
|
23016
24764
|
};
|
|
@@ -23135,7 +24883,8 @@ function registerDefaultWidgets(registry) {
|
|
|
23135
24883
|
prefix: config['prefix'],
|
|
23136
24884
|
suffix: config['suffix'],
|
|
23137
24885
|
icon: config['icon'],
|
|
23138
|
-
trend: config['trend']
|
|
24886
|
+
trend: config['trend'],
|
|
24887
|
+
comparisonText: config['comparisonText']
|
|
23139
24888
|
};
|
|
23140
24889
|
}
|
|
23141
24890
|
if (dataSource.type === 'persistentQuery') {
|
|
@@ -23153,6 +24902,7 @@ function registerDefaultWidgets(registry) {
|
|
|
23153
24902
|
suffix: config['suffix'],
|
|
23154
24903
|
icon: config['icon'],
|
|
23155
24904
|
trend: config['trend'],
|
|
24905
|
+
comparisonText: config['comparisonText'],
|
|
23156
24906
|
filters: config['filters']
|
|
23157
24907
|
};
|
|
23158
24908
|
}
|
|
@@ -23167,6 +24917,7 @@ function registerDefaultWidgets(registry) {
|
|
|
23167
24917
|
suffix: config['suffix'],
|
|
23168
24918
|
icon: config['icon'],
|
|
23169
24919
|
trend: config['trend'],
|
|
24920
|
+
comparisonText: config['comparisonText'],
|
|
23170
24921
|
filters: config['filters']
|
|
23171
24922
|
};
|
|
23172
24923
|
}
|
|
@@ -23741,6 +25492,8 @@ function registerDefaultWidgets(registry) {
|
|
|
23741
25492
|
initialShowLegend: barWidget.showLegend,
|
|
23742
25493
|
initialLegendPosition: barWidget.legendPosition,
|
|
23743
25494
|
initialShowDataLabels: barWidget.showDataLabels,
|
|
25495
|
+
initialColorThresholds: barWidget.colorThresholds,
|
|
25496
|
+
initialDefaultBarColor: barWidget.defaultBarColor,
|
|
23744
25497
|
initialFilters: barWidget.filters
|
|
23745
25498
|
};
|
|
23746
25499
|
},
|
|
@@ -23767,6 +25520,8 @@ function registerDefaultWidgets(registry) {
|
|
|
23767
25520
|
showLegend: result.showLegend,
|
|
23768
25521
|
legendPosition: result.legendPosition,
|
|
23769
25522
|
showDataLabels: result.showDataLabels,
|
|
25523
|
+
colorThresholds: result.colorThresholds,
|
|
25524
|
+
defaultBarColor: result.defaultBarColor,
|
|
23770
25525
|
filters
|
|
23771
25526
|
};
|
|
23772
25527
|
},
|
|
@@ -23797,6 +25552,9 @@ function registerDefaultWidgets(registry) {
|
|
|
23797
25552
|
showLegend: widget.showLegend,
|
|
23798
25553
|
legendPosition: widget.legendPosition,
|
|
23799
25554
|
showDataLabels: widget.showDataLabels,
|
|
25555
|
+
dataLabelSuffix: widget.dataLabelSuffix,
|
|
25556
|
+
colorThresholds: widget.colorThresholds,
|
|
25557
|
+
defaultBarColor: widget.defaultBarColor,
|
|
23800
25558
|
queryName: widget.dataSource.queryName,
|
|
23801
25559
|
queryRtId: widget.dataSource.queryRtId,
|
|
23802
25560
|
filters: widget.filters
|
|
@@ -23819,6 +25577,9 @@ function registerDefaultWidgets(registry) {
|
|
|
23819
25577
|
showLegend: config['showLegend'] ?? true,
|
|
23820
25578
|
legendPosition: config['legendPosition'] ?? 'right',
|
|
23821
25579
|
showDataLabels: config['showDataLabels'] ?? false,
|
|
25580
|
+
dataLabelSuffix: config['dataLabelSuffix'],
|
|
25581
|
+
colorThresholds: config['colorThresholds'],
|
|
25582
|
+
defaultBarColor: config['defaultBarColor'],
|
|
23822
25583
|
filters: config['filters']
|
|
23823
25584
|
};
|
|
23824
25585
|
}
|
|
@@ -23848,6 +25609,7 @@ function registerDefaultWidgets(registry) {
|
|
|
23848
25609
|
initialShowLegend: lineWidget.showLegend,
|
|
23849
25610
|
initialLegendPosition: lineWidget.legendPosition,
|
|
23850
25611
|
initialShowMarkers: lineWidget.showMarkers,
|
|
25612
|
+
initialReferenceLines: lineWidget.referenceLines,
|
|
23851
25613
|
initialFilters: lineWidget.filters
|
|
23852
25614
|
};
|
|
23853
25615
|
},
|
|
@@ -23873,6 +25635,7 @@ function registerDefaultWidgets(registry) {
|
|
|
23873
25635
|
showLegend: result.showLegend,
|
|
23874
25636
|
legendPosition: result.legendPosition,
|
|
23875
25637
|
showMarkers: result.showMarkers,
|
|
25638
|
+
referenceLines: result.referenceLines,
|
|
23876
25639
|
filters
|
|
23877
25640
|
};
|
|
23878
25641
|
},
|
|
@@ -23902,6 +25665,7 @@ function registerDefaultWidgets(registry) {
|
|
|
23902
25665
|
showLegend: widget.showLegend,
|
|
23903
25666
|
legendPosition: widget.legendPosition,
|
|
23904
25667
|
showMarkers: widget.showMarkers,
|
|
25668
|
+
referenceLines: widget.referenceLines,
|
|
23905
25669
|
queryName: widget.dataSource.queryName,
|
|
23906
25670
|
queryRtId: widget.dataSource.queryRtId,
|
|
23907
25671
|
filters: widget.filters
|
|
@@ -23923,6 +25687,7 @@ function registerDefaultWidgets(registry) {
|
|
|
23923
25687
|
showLegend: config['showLegend'] ?? true,
|
|
23924
25688
|
legendPosition: config['legendPosition'] ?? 'right',
|
|
23925
25689
|
showMarkers: config['showMarkers'] ?? false,
|
|
25690
|
+
referenceLines: config['referenceLines'],
|
|
23926
25691
|
filters: config['filters']
|
|
23927
25692
|
};
|
|
23928
25693
|
}
|
|
@@ -24360,107 +26125,401 @@ function registerDefaultWidgets(registry) {
|
|
|
24360
26125
|
configDialogSize: { width: 750, height: 650, minWidth: 550, minHeight: 450 },
|
|
24361
26126
|
configDialogTitle: 'Heatmap Configuration',
|
|
24362
26127
|
defaultSize: { colSpan: 3, rowSpan: 2 },
|
|
24363
|
-
supportedDataSources: ['persistentQuery'],
|
|
26128
|
+
supportedDataSources: ['persistentQuery'],
|
|
26129
|
+
getInitialConfig: (widget) => {
|
|
26130
|
+
const heatmapWidget = widget;
|
|
26131
|
+
const dataSource = heatmapWidget.dataSource;
|
|
26132
|
+
const isPersistentQuery = dataSource.type === 'persistentQuery';
|
|
26133
|
+
return {
|
|
26134
|
+
initialQueryRtId: isPersistentQuery ? dataSource.queryRtId : undefined,
|
|
26135
|
+
initialQueryName: isPersistentQuery ? dataSource.queryName : undefined,
|
|
26136
|
+
initialDateField: heatmapWidget.dateField,
|
|
26137
|
+
initialDateEndField: heatmapWidget.dateEndField,
|
|
26138
|
+
initialValueField: heatmapWidget.valueField,
|
|
26139
|
+
initialAggregation: heatmapWidget.aggregation,
|
|
26140
|
+
initialColorScheme: heatmapWidget.colorScheme,
|
|
26141
|
+
initialShowLegend: heatmapWidget.showLegend,
|
|
26142
|
+
initialLegendPosition: heatmapWidget.legendPosition,
|
|
26143
|
+
initialDecimalPlaces: heatmapWidget.decimalPlaces,
|
|
26144
|
+
initialCompactNumbers: heatmapWidget.compactNumbers,
|
|
26145
|
+
initialValueMultiplier: heatmapWidget.valueMultiplier,
|
|
26146
|
+
initialFilters: heatmapWidget.filters
|
|
26147
|
+
};
|
|
26148
|
+
},
|
|
26149
|
+
applyConfigResult: (widget, result) => {
|
|
26150
|
+
const dataSource = {
|
|
26151
|
+
type: 'persistentQuery',
|
|
26152
|
+
queryRtId: result.queryRtId,
|
|
26153
|
+
queryName: result.queryName
|
|
26154
|
+
};
|
|
26155
|
+
const filters = result.filters?.map(f => ({
|
|
26156
|
+
attributePath: f.attributePath,
|
|
26157
|
+
operator: String(f.operator),
|
|
26158
|
+
comparisonValue: f.comparisonValue
|
|
26159
|
+
}));
|
|
26160
|
+
return {
|
|
26161
|
+
...widget,
|
|
26162
|
+
dataSource,
|
|
26163
|
+
dateField: result.dateField,
|
|
26164
|
+
dateEndField: result.dateEndField,
|
|
26165
|
+
valueField: result.valueField,
|
|
26166
|
+
aggregation: result.aggregation,
|
|
26167
|
+
colorScheme: result.colorScheme,
|
|
26168
|
+
showLegend: result.showLegend,
|
|
26169
|
+
legendPosition: result.legendPosition,
|
|
26170
|
+
decimalPlaces: result.decimalPlaces,
|
|
26171
|
+
compactNumbers: result.compactNumbers,
|
|
26172
|
+
valueMultiplier: result.valueMultiplier,
|
|
26173
|
+
filters
|
|
26174
|
+
};
|
|
26175
|
+
},
|
|
26176
|
+
// SOLID: Factory function
|
|
26177
|
+
createDefaultConfig: (base) => ({
|
|
26178
|
+
...base,
|
|
26179
|
+
type: 'heatmap',
|
|
26180
|
+
colSpan: 3,
|
|
26181
|
+
rowSpan: 2,
|
|
26182
|
+
dataSource: createDefaultPersistentQueryDataSource(),
|
|
26183
|
+
dateField: '',
|
|
26184
|
+
aggregation: 'count',
|
|
26185
|
+
colorScheme: 'green',
|
|
26186
|
+
showLegend: true,
|
|
26187
|
+
legendPosition: 'bottom'
|
|
26188
|
+
}),
|
|
26189
|
+
// SOLID: Serialization for persistence
|
|
26190
|
+
toPersistedConfig: (widget) => ({
|
|
26191
|
+
dataSourceType: 'persistentQuery',
|
|
26192
|
+
dataSourceRtId: widget.dataSource.queryRtId,
|
|
26193
|
+
config: {
|
|
26194
|
+
dateField: widget.dateField,
|
|
26195
|
+
dateEndField: widget.dateEndField,
|
|
26196
|
+
valueField: widget.valueField,
|
|
26197
|
+
aggregation: widget.aggregation,
|
|
26198
|
+
colorScheme: widget.colorScheme,
|
|
26199
|
+
showLegend: widget.showLegend,
|
|
26200
|
+
legendPosition: widget.legendPosition,
|
|
26201
|
+
decimalPlaces: widget.decimalPlaces,
|
|
26202
|
+
compactNumbers: widget.compactNumbers,
|
|
26203
|
+
valueMultiplier: widget.valueMultiplier,
|
|
26204
|
+
queryName: widget.dataSource.queryName,
|
|
26205
|
+
queryRtId: widget.dataSource.queryRtId,
|
|
26206
|
+
filters: widget.filters
|
|
26207
|
+
}
|
|
26208
|
+
}),
|
|
26209
|
+
// SOLID: Deserialization from persistence
|
|
26210
|
+
fromPersistedConfig: (data, base) => {
|
|
26211
|
+
const config = parseConfig(data);
|
|
26212
|
+
const dataSource = buildDataSourceFromPersisted(data, config);
|
|
26213
|
+
return {
|
|
26214
|
+
...base,
|
|
26215
|
+
rtId: data.rtId,
|
|
26216
|
+
type: 'heatmap',
|
|
26217
|
+
dataSource,
|
|
26218
|
+
dateField: config['dateField'] ?? '',
|
|
26219
|
+
dateEndField: config['dateEndField'],
|
|
26220
|
+
valueField: config['valueField'],
|
|
26221
|
+
aggregation: config['aggregation'] ?? 'count',
|
|
26222
|
+
colorScheme: config['colorScheme'] ?? 'green',
|
|
26223
|
+
showLegend: config['showLegend'] ?? true,
|
|
26224
|
+
legendPosition: config['legendPosition'] ?? 'bottom',
|
|
26225
|
+
decimalPlaces: config['decimalPlaces'],
|
|
26226
|
+
compactNumbers: config['compactNumbers'],
|
|
26227
|
+
valueMultiplier: config['valueMultiplier'],
|
|
26228
|
+
filters: config['filters']
|
|
26229
|
+
};
|
|
26230
|
+
}
|
|
26231
|
+
});
|
|
26232
|
+
// Status List Widget
|
|
26233
|
+
registry.registerWidget({
|
|
26234
|
+
type: 'statusList',
|
|
26235
|
+
label: 'Status List',
|
|
26236
|
+
component: StatusListWidgetComponent,
|
|
26237
|
+
configDialogComponent: StatusListConfigDialogComponent,
|
|
26238
|
+
configDialogSize: { width: 650, height: 550, minWidth: 500, minHeight: 400 },
|
|
26239
|
+
configDialogTitle: 'Status List Configuration',
|
|
26240
|
+
defaultSize: { colSpan: 2, rowSpan: 2 },
|
|
26241
|
+
supportedDataSources: ['runtimeEntity'],
|
|
26242
|
+
getInitialConfig: (widget) => {
|
|
26243
|
+
const slWidget = widget;
|
|
26244
|
+
return {
|
|
26245
|
+
initialCkTypeId: slWidget.ckTypeId,
|
|
26246
|
+
initialLabelField: slWidget.labelField,
|
|
26247
|
+
initialStatusField: slWidget.statusField,
|
|
26248
|
+
initialStatusColors: slWidget.statusColors
|
|
26249
|
+
};
|
|
26250
|
+
},
|
|
26251
|
+
applyConfigResult: (widget, result) => ({
|
|
26252
|
+
...widget,
|
|
26253
|
+
ckTypeId: result.ckTypeId,
|
|
26254
|
+
labelField: result.labelField,
|
|
26255
|
+
statusField: result.statusField,
|
|
26256
|
+
statusColors: result.statusColors,
|
|
26257
|
+
dataSource: { type: 'runtimeEntity', ckTypeId: result.ckTypeId }
|
|
26258
|
+
}),
|
|
26259
|
+
createDefaultConfig: (base) => ({
|
|
26260
|
+
...base,
|
|
26261
|
+
type: 'statusList',
|
|
26262
|
+
colSpan: 3,
|
|
26263
|
+
rowSpan: 1,
|
|
26264
|
+
dataSource: { type: 'runtimeEntity' },
|
|
26265
|
+
ckTypeId: '',
|
|
26266
|
+
labelField: 'name',
|
|
26267
|
+
statusField: ''
|
|
26268
|
+
}),
|
|
26269
|
+
toPersistedConfig: (widget) => ({
|
|
26270
|
+
dataSourceType: 'runtimeEntity',
|
|
26271
|
+
dataSourceCkTypeId: widget.ckTypeId,
|
|
26272
|
+
config: {
|
|
26273
|
+
ckTypeId: widget.ckTypeId,
|
|
26274
|
+
labelField: widget.labelField,
|
|
26275
|
+
statusField: widget.statusField,
|
|
26276
|
+
statusColors: widget.statusColors
|
|
26277
|
+
}
|
|
26278
|
+
}),
|
|
26279
|
+
fromPersistedConfig: (data, base) => {
|
|
26280
|
+
const config = parseConfig(data);
|
|
26281
|
+
return {
|
|
26282
|
+
...base,
|
|
26283
|
+
rtId: data.rtId,
|
|
26284
|
+
type: 'statusList',
|
|
26285
|
+
dataSource: { type: 'runtimeEntity', ckTypeId: data.ckTypeId ?? config['ckTypeId'] },
|
|
26286
|
+
ckTypeId: config['ckTypeId'] ?? data.ckTypeId ?? '',
|
|
26287
|
+
labelField: config['labelField'] ?? 'name',
|
|
26288
|
+
statusField: config['statusField'] ?? '',
|
|
26289
|
+
statusColors: config['statusColors']
|
|
26290
|
+
};
|
|
26291
|
+
}
|
|
26292
|
+
});
|
|
26293
|
+
// Summary Card Widget
|
|
26294
|
+
registry.registerWidget({
|
|
26295
|
+
type: 'summaryCard',
|
|
26296
|
+
label: 'Summary Card',
|
|
26297
|
+
component: SummaryCardWidgetComponent,
|
|
26298
|
+
configDialogComponent: SummaryCardConfigDialogComponent,
|
|
26299
|
+
configDialogSize: { width: 750, height: 650, minWidth: 600, minHeight: 500 },
|
|
26300
|
+
configDialogTitle: 'Summary Card Configuration',
|
|
26301
|
+
defaultSize: { colSpan: 2, rowSpan: 2 },
|
|
26302
|
+
supportedDataSources: ['runtimeEntity'],
|
|
26303
|
+
getInitialConfig: (widget) => {
|
|
26304
|
+
const scWidget = widget;
|
|
26305
|
+
return {
|
|
26306
|
+
initialColumns: scWidget.columns,
|
|
26307
|
+
initialTiles: scWidget.tiles
|
|
26308
|
+
};
|
|
26309
|
+
},
|
|
26310
|
+
applyConfigResult: (widget, result) => ({
|
|
26311
|
+
...widget,
|
|
26312
|
+
columns: result.columns,
|
|
26313
|
+
tiles: result.tiles,
|
|
26314
|
+
dataSource: { type: 'runtimeEntity' }
|
|
26315
|
+
}),
|
|
26316
|
+
createDefaultConfig: (base) => ({
|
|
26317
|
+
...base,
|
|
26318
|
+
type: 'summaryCard',
|
|
26319
|
+
colSpan: 2,
|
|
26320
|
+
rowSpan: 2,
|
|
26321
|
+
dataSource: { type: 'runtimeEntity' },
|
|
26322
|
+
columns: 2,
|
|
26323
|
+
tiles: []
|
|
26324
|
+
}),
|
|
26325
|
+
toPersistedConfig: (widget) => ({
|
|
26326
|
+
dataSourceType: 'runtimeEntity',
|
|
26327
|
+
config: {
|
|
26328
|
+
columns: widget.columns,
|
|
26329
|
+
tiles: widget.tiles
|
|
26330
|
+
}
|
|
26331
|
+
}),
|
|
26332
|
+
fromPersistedConfig: (data, base) => {
|
|
26333
|
+
const config = parseConfig(data);
|
|
26334
|
+
return {
|
|
26335
|
+
...base,
|
|
26336
|
+
rtId: data.rtId,
|
|
26337
|
+
type: 'summaryCard',
|
|
26338
|
+
dataSource: { type: 'runtimeEntity', ckTypeId: data.ckTypeId ?? 'configured' },
|
|
26339
|
+
columns: config['columns'] ?? 2,
|
|
26340
|
+
tiles: config['tiles'] ?? []
|
|
26341
|
+
};
|
|
26342
|
+
}
|
|
26343
|
+
});
|
|
26344
|
+
// Alert Banner Widget
|
|
26345
|
+
registry.registerWidget({
|
|
26346
|
+
type: 'alertBanner',
|
|
26347
|
+
label: 'Alert Banner',
|
|
26348
|
+
component: AlertBannerWidgetComponent,
|
|
26349
|
+
configDialogComponent: AlertBannerConfigDialogComponent,
|
|
26350
|
+
configDialogSize: { width: 500, height: 400, minWidth: 400, minHeight: 300 },
|
|
26351
|
+
configDialogTitle: 'Alert Banner Configuration',
|
|
26352
|
+
defaultSize: { colSpan: 4, rowSpan: 1 },
|
|
26353
|
+
supportedDataSources: ['runtimeEntity'],
|
|
26354
|
+
getInitialConfig: (widget) => {
|
|
26355
|
+
const abWidget = widget;
|
|
26356
|
+
return {
|
|
26357
|
+
ckTypeId: abWidget.ckTypeId ?? 'System.Notification/StatefulEvent',
|
|
26358
|
+
rotationInterval: abWidget.rotationInterval ?? 5000,
|
|
26359
|
+
showIcon: abWidget.showIcon ?? true,
|
|
26360
|
+
maxAlerts: abWidget.maxAlerts ?? 20
|
|
26361
|
+
};
|
|
26362
|
+
},
|
|
26363
|
+
applyConfigResult: (widget, result) => ({
|
|
26364
|
+
...widget,
|
|
26365
|
+
ckTypeId: result.ckTypeId,
|
|
26366
|
+
rotationInterval: result.rotationInterval,
|
|
26367
|
+
showIcon: result.showIcon,
|
|
26368
|
+
maxAlerts: result.maxAlerts
|
|
26369
|
+
}),
|
|
26370
|
+
createDefaultConfig: (base) => ({
|
|
26371
|
+
...base,
|
|
26372
|
+
type: 'alertBanner',
|
|
26373
|
+
colSpan: 4,
|
|
26374
|
+
rowSpan: 1,
|
|
26375
|
+
dataSource: { type: 'runtimeEntity' },
|
|
26376
|
+
ckTypeId: 'System.Notification/StatefulEvent'
|
|
26377
|
+
}),
|
|
26378
|
+
toPersistedConfig: (widget) => ({
|
|
26379
|
+
dataSourceType: 'runtimeEntity',
|
|
26380
|
+
dataSourceCkTypeId: widget.ckTypeId ?? 'System.Notification/StatefulEvent',
|
|
26381
|
+
config: {
|
|
26382
|
+
ckTypeId: widget.ckTypeId,
|
|
26383
|
+
rotationInterval: widget.rotationInterval,
|
|
26384
|
+
showIcon: widget.showIcon,
|
|
26385
|
+
maxAlerts: widget.maxAlerts
|
|
26386
|
+
}
|
|
26387
|
+
}),
|
|
26388
|
+
fromPersistedConfig: (data, base) => {
|
|
26389
|
+
const config = parseConfig(data);
|
|
26390
|
+
return {
|
|
26391
|
+
...base,
|
|
26392
|
+
rtId: data.rtId,
|
|
26393
|
+
type: 'alertBanner',
|
|
26394
|
+
dataSource: { type: 'runtimeEntity', ckTypeId: data.ckTypeId ?? 'System.Notification/StatefulEvent' },
|
|
26395
|
+
ckTypeId: config['ckTypeId'] ?? data.ckTypeId ?? 'System.Notification/StatefulEvent',
|
|
26396
|
+
rotationInterval: config['rotationInterval'],
|
|
26397
|
+
showIcon: config['showIcon'],
|
|
26398
|
+
maxAlerts: config['maxAlerts']
|
|
26399
|
+
};
|
|
26400
|
+
}
|
|
26401
|
+
});
|
|
26402
|
+
// Alert List Widget
|
|
26403
|
+
registry.registerWidget({
|
|
26404
|
+
type: 'alertList',
|
|
26405
|
+
label: 'Alert List',
|
|
26406
|
+
component: AlertListWidgetComponent,
|
|
26407
|
+
configDialogComponent: AlertListConfigDialogComponent,
|
|
26408
|
+
configDialogSize: { width: 500, height: 400, minWidth: 400, minHeight: 300 },
|
|
26409
|
+
configDialogTitle: 'Alert List Configuration',
|
|
26410
|
+
defaultSize: { colSpan: 3, rowSpan: 3 },
|
|
26411
|
+
supportedDataSources: ['runtimeEntity'],
|
|
26412
|
+
getInitialConfig: (widget) => {
|
|
26413
|
+
const alWidget = widget;
|
|
26414
|
+
return {
|
|
26415
|
+
ckTypeId: alWidget.ckTypeId ?? 'System.Notification/StatefulEvent',
|
|
26416
|
+
showTimestamp: alWidget.showTimestamp ?? true,
|
|
26417
|
+
sortBySeverity: alWidget.sortBySeverity ?? true,
|
|
26418
|
+
maxAlerts: alWidget.maxAlerts ?? 50
|
|
26419
|
+
};
|
|
26420
|
+
},
|
|
26421
|
+
applyConfigResult: (widget, result) => ({
|
|
26422
|
+
...widget,
|
|
26423
|
+
ckTypeId: result.ckTypeId,
|
|
26424
|
+
showTimestamp: result.showTimestamp,
|
|
26425
|
+
sortBySeverity: result.sortBySeverity,
|
|
26426
|
+
maxAlerts: result.maxAlerts
|
|
26427
|
+
}),
|
|
26428
|
+
createDefaultConfig: (base) => ({
|
|
26429
|
+
...base,
|
|
26430
|
+
type: 'alertList',
|
|
26431
|
+
colSpan: 3,
|
|
26432
|
+
rowSpan: 3,
|
|
26433
|
+
dataSource: { type: 'runtimeEntity' },
|
|
26434
|
+
ckTypeId: 'System.Notification/StatefulEvent'
|
|
26435
|
+
}),
|
|
26436
|
+
toPersistedConfig: (widget) => ({
|
|
26437
|
+
dataSourceType: 'runtimeEntity',
|
|
26438
|
+
dataSourceCkTypeId: widget.ckTypeId ?? 'System.Notification/StatefulEvent',
|
|
26439
|
+
config: {
|
|
26440
|
+
ckTypeId: widget.ckTypeId,
|
|
26441
|
+
showTimestamp: widget.showTimestamp,
|
|
26442
|
+
sortBySeverity: widget.sortBySeverity,
|
|
26443
|
+
maxAlerts: widget.maxAlerts
|
|
26444
|
+
}
|
|
26445
|
+
}),
|
|
26446
|
+
fromPersistedConfig: (data, base) => {
|
|
26447
|
+
const config = parseConfig(data);
|
|
26448
|
+
return {
|
|
26449
|
+
...base,
|
|
26450
|
+
rtId: data.rtId,
|
|
26451
|
+
type: 'alertList',
|
|
26452
|
+
dataSource: { type: 'runtimeEntity', ckTypeId: data.ckTypeId ?? 'System.Notification/StatefulEvent' },
|
|
26453
|
+
ckTypeId: config['ckTypeId'] ?? data.ckTypeId ?? 'System.Notification/StatefulEvent',
|
|
26454
|
+
showTimestamp: config['showTimestamp'],
|
|
26455
|
+
sortBySeverity: config['sortBySeverity'],
|
|
26456
|
+
maxAlerts: config['maxAlerts']
|
|
26457
|
+
};
|
|
26458
|
+
}
|
|
26459
|
+
});
|
|
26460
|
+
// AI Insights Widget
|
|
26461
|
+
registry.registerWidget({
|
|
26462
|
+
type: 'aiInsights',
|
|
26463
|
+
label: 'AI Insights',
|
|
26464
|
+
component: AiInsightsWidgetComponent,
|
|
26465
|
+
configDialogComponent: AiInsightsConfigDialogComponent,
|
|
26466
|
+
configDialogSize: { width: 600, height: 500, minWidth: 450, minHeight: 400 },
|
|
26467
|
+
configDialogTitle: 'AI Insights Configuration',
|
|
26468
|
+
defaultSize: { colSpan: 3, rowSpan: 2 },
|
|
26469
|
+
supportedDataSources: ['static'],
|
|
24364
26470
|
getInitialConfig: (widget) => {
|
|
24365
|
-
const
|
|
24366
|
-
const dataSource = heatmapWidget.dataSource;
|
|
24367
|
-
const isPersistentQuery = dataSource.type === 'persistentQuery';
|
|
24368
|
-
return {
|
|
24369
|
-
initialQueryRtId: isPersistentQuery ? dataSource.queryRtId : undefined,
|
|
24370
|
-
initialQueryName: isPersistentQuery ? dataSource.queryName : undefined,
|
|
24371
|
-
initialDateField: heatmapWidget.dateField,
|
|
24372
|
-
initialDateEndField: heatmapWidget.dateEndField,
|
|
24373
|
-
initialValueField: heatmapWidget.valueField,
|
|
24374
|
-
initialAggregation: heatmapWidget.aggregation,
|
|
24375
|
-
initialColorScheme: heatmapWidget.colorScheme,
|
|
24376
|
-
initialShowLegend: heatmapWidget.showLegend,
|
|
24377
|
-
initialLegendPosition: heatmapWidget.legendPosition,
|
|
24378
|
-
initialDecimalPlaces: heatmapWidget.decimalPlaces,
|
|
24379
|
-
initialCompactNumbers: heatmapWidget.compactNumbers,
|
|
24380
|
-
initialValueMultiplier: heatmapWidget.valueMultiplier,
|
|
24381
|
-
initialFilters: heatmapWidget.filters
|
|
24382
|
-
};
|
|
24383
|
-
},
|
|
24384
|
-
applyConfigResult: (widget, result) => {
|
|
24385
|
-
const dataSource = {
|
|
24386
|
-
type: 'persistentQuery',
|
|
24387
|
-
queryRtId: result.queryRtId,
|
|
24388
|
-
queryName: result.queryName
|
|
24389
|
-
};
|
|
24390
|
-
const filters = result.filters?.map(f => ({
|
|
24391
|
-
attributePath: f.attributePath,
|
|
24392
|
-
operator: String(f.operator),
|
|
24393
|
-
comparisonValue: f.comparisonValue
|
|
24394
|
-
}));
|
|
26471
|
+
const aiWidget = widget;
|
|
24395
26472
|
return {
|
|
24396
|
-
|
|
24397
|
-
|
|
24398
|
-
|
|
24399
|
-
|
|
24400
|
-
|
|
24401
|
-
aggregation: result.aggregation,
|
|
24402
|
-
colorScheme: result.colorScheme,
|
|
24403
|
-
showLegend: result.showLegend,
|
|
24404
|
-
legendPosition: result.legendPosition,
|
|
24405
|
-
decimalPlaces: result.decimalPlaces,
|
|
24406
|
-
compactNumbers: result.compactNumbers,
|
|
24407
|
-
valueMultiplier: result.valueMultiplier,
|
|
24408
|
-
filters
|
|
26473
|
+
apiKey: aiWidget.apiKey,
|
|
26474
|
+
model: aiWidget.model ?? 'claude-sonnet-4-20250514',
|
|
26475
|
+
domainContext: aiWidget.domainContext ?? 'energy management',
|
|
26476
|
+
refreshInterval: aiWidget.refreshInterval ?? 0,
|
|
26477
|
+
maxInsights: aiWidget.maxInsights ?? 4
|
|
24409
26478
|
};
|
|
24410
26479
|
},
|
|
24411
|
-
|
|
26480
|
+
applyConfigResult: (widget, result) => ({
|
|
26481
|
+
...widget,
|
|
26482
|
+
apiKey: result.apiKey,
|
|
26483
|
+
model: result.model,
|
|
26484
|
+
domainContext: result.domainContext,
|
|
26485
|
+
refreshInterval: result.refreshInterval,
|
|
26486
|
+
maxInsights: result.maxInsights,
|
|
26487
|
+
dataSource: { type: 'static' }
|
|
26488
|
+
}),
|
|
24412
26489
|
createDefaultConfig: (base) => ({
|
|
24413
26490
|
...base,
|
|
24414
|
-
type: '
|
|
26491
|
+
type: 'aiInsights',
|
|
24415
26492
|
colSpan: 3,
|
|
24416
26493
|
rowSpan: 2,
|
|
24417
|
-
dataSource:
|
|
24418
|
-
|
|
24419
|
-
|
|
24420
|
-
|
|
24421
|
-
showLegend: true,
|
|
24422
|
-
legendPosition: 'bottom'
|
|
26494
|
+
dataSource: { type: 'static' },
|
|
26495
|
+
refreshInterval: 0,
|
|
26496
|
+
maxInsights: 4,
|
|
26497
|
+
domainContext: 'energy management'
|
|
24423
26498
|
}),
|
|
24424
|
-
// SOLID: Serialization for persistence
|
|
24425
26499
|
toPersistedConfig: (widget) => ({
|
|
24426
|
-
dataSourceType: '
|
|
24427
|
-
dataSourceRtId: widget.dataSource.queryRtId,
|
|
26500
|
+
dataSourceType: 'static',
|
|
24428
26501
|
config: {
|
|
24429
|
-
|
|
24430
|
-
|
|
24431
|
-
|
|
24432
|
-
|
|
24433
|
-
|
|
24434
|
-
|
|
24435
|
-
legendPosition: widget.legendPosition,
|
|
24436
|
-
decimalPlaces: widget.decimalPlaces,
|
|
24437
|
-
compactNumbers: widget.compactNumbers,
|
|
24438
|
-
valueMultiplier: widget.valueMultiplier,
|
|
24439
|
-
queryName: widget.dataSource.queryName,
|
|
24440
|
-
queryRtId: widget.dataSource.queryRtId,
|
|
24441
|
-
filters: widget.filters
|
|
26502
|
+
apiKey: widget.apiKey,
|
|
26503
|
+
model: widget.model,
|
|
26504
|
+
systemPrompt: widget.systemPrompt,
|
|
26505
|
+
refreshInterval: widget.refreshInterval,
|
|
26506
|
+
maxInsights: widget.maxInsights,
|
|
26507
|
+
domainContext: widget.domainContext
|
|
24442
26508
|
}
|
|
24443
26509
|
}),
|
|
24444
|
-
// SOLID: Deserialization from persistence
|
|
24445
26510
|
fromPersistedConfig: (data, base) => {
|
|
24446
26511
|
const config = parseConfig(data);
|
|
24447
|
-
const dataSource = buildDataSourceFromPersisted(data, config);
|
|
24448
26512
|
return {
|
|
24449
26513
|
...base,
|
|
24450
26514
|
rtId: data.rtId,
|
|
24451
|
-
type: '
|
|
24452
|
-
dataSource,
|
|
24453
|
-
|
|
24454
|
-
|
|
24455
|
-
|
|
24456
|
-
|
|
24457
|
-
|
|
24458
|
-
|
|
24459
|
-
legendPosition: config['legendPosition'] ?? 'bottom',
|
|
24460
|
-
decimalPlaces: config['decimalPlaces'],
|
|
24461
|
-
compactNumbers: config['compactNumbers'],
|
|
24462
|
-
valueMultiplier: config['valueMultiplier'],
|
|
24463
|
-
filters: config['filters']
|
|
26515
|
+
type: 'aiInsights',
|
|
26516
|
+
dataSource: { type: 'static' },
|
|
26517
|
+
apiKey: config['apiKey'],
|
|
26518
|
+
model: config['model'],
|
|
26519
|
+
systemPrompt: config['systemPrompt'],
|
|
26520
|
+
refreshInterval: config['refreshInterval'] ?? 0,
|
|
26521
|
+
maxInsights: config['maxInsights'] ?? 4,
|
|
26522
|
+
domainContext: config['domainContext'] ?? 'energy management'
|
|
24464
26523
|
};
|
|
24465
26524
|
}
|
|
24466
26525
|
});
|
|
@@ -25355,7 +27414,7 @@ class AddWidgetDialogComponent {
|
|
|
25355
27414
|
this.dialogRef.close();
|
|
25356
27415
|
}
|
|
25357
27416
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AddWidgetDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
25358
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: AddWidgetDialogComponent, isStandalone: true, selector: "mm-add-widget-dialog", ngImport: i0, template: "<div class=\"add-widget-dialog\">\n <div class=\"dialog-content\">\n <p class=\"dialog-description\">\n Select a widget type to add to your MeshBoard:\n </p>\n\n <div class=\"widget-types-list\">\n <div class=\"list-header\">Available Widgets</div>\n\n @for (item of widgetTypes(); track item.type) {\n <div\n class=\"widget-type-item\"\n [class.selected]=\"isSelected(item.type)\"\n (click)=\"selectType(item.type)\">\n <div class=\"widget-icon\">\n <kendo-svg-icon [icon]=\"item.icon\" size=\"large\"></kendo-svg-icon>\n </div>\n <div class=\"widget-info\">\n <h4 class=\"widget-label\">{{ item.label }}</h4>\n <p class=\"widget-description\">{{ item.description }}</p>\n </div>\n @if (isSelected(item.type)) {\n <div class=\"selected-indicator\">\n <kendo-svg-icon [icon]=\"checkCircleIcon\" size=\"medium\"></kendo-svg-icon>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Dialog Actions -->\n <div class=\"dialog-actions mm-dialog-actions\">\n <button kendoButton (click)=\"cancel()\" fillMode=\"flat\">\n Cancel\n </button>\n <button\n kendoButton\n (click)=\"add()\"\n [disabled]=\"!selectedType()\"\n themeColor=\"primary\">\n Add Widget\n </button>\n </div>\n</div>\n", styles: [".add-widget-dialog{display:flex;flex-direction:column;gap:.75rem;padding:.75rem;max-height:60vh;overflow:hidden}.add-widget-dialog .dialog-content{display:flex;flex-direction:column;gap:1rem;flex:1;min-height:0;overflow:hidden}.add-widget-dialog .dialog-content .dialog-description{margin:0;color:var(--kendo-color-subtle, #757575);font-size:.875rem}.add-widget-dialog .dialog-content .widget-types-list{flex:1;overflow-y:auto;border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.add-widget-dialog .dialog-content .widget-types-list .list-header{font-weight:600;font-size:.75rem;color:var(--kendo-color-on-app-surface, #424242);padding:.5rem .75rem;background-color:var(--kendo-color-surface-alt, #fafafa);border-bottom:1px solid var(--kendo-color-border, #e0e0e0);text-transform:uppercase;letter-spacing:.5px}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item{display:flex;align-items:center;gap:.5rem;padding:.375rem .5rem;cursor:pointer;border-bottom:1px solid var(--kendo-color-border, #e0e0e0);transition:background-color .2s,box-shadow .2s}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item:last-child{border-bottom:none}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item:hover{background-color:var(--kendo-color-base-hover, #f5f5f5)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item.selected{background-color:var(--kendo-color-primary-subtle, #e8eaf6);box-shadow:inset 3px 0 0 var(--kendo-color-primary, #3f51b5)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:4px;background-color:var(--kendo-color-surface-alt, #f5f5f5);color:var(--kendo-color-primary, #3f51b5)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-icon kendo-svg-icon{color:inherit}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-info{flex:1;min-width:0}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-info .widget-label{margin:0 0 .125rem;font-size:.875rem;font-weight:500;color:var(--kendo-color-on-app-surface, #212121)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-info .widget-description{margin:0;font-size:.75rem;color:var(--kendo-color-subtle, #757575);line-height:1.3}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .selected-indicator{flex-shrink:0;color:var(--kendo-color-primary, #3f51b5)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .selected-indicator kendo-svg-icon{color:inherit}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item.selected .widget-icon{background-color:var(--kendo-color-primary, #3f51b5);color:var(--kendo-color-on-primary, #ffffff)}.add-widget-dialog .dialog-actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.5rem;border-top:1px solid var(--kendo-color-border, #e0e0e0);flex-shrink:0}.widget-types-list::-webkit-scrollbar{width:8px}.widget-types-list::-webkit-scrollbar-track{background:var(--kendo-color-surface-alt, #f5f5f5)}.widget-types-list::-webkit-scrollbar-thumb{background:var(--kendo-color-border, #e0e0e0);border-radius:4px}.widget-types-list::-webkit-scrollbar-thumb:hover{background:var(--kendo-color-subtle, #9e9e9e)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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: DialogModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type:
|
|
27417
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: AddWidgetDialogComponent, isStandalone: true, selector: "mm-add-widget-dialog", ngImport: i0, template: "<div class=\"add-widget-dialog\">\n <div class=\"dialog-content\">\n <p class=\"dialog-description\">\n Select a widget type to add to your MeshBoard:\n </p>\n\n <div class=\"widget-types-list\">\n <div class=\"list-header\">Available Widgets</div>\n\n @for (item of widgetTypes(); track item.type) {\n <div\n class=\"widget-type-item\"\n [class.selected]=\"isSelected(item.type)\"\n (click)=\"selectType(item.type)\">\n <div class=\"widget-icon\">\n <kendo-svg-icon [icon]=\"item.icon\" size=\"large\"></kendo-svg-icon>\n </div>\n <div class=\"widget-info\">\n <h4 class=\"widget-label\">{{ item.label }}</h4>\n <p class=\"widget-description\">{{ item.description }}</p>\n </div>\n @if (isSelected(item.type)) {\n <div class=\"selected-indicator\">\n <kendo-svg-icon [icon]=\"checkCircleIcon\" size=\"medium\"></kendo-svg-icon>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Dialog Actions -->\n <div class=\"dialog-actions mm-dialog-actions\">\n <button kendoButton (click)=\"cancel()\" fillMode=\"flat\">\n Cancel\n </button>\n <button\n kendoButton\n (click)=\"add()\"\n [disabled]=\"!selectedType()\"\n themeColor=\"primary\">\n Add Widget\n </button>\n </div>\n</div>\n", styles: [".add-widget-dialog{display:flex;flex-direction:column;gap:.75rem;padding:.75rem;max-height:60vh;overflow:hidden}.add-widget-dialog .dialog-content{display:flex;flex-direction:column;gap:1rem;flex:1;min-height:0;overflow:hidden}.add-widget-dialog .dialog-content .dialog-description{margin:0;color:var(--kendo-color-subtle, #757575);font-size:.875rem}.add-widget-dialog .dialog-content .widget-types-list{flex:1;overflow-y:auto;border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.add-widget-dialog .dialog-content .widget-types-list .list-header{font-weight:600;font-size:.75rem;color:var(--kendo-color-on-app-surface, #424242);padding:.5rem .75rem;background-color:var(--kendo-color-surface-alt, #fafafa);border-bottom:1px solid var(--kendo-color-border, #e0e0e0);text-transform:uppercase;letter-spacing:.5px}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item{display:flex;align-items:center;gap:.5rem;padding:.375rem .5rem;cursor:pointer;border-bottom:1px solid var(--kendo-color-border, #e0e0e0);transition:background-color .2s,box-shadow .2s}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item:last-child{border-bottom:none}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item:hover{background-color:var(--kendo-color-base-hover, #f5f5f5)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item.selected{background-color:var(--kendo-color-primary-subtle, #e8eaf6);box-shadow:inset 3px 0 0 var(--kendo-color-primary, #3f51b5)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:4px;background-color:var(--kendo-color-surface-alt, #f5f5f5);color:var(--kendo-color-primary, #3f51b5)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-icon kendo-svg-icon{color:inherit}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-info{flex:1;min-width:0}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-info .widget-label{margin:0 0 .125rem;font-size:.875rem;font-weight:500;color:var(--kendo-color-on-app-surface, #212121)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .widget-info .widget-description{margin:0;font-size:.75rem;color:var(--kendo-color-subtle, #757575);line-height:1.3}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .selected-indicator{flex-shrink:0;color:var(--kendo-color-primary, #3f51b5)}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item .selected-indicator kendo-svg-icon{color:inherit}.add-widget-dialog .dialog-content .widget-types-list .widget-type-item.selected .widget-icon{background-color:var(--kendo-color-primary, #3f51b5);color:var(--kendo-color-on-primary, #ffffff)}.add-widget-dialog .dialog-actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.5rem;border-top:1px solid var(--kendo-color-border, #e0e0e0);flex-shrink:0}.widget-types-list::-webkit-scrollbar{width:8px}.widget-types-list::-webkit-scrollbar-track{background:var(--kendo-color-surface-alt, #f5f5f5)}.widget-types-list::-webkit-scrollbar-thumb{background:var(--kendo-color-border, #e0e0e0);border-radius:4px}.widget-types-list::-webkit-scrollbar-thumb:hover{background:var(--kendo-color-subtle, #9e9e9e)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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: DialogModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }] });
|
|
25359
27418
|
}
|
|
25360
27419
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AddWidgetDialogComponent, decorators: [{
|
|
25361
27420
|
type: Component,
|
|
@@ -25735,7 +27794,7 @@ class MeshBoardManagerDialogComponent {
|
|
|
25735
27794
|
}
|
|
25736
27795
|
}
|
|
25737
27796
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MeshBoardManagerDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
25738
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: MeshBoardManagerDialogComponent, isStandalone: true, selector: "mm-meshboard-manager-dialog", ngImport: i0, template: "<div class=\"meshboard-manager-dialog\">\n <!-- Header with Create/Import Buttons -->\n <div class=\"dialog-header\">\n <p class=\"dialog-description\">Manage your MeshBoards</p>\n <div class=\"header-actions\">\n @if (canExport) {\n <button\n kendoButton\n [svgIcon]=\"uploadIcon\"\n (click)=\"triggerImport()\"\n [disabled]=\"isCreating() || isLoading() || isImporting()\"\n fillMode=\"outline\">\n Import\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"startCreate()\"\n [disabled]=\"isCreating() || isLoading()\"\n themeColor=\"primary\">\n New MeshBoard\n </button>\n </div>\n </div>\n\n <!-- Create New MeshBoard Form -->\n @if (isCreating()) {\n <div class=\"create-form\">\n <h4 class=\"form-title\">Create New MeshBoard</h4>\n <div class=\"form-fields\">\n <kendo-textbox\n [(ngModel)]=\"newName\"\n placeholder=\"MeshBoard name\"\n [disabled]=\"isLoading()\">\n </kendo-textbox>\n <kendo-textarea\n [(ngModel)]=\"newDescription\"\n placeholder=\"Description (optional)\"\n [rows]=\"2\"\n [disabled]=\"isLoading()\">\n </kendo-textarea>\n </div>\n <div class=\"form-actions\">\n <button\n kendoButton\n [svgIcon]=\"xIcon\"\n (click)=\"cancelCreate()\"\n [disabled]=\"isLoading()\"\n fillMode=\"flat\"\n size=\"small\">\n Cancel\n </button>\n <button\n kendoButton\n [svgIcon]=\"checkIcon\"\n (click)=\"create()\"\n [disabled]=\"newName().trim().length === 0 || isLoading()\"\n themeColor=\"primary\"\n size=\"small\">\n Create\n </button>\n </div>\n </div>\n }\n\n <!-- MeshBoards List -->\n <div class=\"meshboards-list\">\n @if (isLoading()) {\n <div class=\"loading-state\">\n <span class=\"k-loading-text\">Loading...</span>\n </div>\n } @else if (hasMeshBoards()) {\n @for (item of meshBoards(); track item.rtId) {\n <div\n class=\"meshboard-item\"\n [class.active]=\"isActive(item)\"\n [class.editing]=\"isEditing(item)\">\n\n @if (!isEditing(item)) {\n <!-- Display Mode -->\n <div class=\"item-content\" (click)=\"switchTo(item)\">\n <div class=\"item-icon\">\n <kendo-svg-icon [icon]=\"gridLayoutIcon\" size=\"large\"></kendo-svg-icon>\n </div>\n <div class=\"item-info\">\n <h4 class=\"item-name\">\n {{ item.name }}\n @if (isActive(item)) {\n <span class=\"active-badge\">Active</span>\n }\n </h4>\n @if (getDisplayDescription(item)) {\n <p class=\"item-description\">{{ getDisplayDescription(item) }}</p>\n }\n </div>\n <div class=\"item-actions\">\n @if (canExport) {\n <button\n kendoButton\n [svgIcon]=\"downloadIcon\"\n (click)=\"exportMeshBoard(item, $event)\"\n [disabled]=\"isLoading() || isExporting()\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Export\">\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"copyIcon\"\n (click)=\"duplicate(item); $event.stopPropagation()\"\n [disabled]=\"isLoading() || isDuplicating()\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Duplicate\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"startEdit(item); $event.stopPropagation()\"\n [disabled]=\"isLoading()\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Edit\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"delete(item); $event.stopPropagation()\"\n [disabled]=\"isLoading()\"\n fillMode=\"flat\"\n size=\"small\"\n themeColor=\"error\"\n title=\"Delete\">\n </button>\n </div>\n </div>\n } @else {\n <!-- Edit Mode -->\n <div class=\"item-edit\">\n <div class=\"edit-fields\">\n <kendo-textbox\n [(ngModel)]=\"editingName\"\n placeholder=\"MeshBoard name\"\n [disabled]=\"isLoading()\">\n </kendo-textbox>\n <kendo-textarea\n [(ngModel)]=\"editingDescription\"\n placeholder=\"Description (optional)\"\n [rows]=\"2\"\n [disabled]=\"isLoading()\">\n </kendo-textarea>\n </div>\n <div class=\"edit-actions mm-dialog-actions\">\n <button\n kendoButton\n [svgIcon]=\"xIcon\"\n (click)=\"cancelEdit()\"\n [disabled]=\"isLoading()\"\n fillMode=\"flat\"\n size=\"small\">\n Cancel\n </button>\n <button\n kendoButton\n [svgIcon]=\"checkIcon\"\n (click)=\"saveEdit()\"\n [disabled]=\"editingName().trim().length === 0 || isLoading()\"\n themeColor=\"primary\"\n size=\"small\">\n Save\n </button>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <div class=\"empty-state\">\n <kendo-svg-icon [icon]=\"gridLayoutIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h3>No MeshBoards</h3>\n <p>Create your first MeshBoard to get started.</p>\n </div>\n }\n </div>\n\n <!-- Dialog Actions -->\n <div class=\"dialog-actions mm-dialog-actions\">\n <button kendoButton (click)=\"close()\" fillMode=\"flat\">\n Close\n </button>\n </div>\n</div>\n", styles: [".meshboard-manager-dialog{display:flex;flex-direction:column;gap:.75rem;padding:.75rem .75rem 1.5rem;overflow:hidden}.meshboard-manager-dialog .dialog-header{display:flex;justify-content:space-between;align-items:center;gap:1rem}.meshboard-manager-dialog .dialog-header .dialog-description{margin:0;color:var(--kendo-color-subtle, #757575);font-size:.875rem;flex:1}.meshboard-manager-dialog .dialog-header .header-actions{display:flex;gap:.5rem}.meshboard-manager-dialog .create-form{padding:1rem;background-color:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.meshboard-manager-dialog .create-form .form-title{margin:0 0 1rem;font-size:1rem;font-weight:600;color:var(--kendo-color-on-app-surface, #424242)}.meshboard-manager-dialog .create-form .form-fields{display:flex;flex-direction:column;gap:.75rem;margin-bottom:1rem}.meshboard-manager-dialog .create-form .form-fields kendo-textbox,.meshboard-manager-dialog .create-form .form-fields kendo-textarea{width:100%}.meshboard-manager-dialog .create-form .form-actions{display:flex;justify-content:flex-end;gap:.5rem}.meshboard-manager-dialog .meshboards-list{flex:1;min-height:250px;overflow-y:auto;border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px;background-color:var(--kendo-color-surface, #ffffff)}.meshboard-manager-dialog .meshboards-list .loading-state{display:flex;align-items:center;justify-content:center;padding:2rem;color:var(--kendo-color-subtle, #757575)}.meshboard-manager-dialog .meshboards-list .empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem 2rem;text-align:center}.meshboard-manager-dialog .meshboards-list .empty-state kendo-svg-icon{color:var(--kendo-color-subtle, #9e9e9e);margin-bottom:1rem}.meshboard-manager-dialog .meshboards-list .empty-state h3{margin:.5rem 0;color:var(--kendo-color-on-app-surface, #424242);font-size:1.125rem;font-weight:500}.meshboard-manager-dialog .meshboards-list .empty-state p{margin:0;color:var(--kendo-color-subtle, #757575);font-size:.875rem}.meshboard-manager-dialog .meshboards-list .meshboard-item{border-bottom:1px solid var(--kendo-color-border, #e0e0e0);transition:background-color .2s,box-shadow .2s}.meshboard-manager-dialog .meshboards-list .meshboard-item:last-child{border-bottom:none}.meshboard-manager-dialog .meshboards-list .meshboard-item:not(.active):not(.editing):hover{background-color:var(--kendo-color-base-hover, #f5f5f5)}.meshboard-manager-dialog .meshboards-list .meshboard-item.active{background-color:var(--kendo-color-primary-subtle, #e8eaf6);box-shadow:inset 4px 0 0 var(--kendo-color-primary, #3f51b5)}.meshboard-manager-dialog .meshboards-list .meshboard-item.editing{background-color:var(--kendo-color-surface-alt, #fafafa)}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content{display:flex;align-items:center;gap:1rem;padding:1rem;cursor:pointer}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:8px;background-color:var(--kendo-color-surface-alt, #f5f5f5);color:var(--kendo-color-primary, #3f51b5)}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-icon kendo-svg-icon{color:inherit}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-info{flex:1;min-width:0}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-info .item-name{margin:0 0 .25rem;font-size:1rem;font-weight:500;color:var(--kendo-color-on-app-surface, #212121);display:flex;align-items:center;gap:.5rem}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-info .item-name .active-badge{display:inline-block;padding:.125rem .5rem;font-size:.75rem;font-weight:600;color:var(--kendo-color-on-primary, #ffffff);background-color:var(--kendo-color-primary, #3f51b5);border-radius:12px}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-info .item-description{margin:0;font-size:.875rem;color:var(--kendo-color-subtle, #757575);line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-actions{flex-shrink:0;display:flex;gap:.25rem;opacity:.5;transition:opacity .2s}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content:hover .item-actions{opacity:1}.meshboard-manager-dialog .meshboards-list .meshboard-item.active .item-content .item-icon{background-color:var(--kendo-color-primary, #3f51b5);color:var(--kendo-color-on-primary, #ffffff)}.meshboard-manager-dialog .meshboards-list .meshboard-item:not(.active) .item-content:after{content:\"Click to switch\";font-size:.75rem;color:var(--kendo-color-subtle, #9e9e9e);opacity:0;transition:opacity .2s;white-space:nowrap}.meshboard-manager-dialog .meshboards-list .meshboard-item:not(.active):hover .item-content:after{opacity:1}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit{padding:1rem}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit .edit-fields{display:flex;flex-direction:column;gap:.75rem;margin-bottom:.75rem}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit .edit-fields kendo-textbox,.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit .edit-fields kendo-textarea{width:100%}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit .edit-actions{display:flex;justify-content:flex-end;gap:.5rem}.meshboard-manager-dialog .dialog-actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.75rem;border-top:1px solid var(--kendo-color-border, #e0e0e0);flex-shrink:0;margin-bottom:.5rem}.meshboards-list::-webkit-scrollbar{width:8px}.meshboards-list::-webkit-scrollbar-track{background:var(--kendo-color-surface-alt, #f5f5f5)}.meshboards-list::-webkit-scrollbar-thumb{background:var(--kendo-color-border, #e0e0e0);border-radius:4px}.meshboards-list::-webkit-scrollbar-thumb:hover{background:var(--kendo-color-subtle, #9e9e9e)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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: DialogModule }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.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: i3.TextAreaComponent, selector: "kendo-textarea", inputs: ["focusableId", "flow", "inputAttributes", "adornmentsOrientation", "rows", "cols", "maxlength", "maxResizableRows", "tabindex", "tabIndex", "resizable", "size", "rounded", "fillMode", "showPrefixSeparator", "showSuffixSeparator"], outputs: ["focus", "blur", "valueChange"], exportAs: ["kendoTextArea"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i2$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }] });
|
|
27797
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: MeshBoardManagerDialogComponent, isStandalone: true, selector: "mm-meshboard-manager-dialog", ngImport: i0, template: "<div class=\"meshboard-manager-dialog\">\n <!-- Header with Create/Import Buttons -->\n <div class=\"dialog-header\">\n <p class=\"dialog-description\">Manage your MeshBoards</p>\n <div class=\"header-actions\">\n @if (canExport) {\n <button\n kendoButton\n [svgIcon]=\"uploadIcon\"\n (click)=\"triggerImport()\"\n [disabled]=\"isCreating() || isLoading() || isImporting()\"\n fillMode=\"outline\">\n Import\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"startCreate()\"\n [disabled]=\"isCreating() || isLoading()\"\n themeColor=\"primary\">\n New MeshBoard\n </button>\n </div>\n </div>\n\n <!-- Create New MeshBoard Form -->\n @if (isCreating()) {\n <div class=\"create-form\">\n <h4 class=\"form-title\">Create New MeshBoard</h4>\n <div class=\"form-fields\">\n <kendo-textbox\n [(ngModel)]=\"newName\"\n placeholder=\"MeshBoard name\"\n [disabled]=\"isLoading()\">\n </kendo-textbox>\n <kendo-textarea\n [(ngModel)]=\"newDescription\"\n placeholder=\"Description (optional)\"\n [rows]=\"2\"\n [disabled]=\"isLoading()\">\n </kendo-textarea>\n </div>\n <div class=\"form-actions\">\n <button\n kendoButton\n [svgIcon]=\"xIcon\"\n (click)=\"cancelCreate()\"\n [disabled]=\"isLoading()\"\n fillMode=\"flat\"\n size=\"small\">\n Cancel\n </button>\n <button\n kendoButton\n [svgIcon]=\"checkIcon\"\n (click)=\"create()\"\n [disabled]=\"newName().trim().length === 0 || isLoading()\"\n themeColor=\"primary\"\n size=\"small\">\n Create\n </button>\n </div>\n </div>\n }\n\n <!-- MeshBoards List -->\n <div class=\"meshboards-list\">\n @if (isLoading()) {\n <div class=\"loading-state\">\n <span class=\"k-loading-text\">Loading...</span>\n </div>\n } @else if (hasMeshBoards()) {\n @for (item of meshBoards(); track item.rtId) {\n <div\n class=\"meshboard-item\"\n [class.active]=\"isActive(item)\"\n [class.editing]=\"isEditing(item)\">\n\n @if (!isEditing(item)) {\n <!-- Display Mode -->\n <div class=\"item-content\" (click)=\"switchTo(item)\">\n <div class=\"item-icon\">\n <kendo-svg-icon [icon]=\"gridLayoutIcon\" size=\"large\"></kendo-svg-icon>\n </div>\n <div class=\"item-info\">\n <h4 class=\"item-name\">\n {{ item.name }}\n @if (isActive(item)) {\n <span class=\"active-badge\">Active</span>\n }\n </h4>\n @if (getDisplayDescription(item)) {\n <p class=\"item-description\">{{ getDisplayDescription(item) }}</p>\n }\n </div>\n <div class=\"item-actions\">\n @if (canExport) {\n <button\n kendoButton\n [svgIcon]=\"downloadIcon\"\n (click)=\"exportMeshBoard(item, $event)\"\n [disabled]=\"isLoading() || isExporting()\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Export\">\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"copyIcon\"\n (click)=\"duplicate(item); $event.stopPropagation()\"\n [disabled]=\"isLoading() || isDuplicating()\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Duplicate\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"startEdit(item); $event.stopPropagation()\"\n [disabled]=\"isLoading()\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Edit\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"delete(item); $event.stopPropagation()\"\n [disabled]=\"isLoading()\"\n fillMode=\"flat\"\n size=\"small\"\n themeColor=\"error\"\n title=\"Delete\">\n </button>\n </div>\n </div>\n } @else {\n <!-- Edit Mode -->\n <div class=\"item-edit\">\n <div class=\"edit-fields\">\n <kendo-textbox\n [(ngModel)]=\"editingName\"\n placeholder=\"MeshBoard name\"\n [disabled]=\"isLoading()\">\n </kendo-textbox>\n <kendo-textarea\n [(ngModel)]=\"editingDescription\"\n placeholder=\"Description (optional)\"\n [rows]=\"2\"\n [disabled]=\"isLoading()\">\n </kendo-textarea>\n </div>\n <div class=\"edit-actions mm-dialog-actions\">\n <button\n kendoButton\n [svgIcon]=\"xIcon\"\n (click)=\"cancelEdit()\"\n [disabled]=\"isLoading()\"\n fillMode=\"flat\"\n size=\"small\">\n Cancel\n </button>\n <button\n kendoButton\n [svgIcon]=\"checkIcon\"\n (click)=\"saveEdit()\"\n [disabled]=\"editingName().trim().length === 0 || isLoading()\"\n themeColor=\"primary\"\n size=\"small\">\n Save\n </button>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <div class=\"empty-state\">\n <kendo-svg-icon [icon]=\"gridLayoutIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h3>No MeshBoards</h3>\n <p>Create your first MeshBoard to get started.</p>\n </div>\n }\n </div>\n\n <!-- Dialog Actions -->\n <div class=\"dialog-actions mm-dialog-actions\">\n <button kendoButton (click)=\"close()\" fillMode=\"flat\">\n Close\n </button>\n </div>\n</div>\n", styles: [".meshboard-manager-dialog{display:flex;flex-direction:column;gap:.75rem;padding:.75rem .75rem 1.5rem;overflow:hidden}.meshboard-manager-dialog .dialog-header{display:flex;justify-content:space-between;align-items:center;gap:1rem}.meshboard-manager-dialog .dialog-header .dialog-description{margin:0;color:var(--kendo-color-subtle, #757575);font-size:.875rem;flex:1}.meshboard-manager-dialog .dialog-header .header-actions{display:flex;gap:.5rem}.meshboard-manager-dialog .create-form{padding:1rem;background-color:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.meshboard-manager-dialog .create-form .form-title{margin:0 0 1rem;font-size:1rem;font-weight:600;color:var(--kendo-color-on-app-surface, #424242)}.meshboard-manager-dialog .create-form .form-fields{display:flex;flex-direction:column;gap:.75rem;margin-bottom:1rem}.meshboard-manager-dialog .create-form .form-fields kendo-textbox,.meshboard-manager-dialog .create-form .form-fields kendo-textarea{width:100%}.meshboard-manager-dialog .create-form .form-actions{display:flex;justify-content:flex-end;gap:.5rem}.meshboard-manager-dialog .meshboards-list{flex:1;min-height:250px;overflow-y:auto;border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px;background-color:var(--kendo-color-surface, #ffffff)}.meshboard-manager-dialog .meshboards-list .loading-state{display:flex;align-items:center;justify-content:center;padding:2rem;color:var(--kendo-color-subtle, #757575)}.meshboard-manager-dialog .meshboards-list .empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem 2rem;text-align:center}.meshboard-manager-dialog .meshboards-list .empty-state kendo-svg-icon{color:var(--kendo-color-subtle, #9e9e9e);margin-bottom:1rem}.meshboard-manager-dialog .meshboards-list .empty-state h3{margin:.5rem 0;color:var(--kendo-color-on-app-surface, #424242);font-size:1.125rem;font-weight:500}.meshboard-manager-dialog .meshboards-list .empty-state p{margin:0;color:var(--kendo-color-subtle, #757575);font-size:.875rem}.meshboard-manager-dialog .meshboards-list .meshboard-item{border-bottom:1px solid var(--kendo-color-border, #e0e0e0);transition:background-color .2s,box-shadow .2s}.meshboard-manager-dialog .meshboards-list .meshboard-item:last-child{border-bottom:none}.meshboard-manager-dialog .meshboards-list .meshboard-item:not(.active):not(.editing):hover{background-color:var(--kendo-color-base-hover, #f5f5f5)}.meshboard-manager-dialog .meshboards-list .meshboard-item.active{background-color:var(--kendo-color-primary-subtle, #e8eaf6);box-shadow:inset 4px 0 0 var(--kendo-color-primary, #3f51b5)}.meshboard-manager-dialog .meshboards-list .meshboard-item.editing{background-color:var(--kendo-color-surface-alt, #fafafa)}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content{display:flex;align-items:center;gap:1rem;padding:1rem;cursor:pointer}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:8px;background-color:var(--kendo-color-surface-alt, #f5f5f5);color:var(--kendo-color-primary, #3f51b5)}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-icon kendo-svg-icon{color:inherit}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-info{flex:1;min-width:0}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-info .item-name{margin:0 0 .25rem;font-size:1rem;font-weight:500;color:var(--kendo-color-on-app-surface, #212121);display:flex;align-items:center;gap:.5rem}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-info .item-name .active-badge{display:inline-block;padding:.125rem .5rem;font-size:.75rem;font-weight:600;color:var(--kendo-color-on-primary, #ffffff);background-color:var(--kendo-color-primary, #3f51b5);border-radius:12px}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-info .item-description{margin:0;font-size:.875rem;color:var(--kendo-color-subtle, #757575);line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content .item-actions{flex-shrink:0;display:flex;gap:.25rem;opacity:.5;transition:opacity .2s}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-content:hover .item-actions{opacity:1}.meshboard-manager-dialog .meshboards-list .meshboard-item.active .item-content .item-icon{background-color:var(--kendo-color-primary, #3f51b5);color:var(--kendo-color-on-primary, #ffffff)}.meshboard-manager-dialog .meshboards-list .meshboard-item:not(.active) .item-content:after{content:\"Click to switch\";font-size:.75rem;color:var(--kendo-color-subtle, #9e9e9e);opacity:0;transition:opacity .2s;white-space:nowrap}.meshboard-manager-dialog .meshboards-list .meshboard-item:not(.active):hover .item-content:after{opacity:1}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit{padding:1rem}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit .edit-fields{display:flex;flex-direction:column;gap:.75rem;margin-bottom:.75rem}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit .edit-fields kendo-textbox,.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit .edit-fields kendo-textarea{width:100%}.meshboard-manager-dialog .meshboards-list .meshboard-item .item-edit .edit-actions{display:flex;justify-content:flex-end;gap:.5rem}.meshboard-manager-dialog .dialog-actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.75rem;border-top:1px solid var(--kendo-color-border, #e0e0e0);flex-shrink:0;margin-bottom:.5rem}.meshboards-list::-webkit-scrollbar{width:8px}.meshboards-list::-webkit-scrollbar-track{background:var(--kendo-color-surface-alt, #f5f5f5)}.meshboards-list::-webkit-scrollbar-thumb{background:var(--kendo-color-border, #e0e0e0);border-radius:4px}.meshboards-list::-webkit-scrollbar-thumb:hover{background:var(--kendo-color-subtle, #9e9e9e)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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: DialogModule }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.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: i3.TextAreaComponent, selector: "kendo-textarea", inputs: ["focusableId", "flow", "inputAttributes", "adornmentsOrientation", "rows", "cols", "maxlength", "maxResizableRows", "tabindex", "tabIndex", "resizable", "size", "rounded", "fillMode", "showPrefixSeparator", "showSuffixSeparator"], outputs: ["focus", "blur", "valueChange"], exportAs: ["kendoTextArea"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }] });
|
|
25739
27798
|
}
|
|
25740
27799
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MeshBoardManagerDialogComponent, decorators: [{
|
|
25741
27800
|
type: Component,
|
|
@@ -25756,6 +27815,11 @@ class EditWidgetDialogComponent {
|
|
|
25756
27815
|
gridService;
|
|
25757
27816
|
save = new EventEmitter();
|
|
25758
27817
|
cancelled = new EventEmitter();
|
|
27818
|
+
zoneOptions = [
|
|
27819
|
+
{ text: 'Grid', value: 'grid' },
|
|
27820
|
+
{ text: 'Banner', value: 'banner' }
|
|
27821
|
+
];
|
|
27822
|
+
selectedZoneOption = this.zoneOptions[0];
|
|
25759
27823
|
form = {
|
|
25760
27824
|
id: '',
|
|
25761
27825
|
title: '',
|
|
@@ -25766,27 +27830,38 @@ class EditWidgetDialogComponent {
|
|
|
25766
27830
|
};
|
|
25767
27831
|
error = '';
|
|
25768
27832
|
ngOnInit() {
|
|
25769
|
-
|
|
27833
|
+
const zone = this.widget.zone ?? 'grid';
|
|
25770
27834
|
this.form = {
|
|
25771
27835
|
id: this.widget.id,
|
|
25772
27836
|
title: this.widget.title,
|
|
25773
27837
|
col: this.widget.col,
|
|
25774
27838
|
row: this.widget.row,
|
|
25775
27839
|
colSpan: this.widget.colSpan,
|
|
25776
|
-
rowSpan: this.widget.rowSpan
|
|
27840
|
+
rowSpan: this.widget.rowSpan,
|
|
27841
|
+
chromeless: this.widget.chromeless ?? false,
|
|
27842
|
+
zone
|
|
25777
27843
|
};
|
|
27844
|
+
this.selectedZoneOption = this.zoneOptions.find(o => o.value === zone) ?? this.zoneOptions[0];
|
|
27845
|
+
}
|
|
27846
|
+
onZoneChange(option) {
|
|
27847
|
+
this.selectedZoneOption = option;
|
|
27848
|
+
this.form.zone = option.value;
|
|
27849
|
+
this.error = '';
|
|
25778
27850
|
}
|
|
25779
27851
|
onSave() {
|
|
25780
|
-
//
|
|
25781
|
-
|
|
25782
|
-
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
|
|
25789
|
-
|
|
27852
|
+
// Skip grid validation for banner zone widgets
|
|
27853
|
+
if (this.form.zone !== 'banner') {
|
|
27854
|
+
// Validate: check for overlaps
|
|
27855
|
+
const wouldOverlap = this.gridService.wouldCauseOverlap(this.widgets, this.form.id, this.form.col, this.form.row, this.form.colSpan, this.form.rowSpan);
|
|
27856
|
+
if (wouldOverlap) {
|
|
27857
|
+
this.error = 'Position overlaps with another widget. Please choose a different position.';
|
|
27858
|
+
return;
|
|
27859
|
+
}
|
|
27860
|
+
// Validate: check bounds
|
|
27861
|
+
if (this.gridService.isOutOfBounds(this.form.col, this.form.colSpan, this.maxColumns)) {
|
|
27862
|
+
this.error = 'Widget extends beyond the grid. Reduce column or width.';
|
|
27863
|
+
return;
|
|
27864
|
+
}
|
|
25790
27865
|
}
|
|
25791
27866
|
this.save.emit(this.form);
|
|
25792
27867
|
}
|
|
@@ -25811,52 +27886,75 @@ class EditWidgetDialogComponent {
|
|
|
25811
27886
|
</kendo-textbox>
|
|
25812
27887
|
</div>
|
|
25813
27888
|
|
|
25814
|
-
<div class="form-
|
|
25815
|
-
<
|
|
25816
|
-
|
|
25817
|
-
|
|
25818
|
-
|
|
25819
|
-
|
|
25820
|
-
|
|
25821
|
-
|
|
25822
|
-
|
|
25823
|
-
|
|
25824
|
-
</kendo-numerictextbox>
|
|
25825
|
-
</div>
|
|
25826
|
-
<div class="form-field">
|
|
25827
|
-
<label for="editWidgetRow">Row</label>
|
|
25828
|
-
<kendo-numerictextbox
|
|
25829
|
-
id="editWidgetRow"
|
|
25830
|
-
[(value)]="form.row"
|
|
25831
|
-
[min]="1"
|
|
25832
|
-
[decimals]="0"
|
|
25833
|
-
[format]="'n0'">
|
|
25834
|
-
</kendo-numerictextbox>
|
|
25835
|
-
</div>
|
|
27889
|
+
<div class="form-field">
|
|
27890
|
+
<label for="editWidgetZone">Zone</label>
|
|
27891
|
+
<kendo-dropdownlist
|
|
27892
|
+
id="editWidgetZone"
|
|
27893
|
+
[data]="zoneOptions"
|
|
27894
|
+
textField="text"
|
|
27895
|
+
valueField="value"
|
|
27896
|
+
[value]="selectedZoneOption"
|
|
27897
|
+
(valueChange)="onZoneChange($event)">
|
|
27898
|
+
</kendo-dropdownlist>
|
|
25836
27899
|
</div>
|
|
25837
27900
|
|
|
25838
|
-
|
|
25839
|
-
<div class="form-
|
|
25840
|
-
<
|
|
25841
|
-
|
|
25842
|
-
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
27901
|
+
@if (form.zone !== 'banner') {
|
|
27902
|
+
<div class="form-row">
|
|
27903
|
+
<div class="form-field">
|
|
27904
|
+
<label for="editWidgetCol">Column</label>
|
|
27905
|
+
<kendo-numerictextbox
|
|
27906
|
+
id="editWidgetCol"
|
|
27907
|
+
[(value)]="form.col"
|
|
27908
|
+
[min]="1"
|
|
27909
|
+
[max]="maxColumns"
|
|
27910
|
+
[decimals]="0"
|
|
27911
|
+
[format]="'n0'">
|
|
27912
|
+
</kendo-numerictextbox>
|
|
27913
|
+
</div>
|
|
27914
|
+
<div class="form-field">
|
|
27915
|
+
<label for="editWidgetRow">Row</label>
|
|
27916
|
+
<kendo-numerictextbox
|
|
27917
|
+
id="editWidgetRow"
|
|
27918
|
+
[(value)]="form.row"
|
|
27919
|
+
[min]="1"
|
|
27920
|
+
[decimals]="0"
|
|
27921
|
+
[format]="'n0'">
|
|
27922
|
+
</kendo-numerictextbox>
|
|
27923
|
+
</div>
|
|
25849
27924
|
</div>
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
<
|
|
25853
|
-
|
|
25854
|
-
|
|
25855
|
-
|
|
25856
|
-
|
|
25857
|
-
|
|
25858
|
-
|
|
27925
|
+
|
|
27926
|
+
<div class="form-row">
|
|
27927
|
+
<div class="form-field">
|
|
27928
|
+
<label for="editWidgetColSpan">Width (columns)</label>
|
|
27929
|
+
<kendo-numerictextbox
|
|
27930
|
+
id="editWidgetColSpan"
|
|
27931
|
+
[(value)]="form.colSpan"
|
|
27932
|
+
[min]="1"
|
|
27933
|
+
[max]="maxColumns"
|
|
27934
|
+
[decimals]="0"
|
|
27935
|
+
[format]="'n0'">
|
|
27936
|
+
</kendo-numerictextbox>
|
|
27937
|
+
</div>
|
|
27938
|
+
<div class="form-field">
|
|
27939
|
+
<label for="editWidgetRowSpan">Height (rows)</label>
|
|
27940
|
+
<kendo-numerictextbox
|
|
27941
|
+
id="editWidgetRowSpan"
|
|
27942
|
+
[(value)]="form.rowSpan"
|
|
27943
|
+
[min]="1"
|
|
27944
|
+
[decimals]="0"
|
|
27945
|
+
[format]="'n0'">
|
|
27946
|
+
</kendo-numerictextbox>
|
|
27947
|
+
</div>
|
|
27948
|
+
</div>
|
|
27949
|
+
} @else {
|
|
27950
|
+
<div class="form-hint">
|
|
27951
|
+
Banner widgets are rendered as a full-width stack above the grid.
|
|
25859
27952
|
</div>
|
|
27953
|
+
}
|
|
27954
|
+
|
|
27955
|
+
<div class="form-field form-field-checkbox">
|
|
27956
|
+
<input type="checkbox" id="editWidgetChromeless" [(ngModel)]="form.chromeless" />
|
|
27957
|
+
<label for="editWidgetChromeless">Hide title bar and border in view mode</label>
|
|
25860
27958
|
</div>
|
|
25861
27959
|
|
|
25862
27960
|
@if (error) {
|
|
@@ -25875,7 +27973,7 @@ class EditWidgetDialogComponent {
|
|
|
25875
27973
|
</kendo-dialog-actions>
|
|
25876
27974
|
|
|
25877
27975
|
</kendo-dialog>
|
|
25878
|
-
`, isInline: true, styles: [".edit-widget-form{display:flex;flex-direction:column;gap:16px;padding:8px 0}.form-field{display:flex;flex-direction:column;gap:4px}.form-field label{font-weight:500;font-size:.875rem;color:var(--kendo-color-on-app-surface, #212529)}.form-row{display:flex;gap:16px}.form-row .form-field{flex:1}.form-error{color:var(--kendo-color-error, #dc3545);font-size:.875rem;padding:8px;background:var(--kendo-color-error-subtle, rgba(220, 53, 69, .1));border-radius:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$
|
|
27976
|
+
`, isInline: true, styles: [".edit-widget-form{display:flex;flex-direction:column;gap:16px;padding:8px 0}.form-field{display:flex;flex-direction:column;gap:4px}.form-field label{font-weight:500;font-size:.875rem;color:var(--kendo-color-on-app-surface, #212529)}.form-row{display:flex;gap:16px}.form-row .form-field{flex:1}.form-field-checkbox{flex-direction:row;align-items:center;gap:8px}.form-field-checkbox label{font-weight:400}.form-hint{font-size:.8rem;opacity:.7;padding:8px 12px;border-left:3px solid var(--kendo-color-primary, #0d6efd);background:var(--kendo-color-surface-alt, rgba(0, 0, 0, .05));border-radius:2px}.form-error{color:var(--kendo-color-error, #dc3545);font-size:.875rem;padding:8px;background:var(--kendo-color-error-subtle, rgba(220, 53, 69, .1));border-radius:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$5.DialogComponent, selector: "kendo-dialog", inputs: ["actions", "actionsLayout", "autoFocusedElement", "title", "width", "minWidth", "maxWidth", "height", "minHeight", "maxHeight", "animation", "themeColor"], outputs: ["action", "close"], exportAs: ["kendoDialog"] }, { kind: "component", type: i1$5.DialogActionsComponent, selector: "kendo-dialog-actions", inputs: ["actions", "layout"], outputs: ["action"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.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: i3.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: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.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: "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"] }] });
|
|
25879
27977
|
}
|
|
25880
27978
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: EditWidgetDialogComponent, decorators: [{
|
|
25881
27979
|
type: Component,
|
|
@@ -25884,7 +27982,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
25884
27982
|
FormsModule,
|
|
25885
27983
|
DialogModule,
|
|
25886
27984
|
InputsModule,
|
|
25887
|
-
ButtonsModule
|
|
27985
|
+
ButtonsModule,
|
|
27986
|
+
DropDownsModule
|
|
25888
27987
|
], template: `
|
|
25889
27988
|
<kendo-dialog
|
|
25890
27989
|
title="Edit Widget"
|
|
@@ -25902,52 +28001,75 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
25902
28001
|
</kendo-textbox>
|
|
25903
28002
|
</div>
|
|
25904
28003
|
|
|
25905
|
-
<div class="form-
|
|
25906
|
-
<
|
|
25907
|
-
|
|
25908
|
-
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
</kendo-numerictextbox>
|
|
25916
|
-
</div>
|
|
25917
|
-
<div class="form-field">
|
|
25918
|
-
<label for="editWidgetRow">Row</label>
|
|
25919
|
-
<kendo-numerictextbox
|
|
25920
|
-
id="editWidgetRow"
|
|
25921
|
-
[(value)]="form.row"
|
|
25922
|
-
[min]="1"
|
|
25923
|
-
[decimals]="0"
|
|
25924
|
-
[format]="'n0'">
|
|
25925
|
-
</kendo-numerictextbox>
|
|
25926
|
-
</div>
|
|
28004
|
+
<div class="form-field">
|
|
28005
|
+
<label for="editWidgetZone">Zone</label>
|
|
28006
|
+
<kendo-dropdownlist
|
|
28007
|
+
id="editWidgetZone"
|
|
28008
|
+
[data]="zoneOptions"
|
|
28009
|
+
textField="text"
|
|
28010
|
+
valueField="value"
|
|
28011
|
+
[value]="selectedZoneOption"
|
|
28012
|
+
(valueChange)="onZoneChange($event)">
|
|
28013
|
+
</kendo-dropdownlist>
|
|
25927
28014
|
</div>
|
|
25928
28015
|
|
|
25929
|
-
|
|
25930
|
-
<div class="form-
|
|
25931
|
-
<
|
|
25932
|
-
|
|
25933
|
-
|
|
25934
|
-
|
|
25935
|
-
|
|
25936
|
-
|
|
25937
|
-
|
|
25938
|
-
|
|
25939
|
-
|
|
28016
|
+
@if (form.zone !== 'banner') {
|
|
28017
|
+
<div class="form-row">
|
|
28018
|
+
<div class="form-field">
|
|
28019
|
+
<label for="editWidgetCol">Column</label>
|
|
28020
|
+
<kendo-numerictextbox
|
|
28021
|
+
id="editWidgetCol"
|
|
28022
|
+
[(value)]="form.col"
|
|
28023
|
+
[min]="1"
|
|
28024
|
+
[max]="maxColumns"
|
|
28025
|
+
[decimals]="0"
|
|
28026
|
+
[format]="'n0'">
|
|
28027
|
+
</kendo-numerictextbox>
|
|
28028
|
+
</div>
|
|
28029
|
+
<div class="form-field">
|
|
28030
|
+
<label for="editWidgetRow">Row</label>
|
|
28031
|
+
<kendo-numerictextbox
|
|
28032
|
+
id="editWidgetRow"
|
|
28033
|
+
[(value)]="form.row"
|
|
28034
|
+
[min]="1"
|
|
28035
|
+
[decimals]="0"
|
|
28036
|
+
[format]="'n0'">
|
|
28037
|
+
</kendo-numerictextbox>
|
|
28038
|
+
</div>
|
|
25940
28039
|
</div>
|
|
25941
|
-
|
|
25942
|
-
|
|
25943
|
-
<
|
|
25944
|
-
|
|
25945
|
-
|
|
25946
|
-
|
|
25947
|
-
|
|
25948
|
-
|
|
25949
|
-
|
|
28040
|
+
|
|
28041
|
+
<div class="form-row">
|
|
28042
|
+
<div class="form-field">
|
|
28043
|
+
<label for="editWidgetColSpan">Width (columns)</label>
|
|
28044
|
+
<kendo-numerictextbox
|
|
28045
|
+
id="editWidgetColSpan"
|
|
28046
|
+
[(value)]="form.colSpan"
|
|
28047
|
+
[min]="1"
|
|
28048
|
+
[max]="maxColumns"
|
|
28049
|
+
[decimals]="0"
|
|
28050
|
+
[format]="'n0'">
|
|
28051
|
+
</kendo-numerictextbox>
|
|
28052
|
+
</div>
|
|
28053
|
+
<div class="form-field">
|
|
28054
|
+
<label for="editWidgetRowSpan">Height (rows)</label>
|
|
28055
|
+
<kendo-numerictextbox
|
|
28056
|
+
id="editWidgetRowSpan"
|
|
28057
|
+
[(value)]="form.rowSpan"
|
|
28058
|
+
[min]="1"
|
|
28059
|
+
[decimals]="0"
|
|
28060
|
+
[format]="'n0'">
|
|
28061
|
+
</kendo-numerictextbox>
|
|
28062
|
+
</div>
|
|
28063
|
+
</div>
|
|
28064
|
+
} @else {
|
|
28065
|
+
<div class="form-hint">
|
|
28066
|
+
Banner widgets are rendered as a full-width stack above the grid.
|
|
25950
28067
|
</div>
|
|
28068
|
+
}
|
|
28069
|
+
|
|
28070
|
+
<div class="form-field form-field-checkbox">
|
|
28071
|
+
<input type="checkbox" id="editWidgetChromeless" [(ngModel)]="form.chromeless" />
|
|
28072
|
+
<label for="editWidgetChromeless">Hide title bar and border in view mode</label>
|
|
25951
28073
|
</div>
|
|
25952
28074
|
|
|
25953
28075
|
@if (error) {
|
|
@@ -25966,7 +28088,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
25966
28088
|
</kendo-dialog-actions>
|
|
25967
28089
|
|
|
25968
28090
|
</kendo-dialog>
|
|
25969
|
-
`, styles: [".edit-widget-form{display:flex;flex-direction:column;gap:16px;padding:8px 0}.form-field{display:flex;flex-direction:column;gap:4px}.form-field label{font-weight:500;font-size:.875rem;color:var(--kendo-color-on-app-surface, #212529)}.form-row{display:flex;gap:16px}.form-row .form-field{flex:1}.form-error{color:var(--kendo-color-error, #dc3545);font-size:.875rem;padding:8px;background:var(--kendo-color-error-subtle, rgba(220, 53, 69, .1));border-radius:4px}\n"] }]
|
|
28091
|
+
`, styles: [".edit-widget-form{display:flex;flex-direction:column;gap:16px;padding:8px 0}.form-field{display:flex;flex-direction:column;gap:4px}.form-field label{font-weight:500;font-size:.875rem;color:var(--kendo-color-on-app-surface, #212529)}.form-row{display:flex;gap:16px}.form-row .form-field{flex:1}.form-field-checkbox{flex-direction:row;align-items:center;gap:8px}.form-field-checkbox label{font-weight:400}.form-hint{font-size:.8rem;opacity:.7;padding:8px 12px;border-left:3px solid var(--kendo-color-primary, #0d6efd);background:var(--kendo-color-surface-alt, rgba(0, 0, 0, .05));border-radius:2px}.form-error{color:var(--kendo-color-error, #dc3545);font-size:.875rem;padding:8px;background:var(--kendo-color-error-subtle, rgba(220, 53, 69, .1));border-radius:4px}\n"] }]
|
|
25970
28092
|
}], propDecorators: { widget: [{
|
|
25971
28093
|
type: Input
|
|
25972
28094
|
}], widgets: [{
|
|
@@ -26017,6 +28139,9 @@ class MeshBoardViewComponent {
|
|
|
26017
28139
|
undoIcon = undoIcon;
|
|
26018
28140
|
copyIcon = copyIcon;
|
|
26019
28141
|
infoCircleIcon = infoCircleIcon;
|
|
28142
|
+
arrowUpIcon = arrowUpIcon;
|
|
28143
|
+
arrowDownIcon = arrowDownIcon;
|
|
28144
|
+
tileLayout;
|
|
26020
28145
|
// Edit widget dialog state
|
|
26021
28146
|
showEditWidgetDialog = false;
|
|
26022
28147
|
editingWidget = null;
|
|
@@ -26049,6 +28174,9 @@ class MeshBoardViewComponent {
|
|
|
26049
28174
|
}, ...(ngDevMode ? [{ debugName: "meshBoardPageLink" }] : /* istanbul ignore next */ []));
|
|
26050
28175
|
// Computed
|
|
26051
28176
|
hasWidgets = computed(() => this.config().widgets.length > 0, ...(ngDevMode ? [{ debugName: "hasWidgets" }] : /* istanbul ignore next */ []));
|
|
28177
|
+
bannerWidgets = computed(() => this.config().widgets.filter(w => w.zone === 'banner'), ...(ngDevMode ? [{ debugName: "bannerWidgets" }] : /* istanbul ignore next */ []));
|
|
28178
|
+
gridWidgets = computed(() => this.config().widgets.filter(w => w.zone !== 'banner'), ...(ngDevMode ? [{ debugName: "gridWidgets" }] : /* istanbul ignore next */ []));
|
|
28179
|
+
hasGridWidgets = computed(() => this.gridWidgets().length > 0, ...(ngDevMode ? [{ debugName: "hasGridWidgets" }] : /* istanbul ignore next */ []));
|
|
26052
28180
|
canSave = computed(() => this.isEditMode() && !this.isSaving(), ...(ngDevMode ? [{ debugName: "canSave" }] : /* istanbul ignore next */ []));
|
|
26053
28181
|
// Time Filter computed signals
|
|
26054
28182
|
isTimeFilterEnabled = computed(() => this.stateService.isTimeFilterEnabled(), ...(ngDevMode ? [{ debugName: "isTimeFilterEnabled" }] : /* istanbul ignore next */ []));
|
|
@@ -26714,6 +28842,50 @@ class MeshBoardViewComponent {
|
|
|
26714
28842
|
this.editModeService.enterEditMode(this.stateService.getConfig());
|
|
26715
28843
|
}
|
|
26716
28844
|
}
|
|
28845
|
+
/**
|
|
28846
|
+
* Moves a banner widget up (earlier) in the widgets array.
|
|
28847
|
+
*/
|
|
28848
|
+
moveBannerUp(widgetId) {
|
|
28849
|
+
this.stateService.updateConfig(config => {
|
|
28850
|
+
const widgets = [...config.widgets];
|
|
28851
|
+
const index = widgets.findIndex(w => w.id === widgetId);
|
|
28852
|
+
if (index <= 0)
|
|
28853
|
+
return config;
|
|
28854
|
+
// Find previous banner widget
|
|
28855
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
28856
|
+
if (widgets[i].zone === 'banner') {
|
|
28857
|
+
[widgets[i], widgets[index]] = [widgets[index], widgets[i]];
|
|
28858
|
+
return { ...config, widgets };
|
|
28859
|
+
}
|
|
28860
|
+
}
|
|
28861
|
+
return config;
|
|
28862
|
+
});
|
|
28863
|
+
if (!this.isEditMode()) {
|
|
28864
|
+
this.editModeService.enterEditMode(this.stateService.getConfig());
|
|
28865
|
+
}
|
|
28866
|
+
}
|
|
28867
|
+
/**
|
|
28868
|
+
* Moves a banner widget down (later) in the widgets array.
|
|
28869
|
+
*/
|
|
28870
|
+
moveBannerDown(widgetId) {
|
|
28871
|
+
this.stateService.updateConfig(config => {
|
|
28872
|
+
const widgets = [...config.widgets];
|
|
28873
|
+
const index = widgets.findIndex(w => w.id === widgetId);
|
|
28874
|
+
if (index === -1)
|
|
28875
|
+
return config;
|
|
28876
|
+
// Find next banner widget
|
|
28877
|
+
for (let i = index + 1; i < widgets.length; i++) {
|
|
28878
|
+
if (widgets[i].zone === 'banner') {
|
|
28879
|
+
[widgets[i], widgets[index]] = [widgets[index], widgets[i]];
|
|
28880
|
+
return { ...config, widgets };
|
|
28881
|
+
}
|
|
28882
|
+
}
|
|
28883
|
+
return config;
|
|
28884
|
+
});
|
|
28885
|
+
if (!this.isEditMode()) {
|
|
28886
|
+
this.editModeService.enterEditMode(this.stateService.getConfig());
|
|
28887
|
+
}
|
|
28888
|
+
}
|
|
26717
28889
|
/**
|
|
26718
28890
|
* Opens the configuration dialog for a widget.
|
|
26719
28891
|
* Uses the WidgetRegistryService to open a resizable Kendo Window.
|
|
@@ -26750,12 +28922,16 @@ class MeshBoardViewComponent {
|
|
|
26750
28922
|
* Handles TileLayout reorder events.
|
|
26751
28923
|
*/
|
|
26752
28924
|
onReorder(event) {
|
|
26753
|
-
//
|
|
26754
|
-
|
|
26755
|
-
|
|
26756
|
-
|
|
26757
|
-
|
|
26758
|
-
this.
|
|
28925
|
+
// The event provides the new position of the reordered item explicitly,
|
|
28926
|
+
// because the @Input() properties on TileLayoutItemComponent still reflect
|
|
28927
|
+
// the old Angular binding values at event time.
|
|
28928
|
+
// We use event.newCol/newRow for the moved item and read other items'
|
|
28929
|
+
// positions from the TileLayout after Angular has processed the change.
|
|
28930
|
+
this.syncWidgetPositionsFromEvent({
|
|
28931
|
+
movedItemIndex: event.oldIndex,
|
|
28932
|
+
newCol: event.newCol,
|
|
28933
|
+
newRow: event.newRow
|
|
28934
|
+
});
|
|
26759
28935
|
// Enter edit mode if not already in it
|
|
26760
28936
|
if (!this.isEditMode()) {
|
|
26761
28937
|
this.editModeService.enterEditMode(this.stateService.getConfig());
|
|
@@ -26765,26 +28941,85 @@ class MeshBoardViewComponent {
|
|
|
26765
28941
|
* Handles TileLayout resize events.
|
|
26766
28942
|
*/
|
|
26767
28943
|
onResize(event) {
|
|
26768
|
-
//
|
|
26769
|
-
|
|
26770
|
-
|
|
26771
|
-
const
|
|
26772
|
-
|
|
26773
|
-
|
|
26774
|
-
|
|
26775
|
-
|
|
26776
|
-
|
|
26777
|
-
|
|
26778
|
-
|
|
26779
|
-
|
|
26780
|
-
|
|
26781
|
-
|
|
26782
|
-
|
|
26783
|
-
|
|
26784
|
-
|
|
26785
|
-
|
|
26786
|
-
|
|
28944
|
+
// The event provides the new spans of the resized item explicitly.
|
|
28945
|
+
// We find the resized item by matching its old col/row position.
|
|
28946
|
+
const currentGridWidgets = this.gridWidgets();
|
|
28947
|
+
const resizedIndex = currentGridWidgets.findIndex(w => w.col === event.item.col && w.row === event.item.row);
|
|
28948
|
+
this.syncWidgetPositionsFromEvent({
|
|
28949
|
+
movedItemIndex: resizedIndex,
|
|
28950
|
+
newCol: event.item.col,
|
|
28951
|
+
newRow: event.item.row,
|
|
28952
|
+
newColSpan: event.newColSpan,
|
|
28953
|
+
newRowSpan: event.newRowSpan
|
|
28954
|
+
});
|
|
28955
|
+
// Enter edit mode if not already in it
|
|
28956
|
+
if (!this.isEditMode()) {
|
|
28957
|
+
this.editModeService.enterEditMode(this.stateService.getConfig());
|
|
28958
|
+
}
|
|
28959
|
+
}
|
|
28960
|
+
/**
|
|
28961
|
+
* Syncs widget positions from a TileLayout reorder or resize event.
|
|
28962
|
+
*
|
|
28963
|
+
* At event time, the @Input() properties on TileLayoutItemComponent still
|
|
28964
|
+
* hold their old Angular binding values. The only reliable source for the
|
|
28965
|
+
* changed item's new position are the explicit event properties
|
|
28966
|
+
* (newCol/newRow for reorder, newColSpan/newRowSpan for resize).
|
|
28967
|
+
*
|
|
28968
|
+
* After updating the changed item, we schedule a deferred read of all
|
|
28969
|
+
* item positions from the TileLayout ViewChild (after Angular has processed
|
|
28970
|
+
* the change) to pick up any reflow of other widgets.
|
|
28971
|
+
*/
|
|
28972
|
+
syncWidgetPositionsFromEvent(change) {
|
|
28973
|
+
const currentGridWidgets = this.gridWidgets();
|
|
28974
|
+
if (change.movedItemIndex < 0 || change.movedItemIndex >= currentGridWidgets.length) {
|
|
28975
|
+
console.warn('Could not find moved/resized widget at index', change.movedItemIndex);
|
|
28976
|
+
return;
|
|
26787
28977
|
}
|
|
28978
|
+
const movedWidget = currentGridWidgets[change.movedItemIndex];
|
|
28979
|
+
// Update the moved/resized widget with the explicit event values
|
|
28980
|
+
this.stateService.updateWidget(movedWidget.id, w => ({
|
|
28981
|
+
...w,
|
|
28982
|
+
col: change.newCol ?? w.col,
|
|
28983
|
+
row: change.newRow ?? w.row,
|
|
28984
|
+
colSpan: change.newColSpan ?? w.colSpan,
|
|
28985
|
+
rowSpan: change.newRowSpan ?? w.rowSpan
|
|
28986
|
+
}));
|
|
28987
|
+
// After Angular processes the change and Kendo re-renders,
|
|
28988
|
+
// read back all item positions to catch any reflow of other widgets.
|
|
28989
|
+
setTimeout(() => this.syncAllWidgetPositionsFromTileLayout(), 0);
|
|
28990
|
+
}
|
|
28991
|
+
/**
|
|
28992
|
+
* Reads the current col/row/colSpan/rowSpan from all TileLayout items
|
|
28993
|
+
* and updates the widget state accordingly.
|
|
28994
|
+
* Called after Angular change detection so @Input() values are current.
|
|
28995
|
+
*/
|
|
28996
|
+
syncAllWidgetPositionsFromTileLayout() {
|
|
28997
|
+
if (!this.tileLayout)
|
|
28998
|
+
return;
|
|
28999
|
+
const tileItems = this.tileLayout.items?.toArray();
|
|
29000
|
+
if (!tileItems)
|
|
29001
|
+
return;
|
|
29002
|
+
const currentGridWidgets = this.gridWidgets();
|
|
29003
|
+
if (tileItems.length !== currentGridWidgets.length)
|
|
29004
|
+
return;
|
|
29005
|
+
this.stateService.updateConfig(config => ({
|
|
29006
|
+
...config,
|
|
29007
|
+
widgets: config.widgets.map(widget => {
|
|
29008
|
+
if (widget.zone === 'banner')
|
|
29009
|
+
return widget;
|
|
29010
|
+
const gridIndex = currentGridWidgets.findIndex(gw => gw.id === widget.id);
|
|
29011
|
+
if (gridIndex === -1 || gridIndex >= tileItems.length)
|
|
29012
|
+
return widget;
|
|
29013
|
+
const tileItem = tileItems[gridIndex];
|
|
29014
|
+
return {
|
|
29015
|
+
...widget,
|
|
29016
|
+
col: tileItem.col,
|
|
29017
|
+
row: tileItem.row,
|
|
29018
|
+
colSpan: tileItem.colSpan,
|
|
29019
|
+
rowSpan: tileItem.rowSpan
|
|
29020
|
+
};
|
|
29021
|
+
})
|
|
29022
|
+
}));
|
|
26788
29023
|
}
|
|
26789
29024
|
/**
|
|
26790
29025
|
* Track by function for widget rendering.
|
|
@@ -26833,7 +29068,9 @@ class MeshBoardViewComponent {
|
|
|
26833
29068
|
col: update.col,
|
|
26834
29069
|
row: update.row,
|
|
26835
29070
|
colSpan: update.colSpan,
|
|
26836
|
-
rowSpan: update.rowSpan
|
|
29071
|
+
rowSpan: update.rowSpan,
|
|
29072
|
+
chromeless: update.chromeless,
|
|
29073
|
+
zone: update.zone
|
|
26837
29074
|
}));
|
|
26838
29075
|
this.closeEditWidgetDialog();
|
|
26839
29076
|
// Enter edit mode if not already in it
|
|
@@ -26995,7 +29232,7 @@ class MeshBoardViewComponent {
|
|
|
26995
29232
|
}
|
|
26996
29233
|
}
|
|
26997
29234
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MeshBoardViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
26998
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: MeshBoardViewComponent, isStandalone: true, selector: "mm-meshboard-view", providers: [{ provide: HAS_UNSAVED_CHANGES, useExisting: MeshBoardViewComponent }], hostDirectives: [{ directive: i1$7.UnsavedChangesDirective }], ngImport: i0, template: "<div class=\"meshboard-view\">\n @if (isLoading()) {\n <div class=\"loading-container\">\n <div class=\"k-loading-mask\">\n <span class=\"k-loading-text\">Loading MeshBoard...</span>\n <div class=\"k-loading-image\"></div>\n <div class=\"k-loading-color\"></div>\n </div>\n </div>\n } @else if (isModelAvailable() === false) {\n <div class=\"model-unavailable\">\n <div class=\"message-card\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h2>MeshBoard Not Available</h2>\n <p>The MeshBoard feature requires the CK model 'System.UI' version 1.0.1 or higher.</p>\n <p>Please install the required model in your tenant to use this feature.</p>\n </div>\n </div>\n } @else if (notFoundError()) {\n <div class=\"model-unavailable\">\n <div class=\"message-card\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h2>MeshBoard Not Found</h2>\n <p>{{ notFoundError() }}</p>\n <p>To create a MeshBoard with a Well-Known Name:</p>\n <ol>\n <li>Go to the <a [routerLink]=\"meshBoardPageLink()\">MeshBoard</a> page</li>\n <li>Open Settings</li>\n <li>Set the \"Well-Known Name\" field</li>\n <li>Save the MeshBoard</li>\n </ol>\n </div>\n </div>\n } @else if (isInitialized()) {\n <!-- Toolbar -->\n <div class=\"meshboard-toolbar\">\n <div class=\"toolbar-left\">\n <h2 class=\"meshboard-title\">{{ config().name }}</h2>\n @if (config().description) {\n <span class=\"meshboard-description\">{{ config().description }}</span>\n }\n </div>\n\n <!-- Time Filter (center) -->\n @if (isTimeFilterEnabled()) {\n <div class=\"toolbar-center\">\n <mm-time-range-picker\n [config]=\"timeFilterConfig()?.pickerConfig ?? {}\"\n [labels]=\"timeRangeLabels()\"\n [initialSelection]=\"initialTimeSelection()\"\n (rangeChange)=\"onTimeRangeChange($event)\"\n (selectionChange)=\"onTimeSelectionChange($event)\">\n </mm-time-range-picker>\n @if (canResetTimeFilter()) {\n <button\n kendoButton\n [svgIcon]=\"undoIcon\"\n (click)=\"resetTimeFilterToDefault()\"\n title=\"Reset to default time filter\"\n fillMode=\"flat\"\n size=\"small\">\n </button>\n }\n </div>\n }\n\n <!-- Entity Selectors -->\n @if (hasEntitySelectors()) {\n @if (isTimeFilterEnabled()) {\n <div class=\"toolbar-separator\"></div>\n }\n <div class=\"toolbar-entity-selectors\">\n <mm-entity-selector-toolbar\n [entitySelectors]=\"entitySelectorsConfig()\"\n (entitySelected)=\"onEntitySelectorSelected($event)\"\n (entityCleared)=\"onEntitySelectorCleared($event)\">\n </mm-entity-selector-toolbar>\n </div>\n }\n\n @if (isTimeFilterEnabled() || hasEntitySelectors()) {\n <div class=\"toolbar-separator\"></div>\n }\n <div class=\"toolbar-right mm-toolbar-actions\">\n @if (!isReadonly()) {\n <!-- Manager Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"gridLayoutIcon\"\n (click)=\"openManager()\"\n title=\"Manage MeshBoards\"\n fillMode=\"flat\">\n </button>\n\n <!-- Settings Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openSettings()\"\n title=\"MeshBoard Settings\"\n fillMode=\"flat\">\n </button>\n }\n\n <!-- Refresh Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"arrowRotateCwIcon\"\n (click)=\"refresh()\"\n title=\"Refresh All Widgets\"\n fillMode=\"flat\">\n </button>\n\n @if (!isReadonly()) {\n <!-- Edit Mode Toggle (icon only) -->\n @if (!isEditMode()) {\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"toggleEditMode()\"\n title=\"Enter Edit Mode\"\n fillMode=\"flat\">\n </button>\n } @else {\n <button\n kendoButton\n [svgIcon]=\"xIcon\"\n (click)=\"cancelEdit()\"\n title=\"Cancel Edit Mode\"\n fillMode=\"flat\">\n </button>\n }\n\n <!-- Add Widget Button (with text) -->\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"openAddWidget()\"\n title=\"Add Widget\"\n themeColor=\"primary\">\n Add Widget\n </button>\n\n <!-- Save Button (with text) -->\n @if (canSave()) {\n <button\n kendoButton\n [svgIcon]=\"saveIcon\"\n (click)=\"save()\"\n [disabled]=\"isSaving()\"\n title=\"Save MeshBoard\"\n themeColor=\"primary\">\n {{ isSaving() ? 'Saving...' : 'Save' }}\n </button>\n }\n }\n </div>\n </div>\n\n <!-- Entity Selector Hint -->\n @if (unselectedToolbarSelectors().length > 0) {\n <div class=\"entity-selector-hint\">\n <kendo-svg-icon [icon]=\"infoCircleIcon\" size=\"medium\"></kendo-svg-icon>\n <span>\n Please select\n @for (selector of unselectedToolbarSelectors(); track selector.id; let last = $last) {\n <strong>{{ selector.label }}</strong>@if (!last) {, }\n }\n to display dashboard data.\n </span>\n </div>\n }\n\n <!-- Variable Resolution Errors -->\n @if (variableResolutionErrors().length > 0) {\n <div class=\"variable-resolution-errors\">\n <strong>Variable resolution errors:</strong>\n @for (error of variableResolutionErrors(); track error.variableName) {\n <span class=\"error-item\">{{ error.variableName }}: {{ error.message }}</span>\n }\n </div>\n }\n\n <!-- MeshBoard Grid -->\n @if (hasWidgets()) {\n <kendo-tilelayout\n [columns]=\"config().columns\"\n [rowHeight]=\"config().rowHeight\"\n [gap]=\"config().gap\"\n [resizable]=\"isEditMode()\"\n [reorderable]=\"isEditMode()\"\n (reorder)=\"onReorder($event)\"\n (resize)=\"onResize($event)\">\n @for (widget of config().widgets; track trackByWidgetId($index, widget)) {\n <kendo-tilelayout-item\n [col]=\"widget.col\"\n [row]=\"widget.row\"\n [colSpan]=\"widget.colSpan\"\n [rowSpan]=\"widget.rowSpan\">\n <kendo-tilelayout-item-header>\n <div class=\"widget-header\" [class.unconfigured]=\"isWidgetUnconfigured(widget)\">\n <span class=\"widget-title\">{{ widget.title }}</span>\n @if (isWidgetUnconfigured(widget) && !isEditMode()) {\n <span class=\"unconfigured-badge\" title=\"Widget needs configuration\">!</span>\n }\n @if (isEditMode()) {\n <div class=\"widget-actions\" (pointerdown)=\"$event.stopPropagation()\">\n @if (supportsConfiguration(widget)) {\n <button\n kendoButton\n [svgIcon]=\"linkIcon\"\n (click)=\"openWidgetConfig(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"config-widget-btn\"\n [class.needs-config]=\"isWidgetUnconfigured(widget)\"\n [title]=\"isWidgetUnconfigured(widget) ? 'Configure data source (required)' : 'Configure data source'\">\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openEditWidgetDialog(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"edit-widget-btn\"\n title=\"Edit position\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"copyIcon\"\n (click)=\"duplicateWidget(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"duplicate-widget-btn\"\n title=\"Duplicate widget\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeWidget(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"remove-widget-btn\"\n title=\"Delete widget\">\n </button>\n </div>\n }\n </div>\n </kendo-tilelayout-item-header>\n <kendo-tilelayout-item-body>\n @if (getWidgetComponentType(widget.type); as componentType) {\n <ng-container *ngComponentOutlet=\"componentType; inputs: { config: widget }\"></ng-container>\n } @else {\n <div class=\"widget-error\">\n <p>Unknown widget type: {{ widget.type }}</p>\n </div>\n }\n </kendo-tilelayout-item-body>\n </kendo-tilelayout-item>\n }\n </kendo-tilelayout>\n } @else {\n <div class=\"empty-state\">\n <div class=\"empty-state-content\">\n <kendo-svg-icon [icon]=\"plusIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h3>No Widgets</h3>\n <p>Get started by adding widgets to your MeshBoard.</p>\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"openAddWidget()\"\n themeColor=\"primary\">\n Add Your First Widget\n </button>\n </div>\n </div>\n }\n\n <!-- Edit Widget Dialog -->\n @if (showEditWidgetDialog && editingWidget) {\n <mm-edit-widget-dialog\n [widget]=\"editingWidget\"\n [widgets]=\"config().widgets\"\n [maxColumns]=\"config().columns\"\n [gridService]=\"gridService\"\n (save)=\"onEditWidgetSave($event)\"\n (cancelled)=\"closeEditWidgetDialog()\">\n </mm-edit-widget-dialog>\n }\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;height:100%;width:100%}.meshboard-view{display:flex;flex-direction:column;flex:1;min-height:0;width:100%;background-color:var(--kendo-color-surface, #f5f5f5)}.loading-container{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.entity-selector-hint{display:flex;align-items:center;gap:8px;padding:10px 16px;margin-bottom:8px;background:color-mix(in srgb,var(--kendo-color-info, #2196f3) 10%,var(--kendo-color-surface-alt, #fafafa));border:1px solid color-mix(in srgb,var(--kendo-color-info, #2196f3) 40%,transparent);border-radius:4px;font-size:13px;color:var(--kendo-color-on-app-surface, #424242)}.entity-selector-hint strong{color:var(--kendo-color-info, #2196f3)}.variable-resolution-errors{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:8px 16px;margin-bottom:8px;background:color-mix(in srgb,var(--kendo-color-error, #f44336) 10%,var(--kendo-color-surface-alt, #fafafa));border:1px solid var(--kendo-color-error, #f44336);border-radius:4px;font-size:13px;color:var(--kendo-color-on-app-surface, #424242)}.variable-resolution-errors strong{color:var(--kendo-color-error, #f44336)}.variable-resolution-errors .error-item{padding:2px 8px;background:color-mix(in srgb,var(--kendo-color-error, #f44336) 8%,transparent);border-radius:3px;font-family:Consolas,Monaco,monospace;font-size:12px}.model-unavailable{display:flex;align-items:center;justify-content:center;height:100%;width:100%;padding:2rem;overflow:hidden;box-sizing:border-box}.model-unavailable .message-card{text-align:center;max-width:500px;padding:2rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:8px;overflow:hidden}.model-unavailable .message-card kendo-svg-icon{color:var(--kendo-color-warning, #ff9800);margin-bottom:1rem}.model-unavailable .message-card h2{margin:1rem 0;color:var(--kendo-color-on-app-surface, #424242)}.model-unavailable .message-card p{margin:.5rem 0;color:var(--kendo-color-subtle, #757575);line-height:1.5}.model-unavailable .message-card ol{text-align:left;margin:1rem 0;padding-left:1.5rem;color:var(--kendo-color-subtle, #757575);line-height:1.8}.model-unavailable .message-card ol li{margin:.25rem 0}.model-unavailable .message-card ol a{color:var(--kendo-color-primary, #3f51b5);text-decoration:none}.model-unavailable .message-card ol a:hover{text-decoration:underline}.meshboard-toolbar{display:flex;justify-content:space-between;align-items:flex-end;padding:1rem 1.5rem;background-color:var(--kendo-color-surface-alt, white);border-bottom:1px solid var(--kendo-color-border, #e0e0e0);gap:1rem;flex-shrink:0}.meshboard-toolbar .toolbar-left{display:flex;align-items:baseline;gap:1rem;flex:1;min-width:0;align-self:center}.meshboard-toolbar .toolbar-left .meshboard-title{margin:0;font-size:1.5rem;font-weight:500;color:var(--kendo-color-on-app-surface, #212121);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meshboard-toolbar .toolbar-left .meshboard-description{color:var(--kendo-color-subtle, #757575);font-size:.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meshboard-toolbar .toolbar-center{display:flex;justify-content:center;align-items:flex-end;flex-shrink:0}.meshboard-toolbar .toolbar-center mm-time-range-picker ::ng-deep .time-range-picker{gap:.5rem}.meshboard-toolbar .toolbar-separator{width:1px;align-self:stretch;background-color:var(--kendo-color-border, #e0e0e0);flex-shrink:0}.meshboard-toolbar .toolbar-entity-selectors{display:flex;align-items:flex-end;flex-shrink:0}.meshboard-toolbar .toolbar-right{display:flex;gap:.5rem;align-items:center;flex-shrink:0}.empty-state{display:flex;align-items:flex-start;justify-content:center;height:calc(100% - 80px);width:100%;padding-top:4rem}.empty-state .empty-state-content{text-align:center;padding:2rem}.empty-state .empty-state-content kendo-svg-icon{color:var(--kendo-color-subtle, #9e9e9e);margin-bottom:1rem}.empty-state .empty-state-content h3{margin:1rem 0 .5rem;color:var(--kendo-color-on-app-surface, #424242);font-size:1.25rem;font-weight:500}.empty-state .empty-state-content p{margin:0 0 1.5rem;color:var(--kendo-color-subtle, #757575)}kendo-tilelayout{padding:1.5rem;overflow:auto;flex:1;min-height:0}.widget-header{display:flex;justify-content:space-between;align-items:center;width:100%;gap:.5rem}.widget-header.unconfigured{background-color:#ff98001a}.widget-header .widget-title{flex:1;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.widget-header .unconfigured-badge{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;background-color:var(--kendo-color-warning, #ff9800);color:#fff;font-size:12px;font-weight:700;flex-shrink:0}.widget-header .widget-actions{display:flex;align-items:center;gap:.25rem;flex-shrink:0}.widget-header .config-widget-btn,.widget-header .edit-widget-btn,.widget-header .remove-widget-btn{opacity:.6;transition:opacity .2s}.widget-header .config-widget-btn:hover,.widget-header .edit-widget-btn:hover,.widget-header .remove-widget-btn:hover{opacity:1}.widget-header .config-widget-btn.needs-config{color:var(--kendo-color-warning, #ff9800);opacity:1}.widget-header .remove-widget-btn{color:var(--kendo-color-error, #f44336)}.widget-error{display:flex;align-items:center;justify-content:center;height:100%;padding:1rem;color:var(--kendo-color-error, #f44336);text-align:center}.meshboard-view.edit-mode kendo-tilelayout-item:hover{box-shadow:0 0 0 2px var(--kendo-color-primary, #3f51b5)}@media(max-width:768px){.meshboard-toolbar{flex-direction:column;align-items:stretch;gap:.75rem}.meshboard-toolbar .toolbar-left{flex-direction:column;align-items:flex-start;gap:.25rem}.meshboard-toolbar .toolbar-right{flex-wrap:wrap;justify-content:flex-end}kendo-tilelayout{padding:1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i3$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: TileLayoutModule }, { kind: "component", type: i5.TileLayoutComponent, selector: "kendo-tilelayout", inputs: ["columns", "columnWidth", "gap", "reorderable", "resizable", "rowHeight", "autoFlow", "navigable"], outputs: ["reorder", "resize"] }, { kind: "component", type: i5.TileLayoutItemBodyComponent, selector: "kendo-tilelayout-item-body" }, { kind: "component", type: i5.TileLayoutItemComponent, selector: "kendo-tilelayout-item", inputs: ["title", "rowSpan", "colSpan", "order", "col", "row", "reorderable", "resizable"] }, { kind: "component", type: i5.TileLayoutItemHeaderComponent, selector: "kendo-tilelayout-item-header" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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: DialogModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i2$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: EditWidgetDialogComponent, selector: "mm-edit-widget-dialog", inputs: ["widget", "widgets", "maxColumns", "gridService"], outputs: ["save", "cancelled"] }, { kind: "component", type: TimeRangePickerComponent, selector: "mm-time-range-picker", inputs: ["config", "labels", "initialSelection"], outputs: ["rangeChange", "rangeChangeISO", "selectionChange"] }, { kind: "component", type: EntitySelectorToolbarComponent, selector: "mm-entity-selector-toolbar", inputs: ["entitySelectors"], outputs: ["entitySelected", "entityCleared"] }] });
|
|
29235
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: MeshBoardViewComponent, isStandalone: true, selector: "mm-meshboard-view", providers: [{ provide: HAS_UNSAVED_CHANGES, useExisting: MeshBoardViewComponent }], viewQueries: [{ propertyName: "tileLayout", first: true, predicate: TileLayoutComponent, descendants: true }], hostDirectives: [{ directive: i1$8.UnsavedChangesDirective }], ngImport: i0, template: "<div class=\"meshboard-view\">\n @if (isLoading()) {\n <div class=\"loading-container\">\n <div class=\"k-loading-mask\">\n <span class=\"k-loading-text\">Loading MeshBoard...</span>\n <div class=\"k-loading-image\"></div>\n <div class=\"k-loading-color\"></div>\n </div>\n </div>\n } @else if (isModelAvailable() === false) {\n <div class=\"model-unavailable\">\n <div class=\"message-card\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h2>MeshBoard Not Available</h2>\n <p>The MeshBoard feature requires the CK model 'System.UI' version 1.0.1 or higher.</p>\n <p>Please install the required model in your tenant to use this feature.</p>\n </div>\n </div>\n } @else if (notFoundError()) {\n <div class=\"model-unavailable\">\n <div class=\"message-card\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h2>MeshBoard Not Found</h2>\n <p>{{ notFoundError() }}</p>\n <p>To create a MeshBoard with a Well-Known Name:</p>\n <ol>\n <li>Go to the <a [routerLink]=\"meshBoardPageLink()\">MeshBoard</a> page</li>\n <li>Open Settings</li>\n <li>Set the \"Well-Known Name\" field</li>\n <li>Save the MeshBoard</li>\n </ol>\n </div>\n </div>\n } @else if (isInitialized()) {\n <!-- Toolbar -->\n <div class=\"meshboard-toolbar\">\n <div class=\"toolbar-left\">\n <h2 class=\"meshboard-title\">{{ config().name }}</h2>\n @if (config().description) {\n <span class=\"meshboard-description\">{{ config().description }}</span>\n }\n </div>\n\n <!-- Time Filter (center) -->\n @if (isTimeFilterEnabled()) {\n <div class=\"toolbar-center\">\n <mm-time-range-picker\n [config]=\"timeFilterConfig()?.pickerConfig ?? {}\"\n [labels]=\"timeRangeLabels()\"\n [initialSelection]=\"initialTimeSelection()\"\n (rangeChange)=\"onTimeRangeChange($event)\"\n (selectionChange)=\"onTimeSelectionChange($event)\">\n </mm-time-range-picker>\n @if (canResetTimeFilter()) {\n <button\n kendoButton\n [svgIcon]=\"undoIcon\"\n (click)=\"resetTimeFilterToDefault()\"\n title=\"Reset to default time filter\"\n fillMode=\"flat\"\n size=\"small\">\n </button>\n }\n </div>\n }\n\n <!-- Entity Selectors -->\n @if (hasEntitySelectors()) {\n @if (isTimeFilterEnabled()) {\n <div class=\"toolbar-separator\"></div>\n }\n <div class=\"toolbar-entity-selectors\">\n <mm-entity-selector-toolbar\n [entitySelectors]=\"entitySelectorsConfig()\"\n (entitySelected)=\"onEntitySelectorSelected($event)\"\n (entityCleared)=\"onEntitySelectorCleared($event)\">\n </mm-entity-selector-toolbar>\n </div>\n }\n\n @if (isTimeFilterEnabled() || hasEntitySelectors()) {\n <div class=\"toolbar-separator\"></div>\n }\n <div class=\"toolbar-right mm-toolbar-actions\">\n @if (!isReadonly()) {\n <!-- Manager Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"gridLayoutIcon\"\n (click)=\"openManager()\"\n title=\"Manage MeshBoards\"\n fillMode=\"flat\">\n </button>\n\n <!-- Settings Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openSettings()\"\n title=\"MeshBoard Settings\"\n fillMode=\"flat\">\n </button>\n }\n\n <!-- Refresh Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"arrowRotateCwIcon\"\n (click)=\"refresh()\"\n title=\"Refresh All Widgets\"\n fillMode=\"flat\">\n </button>\n\n @if (!isReadonly()) {\n <!-- Edit Mode Toggle (icon only) -->\n @if (!isEditMode()) {\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"toggleEditMode()\"\n title=\"Enter Edit Mode\"\n fillMode=\"flat\">\n </button>\n } @else {\n <button\n kendoButton\n [svgIcon]=\"xIcon\"\n (click)=\"cancelEdit()\"\n title=\"Cancel Edit Mode\"\n fillMode=\"flat\">\n </button>\n }\n\n <!-- Add Widget Button (with text) -->\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"openAddWidget()\"\n title=\"Add Widget\"\n themeColor=\"primary\">\n Add Widget\n </button>\n\n <!-- Save Button (with text) -->\n @if (canSave()) {\n <button\n kendoButton\n [svgIcon]=\"saveIcon\"\n (click)=\"save()\"\n [disabled]=\"isSaving()\"\n title=\"Save MeshBoard\"\n themeColor=\"primary\">\n {{ isSaving() ? 'Saving...' : 'Save' }}\n </button>\n }\n }\n </div>\n </div>\n\n <!-- Entity Selector Hint -->\n @if (unselectedToolbarSelectors().length > 0) {\n <div class=\"entity-selector-hint\">\n <kendo-svg-icon [icon]=\"infoCircleIcon\" size=\"medium\"></kendo-svg-icon>\n <span>\n Please select\n @for (selector of unselectedToolbarSelectors(); track selector.id; let last = $last) {\n <strong>{{ selector.label }}</strong>@if (!last) {, }\n }\n to display dashboard data.\n </span>\n </div>\n }\n\n <!-- Variable Resolution Errors -->\n @if (variableResolutionErrors().length > 0) {\n <div class=\"variable-resolution-errors\">\n <strong>Variable resolution errors:</strong>\n @for (error of variableResolutionErrors(); track error.variableName) {\n <span class=\"error-item\">{{ error.variableName }}: {{ error.message }}</span>\n }\n </div>\n }\n\n <!-- Banner Zone: widgets placed in the banner stack above the grid -->\n @if (isEditMode()) {\n <div class=\"zone-label banner-zone-label\">Banner Zone</div>\n }\n @for (widget of bannerWidgets(); track trackByWidgetId($index, widget); let first = $first; let last = $last) {\n <div class=\"banner-zone-item\" [class.banner-edit]=\"isEditMode()\">\n @if (isEditMode()) {\n <div class=\"banner-edit-header\">\n <span class=\"widget-title\">{{ widget.title }}</span>\n <div class=\"widget-actions\">\n <button\n kendoButton\n [svgIcon]=\"arrowUpIcon\"\n (click)=\"moveBannerUp(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n [disabled]=\"first\"\n title=\"Move up\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"arrowDownIcon\"\n (click)=\"moveBannerDown(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n [disabled]=\"last\"\n title=\"Move down\">\n </button>\n @if (supportsConfiguration(widget)) {\n <button\n kendoButton\n [svgIcon]=\"linkIcon\"\n (click)=\"openWidgetConfig(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"config-widget-btn\"\n [class.needs-config]=\"isWidgetUnconfigured(widget)\"\n [title]=\"isWidgetUnconfigured(widget) ? 'Configure data source (required)' : 'Configure data source'\">\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openEditWidgetDialog(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"edit-widget-btn\"\n title=\"Edit widget\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeWidget(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"remove-widget-btn\"\n title=\"Delete widget\">\n </button>\n </div>\n </div>\n }\n @if (getWidgetComponentType(widget.type); as componentType) {\n <ng-container *ngComponentOutlet=\"componentType; inputs: { config: widget }\"></ng-container>\n }\n </div>\n }\n\n <!-- MeshBoard Grid -->\n @if (isEditMode()) {\n <div class=\"zone-label grid-zone-label\">Grid Zone</div>\n }\n @if (hasGridWidgets()) {\n <kendo-tilelayout\n [columns]=\"config().columns\"\n [rowHeight]=\"config().rowHeight\"\n [gap]=\"config().gap\"\n [class.has-banners]=\"bannerWidgets().length > 0 && !isEditMode()\"\n [resizable]=\"isEditMode()\"\n [reorderable]=\"isEditMode()\"\n (reorder)=\"onReorder($event)\"\n (resize)=\"onResize($event)\">\n @for (widget of gridWidgets(); track trackByWidgetId($index, widget)) {\n <kendo-tilelayout-item\n [col]=\"widget.col\"\n [row]=\"widget.row\"\n [colSpan]=\"widget.colSpan\"\n [rowSpan]=\"widget.rowSpan\">\n <kendo-tilelayout-item-header>\n <div class=\"widget-header\"\n [class.unconfigured]=\"isWidgetUnconfigured(widget)\"\n [class.chromeless-header]=\"widget.chromeless && !isEditMode()\">\n @if (!(widget.chromeless && !isEditMode())) {\n <span class=\"widget-title\">{{ widget.title }}</span>\n @if (isWidgetUnconfigured(widget)) {\n <span class=\"unconfigured-badge\" title=\"Widget needs configuration\">!</span>\n }\n }\n @if (isEditMode()) {\n <div class=\"widget-actions\" (pointerdown)=\"$event.stopPropagation()\">\n @if (supportsConfiguration(widget)) {\n <button\n kendoButton\n [svgIcon]=\"linkIcon\"\n (click)=\"openWidgetConfig(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"config-widget-btn\"\n [class.needs-config]=\"isWidgetUnconfigured(widget)\"\n [title]=\"isWidgetUnconfigured(widget) ? 'Configure data source (required)' : 'Configure data source'\">\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openEditWidgetDialog(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"edit-widget-btn\"\n title=\"Edit position\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"copyIcon\"\n (click)=\"duplicateWidget(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"duplicate-widget-btn\"\n title=\"Duplicate widget\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeWidget(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"remove-widget-btn\"\n title=\"Delete widget\">\n </button>\n </div>\n }\n </div>\n </kendo-tilelayout-item-header>\n <kendo-tilelayout-item-body>\n @if (getWidgetComponentType(widget.type); as componentType) {\n <ng-container *ngComponentOutlet=\"componentType; inputs: { config: widget }\"></ng-container>\n } @else {\n <div class=\"widget-error\">\n <p>Unknown widget type: {{ widget.type }}</p>\n </div>\n }\n </kendo-tilelayout-item-body>\n </kendo-tilelayout-item>\n }\n </kendo-tilelayout>\n } @else {\n <div class=\"empty-state\">\n <div class=\"empty-state-content\">\n <kendo-svg-icon [icon]=\"plusIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h3>No Widgets</h3>\n <p>Get started by adding widgets to your MeshBoard.</p>\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"openAddWidget()\"\n themeColor=\"primary\">\n Add Your First Widget\n </button>\n </div>\n </div>\n }\n\n <!-- Edit Widget Dialog -->\n @if (showEditWidgetDialog && editingWidget) {\n <mm-edit-widget-dialog\n [widget]=\"editingWidget\"\n [widgets]=\"config().widgets\"\n [maxColumns]=\"config().columns\"\n [gridService]=\"gridService\"\n (save)=\"onEditWidgetSave($event)\"\n (cancelled)=\"closeEditWidgetDialog()\">\n </mm-edit-widget-dialog>\n }\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;height:100%;width:100%}.meshboard-view{display:flex;flex-direction:column;flex:1;min-height:0;width:100%;background-color:var(--kendo-color-surface, #f5f5f5)}.loading-container{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.entity-selector-hint{display:flex;align-items:center;gap:8px;padding:10px 16px;margin-bottom:8px;background:color-mix(in srgb,var(--kendo-color-info, #2196f3) 10%,var(--kendo-color-surface-alt, #fafafa));border:1px solid color-mix(in srgb,var(--kendo-color-info, #2196f3) 40%,transparent);border-radius:4px;font-size:13px;color:var(--kendo-color-on-app-surface, #424242)}.entity-selector-hint strong{color:var(--kendo-color-info, #2196f3)}.variable-resolution-errors{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:8px 16px;margin-bottom:8px;background:color-mix(in srgb,var(--kendo-color-error, #f44336) 10%,var(--kendo-color-surface-alt, #fafafa));border:1px solid var(--kendo-color-error, #f44336);border-radius:4px;font-size:13px;color:var(--kendo-color-on-app-surface, #424242)}.variable-resolution-errors strong{color:var(--kendo-color-error, #f44336)}.variable-resolution-errors .error-item{padding:2px 8px;background:color-mix(in srgb,var(--kendo-color-error, #f44336) 8%,transparent);border-radius:3px;font-family:Consolas,Monaco,monospace;font-size:12px}.model-unavailable{display:flex;align-items:center;justify-content:center;height:100%;width:100%;padding:2rem;overflow:hidden;box-sizing:border-box}.model-unavailable .message-card{text-align:center;max-width:500px;padding:2rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:8px;overflow:hidden}.model-unavailable .message-card kendo-svg-icon{color:var(--kendo-color-warning, #ff9800);margin-bottom:1rem}.model-unavailable .message-card h2{margin:1rem 0;color:var(--kendo-color-on-app-surface, #424242)}.model-unavailable .message-card p{margin:.5rem 0;color:var(--kendo-color-subtle, #757575);line-height:1.5}.model-unavailable .message-card ol{text-align:left;margin:1rem 0;padding-left:1.5rem;color:var(--kendo-color-subtle, #757575);line-height:1.8}.model-unavailable .message-card ol li{margin:.25rem 0}.model-unavailable .message-card ol a{color:var(--kendo-color-primary, #3f51b5);text-decoration:none}.model-unavailable .message-card ol a:hover{text-decoration:underline}.meshboard-toolbar{display:flex;justify-content:space-between;align-items:flex-end;padding:1rem 1.5rem;background-color:var(--kendo-color-surface-alt, white);border-bottom:1px solid var(--kendo-color-border, #e0e0e0);gap:1rem;flex-shrink:0}.meshboard-toolbar .toolbar-left{display:flex;align-items:baseline;gap:1rem;flex:1;min-width:0;align-self:center}.meshboard-toolbar .toolbar-left .meshboard-title{margin:0;font-size:1.5rem;font-weight:500;color:var(--kendo-color-on-app-surface, #212121);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meshboard-toolbar .toolbar-left .meshboard-description{color:var(--kendo-color-subtle, #757575);font-size:.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meshboard-toolbar .toolbar-center{display:flex;justify-content:center;align-items:flex-end;flex-shrink:0}.meshboard-toolbar .toolbar-center mm-time-range-picker ::ng-deep .time-range-picker{gap:.5rem}.meshboard-toolbar .toolbar-separator{width:1px;align-self:stretch;background-color:var(--kendo-color-border, #e0e0e0);flex-shrink:0}.meshboard-toolbar .toolbar-entity-selectors{display:flex;align-items:flex-end;flex-shrink:0}.meshboard-toolbar .toolbar-right{display:flex;gap:.5rem;align-items:center;flex-shrink:0}.empty-state{display:flex;align-items:flex-start;justify-content:center;height:calc(100% - 80px);width:100%;padding-top:4rem}.empty-state .empty-state-content{text-align:center;padding:2rem}.empty-state .empty-state-content kendo-svg-icon{color:var(--kendo-color-subtle, #9e9e9e);margin-bottom:1rem}.empty-state .empty-state-content h3{margin:1rem 0 .5rem;color:var(--kendo-color-on-app-surface, #424242);font-size:1.25rem;font-weight:500}.empty-state .empty-state-content p{margin:0 0 1.5rem;color:var(--kendo-color-subtle, #757575)}kendo-tilelayout{padding:1.5rem;overflow:auto;flex:1;min-height:0}.widget-header{display:flex;justify-content:space-between;align-items:center;width:100%;gap:.5rem}.widget-header.unconfigured{background-color:#ff98001a}.widget-header .widget-title{flex:1;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.widget-header .unconfigured-badge{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;background-color:var(--kendo-color-warning, #ff9800);color:#fff;font-size:12px;font-weight:700;flex-shrink:0}.widget-header .widget-actions{display:flex;align-items:center;gap:.25rem;flex-shrink:0}.widget-header .config-widget-btn,.widget-header .edit-widget-btn,.widget-header .remove-widget-btn{opacity:.6;transition:opacity .2s}.widget-header .config-widget-btn:hover,.widget-header .edit-widget-btn:hover,.widget-header .remove-widget-btn:hover{opacity:1}.widget-header .config-widget-btn.needs-config{color:var(--kendo-color-warning, #ff9800);opacity:1}.widget-header .remove-widget-btn{color:var(--kendo-color-error, #f44336)}.widget-error{display:flex;align-items:center;justify-content:center;height:100%;padding:1rem;color:var(--kendo-color-error, #f44336);text-align:center}.meshboard-view.edit-mode kendo-tilelayout-item:hover{box-shadow:0 0 0 2px var(--kendo-color-primary, #3f51b5)}@media(max-width:768px){.meshboard-toolbar{flex-direction:column;align-items:stretch;gap:.75rem}.meshboard-toolbar .toolbar-left{flex-direction:column;align-items:flex-start;gap:.25rem}.meshboard-toolbar .toolbar-right{flex-wrap:wrap;justify-content:flex-end}kendo-tilelayout{padding:1rem}}:host ::ng-deep kendo-tilelayout.has-banners{padding-top:0!important}:host ::ng-deep kendo-tilelayout{padding-bottom:1.5rem!important}.widget-header.chromeless-header{display:none}:host ::ng-deep .k-tilelayout-item-header:has(.chromeless-header){display:none}:host ::ng-deep kendo-tilelayout-item:has(.chromeless-header).k-tilelayout-item,:host ::ng-deep kendo-tilelayout-item:has(.chromeless-header).k-card{border:none;box-shadow:none;background:transparent}.zone-label{font-size:.65rem;text-transform:uppercase;letter-spacing:1.5px;opacity:.4;padding:6px 1rem 2px;font-weight:600}.banner-zone-item{margin:0;padding:8px 1rem}.banner-zone-item.banner-edit{border:1px dashed var(--kendo-color-border, rgba(255, 255, 255, .2));border-radius:4px;margin:0 1rem 12px;padding:4px 0}.banner-edit-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px;opacity:.7}.banner-edit-header .widget-title{font-size:.75rem;text-transform:uppercase;letter-spacing:.5px}.banner-edit-header .widget-actions{display:flex;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i3$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: TileLayoutModule }, { kind: "component", type: i5.TileLayoutComponent, selector: "kendo-tilelayout", inputs: ["columns", "columnWidth", "gap", "reorderable", "resizable", "rowHeight", "autoFlow", "navigable"], outputs: ["reorder", "resize"] }, { kind: "component", type: i5.TileLayoutItemBodyComponent, selector: "kendo-tilelayout-item-body" }, { kind: "component", type: i5.TileLayoutItemComponent, selector: "kendo-tilelayout-item", inputs: ["title", "rowSpan", "colSpan", "order", "col", "row", "reorderable", "resizable"] }, { kind: "component", type: i5.TileLayoutItemHeaderComponent, selector: "kendo-tilelayout-item-header" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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: DialogModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: EditWidgetDialogComponent, selector: "mm-edit-widget-dialog", inputs: ["widget", "widgets", "maxColumns", "gridService"], outputs: ["save", "cancelled"] }, { kind: "component", type: TimeRangePickerComponent, selector: "mm-time-range-picker", inputs: ["config", "labels", "initialSelection"], outputs: ["rangeChange", "rangeChangeISO", "selectionChange"] }, { kind: "component", type: EntitySelectorToolbarComponent, selector: "mm-entity-selector-toolbar", inputs: ["entitySelectors"], outputs: ["entitySelected", "entityCleared"] }] });
|
|
26999
29236
|
}
|
|
27000
29237
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MeshBoardViewComponent, decorators: [{
|
|
27001
29238
|
type: Component,
|
|
@@ -27009,8 +29246,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
27009
29246
|
EditWidgetDialogComponent,
|
|
27010
29247
|
TimeRangePickerComponent,
|
|
27011
29248
|
EntitySelectorToolbarComponent
|
|
27012
|
-
], hostDirectives: [UnsavedChangesDirective], providers: [{ provide: HAS_UNSAVED_CHANGES, useExisting: MeshBoardViewComponent }], template: "<div class=\"meshboard-view\">\n @if (isLoading()) {\n <div class=\"loading-container\">\n <div class=\"k-loading-mask\">\n <span class=\"k-loading-text\">Loading MeshBoard...</span>\n <div class=\"k-loading-image\"></div>\n <div class=\"k-loading-color\"></div>\n </div>\n </div>\n } @else if (isModelAvailable() === false) {\n <div class=\"model-unavailable\">\n <div class=\"message-card\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h2>MeshBoard Not Available</h2>\n <p>The MeshBoard feature requires the CK model 'System.UI' version 1.0.1 or higher.</p>\n <p>Please install the required model in your tenant to use this feature.</p>\n </div>\n </div>\n } @else if (notFoundError()) {\n <div class=\"model-unavailable\">\n <div class=\"message-card\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h2>MeshBoard Not Found</h2>\n <p>{{ notFoundError() }}</p>\n <p>To create a MeshBoard with a Well-Known Name:</p>\n <ol>\n <li>Go to the <a [routerLink]=\"meshBoardPageLink()\">MeshBoard</a> page</li>\n <li>Open Settings</li>\n <li>Set the \"Well-Known Name\" field</li>\n <li>Save the MeshBoard</li>\n </ol>\n </div>\n </div>\n } @else if (isInitialized()) {\n <!-- Toolbar -->\n <div class=\"meshboard-toolbar\">\n <div class=\"toolbar-left\">\n <h2 class=\"meshboard-title\">{{ config().name }}</h2>\n @if (config().description) {\n <span class=\"meshboard-description\">{{ config().description }}</span>\n }\n </div>\n\n <!-- Time Filter (center) -->\n @if (isTimeFilterEnabled()) {\n <div class=\"toolbar-center\">\n <mm-time-range-picker\n [config]=\"timeFilterConfig()?.pickerConfig ?? {}\"\n [labels]=\"timeRangeLabels()\"\n [initialSelection]=\"initialTimeSelection()\"\n (rangeChange)=\"onTimeRangeChange($event)\"\n (selectionChange)=\"onTimeSelectionChange($event)\">\n </mm-time-range-picker>\n @if (canResetTimeFilter()) {\n <button\n kendoButton\n [svgIcon]=\"undoIcon\"\n (click)=\"resetTimeFilterToDefault()\"\n title=\"Reset to default time filter\"\n fillMode=\"flat\"\n size=\"small\">\n </button>\n }\n </div>\n }\n\n <!-- Entity Selectors -->\n @if (hasEntitySelectors()) {\n @if (isTimeFilterEnabled()) {\n <div class=\"toolbar-separator\"></div>\n }\n <div class=\"toolbar-entity-selectors\">\n <mm-entity-selector-toolbar\n [entitySelectors]=\"entitySelectorsConfig()\"\n (entitySelected)=\"onEntitySelectorSelected($event)\"\n (entityCleared)=\"onEntitySelectorCleared($event)\">\n </mm-entity-selector-toolbar>\n </div>\n }\n\n @if (isTimeFilterEnabled() || hasEntitySelectors()) {\n <div class=\"toolbar-separator\"></div>\n }\n <div class=\"toolbar-right mm-toolbar-actions\">\n @if (!isReadonly()) {\n <!-- Manager Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"gridLayoutIcon\"\n (click)=\"openManager()\"\n title=\"Manage MeshBoards\"\n fillMode=\"flat\">\n </button>\n\n <!-- Settings Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openSettings()\"\n title=\"MeshBoard Settings\"\n fillMode=\"flat\">\n </button>\n }\n\n <!-- Refresh Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"arrowRotateCwIcon\"\n (click)=\"refresh()\"\n title=\"Refresh All Widgets\"\n fillMode=\"flat\">\n </button>\n\n @if (!isReadonly()) {\n <!-- Edit Mode Toggle (icon only) -->\n @if (!isEditMode()) {\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"toggleEditMode()\"\n title=\"Enter Edit Mode\"\n fillMode=\"flat\">\n </button>\n } @else {\n <button\n kendoButton\n [svgIcon]=\"xIcon\"\n (click)=\"cancelEdit()\"\n title=\"Cancel Edit Mode\"\n fillMode=\"flat\">\n </button>\n }\n\n <!-- Add Widget Button (with text) -->\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"openAddWidget()\"\n title=\"Add Widget\"\n themeColor=\"primary\">\n Add Widget\n </button>\n\n <!-- Save Button (with text) -->\n @if (canSave()) {\n <button\n kendoButton\n [svgIcon]=\"saveIcon\"\n (click)=\"save()\"\n [disabled]=\"isSaving()\"\n title=\"Save MeshBoard\"\n themeColor=\"primary\">\n {{ isSaving() ? 'Saving...' : 'Save' }}\n </button>\n }\n }\n </div>\n </div>\n\n <!-- Entity Selector Hint -->\n @if (unselectedToolbarSelectors().length > 0) {\n <div class=\"entity-selector-hint\">\n <kendo-svg-icon [icon]=\"infoCircleIcon\" size=\"medium\"></kendo-svg-icon>\n <span>\n Please select\n @for (selector of unselectedToolbarSelectors(); track selector.id; let last = $last) {\n <strong>{{ selector.label }}</strong>@if (!last) {, }\n }\n to display dashboard data.\n </span>\n </div>\n }\n\n <!-- Variable Resolution Errors -->\n @if (variableResolutionErrors().length > 0) {\n <div class=\"variable-resolution-errors\">\n <strong>Variable resolution errors:</strong>\n @for (error of variableResolutionErrors(); track error.variableName) {\n <span class=\"error-item\">{{ error.variableName }}: {{ error.message }}</span>\n }\n </div>\n }\n\n <!-- MeshBoard Grid -->\n @if (hasWidgets()) {\n <kendo-tilelayout\n [columns]=\"config().columns\"\n [rowHeight]=\"config().rowHeight\"\n [gap]=\"config().gap\"\n [resizable]=\"isEditMode()\"\n [reorderable]=\"isEditMode()\"\n (reorder)=\"onReorder($event)\"\n (resize)=\"onResize($event)\">\n @for (widget of config().widgets; track trackByWidgetId($index, widget)) {\n <kendo-tilelayout-item\n [col]=\"widget.col\"\n [row]=\"widget.row\"\n [colSpan]=\"widget.colSpan\"\n [rowSpan]=\"widget.rowSpan\">\n <kendo-tilelayout-item-header>\n <div class=\"widget-header\" [class.unconfigured]=\"isWidgetUnconfigured(widget)\">\n <span class=\"widget-title\">{{ widget.title }}</span>\n @if (isWidgetUnconfigured(widget) && !isEditMode()) {\n <span class=\"unconfigured-badge\" title=\"Widget needs configuration\">!</span>\n }\n @if (isEditMode()) {\n <div class=\"widget-actions\" (pointerdown)=\"$event.stopPropagation()\">\n @if (supportsConfiguration(widget)) {\n <button\n kendoButton\n [svgIcon]=\"linkIcon\"\n (click)=\"openWidgetConfig(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"config-widget-btn\"\n [class.needs-config]=\"isWidgetUnconfigured(widget)\"\n [title]=\"isWidgetUnconfigured(widget) ? 'Configure data source (required)' : 'Configure data source'\">\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openEditWidgetDialog(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"edit-widget-btn\"\n title=\"Edit position\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"copyIcon\"\n (click)=\"duplicateWidget(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"duplicate-widget-btn\"\n title=\"Duplicate widget\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeWidget(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"remove-widget-btn\"\n title=\"Delete widget\">\n </button>\n </div>\n }\n </div>\n </kendo-tilelayout-item-header>\n <kendo-tilelayout-item-body>\n @if (getWidgetComponentType(widget.type); as componentType) {\n <ng-container *ngComponentOutlet=\"componentType; inputs: { config: widget }\"></ng-container>\n } @else {\n <div class=\"widget-error\">\n <p>Unknown widget type: {{ widget.type }}</p>\n </div>\n }\n </kendo-tilelayout-item-body>\n </kendo-tilelayout-item>\n }\n </kendo-tilelayout>\n } @else {\n <div class=\"empty-state\">\n <div class=\"empty-state-content\">\n <kendo-svg-icon [icon]=\"plusIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h3>No Widgets</h3>\n <p>Get started by adding widgets to your MeshBoard.</p>\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"openAddWidget()\"\n themeColor=\"primary\">\n Add Your First Widget\n </button>\n </div>\n </div>\n }\n\n <!-- Edit Widget Dialog -->\n @if (showEditWidgetDialog && editingWidget) {\n <mm-edit-widget-dialog\n [widget]=\"editingWidget\"\n [widgets]=\"config().widgets\"\n [maxColumns]=\"config().columns\"\n [gridService]=\"gridService\"\n (save)=\"onEditWidgetSave($event)\"\n (cancelled)=\"closeEditWidgetDialog()\">\n </mm-edit-widget-dialog>\n }\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;height:100%;width:100%}.meshboard-view{display:flex;flex-direction:column;flex:1;min-height:0;width:100%;background-color:var(--kendo-color-surface, #f5f5f5)}.loading-container{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.entity-selector-hint{display:flex;align-items:center;gap:8px;padding:10px 16px;margin-bottom:8px;background:color-mix(in srgb,var(--kendo-color-info, #2196f3) 10%,var(--kendo-color-surface-alt, #fafafa));border:1px solid color-mix(in srgb,var(--kendo-color-info, #2196f3) 40%,transparent);border-radius:4px;font-size:13px;color:var(--kendo-color-on-app-surface, #424242)}.entity-selector-hint strong{color:var(--kendo-color-info, #2196f3)}.variable-resolution-errors{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:8px 16px;margin-bottom:8px;background:color-mix(in srgb,var(--kendo-color-error, #f44336) 10%,var(--kendo-color-surface-alt, #fafafa));border:1px solid var(--kendo-color-error, #f44336);border-radius:4px;font-size:13px;color:var(--kendo-color-on-app-surface, #424242)}.variable-resolution-errors strong{color:var(--kendo-color-error, #f44336)}.variable-resolution-errors .error-item{padding:2px 8px;background:color-mix(in srgb,var(--kendo-color-error, #f44336) 8%,transparent);border-radius:3px;font-family:Consolas,Monaco,monospace;font-size:12px}.model-unavailable{display:flex;align-items:center;justify-content:center;height:100%;width:100%;padding:2rem;overflow:hidden;box-sizing:border-box}.model-unavailable .message-card{text-align:center;max-width:500px;padding:2rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:8px;overflow:hidden}.model-unavailable .message-card kendo-svg-icon{color:var(--kendo-color-warning, #ff9800);margin-bottom:1rem}.model-unavailable .message-card h2{margin:1rem 0;color:var(--kendo-color-on-app-surface, #424242)}.model-unavailable .message-card p{margin:.5rem 0;color:var(--kendo-color-subtle, #757575);line-height:1.5}.model-unavailable .message-card ol{text-align:left;margin:1rem 0;padding-left:1.5rem;color:var(--kendo-color-subtle, #757575);line-height:1.8}.model-unavailable .message-card ol li{margin:.25rem 0}.model-unavailable .message-card ol a{color:var(--kendo-color-primary, #3f51b5);text-decoration:none}.model-unavailable .message-card ol a:hover{text-decoration:underline}.meshboard-toolbar{display:flex;justify-content:space-between;align-items:flex-end;padding:1rem 1.5rem;background-color:var(--kendo-color-surface-alt, white);border-bottom:1px solid var(--kendo-color-border, #e0e0e0);gap:1rem;flex-shrink:0}.meshboard-toolbar .toolbar-left{display:flex;align-items:baseline;gap:1rem;flex:1;min-width:0;align-self:center}.meshboard-toolbar .toolbar-left .meshboard-title{margin:0;font-size:1.5rem;font-weight:500;color:var(--kendo-color-on-app-surface, #212121);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meshboard-toolbar .toolbar-left .meshboard-description{color:var(--kendo-color-subtle, #757575);font-size:.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meshboard-toolbar .toolbar-center{display:flex;justify-content:center;align-items:flex-end;flex-shrink:0}.meshboard-toolbar .toolbar-center mm-time-range-picker ::ng-deep .time-range-picker{gap:.5rem}.meshboard-toolbar .toolbar-separator{width:1px;align-self:stretch;background-color:var(--kendo-color-border, #e0e0e0);flex-shrink:0}.meshboard-toolbar .toolbar-entity-selectors{display:flex;align-items:flex-end;flex-shrink:0}.meshboard-toolbar .toolbar-right{display:flex;gap:.5rem;align-items:center;flex-shrink:0}.empty-state{display:flex;align-items:flex-start;justify-content:center;height:calc(100% - 80px);width:100%;padding-top:4rem}.empty-state .empty-state-content{text-align:center;padding:2rem}.empty-state .empty-state-content kendo-svg-icon{color:var(--kendo-color-subtle, #9e9e9e);margin-bottom:1rem}.empty-state .empty-state-content h3{margin:1rem 0 .5rem;color:var(--kendo-color-on-app-surface, #424242);font-size:1.25rem;font-weight:500}.empty-state .empty-state-content p{margin:0 0 1.5rem;color:var(--kendo-color-subtle, #757575)}kendo-tilelayout{padding:1.5rem;overflow:auto;flex:1;min-height:0}.widget-header{display:flex;justify-content:space-between;align-items:center;width:100%;gap:.5rem}.widget-header.unconfigured{background-color:#ff98001a}.widget-header .widget-title{flex:1;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.widget-header .unconfigured-badge{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;background-color:var(--kendo-color-warning, #ff9800);color:#fff;font-size:12px;font-weight:700;flex-shrink:0}.widget-header .widget-actions{display:flex;align-items:center;gap:.25rem;flex-shrink:0}.widget-header .config-widget-btn,.widget-header .edit-widget-btn,.widget-header .remove-widget-btn{opacity:.6;transition:opacity .2s}.widget-header .config-widget-btn:hover,.widget-header .edit-widget-btn:hover,.widget-header .remove-widget-btn:hover{opacity:1}.widget-header .config-widget-btn.needs-config{color:var(--kendo-color-warning, #ff9800);opacity:1}.widget-header .remove-widget-btn{color:var(--kendo-color-error, #f44336)}.widget-error{display:flex;align-items:center;justify-content:center;height:100%;padding:1rem;color:var(--kendo-color-error, #f44336);text-align:center}.meshboard-view.edit-mode kendo-tilelayout-item:hover{box-shadow:0 0 0 2px var(--kendo-color-primary, #3f51b5)}@media(max-width:768px){.meshboard-toolbar{flex-direction:column;align-items:stretch;gap:.75rem}.meshboard-toolbar .toolbar-left{flex-direction:column;align-items:flex-start;gap:.25rem}.meshboard-toolbar .toolbar-right{flex-wrap:wrap;justify-content:flex-end}kendo-tilelayout{padding:1rem}}\n"] }]
|
|
27013
|
-
}], ctorParameters: () => []
|
|
29249
|
+
], hostDirectives: [UnsavedChangesDirective], providers: [{ provide: HAS_UNSAVED_CHANGES, useExisting: MeshBoardViewComponent }], template: "<div class=\"meshboard-view\">\n @if (isLoading()) {\n <div class=\"loading-container\">\n <div class=\"k-loading-mask\">\n <span class=\"k-loading-text\">Loading MeshBoard...</span>\n <div class=\"k-loading-image\"></div>\n <div class=\"k-loading-color\"></div>\n </div>\n </div>\n } @else if (isModelAvailable() === false) {\n <div class=\"model-unavailable\">\n <div class=\"message-card\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h2>MeshBoard Not Available</h2>\n <p>The MeshBoard feature requires the CK model 'System.UI' version 1.0.1 or higher.</p>\n <p>Please install the required model in your tenant to use this feature.</p>\n </div>\n </div>\n } @else if (notFoundError()) {\n <div class=\"model-unavailable\">\n <div class=\"message-card\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h2>MeshBoard Not Found</h2>\n <p>{{ notFoundError() }}</p>\n <p>To create a MeshBoard with a Well-Known Name:</p>\n <ol>\n <li>Go to the <a [routerLink]=\"meshBoardPageLink()\">MeshBoard</a> page</li>\n <li>Open Settings</li>\n <li>Set the \"Well-Known Name\" field</li>\n <li>Save the MeshBoard</li>\n </ol>\n </div>\n </div>\n } @else if (isInitialized()) {\n <!-- Toolbar -->\n <div class=\"meshboard-toolbar\">\n <div class=\"toolbar-left\">\n <h2 class=\"meshboard-title\">{{ config().name }}</h2>\n @if (config().description) {\n <span class=\"meshboard-description\">{{ config().description }}</span>\n }\n </div>\n\n <!-- Time Filter (center) -->\n @if (isTimeFilterEnabled()) {\n <div class=\"toolbar-center\">\n <mm-time-range-picker\n [config]=\"timeFilterConfig()?.pickerConfig ?? {}\"\n [labels]=\"timeRangeLabels()\"\n [initialSelection]=\"initialTimeSelection()\"\n (rangeChange)=\"onTimeRangeChange($event)\"\n (selectionChange)=\"onTimeSelectionChange($event)\">\n </mm-time-range-picker>\n @if (canResetTimeFilter()) {\n <button\n kendoButton\n [svgIcon]=\"undoIcon\"\n (click)=\"resetTimeFilterToDefault()\"\n title=\"Reset to default time filter\"\n fillMode=\"flat\"\n size=\"small\">\n </button>\n }\n </div>\n }\n\n <!-- Entity Selectors -->\n @if (hasEntitySelectors()) {\n @if (isTimeFilterEnabled()) {\n <div class=\"toolbar-separator\"></div>\n }\n <div class=\"toolbar-entity-selectors\">\n <mm-entity-selector-toolbar\n [entitySelectors]=\"entitySelectorsConfig()\"\n (entitySelected)=\"onEntitySelectorSelected($event)\"\n (entityCleared)=\"onEntitySelectorCleared($event)\">\n </mm-entity-selector-toolbar>\n </div>\n }\n\n @if (isTimeFilterEnabled() || hasEntitySelectors()) {\n <div class=\"toolbar-separator\"></div>\n }\n <div class=\"toolbar-right mm-toolbar-actions\">\n @if (!isReadonly()) {\n <!-- Manager Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"gridLayoutIcon\"\n (click)=\"openManager()\"\n title=\"Manage MeshBoards\"\n fillMode=\"flat\">\n </button>\n\n <!-- Settings Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openSettings()\"\n title=\"MeshBoard Settings\"\n fillMode=\"flat\">\n </button>\n }\n\n <!-- Refresh Button (icon only) -->\n <button\n kendoButton\n [svgIcon]=\"arrowRotateCwIcon\"\n (click)=\"refresh()\"\n title=\"Refresh All Widgets\"\n fillMode=\"flat\">\n </button>\n\n @if (!isReadonly()) {\n <!-- Edit Mode Toggle (icon only) -->\n @if (!isEditMode()) {\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"toggleEditMode()\"\n title=\"Enter Edit Mode\"\n fillMode=\"flat\">\n </button>\n } @else {\n <button\n kendoButton\n [svgIcon]=\"xIcon\"\n (click)=\"cancelEdit()\"\n title=\"Cancel Edit Mode\"\n fillMode=\"flat\">\n </button>\n }\n\n <!-- Add Widget Button (with text) -->\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"openAddWidget()\"\n title=\"Add Widget\"\n themeColor=\"primary\">\n Add Widget\n </button>\n\n <!-- Save Button (with text) -->\n @if (canSave()) {\n <button\n kendoButton\n [svgIcon]=\"saveIcon\"\n (click)=\"save()\"\n [disabled]=\"isSaving()\"\n title=\"Save MeshBoard\"\n themeColor=\"primary\">\n {{ isSaving() ? 'Saving...' : 'Save' }}\n </button>\n }\n }\n </div>\n </div>\n\n <!-- Entity Selector Hint -->\n @if (unselectedToolbarSelectors().length > 0) {\n <div class=\"entity-selector-hint\">\n <kendo-svg-icon [icon]=\"infoCircleIcon\" size=\"medium\"></kendo-svg-icon>\n <span>\n Please select\n @for (selector of unselectedToolbarSelectors(); track selector.id; let last = $last) {\n <strong>{{ selector.label }}</strong>@if (!last) {, }\n }\n to display dashboard data.\n </span>\n </div>\n }\n\n <!-- Variable Resolution Errors -->\n @if (variableResolutionErrors().length > 0) {\n <div class=\"variable-resolution-errors\">\n <strong>Variable resolution errors:</strong>\n @for (error of variableResolutionErrors(); track error.variableName) {\n <span class=\"error-item\">{{ error.variableName }}: {{ error.message }}</span>\n }\n </div>\n }\n\n <!-- Banner Zone: widgets placed in the banner stack above the grid -->\n @if (isEditMode()) {\n <div class=\"zone-label banner-zone-label\">Banner Zone</div>\n }\n @for (widget of bannerWidgets(); track trackByWidgetId($index, widget); let first = $first; let last = $last) {\n <div class=\"banner-zone-item\" [class.banner-edit]=\"isEditMode()\">\n @if (isEditMode()) {\n <div class=\"banner-edit-header\">\n <span class=\"widget-title\">{{ widget.title }}</span>\n <div class=\"widget-actions\">\n <button\n kendoButton\n [svgIcon]=\"arrowUpIcon\"\n (click)=\"moveBannerUp(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n [disabled]=\"first\"\n title=\"Move up\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"arrowDownIcon\"\n (click)=\"moveBannerDown(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n [disabled]=\"last\"\n title=\"Move down\">\n </button>\n @if (supportsConfiguration(widget)) {\n <button\n kendoButton\n [svgIcon]=\"linkIcon\"\n (click)=\"openWidgetConfig(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"config-widget-btn\"\n [class.needs-config]=\"isWidgetUnconfigured(widget)\"\n [title]=\"isWidgetUnconfigured(widget) ? 'Configure data source (required)' : 'Configure data source'\">\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openEditWidgetDialog(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"edit-widget-btn\"\n title=\"Edit widget\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeWidget(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"remove-widget-btn\"\n title=\"Delete widget\">\n </button>\n </div>\n </div>\n }\n @if (getWidgetComponentType(widget.type); as componentType) {\n <ng-container *ngComponentOutlet=\"componentType; inputs: { config: widget }\"></ng-container>\n }\n </div>\n }\n\n <!-- MeshBoard Grid -->\n @if (isEditMode()) {\n <div class=\"zone-label grid-zone-label\">Grid Zone</div>\n }\n @if (hasGridWidgets()) {\n <kendo-tilelayout\n [columns]=\"config().columns\"\n [rowHeight]=\"config().rowHeight\"\n [gap]=\"config().gap\"\n [class.has-banners]=\"bannerWidgets().length > 0 && !isEditMode()\"\n [resizable]=\"isEditMode()\"\n [reorderable]=\"isEditMode()\"\n (reorder)=\"onReorder($event)\"\n (resize)=\"onResize($event)\">\n @for (widget of gridWidgets(); track trackByWidgetId($index, widget)) {\n <kendo-tilelayout-item\n [col]=\"widget.col\"\n [row]=\"widget.row\"\n [colSpan]=\"widget.colSpan\"\n [rowSpan]=\"widget.rowSpan\">\n <kendo-tilelayout-item-header>\n <div class=\"widget-header\"\n [class.unconfigured]=\"isWidgetUnconfigured(widget)\"\n [class.chromeless-header]=\"widget.chromeless && !isEditMode()\">\n @if (!(widget.chromeless && !isEditMode())) {\n <span class=\"widget-title\">{{ widget.title }}</span>\n @if (isWidgetUnconfigured(widget)) {\n <span class=\"unconfigured-badge\" title=\"Widget needs configuration\">!</span>\n }\n }\n @if (isEditMode()) {\n <div class=\"widget-actions\" (pointerdown)=\"$event.stopPropagation()\">\n @if (supportsConfiguration(widget)) {\n <button\n kendoButton\n [svgIcon]=\"linkIcon\"\n (click)=\"openWidgetConfig(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"config-widget-btn\"\n [class.needs-config]=\"isWidgetUnconfigured(widget)\"\n [title]=\"isWidgetUnconfigured(widget) ? 'Configure data source (required)' : 'Configure data source'\">\n </button>\n }\n <button\n kendoButton\n [svgIcon]=\"gearIcon\"\n (click)=\"openEditWidgetDialog(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"edit-widget-btn\"\n title=\"Edit position\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"copyIcon\"\n (click)=\"duplicateWidget(widget)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"duplicate-widget-btn\"\n title=\"Duplicate widget\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeWidget(widget.id)\"\n fillMode=\"flat\"\n size=\"small\"\n class=\"remove-widget-btn\"\n title=\"Delete widget\">\n </button>\n </div>\n }\n </div>\n </kendo-tilelayout-item-header>\n <kendo-tilelayout-item-body>\n @if (getWidgetComponentType(widget.type); as componentType) {\n <ng-container *ngComponentOutlet=\"componentType; inputs: { config: widget }\"></ng-container>\n } @else {\n <div class=\"widget-error\">\n <p>Unknown widget type: {{ widget.type }}</p>\n </div>\n }\n </kendo-tilelayout-item-body>\n </kendo-tilelayout-item>\n }\n </kendo-tilelayout>\n } @else {\n <div class=\"empty-state\">\n <div class=\"empty-state-content\">\n <kendo-svg-icon [icon]=\"plusIcon\" size=\"xlarge\"></kendo-svg-icon>\n <h3>No Widgets</h3>\n <p>Get started by adding widgets to your MeshBoard.</p>\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"openAddWidget()\"\n themeColor=\"primary\">\n Add Your First Widget\n </button>\n </div>\n </div>\n }\n\n <!-- Edit Widget Dialog -->\n @if (showEditWidgetDialog && editingWidget) {\n <mm-edit-widget-dialog\n [widget]=\"editingWidget\"\n [widgets]=\"config().widgets\"\n [maxColumns]=\"config().columns\"\n [gridService]=\"gridService\"\n (save)=\"onEditWidgetSave($event)\"\n (cancelled)=\"closeEditWidgetDialog()\">\n </mm-edit-widget-dialog>\n }\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;height:100%;width:100%}.meshboard-view{display:flex;flex-direction:column;flex:1;min-height:0;width:100%;background-color:var(--kendo-color-surface, #f5f5f5)}.loading-container{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.entity-selector-hint{display:flex;align-items:center;gap:8px;padding:10px 16px;margin-bottom:8px;background:color-mix(in srgb,var(--kendo-color-info, #2196f3) 10%,var(--kendo-color-surface-alt, #fafafa));border:1px solid color-mix(in srgb,var(--kendo-color-info, #2196f3) 40%,transparent);border-radius:4px;font-size:13px;color:var(--kendo-color-on-app-surface, #424242)}.entity-selector-hint strong{color:var(--kendo-color-info, #2196f3)}.variable-resolution-errors{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:8px 16px;margin-bottom:8px;background:color-mix(in srgb,var(--kendo-color-error, #f44336) 10%,var(--kendo-color-surface-alt, #fafafa));border:1px solid var(--kendo-color-error, #f44336);border-radius:4px;font-size:13px;color:var(--kendo-color-on-app-surface, #424242)}.variable-resolution-errors strong{color:var(--kendo-color-error, #f44336)}.variable-resolution-errors .error-item{padding:2px 8px;background:color-mix(in srgb,var(--kendo-color-error, #f44336) 8%,transparent);border-radius:3px;font-family:Consolas,Monaco,monospace;font-size:12px}.model-unavailable{display:flex;align-items:center;justify-content:center;height:100%;width:100%;padding:2rem;overflow:hidden;box-sizing:border-box}.model-unavailable .message-card{text-align:center;max-width:500px;padding:2rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:8px;overflow:hidden}.model-unavailable .message-card kendo-svg-icon{color:var(--kendo-color-warning, #ff9800);margin-bottom:1rem}.model-unavailable .message-card h2{margin:1rem 0;color:var(--kendo-color-on-app-surface, #424242)}.model-unavailable .message-card p{margin:.5rem 0;color:var(--kendo-color-subtle, #757575);line-height:1.5}.model-unavailable .message-card ol{text-align:left;margin:1rem 0;padding-left:1.5rem;color:var(--kendo-color-subtle, #757575);line-height:1.8}.model-unavailable .message-card ol li{margin:.25rem 0}.model-unavailable .message-card ol a{color:var(--kendo-color-primary, #3f51b5);text-decoration:none}.model-unavailable .message-card ol a:hover{text-decoration:underline}.meshboard-toolbar{display:flex;justify-content:space-between;align-items:flex-end;padding:1rem 1.5rem;background-color:var(--kendo-color-surface-alt, white);border-bottom:1px solid var(--kendo-color-border, #e0e0e0);gap:1rem;flex-shrink:0}.meshboard-toolbar .toolbar-left{display:flex;align-items:baseline;gap:1rem;flex:1;min-width:0;align-self:center}.meshboard-toolbar .toolbar-left .meshboard-title{margin:0;font-size:1.5rem;font-weight:500;color:var(--kendo-color-on-app-surface, #212121);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meshboard-toolbar .toolbar-left .meshboard-description{color:var(--kendo-color-subtle, #757575);font-size:.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meshboard-toolbar .toolbar-center{display:flex;justify-content:center;align-items:flex-end;flex-shrink:0}.meshboard-toolbar .toolbar-center mm-time-range-picker ::ng-deep .time-range-picker{gap:.5rem}.meshboard-toolbar .toolbar-separator{width:1px;align-self:stretch;background-color:var(--kendo-color-border, #e0e0e0);flex-shrink:0}.meshboard-toolbar .toolbar-entity-selectors{display:flex;align-items:flex-end;flex-shrink:0}.meshboard-toolbar .toolbar-right{display:flex;gap:.5rem;align-items:center;flex-shrink:0}.empty-state{display:flex;align-items:flex-start;justify-content:center;height:calc(100% - 80px);width:100%;padding-top:4rem}.empty-state .empty-state-content{text-align:center;padding:2rem}.empty-state .empty-state-content kendo-svg-icon{color:var(--kendo-color-subtle, #9e9e9e);margin-bottom:1rem}.empty-state .empty-state-content h3{margin:1rem 0 .5rem;color:var(--kendo-color-on-app-surface, #424242);font-size:1.25rem;font-weight:500}.empty-state .empty-state-content p{margin:0 0 1.5rem;color:var(--kendo-color-subtle, #757575)}kendo-tilelayout{padding:1.5rem;overflow:auto;flex:1;min-height:0}.widget-header{display:flex;justify-content:space-between;align-items:center;width:100%;gap:.5rem}.widget-header.unconfigured{background-color:#ff98001a}.widget-header .widget-title{flex:1;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.widget-header .unconfigured-badge{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;background-color:var(--kendo-color-warning, #ff9800);color:#fff;font-size:12px;font-weight:700;flex-shrink:0}.widget-header .widget-actions{display:flex;align-items:center;gap:.25rem;flex-shrink:0}.widget-header .config-widget-btn,.widget-header .edit-widget-btn,.widget-header .remove-widget-btn{opacity:.6;transition:opacity .2s}.widget-header .config-widget-btn:hover,.widget-header .edit-widget-btn:hover,.widget-header .remove-widget-btn:hover{opacity:1}.widget-header .config-widget-btn.needs-config{color:var(--kendo-color-warning, #ff9800);opacity:1}.widget-header .remove-widget-btn{color:var(--kendo-color-error, #f44336)}.widget-error{display:flex;align-items:center;justify-content:center;height:100%;padding:1rem;color:var(--kendo-color-error, #f44336);text-align:center}.meshboard-view.edit-mode kendo-tilelayout-item:hover{box-shadow:0 0 0 2px var(--kendo-color-primary, #3f51b5)}@media(max-width:768px){.meshboard-toolbar{flex-direction:column;align-items:stretch;gap:.75rem}.meshboard-toolbar .toolbar-left{flex-direction:column;align-items:flex-start;gap:.25rem}.meshboard-toolbar .toolbar-right{flex-wrap:wrap;justify-content:flex-end}kendo-tilelayout{padding:1rem}}:host ::ng-deep kendo-tilelayout.has-banners{padding-top:0!important}:host ::ng-deep kendo-tilelayout{padding-bottom:1.5rem!important}.widget-header.chromeless-header{display:none}:host ::ng-deep .k-tilelayout-item-header:has(.chromeless-header){display:none}:host ::ng-deep kendo-tilelayout-item:has(.chromeless-header).k-tilelayout-item,:host ::ng-deep kendo-tilelayout-item:has(.chromeless-header).k-card{border:none;box-shadow:none;background:transparent}.zone-label{font-size:.65rem;text-transform:uppercase;letter-spacing:1.5px;opacity:.4;padding:6px 1rem 2px;font-weight:600}.banner-zone-item{margin:0;padding:8px 1rem}.banner-zone-item.banner-edit{border:1px dashed var(--kendo-color-border, rgba(255, 255, 255, .2));border-radius:4px;margin:0 1rem 12px;padding:4px 0}.banner-edit-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px;opacity:.7}.banner-edit-header .widget-title{font-size:.75rem;text-transform:uppercase;letter-spacing:.5px}.banner-edit-header .widget-actions{display:flex;gap:2px}\n"] }]
|
|
29250
|
+
}], ctorParameters: () => [], propDecorators: { tileLayout: [{
|
|
29251
|
+
type: ViewChild,
|
|
29252
|
+
args: [TileLayoutComponent]
|
|
29253
|
+
}] } });
|
|
27014
29254
|
|
|
27015
29255
|
/*
|
|
27016
29256
|
* Public API Surface of @meshmakers/octo-meshboard
|
|
@@ -27021,5 +29261,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
27021
29261
|
* Generated bundle index. Do not edit.
|
|
27022
29262
|
*/
|
|
27023
29263
|
|
|
27024
|
-
export { AddWidgetDialogComponent, AssociationsConfigDialogComponent, BarChartConfigDialogComponent, BarChartWidgetComponent, DashboardDataService, DashboardGridService, EditModeStateService, EditWidgetDialogComponent, EntityAssociationsWidgetComponent, EntityCardConfigDialogComponent, EntityCardWidgetComponent, EntityDetailDialogComponent, EntitySelectorEditorComponent, EntitySelectorToolbarComponent, GaugeConfigDialogComponent, GaugeWidgetComponent, HeatmapConfigDialogComponent, HeatmapWidgetComponent, KpiConfigDialogComponent, KpiWidgetComponent, LineChartConfigDialogComponent, LineChartWidgetComponent, MESHBOARD_OPTIONS, MESHBOARD_TENANT_ID_PROVIDER, MarkdownConfigDialogComponent, MarkdownWidgetComponent, MeshBoardDataService, MeshBoardGridService, MeshBoardManagerDialogComponent, MeshBoardPersistenceService, MeshBoardSettingsDialogComponent, MeshBoardSettingsResult, MeshBoardStateService, MeshBoardViewComponent, PieChartConfigDialogComponent, PieChartWidgetComponent, QuerySelectorComponent, RuntimeEntitySelectorComponent, ServiceHealthConfigDialogComponent, ServiceHealthWidgetComponent, StatsGridConfigDialogComponent, StatsGridWidgetComponent, StatusIndicatorConfigDialogComponent, StatusIndicatorWidgetComponent, TableConfigDialogComponent, TableWidgetComponent, TableWidgetDataSourceDirective, WidgetFactoryService, WidgetGroupComponent, WidgetGroupConfigDialogComponent, WidgetNotConfiguredComponent, WidgetRegistryService, provideDefaultWidgets, provideMeshBoard, provideProcessWidget, provideWidgetRegistrations, registerDefaultWidgets, registerProcessWidget };
|
|
29264
|
+
export { AddWidgetDialogComponent, AiInsightsConfigDialogComponent, AiInsightsService, AiInsightsWidgetComponent, AlertBannerConfigDialogComponent, AlertBannerWidgetComponent, AlertListConfigDialogComponent, AlertListWidgetComponent, AssociationsConfigDialogComponent, BarChartConfigDialogComponent, BarChartWidgetComponent, DashboardDataService, DashboardGridService, EditModeStateService, EditWidgetDialogComponent, EntityAssociationsWidgetComponent, EntityCardConfigDialogComponent, EntityCardWidgetComponent, EntityDetailDialogComponent, EntitySelectorEditorComponent, EntitySelectorToolbarComponent, GaugeConfigDialogComponent, GaugeWidgetComponent, HeatmapConfigDialogComponent, HeatmapWidgetComponent, KpiConfigDialogComponent, KpiWidgetComponent, LineChartConfigDialogComponent, LineChartWidgetComponent, MESHBOARD_OPTIONS, MESHBOARD_TENANT_ID_PROVIDER, MarkdownConfigDialogComponent, MarkdownWidgetComponent, MeshBoardDataService, MeshBoardGridService, MeshBoardManagerDialogComponent, MeshBoardPersistenceService, MeshBoardSettingsDialogComponent, MeshBoardSettingsResult, MeshBoardStateService, MeshBoardViewComponent, PieChartConfigDialogComponent, PieChartWidgetComponent, QuerySelectorComponent, RuntimeEntitySelectorComponent, ServiceHealthConfigDialogComponent, ServiceHealthWidgetComponent, StatsGridConfigDialogComponent, StatsGridWidgetComponent, StatusIndicatorConfigDialogComponent, StatusIndicatorWidgetComponent, StatusListConfigDialogComponent, StatusListWidgetComponent, SummaryCardConfigDialogComponent, SummaryCardWidgetComponent, TableConfigDialogComponent, TableWidgetComponent, TableWidgetDataSourceDirective, WidgetFactoryService, WidgetGroupComponent, WidgetGroupConfigDialogComponent, WidgetNotConfiguredComponent, WidgetRegistryService, provideDefaultWidgets, provideMeshBoard, provideProcessWidget, provideWidgetRegistrations, registerDefaultWidgets, registerProcessWidget };
|
|
27025
29265
|
//# sourceMappingURL=meshmakers-octo-meshboard.mjs.map
|