@masterteam/dashboard-builder 0.0.11 → 0.0.13

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';
@@ -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
  /**
@@ -20476,7 +20454,7 @@ class DashboardViewer {
20476
20454
  });
20477
20455
  }
20478
20456
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardViewer, deps: [], target: i0.ɵɵFactoryTarget.Component });
20479
- 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\">\n @if (\n item.selectedGroupIndex !== null &&\n item.selectedGroupIndex !== undefined\n ) {\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 (\n cIndex === item.selectedGroupIndex &&\n !child.loading\n ) {\n <mt-dashboard-item\n class=\"dashboard-viewer-content-item\"\n [config]=\"child.config\"\n [chartTypeId]=\"\n child.config?.serviceConfig?.dashboardId ||\n child.id\n \"\n [extraFilters]=\"extraFilters()\"\n [ignoreQueryFilter]=\"\n pageConfig()?.dashboardConfig\n ?.ignoreQueryFilter ?? false\n \"\n [inGroup]=\"true\"\n >\n </mt-dashboard-item>\n }\n }\n }\n </div>\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 -->\n @else {\n @if (!item.loading) {\n <mt-dashboard-item\n class=\"dashboard-viewer-content-item\"\n [config]=\"item.config\"\n [chartTypeId]=\"\n item.config?.serviceConfig?.dashboardId || item.id\n \"\n [extraFilters]=\"extraFilters()\"\n [ignoreQueryFilter]=\"\n pageConfig()?.dashboardConfig?.ignoreQueryFilter ??\n false\n \"\n [inGroup]=\"false\"\n >\n </mt-dashboard-item>\n }\n }\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 });
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 });
20480
20458
  }
20481
20459
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: DashboardViewer, decorators: [{
20482
20460
  type: Component,
@@ -20486,7 +20464,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
20486
20464
  ButtonModule,
20487
20465
  DashboardItem,
20488
20466
  Page,
20489
- ], 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\">\n @if (\n item.selectedGroupIndex !== null &&\n item.selectedGroupIndex !== undefined\n ) {\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 (\n cIndex === item.selectedGroupIndex &&\n !child.loading\n ) {\n <mt-dashboard-item\n class=\"dashboard-viewer-content-item\"\n [config]=\"child.config\"\n [chartTypeId]=\"\n child.config?.serviceConfig?.dashboardId ||\n child.id\n \"\n [extraFilters]=\"extraFilters()\"\n [ignoreQueryFilter]=\"\n pageConfig()?.dashboardConfig\n ?.ignoreQueryFilter ?? false\n \"\n [inGroup]=\"true\"\n >\n </mt-dashboard-item>\n }\n }\n }\n </div>\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 -->\n @else {\n @if (!item.loading) {\n <mt-dashboard-item\n class=\"dashboard-viewer-content-item\"\n [config]=\"item.config\"\n [chartTypeId]=\"\n item.config?.serviceConfig?.dashboardId || item.id\n \"\n [extraFilters]=\"extraFilters()\"\n [ignoreQueryFilter]=\"\n pageConfig()?.dashboardConfig?.ignoreQueryFilter ??\n false\n \"\n [inGroup]=\"false\"\n >\n </mt-dashboard-item>\n }\n }\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"] }]
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"] }]
20490
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"] }] } });
20491
20469
 
20492
20470
  /**