ngx-t-reports
Version:
Angular module for creating dynamic reports and dashboards. Supports various data sources, custom templates, and real-time updates.
1,915 lines • 152 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, Output, Input, Component, Injectable, inject, ViewChild, ChangeDetectionStrategy, InjectionToken, Optional, Inject } from '@angular/core';
import * as i1 from 'ngx-echarts';
import { NgxEchartsModule, provideEchartsCore } from 'ngx-echarts';
import * as echarts from 'echarts/core';
import { GridComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
import * as i2 from '@angular/common';
import { CommonModule } from '@angular/common';
import { BarChart, BoxplotChart, CandlestickChart, CustomChart, EffectScatterChart, FunnelChart, GaugeChart, GraphChart, HeatmapChart, LineChart, LinesChart, MapChart, ParallelChart, PictorialBarChart, PieChart, RadarChart, SankeyChart, ScatterChart, SunburstChart, ThemeRiverChart, TreeChart, TreemapChart } from 'echarts/charts';
import * as i4$1 from '@angular/material/progress-spinner';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import * as i3 from '@angular/cdk/drag-drop';
import { moveItemInArray, DragDropModule, CdkDropList, CdkDrag } from '@angular/cdk/drag-drop';
import * as i8 from '@angular/material/button';
import { MatButtonModule } from '@angular/material/button';
import * as i1$1 from '@angular/material/card';
import { MatCardModule, MatCardContent, MatCardHeader, MatCardTitle, MatCardSubtitle } from '@angular/material/card';
import * as i7 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import * as i5 from '@angular/material/menu';
import { MatMenuModule } from '@angular/material/menu';
import * as i9 from '@angular/material/toolbar';
import { MatToolbarModule, MatToolbar } from '@angular/material/toolbar';
import * as i2$1 from '@angular/material/divider';
import { MatDividerModule } from '@angular/material/divider';
import * as i4 from '@angular/material/chips';
import { MatChipsModule } from '@angular/material/chips';
import * as i12 from '@angular/material/table';
import { MatTableModule } from '@angular/material/table';
import * as i6 from '@angular/material/tooltip';
import { MatTooltipModule } from '@angular/material/tooltip';
import { ComponentStore } from '@ngrx/component-store';
import { combineLatest, map, switchMap, tap, distinctUntilChanged, of, BehaviorSubject, Observable, Subscription, take, catchError } from 'rxjs';
import * as XLSX from 'xlsx';
import * as i13 from '@angular/cdk/scrolling';
import { ScrollingModule } from '@angular/cdk/scrolling';
import * as i1$2 from '@angular/material/dialog';
import * as i7$1 from '@angular/material/expansion';
import { MatExpansionModule } from '@angular/material/expansion';
import { assignDeepPropertyToObject, TDynamicDataEditComponent, NGX_T_FORMS_CONFIG_TOKEN, getSectionElements } from 'ngx-t-forms';
import { ElementEditorTypes, SpecialElementKeys, InputDataTypes, ElementTypes } from 'ngx-t-forms-types';
import * as i7$2 from '@angular/forms';
import { FormGroup, FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i6$1 from '@angular/material/datepicker';
import { MatDatepickerModule } from '@angular/material/datepicker';
import * as i5$1 from '@angular/material/form-field';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MAT_DATE_LOCALE, DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import moment from 'moment-timezone';
import moment$1 from 'moment';
echarts.use([
BarChart, BoxplotChart, CandlestickChart, CustomChart, EffectScatterChart,
FunnelChart, GaugeChart, GraphChart, HeatmapChart, LineChart, LinesChart,
MapChart, ParallelChart, PictorialBarChart, PieChart, RadarChart, SankeyChart,
ScatterChart, SunburstChart, ThemeRiverChart, TreeChart, TreemapChart,
GridComponent, CanvasRenderer
]);
class EChartsComponent {
constructor() {
this.chartClick = new EventEmitter();
}
ngOnChanges(changes) {
if (changes['options']) {
this.chartOption = this.options;
}
}
onChartClick(event) {
this.chartClick.emit(event);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EChartsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: EChartsComponent, isStandalone: true, selector: "app-e-charts", inputs: { loading: "loading", options: "options" }, outputs: { chartClick: "chartClick" }, providers: [
provideEchartsCore({ echarts }),
], usesOnChanges: true, ngImport: i0, template: "@if (chartOption ) {\r\n<div echarts style=\"height:338px;\" (chartClick)=\"onChartClick($event)\" [options]=\"chartOption\" [loading]=\"loading\"\r\n class=\"chart\"></div>\r\n}", styles: [""], dependencies: [{ kind: "ngmodule", type: NgxEchartsModule }, { kind: "directive", type: i1.NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }, { kind: "ngmodule", type: CommonModule }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EChartsComponent, decorators: [{
type: Component,
args: [{ selector: 'app-e-charts', standalone: true, imports: [
NgxEchartsModule,
CommonModule
], providers: [
provideEchartsCore({ echarts }),
], template: "@if (chartOption ) {\r\n<div echarts style=\"height:338px;\" (chartClick)=\"onChartClick($event)\" [options]=\"chartOption\" [loading]=\"loading\"\r\n class=\"chart\"></div>\r\n}" }]
}], propDecorators: { loading: [{
type: Input
}], options: [{
type: Input
}], chartClick: [{
type: Output
}] } });
function createADonutChart(id, title, dataSeries) {
return {
title,
id: id,
chart: {
toolbox: {
show: true,
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true }
}
},
grid: {
top: 100,
},
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [{
name: title,
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true
},
emphasis: {
label: {
show: true,
fontSize: '40',
fontWeight: 'bold'
}
},
labelLine: {
lineStyle: {
color: '#235894'
}
},
data: dataSeries
}],
}
};
}
function createABarChart(id, title, dataSeries) {
return {
title,
id,
chart: {
toolbox: {
show: true,
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true }
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: dataSeries.map(v => v.name),
axisTick: {
alignWithLabel: true
}
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '',
type: 'bar',
label: {
show: true
},
emphasis: {
label: {
show: true,
fontSize: '40',
fontWeight: 'bold'
}
},
labelLine: {
lineStyle: {
color: '#235894'
}
},
data: dataSeries.map((v, index) => ({
...v,
itemStyle: {
color: getColors(dataSeries.length)[index]
}
}))
}
],
},
};
}
function createRoseTypeChart(id, title, dataSeries) {
return {
title,
id,
chart: {
legend: {
top: 'bottom'
},
toolbox: {
show: true,
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true }
}
},
series: [
{
name: title,
type: 'pie',
radius: [50, 250],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 8
},
data: dataSeries
}
]
}
};
}
function createAPolarChart(id, title, dataSeries) {
return {
title,
id,
chart: {
title: [
{
text: title
}
],
toolbox: {
show: true,
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true }
}
},
polar: {
radius: [30, '80%']
},
angleAxis: {
startAngle: 75
},
radiusAxis: {
type: 'category',
data: dataSeries.map(v => v.name)
},
tooltip: {},
series: {
type: 'bar',
data: dataSeries.map(v => v.value),
coordinateSystem: 'polar',
label: {
show: true,
position: 'middle',
formatter: '{b}: {c}'
}
}
}
};
}
const colors = [
'#4285F4',
'#FBBC05',
'#34A853',
'#EA4335',
'#55ACEE',
'#3B5998',
'#7CBB00',
'#00A1F1',
'#7B0099',
'#146EB4'
];
function getColors(n) {
const result = [];
for (let i = 0; i < n; i++) {
result.push(colors[i % colors.length]);
}
return result;
}
class GroupingChartComponent {
constructor() {
this.groups = [];
this.numberProperties = [];
this.lastHash = null;
this.chartConfig = {};
this.chatTypesEnum = ChartTypes;
}
get totalDistributionPerGroup() {
// Create a hash or identifier based on the content of the groups to detect changes
const currentHash = JSON.stringify(this.groups);
if (this.lastHash === currentHash && this.cache) {
// If the hash hasn't changed, return the cached result
return this.cache;
}
// Compute the distribution if data has changed
const result = this.numberProperties?.map((property, index) => ({
title: property.label,
id: index.toString(),
dataSeries: this.groups.map(g => ({
name: g.name,
value: Number(g.totals?.[property.formControlName] || 0)
}))
}))
.map(chart => {
const type = this.chartConfig[chart.id]?.chartType || ChartTypes.Donut;
if (type === ChartTypes.Donut || !type) {
return createADonutChart(chart.id, chart.title, chart.dataSeries);
}
if (type === ChartTypes.Bar) {
return createABarChart(chart.id, chart.title, chart.dataSeries);
}
if (type === ChartTypes.Polar) {
return createAPolarChart(chart.id, chart.title, chart.dataSeries);
}
if (type === ChartTypes.RoseType) {
return createRoseTypeChart(chart.id, chart.title, chart.dataSeries);
}
return;
});
// Update cache and hash
this.cache = result;
this.lastHash = currentHash;
return result;
}
isExpanded() {
return this.chartConfig[this.activeChart || '']?.full;
}
chartType() {
return this.chartConfig[this.activeChart || '']?.chartType;
}
setChartType(type) {
if (!this.chartConfig[this.activeChart || '']) {
this.chartConfig[this.activeChart || ''] = {
full: false,
chartType: this.chatTypesEnum.Donut
};
}
if (!this.chartConfig[this.activeChart || ''])
return;
this.chartConfig[this.activeChart || '']['chartType'] = type;
this.lastHash = null;
}
toggleExpansion() {
const st = !!this.chartConfig[this.activeChart || '']?.full;
if (!this.chartConfig[this.activeChart || '']) {
this.chartConfig[this.activeChart || ''] = {
full: false,
chartType: this.chatTypesEnum.Donut
};
}
if (!this.chartConfig[this.activeChart || ''])
return;
return this.chartConfig[this.activeChart || ''].full = !st;
}
getChartSize(id) {
return this.chartConfig[id]?.full ? '12' : '4';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: GroupingChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: GroupingChartComponent, isStandalone: true, selector: "app-grouping-chart", inputs: { groups: "groups", numberProperties: "numberProperties" }, ngImport: i0, template: "<section style=\"background: var(--mat-sys-surface-container);\">\r\n\r\n\r\n <div class=\"row\">\r\n @for (chart of totalDistributionPerGroup||[]; track chart.id) {\r\n <div *ngIf=\"!!chart\" class=\"chart-container\" [class]=\"'col-md-'+getChartSize(chart.id)\">\r\n <mat-card (mouseover)=\"activeChart = chart.id\">\r\n <mat-card-header>\r\n <mat-card-title>\r\n <h4>\r\n {{chart.title}}\r\n <button [matMenuTriggerFor]=\"menu\" style=\"position: absolute;right: 24px;top:24px\"\r\n mat-icon-button>\r\n <mat-icon>\r\n more_vert\r\n </mat-icon>\r\n </button>\r\n </h4>\r\n\r\n </mat-card-title>\r\n\r\n </mat-card-header>\r\n <mat-card-content>\r\n <app-e-charts [options]=\"chart.chart\">\r\n\r\n </app-e-charts>\r\n </mat-card-content>\r\n\r\n </mat-card>\r\n </div>\r\n }\r\n\r\n </div>\r\n</section>\r\n\r\n<mat-menu #menu=\"matMenu\">\r\n <h5> Chart settings </h5>\r\n <button (click)=\"toggleExpansion()\" mat-menu-item>\r\n <mat-icon>{{isExpanded()?'close_fullscreen':'open_in_full'}} </mat-icon>\r\n <span> {{isExpanded()?'Close full view':'View in full size' }}</span>\r\n </button>\r\n <h5> Chart type </h5>\r\n <button (click)=\"setChartType(chatTypesEnum.Donut)\" mat-menu-item>\r\n <mat-icon [color]=\"chartType() ===chatTypesEnum.Donut|| !chartType()?'primary':''\">donut_large</mat-icon>\r\n <span [style.font-weight]=\"chartType() ===chatTypesEnum.Donut|| !chartType()?'bold':''\">Donut chart</span>\r\n </button>\r\n <button (click)=\"setChartType(chatTypesEnum.Bar)\" mat-menu-item>\r\n <mat-icon [color]=\"chartType() ===chatTypesEnum.Bar?'primary':''\">bar_chart</mat-icon>\r\n <span [style.font-weight]=\"chartType() ===chatTypesEnum.Bar?'bold':''\">bar</span>\r\n </button>\r\n <button (click)=\"setChartType(chatTypesEnum.Polar)\" mat-menu-item>\r\n <mat-icon [color]=\"chartType() ===chatTypesEnum.Polar?'primary':''\">radar</mat-icon>\r\n <span [style.font-weight]=\"chartType() ===chatTypesEnum.Bar?'bold':''\">Polar </span>\r\n </button>\r\n <button (click)=\"setChartType(chatTypesEnum.RoseType)\" mat-menu-item>\r\n <mat-icon [color]=\"chartType() ===chatTypesEnum.RoseType?'primary':''\">filter_vintage</mat-icon>\r\n <span [style.font-weight]=\"chartType() ===chatTypesEnum.Bar?'bold':''\">Rose </span>\r\n </button>\r\n\r\n</mat-menu>", styles: ["mat-card{margin:5px}h5{margin-left:16px;margin-bottom:10px}.chart-container{padding:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: EChartsComponent, selector: "app-e-charts", inputs: ["loading", "options"], outputs: ["chartClick"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i1$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i1$1.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i1$1.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i1$1.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i8.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i5.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i5.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: GroupingChartComponent, decorators: [{
type: Component,
args: [{ selector: 'app-grouping-chart', standalone: true, imports: [CommonModule, EChartsComponent, MatCardModule, MatButtonModule, MatIconModule, MatMenuModule], template: "<section style=\"background: var(--mat-sys-surface-container);\">\r\n\r\n\r\n <div class=\"row\">\r\n @for (chart of totalDistributionPerGroup||[]; track chart.id) {\r\n <div *ngIf=\"!!chart\" class=\"chart-container\" [class]=\"'col-md-'+getChartSize(chart.id)\">\r\n <mat-card (mouseover)=\"activeChart = chart.id\">\r\n <mat-card-header>\r\n <mat-card-title>\r\n <h4>\r\n {{chart.title}}\r\n <button [matMenuTriggerFor]=\"menu\" style=\"position: absolute;right: 24px;top:24px\"\r\n mat-icon-button>\r\n <mat-icon>\r\n more_vert\r\n </mat-icon>\r\n </button>\r\n </h4>\r\n\r\n </mat-card-title>\r\n\r\n </mat-card-header>\r\n <mat-card-content>\r\n <app-e-charts [options]=\"chart.chart\">\r\n\r\n </app-e-charts>\r\n </mat-card-content>\r\n\r\n </mat-card>\r\n </div>\r\n }\r\n\r\n </div>\r\n</section>\r\n\r\n<mat-menu #menu=\"matMenu\">\r\n <h5> Chart settings </h5>\r\n <button (click)=\"toggleExpansion()\" mat-menu-item>\r\n <mat-icon>{{isExpanded()?'close_fullscreen':'open_in_full'}} </mat-icon>\r\n <span> {{isExpanded()?'Close full view':'View in full size' }}</span>\r\n </button>\r\n <h5> Chart type </h5>\r\n <button (click)=\"setChartType(chatTypesEnum.Donut)\" mat-menu-item>\r\n <mat-icon [color]=\"chartType() ===chatTypesEnum.Donut|| !chartType()?'primary':''\">donut_large</mat-icon>\r\n <span [style.font-weight]=\"chartType() ===chatTypesEnum.Donut|| !chartType()?'bold':''\">Donut chart</span>\r\n </button>\r\n <button (click)=\"setChartType(chatTypesEnum.Bar)\" mat-menu-item>\r\n <mat-icon [color]=\"chartType() ===chatTypesEnum.Bar?'primary':''\">bar_chart</mat-icon>\r\n <span [style.font-weight]=\"chartType() ===chatTypesEnum.Bar?'bold':''\">bar</span>\r\n </button>\r\n <button (click)=\"setChartType(chatTypesEnum.Polar)\" mat-menu-item>\r\n <mat-icon [color]=\"chartType() ===chatTypesEnum.Polar?'primary':''\">radar</mat-icon>\r\n <span [style.font-weight]=\"chartType() ===chatTypesEnum.Bar?'bold':''\">Polar </span>\r\n </button>\r\n <button (click)=\"setChartType(chatTypesEnum.RoseType)\" mat-menu-item>\r\n <mat-icon [color]=\"chartType() ===chatTypesEnum.RoseType?'primary':''\">filter_vintage</mat-icon>\r\n <span [style.font-weight]=\"chartType() ===chatTypesEnum.Bar?'bold':''\">Rose </span>\r\n </button>\r\n\r\n</mat-menu>", styles: ["mat-card{margin:5px}h5{margin-left:16px;margin-bottom:10px}.chart-container{padding:0}\n"] }]
}], ctorParameters: () => [], propDecorators: { groups: [{
type: Input
}], numberProperties: [{
type: Input
}] } });
var ChartTypes;
(function (ChartTypes) {
ChartTypes["Donut"] = "donutChart";
ChartTypes["Bar"] = "bar";
ChartTypes["Polar"] = "polar";
ChartTypes["RoseType"] = "roseType";
})(ChartTypes || (ChartTypes = {}));
const COLOR_CODES = {
secondary: {
color1: '#FFF5EB', // Very light orange tint (5% of base)
color2: '#FFEBD6', // Light orange tint (10% of base)
color3: '#FFE1C2', // Light-medium orange tint (15% of base)
color4: '#FFD7AD', // Medium orange tint (20% of base)
color5: '#FFCD99', // Medium-deep orange tint (25% of base)
color6: '#FFC285', // Deeper orange tint (30% of base)
color7: '#FFB870', // Even deeper orange tint (35% of base)
color8: '#F68C1E' // Base color
},
blue: {
color1: '#DAE5FC', // Very light tint (5% of base)
color2: '#C0D2F9', // Light tint (10% of base)
color3: '#A6C0F7', // Light-medium tint (15% of base)
color4: '#8DADF5', // Medium tint (20% of base)
color5: '#739BF2', // Medium-deep tint (25% of base)
color6: '#5988F0', // Deeper tint (30% of base)
color7: '#3F76ED', // Even deeper tint (35% of base)
color8: '#2563EB', // Base color
},
green: {
color1: '#ECF2DB', // Very light tint (5% of base)
color2: '#DFE9C2', // Light tint (10% of base)
color3: '#D2E0A9', // Light-medium tint (15% of base)
color4: '#C5D790', // Medium tint (20% of base)
color5: '#B8CE77', // Medium-deep tint (25% of base)
color6: '#ABC55E', // Deeper tint (30% of base)
color7: '#9EBC45', // Even deeper tint (35% of base)
color8: '#91B32C', // Base color
},
blueviolet: {
color1: '#EBDBFA', // Very light tint (5% of base)
color2: '#DDC2F7', // Light tint (10% of base)
color3: '#CFA9F3', // Light-medium tint (15% of base)
color4: '#C290F0', // Medium tint (20% of base)
color5: '#B477EC', // Medium-deep tint (25% of base)
color6: '#A65DE9', // Deeper tint (30% of base)
color7: '#9844E5', // Even deeper tint (35% of base)
color8: '#8A2BE2', // Base color
},
red: {
color1: '#FDE5E1', // Very light tint (5% of base)
color2: '#FBD3CB', // Light tint (10% of base)
color3: '#FAC1B6', // Light-medium tint (15% of base)
color4: '#F8AFA1', // Medium tint (20% of base)
color5: '#F79D8B', // Medium-deep tint (25% of base)
color6: '#F58A76', // Deeper tint (30% of base)
color7: '#F47860', // Even deeper tint (35% of base)
color8: '#F2664B', // Base color
},
purple: {
color1: '#E5DDEE', // Very light tint (5% of base)
color2: '#D3C4E2', // Light tint (10% of base)
color3: '#C1ACD6', // Light-medium tint (15% of base)
color4: '#AF94C9', // Medium tint (20% of base)
color5: '#9D7CBD', // Medium-deep tint (25% of base)
color6: '#8A63B1', // Deeper tint (30% of base)
color7: '#784BA5', // Even deeper tint (35% of base)
color8: '#663399', // Base color
},
// Spectrum Harmony - Best for business applications and financial data
spectrumHarmony: {
color1: '#E3F2FD', // Light blue - first level
color2: '#BBDEFB', // Slightly deeper blue
color3: '#DCEDC8', // Light green - second level
color4: '#C5E1A5', // Slightly deeper green
color5: '#FFF9C4', // Light yellow - third level
color6: '#FFECB3', // Light amber - fourth level
color7: '#FFE082', // Medium amber
color8: '#FFCCBC' // Light red/orange - fifth level
},
// Vibrant Distinction - Best for complex hierarchical data requiring quick differentiation
vibrantDistinction: {
color1: '#E8F5E9', // Mint green - first level
color2: '#C8E6C9', // Medium mint
color3: '#E1F5FE', // Light blue - second level
color4: '#B3E5FC', // Medium light blue
color5: '#F3E5F5', // Lavender - third level
color6: '#FFF8E1', // Light amber - fourth level
color7: '#FFECB3', // Medium amber
color8: '#FFEBEE' // Light pink - fifth level
},
// Natural Progression - Best for environmental data and science applications
naturalProgression: {
color1: '#E0F7FA', // Light cyan - first level
color2: '#B2EBF2', // Medium cyan
color3: '#C8E6C9', // Light green - second level
color4: '#A5D6A7', // Medium green
color5: '#F0F4C3', // Light lime - third level
color6: '#FFE0B2', // Light orange - fourth level
color7: '#FFCC80', // Medium orange
color8: '#FFCDD2' // Light red - fifth level
},
// Colorblind-Friendly - Best for accessibility-focused and public-facing applications
colorblindFriendly: {
color1: '#F5F5F5', // Light gray - first level
color2: '#E0E0E0', // Medium light gray
color3: '#BBDEFB', // Light blue - second level
color4: '#90CAF9', // Medium light blue
color5: '#FFECB3', // Light amber - third level
color6: '#C8E6C9', // Light green - fourth level
color7: '#A5D6A7', // Medium green
color8: '#E1BEE7' // Light purple - fifth level
},
// Corporate Structure - Best for formal reports and corporate environments
corporateStructure: {
color1: '#ECEFF1', // Very light blue-gray - first level
color2: '#CFD8DC', // Light blue-gray - second level
color3: '#B0BEC5', // Medium blue-gray - third level
color4: '#90A4AE', // Medium-dark blue-gray - fourth level
color5: '#78909C', // Dark blue-gray - fifth level
color6: '#607D8B', // Deeper blue-gray
color7: '#546E7A', // Very dark blue-gray
color8: '#455A64' // Extremely dark blue-gray
}
};
// You can implement a function to get random color codes similar to the original example
let colorDeck = [];
const randomColorCode = () => {
// If the deck is empty, refill and shuffle it
if (colorDeck.length === 0) {
colorDeck = [...Object.values(COLOR_CODES)];
// Fisher-Yates shuffle algorithm
for (let i = colorDeck.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[colorDeck[i], colorDeck[j]] = [colorDeck[j], colorDeck[i]];
}
}
// Take the top color from our shuffled deck
return colorDeck.pop();
};
var SectionTypeGroups;
(function (SectionTypeGroups) {
SectionTypeGroups["Transactions"] = "transactions";
SectionTypeGroups["Processes"] = "processes";
SectionTypeGroups["System"] = "system";
SectionTypeGroups["FormPreview"] = "formPreview";
SectionTypeGroups["Home"] = "home";
SectionTypeGroups["Transaction"] = "transaction";
SectionTypeGroups["Report"] = "Report";
})(SectionTypeGroups || (SectionTypeGroups = {}));
var DocumentLitsLabelConfigInterfaceValueType;
(function (DocumentLitsLabelConfigInterfaceValueType) {
DocumentLitsLabelConfigInterfaceValueType["currency"] = "currency";
DocumentLitsLabelConfigInterfaceValueType["number"] = "number";
DocumentLitsLabelConfigInterfaceValueType["string"] = "string";
DocumentLitsLabelConfigInterfaceValueType["date"] = "date";
DocumentLitsLabelConfigInterfaceValueType["daysAgo"] = "daysAgo";
DocumentLitsLabelConfigInterfaceValueType["systemReference"] = "systemReference";
})(DocumentLitsLabelConfigInterfaceValueType || (DocumentLitsLabelConfigInterfaceValueType = {}));
/**
* Compares previous and current objects, returning only properties that changed.
* - Properties in prev but not in current: set to undefined
* - Properties in current but not in prev: included with current value
* - Properties in both with different values: included with current value
*
* @example
* // prev = {a: 8, b: 0}, current = {b: 5}
* // result = {b: 5, a: undefined}
*/
function getChangedProperties(prevPassed, currentPassed) {
// Handle edge cases
// remove undefined in prev
const prev = prevPassed ? Object.keys(prevPassed).reduce((acc, key) => {
if (prevPassed[key] !== undefined) {
acc[key] = prevPassed[key];
}
return acc;
}, {}) : undefined;
// remove undefined in current
const current = currentPassed ? Object.keys(currentPassed).reduce((acc, key) => {
if (currentPassed[key] !== undefined) {
acc[key] = currentPassed[key];
}
return acc;
}, {}) : undefined;
// if both are undefined return empty object
if (!prev && !current)
return {};
if (!prev)
return { ...current };
if (!current) {
return Object.keys(prev).reduce((acc, key) => {
acc[key] = undefined;
return acc;
}, {});
}
const changedProperties = {};
// Check properties from prev
for (const key in prev) {
if (!(key in current)) {
// Property removed in current
changedProperties[key] = undefined;
}
else if (!_isEqual(prev[key], current[key])) {
// Property value changed
changedProperties[key] = current[key];
}
}
// Check for new properties in current
for (const key in current) {
if (!(key in prev)) {
// New property added
changedProperties[key] = current[key];
}
}
return changedProperties;
}
function _isEqual(prev, next) {
if (prev === next)
return true;
const prevType = typeof prev;
const nextType = typeof next;
if (prevType !== nextType)
return false;
if (prevType === 'number') {
return (prev === next) || (Number.isNaN(prev) && Number.isNaN(next));
}
if (prevType === 'string' || prevType === 'boolean') {
return prev === next;
}
if (Array.isArray(prev) && Array.isArray(next)) {
if (prev.length !== next.length)
return false;
for (let i = 0; i < prev.length; i++) {
if (!_isEqual(prev[i], next[i]))
return false;
}
return true;
}
if (prevType === 'object' && prev !== null && next !== null) {
const prevConstructor = prev.constructor;
const nextConstructor = next.constructor;
if (prevConstructor !== nextConstructor)
return false;
if (prevConstructor === Date) {
return prev.getTime() === next.getTime();
}
if (prevConstructor === RegExp) {
return prev.source === next.source && prev.flags === next.flags;
}
const prevKeys = Object.keys(prev);
const nextKeys = Object.keys(next);
if (prevKeys.length !== nextKeys.length)
return false;
for (const key of prevKeys) {
if (!Object.prototype.hasOwnProperty.call(next, key))
return false;
if (!_isEqual(prev[key], next[key]))
return false;
}
return true;
}
return prev === null && next === null;
}
function groupData(items, keys, level = 0) {
if (keys.length === 0)
return new Map([[level, items]]); // No more keys, return items wrapped in a Map
const key = keys[0];
if (!key)
return new Map([[level, items]]);
const restKeys = keys.slice(1);
const grouped = new Map();
for (const item of items) {
const keyValue = item[key];
if (!grouped.has(keyValue)) {
grouped.set(keyValue, []);
}
grouped.get(keyValue).push(item);
}
if (restKeys.length > 0) {
grouped.forEach((value, key) => {
grouped.set(key, groupData(value, restKeys, level + 1));
});
}
return grouped;
}
function matrixTableGroupedData(array, propertyName, closedGroups, listConfig) {
// Use a Map to handle grouping which can be more efficient for lookups and updates
const numberProperties = listConfig.filter((config) => config.valueType === 'number'
|| config.valueType === 'currency').map((config) => config.formControlName);
const noneEmptyPropertyNames = propertyName.filter(n => !!n && (n.length > 0));
const noneEmptyArray = array.filter(a => !!a && Object.keys(a).length > 0);
const groupedData = groupData(noneEmptyArray, noneEmptyPropertyNames);
const result = flattenGroupedData(groupedData, propertyName);
return result;
function getSums(data) {
if (!Array.isArray(data)) {
return {};
}
const sums = {
IS_SUM_ROW: true
};
numberProperties.forEach((property) => {
sums[property] = `${data.reduce((acc, item) => acc + Number(item[property] || 0), 0)}`;
});
return sums;
}
function flattenGroupedData(grouped, keys, currentLevel = 0) {
let result = [];
if (keys.length === currentLevel) {
return Array.from(grouped.values()).flat();
}
grouped.forEach((value, key) => {
const isClosed = closedGroups.includes(key);
let _data = [];
_data = flattenGroupedData(value, keys, currentLevel + 1);
const objectProperty = propertyName[currentLevel];
const propertyConfig = listConfig.find(l => l.formControlName === objectProperty);
const groupConfig = {
name: key,
propertyLabel: propertyConfig?.label || objectProperty || '',
IS_GROUP_CONFIG: true,
LEVELS_BEFORE: Array.from({ length: currentLevel }, (_, i) => i + 1),
level: currentLevel,
totals: getSums(_data),
IS_CLOSED: isClosed
};
result.push(groupConfig);
result = result.concat(isClosed ? [] : _data);
});
return result;
}
}
function NgxTMatrixTableStoreSelectors(store) {
const state$ = store.select(state => state);
const reportName$ = store.select(state => state.reportName);
const list$ = store.select(state => state.list);
const listConfig$ = store.select(state => state.listConfig);
const aggregates$ = store.select(state => state.aggregates);
const groupOptions$ = store.select(state => state.groupOptions);
const closedGroups$ = store.select(state => state.closedGroups);
const isOpen$ = store.select(state => state.isOpen);
const groupChart$ = store.select(state => state.groupChart);
const draggedGroup$ = store.select(state => state.draggedGroup);
const scrollIndex$ = store.select(state => state.scrollIndex);
const tableColumns$ = listConfig$;
let itemsToDisplay = 0;
combineLatest([
groupOptions$,
listConfig$
])
.pipe(map(([groupOptions, listConfig]) => [{ formControlName: 'tree', label: '' }, ...listConfig || []]
.filter((col) => !col.isHidden).map((col) => ({
...col,
INCLUDED_IN_GROUP: Boolean(groupOptions?.columns?.includes(col.formControlName))
}))));
const displayedColumns$ = tableColumns$.pipe(map(tableColumns => tableColumns?.map((config) => config.formControlName) || []));
const groupLabelConfig$ = groupOptions$.pipe(map(groupOptions => groupOptions?.columns?.map((col, index) => ({ level: index, formControlName: index.toString(), label: '' })) || []));
const getGroupedData$ = combineLatest([list$,
listConfig$,
groupOptions$,
closedGroups$,
scrollIndex$]).pipe(map(([list, listConfig, groupOptions, closedGroups, scrollIndex]) => {
const groupBy = groupOptions?.columns || [];
const data = matrixTableGroupedData(list || [], groupBy, closedGroups, listConfig || []);
const filteredData = data.filter(d => !(d.name === undefined && d.IS_GROUP_CONFIG));
const numberOfFilteredItems = filteredData.length;
const pageSize = 100; // Define your desired multiple
if (numberOfFilteredItems === 0) {
return filteredData.slice(0, 0);
}
if (scrollIndex <= pageSize) {
return filteredData.slice(0, pageSize);
}
itemsToDisplay = Math.ceil((scrollIndex + 1) / pageSize) * pageSize;
return filteredData.slice(0, itemsToDisplay);
}));
const groupedColumns$ = combineLatest([
groupOptions$,
listConfig$
]).pipe(map(([groupOptions, listConfig]) => (groupOptions?.columns?.filter((col) => !groupOptions.hiddenColumns?.includes(col)) || []).map(groupCol => ({
formControlName: groupCol,
label: listConfig?.find((config) => config.formControlName === groupCol)?.label || ''
}))));
return {
state$,
reportName$,
list$,
listConfig$,
closedGroups$,
isOpen$,
aggregates$,
groupOptions$,
tableColumns$,
displayedColumns$,
groupLabelConfig$,
getGroupedData$,
groupedColumns$,
groupChart$,
draggedGroup$,
scrollIndex$
};
}
function NgxTMatrixTableStoreActions(store) {
return {
setReportName: store.updater((state, reportName) => ({
...state,
reportName
})),
setList: store.updater((state, list) => ({
...state,
list
})),
setListConfig: store.updater((state, listConfig) => ({
...state,
listConfig
})),
setAggregates: store.updater((state, aggregates) => ({
...state,
aggregates
})),
setGroupOptions: store.updater((state, groupOptions) => ({
...state,
groupOptions
})),
toggleMenu: store.updater((state, row) => ({
...state,
isOpen: _isEqual(state.isOpen, row) ? undefined : row
})),
setGroupChart: store.updater((state, groupChart) => ({
...state,
groupChart
})),
setDraggedGroup: store.updater((state, draggedGroup) => ({
...state,
draggedGroup
})),
setScrollIndex: store.updater((state, scrollIndex) => ({
...state,
scrollIndex
})),
toggleCloseGroup: store.updater((state, name) => {
const closedGroups = state.closedGroups || [];
const isClosed = closedGroups.includes(name);
return {
...state,
closedGroups: isClosed ? closedGroups.filter(g => g !== name) : [...closedGroups, name]
};
}),
removeGroupColumn: store.updater((state, columnName) => {
return {
...state,
groupOptions: state.groupOptions ? {
...state.groupOptions,
columns: state.groupOptions.columns?.filter((col) => col !== columnName)
} : null
};
}),
dropGroupColumn: store.updater((state) => {
const newGroup = state.draggedGroup;
const doesNotInclude = !state.groupOptions?.columns?.includes(newGroup || '');
if (doesNotInclude && newGroup !== undefined && newGroup !== '') {
return {
...state,
groupOptions: {
...state.groupOptions,
columns: [...(state.groupOptions?.columns || []), newGroup]
}
};
}
return state;
}),
dropGroupingToSortGroups: store.updater((state, event) => {
const newColumns = [...(state.groupOptions?.columns || [])];
moveItemInArray(newColumns, event.previousIndex, event.currentIndex);
return {
...state,
groupOptions: {
...state.groupOptions,
columns: newColumns
}
};
}),
addToGrouping: store.updater((state, formControlName) => {
let columns = state.groupOptions?.columns || [];
if (!columns.includes(formControlName)) {
columns = [...columns, formControlName];
}
else {
columns = columns.filter((col) => col !== formControlName);
}
return {
...state,
groupOptions: {
...state.groupOptions,
columns
}
};
})
};
}
function toCamelCase(arrayOfObjects) {
return arrayOfObjects.map((obj) => {
const newObj = {};
Object.keys(obj).forEach((key) => {
const newKey = ''.concat(key);
const camelCaseKey = camelize(newKey);
newObj[camelCaseKey] = obj[key];
});
return newObj;
});
}
function camelize(str) {
if (!!str) {
return str
.replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
return index === 0 ? word.toLowerCase() : word.toUpperCase();
})
.replace(/\s+/g, '');
}
else {
return;
}
}
const getFileName = (name) => {
let timeSpan = new Date().toISOString();
let sheetName = name || 'ExportResult';
let fileName = `${sheetName}-${timeSpan}`;
return {
sheetName,
fileName,
};
};
class TableUtil {
static exportTableToExcel(tableId, name) {
let { sheetName, fileName } = getFileName(name);
let targetTableElm = document.getElementById(tableId);
let wb = XLSX.utils.table_to_book(targetTableElm, {
sheet: sheetName,
});
XLSX.writeFile(wb, `${fileName}.xlsx`);
}
static exportArrayToExcel(arr, name) {
const { sheetName, fileName } = getFileName(name);
const wb = XLSX.utils.book_new();
// Process the main array
const mainArray = arr.map(item => this.processSimpleObject(item));
const mainSheet = XLSX.utils.json_to_sheet(mainArray);
XLSX.utils.book_append_sheet(wb, mainSheet, this.sanitizeSheetName(sheetName));
// Process nested arrays
arr.forEach((item, index) => {
const arrayProperties = this.findArrayProperties(item);
arrayProperties.forEach((arrayData, propertyName) => {
// Only process if the array contains data
if (arrayData.length > 0) {
// Create a sheet name combining property name and parent index
const nestedSheetName = this.sanitizeSheetName(`${propertyName}_${index + 1}`);
// Process the nested array items
const processedArray = arrayData.map(nestedItem => {
if (typeof nestedItem === 'object' && nestedItem !== null) {
return this.processSimpleObject(nestedItem);
}
// If it's a primitive value, wrap it in an object
return { value: nestedItem };
});
// Create and append the sheet if it has data
if (processedArray.length > 0) {
const nestedSheet = XLSX.utils.json_to_sheet(processedArray);
try {
XLSX.utils.book_append_sheet(wb, nestedSheet, nestedSheetName);
}
catch (e) {
const msg = e instanceof Error ? e.message : 'Unknown error';
console.warn(`Could not create sheet for ${nestedSheetName}: ${msg}`);
}
}
}
});
});
// Write the workbook to file
XLSX.writeFile(wb, `${fileName}.xlsx`);
}
static onFileChange(event) {
return new Promise((resolve, reject) => {
try {
const target = event.target;
if (target.files.length !== 1)
throw new Error('Cannot use multiple files');
const reader = new FileReader();
reader.onload = (e) => {
const bstr = e.target.result;
const workbook = XLSX.read(bstr, { type: 'binary' });
const firstSheetName = workbook.SheetNames[0];
if (!firstSheetName)
throw new Error('No sheet found');
const worksheet = workbook.Sheets[firstSheetName];
if (!worksheet)
throw new Error('No worksheet found');
const arrayData = XLSX.utils.sheet_to_json(worksheet, { raw: false });
// Iterate through each row and format date cells
arrayData.forEach((row) => {
for (const key in row) {
if (row.hasOwnProperty(key) &&
typeof row[key] === 'number' &&
XLSX.SSF.isValidDate(row[key])) {
row[key] = XLSX.SSF.format_date(row[key], 'yyyy-mm-dd');
}
}
});
// Do something with arrayData
resolve(toCamelCase(arrayData));
};
reader.onerror = (e) => reject(e);
const file = target.files[0];
if (!file)
throw new Error('No file found');
reader.readAsBinaryString(file);
}
catch (error) {
reject(error);
}
});
}
static processSimpleObject(obj) {
const newObj = {};
for (const key in obj) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
// If it's not an array, convert to string
if (!Array.isArray(obj[key])) {
newObj[key] = JSON.stringify(obj[key]);
}
// Arrays will be handled separately, so we skip them here
}
else {
newObj[key] = obj[key];
}
}
return newObj;
}
static findArrayProperties(obj) {
const arrayProps = new Map();
for (const key in obj) {
if (Array.isArray(obj[key])) {
arrayProps.set(key, obj[key]);
}
}
return arrayProps;
}
static sanitizeSheetName(name) {
// Excel sheet names can't exceed 31 characters and can't contain certain characters
return name.replace(/[\[\]\*\/\\\?\:]/g, '_').substring(0, 31);
}
}
function NgxTMatrixTableStoreEffects(store) {
const exportData = store.effect((trigger$) => trigger$.pipe(switchMap(() => combineLatest([
store.selectors.getGroupedData$,
store.selectors.reportName$
]).pipe(tap(([data, reportName]) => {
TableUtil.exportArrayToExcel(data || [], reportName || 'report');
})))));
const mainShowChart = store.effect((trigger$) => trigger$.pipe(switchMap(() => combineLatest([
store.selectors.getGroupedData$,
store.selectors.listConfig$,
store.selectors.groupOptions$
]).pipe(tap(([data, listConfig, groupOptions]) => {
const mainGroups = data.filter(row => !!row?.IS_GROUP_CONFIG && row?.level === 0);
const numberProperties = listConfig?.filter((config) => config.valueType === 'number' || config.valueType === 'currency') || [];
const groupChart = { groups: mainGroups, numberProperties, mainTitle: groupOptions?.columns?.[0] || '' };
store.actions.setGroupChart(groupChart);
})))));
const showReport = store.effect((row$) => row$.pipe(switchMap((row) => combineLatest([
store.selectors.getGroupedData$,
store.selectors.listConfig$
]).pipe(tap(([data, listConfig]) => {
if (!!row?.IS_GROUP_CONFIG && row?.level !== undefined) {
const i = data.map(d => d?.name).indexOf(row.name);
const mainGroups = [];
for (let index = i + 1; index < data.length; index++) {
const nextRow = data[index];
if (mainGroups.length === 0 && !nextRow?.IS_GROUP_CONFIG
|| nextRow?.IS_GROUP_CONFIG && nextRow?.level === row.level
|| mainGroups.length === 0 && nextRow?.level === row.level) {
break;
}
if (nextRow?.IS_GROUP_CONFIG && nextRow?.level === row.level + 1) {
mainGroups.push(nextRow);
}
}
const numberProperties = listConfig?.filter((config) => config.valueType === 'number' || config.valueType === 'currency') || [];
const groupChart = {
groups: mainGroups,
numberProperties,
mainTitle: row.propertyLabel + ': ' + row.name
};
store.actions.setGroupChart(groupChart);
}
})))));
const foldUnFoldGroup = store.effect((row$) => row$.pipe(switchMap((row) => combineLatest([
store.selectors.getGroupedData$
]).pipe(tap(([data]) => {
const i = data.map(d => d?.name).indexOf(row.name);
const mainGroups = [];
for (let index = i + 1; index < data.length; index++) {
const nextRow = data[index];
if (mainGroups.length === 0 && !nextRow?.IS_GROUP_CONFIG
|| nextRow?.IS_GROUP_CONFIG && nextRow?.level === row.level
|| mainGroups.length === 0 && nextRow?.level === row.level) {
break;
}
if (nextRow?.IS_GROUP_CONFIG && nextRow?.level === row.level + 1) {
mainGroups.push(nextRow);
store.actions.toggleCloseGroup(nextRow.name);
}
}
})))));
return {
exportData,
mainShowChart, foldUnFoldGroup, showReport
};
}
const initialMscoaState = {
reportName: null,
list: null,
listConfig: null,
aggregates: null,
groupOptions: null,
closedGroups: [],
isOpen: undefined,
groupChart: null,
draggedGroup: '',
scrollIndex: 0
};
class NgxTMatrixTableStoreService extends ComponentStore {
constructor() {
super(initialMscoaState);
this.selectors = NgxTMatrixTableStoreSelectors(this);
this.actions = NgxTMatrixTableStoreActions(this);
this.effects = NgxTMatrixTableStoreEffects(this); // Assuming effects are also part of actions for simplicity
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxTMatrixTableStoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxTMatrixTableStoreService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxTMatrixTableStoreService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [] });
class NgxTMatrixTableComponent {
get tableContainerHeight() {
return this.scrollContainer?.nativeElement.clientHeight;
}
set list(value) {
this.store.actions.setList(value);
}
set reportName(value) {
this.store.actions.setReportName(value);
}
set listConfig(value) {
this.store.actions.setListConfig(value);
}
set aggregates(value) {
this.store.actions.setAggregates(value);
}
set groupOptions(value) {
this.store.actions.setGroupOptions(value);
}
constructor(dialog) {
this.dialog = dialog;
this.store = inject(NgxTMatrixTableStoreService);
this.tableColumns$ = this.store.selectors.tableColumns$;
this.displayedColumns$ = this.store.selectors.displayedColumns$;
this.groupLabelConfig$ = this.store.selectors.groupLabelConfig$;
this.groupOptions$ = this.store.selectors.groupOptions$;
this.getGroupedData$ = this.store.selectors.getGroupedData$;
this.groupedColumns$ = this.store.selectors.groupedColumns$;
this.isOpen$ = this.store.selectors.isOpen$;
this.groupChart$ = this.store.selectors.groupChart$;
this.closedGroups$ = this.store.selectors.closedGroups$;
this.draggedGroup$ = this.store.selectors.draggedGroup$;
this.vm$ = combineLatest([
this.groupOptions$,
this.tableColumns$,
this.displayedColumns$,
this.groupLabelConfig$,
this.getGroupedData$.pipe(distinctUntilChanged((prev, curr) => _isEqual(prev, curr))),
this.groupedColumns$,
this.isOpen$,
this.closedGroups$,
this.groupChart$,
this.draggedGroup$
]).pipe(map(([groupOptions, tableColumns, displayedColumns, groupLabelConfig, getGroupedData, groupedColumns, isOpen, closedGroups, groupChart, draggedGroup]) => ({
groupOptions,
tableColumns,
displayedColumns,
groupLabelConfig,
getGroupedData,
groupedColumns,
isOpen,
closedGroups,
groupChart,
draggedGroup
})));
this.ValueTypes = DocumentLitsLabelConfigInterfaceValueType;
this.levelColors = randomColorCode();
}
ngOnInit() {
this.setLevelColors();
}
// @HostListener('document:click', ['$event'])
// onClickOutside(event: Event) {
// if (!this.elementRef.nativeElement.contains(event.target)) {
// this.isOpen = undefined
// }
// }
toggleMenu(row) {
this.store.actions.toggleMenu(row);
}
remove(groupCol) {
this.store.actions.removeGroupColumn(groupCol);
}
drop(event) {
this.store.actions.dropGroupColumn();
}
export() {
this.store.effects.exportData(of());
}
dropChip(event) {
this.store.actions.dropGroupingToSortGroups(event);
}
addToGrouping(formControlName) {
this.store.actions.addToGrouping(formControlName);
}
toggleCloseGroup(name) {
this.store.actions.toggleCloseGroup(name);
}
mainShowChart() {
this.store.effects.mainShowChart(of());
}
foldUnFoldGroup(row) {
this.store.effects.foldUnFoldGroup(row);
}
setDraggedGroup(col) {
this.store.actions.setDraggedGroup(col);
}
showReport(row) {
this.store.effects.showReport(of(row));
}
// Coding
setLevelColors() {
// Set the CSS variables
Object.keys(this.levelColors).forEach((key, index) => {
document.body.style.setProperty(`--custom-level-color-${index + 1}`, this.levelColors[key]);
});
}
get colorPalletArray() {
const colors = COLOR_CODES;
return Object.values(colors).map(v => Object.values(v));
}
selectColor(colors) {
const c = colors.reduce((acc, color, index) => {
acc[`color${index + 1}`] = color;
return acc;
}, {});
this.updateColors(c);
}
// Method to update colors dynamically
updateColors(newColors) {
this.levelColors = { ...this.levelColors, ...newColors };
this.setLevelColors();
}
scrollIndex(event) {
this.store.actions.setScrollIndex(event);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxTMatrixTableComponent, deps: [{ token: i1$2.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NgxTMatrixTableComponent, isStandalone: true, selector: "app-ngx-t-matrix-table", inputs: { list: "list", reportName: "reportName", listConfig: "listConfig", aggregates: "aggregates", groupOptions: "groupOptions" }, providers: [NgxTMatrixTableStoreService,], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["viewPortScroll"], descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"( vm$ |async) as vm\">\r\n\r\n @if (\r\n !!vm.groupChart\r\n ) {\r\n\r\n <mat-toolbar class=\"chartToolbar\">\r\n <h1 style=\" font-weight: bold;\r\n font-size: 1.4rem; \r\n text-transform: capitalize;\">\r\n {{vm.groupChart.mainTitle}}\r\n </h1>\r\n <span class=\"spacer\"></span>\r\n <button (click)=\"vm.groupChart = null\" mat-stroked-button>\r\n Close\r\n <mat-icon>\r\n close\r\n </mat-icon>\r\n </button>\r\n </mat-toolbar>\r\n\r\n\r\n\r\n <mat-divider></mat-divider>\r\n <br>\r\n <app-grouping-chart [groups]=\"vm.groupChart.groups\"\r\n [numberProperties]=\"vm.groupChart.numberProperties\"></app-grouping-chart>\r\n }@else {\r\n\r\n\r\n <section class=\"tableContainer\" #tableContainer>\r\n <mat-toolbar style=\"background: none;\" (cdkDropListDropped)=\"dropChip($event)\"\r\n [cdkDropListData]=\"vm.groupOptions?.columns ||[]\" cdkDropList>\r\n\r\n\r\n @for(groupCol of vm.groupedColumns; track groupCol){\r\n <mat-chip-option color=\"tertiary\" class=\"dragBox\" [style.margin-left]=\"$index !==0? '5px':''\" cdkDrag selected\r\n (removed)=\"remove(groupCol.formControlName)\">\r\n <div class=\"placeholder\" *cdkDragPlaceholder></div>\r\n {{ groupCol.label }}\r\n <button matChipRemove [attr.aria-label]=\"'remove ' + groupCol.label\">\r\n <mat-icon color=\"warn\">cancel</mat-icon>\r\n </button>\r\n </mat-chip-option>\r\n }\r\n @if (vm.groupedColumns.length === 0) {\r\n\r\n <div\r\n style=\"font-size: 14px; font-weight: 400; opacity: 0.7; padding: 8px; color: #666; display: flex; align-items: center;\">\r\n Drag column headers here to group your data\r\n </div>\r\n\r\n\r\n }\r\n\r\n\r\n <span class=\"spacer\"></span>\r\n\r\n <button [matMenuTriggerFor]=\"colorPallet\" mat-icon-button matTooltip=\"Select table color coding\">\r\n <mat-icon>\r\n palette\r\n </mat-icon>\r\n </button>\r\n\r\n <button (click)=\"mainShowChart()\" mat-icon-button matTooltip=\"Show main grouping chart analytics\">\r\n <mat-icon>\r\n analytics\r\n </mat-icon>\r\n </button>\r\n <button (click)=\"export()\" mat-icon-button matTooltip=\"Export to excel\">\r\n <mat-icon>\r\n download\r\n </mat-icon>\r\n </button>\r\n\r\n </mat-toolbar>\r\n\r\n\r\n @defer (on viewport; prefetch on idle) {\r\n <cdk-virtual-scroll-viewport (scrolledIndexChange)=\"scrollIndex($event)\" \r\n #viewPortScroll [itemSize]=\"30\"\r\n style=\"resize: vertical;height:400px;\" >\r\n <table (cdkDropListDropped)=\"drop($event)\" \r\n [multiTemplateDataRows]=\"true\"\r\n cdkDropList mat-table [dataSource]=\"vm.getGroupedData||[]\"\r\n style=\"border-collapse: collapse;\" class=\"example-table\" matSort matSortActive=\"created\" matSortDisableClear\r\n matSortDirection=\"desc\">\r\n @for (col of vm.tableColumns||[]; track col.formControlName) {\r\n <ng-container [matColumnDef]=\"col.formControlName\">\r\n <th cdkDrag (cdkDragStarted)=\"setDraggedGroup(col.formControlName)\"\r\n [attr.data-column-size]=\"col.formControlName\"\r\n style=\"width: fit-content !important; white-space: nowrap !important;\" mat-header-cell *matHeaderCellDef>\r\n @if($index === 0 && vm.closedGroups.length > 0){\r\n <button class=\"cancelGrouping\" (click)=\"vm.closedGroups= []\" color=\"warn\" mat-icon-button>\r\n <mat-icon>highlight_off</mat-icon>\r\n </button>\r\n } @else {\r\n <div class=\"colTitle\" style=\"display: inline-flex; align-items: center;\"\r\n [style.color]=\"col.INCLUDED_IN_GROUP?'':''\" [matTooltip]=\"'Click to group by '+col.label\"\r\n (click)=\"addToGrouping(col.formControlName)\" matRipple>\r\n {{col.label}}\r\n </div>\r\n }\r\n </th>\r\n <td mat-cell style=\"\r\n width: fit-content !important;\r\n white-space: nowrap !important;\" [style.border-top]=\"!!row?.IS_SUM_ROW && $index !==0? '1px solid black':''\"\r\n [style.border-bottom-color]=\"!!row?.IS_GROUP_CONFIG ?'#ffffff00':''\"\r\n [style.background]=\"!!row?.IS_GROUP_CONFIG?'':'none'\"\r\n [style.overflow]=\"!!row?.IS_GROUP_CONFIG ?'visible':''\" *matCellDef=\"let row\">\r\n\r\n @if (!!row?.IS_GROUP_CONFIG ) {\r\n @if( $index ===0){\r\n <div disabled=\"true\" class=\"levelClass\" *ngFor=\"let item of row.LEVELS_BEFORE\" mat-button>\r\n <span>- <div class=\"verticalCenterLine\"></div></span>\r\n </div>\r\n <button (click)=\"toggleCloseGroup(row['name']);\" mat-button>\r\n <mat-icon>{{row['IS_CLOSED'] ?'keyboard_arrow_right':'keyboard_arrow_down'}}</mat-icon>\r\n <span\r\n style=\"color: var(--mat-table-header-headline-color, var(--mat-sys-on-surface, rgba(0, 0, 0, 0.87)));\">\r\n {{row['name']}}\r\n </span>\r\n </button>\r\n\r\n <span class=\"rowMenu\">\r\n <button [color]=\"vm.isOpen?.name === row?.name && vm.isOpen?.level === row.level?'warn':''\"\r\n class=\"groupOptions\" mat-icon-button (click)=\"toggleMenu(row)\">\r\n\r\n <mat-icon>\r\n {{vm.isOpen?.name === row?.name && vm.isOpen?.level === row.level? 'close':'more_horiz'}}\r\n </mat-icon>\r\n </button>\r\n @if(vm.isOpen?.name === row?.name && vm.isOpen?.level === row.level){\r\n <button (click)=\"showReport(row)\" mat-icon-button>\r\n <mat-icon>\r\n analytics\r\n </mat-icon>\r\n </button>\r\n <button (click)=\"foldUnFoldGroup(row)\" mat-icon-button>\r\n <mat-icon>\r\n compress\r\n </mat-icon>\r\n </button>\r\n }\r\n\r\n\r\n </span>\r\n\r\n\r\n } @else if (row['totals'][col.formControlName]) {\r\n <h5 [style.opacity]=\"row['IS_CLOSED']&& row['level'] !==0 ? '0.5':1\">\r\n {{(row['totals'][col.formControlName]) |formatData: col.valueType}}\r\n\r\n </h5>\r\n }\r\n }\r\n\r\n @if (!row['IS_GROUP_CONFIG'] && $index ===0 ) {\r\n @for (item of row['LEVELS_BEFORE']||[]; track item) {\r\n <div disabled=\"true\" class=\"levelClass\" mat-button>\r\n <span>\r\n @if ($last) {\r\n \u2022\r\n }@else {\r\n -\r\n }\r\n\r\n <div class=\"verticalCenterLine\"></div>\r\n </span>\r\n </div>\r\n }\r\n\r\n }\r\n @if (!row?.IS_GROUP_CONFIG && $index !==0 ){\r\n\r\n <div \r\n class=\"row-value-col\"\r\n >\r\n {{ row[col.formControlName]|formatData: col.valueType }}\r\n\r\n\r\n\r\n </div>\r\n\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n <tr mat-header-row *matHeaderRowDef=\"vm.displayedColumns\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: vm.displayedColumns;\" [class.is-group-row]=\"!!row?.IS_GROUP_CONFIG\"\r\n [class]=\"'row-'+row.level\" [class.is-sum-row]=\"!!row['IS_SUM_ROW']\"\r\n style=\"border-bottom: solid 1px rgba(0, 0, 0, .04)\"></tr>\r\n </table>\r\n </cdk-virtual-scroll-viewport>\r\n } @placeholder {\r\n <div style=\"display: flex; justify-content: center; align-items: center; padding: 40px; flex-direction: column;\">\r\n <mat-spinner diameter=\"50\"></mat-spinner>\r\n <p style=\"margin-top: 15px;\">Loading table data...</p>\r\n </div>\r\n } @loading {\r\n <div style=\"padding: 20px; text-align: center;\">\r\n <mat-spinner diameter=\"50\"></mat-spinner>\r\n <p style=\"margin-top: 10px;\">Preparing table view...</p>\r\n </div>\r\n } @error {\r\n <p style=\"color: red; text-align: center; padding: 20px;\">Table failed to load.</p>\r\n }\r\n </section>\r\n }\r\n\r\n</ng-container>\r\n\r\n\r\n<mat-menu #colorPallet=\"matMenu\">\r\n <p>Select Table color coding</p>\r\n\r\n <button *ngFor=\"let item of colorPalletArray\" (click)=\"selectColor(item)\" mat-menu-item>\r\n\r\n <div [style.background]=\"color\" *ngFor=\"let color of item\" style=\"width: 16px;height:24px;display:inline-block\">\r\n\r\n </div>\r\n\r\n\r\n\r\n </button>\r\n\r\n</mat-menu>", styles: [".example-table{width:auto!important;margin:8px!important;border-spacing:0!important;border-collapse:collapse!important}.example-table .mat-mdc-cell,.example-table .mat-mdc-header-cell{padding:0!important;width:fit-content!important;min-width:fit-content!important;max-width:fit-content!important;white-space:nowrap!important;border:none!important}.example-table .mat-mdc-cell>*,.example-table .mat-mdc-header-cell>*{margin:0!important;padding:0!important}.example-table .mat-mdc-row,.example-table .mat-mdc-header-row{display:table-row!important;padding:0!important;height:auto!important;min-height:0!important}.example-table .mat-mdc-row{--level-color-1: var(--custom-level-color-1, #caf0f8);--level-color-2: var(--custom-level-color-2, #ADE8F4);--level-color-3: var(--custom-level-color-3, #90E0EF);--level-color-4: var(--custom-level-color-4, #48CAE4);--level-color-5: var(--custom-level-color-5, #00B4D8);--level-color-6: var(--custom-level-color-6, #0096C7);--level-color-7: var(--custom-level-color-7, #0077B6);--level-color-8: var(--custom-level-color-8, #023E8A);border-bottom:solid 1px rgba(0,0,0,.04);transition:background-color .2s ease}.example-table .mat-mdc-row.row-0{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.row-1{background-color:var(--level-color-1)!important;color:var(--level-text-color-1, #000000)}.example-table .mat-mdc-row.row-2{background-color:var(--level-color-2)!important;color:var(--level-text-color-2, #000000)}.example-table .mat-mdc-row.row-3{background-color:var(--level-color-3)!important;color:var(--level-text-color-3, #000000)}.example-table .mat-mdc-row.row-4{background-color:var(--level-color-4)!important;color:var(--level-text-color-4, #000000)}.example-table .mat-mdc-row.row-5{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.row-6{background-color:var(--level-color-1)!important;color:var(--level-text-color-1, #000000)}.example-table .mat-mdc-row.row-7{background-color:var(--level-color-2)!important;color:var(--level-text-color-2, #000000)}.example-table .mat-mdc-row.row-8{background-color:var(--level-color-3)!important;color:var(--level-text-color-3, #000000)}.example-table .mat-mdc-row.row-9{background-color:var(--level-color-4)!important;color:var(--level-text-color-4, #000000)}.example-table .mat-mdc-row.row-10{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.row-11{background-color:var(--level-color-1)!important;color:var(--level-text-color-1, #000000)}.example-table .mat-mdc-row.row-12{background-color:var(--level-color-2)!important;color:var(--level-text-color-2, #000000)}.example-table .mat-mdc-row.row-13{background-color:var(--level-color-3)!important;color:var(--level-text-color-3, #000000)}.example-table .mat-mdc-row.row-14{background-color:var(--level-color-4)!important;color:var(--level-text-color-4, #000000)}.example-table .mat-mdc-row.row-15{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.row-16{background-color:var(--level-color-1)!important;color:var(--level-text-color-1, #000000)}.example-table .mat-mdc-row.row-17{background-color:var(--level-color-2)!important;color:var(--level-text-color-2, #000000)}.example-table .mat-mdc-row.row-18{background-color:var(--level-color-3)!important;color:var(--level-text-color-3, #000000)}.example-table .mat-mdc-row.row-19{background-color:var(--level-color-4)!important;color:var(--level-text-color-4, #000000)}.example-table .mat-mdc-row.row-20{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.is-group-row{font-weight:500}.example-table .mat-mdc-row.is-sum-row{font-weight:700}.example-table .mat-mdc-row:hover{position:relative}.example-table .mat-mdc-row:hover:after{content:\"\";position:absolute;inset:0;background-color:#0011ff0a;pointer-events:none}.example-table [class*=row-].row-1{padding-left:16px}.example-table [class*=row-].row-2{padding-left:32px}.example-table [class*=row-].row-3{padding-left:48px}.example-table [class*=row-].row-4{padding-left:64px}.example-table [class*=row-].row-5{padding-left:80px}.example-table [class*=row-].row-6{padding-left:96px}.example-table [class*=row-].row-7{padding-left:112px}.example-table [class*=row-].row-8{padding-left:128px}.example-table [class*=row-].row-9{padding-left:144px}.example-table [class*=row-].row-10{padding-left:160px}.example-table [class*=row-].row-11{padding-left:176px}.example-table [class*=row-].row-12{padding-left:192px}.example-table [class*=row-].row-13{padding-left:208px}.example-table [class*=row-].row-14{padding-left:224px}.example-table [class*=row-].row-15{padding-left:240px}.example-table [class*=row-].row-16{padding-left:256px}.example-table [class*=row-].row-17{padding-left:272px}.example-table [class*=row-].row-18{padding-left:288px}.example-table [class*=row-].row-19{padding-left:304px}.example-table [class*=row-].row-20{padding-left:320px}.example-table button.mat-mdc-icon-button{width:24px!important;height:24px!important;padding:0!important;line-height:24px!important}.example-table .mat-mdc-cell:not(:empty),.example-table .mat-mdc-header-cell:not(:empty){padding:0 4px!important}.example-table .mat-mdc-header-row{background-color:var(--mat-sys-surface-container)!important}::ng-deep .mat-mdc-table{background:transparent!important}::ng-deep .mat-mdc-table .mat-mdc-row:not([class*=mat-elevation-z]){box-shadow:none!important}.levelClass{width:18px;margin-right:8px;display:inline-block;text-align:center;color:#243d91;opacity:.6}.verticalCenterLine{border-right:1px solid #243d91;width:8px;height:12px;opacity:.4;position:absolute}.groupOptions{opacity:.2;height:fit-content}.groupOptions:hover{opacity:1}.rowMenu{display:inline-flex;margin-left:8px;margin-right:8px;align-items:center;justify-content:flex-end;gap:4px;vertical-align:middle;z-index:2}small{opacity:.7}.tableContainer{flex-grow:1;overflow-y:auto;max-width:100vw;padding:0;margin-left:16px;margin-right:16px;min-height:0;border:solid 1px var(--mat-divider-color, var(--mat-sys-outline));border-radius:8px 8px 0}.row-value-col{min-height:37px;display:flex;align-items:center;max-width:200px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;transition:max-width .35s ease-in-out,background-color .35s ease-in-out,box-shadow .35s ease-in-out;position:relative;cursor:default}.row-value-col:hover{white-space:normal;overflow:visible;text-overflow:clip;min-height:fit-content}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "component", type: GroupingChartComponent, selector: "app-grouping-chart", inputs: ["groups", "numberProperties"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i3.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i3.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i3.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i4.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "directive", type: i4.MatChipRemove, selector: "[matChipRemove]" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i5.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i5.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i8.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i8.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i9.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i4$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i2$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [i2.NgForOf, i3.CdkDropList, i3.CdkDrag, i6.MatTooltip, i7.MatIcon, i8.MatButton, i8.MatIconButton, i12.MatTable, i12.MatHeaderCellDef, i12.MatHeaderRowDef, i12.MatColumnDef, i12.MatCellDef, i12.MatRowDef, i12.MatHeaderCell, i12.MatCell, i12.MatHeaderRow, i12.MatRow, import('./ngx-t-reports-format-Data.pipe-B5jqpvJP.mjs').then(m => m.FormatDataPipe), i13.CdkFixedSizeVirtualScroll, i13.CdkVirtualScrollViewport]] }); }
}
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "19.2.14", ngImport: i0, type: NgxTMatrixTableComponent, resolveDeferredDeps: () => [import('./ngx-t-reports-format-Data.pipe-B5jqpvJP.mjs').then(m => m.FormatDataPipe)], resolveMetadata: FormatDataPipe => ({ decorators: [{
type: Component,
args: [{ selector: 'app-ngx-t-matrix-table', standalone: true, imports: [CommonModule, GroupingChartComponent, DragDropModule, MatChipsModule,
CdkDropList, CdkDrag, MatMenuModule,
MatTooltipModule,
MatIconModule,
MatButtonModule,
MatTableModule,
MatToolbarModule,
FormatDataPipe,
MatProgressSpinnerModule,
ScrollingModule,
MatDividerModule], providers: [NgxTMatrixTableStoreService,], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *ngIf=\"( vm$ |async) as vm\">\r\n\r\n @if (\r\n !!vm.groupChart\r\n ) {\r\n\r\n <mat-toolbar class=\"chartToolbar\">\r\n <h1 style=\" font-weight: bold;\r\n font-size: 1.4rem; \r\n text-transform: capitalize;\">\r\n {{vm.groupChart.mainTitle}}\r\n </h1>\r\n <span class=\"spacer\"></span>\r\n <button (click)=\"vm.groupChart = null\" mat-stroked-button>\r\n Close\r\n <mat-icon>\r\n close\r\n </mat-icon>\r\n </button>\r\n </mat-toolbar>\r\n\r\n\r\n\r\n <mat-divider></mat-divider>\r\n <br>\r\n <app-grouping-chart [groups]=\"vm.groupChart.groups\"\r\n [numberProperties]=\"vm.groupChart.numberProperties\"></app-grouping-chart>\r\n }@else {\r\n\r\n\r\n <section class=\"tableContainer\" #tableContainer>\r\n <mat-toolbar style=\"background: none;\" (cdkDropListDropped)=\"dropChip($event)\"\r\n [cdkDropListData]=\"vm.groupOptions?.columns ||[]\" cdkDropList>\r\n\r\n\r\n @for(groupCol of vm.groupedColumns; track groupCol){\r\n <mat-chip-option color=\"tertiary\" class=\"dragBox\" [style.margin-left]=\"$index !==0? '5px':''\" cdkDrag selected\r\n (removed)=\"remove(groupCol.formControlName)\">\r\n <div class=\"placeholder\" *cdkDragPlaceholder></div>\r\n {{ groupCol.label }}\r\n <button matChipRemove [attr.aria-label]=\"'remove ' + groupCol.label\">\r\n <mat-icon color=\"warn\">cancel</mat-icon>\r\n </button>\r\n </mat-chip-option>\r\n }\r\n @if (vm.groupedColumns.length === 0) {\r\n\r\n <div\r\n style=\"font-size: 14px; font-weight: 400; opacity: 0.7; padding: 8px; color: #666; display: flex; align-items: center;\">\r\n Drag column headers here to group your data\r\n </div>\r\n\r\n\r\n }\r\n\r\n\r\n <span class=\"spacer\"></span>\r\n\r\n <button [matMenuTriggerFor]=\"colorPallet\" mat-icon-button matTooltip=\"Select table color coding\">\r\n <mat-icon>\r\n palette\r\n </mat-icon>\r\n </button>\r\n\r\n <button (click)=\"mainShowChart()\" mat-icon-button matTooltip=\"Show main grouping chart analytics\">\r\n <mat-icon>\r\n analytics\r\n </mat-icon>\r\n </button>\r\n <button (click)=\"export()\" mat-icon-button matTooltip=\"Export to excel\">\r\n <mat-icon>\r\n download\r\n </mat-icon>\r\n </button>\r\n\r\n </mat-toolbar>\r\n\r\n\r\n @defer (on viewport; prefetch on idle) {\r\n <cdk-virtual-scroll-viewport (scrolledIndexChange)=\"scrollIndex($event)\" \r\n #viewPortScroll [itemSize]=\"30\"\r\n style=\"resize: vertical;height:400px;\" >\r\n <table (cdkDropListDropped)=\"drop($event)\" \r\n [multiTemplateDataRows]=\"true\"\r\n cdkDropList mat-table [dataSource]=\"vm.getGroupedData||[]\"\r\n style=\"border-collapse: collapse;\" class=\"example-table\" matSort matSortActive=\"created\" matSortDisableClear\r\n matSortDirection=\"desc\">\r\n @for (col of vm.tableColumns||[]; track col.formControlName) {\r\n <ng-container [matColumnDef]=\"col.formControlName\">\r\n <th cdkDrag (cdkDragStarted)=\"setDraggedGroup(col.formControlName)\"\r\n [attr.data-column-size]=\"col.formControlName\"\r\n style=\"width: fit-content !important; white-space: nowrap !important;\" mat-header-cell *matHeaderCellDef>\r\n @if($index === 0 && vm.closedGroups.length > 0){\r\n <button class=\"cancelGrouping\" (click)=\"vm.closedGroups= []\" color=\"warn\" mat-icon-button>\r\n <mat-icon>highlight_off</mat-icon>\r\n </button>\r\n } @else {\r\n <div class=\"colTitle\" style=\"display: inline-flex; align-items: center;\"\r\n [style.color]=\"col.INCLUDED_IN_GROUP?'':''\" [matTooltip]=\"'Click to group by '+col.label\"\r\n (click)=\"addToGrouping(col.formControlName)\" matRipple>\r\n {{col.label}}\r\n </div>\r\n }\r\n </th>\r\n <td mat-cell style=\"\r\n width: fit-content !important;\r\n white-space: nowrap !important;\" [style.border-top]=\"!!row?.IS_SUM_ROW && $index !==0? '1px solid black':''\"\r\n [style.border-bottom-color]=\"!!row?.IS_GROUP_CONFIG ?'#ffffff00':''\"\r\n [style.background]=\"!!row?.IS_GROUP_CONFIG?'':'none'\"\r\n [style.overflow]=\"!!row?.IS_GROUP_CONFIG ?'visible':''\" *matCellDef=\"let row\">\r\n\r\n @if (!!row?.IS_GROUP_CONFIG ) {\r\n @if( $index ===0){\r\n <div disabled=\"true\" class=\"levelClass\" *ngFor=\"let item of row.LEVELS_BEFORE\" mat-button>\r\n <span>- <div class=\"verticalCenterLine\"></div></span>\r\n </div>\r\n <button (click)=\"toggleCloseGroup(row['name']);\" mat-button>\r\n <mat-icon>{{row['IS_CLOSED'] ?'keyboard_arrow_right':'keyboard_arrow_down'}}</mat-icon>\r\n <span\r\n style=\"color: var(--mat-table-header-headline-color, var(--mat-sys-on-surface, rgba(0, 0, 0, 0.87)));\">\r\n {{row['name']}}\r\n </span>\r\n </button>\r\n\r\n <span class=\"rowMenu\">\r\n <button [color]=\"vm.isOpen?.name === row?.name && vm.isOpen?.level === row.level?'warn':''\"\r\n class=\"groupOptions\" mat-icon-button (click)=\"toggleMenu(row)\">\r\n\r\n <mat-icon>\r\n {{vm.isOpen?.name === row?.name && vm.isOpen?.level === row.level? 'close':'more_horiz'}}\r\n </mat-icon>\r\n </button>\r\n @if(vm.isOpen?.name === row?.name && vm.isOpen?.level === row.level){\r\n <button (click)=\"showReport(row)\" mat-icon-button>\r\n <mat-icon>\r\n analytics\r\n </mat-icon>\r\n </button>\r\n <button (click)=\"foldUnFoldGroup(row)\" mat-icon-button>\r\n <mat-icon>\r\n compress\r\n </mat-icon>\r\n </button>\r\n }\r\n\r\n\r\n </span>\r\n\r\n\r\n } @else if (row['totals'][col.formControlName]) {\r\n <h5 [style.opacity]=\"row['IS_CLOSED']&& row['level'] !==0 ? '0.5':1\">\r\n {{(row['totals'][col.formControlName]) |formatData: col.valueType}}\r\n\r\n </h5>\r\n }\r\n }\r\n\r\n @if (!row['IS_GROUP_CONFIG'] && $index ===0 ) {\r\n @for (item of row['LEVELS_BEFORE']||[]; track item) {\r\n <div disabled=\"true\" class=\"levelClass\" mat-button>\r\n <span>\r\n @if ($last) {\r\n \u2022\r\n }@else {\r\n -\r\n }\r\n\r\n <div class=\"verticalCenterLine\"></div>\r\n </span>\r\n </div>\r\n }\r\n\r\n }\r\n @if (!row?.IS_GROUP_CONFIG && $index !==0 ){\r\n\r\n <div \r\n class=\"row-value-col\"\r\n >\r\n {{ row[col.formControlName]|formatData: col.valueType }}\r\n\r\n\r\n\r\n </div>\r\n\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n <tr mat-header-row *matHeaderRowDef=\"vm.displayedColumns\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: vm.displayedColumns;\" [class.is-group-row]=\"!!row?.IS_GROUP_CONFIG\"\r\n [class]=\"'row-'+row.level\" [class.is-sum-row]=\"!!row['IS_SUM_ROW']\"\r\n style=\"border-bottom: solid 1px rgba(0, 0, 0, .04)\"></tr>\r\n </table>\r\n </cdk-virtual-scroll-viewport>\r\n } @placeholder {\r\n <div style=\"display: flex; justify-content: center; align-items: center; padding: 40px; flex-direction: column;\">\r\n <mat-spinner diameter=\"50\"></mat-spinner>\r\n <p style=\"margin-top: 15px;\">Loading table data...</p>\r\n </div>\r\n } @loading {\r\n <div style=\"padding: 20px; text-align: center;\">\r\n <mat-spinner diameter=\"50\"></mat-spinner>\r\n <p style=\"margin-top: 10px;\">Preparing table view...</p>\r\n </div>\r\n } @error {\r\n <p style=\"color: red; text-align: center; padding: 20px;\">Table failed to load.</p>\r\n }\r\n </section>\r\n }\r\n\r\n</ng-container>\r\n\r\n\r\n<mat-menu #colorPallet=\"matMenu\">\r\n <p>Select Table color coding</p>\r\n\r\n <button *ngFor=\"let item of colorPalletArray\" (click)=\"selectColor(item)\" mat-menu-item>\r\n\r\n <div [style.background]=\"color\" *ngFor=\"let color of item\" style=\"width: 16px;height:24px;display:inline-block\">\r\n\r\n </div>\r\n\r\n\r\n\r\n </button>\r\n\r\n</mat-menu>", styles: [".example-table{width:auto!important;margin:8px!important;border-spacing:0!important;border-collapse:collapse!important}.example-table .mat-mdc-cell,.example-table .mat-mdc-header-cell{padding:0!important;width:fit-content!important;min-width:fit-content!important;max-width:fit-content!important;white-space:nowrap!important;border:none!important}.example-table .mat-mdc-cell>*,.example-table .mat-mdc-header-cell>*{margin:0!important;padding:0!important}.example-table .mat-mdc-row,.example-table .mat-mdc-header-row{display:table-row!important;padding:0!important;height:auto!important;min-height:0!important}.example-table .mat-mdc-row{--level-color-1: var(--custom-level-color-1, #caf0f8);--level-color-2: var(--custom-level-color-2, #ADE8F4);--level-color-3: var(--custom-level-color-3, #90E0EF);--level-color-4: var(--custom-level-color-4, #48CAE4);--level-color-5: var(--custom-level-color-5, #00B4D8);--level-color-6: var(--custom-level-color-6, #0096C7);--level-color-7: var(--custom-level-color-7, #0077B6);--level-color-8: var(--custom-level-color-8, #023E8A);border-bottom:solid 1px rgba(0,0,0,.04);transition:background-color .2s ease}.example-table .mat-mdc-row.row-0{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.row-1{background-color:var(--level-color-1)!important;color:var(--level-text-color-1, #000000)}.example-table .mat-mdc-row.row-2{background-color:var(--level-color-2)!important;color:var(--level-text-color-2, #000000)}.example-table .mat-mdc-row.row-3{background-color:var(--level-color-3)!important;color:var(--level-text-color-3, #000000)}.example-table .mat-mdc-row.row-4{background-color:var(--level-color-4)!important;color:var(--level-text-color-4, #000000)}.example-table .mat-mdc-row.row-5{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.row-6{background-color:var(--level-color-1)!important;color:var(--level-text-color-1, #000000)}.example-table .mat-mdc-row.row-7{background-color:var(--level-color-2)!important;color:var(--level-text-color-2, #000000)}.example-table .mat-mdc-row.row-8{background-color:var(--level-color-3)!important;color:var(--level-text-color-3, #000000)}.example-table .mat-mdc-row.row-9{background-color:var(--level-color-4)!important;color:var(--level-text-color-4, #000000)}.example-table .mat-mdc-row.row-10{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.row-11{background-color:var(--level-color-1)!important;color:var(--level-text-color-1, #000000)}.example-table .mat-mdc-row.row-12{background-color:var(--level-color-2)!important;color:var(--level-text-color-2, #000000)}.example-table .mat-mdc-row.row-13{background-color:var(--level-color-3)!important;color:var(--level-text-color-3, #000000)}.example-table .mat-mdc-row.row-14{background-color:var(--level-color-4)!important;color:var(--level-text-color-4, #000000)}.example-table .mat-mdc-row.row-15{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.row-16{background-color:var(--level-color-1)!important;color:var(--level-text-color-1, #000000)}.example-table .mat-mdc-row.row-17{background-color:var(--level-color-2)!important;color:var(--level-text-color-2, #000000)}.example-table .mat-mdc-row.row-18{background-color:var(--level-color-3)!important;color:var(--level-text-color-3, #000000)}.example-table .mat-mdc-row.row-19{background-color:var(--level-color-4)!important;color:var(--level-text-color-4, #000000)}.example-table .mat-mdc-row.row-20{background-color:var(--level-color-5)!important;color:var(--level-text-color-5, #000000)}.example-table .mat-mdc-row.is-group-row{font-weight:500}.example-table .mat-mdc-row.is-sum-row{font-weight:700}.example-table .mat-mdc-row:hover{position:relative}.example-table .mat-mdc-row:hover:after{content:\"\";position:absolute;inset:0;background-color:#0011ff0a;pointer-events:none}.example-table [class*=row-].row-1{padding-left:16px}.example-table [class*=row-].row-2{padding-left:32px}.example-table [class*=row-].row-3{padding-left:48px}.example-table [class*=row-].row-4{padding-left:64px}.example-table [class*=row-].row-5{padding-left:80px}.example-table [class*=row-].row-6{padding-left:96px}.example-table [class*=row-].row-7{padding-left:112px}.example-table [class*=row-].row-8{padding-left:128px}.example-table [class*=row-].row-9{padding-left:144px}.example-table [class*=row-].row-10{padding-left:160px}.example-table [class*=row-].row-11{padding-left:176px}.example-table [class*=row-].row-12{padding-left:192px}.example-table [class*=row-].row-13{padding-left:208px}.example-table [class*=row-].row-14{padding-left:224px}.example-table [class*=row-].row-15{padding-left:240px}.example-table [class*=row-].row-16{padding-left:256px}.example-table [class*=row-].row-17{padding-left:272px}.example-table [class*=row-].row-18{padding-left:288px}.example-table [class*=row-].row-19{padding-left:304px}.example-table [class*=row-].row-20{padding-left:320px}.example-table button.mat-mdc-icon-button{width:24px!important;height:24px!important;padding:0!important;line-height:24px!important}.example-table .mat-mdc-cell:not(:empty),.example-table .mat-mdc-header-cell:not(:empty){padding:0 4px!important}.example-table .mat-mdc-header-row{background-color:var(--mat-sys-surface-container)!important}::ng-deep .mat-mdc-table{background:transparent!important}::ng-deep .mat-mdc-table .mat-mdc-row:not([class*=mat-elevation-z]){box-shadow:none!important}.levelClass{width:18px;margin-right:8px;display:inline-block;text-align:center;color:#243d91;opacity:.6}.verticalCenterLine{border-right:1px solid #243d91;width:8px;height:12px;opacity:.4;position:absolute}.groupOptions{opacity:.2;height:fit-content}.groupOptions:hover{opacity:1}.rowMenu{display:inline-flex;margin-left:8px;margin-right:8px;align-items:center;justify-content:flex-end;gap:4px;vertical-align:middle;z-index:2}small{opacity:.7}.tableContainer{flex-grow:1;overflow-y:auto;max-width:100vw;padding:0;margin-left:16px;margin-right:16px;min-height:0;border:solid 1px var(--mat-divider-color, var(--mat-sys-outline));border-radius:8px 8px 0}.row-value-col{min-height:37px;display:flex;align-items:center;max-width:200px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;transition:max-width .35s ease-in-out,background-color .35s ease-in-out,box-shadow .35s ease-in-out;position:relative;cursor:default}.row-value-col:hover{white-space:normal;overflow:visible;text-overflow:clip;min-height:fit-content}\n"] }]
}], ctorParameters: () => [{ type: i1$2.MatDialog }], propDecorators: { scrollContainer: [{
type: ViewChild,
args: ['viewPortScroll']
}], list: [{
type: Input
}], reportName: [{
type: Input
}], listConfig: [{
type: Input
}], aggregates: [{
type: Input
}], groupOptions: [{
type: Input
}] } }) });
const NGX_T_REPORTS_CONFIG_TOKEN = new InjectionToken('NGX_T_REPORTS_CONFIG');
class ElementEditorComponent {
constructor() {
this.inEdit = {};
this.label = '';
this.subTitle = '';
this.editorSections = [];
this.formInputs = [];
this.saveButtonLabel = "Save ";
this.closeEditor = new EventEmitter();
this.saveElement = new EventEmitter();
this.newValue = null;
this.elementConfigurationChanged = (value, ele) => {
this.newValue = {
...this.inEdit
};
assignDeepPropertyToObject(this.newValue, ele.deepBind, value);
};
this.elementBlur = (event, element) => {
};
}
get saveIsDisabled() {
if (!this.newValue)
return true;
if (this.editorSections.length === 0)
return true;
return Boolean(_isEqual(this.newValue, this.inEdit));
}
save() {
this.saveElement.emit(this.newValue);
this.newValue = null;
}
close() {
this.closeEditor.emit();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ElementEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ElementEditorComponent, isStandalone: true, selector: "lib-element-editor", inputs: { inEdit: "inEdit", label: "label", subTitle: "subTitle", editorSections: "editorSections", formInputs: "formInputs", saveButtonLabel: "saveButtonLabel" }, outputs: { closeEditor: "closeEditor", saveElement: "saveElement" }, ngImport: i0, template: "<mat-card \r\n\r\nstyle=\"\r\n\r\nbackground: var(--mat-sys-surface-container, var(--mat-app-surface));\r\nmargin:0px;\r\nborder:solid 1px var(--mat-divider-color, var(--mat-app-outline))\">\r\n \r\n <mat-card-header >\r\n <mat-card-title>\r\n {{label}}\r\n </mat-card-title>\r\n <mat-card-subtitle> {{subTitle }}</mat-card-subtitle>\r\n \r\n </mat-card-header>\r\n \r\n\r\n\r\n \r\n <mat-toolbar class=\"section-header\">\r\n <span class=\"header\">Element configuration</span>\r\n <span class=\"spacer\"></span>\r\n </mat-toolbar>\r\n <mat-divider></mat-divider>\r\n\r\n @defer (on viewport) {\r\n \r\n <mat-accordion togglePosition=\"before\">\r\n @for(section of (editorSections||[]); track section.id) {\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n {{section.label}}\r\n <span class=\"spacer\"></span>\r\n \r\n <span class=\"showItemValue\" *ngIf=\"section.showItemValue && !!(inEdit)\">\r\n {{inEdit[section.showItemValue]}}\r\n </span>\r\n <mat-divider style=\"position: absolute; left:0px; width:100%; bottom:0px;\"></mat-divider>\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n \r\n <div style=\"margin-top: 16px;\"></div>\r\n \r\n <!-- Element type selector (only in first section) -->\r\n \r\n \r\n <!-- Section elements -->\r\n @for(element of section.elements; track element.id) {\r\n <p style=\"margin-bottom: 16px; margin-top: 8px;\">\r\n @defer (on viewport) {\r\n <lib-t-dynamic-data-edit \r\n [editorConfig]=\"element\" [data]=\"inEdit\"\r\n [validationErrors]=\"element.computedErrors||[]\"\r\n (valueChange)=\"elementConfigurationChanged($event, element)\" \r\n [formInputs]=\"formInputs\"\r\n (blur)=\"elementBlur($event, element)\">\r\n </lib-t-dynamic-data-edit>\r\n \r\n \r\n } @placeholder {\r\n <div style=\"padding: 50px; display:flex; justify-content:center; align-items:center; text-align: center;\">\r\n <mat-spinner diameter=\"18\"></mat-spinner>\r\n </div>\r\n }\r\n </p>\r\n }\r\n </mat-expansion-panel>\r\n }\r\n </mat-accordion>\r\n } @placeholder {\r\n <div style=\"padding: 50px; display:flex; justify-content:center; align-items:center; text-align: center;\">\r\n <mat-spinner diameter=\"50\" />\r\n </div>\r\n } @error {\r\n \r\n <h1>Error loading element editor</h1>\r\n <p>An error occurred while loading the element editor. Please try again later.</p>\r\n \r\n }\r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n \r\n\r\n <br>\r\n <mat-divider></mat-divider>\r\n \r\n <mat-card-actions >\r\n <button (click)=\"close()\" class=\"\" mat-button>close</button>\r\n <span class=\"spacer\"></span>\r\n <button mat-flat-button \r\n \r\n (click)=\"save()\"\r\n color=\"primary\">{{\r\n saveButtonLabel\r\n }}</button>\r\n \r\n </mat-card-actions>\r\n</mat-card>", styles: [".showItemValue{opacity:.6}mat-form-field{width:100%}mat-slider{width:100%}.header{margin-bottom:0;font-size:small;opacity:.6}.section-header{height:38px;margin-top:12px}\n"], dependencies: [{ kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i1$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i1$1.MatCardActions, selector: "mat-card-actions", inputs: ["align"], exportAs: ["matCardActions"] }, { kind: "component", type: i1$1.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i1$1.MatCardSubtitle, selector: "mat-card-subtitle, [mat-card-subtitle], [matCardSubtitle]" }, { kind: "directive", type: i1$1.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i2$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i9.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i4$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i8.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }], deferBlockDependencies: [() => [i2$1.MatDivider, i2.NgIf, i4$1.MatProgressSpinner, i7$1.MatAccordion, i7$1.MatExpansionPanel, i7$1.MatExpansionPanelHeader, i7$1.MatExpansionPanelTitle], () => [TDynamicDataEditComponent]] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ElementEditorComponent, decorators: [{
type: Component,
args: [{ selector: 'lib-element-editor', standalone: true, imports: [
MatCardModule,
MatCardContent,
MatCardHeader,
MatCardTitle,
MatCardSubtitle,
MatDividerModule,
CommonModule,
MatToolbarModule,
MatProgressSpinnerModule,
MatExpansionModule,
TDynamicDataEditComponent,
MatIconModule,
MatButtonModule
], template: "<mat-card \r\n\r\nstyle=\"\r\n\r\nbackground: var(--mat-sys-surface-container, var(--mat-app-surface));\r\nmargin:0px;\r\nborder:solid 1px var(--mat-divider-color, var(--mat-app-outline))\">\r\n \r\n <mat-card-header >\r\n <mat-card-title>\r\n {{label}}\r\n </mat-card-title>\r\n <mat-card-subtitle> {{subTitle }}</mat-card-subtitle>\r\n \r\n </mat-card-header>\r\n \r\n\r\n\r\n \r\n <mat-toolbar class=\"section-header\">\r\n <span class=\"header\">Element configuration</span>\r\n <span class=\"spacer\"></span>\r\n </mat-toolbar>\r\n <mat-divider></mat-divider>\r\n\r\n @defer (on viewport) {\r\n \r\n <mat-accordion togglePosition=\"before\">\r\n @for(section of (editorSections||[]); track section.id) {\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n {{section.label}}\r\n <span class=\"spacer\"></span>\r\n \r\n <span class=\"showItemValue\" *ngIf=\"section.showItemValue && !!(inEdit)\">\r\n {{inEdit[section.showItemValue]}}\r\n </span>\r\n <mat-divider style=\"position: absolute; left:0px; width:100%; bottom:0px;\"></mat-divider>\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n \r\n <div style=\"margin-top: 16px;\"></div>\r\n \r\n <!-- Element type selector (only in first section) -->\r\n \r\n \r\n <!-- Section elements -->\r\n @for(element of section.elements; track element.id) {\r\n <p style=\"margin-bottom: 16px; margin-top: 8px;\">\r\n @defer (on viewport) {\r\n <lib-t-dynamic-data-edit \r\n [editorConfig]=\"element\" [data]=\"inEdit\"\r\n [validationErrors]=\"element.computedErrors||[]\"\r\n (valueChange)=\"elementConfigurationChanged($event, element)\" \r\n [formInputs]=\"formInputs\"\r\n (blur)=\"elementBlur($event, element)\">\r\n </lib-t-dynamic-data-edit>\r\n \r\n \r\n } @placeholder {\r\n <div style=\"padding: 50px; display:flex; justify-content:center; align-items:center; text-align: center;\">\r\n <mat-spinner diameter=\"18\"></mat-spinner>\r\n </div>\r\n }\r\n </p>\r\n }\r\n </mat-expansion-panel>\r\n }\r\n </mat-accordion>\r\n } @placeholder {\r\n <div style=\"padding: 50px; display:flex; justify-content:center; align-items:center; text-align: center;\">\r\n <mat-spinner diameter=\"50\" />\r\n </div>\r\n } @error {\r\n \r\n <h1>Error loading element editor</h1>\r\n <p>An error occurred while loading the element editor. Please try again later.</p>\r\n \r\n }\r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n \r\n\r\n <br>\r\n <mat-divider></mat-divider>\r\n \r\n <mat-card-actions >\r\n <button (click)=\"close()\" class=\"\" mat-button>close</button>\r\n <span class=\"spacer\"></span>\r\n <button mat-flat-button \r\n \r\n (click)=\"save()\"\r\n color=\"primary\">{{\r\n saveButtonLabel\r\n }}</button>\r\n \r\n </mat-card-actions>\r\n</mat-card>", styles: [".showItemValue{opacity:.6}mat-form-field{width:100%}mat-slider{width:100%}.header{margin-bottom:0;font-size:small;opacity:.6}.section-header{height:38px;margin-top:12px}\n"] }]
}], propDecorators: { inEdit: [{
type: Input
}], label: [{
type: Input
}], subTitle: [{
type: Input
}], editorSections: [{
type: Input
}], formInputs: [{
type: Input
}], saveButtonLabel: [{
type: Input
}], closeEditor: [{
type: Output
}], saveElement: [{
type: Output
}] } });
var ConfigTypes;
(function (ConfigTypes) {
ConfigTypes["SheetConfig"] = "SheetConfig";
})(ConfigTypes || (ConfigTypes = {}));
const SheetConfig = {
label: "Matrix Report Configuration",
subTitle: "Customize how your data appears and organizes in the report",
saveButtonLabel: "Apply Report Settings",
editorSections: [
{
label: "Data Sources",
id: "dataSources",
elements: [
{
name: SpecialElementKeys.Default,
deepBind: ['dataSources'],
editType: ElementEditorTypes.DataSourcePicker,
hint: `Connect and manage data sources for your report. Select from REST APIs or create MongoDB aggregation pipelines.`,
label: 'Connect Data',
id: "10c7237d-b0a8-4964-97e6-811b93569c68"
},
]
},
{
label: "Column Layout",
id: "tableColumns",
elements: [
{
name: SpecialElementKeys.Default,
deepBind: ['listConfig'],
editType: ElementEditorTypes.ListLabelConfigEditor,
fetchOptions: ['ALL_POSSIBLE_FIELDS'],
hint: `Customize your table display by selecting fields, arranging columns, and setting visibility options.`,
label: 'Visible Columns',
id: "6ed1283c-63e9-435e-a435-16d3a0c6d3f3"
}
]
},
{
label: "Data Hierarchy & Grouping",
id: "dataGrouping",
elements: [
{
name: SpecialElementKeys.Default,
deepBind: ['groupOptions', 'columns'],
editType: ElementEditorTypes.ChipOptionsCreator,
hint: `Define columns for grouping and hierarchy. Ensure the entered column matches column form control names.`,
label: 'Group By',
id: "36877d92-4331-498a-b02f-4d0eaef17372"
},
{
name: SpecialElementKeys.Default,
deepBind: ['groupOptions', 'hiddenColumns'],
editType: ElementEditorTypes.ChipOptionsCreator,
hint: `Defined grouped columns that should be hidden. Ensure the entered column matches column form control names.`,
label: 'Define hidden grouped columns',
id: "2452e44f-d0c9-4935-a743-7d89fbd9a095"
}
]
},
{
label: "Date Range configuration",
id: "dataGrouping",
elements: [
{
name: SpecialElementKeys.Default,
deepBind: ['dateRangeMin'],
editType: ElementEditorTypes.OptionSelect,
fetchOptions: ['EXTERNAL_DATA_INTEGRATION_OPTIONS'],
hint: `Minimum date for the report. This is used to filter the data in the report.`,
label: 'Min Date',
id: "5d31a27a-a4d1-43db-9845-d1a2ce666e17"
},
{
name: SpecialElementKeys.Default,
deepBind: ['dateRangeMax'],
editType: ElementEditorTypes.OptionSelect,
fetchOptions: ['EXTERNAL_DATA_INTEGRATION_OPTIONS'],
hint: `Maximum date for the report. `,
label: 'max Date',
id: "739ce399-3f25-42c1-b2fe-47d2bf14aa38"
},
{
name: SpecialElementKeys.Default,
deepBind: ['dateRangeComparisonStart'],
editType: ElementEditorTypes.OptionSelect,
fetchOptions: ['EXTERNAL_DATA_INTEGRATION_OPTIONS'],
hint: `Highlighted date comparison start date for the picker. `,
label: 'Comparison Start',
id: "e34f377b-f824-4331-924d-5c3e9ee5f08e"
},
{
name: SpecialElementKeys.Default,
deepBind: ['dateRangeComparisonEnd'],
editType: ElementEditorTypes.OptionSelect,
fetchOptions: ['EXTERNAL_DATA_INTEGRATION_OPTIONS'],
hint: `Highlighted date comparison end date for the picker. `,
label: 'Comparison End',
id: "1e65d445-b3ed-4ab6-8689-1f7fe2d85da6"
},
]
},
]
};
const REPORT_SHEET_SIDE_MENU_OPTIONS = [
{
title: "Sheet Configuration",
subTitle: "Configure how your data appears and organizes in the report",
config: ConfigTypes.SheetConfig,
icon: 'construction',
color: '#1976d2'
},
];
const REPORT_DATE_RANGE_PICKER = {
"id": "491523bc-4170-476c-95c9-0a4041e073e2",
"formControlName": "dateRange",
"sectionId": "9bb19b6a-829c-49fb-8cd9-9df753140d92",
"label": "Date range",
"element": ElementTypes.DateRangePicker,
"dataType": InputDataTypes.Date,
"appearance": "outline",
"isCalculatedField": false,
"required": true,
"onlySetTempErrorOnTouch": false,
"colSize": 12,
"validators": [],
"startDateControl": "start",
"endDateControl": "end"
};
const REPORT_SHEET_INPUTS = [
REPORT_DATE_RANGE_PICKER,
];
class TimeZoneAdapterService extends MomentDateAdapter {
constructor(dateLocale) {
super(dateLocale);
// Get user's device locale if not specified otherwise
if (!dateLocale) {
this.setLocale(navigator.language);
}
// Get the user's timezone from their device
this.userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
}
// Override parse method to handle user timezone
parse(value, parseFormat) {
if (value && typeof value === 'string') {
return moment.tz(value, parseFormat, this.locale, true, this.userTimezone);
}
return value ? moment.tz(value, this.userTimezone).locale(this.locale) : null;
}
// Override format to display in user timezone
format(date, displayFormat) {
if (!this.isValid(date)) {
return '';
}
return date.tz(this.userTimezone).locale(this.locale).format(displayFormat);
}
// Override create date to use user timezone
createDate(year, month, date) {
return moment.tz([year, month, date], this.userTimezone).locale(this.locale);
}
// Override to use timezone
today() {
return moment().tz(this.userTimezone).locale(this.locale);
}
// Make sure deserialize maintains timezone
deserialize(value) {
let date;
if (value instanceof Date) {
date = moment(value).tz(this.userTimezone).locale(this.locale);
}
if (typeof value === 'string') {
if (!value) {
return null;
}
date = moment(value, moment.ISO_8601).tz(this.userTimezone).locale(this.locale);
}
if (date && this.isValid(date)) {
return date;
}
return super.deserialize(value);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimeZoneAdapterService, deps: [{ token: MAT_DATE_LOCALE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimeZoneAdapterService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimeZoneAdapterService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [MAT_DATE_LOCALE]
}] }] });
const CUSTOM_DATE_FORMATS = {
parse: {
dateInput: 'L', // Keep this for parsing
},
display: {
// Be more explicit about the format you want
dateInput: 'DD MMM YYYY', // This will show dates like "16 May 2024"
// Or use 'DD/MM/YYYY' for a more compact format
monthYearLabel: 'MMM YYYY',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'MMMM YYYY',
},
};
class ReportSheetComponent {
constructor() {
this._config = null;
this.NGX_T_REPORTS_CONFIG_TOKEN = inject(NGX_T_REPORTS_CONFIG_TOKEN);
this.NGX_T_FORMS_CONFIG = inject(NGX_T_FORMS_CONFIG_TOKEN);
this.EXTERNAL_DATA_INTEGRATION = this.NGX_T_REPORTS_CONFIG_TOKEN.dataIntegration;
this.externalDataIntegration$ = this.EXTERNAL_DATA_INTEGRATION().pipe(distinctUntilChanged((prev, curr) => _isEqual(prev, curr)));
this.reportName = null;
this.editMode = false;
this.sideMenuOptions = REPORT_SHEET_SIDE_MENU_OPTIONS;
this.configChange = new EventEmitter();
this.error$ = new BehaviorSubject(undefined);
this.dataList$ = new BehaviorSubject([]);
this.loading$ = new BehaviorSubject(false);
// Add a class property to track the debounce timer
this.loadDataDebounceTimer = null;
this.configInEdit = null;
this.showSidePanel = false;
this.formsUtils = {
httpGetDataFunction: (url, options) => this.NGX_T_FORMS_CONFIG.formBuilder.httpGetDataFunction(url, options),
httpPostDataFunction: (url, data, options) => this.NGX_T_FORMS_CONFIG.formBuilder.httpPostDataFunction(url, data, options),
submitForm: () => new Observable(subscriber => { subscriber.complete(); }),
setSubmissionStatusValue: (status) => { },
saveFormData: function () {
throw new Error('Function not implemented.');
},
runPipeLine: function (pipeline, workflowId) {
throw new Error('Function not implemented.');
},
fileUpload: function (file, fileName, fileType) {
throw new Error('Function not implemented.');
},
getUserSignature: function () {
throw new Error('Function not implemented.');
},
saveUserSignature: function (signature) {
throw new Error('Function not implemented.');
}
};
this.range = new FormGroup({
startDate: new FormControl(moment$1().subtract(1, 'year')),
endDate: new FormControl(moment$1()), //
});
this.report_inputs = REPORT_SHEET_INPUTS;
this.subscription = new Subscription();
this.config$ = combineLatest([
this.dataList$.asObservable(),
this.externalDataIntegration$
]).pipe(map(([dataList, dataIntegration]) => {
// Create a completely new object
const configCopy = this._config ? JSON.parse(JSON.stringify(this._config)) : {};
return Object.freeze({
...configCopy,
ALL_POSSIBLE_FIELDS: [...new Set(dataList.map((i) => Object.keys(i)).flat())],
EXTERNAL_DATA_INTEGRATION_OPTIONS: Object.keys(dataIntegration).map((key) => ({
value: key,
label: key.replace(/([A-Z])/g, ' $1').trim()
}))
});
}));
this.dateChanged = null;
}
set config(config) {
if (_isEqual(this._config, config))
return;
this._config = config;
this.loadData();
}
get config() {
return this._config;
}
// Modified loadData method with debounce
loadData() {
// Clear any existing timer to restart the countdown
if (this.loadDataDebounceTimer) {
clearTimeout(this.loadDataDebounceTimer);
}
// Set a new timer that will execute after 300ms of inactivity
this.loadDataDebounceTimer = setTimeout(() => {
const dataSource = this.config?.dataSources.at(0);
console.error("Data Source", this.config);
if (!dataSource) {
this.error$.next("No data source found");
return;
}
dataSource.source === 'api'
? this.loadApiData(dataSource.config)
: this.loadMongoData(dataSource.config);
// Clear the timer reference after execution
this.loadDataDebounceTimer = null;
}, 300);
}
loadMongoData(config) {
if (!config.pipeline || config.pipeline.length === 0) {
this.error$.next("No pipeline found");
return;
}
if (!config.workflowId) {
this.error$.next("No workflowId found");
return;
}
this.loading$.next(true);
this.NGX_T_REPORTS_CONFIG_TOKEN.runPipeLine(config.pipeline, config.workflowId).pipe(take(1), tap((data) => {
this.dataList$.next(data.results);
this.loading$.next(false);
this.error$.next(undefined);
}), catchError((error) => {
console.error("Error", error);
const msg = error instanceof Error ? error.message : "Unknown error";
this.error$.next(msg);
this.loading$.next(false);
return error;
})).subscribe();
}
loadApiData(config) {
const missingInputs = [];
const postData = config.backEndConfig.minimumInputRequired.reduce((acc, curr) => {
const rangeValue = this.range.value;
if (curr.type === "default") {
acc[curr.name] = curr.defaultValue;
return acc;
}
if (curr.type === "mapto" && rangeValue[curr.name]) {
acc[curr.name] = rangeValue[curr.name];
return acc;
}
missingInputs.push(curr.name);
return acc;
}, {});
if (missingInputs.length > 0) {
this.error$.next(`Missing inputs: ${missingInputs.join(", ")}`);
return;
}
this.loading$.next(true);
this.NGX_T_REPORTS_CONFIG_TOKEN.postToServer(config.httpEndPoint, postData).pipe(take(1), tap((response) => {
this.dataList$.next(response);
this.loading$.next(false);
this.error$.next(undefined);
}), catchError((error) => {
const msg = !error.error.message && error instanceof Error ? error.message : error.error.message;
console.error("Error", error);
this.error$.next(msg);
this.loading$.next(false);
return error;
})).subscribe();
/************* ✨ Windsurf Command ⭐ *************/
/**
* Loads data from an API based on the provided configuration.
*
* @param config - The configuration object specifying the API endpoint, method, and other parameters
* required for fetching the data.
*/
/******* f6089262-9e73-4765-b244-aec461c2f1be *******/ console.log("API Data Loading", config);
}
get listConfig() {
return this.config?.listConfig || [];
}
set listConfig(value) {
if (this._config) {
this._config = {
...this._config,
listConfig: value
};
this.configChange.emit(this._config);
}
}
get groupingConfig() {
return this.config?.groupOptions || {};
}
set groupingConfig(value) {
if (this._config) {
this._config = {
...this._config,
groupOptions: value
};
this.configChange.emit(this._config);
}
}
setConfigInEdit(type) {
this.configInEdit = type;
}
setShowSidePanel() {
this.showSidePanel = !this.showSidePanel;
if (!this.showSidePanel) {
this.configInEdit = null;
}
}
get configActiveConfig() {
if (this.configInEdit === null)
return null;
const config = {
...SheetConfig
};
return {
...config,
editorSections: config.editorSections.map((section) => {
const elements = getSectionElements(section.elements, {}, this.NGX_T_FORMS_CONFIG, this.formsUtils, []);
return {
...section,
elements: [...elements],
};
})
};
}
saveElement(value) {
let newConfig = {
...this.config,
...value
};
this.configChange.emit(newConfig);
this.configInEdit = null;
}
set configActiveConfig(config) {
this.configInEdit = null;
}
ngOnInit() {
this.subscription.add(this.minMaxDateAndComp$.pipe(distinctUntilChanged((prev, curr) => _isEqual(prev, curr)), tap((data) => {
if (data.minDate) {
this.range.patchValue({
startDate: data.minDate
});
}
if (data.maxDate) {
this.range.patchValue({
endDate: data.maxDate
});
}
})).subscribe());
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
get minMaxDateAndComp$() {
return combineLatest([this.config$, this.externalDataIntegration$]).pipe(map(([config, dataIntegration]) => {
const integrationMinKey = config?.dateRangeMin || '';
const integrationMaxKey = config?.dateRangeMax || '';
const integrationCompStartKey = config?.dateRangeComparisonStart || '';
const integrationCompEndKey = config?.dateRangeComparisonEnd || '';
// Convert to moment objects if values exist, otherwise null
const minDate = dataIntegration[integrationMinKey]
? moment$1(dataIntegration[integrationMinKey])
: null;
const maxDate = dataIntegration[integrationMaxKey]
? moment$1(dataIntegration[integrationMaxKey])
: null;
const compStartDate = dataIntegration[integrationCompStartKey]
? moment$1(dataIntegration[integrationCompStartKey])
: null;
const compEndDate = dataIntegration[integrationCompEndKey]
? moment$1(dataIntegration[integrationCompEndKey])
: null;
return {
minDate,
maxDate,
compStartDate,
compEndDate
};
}));
}
dateRangeChanged(event, control) {
const value = event.value;
if (this.range) {
this.range.patchValue({
[control]: value
});
this.dateChanged = {
...this.dateChanged,
[control]: value
};
}
this.loadData();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReportSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ReportSheetComponent, isStandalone: true, selector: "app-report-sheet", inputs: { reportName: "reportName", editMode: "editMode", config: "config" }, outputs: { configChange: "configChange" }, providers: [
{ provide: MAT_DATE_LOCALE, useFactory: () => navigator.language },
{ provide: DateAdapter, useClass: TimeZoneAdapterService },
{ provide: MAT_DATE_FORMATS, useValue: CUSTOM_DATE_FORMATS }
], ngImport: i0, template: "@if(error$|async){\r\n<mat-toolbar class=\"error-toolbar\">\r\n <mat-icon color=\"warn\" aria-hidden=\"false\" aria-label=\"Error loading transactions\">error_outline</mat-icon>\r\n <span class=\"error-message\">\r\n <div class=\"error-title\">\r\n Failed To load report sheet\r\n </div>\r\n <div class=\"error-details\">\r\n {{error$|async}}\r\n </div>\r\n </span>\r\n <span class=\"spacer\"></span>\r\n <button (click)=\"loadData()\" mat-flat-button color=\"primary\" aria-label=\"Retry loading transactions\">\r\n <mat-icon>refresh</mat-icon>\r\n Retry\r\n </button>\r\n</mat-toolbar>\r\n}\r\n\r\n\r\n\r\n<div class=\"report-sheet-container\">\r\n <div class=\"report-sheet-content\">\r\n @if(loading$|async){\r\n <div\r\n style=\"max-width:400px; margin:auto; padding: 20px; height: 80%; display: flex; align-items: center; justify-content: center;\">\r\n <div style=\"display: flex; flex-direction: column; align-items: center;\">\r\n <mat-spinner diameter=\"40\" mode=\"indeterminate\"></mat-spinner>\r\n <div style=\"text-align: center; margin-top: 10px;\">Loading Report...</div>\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n }@else {\r\n <mat-toolbar class=\"report-inputs-toolbar\">\r\n <mat-form-field [subscriptSizing]=\"'dynamic'\" appearance=\"fill\" class=\"date-range-input\">\r\n <mat-label>Enter a date range</mat-label>\r\n <mat-date-range-input [min]=\"(minMaxDateAndComp$|async)?.minDate\" [max]=\"(minMaxDateAndComp$|async)?.maxDate\" [formGroup]=\"range\"\r\n \r\n [rangePicker]=\"picker\"\r\n [comparisonStart]=\"(minMaxDateAndComp$|async)?.compStartDate\"\r\n [comparisonEnd]=\"(minMaxDateAndComp$|async)?.compEndDate\"\r\n >\r\n <input matStartDate (dateChange)=\"dateRangeChanged($event, 'startDate')\" formControlName=\"startDate\"\r\n placeholder=\"Start date\">\r\n <input (dateChange)=\"dateRangeChanged($event, 'endDate')\" matEndDate formControlName=\"endDate\"\r\n placeholder=\"End date\">\r\n </mat-date-range-input>\r\n\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-date-range-picker touchUi #picker>\r\n <mat-date-range-picker-actions>\r\n <button mat-button matDateRangePickerCancel>Cancel</button>\r\n <button mat-raised-button color=\"primary\" style=\"min-width:120px;border-radius:16px\" matDateRangePickerApply>Apply</button>\r\n </mat-date-range-picker-actions>\r\n </mat-date-range-picker>\r\n\r\n @if (range?.controls?.['startDate']?.hasError('matStartDateInvalid')) {\r\n <mat-error>Invalid start date</mat-error>\r\n }\r\n @if (range?.controls?.['endDate']?.hasError('matEndDateInvalid')) {\r\n <mat-error>Invalid end date</mat-error>\r\n }\r\n <button matSuffix mat-icon-button style=\" margin-right: px;\r\n margin-left: px;\" color=\"primary\" (click)=\"loadData()\" matTooltip=\"Refresh data\" [matTooltipPosition]=\"'before'\">\r\n <mat-icon>\r\n refresh\r\n </mat-icon>\r\n </button>\r\n </mat-form-field>\r\n\r\n\r\n\r\n\r\n </mat-toolbar>\r\n <mat-divider></mat-divider>\r\n\r\n <app-ngx-t-matrix-table\r\n \r\n [list]=\"dataList$|async\" [listConfig]=\"listConfig\" [groupOptions]=\"groupingConfig\"\r\n [reportName]=\"reportName\">\r\n <ng-content ></ng-content>\r\n \r\n </app-ngx-t-matrix-table>}\r\n </div>\r\n <div class=\"report-sheet-sidebar-right\">\r\n\r\n\r\n @if(showSidePanel && configActiveConfig === null){\r\n <div class=\"side-buttons-container\">\r\n @for(item of sideMenuOptions; track item.config){\r\n <button (click)=\"setConfigInEdit(item.config)\" mat-icon-button [matTooltip]=\"item.title\"\r\n [matTooltipPosition]=\"'before'\">\r\n <mat-icon [style.color]=\"item.color\">{{item.icon}}</mat-icon>\r\n\r\n </button>\r\n\r\n }\r\n </div>\r\n }\r\n\r\n\r\n @if(configActiveConfig && configInEdit !== null){\r\n <lib-element-editor [inEdit]=\"config$|async\" [label]=\"configActiveConfig.label\"\r\n [subTitle]=\"configActiveConfig.subTitle\" [editorSections]=\"configActiveConfig.editorSections\"\r\n [formInputs]=\" report_inputs \" [saveButtonLabel]=\"configActiveConfig.saveButtonLabel\"\r\n (closeEditor)=\"configActiveConfig = null\" (saveElement)=\"saveElement($event)\">\r\n </lib-element-editor>\r\n }\r\n\r\n @if(editMode && configInEdit === null){\r\n <button (click)=\"setShowSidePanel()\" mat-flat-button class=\"config-options\" color=\"accent\">\r\n <mat-icon>\r\n {{\r\n showSidePanel ? 'navigate_next' : 'navigate_before'\r\n }}\r\n\r\n </mat-icon>\r\n <span class=\"label\">\r\n {{\r\n showSidePanel ? 'Hide side panel' : 'Show side panel'\r\n }}\r\n\r\n </span>\r\n\r\n </button>\r\n }\r\n\r\n\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n\r\n\r\n<!-- @for(item of tMatrixTableConfigMenu; track item.config){\r\n <button (click)=\"setActiveConfig(item.config)\" mat-menu-item>\r\n <mat-icon>{{item.icon}}</mat-icon>\r\n <span>\r\n <div>{{item.title}}</div>\r\n <small>\r\n {{item.subTitle}}\r\n </small>\r\n </span>\r\n \r\n </button>\r\n \r\n <mat-divider></mat-divider>\r\n \r\n } -->", styles: [".error-message{margin-left:16px}.error-title{font-size:14px;font-weight:500;line-height:1.5}.error-details{font-size:14px;opacity:.8;line-height:normal}.report-sheet-container{display:flex;flex-direction:row;max-width:100vw;height:100%;min-height:0}.report-sheet-content{flex:1 1 auto;overflow-y:auto;overflow-x:hidden;padding:0;min-height:0;height:100%;max-height:100%}.report-sheet-sidebar-right{flex:0 1 auto;min-width:0;max-width:480px;border-left:1px solid rgba(0,0,0,.12);background-color:var(--mat-sys-surface-container);height:100%}.config-options{border-radius:58px 0 0 58px!important;transition:all .3s cubic-bezier(.25,1,.5,1);padding-right:10px;z-index:1000;position:absolute;bottom:16px;min-width:0px;right:0;overflow:hidden}.config-options .label{display:none}.config-options:hover{padding-right:28px;box-shadow:#32325d4d 0 4px 6px -1px,#00000059 0 2px 4px -1px}.config-options:hover .label{display:block}.config-options mat-icon{color:#fff!important}.side-buttons-container{display:flex;flex-direction:column;gap:4px;padding:4px}.report-inputs-toolbar{height:fit-content;padding-top:28px;padding-bottom:0}.date-range-input{min-width:100%}.error-toolbar{background:var(--mat-sys-on-error-container)!important;color:var(--mat-sys-on-error)!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "component", type: MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: NgxTMatrixTableComponent, selector: "app-ngx-t-matrix-table", inputs: ["list", "reportName", "listConfig", "aggregates", "groupOptions"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i2$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i5$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i6$1.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "component", type: i6$1.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i6$1.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i6$1.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i6$1.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "component", type: i6$1.MatDatepickerActions, selector: "mat-datepicker-actions, mat-date-range-picker-actions" }, { kind: "directive", type: i6$1.MatDatepickerCancel, selector: "[matDatepickerCancel], [matDateRangePickerCancel]" }, { kind: "directive", type: i6$1.MatDatepickerApply, selector: "[matDatepickerApply], [matDateRangePickerApply]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i7$2.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: i7$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i7$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i7$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i4$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: ElementEditorComponent, selector: "lib-element-editor", inputs: ["inEdit", "label", "subTitle", "editorSections", "formInputs", "saveButtonLabel"], outputs: ["closeEditor", "saveElement"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i8.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i8.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReportSheetComponent, decorators: [{
type: Component,
args: [{ selector: 'app-report-sheet', standalone: true, imports: [CommonModule, MatToolbar, MatIconModule, NgxTMatrixTableComponent,
MatTooltipModule,
MatDividerModule,
MatFormFieldModule, MatDatepickerModule, FormsModule, ReactiveFormsModule,
MatProgressSpinnerModule,
ElementEditorComponent,
MatButtonModule], providers: [
{ provide: MAT_DATE_LOCALE, useFactory: () => navigator.language },
{ provide: DateAdapter, useClass: TimeZoneAdapterService },
{ provide: MAT_DATE_FORMATS, useValue: CUSTOM_DATE_FORMATS }
], template: "@if(error$|async){\r\n<mat-toolbar class=\"error-toolbar\">\r\n <mat-icon color=\"warn\" aria-hidden=\"false\" aria-label=\"Error loading transactions\">error_outline</mat-icon>\r\n <span class=\"error-message\">\r\n <div class=\"error-title\">\r\n Failed To load report sheet\r\n </div>\r\n <div class=\"error-details\">\r\n {{error$|async}}\r\n </div>\r\n </span>\r\n <span class=\"spacer\"></span>\r\n <button (click)=\"loadData()\" mat-flat-button color=\"primary\" aria-label=\"Retry loading transactions\">\r\n <mat-icon>refresh</mat-icon>\r\n Retry\r\n </button>\r\n</mat-toolbar>\r\n}\r\n\r\n\r\n\r\n<div class=\"report-sheet-container\">\r\n <div class=\"report-sheet-content\">\r\n @if(loading$|async){\r\n <div\r\n style=\"max-width:400px; margin:auto; padding: 20px; height: 80%; display: flex; align-items: center; justify-content: center;\">\r\n <div style=\"display: flex; flex-direction: column; align-items: center;\">\r\n <mat-spinner diameter=\"40\" mode=\"indeterminate\"></mat-spinner>\r\n <div style=\"text-align: center; margin-top: 10px;\">Loading Report...</div>\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n }@else {\r\n <mat-toolbar class=\"report-inputs-toolbar\">\r\n <mat-form-field [subscriptSizing]=\"'dynamic'\" appearance=\"fill\" class=\"date-range-input\">\r\n <mat-label>Enter a date range</mat-label>\r\n <mat-date-range-input [min]=\"(minMaxDateAndComp$|async)?.minDate\" [max]=\"(minMaxDateAndComp$|async)?.maxDate\" [formGroup]=\"range\"\r\n \r\n [rangePicker]=\"picker\"\r\n [comparisonStart]=\"(minMaxDateAndComp$|async)?.compStartDate\"\r\n [comparisonEnd]=\"(minMaxDateAndComp$|async)?.compEndDate\"\r\n >\r\n <input matStartDate (dateChange)=\"dateRangeChanged($event, 'startDate')\" formControlName=\"startDate\"\r\n placeholder=\"Start date\">\r\n <input (dateChange)=\"dateRangeChanged($event, 'endDate')\" matEndDate formControlName=\"endDate\"\r\n placeholder=\"End date\">\r\n </mat-date-range-input>\r\n\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-date-range-picker touchUi #picker>\r\n <mat-date-range-picker-actions>\r\n <button mat-button matDateRangePickerCancel>Cancel</button>\r\n <button mat-raised-button color=\"primary\" style=\"min-width:120px;border-radius:16px\" matDateRangePickerApply>Apply</button>\r\n </mat-date-range-picker-actions>\r\n </mat-date-range-picker>\r\n\r\n @if (range?.controls?.['startDate']?.hasError('matStartDateInvalid')) {\r\n <mat-error>Invalid start date</mat-error>\r\n }\r\n @if (range?.controls?.['endDate']?.hasError('matEndDateInvalid')) {\r\n <mat-error>Invalid end date</mat-error>\r\n }\r\n <button matSuffix mat-icon-button style=\" margin-right: px;\r\n margin-left: px;\" color=\"primary\" (click)=\"loadData()\" matTooltip=\"Refresh data\" [matTooltipPosition]=\"'before'\">\r\n <mat-icon>\r\n refresh\r\n </mat-icon>\r\n </button>\r\n </mat-form-field>\r\n\r\n\r\n\r\n\r\n </mat-toolbar>\r\n <mat-divider></mat-divider>\r\n\r\n <app-ngx-t-matrix-table\r\n \r\n [list]=\"dataList$|async\" [listConfig]=\"listConfig\" [groupOptions]=\"groupingConfig\"\r\n [reportName]=\"reportName\">\r\n <ng-content ></ng-content>\r\n \r\n </app-ngx-t-matrix-table>}\r\n </div>\r\n <div class=\"report-sheet-sidebar-right\">\r\n\r\n\r\n @if(showSidePanel && configActiveConfig === null){\r\n <div class=\"side-buttons-container\">\r\n @for(item of sideMenuOptions; track item.config){\r\n <button (click)=\"setConfigInEdit(item.config)\" mat-icon-button [matTooltip]=\"item.title\"\r\n [matTooltipPosition]=\"'before'\">\r\n <mat-icon [style.color]=\"item.color\">{{item.icon}}</mat-icon>\r\n\r\n </button>\r\n\r\n }\r\n </div>\r\n }\r\n\r\n\r\n @if(configActiveConfig && configInEdit !== null){\r\n <lib-element-editor [inEdit]=\"config$|async\" [label]=\"configActiveConfig.label\"\r\n [subTitle]=\"configActiveConfig.subTitle\" [editorSections]=\"configActiveConfig.editorSections\"\r\n [formInputs]=\" report_inputs \" [saveButtonLabel]=\"configActiveConfig.saveButtonLabel\"\r\n (closeEditor)=\"configActiveConfig = null\" (saveElement)=\"saveElement($event)\">\r\n </lib-element-editor>\r\n }\r\n\r\n @if(editMode && configInEdit === null){\r\n <button (click)=\"setShowSidePanel()\" mat-flat-button class=\"config-options\" color=\"accent\">\r\n <mat-icon>\r\n {{\r\n showSidePanel ? 'navigate_next' : 'navigate_before'\r\n }}\r\n\r\n </mat-icon>\r\n <span class=\"label\">\r\n {{\r\n showSidePanel ? 'Hide side panel' : 'Show side panel'\r\n }}\r\n\r\n </span>\r\n\r\n </button>\r\n }\r\n\r\n\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n\r\n\r\n<!-- @for(item of tMatrixTableConfigMenu; track item.config){\r\n <button (click)=\"setActiveConfig(item.config)\" mat-menu-item>\r\n <mat-icon>{{item.icon}}</mat-icon>\r\n <span>\r\n <div>{{item.title}}</div>\r\n <small>\r\n {{item.subTitle}}\r\n </small>\r\n </span>\r\n \r\n </button>\r\n \r\n <mat-divider></mat-divider>\r\n \r\n } -->", styles: [".error-message{margin-left:16px}.error-title{font-size:14px;font-weight:500;line-height:1.5}.error-details{font-size:14px;opacity:.8;line-height:normal}.report-sheet-container{display:flex;flex-direction:row;max-width:100vw;height:100%;min-height:0}.report-sheet-content{flex:1 1 auto;overflow-y:auto;overflow-x:hidden;padding:0;min-height:0;height:100%;max-height:100%}.report-sheet-sidebar-right{flex:0 1 auto;min-width:0;max-width:480px;border-left:1px solid rgba(0,0,0,.12);background-color:var(--mat-sys-surface-container);height:100%}.config-options{border-radius:58px 0 0 58px!important;transition:all .3s cubic-bezier(.25,1,.5,1);padding-right:10px;z-index:1000;position:absolute;bottom:16px;min-width:0px;right:0;overflow:hidden}.config-options .label{display:none}.config-options:hover{padding-right:28px;box-shadow:#32325d4d 0 4px 6px -1px,#00000059 0 2px 4px -1px}.config-options:hover .label{display:block}.config-options mat-icon{color:#fff!important}.side-buttons-container{display:flex;flex-direction:column;gap:4px;padding:4px}.report-inputs-toolbar{height:fit-content;padding-top:28px;padding-bottom:0}.date-range-input{min-width:100%}.error-toolbar{background:var(--mat-sys-on-error-container)!important;color:var(--mat-sys-on-error)!important}\n"] }]
}], propDecorators: { reportName: [{
type: Input
}], editMode: [{
type: Input
}], config: [{
type: Input
}], configChange: [{
type: Output
}] } });
/*
* Public API Surface of ngx-t-reports
*/
/**
* Generated bundle index. Do not edit.
*/
export { EChartsComponent, NGX_T_REPORTS_CONFIG_TOKEN, NgxTMatrixTableComponent, ReportSheetComponent, TimeZoneAdapterService };
//# sourceMappingURL=ngx-t-reports.mjs.map