@masterteam/dashboard-builder 0.0.10 → 0.0.12

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.
@@ -49,6 +49,7 @@ import * as i6 from 'primeng/select';
49
49
  import { SelectModule } from 'primeng/select';
50
50
  import { Router, ActivatedRoute } from '@angular/router';
51
51
  import * as mtIcons from '@masterteam/icons/assets/select-icons.json';
52
+ import { EntityPreview } from '@masterteam/components/entities';
52
53
  import * as i2$3 from 'primeng/button';
53
54
  import { ButtonModule } from 'primeng/button';
54
55
  import { rxResource } from '@angular/core/rxjs-interop';
@@ -15230,7 +15231,7 @@ class ActionService {
15230
15231
  const actionData = this.handleActionsConfig(action.config, data, config);
15231
15232
  debugger;
15232
15233
  // Dynamically import DashboardDialogComponent to avoid circular dependency
15233
- import('./masterteam-dashboard-builder-dashboard-dialog.component-697C4AOo.mjs').then(({ DashboardDialogComponent }) => {
15234
+ import('./masterteam-dashboard-builder-dashboard-dialog.component-CF1Lnyxf.mjs').then(({ DashboardDialogComponent }) => {
15234
15235
  // Open dialog using ModalService
15235
15236
  this.modalService.openModal(DashboardDialogComponent, 'dialog', {
15236
15237
  header: config.clientConfig?.title?.['en'] || '',
@@ -17996,8 +17997,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
17996
17997
  *
17997
17998
  * Matches old app-entity-preview-card behavior:
17998
17999
  * - Reads DataHandled.values from store
17999
- * - Transforms to props array with propInfo and config
18000
- * - Displays using entity-info style layout
18000
+ * - Passes property items to the shared entity preview with layout config
18001
+ * - Displays using the shared entity preview components package
18001
18002
  */
18002
18003
  /**
18003
18004
  * Reorder properties based on selectedProperties order
@@ -18061,7 +18062,6 @@ class EntityPreviewCardComponent {
18061
18062
  * Subscribe to store updates for this dashboard ID
18062
18063
  */
18063
18064
  subscribeToStore(dashboardId) {
18064
- const languageCode = this.getLanguageCode();
18065
18065
  // Subscribe to ConfigurationItem
18066
18066
  this.subscription.add(this.storeService
18067
18067
  .selectItemProperty$(dashboardId, 'ConfigurationItem')
@@ -18080,36 +18080,15 @@ class EntityPreviewCardComponent {
18080
18080
  // Transform values to props array (same as old logic)
18081
18081
  const config = this.configurationItem();
18082
18082
  const propsArray = Object.keys(values).map((key) => values[key]);
18083
- console.log('qqq', config?.serviceConfig?.query?.['selectedProperties']);
18084
18083
  // Reorder based on selectedProperties
18085
18084
  const propsOrdered = reorderPropsAsKey(propsArray, config?.serviceConfig?.query?.['selectedProperties']);
18086
18085
  // Apply configAsType settings
18087
18086
  const configAsType = config?.clientConfig?.configAsType;
18088
- const overrideLabels = configAsType?.['overrideSelectedPropertiesLabels'];
18089
18087
  const propsConfigAsIndex = configAsType?.['propsConfigAsIndex'] || {};
18090
18088
  const processedProps = propsOrdered.map((prop, index) => {
18091
- const propConfig = propsConfigAsIndex[index] || {};
18092
- // Handle colorAsProperty
18093
- if (propConfig.colorAsProperty) {
18094
- if (propConfig.colorAsProperty.startsWith('#')) {
18095
- if (prop.value && typeof prop.value === 'object') {
18096
- prop.value.color = propConfig.colorAsProperty;
18097
- }
18098
- }
18099
- else {
18100
- const colorProp = propsOrdered.find((p) => p.key?.toLowerCase() ===
18101
- propConfig.colorAsProperty?.toLowerCase());
18102
- if (colorProp?.value?.details?.color && prop.value) {
18103
- if (typeof prop.value === 'object') {
18104
- prop.value.color = colorProp.value.details.color;
18105
- }
18106
- }
18107
- }
18108
- }
18109
- // Override label if configured
18110
- if (overrideLabels?.[prop.key]) {
18111
- prop.label = overrideLabels[prop.key]?.[languageCode];
18112
- }
18089
+ const propConfig = {
18090
+ ...(propsConfigAsIndex[index] || {}),
18091
+ };
18113
18092
  // Mobile adjustments
18114
18093
  if (isMobilePlatform()) {
18115
18094
  if (!propConfig.width || parseInt(propConfig.width, 10) < 50) {
@@ -18121,7 +18100,6 @@ class EntityPreviewCardComponent {
18121
18100
  config: propConfig,
18122
18101
  };
18123
18102
  });
18124
- console.log('processedProps', processedProps);
18125
18103
  this.props.set(processedProps);
18126
18104
  }));
18127
18105
  }
@@ -18132,7 +18110,7 @@ class EntityPreviewCardComponent {
18132
18110
  return (this.transloco.getActiveLang() || localStorage.getItem('langCode') || 'en');
18133
18111
  }
18134
18112
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: EntityPreviewCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18135
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: EntityPreviewCardComponent, isStandalone: true, selector: "mt-entity-preview-card", inputs: { dashboardId: { classPropertyName: "dashboardId", publicName: "dashboardId", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<mt-card-content\r\n [inGroup]=\"false\"\r\n [headerConfig]=\"\r\n configurationItem()?.clientConfig?.configAsType?.['headerCardConfig']\r\n \"\r\n [styleConfig]=\"\r\n configurationItem()?.clientConfig?.displayConfig?.['StyleConfig']\r\n \"\r\n [cardStyleConfig]=\"\r\n configurationItem()?.clientConfig?.configAsType?.['cardStyleConfig']\r\n \"\r\n [title]=\"title()\"\r\n>\r\n <ng-container body>\r\n <div\r\n class=\"flex flex-wrap flex-1 p-4 pb-5\"\r\n *transloco=\"let t; prefix: 'dashboard'\"\r\n >\r\n @for (item of props(); track item.propInfo.key) {\r\n @if (!item.config?.hidden) {\r\n <div\r\n [style.width]=\"item.config?.width || '100%'\"\r\n style=\"min-width: 180px\"\r\n [class.border]=\"item.config?.border?.includes('all')\"\r\n [class.border-t]=\"item.config?.border?.includes('top')\"\r\n [class.border-l]=\"item.config?.border?.includes('left')\"\r\n [class.border-b]=\"item.config?.border?.includes('bottom')\"\r\n [class.border-r]=\"item.config?.border?.includes('right')\"\r\n class=\"flex items-center p-2\"\r\n >\r\n <!-- Use EntityInfoComponent for proper viewType rendering -->\r\n <mt-entity-info\r\n [data]=\"item.propInfo\"\r\n [displayType]=\"'entity'\"\r\n class=\"w-full\"\r\n ></mt-entity-info>\r\n </div>\r\n }\r\n }\r\n\r\n @if (props().length === 0) {\r\n <div\r\n class=\"no-data-container w-full flex items-center justify-center py-8 text-muted-color\"\r\n >\r\n <i class=\"pi pi-info-circle mr-2\"></i>\r\n <span>{{ t(\"noData\") }}</span>\r\n </div>\r\n }\r\n </div>\r\n </ng-container>\r\n</mt-card-content>\r\n", styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: CardContentComponent, selector: "mt-card-content", inputs: ["title", "inGroup", "headerConfig", "styleConfig", "cardStyleConfig", "showHeader", "headerClickable", "isNoTopEnd", "isChart"], outputs: ["headerClick"] }, { kind: "component", type: EntityInfoComponent, selector: "mt-entity-info", inputs: ["data", "displayType", "extraInfoData", "limitWords", "customClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18113
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: EntityPreviewCardComponent, isStandalone: true, selector: "mt-entity-preview-card", inputs: { dashboardId: { classPropertyName: "dashboardId", publicName: "dashboardId", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<mt-card-content\r\n [inGroup]=\"false\"\r\n [headerConfig]=\"\r\n configurationItem()?.clientConfig?.configAsType?.['headerCardConfig']\r\n \"\r\n [styleConfig]=\"\r\n configurationItem()?.clientConfig?.displayConfig?.['StyleConfig']\r\n \"\r\n [cardStyleConfig]=\"\r\n configurationItem()?.clientConfig?.configAsType?.['cardStyleConfig']\r\n \"\r\n [title]=\"title()\"\r\n>\r\n <ng-container body>\r\n <div\r\n class=\"flex flex-wrap flex-1 p-4 pb-5\"\r\n *transloco=\"let t; prefix: 'dashboard'\"\r\n >\r\n @for (item of props(); track item.propInfo.key) {\n @if (!item.config?.hidden) {\n <div\n [style.width]=\"item.config?.width || '100%'\"\n style=\"min-width: 180px\"\n [class.border]=\"item.config?.border?.includes('all')\"\r\n [class.border-t]=\"item.config?.border?.includes('top')\"\r\n [class.border-l]=\"item.config?.border?.includes('left')\"\r\n [class.border-b]=\"item.config?.border?.includes('bottom')\"\n [class.border-r]=\"item.config?.border?.includes('right')\"\n class=\"flex items-center p-2\"\n >\n <mt-entity-preview [data]=\"item.propInfo\" class=\"w-full\">\n </mt-entity-preview>\n </div>\n }\n }\n\r\n @if (props().length === 0) {\r\n <div\r\n class=\"no-data-container w-full flex items-center justify-center py-8 text-muted-color\"\r\n >\r\n <i class=\"pi pi-info-circle mr-2\"></i>\r\n <span>{{ t(\"noData\") }}</span>\r\n </div>\r\n }\r\n </div>\r\n </ng-container>\r\n</mt-card-content>\r\n", styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: CardContentComponent, selector: "mt-card-content", inputs: ["title", "inGroup", "headerConfig", "styleConfig", "cardStyleConfig", "showHeader", "headerClickable", "isNoTopEnd", "isChart"], outputs: ["headerClick"] }, { kind: "component", type: EntityPreview, selector: "mt-entity-preview", inputs: ["data", "attachmentShape"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18136
18114
  }
18137
18115
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: EntityPreviewCardComponent, decorators: [{
18138
18116
  type: Component,
@@ -18140,8 +18118,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
18140
18118
  CommonModule,
18141
18119
  TranslocoDirective,
18142
18120
  CardContentComponent,
18143
- EntityInfoComponent,
18144
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<mt-card-content\r\n [inGroup]=\"false\"\r\n [headerConfig]=\"\r\n configurationItem()?.clientConfig?.configAsType?.['headerCardConfig']\r\n \"\r\n [styleConfig]=\"\r\n configurationItem()?.clientConfig?.displayConfig?.['StyleConfig']\r\n \"\r\n [cardStyleConfig]=\"\r\n configurationItem()?.clientConfig?.configAsType?.['cardStyleConfig']\r\n \"\r\n [title]=\"title()\"\r\n>\r\n <ng-container body>\r\n <div\r\n class=\"flex flex-wrap flex-1 p-4 pb-5\"\r\n *transloco=\"let t; prefix: 'dashboard'\"\r\n >\r\n @for (item of props(); track item.propInfo.key) {\r\n @if (!item.config?.hidden) {\r\n <div\r\n [style.width]=\"item.config?.width || '100%'\"\r\n style=\"min-width: 180px\"\r\n [class.border]=\"item.config?.border?.includes('all')\"\r\n [class.border-t]=\"item.config?.border?.includes('top')\"\r\n [class.border-l]=\"item.config?.border?.includes('left')\"\r\n [class.border-b]=\"item.config?.border?.includes('bottom')\"\r\n [class.border-r]=\"item.config?.border?.includes('right')\"\r\n class=\"flex items-center p-2\"\r\n >\r\n <!-- Use EntityInfoComponent for proper viewType rendering -->\r\n <mt-entity-info\r\n [data]=\"item.propInfo\"\r\n [displayType]=\"'entity'\"\r\n class=\"w-full\"\r\n ></mt-entity-info>\r\n </div>\r\n }\r\n }\r\n\r\n @if (props().length === 0) {\r\n <div\r\n class=\"no-data-container w-full flex items-center justify-center py-8 text-muted-color\"\r\n >\r\n <i class=\"pi pi-info-circle mr-2\"></i>\r\n <span>{{ t(\"noData\") }}</span>\r\n </div>\r\n }\r\n </div>\r\n </ng-container>\r\n</mt-card-content>\r\n", styles: [":host{display:block;height:100%}\n"] }]
18121
+ EntityPreview,
18122
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<mt-card-content\r\n [inGroup]=\"false\"\r\n [headerConfig]=\"\r\n configurationItem()?.clientConfig?.configAsType?.['headerCardConfig']\r\n \"\r\n [styleConfig]=\"\r\n configurationItem()?.clientConfig?.displayConfig?.['StyleConfig']\r\n \"\r\n [cardStyleConfig]=\"\r\n configurationItem()?.clientConfig?.configAsType?.['cardStyleConfig']\r\n \"\r\n [title]=\"title()\"\r\n>\r\n <ng-container body>\r\n <div\r\n class=\"flex flex-wrap flex-1 p-4 pb-5\"\r\n *transloco=\"let t; prefix: 'dashboard'\"\r\n >\r\n @for (item of props(); track item.propInfo.key) {\n @if (!item.config?.hidden) {\n <div\n [style.width]=\"item.config?.width || '100%'\"\n style=\"min-width: 180px\"\n [class.border]=\"item.config?.border?.includes('all')\"\r\n [class.border-t]=\"item.config?.border?.includes('top')\"\r\n [class.border-l]=\"item.config?.border?.includes('left')\"\r\n [class.border-b]=\"item.config?.border?.includes('bottom')\"\n [class.border-r]=\"item.config?.border?.includes('right')\"\n class=\"flex items-center p-2\"\n >\n <mt-entity-preview [data]=\"item.propInfo\" class=\"w-full\">\n </mt-entity-preview>\n </div>\n }\n }\n\r\n @if (props().length === 0) {\r\n <div\r\n class=\"no-data-container w-full flex items-center justify-center py-8 text-muted-color\"\r\n >\r\n <i class=\"pi pi-info-circle mr-2\"></i>\r\n <span>{{ t(\"noData\") }}</span>\r\n </div>\r\n }\r\n </div>\r\n </ng-container>\r\n</mt-card-content>\r\n", styles: [":host{display:block;height:100%}\n"] }]
18145
18123
  }], propDecorators: { dashboardId: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardId", required: true }] }] } });
18146
18124
 
18147
18125
  /**
@@ -18291,6 +18269,10 @@ class DashboardItem {
18291
18269
  isDialog = input(false, ...(ngDevMode ? [{ debugName: "isDialog" }] : []));
18292
18270
  /** Query params passed from parent (e.g., dialog) */
18293
18271
  queryParams = input({}, ...(ngDevMode ? [{ debugName: "queryParams" }] : []));
18272
+ /** Dashboard-wide extra filters passed from viewer/context */
18273
+ extraFilters = input({}, ...(ngDevMode ? [{ debugName: "extraFilters" }] : []));
18274
+ /** Skip global route/query filters for this item */
18275
+ ignoreQueryFilter = input(false, ...(ngDevMode ? [{ debugName: "ignoreQueryFilter" }] : []));
18294
18276
  // ============================================
18295
18277
  // Outputs
18296
18278
  // ============================================
@@ -18462,11 +18444,7 @@ class DashboardItem {
18462
18444
  this.isLoading.set(false);
18463
18445
  return;
18464
18446
  }
18465
- // Get query params and extra filters from store
18466
- const queryParams = this.storeService.getQueryParams() || {};
18467
- const extraFilters = this.storeService.getExtraFilterForCurrentAction(dashboardId) || {};
18468
- // Merge all filters
18469
- const filters = { ...queryParams, ...extraFilters };
18447
+ const filters = this.buildRequestFilters(dashboardId);
18470
18448
  this.isLoading.set(true);
18471
18449
  this.hasError.set(false);
18472
18450
  // Use ChartDataService for ALL request types
@@ -18527,8 +18505,7 @@ class DashboardItem {
18527
18505
  if (!dashboardId) {
18528
18506
  return;
18529
18507
  }
18530
- // Get parent filter from store
18531
- const parentFilter = this.storeService.getExtraFilterForCurrentAction(dashboardId) || {};
18508
+ const parentFilter = this.buildParentFilter(dashboardId);
18532
18509
  // Merge event data with parent filter context
18533
18510
  const enrichedData = {
18534
18511
  ...eventData?.data,
@@ -18543,8 +18520,40 @@ class DashboardItem {
18543
18520
  config,
18544
18521
  });
18545
18522
  }
18523
+ buildRequestFilters(dashboardId) {
18524
+ const globalQueryParams = this.ignoreQueryFilter()
18525
+ ? {}
18526
+ : (this.storeService.getQueryParams() ?? {});
18527
+ const globalExtraFilters = this.extraFilters() ?? {};
18528
+ const itemStoreQueryParams = this.itemStoreService.getQueryParams(dashboardId) ?? {};
18529
+ const preQueryParams = this.itemStoreService.getStore(dashboardId)?.preQueryParams ?? {};
18530
+ const inputQueryParams = this.queryParams() ?? {};
18531
+ const itemExtraFilters = this.itemStoreService.getExtraFilterForCurrentAction(dashboardId) ?? {};
18532
+ return this.mergeRequestFilters(globalQueryParams, globalExtraFilters, preQueryParams, itemStoreQueryParams, inputQueryParams, itemExtraFilters);
18533
+ }
18534
+ buildParentFilter(dashboardId) {
18535
+ return this.buildRequestFilters(dashboardId);
18536
+ }
18537
+ mergeRequestFilters(...sources) {
18538
+ const merged = {};
18539
+ sources.forEach((source) => {
18540
+ if (!source) {
18541
+ return;
18542
+ }
18543
+ Object.entries(source).forEach(([key, value]) => {
18544
+ if (value === undefined || value === null || value === '') {
18545
+ return;
18546
+ }
18547
+ if (Array.isArray(value) && value.length === 0) {
18548
+ return;
18549
+ }
18550
+ merged[key] = value;
18551
+ });
18552
+ });
18553
+ return merged;
18554
+ }
18546
18555
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
18547
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: DashboardItem, isStandalone: true, selector: "mt-dashboard-item", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, chartTypeId: { classPropertyName: "chartTypeId", publicName: "chartTypeId", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, inGroup: { classPropertyName: "inGroup", publicName: "inGroup", isSignal: true, isRequired: false, transformFunction: null }, isDialog: { classPropertyName: "isDialog", publicName: "isDialog", isSignal: true, isRequired: false, transformFunction: null }, queryParams: { classPropertyName: "queryParams", publicName: "queryParams", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionTriggered: "actionTriggered" }, ngImport: i0, template: "@if (isLoading()) {\r\n <mt-skeleton-card [dashboardId]=\"dashboardId()\"></mt-skeleton-card>\r\n} @else if (hasError()) {\r\n <div class=\"flex flex-col items-center justify-center h-full p-4 text-center\">\r\n <mt-icon\r\n icon=\"general.alert-circle\"\r\n class=\"text-4xl text-red-500 mb-2\"\r\n ></mt-icon>\r\n <p class=\"text-red-500 font-medium\">\r\n {{ errorMessage() || \"Error loading data\" }}\r\n </p>\r\n <mt-button\r\n class=\"mt-2\"\r\n [label]=\"'Retry'\"\r\n severity=\"secondary\"\r\n size=\"small\"\r\n icon=\"arrow.refresh-cw-01\"\r\n (onClick)=\"refresh()\"\r\n />\r\n </div>\r\n} @else {\r\n @switch (componentName()) {\r\n <!-- Chart Types -->\r\n @case (\"chart\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Map Chart -->\r\n @case (\"map\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Background Text -->\r\n @case (\"BackgroundTextChart\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Chart Custom With Card Info -->\r\n @case (\"chartCustomWithCardInfo\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- List of Charts -->\r\n @case (\"listOfcharts\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- List of Cards Statistic -->\r\n @case (\"listOfCardsStatistic\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Cards Statistic (single) -->\r\n @case (\"cardsStatistic\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Header -->\r\n @case (\"header\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Footer (same as header) -->\r\n @case (\"footer\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Label (same as header) -->\r\n @case (\"label\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- List of Level Cards -->\r\n @case (\"listOfLevelCards\") {\r\n <mt-level-card-list\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-level-card-list>\r\n }\r\n\r\n <!-- Table -->\r\n @case (\"table\") {\r\n <mt-table-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-table-card>\r\n }\r\n\r\n <!-- Phase Gate Table -->\r\n @case (\"phaseGateTable\") {\r\n <mt-table-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-table-card>\r\n }\r\n\r\n <!-- Topbar -->\r\n @case (\"topbar\") {\r\n <!-- Topbar handled at viewer level -->\r\n }\r\n\r\n <!-- Timeline -->\r\n @case (\"timeline\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Timeline Project Dependencies -->\r\n @case (\"timelineProjectDependencies\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Last History Level Card -->\r\n @case (\"lastHistoryLevelCard\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Entity Preview -->\r\n @case (\"entityPreview\") {\r\n <mt-entity-preview-card [dashboardId]=\"dashboardId()\">\r\n </mt-entity-preview-card>\r\n }\r\n\r\n <!-- Cluster Stacked Chart -->\r\n @case (\"clusterStackedChart\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- List Component S+ Card -->\r\n @case (\"listComponentSplusCard\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Splitter Chart -->\r\n @case (\"splitterChart\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Repeater (same as header) -->\r\n @case (\"repeater\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Level Cards With Static -->\r\n @case (\"levelCardsWithStatic\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Phase Gate Stepper Card -->\r\n @case (\"phaseGateStepperCard\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Dialog -->\r\n @case (\"dialog\") {\r\n <!-- Dialog handled separately via ModalService -->\r\n }\r\n\r\n <!-- Default: try chart -->\r\n @default {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n }\r\n}\r\n", styles: [":host{display:block;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: ChartCardComponent, selector: "mt-chart-card", inputs: ["dashboardId", "inGroup", "isConfigMode"], outputs: ["onAction"] }, { kind: "component", type: TableCardComponent, selector: "mt-table-card", inputs: ["dashboardId", "inGroup"], outputs: ["onAction"] }, { kind: "component", type: ListStatisticCardComponent, selector: "mt-list-statistic-card", inputs: ["dashboardId"] }, { kind: "component", type: HeaderCardComponent, selector: "mt-header-card", inputs: ["dashboardId"], outputs: ["onAction"] }, { kind: "component", type: SkeletonCardComponent, selector: "mt-skeleton-card", inputs: ["dashboardId"] }, { kind: "component", type: EntityPreviewCardComponent, selector: "mt-entity-preview-card", inputs: ["dashboardId"] }, { kind: "component", type: LevelCardListComponent, selector: "mt-level-card-list", inputs: ["dashboardId", "inGroup", "hideProperties"], outputs: ["onAction"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
18556
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: DashboardItem, isStandalone: true, selector: "mt-dashboard-item", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, chartTypeId: { classPropertyName: "chartTypeId", publicName: "chartTypeId", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, inGroup: { classPropertyName: "inGroup", publicName: "inGroup", isSignal: true, isRequired: false, transformFunction: null }, isDialog: { classPropertyName: "isDialog", publicName: "isDialog", isSignal: true, isRequired: false, transformFunction: null }, queryParams: { classPropertyName: "queryParams", publicName: "queryParams", isSignal: true, isRequired: false, transformFunction: null }, extraFilters: { classPropertyName: "extraFilters", publicName: "extraFilters", isSignal: true, isRequired: false, transformFunction: null }, ignoreQueryFilter: { classPropertyName: "ignoreQueryFilter", publicName: "ignoreQueryFilter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionTriggered: "actionTriggered" }, ngImport: i0, template: "@if (isLoading()) {\r\n <mt-skeleton-card [dashboardId]=\"dashboardId()\"></mt-skeleton-card>\r\n} @else if (hasError()) {\r\n <div class=\"flex flex-col items-center justify-center h-full p-4 text-center\">\r\n <mt-icon\r\n icon=\"general.alert-circle\"\r\n class=\"text-4xl text-red-500 mb-2\"\r\n ></mt-icon>\r\n <p class=\"text-red-500 font-medium\">\r\n {{ errorMessage() || \"Error loading data\" }}\r\n </p>\r\n <mt-button\r\n class=\"mt-2\"\r\n [label]=\"'Retry'\"\r\n severity=\"secondary\"\r\n size=\"small\"\r\n icon=\"arrow.refresh-cw-01\"\r\n (onClick)=\"refresh()\"\r\n />\r\n </div>\r\n} @else {\r\n @switch (componentName()) {\r\n <!-- Chart Types -->\r\n @case (\"chart\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Map Chart -->\r\n @case (\"map\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Background Text -->\r\n @case (\"BackgroundTextChart\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Chart Custom With Card Info -->\r\n @case (\"chartCustomWithCardInfo\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- List of Charts -->\r\n @case (\"listOfcharts\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- List of Cards Statistic -->\r\n @case (\"listOfCardsStatistic\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Cards Statistic (single) -->\r\n @case (\"cardsStatistic\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Header -->\r\n @case (\"header\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Footer (same as header) -->\r\n @case (\"footer\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Label (same as header) -->\r\n @case (\"label\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- List of Level Cards -->\r\n @case (\"listOfLevelCards\") {\r\n <mt-level-card-list\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-level-card-list>\r\n }\r\n\r\n <!-- Table -->\r\n @case (\"table\") {\r\n <mt-table-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-table-card>\r\n }\r\n\r\n <!-- Phase Gate Table -->\r\n @case (\"phaseGateTable\") {\r\n <mt-table-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-table-card>\r\n }\r\n\r\n <!-- Topbar -->\r\n @case (\"topbar\") {\r\n <!-- Topbar handled at viewer level -->\r\n }\r\n\r\n <!-- Timeline -->\r\n @case (\"timeline\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Timeline Project Dependencies -->\r\n @case (\"timelineProjectDependencies\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Last History Level Card -->\r\n @case (\"lastHistoryLevelCard\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Entity Preview -->\r\n @case (\"entityPreview\") {\r\n <mt-entity-preview-card [dashboardId]=\"dashboardId()\">\r\n </mt-entity-preview-card>\r\n }\r\n\r\n <!-- Cluster Stacked Chart -->\r\n @case (\"clusterStackedChart\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- List Component S+ Card -->\r\n @case (\"listComponentSplusCard\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Splitter Chart -->\r\n @case (\"splitterChart\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Repeater (same as header) -->\r\n @case (\"repeater\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Level Cards With Static -->\r\n @case (\"levelCardsWithStatic\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Phase Gate Stepper Card -->\r\n @case (\"phaseGateStepperCard\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Dialog -->\r\n @case (\"dialog\") {\r\n <!-- Dialog handled separately via ModalService -->\r\n }\r\n\r\n <!-- Default: try chart -->\r\n @default {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n }\r\n}\r\n", styles: [":host{display:block;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: ChartCardComponent, selector: "mt-chart-card", inputs: ["dashboardId", "inGroup", "isConfigMode"], outputs: ["onAction"] }, { kind: "component", type: TableCardComponent, selector: "mt-table-card", inputs: ["dashboardId", "inGroup"], outputs: ["onAction"] }, { kind: "component", type: ListStatisticCardComponent, selector: "mt-list-statistic-card", inputs: ["dashboardId"] }, { kind: "component", type: HeaderCardComponent, selector: "mt-header-card", inputs: ["dashboardId"], outputs: ["onAction"] }, { kind: "component", type: SkeletonCardComponent, selector: "mt-skeleton-card", inputs: ["dashboardId"] }, { kind: "component", type: EntityPreviewCardComponent, selector: "mt-entity-preview-card", inputs: ["dashboardId"] }, { kind: "component", type: LevelCardListComponent, selector: "mt-level-card-list", inputs: ["dashboardId", "inGroup", "hideProperties"], outputs: ["onAction"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
18548
18557
  }
18549
18558
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardItem, decorators: [{
18550
18559
  type: Component,
@@ -18562,7 +18571,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
18562
18571
  EntityPreviewCardComponent,
18563
18572
  LevelCardListComponent,
18564
18573
  ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (isLoading()) {\r\n <mt-skeleton-card [dashboardId]=\"dashboardId()\"></mt-skeleton-card>\r\n} @else if (hasError()) {\r\n <div class=\"flex flex-col items-center justify-center h-full p-4 text-center\">\r\n <mt-icon\r\n icon=\"general.alert-circle\"\r\n class=\"text-4xl text-red-500 mb-2\"\r\n ></mt-icon>\r\n <p class=\"text-red-500 font-medium\">\r\n {{ errorMessage() || \"Error loading data\" }}\r\n </p>\r\n <mt-button\r\n class=\"mt-2\"\r\n [label]=\"'Retry'\"\r\n severity=\"secondary\"\r\n size=\"small\"\r\n icon=\"arrow.refresh-cw-01\"\r\n (onClick)=\"refresh()\"\r\n />\r\n </div>\r\n} @else {\r\n @switch (componentName()) {\r\n <!-- Chart Types -->\r\n @case (\"chart\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Map Chart -->\r\n @case (\"map\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Background Text -->\r\n @case (\"BackgroundTextChart\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Chart Custom With Card Info -->\r\n @case (\"chartCustomWithCardInfo\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- List of Charts -->\r\n @case (\"listOfcharts\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- List of Cards Statistic -->\r\n @case (\"listOfCardsStatistic\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Cards Statistic (single) -->\r\n @case (\"cardsStatistic\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Header -->\r\n @case (\"header\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Footer (same as header) -->\r\n @case (\"footer\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Label (same as header) -->\r\n @case (\"label\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- List of Level Cards -->\r\n @case (\"listOfLevelCards\") {\r\n <mt-level-card-list\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-level-card-list>\r\n }\r\n\r\n <!-- Table -->\r\n @case (\"table\") {\r\n <mt-table-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-table-card>\r\n }\r\n\r\n <!-- Phase Gate Table -->\r\n @case (\"phaseGateTable\") {\r\n <mt-table-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-table-card>\r\n }\r\n\r\n <!-- Topbar -->\r\n @case (\"topbar\") {\r\n <!-- Topbar handled at viewer level -->\r\n }\r\n\r\n <!-- Timeline -->\r\n @case (\"timeline\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Timeline Project Dependencies -->\r\n @case (\"timelineProjectDependencies\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Last History Level Card -->\r\n @case (\"lastHistoryLevelCard\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Entity Preview -->\r\n @case (\"entityPreview\") {\r\n <mt-entity-preview-card [dashboardId]=\"dashboardId()\">\r\n </mt-entity-preview-card>\r\n }\r\n\r\n <!-- Cluster Stacked Chart -->\r\n @case (\"clusterStackedChart\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- List Component S+ Card -->\r\n @case (\"listComponentSplusCard\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Splitter Chart -->\r\n @case (\"splitterChart\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Repeater (same as header) -->\r\n @case (\"repeater\") {\r\n <mt-header-card [dashboardId]=\"dashboardId()\"> </mt-header-card>\r\n }\r\n\r\n <!-- Level Cards With Static -->\r\n @case (\"levelCardsWithStatic\") {\r\n <mt-list-statistic-card [dashboardId]=\"dashboardId()\">\r\n </mt-list-statistic-card>\r\n }\r\n\r\n <!-- Phase Gate Stepper Card -->\r\n @case (\"phaseGateStepperCard\") {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n\r\n <!-- Dialog -->\r\n @case (\"dialog\") {\r\n <!-- Dialog handled separately via ModalService -->\r\n }\r\n\r\n <!-- Default: try chart -->\r\n @default {\r\n <mt-chart-card\r\n [dashboardId]=\"dashboardId()\"\r\n [inGroup]=\"inGroup()\"\r\n (onAction)=\"doActions($event.data, $event.type)\"\r\n >\r\n </mt-chart-card>\r\n }\r\n }\r\n}\r\n", styles: [":host{display:block;width:100%;height:100%}\n"] }]
18565
- }], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], chartTypeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "chartTypeId", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], inGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "inGroup", required: false }] }], isDialog: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDialog", required: false }] }], queryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryParams", required: false }] }], actionTriggered: [{ type: i0.Output, args: ["actionTriggered"] }] } });
18574
+ }], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], chartTypeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "chartTypeId", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], inGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "inGroup", required: false }] }], isDialog: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDialog", required: false }] }], queryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryParams", required: false }] }], extraFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "extraFilters", required: false }] }], ignoreQueryFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "ignoreQueryFilter", required: false }] }], actionTriggered: [{ type: i0.Output, args: ["actionTriggered"] }] } });
18566
18575
 
18567
18576
  /**
18568
18577
  * Chart Viewer Component
@@ -18585,7 +18594,7 @@ class ChartViewer {
18585
18594
  }, 500);
18586
18595
  }
18587
18596
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ChartViewer, deps: [], target: i0.ɵɵFactoryTarget.Component });
18588
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ChartViewer, isStandalone: true, selector: "mt-chart-viewer", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, chartTypeId: { classPropertyName: "chartTypeId", publicName: "chartTypeId", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder'\">\r\n <div class=\"h-full flex flex-col bg-surface-50 border-l border-surface-200\">\r\n <!-- Header -->\r\n <div\r\n class=\"flex items-center justify-between px-4 py-3 bg-surface-0 border-b border-surface-200\"\r\n >\r\n <span class=\"text-sm font-medium text-surface-700\">{{\r\n t(\"preview\")\r\n }}</span>\r\n <mt-button\r\n icon=\"arrow.refresh-cw-01\"\r\n [rounded]=\"true\"\r\n size=\"small\"\r\n severity=\"help\"\r\n [tooltip]=\"t('refresh')\"\r\n (onClick)=\"refresh()\"\r\n />\r\n </div>\r\n\r\n <!-- Preview Content -->\r\n <div class=\"flex-1 p-4 overflow-auto\">\r\n @if (loading()) {\r\n <div class=\"flex items-center justify-center h-full\">\r\n <mt-icon\r\n icon=\"general.loading-02\"\r\n class=\"text-3xl text-primary animate-spin\"\r\n />\r\n </div>\r\n } @else if (config() && chartTypeId()) {\r\n <div\r\n class=\"h-full bg-surface-0 rounded-lg shadow-sm border border-surface-200 overflow-hidden\"\r\n >\r\n <mt-dashboard-item\r\n [config]=\"config()\"\r\n [chartTypeId]=\"chartTypeId()\"\r\n [readonly]=\"true\"\r\n />\r\n </div>\r\n } @else {\r\n <div\r\n class=\"flex flex-col items-center justify-center h-full text-muted-color\"\r\n >\r\n <mt-icon icon=\"chart.bar-chart-10\" class=\"text-4xl mb-3 opacity-50\" />\r\n <p class=\"text-sm\">{{ t(\"noPreviewAvailable\") }}</p>\r\n <p class=\"text-xs opacity-70\">{{ t(\"configureChartToPreview\") }}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</ng-container>\r\n", styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: DashboardItem, selector: "mt-dashboard-item", inputs: ["config", "chartTypeId", "readonly", "inGroup", "isDialog", "queryParams"], outputs: ["actionTriggered"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
18597
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: ChartViewer, isStandalone: true, selector: "mt-chart-viewer", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, chartTypeId: { classPropertyName: "chartTypeId", publicName: "chartTypeId", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder'\">\r\n <div class=\"h-full flex flex-col bg-surface-50 border-l border-surface-200\">\r\n <!-- Header -->\r\n <div\r\n class=\"flex items-center justify-between px-4 py-3 bg-surface-0 border-b border-surface-200\"\r\n >\r\n <span class=\"text-sm font-medium text-surface-700\">{{\r\n t(\"preview\")\r\n }}</span>\r\n <mt-button\r\n icon=\"arrow.refresh-cw-01\"\r\n [rounded]=\"true\"\r\n size=\"small\"\r\n severity=\"help\"\r\n [tooltip]=\"t('refresh')\"\r\n (onClick)=\"refresh()\"\r\n />\r\n </div>\r\n\r\n <!-- Preview Content -->\r\n <div class=\"flex-1 p-4 overflow-auto\">\r\n @if (loading()) {\r\n <div class=\"flex items-center justify-center h-full\">\r\n <mt-icon\r\n icon=\"general.loading-02\"\r\n class=\"text-3xl text-primary animate-spin\"\r\n />\r\n </div>\r\n } @else if (config() && chartTypeId()) {\r\n <div\r\n class=\"h-full bg-surface-0 rounded-lg shadow-sm border border-surface-200 overflow-hidden\"\r\n >\r\n <mt-dashboard-item\r\n [config]=\"config()\"\r\n [chartTypeId]=\"chartTypeId()\"\r\n [readonly]=\"true\"\r\n />\r\n </div>\r\n } @else {\r\n <div\r\n class=\"flex flex-col items-center justify-center h-full text-muted-color\"\r\n >\r\n <mt-icon icon=\"chart.bar-chart-10\" class=\"text-4xl mb-3 opacity-50\" />\r\n <p class=\"text-sm\">{{ t(\"noPreviewAvailable\") }}</p>\r\n <p class=\"text-xs opacity-70\">{{ t(\"configureChartToPreview\") }}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</ng-container>\r\n", styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: DashboardItem, selector: "mt-dashboard-item", inputs: ["config", "chartTypeId", "readonly", "inGroup", "isDialog", "queryParams", "extraFilters", "ignoreQueryFilter"], outputs: ["actionTriggered"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
18589
18598
  }
18590
18599
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: ChartViewer, decorators: [{
18591
18600
  type: Component,
@@ -20033,7 +20042,7 @@ class DashboardBuilder {
20033
20042
  this.emittingDashboardData = false;
20034
20043
  }
20035
20044
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
20036
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: DashboardBuilder, isStandalone: true, selector: "mt-dashboard-builder", inputs: { isPage: { classPropertyName: "isPage", publicName: "isPage", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, backButton: { classPropertyName: "backButton", publicName: "backButton", isSignal: true, isRequired: false, transformFunction: null }, pageId: { classPropertyName: "pageId", publicName: "pageId", isSignal: true, isRequired: false, transformFunction: null }, standalone: { classPropertyName: "standalone", publicName: "standalone", isSignal: true, isRequired: false, transformFunction: null }, dashboardData: { classPropertyName: "dashboardData", publicName: "dashboardData", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dashboardData: "dashboardDataChange", pageChange: "pageChange", chartsChange: "chartsChange", onSave: "onSave", onBack: "onBack", onAddChart: "onAddChart", onEditChart: "onEditChart" }, host: { listeners: { "window:keydown": "onKeyDown($event)" } }, viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["contextMenu"], descendants: true, isSignal: true }, { propertyName: "gridsterContainer", first: true, predicate: ["gridsterContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder'\">\r\n <!-- Shared Actions Template -->\r\n <ng-template #actionsTemplate let-inPage=\"inPage\">\r\n <mt-button\r\n [label]=\"t('addWidget')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"toggleWidgetPalette()\"\r\n severity=\"primary\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"t('save')\"\r\n icon=\"general.save-02\"\r\n [loading]=\"saving()\"\r\n [disabled]=\"saving()\"\r\n (onClick)=\"saveDash()\"\r\n severity=\"primary\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n\r\n <span [class]=\"'w-px h-5 bg-surface-300'\"></span>\r\n\r\n <mt-button\r\n [icon]=\"stopActionsOnCards() ? 'media.play' : 'media.stop'\"\r\n [tooltip]=\"stopActionsOnCards() ? t('activateActions') : t('stopActions')\"\r\n (onClick)=\"stopAndActiveActions()\"\r\n [severity]=\"'warn'\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n\r\n <mt-button\r\n icon=\"general.edit-05\"\r\n [tooltip]=\"t('pageSettings')\"\r\n (onClick)=\"addOrEditPage(pageConfig()!)\"\r\n [severity]=\"'help'\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n\r\n <mt-button\r\n icon=\"general.filter-lines\"\r\n [tooltip]=\"t('manageFilter')\"\r\n (onClick)=\"openManageFilter()\"\r\n [severity]=\"'info'\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n </ng-template>\r\n\r\n <!-- Page mode -->\r\n @if (isPage()) {\r\n <mt-page\r\n [title]=\"\r\n pageTitle() ||\r\n pageConfig()?.name?.[languageCode()] ||\r\n pageConfig()?.name?.['en'] ||\r\n t('builder')\r\n \"\r\n [backButton]=\"backButton()\"\r\n (backButtonClick)=\"onBack.emit()\"\r\n >\r\n <!-- Header actions -->\r\n <ng-template #headerEnd>\r\n <div class=\"flex items-center gap-2\">\r\n <ng-container\r\n *ngTemplateOutlet=\"actionsTemplate; context: { inPage: true }\"\r\n />\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Content -->\r\n <ng-container *ngTemplateOutlet=\"builderContent\" />\r\n </mt-page>\r\n } @else {\r\n <!-- Non-page mode -->\r\n <ng-container *ngTemplateOutlet=\"builderContent\" />\r\n }\r\n\r\n <!-- Reusable builder content template -->\r\n <ng-template #builderContent>\r\n <div class=\"relative h-full min-h-[600px]\">\r\n <!-- Loading State -->\r\n @if (loading()) {\r\n <div class=\"flex items-center justify-center h-64\">\r\n <i class=\"mti mti-spinner-third mti-spin text-4xl text-primary\"></i>\r\n </div>\r\n }\r\n\r\n <!-- Main Content -->\r\n @if (!loading() && pageConfig()) {\r\n <!-- Fixed Actions Bar (only when NOT page mode) -->\r\n @if (!isPage()) {\r\n <div\r\n class=\"fixed bottom-4 left-1/2 -translate-x-1/2 z-50 flex items-center gap-2 bg-surface-0 rounded-full shadow-lg px-4 py-2 border border-surface-200\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"actionsTemplate; context: { inPage: false }\"\r\n />\r\n </div>\r\n }\r\n\r\n <!-- Gridster Container -->\r\n <gridster\r\n #gridsterContainer\r\n [options]=\"options\"\r\n class=\"h-full min-h-[500px] bg-surface-50!\"\r\n [class.is-page]=\"isPage()\"\r\n [class.pointer-events-none]=\"readonly()\"\r\n (dragover)=\"onGridDragOver($event)\"\r\n (drop)=\"onGridDrop($event)\"\r\n >\r\n @for (chart of charts() | filterByGroup: undefined; track $index) {\r\n <gridster-item\r\n [item]=\"chart\"\r\n class=\"bg-surface-0 rounded-lg shadow-sm border border-surface-200 overflow-hidden transition-all duration-200 hover:shadow-md hover:border-surface-300\"\r\n [class.ring-2]=\"isSelected(chart)\"\r\n [class.ring-primary]=\"isSelected(chart)\"\r\n [class.ring-offset-2]=\"isSelected(chart)\"\r\n (click)=\"onSelect(chart, $event)\"\r\n >\r\n <!-- Chart Content -->\r\n <div class=\"h-full w-full relative group\">\r\n <!-- Drag Handle -->\r\n @if (!readonly()) {\r\n <div\r\n class=\"drag-handler absolute top-0 left-0 right-0 h-6 cursor-move bg-gradient-to-b from-black/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center z-10\"\r\n >\r\n <i\r\n class=\"mti mti-grip-horizontal text-white/70 text-sm\"\r\n ></i>\r\n </div>\r\n }\r\n\r\n <!-- Action Buttons -->\r\n @if (!readonly()) {\r\n <div\r\n class=\"absolute top-1 right-1 flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity z-20\"\r\n >\r\n <!-- Quick Manage Button -->\r\n @if (showQuickManageButton(chart)) {\r\n <mt-button\r\n icon=\"editor.palette\"\r\n [tooltip]=\"t('quickManage')\"\r\n (onClick)=\"openQuickManage($event, chart)\"\r\n severity=\"primary\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n }\r\n\r\n <!-- Breadcrumb Button (for topbar) -->\r\n @if (\r\n chart.config?.clientConfig?.componentName === \"topbar\"\r\n ) {\r\n <mt-button\r\n icon=\"general.slash-divider\"\r\n [tooltip]=\"t('manageBreadcrumb')\"\r\n (onClick)=\"openBreadcrumb(chart)\"\r\n severity=\"info\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n }\r\n\r\n <!-- Edit Button -->\r\n @if (\r\n chart.config?.clientConfig?.componentName !== \"header\" &&\r\n chart.config?.clientConfig?.componentName !== \"topbar\" &&\r\n chart.config?.clientConfig?.componentName !== \"Group\"\r\n ) {\r\n <mt-button\r\n icon=\"general.edit-05\"\r\n [tooltip]=\"t('edit')\"\r\n (onClick)=\"editItem(chart)\"\r\n severity=\"warn\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n }\r\n\r\n <!-- Delete Button -->\r\n @if (\r\n chart.config?.clientConfig?.componentName === \"Group\"\r\n ) {\r\n <mt-button\r\n icon=\"general.trash-01\"\r\n [tooltip]=\"t('removeGroup')\"\r\n (onClick)=\"removeGroup(chart)\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n } @else {\r\n <mt-button\r\n icon=\"general.trash-01\"\r\n [tooltip]=\"t('delete')\"\r\n (onClick)=\"deleteItem(chart)\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n }\r\n\r\n <!-- Advanced/Dialog Actions Button (pipe handles all conditions) -->\r\n @let dialogActions =\r\n chart | getChartActions: chartActionsContext;\r\n @if (dialogActions.length > 0) {\r\n <mt-button\r\n icon=\"general.dots-vertical\"\r\n [tooltip]=\"t('advanced')\"\r\n (onClick)=\"advancedPopover.toggle($event)\"\r\n severity=\"info\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n <p-popover #advancedPopover appendTo=\"body\">\r\n <div class=\"flex flex-col gap-1 min-w-[160px]\">\r\n @for (action of dialogActions; track action.label) {\r\n <mt-button\r\n [label]=\"action.label\"\r\n [icon]=\"action.icon\"\r\n severity=\"secondary\"\r\n [text]=\"true\"\r\n size=\"small\"\r\n (onClick)=\"\r\n action.command?.({\r\n originalEvent: $event,\r\n item: action,\r\n });\r\n advancedPopover.hide()\r\n \"\r\n />\r\n }\r\n </div>\r\n </p-popover>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Chart Rendering -->\r\n @if (!chart.loading) {\r\n <mt-dashboard-item\r\n [config]=\"chart.config\"\r\n [chartTypeId]=\"chart.chartTypeId\"\r\n [readonly]=\"readonly()\"\r\n />\r\n } @else {\r\n <div class=\"flex items-center justify-center h-full\">\r\n <i\r\n class=\"mti mti-spinner-third mti-spin text-2xl text-primary\"\r\n ></i>\r\n </div>\r\n }\r\n\r\n <!-- Group Children -->\r\n @if (chart.config?.clientConfig?.componentName === \"Group\") {\r\n <div class=\"h-full flex flex-col p-2\">\r\n @for (\r\n childChart of charts()\r\n | filterByGroup\r\n : chart.config?.serviceConfig?.dashboardId;\r\n track childChart.config?.serviceConfig?.dashboardId;\r\n let idx = $index\r\n ) {\r\n @if (idx === chart.selectedGroupIndex) {\r\n <div class=\"flex-1 min-h-0\">\r\n <mt-dashboard-item\r\n [config]=\"childChart.config\"\r\n [chartTypeId]=\"childChart.chartTypeId\"\r\n [readonly]=\"readonly()\"\r\n />\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Group Tabs -->\r\n <div\r\n class=\"absolute bottom-0 left-0 right-0 flex gap-1 p-1 bg-surface-100 rounded-b-lg overflow-x-auto\"\r\n >\r\n @for (\r\n childChart of charts()\r\n | filterByGroup\r\n : chart.config?.serviceConfig?.dashboardId;\r\n track childChart.config?.serviceConfig?.dashboardId;\r\n let idx = $index\r\n ) {\r\n <mt-button\r\n [label]=\"\r\n childChart.config?.clientConfig?.title?.[\r\n languageCode()\r\n ] ||\r\n childChart.config?.clientConfig?.title?.['en'] ||\r\n 'Chart ' + (idx + 1)\r\n \"\r\n [severity]=\"\r\n idx === chart.selectedGroupIndex\r\n ? 'primary'\r\n : 'secondary'\r\n \"\r\n [text]=\"idx !== chart.selectedGroupIndex\"\r\n size=\"small\"\r\n (onClick)=\"chart.selectedGroupIndex = idx\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </gridster-item>\r\n }\r\n </gridster>\r\n\r\n <!-- Context Menu -->\r\n <p-contextMenu #contextMenu [model]=\"menuItems()\" appendTo=\"body\" />\r\n }\r\n\r\n <!-- Empty State -->\r\n @if (!loading() && !pageConfig()) {\r\n <div\r\n class=\"flex flex-col items-center justify-center h-64 text-muted-color\"\r\n >\r\n <i class=\"mti mti-layout text-6xl mb-4\"></i>\r\n <p class=\"text-lg\">{{ t(\"noPageSelected\") }}</p>\r\n <p class=\"text-sm\">{{ t(\"selectPageToStart\") }}</p>\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n</ng-container>\r\n", styles: [":host{display:block;height:100%}gridster{min-height:calc(100vh - 120px);height:100%}gridster .gridster-column{border-left:1px solid var(--p-primary-100)!important;border-right:1px solid var(--p-primary-100)!important}gridster .gridster-row{border-top:1px solid var(--p-primary-100)!important;border-bottom:1px solid var(--p-primary-100)!important}gridster.is-page{min-height:calc(100vh - 200px)}.mti-spin{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.gridster-drop-indicator{position:absolute;background:var(--p-primary-100);border:2px dashed var(--p-primary-400);border-radius:8px;pointer-events:none;z-index:100}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: GridsterModule }, { kind: "component", type: i2$2.GridsterComponent, selector: "gridster", inputs: ["options"] }, { kind: "component", type: i2$2.GridsterItemComponent, selector: "gridster-item", inputs: ["item"], outputs: ["itemInit", "itemChange", "itemResize"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "component", type: Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i3$1.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: DashboardItem, selector: "mt-dashboard-item", inputs: ["config", "chartTypeId", "readonly", "inGroup", "isDialog", "queryParams"], outputs: ["actionTriggered"] }, { kind: "pipe", type: FilterByGroupPipe, name: "filterByGroup" }, { kind: "pipe", type: GetChartActionsPipe, name: "getChartActions" }], encapsulation: i0.ViewEncapsulation.None });
20045
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: DashboardBuilder, isStandalone: true, selector: "mt-dashboard-builder", inputs: { isPage: { classPropertyName: "isPage", publicName: "isPage", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, backButton: { classPropertyName: "backButton", publicName: "backButton", isSignal: true, isRequired: false, transformFunction: null }, pageId: { classPropertyName: "pageId", publicName: "pageId", isSignal: true, isRequired: false, transformFunction: null }, standalone: { classPropertyName: "standalone", publicName: "standalone", isSignal: true, isRequired: false, transformFunction: null }, dashboardData: { classPropertyName: "dashboardData", publicName: "dashboardData", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dashboardData: "dashboardDataChange", pageChange: "pageChange", chartsChange: "chartsChange", onSave: "onSave", onBack: "onBack", onAddChart: "onAddChart", onEditChart: "onEditChart" }, host: { listeners: { "window:keydown": "onKeyDown($event)" } }, viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["contextMenu"], descendants: true, isSignal: true }, { propertyName: "gridsterContainer", first: true, predicate: ["gridsterContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder'\">\r\n <!-- Shared Actions Template -->\r\n <ng-template #actionsTemplate let-inPage=\"inPage\">\r\n <mt-button\r\n [label]=\"t('addWidget')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"toggleWidgetPalette()\"\r\n severity=\"primary\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n\r\n <mt-button\r\n [label]=\"t('save')\"\r\n icon=\"general.save-02\"\r\n [loading]=\"saving()\"\r\n [disabled]=\"saving()\"\r\n (onClick)=\"saveDash()\"\r\n severity=\"primary\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n\r\n <span [class]=\"'w-px h-5 bg-surface-300'\"></span>\r\n\r\n <mt-button\r\n [icon]=\"stopActionsOnCards() ? 'media.play' : 'media.stop'\"\r\n [tooltip]=\"stopActionsOnCards() ? t('activateActions') : t('stopActions')\"\r\n (onClick)=\"stopAndActiveActions()\"\r\n [severity]=\"'warn'\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n\r\n <mt-button\r\n icon=\"general.edit-05\"\r\n [tooltip]=\"t('pageSettings')\"\r\n (onClick)=\"addOrEditPage(pageConfig()!)\"\r\n [severity]=\"'help'\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n\r\n <mt-button\r\n icon=\"general.filter-lines\"\r\n [tooltip]=\"t('manageFilter')\"\r\n (onClick)=\"openManageFilter()\"\r\n [severity]=\"'info'\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [rounded]=\"!inPage\"\r\n />\r\n </ng-template>\r\n\r\n <!-- Page mode -->\r\n @if (isPage()) {\r\n <mt-page\r\n [title]=\"\r\n pageTitle() ||\r\n pageConfig()?.name?.[languageCode()] ||\r\n pageConfig()?.name?.['en'] ||\r\n t('builder')\r\n \"\r\n [backButton]=\"backButton()\"\r\n (backButtonClick)=\"onBack.emit()\"\r\n >\r\n <!-- Header actions -->\r\n <ng-template #headerEnd>\r\n <div class=\"flex items-center gap-2\">\r\n <ng-container\r\n *ngTemplateOutlet=\"actionsTemplate; context: { inPage: true }\"\r\n />\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Content -->\r\n <ng-container *ngTemplateOutlet=\"builderContent\" />\r\n </mt-page>\r\n } @else {\r\n <!-- Non-page mode -->\r\n <ng-container *ngTemplateOutlet=\"builderContent\" />\r\n }\r\n\r\n <!-- Reusable builder content template -->\r\n <ng-template #builderContent>\r\n <div class=\"relative h-full min-h-[600px]\">\r\n <!-- Loading State -->\r\n @if (loading()) {\r\n <div class=\"flex items-center justify-center h-64\">\r\n <i class=\"mti mti-spinner-third mti-spin text-4xl text-primary\"></i>\r\n </div>\r\n }\r\n\r\n <!-- Main Content -->\r\n @if (!loading() && pageConfig()) {\r\n <!-- Fixed Actions Bar (only when NOT page mode) -->\r\n @if (!isPage()) {\r\n <div\r\n class=\"fixed bottom-4 left-1/2 -translate-x-1/2 z-50 flex items-center gap-2 bg-surface-0 rounded-full shadow-lg px-4 py-2 border border-surface-200\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"actionsTemplate; context: { inPage: false }\"\r\n />\r\n </div>\r\n }\r\n\r\n <!-- Gridster Container -->\r\n <gridster\r\n #gridsterContainer\r\n [options]=\"options\"\r\n class=\"h-full min-h-[500px] bg-surface-50!\"\r\n [class.is-page]=\"isPage()\"\r\n [class.pointer-events-none]=\"readonly()\"\r\n (dragover)=\"onGridDragOver($event)\"\r\n (drop)=\"onGridDrop($event)\"\r\n >\r\n @for (chart of charts() | filterByGroup: undefined; track $index) {\r\n <gridster-item\r\n [item]=\"chart\"\r\n class=\"bg-surface-0 rounded-lg shadow-sm border border-surface-200 overflow-hidden transition-all duration-200 hover:shadow-md hover:border-surface-300\"\r\n [class.ring-2]=\"isSelected(chart)\"\r\n [class.ring-primary]=\"isSelected(chart)\"\r\n [class.ring-offset-2]=\"isSelected(chart)\"\r\n (click)=\"onSelect(chart, $event)\"\r\n >\r\n <!-- Chart Content -->\r\n <div class=\"h-full w-full relative group\">\r\n <!-- Drag Handle -->\r\n @if (!readonly()) {\r\n <div\r\n class=\"drag-handler absolute top-0 left-0 right-0 h-6 cursor-move bg-gradient-to-b from-black/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center z-10\"\r\n >\r\n <i\r\n class=\"mti mti-grip-horizontal text-white/70 text-sm\"\r\n ></i>\r\n </div>\r\n }\r\n\r\n <!-- Action Buttons -->\r\n @if (!readonly()) {\r\n <div\r\n class=\"absolute top-1 right-1 flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity z-20\"\r\n >\r\n <!-- Quick Manage Button -->\r\n @if (showQuickManageButton(chart)) {\r\n <mt-button\r\n icon=\"editor.palette\"\r\n [tooltip]=\"t('quickManage')\"\r\n (onClick)=\"openQuickManage($event, chart)\"\r\n severity=\"primary\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n }\r\n\r\n <!-- Breadcrumb Button (for topbar) -->\r\n @if (\r\n chart.config?.clientConfig?.componentName === \"topbar\"\r\n ) {\r\n <mt-button\r\n icon=\"general.slash-divider\"\r\n [tooltip]=\"t('manageBreadcrumb')\"\r\n (onClick)=\"openBreadcrumb(chart)\"\r\n severity=\"info\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n }\r\n\r\n <!-- Edit Button -->\r\n @if (\r\n chart.config?.clientConfig?.componentName !== \"header\" &&\r\n chart.config?.clientConfig?.componentName !== \"topbar\" &&\r\n chart.config?.clientConfig?.componentName !== \"Group\"\r\n ) {\r\n <mt-button\r\n icon=\"general.edit-05\"\r\n [tooltip]=\"t('edit')\"\r\n (onClick)=\"editItem(chart)\"\r\n severity=\"warn\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n }\r\n\r\n <!-- Delete Button -->\r\n @if (\r\n chart.config?.clientConfig?.componentName === \"Group\"\r\n ) {\r\n <mt-button\r\n icon=\"general.trash-01\"\r\n [tooltip]=\"t('removeGroup')\"\r\n (onClick)=\"removeGroup(chart)\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n } @else {\r\n <mt-button\r\n icon=\"general.trash-01\"\r\n [tooltip]=\"t('delete')\"\r\n (onClick)=\"deleteItem(chart)\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n }\r\n\r\n <!-- Advanced/Dialog Actions Button (pipe handles all conditions) -->\r\n @let dialogActions =\r\n chart | getChartActions: chartActionsContext;\r\n @if (dialogActions.length > 0) {\r\n <mt-button\r\n icon=\"general.dots-vertical\"\r\n [tooltip]=\"t('advanced')\"\r\n (onClick)=\"advancedPopover.toggle($event)\"\r\n severity=\"info\"\r\n size=\"small\"\r\n [rounded]=\"true\"\r\n />\r\n <p-popover #advancedPopover appendTo=\"body\">\r\n <div class=\"flex flex-col gap-1 min-w-[160px]\">\r\n @for (action of dialogActions; track action.label) {\r\n <mt-button\r\n [label]=\"action.label\"\r\n [icon]=\"action.icon\"\r\n severity=\"secondary\"\r\n [text]=\"true\"\r\n size=\"small\"\r\n (onClick)=\"\r\n action.command?.({\r\n originalEvent: $event,\r\n item: action,\r\n });\r\n advancedPopover.hide()\r\n \"\r\n />\r\n }\r\n </div>\r\n </p-popover>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Chart Rendering -->\r\n @if (!chart.loading) {\r\n <mt-dashboard-item\r\n [config]=\"chart.config\"\r\n [chartTypeId]=\"chart.chartTypeId\"\r\n [readonly]=\"readonly()\"\r\n />\r\n } @else {\r\n <div class=\"flex items-center justify-center h-full\">\r\n <i\r\n class=\"mti mti-spinner-third mti-spin text-2xl text-primary\"\r\n ></i>\r\n </div>\r\n }\r\n\r\n <!-- Group Children -->\r\n @if (chart.config?.clientConfig?.componentName === \"Group\") {\r\n <div class=\"h-full flex flex-col p-2\">\r\n @for (\r\n childChart of charts()\r\n | filterByGroup\r\n : chart.config?.serviceConfig?.dashboardId;\r\n track childChart.config?.serviceConfig?.dashboardId;\r\n let idx = $index\r\n ) {\r\n @if (idx === chart.selectedGroupIndex) {\r\n <div class=\"flex-1 min-h-0\">\r\n <mt-dashboard-item\r\n [config]=\"childChart.config\"\r\n [chartTypeId]=\"childChart.chartTypeId\"\r\n [readonly]=\"readonly()\"\r\n />\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Group Tabs -->\r\n <div\r\n class=\"absolute bottom-0 left-0 right-0 flex gap-1 p-1 bg-surface-100 rounded-b-lg overflow-x-auto\"\r\n >\r\n @for (\r\n childChart of charts()\r\n | filterByGroup\r\n : chart.config?.serviceConfig?.dashboardId;\r\n track childChart.config?.serviceConfig?.dashboardId;\r\n let idx = $index\r\n ) {\r\n <mt-button\r\n [label]=\"\r\n childChart.config?.clientConfig?.title?.[\r\n languageCode()\r\n ] ||\r\n childChart.config?.clientConfig?.title?.['en'] ||\r\n 'Chart ' + (idx + 1)\r\n \"\r\n [severity]=\"\r\n idx === chart.selectedGroupIndex\r\n ? 'primary'\r\n : 'secondary'\r\n \"\r\n [text]=\"idx !== chart.selectedGroupIndex\"\r\n size=\"small\"\r\n (onClick)=\"chart.selectedGroupIndex = idx\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </gridster-item>\r\n }\r\n </gridster>\r\n\r\n <!-- Context Menu -->\r\n <p-contextMenu #contextMenu [model]=\"menuItems()\" appendTo=\"body\" />\r\n }\r\n\r\n <!-- Empty State -->\r\n @if (!loading() && !pageConfig()) {\r\n <div\r\n class=\"flex flex-col items-center justify-center h-64 text-muted-color\"\r\n >\r\n <i class=\"mti mti-layout text-6xl mb-4\"></i>\r\n <p class=\"text-lg\">{{ t(\"noPageSelected\") }}</p>\r\n <p class=\"text-sm\">{{ t(\"selectPageToStart\") }}</p>\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n</ng-container>\r\n", styles: [":host{display:block;height:100%}gridster{min-height:calc(100vh - 120px);height:100%}gridster .gridster-column{border-left:1px solid var(--p-primary-100)!important;border-right:1px solid var(--p-primary-100)!important}gridster .gridster-row{border-top:1px solid var(--p-primary-100)!important;border-bottom:1px solid var(--p-primary-100)!important}gridster.is-page{min-height:calc(100vh - 200px)}.mti-spin{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.gridster-drop-indicator{position:absolute;background:var(--p-primary-100);border:2px dashed var(--p-primary-400);border-radius:8px;pointer-events:none;z-index:100}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: GridsterModule }, { kind: "component", type: i2$2.GridsterComponent, selector: "gridster", inputs: ["options"] }, { kind: "component", type: i2$2.GridsterItemComponent, selector: "gridster-item", inputs: ["item"], outputs: ["itemInit", "itemChange", "itemResize"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "component", type: Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i3$1.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: DashboardItem, selector: "mt-dashboard-item", inputs: ["config", "chartTypeId", "readonly", "inGroup", "isDialog", "queryParams", "extraFilters", "ignoreQueryFilter"], outputs: ["actionTriggered"] }, { kind: "pipe", type: FilterByGroupPipe, name: "filterByGroup" }, { kind: "pipe", type: GetChartActionsPipe, name: "getChartActions" }], encapsulation: i0.ViewEncapsulation.None });
20037
20046
  }
20038
20047
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardBuilder, decorators: [{
20039
20048
  type: Component,
@@ -20076,6 +20085,8 @@ class DashboardViewer {
20076
20085
  cdr = inject(ChangeDetectorRef);
20077
20086
  dashboardService = inject(DashboardBuilderService);
20078
20087
  storeService = inject(DashboardStoreService);
20088
+ itemStoreService = inject(DashboardItemStoreService);
20089
+ route = inject(ActivatedRoute, { optional: true });
20079
20090
  // ============================================
20080
20091
  // Inputs
20081
20092
  // ============================================
@@ -20095,6 +20106,8 @@ class DashboardViewer {
20095
20106
  dialogsData = input([], ...(ngDevMode ? [{ debugName: "dialogsData" }] : []));
20096
20107
  /** Pre-loaded filters (optional) */
20097
20108
  filtersData = input([], ...(ngDevMode ? [{ debugName: "filtersData" }] : []));
20109
+ /** Dashboard-wide extra filters applied to every item request */
20110
+ extraFilters = input({}, ...(ngDevMode ? [{ debugName: "extraFilters" }] : []));
20098
20111
  /** Show filters sidebar */
20099
20112
  showFilters = input(true, ...(ngDevMode ? [{ debugName: "showFilters" }] : []));
20100
20113
  // ============================================
@@ -20150,6 +20163,7 @@ class DashboardViewer {
20150
20163
  // ============================================
20151
20164
  subscription = new Subscription();
20152
20165
  rowHeight = 60;
20166
+ lastExtraFiltersSignature = null;
20153
20167
  hasSplitPreloadedData = computed(() => {
20154
20168
  return (this.chartsData().length > 0 ||
20155
20169
  this.dialogsData().length > 0 ||
@@ -20181,6 +20195,17 @@ class DashboardViewer {
20181
20195
  this.filters.set(data ?? []);
20182
20196
  this.dashboardService.filters.set(this.filters());
20183
20197
  }, { ...(ngDevMode ? { debugName: "filtersDataEffect" } : {}), allowSignalWrites: true });
20198
+ extraFiltersEffect = effect(() => {
20199
+ const signature = JSON.stringify(this.extraFilters() ?? {});
20200
+ if (this.lastExtraFiltersSignature === null) {
20201
+ this.lastExtraFiltersSignature = signature;
20202
+ return;
20203
+ }
20204
+ if (this.lastExtraFiltersSignature !== signature) {
20205
+ this.lastExtraFiltersSignature = signature;
20206
+ this.refreshAllItems();
20207
+ }
20208
+ }, ...(ngDevMode ? [{ debugName: "extraFiltersEffect" }] : []));
20184
20209
  // Preloaded split inputs do not include page metadata, so clear stale page state.
20185
20210
  preloadedInputsEffect = effect(() => {
20186
20211
  if (this.dashboardData() || !this.hasSplitPreloadedData())
@@ -20208,8 +20233,15 @@ class DashboardViewer {
20208
20233
  }
20209
20234
  ngOnDestroy() {
20210
20235
  this.subscription.unsubscribe();
20236
+ this.storeService.updateQueryParams({});
20211
20237
  }
20212
20238
  setupSubscriptions() {
20239
+ if (this.route) {
20240
+ this.subscription.add(this.route.queryParams.subscribe((params) => {
20241
+ this.storeService.updateQueryParams(params ?? {});
20242
+ this.refreshAllItems();
20243
+ }));
20244
+ }
20213
20245
  // Subscribe to reload events from store
20214
20246
  this.subscription.add(this.storeService.reloadCurrentPage$.subscribe(() => {
20215
20247
  this.reloadPage();
@@ -20413,8 +20445,16 @@ class DashboardViewer {
20413
20445
  trackByDashboardId(_index, item) {
20414
20446
  return item.config?.serviceConfig?.dashboardId || String(_index);
20415
20447
  }
20448
+ refreshAllItems() {
20449
+ this.charts().forEach((chart) => {
20450
+ const dashboardId = chart.config?.serviceConfig?.dashboardId;
20451
+ if (dashboardId) {
20452
+ this.itemStoreService.triggerDataFetch(dashboardId);
20453
+ }
20454
+ });
20455
+ }
20416
20456
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardViewer, deps: [], target: i0.ɵɵFactoryTarget.Component });
20417
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: DashboardViewer, isStandalone: true, selector: "mt-dashboard-viewer", inputs: { isPage: { classPropertyName: "isPage", publicName: "isPage", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, backButton: { classPropertyName: "backButton", publicName: "backButton", isSignal: true, isRequired: false, transformFunction: null }, pageId: { classPropertyName: "pageId", publicName: "pageId", isSignal: true, isRequired: false, transformFunction: null }, dashboardData: { classPropertyName: "dashboardData", publicName: "dashboardData", isSignal: true, isRequired: false, transformFunction: null }, chartsData: { classPropertyName: "chartsData", publicName: "chartsData", isSignal: true, isRequired: false, transformFunction: null }, dialogsData: { classPropertyName: "dialogsData", publicName: "dialogsData", isSignal: true, isRequired: false, transformFunction: null }, filtersData: { classPropertyName: "filtersData", publicName: "filtersData", isSignal: true, isRequired: false, transformFunction: null }, showFilters: { classPropertyName: "showFilters", publicName: "showFilters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageLoaded: "pageLoaded", onBack: "onBack", chartClick: "chartClick" }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder'\">\r\n <!-- Page mode -->\r\n @if (isPage()) {\r\n <mt-page\r\n [title]=\"\r\n pageTitle() ||\r\n pageConfig()?.name?.[languageCode()] ||\r\n pageConfig()?.name?.['en'] ||\r\n t('dashboard')\r\n \"\r\n [backButton]=\"backButton()\"\r\n (backButtonClick)=\"onBack.emit()\"\r\n >\r\n <ng-container *ngTemplateOutlet=\"viewerContent\" />\r\n </mt-page>\r\n } @else {\r\n <!-- Non-page mode -->\r\n <ng-container *ngTemplateOutlet=\"viewerContent\" />\r\n }\r\n\r\n <!-- Reusable viewer content template -->\r\n <ng-template #viewerContent>\r\n <div class=\"relative h-full min-h-[600px] mt-dashboard-viewer\">\r\n @if (!loading() && hasRenderableContent()) {\r\n <div class=\"dashboard-viewer-layout-wrapper\">\r\n <div\r\n class=\"dashboard-viewer-layout\"\r\n [style.min-height]=\"layoutHeight()\"\r\n >\r\n @for (\r\n item of visibleItems();\r\n track trackByDashboardId($index, item)\r\n ) {\r\n <div\r\n class=\"dashboard-viewer-item\"\r\n [class.dashboard-viewer-item--group]=\"isGroup(item)\"\r\n [class.dashboard-viewer-item--header]=\"isHeader(item)\"\r\n [class.dashboard-viewer-item--topbar]=\"isTopbar(item)\"\r\n [style.grid-column]=\"getGridColumn(item)\"\r\n [style.grid-row]=\"getGridRow(item)\"\r\n >\r\n <!-- Group Container -->\r\n @if (isGroup(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"group-container h-full flex flex-col rounded-lg border border-surface-200 overflow-hidden\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-card)'\r\n \"\r\n >\r\n <!-- Group Header with Tabs -->\r\n <div\r\n class=\"group-header flex items-center justify-between px-4 py-2 bg-surface-50 border-b border-surface-200\"\r\n >\r\n <span class=\"font-medium text-sm\">{{\r\n getGroupTitle(item)\r\n }}</span>\r\n <div class=\"group-tabs flex items-center gap-1\">\r\n @for (\r\n child of getGroupChildren(item);\r\n track child.config?.serviceConfig?.dashboardId;\r\n let gIndex = $index\r\n ) {\r\n @if (getItemIcon(child); as icon) {\r\n <button\r\n type=\"button\"\r\n class=\"w-8 h-8 flex items-center justify-center rounded transition-colors\"\r\n [class.bg-primary-100]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.text-primary-600]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.text-muted-color]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n [class.hover:bg-surface-100]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n (click)=\"selectGroupChild(item, gIndex)\"\r\n >\r\n <i class=\"mti mti-{{ icon }}\"></i>\r\n </button>\r\n } @else {\r\n <button\r\n pButton\r\n type=\"button\"\r\n class=\"p-button-text p-button-sm\"\r\n [class.p-button-primary]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.p-button-secondary]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n [label]=\"getItemTitle(child)\"\r\n (click)=\"selectGroupChild(item, gIndex)\"\r\n ></button>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Group Body - Selected Child Content -->\r\n <div class=\"group-body flex-1 min-h-0 p-2\">\r\n @if (\r\n item.selectedGroupIndex !== null &&\r\n item.selectedGroupIndex !== undefined\r\n ) {\r\n @for (\r\n child of getGroupChildren(item);\r\n track child.config?.serviceConfig?.dashboardId;\r\n let cIndex = $index\r\n ) {\r\n @if (\r\n cIndex === item.selectedGroupIndex &&\r\n !child.loading\r\n ) {\r\n <div class=\"w-full h-full\">\r\n <mt-dashboard-item\r\n [config]=\"child.config\"\r\n [chartTypeId]=\"\r\n child.config?.serviceConfig?.dashboardId ||\r\n child.id\r\n \"\r\n [inGroup]=\"true\"\r\n >\r\n </mt-dashboard-item>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n <!-- Header Item -->\r\n @else if (isHeader(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"header-item flex items-center h-full px-4 border-b-2 border-primary\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'transparent'\r\n \"\r\n [style.color]=\"getStyleConfig(item)['color'] || 'inherit'\"\r\n >\r\n <h3 class=\"font-semibold text-lg m-0\">\r\n {{ getItemTitle(item) }}\r\n </h3>\r\n </div>\r\n }\r\n }\r\n <!-- Topbar Item -->\r\n @else if (isTopbar(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"topbar-item flex items-center h-full px-4 bg-surface-50\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-50)'\r\n \"\r\n >\r\n <nav class=\"flex items-center gap-2 text-sm\">\r\n @for (\r\n crumb of getBreadcrumb(item);\r\n track $index;\r\n let last = $last\r\n ) {\r\n <span\r\n class=\"text-muted-color hover:text-primary cursor-pointer transition-colors\"\r\n >\r\n {{\r\n crumb.label?.[languageCode()] ||\r\n crumb.label?.[\"en\"] ||\r\n crumb.label\r\n }}\r\n </span>\r\n @if (!last) {\r\n <i\r\n class=\"mti mti-chevron-right text-xs text-muted-color\"\r\n ></i>\r\n }\r\n }\r\n @if (getBreadcrumb(item).length === 0) {\r\n <span class=\"text-muted-color italic\">{{\r\n t(\"noBreadcrumb\")\r\n }}</span>\r\n }\r\n </nav>\r\n </div>\r\n }\r\n }\r\n <!-- Regular Chart Item -->\r\n @else {\r\n @if (!item.loading) {\r\n <div\r\n class=\"chart-item h-full flex flex-col rounded-lg border border-surface-200 overflow-hidden\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-card)'\r\n \"\r\n >\r\n <mt-dashboard-item\r\n [config]=\"item.config\"\r\n [chartTypeId]=\"\r\n item.config?.serviceConfig?.dashboardId || item.id\r\n \"\r\n [inGroup]=\"false\"\r\n >\r\n </mt-dashboard-item>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden Dialogs (for modal rendering) -->\r\n @if (dialogs().length > 0) {\r\n <div class=\"hidden\">\r\n @for (\r\n dialog of dialogs();\r\n track dialog.config?.serviceConfig?.dashboardId\r\n ) {\r\n <!-- Dialog placeholder - will be rendered in modal when triggered -->\r\n <div\r\n [attr.data-dialog-id]=\"\r\n dialog.config?.serviceConfig?.dashboardId\r\n \"\r\n ></div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Filters Sidebar Placeholder -->\r\n @if (showFilters() && filters().length > 0) {\r\n <div\r\n class=\"filters-sidebar fixed end-0 top-0 bottom-0 w-64 bg-surface-card border-s border-surface-200 p-4 shadow-lg z-50 hidden\"\r\n >\r\n <!-- Filter sidebar content - integrate with actual filter component -->\r\n <h4 class=\"font-semibold mb-4\">{{ t(\"filters\") }}</h4>\r\n @for (filter of filters(); track $index) {\r\n <div class=\"filter-item mb-3 p-2 bg-surface-50 rounded\">\r\n <span class=\"text-sm\">{{\r\n filter.label || filter.propertyKey\r\n }}</span>\r\n </div>\r\n }\r\n </div>\r\n }\r\n } @else if (loading()) {\r\n <!-- Loading State -->\r\n <div class=\"flex items-center justify-center h-64\">\r\n <div class=\"text-center\">\r\n <i\r\n class=\"mti mti-loader-2 text-4xl text-primary animate-spin mb-2\"\r\n ></i>\r\n <p class=\"text-muted-color\">{{ t(\"loading\") }}</p>\r\n </div>\r\n </div>\r\n } @else {\r\n <div\r\n class=\"flex flex-col items-center justify-center h-64 text-muted-color\"\r\n >\r\n <i class=\"mti mti-layout text-6xl mb-4\"></i>\r\n <p class=\"text-lg\">{{ t(\"noPageSelected\") }}</p>\r\n <p class=\"text-sm\">{{ t(\"selectPageToStart\") }}</p>\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n</ng-container>\r\n", styles: [".mt-dashboard-viewer{width:100%;height:100%}.dashboard-viewer-layout-wrapper{width:100%}.dashboard-viewer-layout{display:grid;grid-template-columns:repeat(36,minmax(0,1fr));grid-auto-rows:60px;gap:10px;width:100%;align-items:stretch}.dashboard-viewer-item{min-width:0;min-height:0}.dashboard-viewer-item>*{width:100%;height:100%}.dashboard-viewer-item--header{z-index:1}.dashboard-viewer-item--topbar{z-index:2}.group-container{background-color:var(--surface-card)}.group-header{flex-shrink:0}.group-tabs{flex-wrap:wrap}.group-body{background-color:var(--surface-0)}.chart-item{background-color:var(--surface-card)}.header-item{background-color:transparent}.topbar-item{background-color:var(--surface-50)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.filters-sidebar{transform:translate(100%);transition:transform .3s ease-in-out}.filters-sidebar.open{transform:translate(0)}[dir=rtl] .filters-sidebar{transform:translate(-100%)}[dir=rtl] .filters-sidebar.open{transform:translate(0)}.table-responsive{max-height:75vh;overflow-y:auto;overflow-x:auto}.modal-content{border:0!important}.custom-donut-chart{display:flex;justify-content:center;height:auto!important}.custom-donut-chart canvas{margin-top:-1.5rem!important}@media(max-width:768px){.dashboard-viewer-layout{display:flex;flex-direction:column;min-height:0!important}.dashboard-viewer-item{min-height:220px}.dashboard-viewer-item--header,.dashboard-viewer-item--topbar{min-height:72px}.dashboard-viewer-item--group{min-height:280px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$3.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: DashboardItem, selector: "mt-dashboard-item", inputs: ["config", "chartTypeId", "readonly", "inGroup", "isDialog", "queryParams"], outputs: ["actionTriggered"] }, { kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
20457
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: DashboardViewer, isStandalone: true, selector: "mt-dashboard-viewer", inputs: { isPage: { classPropertyName: "isPage", publicName: "isPage", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, backButton: { classPropertyName: "backButton", publicName: "backButton", isSignal: true, isRequired: false, transformFunction: null }, pageId: { classPropertyName: "pageId", publicName: "pageId", isSignal: true, isRequired: false, transformFunction: null }, dashboardData: { classPropertyName: "dashboardData", publicName: "dashboardData", isSignal: true, isRequired: false, transformFunction: null }, chartsData: { classPropertyName: "chartsData", publicName: "chartsData", isSignal: true, isRequired: false, transformFunction: null }, dialogsData: { classPropertyName: "dialogsData", publicName: "dialogsData", isSignal: true, isRequired: false, transformFunction: null }, filtersData: { classPropertyName: "filtersData", publicName: "filtersData", isSignal: true, isRequired: false, transformFunction: null }, extraFilters: { classPropertyName: "extraFilters", publicName: "extraFilters", isSignal: true, isRequired: false, transformFunction: null }, showFilters: { classPropertyName: "showFilters", publicName: "showFilters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageLoaded: "pageLoaded", onBack: "onBack", chartClick: "chartClick" }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder'\">\r\n <!-- Page mode -->\r\n @if (isPage()) {\r\n <mt-page\r\n [title]=\"\r\n pageTitle() ||\r\n pageConfig()?.name?.[languageCode()] ||\r\n pageConfig()?.name?.['en'] ||\r\n t('dashboard')\r\n \"\r\n [backButton]=\"backButton()\"\r\n (backButtonClick)=\"onBack.emit()\"\r\n >\r\n <ng-container *ngTemplateOutlet=\"viewerContent\" />\r\n </mt-page>\r\n } @else {\r\n <!-- Non-page mode -->\r\n <ng-container *ngTemplateOutlet=\"viewerContent\" />\r\n }\r\n\r\n <!-- Reusable viewer content template -->\r\n <ng-template #viewerContent>\r\n <div class=\"relative h-full min-h-[600px] mt-dashboard-viewer\">\r\n @if (!loading() && hasRenderableContent()) {\r\n <div class=\"dashboard-viewer-layout-wrapper\">\r\n <div\r\n class=\"dashboard-viewer-layout\"\r\n [style.min-height]=\"layoutHeight()\"\r\n >\r\n @for (\r\n item of visibleItems();\r\n track trackByDashboardId($index, item)\r\n ) {\r\n <div\r\n class=\"dashboard-viewer-item\"\r\n [class.dashboard-viewer-item--group]=\"isGroup(item)\"\r\n [class.dashboard-viewer-item--header]=\"isHeader(item)\"\r\n [class.dashboard-viewer-item--topbar]=\"isTopbar(item)\"\r\n [style.grid-column]=\"getGridColumn(item)\"\r\n [style.grid-row]=\"getGridRow(item)\"\r\n >\r\n <!-- Group Container -->\r\n @if (isGroup(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"group-container h-full flex flex-col rounded-lg border border-surface-200 overflow-hidden\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-card)'\r\n \"\r\n >\r\n <!-- Group Header with Tabs -->\r\n <div\r\n class=\"group-header flex items-center justify-between px-4 py-2 bg-surface-50 border-b border-surface-200\"\r\n >\r\n <span class=\"font-medium text-sm\">{{\r\n getGroupTitle(item)\r\n }}</span>\r\n <div class=\"group-tabs flex items-center gap-1\">\r\n @for (\r\n child of getGroupChildren(item);\r\n track child.config?.serviceConfig?.dashboardId;\r\n let gIndex = $index\r\n ) {\r\n @if (getItemIcon(child); as icon) {\r\n <button\r\n type=\"button\"\r\n class=\"w-8 h-8 flex items-center justify-center rounded transition-colors\"\r\n [class.bg-primary-100]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.text-primary-600]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.text-muted-color]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n [class.hover:bg-surface-100]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n (click)=\"selectGroupChild(item, gIndex)\"\r\n >\r\n <i class=\"mti mti-{{ icon }}\"></i>\r\n </button>\r\n } @else {\r\n <button\r\n pButton\r\n type=\"button\"\r\n class=\"p-button-text p-button-sm\"\r\n [class.p-button-primary]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.p-button-secondary]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n [label]=\"getItemTitle(child)\"\r\n (click)=\"selectGroupChild(item, gIndex)\"\r\n ></button>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Group Body - Selected Child Content -->\r\n <div class=\"group-body flex-1 min-h-0\">\r\n @if (\r\n item.selectedGroupIndex !== null &&\r\n item.selectedGroupIndex !== undefined\r\n ) {\r\n @for (\r\n child of getGroupChildren(item);\r\n track child.config?.serviceConfig?.dashboardId;\r\n let cIndex = $index\r\n ) {\r\n @if (\r\n cIndex === item.selectedGroupIndex &&\r\n !child.loading\r\n ) {\r\n <mt-dashboard-item\r\n class=\"dashboard-viewer-content-item\"\r\n [config]=\"child.config\"\r\n [chartTypeId]=\"\r\n child.config?.serviceConfig?.dashboardId ||\r\n child.id\r\n \"\r\n [extraFilters]=\"extraFilters()\"\r\n [ignoreQueryFilter]=\"\r\n pageConfig()?.dashboardConfig\r\n ?.ignoreQueryFilter ?? false\r\n \"\r\n [inGroup]=\"true\"\r\n >\r\n </mt-dashboard-item>\r\n }\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n <!-- Header Item -->\r\n @else if (isHeader(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"header-item flex items-center h-full px-4 border-b-2 border-primary\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'transparent'\r\n \"\r\n [style.color]=\"getStyleConfig(item)['color'] || 'inherit'\"\r\n >\r\n <h3 class=\"font-semibold text-lg m-0\">\r\n {{ getItemTitle(item) }}\r\n </h3>\r\n </div>\r\n }\r\n }\r\n <!-- Topbar Item -->\r\n @else if (isTopbar(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"topbar-item flex items-center h-full px-4 bg-surface-50\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-50)'\r\n \"\r\n >\r\n <nav class=\"flex items-center gap-2 text-sm\">\r\n @for (\r\n crumb of getBreadcrumb(item);\r\n track $index;\r\n let last = $last\r\n ) {\r\n <span\r\n class=\"text-muted-color hover:text-primary cursor-pointer transition-colors\"\r\n >\r\n {{\r\n crumb.label?.[languageCode()] ||\r\n crumb.label?.[\"en\"] ||\r\n crumb.label\r\n }}\r\n </span>\r\n @if (!last) {\r\n <i\r\n class=\"mti mti-chevron-right text-xs text-muted-color\"\r\n ></i>\r\n }\r\n }\r\n @if (getBreadcrumb(item).length === 0) {\r\n <span class=\"text-muted-color italic\">{{\r\n t(\"noBreadcrumb\")\r\n }}</span>\r\n }\r\n </nav>\r\n </div>\r\n }\r\n }\r\n <!-- Regular Chart Item -->\r\n @else {\r\n @if (!item.loading) {\r\n <mt-dashboard-item\r\n class=\"dashboard-viewer-content-item\"\r\n [config]=\"item.config\"\r\n [chartTypeId]=\"\r\n item.config?.serviceConfig?.dashboardId || item.id\r\n \"\r\n [extraFilters]=\"extraFilters()\"\r\n [ignoreQueryFilter]=\"\r\n pageConfig()?.dashboardConfig?.ignoreQueryFilter ??\r\n false\r\n \"\r\n [inGroup]=\"false\"\r\n >\r\n </mt-dashboard-item>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden Dialogs (for modal rendering) -->\r\n @if (dialogs().length > 0) {\r\n <div class=\"hidden\">\r\n @for (\r\n dialog of dialogs();\r\n track dialog.config?.serviceConfig?.dashboardId\r\n ) {\r\n <!-- Dialog placeholder - will be rendered in modal when triggered -->\r\n <div\r\n [attr.data-dialog-id]=\"\r\n dialog.config?.serviceConfig?.dashboardId\r\n \"\r\n ></div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Filters Sidebar Placeholder -->\r\n @if (showFilters() && filters().length > 0) {\r\n <div\r\n class=\"filters-sidebar fixed end-0 top-0 bottom-0 w-64 bg-surface-card border-s border-surface-200 p-4 shadow-lg z-50 hidden\"\r\n >\r\n <!-- Filter sidebar content - integrate with actual filter component -->\r\n <h4 class=\"font-semibold mb-4\">{{ t(\"filters\") }}</h4>\r\n @for (filter of filters(); track $index) {\r\n <div class=\"filter-item mb-3 p-2 bg-surface-50 rounded\">\r\n <span class=\"text-sm\">{{\r\n filter.label || filter.propertyKey\r\n }}</span>\r\n </div>\r\n }\r\n </div>\r\n }\r\n } @else if (loading()) {\r\n <!-- Loading State -->\r\n <div class=\"flex items-center justify-center h-64\">\r\n <div class=\"text-center\">\r\n <i\r\n class=\"mti mti-loader-2 text-4xl text-primary animate-spin mb-2\"\r\n ></i>\r\n <p class=\"text-muted-color\">{{ t(\"loading\") }}</p>\r\n </div>\r\n </div>\r\n } @else {\r\n <div\r\n class=\"flex flex-col items-center justify-center h-64 text-muted-color\"\r\n >\r\n <i class=\"mti mti-layout text-6xl mb-4\"></i>\r\n <p class=\"text-lg\">{{ t(\"noPageSelected\") }}</p>\r\n <p class=\"text-sm\">{{ t(\"selectPageToStart\") }}</p>\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n</ng-container>\r\n", styles: [".mt-dashboard-viewer{width:100%;height:100%}.dashboard-viewer-layout-wrapper{width:100%}.dashboard-viewer-layout{display:grid;grid-template-columns:repeat(36,minmax(0,1fr));grid-auto-rows:60px;gap:10px;width:100%;align-items:stretch}.dashboard-viewer-item{display:flex;min-width:0;min-height:0}.dashboard-viewer-item>*{width:100%;height:100%}.dashboard-viewer-content-item{display:block;flex:1 1 auto;min-width:0;min-height:0;width:100%;height:100%}.dashboard-viewer-item--header{z-index:1}.dashboard-viewer-item--topbar{z-index:2}.group-container{background-color:var(--surface-card)}.group-header{flex-shrink:0}.group-tabs{flex-wrap:wrap}.group-body{background-color:var(--surface-0)}.header-item{background-color:transparent}.topbar-item{background-color:var(--surface-50)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.filters-sidebar{transform:translate(100%);transition:transform .3s ease-in-out}.filters-sidebar.open{transform:translate(0)}[dir=rtl] .filters-sidebar{transform:translate(-100%)}[dir=rtl] .filters-sidebar.open{transform:translate(0)}.table-responsive{max-height:75vh;overflow-y:auto;overflow-x:auto}.modal-content{border:0!important}.custom-donut-chart{display:flex;justify-content:center;height:auto!important}.custom-donut-chart canvas{margin-top:-1.5rem!important}@media(max-width:768px){.dashboard-viewer-layout{display:flex;flex-direction:column;min-height:0!important}.dashboard-viewer-item{min-height:220px}.dashboard-viewer-item--header,.dashboard-viewer-item--topbar{min-height:72px}.dashboard-viewer-item--group{min-height:280px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$3.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: DashboardItem, selector: "mt-dashboard-item", inputs: ["config", "chartTypeId", "readonly", "inGroup", "isDialog", "queryParams", "extraFilters", "ignoreQueryFilter"], outputs: ["actionTriggered"] }, { kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
20418
20458
  }
20419
20459
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardViewer, decorators: [{
20420
20460
  type: Component,
@@ -20424,8 +20464,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
20424
20464
  ButtonModule,
20425
20465
  DashboardItem,
20426
20466
  Page,
20427
- ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder'\">\r\n <!-- Page mode -->\r\n @if (isPage()) {\r\n <mt-page\r\n [title]=\"\r\n pageTitle() ||\r\n pageConfig()?.name?.[languageCode()] ||\r\n pageConfig()?.name?.['en'] ||\r\n t('dashboard')\r\n \"\r\n [backButton]=\"backButton()\"\r\n (backButtonClick)=\"onBack.emit()\"\r\n >\r\n <ng-container *ngTemplateOutlet=\"viewerContent\" />\r\n </mt-page>\r\n } @else {\r\n <!-- Non-page mode -->\r\n <ng-container *ngTemplateOutlet=\"viewerContent\" />\r\n }\r\n\r\n <!-- Reusable viewer content template -->\r\n <ng-template #viewerContent>\r\n <div class=\"relative h-full min-h-[600px] mt-dashboard-viewer\">\r\n @if (!loading() && hasRenderableContent()) {\r\n <div class=\"dashboard-viewer-layout-wrapper\">\r\n <div\r\n class=\"dashboard-viewer-layout\"\r\n [style.min-height]=\"layoutHeight()\"\r\n >\r\n @for (\r\n item of visibleItems();\r\n track trackByDashboardId($index, item)\r\n ) {\r\n <div\r\n class=\"dashboard-viewer-item\"\r\n [class.dashboard-viewer-item--group]=\"isGroup(item)\"\r\n [class.dashboard-viewer-item--header]=\"isHeader(item)\"\r\n [class.dashboard-viewer-item--topbar]=\"isTopbar(item)\"\r\n [style.grid-column]=\"getGridColumn(item)\"\r\n [style.grid-row]=\"getGridRow(item)\"\r\n >\r\n <!-- Group Container -->\r\n @if (isGroup(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"group-container h-full flex flex-col rounded-lg border border-surface-200 overflow-hidden\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-card)'\r\n \"\r\n >\r\n <!-- Group Header with Tabs -->\r\n <div\r\n class=\"group-header flex items-center justify-between px-4 py-2 bg-surface-50 border-b border-surface-200\"\r\n >\r\n <span class=\"font-medium text-sm\">{{\r\n getGroupTitle(item)\r\n }}</span>\r\n <div class=\"group-tabs flex items-center gap-1\">\r\n @for (\r\n child of getGroupChildren(item);\r\n track child.config?.serviceConfig?.dashboardId;\r\n let gIndex = $index\r\n ) {\r\n @if (getItemIcon(child); as icon) {\r\n <button\r\n type=\"button\"\r\n class=\"w-8 h-8 flex items-center justify-center rounded transition-colors\"\r\n [class.bg-primary-100]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.text-primary-600]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.text-muted-color]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n [class.hover:bg-surface-100]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n (click)=\"selectGroupChild(item, gIndex)\"\r\n >\r\n <i class=\"mti mti-{{ icon }}\"></i>\r\n </button>\r\n } @else {\r\n <button\r\n pButton\r\n type=\"button\"\r\n class=\"p-button-text p-button-sm\"\r\n [class.p-button-primary]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.p-button-secondary]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n [label]=\"getItemTitle(child)\"\r\n (click)=\"selectGroupChild(item, gIndex)\"\r\n ></button>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Group Body - Selected Child Content -->\r\n <div class=\"group-body flex-1 min-h-0 p-2\">\r\n @if (\r\n item.selectedGroupIndex !== null &&\r\n item.selectedGroupIndex !== undefined\r\n ) {\r\n @for (\r\n child of getGroupChildren(item);\r\n track child.config?.serviceConfig?.dashboardId;\r\n let cIndex = $index\r\n ) {\r\n @if (\r\n cIndex === item.selectedGroupIndex &&\r\n !child.loading\r\n ) {\r\n <div class=\"w-full h-full\">\r\n <mt-dashboard-item\r\n [config]=\"child.config\"\r\n [chartTypeId]=\"\r\n child.config?.serviceConfig?.dashboardId ||\r\n child.id\r\n \"\r\n [inGroup]=\"true\"\r\n >\r\n </mt-dashboard-item>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n <!-- Header Item -->\r\n @else if (isHeader(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"header-item flex items-center h-full px-4 border-b-2 border-primary\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'transparent'\r\n \"\r\n [style.color]=\"getStyleConfig(item)['color'] || 'inherit'\"\r\n >\r\n <h3 class=\"font-semibold text-lg m-0\">\r\n {{ getItemTitle(item) }}\r\n </h3>\r\n </div>\r\n }\r\n }\r\n <!-- Topbar Item -->\r\n @else if (isTopbar(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"topbar-item flex items-center h-full px-4 bg-surface-50\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-50)'\r\n \"\r\n >\r\n <nav class=\"flex items-center gap-2 text-sm\">\r\n @for (\r\n crumb of getBreadcrumb(item);\r\n track $index;\r\n let last = $last\r\n ) {\r\n <span\r\n class=\"text-muted-color hover:text-primary cursor-pointer transition-colors\"\r\n >\r\n {{\r\n crumb.label?.[languageCode()] ||\r\n crumb.label?.[\"en\"] ||\r\n crumb.label\r\n }}\r\n </span>\r\n @if (!last) {\r\n <i\r\n class=\"mti mti-chevron-right text-xs text-muted-color\"\r\n ></i>\r\n }\r\n }\r\n @if (getBreadcrumb(item).length === 0) {\r\n <span class=\"text-muted-color italic\">{{\r\n t(\"noBreadcrumb\")\r\n }}</span>\r\n }\r\n </nav>\r\n </div>\r\n }\r\n }\r\n <!-- Regular Chart Item -->\r\n @else {\r\n @if (!item.loading) {\r\n <div\r\n class=\"chart-item h-full flex flex-col rounded-lg border border-surface-200 overflow-hidden\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-card)'\r\n \"\r\n >\r\n <mt-dashboard-item\r\n [config]=\"item.config\"\r\n [chartTypeId]=\"\r\n item.config?.serviceConfig?.dashboardId || item.id\r\n \"\r\n [inGroup]=\"false\"\r\n >\r\n </mt-dashboard-item>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden Dialogs (for modal rendering) -->\r\n @if (dialogs().length > 0) {\r\n <div class=\"hidden\">\r\n @for (\r\n dialog of dialogs();\r\n track dialog.config?.serviceConfig?.dashboardId\r\n ) {\r\n <!-- Dialog placeholder - will be rendered in modal when triggered -->\r\n <div\r\n [attr.data-dialog-id]=\"\r\n dialog.config?.serviceConfig?.dashboardId\r\n \"\r\n ></div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Filters Sidebar Placeholder -->\r\n @if (showFilters() && filters().length > 0) {\r\n <div\r\n class=\"filters-sidebar fixed end-0 top-0 bottom-0 w-64 bg-surface-card border-s border-surface-200 p-4 shadow-lg z-50 hidden\"\r\n >\r\n <!-- Filter sidebar content - integrate with actual filter component -->\r\n <h4 class=\"font-semibold mb-4\">{{ t(\"filters\") }}</h4>\r\n @for (filter of filters(); track $index) {\r\n <div class=\"filter-item mb-3 p-2 bg-surface-50 rounded\">\r\n <span class=\"text-sm\">{{\r\n filter.label || filter.propertyKey\r\n }}</span>\r\n </div>\r\n }\r\n </div>\r\n }\r\n } @else if (loading()) {\r\n <!-- Loading State -->\r\n <div class=\"flex items-center justify-center h-64\">\r\n <div class=\"text-center\">\r\n <i\r\n class=\"mti mti-loader-2 text-4xl text-primary animate-spin mb-2\"\r\n ></i>\r\n <p class=\"text-muted-color\">{{ t(\"loading\") }}</p>\r\n </div>\r\n </div>\r\n } @else {\r\n <div\r\n class=\"flex flex-col items-center justify-center h-64 text-muted-color\"\r\n >\r\n <i class=\"mti mti-layout text-6xl mb-4\"></i>\r\n <p class=\"text-lg\">{{ t(\"noPageSelected\") }}</p>\r\n <p class=\"text-sm\">{{ t(\"selectPageToStart\") }}</p>\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n</ng-container>\r\n", styles: [".mt-dashboard-viewer{width:100%;height:100%}.dashboard-viewer-layout-wrapper{width:100%}.dashboard-viewer-layout{display:grid;grid-template-columns:repeat(36,minmax(0,1fr));grid-auto-rows:60px;gap:10px;width:100%;align-items:stretch}.dashboard-viewer-item{min-width:0;min-height:0}.dashboard-viewer-item>*{width:100%;height:100%}.dashboard-viewer-item--header{z-index:1}.dashboard-viewer-item--topbar{z-index:2}.group-container{background-color:var(--surface-card)}.group-header{flex-shrink:0}.group-tabs{flex-wrap:wrap}.group-body{background-color:var(--surface-0)}.chart-item{background-color:var(--surface-card)}.header-item{background-color:transparent}.topbar-item{background-color:var(--surface-50)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.filters-sidebar{transform:translate(100%);transition:transform .3s ease-in-out}.filters-sidebar.open{transform:translate(0)}[dir=rtl] .filters-sidebar{transform:translate(-100%)}[dir=rtl] .filters-sidebar.open{transform:translate(0)}.table-responsive{max-height:75vh;overflow-y:auto;overflow-x:auto}.modal-content{border:0!important}.custom-donut-chart{display:flex;justify-content:center;height:auto!important}.custom-donut-chart canvas{margin-top:-1.5rem!important}@media(max-width:768px){.dashboard-viewer-layout{display:flex;flex-direction:column;min-height:0!important}.dashboard-viewer-item{min-height:220px}.dashboard-viewer-item--header,.dashboard-viewer-item--topbar{min-height:72px}.dashboard-viewer-item--group{min-height:280px}}\n"] }]
20428
- }], propDecorators: { isPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "isPage", required: false }] }], pageTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageTitle", required: false }] }], backButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "backButton", required: false }] }], pageId: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageId", required: false }] }], dashboardData: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardData", required: false }] }], chartsData: [{ type: i0.Input, args: [{ isSignal: true, alias: "chartsData", required: false }] }], dialogsData: [{ type: i0.Input, args: [{ isSignal: true, alias: "dialogsData", required: false }] }], filtersData: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtersData", required: false }] }], showFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilters", required: false }] }], pageLoaded: [{ type: i0.Output, args: ["pageLoaded"] }], onBack: [{ type: i0.Output, args: ["onBack"] }], chartClick: [{ type: i0.Output, args: ["chartClick"] }] } });
20467
+ ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder'\">\r\n <!-- Page mode -->\r\n @if (isPage()) {\r\n <mt-page\r\n [title]=\"\r\n pageTitle() ||\r\n pageConfig()?.name?.[languageCode()] ||\r\n pageConfig()?.name?.['en'] ||\r\n t('dashboard')\r\n \"\r\n [backButton]=\"backButton()\"\r\n (backButtonClick)=\"onBack.emit()\"\r\n >\r\n <ng-container *ngTemplateOutlet=\"viewerContent\" />\r\n </mt-page>\r\n } @else {\r\n <!-- Non-page mode -->\r\n <ng-container *ngTemplateOutlet=\"viewerContent\" />\r\n }\r\n\r\n <!-- Reusable viewer content template -->\r\n <ng-template #viewerContent>\r\n <div class=\"relative h-full min-h-[600px] mt-dashboard-viewer\">\r\n @if (!loading() && hasRenderableContent()) {\r\n <div class=\"dashboard-viewer-layout-wrapper\">\r\n <div\r\n class=\"dashboard-viewer-layout\"\r\n [style.min-height]=\"layoutHeight()\"\r\n >\r\n @for (\r\n item of visibleItems();\r\n track trackByDashboardId($index, item)\r\n ) {\r\n <div\r\n class=\"dashboard-viewer-item\"\r\n [class.dashboard-viewer-item--group]=\"isGroup(item)\"\r\n [class.dashboard-viewer-item--header]=\"isHeader(item)\"\r\n [class.dashboard-viewer-item--topbar]=\"isTopbar(item)\"\r\n [style.grid-column]=\"getGridColumn(item)\"\r\n [style.grid-row]=\"getGridRow(item)\"\r\n >\r\n <!-- Group Container -->\r\n @if (isGroup(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"group-container h-full flex flex-col rounded-lg border border-surface-200 overflow-hidden\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-card)'\r\n \"\r\n >\r\n <!-- Group Header with Tabs -->\r\n <div\r\n class=\"group-header flex items-center justify-between px-4 py-2 bg-surface-50 border-b border-surface-200\"\r\n >\r\n <span class=\"font-medium text-sm\">{{\r\n getGroupTitle(item)\r\n }}</span>\r\n <div class=\"group-tabs flex items-center gap-1\">\r\n @for (\r\n child of getGroupChildren(item);\r\n track child.config?.serviceConfig?.dashboardId;\r\n let gIndex = $index\r\n ) {\r\n @if (getItemIcon(child); as icon) {\r\n <button\r\n type=\"button\"\r\n class=\"w-8 h-8 flex items-center justify-center rounded transition-colors\"\r\n [class.bg-primary-100]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.text-primary-600]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.text-muted-color]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n [class.hover:bg-surface-100]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n (click)=\"selectGroupChild(item, gIndex)\"\r\n >\r\n <i class=\"mti mti-{{ icon }}\"></i>\r\n </button>\r\n } @else {\r\n <button\r\n pButton\r\n type=\"button\"\r\n class=\"p-button-text p-button-sm\"\r\n [class.p-button-primary]=\"\r\n item.selectedGroupIndex === gIndex\r\n \"\r\n [class.p-button-secondary]=\"\r\n item.selectedGroupIndex !== gIndex\r\n \"\r\n [label]=\"getItemTitle(child)\"\r\n (click)=\"selectGroupChild(item, gIndex)\"\r\n ></button>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Group Body - Selected Child Content -->\r\n <div class=\"group-body flex-1 min-h-0\">\r\n @if (\r\n item.selectedGroupIndex !== null &&\r\n item.selectedGroupIndex !== undefined\r\n ) {\r\n @for (\r\n child of getGroupChildren(item);\r\n track child.config?.serviceConfig?.dashboardId;\r\n let cIndex = $index\r\n ) {\r\n @if (\r\n cIndex === item.selectedGroupIndex &&\r\n !child.loading\r\n ) {\r\n <mt-dashboard-item\r\n class=\"dashboard-viewer-content-item\"\r\n [config]=\"child.config\"\r\n [chartTypeId]=\"\r\n child.config?.serviceConfig?.dashboardId ||\r\n child.id\r\n \"\r\n [extraFilters]=\"extraFilters()\"\r\n [ignoreQueryFilter]=\"\r\n pageConfig()?.dashboardConfig\r\n ?.ignoreQueryFilter ?? false\r\n \"\r\n [inGroup]=\"true\"\r\n >\r\n </mt-dashboard-item>\r\n }\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n <!-- Header Item -->\r\n @else if (isHeader(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"header-item flex items-center h-full px-4 border-b-2 border-primary\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'transparent'\r\n \"\r\n [style.color]=\"getStyleConfig(item)['color'] || 'inherit'\"\r\n >\r\n <h3 class=\"font-semibold text-lg m-0\">\r\n {{ getItemTitle(item) }}\r\n </h3>\r\n </div>\r\n }\r\n }\r\n <!-- Topbar Item -->\r\n @else if (isTopbar(item)) {\r\n @if (!item.loading) {\r\n <div\r\n class=\"topbar-item flex items-center h-full px-4 bg-surface-50\"\r\n [style.background-color]=\"\r\n getStyleConfig(item)['background-color'] ||\r\n 'var(--surface-50)'\r\n \"\r\n >\r\n <nav class=\"flex items-center gap-2 text-sm\">\r\n @for (\r\n crumb of getBreadcrumb(item);\r\n track $index;\r\n let last = $last\r\n ) {\r\n <span\r\n class=\"text-muted-color hover:text-primary cursor-pointer transition-colors\"\r\n >\r\n {{\r\n crumb.label?.[languageCode()] ||\r\n crumb.label?.[\"en\"] ||\r\n crumb.label\r\n }}\r\n </span>\r\n @if (!last) {\r\n <i\r\n class=\"mti mti-chevron-right text-xs text-muted-color\"\r\n ></i>\r\n }\r\n }\r\n @if (getBreadcrumb(item).length === 0) {\r\n <span class=\"text-muted-color italic\">{{\r\n t(\"noBreadcrumb\")\r\n }}</span>\r\n }\r\n </nav>\r\n </div>\r\n }\r\n }\r\n <!-- Regular Chart Item -->\r\n @else {\r\n @if (!item.loading) {\r\n <mt-dashboard-item\r\n class=\"dashboard-viewer-content-item\"\r\n [config]=\"item.config\"\r\n [chartTypeId]=\"\r\n item.config?.serviceConfig?.dashboardId || item.id\r\n \"\r\n [extraFilters]=\"extraFilters()\"\r\n [ignoreQueryFilter]=\"\r\n pageConfig()?.dashboardConfig?.ignoreQueryFilter ??\r\n false\r\n \"\r\n [inGroup]=\"false\"\r\n >\r\n </mt-dashboard-item>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden Dialogs (for modal rendering) -->\r\n @if (dialogs().length > 0) {\r\n <div class=\"hidden\">\r\n @for (\r\n dialog of dialogs();\r\n track dialog.config?.serviceConfig?.dashboardId\r\n ) {\r\n <!-- Dialog placeholder - will be rendered in modal when triggered -->\r\n <div\r\n [attr.data-dialog-id]=\"\r\n dialog.config?.serviceConfig?.dashboardId\r\n \"\r\n ></div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Filters Sidebar Placeholder -->\r\n @if (showFilters() && filters().length > 0) {\r\n <div\r\n class=\"filters-sidebar fixed end-0 top-0 bottom-0 w-64 bg-surface-card border-s border-surface-200 p-4 shadow-lg z-50 hidden\"\r\n >\r\n <!-- Filter sidebar content - integrate with actual filter component -->\r\n <h4 class=\"font-semibold mb-4\">{{ t(\"filters\") }}</h4>\r\n @for (filter of filters(); track $index) {\r\n <div class=\"filter-item mb-3 p-2 bg-surface-50 rounded\">\r\n <span class=\"text-sm\">{{\r\n filter.label || filter.propertyKey\r\n }}</span>\r\n </div>\r\n }\r\n </div>\r\n }\r\n } @else if (loading()) {\r\n <!-- Loading State -->\r\n <div class=\"flex items-center justify-center h-64\">\r\n <div class=\"text-center\">\r\n <i\r\n class=\"mti mti-loader-2 text-4xl text-primary animate-spin mb-2\"\r\n ></i>\r\n <p class=\"text-muted-color\">{{ t(\"loading\") }}</p>\r\n </div>\r\n </div>\r\n } @else {\r\n <div\r\n class=\"flex flex-col items-center justify-center h-64 text-muted-color\"\r\n >\r\n <i class=\"mti mti-layout text-6xl mb-4\"></i>\r\n <p class=\"text-lg\">{{ t(\"noPageSelected\") }}</p>\r\n <p class=\"text-sm\">{{ t(\"selectPageToStart\") }}</p>\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n</ng-container>\r\n", styles: [".mt-dashboard-viewer{width:100%;height:100%}.dashboard-viewer-layout-wrapper{width:100%}.dashboard-viewer-layout{display:grid;grid-template-columns:repeat(36,minmax(0,1fr));grid-auto-rows:60px;gap:10px;width:100%;align-items:stretch}.dashboard-viewer-item{display:flex;min-width:0;min-height:0}.dashboard-viewer-item>*{width:100%;height:100%}.dashboard-viewer-content-item{display:block;flex:1 1 auto;min-width:0;min-height:0;width:100%;height:100%}.dashboard-viewer-item--header{z-index:1}.dashboard-viewer-item--topbar{z-index:2}.group-container{background-color:var(--surface-card)}.group-header{flex-shrink:0}.group-tabs{flex-wrap:wrap}.group-body{background-color:var(--surface-0)}.header-item{background-color:transparent}.topbar-item{background-color:var(--surface-50)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.filters-sidebar{transform:translate(100%);transition:transform .3s ease-in-out}.filters-sidebar.open{transform:translate(0)}[dir=rtl] .filters-sidebar{transform:translate(-100%)}[dir=rtl] .filters-sidebar.open{transform:translate(0)}.table-responsive{max-height:75vh;overflow-y:auto;overflow-x:auto}.modal-content{border:0!important}.custom-donut-chart{display:flex;justify-content:center;height:auto!important}.custom-donut-chart canvas{margin-top:-1.5rem!important}@media(max-width:768px){.dashboard-viewer-layout{display:flex;flex-direction:column;min-height:0!important}.dashboard-viewer-item{min-height:220px}.dashboard-viewer-item--header,.dashboard-viewer-item--topbar{min-height:72px}.dashboard-viewer-item--group{min-height:280px}}\n"] }]
20468
+ }], propDecorators: { isPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "isPage", required: false }] }], pageTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageTitle", required: false }] }], backButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "backButton", required: false }] }], pageId: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageId", required: false }] }], dashboardData: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardData", required: false }] }], chartsData: [{ type: i0.Input, args: [{ isSignal: true, alias: "chartsData", required: false }] }], dialogsData: [{ type: i0.Input, args: [{ isSignal: true, alias: "dialogsData", required: false }] }], filtersData: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtersData", required: false }] }], extraFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "extraFilters", required: false }] }], showFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilters", required: false }] }], pageLoaded: [{ type: i0.Output, args: ["pageLoaded"] }], onBack: [{ type: i0.Output, args: ["onBack"] }], chartClick: [{ type: i0.Output, args: ["chartClick"] }] } });
20429
20469
 
20430
20470
  /**
20431
20471
  * Dashboard List Component
@@ -20631,7 +20671,7 @@ class DashboardList {
20631
20671
  this.dashboards.reload();
20632
20672
  }
20633
20673
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardList, deps: [], target: i0.ɵɵFactoryTarget.Component });
20634
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: DashboardList, isStandalone: true, selector: "mt-dashboard-list", inputs: { isPage: { classPropertyName: "isPage", publicName: "isPage", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, baseRoute: { classPropertyName: "baseRoute", publicName: "baseRoute", isSignal: true, isRequired: false, transformFunction: null }, showCreateButton: { classPropertyName: "showCreateButton", publicName: "showCreateButton", isSignal: true, isRequired: false, transformFunction: null }, showViewButton: { classPropertyName: "showViewButton", publicName: "showViewButton", isSignal: true, isRequired: false, transformFunction: null }, showEditButton: { classPropertyName: "showEditButton", publicName: "showEditButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dashboardAction: "dashboardAction", dashboardCreated: "dashboardCreated" }, viewQueries: [{ propertyName: "nameTpl", first: true, predicate: ["nameTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder.dashboard'\">\r\n <!-- Page mode -->\r\n @if (isPage()) {\r\n <mt-page [title]=\"pageTitle() || t('dashboards')\">\r\n <!-- Header actions -->\r\n <ng-template #headerEnd>\r\n @if (showCreateButton()) {\r\n <mt-button\r\n [label]=\"t('createDashboard')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"openCreateModal()\"\r\n severity=\"primary\"\r\n size=\"small\"\r\n />\r\n }\r\n </ng-template>\r\n\r\n <!-- Content -->\r\n <ng-container *ngTemplateOutlet=\"tableContent\" />\r\n </mt-page>\r\n } @else {\r\n <!-- Non-page mode -->\r\n <div class=\"dashboard-list space-y-3\">\r\n <!-- Header -->\r\n <div class=\"flex justify-between items-center gap-4\">\r\n <h2 class=\"text-2xl font-semibold text-surface-900 dark:text-surface-0\">\r\n {{ t(\"dashboards\") }}\r\n </h2>\r\n\r\n @if (showCreateButton()) {\r\n <mt-button\r\n [label]=\"t('createDashboard')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"openCreateModal()\"\r\n severity=\"primary\"\r\n />\r\n }\r\n </div>\r\n\r\n <!-- Content -->\r\n <ng-container *ngTemplateOutlet=\"tableContent\" />\r\n </div>\r\n }\r\n\r\n <!-- Reusable table content template -->\r\n <ng-template #tableContent>\r\n <!-- Custom cell templates -->\r\n <ng-template #nameTpl let-row>\r\n <span class=\"font-semibold text-surface-900 dark:text-surface-0\">\r\n {{ getDashboardName(row) }}\r\n </span>\r\n </ng-template>\r\n\r\n <!-- Dashboard Table -->\r\n <mt-table\r\n [data]=\"dashboardList()\"\r\n [columns]=\"tableColumns()\"\r\n [rowActions]=\"rowActions()\"\r\n [loading]=\"isLoading()\"\r\n [showFilters]=\"true\"\r\n [generalSearch]=\"true\"\r\n dataKey=\"id\"\r\n />\r\n </ng-template>\r\n</ng-container>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "showFilters", "loading", "updating", "lazy", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "exportable", "exportFilename", "actionShape", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "pageSize", "currentPage", "first", "filterTerm"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange"] }, { kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
20674
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: DashboardList, isStandalone: true, selector: "mt-dashboard-list", inputs: { isPage: { classPropertyName: "isPage", publicName: "isPage", isSignal: true, isRequired: false, transformFunction: null }, pageTitle: { classPropertyName: "pageTitle", publicName: "pageTitle", isSignal: true, isRequired: false, transformFunction: null }, baseRoute: { classPropertyName: "baseRoute", publicName: "baseRoute", isSignal: true, isRequired: false, transformFunction: null }, showCreateButton: { classPropertyName: "showCreateButton", publicName: "showCreateButton", isSignal: true, isRequired: false, transformFunction: null }, showViewButton: { classPropertyName: "showViewButton", publicName: "showViewButton", isSignal: true, isRequired: false, transformFunction: null }, showEditButton: { classPropertyName: "showEditButton", publicName: "showEditButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dashboardAction: "dashboardAction", dashboardCreated: "dashboardCreated" }, viewQueries: [{ propertyName: "nameTpl", first: true, predicate: ["nameTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder.dashboard'\">\r\n <!-- Page mode -->\r\n @if (isPage()) {\r\n <mt-page [title]=\"pageTitle() || t('dashboards')\">\r\n <!-- Header actions -->\r\n <ng-template #headerEnd>\r\n @if (showCreateButton()) {\r\n <mt-button\r\n [label]=\"t('createDashboard')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"openCreateModal()\"\r\n severity=\"primary\"\r\n size=\"small\"\r\n />\r\n }\r\n </ng-template>\r\n\r\n <!-- Content -->\r\n <ng-container *ngTemplateOutlet=\"tableContent\" />\r\n </mt-page>\r\n } @else {\r\n <!-- Non-page mode -->\r\n <div class=\"dashboard-list space-y-3\">\r\n <!-- Header -->\r\n <div class=\"flex justify-between items-center gap-4\">\r\n <h2 class=\"text-2xl font-semibold text-surface-900 dark:text-surface-0\">\r\n {{ t(\"dashboards\") }}\r\n </h2>\r\n\r\n @if (showCreateButton()) {\r\n <mt-button\r\n [label]=\"t('createDashboard')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"openCreateModal()\"\r\n severity=\"primary\"\r\n />\r\n }\r\n </div>\r\n\r\n <!-- Content -->\r\n <ng-container *ngTemplateOutlet=\"tableContent\" />\r\n </div>\r\n }\r\n\r\n <!-- Reusable table content template -->\r\n <ng-template #tableContent>\r\n <!-- Custom cell templates -->\r\n <ng-template #nameTpl let-row>\r\n <span class=\"font-semibold text-surface-900 dark:text-surface-0\">\r\n {{ getDashboardName(row) }}\r\n </span>\r\n </ng-template>\r\n\r\n <!-- Dashboard Table -->\r\n <mt-table\r\n [data]=\"dashboardList()\"\r\n [columns]=\"tableColumns()\"\r\n [rowActions]=\"rowActions()\"\r\n [loading]=\"isLoading()\"\r\n [showFilters]=\"true\"\r\n [generalSearch]=\"true\"\r\n dataKey=\"id\"\r\n />\r\n </ng-template>\r\n</ng-container>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "exportable", "exportFilename", "actionShape", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "pageSize", "currentPage", "first", "filterTerm"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange"] }, { kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
20635
20675
  }
20636
20676
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardList, decorators: [{
20637
20677
  type: Component,