diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.html new file mode 100644 index 0000000..26c2383 --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.html @@ -0,0 +1,113 @@ + +
+
+
Compact Filter Configuration
+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+ {{ availableValues.join(', ') }} +
+
+ +
+ + +
+
+
+ + +
+
+ {{ filterLabel }} + {{ filterKey }} + ({{ filterType }}) + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + +
+ + +
+
\ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.scss b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.scss new file mode 100644 index 0000000..6f044c4 --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.scss @@ -0,0 +1,189 @@ +.compact-filter { + display: inline-block; + min-width: 120px; + max-width: 200px; + margin: 5px; + padding: 5px; + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 4px; + font-size: 12px; + + .filter-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 5px; + + .filter-label, .filter-key { + font-weight: bold; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-grow: 1; + } + + .filter-type { + font-size: 10px; + color: #6c757d; + margin-left: 5px; + } + + .btn-icon { + padding: 2px; + min-width: auto; + } + } + + .filter-control { + display: flex; + align-items: center; + gap: 5px; + + &.date-range { + flex-direction: column; + } + + &.toggle { + justify-content: center; + } + } + + .compact-input, + .compact-select, + .compact-multiselect, + .compact-date { + width: 100%; + padding: 4px 6px; + font-size: 12px; + border-radius: 3px; + min-height: 24px; + } + + .compact-select, + .compact-multiselect { + height: 24px; + } + + .compact-multiselect { + height: auto; + min-height: 24px; + } + + .toggle-label { + margin: 0; + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100px; + } + + .clr-toggle { + margin: 0 5px 0 0; + } + + /* Responsive design */ + @media (max-width: 768px) { + min-width: 100px; + max-width: 150px; + + .compact-input, + .compact-select, + .compact-multiselect, + .compact-date { + font-size: 11px; + padding: 3px 5px; + } + + .toggle-label { + font-size: 11px; + max-width: 80px; + } + } + + @media (max-width: 480px) { + min-width: 80px; + max-width: 120px; + + .compact-input, + .compact-select, + .compact-multiselect, + .compact-date { + font-size: 10px; + padding: 2px 4px; + } + + .toggle-label { + font-size: 10px; + max-width: 60px; + } + } +} + +.compact-filter-config { + min-width: 200px; + max-width: 300px; + padding: 10px; + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 4px; + + .config-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + + h5 { + margin: 0; + } + + .btn-link { + padding: 2px; + min-width: auto; + } + } + + .config-form { + .clr-form-control { + margin-bottom: 8px; + + .clr-control-label { + font-size: 12px; + } + + .clr-input, + .clr-select, + .clr-textarea { + font-size: 12px; + padding: 4px 6px; + } + + .clr-textarea { + min-height: 60px; + } + + .available-values { + background: #e9ecef; + border-radius: 3px; + padding: 6px; + font-size: 11px; + margin-top: 4px; + word-break: break-all; + } + } + + .config-actions { + display: flex; + justify-content: flex-end; + gap: 5px; + margin-top: 10px; + + .btn { + font-size: 12px; + padding: 4px 8px; + } + } + } +} \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.ts new file mode 100644 index 0000000..ec53eae --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/compact-filter.component.ts @@ -0,0 +1,218 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { FilterService, Filter } from './filter.service'; +import { AlertsService } from 'src/app/services/fnd/alerts.service'; + +@Component({ + selector: 'app-compact-filter', + templateUrl: './compact-filter.component.html', + styleUrls: ['./compact-filter.component.scss'] +}) +export class CompactFilterComponent implements OnInit { + @Input() filterKey: string = ''; + @Input() filterType: string = 'text'; + @Input() filterOptions: string[] = []; + @Input() filterLabel: string = ''; + @Input() apiUrl: string = ''; + @Input() connectionId: number | undefined; + @Output() filterChange = new EventEmitter(); + @Output() configChange = new EventEmitter(); + + selectedFilter: Filter | null = null; + filterValue: any = ''; + availableFilters: Filter[] = []; + availableKeys: string[] = []; + availableValues: string[] = []; + + // Configuration properties + isConfigMode: boolean = false; + configFilterKey: string = ''; + configFilterType: string = 'text'; + configFilterOptions: string = ''; + configFilterLabel: string = ''; + configApiUrl: string = ''; + configConnectionId: number | undefined; + + constructor( + private filterService: FilterService, + private alertService: AlertsService + ) { } + + ngOnInit(): void { + // Subscribe to filter definitions to get available filters + this.filterService.filters$.subscribe(filters => { + this.availableFilters = filters; + this.updateSelectedFilter(); + }); + + // Subscribe to filter state changes + this.filterService.filterState$.subscribe(state => { + if (this.selectedFilter && state.hasOwnProperty(this.selectedFilter.id)) { + this.filterValue = state[this.selectedFilter.id]; + } + }); + + // Initialize configuration from inputs + this.configFilterKey = this.filterKey; + this.configFilterType = this.filterType; + this.configFilterLabel = this.filterLabel; + this.configFilterOptions = this.filterOptions.join(','); + this.configApiUrl = this.apiUrl; + this.configConnectionId = this.connectionId; + + // Load available keys and values if API URL and filter key are provided + if (this.apiUrl) { + this.loadAvailableKeys(); + // Load available values for the current filter key if it's a dropdown or multiselect + if ((this.filterType === 'dropdown' || this.filterType === 'multiselect') && this.filterKey) { + this.loadAvailableValues(this.filterKey); + } + } + } + + updateSelectedFilter(): void { + if (this.filterKey && this.availableFilters.length > 0) { + this.selectedFilter = this.availableFilters.find(f => f.field === this.filterKey) || null; + if (this.selectedFilter) { + // Get current value for this filter + const currentState = this.filterService.getFilterValues(); + this.filterValue = currentState[this.selectedFilter.id] || ''; + } + } + } + + onFilterValueChange(value: any): void { + if (this.selectedFilter) { + this.filterValue = value; + this.filterService.updateFilterValue(this.selectedFilter.id, value); + this.filterChange.emit({ filterId: this.selectedFilter.id, value: value }); + } + } + + onToggleChange(checked: boolean): void { + this.onFilterValueChange(checked); + } + + onDateRangeChange(dateRange: { start: string | null, end: string | null }): void { + this.onFilterValueChange(dateRange); + } + + // Load available keys from API + loadAvailableKeys(): void { + if (this.apiUrl) { + this.alertService.getColumnfromurl(this.apiUrl, this.connectionId).subscribe( + (keys: string[]) => { + this.availableKeys = keys; + }, + (error) => { + console.error('Error loading available keys:', error); + this.availableKeys = []; + } + ); + } + } + + // Load available values for a specific key + loadAvailableValues(key: string): void { + if (this.apiUrl && key) { + this.alertService.getValuesFromUrl(this.apiUrl, this.connectionId, key).subscribe( + (values: string[]) => { + this.availableValues = values; + // Update filter options if this is a dropdown or multiselect + if (this.filterType === 'dropdown' || this.filterType === 'multiselect') { + this.filterOptions = values; + } + }, + (error) => { + console.error('Error loading available values:', error); + this.availableValues = []; + } + ); + } + } + + // Configuration methods + toggleConfigMode(): void { + this.isConfigMode = !this.isConfigMode; + if (this.isConfigMode) { + // Initialize config values + this.configFilterKey = this.filterKey; + this.configFilterType = this.filterType; + this.configFilterLabel = this.filterLabel; + this.configFilterOptions = this.filterOptions.join(','); + this.configApiUrl = this.apiUrl; + this.configConnectionId = this.connectionId; + } + } + + saveConfiguration(): void { + const config = { + filterKey: this.configFilterKey, + filterType: this.configFilterType, + filterLabel: this.configFilterLabel, + filterOptions: this.configFilterOptions.split(',').map(opt => opt.trim()).filter(opt => opt), + apiUrl: this.configApiUrl, + connectionId: this.configConnectionId + }; + + // Emit configuration change + this.configChange.emit(config); + + // Update local properties + this.filterKey = config.filterKey; + this.filterType = config.filterType; + this.filterLabel = config.filterLabel; + this.filterOptions = config.filterOptions; + this.apiUrl = config.apiUrl; + this.connectionId = config.connectionId; + + // Load available keys if API URL is provided + if (this.apiUrl) { + this.loadAvailableKeys(); + } + + // Load available values for the selected key if it's a dropdown or multiselect + if ((this.filterType === 'dropdown' || this.filterType === 'multiselect') && this.filterKey) { + this.loadAvailableValues(this.filterKey); + } + + // Update selected filter + this.updateSelectedFilter(); + + // Exit config mode + this.isConfigMode = false; + } + + cancelConfiguration(): void { + this.isConfigMode = false; + } + + // Handle filter key change in configuration + onFilterKeyChange(key: string): void { + this.configFilterKey = key; + // Load available values for the selected key if it's a dropdown or multiselect + if ((this.configFilterType === 'dropdown' || this.configFilterType === 'multiselect') && key) { + this.loadAvailableValues(key); + } + } + + // Handle API URL change in configuration + onApiUrlChange(url: string): void { + this.configApiUrl = url; + // Load available keys when API URL changes + if (url) { + this.loadAvailableKeys(); + // Also clear available values since the API has changed + this.availableValues = []; + this.filterOptions = []; + } + } + + // Handle filter type change in configuration + onFilterTypeChange(type: string): void { + this.configFilterType = type; + // If changing to dropdown or multiselect and we have a key selected, load values + if ((type === 'dropdown' || type === 'multiselect') && this.configFilterKey) { + this.loadAvailableValues(this.configFilterKey); + } + } +} \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/index.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/index.ts index 401a53d..a770f81 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/index.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/common-filter/index.ts @@ -1,3 +1,4 @@ export * from './filter.service'; export * from './common-filter.component'; -export * from './chart-wrapper.component'; \ No newline at end of file +export * from './chart-wrapper.component'; +export * from './compact-filter.component'; \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.ts index 0d9f161..3f0ce66 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/editnewdash/editnewdash.component.ts @@ -24,6 +24,8 @@ import { isArray } from 'highcharts'; import { SureconnectService } from '../sureconnect/sureconnect.service'; // Add the CommonFilterComponent import import { CommonFilterComponent } from '../common-filter/common-filter.component'; +// Add the CompactFilterComponent import +import { CompactFilterComponent } from '../common-filter'; function isNullArray(arr) { return !Array.isArray(arr) || arr.length === 0; @@ -98,6 +100,10 @@ export class EditnewdashComponent implements OnInit { { name: 'Grid View', identifier: 'grid_view' + }, + { + name: 'Compact Filter', + identifier: 'compact_filter' } ] @@ -123,6 +129,7 @@ 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 ]; model: any; linesdata: any; @@ -168,7 +175,12 @@ export class EditnewdashComponent implements OnInit { drilldownLayers: [] as any[], // Common filter properties commonFilterEnabled: false, - commonFilterEnabledDrilldown: false + commonFilterEnabledDrilldown: false, + // Compact filter properties + filterKey: '', + filterType: 'text', + filterLabel: '', + filterOptions: [] as string[] }; // Add sureconnect data property @@ -519,6 +531,21 @@ export class EditnewdashComponent implements OnInit { component: CommonFilterComponent, name: "Common Filter" }); + case "compact_filter": + return this.dashboardArray.push({ + cols: 3, + rows: 2, + x: 0, + y: 0, + chartid: maxChartId + 1, + component: CompactFilterComponent, + name: "Compact Filter", + // Add default configuration for compact filter + filterKey: '', + filterType: 'text', + filterLabel: '', + filterOptions: [] + }); case "grid_view": return this.dashboardArray.push({ cols: 5, @@ -561,6 +588,19 @@ export class EditnewdashComponent implements OnInit { if (item['commonFilterEnabledDrilldown'] === undefined) { this.gadgetsEditdata['commonFilterEnabledDrilldown'] = false; } + // Initialize compact filter properties if not present + if (item['filterKey'] === undefined) { + this.gadgetsEditdata['filterKey'] = ''; + } + if (item['filterType'] === undefined) { + this.gadgetsEditdata['filterType'] = 'text'; + } + if (item['filterLabel'] === undefined) { + this.gadgetsEditdata['filterLabel'] = ''; + } + if (item['filterOptions'] === undefined) { + this.gadgetsEditdata['filterOptions'] = []; + } this.getStores(); // Set default connection if none is set and we have connections @@ -736,6 +776,14 @@ export class EditnewdashComponent implements OnInit { xyz.drilldownLayers = this.gadgetsEditdata.drilldownLayers; xyz.commonFilterEnabled = this.gadgetsEditdata.commonFilterEnabled; // Add common filter property + // For compact filter, preserve filter configuration properties + if (item.component && item.component.name === 'CompactFilterComponent') { + xyz.filterKey = this.gadgetsEditdata.filterKey || ''; + xyz.filterType = this.gadgetsEditdata.filterType || 'text'; + xyz.filterLabel = this.gadgetsEditdata.filterLabel || ''; + xyz.filterOptions = this.gadgetsEditdata.filterOptions || []; + } + console.log(xyz); return xyz; } @@ -809,6 +857,16 @@ export class EditnewdashComponent implements OnInit { chartInputs['connection'] = item['connection'] || undefined; } + // For CompactFilterComponent, pass filter configuration properties + if (item.component && item.component.name === 'CompactFilterComponent') { + chartInputs['filterKey'] = item['filterKey'] || ''; + chartInputs['filterType'] = item['filterType'] || 'text'; + chartInputs['filterLabel'] = item['filterLabel'] || ''; + chartInputs['filterOptions'] = item['filterOptions'] || []; + chartInputs['apiUrl'] = item['table'] || ''; // Use table as API URL + chartInputs['connectionId'] = item['connection'] ? parseInt(item['connection'], 10) : undefined; + } + // Remove undefined properties to avoid passing unnecessary data Object.keys(chartInputs).forEach(key => { if (chartInputs[key] === undefined) { @@ -863,6 +921,16 @@ export class EditnewdashComponent implements OnInit { updatedItem.commonFilterEnabled = this.gadgetsEditdata.commonFilterEnabled; // Add common filter property updatedItem.commonFilterEnabledDrilldown = this.gadgetsEditdata.commonFilterEnabledDrilldown; // Add drilldown common filter property + // For compact filter, preserve filter configuration properties + if (item.component && item.component.name === 'CompactFilterComponent') { + updatedItem.filterKey = this.gadgetsEditdata.filterKey || ''; + updatedItem.filterType = this.gadgetsEditdata.filterType || 'text'; + updatedItem.filterLabel = this.gadgetsEditdata.filterLabel || ''; + updatedItem.filterOptions = this.gadgetsEditdata.filterOptions || []; + updatedItem.table = this.gadgetsEditdata.table || ''; // API URL + updatedItem.connection = this.gadgetsEditdata.connection || undefined; // Connection ID + } + console.log('Updated item:', updatedItem); return updatedItem; } diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.html index 83d155a..7a1bccb 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.html +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.html @@ -1,32 +1,50 @@ -
- - +
+ +
+ + +
-
- Drilldown Level: {{currentDrilldownLevel}} - -
- -
- No data available +
+

{{ charttitle || 'Bar Chart' }}

- -
- - +
+
+ +
+

No data available

+
+ + + + + + +
+
+
+
\ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss index 2f11ebd..05c97cb 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.scss @@ -5,27 +5,214 @@ width: 100%; } -.bar-chart-container { - position: relative; +.chart-container { height: 100%; - width: 100%; + display: flex; + flex-direction: column; + background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + transition: all 0.3s ease; + border: 1px solid #eaeaea; + padding: 20px; } -canvas { - display: block; - max-width: 100%; - max-height: 100%; +.chart-container:hover { + box-shadow: 0 6px 25px rgba(0, 0, 0, 0.2); + transform: translateY(-2px); +} + +.compact-filters-container { + display: flex; + flex-wrap: wrap; + gap: 5px; + margin-bottom: 10px; + padding: 5px; + background: #ffffff; + border: 1px solid #e9ecef; + border-radius: 6px; + min-height: 40px; +} + +.drilldown-indicator { + background-color: #e0e0e0; + padding: 10px; + margin-bottom: 15px; + border-radius: 8px; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + gap: 10px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.drilldown-text { + font-weight: bold; + color: #333; + font-size: 16px; +} + +.btn { + padding: 6px 12px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: all 0.3s ease; +} + +.btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.btn-sm { + padding: 4px 8px; + font-size: 12px; +} + +.btn-secondary { + background-color: #007cba; + color: white; +} + +.btn-danger { + background-color: #dc3545; + color: white; +} + +.chart-header { + margin-bottom: 20px; + + h3 { + font-size: 22px; + font-weight: 600; + color: #0a192f; + margin: 0; + text-align: center; + padding-bottom: 10px; + border-bottom: 2px solid #3498db; + } +} + +.chart-wrapper { + flex: 1; + position: relative; + + .chart-content { + position: relative; + height: 100%; + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 8px; + padding: 10px; + display: flex; + align-items: center; + justify-content: center; + + &.loading { + opacity: 0.7; + + canvas { + filter: blur(2px); + } + } + + canvas { + max-width: 100%; + max-height: 100%; + transition: filter 0.3s ease; + filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1)); + } + + canvas:hover { + filter: drop-shadow(0 4px 8px rgba(0,0,0,0.15)); + transform: scale(1.02); + transition: all 0.3s ease; + } + + .no-data-message { + text-align: center; + padding: 30px; + color: #666; + font-size: 18px; + font-style: italic; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + } + + .no-data-message p { + margin: 0; + } + + .loading-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.8); + + .shimmer-bar { + width: 80%; + height: 20px; + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + border-radius: 4px; + } + } + } +} + +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } } // Responsive design for chart container @media (max-width: 768px) { - .bar-chart-container { - height: 300px; + .chart-container { + padding: 15px; + } + + .chart-header h3 { + font-size: 18px; + } + + .drilldown-indicator { + flex-direction: column; + gap: 5px; + } + + .drilldown-text { + font-size: 14px; + } + + .compact-filters-container { + flex-wrap: wrap; } } @media (max-width: 480px) { - .bar-chart-container { - height: 250px; + .chart-container { + padding: 10px; + } + + .chart-header h3 { + font-size: 16px; } } \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts index 8e0365c..401b182 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/bar-chart/bar-chart.component.ts @@ -140,6 +140,12 @@ export class BarChartComponent implements OnInit, OnChanges, OnDestroy { console.log('Chart legend changed to:', this.barChartLegend); } } + + // Handle filter changes from compact filters + onFilterChange(event: { filterId: string, value: any }): void { + console.log('Compact filter changed:', event); + // The filter service will automatically trigger chart updates through the subscription + } fetchChartData(): void { // Set flag to prevent recursive calls diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.html b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.html index 0828c55..a13b432 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.html +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.html @@ -1,39 +1,53 @@
+ +
+ + +
+ -
- Drilldown Level: {{currentDrilldownLevel}} - -
-

{{ charttitle }}

-
- -
-
-

Loading chart data...

-
- - -
-

No chart data available

-
- - - - +
+

{{ charttitle }}

+ +
+
+ +
+

No chart data available

+
+ + + + + + +
+
+
+
+
+
diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.scss b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.scss index 6212182..9d1cbdc 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.scss +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.scss @@ -17,17 +17,78 @@ transform: translateY(-2px); } +.compact-filters-container { + display: flex; + flex-wrap: wrap; + gap: 5px; + margin-bottom: 10px; + padding: 5px; + background: #ffffff; + border: 1px solid #e9ecef; + border-radius: 6px; + min-height: 40px; +} - -.chart-title { - font-size: 26px; - font-weight: 700; - color: #2c3e50; - margin-bottom: 20px; +.drilldown-indicator { + background-color: #e0e0e0; + padding: 10px; + margin-bottom: 15px; + border-radius: 8px; text-align: center; - padding-bottom: 15px; - border-bottom: 2px solid #3498db; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + display: flex; + justify-content: center; + align-items: center; + gap: 10px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.drilldown-text { + font-weight: bold; + color: #333; + font-size: 16px; +} + +.btn { + padding: 6px 12px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: all 0.3s ease; +} + +.btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.btn-sm { + padding: 4px 8px; + font-size: 12px; +} + +.btn-secondary { + background-color: #007cba; + color: white; +} + +.btn-danger { + background-color: #dc3545; + color: white; +} + +.chart-header { + margin-bottom: 20px; + + .chart-title { + font-size: 22px; + font-weight: 600; + color: #0a192f; + margin: 0; + text-align: center; + padding-bottom: 10px; + border-bottom: 2px solid #3498db; + } } .chart-wrapper { @@ -56,6 +117,62 @@ transition: all 0.3s ease; } +.chart-content { + position: relative; + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + + &.loading { + opacity: 0.7; + + canvas { + filter: blur(2px); + } + } + + .no-data-message { + text-align: center; + padding: 30px; + color: #666; + font-size: 18px; + font-style: italic; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + } + + .no-data-message p { + margin: 0; + } + + .loading-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.8); + + .shimmer-donut { + width: 120px; + height: 120px; + border-radius: 50%; + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + } + } +} + .chart-legend { display: flex; flex-wrap: wrap; @@ -119,36 +236,13 @@ text-align: center; } -.loading-indicator, .no-data-message { - text-align: center; - padding: 30px; - color: #666; - font-size: 18px; - font-style: italic; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; -} - -.loading-indicator p, .no-data-message p { - margin: 10px 0 0 0; -} - -.spinner { - border: 4px solid #f3f3f3; - border-top: 4px solid #3498db; - border-radius: 50%; - width: 40px; - height: 40px; - animation: spin 1s linear infinite; - margin-bottom: 10px; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } } /* Responsive design */ @@ -157,9 +251,17 @@ padding: 15px; } - .chart-title { - font-size: 20px; - margin-bottom: 15px; + .chart-header .chart-title { + font-size: 18px; + } + + .drilldown-indicator { + flex-direction: column; + gap: 5px; + } + + .drilldown-text { + font-size: 14px; } .chart-wrapper { @@ -181,4 +283,8 @@ font-size: 16px; padding: 20px; } + + .compact-filters-container { + flex-wrap: wrap; + } } \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.ts index 9153a55..106e61c 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/doughnut-chart/doughnut-chart.component.ts @@ -200,6 +200,12 @@ export class DoughnutChartComponent implements OnInit, OnChanges, AfterViewCheck this.subscriptions.forEach(sub => sub.unsubscribe()); } + // Handle filter changes from compact filters + onFilterChange(event: { filterId: string, value: any }): void { + console.log('Compact filter changed:', event); + // The filter service will automatically trigger chart updates through the subscription + } + // Public method to refresh data when filters change refreshData(): void { this.fetchChartData(); diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/components/sidebar-filters/sidebar-filters.component.html.new b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/components/sidebar-filters/sidebar-filters.component.html.new new file mode 100644 index 0000000..95849af --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/components/sidebar-filters/sidebar-filters.component.html.new @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/components/sidebar-filters/sidebar-filters.component.ts.new b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/components/sidebar-filters/sidebar-filters.component.ts.new new file mode 100644 index 0000000..295f2c1 --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/components/sidebar-filters/sidebar-filters.component.ts.new @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import { DashboardFilterService } from '../../services/dashboard-filter.service'; + +@Component({ + selector: 'app-shield-sidebar-filters', + templateUrl: './sidebar-filters.component.html', + styleUrls: ['./sidebar-filters.component.scss'] +}) +export class SidebarFiltersComponent implements OnInit { + + constructor(private filterService: DashboardFilterService) { } + + ngOnInit(): void { + // Component initialization + } +} \ No newline at end of file diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/shield-dashboard.component.ts b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/shield-dashboard.component.ts index 8343d0b..5fec6fc 100644 --- a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/shield-dashboard.component.ts +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/shield-dashboard.component.ts @@ -74,6 +74,10 @@ export class ShieldDashboardComponent implements OnInit { { name: 'Quarterwise Flow', identifier: 'line_chart' + }, + { + name: 'Compact Filter', + identifier: 'compact_filter' } ]; @@ -300,6 +304,20 @@ export class ShieldDashboardComponent implements OnInit { drilldownLayers: [] }; break; + case "compact_filter": + newItem = { + cols: 3, + rows: 2, + y: 0, + x: 0, + chartType: 'compact-filter', + name: 'Compact Filter', + id: newId, + baseFilters: [], + drilldownEnabled: false, + drilldownLayers: [] + }; + break; default: newItem = { cols: 5, diff --git a/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/shield-dashboard.component.ts.fixed b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/shield-dashboard.component.ts.fixed new file mode 100644 index 0000000..32eb2b3 --- /dev/null +++ b/frontend/angular-clarity-master/src/app/modules/main/builder/dashboardnew/gadgets/shield-dashboard/shield-dashboard.component.ts.fixed @@ -0,0 +1,223 @@ +import { Component, OnInit } from '@angular/core'; +import { GridsterConfig, GridsterItem } from 'angular-gridster2'; + +interface ShieldDashboardItem extends GridsterItem { + chartType: string; + name: string; + id: number; + component?: any; +} + +@Component({ + selector: 'app-shield-dashboard', + templateUrl: './shield-dashboard.component.html', + styleUrls: ['./shield-dashboard.component.scss'] +}) +export class ShieldDashboardComponent implements OnInit { + options: GridsterConfig; + dashboard: Array; + + // Keep track of deleted items + deletedItems: Array = []; + + constructor() { } + + ngOnInit(): void { + this.options = { + gridType: 'fit', + enableEmptyCellDrop: true, + emptyCellDropCallback: this.onDrop, + pushItems: true, + swap: true, + pushDirections: { north: true, east: true, south: true, west: true }, + resizable: { enabled: true }, + itemChangeCallback: this.itemChange.bind(this), + draggable: { + enabled: true, + ignoreContent: true, + dropOverItems: true, + dragHandleClass: 'drag-handler', + ignoreContentClass: 'no-drag', + }, + displayGrid: 'always', + minCols: 10, + minRows: 10, + itemResizeCallback: this.itemResize.bind(this) + }; + + // Initialize the dashboard with empty canvas + this.dashboard = []; + } + + onDrop = (event: any) => { + // Handle dropping new components onto the dashboard + console.log('Item dropped:', event); + + // Get the component identifier from the drag event + const componentType = event.dataTransfer ? event.dataTransfer.getData('widgetIdentifier') : ''; + console.log('Component type dropped:', componentType); + + if (componentType) { + this.addComponentToDashboard(componentType); + } else { + console.log('No component type found in drag data'); + } + } + + // Add a new component to the dashboard + addComponentToDashboard(componentType: string) { + // Generate a new ID for the component + const newId = this.dashboard.length > 0 ? Math.max(...this.dashboard.map(item => item.id), 0) + 1 : 1; + + let newItem: ShieldDashboardItem; + + switch (componentType) { + case "bar_chart": + newItem = { + cols: 5, + rows: 6, + y: 0, + x: 0, + chartType: 'bar-chart', + name: 'Bar Chart', + id: newId + }; + break; + case "doughnut_chart": + // For doughnut charts, we'll need to determine which one based on existing items + const donutCount = this.dashboard.filter(item => item.chartType === 'donut-chart').length; + if (donutCount % 2 === 0) { + newItem = { + cols: 5, + rows: 6, + y: 0, + x: 0, + chartType: 'donut-chart', + name: 'End Customer Donut', + id: newId + }; + } else { + newItem = { + cols: 5, + rows: 6, + y: 0, + x: 0, + chartType: 'donut-chart', + name: 'Segment Penetration Donut', + id: newId + }; + } + break; + case "map_chart": + newItem = { + cols: 5, + rows: 6, + y: 0, + x: 0, + chartType: 'map-chart', + name: 'Map Chart', + id: newId + }; + break; + case "grid_view": + newItem = { + cols: 10, + rows: 6, + y: 0, + x: 0, + chartType: 'data-table', + name: 'Data Table', + id: newId + }; + break; + case "to_do_chart": + newItem = { + cols: 5, + rows: 6, + y: 0, + x: 0, + chartType: 'deal-details', + name: 'Deal Details', + id: newId + }; + break; + case "line_chart": + newItem = { + cols: 5, + rows: 6, + y: 0, + x: 0, + chartType: 'quarterwise-flow', + name: 'Quarterwise Flow', + id: newId + }; + break; + default: + newItem = { + cols: 5, + rows: 6, + y: 0, + x: 0, + chartType: componentType, + name: componentType, + id: newId + }; + } + + // Add the new item to the dashboard + this.dashboard.push(newItem); + } + + removeItem(item: ShieldDashboardItem) { + // Add the item to deleted items list before removing + this.deletedItems.push({...item}); + + // Remove the item from the dashboard + this.dashboard.splice(this.dashboard.indexOf(item), 1); + } + + // Restore a deleted item + restoreItem(item: ShieldDashboardItem) { + // Remove from deleted items + this.deletedItems.splice(this.deletedItems.indexOf(item), 1); + + // Add back to dashboard + this.dashboard.push(item); + } + + // Clear all deleted items + clearDeletedItems() { + this.deletedItems = []; + } + + itemChange() { + console.log('Item changed:', this.dashboard); + } + + itemResize(item: any, itemComponent: any) { + console.log('Item resized:', item); + // Trigger a window resize event to notify charts to resize + window.dispatchEvent(new Event('resize')); + } + + /** + * Extract only the relevant chart configuration properties to pass to chart components + * This prevents errors when trying to set properties that don't exist on the components + */ + getChartInputs(item: any): any { + // Only pass properties that are relevant to chart components + const chartInputs = { + chartType: item.chartType, + name: item.name + }; + + // Remove undefined properties to avoid passing unnecessary data + Object.keys(chartInputs).forEach(key => { + if (chartInputs[key] === undefined) { + delete chartInputs[key]; + } + }); + + return chartInputs; + } +} \ No newline at end of file