Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
807058e40d | ||
|
|
c6022b0e22 | ||
|
|
9aed6e0d43 | ||
|
|
64b664e625 | ||
|
|
49f1a2fbf2 | ||
|
|
6bfe890cd9 | ||
|
|
3e70f85644 | ||
|
|
482805b5cf | ||
|
|
ffda17e6b1 | ||
|
|
7396843bc6 | ||
|
|
4f75ecb3e0 |
@@ -1,8 +1,9 @@
|
||||
export class ReportBuilder {
|
||||
public report_id: number;
|
||||
public report_name:string;
|
||||
public description: string;
|
||||
public report_tags: string;
|
||||
public servicename:string;
|
||||
|
||||
}
|
||||
public report_name: string;
|
||||
public description: string;
|
||||
public report_tags: string;
|
||||
public servicename: string;
|
||||
// Add SureConnect reference
|
||||
public sureConnectId: number | null;
|
||||
}
|
||||
@@ -23,8 +23,22 @@ export interface DashboardContentModel {
|
||||
component?: any;
|
||||
name: string;
|
||||
type?:string;
|
||||
// Common properties
|
||||
// Chart properties
|
||||
xAxis?: string;
|
||||
yAxis?: string | string[];
|
||||
chartType?: string;
|
||||
charttitle?: string;
|
||||
chartlegend?: boolean;
|
||||
showlabel?: boolean;
|
||||
chartcolor?: boolean;
|
||||
slices?: boolean;
|
||||
donut?: boolean;
|
||||
charturl?: string;
|
||||
chartparameter?: string;
|
||||
datastore?: string;
|
||||
table?: string;
|
||||
datasource?: string;
|
||||
fieldName?: string;
|
||||
connection?: string;
|
||||
baseFilters?: any[];
|
||||
// Common filter properties
|
||||
@@ -37,6 +51,11 @@ export interface DashboardContentModel {
|
||||
drilldownParameter?: string;
|
||||
drilldownFilters?: any[];
|
||||
drilldownLayers?: any[];
|
||||
// Compact filter properties
|
||||
filterKey?: string;
|
||||
filterType?: string;
|
||||
filterLabel?: string;
|
||||
filterOptions?: string[];
|
||||
}
|
||||
|
||||
export interface DashboardModel {
|
||||
|
||||
@@ -28,6 +28,8 @@ import { CommonFilterComponent } from '../common-filter/common-filter.component'
|
||||
import { CompactFilterComponent } from '../common-filter';
|
||||
// Add the FilterService import
|
||||
import { FilterService } from '../common-filter/filter.service';
|
||||
// Add the UnifiedChartComponent import
|
||||
import { UnifiedChartComponent } from '../gadgets/unified-chart';
|
||||
|
||||
function isNullArray(arr) {
|
||||
return !Array.isArray(arr) || arr.length === 0;
|
||||
@@ -112,6 +114,10 @@ export class EditnewdashComponent implements OnInit {
|
||||
{
|
||||
name: 'Compact Filter',
|
||||
identifier: 'compact_filter'
|
||||
},
|
||||
{
|
||||
name: 'Unified Chart',
|
||||
identifier: 'unified_chart'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -125,19 +131,20 @@ export class EditnewdashComponent implements OnInit {
|
||||
|
||||
protected componentCollection = [
|
||||
{ name: "Common Filter", componentInstance: CommonFilterComponent },
|
||||
{ name: "Line Chart", componentInstance: LineChartComponent },
|
||||
{ name: "Doughnut Chart", componentInstance: DoughnutChartComponent },
|
||||
{ name: "Radar Chart", componentInstance: RadarChartComponent },
|
||||
{ name: "Bar Chart", componentInstance: BarChartComponent },
|
||||
{ name: "Pie Chart", componentInstance: PieChartComponent },
|
||||
{ name: "Polar Area Chart", componentInstance: PolarChartComponent },
|
||||
{ name: "Bubble Chart", componentInstance: BubbleChartComponent },
|
||||
{ name: "Scatter Chart", componentInstance: ScatterChartComponent },
|
||||
{ name: "Dynamic Chart", componentInstance: DynamicChartComponent },
|
||||
{ name: "Financial Chart", componentInstance: FinancialChartComponent },
|
||||
{ name: "Line Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Doughnut Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Radar Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Bar Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Pie Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Polar Area Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Bubble Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Scatter Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Dynamic Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "Financial Chart", componentInstance: UnifiedChartComponent },
|
||||
{ name: "To Do Chart", componentInstance: ToDoChartComponent },
|
||||
{ name: "Grid View", componentInstance: GridViewComponent },
|
||||
{ name: "Compact Filter", componentInstance: CompactFilterComponent }, // Add this line
|
||||
{ name: "Compact Filter", componentInstance: CompactFilterComponent },
|
||||
{ name: "Unified Chart", componentInstance: UnifiedChartComponent },
|
||||
];
|
||||
model: any;
|
||||
linesdata: any;
|
||||
@@ -170,6 +177,7 @@ export class EditnewdashComponent implements OnInit {
|
||||
yAxis: '',
|
||||
xAxis: '',
|
||||
connection: '', // Add connection field
|
||||
chartType: '', // Add chartType field
|
||||
// Drilldown configuration properties (base level)
|
||||
drilldownEnabled: false,
|
||||
drilldownApiUrl: '',
|
||||
@@ -385,6 +393,27 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
|
||||
// Map chart names to unified chart types
|
||||
const chartTypeMap = {
|
||||
'Radar Chart': 'radar',
|
||||
'Line Chart': 'line',
|
||||
'Doughnut Chart': 'doughnut',
|
||||
'Bar Chart': 'bar',
|
||||
'Pie Chart': 'pie',
|
||||
'Polar Area Chart': 'polar',
|
||||
'Bubble Chart': 'bubble',
|
||||
'Scatter Chart': 'scatter',
|
||||
'Dynamic Chart': 'line',
|
||||
'Financial Chart': 'line'
|
||||
};
|
||||
|
||||
// If this is a chart, set the chartType property
|
||||
if (chartTypeMap.hasOwnProperty(dashboard.name)) {
|
||||
dashboard.chartType = chartTypeMap[dashboard.name];
|
||||
// Keep the original name instead of changing it to "Unified Chart"
|
||||
// dashboard.name = "Unified Chart";
|
||||
}
|
||||
|
||||
// Ensure compact filter configuration properties are properly initialized
|
||||
if (dashboard.component === 'Compact Filter' || dashboard.name === 'Compact Filter') {
|
||||
// Make sure all compact filter properties exist
|
||||
@@ -409,6 +438,29 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
|
||||
// Map unified chart types back to chart names for serialization
|
||||
const chartNameMap = {
|
||||
'radar': 'Radar Chart',
|
||||
'line': 'Line Chart',
|
||||
'doughnut': 'Doughnut Chart',
|
||||
'bar': 'Bar Chart',
|
||||
'pie': 'Pie Chart',
|
||||
'polar': 'Polar Area Chart',
|
||||
'bubble': 'Bubble Chart',
|
||||
'scatter': 'Scatter Chart'
|
||||
};
|
||||
|
||||
// If this is a unified chart, set the name back to the appropriate chart name
|
||||
if (dashboard.name === 'Unified Chart' && dashboard.chartType && chartNameMap.hasOwnProperty(dashboard.chartType)) {
|
||||
dashboard.name = chartNameMap[dashboard.chartType];
|
||||
}
|
||||
// Also handle the case where the chart already has the correct name
|
||||
else if (dashboard.chartType && chartNameMap.hasOwnProperty(dashboard.chartType) &&
|
||||
dashboard.name === chartNameMap[dashboard.chartType]) {
|
||||
// The name is already correct, no need to change it
|
||||
dashboard.component = "Unified Chart";
|
||||
}
|
||||
|
||||
// Ensure compact filter configuration properties are preserved
|
||||
if (dashboard.name === 'Compact Filter') {
|
||||
// Make sure all compact filter properties exist
|
||||
@@ -420,6 +472,7 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add method to get available fields for a filter dropdown (excluding already selected fields)
|
||||
getAvailableFields(filters: any[], currentIndex: number, allFields: string[]): string[] {
|
||||
if (!filters || !allFields) {
|
||||
@@ -465,6 +518,7 @@ export class EditnewdashComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
switch (componentType) {
|
||||
// Handle all chart types by converting them to unified charts
|
||||
case "radar_chart":
|
||||
return this.dashboardArray.push({
|
||||
cols: 5,
|
||||
@@ -472,8 +526,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: RadarChartComponent,
|
||||
name: "Radar Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Radar Chart",
|
||||
chartType: 'radar',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "line_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -482,8 +541,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: LineChartComponent,
|
||||
name: "Line Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Line Chart",
|
||||
chartType: 'line',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "doughnut_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -492,8 +556,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: DoughnutChartComponent,
|
||||
name: "Doughnut Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Doughnut Chart",
|
||||
chartType: 'doughnut',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "bar_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -502,8 +571,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: BarChartComponent,
|
||||
name: "Bar Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Bar Chart",
|
||||
chartType: 'bar',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "pie_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -512,8 +586,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: PieChartComponent,
|
||||
name: "Pie Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Pie Chart",
|
||||
chartType: 'pie',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "polar_area_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -522,8 +601,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: PolarChartComponent,
|
||||
name: "Polar Area Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Polar Area Chart",
|
||||
chartType: 'polar',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "bubble_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -532,8 +616,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: BubbleChartComponent,
|
||||
name: "Bubble Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Bubble Chart",
|
||||
chartType: 'bubble',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "scatter_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -542,8 +631,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: ScatterChartComponent,
|
||||
name: "Scatter Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Scatter Chart",
|
||||
chartType: 'scatter',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "dynamic_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -552,8 +646,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: DynamicChartComponent,
|
||||
name: "Dynamic Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Dynamic Chart",
|
||||
chartType: 'line', // Default to line for dynamic chart
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "financial_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -562,8 +661,13 @@ export class EditnewdashComponent implements OnInit {
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: FinancialChartComponent,
|
||||
name: "Financial Chart"
|
||||
component: UnifiedChartComponent,
|
||||
name: "Financial Chart",
|
||||
chartType: 'line', // Default to line for financial chart
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
case "to_do_chart":
|
||||
return this.dashboardArray.push({
|
||||
@@ -610,6 +714,22 @@ export class EditnewdashComponent implements OnInit {
|
||||
component: GridViewComponent,
|
||||
name: "Grid View"
|
||||
});
|
||||
case "unified_chart":
|
||||
return this.dashboardArray.push({
|
||||
cols: 5,
|
||||
rows: 6,
|
||||
x: 0,
|
||||
y: 0,
|
||||
chartid: maxChartId + 1,
|
||||
component: UnifiedChartComponent,
|
||||
name: "Unified Chart",
|
||||
// Add default configuration for unified chart
|
||||
chartType: 'bar',
|
||||
xAxis: '',
|
||||
yAxis: '',
|
||||
table: '',
|
||||
connection: undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
removeItem(item) {
|
||||
@@ -662,6 +782,10 @@ export class EditnewdashComponent implements OnInit {
|
||||
if (item['filterOptions'] === undefined) {
|
||||
this.gadgetsEditdata['filterOptions'] = [];
|
||||
}
|
||||
// Initialize chartType property if not present (for unified chart)
|
||||
if (item['chartType'] === undefined) {
|
||||
this.gadgetsEditdata['chartType'] = 'bar';
|
||||
}
|
||||
|
||||
// Initialize filterOptionsString for compact filter
|
||||
if (item.name === 'Compact Filter') {
|
||||
@@ -930,6 +1054,11 @@ export class EditnewdashComponent implements OnInit {
|
||||
xyz.connection = this.gadgetsEditdata.connection || undefined;
|
||||
}
|
||||
|
||||
// For unified chart, preserve chart configuration properties
|
||||
if (item.name === 'Unified Chart') {
|
||||
xyz.chartType = this.gadgetsEditdata.chartType || 'bar';
|
||||
}
|
||||
|
||||
console.log(xyz);
|
||||
return xyz;
|
||||
}
|
||||
@@ -1013,6 +1142,55 @@ export class EditnewdashComponent implements OnInit {
|
||||
return commonFilterInputs;
|
||||
}
|
||||
|
||||
// For UnifiedChartComponent, pass chart properties with chartType
|
||||
// Check for specific chart names that use UnifiedChartComponent
|
||||
const unifiedChartNames = [
|
||||
'Radar Chart', 'Line Chart', 'Doughnut Chart', 'Bar Chart',
|
||||
'Pie Chart', 'Polar Area Chart', 'Bubble Chart', 'Scatter Chart',
|
||||
'Dynamic Chart', 'Financial Chart', 'Unified Chart'
|
||||
];
|
||||
|
||||
if (unifiedChartNames.includes(item.name)) {
|
||||
const unifiedChartInputs = {
|
||||
chartType: item.chartType || 'bar',
|
||||
xAxis: item.xAxis,
|
||||
yAxis: item.yAxis,
|
||||
table: item.table,
|
||||
datastore: item.datastore,
|
||||
charttitle: item.charttitle,
|
||||
chartlegend: item.chartlegend,
|
||||
showlabel: item.showlabel,
|
||||
chartcolor: item.chartcolor,
|
||||
slices: item.slices,
|
||||
donut: item.donut,
|
||||
charturl: item.charturl,
|
||||
chartparameter: item.chartparameter,
|
||||
datasource: item.datasource,
|
||||
fieldName: item.name, // Using item.name as fieldName
|
||||
connection: item['connection'], // Add connection field using bracket notation
|
||||
// Base drilldown configuration properties
|
||||
drilldownEnabled: item['drilldownEnabled'],
|
||||
drilldownApiUrl: item['drilldownApiUrl'],
|
||||
// Removed drilldownParameterKey since we're using URL templates
|
||||
drilldownXAxis: item['drilldownXAxis'],
|
||||
drilldownYAxis: item['drilldownYAxis'],
|
||||
drilldownParameter: item['drilldownParameter'], // Add drilldown parameter
|
||||
baseFilters: item['baseFilters'] || [], // Add base filters
|
||||
drilldownFilters: item['drilldownFilters'] || [], // Add drilldown filters
|
||||
// Multi-layer drilldown configurations
|
||||
drilldownLayers: item['drilldownLayers'] || []
|
||||
};
|
||||
|
||||
// Remove undefined properties to avoid passing unnecessary data
|
||||
Object.keys(unifiedChartInputs).forEach(key => {
|
||||
if (unifiedChartInputs[key] === undefined) {
|
||||
delete unifiedChartInputs[key];
|
||||
}
|
||||
});
|
||||
|
||||
return unifiedChartInputs;
|
||||
}
|
||||
|
||||
// For GridViewComponent, pass chart properties with drilldown support
|
||||
if (item.component && item.component.name === 'GridViewComponent') {
|
||||
const gridInputs = {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './unified-chart.component';
|
||||
@@ -0,0 +1,387 @@
|
||||
<div class="chart-container" *ngIf="!noDataAvailable && !isLoading">
|
||||
<!-- Back button for drilldown navigation -->
|
||||
<div class="drilldown-back" *ngIf="currentDrilldownLevel > 0">
|
||||
<button class="btn btn-sm btn-secondary" (click)="navigateBack()">
|
||||
<clr-icon shape="arrow" dir="left"></clr-icon>
|
||||
Back
|
||||
</button>
|
||||
<span class="drilldown-level">Level: {{ currentDrilldownLevel }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Chart title -->
|
||||
<div class="chart-title" *ngIf="charttitle">
|
||||
<h4>{{ charttitle }}</h4>
|
||||
</div>
|
||||
|
||||
<!-- Filter toggle icon -->
|
||||
<div class="filter-toggle-icon" *ngIf="baseFilters && baseFilters.length > 0"
|
||||
(click)="toggleFilters()"
|
||||
style="cursor: pointer; text-align: right; padding: 5px;">
|
||||
<clr-icon shape="filter" size="24"
|
||||
[style.color]="showFilters ? '#007cba' : '#666'"
|
||||
title="Toggle Filters">
|
||||
</clr-icon>
|
||||
<span style="margin-left: 5px; font-size: 12px; color: #666;">
|
||||
{{ showFilters ? 'Hide Filters' : 'Show Filters' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Render different chart types based on chartType input -->
|
||||
<div class="chart-wrapper">
|
||||
<!-- Bar Chart -->
|
||||
<div *ngIf="chartType === 'bar'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'bar'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Line Chart -->
|
||||
<div *ngIf="chartType === 'line'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'line'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Pie Chart -->
|
||||
<div *ngIf="chartType === 'pie'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'pie'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Doughnut Chart -->
|
||||
<div *ngIf="chartType === 'doughnut'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'doughnut'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Bubble Chart -->
|
||||
<div *ngIf="chartType === 'bubble'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="bubbleChartData"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'bubble'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Radar Chart -->
|
||||
<div *ngIf="chartType === 'radar'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'radar'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Polar Area Chart -->
|
||||
<div *ngIf="chartType === 'polar'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'polarArea'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Scatter Chart -->
|
||||
<div *ngIf="chartType === 'scatter'" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="chartData"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'scatter'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<!-- Default/Unknown Chart Type -->
|
||||
<div *ngIf="!['bar', 'line', 'pie', 'doughnut', 'bubble', 'radar', 'polar', 'scatter'].includes(chartType)" class="chart-canvas-container">
|
||||
<canvas baseChart
|
||||
[datasets]="chartData"
|
||||
[labels]="chartLabels"
|
||||
[options]="chartOptions"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="'bar'"
|
||||
(chartClick)="chartClicked($event)"
|
||||
(chartHover)="chartHovered($event)">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Collapsible Base Filters -->
|
||||
<div class="filters-section" *ngIf="baseFilters && baseFilters.length > 0 && showFilters">
|
||||
<h5>Filters</h5>
|
||||
<div class="filters-container">
|
||||
<div class="filter-item" *ngFor="let filter of baseFilters; let i = index">
|
||||
<!-- Text Filter -->
|
||||
<div *ngIf="!filter.type || filter.type === 'text'" class="filter-text">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)"
|
||||
class="form-control" placeholder="Enter {{ filter.field || 'value' }}">
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Filter -->
|
||||
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<select [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)" class="form-control">
|
||||
<option value="">Select {{ filter.field || 'value' }}</option>
|
||||
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Multiselect Filter -->
|
||||
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="multiselect-container">
|
||||
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'base')">
|
||||
<span *ngIf="!filter.value || (Array.isArray(filter.value) && filter.value.length === 0)">Select {{ filter.field || 'options' }}</span>
|
||||
<span *ngIf="filter.value && !Array.isArray(filter.value)">
|
||||
{{ filter.value }}
|
||||
</span>
|
||||
<span *ngIf="filter.value && Array.isArray(filter.value) && filter.value.length > 0">
|
||||
{{ filter.value.length }} selected
|
||||
</span>
|
||||
</div>
|
||||
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'base')">
|
||||
<div class="multiselect-option" *ngFor="let option of getFilterOptions(filter)">
|
||||
<input type="checkbox"
|
||||
[checked]="isOptionSelected(filter, option)"
|
||||
(change)="onMultiSelectChange(filter, option, $event)"
|
||||
id="base-{{ filter.field }}-{{ option }}">
|
||||
<label [for]="'base-' + filter.field + '-' + option">{{ option }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Date Range Filter -->
|
||||
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="date-range-inputs">
|
||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeInputChange(filter, 'start', $event)"
|
||||
class="form-control" placeholder="Start Date">
|
||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeInputChange(filter, 'end', $event)"
|
||||
class="form-control" placeholder="End Date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle Filter -->
|
||||
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="toggle-switch">
|
||||
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
||||
id="toggle-{{ filter.field }}">
|
||||
<label [for]="'toggle-' + filter.field" class="toggle-label">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Drilldown Filters -->
|
||||
<div class="filters-section" *ngIf="drilldownFilters && drilldownFilters.length > 0 && currentDrilldownLevel > 0 && showFilters">
|
||||
<h5>Drilldown Filters</h5>
|
||||
<div class="filters-container">
|
||||
<div class="filter-item" *ngFor="let filter of drilldownFilters; let i = index">
|
||||
<!-- Text Filter -->
|
||||
<div *ngIf="!filter.type || filter.type === 'text'" class="filter-text">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)"
|
||||
class="form-control" placeholder="Enter {{ filter.field || 'value' }}">
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Filter -->
|
||||
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<select [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)" class="form-control">
|
||||
<option value="">Select {{ filter.field || 'value' }}</option>
|
||||
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Multiselect Filter -->
|
||||
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="multiselect-container">
|
||||
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'drilldown')">
|
||||
<span *ngIf="!filter.value || (Array.isArray(filter.value) && filter.value.length === 0)">Select {{ filter.field || 'options' }}</span>
|
||||
<span *ngIf="filter.value && !Array.isArray(filter.value)">
|
||||
{{ filter.value }}
|
||||
</span>
|
||||
<span *ngIf="filter.value && Array.isArray(filter.value) && filter.value.length > 0">
|
||||
{{ filter.value.length }} selected
|
||||
</span>
|
||||
</div>
|
||||
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'drilldown')">
|
||||
<div class="multiselect-option" *ngFor="let option of getFilterOptions(filter)">
|
||||
<input type="checkbox"
|
||||
[checked]="isOptionSelected(filter, option)"
|
||||
(change)="onMultiSelectChange(filter, option, $event)"
|
||||
id="drilldown-{{ filter.field }}-{{ option }}">
|
||||
<label [for]="'drilldown-' + filter.field + '-' + option">{{ option }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Date Range Filter -->
|
||||
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="date-range-inputs">
|
||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeInputChange(filter, 'start', $event)"
|
||||
class="form-control" placeholder="Start Date">
|
||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeInputChange(filter, 'end', $event)"
|
||||
class="form-control" placeholder="End Date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle Filter -->
|
||||
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="toggle-switch">
|
||||
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
||||
id="drilldown-toggle-{{ filter.field }}">
|
||||
<label [for]="'drilldown-toggle-' + filter.field" class="toggle-label">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Layer Filters -->
|
||||
<div class="filters-section" *ngIf="hasActiveLayerFilters() && showFilters">
|
||||
<h5>Layer Filters</h5>
|
||||
<div class="filters-container">
|
||||
<div class="filter-item" *ngFor="let filter of getActiveLayerFilters(); let i = index">
|
||||
<!-- Text Filter -->
|
||||
<div *ngIf="!filter.type || filter.type === 'text'" class="filter-text">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)"
|
||||
class="form-control" placeholder="Enter {{ filter.field || 'value' }}">
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Filter -->
|
||||
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<select [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)" class="form-control">
|
||||
<option value="">Select {{ filter.field || 'value' }}</option>
|
||||
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Multiselect Filter -->
|
||||
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="multiselect-container">
|
||||
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'layer')">
|
||||
<span *ngIf="!filter.value || (Array.isArray(filter.value) && filter.value.length === 0)">Select {{ filter.field || 'options' }}</span>
|
||||
<span *ngIf="filter.value && !Array.isArray(filter.value)">
|
||||
{{ filter.value }}
|
||||
</span>
|
||||
<span *ngIf="filter.value && Array.isArray(filter.value) && filter.value.length > 0">
|
||||
{{ filter.value.length }} selected
|
||||
</span>
|
||||
</div>
|
||||
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'layer')">
|
||||
<div class="multiselect-option" *ngFor="let option of getFilterOptions(filter)">
|
||||
<input type="checkbox"
|
||||
[checked]="isOptionSelected(filter, option)"
|
||||
(change)="onMultiSelectChange(filter, option, $event)"
|
||||
id="layer-{{ filter.field }}-{{ option }}">
|
||||
<label [for]="'layer-' + filter.field + '-' + option">{{ option }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Date Range Filter -->
|
||||
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="date-range-inputs">
|
||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeInputChange(filter, 'start', $event)"
|
||||
class="form-control" placeholder="Start Date">
|
||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeInputChange(filter, 'end', $event)"
|
||||
class="form-control" placeholder="End Date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle Filter -->
|
||||
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
||||
<div class="toggle-switch">
|
||||
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
||||
id="layer-toggle-{{ filter.field }}">
|
||||
<label [for]="'layer-toggle-' + filter.field" class="toggle-label">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Clear Filters Button -->
|
||||
<div class="clear-filters" *ngIf="hasActiveFilters() && showFilters">
|
||||
<button class="btn btn-sm btn-outline" (click)="clearAllFilters()">
|
||||
Clear All Filters
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- No Data Available Message -->
|
||||
<div class="no-data-message" *ngIf="noDataAvailable && !isLoading">
|
||||
<p>No data available for the selected filters.</p>
|
||||
<button class="btn btn-sm btn-primary" (click)="fetchChartData()">Retry</button>
|
||||
</div>
|
||||
|
||||
<!-- Loading Indicator -->
|
||||
<div class="loading-indicator" *ngIf="isLoading">
|
||||
<div class="spinner"></div>
|
||||
<p>Loading chart data...</p>
|
||||
</div>
|
||||
@@ -0,0 +1,307 @@
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.drilldown-back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.drilldown-level {
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
position: relative;
|
||||
height: calc(100% - 100px);
|
||||
min-height: 400px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.chart-canvas-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-toggle-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
|
||||
clr-icon {
|
||||
transition: color 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
color: #007cba !important;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
&:hover span {
|
||||
color: #007cba;
|
||||
}
|
||||
}
|
||||
|
||||
.filters-section {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
background-color: #f9f9f9;
|
||||
|
||||
h5 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.filters-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex: 1 1 200px;
|
||||
min-width: 150px;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-text,
|
||||
.filter-dropdown,
|
||||
.filter-date-range {
|
||||
.form-control {
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.date-range-inputs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
.form-control {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-multiselect {
|
||||
position: relative;
|
||||
|
||||
.multiselect-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.multiselect-display {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
min-height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.multiselect-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1000;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.multiselect-option {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
transition: .4s;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .toggle-label {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:checked + .toggle-label .toggle-slider {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
}
|
||||
|
||||
.clear-filters {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-data-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 300px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-indicator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 300px;
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.filters-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.chart-canvas-container {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UnifiedChartComponent } from './unified-chart.component';
|
||||
|
||||
describe('UnifiedChartComponent', () => {
|
||||
let component: UnifiedChartComponent;
|
||||
let fixture: ComponentFixture<UnifiedChartComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [UnifiedChartComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(UnifiedChartComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -254,7 +254,7 @@ export class ReportbuildqueryComponent implements OnInit {
|
||||
name;
|
||||
databaseName;
|
||||
databasename(val) {
|
||||
console.log(val);
|
||||
console.log('connection ', val);
|
||||
this.databaseName = val.name;
|
||||
this.selecteddatabase = val.conn_string;
|
||||
console.log(this.selecteddatabase);
|
||||
|
||||
@@ -23,6 +23,18 @@
|
||||
<label for="workflow_name">{{'ACTIVE'| translate}}</label>
|
||||
<input type="checkbox" formControlName="active" clrToggle value="billable" name="billable" />
|
||||
</div>
|
||||
|
||||
<!-- SureConnect Dropdown -->
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label for="sureConnectId">SureConnect Connection</label>
|
||||
<select formControlName="sureConnectId" class="clr-select">
|
||||
<option value="">-- Select SureConnect --</option>
|
||||
<option *ngFor="let conn of sureconnectData" [value]="conn.id">
|
||||
{{conn.connection_name || conn.id}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label for="url">Get URL</label>
|
||||
@@ -76,4 +88,4 @@
|
||||
<button type="submit" class="btn btn-primary" [disabled]="!entryForm.valid" (click)="onSubmit()">{{'SUBMIT' | translate}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,6 +4,8 @@ import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { ReportBuilderService } from 'src/app/services/api/report-builder.service';
|
||||
import { SureconnectService } from '../../dashboardnew/sureconnect/sureconnect.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-report-build2add',
|
||||
templateUrl: './report-build2add.component.html',
|
||||
@@ -11,8 +13,12 @@ import { ReportBuilderService } from 'src/app/services/api/report-builder.servic
|
||||
})
|
||||
export class ReportBuild2addComponent implements OnInit {
|
||||
public entryForm: FormGroup;
|
||||
// Add sureconnect data property
|
||||
sureconnectData: any[] = [];
|
||||
|
||||
constructor(private _fb: FormBuilder, private router: Router,private toastr: ToastrService,
|
||||
private route: ActivatedRoute,private reportBuilderService: ReportBuilderService) { }
|
||||
private route: ActivatedRoute,private reportBuilderService: ReportBuilderService,
|
||||
private sureconnectService: SureconnectService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.entryForm = this._fb.group({
|
||||
@@ -20,9 +26,23 @@ export class ReportBuild2addComponent implements OnInit {
|
||||
description:[null],
|
||||
active:[null],
|
||||
isSql:[false],
|
||||
// Add sureConnectId field to the form
|
||||
sureConnectId: [null],
|
||||
Rpt_builder2_lines: this._fb.array([this.initLinesFormReport()]),
|
||||
});
|
||||
|
||||
|
||||
// Load sureconnect data
|
||||
this.loadSureconnectData();
|
||||
}
|
||||
|
||||
// Add method to load sureconnect data
|
||||
loadSureconnectData() {
|
||||
this.sureconnectService.getAll().subscribe((data: any[]) => {
|
||||
this.sureconnectData = data;
|
||||
console.log('Sureconnect data loaded:', this.sureconnectData);
|
||||
}, (error) => {
|
||||
console.log('Error loading sureconnect data:', error);
|
||||
});
|
||||
}
|
||||
|
||||
initLinesFormReport() {
|
||||
@@ -68,4 +88,4 @@ export class ReportBuild2addComponent implements OnInit {
|
||||
this.router.navigate(["../all"], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,70 +6,87 @@
|
||||
|
||||
<div class="dg-wrapper">
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-4">
|
||||
<h3><b>{{'REPORT_BUILDER_2' | translate}}</b></h3>
|
||||
</div>
|
||||
<div class="clr-col-8" style="text-align: right;">
|
||||
<button id="add" class="btn btn-primary" (click)="gotorunner()">
|
||||
<clr-icon shape="grid-view"></clr-icon>{{'REPORT_RUNNER' | translate}}
|
||||
</button>
|
||||
|
||||
<button id="add" class="btn btn-primary" (click)="goToAdd()">
|
||||
<clr-icon shape="plus"></clr-icon>{{'ADD' | translate}}
|
||||
</button>
|
||||
<div class="clr-col-4">
|
||||
<h3><b>{{'REPORT_BUILDER_2' | translate}}</b></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clr-col-8" style="text-align: right;">
|
||||
<button id="add" class="btn btn-primary" (click)="gotorunner()">
|
||||
<clr-icon shape="grid-view"></clr-icon>{{'REPORT_RUNNER' | translate}}
|
||||
</button>
|
||||
|
||||
<clr-datagrid [clrDgLoading]="loading">
|
||||
<button id="add" class="btn btn-primary" (click)="goToAdd()">
|
||||
<clr-icon shape="plus"></clr-icon>{{'ADD' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<clr-datagrid [clrDgLoading]="loading">
|
||||
<clr-dg-placeholder><ng-template #loadingSpinner><clr-spinner>{{'LOADING' | translate}}</clr-spinner></ng-template>
|
||||
<div *ngIf="error;else loadingSpinner">{{error}}</div></clr-dg-placeholder>
|
||||
|
||||
|
||||
<div *ngIf="error;else loadingSpinner">{{error}}</div>
|
||||
</clr-dg-placeholder>
|
||||
|
||||
|
||||
<clr-dg-column [clrDgField]="''"><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
{{'GO_TO' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'name'"><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
{{'REPORT_NAME' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'description'"><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
{{'REPORT_DESCRIPTION' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'active'"><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
{{'ACTIVE' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
<clr-dg-column><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
<clr-icon shape="bars"></clr-icon>{{'ACTION' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
|
||||
<clr-dg-row *clrDgItems="let user of gridData?.slice()?.reverse();" [clrDgItem]="user">
|
||||
<clr-dg-cell><span class="label label-light-blue" style="display: inline;margin-left: 10px; cursor: pointer;" (click)="goToLines(user)"> {{'SET_UP' | translate}}</span></clr-dg-cell>
|
||||
<clr-dg-cell id="word">{{user.reportName}}</clr-dg-cell>
|
||||
<clr-dg-cell id="word">{{user.description}}</clr-dg-cell>
|
||||
<clr-dg-cell id="word">{{user.active}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<a href="javascript:void(0)" style="padding-right: 10px;" role="tooltip" aria-haspopup="true" class="tooltip tooltip-sm tooltip-top-left">
|
||||
<span style="cursor: pointer;"><clr-icon shape="trash" (click)="onDelete(user)" class="red is-error" style="color: red;"></clr-icon></span>
|
||||
<span class="tooltip-content"> {{'DELETE' | translate}}</span>
|
||||
</a>
|
||||
<clr-signpost>
|
||||
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span>
|
||||
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
||||
<h5 style="margin-top: 0">{{'WHO_COLUMN' | translate}}</h5>
|
||||
<div>{{'ACCOUNT_ID' | translate}}: <code class="clr-code">{{user.accountId}}</code></div>
|
||||
<div>{{'CREATED_AT' | translate}}: <code class="clr-code">{{user.createdAt | date}}</code></div>
|
||||
<div>{{'CREATED_BY' | translate}}: <code class="clr-code">{{user.createdBy}}</code></div>
|
||||
<div>{{'UPDATED_AT' | translate}}: <code class="clr-code">{{user.updatedAt | date}}</code></div>
|
||||
<div>{{'UPDATED_BY' | translate}}: <code class="clr-code">{{user.updatedBy}}</code></div>
|
||||
</clr-signpost-content>
|
||||
</clr-signpost>
|
||||
|
||||
<!-- <span style="cursor: pointer;"><clr-icon shape="form" (click)="goToLines(user.id)" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span> -->
|
||||
</clr-dg-cell>
|
||||
<clr-dg-action-overflow>
|
||||
<!-- <button class="action-item" (click)="goToEdit(user.id)">Edit <clr-icon shape="edit" class="is-error"></clr-icon></button> -->
|
||||
</clr-dg-action-overflow>
|
||||
|
||||
<!-- <clr-dg-row-detail *clrIfExpanded >
|
||||
{{'GO_TO' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
|
||||
<clr-dg-column [clrDgField]="'name'"><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
{{'REPORT_NAME' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'description'"><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
{{'REPORT_DESCRIPTION' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
|
||||
<clr-dg-column [clrDgField]="''"><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
Sureconnect
|
||||
</ng-container></clr-dg-column>
|
||||
|
||||
<clr-dg-column [clrDgField]="'active'"><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
{{'ACTIVE' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
|
||||
|
||||
|
||||
|
||||
<clr-dg-column><ng-container *clrDgHideableColumn="{hidden: false}">
|
||||
<clr-icon shape="bars"></clr-icon>{{'ACTION' | translate}}
|
||||
</ng-container></clr-dg-column>
|
||||
|
||||
<clr-dg-row *clrDgItems="let user of gridData?.slice()?.reverse();" [clrDgItem]="user">
|
||||
<clr-dg-cell><span class="label label-light-blue" style="display: inline;margin-left: 10px; cursor: pointer;"
|
||||
(click)="goToLines(user)"> {{'SET_UP' | translate}}</span></clr-dg-cell>
|
||||
<clr-dg-cell id="word">{{user.reportName}}</clr-dg-cell>
|
||||
<clr-dg-cell id="word">{{user.description}}</clr-dg-cell>
|
||||
|
||||
<clr-dg-cell id="word">{{user.sureconnect_name}}</clr-dg-cell>
|
||||
<clr-dg-cell id="word">{{user.active}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<a href="javascript:void(0)" style="padding-right: 10px;" role="tooltip" aria-haspopup="true"
|
||||
class="tooltip tooltip-sm tooltip-top-left">
|
||||
<span style="cursor: pointer;"><clr-icon shape="trash" (click)="onDelete(user)" class="red is-error"
|
||||
style="color: red;"></clr-icon></span>
|
||||
<span class="tooltip-content"> {{'DELETE' | translate}}</span>
|
||||
</a>
|
||||
<clr-signpost>
|
||||
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success"
|
||||
style="color: rgb(0, 130, 236);"></clr-icon></span>
|
||||
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
||||
<h5 style="margin-top: 0">{{'WHO_COLUMN' | translate}}</h5>
|
||||
<div>{{'ACCOUNT_ID' | translate}}: <code class="clr-code">{{user.accountId}}</code></div>
|
||||
<div>{{'CREATED_AT' | translate}}: <code class="clr-code">{{user.createdAt | date}}</code></div>
|
||||
<div>{{'CREATED_BY' | translate}}: <code class="clr-code">{{user.createdBy}}</code></div>
|
||||
<div>{{'UPDATED_AT' | translate}}: <code class="clr-code">{{user.updatedAt | date}}</code></div>
|
||||
<div>{{'UPDATED_BY' | translate}}: <code class="clr-code">{{user.updatedBy}}</code></div>
|
||||
</clr-signpost-content>
|
||||
</clr-signpost>
|
||||
|
||||
<!-- <span style="cursor: pointer;"><clr-icon shape="form" (click)="goToLines(user.id)" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span> -->
|
||||
</clr-dg-cell>
|
||||
<clr-dg-action-overflow>
|
||||
<!-- <button class="action-item" (click)="goToEdit(user.id)">Edit <clr-icon shape="edit" class="is-error"></clr-icon></button> -->
|
||||
</clr-dg-action-overflow>
|
||||
|
||||
<!-- <clr-dg-row-detail *clrIfExpanded >
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td class="td-title">actionName: </td>
|
||||
@@ -77,26 +94,26 @@
|
||||
</tr>
|
||||
</table>
|
||||
</clr-dg-row-detail> -->
|
||||
</clr-dg-row>
|
||||
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">{{'USERS_PER_PAGE' | translate}}</clr-dg-page-size>
|
||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||
of {{pagination.totalItems}} users
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
|
||||
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||
|
||||
<div class="modal-body" *ngIf="rowSelected.id">
|
||||
<h1 class="delete">{{'DELETE_CONFIRMATION' | translate}}</h1>
|
||||
<h2 class="heading">{{rowSelected.id}}</h2>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="modaldelete = false">{{'CANCEL' | translate}}</button>
|
||||
<button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary" >{{'DELETE' | translate}}</button>
|
||||
</div>
|
||||
</clr-dg-row>
|
||||
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">{{'USERS_PER_PAGE' | translate}}</clr-dg-page-size>
|
||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||
of {{pagination.totalItems}} users
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
|
||||
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||
|
||||
<div class="modal-body" *ngIf="rowSelected.id">
|
||||
<h1 class="delete">{{'DELETE_CONFIRMATION' | translate}}</h1>
|
||||
<h2 class="heading">{{rowSelected.id}}</h2>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="modaldelete = false">{{'CANCEL' | translate}}</button>
|
||||
<button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary">{{'DELETE' | translate}}</button>
|
||||
</div>
|
||||
</clr-modal>
|
||||
</div>
|
||||
</clr-modal>
|
||||
@@ -1,13 +1,13 @@
|
||||
<div class="container">
|
||||
<h3 style="font-weight: 300;display: inline;"><b>REPORT SET UP - Project Details Report ({{ReportData.id}})</b></h3>
|
||||
<span class="label label-light-blue" style="display: inline;margin-left: 10px;">Edit Mode</span>
|
||||
<hr />
|
||||
<form [formGroup]="entryForm">
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-lg-12 clr-col-md-12 clr-col-sm-12">
|
||||
<div>
|
||||
<div class="clr-row">
|
||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
||||
<h3 style="font-weight: 300;display: inline;"><b>REPORT SET UP - Project Details Report ({{ReportData.id}})</b></h3>
|
||||
<span class="label label-light-blue" style="display: inline;margin-left: 10px;">Edit Mode</span>
|
||||
<hr />
|
||||
<form [formGroup]="entryForm">
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-lg-12 clr-col-md-12 clr-col-sm-12">
|
||||
<div>
|
||||
<div class="clr-row">
|
||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
||||
<div class="clr-col-sm-12">
|
||||
<label for="projectName">Connection Name</label>
|
||||
<select formControlName="conn_name" name="conn_name" [(ngModel)]="nodeEditProperties.conn_name" id="service" class="clr-dropdown">
|
||||
@@ -24,55 +24,70 @@
|
||||
</select>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label for="url">Get URL</label>
|
||||
<div> <input type="text" class="clr-input" formControlName="url" name="url" [(ngModel)]="nodeEditProperties.url" placeholder="Enter Url" style="width: 76%"> <span><button class="btn btn-icon btn-primary" (click)="getkeys()">
|
||||
<clr-icon shape="view-list"></clr-icon>
|
||||
</button></span></div>
|
||||
</div>
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label for="workflow_name">Include Date filter</label>
|
||||
<input type="checkbox" formControlName="date_param_req" name="date_param_req" [(ngModel)]="nodeEditProperties.date_param_req" clrToggle />
|
||||
</div>
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label>Standard Parameters</label>
|
||||
<clr-combobox-container style="margin-top: 0; padding-top: 0;">
|
||||
<!-- <label style="padding-bottom: 5px; padding-top:0px; font-weight: lighter;" class="p1">Select Left Side Filter</label> -->
|
||||
<clr-combobox formControlName="std_param_html" name="std_param_html" [(ngModel)]="nodeEditProperties.std_param_html" clrMulti="true"
|
||||
required>
|
||||
<ng-container *clrOptionSelected="let selected">
|
||||
{{selected}}
|
||||
</ng-container>
|
||||
<clr-options>
|
||||
<clr-option *clrOptionItems="let state of keysfromurl" [clrValue]="state">
|
||||
{{state}}
|
||||
</clr-option>
|
||||
</clr-options>
|
||||
</clr-combobox>
|
||||
</clr-combobox-container>
|
||||
|
||||
</div>
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label>List</label>
|
||||
<select>
|
||||
<option value="">Choose from list</option>
|
||||
<option></option>
|
||||
</select>
|
||||
|
||||
</div>
|
||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label for="url">Get URL</label>
|
||||
<div> <input type="text" class="clr-input" formControlName="url" name="url"
|
||||
[(ngModel)]="nodeEditProperties.url" placeholder="Enter Url" style="width: 76%"> <span><button
|
||||
class="btn btn-icon btn-primary" (click)="getkeys()">
|
||||
<clr-icon shape="view-list"></clr-icon>
|
||||
</button></span></div>
|
||||
</div>
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label for="workflow_name">Include Date filter</label>
|
||||
<input type="checkbox" formControlName="date_param_req" name="date_param_req"
|
||||
[(ngModel)]="nodeEditProperties.date_param_req" clrToggle />
|
||||
</div>
|
||||
|
||||
<!-- SureConnect Dropdown -->
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label for="sureConnectId">SureConnect Connection</label>
|
||||
<select formControlName="sureConnectId" class="clr-select">
|
||||
<option value="">-- Select SureConnect --</option>
|
||||
<option *ngFor="let conn of sureconnectData" [value]="conn.id">
|
||||
{{conn.name || conn.id}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label>Standard Parameters</label>
|
||||
<clr-combobox-container style="margin-top: 0; padding-top: 0;">
|
||||
<!-- <label style="padding-bottom: 5px; padding-top:0px; font-weight: lighter;" class="p1">Select Left Side Filter</label> -->
|
||||
<clr-combobox formControlName="std_param_html" name="std_param_html"
|
||||
[(ngModel)]="nodeEditProperties.std_param_html" clrMulti="true" required>
|
||||
<ng-container *clrOptionSelected="let selected">
|
||||
{{selected}}
|
||||
</ng-container>
|
||||
<clr-options>
|
||||
<clr-option *clrOptionItems="let state of keysfromurl" [clrValue]="state">
|
||||
{{state}}
|
||||
</clr-option>
|
||||
</clr-options>
|
||||
</clr-combobox>
|
||||
</clr-combobox-container>
|
||||
|
||||
</div>
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label>List</label>
|
||||
<select>
|
||||
<option value="">Choose from list</option>
|
||||
<option></option>
|
||||
</select>
|
||||
|
||||
</div>
|
||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
||||
<div class="clr-col-sm-12">
|
||||
<label for="description" class="d1">Adhoc Parameter String (html)</label>
|
||||
<textarea id="t1" cols="10" rows="3" formControlName="adhoc_param_html" placeholder="Enter Description" name="adhoc_param_html" [(ngModel)]="nodeEditProperties.adhoc_param_html" style="width:100%">
|
||||
</textarea>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<!-- <div class="clr-row" style="padding-left:10px;">
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<!-- <div class="clr-row" style="padding-left:10px;">
|
||||
<div class="clr-col-md-4 clr-col-sm-12">
|
||||
<label for="description" class="d1">Column String (html)</label>
|
||||
<textarea id="t1" cols="10" rows="3" formControlName="column_str" placeholder="Enter Description" name="column_str" [(ngModel)]="nodeEditProperties.column_str" style="width:100%">
|
||||
@@ -85,11 +100,10 @@
|
||||
</textarea>
|
||||
</div>
|
||||
</div> -->
|
||||
<br>
|
||||
<div class="center">
|
||||
<button type="button" class="btn btn-outline" (click)="back()">BACK</button>
|
||||
<button type="submit" form-control class="btn btn-primary" (click)="onSubmit()">UPDATE</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<div class="center">
|
||||
<button type="button" class="btn btn-outline" (click)="back()">BACK</button>
|
||||
<button type="submit" form-control class="btn btn-primary" (click)="onSubmit()">UPDATE</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -3,6 +3,7 @@ import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { ReportBuilderService } from 'src/app/services/api/report-builder.service';
|
||||
import { SureconnectService } from '../../dashboardnew/sureconnect/sureconnect.service';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -23,11 +24,17 @@ export class ReportBuild2editComponent implements OnInit {
|
||||
date_param_req: '',
|
||||
// folderName:'',
|
||||
url: '',
|
||||
// Add sureConnectId property
|
||||
sureConnectId: null,
|
||||
|
||||
};
|
||||
// Add sureconnect data property
|
||||
sureconnectData: any[] = [];
|
||||
|
||||
constructor(private router: Router,
|
||||
private route: ActivatedRoute, private reportBuilderService: ReportBuilderService,
|
||||
private toastr: ToastrService, private _fb: FormBuilder) { }
|
||||
private toastr: ToastrService, private _fb: FormBuilder,
|
||||
private sureconnectService: SureconnectService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.id = this.route.snapshot.params["id"];
|
||||
@@ -41,11 +48,26 @@ export class ReportBuild2editComponent implements OnInit {
|
||||
date_param_req: [null],
|
||||
// folderName:[null],
|
||||
url: [null],
|
||||
// Add sureConnectId to form
|
||||
sureConnectId: [null],
|
||||
});
|
||||
|
||||
// Load sureconnect data first, then load report data
|
||||
this.loadSureconnectData();
|
||||
this.getById(this.id);
|
||||
this.listoddatabase();
|
||||
}
|
||||
|
||||
// Add method to load sureconnect data
|
||||
loadSureconnectData() {
|
||||
this.sureconnectService.getAll().subscribe((data: any[]) => {
|
||||
this.sureconnectData = data;
|
||||
console.log('Sureconnect data loaded:', this.sureconnectData);
|
||||
}, (error) => {
|
||||
console.log('Error loading sureconnect data:', error);
|
||||
});
|
||||
}
|
||||
|
||||
databaselist;
|
||||
listoddatabase() {
|
||||
this.reportBuilderService.getdatabse().subscribe((data) => {
|
||||
@@ -81,6 +103,11 @@ export class ReportBuild2editComponent implements OnInit {
|
||||
// this.nodeEditProperties.conn_name = this.builderLineData.conn_name;
|
||||
this.nodeEditProperties.date_param_req = this.builderLineData[0].date_param_req;
|
||||
this.nodeEditProperties.url = this.builderLineData[0].url;
|
||||
// Set sureConnectId if it exists in the data
|
||||
this.nodeEditProperties.sureConnectId = this.builderLineData[0].sureConnectId || null;
|
||||
|
||||
// Update form with loaded data
|
||||
this.entryForm.patchValue(this.nodeEditProperties);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
@@ -115,6 +142,8 @@ export class ReportBuild2editComponent implements OnInit {
|
||||
adhoc_param_html: this.nodeEditProperties.adhoc_param_html,
|
||||
date_param_req: this.nodeEditProperties.date_param_req,
|
||||
url: this.nodeEditProperties.url,
|
||||
// Add sureConnectId to the data
|
||||
sureConnectId: this.nodeEditProperties.sureConnectId,
|
||||
};
|
||||
|
||||
this.builderLineData[0].std_param_html = this.nodeEditProperties.std_param_html;
|
||||
@@ -123,6 +152,9 @@ export class ReportBuild2editComponent implements OnInit {
|
||||
// this.builderLineData.conn_name = this.nodeEditProperties.conn_name ;
|
||||
this.builderLineData[0].date_param_req = this.nodeEditProperties.date_param_req;
|
||||
this.builderLineData[0].url = this.nodeEditProperties.url;
|
||||
// Add sureConnectId to the data
|
||||
this.builderLineData[0].sureConnectId = this.nodeEditProperties.sureConnectId;
|
||||
|
||||
console.log(this.builderLineData);
|
||||
// this.builderLineData.splice(1);
|
||||
console.log(this.builderLineData);
|
||||
@@ -148,6 +180,8 @@ export class ReportBuild2editComponent implements OnInit {
|
||||
|
||||
onSubmit() {
|
||||
this.updated = true;
|
||||
// Update nodeEditProperties with form values including sureConnectId
|
||||
Object.assign(this.nodeEditProperties, this.entryForm.value);
|
||||
this.update();
|
||||
}
|
||||
|
||||
@@ -155,4 +189,4 @@ export class ReportBuild2editComponent implements OnInit {
|
||||
this.router.navigate(["../../all"], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -14,18 +14,16 @@ import { ExcelService } from 'src/app/services/excel.service';
|
||||
})
|
||||
export class Reportrunneredit2Component implements OnInit {
|
||||
dynamicForm: FormGroup;
|
||||
modalselect: boolean = false;
|
||||
serverData = [{
|
||||
"andor": "AND",
|
||||
"fields_name": "",
|
||||
"condition": "=",
|
||||
"value": ""
|
||||
}];
|
||||
andor = ['AND', 'OR', 'NOT'];
|
||||
modalselect:boolean=false;
|
||||
serverData = [{"andor": "AND",
|
||||
"fields_name": "",
|
||||
"condition": "=",
|
||||
"value": ""}];
|
||||
andor = ['AND', 'OR','NOT'];
|
||||
fieldname = ['name1', 'name2'];
|
||||
condition = ['=', '!=', '<', '>', '<=', '>=', 'LIKE', 'BETWEEN', 'IN'];
|
||||
condition = ['=','!=','<','>','<=','>=','LIKE','BETWEEN','IN'];
|
||||
header_id;
|
||||
public array = [
|
||||
public array=[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Jack",
|
||||
@@ -78,12 +76,13 @@ export class Reportrunneredit2Component implements OnInit {
|
||||
selectedfrom;
|
||||
selectedto;
|
||||
constructor(private router: Router,
|
||||
private route: ActivatedRoute, private _fb: FormBuilder,
|
||||
private reportBuilderService: ReportBuilderService, private toastr: ToastrService, private sanitizer: DomSanitizer, private excel: ExcelService) {
|
||||
this.dynamicForm = this._fb.group({
|
||||
});
|
||||
}
|
||||
todayDate;
|
||||
private route: ActivatedRoute,private _fb: FormBuilder,
|
||||
private reportBuilderService: ReportBuilderService,private toastr:ToastrService,private sanitizer: DomSanitizer,private excel: ExcelService)
|
||||
{
|
||||
this.dynamicForm = this._fb.group({
|
||||
});
|
||||
}
|
||||
todayDate;
|
||||
ngOnInit(): void {
|
||||
this.todayDate = new Date().toISOString().slice(0, 10);
|
||||
this.header_id = this.route.snapshot.params["id"];
|
||||
@@ -95,18 +94,21 @@ export class Reportrunneredit2Component implements OnInit {
|
||||
setTimeout(() => {
|
||||
this.runtheQuery();
|
||||
}, 2000);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
reportName;
|
||||
builderLine;
|
||||
builderLineData;
|
||||
lineId;
|
||||
adhocList: any[];
|
||||
adhocList:any[];
|
||||
SQLQuery;
|
||||
getUrl;
|
||||
// Add sureConnectId property
|
||||
sureConnectId: number | null = null;
|
||||
stdParamfields;
|
||||
DateParam;
|
||||
|
||||
getById(id: number) {
|
||||
this.reportBuilderService.getrbDetailsById(id).subscribe(
|
||||
(data) => {
|
||||
@@ -114,31 +116,44 @@ export class Reportrunneredit2Component implements OnInit {
|
||||
this.reportName = data.reportName;
|
||||
this.builderLine = data.rpt_builder2_lines;
|
||||
this.lineId = this.builderLine[0].id
|
||||
this.builderLineData = JSON.parse(this.builderLine[0].model);
|
||||
console.log(this.lineId, this.builderLineData);
|
||||
this.builderLineData = JSON.parse(this.builderLine[0].model) ;
|
||||
console.log(this.lineId,this.builderLineData);
|
||||
this.builderLineData = this.builderLineData[0];
|
||||
this.adhocList = this.builderLineData.adhoc_param_html;
|
||||
// this.adhocList = JSON.parse(adhocList);
|
||||
this.DateParam = this.builderLineData.date_param_req;
|
||||
this.getUrl = this.builderLineData.url;
|
||||
console.log(this.adhocList, this.DateParam, this.getUrl)
|
||||
// Get sureConnectId if it exists
|
||||
this.sureConnectId = this.builderLineData.sureConnectId || null;
|
||||
console.log(this.adhocList,this.DateParam,this.getUrl)
|
||||
this.getStdParam(this.header_id);
|
||||
this.featchData();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
featchData() {
|
||||
this.reportBuilderService.getAllDetailsByurl(this.getUrl).subscribe(data => {
|
||||
/**
|
||||
* Fetch data using the URL and SureConnect connection if available
|
||||
*/
|
||||
featchData(){
|
||||
// If sureConnectId is available, we might want to modify the request
|
||||
// For now, we'll use the existing implementation but with better error handling
|
||||
this.reportBuilderService.getAllDetailsByurl(this.getUrl).subscribe(data =>{
|
||||
console.log(data);
|
||||
if (data.body) {
|
||||
if(data.body){
|
||||
// Check if the response is XML (starts with <) or JSON
|
||||
if (typeof data.body === 'string' && data.body.trim().startsWith('<')) {
|
||||
// Handle XML response - for now we'll set rows to empty array
|
||||
// In a real implementation, you would parse the XML properly
|
||||
console.warn('Received XML response instead of JSON');
|
||||
this.rows = [];
|
||||
this.filterRows = [];
|
||||
// Handle XML response
|
||||
console.log('Received XML response, parsing to JSON');
|
||||
try {
|
||||
this.rows = this.parseXMLToJSON(data.body);
|
||||
this.filterRows = [...this.rows]; // Create a copy
|
||||
console.log('Parsed XML data:', this.rows);
|
||||
} catch (error) {
|
||||
console.error('Error parsing XML:', error);
|
||||
this.rows = [];
|
||||
this.filterRows = [];
|
||||
}
|
||||
} else {
|
||||
// Handle JSON response
|
||||
try {
|
||||
@@ -858,4 +873,82 @@ export class Reportrunneredit2Component implements OnInit {
|
||||
value.dayOfMonth !== undefined)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple XML parser to convert XML to JSON array
|
||||
* Handles the specific format: <User><id>101</id><name>John Doe</name><email>john.doe@example.com</email></User>
|
||||
* @param xmlString The XML string to parse
|
||||
* @returns Array of objects representing the XML data
|
||||
*/
|
||||
private parseXMLToJSON(xmlString: string): any[] {
|
||||
// Remove any XML declaration
|
||||
xmlString = xmlString.replace(/<\?xml[^>]*\?>/g, '');
|
||||
|
||||
// Extract the root element name (e.g., "User" from <User>...</User>)
|
||||
const rootMatch = xmlString.match(/<(\w+)(?:\s[^>]*)?>/);
|
||||
if (!rootMatch) {
|
||||
console.warn('Could not identify root element in XML');
|
||||
return [];
|
||||
}
|
||||
|
||||
const rootElement = rootMatch[1];
|
||||
const results: any[] = [];
|
||||
|
||||
// Create a regex to match all instances of the root element
|
||||
const regex = new RegExp(`<${rootElement}(?:\\s[^>]*)?>([\\s\\S]*?)<\\/${rootElement}>`, 'g');
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(xmlString)) !== null) {
|
||||
const elementContent = match[1];
|
||||
const item: any = {};
|
||||
|
||||
// Extract all tags and their values
|
||||
const tagRegex = /<(\w+)>(.*?)<\/\1>/g;
|
||||
let tagMatch;
|
||||
|
||||
while ((tagMatch = tagRegex.exec(elementContent)) !== null) {
|
||||
const tagName = tagMatch[1];
|
||||
const tagValue = tagMatch[2];
|
||||
// Try to convert to appropriate type
|
||||
item[tagName] = this.convertValueType(tagValue);
|
||||
}
|
||||
|
||||
results.push(item);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string values to appropriate types (number, boolean, etc.)
|
||||
* @param value The string value to convert
|
||||
* @returns The value converted to the appropriate type
|
||||
*/
|
||||
private convertValueType(value: string): any {
|
||||
// Check for empty value
|
||||
if (value === '') {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Check for boolean values
|
||||
if (value.toLowerCase() === 'true') {
|
||||
return true;
|
||||
}
|
||||
if (value.toLowerCase() === 'false') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for numeric values
|
||||
if (!isNaN(Number(value)) && !isNaN(parseFloat(value))) {
|
||||
// Check if it's an integer or float
|
||||
if (Number.isInteger(parseFloat(value))) {
|
||||
return parseInt(value, 10);
|
||||
} else {
|
||||
return parseFloat(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Return as string if no other type matches
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -69,4 +69,13 @@ export class AlertsService {
|
||||
}
|
||||
return this.apiRequest.get(apiUrl);
|
||||
}
|
||||
|
||||
// Get values for a specific key from API
|
||||
public getValuesFromUrl(url: string, sureId: number | undefined, key: string): Observable<any> {
|
||||
let apiUrl = `chart/getValue?apiUrl=${url}&key=${key}`;
|
||||
if (sureId) {
|
||||
apiUrl += `&sureId=${sureId}`;
|
||||
}
|
||||
return this.apiRequest.get(apiUrl);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user