ngx-t-reports
Version:
Angular module for creating dynamic reports and dashboards. Supports various data sources, custom templates, and real-time updates.
1 lines • 178 kB
Source Map (JSON)
{"version":3,"file":"ngx-t-reports.mjs","sources":["../../../projects/ngx-t-reports/src/lib/components/e-charts/e-charts/e-charts.component.ts","../../../projects/ngx-t-reports/src/lib/components/e-charts/e-charts/e-charts.component.html","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/functions/createPieChart.ts","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/components/grouping-chart/grouping-chart.component.ts","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/components/grouping-chart/grouping-chart.component.html","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/constants/colorCodes.ts","../../../projects/ngx-t-reports/src/types/DocumentSectionConfigurationsInterface.ts","../../../projects/ngx-t-reports/src/lib/shared/functions/isEqual.ts","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/functions/groupedBy.ts","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/store/NgxTMatrixTable-selectors.ts","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/store/NgxTMatrixTable-actions.ts","../../../projects/ngx-t-reports/src/lib/shared/functions/keysToCamelCase.ts","../../../projects/ngx-t-reports/src/lib/shared/functions/tableutills.ts","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/store/NgxTMatrixTable-effects.ts","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/store/ngx-tmatrix-table-store.service.ts","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/ngx-t-matrix-table.component.ts","../../../projects/ngx-t-reports/src/lib/components/ngx-t-matrix-table/ngx-t-matrix-table.component.html","../../../projects/ngx-t-reports/src/lib/injection-tokens/index.ts","../../../projects/ngx-t-reports/src/lib/components/element-editor/element-editor.component.ts","../../../projects/ngx-t-reports/src/lib/components/element-editor/element-editor.component.html","../../../projects/ngx-t-reports/src/lib/config/ngx-t-matrix-table-config.ts","../../../projects/ngx-t-reports/src/lib/config/Report-inputs.ts","../../../projects/ngx-t-reports/src/lib/services/time-zone-adapter/time-zone-adapter.service.ts","../../../projects/ngx-t-reports/src/lib/components/report-sheet/report-sheet.component.ts","../../../projects/ngx-t-reports/src/lib/components/report-sheet/report-sheet.component.html","../../../projects/ngx-t-reports/src/public-api.ts","../../../projects/ngx-t-reports/src/ngx-t-reports.ts"],"sourcesContent":["\r\nimport { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';\r\nimport { EChartsOption } from 'echarts';\r\nimport { NgxEchartsModule, provideEchartsCore } from 'ngx-echarts';\r\nimport * as echarts from 'echarts/core';\r\nimport { GridComponent } from 'echarts/components';\r\nimport { CanvasRenderer } from 'echarts/renderers';\r\nimport { CommonModule } from '@angular/common';\r\nimport {\r\n BarChart, BoxplotChart, CandlestickChart, CustomChart, EffectScatterChart,\r\n FunnelChart, GaugeChart, GraphChart, HeatmapChart, LineChart, LinesChart,\r\n MapChart, ParallelChart, PictorialBarChart, PieChart, RadarChart, SankeyChart,\r\n ScatterChart, SunburstChart, ThemeRiverChart, TreeChart, TreemapChart\r\n} from 'echarts/charts';\r\n\r\necharts.use([\r\n BarChart, BoxplotChart, CandlestickChart, CustomChart, EffectScatterChart,\r\n FunnelChart, GaugeChart, GraphChart, HeatmapChart, LineChart, LinesChart,\r\n MapChart, ParallelChart, PictorialBarChart, PieChart, RadarChart, SankeyChart,\r\n ScatterChart, SunburstChart, ThemeRiverChart, TreeChart, TreemapChart,\r\n GridComponent, CanvasRenderer\r\n]);\r\n@Component({\r\n selector: 'app-e-charts',\r\n standalone: true,\r\n imports: [\r\n NgxEchartsModule,\r\n CommonModule\r\n ],\r\n providers: [\r\n provideEchartsCore({ echarts }),\r\n ],\r\n templateUrl: './e-charts.component.html',\r\n styleUrl: './e-charts.component.scss'\r\n})\r\nexport class EChartsComponent implements OnChanges {\r\n @Input() loading!: boolean\r\n @Input() options!: EChartsOption\r\n @Output() chartClick = new EventEmitter<any>();\r\n chartOption: EChartsOption | undefined\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n\r\n if (changes['options']) {\r\n\r\n\r\n\r\n this.chartOption = this.options\r\n\r\n }\r\n\r\n }\r\n\r\n onChartClick(event: any) {\r\n this.chartClick.emit(event);\r\n }\r\n}\r\n","@if (chartOption ) {\r\n<div echarts style=\"height:338px;\" (chartClick)=\"onChartClick($event)\" [options]=\"chartOption\" [loading]=\"loading\"\r\n class=\"chart\"></div>\r\n}","import { EChartsOption } from \"echarts\"\r\n\r\nexport function createADonutChart(id: string, title: string, dataSeries: {\r\n value: number,\r\n name: string\r\n}[]) {\r\n return {\r\n title,\r\n id: id,\r\n chart: {\r\n\r\n toolbox: {\r\n show: true,\r\n feature: {\r\n mark: { show: true },\r\n dataView: { show: true, readOnly: false },\r\n restore: { show: true },\r\n saveAsImage: { show: true }\r\n }\r\n },\r\n\r\n grid: {\r\n top: 100,\r\n },\r\n tooltip: {\r\n trigger: 'item'\r\n },\r\n legend: {\r\n top: '5%',\r\n left: 'center'\r\n },\r\n series:\r\n [{\r\n name: title,\r\n type: 'pie',\r\n radius: ['40%', '70%'],\r\n avoidLabelOverlap: false,\r\n itemStyle: {\r\n borderRadius: 10,\r\n borderColor: '#fff',\r\n borderWidth: 2\r\n },\r\n label: {\r\n show: true\r\n },\r\n emphasis: {\r\n label: {\r\n show: true,\r\n fontSize: '40',\r\n fontWeight: 'bold'\r\n }\r\n },\r\n labelLine: {\r\n lineStyle: {\r\n color: '#235894'\r\n }\r\n },\r\n data: dataSeries\r\n }],\r\n\r\n\r\n\r\n\r\n\r\n }\r\n }\r\n}\r\n\r\nexport function createABarChart(id: string, title: string, dataSeries: {\r\n value: number,\r\n name: string\r\n}[]) {\r\n return {\r\n title,\r\n id,\r\n chart: {\r\n toolbox: {\r\n show: true,\r\n feature: {\r\n mark: { show: true },\r\n dataView: { show: true, readOnly: false },\r\n restore: { show: true },\r\n saveAsImage: { show: true }\r\n }\r\n },\r\n tooltip: {\r\n trigger: 'axis',\r\n axisPointer: {\r\n type: 'shadow'\r\n }\r\n },\r\n grid: {\r\n left: '3%',\r\n right: '4%',\r\n bottom: '3%',\r\n containLabel: true\r\n },\r\n xAxis: [\r\n {\r\n type: 'category',\r\n data: dataSeries.map(v => v.name),\r\n axisTick: {\r\n alignWithLabel: true\r\n }\r\n }\r\n ],\r\n yAxis: [\r\n {\r\n type: 'value'\r\n }\r\n ],\r\n series: [\r\n\r\n {\r\n name: '',\r\n type: 'bar',\r\n label: {\r\n show: true\r\n },\r\n emphasis: {\r\n label: {\r\n show: true,\r\n fontSize: '40',\r\n fontWeight: 'bold'\r\n }\r\n },\r\n labelLine: {\r\n lineStyle: {\r\n color: '#235894'\r\n }\r\n },\r\n data: dataSeries.map(\r\n (v, index) => ({\r\n ...v,\r\n itemStyle: {\r\n color: getColors(dataSeries.length)[index]\r\n }\r\n })\r\n )\r\n\r\n\r\n }\r\n ],\r\n\r\n },\r\n\r\n }\r\n}\r\nexport function createRoseTypeChart(id: string, title: string, dataSeries: {\r\n value: number,\r\n name: string\r\n}[]) {\r\n return {\r\n title,\r\n id,\r\n chart: {\r\n legend: {\r\n top: 'bottom'\r\n },\r\n toolbox: {\r\n show: true,\r\n feature: {\r\n mark: { show: true },\r\n dataView: { show: true, readOnly: false },\r\n restore: { show: true },\r\n saveAsImage: { show: true }\r\n }\r\n },\r\n series: [\r\n {\r\n name: title,\r\n type: 'pie',\r\n radius: [50, 250],\r\n center: ['50%', '50%'],\r\n roseType: 'area',\r\n itemStyle: {\r\n borderRadius: 8\r\n },\r\n data: dataSeries\r\n }\r\n ]\r\n }\r\n }\r\n}\r\nexport function createAPolarChart(id: string, title: string, dataSeries: {\r\n value: number,\r\n name: string\r\n}[]) {\r\n\r\n return {\r\n title,\r\n id,\r\n chart: {\r\n title: [\r\n {\r\n text: title\r\n }\r\n ],\r\n toolbox: {\r\n show: true,\r\n feature: {\r\n mark: { show: true },\r\n dataView: { show: true, readOnly: false },\r\n restore: { show: true },\r\n saveAsImage: { show: true }\r\n }\r\n },\r\n polar: {\r\n radius: [30, '80%']\r\n },\r\n angleAxis: {\r\n\r\n startAngle: 75\r\n },\r\n radiusAxis: {\r\n type: 'category',\r\n data: dataSeries.map(v => v.name)\r\n },\r\n tooltip: {},\r\n series: {\r\n type: 'bar',\r\n data: dataSeries.map(v => v.value),\r\n coordinateSystem: 'polar',\r\n label: {\r\n show: true,\r\n position: 'middle',\r\n formatter: '{b}: {c}'\r\n }\r\n }\r\n } as EChartsOption\r\n\r\n\r\n }\r\n}\r\nexport const colors = [\r\n '#4285F4',\r\n '#FBBC05',\r\n '#34A853',\r\n '#EA4335',\r\n '#55ACEE',\r\n '#3B5998',\r\n '#7CBB00',\r\n '#00A1F1',\r\n '#7B0099',\r\n '#146EB4'\r\n];\r\n\r\nexport function getColors(n: number) {\r\n const result = [];\r\n for (let i = 0; i < n; i++) {\r\n result.push(colors[i % colors.length]);\r\n }\r\n return result;\r\n}\r\n","import { CommonModule } from '@angular/common';\r\nimport { Component, Input } from '@angular/core';\r\n\r\n\r\nimport { GroupConfig } from '../../functions/groupedBy';\r\n\r\nimport { createABarChart, createADonutChart, createAPolarChart, createRoseTypeChart } from '../../functions/createPieChart';\r\nimport { EChartsOption } from 'echarts';\r\nimport { DocumentLitsLabelConfigInterface } from '../../../../../types/DocumentSectionConfigurationsInterface';\r\nimport {MatButtonModule} from '@angular/material/button';\r\nimport { EChartsComponent } from '../../../e-charts/e-charts/e-charts.component';\r\nimport {MatCardModule} from '@angular/material/card';\r\nimport {MatIconModule} from '@angular/material/icon';\r\nimport {MatMenuModule} from '@angular/material/menu';\r\n@Component({\r\n selector: 'app-grouping-chart',\r\n standalone: true,\r\n imports: [CommonModule, EChartsComponent,MatCardModule,MatButtonModule,MatIconModule,MatMenuModule],\r\n templateUrl: './grouping-chart.component.html',\r\n styleUrl: './grouping-chart.component.scss'\r\n})\r\nexport class GroupingChartComponent {\r\n @Input() groups: GroupConfig[] = []\r\n @Input() numberProperties: DocumentLitsLabelConfigInterface[] = []\r\n\r\n constructor() {\r\n\r\n }\r\n private cache!: { chart: EChartsOption, title: string, id: string }[]\r\n private lastHash: string | null = null;\r\n\r\n\r\n get totalDistributionPerGroup(): { chart: EChartsOption, title: string, id: string }[] {\r\n // Create a hash or identifier based on the content of the groups to detect changes\r\n const currentHash = JSON.stringify(this.groups);\r\n if (this.lastHash === currentHash && this.cache) {\r\n // If the hash hasn't changed, return the cached result\r\n return this.cache;\r\n }\r\n\r\n // Compute the distribution if data has changed\r\n const result = this.numberProperties?.map((property, index) => ({\r\n title: property.label,\r\n id: index.toString(),\r\n dataSeries: this.groups.map(g => ({\r\n name: g.name,\r\n\r\n value: Number(g.totals?.[property.formControlName] || 0)\r\n\r\n }))\r\n }))\r\n .map(chart => {\r\n const type = this.chartConfig[chart.id]?.chartType || ChartTypes.Donut\r\n if (type === ChartTypes.Donut || !type) {\r\n return createADonutChart(chart.id, chart.title, chart.dataSeries)\r\n }\r\n\r\n if (type === ChartTypes.Bar) {\r\n return createABarChart(chart.id, chart.title, chart.dataSeries)\r\n }\r\n\r\n if (type === ChartTypes.Polar) {\r\n return createAPolarChart(chart.id, chart.title, chart.dataSeries)\r\n }\r\n if (type === ChartTypes.RoseType) {\r\n return createRoseTypeChart(chart.id, chart.title, chart.dataSeries)\r\n }\r\n\r\n return\r\n\r\n\r\n\r\n })\r\n\r\n // Update cache and hash\r\n this.cache = result as any;\r\n this.lastHash = currentHash;\r\n\r\n return result as { chart: EChartsOption, title: string, id: string }[]\r\n }\r\n activeChart: string | undefined\r\n chartConfig: {\r\n [key: string]: {\r\n full: boolean,\r\n chartType: ChartTypes\r\n }\r\n } = {}\r\n isExpanded() {\r\n return this.chartConfig[this.activeChart || '']?.full\r\n }\r\n chatTypesEnum = ChartTypes\r\n chartType() {\r\n return this.chartConfig[this.activeChart || '']?.chartType\r\n }\r\n setChartType(type: ChartTypes) {\r\n if (!this.chartConfig[this.activeChart || '']) {\r\n this.chartConfig[this.activeChart || ''] = {\r\n full: false,\r\n chartType: this.chatTypesEnum.Donut\r\n }\r\n }\r\n if (!this.chartConfig[this.activeChart || '']) return;\r\n this.chartConfig[this.activeChart || '']!['chartType'] = type\r\n this.lastHash = null\r\n }\r\n toggleExpansion() {\r\n const st = !!this.chartConfig[this.activeChart || '']?.full\r\n if (!this.chartConfig[this.activeChart || '']) {\r\n this.chartConfig[this.activeChart || ''] = {\r\n full: false,\r\n chartType: this.chatTypesEnum.Donut\r\n }\r\n }\r\n if (!this.chartConfig[this.activeChart || '']) return;\r\n return this.chartConfig[this.activeChart || '']!.full = !st\r\n }\r\n getChartSize(id: string) {\r\n return this.chartConfig[id]?.full ? '12' : '4'\r\n }\r\n}\r\nexport enum ChartTypes {\r\n Donut = \"donutChart\",\r\n Bar = \"bar\",\r\n Polar = \"polar\",\r\n RoseType = \"roseType\"\r\n\r\n}","<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>","export const COLOR_CODES = {\r\n secondary: {\r\n color1: '#FFF5EB', // Very light orange tint (5% of base)\r\n color2: '#FFEBD6', // Light orange tint (10% of base)\r\n color3: '#FFE1C2', // Light-medium orange tint (15% of base)\r\n color4: '#FFD7AD', // Medium orange tint (20% of base)\r\n color5: '#FFCD99', // Medium-deep orange tint (25% of base)\r\n color6: '#FFC285', // Deeper orange tint (30% of base)\r\n color7: '#FFB870', // Even deeper orange tint (35% of base)\r\n color8: '#F68C1E' // Base color\r\n },\r\n blue: {\r\n color1: '#DAE5FC', // Very light tint (5% of base)\r\n color2: '#C0D2F9', // Light tint (10% of base)\r\n color3: '#A6C0F7', // Light-medium tint (15% of base)\r\n color4: '#8DADF5', // Medium tint (20% of base)\r\n color5: '#739BF2', // Medium-deep tint (25% of base)\r\n color6: '#5988F0', // Deeper tint (30% of base)\r\n color7: '#3F76ED', // Even deeper tint (35% of base)\r\n color8: '#2563EB', // Base color\r\n },\r\n green: {\r\n color1: '#ECF2DB', // Very light tint (5% of base)\r\n color2: '#DFE9C2', // Light tint (10% of base)\r\n color3: '#D2E0A9', // Light-medium tint (15% of base)\r\n color4: '#C5D790', // Medium tint (20% of base)\r\n color5: '#B8CE77', // Medium-deep tint (25% of base)\r\n color6: '#ABC55E', // Deeper tint (30% of base)\r\n color7: '#9EBC45', // Even deeper tint (35% of base)\r\n color8: '#91B32C', // Base color\r\n },\r\n\r\n blueviolet: {\r\n color1: '#EBDBFA', // Very light tint (5% of base)\r\n color2: '#DDC2F7', // Light tint (10% of base)\r\n color3: '#CFA9F3', // Light-medium tint (15% of base)\r\n color4: '#C290F0', // Medium tint (20% of base)\r\n color5: '#B477EC', // Medium-deep tint (25% of base)\r\n color6: '#A65DE9', // Deeper tint (30% of base)\r\n color7: '#9844E5', // Even deeper tint (35% of base)\r\n color8: '#8A2BE2', // Base color\r\n },\r\n red: {\r\n color1: '#FDE5E1', // Very light tint (5% of base)\r\n color2: '#FBD3CB', // Light tint (10% of base)\r\n color3: '#FAC1B6', // Light-medium tint (15% of base)\r\n color4: '#F8AFA1', // Medium tint (20% of base)\r\n color5: '#F79D8B', // Medium-deep tint (25% of base)\r\n color6: '#F58A76', // Deeper tint (30% of base)\r\n color7: '#F47860', // Even deeper tint (35% of base)\r\n color8: '#F2664B', // Base color\r\n },\r\n purple: {\r\n color1: '#E5DDEE', // Very light tint (5% of base)\r\n color2: '#D3C4E2', // Light tint (10% of base)\r\n color3: '#C1ACD6', // Light-medium tint (15% of base)\r\n color4: '#AF94C9', // Medium tint (20% of base)\r\n color5: '#9D7CBD', // Medium-deep tint (25% of base)\r\n color6: '#8A63B1', // Deeper tint (30% of base)\r\n color7: '#784BA5', // Even deeper tint (35% of base)\r\n color8: '#663399', // Base color\r\n },\r\n // Spectrum Harmony - Best for business applications and financial data\r\n spectrumHarmony: {\r\n color1: '#E3F2FD', // Light blue - first level\r\n color2: '#BBDEFB', // Slightly deeper blue\r\n color3: '#DCEDC8', // Light green - second level\r\n color4: '#C5E1A5', // Slightly deeper green\r\n color5: '#FFF9C4', // Light yellow - third level\r\n color6: '#FFECB3', // Light amber - fourth level\r\n color7: '#FFE082', // Medium amber\r\n color8: '#FFCCBC' // Light red/orange - fifth level\r\n },\r\n\r\n // Vibrant Distinction - Best for complex hierarchical data requiring quick differentiation\r\n vibrantDistinction: {\r\n color1: '#E8F5E9', // Mint green - first level\r\n color2: '#C8E6C9', // Medium mint\r\n color3: '#E1F5FE', // Light blue - second level\r\n color4: '#B3E5FC', // Medium light blue\r\n color5: '#F3E5F5', // Lavender - third level\r\n color6: '#FFF8E1', // Light amber - fourth level\r\n color7: '#FFECB3', // Medium amber\r\n color8: '#FFEBEE' // Light pink - fifth level\r\n },\r\n\r\n // Natural Progression - Best for environmental data and science applications\r\n naturalProgression: {\r\n color1: '#E0F7FA', // Light cyan - first level\r\n color2: '#B2EBF2', // Medium cyan\r\n color3: '#C8E6C9', // Light green - second level\r\n color4: '#A5D6A7', // Medium green\r\n color5: '#F0F4C3', // Light lime - third level\r\n color6: '#FFE0B2', // Light orange - fourth level\r\n color7: '#FFCC80', // Medium orange\r\n color8: '#FFCDD2' // Light red - fifth level\r\n },\r\n\r\n // Colorblind-Friendly - Best for accessibility-focused and public-facing applications\r\n colorblindFriendly: {\r\n color1: '#F5F5F5', // Light gray - first level\r\n color2: '#E0E0E0', // Medium light gray\r\n color3: '#BBDEFB', // Light blue - second level\r\n color4: '#90CAF9', // Medium light blue\r\n color5: '#FFECB3', // Light amber - third level\r\n color6: '#C8E6C9', // Light green - fourth level\r\n color7: '#A5D6A7', // Medium green\r\n color8: '#E1BEE7' // Light purple - fifth level\r\n },\r\n\r\n // Corporate Structure - Best for formal reports and corporate environments\r\n corporateStructure: {\r\n color1: '#ECEFF1', // Very light blue-gray - first level\r\n color2: '#CFD8DC', // Light blue-gray - second level\r\n color3: '#B0BEC5', // Medium blue-gray - third level\r\n color4: '#90A4AE', // Medium-dark blue-gray - fourth level\r\n color5: '#78909C', // Dark blue-gray - fifth level\r\n color6: '#607D8B', // Deeper blue-gray\r\n color7: '#546E7A', // Very dark blue-gray\r\n color8: '#455A64' // Extremely dark blue-gray\r\n }\r\n};\r\n\r\n// You can implement a function to get random color codes similar to the original example\r\nlet colorDeck: any[] = [];\r\n\r\nexport const randomColorCode = () => {\r\n // If the deck is empty, refill and shuffle it\r\n if (colorDeck.length === 0) {\r\n colorDeck = [...Object.values(COLOR_CODES)];\r\n\r\n // Fisher-Yates shuffle algorithm\r\n for (let i = colorDeck.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1));\r\n [colorDeck[i], colorDeck[j]] = [colorDeck[j], colorDeck[i]];\r\n }\r\n }\r\n\r\n // Take the top color from our shuffled deck\r\n return colorDeck.pop();\r\n};","export enum SectionTypeGroups {\r\n Transactions = 'transactions',\r\n Processes = 'processes',\r\n System = 'system',\r\n FormPreview = \"formPreview\",\r\n Home = \"home\",\r\n Transaction = \"transaction\",\r\n Report = \"Report\"\r\n}\r\n\r\nexport interface DocumentSectionConfigurationsInterface {\r\n sectionTitle: string;\r\n icon: string;\r\n getSectionList:\r\n {\r\n submissionId: string;\r\n httpEndPoint: string\r\n }\r\n documentDetailsConfig?: {\r\n title: string;\r\n };\r\n sectionGroup: SectionTypeGroups\r\n docuListConfig?: DocumentLitsConfigInterface\r\n\r\n\r\n}\r\nexport interface DocumentLitsConfigInterface {\r\n\r\n segments: any[];\r\n activeSegment: string;\r\n labelConfiguration: DocumentLitsLabelConfigInterface[]\r\n\r\n}\r\nexport enum DocumentLitsLabelConfigInterfaceValueType {\r\n currency = 'currency',\r\n number = 'number',\r\n string = 'string',\r\n date = 'date',\r\n daysAgo = 'daysAgo',\r\n systemReference = 'systemReference'\r\n}\r\nexport interface DocumentLitsLabelConfigInterface {\r\n label: string;\r\n formControlName: string;\r\n isHidden?: boolean;\r\n stepName: string | null;\r\n valueType: DocumentLitsLabelConfigInterfaceValueType\r\n}\r\n\r\nexport interface GridListLabelConfigInterface {\r\n labelConfiguration: DocumentLitsLabelConfigInterface[]\r\n}","/**\r\n * Compares previous and current objects, returning only properties that changed.\r\n * - Properties in prev but not in current: set to undefined\r\n * - Properties in current but not in prev: included with current value\r\n * - Properties in both with different values: included with current value\r\n * \r\n * @example\r\n * // prev = {a: 8, b: 0}, current = {b: 5}\r\n * // result = {b: 5, a: undefined}\r\n */\r\nexport function getChangedProperties<T extends Record<string, any>>(\r\n prevPassed: T | null | undefined, \r\n currentPassed: T | null | undefined\r\n ): Partial<T> {\r\n // Handle edge cases\r\n // remove undefined in prev \r\n const prev = prevPassed ? Object.keys(prevPassed).reduce((acc, key) => {\r\n if (prevPassed[key] !== undefined) {\r\n (acc as any)[key] = prevPassed[key];\r\n }\r\n return acc;\r\n }, {} as Partial<T>) : undefined;\r\n // remove undefined in current\r\n const current = currentPassed ? Object.keys(currentPassed).reduce((acc, key) => {\r\n if (currentPassed[key] !== undefined) {\r\n (acc as any)[key] = currentPassed[key];\r\n }\r\n return acc;\r\n }, {} as Partial<T>) : undefined;\r\n // if both are undefined return empty object\r\n\r\n if (!prev && !current) return {};\r\n if (!prev) return { ...current } as Partial<T>;\r\n if (!current) {\r\n return Object.keys(prev).reduce((acc, key) => {\r\n (acc as any)[key] = undefined;\r\n return acc;\r\n }, {} as Partial<T>);\r\n }\r\n \r\n const changedProperties: Partial<T> = {};\r\n \r\n // Check properties from prev\r\n for (const key in prev) {\r\n if (!(key in current)) {\r\n // Property removed in current\r\n changedProperties[key] = undefined;\r\n } else if (!_isEqual(prev[key], current[key])) {\r\n // Property value changed\r\n changedProperties[key] = current[key];\r\n }\r\n }\r\n \r\n // Check for new properties in current\r\n for (const key in current) {\r\n if (!(key in prev)) {\r\n // New property added\r\n changedProperties[key] = current[key];\r\n }\r\n }\r\n \r\n return changedProperties;\r\n }\r\n \r\nexport function _isEqual(prev: any, next: any): boolean {\r\n if (prev === next) return true;\r\n\r\n const prevType = typeof prev;\r\n const nextType = typeof next;\r\n\r\n if (prevType !== nextType) return false;\r\n\r\n if (prevType === 'number') {\r\n return (prev === next) || (Number.isNaN(prev) && Number.isNaN(next));\r\n }\r\n\r\n if (prevType === 'string' || prevType === 'boolean') {\r\n return prev === next;\r\n }\r\n\r\n if (Array.isArray(prev) && Array.isArray(next)) {\r\n if (prev.length !== next.length) return false;\r\n for (let i = 0; i < prev.length; i++) {\r\n if (!_isEqual(prev[i], next[i])) return false;\r\n }\r\n return true;\r\n }\r\n\r\n if (prevType === 'object' && prev !== null && next !== null) {\r\n const prevConstructor = prev.constructor;\r\n const nextConstructor = next.constructor;\r\n\r\n if (prevConstructor !== nextConstructor) return false;\r\n\r\n if (prevConstructor === Date) {\r\n return prev.getTime() === next.getTime();\r\n }\r\n\r\n if (prevConstructor === RegExp) {\r\n return prev.source === next.source && prev.flags === next.flags;\r\n }\r\n\r\n const prevKeys = Object.keys(prev);\r\n const nextKeys = Object.keys(next);\r\n\r\n if (prevKeys.length !== nextKeys.length) return false;\r\n\r\n for (const key of prevKeys) {\r\n if (!Object.prototype.hasOwnProperty.call(next, key)) return false;\r\n if (!_isEqual(prev[key], next[key])) return false;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n return prev === null && next === null;\r\n}","import { DocumentLitsLabelConfigInterface } from \"../../../../types/DocumentSectionConfigurationsInterface\";\r\n\r\n\r\nexport interface GroupConfig {\r\n name: string;\r\n propertyLabel: string;\r\n IS_GROUP_CONFIG: boolean;\r\n LEVELS_BEFORE:number[]\r\n IS_CLOSED: boolean;\r\n level: number;\r\n totals?: {\r\n [key: string]: number\r\n\r\n },\r\n realTotal?: {\r\n [key: string]: number\r\n\r\n },\r\n}\r\nexport function groupData(items: any[], keys: string[], level: number = 0): Map<any, any> {\r\n if (keys.length === 0) return new Map([[level, items]]); // No more keys, return items wrapped in a Map\r\n\r\n const key = keys[0];\r\n if(!key) return new Map([[level, items]]);\r\n const restKeys = keys.slice(1);\r\n const grouped = new Map();\r\n \r\n for (const item of items) {\r\n const keyValue = item[key];\r\n \r\n if (!grouped.has(keyValue)) {\r\n grouped.set(keyValue, []);\r\n }\r\n \r\n grouped.get(keyValue).push(item);\r\n }\r\n\r\n if (restKeys.length > 0) {\r\n grouped.forEach((value, key) => { \r\n grouped.set(key, groupData(value, restKeys, level + 1));\r\n });\r\n }\r\n\r\n return grouped;\r\n}\r\n\r\nexport function matrixTableGroupedData(\r\n array: any[],\r\n propertyName: string[],\r\n closedGroups: string[],\r\n listConfig: DocumentLitsLabelConfigInterface[]\r\n): Array<GroupConfig | { [key: string]: any }> {\r\n // Use a Map to handle grouping which can be more efficient for lookups and updates\r\n const numberProperties = listConfig.filter((config) => config.valueType === 'number'\r\n || config.valueType === 'currency').map((config) => config.formControlName)\r\n const noneEmptyPropertyNames = propertyName.filter(n=> !!n&&(n.length>0));\r\n const noneEmptyArray = array.filter(a=>!!a && Object.keys(a).length>0)\r\n const groupedData = groupData(noneEmptyArray, noneEmptyPropertyNames);\r\n const result = flattenGroupedData(groupedData, propertyName);\r\n return result\r\n\r\n\r\n function getSums(data: any[]) {\r\n if (!Array.isArray(data)) {\r\n return {}\r\n }\r\n const sums: any = {\r\n IS_SUM_ROW: true\r\n }\r\n numberProperties.forEach((property) => {\r\n sums[property] = `${data.reduce((acc, item) => acc + Number(item[property] || 0), 0)}`\r\n })\r\n return sums\r\n }\r\n function flattenGroupedData(grouped: Map<any, any>, keys: string[], currentLevel = 0): any[] {\r\n let result: any[] = [];\r\n if (keys.length === currentLevel) {\r\n return Array.from(grouped.values()).flat();\r\n }\r\n\r\n grouped.forEach((value, key) => {\r\n\r\n const isClosed = closedGroups.includes(key);\r\n let _data = []\r\n\r\n _data = flattenGroupedData(value, keys, currentLevel + 1)\r\n\r\n const objectProperty = propertyName[currentLevel]\r\n const propertyConfig = listConfig.find(l => l.formControlName === objectProperty)\r\n const groupConfig: GroupConfig = {\r\n name: key,\r\n propertyLabel: propertyConfig?.label || objectProperty || '',\r\n IS_GROUP_CONFIG: true,\r\n LEVELS_BEFORE:Array.from({ length: currentLevel }, (_, i) => i + 1),\r\n level: currentLevel,\r\n totals: getSums(_data),\r\n IS_CLOSED: isClosed\r\n\r\n };\r\n \r\n result.push(groupConfig);\r\n result = result.concat(isClosed ? [] : _data)\r\n\r\n\r\n });\r\n return result;\r\n }\r\n\r\n}\r\n\r\n\r\n","import { combineLatest, map, Observable } from \"rxjs\";\r\nimport { NgxTMatrixTableStoreService } from \"./ngx-tmatrix-table-store.service\";\r\nimport { matrixTableGroupedData } from \"../functions/groupedBy\";\r\nimport { IDocumentListConfigLocal } from \"../../../../types/IMatrixTableState\";\r\n\r\nexport function NgxTMatrixTableStoreSelectors(store: NgxTMatrixTableStoreService) {\r\n const state$ = store.select(state => state);\r\n const reportName$ = store.select(state => state.reportName);\r\n const list$ = store.select(state => state.list);\r\n const listConfig$ = store.select(state => state.listConfig);\r\n const aggregates$ = store.select(state => state.aggregates);\r\n const groupOptions$ = store.select(state => state.groupOptions);\r\n const closedGroups$ = store.select(state => state.closedGroups);\r\n const isOpen$ = store.select(state => state.isOpen);\r\n const groupChart$ = store.select(state => state.groupChart);\r\n const draggedGroup$ = store.select(state => state.draggedGroup);\r\n const scrollIndex$ = store.select(state => state.scrollIndex);\r\n\r\n const tableColumns$:Observable<IDocumentListConfigLocal[] | null | undefined> = listConfig$\r\n let itemsToDisplay = 0\r\n combineLatest(\r\n [\r\n groupOptions$,\r\n listConfig$\r\n ]\r\n )\r\n .pipe(\r\n map(([\r\n groupOptions,\r\n listConfig\r\n \r\n \r\n ]) => (\r\n [{formControlName: 'tree',label: ''}, ...listConfig || []] as any)\r\n .filter((col: any) => !col.isHidden).map(\r\n (col: any) =>({\r\n ...col,\r\n INCLUDED_IN_GROUP:Boolean(groupOptions?.columns?.includes(col.formControlName))\r\n })\r\n )\r\n \r\n )\r\n )\r\n const displayedColumns$ = tableColumns$.pipe(\r\n map(tableColumns => tableColumns?.map((config: any) => config.formControlName) || [])\r\n )\r\n const groupLabelConfig$ = groupOptions$.pipe(\r\n map(groupOptions => groupOptions?.columns?.map((col, index) => ({ level: index, formControlName: index.toString(), label: '' })) || [])\r\n )\r\n const getGroupedData$ = combineLatest(\r\n [list$,\r\n listConfig$,\r\n groupOptions$,\r\n closedGroups$,\r\n scrollIndex$]\r\n ).pipe(\r\n map(\r\n ([list,listConfig,groupOptions, closedGroups,scrollIndex]) => {\r\n const groupBy = groupOptions?.columns || [];\r\n const data = matrixTableGroupedData(list || [], groupBy, closedGroups, listConfig || []);\r\n const filteredData = data.filter(d => !(d.name === undefined && d.IS_GROUP_CONFIG))\r\n const numberOfFilteredItems = filteredData.length;\r\n const pageSize = 100; // Define your desired multiple\r\n \r\n if (numberOfFilteredItems === 0) {\r\n return filteredData.slice(0, 0);\r\n }\r\n if(scrollIndex <= pageSize){\r\n return filteredData.slice(0, pageSize);\r\n }\r\n itemsToDisplay = Math.ceil((scrollIndex + 1) / pageSize) * pageSize\r\n \r\n return filteredData.slice(0, itemsToDisplay )\r\n \r\n }\r\n )\r\n )\r\n const groupedColumns$ = combineLatest(\r\n [\r\n groupOptions$,\r\n listConfig$\r\n ]\r\n ).pipe(\r\n map(([\r\n groupOptions,\r\n listConfig\r\n ]) => \r\n (groupOptions?.columns?.filter((col) => !groupOptions.hiddenColumns?.includes(col)) || []).map(\r\n groupCol=>({\r\n formControlName:groupCol,\r\n label:listConfig?.find((config) => config.formControlName === groupCol)?.label || ''\r\n })\r\n )\r\n \r\n\r\n \r\n \r\n )\r\n )\r\n return {\r\n state$,\r\n reportName$,\r\n list$,\r\n listConfig$,\r\n closedGroups$,\r\n isOpen$,\r\n aggregates$,\r\n groupOptions$,\r\n tableColumns$,\r\n displayedColumns$,\r\n groupLabelConfig$,\r\n getGroupedData$ ,\r\n groupedColumns$ ,\r\n groupChart$ ,\r\n draggedGroup$,\r\n scrollIndex$ \r\n\r\n }\r\n}","import { DocumentLitsLabelConfigInterface } from \"ngx-t-forms-types\";\r\nimport { NgxTMatrixTableStoreService } from \"./ngx-tmatrix-table-store.service\";\r\nimport { GroupSettingsModel } from \"../interface/GroupSettingsModel\";\r\nimport { _isEqual } from \"../../../shared/functions/isEqual\";\r\nimport { CdkDragDrop, moveItemInArray } from \"@angular/cdk/drag-drop\";\r\nimport { GroupChartConfig } from \"../ngx-t-matrix-table.component\";\r\n\r\nexport function NgxTMatrixTableStoreActions(store:NgxTMatrixTableStoreService) {\r\n \r\n return {\r\n setReportName: store.updater((state, reportName: string | null) => ({\r\n ...state,\r\n reportName\r\n })),\r\n \r\n setList: store.updater((state, list: any[] | null) => ({\r\n ...state,\r\n list\r\n })),\r\n\r\n setListConfig: store.updater((state, listConfig: DocumentLitsLabelConfigInterface[] | null | undefined) => ({\r\n ...state,\r\n listConfig\r\n })),\r\n\r\n setAggregates: store.updater((state, aggregates: DocumentLitsLabelConfigInterface[] | null) => ({\r\n ...state,\r\n aggregates\r\n })),\r\n\r\n setGroupOptions: store.updater((state, groupOptions: GroupSettingsModel | null) => ({\r\n ...state,\r\n groupOptions\r\n })),\r\n toggleMenu: store.updater((state, row: any) => ({\r\n ...state,\r\n isOpen:_isEqual(state.isOpen, row) ? undefined : row\r\n })),\r\n setGroupChart: store.updater((state, groupChart: GroupChartConfig | null) => ({\r\n ...state,\r\n groupChart\r\n })),\r\n setDraggedGroup : store.updater((state, draggedGroup: string) => ({\r\n ...state,\r\n draggedGroup\r\n })),\r\n setScrollIndex: store.updater((state, scrollIndex: number) => ({\r\n ...state,\r\n scrollIndex\r\n })),\r\n toggleCloseGroup: store.updater((state, name: any) => {\r\n const closedGroups = state.closedGroups || [];\r\n const isClosed = closedGroups.includes(name);\r\n return {\r\n ...state,\r\n closedGroups: isClosed ? closedGroups.filter(g => g !== name) : [...closedGroups, name]\r\n };\r\n }),\r\n removeGroupColumn: store.updater((state, columnName: string) => {\r\n return {\r\n ...state,\r\n groupOptions: state.groupOptions ? {\r\n ...state.groupOptions,\r\n columns: state.groupOptions.columns?.filter((col) => col !== columnName)\r\n } : null\r\n }\r\n }),\r\n dropGroupColumn: store.updater((state) => {\r\n const newGroup = state.draggedGroup\r\n const doesNotInclude = !state.groupOptions?.columns?.includes(newGroup || '');\r\n if (doesNotInclude && newGroup !== undefined && newGroup !== '') {\r\n return {\r\n ...state,\r\n groupOptions: {\r\n ...state.groupOptions,\r\n columns: [...(state.groupOptions?.columns || []), newGroup]\r\n }\r\n }\r\n }\r\n return state\r\n }),\r\n dropGroupingToSortGroups: store.updater((state, event: CdkDragDrop<string[]>) => {\r\n const newColumns = [...(state.groupOptions?.columns || [])];\r\n moveItemInArray(newColumns, event.previousIndex, event.currentIndex);\r\n return {\r\n ...state,\r\n groupOptions: {\r\n ...state.groupOptions,\r\n columns: newColumns\r\n }\r\n }\r\n }),\r\n addToGrouping: store.updater((state, formControlName: string) => {\r\n let columns = state.groupOptions?.columns || [];\r\n if (!columns.includes(formControlName)) {\r\n columns = [...columns, formControlName];\r\n } else{\r\n columns =columns.filter((col) => col !== formControlName)\r\n }\r\n return {\r\n ...state,\r\n groupOptions: {\r\n ...state.groupOptions,\r\n columns\r\n }\r\n\r\n }\r\n })\r\n\r\n };\r\n}","export function toCamelCase(arrayOfObjects: any[]): any[] {\r\n return arrayOfObjects.map((obj) => {\r\n const newObj: any = {};\r\n Object.keys(obj).forEach((key) => {\r\n const newKey = ''.concat(key);\r\n const camelCaseKey = camelize(newKey) as string;\r\n newObj[camelCaseKey] = obj[key];\r\n });\r\n\r\n return newObj;\r\n });\r\n}\r\n\r\nexport function camelize(str: string) {\r\n if (!!str) {\r\n return str\r\n .replace(/(?:^\\w|[A-Z]|\\b\\w)/g, function (word: string, index: number) {\r\n return index === 0 ? word.toLowerCase() : word.toUpperCase();\r\n })\r\n .replace(/\\s+/g, '');\r\n } else {\r\n return;\r\n }\r\n}\r\n","import * as XLSX from 'xlsx';\r\nimport { toCamelCase } from './keysToCamelCase';\r\n\r\n\r\nconst getFileName = (name: string) => {\r\n let timeSpan = new Date().toISOString();\r\n let sheetName = name || 'ExportResult';\r\n let fileName = `${sheetName}-${timeSpan}`;\r\n return {\r\n sheetName,\r\n fileName,\r\n };\r\n};\r\nexport class TableUtil {\r\n static exportTableToExcel(tableId: string, name: string) {\r\n let { sheetName, fileName } = getFileName(name);\r\n let targetTableElm = document.getElementById(tableId);\r\n let wb = XLSX.utils.table_to_book(targetTableElm, <XLSX.Table2SheetOpts>{\r\n sheet: sheetName,\r\n });\r\n XLSX.writeFile(wb, `${fileName}.xlsx`);\r\n }\r\n\r\n static exportArrayToExcel(arr: any[], name: string) {\r\n const { sheetName, fileName } = getFileName(name);\r\n const wb = XLSX.utils.book_new();\r\n\r\n // Process the main array\r\n const mainArray = arr.map(item => this.processSimpleObject(item));\r\n const mainSheet = XLSX.utils.json_to_sheet(mainArray);\r\n XLSX.utils.book_append_sheet(wb, mainSheet, this.sanitizeSheetName(sheetName));\r\n\r\n // Process nested arrays\r\n arr.forEach((item, index) => {\r\n const arrayProperties = this.findArrayProperties(item);\r\n\r\n arrayProperties.forEach((arrayData, propertyName) => {\r\n // Only process if the array contains data\r\n if (arrayData.length > 0) {\r\n // Create a sheet name combining property name and parent index\r\n const nestedSheetName = this.sanitizeSheetName(`${propertyName}_${index + 1}`);\r\n\r\n // Process the nested array items\r\n const processedArray = arrayData.map(nestedItem => {\r\n if (typeof nestedItem === 'object' && nestedItem !== null) {\r\n return this.processSimpleObject(nestedItem);\r\n }\r\n // If it's a primitive value, wrap it in an object\r\n return { value: nestedItem };\r\n });\r\n\r\n // Create and append the sheet if it has data\r\n if (processedArray.length > 0) {\r\n const nestedSheet = XLSX.utils.json_to_sheet(processedArray);\r\n try {\r\n XLSX.utils.book_append_sheet(wb, nestedSheet, nestedSheetName);\r\n } catch (e) {\r\n const msg = e instanceof Error ? e.message : 'Unknown error';\r\n console.warn(`Could not create sheet for ${nestedSheetName}: ${msg}`);\r\n }\r\n }\r\n }\r\n });\r\n });\r\n\r\n // Write the workbook to file\r\n XLSX.writeFile(wb, `${fileName}.xlsx`);\r\n }\r\n\r\n static onFileChange(event: any): Promise<any[]> {\r\n return new Promise<any[]>((resolve, reject) => {\r\n try {\r\n const target: DataTransfer = <DataTransfer>event.target;\r\n if (target.files.length !== 1)\r\n throw new Error('Cannot use multiple files');\r\n const reader: FileReader = new FileReader();\r\n reader.onload = (e: any) => {\r\n const bstr: string = e.target.result;\r\n const workbook: XLSX.WorkBook = XLSX.read(bstr, { type: 'binary' });\r\n const firstSheetName = workbook.SheetNames[0];\r\n if (!firstSheetName) throw new Error('No sheet found');\r\n const worksheet = workbook.Sheets[firstSheetName];\r\n if (!worksheet) throw new Error('No worksheet found');\r\n const arrayData = XLSX.utils.sheet_to_json(worksheet, { raw: false });\r\n // Iterate through each row and format date cells\r\n arrayData.forEach((row: any) => {\r\n for (const key in row) {\r\n if (\r\n row.hasOwnProperty(key) &&\r\n typeof row[key] === 'number' &&\r\n XLSX.SSF.isValidDate(row[key])\r\n ) {\r\n row[key] = XLSX.SSF.format_date(row[key], 'yyyy-mm-dd');\r\n }\r\n }\r\n });\r\n // Do something with arrayData\r\n resolve(toCamelCase(arrayData));\r\n };\r\n reader.onerror = (e) => reject(e);\r\n const file = target.files[0]\r\n if (!file) throw new Error('No file found');\r\n reader.readAsBinaryString(file);\r\n } catch (error) {\r\n reject(error);\r\n }\r\n });\r\n }\r\n private static processSimpleObject(obj: any): any {\r\n const newObj: any = {};\r\n for (const key in obj) {\r\n if (typeof obj[key] === 'object' && obj[key] !== null) {\r\n // If it's not an array, convert to string\r\n if (!Array.isArray(obj[key])) {\r\n newObj[key] = JSON.stringify(obj[key]);\r\n }\r\n // Arrays will be handled separately, so we skip them here\r\n } else {\r\n newObj[key] = obj[key];\r\n }\r\n }\r\n return newObj;\r\n }\r\n private static findArrayProperties(obj: any): Map<string, any[]> {\r\n const arrayProps = new Map<string, any[]>();\r\n for (const key in obj) {\r\n if (Array.isArray(obj[key])) {\r\n arrayProps.set(key, obj[key]);\r\n }\r\n }\r\n return arrayProps;\r\n }\r\n private static sanitizeSheetName(name: string): string {\r\n // Excel sheet names can't exceed 31 characters and can't contain certain characters\r\n return name.replace(/[\\[\\]\\*\\/\\\\\\?\\:]/g, '_').substring(0, 31);\r\n }\r\n\r\n}\r\n","import { combineLatest, Observable,switchMap, tap } from \"rxjs\";\r\nimport { NgxTMatrixTableStoreService } from \"./ngx-tmatrix-table-store.service\";\r\nimport { TableUtil } from \"../../../shared/functions/tableutills\";\r\n\r\nexport function NgxTMatrixTableStoreEffects(store: NgxTMatrixTableStoreService) {\r\n const exportData = store