@meshmakers/octo-meshboard 3.3.680 → 3.3.700

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$7 from '@meshmakers/shared-ui';
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$4 from '@progress/kendo-angular-dialog';
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 i2$1 from '@progress/kendo-angular-icons';
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, checkCircleIcon, chartLineIcon, gearsIcon, clipboardMarkdownIcon, copyIcon, gridIcon, heartIcon, gridLayoutIcon, chartLineMarkersIcon, chartColumnStackedIcon, chartDoughnutIcon, tableIcon, shareIcon, fileTxtIcon, checkIcon, xIcon, downloadIcon, uploadIcon, gearIcon, saveIcon, arrowRotateCwIcon, undoIcon, infoCircleIcon } from '@progress/kendo-svg-icons';
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$5 from '@progress/kendo-angular-gauges';
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$6 from '@progress/kendo-angular-charts';
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$2 from 'ngx-markdown';
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
43
  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';
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,7 @@ 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) : {};
1123
1124
  return {
1124
1125
  id: data.rtId,
1125
1126
  title: data.name,
@@ -1127,7 +1128,8 @@ class WidgetRegistryService {
1127
1128
  row: data.row,
1128
1129
  colSpan: data.colSpan,
1129
1130
  rowSpan: data.rowSpan,
1130
- configurable: true
1131
+ configurable: true,
1132
+ chromeless: parsedConfig['chromeless'] === true ? true : undefined
1131
1133
  };
1132
1134
  }
1133
1135
  /**
@@ -1549,7 +1551,10 @@ class MeshBoardPersistenceService {
1549
1551
  dataSourceType,
1550
1552
  dataSourceCkTypeId: persistenceData.dataSourceCkTypeId ?? '',
1551
1553
  dataSourceRtId: persistenceData.dataSourceRtId ?? '',
1552
- config: JSON.stringify(persistenceData.config)
1554
+ config: JSON.stringify({
1555
+ ...persistenceData.config,
1556
+ ...(widget.chromeless ? { chromeless: true } : {})
1557
+ })
1553
1558
  };
1554
1559
  // Only include parent if there are association changes
1555
1560
  if (parentAssociations.length > 0) {
@@ -4019,6 +4024,12 @@ class KpiWidgetComponent {
4019
4024
  default: return 'trend-neutral';
4020
4025
  }
4021
4026
  }, ...(ngDevMode ? [{ debugName: "trendClass" }] : /* istanbul ignore next */ []));
4027
+ comparisonText = computed(() => {
4028
+ if (!this.config?.comparisonText)
4029
+ return null;
4030
+ const variables = this.stateService.getVariables();
4031
+ return this.variableService.resolveVariables(this.config.comparisonText, variables);
4032
+ }, ...(ngDevMode ? [{ debugName: "comparisonText" }] : /* istanbul ignore next */ []));
4022
4033
  ngOnInit() {
4023
4034
  this.loadData();
4024
4035
  }
@@ -4265,11 +4276,11 @@ class KpiWidgetComponent {
4265
4276
  return this.variableService.convertToFieldFilterDto(filters, variables);
4266
4277
  }
4267
4278
  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: i2$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
4279
+ 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
4280
  }
4270
4281
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: KpiWidgetComponent, decorators: [{
4271
4282
  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"] }]
4283
+ 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
4284
  }], propDecorators: { config: [{
4274
4285
  type: Input
4275
4286
  }] } });
@@ -4296,6 +4307,7 @@ class KpiConfigDialogComponent {
4296
4307
  initialPrefix;
4297
4308
  initialSuffix;
4298
4309
  initialTrend;
4310
+ initialComparisonText;
4299
4311
  // Initial values for editing - Persistent Query
4300
4312
  initialDataSourceType;
4301
4313
  initialQueryRtId;
@@ -4338,6 +4350,7 @@ class KpiConfigDialogComponent {
4338
4350
  prefix: '',
4339
4351
  suffix: '',
4340
4352
  trend: undefined,
4353
+ comparisonText: '',
4341
4354
  // Query-specific form fields
4342
4355
  queryValueField: '',
4343
4356
  queryCategoryField: '',
@@ -4395,6 +4408,7 @@ class KpiConfigDialogComponent {
4395
4408
  this.form.prefix = this.initialPrefix || '';
4396
4409
  this.form.suffix = this.initialSuffix || '';
4397
4410
  this.form.trend = this.initialTrend;
4411
+ this.form.comparisonText = this.initialComparisonText || '';
4398
4412
  this.form.queryValueField = this.initialQueryValueField || '';
4399
4413
  this.form.queryCategoryField = this.initialQueryCategoryField || '';
4400
4414
  this.form.queryCategoryValue = this.initialQueryCategoryValue || '';
@@ -4557,7 +4571,8 @@ class KpiConfigDialogComponent {
4557
4571
  staticValue: this.form.staticValue,
4558
4572
  prefix: this.form.prefix || undefined,
4559
4573
  suffix: this.form.suffix || undefined,
4560
- trend: this.form.trend
4574
+ trend: this.form.trend,
4575
+ comparisonText: this.form.comparisonText || undefined
4561
4576
  });
4562
4577
  return;
4563
4578
  }
@@ -4576,6 +4591,7 @@ class KpiConfigDialogComponent {
4576
4591
  prefix: this.form.prefix || undefined,
4577
4592
  suffix: this.form.suffix || undefined,
4578
4593
  trend: this.form.trend,
4594
+ comparisonText: this.form.comparisonText || undefined,
4579
4595
  filters: filtersDto
4580
4596
  });
4581
4597
  }
@@ -4589,6 +4605,7 @@ class KpiConfigDialogComponent {
4589
4605
  prefix: this.form.prefix || undefined,
4590
4606
  suffix: this.form.suffix || undefined,
4591
4607
  trend: this.form.trend,
4608
+ comparisonText: this.form.comparisonText || undefined,
4592
4609
  filters: filtersDto
4593
4610
  });
4594
4611
  }
@@ -4734,7 +4751,7 @@ class KpiConfigDialogComponent {
4734
4751
  this.filters = updatedFilters;
4735
4752
  }
4736
4753
  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: `
4754
+ 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
4755
  <div class="config-container">
4739
4756
 
4740
4757
  <div class="config-form" [class.loading]="isLoadingInitial">
@@ -5070,6 +5087,11 @@ class KpiConfigDialogComponent {
5070
5087
  [(ngModel)]="form.trend">
5071
5088
  </kendo-dropdownlist>
5072
5089
  </div>
5090
+ <div class="form-field">
5091
+ <label>Comparison Text</label>
5092
+ <kendo-textbox [(ngModel)]="form.comparisonText" placeholder="e.g. +3,1% vs. last week"></kendo-textbox>
5093
+ <p class="section-hint">Displayed below the value in the trend color. Supports {{ variableSyntaxHint }}</p>
5094
+ </div>
5073
5095
  </div>
5074
5096
 
5075
5097
  <!-- Filters Section -->
@@ -5450,6 +5472,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
5450
5472
  [(ngModel)]="form.trend">
5451
5473
  </kendo-dropdownlist>
5452
5474
  </div>
5475
+ <div class="form-field">
5476
+ <label>Comparison Text</label>
5477
+ <kendo-textbox [(ngModel)]="form.comparisonText" placeholder="e.g. +3,1% vs. last week"></kendo-textbox>
5478
+ <p class="section-hint">Displayed below the value in the trend color. Supports {{ variableSyntaxHint }}</p>
5479
+ </div>
5453
5480
  </div>
5454
5481
 
5455
5482
  <!-- Filters Section -->
@@ -5503,6 +5530,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
5503
5530
  type: Input
5504
5531
  }], initialTrend: [{
5505
5532
  type: Input
5533
+ }], initialComparisonText: [{
5534
+ type: Input
5506
5535
  }], initialDataSourceType: [{
5507
5536
  type: Input
5508
5537
  }], initialQueryRtId: [{
@@ -5688,7 +5717,7 @@ class EntityDetailDialogComponent {
5688
5717
  <button kendoButton (click)="onClose()">Close</button>
5689
5718
  </kendo-dialog-actions>
5690
5719
  </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$4.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$4.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"] }] });
5720
+ `, 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
5721
  }
5693
5722
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: EntityDetailDialogComponent, decorators: [{
5694
5723
  type: Component,
@@ -6061,7 +6090,7 @@ class EntityAssociationsWidgetComponent {
6061
6090
  .trim();
6062
6091
  }
6063
6092
  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: i2$1.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" }] });
6093
+ 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
6094
  }
6066
6095
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: EntityAssociationsWidgetComponent, decorators: [{
6067
6096
  type: Component,
@@ -7205,6 +7234,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
7205
7234
  type: Input
7206
7235
  }] } });
7207
7236
 
7237
+ const ICON_MAP = {
7238
+ 'check-circle': checkCircleIcon,
7239
+ 'x-circle': xCircleIcon,
7240
+ 'exclamation-circle': exclamationCircleIcon,
7241
+ 'warning-triangle': warningTriangleIcon,
7242
+ 'minus-circle': minusCircleIcon,
7243
+ 'question-circle': questionCircleIcon,
7244
+ 'circle': circleIcon,
7245
+ };
7246
+ function resolveStatusMapping(config) {
7247
+ return Object.fromEntries(Object.entries(config).map(([key, val]) => [
7248
+ key,
7249
+ { icon: ICON_MAP[val.icon] ?? questionCircleIcon, tooltip: val.tooltip, color: val.color }
7250
+ ]));
7251
+ }
7208
7252
  class TableWidgetComponent {
7209
7253
  config;
7210
7254
  dataSource;
@@ -7235,25 +7279,25 @@ class TableWidgetComponent {
7235
7279
  * For persistent queries, uses dynamically derived columns from the query response.
7236
7280
  */
7237
7281
  listViewColumns = computed(() => {
7238
- // For persistent query, use derived columns
7282
+ // If explicit columns are configured, use them (works for both data source types)
7283
+ if (this.config?.columns && this.config.columns.length > 0) {
7284
+ return this.config.columns.map(col => ({
7285
+ field: col.field,
7286
+ displayName: col.title,
7287
+ dataType: (col.dataType ?? 'text'),
7288
+ width: col.width,
7289
+ ...(col.statusMapping ? { statusMapping: resolveStatusMapping(col.statusMapping) } : {})
7290
+ }));
7291
+ }
7292
+ // For persistent query without explicit columns, use derived columns
7239
7293
  if (this.config?.dataSource?.type === 'persistentQuery') {
7240
- // Return query-derived columns if available
7241
7294
  const queryColumns = this._queryColumnsForView();
7242
7295
  if (queryColumns.length > 0) {
7243
7296
  return queryColumns;
7244
7297
  }
7245
- // Return empty array while waiting for first data load
7246
7298
  return [];
7247
7299
  }
7248
- // For runtime entity, use configured columns
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
- }));
7300
+ return [];
7257
7301
  }, ...(ngDevMode ? [{ debugName: "listViewColumns" }] : /* istanbul ignore next */ []));
7258
7302
  /**
7259
7303
  * Checks if the widget has a valid configuration.
@@ -7847,7 +7891,7 @@ class TableConfigDialogComponent {
7847
7891
  </button>
7848
7892
  </div>
7849
7893
  </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: i2$1.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"] }] });
7894
+ `, 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
7895
  }
7852
7896
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: TableConfigDialogComponent, decorators: [{
7853
7897
  type: Component,
@@ -8521,7 +8565,7 @@ class GaugeWidgetComponent {
8521
8565
  </div>
8522
8566
  }
8523
8567
  </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, #212529)}.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, #212529)}.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$5.ArcGaugeComponent, selector: "kendo-arcgauge", inputs: ["value", "color", "colors", "opacity", "scale"], exportAs: ["kendoArcGauge"] }, { kind: "directive", type: i1$5.ArcCenterTemplateDirective, selector: "[kendoArcGaugeCenterTemplate]" }, { kind: "component", type: i1$5.ArcScaleComponent, selector: "kendo-arcgauge-scale", inputs: ["labels", "rangeDistance", "rangeLineCap", "startAngle", "endAngle"] }, { kind: "component", type: i1$5.ColorsComponent, selector: "kendo-arcgauge-colors" }, { kind: "component", type: i1$5.ColorComponent, selector: "kendo-arcgauge-color", inputs: ["color", "opacity", "from", "to"] }, { kind: "component", type: i1$5.CircularGaugeComponent, selector: "kendo-circulargauge", inputs: ["scale"], exportAs: ["kendoCircularGauge"] }, { kind: "directive", type: i1$5.CircularGaugeCenterTemplateDirective, selector: "[kendoCircularGaugeCenterTemplate]" }, { kind: "component", type: i1$5.CircularGaugeScaleComponent, selector: "kendo-circulargauge-scale" }, { kind: "component", type: i1$5.LinearGaugeComponent, selector: "kendo-lineargauge", inputs: ["pointer", "scale"], exportAs: ["kendoLinearGauge"] }, { kind: "component", type: i1$5.LinearScaleComponent, selector: "kendo-lineargauge-scale", inputs: ["line", "ranges", "mirror", "vertical"] }, { kind: "component", type: i1$5.LinearLabelsComponent, selector: "kendo-lineargauge-scale-labels" }, { kind: "component", type: i1$5.LinearPointersComponent, selector: "kendo-lineargauge-pointers" }, { kind: "component", type: i1$5.LinearPointerComponent, selector: "kendo-lineargauge-pointer", inputs: ["border", "color", "margin", "opacity", "shape", "size", "value"] }, { kind: "component", type: i1$5.LinearRangeComponent, selector: "kendo-lineargauge-scale-range" }, { kind: "component", type: i1$5.LinearRangesComponent, selector: "kendo-lineargauge-scale-ranges" }, { kind: "component", type: i1$5.RadialGaugeComponent, selector: "kendo-radialgauge", inputs: ["pointer", "scale"], exportAs: ["kendoRadialGauge"] }, { kind: "component", type: i1$5.RadialScaleComponent, selector: "kendo-radialgauge-scale", inputs: ["labels", "rangeDistance", "ranges", "startAngle", "endAngle"] }, { kind: "component", type: i1$5.RadialPointerComponent, selector: "kendo-radialgauge-pointer", inputs: ["cap", "color", "length", "value"] }, { kind: "component", type: i1$5.RadialRangeComponent, selector: "kendo-radialgauge-scale-range" }, { kind: "component", type: i1$5.RadialRangesComponent, selector: "kendo-radialgauge-scale-ranges" }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
8568
+ `, 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
8569
  }
8526
8570
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: GaugeWidgetComponent, decorators: [{
8527
8571
  type: Component,
@@ -8698,7 +8742,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
8698
8742
  </div>
8699
8743
  }
8700
8744
  </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, #212529)}.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, #212529)}.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"] }]
8745
+ `, 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
8746
  }], propDecorators: { config: [{
8703
8747
  type: Input
8704
8748
  }] } });
@@ -10241,8 +10285,8 @@ class PieChartWidgetComponent {
10241
10285
  <span>{{ error() }}</span>
10242
10286
  </div>
10243
10287
  } @else {
10244
- <kendo-chart class="chart-container">
10245
- <kendo-chart-plot-area [margin]="plotAreaMargin"></kendo-chart-plot-area>
10288
+ <kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: plotAreaMargin }">
10289
+ <kendo-chart-area [background]="'transparent'"></kendo-chart-area>
10246
10290
  <kendo-chart-series>
10247
10291
  <kendo-chart-series-item
10248
10292
  [type]="config.chartType"
@@ -10266,7 +10310,7 @@ class PieChartWidgetComponent {
10266
10310
  </kendo-chart>
10267
10311
  }
10268
10312
  </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$6.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$6.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$6.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$6.PlotAreaComponent, selector: "kendo-chart-plot-area", inputs: ["background", "border", "margin", "opacity", "padding"] }, { kind: "component", type: i1$6.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$6.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$6.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
10313
+ `, 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
10314
  }
10271
10315
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: PieChartWidgetComponent, decorators: [{
10272
10316
  type: Component,
@@ -10287,8 +10331,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
10287
10331
  <span>{{ error() }}</span>
10288
10332
  </div>
10289
10333
  } @else {
10290
- <kendo-chart class="chart-container">
10291
- <kendo-chart-plot-area [margin]="plotAreaMargin"></kendo-chart-plot-area>
10334
+ <kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: plotAreaMargin }">
10335
+ <kendo-chart-area [background]="'transparent'"></kendo-chart-area>
10292
10336
  <kendo-chart-series>
10293
10337
  <kendo-chart-series-item
10294
10338
  [type]="config.chartType"
@@ -11195,7 +11239,7 @@ class BarChartWidgetComponent {
11195
11239
  continue;
11196
11240
  const sanitizedPath = this.sanitizeFieldName(cell.attributePath);
11197
11241
  if (sanitizedPath === this.sanitizeFieldName(this.config.categoryField)) {
11198
- categoryValue = String(cell.value ?? '');
11242
+ categoryValue = this.formatCategoryValue(cell.value);
11199
11243
  }
11200
11244
  // Check if this cell is one of our series fields
11201
11245
  for (const seriesConfig of (this.config.series ?? [])) {
@@ -11215,11 +11259,14 @@ class BarChartWidgetComponent {
11215
11259
  }
11216
11260
  }
11217
11261
  // Convert to series data array
11218
- const seriesData = (this.config.series ?? []).map(seriesConfig => ({
11219
- name: seriesConfig.name ?? seriesConfig.field,
11220
- data: seriesMap.get(seriesConfig.field) ?? [],
11221
- color: seriesConfig.color
11222
- }));
11262
+ const seriesData = (this.config.series ?? []).map(seriesConfig => {
11263
+ const rawData = seriesMap.get(seriesConfig.field) ?? [];
11264
+ return {
11265
+ name: seriesConfig.name ?? seriesConfig.field,
11266
+ data: this.hasColorThresholds() ? this.applyColorThresholds(rawData) : rawData,
11267
+ color: seriesConfig.color
11268
+ };
11269
+ });
11223
11270
  this._categories.set(categories);
11224
11271
  this._seriesData.set(seriesData);
11225
11272
  }
@@ -11257,7 +11304,7 @@ class BarChartWidgetComponent {
11257
11304
  continue;
11258
11305
  const sanitizedPath = this.sanitizeFieldName(cell.attributePath);
11259
11306
  if (sanitizedPath === this.sanitizeFieldName(categoryField)) {
11260
- categoryValue = String(cell.value ?? '');
11307
+ categoryValue = this.formatCategoryValue(cell.value);
11261
11308
  }
11262
11309
  else if (sanitizedPath === this.sanitizeFieldName(seriesGroupField)) {
11263
11310
  seriesGroupValue = String(cell.value ?? '');
@@ -11283,12 +11330,12 @@ class BarChartWidgetComponent {
11283
11330
  const seriesGroups = Array.from(allSeriesGroups);
11284
11331
  // Build series data
11285
11332
  const seriesData = seriesGroups.map(seriesGroup => {
11286
- const data = categories.map(category => {
11333
+ const rawData = categories.map(category => {
11287
11334
  return dataMap.get(category)?.get(seriesGroup) ?? 0;
11288
11335
  });
11289
11336
  return {
11290
11337
  name: seriesGroup,
11291
- data
11338
+ data: this.hasColorThresholds() ? this.applyColorThresholds(rawData) : rawData
11292
11339
  };
11293
11340
  });
11294
11341
  this._categories.set(categories);
@@ -11298,6 +11345,42 @@ class BarChartWidgetComponent {
11298
11345
  * Sanitizes field names for comparison.
11299
11346
  * Replaces dots with underscores (same as table widget).
11300
11347
  */
11348
+ dataLabelFormat = computed(() => {
11349
+ const suffix = this.config?.dataLabelSuffix ?? '';
11350
+ return `{0:n0}${suffix}`;
11351
+ }, ...(ngDevMode ? [{ debugName: "dataLabelFormat" }] : /* istanbul ignore next */ []));
11352
+ hasColorThresholds() {
11353
+ return (this.config?.colorThresholds?.length ?? 0) > 0;
11354
+ }
11355
+ applyColorThresholds(data) {
11356
+ const thresholds = this.config.colorThresholds ?? [];
11357
+ const defaultColor = this.config.defaultBarColor ?? '#6b7280';
11358
+ return data.map(val => ({
11359
+ value: val,
11360
+ _color: this.getColorForValue(val, thresholds, defaultColor)
11361
+ }));
11362
+ }
11363
+ getColorForValue(val, thresholds, defaultColor) {
11364
+ for (const t of thresholds) {
11365
+ if (val <= t.value)
11366
+ return t.color;
11367
+ }
11368
+ return defaultColor;
11369
+ }
11370
+ formatCategoryValue(value) {
11371
+ const str = String(value ?? '');
11372
+ // Detect ISO 8601 timestamps and format as readable date/time
11373
+ if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(str)) {
11374
+ const date = new Date(str);
11375
+ if (!isNaN(date.getTime())) {
11376
+ return date.toLocaleString('de-AT', {
11377
+ day: '2-digit', month: '2-digit',
11378
+ hour: '2-digit', minute: '2-digit'
11379
+ });
11380
+ }
11381
+ }
11382
+ return str;
11383
+ }
11301
11384
  sanitizeFieldName(fieldName) {
11302
11385
  return fieldName.replace(/\./g, '_');
11303
11386
  }
@@ -11323,10 +11406,13 @@ class BarChartWidgetComponent {
11323
11406
  <span>{{ error() }}</span>
11324
11407
  </div>
11325
11408
  } @else {
11326
- <kendo-chart class="chart-container">
11327
- <kendo-chart-plot-area [margin]="plotAreaMargin()"></kendo-chart-plot-area>
11409
+ <kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: plotAreaMargin() }">
11410
+ <kendo-chart-area [background]="'transparent'"></kendo-chart-area>
11328
11411
  <kendo-chart-category-axis>
11329
- <kendo-chart-category-axis-item [categories]="categories()">
11412
+ <kendo-chart-category-axis-item
11413
+ [categories]="categories()"
11414
+ [line]="{ visible: false }"
11415
+ [majorGridLines]="{ visible: false }">
11330
11416
  <kendo-chart-category-axis-item-labels
11331
11417
  [rotation]="labelRotation()"
11332
11418
  [content]="categoryLabelContent">
@@ -11334,6 +11420,13 @@ class BarChartWidgetComponent {
11334
11420
  </kendo-chart-category-axis-item>
11335
11421
  </kendo-chart-category-axis>
11336
11422
 
11423
+ <kendo-chart-value-axis>
11424
+ <kendo-chart-value-axis-item
11425
+ [line]="{ visible: false }"
11426
+ [majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }">
11427
+ </kendo-chart-value-axis-item>
11428
+ </kendo-chart-value-axis>
11429
+
11337
11430
  <kendo-chart-series>
11338
11431
  @for (series of seriesData(); track series.name) {
11339
11432
  <kendo-chart-series-item
@@ -11341,11 +11434,15 @@ class BarChartWidgetComponent {
11341
11434
  [data]="series.data"
11342
11435
  [name]="series.name"
11343
11436
  [color]="series.color"
11437
+ [field]="hasColorThresholds() ? 'value' : ''"
11438
+ [colorField]="hasColorThresholds() ? '_color' : ''"
11439
+ [border]="{ width: 0 }"
11440
+ [gap]="0.8"
11344
11441
  [stack]="stackConfig()">
11345
11442
  @if (config.showDataLabels) {
11346
11443
  <kendo-chart-series-item-labels
11347
11444
  [visible]="true"
11348
- [format]="'{0:n0}'">
11445
+ [format]="dataLabelFormat()">
11349
11446
  </kendo-chart-series-item-labels>
11350
11447
  }
11351
11448
  </kendo-chart-series-item>
@@ -11368,7 +11465,7 @@ class BarChartWidgetComponent {
11368
11465
  </kendo-chart>
11369
11466
  }
11370
11467
  </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$6.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$6.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$6.CategoryAxisComponent, selector: "kendo-chart-category-axis" }, { kind: "component", type: i1$6.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$6.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$6.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$6.PlotAreaComponent, selector: "kendo-chart-plot-area", inputs: ["background", "border", "margin", "opacity", "padding"] }, { kind: "component", type: i1$6.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$6.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$6.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$6.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
11468
+ `, 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
11469
  }
11373
11470
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BarChartWidgetComponent, decorators: [{
11374
11471
  type: Component,
@@ -11389,10 +11486,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
11389
11486
  <span>{{ error() }}</span>
11390
11487
  </div>
11391
11488
  } @else {
11392
- <kendo-chart class="chart-container">
11393
- <kendo-chart-plot-area [margin]="plotAreaMargin()"></kendo-chart-plot-area>
11489
+ <kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: plotAreaMargin() }">
11490
+ <kendo-chart-area [background]="'transparent'"></kendo-chart-area>
11394
11491
  <kendo-chart-category-axis>
11395
- <kendo-chart-category-axis-item [categories]="categories()">
11492
+ <kendo-chart-category-axis-item
11493
+ [categories]="categories()"
11494
+ [line]="{ visible: false }"
11495
+ [majorGridLines]="{ visible: false }">
11396
11496
  <kendo-chart-category-axis-item-labels
11397
11497
  [rotation]="labelRotation()"
11398
11498
  [content]="categoryLabelContent">
@@ -11400,6 +11500,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
11400
11500
  </kendo-chart-category-axis-item>
11401
11501
  </kendo-chart-category-axis>
11402
11502
 
11503
+ <kendo-chart-value-axis>
11504
+ <kendo-chart-value-axis-item
11505
+ [line]="{ visible: false }"
11506
+ [majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }">
11507
+ </kendo-chart-value-axis-item>
11508
+ </kendo-chart-value-axis>
11509
+
11403
11510
  <kendo-chart-series>
11404
11511
  @for (series of seriesData(); track series.name) {
11405
11512
  <kendo-chart-series-item
@@ -11407,11 +11514,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
11407
11514
  [data]="series.data"
11408
11515
  [name]="series.name"
11409
11516
  [color]="series.color"
11517
+ [field]="hasColorThresholds() ? 'value' : ''"
11518
+ [colorField]="hasColorThresholds() ? '_color' : ''"
11519
+ [border]="{ width: 0 }"
11520
+ [gap]="0.8"
11410
11521
  [stack]="stackConfig()">
11411
11522
  @if (config.showDataLabels) {
11412
11523
  <kendo-chart-series-item-labels
11413
11524
  [visible]="true"
11414
- [format]="'{0:n0}'">
11525
+ [format]="dataLabelFormat()">
11415
11526
  </kendo-chart-series-item-labels>
11416
11527
  }
11417
11528
  </kendo-chart-series-item>
@@ -11459,6 +11570,8 @@ class BarChartConfigDialogComponent {
11459
11570
  initialShowLegend;
11460
11571
  initialLegendPosition;
11461
11572
  initialShowDataLabels;
11573
+ initialColorThresholds;
11574
+ initialDefaultBarColor;
11462
11575
  initialFilters;
11463
11576
  // State
11464
11577
  isLoadingInitial = false;
@@ -11495,7 +11608,9 @@ class BarChartConfigDialogComponent {
11495
11608
  categoryField: '',
11496
11609
  showLegend: true,
11497
11610
  legendPosition: 'right',
11498
- showDataLabels: false
11611
+ showDataLabels: false,
11612
+ colorThresholds: [],
11613
+ defaultBarColor: ''
11499
11614
  };
11500
11615
  get isValid() {
11501
11616
  if (this.selectedPersistentQuery === null || this.form.categoryField === '') {
@@ -11521,6 +11636,8 @@ class BarChartConfigDialogComponent {
11521
11636
  this.form.showLegend = this.initialShowLegend ?? true;
11522
11637
  this.form.legendPosition = this.initialLegendPosition ?? 'right';
11523
11638
  this.form.showDataLabels = this.initialShowDataLabels ?? false;
11639
+ this.form.colorThresholds = this.initialColorThresholds ? [...this.initialColorThresholds] : [];
11640
+ this.form.defaultBarColor = this.initialDefaultBarColor ?? '';
11524
11641
  // Determine series mode from initial values
11525
11642
  if (this.initialSeriesGroupField && this.initialValueField) {
11526
11643
  this.seriesMode = 'dynamic';
@@ -11664,6 +11781,8 @@ class BarChartConfigDialogComponent {
11664
11781
  showLegend: this.form.showLegend,
11665
11782
  legendPosition: this.form.legendPosition,
11666
11783
  showDataLabels: this.form.showDataLabels,
11784
+ colorThresholds: this.form.colorThresholds.length > 0 ? this.form.colorThresholds : undefined,
11785
+ defaultBarColor: this.form.defaultBarColor || undefined,
11667
11786
  filters: filtersDto
11668
11787
  };
11669
11788
  // Add dynamic series fields if in dynamic mode
@@ -11673,11 +11792,17 @@ class BarChartConfigDialogComponent {
11673
11792
  }
11674
11793
  this.windowRef.close(result);
11675
11794
  }
11795
+ addColorThreshold() {
11796
+ this.form.colorThresholds.push({ value: 0, color: '#10b981' });
11797
+ }
11798
+ removeColorThreshold(index) {
11799
+ this.form.colorThresholds.splice(index, 1);
11800
+ }
11676
11801
  onCancel() {
11677
11802
  this.windowRef.close();
11678
11803
  }
11679
11804
  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: `
11805
+ 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
11806
  <div class="config-container">
11682
11807
 
11683
11808
  <div class="config-form" [class.loading]="isLoadingInitial">
@@ -11907,6 +12032,24 @@ class BarChartConfigDialogComponent {
11907
12032
  </div>
11908
12033
  }
11909
12034
  </div>
12035
+
12036
+ <div class="form-section">
12037
+ <h4>Conditional Colors</h4>
12038
+ <p class="section-hint">Color bars based on value thresholds (sorted ascending).</p>
12039
+ @for (threshold of form.colorThresholds; track $index) {
12040
+ <div class="threshold-row">
12041
+ <label>Values &le;</label>
12042
+ <kendo-numerictextbox [(ngModel)]="threshold.value" [format]="'n0'" [spinners]="false" style="width: 100px;"></kendo-numerictextbox>
12043
+ <kendo-textbox [(ngModel)]="threshold.color" [placeholder]="'Color (#10b981)'" style="width: 120px;"></kendo-textbox>
12044
+ <button kendoButton fillMode="flat" (click)="removeColorThreshold($index)">Remove</button>
12045
+ </div>
12046
+ }
12047
+ <div class="threshold-row">
12048
+ <label>Default color</label>
12049
+ <kendo-textbox [(ngModel)]="form.defaultBarColor" [placeholder]="'#ef4444'" style="width: 120px;"></kendo-textbox>
12050
+ </div>
12051
+ <button kendoButton fillMode="flat" (click)="addColorThreshold()">+ Add Threshold</button>
12052
+ </div>
11910
12053
  </div>
11911
12054
 
11912
12055
  <div class="action-bar mm-dialog-actions">
@@ -11920,7 +12063,7 @@ class BarChartConfigDialogComponent {
11920
12063
  </button>
11921
12064
  </div>
11922
12065
  </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"] }] });
12066
+ `, 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
12067
  }
11925
12068
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BarChartConfigDialogComponent, decorators: [{
11926
12069
  type: Component,
@@ -12164,6 +12307,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
12164
12307
  </div>
12165
12308
  }
12166
12309
  </div>
12310
+
12311
+ <div class="form-section">
12312
+ <h4>Conditional Colors</h4>
12313
+ <p class="section-hint">Color bars based on value thresholds (sorted ascending).</p>
12314
+ @for (threshold of form.colorThresholds; track $index) {
12315
+ <div class="threshold-row">
12316
+ <label>Values &le;</label>
12317
+ <kendo-numerictextbox [(ngModel)]="threshold.value" [format]="'n0'" [spinners]="false" style="width: 100px;"></kendo-numerictextbox>
12318
+ <kendo-textbox [(ngModel)]="threshold.color" [placeholder]="'Color (#10b981)'" style="width: 120px;"></kendo-textbox>
12319
+ <button kendoButton fillMode="flat" (click)="removeColorThreshold($index)">Remove</button>
12320
+ </div>
12321
+ }
12322
+ <div class="threshold-row">
12323
+ <label>Default color</label>
12324
+ <kendo-textbox [(ngModel)]="form.defaultBarColor" [placeholder]="'#ef4444'" style="width: 120px;"></kendo-textbox>
12325
+ </div>
12326
+ <button kendoButton fillMode="flat" (click)="addColorThreshold()">+ Add Threshold</button>
12327
+ </div>
12167
12328
  </div>
12168
12329
 
12169
12330
  <div class="action-bar mm-dialog-actions">
@@ -12177,7 +12338,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
12177
12338
  </button>
12178
12339
  </div>
12179
12340
  </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"] }]
12341
+ `, 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
12342
  }], propDecorators: { querySelector: [{
12182
12343
  type: ViewChild,
12183
12344
  args: ['querySelector']
@@ -12201,6 +12362,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
12201
12362
  type: Input
12202
12363
  }], initialShowDataLabels: [{
12203
12364
  type: Input
12365
+ }], initialColorThresholds: [{
12366
+ type: Input
12367
+ }], initialDefaultBarColor: [{
12368
+ type: Input
12204
12369
  }], initialFilters: [{
12205
12370
  type: Input
12206
12371
  }] } });
@@ -12223,6 +12388,28 @@ class LineChartWidgetComponent {
12223
12388
  valueAxes = this._valueAxes.asReadonly();
12224
12389
  error = this._error.asReadonly();
12225
12390
  data = computed(() => this._seriesData(), ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
12391
+ plotBands = computed(() => {
12392
+ if (!this.config?.referenceLines?.length)
12393
+ return [];
12394
+ return this.config.referenceLines.map(ref => {
12395
+ const lineColor = ref.color ?? '#ef4444';
12396
+ const bandWidth = ref.value * 0.002 || 1;
12397
+ return {
12398
+ from: ref.value - bandWidth,
12399
+ to: ref.value + bandWidth,
12400
+ color: lineColor,
12401
+ opacity: ref.opacity ?? 0.8,
12402
+ label: ref.label ? {
12403
+ text: ref.label,
12404
+ position: 'top',
12405
+ align: 'right',
12406
+ color: lineColor,
12407
+ font: '500 0.8rem sans-serif',
12408
+ padding: { top: 2, right: 4, bottom: 2, left: 4 }
12409
+ } : undefined
12410
+ };
12411
+ });
12412
+ }, ...(ngDevMode ? [{ debugName: "plotBands" }] : /* istanbul ignore next */ []));
12226
12413
  chartType = computed(() => {
12227
12414
  return this.config?.chartType ?? 'line';
12228
12415
  }, ...(ngDevMode ? [{ debugName: "chartType" }] : /* istanbul ignore next */ []));
@@ -12482,11 +12669,14 @@ class LineChartWidgetComponent {
12482
12669
  <span>{{ error() }}</span>
12483
12670
  </div>
12484
12671
  } @else {
12485
- <kendo-chart class="chart-container">
12486
- <kendo-chart-plot-area [margin]="{ top: 0, right: 0, bottom: 0, left: 0 }"></kendo-chart-plot-area>
12672
+ <kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: { top: 0, right: 0, bottom: 0, left: 0 } }">
12673
+ <kendo-chart-area [background]="'transparent'"></kendo-chart-area>
12487
12674
 
12488
12675
  <kendo-chart-category-axis>
12489
- <kendo-chart-category-axis-item [categories]="categories()">
12676
+ <kendo-chart-category-axis-item
12677
+ [categories]="categories()"
12678
+ [line]="{ visible: false }"
12679
+ [majorGridLines]="{ visible: false }">
12490
12680
  <kendo-chart-category-axis-item-labels
12491
12681
  [rotation]="labelRotation()"
12492
12682
  [step]="labelStep()"
@@ -12500,19 +12690,36 @@ class LineChartWidgetComponent {
12500
12690
  @for (axis of valueAxes(); track axis.name) {
12501
12691
  <kendo-chart-value-axis-item
12502
12692
  [name]="axis.name"
12503
- [title]="{ text: axis.unit }">
12693
+ [title]="{ text: axis.unit }"
12694
+ [line]="{ visible: false }"
12695
+ [majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }"
12696
+ [plotBands]="plotBands()">
12504
12697
  </kendo-chart-value-axis-item>
12505
12698
  }
12506
12699
  </kendo-chart-value-axis>
12507
12700
  }
12508
12701
 
12702
+ @if (valueAxes().length === 0) {
12703
+ <kendo-chart-value-axis>
12704
+ <kendo-chart-value-axis-item
12705
+ [name]="''"
12706
+ [title]="{ text: config.valueAxisTitle ?? '' }"
12707
+ [line]="{ visible: false }"
12708
+ [majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }"
12709
+ [plotBands]="plotBands()">
12710
+ </kendo-chart-value-axis-item>
12711
+ </kendo-chart-value-axis>
12712
+ }
12713
+
12509
12714
  <kendo-chart-series>
12510
12715
  @for (series of seriesData(); track series.name) {
12511
12716
  <kendo-chart-series-item
12512
12717
  [type]="chartType()"
12718
+ [style]="'smooth'"
12513
12719
  [data]="series.data"
12514
12720
  [name]="series.name"
12515
12721
  [axis]="series.axisName ?? ''"
12722
+ [opacity]="0.7"
12516
12723
  [markers]="{ visible: config.showMarkers ?? false }">
12517
12724
  </kendo-chart-series-item>
12518
12725
  }
@@ -12534,7 +12741,7 @@ class LineChartWidgetComponent {
12534
12741
  </kendo-chart>
12535
12742
  }
12536
12743
  </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$6.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$6.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$6.CategoryAxisComponent, selector: "kendo-chart-category-axis" }, { kind: "component", type: i1$6.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$6.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$6.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$6.PlotAreaComponent, selector: "kendo-chart-plot-area", inputs: ["background", "border", "margin", "opacity", "padding"] }, { kind: "component", type: i1$6.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$6.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$6.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: i1$6.ValueAxisComponent, selector: "kendo-chart-value-axis" }, { kind: "component", type: i1$6.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" }] });
12744
+ `, 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
12745
  }
12539
12746
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: LineChartWidgetComponent, decorators: [{
12540
12747
  type: Component,
@@ -12555,11 +12762,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
12555
12762
  <span>{{ error() }}</span>
12556
12763
  </div>
12557
12764
  } @else {
12558
- <kendo-chart class="chart-container">
12559
- <kendo-chart-plot-area [margin]="{ top: 0, right: 0, bottom: 0, left: 0 }"></kendo-chart-plot-area>
12765
+ <kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: { top: 0, right: 0, bottom: 0, left: 0 } }">
12766
+ <kendo-chart-area [background]="'transparent'"></kendo-chart-area>
12560
12767
 
12561
12768
  <kendo-chart-category-axis>
12562
- <kendo-chart-category-axis-item [categories]="categories()">
12769
+ <kendo-chart-category-axis-item
12770
+ [categories]="categories()"
12771
+ [line]="{ visible: false }"
12772
+ [majorGridLines]="{ visible: false }">
12563
12773
  <kendo-chart-category-axis-item-labels
12564
12774
  [rotation]="labelRotation()"
12565
12775
  [step]="labelStep()"
@@ -12573,19 +12783,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
12573
12783
  @for (axis of valueAxes(); track axis.name) {
12574
12784
  <kendo-chart-value-axis-item
12575
12785
  [name]="axis.name"
12576
- [title]="{ text: axis.unit }">
12786
+ [title]="{ text: axis.unit }"
12787
+ [line]="{ visible: false }"
12788
+ [majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }"
12789
+ [plotBands]="plotBands()">
12577
12790
  </kendo-chart-value-axis-item>
12578
12791
  }
12579
12792
  </kendo-chart-value-axis>
12580
12793
  }
12581
12794
 
12795
+ @if (valueAxes().length === 0) {
12796
+ <kendo-chart-value-axis>
12797
+ <kendo-chart-value-axis-item
12798
+ [name]="''"
12799
+ [title]="{ text: config.valueAxisTitle ?? '' }"
12800
+ [line]="{ visible: false }"
12801
+ [majorGridLines]="{ color: 'rgba(255,255,255,0.06)' }"
12802
+ [plotBands]="plotBands()">
12803
+ </kendo-chart-value-axis-item>
12804
+ </kendo-chart-value-axis>
12805
+ }
12806
+
12582
12807
  <kendo-chart-series>
12583
12808
  @for (series of seriesData(); track series.name) {
12584
12809
  <kendo-chart-series-item
12585
12810
  [type]="chartType()"
12811
+ [style]="'smooth'"
12586
12812
  [data]="series.data"
12587
12813
  [name]="series.name"
12588
12814
  [axis]="series.axisName ?? ''"
12815
+ [opacity]="0.7"
12589
12816
  [markers]="{ visible: config.showMarkers ?? false }">
12590
12817
  </kendo-chart-series-item>
12591
12818
  }
@@ -12632,6 +12859,7 @@ class LineChartConfigDialogComponent {
12632
12859
  initialShowLegend;
12633
12860
  initialLegendPosition;
12634
12861
  initialShowMarkers;
12862
+ initialReferenceLines;
12635
12863
  initialFilters;
12636
12864
  // State
12637
12865
  isLoadingInitial = false;
@@ -12660,7 +12888,8 @@ class LineChartConfigDialogComponent {
12660
12888
  unitField: '',
12661
12889
  showLegend: true,
12662
12890
  legendPosition: 'right',
12663
- showMarkers: false
12891
+ showMarkers: false,
12892
+ referenceLines: []
12664
12893
  };
12665
12894
  get isValid() {
12666
12895
  return this.selectedPersistentQuery !== null
@@ -12684,6 +12913,7 @@ class LineChartConfigDialogComponent {
12684
12913
  this.form.showLegend = this.initialShowLegend ?? true;
12685
12914
  this.form.legendPosition = this.initialLegendPosition ?? 'right';
12686
12915
  this.form.showMarkers = this.initialShowMarkers ?? false;
12916
+ this.form.referenceLines = this.initialReferenceLines ? [...this.initialReferenceLines] : [];
12687
12917
  // Initialize filters
12688
12918
  if (this.initialFilters && this.initialFilters.length > 0) {
12689
12919
  this.filters = this.initialFilters.map((f, index) => ({
@@ -12784,15 +13014,22 @@ class LineChartConfigDialogComponent {
12784
13014
  showLegend: this.form.showLegend,
12785
13015
  legendPosition: this.form.legendPosition,
12786
13016
  showMarkers: this.form.showMarkers,
13017
+ referenceLines: this.form.referenceLines.length > 0 ? this.form.referenceLines : undefined,
12787
13018
  filters: filtersDto
12788
13019
  };
12789
13020
  this.windowRef.close(result);
12790
13021
  }
13022
+ addReferenceLine() {
13023
+ this.form.referenceLines.push({ value: 0, label: '', color: '#ef4444' });
13024
+ }
13025
+ removeReferenceLine(index) {
13026
+ this.form.referenceLines.splice(index, 1);
13027
+ }
12791
13028
  onCancel() {
12792
13029
  this.windowRef.close();
12793
13030
  }
12794
13031
  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: `
13032
+ 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
13033
  <div class="config-container">
12797
13034
 
12798
13035
  <div class="config-form" [class.loading]="isLoadingInitial">
@@ -12968,6 +13205,20 @@ class LineChartConfigDialogComponent {
12968
13205
  </div>
12969
13206
  }
12970
13207
  </div>
13208
+
13209
+ <div class="form-section">
13210
+ <h4>Reference Lines</h4>
13211
+ <p class="section-hint">Add horizontal threshold lines to the chart.</p>
13212
+ @for (refLine of form.referenceLines; track $index) {
13213
+ <div class="reference-line-row">
13214
+ <kendo-numerictextbox [(ngModel)]="refLine.value" [format]="'n0'" [placeholder]="'Value'" [spinners]="false" style="width: 100px;"></kendo-numerictextbox>
13215
+ <kendo-textbox [(ngModel)]="refLine.label" [placeholder]="'Label (e.g. Limit)'" style="flex: 1;"></kendo-textbox>
13216
+ <kendo-textbox [(ngModel)]="refLine.color" [placeholder]="'Color (#ef4444)'" style="width: 110px;"></kendo-textbox>
13217
+ <button kendoButton fillMode="flat" (click)="removeReferenceLine($index)">Remove</button>
13218
+ </div>
13219
+ }
13220
+ <button kendoButton fillMode="flat" (click)="addReferenceLine()">+ Add Reference Line</button>
13221
+ </div>
12971
13222
  </div>
12972
13223
 
12973
13224
  <div class="action-bar mm-dialog-actions">
@@ -12981,7 +13232,7 @@ class LineChartConfigDialogComponent {
12981
13232
  </button>
12982
13233
  </div>
12983
13234
  </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"] }] });
13235
+ `, 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
13236
  }
12986
13237
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: LineChartConfigDialogComponent, decorators: [{
12987
13238
  type: Component,
@@ -13171,6 +13422,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
13171
13422
  </div>
13172
13423
  }
13173
13424
  </div>
13425
+
13426
+ <div class="form-section">
13427
+ <h4>Reference Lines</h4>
13428
+ <p class="section-hint">Add horizontal threshold lines to the chart.</p>
13429
+ @for (refLine of form.referenceLines; track $index) {
13430
+ <div class="reference-line-row">
13431
+ <kendo-numerictextbox [(ngModel)]="refLine.value" [format]="'n0'" [placeholder]="'Value'" [spinners]="false" style="width: 100px;"></kendo-numerictextbox>
13432
+ <kendo-textbox [(ngModel)]="refLine.label" [placeholder]="'Label (e.g. Limit)'" style="flex: 1;"></kendo-textbox>
13433
+ <kendo-textbox [(ngModel)]="refLine.color" [placeholder]="'Color (#ef4444)'" style="width: 110px;"></kendo-textbox>
13434
+ <button kendoButton fillMode="flat" (click)="removeReferenceLine($index)">Remove</button>
13435
+ </div>
13436
+ }
13437
+ <button kendoButton fillMode="flat" (click)="addReferenceLine()">+ Add Reference Line</button>
13438
+ </div>
13174
13439
  </div>
13175
13440
 
13176
13441
  <div class="action-bar mm-dialog-actions">
@@ -13184,7 +13449,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
13184
13449
  </button>
13185
13450
  </div>
13186
13451
  </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"] }]
13452
+ `, 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
13453
  }], propDecorators: { querySelector: [{
13189
13454
  type: ViewChild,
13190
13455
  args: ['querySelector']
@@ -13208,6 +13473,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
13208
13473
  type: Input
13209
13474
  }], initialShowMarkers: [{
13210
13475
  type: Input
13476
+ }], initialReferenceLines: [{
13477
+ type: Input
13211
13478
  }], initialFilters: [{
13212
13479
  type: Input
13213
13480
  }] } });
@@ -13667,7 +13934,7 @@ class HeatmapWidgetComponent {
13667
13934
  </kendo-chart>
13668
13935
  }
13669
13936
  </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$6.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$6.XAxisComponent, selector: "kendo-chart-x-axis" }, { kind: "component", type: i1$6.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$6.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$6.YAxisComponent, selector: "kendo-chart-y-axis" }, { kind: "component", type: i1$6.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$6.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$6.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$6.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$6.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$6.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$6.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
13937
+ `, 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
13938
  }
13672
13939
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: HeatmapWidgetComponent, decorators: [{
13673
13940
  type: Component,
@@ -17021,7 +17288,7 @@ class MarkdownWidgetComponent {
17021
17288
  this._data.set(this.config?.content ?? '');
17022
17289
  }
17023
17290
  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$2.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" }] });
17291
+ 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
17292
  }
17026
17293
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MarkdownWidgetComponent, decorators: [{
17027
17294
  type: Component,
@@ -17159,7 +17426,7 @@ Variables: $variableName or \${variableName}">
17159
17426
  <button kendoButton themeColor="primary" (click)="onSave()">Save</button>
17160
17427
  </div>
17161
17428
  </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$2.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"] }] });
17429
+ `, 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
17430
  }
17164
17431
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MarkdownConfigDialogComponent, decorators: [{
17165
17432
  type: Component,
@@ -17279,150 +17546,1621 @@ Variables: $variableName or \${variableName}">
17279
17546
  type: Input
17280
17547
  }] } });
17281
17548
 
17282
- class ProcessDataService {
17283
- dataService = inject(MeshBoardDataService);
17284
- variableService = inject(MeshBoardVariableService);
17285
- stateService = inject(MeshBoardStateService);
17286
- getProcessDiagramGQL = inject(GetProcessDiagramDtoGQL);
17287
- getProcessDiagramsGQL = inject(GetProcessDiagramsDtoGQL);
17288
- createProcessDiagramGQL = inject(CreateProcessDiagramDtoGQL);
17289
- updateProcessDiagramGQL = inject(UpdateProcessDiagramDtoGQL);
17290
- executeRuntimeQueryGQL = inject(ExecuteRuntimeQueryDtoGQL);
17291
- /**
17292
- * Loads a list of available process diagrams from the backend
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
- }
17549
+ class StatusListWidgetComponent {
17550
+ getEntitiesByCkTypeGQL = inject(GetEntitiesByCkTypeDtoGQL);
17551
+ config;
17552
+ _isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
17553
+ _error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
17554
+ _items = signal([], ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
17555
+ isLoading = this._isLoading.asReadonly();
17556
+ error = this._error.asReadonly();
17557
+ data = this._items.asReadonly();
17558
+ items = this._items.asReadonly();
17559
+ isNotConfigured() {
17560
+ return !this.config?.ckTypeId || !this.config?.labelField || !this.config?.statusField;
17320
17561
  }
17321
- /**
17322
- * Loads a process diagram configuration from the backend
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
- };
17562
+ ngOnInit() {
17563
+ this.loadData();
17374
17564
  }
17375
- /**
17376
- * Creates a new process diagram in the backend
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');
17565
+ ngOnChanges(changes) {
17566
+ if (changes['config'] && !changes['config'].firstChange) {
17567
+ this.loadData();
17389
17568
  }
17390
- return {
17391
- ...diagram,
17392
- id: created.rtId
17393
- };
17394
17569
  }
17395
- /**
17396
- * Updates an existing process diagram in the backend
17397
- *
17398
- * @param diagram - The process diagram configuration to update
17399
- * @returns The updated diagram
17400
- */
17401
- async updateDiagram(diagram) {
17402
- if (!diagram.id) {
17403
- throw new Error('Cannot update diagram without rtId');
17570
+ refresh() {
17571
+ this.loadData();
17572
+ }
17573
+ async loadData() {
17574
+ if (this.isNotConfigured())
17575
+ return;
17576
+ this._isLoading.set(true);
17577
+ this._error.set(null);
17578
+ try {
17579
+ const result = await firstValueFrom(this.getEntitiesByCkTypeGQL.fetch({
17580
+ variables: {
17581
+ ckTypeId: this.config.ckTypeId,
17582
+ first: 50
17583
+ }
17584
+ }));
17585
+ const entities = result.data?.runtime?.runtimeEntities?.items ?? [];
17586
+ const items = [];
17587
+ for (const entity of entities) {
17588
+ if (!entity)
17589
+ continue;
17590
+ const attrs = (entity.attributes?.items ?? [])
17591
+ .filter((a) => a != null && a.attributeName != null);
17592
+ const label = this.getAttributeValue(attrs, this.config.labelField) ?? '';
17593
+ const status = this.getAttributeValue(attrs, this.config.statusField) ?? '';
17594
+ const colorConfig = this.config.statusColors?.[status];
17595
+ items.push({
17596
+ label,
17597
+ status,
17598
+ color: colorConfig?.color ?? '#6b7280',
17599
+ displayLabel: colorConfig?.label ?? status
17600
+ });
17601
+ }
17602
+ this._items.set(items);
17404
17603
  }
17405
- const input = {
17406
- rtId: diagram.id,
17407
- item: this.toInputDto(diagram)
17408
- };
17409
- const result = await firstValueFrom(this.updateProcessDiagramGQL.mutate({
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');
17604
+ catch (err) {
17605
+ this._error.set(err instanceof Error ? err.message : 'Failed to load data');
17606
+ }
17607
+ finally {
17608
+ this._isLoading.set(false);
17415
17609
  }
17416
- return diagram;
17417
17610
  }
17418
- /**
17419
- * Saves a process diagram (creates new or updates existing)
17420
- *
17421
- * @param diagram - The process diagram configuration to save
17422
- * @param isNew - Whether this is a new diagram (default: false)
17423
- * @returns The saved diagram
17424
- */
17425
- async saveDiagram(diagram, isNew = false) {
17611
+ getAttributeValue(attrs, field) {
17612
+ const attr = attrs.find(a => a.attributeName === field);
17613
+ return attr?.value != null ? String(attr.value) : null;
17614
+ }
17615
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: StatusListWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
17616
+ 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: `
17617
+ <div class="status-list-widget">
17618
+ @if (isNotConfigured()) {
17619
+ <mm-widget-not-configured></mm-widget-not-configured>
17620
+ } @else if (error()) {
17621
+ <div class="error-message">{{ error() }}</div>
17622
+ } @else {
17623
+ <div class="status-list">
17624
+ @for (item of items(); track item.label) {
17625
+ <div class="status-list-item">
17626
+ <span class="item-label">{{ item.label }}</span>
17627
+ <span class="item-badge" [style.background-color]="item.color">
17628
+ {{ item.displayLabel }}
17629
+ </span>
17630
+ </div>
17631
+ }
17632
+ @if (!isLoading() && items().length === 0) {
17633
+ <div class="empty-message">No items found</div>
17634
+ }
17635
+ </div>
17636
+ }
17637
+ </div>
17638
+ `, 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" }] });
17639
+ }
17640
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: StatusListWidgetComponent, decorators: [{
17641
+ type: Component,
17642
+ args: [{ selector: 'mm-status-list-widget', standalone: true, imports: [CommonModule, WidgetNotConfiguredComponent], template: `
17643
+ <div class="status-list-widget">
17644
+ @if (isNotConfigured()) {
17645
+ <mm-widget-not-configured></mm-widget-not-configured>
17646
+ } @else if (error()) {
17647
+ <div class="error-message">{{ error() }}</div>
17648
+ } @else {
17649
+ <div class="status-list">
17650
+ @for (item of items(); track item.label) {
17651
+ <div class="status-list-item">
17652
+ <span class="item-label">{{ item.label }}</span>
17653
+ <span class="item-badge" [style.background-color]="item.color">
17654
+ {{ item.displayLabel }}
17655
+ </span>
17656
+ </div>
17657
+ }
17658
+ @if (!isLoading() && items().length === 0) {
17659
+ <div class="empty-message">No items found</div>
17660
+ }
17661
+ </div>
17662
+ }
17663
+ </div>
17664
+ `, 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"] }]
17665
+ }], propDecorators: { config: [{
17666
+ type: Input
17667
+ }] } });
17668
+
17669
+ class StatusListConfigDialogComponent {
17670
+ windowRef = inject(WindowRef);
17671
+ initialCkTypeId;
17672
+ initialLabelField;
17673
+ initialStatusField;
17674
+ initialStatusColors;
17675
+ form = {
17676
+ ckTypeId: '',
17677
+ labelField: '',
17678
+ statusField: ''
17679
+ };
17680
+ statusColorEntries = [];
17681
+ get isValid() {
17682
+ return !!this.form.ckTypeId && !!this.form.labelField && !!this.form.statusField;
17683
+ }
17684
+ ngOnInit() {
17685
+ this.form.ckTypeId = this.initialCkTypeId ?? '';
17686
+ this.form.labelField = this.initialLabelField ?? '';
17687
+ this.form.statusField = this.initialStatusField ?? '';
17688
+ if (this.initialStatusColors) {
17689
+ this.statusColorEntries = Object.entries(this.initialStatusColors).map(([key, val]) => ({
17690
+ key,
17691
+ color: val.color,
17692
+ label: val.label ?? ''
17693
+ }));
17694
+ }
17695
+ }
17696
+ addStatusColor() {
17697
+ this.statusColorEntries.push({ key: '', color: '#10b981', label: '' });
17698
+ }
17699
+ removeStatusColor(index) {
17700
+ this.statusColorEntries.splice(index, 1);
17701
+ }
17702
+ onSave() {
17703
+ const statusColors = {};
17704
+ for (const entry of this.statusColorEntries) {
17705
+ if (entry.key) {
17706
+ statusColors[entry.key] = { color: entry.color, label: entry.label || undefined };
17707
+ }
17708
+ }
17709
+ this.windowRef.close({
17710
+ ckTypeId: this.form.ckTypeId,
17711
+ labelField: this.form.labelField,
17712
+ statusField: this.form.statusField,
17713
+ statusColors: Object.keys(statusColors).length > 0 ? statusColors : undefined
17714
+ });
17715
+ }
17716
+ onCancel() {
17717
+ this.windowRef.close();
17718
+ }
17719
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: StatusListConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
17720
+ 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: `
17721
+ <div class="config-container">
17722
+ <div class="config-form">
17723
+ <div class="form-field">
17724
+ <label>Entity Type <span class="required">*</span></label>
17725
+ <kendo-textbox [(ngModel)]="form.ckTypeId" placeholder="e.g. Environment/ComplianceRecord"></kendo-textbox>
17726
+ <p class="field-hint">Full CK type ID</p>
17727
+ </div>
17728
+
17729
+ <div class="form-field">
17730
+ <label>Label Field <span class="required">*</span></label>
17731
+ <kendo-textbox [(ngModel)]="form.labelField" placeholder="e.g. name"></kendo-textbox>
17732
+ <p class="field-hint">Attribute name for the item label</p>
17733
+ </div>
17734
+
17735
+ <div class="form-field">
17736
+ <label>Status Field <span class="required">*</span></label>
17737
+ <kendo-textbox [(ngModel)]="form.statusField" placeholder="e.g. complianceStatus"></kendo-textbox>
17738
+ <p class="field-hint">Attribute name for the status value (enum)</p>
17739
+ </div>
17740
+
17741
+ <div class="form-section">
17742
+ <h4>Status Colors</h4>
17743
+ <p class="field-hint">Map status values to badge colors and labels.</p>
17744
+ @for (entry of statusColorEntries; track $index) {
17745
+ <div class="color-row">
17746
+ <kendo-textbox [(ngModel)]="entry.key" placeholder="Status value" style="width: 120px;"></kendo-textbox>
17747
+ <kendo-textbox [(ngModel)]="entry.color" placeholder="#10b981" style="width: 100px;"></kendo-textbox>
17748
+ <kendo-textbox [(ngModel)]="entry.label" placeholder="Badge label" style="flex: 1;"></kendo-textbox>
17749
+ <button kendoButton fillMode="flat" (click)="removeStatusColor($index)">Remove</button>
17750
+ </div>
17751
+ }
17752
+ <button kendoButton fillMode="flat" (click)="addStatusColor()">+ Add Status Color</button>
17753
+ </div>
17754
+ </div>
17755
+
17756
+ <div class="action-bar mm-dialog-actions">
17757
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
17758
+ <button kendoButton themeColor="primary" [disabled]="!isValid" (click)="onSave()">Save</button>
17759
+ </div>
17760
+ </div>
17761
+ `, 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"] }] });
17762
+ }
17763
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: StatusListConfigDialogComponent, decorators: [{
17764
+ type: Component,
17765
+ args: [{ selector: 'mm-status-list-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule], template: `
17766
+ <div class="config-container">
17767
+ <div class="config-form">
17768
+ <div class="form-field">
17769
+ <label>Entity Type <span class="required">*</span></label>
17770
+ <kendo-textbox [(ngModel)]="form.ckTypeId" placeholder="e.g. Environment/ComplianceRecord"></kendo-textbox>
17771
+ <p class="field-hint">Full CK type ID</p>
17772
+ </div>
17773
+
17774
+ <div class="form-field">
17775
+ <label>Label Field <span class="required">*</span></label>
17776
+ <kendo-textbox [(ngModel)]="form.labelField" placeholder="e.g. name"></kendo-textbox>
17777
+ <p class="field-hint">Attribute name for the item label</p>
17778
+ </div>
17779
+
17780
+ <div class="form-field">
17781
+ <label>Status Field <span class="required">*</span></label>
17782
+ <kendo-textbox [(ngModel)]="form.statusField" placeholder="e.g. complianceStatus"></kendo-textbox>
17783
+ <p class="field-hint">Attribute name for the status value (enum)</p>
17784
+ </div>
17785
+
17786
+ <div class="form-section">
17787
+ <h4>Status Colors</h4>
17788
+ <p class="field-hint">Map status values to badge colors and labels.</p>
17789
+ @for (entry of statusColorEntries; track $index) {
17790
+ <div class="color-row">
17791
+ <kendo-textbox [(ngModel)]="entry.key" placeholder="Status value" style="width: 120px;"></kendo-textbox>
17792
+ <kendo-textbox [(ngModel)]="entry.color" placeholder="#10b981" style="width: 100px;"></kendo-textbox>
17793
+ <kendo-textbox [(ngModel)]="entry.label" placeholder="Badge label" style="flex: 1;"></kendo-textbox>
17794
+ <button kendoButton fillMode="flat" (click)="removeStatusColor($index)">Remove</button>
17795
+ </div>
17796
+ }
17797
+ <button kendoButton fillMode="flat" (click)="addStatusColor()">+ Add Status Color</button>
17798
+ </div>
17799
+ </div>
17800
+
17801
+ <div class="action-bar mm-dialog-actions">
17802
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
17803
+ <button kendoButton themeColor="primary" [disabled]="!isValid" (click)="onSave()">Save</button>
17804
+ </div>
17805
+ </div>
17806
+ `, 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"] }]
17807
+ }], propDecorators: { initialCkTypeId: [{
17808
+ type: Input
17809
+ }], initialLabelField: [{
17810
+ type: Input
17811
+ }], initialStatusField: [{
17812
+ type: Input
17813
+ }], initialStatusColors: [{
17814
+ type: Input
17815
+ }] } });
17816
+
17817
+ class SummaryCardWidgetComponent {
17818
+ entityGQL = inject(GetDashboardEntityDtoGQL);
17819
+ dataService = inject(DashboardDataService);
17820
+ config;
17821
+ _isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
17822
+ _error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
17823
+ _tileValues = signal([], ...(ngDevMode ? [{ debugName: "_tileValues" }] : /* istanbul ignore next */ []));
17824
+ isLoading = this._isLoading.asReadonly();
17825
+ error = this._error.asReadonly();
17826
+ data = this._tileValues.asReadonly();
17827
+ tileValues = this._tileValues.asReadonly();
17828
+ isNotConfigured() {
17829
+ return !this.config?.tiles?.length;
17830
+ }
17831
+ ngOnInit() {
17832
+ this.loadData();
17833
+ }
17834
+ ngOnChanges(changes) {
17835
+ if (changes['config'] && !changes['config'].firstChange) {
17836
+ this.loadData();
17837
+ }
17838
+ }
17839
+ refresh() {
17840
+ this.loadData();
17841
+ }
17842
+ async loadData() {
17843
+ if (this.isNotConfigured())
17844
+ return;
17845
+ this._isLoading.set(true);
17846
+ this._error.set(null);
17847
+ try {
17848
+ // Cache entities by rtId to avoid duplicate fetches
17849
+ const entityCache = new Map();
17850
+ const results = await Promise.all(this.config.tiles.map(tile => this.fetchTileValue(tile, entityCache)));
17851
+ const tileValues = this.config.tiles.map((tile, i) => ({
17852
+ id: tile.id,
17853
+ label: tile.label,
17854
+ value: this.formatValue(results[i]),
17855
+ prefix: tile.prefix ?? '',
17856
+ suffix: tile.suffix ?? '',
17857
+ color: tile.color ?? 'default',
17858
+ fullWidth: tile.size === 'full'
17859
+ }));
17860
+ this._tileValues.set(tileValues);
17861
+ }
17862
+ catch (err) {
17863
+ this._error.set(err instanceof Error ? err.message : 'Failed to load data');
17864
+ }
17865
+ finally {
17866
+ this._isLoading.set(false);
17867
+ }
17868
+ }
17869
+ async fetchTileValue(tile, entityCache) {
17870
+ if (tile.entitySource) {
17871
+ const { rtId, ckTypeId, attributePath } = tile.entitySource;
17872
+ if (!entityCache.has(rtId)) {
17873
+ const attrs = await this.fetchEntityAttributes(rtId, ckTypeId);
17874
+ entityCache.set(rtId, attrs);
17875
+ }
17876
+ return entityCache.get(rtId)?.get(attributePath) ?? null;
17877
+ }
17878
+ if (tile.aggregationSource) {
17879
+ const query = {
17880
+ id: tile.id,
17881
+ ckTypeId: tile.aggregationSource.ckTypeId,
17882
+ aggregation: tile.aggregationSource.aggregation,
17883
+ attribute: tile.aggregationSource.attribute,
17884
+ filters: tile.aggregationSource.filters
17885
+ };
17886
+ const results = await this.dataService.fetchAggregations([query]);
17887
+ return results.get(tile.id) ?? null;
17888
+ }
17889
+ return null;
17890
+ }
17891
+ async fetchEntityAttributes(rtId, ckTypeId) {
17892
+ const result = await firstValueFrom(this.entityGQL.fetch({
17893
+ variables: { rtId, ckTypeId }
17894
+ }));
17895
+ const attrs = new Map();
17896
+ const items = result.data?.runtime?.runtimeEntities?.items?.[0]?.attributes?.items ?? [];
17897
+ for (const item of items) {
17898
+ if (item?.attributeName) {
17899
+ attrs.set(item.attributeName, item.value);
17900
+ }
17901
+ }
17902
+ return attrs;
17903
+ }
17904
+ formatValue(value) {
17905
+ if (value == null)
17906
+ return '-';
17907
+ if (typeof value === 'number') {
17908
+ return value.toLocaleString('de-AT', {
17909
+ minimumFractionDigits: value % 1 !== 0 ? 1 : 0,
17910
+ maximumFractionDigits: 2
17911
+ });
17912
+ }
17913
+ return String(value);
17914
+ }
17915
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SummaryCardWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
17916
+ 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: `
17917
+ <div class="summary-card-widget">
17918
+ @if (isNotConfigured()) {
17919
+ <mm-widget-not-configured></mm-widget-not-configured>
17920
+ } @else if (error()) {
17921
+ <div class="error-message">{{ error() }}</div>
17922
+ } @else {
17923
+ <div class="summary-grid" [style.--columns]="config.columns ?? 2">
17924
+ @for (tile of tileValues(); track tile.id) {
17925
+ <div class="tile" [class]="tile.color" [class.full-width]="tile.fullWidth">
17926
+ <div class="tile-value">
17927
+ {{ tile.prefix }}{{ tile.value }}{{ tile.suffix }}
17928
+ </div>
17929
+ <div class="tile-label">{{ tile.label }}</div>
17930
+ </div>
17931
+ }
17932
+ </div>
17933
+ }
17934
+ </div>
17935
+ `, 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" }] });
17936
+ }
17937
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SummaryCardWidgetComponent, decorators: [{
17938
+ type: Component,
17939
+ args: [{ selector: 'mm-summary-card-widget', standalone: true, imports: [CommonModule, WidgetNotConfiguredComponent], template: `
17940
+ <div class="summary-card-widget">
17941
+ @if (isNotConfigured()) {
17942
+ <mm-widget-not-configured></mm-widget-not-configured>
17943
+ } @else if (error()) {
17944
+ <div class="error-message">{{ error() }}</div>
17945
+ } @else {
17946
+ <div class="summary-grid" [style.--columns]="config.columns ?? 2">
17947
+ @for (tile of tileValues(); track tile.id) {
17948
+ <div class="tile" [class]="tile.color" [class.full-width]="tile.fullWidth">
17949
+ <div class="tile-value">
17950
+ {{ tile.prefix }}{{ tile.value }}{{ tile.suffix }}
17951
+ </div>
17952
+ <div class="tile-label">{{ tile.label }}</div>
17953
+ </div>
17954
+ }
17955
+ </div>
17956
+ }
17957
+ </div>
17958
+ `, 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"] }]
17959
+ }], propDecorators: { config: [{
17960
+ type: Input
17961
+ }] } });
17962
+
17963
+ class SummaryCardConfigDialogComponent {
17964
+ windowRef = inject(WindowRef);
17965
+ initialColumns;
17966
+ initialTiles;
17967
+ form = {
17968
+ columns: 2,
17969
+ tiles: []
17970
+ };
17971
+ colorOptions = ['default', 'primary', 'success', 'warning', 'error'];
17972
+ sizeOptions = ['normal', 'full'];
17973
+ sourceTypeOptions = ['entity', 'aggregation'];
17974
+ aggregationOptions = ['count', 'sum', 'avg', 'min', 'max'];
17975
+ get isValid() {
17976
+ return this.form.tiles.length > 0 && this.form.tiles.every(t => t.label && t.ckTypeId);
17977
+ }
17978
+ ngOnInit() {
17979
+ this.form.columns = this.initialColumns ?? 2;
17980
+ if (this.initialTiles) {
17981
+ this.form.tiles = this.initialTiles.map((t, i) => ({
17982
+ id: t.id || `tile-${i}`,
17983
+ label: t.label,
17984
+ prefix: t.prefix ?? '',
17985
+ suffix: t.suffix ?? '',
17986
+ color: t.color ?? 'default',
17987
+ size: t.size ?? 'normal',
17988
+ sourceType: t.entitySource ? 'entity' : 'aggregation',
17989
+ rtId: t.entitySource?.rtId ?? '',
17990
+ ckTypeId: t.entitySource?.ckTypeId ?? t.aggregationSource?.ckTypeId ?? '',
17991
+ attributePath: t.entitySource?.attributePath ?? '',
17992
+ aggregation: t.aggregationSource?.aggregation ?? 'count',
17993
+ aggAttribute: t.aggregationSource?.attribute ?? ''
17994
+ }));
17995
+ }
17996
+ }
17997
+ addTile() {
17998
+ this.form.tiles.push({
17999
+ id: `tile-${Date.now()}`,
18000
+ label: '', prefix: '', suffix: '',
18001
+ color: 'default', size: 'normal',
18002
+ sourceType: 'entity',
18003
+ rtId: '', ckTypeId: '', attributePath: '',
18004
+ aggregation: 'count', aggAttribute: ''
18005
+ });
18006
+ }
18007
+ removeTile(index) {
18008
+ this.form.tiles.splice(index, 1);
18009
+ }
18010
+ onSave() {
18011
+ const tiles = this.form.tiles.map(t => {
18012
+ const tile = {
18013
+ id: t.id,
18014
+ label: t.label,
18015
+ prefix: t.prefix || undefined,
18016
+ suffix: t.suffix || undefined,
18017
+ color: t.color || undefined,
18018
+ size: t.size || undefined
18019
+ };
18020
+ if (t.sourceType === 'entity') {
18021
+ tile.entitySource = { rtId: t.rtId, ckTypeId: t.ckTypeId, attributePath: t.attributePath };
18022
+ }
18023
+ else {
18024
+ tile.aggregationSource = {
18025
+ ckTypeId: t.ckTypeId,
18026
+ aggregation: t.aggregation,
18027
+ attribute: t.aggAttribute || undefined
18028
+ };
18029
+ }
18030
+ return tile;
18031
+ });
18032
+ this.windowRef.close({
18033
+ ckTypeId: '',
18034
+ columns: this.form.columns,
18035
+ tiles
18036
+ });
18037
+ }
18038
+ onCancel() {
18039
+ this.windowRef.close();
18040
+ }
18041
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SummaryCardConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18042
+ 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: `
18043
+ <div class="config-container">
18044
+ <div class="config-form">
18045
+ <div class="form-field">
18046
+ <label>Grid Columns</label>
18047
+ <kendo-numerictextbox [(ngModel)]="form.columns" [min]="1" [max]="6" [format]="'n0'" [spinners]="true"></kendo-numerictextbox>
18048
+ </div>
18049
+
18050
+ <h4>Tiles</h4>
18051
+ @for (tile of form.tiles; track $index) {
18052
+ <div class="tile-config">
18053
+ <div class="tile-header">
18054
+ <strong>Tile {{ $index + 1 }}</strong>
18055
+ <button kendoButton fillMode="flat" (click)="removeTile($index)">Remove</button>
18056
+ </div>
18057
+ <div class="tile-row">
18058
+ <kendo-textbox [(ngModel)]="tile.label" placeholder="Label" style="flex: 1;"></kendo-textbox>
18059
+ <kendo-textbox [(ngModel)]="tile.prefix" placeholder="Prefix" style="width: 60px;"></kendo-textbox>
18060
+ <kendo-textbox [(ngModel)]="tile.suffix" placeholder="Suffix" style="width: 80px;"></kendo-textbox>
18061
+ </div>
18062
+ <div class="tile-row">
18063
+ <kendo-dropdownlist [data]="colorOptions" [valuePrimitive]="true" [(ngModel)]="tile.color" style="width: 110px;"></kendo-dropdownlist>
18064
+ <kendo-dropdownlist [data]="sizeOptions" [valuePrimitive]="true" [(ngModel)]="tile.size" style="width: 110px;"></kendo-dropdownlist>
18065
+ <kendo-dropdownlist [data]="sourceTypeOptions" [valuePrimitive]="true" [(ngModel)]="tile.sourceType" style="width: 120px;"></kendo-dropdownlist>
18066
+ </div>
18067
+ @if (tile.sourceType === 'entity') {
18068
+ <div class="tile-row">
18069
+ <kendo-textbox [(ngModel)]="tile.ckTypeId" placeholder="CK Type ID" style="flex: 1;"></kendo-textbox>
18070
+ <kendo-textbox [(ngModel)]="tile.rtId" placeholder="Runtime ID" style="flex: 1;"></kendo-textbox>
18071
+ <kendo-textbox [(ngModel)]="tile.attributePath" placeholder="Attribute" style="width: 120px;"></kendo-textbox>
18072
+ </div>
18073
+ }
18074
+ @if (tile.sourceType === 'aggregation') {
18075
+ <div class="tile-row">
18076
+ <kendo-textbox [(ngModel)]="tile.ckTypeId" placeholder="CK Type ID" style="flex: 1;"></kendo-textbox>
18077
+ <kendo-dropdownlist [data]="aggregationOptions" [valuePrimitive]="true" [(ngModel)]="tile.aggregation" style="width: 100px;"></kendo-dropdownlist>
18078
+ <kendo-textbox [(ngModel)]="tile.aggAttribute" placeholder="Attribute (for sum/avg)" style="flex: 1;"></kendo-textbox>
18079
+ </div>
18080
+ }
18081
+ </div>
18082
+ }
18083
+ <button kendoButton fillMode="flat" (click)="addTile()">+ Add Tile</button>
18084
+ </div>
18085
+
18086
+ <div class="action-bar mm-dialog-actions">
18087
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
18088
+ <button kendoButton themeColor="primary" [disabled]="!isValid" (click)="onSave()">Save</button>
18089
+ </div>
18090
+ </div>
18091
+ `, 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"] }] });
18092
+ }
18093
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SummaryCardConfigDialogComponent, decorators: [{
18094
+ type: Component,
18095
+ args: [{ selector: 'mm-summary-card-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule, DropDownsModule], template: `
18096
+ <div class="config-container">
18097
+ <div class="config-form">
18098
+ <div class="form-field">
18099
+ <label>Grid Columns</label>
18100
+ <kendo-numerictextbox [(ngModel)]="form.columns" [min]="1" [max]="6" [format]="'n0'" [spinners]="true"></kendo-numerictextbox>
18101
+ </div>
18102
+
18103
+ <h4>Tiles</h4>
18104
+ @for (tile of form.tiles; track $index) {
18105
+ <div class="tile-config">
18106
+ <div class="tile-header">
18107
+ <strong>Tile {{ $index + 1 }}</strong>
18108
+ <button kendoButton fillMode="flat" (click)="removeTile($index)">Remove</button>
18109
+ </div>
18110
+ <div class="tile-row">
18111
+ <kendo-textbox [(ngModel)]="tile.label" placeholder="Label" style="flex: 1;"></kendo-textbox>
18112
+ <kendo-textbox [(ngModel)]="tile.prefix" placeholder="Prefix" style="width: 60px;"></kendo-textbox>
18113
+ <kendo-textbox [(ngModel)]="tile.suffix" placeholder="Suffix" style="width: 80px;"></kendo-textbox>
18114
+ </div>
18115
+ <div class="tile-row">
18116
+ <kendo-dropdownlist [data]="colorOptions" [valuePrimitive]="true" [(ngModel)]="tile.color" style="width: 110px;"></kendo-dropdownlist>
18117
+ <kendo-dropdownlist [data]="sizeOptions" [valuePrimitive]="true" [(ngModel)]="tile.size" style="width: 110px;"></kendo-dropdownlist>
18118
+ <kendo-dropdownlist [data]="sourceTypeOptions" [valuePrimitive]="true" [(ngModel)]="tile.sourceType" style="width: 120px;"></kendo-dropdownlist>
18119
+ </div>
18120
+ @if (tile.sourceType === 'entity') {
18121
+ <div class="tile-row">
18122
+ <kendo-textbox [(ngModel)]="tile.ckTypeId" placeholder="CK Type ID" style="flex: 1;"></kendo-textbox>
18123
+ <kendo-textbox [(ngModel)]="tile.rtId" placeholder="Runtime ID" style="flex: 1;"></kendo-textbox>
18124
+ <kendo-textbox [(ngModel)]="tile.attributePath" placeholder="Attribute" style="width: 120px;"></kendo-textbox>
18125
+ </div>
18126
+ }
18127
+ @if (tile.sourceType === 'aggregation') {
18128
+ <div class="tile-row">
18129
+ <kendo-textbox [(ngModel)]="tile.ckTypeId" placeholder="CK Type ID" style="flex: 1;"></kendo-textbox>
18130
+ <kendo-dropdownlist [data]="aggregationOptions" [valuePrimitive]="true" [(ngModel)]="tile.aggregation" style="width: 100px;"></kendo-dropdownlist>
18131
+ <kendo-textbox [(ngModel)]="tile.aggAttribute" placeholder="Attribute (for sum/avg)" style="flex: 1;"></kendo-textbox>
18132
+ </div>
18133
+ }
18134
+ </div>
18135
+ }
18136
+ <button kendoButton fillMode="flat" (click)="addTile()">+ Add Tile</button>
18137
+ </div>
18138
+
18139
+ <div class="action-bar mm-dialog-actions">
18140
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
18141
+ <button kendoButton themeColor="primary" [disabled]="!isValid" (click)="onSave()">Save</button>
18142
+ </div>
18143
+ </div>
18144
+ `, 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"] }]
18145
+ }], propDecorators: { initialColumns: [{
18146
+ type: Input
18147
+ }], initialTiles: [{
18148
+ type: Input
18149
+ }] } });
18150
+
18151
+ function getAlertSeverityColor(level) {
18152
+ switch (level?.toUpperCase()) {
18153
+ case 'CRITICAL': return 'var(--mm-alert-critical, #dc2626)';
18154
+ case 'ERROR': return 'var(--mm-alert-error, #ef4444)';
18155
+ case 'WARNING': return 'var(--mm-alert-warning, #f59e0b)';
18156
+ case 'INFORMATION': return 'var(--mm-alert-info, #3b82f6)';
18157
+ case 'DEBUG': return 'var(--mm-alert-debug, #6b7280)';
18158
+ default: return 'var(--mm-alert-debug, #6b7280)';
18159
+ }
18160
+ }
18161
+ function getAlertSeverityIcon(level) {
18162
+ switch (level?.toUpperCase()) {
18163
+ case 'CRITICAL':
18164
+ case 'ERROR':
18165
+ return xCircleIcon;
18166
+ case 'WARNING':
18167
+ return exclamationCircleIcon;
18168
+ default:
18169
+ return infoCircleIcon;
18170
+ }
18171
+ }
18172
+ function getAlertSeverityOrder(level) {
18173
+ switch (level?.toUpperCase()) {
18174
+ case 'CRITICAL': return 4;
18175
+ case 'ERROR': return 3;
18176
+ case 'WARNING': return 2;
18177
+ case 'INFORMATION': return 1;
18178
+ case 'DEBUG': return 0;
18179
+ default: return -1;
18180
+ }
18181
+ }
18182
+ const DEFAULT_ALERT_CK_TYPE = 'System.Notification/StatefulEvent';
18183
+
18184
+ class AlertBannerWidgetComponent {
18185
+ getEntitiesByCkTypeGQL = inject(GetEntitiesByCkTypeDtoGQL);
18186
+ rotationTimer = null;
18187
+ config;
18188
+ _isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
18189
+ _error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
18190
+ _items = signal([], ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
18191
+ _currentIndex = signal(0, ...(ngDevMode ? [{ debugName: "_currentIndex" }] : /* istanbul ignore next */ []));
18192
+ isLoading = this._isLoading.asReadonly();
18193
+ error = this._error.asReadonly();
18194
+ data = this._items.asReadonly();
18195
+ items = this._items.asReadonly();
18196
+ currentIndex = this._currentIndex.asReadonly();
18197
+ currentAlert = computed(() => {
18198
+ const items = this._items();
18199
+ if (items.length === 0)
18200
+ return null;
18201
+ return items[this._currentIndex() % items.length];
18202
+ }, ...(ngDevMode ? [{ debugName: "currentAlert" }] : /* istanbul ignore next */ []));
18203
+ currentColor = computed(() => {
18204
+ const alert = this.currentAlert();
18205
+ return alert ? getAlertSeverityColor(alert.level) : '';
18206
+ }, ...(ngDevMode ? [{ debugName: "currentColor" }] : /* istanbul ignore next */ []));
18207
+ currentIcon = computed(() => {
18208
+ const alert = this.currentAlert();
18209
+ return alert ? getAlertSeverityIcon(alert.level) : getAlertSeverityIcon('DEBUG');
18210
+ }, ...(ngDevMode ? [{ debugName: "currentIcon" }] : /* istanbul ignore next */ []));
18211
+ isNotConfigured() {
18212
+ return false;
18213
+ }
18214
+ ngOnInit() {
18215
+ this.loadData();
18216
+ }
18217
+ ngOnChanges(changes) {
18218
+ if (changes['config'] && !changes['config'].firstChange) {
18219
+ this.loadData();
18220
+ }
18221
+ }
18222
+ ngOnDestroy() {
18223
+ this.stopRotation();
18224
+ }
18225
+ refresh() {
18226
+ this.loadData();
18227
+ }
18228
+ startRotation() {
18229
+ this.stopRotation();
18230
+ const interval = this.config?.rotationInterval ?? 5000;
18231
+ if (this._items().length > 1) {
18232
+ this.rotationTimer = setInterval(() => {
18233
+ this._currentIndex.update(i => (i + 1) % this._items().length);
18234
+ }, interval);
18235
+ }
18236
+ }
18237
+ stopRotation() {
18238
+ if (this.rotationTimer) {
18239
+ clearInterval(this.rotationTimer);
18240
+ this.rotationTimer = null;
18241
+ }
18242
+ }
18243
+ async loadData() {
18244
+ this._isLoading.set(true);
18245
+ this._error.set(null);
18246
+ try {
18247
+ const ckTypeId = this.config?.ckTypeId || DEFAULT_ALERT_CK_TYPE;
18248
+ const result = await firstValueFrom(this.getEntitiesByCkTypeGQL.fetch({
18249
+ variables: {
18250
+ ckTypeId,
18251
+ first: this.config?.maxAlerts ?? 20,
18252
+ fieldFilters: [
18253
+ { attributePath: 'state', operator: FieldFilterOperatorsDto.EqualsDto, comparisonValue: '0' }
18254
+ ],
18255
+ sort: [
18256
+ { attributePath: 'level', sortOrder: SortOrdersDto.DescendingDto }
18257
+ ]
18258
+ }
18259
+ }));
18260
+ const entities = result.data?.runtime?.runtimeEntities?.items ?? [];
18261
+ const items = [];
18262
+ for (const entity of entities) {
18263
+ if (!entity)
18264
+ continue;
18265
+ const attrs = (entity.attributes?.items ?? [])
18266
+ .filter((a) => a != null && a.attributeName != null);
18267
+ items.push({
18268
+ rtId: entity.rtId ?? '',
18269
+ message: this.getAttr(attrs, 'message') ?? '',
18270
+ level: this.getAttr(attrs, 'level') ?? 'INFORMATION',
18271
+ state: this.getAttr(attrs, 'state') ?? '',
18272
+ source: this.getAttr(attrs, 'source') ?? '',
18273
+ timestamp: entity.rtCreationDateTime ?? undefined
18274
+ });
18275
+ }
18276
+ items.sort((a, b) => getAlertSeverityOrder(b.level) - getAlertSeverityOrder(a.level));
18277
+ this._items.set(items);
18278
+ this._currentIndex.set(0);
18279
+ this.startRotation();
18280
+ }
18281
+ catch (err) {
18282
+ this._error.set(err instanceof Error ? err.message : 'Failed to load alerts');
18283
+ }
18284
+ finally {
18285
+ this._isLoading.set(false);
18286
+ }
18287
+ }
18288
+ getAttr(attrs, name) {
18289
+ const attr = attrs.find(a => a.attributeName === name);
18290
+ return attr?.value != null ? String(attr.value) : null;
18291
+ }
18292
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertBannerWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18293
+ 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: `
18294
+ <div class="alert-banner-widget">
18295
+ @if (isNotConfigured()) {
18296
+ <mm-widget-not-configured></mm-widget-not-configured>
18297
+ } @else if (error()) {
18298
+ <div class="error-message">{{ error() }}</div>
18299
+ } @else if (currentAlert()) {
18300
+ <div class="alert-banner" [class.critical]="currentAlert()!.level === 'CRITICAL'"
18301
+ [style.--alert-color]="currentColor()">
18302
+ @if (config.showIcon !== false) {
18303
+ <kendo-svg-icon [icon]="currentIcon()" class="alert-icon"></kendo-svg-icon>
18304
+ }
18305
+ <span class="alert-severity-badge">{{ currentAlert()!.level }}</span>
18306
+ <span class="alert-message">{{ currentAlert()!.message }}</span>
18307
+ @if (items().length > 1) {
18308
+ <span class="alert-counter">{{ currentIndex() + 1 }}/{{ items().length }}</span>
18309
+ }
18310
+ </div>
18311
+ } @else if (!isLoading()) {
18312
+ <div class="alert-banner no-alerts">
18313
+ <span class="alert-message">No active alerts</span>
18314
+ </div>
18315
+ }
18316
+ </div>
18317
+ `, 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" }] });
18318
+ }
18319
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertBannerWidgetComponent, decorators: [{
18320
+ type: Component,
18321
+ args: [{ selector: 'mm-alert-banner-widget', standalone: true, imports: [CommonModule, SVGIconModule, WidgetNotConfiguredComponent], template: `
18322
+ <div class="alert-banner-widget">
18323
+ @if (isNotConfigured()) {
18324
+ <mm-widget-not-configured></mm-widget-not-configured>
18325
+ } @else if (error()) {
18326
+ <div class="error-message">{{ error() }}</div>
18327
+ } @else if (currentAlert()) {
18328
+ <div class="alert-banner" [class.critical]="currentAlert()!.level === 'CRITICAL'"
18329
+ [style.--alert-color]="currentColor()">
18330
+ @if (config.showIcon !== false) {
18331
+ <kendo-svg-icon [icon]="currentIcon()" class="alert-icon"></kendo-svg-icon>
18332
+ }
18333
+ <span class="alert-severity-badge">{{ currentAlert()!.level }}</span>
18334
+ <span class="alert-message">{{ currentAlert()!.message }}</span>
18335
+ @if (items().length > 1) {
18336
+ <span class="alert-counter">{{ currentIndex() + 1 }}/{{ items().length }}</span>
18337
+ }
18338
+ </div>
18339
+ } @else if (!isLoading()) {
18340
+ <div class="alert-banner no-alerts">
18341
+ <span class="alert-message">No active alerts</span>
18342
+ </div>
18343
+ }
18344
+ </div>
18345
+ `, 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"] }]
18346
+ }], propDecorators: { config: [{
18347
+ type: Input
18348
+ }] } });
18349
+
18350
+ class AlertBannerConfigDialogComponent {
18351
+ windowRef = inject(WindowRef);
18352
+ ckTypeId = 'System.Notification/StatefulEvent';
18353
+ rotationInterval = 5000;
18354
+ showIcon = true;
18355
+ maxAlerts = 20;
18356
+ onSave() {
18357
+ const result = {
18358
+ ckTypeId: this.ckTypeId,
18359
+ rotationInterval: this.rotationInterval,
18360
+ showIcon: this.showIcon,
18361
+ maxAlerts: this.maxAlerts
18362
+ };
18363
+ this.windowRef.close(result);
18364
+ }
18365
+ onCancel() {
18366
+ this.windowRef.close();
18367
+ }
18368
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertBannerConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18369
+ 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: `
18370
+ <div class="config-form">
18371
+ <div class="form-group">
18372
+ <kendo-label text="CK Type ID">
18373
+ <kendo-textbox [(ngModel)]="ckTypeId"></kendo-textbox>
18374
+ </kendo-label>
18375
+ </div>
18376
+ <div class="form-group">
18377
+ <kendo-label text="Rotation Interval (ms)">
18378
+ <kendo-numerictextbox [(ngModel)]="rotationInterval" [min]="1000" [step]="1000" [format]="'n0'"></kendo-numerictextbox>
18379
+ </kendo-label>
18380
+ </div>
18381
+ <div class="form-group">
18382
+ <kendo-label text="Max Alerts">
18383
+ <kendo-numerictextbox [(ngModel)]="maxAlerts" [min]="1" [max]="100" [format]="'n0'"></kendo-numerictextbox>
18384
+ </kendo-label>
18385
+ </div>
18386
+ <div class="mm-dialog-actions">
18387
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
18388
+ <button kendoButton themeColor="primary" (click)="onSave()">Save</button>
18389
+ </div>
18390
+ </div>
18391
+ `, 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"] }] });
18392
+ }
18393
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertBannerConfigDialogComponent, decorators: [{
18394
+ type: Component,
18395
+ args: [{ selector: 'mm-alert-banner-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule, LabelModule], template: `
18396
+ <div class="config-form">
18397
+ <div class="form-group">
18398
+ <kendo-label text="CK Type ID">
18399
+ <kendo-textbox [(ngModel)]="ckTypeId"></kendo-textbox>
18400
+ </kendo-label>
18401
+ </div>
18402
+ <div class="form-group">
18403
+ <kendo-label text="Rotation Interval (ms)">
18404
+ <kendo-numerictextbox [(ngModel)]="rotationInterval" [min]="1000" [step]="1000" [format]="'n0'"></kendo-numerictextbox>
18405
+ </kendo-label>
18406
+ </div>
18407
+ <div class="form-group">
18408
+ <kendo-label text="Max Alerts">
18409
+ <kendo-numerictextbox [(ngModel)]="maxAlerts" [min]="1" [max]="100" [format]="'n0'"></kendo-numerictextbox>
18410
+ </kendo-label>
18411
+ </div>
18412
+ <div class="mm-dialog-actions">
18413
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
18414
+ <button kendoButton themeColor="primary" (click)="onSave()">Save</button>
18415
+ </div>
18416
+ </div>
18417
+ `, 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"] }]
18418
+ }] });
18419
+
18420
+ class AlertListWidgetComponent {
18421
+ getEntitiesByCkTypeGQL = inject(GetEntitiesByCkTypeDtoGQL);
18422
+ config;
18423
+ _isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
18424
+ _error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
18425
+ _items = signal([], ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
18426
+ isLoading = this._isLoading.asReadonly();
18427
+ error = this._error.asReadonly();
18428
+ data = this._items.asReadonly();
18429
+ items = this._items.asReadonly();
18430
+ isNotConfigured() {
18431
+ return false;
18432
+ }
18433
+ ngOnInit() {
18434
+ this.loadData();
18435
+ }
18436
+ ngOnChanges(changes) {
18437
+ if (changes['config'] && !changes['config'].firstChange) {
18438
+ this.loadData();
18439
+ }
18440
+ }
18441
+ refresh() {
18442
+ this.loadData();
18443
+ }
18444
+ getColor(level) {
18445
+ return getAlertSeverityColor(level);
18446
+ }
18447
+ getIcon(level) {
18448
+ return getAlertSeverityIcon(level);
18449
+ }
18450
+ formatTime(timestamp) {
18451
+ try {
18452
+ const date = new Date(timestamp);
18453
+ return date.toLocaleString('de-AT', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
18454
+ }
18455
+ catch {
18456
+ return '';
18457
+ }
18458
+ }
18459
+ async loadData() {
18460
+ this._isLoading.set(true);
18461
+ this._error.set(null);
18462
+ try {
18463
+ const ckTypeId = this.config?.ckTypeId || DEFAULT_ALERT_CK_TYPE;
18464
+ const result = await firstValueFrom(this.getEntitiesByCkTypeGQL.fetch({
18465
+ variables: {
18466
+ ckTypeId,
18467
+ first: this.config?.maxAlerts ?? 50,
18468
+ fieldFilters: [
18469
+ { attributePath: 'state', operator: FieldFilterOperatorsDto.EqualsDto, comparisonValue: '0' }
18470
+ ],
18471
+ sort: [
18472
+ { attributePath: 'level', sortOrder: SortOrdersDto.DescendingDto }
18473
+ ]
18474
+ }
18475
+ }));
18476
+ const entities = result.data?.runtime?.runtimeEntities?.items ?? [];
18477
+ const items = [];
18478
+ for (const entity of entities) {
18479
+ if (!entity)
18480
+ continue;
18481
+ const attrs = (entity.attributes?.items ?? [])
18482
+ .filter((a) => a != null && a.attributeName != null);
18483
+ items.push({
18484
+ rtId: entity.rtId ?? '',
18485
+ message: this.getAttr(attrs, 'message') ?? '',
18486
+ level: this.getAttr(attrs, 'level') ?? 'INFORMATION',
18487
+ state: this.getAttr(attrs, 'state') ?? '',
18488
+ source: this.getAttr(attrs, 'source') ?? '',
18489
+ timestamp: entity.rtCreationDateTime ?? undefined
18490
+ });
18491
+ }
18492
+ if (this.config?.sortBySeverity !== false) {
18493
+ items.sort((a, b) => getAlertSeverityOrder(b.level) - getAlertSeverityOrder(a.level));
18494
+ }
18495
+ this._items.set(items);
18496
+ }
18497
+ catch (err) {
18498
+ this._error.set(err instanceof Error ? err.message : 'Failed to load alerts');
18499
+ }
18500
+ finally {
18501
+ this._isLoading.set(false);
18502
+ }
18503
+ }
18504
+ getAttr(attrs, name) {
18505
+ const attr = attrs.find(a => a.attributeName === name);
18506
+ return attr?.value != null ? String(attr.value) : null;
18507
+ }
18508
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertListWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18509
+ 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: `
18510
+ <div class="alert-list-widget">
18511
+ @if (isNotConfigured()) {
18512
+ <mm-widget-not-configured></mm-widget-not-configured>
18513
+ } @else if (error()) {
18514
+ <div class="error-message">{{ error() }}</div>
18515
+ } @else {
18516
+ <div class="alert-list">
18517
+ @for (item of items(); track item.rtId) {
18518
+ <div class="alert-item" [style.--alert-color]="getColor(item.level)">
18519
+ <kendo-svg-icon [icon]="getIcon(item.level)" class="alert-icon"></kendo-svg-icon>
18520
+ <span class="alert-badge" [style.background-color]="getColor(item.level)">
18521
+ {{ item.level }}
18522
+ </span>
18523
+ <span class="alert-message">{{ item.message }}</span>
18524
+ @if (config.showTimestamp !== false && item.timestamp) {
18525
+ <span class="alert-time">{{ formatTime(item.timestamp) }}</span>
18526
+ }
18527
+ </div>
18528
+ }
18529
+ @if (!isLoading() && items().length === 0) {
18530
+ <div class="empty-message">No active alerts</div>
18531
+ }
18532
+ </div>
18533
+ }
18534
+ </div>
18535
+ `, 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" }] });
18536
+ }
18537
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertListWidgetComponent, decorators: [{
18538
+ type: Component,
18539
+ args: [{ selector: 'mm-alert-list-widget', standalone: true, imports: [CommonModule, SVGIconModule, WidgetNotConfiguredComponent], template: `
18540
+ <div class="alert-list-widget">
18541
+ @if (isNotConfigured()) {
18542
+ <mm-widget-not-configured></mm-widget-not-configured>
18543
+ } @else if (error()) {
18544
+ <div class="error-message">{{ error() }}</div>
18545
+ } @else {
18546
+ <div class="alert-list">
18547
+ @for (item of items(); track item.rtId) {
18548
+ <div class="alert-item" [style.--alert-color]="getColor(item.level)">
18549
+ <kendo-svg-icon [icon]="getIcon(item.level)" class="alert-icon"></kendo-svg-icon>
18550
+ <span class="alert-badge" [style.background-color]="getColor(item.level)">
18551
+ {{ item.level }}
18552
+ </span>
18553
+ <span class="alert-message">{{ item.message }}</span>
18554
+ @if (config.showTimestamp !== false && item.timestamp) {
18555
+ <span class="alert-time">{{ formatTime(item.timestamp) }}</span>
18556
+ }
18557
+ </div>
18558
+ }
18559
+ @if (!isLoading() && items().length === 0) {
18560
+ <div class="empty-message">No active alerts</div>
18561
+ }
18562
+ </div>
18563
+ }
18564
+ </div>
18565
+ `, 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"] }]
18566
+ }], propDecorators: { config: [{
18567
+ type: Input
18568
+ }] } });
18569
+
18570
+ class AlertListConfigDialogComponent {
18571
+ windowRef = inject(WindowRef);
18572
+ ckTypeId = 'System.Notification/StatefulEvent';
18573
+ showTimestamp = true;
18574
+ sortBySeverity = true;
18575
+ maxAlerts = 50;
18576
+ onSave() {
18577
+ const result = {
18578
+ ckTypeId: this.ckTypeId,
18579
+ showTimestamp: this.showTimestamp,
18580
+ sortBySeverity: this.sortBySeverity,
18581
+ maxAlerts: this.maxAlerts
18582
+ };
18583
+ this.windowRef.close(result);
18584
+ }
18585
+ onCancel() {
18586
+ this.windowRef.close();
18587
+ }
18588
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertListConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18589
+ 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: `
18590
+ <div class="config-form">
18591
+ <div class="form-group">
18592
+ <kendo-label text="CK Type ID">
18593
+ <kendo-textbox [(ngModel)]="ckTypeId"></kendo-textbox>
18594
+ </kendo-label>
18595
+ </div>
18596
+ <div class="form-group">
18597
+ <kendo-label text="Max Alerts">
18598
+ <kendo-numerictextbox [(ngModel)]="maxAlerts" [min]="1" [max]="200" [format]="'n0'"></kendo-numerictextbox>
18599
+ </kendo-label>
18600
+ </div>
18601
+ <div class="mm-dialog-actions">
18602
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
18603
+ <button kendoButton themeColor="primary" (click)="onSave()">Save</button>
18604
+ </div>
18605
+ </div>
18606
+ `, 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"] }] });
18607
+ }
18608
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AlertListConfigDialogComponent, decorators: [{
18609
+ type: Component,
18610
+ args: [{ selector: 'mm-alert-list-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule, LabelModule], template: `
18611
+ <div class="config-form">
18612
+ <div class="form-group">
18613
+ <kendo-label text="CK Type ID">
18614
+ <kendo-textbox [(ngModel)]="ckTypeId"></kendo-textbox>
18615
+ </kendo-label>
18616
+ </div>
18617
+ <div class="form-group">
18618
+ <kendo-label text="Max Alerts">
18619
+ <kendo-numerictextbox [(ngModel)]="maxAlerts" [min]="1" [max]="200" [format]="'n0'"></kendo-numerictextbox>
18620
+ </kendo-label>
18621
+ </div>
18622
+ <div class="mm-dialog-actions">
18623
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
18624
+ <button kendoButton themeColor="primary" (click)="onSave()">Save</button>
18625
+ </div>
18626
+ </div>
18627
+ `, 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"] }]
18628
+ }] });
18629
+
18630
+ class AiInsightsService {
18631
+ async generateInsights(context) {
18632
+ if (context.apiKey) {
18633
+ return this.callClaudeApi(context);
18634
+ }
18635
+ return this.getSimulatedInsights(context);
18636
+ }
18637
+ gatherWidgetContext(widgets) {
18638
+ return widgets
18639
+ .filter(w => w.type !== 'aiInsights')
18640
+ .map(w => this.extractWidgetSummary(w))
18641
+ .filter(s => s.dataPoints.length > 0 || s.type !== 'unknown');
18642
+ }
18643
+ extractWidgetSummary(widget) {
18644
+ const summary = {
18645
+ title: widget.title,
18646
+ type: widget.type,
18647
+ dataPoints: []
18648
+ };
18649
+ if (widget.type === 'kpi') {
18650
+ const kpi = widget;
18651
+ if (kpi.staticValue) {
18652
+ summary.dataPoints.push({ label: 'value', value: `${kpi.staticValue}${kpi.suffix ?? ''}` });
18653
+ }
18654
+ if (kpi.comparisonText) {
18655
+ summary.dataPoints.push({ label: 'comparison', value: kpi.comparisonText });
18656
+ }
18657
+ }
18658
+ if (widget.type === 'gauge') {
18659
+ const gauge = widget;
18660
+ if (gauge.valueAttribute) {
18661
+ summary.dataPoints.push({ label: 'attribute', value: gauge.valueAttribute });
18662
+ }
18663
+ }
18664
+ return summary;
18665
+ }
18666
+ async callClaudeApi(context) {
18667
+ const systemPrompt = context.systemPrompt ?? this.buildDefaultSystemPrompt(context);
18668
+ const userMessage = this.buildUserMessage(context);
18669
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
18670
+ method: 'POST',
18671
+ headers: {
18672
+ 'Content-Type': 'application/json',
18673
+ 'x-api-key': context.apiKey,
18674
+ 'anthropic-version': '2023-06-01',
18675
+ 'anthropic-dangerous-direct-browser-access': 'true'
18676
+ },
18677
+ body: JSON.stringify({
18678
+ model: context.model ?? 'claude-sonnet-4-20250514',
18679
+ max_tokens: 1024,
18680
+ system: systemPrompt,
18681
+ messages: [{ role: 'user', content: userMessage }]
18682
+ })
18683
+ });
18684
+ if (!response.ok) {
18685
+ throw new Error(`Claude API error: ${response.status}`);
18686
+ }
18687
+ const data = await response.json();
18688
+ const text = data.content?.[0]?.text ?? '';
18689
+ return this.parseInsightsFromResponse(text, context.maxInsights ?? 4);
18690
+ }
18691
+ buildDefaultSystemPrompt(context) {
18692
+ const domain = context.domainContext ?? 'energy management';
18693
+ return `You are an AI advisor for an industrial ${domain} dashboard.
18694
+ Analyze the provided dashboard data and generate actionable insights.
18695
+ Respond ONLY with a JSON array of insight objects. Each object has:
18696
+ - "title": short headline (max 8 words)
18697
+ - "description": actionable recommendation (1-2 sentences, German language)
18698
+ - "severity": one of "info", "warning", "success", "critical"
18699
+
18700
+ Focus on energy efficiency, cost savings, anomalies, and optimization opportunities.
18701
+ Return at most ${context.maxInsights ?? 4} insights. No markdown, no explanation, only the JSON array.`;
18702
+ }
18703
+ buildUserMessage(context) {
18704
+ const widgetData = context.widgetSummaries.map(w => ({
18705
+ widget: w.title,
18706
+ type: w.type,
18707
+ data: w.dataPoints
18708
+ }));
18709
+ return `Current dashboard state:\n${JSON.stringify(widgetData, null, 2)}`;
18710
+ }
18711
+ parseInsightsFromResponse(text, maxInsights) {
18712
+ try {
18713
+ const jsonMatch = text.match(/\[[\s\S]*\]/);
18714
+ if (!jsonMatch)
18715
+ return this.fallbackInsights();
18716
+ const parsed = JSON.parse(jsonMatch[0]);
18717
+ return parsed.slice(0, maxInsights).map(i => ({
18718
+ title: i.title ?? 'Insight',
18719
+ description: i.description ?? '',
18720
+ severity: (['info', 'warning', 'success', 'critical'].includes(i.severity) ? i.severity : 'info')
18721
+ }));
18722
+ }
18723
+ catch {
18724
+ return this.fallbackInsights();
18725
+ }
18726
+ }
18727
+ getSimulatedInsights(_context) {
18728
+ const hour = new Date().getHours();
18729
+ const insights = [
18730
+ {
18731
+ title: 'Lastspitze prognostiziert',
18732
+ 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.',
18733
+ severity: 'warning'
18734
+ },
18735
+ {
18736
+ title: 'PV-Optimierung möglich',
18737
+ description: hour < 14
18738
+ ? 'Wettervorhersage: sonnig bis 16:00 Uhr. Energieintensive Prozesse jetzt starten — erwartete PV-Einspeisung von 810 kW nutzen.'
18739
+ : 'PV-Einspeisung sinkt ab 16:00 Uhr. Batteriespeicher auf 80% laden, solange Überschuss verfügbar ist.',
18740
+ severity: 'success'
18741
+ },
18742
+ {
18743
+ title: 'Kompressoranlage C3 auffällig',
18744
+ description: 'Verbrauch 18% über dem 30-Tage-Durchschnitt bei gleicher Auslastung. Mögliche Ursache: verschlissene Dichtungen oder Druckverlust. Wartung empfohlen.',
18745
+ severity: 'warning'
18746
+ },
18747
+ {
18748
+ title: 'CO2-Budget im Zeitplan',
18749
+ description: 'Mit 489 t von 680 t Budget (72%) und 9 Monaten verbleibend liegt der Verbrauch 4% unter Plan. Zertifikats-Beschaffung kann verschoben werden.',
18750
+ severity: 'success'
18751
+ }
18752
+ ];
18753
+ return insights;
18754
+ }
18755
+ fallbackInsights() {
18756
+ return [{
18757
+ title: 'Analyse nicht verfügbar',
18758
+ description: 'Die KI-Analyse konnte nicht durchgeführt werden. Bitte prüfen Sie den API-Schlüssel in den Widget-Einstellungen.',
18759
+ severity: 'info'
18760
+ }];
18761
+ }
18762
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18763
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsService, providedIn: 'root' });
18764
+ }
18765
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsService, decorators: [{
18766
+ type: Injectable,
18767
+ args: [{ providedIn: 'root' }]
18768
+ }] });
18769
+
18770
+ class AiInsightsWidgetComponent {
18771
+ aiService = inject(AiInsightsService);
18772
+ stateService = inject(MeshBoardStateService);
18773
+ refreshTimer = null;
18774
+ infoIcon = infoCircleIcon;
18775
+ warningIcon = exclamationCircleIcon;
18776
+ successIcon = checkCircleIcon;
18777
+ criticalIcon = xCircleIcon;
18778
+ config;
18779
+ _isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
18780
+ _error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
18781
+ _insights = signal(null, ...(ngDevMode ? [{ debugName: "_insights" }] : /* istanbul ignore next */ []));
18782
+ isLoading = this._isLoading.asReadonly();
18783
+ error = this._error.asReadonly();
18784
+ data = this._insights.asReadonly();
18785
+ insights = this._insights.asReadonly();
18786
+ isNotConfigured() {
18787
+ return false;
18788
+ }
18789
+ ngOnInit() {
18790
+ this.loadInsights();
18791
+ this.startAutoRefresh();
18792
+ }
18793
+ ngOnChanges(changes) {
18794
+ if (changes['config'] && !changes['config'].firstChange) {
18795
+ this.stopAutoRefresh();
18796
+ this.loadInsights();
18797
+ this.startAutoRefresh();
18798
+ }
18799
+ }
18800
+ ngOnDestroy() {
18801
+ this.stopAutoRefresh();
18802
+ }
18803
+ refresh() {
18804
+ this.loadInsights();
18805
+ }
18806
+ getIcon(severity) {
18807
+ switch (severity) {
18808
+ case 'warning': return this.warningIcon;
18809
+ case 'success': return this.successIcon;
18810
+ case 'critical': return this.criticalIcon;
18811
+ default: return this.infoIcon;
18812
+ }
18813
+ }
18814
+ async loadInsights() {
18815
+ this._isLoading.set(true);
18816
+ this._error.set(null);
18817
+ try {
18818
+ const widgets = this.stateService.widgets();
18819
+ const widgetSummaries = this.aiService.gatherWidgetContext(widgets);
18820
+ const context = {
18821
+ apiKey: this.config.apiKey,
18822
+ model: this.config.model,
18823
+ systemPrompt: this.config.systemPrompt,
18824
+ maxInsights: this.config.maxInsights ?? 4,
18825
+ domainContext: this.config.domainContext ?? 'energy management',
18826
+ widgetSummaries
18827
+ };
18828
+ const insights = await this.aiService.generateInsights(context);
18829
+ this._insights.set(insights);
18830
+ }
18831
+ catch (err) {
18832
+ this._error.set(err instanceof Error ? err.message : 'Failed to generate insights');
18833
+ }
18834
+ finally {
18835
+ this._isLoading.set(false);
18836
+ }
18837
+ }
18838
+ startAutoRefresh() {
18839
+ this.stopAutoRefresh();
18840
+ const interval = (this.config?.refreshInterval ?? 0) * 1000;
18841
+ if (interval > 0) {
18842
+ this.refreshTimer = setInterval(() => this.loadInsights(), interval);
18843
+ }
18844
+ }
18845
+ stopAutoRefresh() {
18846
+ if (this.refreshTimer) {
18847
+ clearInterval(this.refreshTimer);
18848
+ this.refreshTimer = null;
18849
+ }
18850
+ }
18851
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18852
+ 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: `
18853
+ <div class="ai-insights-widget">
18854
+ @if (isLoading() && !insights()) {
18855
+ <div class="loading-state">
18856
+ <div class="loading-dots">
18857
+ <span></span><span></span><span></span>
18858
+ </div>
18859
+ <span class="loading-text">Generating AI insights...</span>
18860
+ </div>
18861
+ } @else if (error()) {
18862
+ <div class="error-state">{{ error() }}</div>
18863
+ } @else if (insights(); as items) {
18864
+ <div class="insights-list">
18865
+ @for (insight of items; track insight.title) {
18866
+ <div class="insight-card" [class]="'severity-' + insight.severity">
18867
+ <div class="insight-icon">
18868
+ <kendo-svg-icon [icon]="getIcon(insight.severity)" size="medium"></kendo-svg-icon>
18869
+ </div>
18870
+ <div class="insight-content">
18871
+ <div class="insight-title">{{ insight.title }}</div>
18872
+ <div class="insight-description">{{ insight.description }}</div>
18873
+ </div>
18874
+ </div>
18875
+ }
18876
+ </div>
18877
+ @if (isLoading()) {
18878
+ <div class="refresh-indicator">Aktualisierung...</div>
18879
+ }
18880
+ }
18881
+ </div>
18882
+ `, 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"] }] });
18883
+ }
18884
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsWidgetComponent, decorators: [{
18885
+ type: Component,
18886
+ args: [{ selector: 'mm-ai-insights-widget', standalone: true, imports: [CommonModule, SVGIconModule, WidgetNotConfiguredComponent], template: `
18887
+ <div class="ai-insights-widget">
18888
+ @if (isLoading() && !insights()) {
18889
+ <div class="loading-state">
18890
+ <div class="loading-dots">
18891
+ <span></span><span></span><span></span>
18892
+ </div>
18893
+ <span class="loading-text">Generating AI insights...</span>
18894
+ </div>
18895
+ } @else if (error()) {
18896
+ <div class="error-state">{{ error() }}</div>
18897
+ } @else if (insights(); as items) {
18898
+ <div class="insights-list">
18899
+ @for (insight of items; track insight.title) {
18900
+ <div class="insight-card" [class]="'severity-' + insight.severity">
18901
+ <div class="insight-icon">
18902
+ <kendo-svg-icon [icon]="getIcon(insight.severity)" size="medium"></kendo-svg-icon>
18903
+ </div>
18904
+ <div class="insight-content">
18905
+ <div class="insight-title">{{ insight.title }}</div>
18906
+ <div class="insight-description">{{ insight.description }}</div>
18907
+ </div>
18908
+ </div>
18909
+ }
18910
+ </div>
18911
+ @if (isLoading()) {
18912
+ <div class="refresh-indicator">Aktualisierung...</div>
18913
+ }
18914
+ }
18915
+ </div>
18916
+ `, 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"] }]
18917
+ }], propDecorators: { config: [{
18918
+ type: Input
18919
+ }] } });
18920
+
18921
+ class AiInsightsConfigDialogComponent {
18922
+ windowRef = inject(WindowRef);
18923
+ apiKey = '';
18924
+ model = 'claude-sonnet-4-20250514';
18925
+ domainContext = 'energy management';
18926
+ refreshInterval = 0;
18927
+ maxInsights = 4;
18928
+ onSave() {
18929
+ const result = {
18930
+ ckTypeId: '',
18931
+ apiKey: this.apiKey || undefined,
18932
+ model: this.model,
18933
+ domainContext: this.domainContext,
18934
+ refreshInterval: this.refreshInterval,
18935
+ maxInsights: this.maxInsights
18936
+ };
18937
+ this.windowRef.close(result);
18938
+ }
18939
+ onCancel() {
18940
+ this.windowRef.close();
18941
+ }
18942
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18943
+ 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: `
18944
+ <div class="config-form">
18945
+ <div class="form-group">
18946
+ <kendo-label text="Anthropic API Key (optional for demo)">
18947
+ <kendo-textbox [(ngModel)]="apiKey" type="password" placeholder="sk-ant-..."></kendo-textbox>
18948
+ </kendo-label>
18949
+ <span class="hint">Without API key, simulated insights are shown.</span>
18950
+ </div>
18951
+ <div class="form-group">
18952
+ <kendo-label text="Model">
18953
+ <kendo-textbox [(ngModel)]="model"></kendo-textbox>
18954
+ </kendo-label>
18955
+ </div>
18956
+ <div class="form-group">
18957
+ <kendo-label text="Domain Context">
18958
+ <kendo-textbox [(ngModel)]="domainContext" placeholder="e.g. energy management"></kendo-textbox>
18959
+ </kendo-label>
18960
+ </div>
18961
+ <div class="form-row">
18962
+ <div class="form-group">
18963
+ <kendo-label text="Refresh Interval (seconds, 0=off)">
18964
+ <kendo-numerictextbox [(ngModel)]="refreshInterval" [min]="0" [step]="10" [format]="'n0'"></kendo-numerictextbox>
18965
+ </kendo-label>
18966
+ </div>
18967
+ <div class="form-group">
18968
+ <kendo-label text="Max Insights">
18969
+ <kendo-numerictextbox [(ngModel)]="maxInsights" [min]="1" [max]="8" [format]="'n0'"></kendo-numerictextbox>
18970
+ </kendo-label>
18971
+ </div>
18972
+ </div>
18973
+ <div class="mm-dialog-actions">
18974
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
18975
+ <button kendoButton themeColor="primary" (click)="onSave()">Save</button>
18976
+ </div>
18977
+ </div>
18978
+ `, 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"] }] });
18979
+ }
18980
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AiInsightsConfigDialogComponent, decorators: [{
18981
+ type: Component,
18982
+ args: [{ selector: 'mm-ai-insights-config-dialog', standalone: true, imports: [CommonModule, FormsModule, ButtonsModule, InputsModule, LabelModule], template: `
18983
+ <div class="config-form">
18984
+ <div class="form-group">
18985
+ <kendo-label text="Anthropic API Key (optional for demo)">
18986
+ <kendo-textbox [(ngModel)]="apiKey" type="password" placeholder="sk-ant-..."></kendo-textbox>
18987
+ </kendo-label>
18988
+ <span class="hint">Without API key, simulated insights are shown.</span>
18989
+ </div>
18990
+ <div class="form-group">
18991
+ <kendo-label text="Model">
18992
+ <kendo-textbox [(ngModel)]="model"></kendo-textbox>
18993
+ </kendo-label>
18994
+ </div>
18995
+ <div class="form-group">
18996
+ <kendo-label text="Domain Context">
18997
+ <kendo-textbox [(ngModel)]="domainContext" placeholder="e.g. energy management"></kendo-textbox>
18998
+ </kendo-label>
18999
+ </div>
19000
+ <div class="form-row">
19001
+ <div class="form-group">
19002
+ <kendo-label text="Refresh Interval (seconds, 0=off)">
19003
+ <kendo-numerictextbox [(ngModel)]="refreshInterval" [min]="0" [step]="10" [format]="'n0'"></kendo-numerictextbox>
19004
+ </kendo-label>
19005
+ </div>
19006
+ <div class="form-group">
19007
+ <kendo-label text="Max Insights">
19008
+ <kendo-numerictextbox [(ngModel)]="maxInsights" [min]="1" [max]="8" [format]="'n0'"></kendo-numerictextbox>
19009
+ </kendo-label>
19010
+ </div>
19011
+ </div>
19012
+ <div class="mm-dialog-actions">
19013
+ <button kendoButton fillMode="flat" (click)="onCancel()">Cancel</button>
19014
+ <button kendoButton themeColor="primary" (click)="onSave()">Save</button>
19015
+ </div>
19016
+ </div>
19017
+ `, 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"] }]
19018
+ }] });
19019
+
19020
+ class ProcessDataService {
19021
+ dataService = inject(MeshBoardDataService);
19022
+ variableService = inject(MeshBoardVariableService);
19023
+ stateService = inject(MeshBoardStateService);
19024
+ getProcessDiagramGQL = inject(GetProcessDiagramDtoGQL);
19025
+ getProcessDiagramsGQL = inject(GetProcessDiagramsDtoGQL);
19026
+ createProcessDiagramGQL = inject(CreateProcessDiagramDtoGQL);
19027
+ updateProcessDiagramGQL = inject(UpdateProcessDiagramDtoGQL);
19028
+ executeRuntimeQueryGQL = inject(ExecuteRuntimeQueryDtoGQL);
19029
+ /**
19030
+ * Loads a list of available process diagrams from the backend
19031
+ *
19032
+ * @param searchText - Optional search text to filter diagrams
19033
+ * @returns List of process diagram summaries
19034
+ */
19035
+ async loadDiagramList(searchText) {
19036
+ try {
19037
+ const result = await firstValueFrom(this.getProcessDiagramsGQL.fetch({
19038
+ first: 100,
19039
+ searchFilter: searchText ? { searchTerm: searchText, language: 'de' } : undefined
19040
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Apollo fetch requires flexible variable typing
19041
+ }));
19042
+ const items = result.data?.runtime?.systemUIProcessDiagram?.items || [];
19043
+ return items
19044
+ .filter((item) => item !== null)
19045
+ .map(item => ({
19046
+ rtId: item.rtId,
19047
+ name: item.name,
19048
+ description: item.description,
19049
+ version: item.version,
19050
+ canvasWidth: item.canvasWidth,
19051
+ canvasHeight: item.canvasHeight
19052
+ }));
19053
+ }
19054
+ catch (error) {
19055
+ console.error('Error loading process diagrams:', error);
19056
+ return [];
19057
+ }
19058
+ }
19059
+ /**
19060
+ * Loads a process diagram configuration from the backend
19061
+ *
19062
+ * @param rtId - The runtime ID of the process diagram
19063
+ * @returns The process diagram configuration
19064
+ */
19065
+ async loadDiagram(rtId) {
19066
+ const result = await firstValueFrom(this.getProcessDiagramGQL.fetch({ variables: { rtId } }));
19067
+ const item = result.data?.runtime?.systemUIProcessDiagram?.items?.[0];
19068
+ if (!item) {
19069
+ throw new Error(`Process diagram not found: ${rtId}`);
19070
+ }
19071
+ // Parse JSON fields
19072
+ const elements = this.parseJsonField(item.elements, []);
19073
+ const itemWithExtensions = item;
19074
+ const primitives = itemWithExtensions.primitives
19075
+ ? this.parseJsonField(itemWithExtensions.primitives, [])
19076
+ : undefined;
19077
+ const symbolInstances = itemWithExtensions.symbolInstances
19078
+ ? this.parseJsonField(itemWithExtensions.symbolInstances, [])
19079
+ : undefined;
19080
+ const connections = this.parseJsonField(item.connections, []);
19081
+ const variables = item.variables ? this.parseJsonField(item.variables, []) : undefined;
19082
+ // Parse diagram-level property fields
19083
+ const transformProperties = itemWithExtensions.transformProperties
19084
+ ? this.parseJsonField(itemWithExtensions.transformProperties, [])
19085
+ : undefined;
19086
+ const propertyBindings = itemWithExtensions.propertyBindings
19087
+ ? this.parseJsonField(itemWithExtensions.propertyBindings, [])
19088
+ : undefined;
19089
+ const animations = itemWithExtensions.animations
19090
+ ? this.parseJsonField(itemWithExtensions.animations, [])
19091
+ : undefined;
19092
+ return {
19093
+ id: item.rtId,
19094
+ name: item.name,
19095
+ description: item.description ?? undefined,
19096
+ version: item.version,
19097
+ canvas: {
19098
+ width: item.canvasWidth,
19099
+ height: item.canvasHeight,
19100
+ backgroundColor: item.canvasBackgroundColor ?? undefined
19101
+ },
19102
+ elements,
19103
+ primitives,
19104
+ symbolInstances,
19105
+ connections,
19106
+ variables,
19107
+ transformProperties,
19108
+ propertyBindings,
19109
+ animations,
19110
+ refreshInterval: item.refreshInterval ?? undefined
19111
+ };
19112
+ }
19113
+ /**
19114
+ * Creates a new process diagram in the backend
19115
+ *
19116
+ * @param diagram - The process diagram configuration to create
19117
+ * @returns The created diagram with its new rtId
19118
+ */
19119
+ async createDiagram(diagram) {
19120
+ const input = this.toInputDto(diagram);
19121
+ const result = await firstValueFrom(this.createProcessDiagramGQL.mutate({
19122
+ variables: { entities: [input] }
19123
+ }));
19124
+ const created = result.data?.runtime?.systemUIProcessDiagrams?.create?.[0];
19125
+ if (!created) {
19126
+ throw new Error('Failed to create process diagram');
19127
+ }
19128
+ return {
19129
+ ...diagram,
19130
+ id: created.rtId
19131
+ };
19132
+ }
19133
+ /**
19134
+ * Updates an existing process diagram in the backend
19135
+ *
19136
+ * @param diagram - The process diagram configuration to update
19137
+ * @returns The updated diagram
19138
+ */
19139
+ async updateDiagram(diagram) {
19140
+ if (!diagram.id) {
19141
+ throw new Error('Cannot update diagram without rtId');
19142
+ }
19143
+ const input = {
19144
+ rtId: diagram.id,
19145
+ item: this.toInputDto(diagram)
19146
+ };
19147
+ const result = await firstValueFrom(this.updateProcessDiagramGQL.mutate({
19148
+ variables: { entities: [input] }
19149
+ }));
19150
+ const updated = result.data?.runtime?.systemUIProcessDiagrams?.update?.[0];
19151
+ if (!updated) {
19152
+ throw new Error('Failed to update process diagram');
19153
+ }
19154
+ return diagram;
19155
+ }
19156
+ /**
19157
+ * Saves a process diagram (creates new or updates existing)
19158
+ *
19159
+ * @param diagram - The process diagram configuration to save
19160
+ * @param isNew - Whether this is a new diagram (default: false)
19161
+ * @returns The saved diagram
19162
+ */
19163
+ async saveDiagram(diagram, isNew = false) {
17426
19164
  if (isNew || !diagram.id) {
17427
19165
  return this.createDiagram(diagram);
17428
19166
  }
@@ -23011,6 +24749,7 @@ function registerDefaultWidgets(registry) {
23011
24749
  initialPrefix: kpiWidget.prefix,
23012
24750
  initialSuffix: kpiWidget.suffix,
23013
24751
  initialTrend: kpiWidget.trend,
24752
+ initialComparisonText: kpiWidget.comparisonText,
23014
24753
  // Filters
23015
24754
  initialFilters: kpiWidget.filters
23016
24755
  };
@@ -23135,7 +24874,8 @@ function registerDefaultWidgets(registry) {
23135
24874
  prefix: config['prefix'],
23136
24875
  suffix: config['suffix'],
23137
24876
  icon: config['icon'],
23138
- trend: config['trend']
24877
+ trend: config['trend'],
24878
+ comparisonText: config['comparisonText']
23139
24879
  };
23140
24880
  }
23141
24881
  if (dataSource.type === 'persistentQuery') {
@@ -23153,6 +24893,7 @@ function registerDefaultWidgets(registry) {
23153
24893
  suffix: config['suffix'],
23154
24894
  icon: config['icon'],
23155
24895
  trend: config['trend'],
24896
+ comparisonText: config['comparisonText'],
23156
24897
  filters: config['filters']
23157
24898
  };
23158
24899
  }
@@ -23167,6 +24908,7 @@ function registerDefaultWidgets(registry) {
23167
24908
  suffix: config['suffix'],
23168
24909
  icon: config['icon'],
23169
24910
  trend: config['trend'],
24911
+ comparisonText: config['comparisonText'],
23170
24912
  filters: config['filters']
23171
24913
  };
23172
24914
  }
@@ -23741,6 +25483,8 @@ function registerDefaultWidgets(registry) {
23741
25483
  initialShowLegend: barWidget.showLegend,
23742
25484
  initialLegendPosition: barWidget.legendPosition,
23743
25485
  initialShowDataLabels: barWidget.showDataLabels,
25486
+ initialColorThresholds: barWidget.colorThresholds,
25487
+ initialDefaultBarColor: barWidget.defaultBarColor,
23744
25488
  initialFilters: barWidget.filters
23745
25489
  };
23746
25490
  },
@@ -23767,6 +25511,8 @@ function registerDefaultWidgets(registry) {
23767
25511
  showLegend: result.showLegend,
23768
25512
  legendPosition: result.legendPosition,
23769
25513
  showDataLabels: result.showDataLabels,
25514
+ colorThresholds: result.colorThresholds,
25515
+ defaultBarColor: result.defaultBarColor,
23770
25516
  filters
23771
25517
  };
23772
25518
  },
@@ -23797,6 +25543,9 @@ function registerDefaultWidgets(registry) {
23797
25543
  showLegend: widget.showLegend,
23798
25544
  legendPosition: widget.legendPosition,
23799
25545
  showDataLabels: widget.showDataLabels,
25546
+ dataLabelSuffix: widget.dataLabelSuffix,
25547
+ colorThresholds: widget.colorThresholds,
25548
+ defaultBarColor: widget.defaultBarColor,
23800
25549
  queryName: widget.dataSource.queryName,
23801
25550
  queryRtId: widget.dataSource.queryRtId,
23802
25551
  filters: widget.filters
@@ -23819,6 +25568,9 @@ function registerDefaultWidgets(registry) {
23819
25568
  showLegend: config['showLegend'] ?? true,
23820
25569
  legendPosition: config['legendPosition'] ?? 'right',
23821
25570
  showDataLabels: config['showDataLabels'] ?? false,
25571
+ dataLabelSuffix: config['dataLabelSuffix'],
25572
+ colorThresholds: config['colorThresholds'],
25573
+ defaultBarColor: config['defaultBarColor'],
23822
25574
  filters: config['filters']
23823
25575
  };
23824
25576
  }
@@ -23848,6 +25600,7 @@ function registerDefaultWidgets(registry) {
23848
25600
  initialShowLegend: lineWidget.showLegend,
23849
25601
  initialLegendPosition: lineWidget.legendPosition,
23850
25602
  initialShowMarkers: lineWidget.showMarkers,
25603
+ initialReferenceLines: lineWidget.referenceLines,
23851
25604
  initialFilters: lineWidget.filters
23852
25605
  };
23853
25606
  },
@@ -23873,6 +25626,7 @@ function registerDefaultWidgets(registry) {
23873
25626
  showLegend: result.showLegend,
23874
25627
  legendPosition: result.legendPosition,
23875
25628
  showMarkers: result.showMarkers,
25629
+ referenceLines: result.referenceLines,
23876
25630
  filters
23877
25631
  };
23878
25632
  },
@@ -23902,6 +25656,7 @@ function registerDefaultWidgets(registry) {
23902
25656
  showLegend: widget.showLegend,
23903
25657
  legendPosition: widget.legendPosition,
23904
25658
  showMarkers: widget.showMarkers,
25659
+ referenceLines: widget.referenceLines,
23905
25660
  queryName: widget.dataSource.queryName,
23906
25661
  queryRtId: widget.dataSource.queryRtId,
23907
25662
  filters: widget.filters
@@ -23923,6 +25678,7 @@ function registerDefaultWidgets(registry) {
23923
25678
  showLegend: config['showLegend'] ?? true,
23924
25679
  legendPosition: config['legendPosition'] ?? 'right',
23925
25680
  showMarkers: config['showMarkers'] ?? false,
25681
+ referenceLines: config['referenceLines'],
23926
25682
  filters: config['filters']
23927
25683
  };
23928
25684
  }
@@ -24464,6 +26220,300 @@ function registerDefaultWidgets(registry) {
24464
26220
  };
24465
26221
  }
24466
26222
  });
26223
+ // Status List Widget
26224
+ registry.registerWidget({
26225
+ type: 'statusList',
26226
+ label: 'Status List',
26227
+ component: StatusListWidgetComponent,
26228
+ configDialogComponent: StatusListConfigDialogComponent,
26229
+ configDialogSize: { width: 650, height: 550, minWidth: 500, minHeight: 400 },
26230
+ configDialogTitle: 'Status List Configuration',
26231
+ defaultSize: { colSpan: 2, rowSpan: 2 },
26232
+ supportedDataSources: ['runtimeEntity'],
26233
+ getInitialConfig: (widget) => {
26234
+ const slWidget = widget;
26235
+ return {
26236
+ initialCkTypeId: slWidget.ckTypeId,
26237
+ initialLabelField: slWidget.labelField,
26238
+ initialStatusField: slWidget.statusField,
26239
+ initialStatusColors: slWidget.statusColors
26240
+ };
26241
+ },
26242
+ applyConfigResult: (widget, result) => ({
26243
+ ...widget,
26244
+ ckTypeId: result.ckTypeId,
26245
+ labelField: result.labelField,
26246
+ statusField: result.statusField,
26247
+ statusColors: result.statusColors,
26248
+ dataSource: { type: 'runtimeEntity', ckTypeId: result.ckTypeId }
26249
+ }),
26250
+ createDefaultConfig: (base) => ({
26251
+ ...base,
26252
+ type: 'statusList',
26253
+ colSpan: 3,
26254
+ rowSpan: 1,
26255
+ dataSource: { type: 'runtimeEntity' },
26256
+ ckTypeId: '',
26257
+ labelField: 'name',
26258
+ statusField: ''
26259
+ }),
26260
+ toPersistedConfig: (widget) => ({
26261
+ dataSourceType: 'runtimeEntity',
26262
+ dataSourceCkTypeId: widget.ckTypeId,
26263
+ config: {
26264
+ ckTypeId: widget.ckTypeId,
26265
+ labelField: widget.labelField,
26266
+ statusField: widget.statusField,
26267
+ statusColors: widget.statusColors
26268
+ }
26269
+ }),
26270
+ fromPersistedConfig: (data, base) => {
26271
+ const config = parseConfig(data);
26272
+ return {
26273
+ ...base,
26274
+ rtId: data.rtId,
26275
+ type: 'statusList',
26276
+ dataSource: { type: 'runtimeEntity', ckTypeId: data.ckTypeId ?? config['ckTypeId'] },
26277
+ ckTypeId: config['ckTypeId'] ?? data.ckTypeId ?? '',
26278
+ labelField: config['labelField'] ?? 'name',
26279
+ statusField: config['statusField'] ?? '',
26280
+ statusColors: config['statusColors']
26281
+ };
26282
+ }
26283
+ });
26284
+ // Summary Card Widget
26285
+ registry.registerWidget({
26286
+ type: 'summaryCard',
26287
+ label: 'Summary Card',
26288
+ component: SummaryCardWidgetComponent,
26289
+ configDialogComponent: SummaryCardConfigDialogComponent,
26290
+ configDialogSize: { width: 750, height: 650, minWidth: 600, minHeight: 500 },
26291
+ configDialogTitle: 'Summary Card Configuration',
26292
+ defaultSize: { colSpan: 2, rowSpan: 2 },
26293
+ supportedDataSources: ['runtimeEntity'],
26294
+ getInitialConfig: (widget) => {
26295
+ const scWidget = widget;
26296
+ return {
26297
+ initialColumns: scWidget.columns,
26298
+ initialTiles: scWidget.tiles
26299
+ };
26300
+ },
26301
+ applyConfigResult: (widget, result) => ({
26302
+ ...widget,
26303
+ columns: result.columns,
26304
+ tiles: result.tiles,
26305
+ dataSource: { type: 'runtimeEntity' }
26306
+ }),
26307
+ createDefaultConfig: (base) => ({
26308
+ ...base,
26309
+ type: 'summaryCard',
26310
+ colSpan: 2,
26311
+ rowSpan: 2,
26312
+ dataSource: { type: 'runtimeEntity' },
26313
+ columns: 2,
26314
+ tiles: []
26315
+ }),
26316
+ toPersistedConfig: (widget) => ({
26317
+ dataSourceType: 'runtimeEntity',
26318
+ config: {
26319
+ columns: widget.columns,
26320
+ tiles: widget.tiles
26321
+ }
26322
+ }),
26323
+ fromPersistedConfig: (data, base) => {
26324
+ const config = parseConfig(data);
26325
+ return {
26326
+ ...base,
26327
+ rtId: data.rtId,
26328
+ type: 'summaryCard',
26329
+ dataSource: { type: 'runtimeEntity', ckTypeId: data.ckTypeId ?? 'configured' },
26330
+ columns: config['columns'] ?? 2,
26331
+ tiles: config['tiles'] ?? []
26332
+ };
26333
+ }
26334
+ });
26335
+ // Alert Banner Widget
26336
+ registry.registerWidget({
26337
+ type: 'alertBanner',
26338
+ label: 'Alert Banner',
26339
+ component: AlertBannerWidgetComponent,
26340
+ configDialogComponent: AlertBannerConfigDialogComponent,
26341
+ configDialogSize: { width: 500, height: 400, minWidth: 400, minHeight: 300 },
26342
+ configDialogTitle: 'Alert Banner Configuration',
26343
+ defaultSize: { colSpan: 4, rowSpan: 1 },
26344
+ supportedDataSources: ['runtimeEntity'],
26345
+ getInitialConfig: (widget) => {
26346
+ const abWidget = widget;
26347
+ return {
26348
+ ckTypeId: abWidget.ckTypeId ?? 'System.Notification/StatefulEvent',
26349
+ rotationInterval: abWidget.rotationInterval ?? 5000,
26350
+ showIcon: abWidget.showIcon ?? true,
26351
+ maxAlerts: abWidget.maxAlerts ?? 20
26352
+ };
26353
+ },
26354
+ applyConfigResult: (widget, result) => ({
26355
+ ...widget,
26356
+ ckTypeId: result.ckTypeId,
26357
+ rotationInterval: result.rotationInterval,
26358
+ showIcon: result.showIcon,
26359
+ maxAlerts: result.maxAlerts
26360
+ }),
26361
+ createDefaultConfig: (base) => ({
26362
+ ...base,
26363
+ type: 'alertBanner',
26364
+ colSpan: 4,
26365
+ rowSpan: 1,
26366
+ dataSource: { type: 'runtimeEntity' },
26367
+ ckTypeId: 'System.Notification/StatefulEvent'
26368
+ }),
26369
+ toPersistedConfig: (widget) => ({
26370
+ dataSourceType: 'runtimeEntity',
26371
+ dataSourceCkTypeId: widget.ckTypeId ?? 'System.Notification/StatefulEvent',
26372
+ config: {
26373
+ ckTypeId: widget.ckTypeId,
26374
+ rotationInterval: widget.rotationInterval,
26375
+ showIcon: widget.showIcon,
26376
+ maxAlerts: widget.maxAlerts
26377
+ }
26378
+ }),
26379
+ fromPersistedConfig: (data, base) => {
26380
+ const config = parseConfig(data);
26381
+ return {
26382
+ ...base,
26383
+ rtId: data.rtId,
26384
+ type: 'alertBanner',
26385
+ dataSource: { type: 'runtimeEntity', ckTypeId: data.ckTypeId ?? 'System.Notification/StatefulEvent' },
26386
+ ckTypeId: config['ckTypeId'] ?? data.ckTypeId ?? 'System.Notification/StatefulEvent',
26387
+ rotationInterval: config['rotationInterval'],
26388
+ showIcon: config['showIcon'],
26389
+ maxAlerts: config['maxAlerts']
26390
+ };
26391
+ }
26392
+ });
26393
+ // Alert List Widget
26394
+ registry.registerWidget({
26395
+ type: 'alertList',
26396
+ label: 'Alert List',
26397
+ component: AlertListWidgetComponent,
26398
+ configDialogComponent: AlertListConfigDialogComponent,
26399
+ configDialogSize: { width: 500, height: 400, minWidth: 400, minHeight: 300 },
26400
+ configDialogTitle: 'Alert List Configuration',
26401
+ defaultSize: { colSpan: 3, rowSpan: 3 },
26402
+ supportedDataSources: ['runtimeEntity'],
26403
+ getInitialConfig: (widget) => {
26404
+ const alWidget = widget;
26405
+ return {
26406
+ ckTypeId: alWidget.ckTypeId ?? 'System.Notification/StatefulEvent',
26407
+ showTimestamp: alWidget.showTimestamp ?? true,
26408
+ sortBySeverity: alWidget.sortBySeverity ?? true,
26409
+ maxAlerts: alWidget.maxAlerts ?? 50
26410
+ };
26411
+ },
26412
+ applyConfigResult: (widget, result) => ({
26413
+ ...widget,
26414
+ ckTypeId: result.ckTypeId,
26415
+ showTimestamp: result.showTimestamp,
26416
+ sortBySeverity: result.sortBySeverity,
26417
+ maxAlerts: result.maxAlerts
26418
+ }),
26419
+ createDefaultConfig: (base) => ({
26420
+ ...base,
26421
+ type: 'alertList',
26422
+ colSpan: 3,
26423
+ rowSpan: 3,
26424
+ dataSource: { type: 'runtimeEntity' },
26425
+ ckTypeId: 'System.Notification/StatefulEvent'
26426
+ }),
26427
+ toPersistedConfig: (widget) => ({
26428
+ dataSourceType: 'runtimeEntity',
26429
+ dataSourceCkTypeId: widget.ckTypeId ?? 'System.Notification/StatefulEvent',
26430
+ config: {
26431
+ ckTypeId: widget.ckTypeId,
26432
+ showTimestamp: widget.showTimestamp,
26433
+ sortBySeverity: widget.sortBySeverity,
26434
+ maxAlerts: widget.maxAlerts
26435
+ }
26436
+ }),
26437
+ fromPersistedConfig: (data, base) => {
26438
+ const config = parseConfig(data);
26439
+ return {
26440
+ ...base,
26441
+ rtId: data.rtId,
26442
+ type: 'alertList',
26443
+ dataSource: { type: 'runtimeEntity', ckTypeId: data.ckTypeId ?? 'System.Notification/StatefulEvent' },
26444
+ ckTypeId: config['ckTypeId'] ?? data.ckTypeId ?? 'System.Notification/StatefulEvent',
26445
+ showTimestamp: config['showTimestamp'],
26446
+ sortBySeverity: config['sortBySeverity'],
26447
+ maxAlerts: config['maxAlerts']
26448
+ };
26449
+ }
26450
+ });
26451
+ // AI Insights Widget
26452
+ registry.registerWidget({
26453
+ type: 'aiInsights',
26454
+ label: 'AI Insights',
26455
+ component: AiInsightsWidgetComponent,
26456
+ configDialogComponent: AiInsightsConfigDialogComponent,
26457
+ configDialogSize: { width: 600, height: 500, minWidth: 450, minHeight: 400 },
26458
+ configDialogTitle: 'AI Insights Configuration',
26459
+ defaultSize: { colSpan: 3, rowSpan: 2 },
26460
+ supportedDataSources: ['static'],
26461
+ getInitialConfig: (widget) => {
26462
+ const aiWidget = widget;
26463
+ return {
26464
+ apiKey: aiWidget.apiKey,
26465
+ model: aiWidget.model ?? 'claude-sonnet-4-20250514',
26466
+ domainContext: aiWidget.domainContext ?? 'energy management',
26467
+ refreshInterval: aiWidget.refreshInterval ?? 0,
26468
+ maxInsights: aiWidget.maxInsights ?? 4
26469
+ };
26470
+ },
26471
+ applyConfigResult: (widget, result) => ({
26472
+ ...widget,
26473
+ apiKey: result.apiKey,
26474
+ model: result.model,
26475
+ domainContext: result.domainContext,
26476
+ refreshInterval: result.refreshInterval,
26477
+ maxInsights: result.maxInsights,
26478
+ dataSource: { type: 'static' }
26479
+ }),
26480
+ createDefaultConfig: (base) => ({
26481
+ ...base,
26482
+ type: 'aiInsights',
26483
+ colSpan: 3,
26484
+ rowSpan: 2,
26485
+ dataSource: { type: 'static' },
26486
+ refreshInterval: 0,
26487
+ maxInsights: 4,
26488
+ domainContext: 'energy management'
26489
+ }),
26490
+ toPersistedConfig: (widget) => ({
26491
+ dataSourceType: 'static',
26492
+ config: {
26493
+ apiKey: widget.apiKey,
26494
+ model: widget.model,
26495
+ systemPrompt: widget.systemPrompt,
26496
+ refreshInterval: widget.refreshInterval,
26497
+ maxInsights: widget.maxInsights,
26498
+ domainContext: widget.domainContext
26499
+ }
26500
+ }),
26501
+ fromPersistedConfig: (data, base) => {
26502
+ const config = parseConfig(data);
26503
+ return {
26504
+ ...base,
26505
+ rtId: data.rtId,
26506
+ type: 'aiInsights',
26507
+ dataSource: { type: 'static' },
26508
+ apiKey: config['apiKey'],
26509
+ model: config['model'],
26510
+ systemPrompt: config['systemPrompt'],
26511
+ refreshInterval: config['refreshInterval'] ?? 0,
26512
+ maxInsights: config['maxInsights'] ?? 4,
26513
+ domainContext: config['domainContext'] ?? 'energy management'
26514
+ };
26515
+ }
26516
+ });
24467
26517
  // Note: Process Widget registration moved to process-widget-registration.ts
24468
26518
  // Use provideProcessWidget() or registerProcessWidget() to enable it
24469
26519
  }
@@ -25355,7 +27405,7 @@ class AddWidgetDialogComponent {
25355
27405
  this.dialogRef.close();
25356
27406
  }
25357
27407
  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: i2$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }] });
27408
+ 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
27409
  }
25360
27410
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AddWidgetDialogComponent, decorators: [{
25361
27411
  type: Component,
@@ -25735,7 +27785,7 @@ class MeshBoardManagerDialogComponent {
25735
27785
  }
25736
27786
  }
25737
27787
  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"] }] });
27788
+ 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
27789
  }
25740
27790
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MeshBoardManagerDialogComponent, decorators: [{
25741
27791
  type: Component,
@@ -25773,7 +27823,8 @@ class EditWidgetDialogComponent {
25773
27823
  col: this.widget.col,
25774
27824
  row: this.widget.row,
25775
27825
  colSpan: this.widget.colSpan,
25776
- rowSpan: this.widget.rowSpan
27826
+ rowSpan: this.widget.rowSpan,
27827
+ chromeless: this.widget.chromeless ?? false
25777
27828
  };
25778
27829
  }
25779
27830
  onSave() {
@@ -25859,6 +27910,11 @@ class EditWidgetDialogComponent {
25859
27910
  </div>
25860
27911
  </div>
25861
27912
 
27913
+ <div class="form-field form-field-checkbox">
27914
+ <input type="checkbox" id="editWidgetChromeless" [(ngModel)]="form.chromeless" />
27915
+ <label for="editWidgetChromeless">Chromeless (hide title bar and border in view mode)</label>
27916
+ </div>
27917
+
25862
27918
  @if (error) {
25863
27919
  <div class="form-error">{{ error }}</div>
25864
27920
  }
@@ -25875,7 +27931,7 @@ class EditWidgetDialogComponent {
25875
27931
  </kendo-dialog-actions>
25876
27932
 
25877
27933
  </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$4.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$4.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"] }] });
27934
+ `, 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-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"] }] });
25879
27935
  }
25880
27936
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: EditWidgetDialogComponent, decorators: [{
25881
27937
  type: Component,
@@ -25950,6 +28006,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
25950
28006
  </div>
25951
28007
  </div>
25952
28008
 
28009
+ <div class="form-field form-field-checkbox">
28010
+ <input type="checkbox" id="editWidgetChromeless" [(ngModel)]="form.chromeless" />
28011
+ <label for="editWidgetChromeless">Chromeless (hide title bar and border in view mode)</label>
28012
+ </div>
28013
+
25953
28014
  @if (error) {
25954
28015
  <div class="form-error">{{ error }}</div>
25955
28016
  }
@@ -25966,7 +28027,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
25966
28027
  </kendo-dialog-actions>
25967
28028
 
25968
28029
  </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"] }]
28030
+ `, 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-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
28031
  }], propDecorators: { widget: [{
25971
28032
  type: Input
25972
28033
  }], widgets: [{
@@ -26049,6 +28110,9 @@ class MeshBoardViewComponent {
26049
28110
  }, ...(ngDevMode ? [{ debugName: "meshBoardPageLink" }] : /* istanbul ignore next */ []));
26050
28111
  // Computed
26051
28112
  hasWidgets = computed(() => this.config().widgets.length > 0, ...(ngDevMode ? [{ debugName: "hasWidgets" }] : /* istanbul ignore next */ []));
28113
+ bannerWidgets = computed(() => this.config().widgets.filter(w => w.chromeless), ...(ngDevMode ? [{ debugName: "bannerWidgets" }] : /* istanbul ignore next */ []));
28114
+ gridWidgets = computed(() => this.config().widgets.filter(w => !w.chromeless), ...(ngDevMode ? [{ debugName: "gridWidgets" }] : /* istanbul ignore next */ []));
28115
+ hasGridWidgets = computed(() => this.gridWidgets().length > 0, ...(ngDevMode ? [{ debugName: "hasGridWidgets" }] : /* istanbul ignore next */ []));
26052
28116
  canSave = computed(() => this.isEditMode() && !this.isSaving(), ...(ngDevMode ? [{ debugName: "canSave" }] : /* istanbul ignore next */ []));
26053
28117
  // Time Filter computed signals
26054
28118
  isTimeFilterEnabled = computed(() => this.stateService.isTimeFilterEnabled(), ...(ngDevMode ? [{ debugName: "isTimeFilterEnabled" }] : /* istanbul ignore next */ []));
@@ -26833,7 +28897,8 @@ class MeshBoardViewComponent {
26833
28897
  col: update.col,
26834
28898
  row: update.row,
26835
28899
  colSpan: update.colSpan,
26836
- rowSpan: update.rowSpan
28900
+ rowSpan: update.rowSpan,
28901
+ chromeless: update.chromeless
26837
28902
  }));
26838
28903
  this.closeEditWidgetDialog();
26839
28904
  // Enter edit mode if not already in it
@@ -26995,7 +29060,7 @@ class MeshBoardViewComponent {
26995
29060
  }
26996
29061
  }
26997
29062
  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"] }] });
29063
+ 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$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: chromeless widgets rendered outside the grid -->\n @for (widget of bannerWidgets(); track trackByWidgetId($index, widget)) {\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 @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]=\"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 (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\" [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}}:host ::ng-deep kendo-tilelayout.has-banners{padding-top:0!important}:host ::ng-deep kendo-tilelayout{padding-bottom:1.5rem!important}.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
29064
  }
27000
29065
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MeshBoardViewComponent, decorators: [{
27001
29066
  type: Component,
@@ -27009,7 +29074,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
27009
29074
  EditWidgetDialogComponent,
27010
29075
  TimeRangePickerComponent,
27011
29076
  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"] }]
29077
+ ], 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: chromeless widgets rendered outside the grid -->\n @for (widget of bannerWidgets(); track trackByWidgetId($index, widget)) {\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 @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]=\"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 (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\" [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}}:host ::ng-deep kendo-tilelayout.has-banners{padding-top:0!important}:host ::ng-deep kendo-tilelayout{padding-bottom:1.5rem!important}.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"] }]
27013
29078
  }], ctorParameters: () => [] });
27014
29079
 
27015
29080
  /*
@@ -27021,5 +29086,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
27021
29086
  * Generated bundle index. Do not edit.
27022
29087
  */
27023
29088
 
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 };
29089
+ 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
29090
  //# sourceMappingURL=meshmakers-octo-meshboard.mjs.map