@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
|
-
* -
|
|
18000
|
-
* - Displays using entity
|
|
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 =
|
|
18092
|
-
|
|
18093
|
-
|
|
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) {\
|
|
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
|
-
|
|
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) {\
|
|
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
|
/**
|