@masterteam/dashboard-builder 0.0.36 → 0.0.37

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.
@@ -24683,7 +24683,7 @@ class EChartComponent {
24683
24683
  return this.chart;
24684
24684
  }
24685
24685
  applyResponsiveOptions(option, bounds) {
24686
- let next = { ...option };
24686
+ const next = { ...option };
24687
24687
  const configuredLegendSide = this.resolveConfiguredLegendSide();
24688
24688
  let legendSide = this.resolveEffectiveLegendSide(configuredLegendSide, this.resolveLegendSide(next.legend));
24689
24689
  if (next.legend && legendSide) {
@@ -25345,7 +25345,7 @@ class ChartCardComponent {
25345
25345
  }
25346
25346
  }
25347
25347
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ChartCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
25348
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ChartCardComponent, isStandalone: true, selector: "mt-chart-card", inputs: { dashboardId: { classPropertyName: "dashboardId", publicName: "dashboardId", isSignal: true, isRequired: true, transformFunction: null }, inGroup: { classPropertyName: "inGroup", publicName: "inGroup", isSignal: true, isRequired: false, transformFunction: null }, isConfigMode: { classPropertyName: "isConfigMode", publicName: "isConfigMode", isSignal: true, isRequired: false, transformFunction: null }, titleEditable: { classPropertyName: "titleEditable", publicName: "titleEditable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onAction: "onAction", onConfigure: "onConfigure", onTitleChange: "onTitleChange" }, ngImport: i0, template: "<mt-card-content\n [inGroup]=\"inGroup()\"\n [headerConfig]=\"\n configurationItem()?.clientConfig?.configAsType?.['headerCardConfig']\n \"\n [styleConfig]=\"\n configurationItem()?.clientConfig?.displayConfig?.['StyleConfig']\n \"\n [cardStyleConfig]=\"\n configurationItem()?.clientConfig?.configAsType?.['cardStyleConfig']\n \"\n [density]=\"configurationItem()?.clientConfig?.configAsType?.['density']\"\n [title]=\"title()\"\n [titleEditable]=\"titleEditable()\"\n (titleChange)=\"onTitleChange.emit($event)\"\n [isNoTopEnd]=\"!showTopEnd()\"\n [isChart]=\"true\"\n (headerClick)=\"onHeaderClick()\"\n>\n <ng-container topEnd>\n @if (showStaticFilters()) {\n <mt-static-filters\n [config]=\"staticFilterConfig()\"\n [dashboardId]=\"dashboardId()\"\n >\n </mt-static-filters>\n }\n @if (showDynamicFilters()) {\n <mt-dynamic-filters\n [fields]=\"dynamicFilterFields()\"\n [dashboardId]=\"dashboardId()\"\n [showLabels]=\"false\"\n >\n </mt-dynamic-filters>\n }\n @if (showFilter()) {\n <mt-card-filter [config]=\"filterConfig()\" [dashboardId]=\"dashboardId()\">\n </mt-card-filter>\n }\n @if (cardInfoConfig()?.show && cardInfoConfig()?.value) {\n <mt-card-info [cardInfo]=\"cardInfoConfig()\"></mt-card-info>\n }\n </ng-container>\n\n <ng-container body>\n <div\n class=\"chart-container\"\n [class.config-mode]=\"isConfigMode()\"\n *transloco=\"let t; prefix: 'dashboard'\"\n >\n @if (componentViewMode() === \"splitter\" && hasSplitterData()) {\n <div\n class=\"splitter-view\"\n [class.splitter-vertical]=\"splitterOrientation() === 'vertical'\"\n [style.gap.px]=\"splitterGap()\"\n >\n <div class=\"splitter-side splitter-positive\">\n <div class=\"splitter-count\">\n {{ dataHandled()?.positive?.groupedValue ?? 0 }}\n </div>\n <ul class=\"splitter-labels\">\n @for (\n label of dataHandled()?.positive?.labels || [];\n track $index\n ) {\n <li>{{ label }}</li>\n }\n </ul>\n </div>\n <div class=\"splitter-divider\"></div>\n <div class=\"splitter-side splitter-negative\">\n <div class=\"splitter-count\">\n {{ dataHandled()?.negative?.groupedValue ?? 0 }}\n </div>\n <ul class=\"splitter-labels\">\n @for (\n label of dataHandled()?.negative?.labels || [];\n track $index\n ) {\n <li>{{ label }}</li>\n }\n </ul>\n </div>\n </div>\n } @else if (componentViewMode() === \"timeline\" && hasTimelineData()) {\n <div class=\"timeline-view\">\n @if (timelineLabels().length) {\n <div class=\"timeline-legend\">\n @for (label of timelineLabels(); track $index) {\n <span\n class=\"timeline-legend-chip\"\n [style.color]=\"label.color || '#666'\"\n [style.borderColor]=\"label.color || '#666'\"\n >\n {{ label.label }}\n </span>\n }\n </div>\n }\n <ul class=\"timeline-resources\">\n @for (res of timelineResources(); track res.id) {\n <li class=\"timeline-resource\">\n <div class=\"timeline-resource-title\">\n {{ res.title || res.id }}\n </div>\n @if (res.children?.length) {\n <ul class=\"timeline-children\">\n @for (child of res.children; track child.id) {\n <li>\n <div class=\"timeline-resource-title small\">\n {{ child.title || child.id }}\n </div>\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n <ul class=\"timeline-events\">\n @for (\n evt of timelineEventsLimited();\n track evt.resourceId + $index\n ) {\n <li class=\"timeline-event\">\n <span\n class=\"timeline-event-color\"\n [style.background]=\"evt.color || '#999'\"\n ></span>\n <span class=\"timeline-event-title\">{{ evt.title }}</span>\n <span class=\"timeline-event-range\"\n >{{ formatDate(evt.start) }} \u2192 {{ formatDate(evt.end) }}</span\n >\n @if (evt.progress !== null && evt.progress !== undefined) {\n <span class=\"timeline-event-progress\">\n <span\n class=\"bar\"\n [style.width.%]=\"\n evt.progress > 100\n ? 100\n : evt.progress < 0\n ? 0\n : evt.progress\n \"\n ></span>\n </span>\n }\n </li>\n }\n </ul>\n @if (timelineEventsTruncated()) {\n <div class=\"timeline-more\">\n + {{ timelineEvents().length - 50 }} more\n </div>\n }\n </div>\n } @else if (\n componentViewMode() === \"phaseGateStepper\" && hasPhaseGateData()\n ) {\n <div\n class=\"phase-gate-view\"\n [class.phase-gate-compact]=\"phaseGateCompact()\"\n >\n <ol class=\"phase-steps\">\n @for (step of phaseGateSteps(); track $index) {\n <li\n class=\"phase-step\"\n [class.is-current]=\"$index === phaseGateCurrentIndex()\"\n [class.is-done]=\"$index < phaseGateCurrentIndex()\"\n >\n <span class=\"phase-step-number\">{{ $index + 1 }}</span>\n @if (phaseGateShowLabels()) {\n <span class=\"phase-step-name\">{{\n step?.name || step?.title || step?.label || t(\"phase\")\n }}</span>\n }\n </li>\n }\n </ol>\n </div>\n } @else if (dataHandled() && !dataHandled()?.noData) {\n <mt-echart\n [dashboardId]=\"dashboardId()\"\n [chartConfig]=\"dataHandled()\"\n [configurationItem]=\"configurationItem()\"\n [inGroup]=\"inGroup()\"\n (chartClick)=\"onChartClick($event)\"\n >\n </mt-echart>\n } @else if (dataHandled()?.noData) {\n <div class=\"no-data-container\">\n <i class=\"pi pi-info-circle\"></i>\n <span>{{ t(\"noChartData\") }}</span>\n </div>\n } @else {\n <div class=\"loading-container\">\n <div class=\"loading-spinner\"></div>\n </div>\n }\n </div>\n </ng-container>\n</mt-card-content>\n", styles: [":host{display:block;height:100%}.chart-container{height:100%;min-height:300px}.chart-container.config-mode{min-height:400px}.no-data-container,.needs-config-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;min-height:200px;color:#999;gap:.5rem;padding:1rem;text-align:center}.no-data-container i,.needs-config-container i{font-size:2rem}.needs-config-container .title{font-weight:600;color:#555;font-size:.95rem}.needs-config-container .hint{font-size:.8rem;max-width:28rem;line-height:1.4}.needs-config-container .configure-btn{margin-top:.5rem;display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .9rem;font-size:.85rem;font-weight:600;color:#fff;background:var(--p-primary-500, #2563eb);border:0;border-radius:.375rem;cursor:pointer;box-shadow:0 1px 2px #0000000d}.needs-config-container .configure-btn:hover{background:var(--p-primary-600, #1d4ed8)}.loading-container{display:flex;align-items:center;justify-content:center;height:100%;min-height:200px}.loading-spinner{width:2rem;height:2rem;border:3px solid #f0f0f0;border-top-color:#4caf50;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.splitter-view{display:flex;align-items:stretch;height:100%;gap:.5rem}.splitter-view.splitter-vertical{flex-direction:column}.splitter-view.splitter-vertical .splitter-divider{width:auto;height:1px}.splitter-side{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:1rem;border-radius:.5rem}.splitter-positive{background:#22c55e14}.splitter-negative{background:#ef444414}.splitter-count{font-size:2rem;font-weight:700;margin-bottom:.5rem}.splitter-positive .splitter-count{color:#16a34a}.splitter-negative .splitter-count{color:#dc2626}.splitter-labels{list-style:none;padding:0;margin:0;text-align:center;font-size:.8rem;color:#555}.splitter-labels li{padding:.15rem 0}.splitter-divider{width:1px;background:#00000014}.timeline-view{display:flex;flex-direction:column;gap:.5rem;padding:.5rem;height:100%;overflow:auto}.timeline-legend{display:flex;flex-wrap:wrap;gap:.5rem}.timeline-legend-chip{padding:.15rem .5rem;border:1px solid;border-radius:999px;font-size:.75rem}.timeline-resources{list-style:none;padding:0;margin:0 0 .25rem}.timeline-resource{padding:.25rem 0;border-bottom:1px solid rgba(0,0,0,.04)}.timeline-resource-title{font-weight:600;font-size:.85rem;color:#333}.timeline-resource-title.small{font-weight:500;font-size:.8rem;color:#555}.timeline-children{list-style:none;margin:.25rem 0 0 1rem;padding:0}.timeline-events{list-style:none;padding:0;margin:0}.timeline-event{display:grid;grid-template-columns:.5rem 1fr auto auto;align-items:center;gap:.5rem;padding:.25rem 0;font-size:.8rem}.timeline-event-color{display:inline-block;width:8px;height:8px;border-radius:999px}.timeline-event-title{color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.timeline-event-range{color:#666;font-size:.7rem;white-space:nowrap}.timeline-event-progress{position:relative;width:4rem;height:6px;background:#0000000f;border-radius:999px;overflow:hidden}.timeline-event-progress .bar{position:absolute;inset:0;background:var(--p-primary-500, #2563eb)}.timeline-more{text-align:center;font-size:.75rem;color:#888;padding:.25rem 0}.phase-gate-view{height:100%;display:flex;align-items:center;justify-content:center;padding:1rem;overflow-x:auto}.phase-steps{display:flex;align-items:center;gap:.75rem;list-style:none;margin:0;padding:0}.phase-step{display:flex;align-items:center;gap:.5rem;padding:.4rem .75rem;border-radius:999px;background:#0000000a;font-size:.8rem;color:#555;white-space:nowrap}.phase-step-number{width:1.5rem;height:1.5rem;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;font-weight:700;background:#0000000f;color:#666}.phase-step.is-done{background:#22c55e1a;color:#16a34a}.phase-step.is-done .phase-step-number{background:#16a34a;color:#fff}.phase-step.is-current{background:#2563eb1a;color:#2563eb;font-weight:600}.phase-step.is-current .phase-step-number{background:#2563eb;color:#fff}.phase-gate-view.phase-gate-compact .phase-step{padding:.2rem .45rem;font-size:.7rem;gap:.25rem}.phase-gate-view.phase-gate-compact .phase-step-number{width:1.1rem;height:1.1rem;font-size:.7rem}\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", "density", "showHeader", "headerClickable", "isNoTopEnd", "isChart", "titleEditable"], outputs: ["headerClick", "titleChange"] }, { kind: "component", type: CardFilterComponent, selector: "mt-card-filter", inputs: ["config", "dashboardId"], outputs: ["filterChange"] }, { kind: "component", type: StaticFiltersComponent, selector: "mt-static-filters", inputs: ["config", "dashboardId"], outputs: ["filterChange"] }, { kind: "component", type: DynamicFiltersComponent, selector: "mt-dynamic-filters", inputs: ["fields", "dashboardId", "showLabels"], outputs: ["filterChange", "filtersChange"] }, { kind: "component", type: CardInfoComponent, selector: "mt-card-info", inputs: ["cardInfo"] }, { kind: "component", type: EChartComponent, selector: "mt-echart", inputs: ["dashboardId", "chartConfig", "configurationItem", "height", "inGroup", "headerHidden"], outputs: ["chartClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
25348
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ChartCardComponent, isStandalone: true, selector: "mt-chart-card", inputs: { dashboardId: { classPropertyName: "dashboardId", publicName: "dashboardId", isSignal: true, isRequired: true, transformFunction: null }, inGroup: { classPropertyName: "inGroup", publicName: "inGroup", isSignal: true, isRequired: false, transformFunction: null }, isConfigMode: { classPropertyName: "isConfigMode", publicName: "isConfigMode", isSignal: true, isRequired: false, transformFunction: null }, titleEditable: { classPropertyName: "titleEditable", publicName: "titleEditable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onAction: "onAction", onConfigure: "onConfigure", onTitleChange: "onTitleChange" }, ngImport: i0, template: "<mt-card-content\n [inGroup]=\"inGroup()\"\n [headerConfig]=\"\n configurationItem()?.clientConfig?.configAsType?.['headerCardConfig']\n \"\n [styleConfig]=\"\n configurationItem()?.clientConfig?.displayConfig?.['StyleConfig']\n \"\n [cardStyleConfig]=\"\n configurationItem()?.clientConfig?.configAsType?.['cardStyleConfig']\n \"\n [density]=\"configurationItem()?.clientConfig?.configAsType?.['density']\"\n [title]=\"title()\"\n [titleEditable]=\"titleEditable()\"\n (titleChange)=\"onTitleChange.emit($event)\"\n [isNoTopEnd]=\"!showTopEnd()\"\n [isChart]=\"true\"\n (headerClick)=\"onHeaderClick()\"\n>\n <ng-container topEnd>\n @if (showStaticFilters()) {\n <mt-static-filters\n [config]=\"staticFilterConfig()\"\n [dashboardId]=\"dashboardId()\"\n >\n </mt-static-filters>\n }\n @if (showDynamicFilters()) {\n <mt-dynamic-filters\n [fields]=\"dynamicFilterFields()\"\n [dashboardId]=\"dashboardId()\"\n [showLabels]=\"false\"\n >\n </mt-dynamic-filters>\n }\n @if (showFilter()) {\n <mt-card-filter [config]=\"filterConfig()\" [dashboardId]=\"dashboardId()\">\n </mt-card-filter>\n }\n @if (cardInfoConfig()?.show && cardInfoConfig()?.value) {\n <mt-card-info [cardInfo]=\"cardInfoConfig()\"></mt-card-info>\n }\n </ng-container>\n\n <ng-container body>\n <div\n class=\"chart-container\"\n [class.config-mode]=\"isConfigMode()\"\n *transloco=\"let t; prefix: 'dashboard'\"\n >\n @if (componentViewMode() === \"splitter\" && hasSplitterData()) {\n <div\n class=\"splitter-view\"\n [class.splitter-vertical]=\"splitterOrientation() === 'vertical'\"\n [style.gap.px]=\"splitterGap()\"\n >\n <div class=\"splitter-side splitter-positive\">\n <div class=\"splitter-count\">\n {{ dataHandled()?.positive?.groupedValue ?? 0 }}\n </div>\n <ul class=\"splitter-labels\">\n @for (\n label of dataHandled()?.positive?.labels || [];\n track $index\n ) {\n <li>{{ label }}</li>\n }\n </ul>\n </div>\n <div class=\"splitter-divider\"></div>\n <div class=\"splitter-side splitter-negative\">\n <div class=\"splitter-count\">\n {{ dataHandled()?.negative?.groupedValue ?? 0 }}\n </div>\n <ul class=\"splitter-labels\">\n @for (\n label of dataHandled()?.negative?.labels || [];\n track $index\n ) {\n <li>{{ label }}</li>\n }\n </ul>\n </div>\n </div>\n } @else if (componentViewMode() === \"timeline\" && hasTimelineData()) {\n <div class=\"timeline-view\">\n @if (timelineLabels().length) {\n <div class=\"timeline-legend\">\n @for (label of timelineLabels(); track $index) {\n <span\n class=\"timeline-legend-chip\"\n [style.color]=\"label.color || '#666'\"\n [style.borderColor]=\"label.color || '#666'\"\n >\n {{ label.label }}\n </span>\n }\n </div>\n }\n <ul class=\"timeline-resources\">\n @for (res of timelineResources(); track res.id) {\n <li class=\"timeline-resource\">\n <div class=\"timeline-resource-title\">\n {{ res.title || res.id }}\n </div>\n @if (res.children?.length) {\n <ul class=\"timeline-children\">\n @for (child of res.children; track child.id) {\n <li>\n <div class=\"timeline-resource-title small\">\n {{ child.title || child.id }}\n </div>\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n <ul class=\"timeline-events\">\n @for (\n evt of timelineEventsLimited();\n track evt.resourceId + $index\n ) {\n <li class=\"timeline-event\">\n <span\n class=\"timeline-event-color\"\n [style.background]=\"evt.color || '#999'\"\n ></span>\n <span class=\"timeline-event-title\">{{ evt.title }}</span>\n <span class=\"timeline-event-range\"\n >{{ formatDate(evt.start) }} \u2192 {{ formatDate(evt.end) }}</span\n >\n @if (evt.progress !== null && evt.progress !== undefined) {\n <span class=\"timeline-event-progress\">\n <span\n class=\"bar\"\n [style.width.%]=\"\n evt.progress > 100\n ? 100\n : evt.progress < 0\n ? 0\n : evt.progress\n \"\n ></span>\n </span>\n }\n </li>\n }\n </ul>\n @if (timelineEventsTruncated()) {\n <div class=\"timeline-more\">\n + {{ timelineEvents().length - 50 }} more\n </div>\n }\n </div>\n } @else if (\n componentViewMode() === \"phaseGateStepper\" && hasPhaseGateData()\n ) {\n <div\n class=\"phase-gate-view\"\n [class.phase-gate-compact]=\"phaseGateCompact()\"\n >\n <ol class=\"phase-steps\">\n @for (step of phaseGateSteps(); track $index) {\n <li\n class=\"phase-step\"\n [class.is-current]=\"$index === phaseGateCurrentIndex()\"\n [class.is-done]=\"$index < phaseGateCurrentIndex()\"\n >\n <span class=\"phase-step-number\">{{ $index + 1 }}</span>\n @if (phaseGateShowLabels()) {\n <span class=\"phase-step-name\">{{\n step?.name || step?.title || step?.label || t(\"phase\")\n }}</span>\n }\n </li>\n }\n </ol>\n </div>\n } @else if (dataHandled() && !dataHandled()?.noData) {\n <mt-echart\n [dashboardId]=\"dashboardId()\"\n [chartConfig]=\"dataHandled()\"\n [configurationItem]=\"configurationItem()\"\n [inGroup]=\"inGroup()\"\n (chartClick)=\"onChartClick($event)\"\n >\n </mt-echart>\n } @else if (dataHandled()?.noData) {\n <div class=\"no-data-container\">\n <i class=\"pi pi-info-circle\"></i>\n <span>{{ t(\"noChartData\") }}</span>\n </div>\n } @else {\n <div class=\"loading-container\">\n <div class=\"loading-spinner\"></div>\n </div>\n }\n </div>\n </ng-container>\n</mt-card-content>\n", styles: [":host{display:block;height:100%}.chart-container{height:100%;min-height:300px}.chart-container.config-mode{min-height:400px}.no-data-container,.needs-config-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;min-height:200px;color:#999;gap:.5rem;padding:1rem;text-align:center}.no-data-container i,.needs-config-container i{font-size:2rem}.needs-config-container .title{font-weight:600;color:#555;font-size:.95rem}.needs-config-container .hint{font-size:.8rem;max-width:28rem;line-height:1.4}.needs-config-container .configure-btn{margin-top:.5rem;display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .9rem;font-size:.85rem;font-weight:600;color:#fff;background:var(--p-primary-500, #2563eb);border:0;border-radius:.375rem;cursor:pointer;box-shadow:0 1px 2px #0000000d}.needs-config-container .configure-btn:hover{background:var(--p-primary-600, #1d4ed8)}.loading-container{display:flex;align-items:center;justify-content:center;height:100%;min-height:200px}.loading-spinner{width:2rem;height:2rem;border:3px solid #f0f0f0;border-top-color:#4caf50;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.splitter-view{display:flex;align-items:stretch;height:100%;gap:.5rem}.splitter-view.splitter-vertical{flex-direction:column}.splitter-view.splitter-vertical .splitter-divider{width:auto;height:1px}.splitter-side{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:1rem;border-radius:.5rem}.splitter-positive{background:#22c55e14}.splitter-negative{background:#ef444414}.splitter-count{font-size:2rem;font-weight:700;margin-bottom:.5rem}.splitter-positive .splitter-count{color:#16a34a}.splitter-negative .splitter-count{color:#dc2626}.splitter-labels{list-style:none;padding:0;margin:0;text-align:center;font-size:.8rem;color:#555}.splitter-labels li{padding:.15rem 0}.splitter-divider{width:1px;background:#00000014}.timeline-view{display:flex;flex-direction:column;gap:.5rem;padding:.5rem;height:100%;overflow:auto}.timeline-legend{display:flex;flex-wrap:wrap;gap:.5rem}.timeline-legend-chip{padding:.15rem .5rem;border:1px solid;border-radius:999px;font-size:.75rem}.timeline-resources{list-style:none;padding:0;margin:0 0 .25rem}.timeline-resource{padding:.25rem 0;border-bottom:1px solid rgba(0,0,0,.04)}.timeline-resource-title{font-weight:600;font-size:.85rem;color:#333}.timeline-resource-title.small{font-weight:500;font-size:.8rem;color:#555}.timeline-children{list-style:none;margin:.25rem 0 0 1rem;padding:0}.timeline-events{list-style:none;padding:0;margin:0}.timeline-event{display:grid;grid-template-columns:.5rem 1fr auto auto;align-items:center;gap:.5rem;padding:.25rem 0;font-size:.8rem}.timeline-event-color{display:inline-block;width:8px;height:8px;border-radius:999px}.timeline-event-title{color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.timeline-event-range{color:#666;font-size:.7rem;white-space:nowrap}.timeline-event-progress{position:relative;width:4rem;height:6px;background:#0000000f;border-radius:999px;overflow:hidden}.timeline-event-progress .bar{position:absolute;inset:0;background:var(--p-primary-500, #2563eb)}.timeline-more{text-align:center;font-size:.75rem;color:#888;padding:.25rem 0}.phase-gate-view{height:100%;display:flex;align-items:center;justify-content:center;padding:1rem;overflow-x:auto}.phase-steps{display:flex;align-items:center;gap:.75rem;list-style:none;margin:0;padding:0}.phase-step{display:flex;align-items:center;gap:.5rem;padding:.4rem .75rem;border-radius:999px;background:#0000000a;font-size:.8rem;color:#555;white-space:nowrap}.phase-step-number{width:1.5rem;height:1.5rem;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;font-weight:700;background:#0000000f;color:#666}.phase-step.is-done{background:#22c55e1a;color:#16a34a}.phase-step.is-done .phase-step-number{background:#16a34a;color:#fff}.phase-step.is-current{background:#2563eb1a;color:#2563eb;font-weight:600}.phase-step.is-current .phase-step-number{background:#2563eb;color:#fff}.phase-gate-view.phase-gate-compact .phase-step{padding:.2rem .45rem;font-size:.7rem;gap:.25rem}.phase-gate-view.phase-gate-compact .phase-step-number{width:1.1rem;height:1.1rem;font-size:.7rem}\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", "density", "showHeader", "headerClickable", "isNoTopEnd", "isChart", "titleEditable"], outputs: ["headerClick", "titleChange"] }, { kind: "component", type: CardFilterComponent, selector: "mt-card-filter", inputs: ["config", "dashboardId"], outputs: ["filterChange"] }, { kind: "component", type: StaticFiltersComponent, selector: "mt-static-filters", inputs: ["config", "dashboardId"], outputs: ["filterChange"] }, { kind: "component", type: DynamicFiltersComponent, selector: "mt-dynamic-filters", inputs: ["fields", "dashboardId", "showLabels"], outputs: ["filterChange", "filtersChange"] }, { kind: "component", type: CardInfoComponent, selector: "mt-card-info", inputs: ["cardInfo"] }, { kind: "component", type: EChartComponent, selector: "mt-echart", inputs: ["dashboardId", "chartConfig", "configurationItem", "height", "inGroup", "headerHidden"], outputs: ["chartClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
25349
25349
  }
25350
25350
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ChartCardComponent, decorators: [{
25351
25351
  type: Component,
@@ -25358,7 +25358,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
25358
25358
  DynamicFiltersComponent,
25359
25359
  CardInfoComponent,
25360
25360
  EChartComponent,
25361
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<mt-card-content\n [inGroup]=\"inGroup()\"\n [headerConfig]=\"\n configurationItem()?.clientConfig?.configAsType?.['headerCardConfig']\n \"\n [styleConfig]=\"\n configurationItem()?.clientConfig?.displayConfig?.['StyleConfig']\n \"\n [cardStyleConfig]=\"\n configurationItem()?.clientConfig?.configAsType?.['cardStyleConfig']\n \"\n [density]=\"configurationItem()?.clientConfig?.configAsType?.['density']\"\n [title]=\"title()\"\n [titleEditable]=\"titleEditable()\"\n (titleChange)=\"onTitleChange.emit($event)\"\n [isNoTopEnd]=\"!showTopEnd()\"\n [isChart]=\"true\"\n (headerClick)=\"onHeaderClick()\"\n>\n <ng-container topEnd>\n @if (showStaticFilters()) {\n <mt-static-filters\n [config]=\"staticFilterConfig()\"\n [dashboardId]=\"dashboardId()\"\n >\n </mt-static-filters>\n }\n @if (showDynamicFilters()) {\n <mt-dynamic-filters\n [fields]=\"dynamicFilterFields()\"\n [dashboardId]=\"dashboardId()\"\n [showLabels]=\"false\"\n >\n </mt-dynamic-filters>\n }\n @if (showFilter()) {\n <mt-card-filter [config]=\"filterConfig()\" [dashboardId]=\"dashboardId()\">\n </mt-card-filter>\n }\n @if (cardInfoConfig()?.show && cardInfoConfig()?.value) {\n <mt-card-info [cardInfo]=\"cardInfoConfig()\"></mt-card-info>\n }\n </ng-container>\n\n <ng-container body>\n <div\n class=\"chart-container\"\n [class.config-mode]=\"isConfigMode()\"\n *transloco=\"let t; prefix: 'dashboard'\"\n >\n @if (componentViewMode() === \"splitter\" && hasSplitterData()) {\n <div\n class=\"splitter-view\"\n [class.splitter-vertical]=\"splitterOrientation() === 'vertical'\"\n [style.gap.px]=\"splitterGap()\"\n >\n <div class=\"splitter-side splitter-positive\">\n <div class=\"splitter-count\">\n {{ dataHandled()?.positive?.groupedValue ?? 0 }}\n </div>\n <ul class=\"splitter-labels\">\n @for (\n label of dataHandled()?.positive?.labels || [];\n track $index\n ) {\n <li>{{ label }}</li>\n }\n </ul>\n </div>\n <div class=\"splitter-divider\"></div>\n <div class=\"splitter-side splitter-negative\">\n <div class=\"splitter-count\">\n {{ dataHandled()?.negative?.groupedValue ?? 0 }}\n </div>\n <ul class=\"splitter-labels\">\n @for (\n label of dataHandled()?.negative?.labels || [];\n track $index\n ) {\n <li>{{ label }}</li>\n }\n </ul>\n </div>\n </div>\n } @else if (componentViewMode() === \"timeline\" && hasTimelineData()) {\n <div class=\"timeline-view\">\n @if (timelineLabels().length) {\n <div class=\"timeline-legend\">\n @for (label of timelineLabels(); track $index) {\n <span\n class=\"timeline-legend-chip\"\n [style.color]=\"label.color || '#666'\"\n [style.borderColor]=\"label.color || '#666'\"\n >\n {{ label.label }}\n </span>\n }\n </div>\n }\n <ul class=\"timeline-resources\">\n @for (res of timelineResources(); track res.id) {\n <li class=\"timeline-resource\">\n <div class=\"timeline-resource-title\">\n {{ res.title || res.id }}\n </div>\n @if (res.children?.length) {\n <ul class=\"timeline-children\">\n @for (child of res.children; track child.id) {\n <li>\n <div class=\"timeline-resource-title small\">\n {{ child.title || child.id }}\n </div>\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n <ul class=\"timeline-events\">\n @for (\n evt of timelineEventsLimited();\n track evt.resourceId + $index\n ) {\n <li class=\"timeline-event\">\n <span\n class=\"timeline-event-color\"\n [style.background]=\"evt.color || '#999'\"\n ></span>\n <span class=\"timeline-event-title\">{{ evt.title }}</span>\n <span class=\"timeline-event-range\"\n >{{ formatDate(evt.start) }} \u2192 {{ formatDate(evt.end) }}</span\n >\n @if (evt.progress !== null && evt.progress !== undefined) {\n <span class=\"timeline-event-progress\">\n <span\n class=\"bar\"\n [style.width.%]=\"\n evt.progress > 100\n ? 100\n : evt.progress < 0\n ? 0\n : evt.progress\n \"\n ></span>\n </span>\n }\n </li>\n }\n </ul>\n @if (timelineEventsTruncated()) {\n <div class=\"timeline-more\">\n + {{ timelineEvents().length - 50 }} more\n </div>\n }\n </div>\n } @else if (\n componentViewMode() === \"phaseGateStepper\" && hasPhaseGateData()\n ) {\n <div\n class=\"phase-gate-view\"\n [class.phase-gate-compact]=\"phaseGateCompact()\"\n >\n <ol class=\"phase-steps\">\n @for (step of phaseGateSteps(); track $index) {\n <li\n class=\"phase-step\"\n [class.is-current]=\"$index === phaseGateCurrentIndex()\"\n [class.is-done]=\"$index < phaseGateCurrentIndex()\"\n >\n <span class=\"phase-step-number\">{{ $index + 1 }}</span>\n @if (phaseGateShowLabels()) {\n <span class=\"phase-step-name\">{{\n step?.name || step?.title || step?.label || t(\"phase\")\n }}</span>\n }\n </li>\n }\n </ol>\n </div>\n } @else if (dataHandled() && !dataHandled()?.noData) {\n <mt-echart\n [dashboardId]=\"dashboardId()\"\n [chartConfig]=\"dataHandled()\"\n [configurationItem]=\"configurationItem()\"\n [inGroup]=\"inGroup()\"\n (chartClick)=\"onChartClick($event)\"\n >\n </mt-echart>\n } @else if (dataHandled()?.noData) {\n <div class=\"no-data-container\">\n <i class=\"pi pi-info-circle\"></i>\n <span>{{ t(\"noChartData\") }}</span>\n </div>\n } @else {\n <div class=\"loading-container\">\n <div class=\"loading-spinner\"></div>\n </div>\n }\n </div>\n </ng-container>\n</mt-card-content>\n", styles: [":host{display:block;height:100%}.chart-container{height:100%;min-height:300px}.chart-container.config-mode{min-height:400px}.no-data-container,.needs-config-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;min-height:200px;color:#999;gap:.5rem;padding:1rem;text-align:center}.no-data-container i,.needs-config-container i{font-size:2rem}.needs-config-container .title{font-weight:600;color:#555;font-size:.95rem}.needs-config-container .hint{font-size:.8rem;max-width:28rem;line-height:1.4}.needs-config-container .configure-btn{margin-top:.5rem;display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .9rem;font-size:.85rem;font-weight:600;color:#fff;background:var(--p-primary-500, #2563eb);border:0;border-radius:.375rem;cursor:pointer;box-shadow:0 1px 2px #0000000d}.needs-config-container .configure-btn:hover{background:var(--p-primary-600, #1d4ed8)}.loading-container{display:flex;align-items:center;justify-content:center;height:100%;min-height:200px}.loading-spinner{width:2rem;height:2rem;border:3px solid #f0f0f0;border-top-color:#4caf50;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.splitter-view{display:flex;align-items:stretch;height:100%;gap:.5rem}.splitter-view.splitter-vertical{flex-direction:column}.splitter-view.splitter-vertical .splitter-divider{width:auto;height:1px}.splitter-side{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:1rem;border-radius:.5rem}.splitter-positive{background:#22c55e14}.splitter-negative{background:#ef444414}.splitter-count{font-size:2rem;font-weight:700;margin-bottom:.5rem}.splitter-positive .splitter-count{color:#16a34a}.splitter-negative .splitter-count{color:#dc2626}.splitter-labels{list-style:none;padding:0;margin:0;text-align:center;font-size:.8rem;color:#555}.splitter-labels li{padding:.15rem 0}.splitter-divider{width:1px;background:#00000014}.timeline-view{display:flex;flex-direction:column;gap:.5rem;padding:.5rem;height:100%;overflow:auto}.timeline-legend{display:flex;flex-wrap:wrap;gap:.5rem}.timeline-legend-chip{padding:.15rem .5rem;border:1px solid;border-radius:999px;font-size:.75rem}.timeline-resources{list-style:none;padding:0;margin:0 0 .25rem}.timeline-resource{padding:.25rem 0;border-bottom:1px solid rgba(0,0,0,.04)}.timeline-resource-title{font-weight:600;font-size:.85rem;color:#333}.timeline-resource-title.small{font-weight:500;font-size:.8rem;color:#555}.timeline-children{list-style:none;margin:.25rem 0 0 1rem;padding:0}.timeline-events{list-style:none;padding:0;margin:0}.timeline-event{display:grid;grid-template-columns:.5rem 1fr auto auto;align-items:center;gap:.5rem;padding:.25rem 0;font-size:.8rem}.timeline-event-color{display:inline-block;width:8px;height:8px;border-radius:999px}.timeline-event-title{color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.timeline-event-range{color:#666;font-size:.7rem;white-space:nowrap}.timeline-event-progress{position:relative;width:4rem;height:6px;background:#0000000f;border-radius:999px;overflow:hidden}.timeline-event-progress .bar{position:absolute;inset:0;background:var(--p-primary-500, #2563eb)}.timeline-more{text-align:center;font-size:.75rem;color:#888;padding:.25rem 0}.phase-gate-view{height:100%;display:flex;align-items:center;justify-content:center;padding:1rem;overflow-x:auto}.phase-steps{display:flex;align-items:center;gap:.75rem;list-style:none;margin:0;padding:0}.phase-step{display:flex;align-items:center;gap:.5rem;padding:.4rem .75rem;border-radius:999px;background:#0000000a;font-size:.8rem;color:#555;white-space:nowrap}.phase-step-number{width:1.5rem;height:1.5rem;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;font-weight:700;background:#0000000f;color:#666}.phase-step.is-done{background:#22c55e1a;color:#16a34a}.phase-step.is-done .phase-step-number{background:#16a34a;color:#fff}.phase-step.is-current{background:#2563eb1a;color:#2563eb;font-weight:600}.phase-step.is-current .phase-step-number{background:#2563eb;color:#fff}.phase-gate-view.phase-gate-compact .phase-step{padding:.2rem .45rem;font-size:.7rem;gap:.25rem}.phase-gate-view.phase-gate-compact .phase-step-number{width:1.1rem;height:1.1rem;font-size:.7rem}\n"] }]
25361
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<mt-card-content\n [inGroup]=\"inGroup()\"\n [headerConfig]=\"\n configurationItem()?.clientConfig?.configAsType?.['headerCardConfig']\n \"\n [styleConfig]=\"\n configurationItem()?.clientConfig?.displayConfig?.['StyleConfig']\n \"\n [cardStyleConfig]=\"\n configurationItem()?.clientConfig?.configAsType?.['cardStyleConfig']\n \"\n [density]=\"configurationItem()?.clientConfig?.configAsType?.['density']\"\n [title]=\"title()\"\n [titleEditable]=\"titleEditable()\"\n (titleChange)=\"onTitleChange.emit($event)\"\n [isNoTopEnd]=\"!showTopEnd()\"\n [isChart]=\"true\"\n (headerClick)=\"onHeaderClick()\"\n>\n <ng-container topEnd>\n @if (showStaticFilters()) {\n <mt-static-filters\n [config]=\"staticFilterConfig()\"\n [dashboardId]=\"dashboardId()\"\n >\n </mt-static-filters>\n }\n @if (showDynamicFilters()) {\n <mt-dynamic-filters\n [fields]=\"dynamicFilterFields()\"\n [dashboardId]=\"dashboardId()\"\n [showLabels]=\"false\"\n >\n </mt-dynamic-filters>\n }\n @if (showFilter()) {\n <mt-card-filter [config]=\"filterConfig()\" [dashboardId]=\"dashboardId()\">\n </mt-card-filter>\n }\n @if (cardInfoConfig()?.show && cardInfoConfig()?.value) {\n <mt-card-info [cardInfo]=\"cardInfoConfig()\"></mt-card-info>\n }\n </ng-container>\n\n <ng-container body>\n <div\n class=\"chart-container\"\n [class.config-mode]=\"isConfigMode()\"\n *transloco=\"let t; prefix: 'dashboard'\"\n >\n @if (componentViewMode() === \"splitter\" && hasSplitterData()) {\n <div\n class=\"splitter-view\"\n [class.splitter-vertical]=\"splitterOrientation() === 'vertical'\"\n [style.gap.px]=\"splitterGap()\"\n >\n <div class=\"splitter-side splitter-positive\">\n <div class=\"splitter-count\">\n {{ dataHandled()?.positive?.groupedValue ?? 0 }}\n </div>\n <ul class=\"splitter-labels\">\n @for (\n label of dataHandled()?.positive?.labels || [];\n track $index\n ) {\n <li>{{ label }}</li>\n }\n </ul>\n </div>\n <div class=\"splitter-divider\"></div>\n <div class=\"splitter-side splitter-negative\">\n <div class=\"splitter-count\">\n {{ dataHandled()?.negative?.groupedValue ?? 0 }}\n </div>\n <ul class=\"splitter-labels\">\n @for (\n label of dataHandled()?.negative?.labels || [];\n track $index\n ) {\n <li>{{ label }}</li>\n }\n </ul>\n </div>\n </div>\n } @else if (componentViewMode() === \"timeline\" && hasTimelineData()) {\n <div class=\"timeline-view\">\n @if (timelineLabels().length) {\n <div class=\"timeline-legend\">\n @for (label of timelineLabels(); track $index) {\n <span\n class=\"timeline-legend-chip\"\n [style.color]=\"label.color || '#666'\"\n [style.borderColor]=\"label.color || '#666'\"\n >\n {{ label.label }}\n </span>\n }\n </div>\n }\n <ul class=\"timeline-resources\">\n @for (res of timelineResources(); track res.id) {\n <li class=\"timeline-resource\">\n <div class=\"timeline-resource-title\">\n {{ res.title || res.id }}\n </div>\n @if (res.children?.length) {\n <ul class=\"timeline-children\">\n @for (child of res.children; track child.id) {\n <li>\n <div class=\"timeline-resource-title small\">\n {{ child.title || child.id }}\n </div>\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n <ul class=\"timeline-events\">\n @for (\n evt of timelineEventsLimited();\n track evt.resourceId + $index\n ) {\n <li class=\"timeline-event\">\n <span\n class=\"timeline-event-color\"\n [style.background]=\"evt.color || '#999'\"\n ></span>\n <span class=\"timeline-event-title\">{{ evt.title }}</span>\n <span class=\"timeline-event-range\"\n >{{ formatDate(evt.start) }} \u2192 {{ formatDate(evt.end) }}</span\n >\n @if (evt.progress !== null && evt.progress !== undefined) {\n <span class=\"timeline-event-progress\">\n <span\n class=\"bar\"\n [style.width.%]=\"\n evt.progress > 100\n ? 100\n : evt.progress < 0\n ? 0\n : evt.progress\n \"\n ></span>\n </span>\n }\n </li>\n }\n </ul>\n @if (timelineEventsTruncated()) {\n <div class=\"timeline-more\">\n + {{ timelineEvents().length - 50 }} more\n </div>\n }\n </div>\n } @else if (\n componentViewMode() === \"phaseGateStepper\" && hasPhaseGateData()\n ) {\n <div\n class=\"phase-gate-view\"\n [class.phase-gate-compact]=\"phaseGateCompact()\"\n >\n <ol class=\"phase-steps\">\n @for (step of phaseGateSteps(); track $index) {\n <li\n class=\"phase-step\"\n [class.is-current]=\"$index === phaseGateCurrentIndex()\"\n [class.is-done]=\"$index < phaseGateCurrentIndex()\"\n >\n <span class=\"phase-step-number\">{{ $index + 1 }}</span>\n @if (phaseGateShowLabels()) {\n <span class=\"phase-step-name\">{{\n step?.name || step?.title || step?.label || t(\"phase\")\n }}</span>\n }\n </li>\n }\n </ol>\n </div>\n } @else if (dataHandled() && !dataHandled()?.noData) {\n <mt-echart\n [dashboardId]=\"dashboardId()\"\n [chartConfig]=\"dataHandled()\"\n [configurationItem]=\"configurationItem()\"\n [inGroup]=\"inGroup()\"\n (chartClick)=\"onChartClick($event)\"\n >\n </mt-echart>\n } @else if (dataHandled()?.noData) {\n <div class=\"no-data-container\">\n <i class=\"pi pi-info-circle\"></i>\n <span>{{ t(\"noChartData\") }}</span>\n </div>\n } @else {\n <div class=\"loading-container\">\n <div class=\"loading-spinner\"></div>\n </div>\n }\n </div>\n </ng-container>\n</mt-card-content>\n", styles: [":host{display:block;height:100%}.chart-container{height:100%;min-height:300px}.chart-container.config-mode{min-height:400px}.no-data-container,.needs-config-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;min-height:200px;color:#999;gap:.5rem;padding:1rem;text-align:center}.no-data-container i,.needs-config-container i{font-size:2rem}.needs-config-container .title{font-weight:600;color:#555;font-size:.95rem}.needs-config-container .hint{font-size:.8rem;max-width:28rem;line-height:1.4}.needs-config-container .configure-btn{margin-top:.5rem;display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .9rem;font-size:.85rem;font-weight:600;color:#fff;background:var(--p-primary-500, #2563eb);border:0;border-radius:.375rem;cursor:pointer;box-shadow:0 1px 2px #0000000d}.needs-config-container .configure-btn:hover{background:var(--p-primary-600, #1d4ed8)}.loading-container{display:flex;align-items:center;justify-content:center;height:100%;min-height:200px}.loading-spinner{width:2rem;height:2rem;border:3px solid #f0f0f0;border-top-color:#4caf50;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.splitter-view{display:flex;align-items:stretch;height:100%;gap:.5rem}.splitter-view.splitter-vertical{flex-direction:column}.splitter-view.splitter-vertical .splitter-divider{width:auto;height:1px}.splitter-side{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:1rem;border-radius:.5rem}.splitter-positive{background:#22c55e14}.splitter-negative{background:#ef444414}.splitter-count{font-size:2rem;font-weight:700;margin-bottom:.5rem}.splitter-positive .splitter-count{color:#16a34a}.splitter-negative .splitter-count{color:#dc2626}.splitter-labels{list-style:none;padding:0;margin:0;text-align:center;font-size:.8rem;color:#555}.splitter-labels li{padding:.15rem 0}.splitter-divider{width:1px;background:#00000014}.timeline-view{display:flex;flex-direction:column;gap:.5rem;padding:.5rem;height:100%;overflow:auto}.timeline-legend{display:flex;flex-wrap:wrap;gap:.5rem}.timeline-legend-chip{padding:.15rem .5rem;border:1px solid;border-radius:999px;font-size:.75rem}.timeline-resources{list-style:none;padding:0;margin:0 0 .25rem}.timeline-resource{padding:.25rem 0;border-bottom:1px solid rgba(0,0,0,.04)}.timeline-resource-title{font-weight:600;font-size:.85rem;color:#333}.timeline-resource-title.small{font-weight:500;font-size:.8rem;color:#555}.timeline-children{list-style:none;margin:.25rem 0 0 1rem;padding:0}.timeline-events{list-style:none;padding:0;margin:0}.timeline-event{display:grid;grid-template-columns:.5rem 1fr auto auto;align-items:center;gap:.5rem;padding:.25rem 0;font-size:.8rem}.timeline-event-color{display:inline-block;width:8px;height:8px;border-radius:999px}.timeline-event-title{color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.timeline-event-range{color:#666;font-size:.7rem;white-space:nowrap}.timeline-event-progress{position:relative;width:4rem;height:6px;background:#0000000f;border-radius:999px;overflow:hidden}.timeline-event-progress .bar{position:absolute;inset:0;background:var(--p-primary-500, #2563eb)}.timeline-more{text-align:center;font-size:.75rem;color:#888;padding:.25rem 0}.phase-gate-view{height:100%;display:flex;align-items:center;justify-content:center;padding:1rem;overflow-x:auto}.phase-steps{display:flex;align-items:center;gap:.75rem;list-style:none;margin:0;padding:0}.phase-step{display:flex;align-items:center;gap:.5rem;padding:.4rem .75rem;border-radius:999px;background:#0000000a;font-size:.8rem;color:#555;white-space:nowrap}.phase-step-number{width:1.5rem;height:1.5rem;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;font-weight:700;background:#0000000f;color:#666}.phase-step.is-done{background:#22c55e1a;color:#16a34a}.phase-step.is-done .phase-step-number{background:#16a34a;color:#fff}.phase-step.is-current{background:#2563eb1a;color:#2563eb;font-weight:600}.phase-step.is-current .phase-step-number{background:#2563eb;color:#fff}.phase-gate-view.phase-gate-compact .phase-step{padding:.2rem .45rem;font-size:.7rem;gap:.25rem}.phase-gate-view.phase-gate-compact .phase-step-number{width:1.1rem;height:1.1rem;font-size:.7rem}\n"] }]
25362
25362
  }], propDecorators: { dashboardId: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardId", required: true }] }], inGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "inGroup", required: false }] }], isConfigMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "isConfigMode", required: false }] }], titleEditable: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleEditable", required: false }] }], onAction: [{ type: i0.Output, args: ["onAction"] }], onConfigure: [{ type: i0.Output, args: ["onConfigure"] }], onTitleChange: [{ type: i0.Output, args: ["onTitleChange"] }] } });
25363
25363
 
25364
25364
  /**
@@ -26172,11 +26172,11 @@ class StatisticCardComponent {
26172
26172
  return this.iconByShortName.get(normalized) || this.fallbackIcon;
26173
26173
  }
26174
26174
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: StatisticCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
26175
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: StatisticCardComponent, isStandalone: true, selector: "mt-statistic-card", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, styleConfig: { classPropertyName: "styleConfig", publicName: "styleConfig", isSignal: true, isRequired: false, transformFunction: null }, configurationItem: { classPropertyName: "configurationItem", publicName: "configurationItem", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cardClick: "cardClick" }, ngImport: i0, template: "<div\n class=\"relative flex h-full w-full flex-col rounded-lg bg-white shadow-sm transition-all duration-150\"\n *transloco=\"let t; prefix: 'dashboardBuilder'\"\n [class.cursor-pointer]=\"config()?.isClickable\"\n [class.hover:shadow-md]=\"config()?.isClickable\"\n [class.hover:-translate-y-0.5]=\"config()?.isClickable\"\n [ngStyle]=\"cardStyles()\"\n (click)=\"onClick()\"\n>\n <!-- Card Info Icon (top right corner) -->\n @if (showCardInfo()) {\n <div\n class=\"absolute right-2 top-2 cursor-pointer text-gray-400 hover:text-gray-600\"\n [pTooltip]=\"cardInfoTooltip()\"\n tooltipPosition=\"top\"\n >\n <mt-icon icon=\"general.info-circle\" class=\"text-xl\" />\n </div>\n }\n\n <!-- Card body -->\n <div\n class=\"flex flex-1 items-center gap-3 px-4 py-5\"\n [ngStyle]=\"borderTopStyle()\"\n >\n <!-- Icon -->\n @if (config()?.icon) {\n <div\n class=\"flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg\"\n [ngStyle]=\"iconStyles()\"\n >\n <mt-icon [icon]=\"resolveIcon(config()?.icon)\" class=\"text-xl\" />\n </div>\n }\n\n <!-- Content -->\n <div class=\"min-w-0 flex-1\" [class.px-2]=\"!config()?.icon\">\n <div class=\"flex items-start justify-between\">\n <p class=\"mb-0 flex-grow\">\n <strong\n class=\"text-2xl font-bold leading-tight\"\n [ngStyle]=\"{ 'font-size': fontSizeValue() }\"\n [pTooltip]=\"tooltipValue()\"\n tooltipPosition=\"top\"\n [innerHTML]=\"config()?.value ?? '-'\"\n ></strong>\n </p>\n </div>\n <p\n class=\"mb-0 mt-1 text-sm text-gray-600\"\n [ngStyle]=\"{ 'font-size': fontSizeTitle() }\"\n >\n {{ config()?.title }}\n </p>\n @if (config()?.subTitle) {\n <p class=\"mb-0 mt-0.5 text-xs text-gray-400\">\n {{ config()?.subTitle }}\n </p>\n }\n </div>\n\n <!-- Extra data (phone/email) if present -->\n @if (config()?.data) {\n <div class=\"text-sm\">\n <div class=\"flex items-center gap-2\">\n <mt-icon icon=\"communication.phone\" class=\"text-base text-gray-500\" />\n <strong>{{ t(\"phone\") }} : </strong>\n <span>{{ config()?.data?.phone ?? \"-\" }}</span>\n </div>\n <div class=\"mt-2 flex items-center gap-2\">\n <mt-icon icon=\"communication.mail-01\" class=\"text-base text-gray-500\" />\n <strong>{{ t(\"email\") }} : </strong>\n <span>{{ config()?.data?.email ?? \"-\" }}</span>\n </div>\n </div>\n }\n </div>\n\n <!-- Hover cards (popHover) -->\n @if (config()?.popHover?.length) {\n <div\n class=\"flex cursor-pointer items-center justify-between gap-4 border-t px-4 py-4\"\n >\n @for (card of config()?.popHover; track $index) {\n <div class=\"text-center\">\n <div class=\"flex justify-center\">\n <img\n [src]=\"pathImages + card?.icon + '.svg'\"\n class=\"mx-2 h-10 w-10\"\n [alt]=\"card?.title\"\n />\n </div>\n <p class=\"mb-0 text-xs text-gray-500\">{{ card?.title }}</p>\n <p class=\"mb-0 font-semibold\">{{ card?.value }}</p>\n </div>\n }\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
26175
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: StatisticCardComponent, isStandalone: true, selector: "mt-statistic-card", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, styleConfig: { classPropertyName: "styleConfig", publicName: "styleConfig", isSignal: true, isRequired: false, transformFunction: null }, configurationItem: { classPropertyName: "configurationItem", publicName: "configurationItem", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cardClick: "cardClick" }, ngImport: i0, template: "<div\n class=\"relative flex h-full w-full flex-col rounded-lg bg-white shadow-sm transition-all duration-150\"\n *transloco=\"let t; prefix: 'dashboardBuilder'\"\n [class.cursor-pointer]=\"config()?.isClickable\"\n [class.hover:shadow-md]=\"config()?.isClickable\"\n [class.hover:-translate-y-0.5]=\"config()?.isClickable\"\n [ngStyle]=\"cardStyles()\"\n (click)=\"onClick()\"\n>\n <!-- Card Info Icon (top right corner) -->\n @if (showCardInfo()) {\n <div\n class=\"absolute right-2 top-2 cursor-pointer text-gray-400 hover:text-gray-600\"\n [pTooltip]=\"cardInfoTooltip()\"\n tooltipPosition=\"top\"\n >\n <mt-icon icon=\"general.info-circle\" class=\"text-xl\" />\n </div>\n }\n\n <!-- Card body -->\n <div\n class=\"flex flex-1 items-center gap-3 px-4 py-5\"\n [ngStyle]=\"borderTopStyle()\"\n >\n <!-- Icon -->\n @if (config()?.icon) {\n <div\n class=\"flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg\"\n [ngStyle]=\"iconStyles()\"\n >\n <mt-icon [icon]=\"resolveIcon(config()?.icon)\" class=\"text-xl\" />\n </div>\n }\n\n <!-- Content -->\n <div class=\"min-w-0 flex-1\" [class.px-2]=\"!config()?.icon\">\n <div class=\"flex items-start justify-between\">\n <p class=\"mb-0 flex-grow\">\n <strong\n class=\"text-2xl font-bold leading-tight\"\n [ngStyle]=\"{ 'font-size': fontSizeValue() }\"\n [pTooltip]=\"tooltipValue()\"\n tooltipPosition=\"top\"\n [innerHTML]=\"config()?.value ?? '-'\"\n ></strong>\n </p>\n </div>\n <p\n class=\"mb-0 mt-1 text-sm text-gray-600\"\n [ngStyle]=\"{ 'font-size': fontSizeTitle() }\"\n >\n {{ config()?.title }}\n </p>\n @if (config()?.subTitle) {\n <p class=\"mb-0 mt-0.5 text-xs text-gray-400\">\n {{ config()?.subTitle }}\n </p>\n }\n </div>\n\n <!-- Extra data (phone/email) if present -->\n @if (config()?.data) {\n <div class=\"text-sm\">\n <div class=\"flex items-center gap-2\">\n <mt-icon icon=\"communication.phone\" class=\"text-base text-gray-500\" />\n <strong>{{ t(\"phone\") }} : </strong>\n <span>{{ config()?.data?.phone ?? \"-\" }}</span>\n </div>\n <div class=\"mt-2 flex items-center gap-2\">\n <mt-icon\n icon=\"communication.mail-01\"\n class=\"text-base text-gray-500\"\n />\n <strong>{{ t(\"email\") }} : </strong>\n <span>{{ config()?.data?.email ?? \"-\" }}</span>\n </div>\n </div>\n }\n </div>\n\n <!-- Hover cards (popHover) -->\n @if (config()?.popHover?.length) {\n <div\n class=\"flex cursor-pointer items-center justify-between gap-4 border-t px-4 py-4\"\n >\n @for (card of config()?.popHover; track $index) {\n <div class=\"text-center\">\n <div class=\"flex justify-center\">\n <img\n [src]=\"pathImages + card?.icon + '.svg'\"\n class=\"mx-2 h-10 w-10\"\n [alt]=\"card?.title\"\n />\n </div>\n <p class=\"mb-0 text-xs text-gray-500\">{{ card?.title }}</p>\n <p class=\"mb-0 font-semibold\">{{ card?.value }}</p>\n </div>\n }\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
26176
26176
  }
26177
26177
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: StatisticCardComponent, decorators: [{
26178
26178
  type: Component,
26179
- args: [{ selector: 'mt-statistic-card', standalone: true, imports: [CommonModule, TranslocoDirective, TooltipModule, Icon], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"relative flex h-full w-full flex-col rounded-lg bg-white shadow-sm transition-all duration-150\"\n *transloco=\"let t; prefix: 'dashboardBuilder'\"\n [class.cursor-pointer]=\"config()?.isClickable\"\n [class.hover:shadow-md]=\"config()?.isClickable\"\n [class.hover:-translate-y-0.5]=\"config()?.isClickable\"\n [ngStyle]=\"cardStyles()\"\n (click)=\"onClick()\"\n>\n <!-- Card Info Icon (top right corner) -->\n @if (showCardInfo()) {\n <div\n class=\"absolute right-2 top-2 cursor-pointer text-gray-400 hover:text-gray-600\"\n [pTooltip]=\"cardInfoTooltip()\"\n tooltipPosition=\"top\"\n >\n <mt-icon icon=\"general.info-circle\" class=\"text-xl\" />\n </div>\n }\n\n <!-- Card body -->\n <div\n class=\"flex flex-1 items-center gap-3 px-4 py-5\"\n [ngStyle]=\"borderTopStyle()\"\n >\n <!-- Icon -->\n @if (config()?.icon) {\n <div\n class=\"flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg\"\n [ngStyle]=\"iconStyles()\"\n >\n <mt-icon [icon]=\"resolveIcon(config()?.icon)\" class=\"text-xl\" />\n </div>\n }\n\n <!-- Content -->\n <div class=\"min-w-0 flex-1\" [class.px-2]=\"!config()?.icon\">\n <div class=\"flex items-start justify-between\">\n <p class=\"mb-0 flex-grow\">\n <strong\n class=\"text-2xl font-bold leading-tight\"\n [ngStyle]=\"{ 'font-size': fontSizeValue() }\"\n [pTooltip]=\"tooltipValue()\"\n tooltipPosition=\"top\"\n [innerHTML]=\"config()?.value ?? '-'\"\n ></strong>\n </p>\n </div>\n <p\n class=\"mb-0 mt-1 text-sm text-gray-600\"\n [ngStyle]=\"{ 'font-size': fontSizeTitle() }\"\n >\n {{ config()?.title }}\n </p>\n @if (config()?.subTitle) {\n <p class=\"mb-0 mt-0.5 text-xs text-gray-400\">\n {{ config()?.subTitle }}\n </p>\n }\n </div>\n\n <!-- Extra data (phone/email) if present -->\n @if (config()?.data) {\n <div class=\"text-sm\">\n <div class=\"flex items-center gap-2\">\n <mt-icon icon=\"communication.phone\" class=\"text-base text-gray-500\" />\n <strong>{{ t(\"phone\") }} : </strong>\n <span>{{ config()?.data?.phone ?? \"-\" }}</span>\n </div>\n <div class=\"mt-2 flex items-center gap-2\">\n <mt-icon icon=\"communication.mail-01\" class=\"text-base text-gray-500\" />\n <strong>{{ t(\"email\") }} : </strong>\n <span>{{ config()?.data?.email ?? \"-\" }}</span>\n </div>\n </div>\n }\n </div>\n\n <!-- Hover cards (popHover) -->\n @if (config()?.popHover?.length) {\n <div\n class=\"flex cursor-pointer items-center justify-between gap-4 border-t px-4 py-4\"\n >\n @for (card of config()?.popHover; track $index) {\n <div class=\"text-center\">\n <div class=\"flex justify-center\">\n <img\n [src]=\"pathImages + card?.icon + '.svg'\"\n class=\"mx-2 h-10 w-10\"\n [alt]=\"card?.title\"\n />\n </div>\n <p class=\"mb-0 text-xs text-gray-500\">{{ card?.title }}</p>\n <p class=\"mb-0 font-semibold\">{{ card?.value }}</p>\n </div>\n }\n </div>\n }\n</div>\n" }]
26179
+ args: [{ selector: 'mt-statistic-card', standalone: true, imports: [CommonModule, TranslocoDirective, TooltipModule, Icon], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"relative flex h-full w-full flex-col rounded-lg bg-white shadow-sm transition-all duration-150\"\n *transloco=\"let t; prefix: 'dashboardBuilder'\"\n [class.cursor-pointer]=\"config()?.isClickable\"\n [class.hover:shadow-md]=\"config()?.isClickable\"\n [class.hover:-translate-y-0.5]=\"config()?.isClickable\"\n [ngStyle]=\"cardStyles()\"\n (click)=\"onClick()\"\n>\n <!-- Card Info Icon (top right corner) -->\n @if (showCardInfo()) {\n <div\n class=\"absolute right-2 top-2 cursor-pointer text-gray-400 hover:text-gray-600\"\n [pTooltip]=\"cardInfoTooltip()\"\n tooltipPosition=\"top\"\n >\n <mt-icon icon=\"general.info-circle\" class=\"text-xl\" />\n </div>\n }\n\n <!-- Card body -->\n <div\n class=\"flex flex-1 items-center gap-3 px-4 py-5\"\n [ngStyle]=\"borderTopStyle()\"\n >\n <!-- Icon -->\n @if (config()?.icon) {\n <div\n class=\"flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg\"\n [ngStyle]=\"iconStyles()\"\n >\n <mt-icon [icon]=\"resolveIcon(config()?.icon)\" class=\"text-xl\" />\n </div>\n }\n\n <!-- Content -->\n <div class=\"min-w-0 flex-1\" [class.px-2]=\"!config()?.icon\">\n <div class=\"flex items-start justify-between\">\n <p class=\"mb-0 flex-grow\">\n <strong\n class=\"text-2xl font-bold leading-tight\"\n [ngStyle]=\"{ 'font-size': fontSizeValue() }\"\n [pTooltip]=\"tooltipValue()\"\n tooltipPosition=\"top\"\n [innerHTML]=\"config()?.value ?? '-'\"\n ></strong>\n </p>\n </div>\n <p\n class=\"mb-0 mt-1 text-sm text-gray-600\"\n [ngStyle]=\"{ 'font-size': fontSizeTitle() }\"\n >\n {{ config()?.title }}\n </p>\n @if (config()?.subTitle) {\n <p class=\"mb-0 mt-0.5 text-xs text-gray-400\">\n {{ config()?.subTitle }}\n </p>\n }\n </div>\n\n <!-- Extra data (phone/email) if present -->\n @if (config()?.data) {\n <div class=\"text-sm\">\n <div class=\"flex items-center gap-2\">\n <mt-icon icon=\"communication.phone\" class=\"text-base text-gray-500\" />\n <strong>{{ t(\"phone\") }} : </strong>\n <span>{{ config()?.data?.phone ?? \"-\" }}</span>\n </div>\n <div class=\"mt-2 flex items-center gap-2\">\n <mt-icon\n icon=\"communication.mail-01\"\n class=\"text-base text-gray-500\"\n />\n <strong>{{ t(\"email\") }} : </strong>\n <span>{{ config()?.data?.email ?? \"-\" }}</span>\n </div>\n </div>\n }\n </div>\n\n <!-- Hover cards (popHover) -->\n @if (config()?.popHover?.length) {\n <div\n class=\"flex cursor-pointer items-center justify-between gap-4 border-t px-4 py-4\"\n >\n @for (card of config()?.popHover; track $index) {\n <div class=\"text-center\">\n <div class=\"flex justify-center\">\n <img\n [src]=\"pathImages + card?.icon + '.svg'\"\n class=\"mx-2 h-10 w-10\"\n [alt]=\"card?.title\"\n />\n </div>\n <p class=\"mb-0 text-xs text-gray-500\">{{ card?.title }}</p>\n <p class=\"mb-0 font-semibold\">{{ card?.value }}</p>\n </div>\n }\n </div>\n }\n</div>\n" }]
26180
26180
  }], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], styleConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleConfig", required: false }] }], configurationItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "configurationItem", required: false }] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }] } });
26181
26181
 
26182
26182
  /**
@@ -28510,7 +28510,9 @@ class FilterBuilderComponent {
28510
28510
  !isKnownField(chip.propertyKey)
28511
28511
  ) {
28512
28512
  <option [value]="chip.propertyKey">
28513
- {{ chip.propertyKey }} ({{ t('filterBuilder.customSuffix') }})
28513
+ {{ chip.propertyKey }} ({{
28514
+ t('filterBuilder.customSuffix')
28515
+ }})
28514
28516
  </option>
28515
28517
  }
28516
28518
  </select>
@@ -28572,7 +28574,11 @@ class FilterBuilderComponent {
28572
28574
  [ngModel]="!!chip.value"
28573
28575
  (ngModelChange)="setValue(chip, $event)"
28574
28576
  />
28575
- <span>{{ chip.value ? t('filterBuilder.trueValue') : t('filterBuilder.falseValue') }}</span>
28577
+ <span>{{
28578
+ chip.value
28579
+ ? t('filterBuilder.trueValue')
28580
+ : t('filterBuilder.falseValue')
28581
+ }}</span>
28576
28582
  </label>
28577
28583
  }
28578
28584
  @case ('number') {
@@ -28767,7 +28773,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
28767
28773
  !isKnownField(chip.propertyKey)
28768
28774
  ) {
28769
28775
  <option [value]="chip.propertyKey">
28770
- {{ chip.propertyKey }} ({{ t('filterBuilder.customSuffix') }})
28776
+ {{ chip.propertyKey }} ({{
28777
+ t('filterBuilder.customSuffix')
28778
+ }})
28771
28779
  </option>
28772
28780
  }
28773
28781
  </select>
@@ -28829,7 +28837,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
28829
28837
  [ngModel]="!!chip.value"
28830
28838
  (ngModelChange)="setValue(chip, $event)"
28831
28839
  />
28832
- <span>{{ chip.value ? t('filterBuilder.trueValue') : t('filterBuilder.falseValue') }}</span>
28840
+ <span>{{
28841
+ chip.value
28842
+ ? t('filterBuilder.trueValue')
28843
+ : t('filterBuilder.falseValue')
28844
+ }}</span>
28833
28845
  </label>
28834
28846
  }
28835
28847
  @case ('number') {
@@ -30066,7 +30078,7 @@ class ItemConfigPopoverComponent {
30066
30078
  return JSON.parse(JSON.stringify(item));
30067
30079
  }
30068
30080
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ItemConfigPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
30069
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ItemConfigPopoverComponent, isStandalone: true, selector: "mt-item-config-popover", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder.pass4'\">\n @if (item(); as currentItem) {\n <div class=\"db-config-popover\">\n <header class=\"db-config-popover__header\">\n <div class=\"db-config-popover__title-row\">\n <mt-icon\n [icon]=\"capability()?.icon ?? 'general.settings-02'\"\n class=\"db-config-popover__icon\"\n />\n <div class=\"db-config-popover__title\">\n <input\n type=\"text\"\n class=\"db-config-popover__title-input\"\n [ngModel]=\"ui()?.title?.title ?? ''\"\n (ngModelChange)=\"onTitleChange($event)\"\n [placeholder]=\"t('header.titlePlaceholder')\"\n />\n <span class=\"db-config-popover__chart-type\">\n {{ capability()?.name }}\n </span>\n </div>\n </div>\n <div class=\"db-config-popover__close\">\n <mt-button\n icon=\"general.x-close\"\n [tooltip]=\"t('header.close')\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n (onClick)=\"cancel()\"\n />\n </div>\n </header>\n\n <mt-tabs\n [options]=\"translatedTabs(t)\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [(active)]=\"activeTab\"\n class=\"db-config-popover__tabs\"\n />\n\n <main class=\"db-config-popover__main\">\n <!-- DATA TAB -->\n <section [hidden]=\"activeTab() !== 'data'\" class=\"db-config-section\">\n <mt-datasource-panel\n [item]=\"currentItem\"\n (serviceConfigChange)=\"onServiceConfigPatch($event)\"\n (openAdvanced)=\"openAdvancedEditor('dataSource')\"\n />\n\n @if (issuesByDomain().datasource.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().datasource; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n @if (issuesByDomain().fields.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().fields; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n </section>\n\n <!-- FILTERS TAB -->\n <section [hidden]=\"activeTab() !== 'filters'\" class=\"db-config-section\">\n <mt-filter-builder\n [chips]=\"chips()\"\n [persisted]=\"currentItem.config\"\n (chipsChange)=\"onChipsChange($event)\"\n />\n @if (issuesByDomain().filters.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().filters; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n </section>\n\n <!-- STYLE TAB -->\n <section [hidden]=\"activeTab() !== 'style'\" class=\"db-config-section\">\n <h4 class=\"db-config-section__title\">\n {{ t(\"style.presets\") }}\n </h4>\n <div class=\"db-style-presets\">\n @for (preset of presets(); track preset.key) {\n <button\n type=\"button\"\n class=\"db-style-preset\"\n [class.is-selected]=\"selectedPresetKey() === preset.key\"\n (click)=\"onApplyPreset(preset)\"\n >\n <span class=\"db-style-preset__label\">{{ preset.label }}</span>\n <span class=\"db-style-preset__desc\">{{\n preset.description\n }}</span>\n </button>\n }\n </div>\n\n <div\n class=\"db-config-section__actions db-config-section__actions--scope\"\n >\n <button\n type=\"button\"\n class=\"db-scope-toggle\"\n (click)=\"toggleControlScope()\"\n >\n <i\n [class]=\"\n 'mti mti-' +\n (controlScope() === 'advanced'\n ? 'general.eye'\n : 'general.eye-off')\n \"\n ></i>\n <span>\n {{\n controlScope() === \"advanced\"\n ? t(\"controls.simpleMode\")\n : t(\"controls.advancedMode\")\n }}\n </span>\n </button>\n </div>\n\n <mt-schema-control-renderer\n [schema]=\"schema()\"\n [persisted]=\"persistedConfig()\"\n [capability]=\"capability()\"\n [scope]=\"controlScope()\"\n (patch)=\"onSchemaPatch($event)\"\n />\n\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('style.openLegacyDrawer')\"\n icon=\"general.settings-02\"\n severity=\"secondary\"\n size=\"small\"\n [outlined]=\"true\"\n (onClick)=\"openChartSettingsDrawer()\"\n />\n </div>\n </section>\n\n <!-- BEHAVIOR TAB -->\n <section\n [hidden]=\"activeTab() !== 'behavior'\"\n class=\"db-config-section\"\n >\n <h4 class=\"db-config-section__title\">\n {{ t(\"behavior.title\") }}\n </h4>\n <label class=\"db-style-row\">\n <span>\n {{ t(\"behavior.showFiltersBar\") }}\n </span>\n <input\n type=\"checkbox\"\n [checked]=\"ui()?.behavior?.filtersBarVisible || false\"\n (change)=\"onShowFiltersBarChange($any($event.target).checked)\"\n />\n </label>\n <p class=\"db-config-summary__hint\">\n {{\n t(\"behavior.actionsCount\")\n }}\n ({{ ui()?.behavior?.cardActions || 0 }})\n </p>\n\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('behavior.openAdvanced')\"\n icon=\"general.cursor-click-02\"\n severity=\"help\"\n size=\"small\"\n [outlined]=\"true\"\n (onClick)=\"openAdvancedEditor('actions')\"\n />\n </div>\n </section>\n\n <!-- ADVANCED TAB -->\n <section\n [hidden]=\"activeTab() !== 'advanced'\"\n class=\"db-config-section\"\n >\n <p class=\"db-config-summary__hint\">\n {{ t(\"advanced.hint\") }}\n </p>\n\n <mt-schema-control-renderer\n [schema]=\"schema()\"\n [persisted]=\"persistedConfig()\"\n [capability]=\"capability()\"\n scope=\"advanced\"\n (patch)=\"onSchemaPatch($event)\"\n />\n\n <h4 class=\"db-config-section__title\">\n {{ t(\"advanced.json\") }}\n </h4>\n <textarea\n class=\"db-config-advanced-editor\"\n rows=\"14\"\n spellcheck=\"false\"\n (focus)=\"initAdvancedJsonIfNeeded()\"\n [value]=\"advancedJsonDraft()\"\n (input)=\"onAdvancedJsonChange($any($event.target).value)\"\n ></textarea>\n @if (advancedJsonError()) {\n <p class=\"db-config-advanced-editor__error\">\n {{ advancedJsonError() }}\n </p>\n }\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('advanced.apply')\"\n icon=\"general.check\"\n severity=\"primary\"\n size=\"small\"\n (onClick)=\"applyAdvancedJson()\"\n />\n </div>\n </section>\n </main>\n\n <footer class=\"db-config-popover__footer\">\n @if (hasErrors()) {\n <span\n class=\"db-config-popover__status db-config-popover__status--error\"\n >\n <mt-icon icon=\"general.alert-circle\" class=\"text-base\" />\n {{ t(\"footer.errors\") }}\n </span>\n } @else {\n <span class=\"db-config-popover__status db-config-popover__status--ok\">\n <mt-icon icon=\"general.check-circle\" class=\"text-base\" />\n {{ t(\"footer.ready\") }}\n </span>\n }\n <mt-button\n [label]=\"t('footer.cancel')\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"cancel()\"\n />\n <mt-button\n [label]=\"t('footer.apply')\"\n icon=\"general.check\"\n severity=\"primary\"\n [disabled]=\"hasErrors()\"\n (onClick)=\"apply()\"\n />\n </footer>\n </div>\n }\n</ng-container>\n", styles: [".db-config-popover{display:flex;flex-direction:column;height:100%;width:100%;background:var(--p-surface-0, #fff)}.db-config-popover__header{display:flex;align-items:flex-start;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--p-surface-200, #e5e7eb);gap:.75rem}.db-config-popover__title-row{display:flex;align-items:center;gap:.5rem;min-width:0;flex:1}.db-config-popover__icon{color:var(--p-primary-600, #2563eb);font-size:1.4rem}.db-config-popover__title{display:flex;flex-direction:column;gap:.15rem;min-width:0;flex:1}.db-config-popover__title-input{background:transparent;border:0;border-bottom:1px dashed transparent;font-size:.95rem;font-weight:600;color:var(--p-text-color, #1f2937);padding:.05rem 0;width:100%;outline:none}.db-config-popover__title-input:hover{border-bottom-color:var(--p-surface-300, #d1d5db)}.db-config-popover__title-input:focus{border-bottom-color:var(--p-primary-500, #3b82f6)}.db-config-popover__chart-type{font-size:.7rem;color:var(--p-text-muted-color, #6b7280);text-transform:uppercase;letter-spacing:.05em}.db-config-popover__close{flex-shrink:0}.db-config-popover__tabs{border-bottom:1px solid var(--p-surface-200, #e5e7eb);padding:0 1rem}.db-config-popover__main{flex:1 1 0;min-height:0;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:1rem}.db-config-popover__footer{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem;border-top:1px solid var(--p-surface-200, #e5e7eb);background:var(--p-surface-0, #fff);position:sticky;bottom:0}.db-config-popover__status{flex:1;display:inline-flex;align-items:center;gap:.4rem;font-size:.8rem}.db-config-popover__status--ok{color:var(--p-green-600, #16a34a)}.db-config-popover__status--error{color:var(--p-amber-600, #d97706)}.db-config-section{display:flex;flex-direction:column;gap:.75rem}.db-config-section__title{font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--p-text-muted-color, #6b7280);margin:0}.db-config-section__actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.5rem;border-top:1px dashed var(--p-surface-200, #e5e7eb)}.db-config-summary{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.4rem}.db-config-summary__row{display:grid;grid-template-columns:7rem 1fr;align-items:start;gap:.75rem;font-size:.8rem}.db-config-summary__key{color:var(--p-text-muted-color, #6b7280);font-weight:500}.db-config-summary__value{color:var(--p-text-color, #1f2937);word-break:break-word;display:inline-flex;flex-wrap:wrap;gap:.25rem}.db-config-summary__value--ok{color:var(--p-green-600, #16a34a);font-weight:600}.db-config-summary__value--missing{color:var(--p-amber-600, #d97706);font-weight:600}.db-config-summary__formula{background:var(--p-surface-50, #f9fafb);padding:.25rem .4rem;border-radius:.25rem;font-size:.75rem;word-break:break-all}.db-config-summary__hint{font-size:.78rem;color:var(--p-text-muted-color, #6b7280);margin:0}.db-config-chip{background:var(--p-surface-100, #f3f4f6);border:1px solid var(--p-surface-200, #e5e7eb);padding:.1rem .5rem;border-radius:999px;font-size:.75rem;color:var(--p-text-color, #1f2937)}.db-config-issues{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.25rem}.db-config-issue{display:flex;align-items:center;gap:.4rem;font-size:.78rem;padding:.4rem .55rem;border-radius:.4rem}.db-config-issue i{font-size:.85rem}.db-config-issue[data-severity=error]{color:var(--p-red-700, #b91c1c);background:var(--p-red-50, #fef2f2);border:1px solid var(--p-red-100, #fee2e2)}.db-config-issue[data-severity=warning]{color:var(--p-amber-700, #b45309);background:var(--p-amber-50, #fffbeb);border:1px solid var(--p-amber-100, #fef3c7)}.db-config-issue[data-severity=info]{color:var(--p-blue-700, #1d4ed8);background:var(--p-blue-50, #eff6ff);border:1px solid var(--p-blue-100, #dbeafe)}.db-style-presets{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:.5rem}.db-style-preset{display:flex;flex-direction:column;gap:.2rem;padding:.6rem .7rem;background:var(--p-surface-0, #fff);border:1px solid var(--p-surface-200, #e5e7eb);border-radius:.5rem;text-align:left;cursor:pointer;transition:all .12s ease}.db-style-preset:hover{border-color:var(--p-primary-300, #93c5fd);background:var(--p-primary-50, #eff6ff)}.db-style-preset.is-selected{border-color:var(--p-primary-500, #3b82f6);background:var(--p-primary-50, #eff6ff);box-shadow:0 0 0 2px var(--p-primary-100, #dbeafe)}.db-style-preset__label{font-size:.85rem;font-weight:600;color:var(--p-text-color, #1f2937)}.db-style-preset__desc{font-size:.72rem;color:var(--p-text-muted-color, #6b7280)}.db-style-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.5rem .75rem}.db-style-row{display:grid;grid-template-columns:1fr auto;align-items:center;gap:.5rem;font-size:.8rem;color:var(--p-text-color, #1f2937)}.db-style-row input[type=color]{width:2.5rem;height:1.6rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;cursor:pointer;background:transparent;padding:0}.db-style-row input[type=number]{width:4.5rem;padding:.25rem .4rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;font-size:.78rem}.db-style-row input[type=checkbox]{width:1rem;height:1rem}.db-style-select{padding:.25rem .4rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;font-size:.78rem;background:var(--p-surface-0, #fff)}.db-config-section__actions--scope{border-top:0;padding-top:0;justify-content:flex-end}.db-scope-toggle{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .55rem;font-size:.75rem;border-radius:999px;border:1px solid var(--p-surface-300, #d1d5db);background:var(--p-surface-0, #fff);color:var(--p-text-color, #1f2937);cursor:pointer}.db-scope-toggle:hover{background:var(--p-surface-100, #f3f4f6)}.db-config-advanced-editor{width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.75rem;padding:.6rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.4rem;background:var(--p-surface-50, #f9fafb);resize:vertical}.db-config-advanced-editor__error{color:var(--p-red-600, #dc2626);font-size:.78rem;margin:0}@media(max-width:768px){.db-config-popover__header{padding:.6rem .75rem}.db-config-popover__main{padding:.75rem}.db-style-grid,.db-style-presets,.db-config-summary__row{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "defaultIcon", "size", "fluid", "disabled", "searchThreshold"], outputs: ["activeChange", "onChange"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: FilterBuilderComponent, selector: "mt-filter-builder", inputs: ["chips", "persisted"], outputs: ["chipsChange"] }, { kind: "component", type: DatasourcePanelComponent, selector: "mt-datasource-panel", inputs: ["item"], outputs: ["serviceConfigChange", "openAdvanced"] }, { kind: "component", type: SchemaControlRendererComponent, selector: "mt-schema-control-renderer", inputs: ["schema", "persisted", "capability", "scope"], outputs: ["patch"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
30081
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ItemConfigPopoverComponent, isStandalone: true, selector: "mt-item-config-popover", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder.pass4'\">\n @if (item(); as currentItem) {\n <div class=\"db-config-popover\">\n <header class=\"db-config-popover__header\">\n <div class=\"db-config-popover__title-row\">\n <mt-icon\n [icon]=\"capability()?.icon ?? 'general.settings-02'\"\n class=\"db-config-popover__icon\"\n />\n <div class=\"db-config-popover__title\">\n <input\n type=\"text\"\n class=\"db-config-popover__title-input\"\n [ngModel]=\"ui()?.title?.title ?? ''\"\n (ngModelChange)=\"onTitleChange($event)\"\n [placeholder]=\"t('header.titlePlaceholder')\"\n />\n <span class=\"db-config-popover__chart-type\">\n {{ capability()?.name }}\n </span>\n </div>\n </div>\n <div class=\"db-config-popover__close\">\n <mt-button\n icon=\"general.x-close\"\n [tooltip]=\"t('header.close')\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n (onClick)=\"cancel()\"\n />\n </div>\n </header>\n\n <mt-tabs\n [options]=\"translatedTabs(t)\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [(active)]=\"activeTab\"\n class=\"db-config-popover__tabs\"\n />\n\n <main class=\"db-config-popover__main\">\n <!-- DATA TAB -->\n <section [hidden]=\"activeTab() !== 'data'\" class=\"db-config-section\">\n <mt-datasource-panel\n [item]=\"currentItem\"\n (serviceConfigChange)=\"onServiceConfigPatch($event)\"\n (openAdvanced)=\"openAdvancedEditor('dataSource')\"\n />\n\n @if (issuesByDomain().datasource.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().datasource; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n @if (issuesByDomain().fields.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().fields; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n </section>\n\n <!-- FILTERS TAB -->\n <section [hidden]=\"activeTab() !== 'filters'\" class=\"db-config-section\">\n <mt-filter-builder\n [chips]=\"chips()\"\n [persisted]=\"currentItem.config\"\n (chipsChange)=\"onChipsChange($event)\"\n />\n @if (issuesByDomain().filters.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().filters; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n </section>\n\n <!-- STYLE TAB -->\n <section [hidden]=\"activeTab() !== 'style'\" class=\"db-config-section\">\n <h4 class=\"db-config-section__title\">\n {{ t(\"style.presets\") }}\n </h4>\n <div class=\"db-style-presets\">\n @for (preset of presets(); track preset.key) {\n <button\n type=\"button\"\n class=\"db-style-preset\"\n [class.is-selected]=\"selectedPresetKey() === preset.key\"\n (click)=\"onApplyPreset(preset)\"\n >\n <span class=\"db-style-preset__label\">{{ preset.label }}</span>\n <span class=\"db-style-preset__desc\">{{\n preset.description\n }}</span>\n </button>\n }\n </div>\n\n <div\n class=\"db-config-section__actions db-config-section__actions--scope\"\n >\n <button\n type=\"button\"\n class=\"db-scope-toggle\"\n (click)=\"toggleControlScope()\"\n >\n <i\n [class]=\"\n 'mti mti-' +\n (controlScope() === 'advanced'\n ? 'general.eye'\n : 'general.eye-off')\n \"\n ></i>\n <span>\n {{\n controlScope() === \"advanced\"\n ? t(\"controls.simpleMode\")\n : t(\"controls.advancedMode\")\n }}\n </span>\n </button>\n </div>\n\n <mt-schema-control-renderer\n [schema]=\"schema()\"\n [persisted]=\"persistedConfig()\"\n [capability]=\"capability()\"\n [scope]=\"controlScope()\"\n (patch)=\"onSchemaPatch($event)\"\n />\n\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('style.openLegacyDrawer')\"\n icon=\"general.settings-02\"\n severity=\"secondary\"\n size=\"small\"\n [outlined]=\"true\"\n (onClick)=\"openChartSettingsDrawer()\"\n />\n </div>\n </section>\n\n <!-- BEHAVIOR TAB -->\n <section\n [hidden]=\"activeTab() !== 'behavior'\"\n class=\"db-config-section\"\n >\n <h4 class=\"db-config-section__title\">\n {{ t(\"behavior.title\") }}\n </h4>\n <label class=\"db-style-row\">\n <span>\n {{ t(\"behavior.showFiltersBar\") }}\n </span>\n <input\n type=\"checkbox\"\n [checked]=\"ui()?.behavior?.filtersBarVisible || false\"\n (change)=\"onShowFiltersBarChange($any($event.target).checked)\"\n />\n </label>\n <p class=\"db-config-summary__hint\">\n {{ t(\"behavior.actionsCount\") }}\n ({{ ui()?.behavior?.cardActions || 0 }})\n </p>\n\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('behavior.openAdvanced')\"\n icon=\"general.cursor-click-02\"\n severity=\"help\"\n size=\"small\"\n [outlined]=\"true\"\n (onClick)=\"openAdvancedEditor('actions')\"\n />\n </div>\n </section>\n\n <!-- ADVANCED TAB -->\n <section\n [hidden]=\"activeTab() !== 'advanced'\"\n class=\"db-config-section\"\n >\n <p class=\"db-config-summary__hint\">\n {{ t(\"advanced.hint\") }}\n </p>\n\n <mt-schema-control-renderer\n [schema]=\"schema()\"\n [persisted]=\"persistedConfig()\"\n [capability]=\"capability()\"\n scope=\"advanced\"\n (patch)=\"onSchemaPatch($event)\"\n />\n\n <h4 class=\"db-config-section__title\">\n {{ t(\"advanced.json\") }}\n </h4>\n <textarea\n class=\"db-config-advanced-editor\"\n rows=\"14\"\n spellcheck=\"false\"\n (focus)=\"initAdvancedJsonIfNeeded()\"\n [value]=\"advancedJsonDraft()\"\n (input)=\"onAdvancedJsonChange($any($event.target).value)\"\n ></textarea>\n @if (advancedJsonError()) {\n <p class=\"db-config-advanced-editor__error\">\n {{ advancedJsonError() }}\n </p>\n }\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('advanced.apply')\"\n icon=\"general.check\"\n severity=\"primary\"\n size=\"small\"\n (onClick)=\"applyAdvancedJson()\"\n />\n </div>\n </section>\n </main>\n\n <footer class=\"db-config-popover__footer\">\n @if (hasErrors()) {\n <span\n class=\"db-config-popover__status db-config-popover__status--error\"\n >\n <mt-icon icon=\"general.alert-circle\" class=\"text-base\" />\n {{ t(\"footer.errors\") }}\n </span>\n } @else {\n <span class=\"db-config-popover__status db-config-popover__status--ok\">\n <mt-icon icon=\"general.check-circle\" class=\"text-base\" />\n {{ t(\"footer.ready\") }}\n </span>\n }\n <mt-button\n [label]=\"t('footer.cancel')\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"cancel()\"\n />\n <mt-button\n [label]=\"t('footer.apply')\"\n icon=\"general.check\"\n severity=\"primary\"\n [disabled]=\"hasErrors()\"\n (onClick)=\"apply()\"\n />\n </footer>\n </div>\n }\n</ng-container>\n", styles: [".db-config-popover{display:flex;flex-direction:column;height:100%;width:100%;background:var(--p-surface-0, #fff)}.db-config-popover__header{display:flex;align-items:flex-start;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--p-surface-200, #e5e7eb);gap:.75rem}.db-config-popover__title-row{display:flex;align-items:center;gap:.5rem;min-width:0;flex:1}.db-config-popover__icon{color:var(--p-primary-600, #2563eb);font-size:1.4rem}.db-config-popover__title{display:flex;flex-direction:column;gap:.15rem;min-width:0;flex:1}.db-config-popover__title-input{background:transparent;border:0;border-bottom:1px dashed transparent;font-size:.95rem;font-weight:600;color:var(--p-text-color, #1f2937);padding:.05rem 0;width:100%;outline:none}.db-config-popover__title-input:hover{border-bottom-color:var(--p-surface-300, #d1d5db)}.db-config-popover__title-input:focus{border-bottom-color:var(--p-primary-500, #3b82f6)}.db-config-popover__chart-type{font-size:.7rem;color:var(--p-text-muted-color, #6b7280);text-transform:uppercase;letter-spacing:.05em}.db-config-popover__close{flex-shrink:0}.db-config-popover__tabs{border-bottom:1px solid var(--p-surface-200, #e5e7eb);padding:0 1rem}.db-config-popover__main{flex:1 1 0;min-height:0;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:1rem}.db-config-popover__footer{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem;border-top:1px solid var(--p-surface-200, #e5e7eb);background:var(--p-surface-0, #fff);position:sticky;bottom:0}.db-config-popover__status{flex:1;display:inline-flex;align-items:center;gap:.4rem;font-size:.8rem}.db-config-popover__status--ok{color:var(--p-green-600, #16a34a)}.db-config-popover__status--error{color:var(--p-amber-600, #d97706)}.db-config-section{display:flex;flex-direction:column;gap:.75rem}.db-config-section__title{font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--p-text-muted-color, #6b7280);margin:0}.db-config-section__actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.5rem;border-top:1px dashed var(--p-surface-200, #e5e7eb)}.db-config-summary{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.4rem}.db-config-summary__row{display:grid;grid-template-columns:7rem 1fr;align-items:start;gap:.75rem;font-size:.8rem}.db-config-summary__key{color:var(--p-text-muted-color, #6b7280);font-weight:500}.db-config-summary__value{color:var(--p-text-color, #1f2937);word-break:break-word;display:inline-flex;flex-wrap:wrap;gap:.25rem}.db-config-summary__value--ok{color:var(--p-green-600, #16a34a);font-weight:600}.db-config-summary__value--missing{color:var(--p-amber-600, #d97706);font-weight:600}.db-config-summary__formula{background:var(--p-surface-50, #f9fafb);padding:.25rem .4rem;border-radius:.25rem;font-size:.75rem;word-break:break-all}.db-config-summary__hint{font-size:.78rem;color:var(--p-text-muted-color, #6b7280);margin:0}.db-config-chip{background:var(--p-surface-100, #f3f4f6);border:1px solid var(--p-surface-200, #e5e7eb);padding:.1rem .5rem;border-radius:999px;font-size:.75rem;color:var(--p-text-color, #1f2937)}.db-config-issues{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.25rem}.db-config-issue{display:flex;align-items:center;gap:.4rem;font-size:.78rem;padding:.4rem .55rem;border-radius:.4rem}.db-config-issue i{font-size:.85rem}.db-config-issue[data-severity=error]{color:var(--p-red-700, #b91c1c);background:var(--p-red-50, #fef2f2);border:1px solid var(--p-red-100, #fee2e2)}.db-config-issue[data-severity=warning]{color:var(--p-amber-700, #b45309);background:var(--p-amber-50, #fffbeb);border:1px solid var(--p-amber-100, #fef3c7)}.db-config-issue[data-severity=info]{color:var(--p-blue-700, #1d4ed8);background:var(--p-blue-50, #eff6ff);border:1px solid var(--p-blue-100, #dbeafe)}.db-style-presets{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:.5rem}.db-style-preset{display:flex;flex-direction:column;gap:.2rem;padding:.6rem .7rem;background:var(--p-surface-0, #fff);border:1px solid var(--p-surface-200, #e5e7eb);border-radius:.5rem;text-align:left;cursor:pointer;transition:all .12s ease}.db-style-preset:hover{border-color:var(--p-primary-300, #93c5fd);background:var(--p-primary-50, #eff6ff)}.db-style-preset.is-selected{border-color:var(--p-primary-500, #3b82f6);background:var(--p-primary-50, #eff6ff);box-shadow:0 0 0 2px var(--p-primary-100, #dbeafe)}.db-style-preset__label{font-size:.85rem;font-weight:600;color:var(--p-text-color, #1f2937)}.db-style-preset__desc{font-size:.72rem;color:var(--p-text-muted-color, #6b7280)}.db-style-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.5rem .75rem}.db-style-row{display:grid;grid-template-columns:1fr auto;align-items:center;gap:.5rem;font-size:.8rem;color:var(--p-text-color, #1f2937)}.db-style-row input[type=color]{width:2.5rem;height:1.6rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;cursor:pointer;background:transparent;padding:0}.db-style-row input[type=number]{width:4.5rem;padding:.25rem .4rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;font-size:.78rem}.db-style-row input[type=checkbox]{width:1rem;height:1rem}.db-style-select{padding:.25rem .4rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;font-size:.78rem;background:var(--p-surface-0, #fff)}.db-config-section__actions--scope{border-top:0;padding-top:0;justify-content:flex-end}.db-scope-toggle{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .55rem;font-size:.75rem;border-radius:999px;border:1px solid var(--p-surface-300, #d1d5db);background:var(--p-surface-0, #fff);color:var(--p-text-color, #1f2937);cursor:pointer}.db-scope-toggle:hover{background:var(--p-surface-100, #f3f4f6)}.db-config-advanced-editor{width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.75rem;padding:.6rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.4rem;background:var(--p-surface-50, #f9fafb);resize:vertical}.db-config-advanced-editor__error{color:var(--p-red-600, #dc2626);font-size:.78rem;margin:0}@media(max-width:768px){.db-config-popover__header{padding:.6rem .75rem}.db-config-popover__main{padding:.75rem}.db-style-grid,.db-style-presets,.db-config-summary__row{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "defaultIcon", "size", "fluid", "disabled", "searchThreshold"], outputs: ["activeChange", "onChange"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: FilterBuilderComponent, selector: "mt-filter-builder", inputs: ["chips", "persisted"], outputs: ["chipsChange"] }, { kind: "component", type: DatasourcePanelComponent, selector: "mt-datasource-panel", inputs: ["item"], outputs: ["serviceConfigChange", "openAdvanced"] }, { kind: "component", type: SchemaControlRendererComponent, selector: "mt-schema-control-renderer", inputs: ["schema", "persisted", "capability", "scope"], outputs: ["patch"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
30070
30082
  }
30071
30083
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ItemConfigPopoverComponent, decorators: [{
30072
30084
  type: Component,
@@ -30080,7 +30092,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
30080
30092
  FilterBuilderComponent,
30081
30093
  DatasourcePanelComponent,
30082
30094
  SchemaControlRendererComponent,
30083
- ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder.pass4'\">\n @if (item(); as currentItem) {\n <div class=\"db-config-popover\">\n <header class=\"db-config-popover__header\">\n <div class=\"db-config-popover__title-row\">\n <mt-icon\n [icon]=\"capability()?.icon ?? 'general.settings-02'\"\n class=\"db-config-popover__icon\"\n />\n <div class=\"db-config-popover__title\">\n <input\n type=\"text\"\n class=\"db-config-popover__title-input\"\n [ngModel]=\"ui()?.title?.title ?? ''\"\n (ngModelChange)=\"onTitleChange($event)\"\n [placeholder]=\"t('header.titlePlaceholder')\"\n />\n <span class=\"db-config-popover__chart-type\">\n {{ capability()?.name }}\n </span>\n </div>\n </div>\n <div class=\"db-config-popover__close\">\n <mt-button\n icon=\"general.x-close\"\n [tooltip]=\"t('header.close')\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n (onClick)=\"cancel()\"\n />\n </div>\n </header>\n\n <mt-tabs\n [options]=\"translatedTabs(t)\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [(active)]=\"activeTab\"\n class=\"db-config-popover__tabs\"\n />\n\n <main class=\"db-config-popover__main\">\n <!-- DATA TAB -->\n <section [hidden]=\"activeTab() !== 'data'\" class=\"db-config-section\">\n <mt-datasource-panel\n [item]=\"currentItem\"\n (serviceConfigChange)=\"onServiceConfigPatch($event)\"\n (openAdvanced)=\"openAdvancedEditor('dataSource')\"\n />\n\n @if (issuesByDomain().datasource.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().datasource; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n @if (issuesByDomain().fields.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().fields; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n </section>\n\n <!-- FILTERS TAB -->\n <section [hidden]=\"activeTab() !== 'filters'\" class=\"db-config-section\">\n <mt-filter-builder\n [chips]=\"chips()\"\n [persisted]=\"currentItem.config\"\n (chipsChange)=\"onChipsChange($event)\"\n />\n @if (issuesByDomain().filters.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().filters; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n </section>\n\n <!-- STYLE TAB -->\n <section [hidden]=\"activeTab() !== 'style'\" class=\"db-config-section\">\n <h4 class=\"db-config-section__title\">\n {{ t(\"style.presets\") }}\n </h4>\n <div class=\"db-style-presets\">\n @for (preset of presets(); track preset.key) {\n <button\n type=\"button\"\n class=\"db-style-preset\"\n [class.is-selected]=\"selectedPresetKey() === preset.key\"\n (click)=\"onApplyPreset(preset)\"\n >\n <span class=\"db-style-preset__label\">{{ preset.label }}</span>\n <span class=\"db-style-preset__desc\">{{\n preset.description\n }}</span>\n </button>\n }\n </div>\n\n <div\n class=\"db-config-section__actions db-config-section__actions--scope\"\n >\n <button\n type=\"button\"\n class=\"db-scope-toggle\"\n (click)=\"toggleControlScope()\"\n >\n <i\n [class]=\"\n 'mti mti-' +\n (controlScope() === 'advanced'\n ? 'general.eye'\n : 'general.eye-off')\n \"\n ></i>\n <span>\n {{\n controlScope() === \"advanced\"\n ? t(\"controls.simpleMode\")\n : t(\"controls.advancedMode\")\n }}\n </span>\n </button>\n </div>\n\n <mt-schema-control-renderer\n [schema]=\"schema()\"\n [persisted]=\"persistedConfig()\"\n [capability]=\"capability()\"\n [scope]=\"controlScope()\"\n (patch)=\"onSchemaPatch($event)\"\n />\n\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('style.openLegacyDrawer')\"\n icon=\"general.settings-02\"\n severity=\"secondary\"\n size=\"small\"\n [outlined]=\"true\"\n (onClick)=\"openChartSettingsDrawer()\"\n />\n </div>\n </section>\n\n <!-- BEHAVIOR TAB -->\n <section\n [hidden]=\"activeTab() !== 'behavior'\"\n class=\"db-config-section\"\n >\n <h4 class=\"db-config-section__title\">\n {{ t(\"behavior.title\") }}\n </h4>\n <label class=\"db-style-row\">\n <span>\n {{ t(\"behavior.showFiltersBar\") }}\n </span>\n <input\n type=\"checkbox\"\n [checked]=\"ui()?.behavior?.filtersBarVisible || false\"\n (change)=\"onShowFiltersBarChange($any($event.target).checked)\"\n />\n </label>\n <p class=\"db-config-summary__hint\">\n {{\n t(\"behavior.actionsCount\")\n }}\n ({{ ui()?.behavior?.cardActions || 0 }})\n </p>\n\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('behavior.openAdvanced')\"\n icon=\"general.cursor-click-02\"\n severity=\"help\"\n size=\"small\"\n [outlined]=\"true\"\n (onClick)=\"openAdvancedEditor('actions')\"\n />\n </div>\n </section>\n\n <!-- ADVANCED TAB -->\n <section\n [hidden]=\"activeTab() !== 'advanced'\"\n class=\"db-config-section\"\n >\n <p class=\"db-config-summary__hint\">\n {{ t(\"advanced.hint\") }}\n </p>\n\n <mt-schema-control-renderer\n [schema]=\"schema()\"\n [persisted]=\"persistedConfig()\"\n [capability]=\"capability()\"\n scope=\"advanced\"\n (patch)=\"onSchemaPatch($event)\"\n />\n\n <h4 class=\"db-config-section__title\">\n {{ t(\"advanced.json\") }}\n </h4>\n <textarea\n class=\"db-config-advanced-editor\"\n rows=\"14\"\n spellcheck=\"false\"\n (focus)=\"initAdvancedJsonIfNeeded()\"\n [value]=\"advancedJsonDraft()\"\n (input)=\"onAdvancedJsonChange($any($event.target).value)\"\n ></textarea>\n @if (advancedJsonError()) {\n <p class=\"db-config-advanced-editor__error\">\n {{ advancedJsonError() }}\n </p>\n }\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('advanced.apply')\"\n icon=\"general.check\"\n severity=\"primary\"\n size=\"small\"\n (onClick)=\"applyAdvancedJson()\"\n />\n </div>\n </section>\n </main>\n\n <footer class=\"db-config-popover__footer\">\n @if (hasErrors()) {\n <span\n class=\"db-config-popover__status db-config-popover__status--error\"\n >\n <mt-icon icon=\"general.alert-circle\" class=\"text-base\" />\n {{ t(\"footer.errors\") }}\n </span>\n } @else {\n <span class=\"db-config-popover__status db-config-popover__status--ok\">\n <mt-icon icon=\"general.check-circle\" class=\"text-base\" />\n {{ t(\"footer.ready\") }}\n </span>\n }\n <mt-button\n [label]=\"t('footer.cancel')\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"cancel()\"\n />\n <mt-button\n [label]=\"t('footer.apply')\"\n icon=\"general.check\"\n severity=\"primary\"\n [disabled]=\"hasErrors()\"\n (onClick)=\"apply()\"\n />\n </footer>\n </div>\n }\n</ng-container>\n", styles: [".db-config-popover{display:flex;flex-direction:column;height:100%;width:100%;background:var(--p-surface-0, #fff)}.db-config-popover__header{display:flex;align-items:flex-start;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--p-surface-200, #e5e7eb);gap:.75rem}.db-config-popover__title-row{display:flex;align-items:center;gap:.5rem;min-width:0;flex:1}.db-config-popover__icon{color:var(--p-primary-600, #2563eb);font-size:1.4rem}.db-config-popover__title{display:flex;flex-direction:column;gap:.15rem;min-width:0;flex:1}.db-config-popover__title-input{background:transparent;border:0;border-bottom:1px dashed transparent;font-size:.95rem;font-weight:600;color:var(--p-text-color, #1f2937);padding:.05rem 0;width:100%;outline:none}.db-config-popover__title-input:hover{border-bottom-color:var(--p-surface-300, #d1d5db)}.db-config-popover__title-input:focus{border-bottom-color:var(--p-primary-500, #3b82f6)}.db-config-popover__chart-type{font-size:.7rem;color:var(--p-text-muted-color, #6b7280);text-transform:uppercase;letter-spacing:.05em}.db-config-popover__close{flex-shrink:0}.db-config-popover__tabs{border-bottom:1px solid var(--p-surface-200, #e5e7eb);padding:0 1rem}.db-config-popover__main{flex:1 1 0;min-height:0;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:1rem}.db-config-popover__footer{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem;border-top:1px solid var(--p-surface-200, #e5e7eb);background:var(--p-surface-0, #fff);position:sticky;bottom:0}.db-config-popover__status{flex:1;display:inline-flex;align-items:center;gap:.4rem;font-size:.8rem}.db-config-popover__status--ok{color:var(--p-green-600, #16a34a)}.db-config-popover__status--error{color:var(--p-amber-600, #d97706)}.db-config-section{display:flex;flex-direction:column;gap:.75rem}.db-config-section__title{font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--p-text-muted-color, #6b7280);margin:0}.db-config-section__actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.5rem;border-top:1px dashed var(--p-surface-200, #e5e7eb)}.db-config-summary{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.4rem}.db-config-summary__row{display:grid;grid-template-columns:7rem 1fr;align-items:start;gap:.75rem;font-size:.8rem}.db-config-summary__key{color:var(--p-text-muted-color, #6b7280);font-weight:500}.db-config-summary__value{color:var(--p-text-color, #1f2937);word-break:break-word;display:inline-flex;flex-wrap:wrap;gap:.25rem}.db-config-summary__value--ok{color:var(--p-green-600, #16a34a);font-weight:600}.db-config-summary__value--missing{color:var(--p-amber-600, #d97706);font-weight:600}.db-config-summary__formula{background:var(--p-surface-50, #f9fafb);padding:.25rem .4rem;border-radius:.25rem;font-size:.75rem;word-break:break-all}.db-config-summary__hint{font-size:.78rem;color:var(--p-text-muted-color, #6b7280);margin:0}.db-config-chip{background:var(--p-surface-100, #f3f4f6);border:1px solid var(--p-surface-200, #e5e7eb);padding:.1rem .5rem;border-radius:999px;font-size:.75rem;color:var(--p-text-color, #1f2937)}.db-config-issues{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.25rem}.db-config-issue{display:flex;align-items:center;gap:.4rem;font-size:.78rem;padding:.4rem .55rem;border-radius:.4rem}.db-config-issue i{font-size:.85rem}.db-config-issue[data-severity=error]{color:var(--p-red-700, #b91c1c);background:var(--p-red-50, #fef2f2);border:1px solid var(--p-red-100, #fee2e2)}.db-config-issue[data-severity=warning]{color:var(--p-amber-700, #b45309);background:var(--p-amber-50, #fffbeb);border:1px solid var(--p-amber-100, #fef3c7)}.db-config-issue[data-severity=info]{color:var(--p-blue-700, #1d4ed8);background:var(--p-blue-50, #eff6ff);border:1px solid var(--p-blue-100, #dbeafe)}.db-style-presets{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:.5rem}.db-style-preset{display:flex;flex-direction:column;gap:.2rem;padding:.6rem .7rem;background:var(--p-surface-0, #fff);border:1px solid var(--p-surface-200, #e5e7eb);border-radius:.5rem;text-align:left;cursor:pointer;transition:all .12s ease}.db-style-preset:hover{border-color:var(--p-primary-300, #93c5fd);background:var(--p-primary-50, #eff6ff)}.db-style-preset.is-selected{border-color:var(--p-primary-500, #3b82f6);background:var(--p-primary-50, #eff6ff);box-shadow:0 0 0 2px var(--p-primary-100, #dbeafe)}.db-style-preset__label{font-size:.85rem;font-weight:600;color:var(--p-text-color, #1f2937)}.db-style-preset__desc{font-size:.72rem;color:var(--p-text-muted-color, #6b7280)}.db-style-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.5rem .75rem}.db-style-row{display:grid;grid-template-columns:1fr auto;align-items:center;gap:.5rem;font-size:.8rem;color:var(--p-text-color, #1f2937)}.db-style-row input[type=color]{width:2.5rem;height:1.6rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;cursor:pointer;background:transparent;padding:0}.db-style-row input[type=number]{width:4.5rem;padding:.25rem .4rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;font-size:.78rem}.db-style-row input[type=checkbox]{width:1rem;height:1rem}.db-style-select{padding:.25rem .4rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;font-size:.78rem;background:var(--p-surface-0, #fff)}.db-config-section__actions--scope{border-top:0;padding-top:0;justify-content:flex-end}.db-scope-toggle{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .55rem;font-size:.75rem;border-radius:999px;border:1px solid var(--p-surface-300, #d1d5db);background:var(--p-surface-0, #fff);color:var(--p-text-color, #1f2937);cursor:pointer}.db-scope-toggle:hover{background:var(--p-surface-100, #f3f4f6)}.db-config-advanced-editor{width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.75rem;padding:.6rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.4rem;background:var(--p-surface-50, #f9fafb);resize:vertical}.db-config-advanced-editor__error{color:var(--p-red-600, #dc2626);font-size:.78rem;margin:0}@media(max-width:768px){.db-config-popover__header{padding:.6rem .75rem}.db-config-popover__main{padding:.75rem}.db-style-grid,.db-style-presets,.db-config-summary__row{grid-template-columns:1fr}}\n"] }]
30095
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<ng-container *transloco=\"let t; prefix: 'dashboardBuilder.pass4'\">\n @if (item(); as currentItem) {\n <div class=\"db-config-popover\">\n <header class=\"db-config-popover__header\">\n <div class=\"db-config-popover__title-row\">\n <mt-icon\n [icon]=\"capability()?.icon ?? 'general.settings-02'\"\n class=\"db-config-popover__icon\"\n />\n <div class=\"db-config-popover__title\">\n <input\n type=\"text\"\n class=\"db-config-popover__title-input\"\n [ngModel]=\"ui()?.title?.title ?? ''\"\n (ngModelChange)=\"onTitleChange($event)\"\n [placeholder]=\"t('header.titlePlaceholder')\"\n />\n <span class=\"db-config-popover__chart-type\">\n {{ capability()?.name }}\n </span>\n </div>\n </div>\n <div class=\"db-config-popover__close\">\n <mt-button\n icon=\"general.x-close\"\n [tooltip]=\"t('header.close')\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n (onClick)=\"cancel()\"\n />\n </div>\n </header>\n\n <mt-tabs\n [options]=\"translatedTabs(t)\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [(active)]=\"activeTab\"\n class=\"db-config-popover__tabs\"\n />\n\n <main class=\"db-config-popover__main\">\n <!-- DATA TAB -->\n <section [hidden]=\"activeTab() !== 'data'\" class=\"db-config-section\">\n <mt-datasource-panel\n [item]=\"currentItem\"\n (serviceConfigChange)=\"onServiceConfigPatch($event)\"\n (openAdvanced)=\"openAdvancedEditor('dataSource')\"\n />\n\n @if (issuesByDomain().datasource.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().datasource; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n @if (issuesByDomain().fields.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().fields; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n </section>\n\n <!-- FILTERS TAB -->\n <section [hidden]=\"activeTab() !== 'filters'\" class=\"db-config-section\">\n <mt-filter-builder\n [chips]=\"chips()\"\n [persisted]=\"currentItem.config\"\n (chipsChange)=\"onChipsChange($event)\"\n />\n @if (issuesByDomain().filters.length) {\n <ul class=\"db-config-issues\">\n @for (issue of issuesByDomain().filters; track issue.code) {\n <li\n class=\"db-config-issue\"\n [attr.data-severity]=\"issue.severity\"\n >\n <i class=\"mti mti-general.alert-circle\"></i>\n <span>{{ issueMessage(issue, t) }}</span>\n </li>\n }\n </ul>\n }\n </section>\n\n <!-- STYLE TAB -->\n <section [hidden]=\"activeTab() !== 'style'\" class=\"db-config-section\">\n <h4 class=\"db-config-section__title\">\n {{ t(\"style.presets\") }}\n </h4>\n <div class=\"db-style-presets\">\n @for (preset of presets(); track preset.key) {\n <button\n type=\"button\"\n class=\"db-style-preset\"\n [class.is-selected]=\"selectedPresetKey() === preset.key\"\n (click)=\"onApplyPreset(preset)\"\n >\n <span class=\"db-style-preset__label\">{{ preset.label }}</span>\n <span class=\"db-style-preset__desc\">{{\n preset.description\n }}</span>\n </button>\n }\n </div>\n\n <div\n class=\"db-config-section__actions db-config-section__actions--scope\"\n >\n <button\n type=\"button\"\n class=\"db-scope-toggle\"\n (click)=\"toggleControlScope()\"\n >\n <i\n [class]=\"\n 'mti mti-' +\n (controlScope() === 'advanced'\n ? 'general.eye'\n : 'general.eye-off')\n \"\n ></i>\n <span>\n {{\n controlScope() === \"advanced\"\n ? t(\"controls.simpleMode\")\n : t(\"controls.advancedMode\")\n }}\n </span>\n </button>\n </div>\n\n <mt-schema-control-renderer\n [schema]=\"schema()\"\n [persisted]=\"persistedConfig()\"\n [capability]=\"capability()\"\n [scope]=\"controlScope()\"\n (patch)=\"onSchemaPatch($event)\"\n />\n\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('style.openLegacyDrawer')\"\n icon=\"general.settings-02\"\n severity=\"secondary\"\n size=\"small\"\n [outlined]=\"true\"\n (onClick)=\"openChartSettingsDrawer()\"\n />\n </div>\n </section>\n\n <!-- BEHAVIOR TAB -->\n <section\n [hidden]=\"activeTab() !== 'behavior'\"\n class=\"db-config-section\"\n >\n <h4 class=\"db-config-section__title\">\n {{ t(\"behavior.title\") }}\n </h4>\n <label class=\"db-style-row\">\n <span>\n {{ t(\"behavior.showFiltersBar\") }}\n </span>\n <input\n type=\"checkbox\"\n [checked]=\"ui()?.behavior?.filtersBarVisible || false\"\n (change)=\"onShowFiltersBarChange($any($event.target).checked)\"\n />\n </label>\n <p class=\"db-config-summary__hint\">\n {{ t(\"behavior.actionsCount\") }}\n ({{ ui()?.behavior?.cardActions || 0 }})\n </p>\n\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('behavior.openAdvanced')\"\n icon=\"general.cursor-click-02\"\n severity=\"help\"\n size=\"small\"\n [outlined]=\"true\"\n (onClick)=\"openAdvancedEditor('actions')\"\n />\n </div>\n </section>\n\n <!-- ADVANCED TAB -->\n <section\n [hidden]=\"activeTab() !== 'advanced'\"\n class=\"db-config-section\"\n >\n <p class=\"db-config-summary__hint\">\n {{ t(\"advanced.hint\") }}\n </p>\n\n <mt-schema-control-renderer\n [schema]=\"schema()\"\n [persisted]=\"persistedConfig()\"\n [capability]=\"capability()\"\n scope=\"advanced\"\n (patch)=\"onSchemaPatch($event)\"\n />\n\n <h4 class=\"db-config-section__title\">\n {{ t(\"advanced.json\") }}\n </h4>\n <textarea\n class=\"db-config-advanced-editor\"\n rows=\"14\"\n spellcheck=\"false\"\n (focus)=\"initAdvancedJsonIfNeeded()\"\n [value]=\"advancedJsonDraft()\"\n (input)=\"onAdvancedJsonChange($any($event.target).value)\"\n ></textarea>\n @if (advancedJsonError()) {\n <p class=\"db-config-advanced-editor__error\">\n {{ advancedJsonError() }}\n </p>\n }\n <div class=\"db-config-section__actions\">\n <mt-button\n [label]=\"t('advanced.apply')\"\n icon=\"general.check\"\n severity=\"primary\"\n size=\"small\"\n (onClick)=\"applyAdvancedJson()\"\n />\n </div>\n </section>\n </main>\n\n <footer class=\"db-config-popover__footer\">\n @if (hasErrors()) {\n <span\n class=\"db-config-popover__status db-config-popover__status--error\"\n >\n <mt-icon icon=\"general.alert-circle\" class=\"text-base\" />\n {{ t(\"footer.errors\") }}\n </span>\n } @else {\n <span class=\"db-config-popover__status db-config-popover__status--ok\">\n <mt-icon icon=\"general.check-circle\" class=\"text-base\" />\n {{ t(\"footer.ready\") }}\n </span>\n }\n <mt-button\n [label]=\"t('footer.cancel')\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"cancel()\"\n />\n <mt-button\n [label]=\"t('footer.apply')\"\n icon=\"general.check\"\n severity=\"primary\"\n [disabled]=\"hasErrors()\"\n (onClick)=\"apply()\"\n />\n </footer>\n </div>\n }\n</ng-container>\n", styles: [".db-config-popover{display:flex;flex-direction:column;height:100%;width:100%;background:var(--p-surface-0, #fff)}.db-config-popover__header{display:flex;align-items:flex-start;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--p-surface-200, #e5e7eb);gap:.75rem}.db-config-popover__title-row{display:flex;align-items:center;gap:.5rem;min-width:0;flex:1}.db-config-popover__icon{color:var(--p-primary-600, #2563eb);font-size:1.4rem}.db-config-popover__title{display:flex;flex-direction:column;gap:.15rem;min-width:0;flex:1}.db-config-popover__title-input{background:transparent;border:0;border-bottom:1px dashed transparent;font-size:.95rem;font-weight:600;color:var(--p-text-color, #1f2937);padding:.05rem 0;width:100%;outline:none}.db-config-popover__title-input:hover{border-bottom-color:var(--p-surface-300, #d1d5db)}.db-config-popover__title-input:focus{border-bottom-color:var(--p-primary-500, #3b82f6)}.db-config-popover__chart-type{font-size:.7rem;color:var(--p-text-muted-color, #6b7280);text-transform:uppercase;letter-spacing:.05em}.db-config-popover__close{flex-shrink:0}.db-config-popover__tabs{border-bottom:1px solid var(--p-surface-200, #e5e7eb);padding:0 1rem}.db-config-popover__main{flex:1 1 0;min-height:0;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:1rem}.db-config-popover__footer{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem;border-top:1px solid var(--p-surface-200, #e5e7eb);background:var(--p-surface-0, #fff);position:sticky;bottom:0}.db-config-popover__status{flex:1;display:inline-flex;align-items:center;gap:.4rem;font-size:.8rem}.db-config-popover__status--ok{color:var(--p-green-600, #16a34a)}.db-config-popover__status--error{color:var(--p-amber-600, #d97706)}.db-config-section{display:flex;flex-direction:column;gap:.75rem}.db-config-section__title{font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--p-text-muted-color, #6b7280);margin:0}.db-config-section__actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.5rem;border-top:1px dashed var(--p-surface-200, #e5e7eb)}.db-config-summary{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.4rem}.db-config-summary__row{display:grid;grid-template-columns:7rem 1fr;align-items:start;gap:.75rem;font-size:.8rem}.db-config-summary__key{color:var(--p-text-muted-color, #6b7280);font-weight:500}.db-config-summary__value{color:var(--p-text-color, #1f2937);word-break:break-word;display:inline-flex;flex-wrap:wrap;gap:.25rem}.db-config-summary__value--ok{color:var(--p-green-600, #16a34a);font-weight:600}.db-config-summary__value--missing{color:var(--p-amber-600, #d97706);font-weight:600}.db-config-summary__formula{background:var(--p-surface-50, #f9fafb);padding:.25rem .4rem;border-radius:.25rem;font-size:.75rem;word-break:break-all}.db-config-summary__hint{font-size:.78rem;color:var(--p-text-muted-color, #6b7280);margin:0}.db-config-chip{background:var(--p-surface-100, #f3f4f6);border:1px solid var(--p-surface-200, #e5e7eb);padding:.1rem .5rem;border-radius:999px;font-size:.75rem;color:var(--p-text-color, #1f2937)}.db-config-issues{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.25rem}.db-config-issue{display:flex;align-items:center;gap:.4rem;font-size:.78rem;padding:.4rem .55rem;border-radius:.4rem}.db-config-issue i{font-size:.85rem}.db-config-issue[data-severity=error]{color:var(--p-red-700, #b91c1c);background:var(--p-red-50, #fef2f2);border:1px solid var(--p-red-100, #fee2e2)}.db-config-issue[data-severity=warning]{color:var(--p-amber-700, #b45309);background:var(--p-amber-50, #fffbeb);border:1px solid var(--p-amber-100, #fef3c7)}.db-config-issue[data-severity=info]{color:var(--p-blue-700, #1d4ed8);background:var(--p-blue-50, #eff6ff);border:1px solid var(--p-blue-100, #dbeafe)}.db-style-presets{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:.5rem}.db-style-preset{display:flex;flex-direction:column;gap:.2rem;padding:.6rem .7rem;background:var(--p-surface-0, #fff);border:1px solid var(--p-surface-200, #e5e7eb);border-radius:.5rem;text-align:left;cursor:pointer;transition:all .12s ease}.db-style-preset:hover{border-color:var(--p-primary-300, #93c5fd);background:var(--p-primary-50, #eff6ff)}.db-style-preset.is-selected{border-color:var(--p-primary-500, #3b82f6);background:var(--p-primary-50, #eff6ff);box-shadow:0 0 0 2px var(--p-primary-100, #dbeafe)}.db-style-preset__label{font-size:.85rem;font-weight:600;color:var(--p-text-color, #1f2937)}.db-style-preset__desc{font-size:.72rem;color:var(--p-text-muted-color, #6b7280)}.db-style-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.5rem .75rem}.db-style-row{display:grid;grid-template-columns:1fr auto;align-items:center;gap:.5rem;font-size:.8rem;color:var(--p-text-color, #1f2937)}.db-style-row input[type=color]{width:2.5rem;height:1.6rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;cursor:pointer;background:transparent;padding:0}.db-style-row input[type=number]{width:4.5rem;padding:.25rem .4rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;font-size:.78rem}.db-style-row input[type=checkbox]{width:1rem;height:1rem}.db-style-select{padding:.25rem .4rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.25rem;font-size:.78rem;background:var(--p-surface-0, #fff)}.db-config-section__actions--scope{border-top:0;padding-top:0;justify-content:flex-end}.db-scope-toggle{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .55rem;font-size:.75rem;border-radius:999px;border:1px solid var(--p-surface-300, #d1d5db);background:var(--p-surface-0, #fff);color:var(--p-text-color, #1f2937);cursor:pointer}.db-scope-toggle:hover{background:var(--p-surface-100, #f3f4f6)}.db-config-advanced-editor{width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.75rem;padding:.6rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.4rem;background:var(--p-surface-50, #f9fafb);resize:vertical}.db-config-advanced-editor__error{color:var(--p-red-600, #dc2626);font-size:.78rem;margin:0}@media(max-width:768px){.db-config-popover__header{padding:.6rem .75rem}.db-config-popover__main{padding:.75rem}.db-style-grid,.db-style-presets,.db-config-summary__row{grid-template-columns:1fr}}\n"] }]
30084
30096
  }], ctorParameters: () => [] });
30085
30097
  function matchesPartial(full, partial) {
30086
30098
  for (const key of Object.keys(partial)) {
@@ -31140,9 +31152,7 @@ class FilterChipsBarComponent {
31140
31152
  type="button"
31141
31153
  class="db-filter-chip__toggle"
31142
31154
  [title]="
31143
- chip.active
31144
- ? t('filters.disable')
31145
- : t('filters.enable')
31155
+ chip.active ? t('filters.disable') : t('filters.enable')
31146
31156
  "
31147
31157
  (click)="toggle(chip)"
31148
31158
  [disabled]="
@@ -31266,9 +31276,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
31266
31276
  type="button"
31267
31277
  class="db-filter-chip__toggle"
31268
31278
  [title]="
31269
- chip.active
31270
- ? t('filters.disable')
31271
- : t('filters.enable')
31279
+ chip.active ? t('filters.disable') : t('filters.enable')
31272
31280
  "
31273
31281
  (click)="toggle(chip)"
31274
31282
  [disabled]="