dashboard
This commit is contained in:
		
							parent
							
								
									02f37a1bc5
								
							
						
					
					
						commit
						f2b6a4d145
					
				| @ -1,76 +1,3 @@ | |||||||
| <div class="sidebar-filters"> | <div class="sidebar-filters"> | ||||||
|   <!-- Application Header --> |   <!-- Component Palette Button and List have been moved to the main shield dashboard component --> | ||||||
|   <div class="app-header"> |  | ||||||
|     <h1 class="app-name">Shield</h1> |  | ||||||
|     <h2 class="dashboard-title">Overall Dashboard</h2> |  | ||||||
|   </div> |  | ||||||
|    |  | ||||||
|   <!-- KPI Metrics --> |  | ||||||
|   <div class="kpi-section"> |  | ||||||
|     <div class="kpi-card total-leads"> |  | ||||||
|       <div class="kpi-title">Total Leads</div> |  | ||||||
|       <div class="kpi-value">{{ totalLeads }}</div> |  | ||||||
|     </div> |  | ||||||
|     <div class="kpi-card total-deals"> |  | ||||||
|       <div class="kpi-title">Total Deals</div> |  | ||||||
|       <div class="kpi-value">{{ totalDeals }}</div> |  | ||||||
|     </div> |  | ||||||
|   </div> |  | ||||||
|    |  | ||||||
|   <!-- Filters Section --> |  | ||||||
|   <div class="filters-section"> |  | ||||||
|     <h3 class="filters-title">Filters</h3> |  | ||||||
|      |  | ||||||
|     <div class="filter-group"> |  | ||||||
|       <label for="salesRep">Sales Rep</label> |  | ||||||
|       <select  |  | ||||||
|         id="salesRep"  |  | ||||||
|         [(ngModel)]="selectedSalesRep"  |  | ||||||
|         (ngModelChange)="updateFilter('salesRep', $event)" |  | ||||||
|         class="filter-select"> |  | ||||||
|         <option value="">All Sales Reps</option> |  | ||||||
|         <option *ngFor="let rep of salesReps" [value]="rep">{{ rep }}</option> |  | ||||||
|       </select> |  | ||||||
|     </div> |  | ||||||
|      |  | ||||||
|     <div class="filter-group"> |  | ||||||
|       <label for="partner">Partner</label> |  | ||||||
|       <select  |  | ||||||
|         id="partner"  |  | ||||||
|         [(ngModel)]="selectedPartner"  |  | ||||||
|         (ngModelChange)="updateFilter('partner', $event)" |  | ||||||
|         class="filter-select"> |  | ||||||
|         <option value="">All Partners</option> |  | ||||||
|         <option *ngFor="let partner of partners" [value]="partner">{{ partner }}</option> |  | ||||||
|       </select> |  | ||||||
|     </div> |  | ||||||
|      |  | ||||||
|     <div class="filter-group"> |  | ||||||
|       <label for="tractionChannel">Traction Channel</label> |  | ||||||
|       <select  |  | ||||||
|         id="tractionChannel"  |  | ||||||
|         [(ngModel)]="selectedTractionChannel"  |  | ||||||
|         (ngModelChange)="updateFilter('tractionChannel', $event)" |  | ||||||
|         class="filter-select"> |  | ||||||
|         <option value="">All Channels</option> |  | ||||||
|         <option *ngFor="let channel of tractionChannels" [value]="channel">{{ channel }}</option> |  | ||||||
|       </select> |  | ||||||
|     </div> |  | ||||||
|      |  | ||||||
|     <div class="filter-group"> |  | ||||||
|       <label for="subProductLine">Sub Product Line</label> |  | ||||||
|       <select  |  | ||||||
|         id="subProductLine"  |  | ||||||
|         [(ngModel)]="selectedSubProductLine"  |  | ||||||
|         (ngModelChange)="updateFilter('subProductLine', $event)" |  | ||||||
|         class="filter-select"> |  | ||||||
|         <option value="">All Lines</option> |  | ||||||
|         <option *ngFor="let line of subProductLines" [value]="line">{{ line }}</option> |  | ||||||
|       </select> |  | ||||||
|     </div> |  | ||||||
|      |  | ||||||
|     <button class="reset-button" (click)="resetFilters()"> |  | ||||||
|       Reset Filters |  | ||||||
|     </button> |  | ||||||
|   </div> |  | ||||||
| </div> | </div> | ||||||
| @ -3,6 +3,35 @@ | |||||||
|   display: flex; |   display: flex; | ||||||
|   flex-direction: column; |   flex-direction: column; | ||||||
|    |    | ||||||
|  |   .componentbtn { | ||||||
|  |     margin: 10px; | ||||||
|  |     width: calc(100% - 20px); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .nav-list { | ||||||
|  |     padding: 0; | ||||||
|  |     margin: 0 10px; | ||||||
|  |      | ||||||
|  |     .nav-link { | ||||||
|  |       display: flex; | ||||||
|  |       align-items: center; | ||||||
|  |       padding: 8px 12px; | ||||||
|  |       margin-bottom: 5px; | ||||||
|  |       background: #f8f9fa; | ||||||
|  |       border: 1px solid #e9ecef; | ||||||
|  |       border-radius: 4px; | ||||||
|  |       cursor: move; | ||||||
|  |        | ||||||
|  |       &:hover { | ||||||
|  |         background: #e9ecef; | ||||||
|  |       } | ||||||
|  |        | ||||||
|  |       .has-badge { | ||||||
|  |         margin-left: auto; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|   .app-header { |   .app-header { | ||||||
|     margin-bottom: 30px; |     margin-bottom: 30px; | ||||||
|      |      | ||||||
| @ -55,6 +84,74 @@ | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|    |    | ||||||
|  |   .component-palette-section { | ||||||
|  |     margin: 20px; | ||||||
|  |      | ||||||
|  |     .component-palette-button { | ||||||
|  |       width: 100%; | ||||||
|  |       padding: 12px; | ||||||
|  |       background: rgba(255, 107, 53, 0.2); | ||||||
|  |       color: white; | ||||||
|  |       border: 1px solid rgba(255, 107, 53, 0.5); | ||||||
|  |       border-radius: 8px; | ||||||
|  |       font-size: 16px; | ||||||
|  |       font-weight: 500; | ||||||
|  |       cursor: pointer; | ||||||
|  |       transition: all 0.2s ease; | ||||||
|  |       display: flex; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: center; | ||||||
|  |       gap: 8px; | ||||||
|  |        | ||||||
|  |       &:hover { | ||||||
|  |         background: rgba(255, 107, 53, 0.3); | ||||||
|  |         border-color: #ff6b35; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     .component-palette { | ||||||
|  |       margin-top: 15px; | ||||||
|  |       background: rgba(255, 255, 255, 0.05); | ||||||
|  |       border-radius: 8px; | ||||||
|  |       padding: 15px; | ||||||
|  |        | ||||||
|  |       .palette-title { | ||||||
|  |         font-size: 16px; | ||||||
|  |         color: white; | ||||||
|  |         margin: 0 0 15px 0; | ||||||
|  |         text-align: center; | ||||||
|  |       } | ||||||
|  |        | ||||||
|  |       .component-list { | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |         gap: 10px; | ||||||
|  |          | ||||||
|  |         .component-item { | ||||||
|  |           padding: 10px 15px; | ||||||
|  |           background: rgba(255, 255, 255, 0.1); | ||||||
|  |           border: 1px solid rgba(255, 255, 255, 0.2); | ||||||
|  |           border-radius: 6px; | ||||||
|  |           color: white; | ||||||
|  |           cursor: move; | ||||||
|  |           display: flex; | ||||||
|  |           align-items: center; | ||||||
|  |           gap: 10px; | ||||||
|  |           transition: all 0.2s ease; | ||||||
|  |            | ||||||
|  |           &:hover { | ||||||
|  |             background: rgba(255, 107, 53, 0.2); | ||||||
|  |             border-color: #ff6b35; | ||||||
|  |           } | ||||||
|  |            | ||||||
|  |           .drag-icon { | ||||||
|  |             font-size: 16px; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|   .filters-section { |   .filters-section { | ||||||
|     flex: 1; |     flex: 1; | ||||||
|      |      | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||||
| import { DashboardFilterService, FilterState } from '../../services/dashboard-filter.service'; | import { DashboardFilterService } from '../../services/dashboard-filter.service'; | ||||||
| import { Observable } from 'rxjs'; |  | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-shield-sidebar-filters', |   selector: 'app-shield-sidebar-filters', | ||||||
| @ -8,44 +7,10 @@ import { Observable } from 'rxjs'; | |||||||
|   styleUrls: ['./sidebar-filters.component.scss'] |   styleUrls: ['./sidebar-filters.component.scss'] | ||||||
| }) | }) | ||||||
| export class SidebarFiltersComponent implements OnInit { | export class SidebarFiltersComponent implements OnInit { | ||||||
|   // Filter options
 |  | ||||||
|   salesReps = ['All Sales Reps', 'John Smith', 'Jane Doe', 'Mike Johnson', 'Sarah Wilson']; |  | ||||||
|   partners = ['All Partners', 'Partner A', 'Partner B', 'Partner C', 'Partner D']; |  | ||||||
|   tractionChannels = ['All Channels', 'Direct', 'Indirect', 'Online', 'Referral']; |  | ||||||
|   subProductLines = ['All Lines', 'Product Line 1', 'Product Line 2', 'Product Line 3']; |  | ||||||
|    |  | ||||||
|   // Current filter values
 |  | ||||||
|   selectedSalesRep = ''; |  | ||||||
|   selectedPartner = ''; |  | ||||||
|   selectedTractionChannel = ''; |  | ||||||
|   selectedSubProductLine = ''; |  | ||||||
|    |  | ||||||
|   // KPI data
 |  | ||||||
|   totalLeads = 1248; |  | ||||||
|   totalDeals = 842; |  | ||||||
| 
 | 
 | ||||||
|   constructor(private filterService: DashboardFilterService) { } |   constructor(private filterService: DashboardFilterService) { } | ||||||
| 
 | 
 | ||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
|     // Subscribe to KPI data changes
 |     // Component initialization
 | ||||||
|     this.filterService.kpiData$.subscribe(kpiData => { |  | ||||||
|       this.totalLeads = kpiData.totalLeads; |  | ||||||
|       this.totalDeals = kpiData.totalDeals; |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   // Update filter state when any filter changes
 |  | ||||||
|   updateFilter(filterType: keyof FilterState, value: string): void { |  | ||||||
|     this.filterService.updateFilter(filterType, value); |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   // Reset all filters to default values
 |  | ||||||
|   resetFilters(): void { |  | ||||||
|     this.selectedSalesRep = ''; |  | ||||||
|     this.selectedPartner = ''; |  | ||||||
|     this.selectedTractionChannel = ''; |  | ||||||
|     this.selectedSubProductLine = ''; |  | ||||||
|      |  | ||||||
|     this.filterService.resetFilters(); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -1,12 +1,30 @@ | |||||||
| <div class="shield-dashboard"> | <div class="shield-dashboard"> | ||||||
|   <div class="dashboard-container"> |   <div class="dashboard-container"> | ||||||
|     <!-- Sidebar Filters --> |     <!-- Sidebar Filters --> | ||||||
|     <app-shield-sidebar-filters class="sidebar"></app-shield-sidebar-filters> |     <div class="sidebar"> | ||||||
|  |       <button class="btn componentbtn" (click)="toggleComponentPalette()"> | ||||||
|  |         <clr-icon shape="plus"></clr-icon> Component | ||||||
|  |       </button> | ||||||
|  |        | ||||||
|  |       <ul class="nav-list" style="list-style-type: none;" *ngIf="showComponentPalette"> | ||||||
|  |         <li *ngFor="let widget of WidgetsMock"> | ||||||
|  |           <!-- | ||||||
|  |             Draggable widget from store using vanilla javascript event (dragstart) | ||||||
|  |             onDrag() is call, it take $event and a widget identifier as parameters | ||||||
|  |           --> | ||||||
|  |           <a draggable="true" class="nav-link" (dragstart)="onDrag($event, widget.identifier)"> | ||||||
|  |             <clr-icon shape="drag-handle" style="margin-right: 10px;"></clr-icon> | ||||||
|  |             {{ widget.name }} | ||||||
|  |             <clr-icon shape="plugin" class="has-badge"></clr-icon> | ||||||
|  |           </a> | ||||||
|  |         </li> | ||||||
|  |       </ul> | ||||||
|  |     </div> | ||||||
|      |      | ||||||
|     <!-- Main Dashboard Content --> |     <!-- Main Dashboard Content --> | ||||||
|     <div class="main-content"> |     <div class="main-content"> | ||||||
|       <!-- KPI Metrics --> |       <!-- KPI Metrics --> | ||||||
|       <div class="kpi-section"> |       <!-- <div class="kpi-section"> | ||||||
|         <div class="kpi-card total-leads"> |         <div class="kpi-card total-leads"> | ||||||
|           <div class="kpi-title">Total Leads</div> |           <div class="kpi-title">Total Leads</div> | ||||||
|           <div class="kpi-value">1,248</div> |           <div class="kpi-value">1,248</div> | ||||||
| @ -15,10 +33,29 @@ | |||||||
|           <div class="kpi-title">Total Deals</div> |           <div class="kpi-title">Total Deals</div> | ||||||
|           <div class="kpi-value">842</div> |           <div class="kpi-value">842</div> | ||||||
|         </div> |         </div> | ||||||
|  |       </div> --> | ||||||
|  |        | ||||||
|  |       <!-- Deleted Items Section --> | ||||||
|  |       <div class="deleted-items-section" *ngIf="deletedItems.length > 0"> | ||||||
|  |         <h3>Deleted Items</h3> | ||||||
|  |         <div class="deleted-items-list"> | ||||||
|  |           <div *ngFor="let item of deletedItems" class="deleted-item"> | ||||||
|  |             <span>{{ item.name }}</span> | ||||||
|  |             <button class="btn btn-sm btn-primary" (click)="restoreItem(item)"> | ||||||
|  |               <clr-icon shape="undo"></clr-icon> Restore | ||||||
|  |             </button> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <button class="btn btn-sm btn-danger" (click)="clearDeletedItems()"> | ||||||
|  |           <clr-icon shape="trash"></clr-icon> Clear All | ||||||
|  |         </button> | ||||||
|       </div> |       </div> | ||||||
|        |        | ||||||
|       <!-- Dashboard Grid with Drag and Drop --> |       <!-- Dashboard Grid with Drag and Drop --> | ||||||
|       <gridster [options]="options" style="background-color: transparent;"> |       <!-- <div class="drop-zone-indicator" *ngIf="dashboard.length === 0"> | ||||||
|  |         <p>Drag components here from the sidebar</p> | ||||||
|  |       </div> --> | ||||||
|  |       <gridster [options]="options" (drop)="onDrop($event)" style="background-color: transparent; min-height: 500px;"> | ||||||
|         <gridster-item [item]="item" *ngFor="let item of dashboard"> |         <gridster-item [item]="item" *ngFor="let item of dashboard"> | ||||||
|           <!-- Remove Button --> |           <!-- Remove Button --> | ||||||
|           <button class="btn btn-icon btn-danger" style="margin-left: 10px; margin-top: 10px;" (click)="removeItem(item)"> |           <button class="btn btn-icon btn-danger" style="margin-left: 10px; margin-top: 10px;" (click)="removeItem(item)"> | ||||||
| @ -36,10 +73,10 @@ | |||||||
|             <div *ngIf="item.chartType === 'bar-chart'"> |             <div *ngIf="item.chartType === 'bar-chart'"> | ||||||
|               <app-shield-bar-chart></app-shield-bar-chart> |               <app-shield-bar-chart></app-shield-bar-chart> | ||||||
|             </div> |             </div> | ||||||
|             <div *ngIf="item.chartType === 'donut-chart' && item.name === 'End Customer'"> |             <div *ngIf="item.chartType === 'donut-chart' && item.name === 'End Customer Donut'"> | ||||||
|               <app-shield-donut-chart chartType="endCustomer"></app-shield-donut-chart> |               <app-shield-donut-chart chartType="endCustomer"></app-shield-donut-chart> | ||||||
|             </div> |             </div> | ||||||
|             <div *ngIf="item.chartType === 'donut-chart' && item.name === 'Segment Penetration'"> |             <div *ngIf="item.chartType === 'donut-chart' && item.name === 'Segment Penetration Donut'"> | ||||||
|               <app-shield-donut-chart chartType="segmentPenetration"></app-shield-donut-chart> |               <app-shield-donut-chart chartType="segmentPenetration"></app-shield-donut-chart> | ||||||
|             </div> |             </div> | ||||||
|             <div *ngIf="item.chartType === 'map-chart'"> |             <div *ngIf="item.chartType === 'map-chart'"> | ||||||
|  | |||||||
| @ -59,6 +59,42 @@ | |||||||
|   border-top: 4px solid #0f9d58; |   border-top: 4px solid #0f9d58; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .deleted-items-section { | ||||||
|  |   background: white; | ||||||
|  |   border-radius: 4px; | ||||||
|  |   box-shadow: 0 2px 4px rgba(0,0,0,0.1); | ||||||
|  |   padding: 20px; | ||||||
|  |    | ||||||
|  |   h3 { | ||||||
|  |     margin-top: 0; | ||||||
|  |     color: #333; | ||||||
|  |     border-bottom: 1px solid #eee; | ||||||
|  |     padding-bottom: 10px; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .deleted-items-list { | ||||||
|  |     display: flex; | ||||||
|  |     flex-wrap: wrap; | ||||||
|  |     gap: 10px; | ||||||
|  |     margin-bottom: 15px; | ||||||
|  |      | ||||||
|  |     .deleted-item { | ||||||
|  |       display: flex; | ||||||
|  |       align-items: center; | ||||||
|  |       gap: 10px; | ||||||
|  |       background: #f8f9fa; | ||||||
|  |       border: 1px solid #e9ecef; | ||||||
|  |       border-radius: 4px; | ||||||
|  |       padding: 8px 12px; | ||||||
|  |        | ||||||
|  |       span { | ||||||
|  |         font-size: 14px; | ||||||
|  |         color: #495057; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .dashboard-grid { | .dashboard-grid { | ||||||
|   display: grid; |   display: grid; | ||||||
|   grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); |   grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); | ||||||
| @ -81,6 +117,23 @@ | |||||||
|   cursor: move; |   cursor: move; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .drop-zone-indicator { | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  |   min-height: 500px; | ||||||
|  |   background-color: #f8f9fa; | ||||||
|  |   border: 2px dashed #dee2e6; | ||||||
|  |   border-radius: 4px; | ||||||
|  |   color: #6c757d; | ||||||
|  |   font-size: 18px; | ||||||
|  |   text-align: center; | ||||||
|  |    | ||||||
|  |   p { | ||||||
|  |     margin: 0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Gridster specific styles */ | /* Gridster specific styles */ | ||||||
| gridster { | gridster { | ||||||
|   background: transparent !important; |   background: transparent !important; | ||||||
|  | |||||||
| @ -5,6 +5,12 @@ interface ShieldDashboardItem extends GridsterItem { | |||||||
|   chartType: string; |   chartType: string; | ||||||
|   name: string; |   name: string; | ||||||
|   id: number; |   id: number; | ||||||
|  |   component?: any; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface WidgetModel { | ||||||
|  |   name: string; | ||||||
|  |   identifier: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
| @ -15,6 +21,38 @@ interface ShieldDashboardItem extends GridsterItem { | |||||||
| export class ShieldDashboardComponent implements OnInit { | export class ShieldDashboardComponent implements OnInit { | ||||||
|   options: GridsterConfig; |   options: GridsterConfig; | ||||||
|   dashboard: Array<ShieldDashboardItem>; |   dashboard: Array<ShieldDashboardItem>; | ||||||
|  |    | ||||||
|  |   // Component palette
 | ||||||
|  |   showComponentPalette = false; | ||||||
|  |   WidgetsMock: WidgetModel[] = [ | ||||||
|  |     { | ||||||
|  |       name: 'Bar Chart', | ||||||
|  |       identifier: 'bar_chart' | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'Doughnut Chart', | ||||||
|  |       identifier: 'doughnut_chart' | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'Map Chart', | ||||||
|  |       identifier: 'map_chart' | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'Data Table', | ||||||
|  |       identifier: 'grid_view' | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'Deal Details', | ||||||
|  |       identifier: 'to_do_chart' | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'Quarterwise Flow', | ||||||
|  |       identifier: 'line_chart' | ||||||
|  |     } | ||||||
|  |   ]; | ||||||
|  |    | ||||||
|  |   // Keep track of deleted items
 | ||||||
|  |   deletedItems: Array<ShieldDashboardItem> = []; | ||||||
| 
 | 
 | ||||||
|   constructor() { } |   constructor() { } | ||||||
| 
 | 
 | ||||||
| @ -22,7 +60,7 @@ export class ShieldDashboardComponent implements OnInit { | |||||||
|     this.options = { |     this.options = { | ||||||
|       gridType: 'fit', |       gridType: 'fit', | ||||||
|       enableEmptyCellDrop: true, |       enableEmptyCellDrop: true, | ||||||
|       emptyCellDropCallback: this.onDrop.bind(this), |       emptyCellDropCallback: this.onDrop, | ||||||
|       pushItems: true, |       pushItems: true, | ||||||
|       swap: true, |       swap: true, | ||||||
|       pushDirections: { north: true, east: true, south: true, west: true }, |       pushDirections: { north: true, east: true, south: true, west: true }, | ||||||
| @ -41,26 +79,164 @@ export class ShieldDashboardComponent implements OnInit { | |||||||
|       itemResizeCallback: this.itemResize.bind(this) |       itemResizeCallback: this.itemResize.bind(this) | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // Initialize the dashboard with default components
 |     // Initialize the dashboard with empty canvas
 | ||||||
|     this.dashboard = [ |     this.dashboard = []; | ||||||
|       { cols: 5, rows: 6, y: 0, x: 0, chartType: 'bar-chart', name: 'Bar Chart', id: 1 }, |   } | ||||||
|       { cols: 5, rows: 6, y: 0, x: 5, chartType: 'donut-chart', name: 'End Customer', id: 2 }, |    | ||||||
|       { cols: 5, rows: 6, y: 6, x: 0, chartType: 'donut-chart', name: 'Segment Penetration', id: 3 }, |   // Toggle component palette visibility
 | ||||||
|       { cols: 5, rows: 6, y: 6, x: 5, chartType: 'map-chart', name: 'Map Chart', id: 4 }, |   toggleComponentPalette(): void { | ||||||
|       { cols: 10, rows: 6, y: 12, x: 0, chartType: 'data-table', name: 'Data Table', id: 5 }, |     this.showComponentPalette = !this.showComponentPalette; | ||||||
|       { cols: 5, rows: 6, y: 18, x: 0, chartType: 'deal-details', name: 'Deal Details', id: 6 }, |   } | ||||||
|       { cols: 5, rows: 6, y: 18, x: 5, chartType: 'quarterwise-flow', name: 'Quarterwise Flow', id: 7 } |    | ||||||
|     ]; |   // Handle drag start event for components - matching the working implementation
 | ||||||
|  |   onDrag(event: DragEvent, identifier: string): void { | ||||||
|  |     console.log("on drag", identifier); | ||||||
|  |     console.log("on drag ", event); | ||||||
|  |     if (event.dataTransfer) { | ||||||
|  |       event.dataTransfer.setData('widgetIdentifier', identifier); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onDrop(event: any) { |   onDrop(ev: any) { | ||||||
|     // Handle dropping new components onto the dashboard
 |     // Handle dropping new components onto the dashboard
 | ||||||
|     console.log('Item dropped:', event); |     console.log('Item dropped:', ev); | ||||||
|  |      | ||||||
|  |     // Get the component identifier from the drag event
 | ||||||
|  |     const componentType = ev.dataTransfer ? ev.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) { |   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); |     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() { |   itemChange() { | ||||||
|     console.log('Item changed:', this.dashboard); |     console.log('Item changed:', this.dashboard); | ||||||
| @ -71,4 +247,25 @@ export class ShieldDashboardComponent implements OnInit { | |||||||
|     // Trigger a window resize event to notify charts to resize
 |     // Trigger a window resize event to notify charts to resize
 | ||||||
|     window.dispatchEvent(new Event('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; | ||||||
|  |   } | ||||||
| } | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user