5 Commits

Author SHA1 Message Date
Gaurav Kumar
3e70f85644 dashboard 2025-10-31 17:19:18 +05:30
Gaurav Kumar
482805b5cf Update editnewdash.component.ts 2025-10-31 17:17:01 +05:30
Gaurav Kumar
ffda17e6b1 unified 2025-10-31 16:35:15 +05:30
Gaurav Kumar
7396843bc6 Update report-build2all.component.html 2025-10-31 16:03:37 +05:30
Gaurav Kumar
4f75ecb3e0 builder 2025-10-31 15:51:28 +05:30
16 changed files with 2479 additions and 187 deletions

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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'
}
]
@@ -137,7 +143,8 @@ export class EditnewdashComponent implements OnInit {
{ name: "Financial Chart", componentInstance: FinancialChartComponent },
{ 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: '',
@@ -610,6 +618,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 +686,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 +958,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 +1046,48 @@ export class EditnewdashComponent implements OnInit {
return commonFilterInputs;
}
// For UnifiedChartComponent, pass chart properties with chartType
if (item.name === 'Unified Chart') {
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 = {

View File

@@ -0,0 +1 @@
export * from './unified-chart.component';

View File

@@ -0,0 +1,365 @@
<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>
<!-- Render different chart types based on chartType input -->
<div class="chart-wrapper">
<!-- Bar Chart -->
<div *ngIf="chartType === 'bar'">
<canvas baseChart
[data]="chartData"
[labels]="chartLabels"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="'bar'"
(chartClick)="chartClicked($event)"
(chartHover)="chartHovered($event)">
</canvas>
</div>
<!-- Line Chart -->
<div *ngIf="chartType === 'line'">
<canvas baseChart
[data]="chartData"
[labels]="chartLabels"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="'line'"
(chartClick)="chartClicked($event)"
(chartHover)="chartHovered($event)">
</canvas>
</div>
<!-- Pie Chart -->
<div *ngIf="chartType === 'pie'">
<canvas baseChart
[data]="chartData"
[labels]="chartLabels"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="'pie'"
(chartClick)="chartClicked($event)"
(chartHover)="chartHovered($event)">
</canvas>
</div>
<!-- Doughnut Chart -->
<div *ngIf="chartType === 'doughnut'">
<canvas baseChart
[data]="chartData"
[labels]="chartLabels"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="'doughnut'"
(chartClick)="chartClicked($event)"
(chartHover)="chartHovered($event)">
</canvas>
</div>
<!-- Bubble Chart -->
<div *ngIf="chartType === 'bubble'">
<canvas baseChart
[datasets]="bubbleChartData"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="'bubble'"
(chartClick)="chartClicked($event)"
(chartHover)="chartHovered($event)">
</canvas>
</div>
<!-- Radar Chart -->
<div *ngIf="chartType === 'radar'">
<canvas baseChart
[data]="chartData"
[labels]="chartLabels"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="'radar'"
(chartClick)="chartClicked($event)"
(chartHover)="chartHovered($event)">
</canvas>
</div>
<!-- Polar Area Chart -->
<div *ngIf="chartType === 'polar'">
<canvas baseChart
[data]="chartData"
[labels]="chartLabels"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="'polarArea'"
(chartClick)="chartClicked($event)"
(chartHover)="chartHovered($event)">
</canvas>
</div>
<!-- Scatter Chart -->
<div *ngIf="chartType === 'scatter'">
<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)">
<canvas baseChart
[data]="chartData"
[labels]="chartLabels"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="'bar'"
(chartClick)="chartClicked($event)"
(chartHover)="chartHovered($event)">
</canvas>
</div>
</div>
<!-- Base Filters -->
<div class="filters-section" *ngIf="baseFilters && baseFilters.length > 0">
<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 === 'text'" class="filter-text">
<label>{{ filter.field }}</label>
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)"
class="form-control" placeholder="Enter {{ filter.field }}">
</div>
<!-- Dropdown Filter -->
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
<label>{{ filter.field }}</label>
<select [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)" class="form-control">
<option value="">Select {{ filter.field }}</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 }}</label>
<div class="multiselect-container">
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'base')">
<span *ngIf="getSelectedOptionsCount(filter) === 0">Select {{ filter.field }}</span>
<span *ngIf="getSelectedOptionsCount(filter) > 0">
{{ getSelectedOptionsCount(filter) }} 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 }}</label>
<div class="date-range-inputs">
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeChange(filter, filter.value)"
class="form-control" placeholder="Start Date">
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeChange(filter, filter.value)"
class="form-control" placeholder="End Date">
</div>
</div>
<!-- Toggle Filter -->
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
<label>{{ filter.field }}</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">
<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 === 'text'" class="filter-text">
<label>{{ filter.field }}</label>
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)"
class="form-control" placeholder="Enter {{ filter.field }}">
</div>
<!-- Dropdown Filter -->
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
<label>{{ filter.field }}</label>
<select [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)" class="form-control">
<option value="">Select {{ filter.field }}</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 }}</label>
<div class="multiselect-container">
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'drilldown')">
<span *ngIf="getSelectedOptionsCount(filter) === 0">Select {{ filter.field }}</span>
<span *ngIf="getSelectedOptionsCount(filter) > 0">
{{ getSelectedOptionsCount(filter) }} 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 }}</label>
<div class="date-range-inputs">
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeChange(filter, filter.value)"
class="form-control" placeholder="Start Date">
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeChange(filter, filter.value)"
class="form-control" placeholder="End Date">
</div>
</div>
<!-- Toggle Filter -->
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
<label>{{ filter.field }}</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()">
<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 === 'text'" class="filter-text">
<label>{{ filter.field }}</label>
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)"
class="form-control" placeholder="Enter {{ filter.field }}">
</div>
<!-- Dropdown Filter -->
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
<label>{{ filter.field }}</label>
<select [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)" class="form-control">
<option value="">Select {{ filter.field }}</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 }}</label>
<div class="multiselect-container">
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'layer')">
<span *ngIf="getSelectedOptionsCount(filter) === 0">Select {{ filter.field }}</span>
<span *ngIf="getSelectedOptionsCount(filter) > 0">
{{ getSelectedOptionsCount(filter) }} 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 }}</label>
<div class="date-range-inputs">
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeChange(filter, filter.value)"
class="form-control" placeholder="Start Date">
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeChange(filter, filter.value)"
class="form-control" placeholder="End Date">
</div>
</div>
<!-- Toggle Filter -->
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
<label>{{ filter.field }}</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()">
<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>

View File

@@ -0,0 +1,262 @@
.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: 300px;
}
.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); }
}
// Responsive adjustments
@media (max-width: 768px) {
.filters-container {
flex-direction: column;
}
.filter-item {
min-width: 100%;
}
.chart-wrapper {
min-height: 250px;
}
}

View File

@@ -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();
});
});

View File

@@ -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);

View File

@@ -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>

View File

@@ -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() {

View File

@@ -18,41 +18,58 @@
<clr-icon shape="plus"></clr-icon>{{'ADD' | translate}}
</button>
</div>
</div>
</div>
<clr-datagrid [clrDgLoading]="loading">
<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]="''"><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><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>
<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>
<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>
@@ -87,16 +104,16 @@
</clr-dg-pagination>
</clr-dg-footer>
</clr-datagrid>
</div>
</div>
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
<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>
<button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary">{{'DELETE' | translate}}</button>
</div>
</div>
</clr-modal>
</clr-modal>

View File

@@ -26,20 +26,35 @@
</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%">&nbsp;<span><button class="btn btn-icon btn-primary" (click)="getkeys()">
<div> <input type="text" class="clr-input" formControlName="url" name="url"
[(ngModel)]="nodeEditProperties.url" placeholder="Enter Url" style="width: 76%">&nbsp;<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 />
<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>
<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>
@@ -91,5 +106,4 @@
<button type="submit" form-control class="btn btn-primary" (click)="onSubmit()">UPDATE</button>
</div>
</form>
</div>
</div>

View File

@@ -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();
}

View File

@@ -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",
modalselect:boolean=false;
serverData = [{"andor": "AND",
"fields_name": "",
"condition": "=",
"value": ""
}];
andor = ['AND', 'OR', 'NOT'];
"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) {
private route: ActivatedRoute,private _fb: FormBuilder,
private reportBuilderService: ReportBuilderService,private toastr:ToastrService,private sanitizer: DomSanitizer,private excel: ExcelService)
{
this.dynamicForm = this._fb.group({
});
}
todayDate;
todayDate;
ngOnInit(): void {
this.todayDate = new Date().toISOString().slice(0, 10);
this.header_id = this.route.snapshot.params["id"];
@@ -102,11 +101,14 @@ export class Reportrunneredit2Component implements OnInit {
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');
// 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;
}
}

View File

@@ -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);
}
}