Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/.idea/workspace.xml
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/xml
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/djproject/urls.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/djproject/urls.py	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/djproject/urls.py	(working copy)
@@ -17,7 +17,8 @@
 from hive.log_location import handle_log_location_app
 from hive.controller.device_metrics import handle_device_metrics_req
 from hive.controller.slb_stats import handle_slb_stats_req
-from hive.controller.slb_vpn_stats import handle_ssl_vpn_stats_req
+from hive.controller.ssl_vpn_stats import handle_ssl_vpn_stats_req
+from hive.controller.sslvpn_metrics import handle_get_top_ssl_vpn_metrics
 from hive.llb_stats import handle_llb_stats_req
 from hive.report.generate_report import handle_report_generation
 from hive.controller.backup_controller import handle_backup_req
@@ -116,6 +117,7 @@
     re_path(r'^top-apv-virtual-metrics$', handle_get_top_apv_virtual_services_metrics),
     re_path(r'^top-apv-real-metrics$', handle_get_top_apv_real_services_metrics),
     re_path(r'^top-apv-llb-metrics$', handle_get_top_llb_metrics),
+    re_path(r'^top-ssl-vpn-metrics$', handle_get_top_ssl_vpn_metrics),
 ]
 
 handler404 = 'hive.shared.custom_404_view'
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-apv/dashboard-insights-apv.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-apv/dashboard-insights-apv.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-apv/dashboard-insights-apv.html	(working copy)
@@ -12,25 +12,9 @@
                 <span class="card-title-text">Hits - Top 5</span>
               </mat-card-title>
             </div>
-            <table mat-table [dataSource]="vSHitsDataSource" class="mat-elevation-z1 small-table">
-              <ng-container matColumnDef="serial">
-                <th mat-header-cell *matHeaderCellDef> Top</th>
-                <td mat-cell *matCellDef="let element; let i = index;"> {{ i + 1 }}</td>
-              </ng-container>
-              <ng-container matColumnDef="name">
-                <th mat-header-cell *matHeaderCellDef> Service Name</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.name }}</td>
-              </ng-container>
-              <ng-container matColumnDef="hits">
-                <th mat-header-cell *matHeaderCellDef> Hits</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.value }}</td>
-              </ng-container>
-              <tr mat-header-row *matHeaderRowDef="hitsColumns"></tr>
-              <tr mat-row *matRowDef="let row; columns: hitsColumns;"></tr>
-              <tr class="mat-row table-no-data" *matNoDataRow>
-                <td class="mat-cell" colspan="3">No results found.</td>
-              </tr>
-            </table>
+            <div class="chart-flex-container">
+              <div echarts [options]="vSHitsChartOption" class="chart-container"></div>
+            </div>
           </mat-card-content>
         </mat-card>
       </mat-grid-tile>
@@ -42,25 +26,9 @@
                 <span class="card-title-text">Open Connections - Top 5</span>
               </mat-card-title>
             </div>
-            <table mat-table [dataSource]="vSConnectionsDataSource" class="mat-elevation-z1 small-table">
-              <ng-container matColumnDef="serial">
-                <th mat-header-cell *matHeaderCellDef> Top</th>
-                <td mat-cell *matCellDef="let element; let i = index;"> {{ i + 1 }}</td>
-              </ng-container>
-              <ng-container matColumnDef="name">
-                <th mat-header-cell *matHeaderCellDef> Service Name</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.name }}</td>
-              </ng-container>
-              <ng-container matColumnDef="connections">
-                <th mat-header-cell *matHeaderCellDef> Connections</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.value }}</td>
-              </ng-container>
-              <tr mat-header-row *matHeaderRowDef="connectionsColumns"></tr>
-              <tr mat-row *matRowDef="let row; columns: connectionsColumns;"></tr>
-              <tr class="mat-row table-no-data" *matNoDataRow>
-                <td class="mat-cell" colspan="3">No results found.</td>
-              </tr>
-            </table>
+            <div class="chart-flex-container">
+              <div echarts [options]="vSConnectionsChartOption" class="chart-container"></div>
+            </div>
           </mat-card-content>
         </mat-card>
       </mat-grid-tile>
@@ -72,35 +40,9 @@
                 <span class="card-title-text">Throughput - Top 5</span>
               </mat-card-title>
             </div>
-            <table mat-table [dataSource]="vSThroughputDataSource" class="mat-elevation-z1 small-table">
-              <ng-container matColumnDef="serial">
-                <th mat-header-cell *matHeaderCellDef> Top</th>
-                <td mat-cell *matCellDef="let element; let i = index;"> {{ i + 1 }}</td>
-              </ng-container>
-              <ng-container matColumnDef="name">
-                <th mat-header-cell *matHeaderCellDef> Service Name</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.name }}</td>
-              </ng-container>
-              <ng-container matColumnDef="inboundMbps">
-                <th mat-header-cell *matHeaderCellDef> Received (Mbps)</th>
-                <td mat-cell *matCellDef="let element">
-                  <fa-icon [icon]="['fas', 'arrow-down-long']" class="delete-icon"></fa-icon>
-                  {{ element?.inboundMbps }}
-                </td>
-              </ng-container>
-              <ng-container matColumnDef="outboundMbps">
-                <th mat-header-cell *matHeaderCellDef> Sent (Mbps)</th>
-                <td mat-cell *matCellDef="let element">
-                  <fa-icon [icon]="['fas', 'arrow-up-long']" class="success-icon"></fa-icon>
-                  {{ element?.outboundMbps }}
-                </td>
-              </ng-container>
-              <tr mat-header-row *matHeaderRowDef="throughputColumns"></tr>
-              <tr mat-row *matRowDef="let row; columns: throughputColumns;"></tr>
-              <tr class="mat-row table-no-data" *matNoDataRow>
-                <td class="mat-cell" colspan="4">No results found.</td>
-              </tr>
-            </table>
+            <div class="chart-flex-container">
+              <div echarts [options]="vSThroughputChartOption" class="chart-container"></div>
+            </div>
           </mat-card-content>
         </mat-card>
       </mat-grid-tile>
@@ -121,25 +63,9 @@
                 <span class="card-title-text">Hits - Top 5</span>
               </mat-card-title>
             </div>
-            <table mat-table [dataSource]="rSHitsDataSource" class="mat-elevation-z1 small-table">
-              <ng-container matColumnDef="serial">
-                <th mat-header-cell *matHeaderCellDef> Top</th>
-                <td mat-cell *matCellDef="let element; let i = index;"> {{ i + 1 }}</td>
-              </ng-container>
-              <ng-container matColumnDef="name">
-                <th mat-header-cell *matHeaderCellDef> Service Name</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.name }}</td>
-              </ng-container>
-              <ng-container matColumnDef="hits">
-                <th mat-header-cell *matHeaderCellDef> Hits</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.value }}</td>
-              </ng-container>
-              <tr mat-header-row *matHeaderRowDef="hitsColumns"></tr>
-              <tr mat-row *matRowDef="let row; columns: hitsColumns;"></tr>
-              <tr class="mat-row table-no-data" *matNoDataRow>
-                <td class="mat-cell" colspan="3">No results found.</td>
-              </tr>
-            </table>
+            <div class="chart-flex-container">
+              <div echarts [options]="rSHitsChartOption" class="chart-container"></div>
+            </div>
           </mat-card-content>
         </mat-card>
       </mat-grid-tile>
@@ -151,25 +77,9 @@
                 <span class="card-title-text">Open Connections - Top 5</span>
               </mat-card-title>
             </div>
-            <table mat-table [dataSource]="rSConnectionsDataSource" class="mat-elevation-z1 small-table">
-              <ng-container matColumnDef="serial">
-                <th mat-header-cell *matHeaderCellDef> Top</th>
-                <td mat-cell *matCellDef="let element; let i = index;"> {{ i + 1 }}</td>
-              </ng-container>
-              <ng-container matColumnDef="name">
-                <th mat-header-cell *matHeaderCellDef> Service Name</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.name }}</td>
-              </ng-container>
-              <ng-container matColumnDef="connections">
-                <th mat-header-cell *matHeaderCellDef> Connections</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.value }}</td>
-              </ng-container>
-              <tr mat-header-row *matHeaderRowDef="connectionsColumns"></tr>
-              <tr mat-row *matRowDef="let row; columns: connectionsColumns;"></tr>
-              <tr class="mat-row table-no-data" *matNoDataRow>
-                <td class="mat-cell" colspan="3">No results found.</td>
-              </tr>
-            </table>
+            <div class="chart-flex-container">
+              <div echarts [options]="rSConnectionsChartOption" class="chart-container"></div>
+            </div>
           </mat-card-content>
         </mat-card>
       </mat-grid-tile>
@@ -181,35 +91,9 @@
                 <span class="card-title-text">Throughput - Top 5</span>
               </mat-card-title>
             </div>
-            <table mat-table [dataSource]="rSThroughputDataSource" class="mat-elevation-z1 small-table">
-              <ng-container matColumnDef="serial">
-                <th mat-header-cell *matHeaderCellDef> Top</th>
-                <td mat-cell *matCellDef="let element; let i = index;"> {{ i + 1 }}</td>
-              </ng-container>
-              <ng-container matColumnDef="name">
-                <th mat-header-cell *matHeaderCellDef> Service Name</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.name }}</td>
-              </ng-container>
-              <ng-container matColumnDef="inboundMbps">
-                <th mat-header-cell *matHeaderCellDef> Received (Mbps)</th>
-                <td mat-cell *matCellDef="let element">
-                  <fa-icon [icon]="['fas', 'arrow-down-long']" class="delete-icon"></fa-icon>
-                  {{ element?.inboundMbps }}
-                </td>
-              </ng-container>
-              <ng-container matColumnDef="outboundMbps">
-                <th mat-header-cell *matHeaderCellDef> Sent (Mbps)</th>
-                <td mat-cell *matCellDef="let element">
-                  <fa-icon [icon]="['fas', 'arrow-up-long']" class="success-icon"></fa-icon>
-                  {{ element?.outboundMbps }}
-                </td>
-              </ng-container>
-              <tr mat-header-row *matHeaderRowDef="throughputColumns"></tr>
-              <tr mat-row *matRowDef="let row; columns: throughputColumns;"></tr>
-              <tr class="mat-row table-no-data" *matNoDataRow>
-                <td class="mat-cell" colspan="4">No results found.</td>
-              </tr>
-            </table>
+            <div class="chart-flex-container">
+              <div echarts [options]="rSThroughputChartOption" class="chart-container"></div>
+            </div>
           </mat-card-content>
         </mat-card>
       </mat-grid-tile>
@@ -230,25 +114,9 @@
                 <span class="card-title-text">Hits - Top 5</span>
               </mat-card-title>
             </div>
-            <table mat-table [dataSource]="llbHitsDataSource" class="mat-elevation-z1 small-table">
-              <ng-container matColumnDef="serial">
-                <th mat-header-cell *matHeaderCellDef> Top</th>
-                <td mat-cell *matCellDef="let element; let i = index;"> {{ i + 1 }}</td>
-              </ng-container>
-              <ng-container matColumnDef="name">
-                <th mat-header-cell *matHeaderCellDef> Service Name</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.name }}</td>
-              </ng-container>
-              <ng-container matColumnDef="hits">
-                <th mat-header-cell *matHeaderCellDef> Hits</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.value }}</td>
-              </ng-container>
-              <tr mat-header-row *matHeaderRowDef="hitsColumns"></tr>
-              <tr mat-row *matRowDef="let row; columns: hitsColumns;"></tr>
-              <tr class="mat-row table-no-data" *matNoDataRow>
-                <td class="mat-cell" colspan="3">No results found.</td>
-              </tr>
-            </table>
+            <div class="chart-flex-container">
+              <div echarts [options]="lLBHitsChartOption" class="chart-container"></div>
+            </div>
           </mat-card-content>
         </mat-card>
       </mat-grid-tile>
@@ -260,25 +128,9 @@
                 <span class="card-title-text">Open Connections - Top 5</span>
               </mat-card-title>
             </div>
-            <table mat-table [dataSource]="llbConnectionsDataSource" class="mat-elevation-z1 small-table">
-              <ng-container matColumnDef="serial">
-                <th mat-header-cell *matHeaderCellDef> Top</th>
-                <td mat-cell *matCellDef="let element; let i = index;"> {{ i + 1 }}</td>
-              </ng-container>
-              <ng-container matColumnDef="name">
-                <th mat-header-cell *matHeaderCellDef> Service Name</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.name }}</td>
-              </ng-container>
-              <ng-container matColumnDef="connections">
-                <th mat-header-cell *matHeaderCellDef> Connections</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.value }}</td>
-              </ng-container>
-              <tr mat-header-row *matHeaderRowDef="connectionsColumns"></tr>
-              <tr mat-row *matRowDef="let row; columns: connectionsColumns;"></tr>
-              <tr class="mat-row table-no-data" *matNoDataRow>
-                <td class="mat-cell" colspan="3">No results found.</td>
-              </tr>
-            </table>
+            <div class="chart-flex-container">
+              <div echarts [options]="lLBConnectionsChartOption" class="chart-container"></div>
+            </div>
           </mat-card-content>
         </mat-card>
       </mat-grid-tile>
@@ -290,35 +142,9 @@
                 <span class="card-title-text">Throughput - Top 5</span>
               </mat-card-title>
             </div>
-            <table mat-table [dataSource]="llbThroughputDataSource" class="mat-elevation-z1 small-table">
-              <ng-container matColumnDef="serial">
-                <th mat-header-cell *matHeaderCellDef> Top</th>
-                <td mat-cell *matCellDef="let element; let i = index;"> {{ i + 1 }}</td>
-              </ng-container>
-              <ng-container matColumnDef="name">
-                <th mat-header-cell *matHeaderCellDef> Service Name</th>
-                <td mat-cell *matCellDef="let element"> {{ element?.name }}</td>
-              </ng-container>
-              <ng-container matColumnDef="inboundMbps">
-                <th mat-header-cell *matHeaderCellDef> Received (Mbps)</th>
-                <td mat-cell *matCellDef="let element">
-                  <fa-icon [icon]="['fas', 'arrow-down-long']" class="delete-icon"></fa-icon>
-                  {{ element?.inboundMbps }}
-                </td>
-              </ng-container>
-              <ng-container matColumnDef="outboundMbps">
-                <th mat-header-cell *matHeaderCellDef> Sent (Mbps)</th>
-                <td mat-cell *matCellDef="let element">
-                  <fa-icon [icon]="['fas', 'arrow-up-long']" class="success-icon"></fa-icon>
-                  {{ element?.outboundMbps }}
-                </td>
-              </ng-container>
-              <tr mat-header-row *matHeaderRowDef="throughputColumns"></tr>
-              <tr mat-row *matRowDef="let row; columns: throughputColumns;"></tr>
-              <tr class="mat-row table-no-data" *matNoDataRow>
-                <td class="mat-cell" colspan="4">No results found.</td>
-              </tr>
-            </table>
+            <div class="chart-flex-container">
+              <div echarts [options]="lLBThroughputChartOption" class="chart-container"></div>
+            </div>
           </mat-card-content>
         </mat-card>
       </mat-grid-tile>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-apv/dashboard-insights-apv.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-apv/dashboard-insights-apv.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-apv/dashboard-insights-apv.ts	(working copy)
@@ -10,6 +10,8 @@
 import {take} from 'rxjs/operators';
 import {DeviceService} from '../../../services/device-service';
 import {MatTableDataSource} from '@angular/material/table';
+import {ChartOptions} from '../../../services/chart-options';
+import {UtilsService} from '../../../services/utils-service';
 
 @Component({
   selector: 'app-dashboard-insights-apv',
@@ -21,26 +23,30 @@
 })
 export class DashboardInsightsApv implements OnInit, OnChanges {
 
-  hitsColumns: string[] = ['serial', 'name', 'hits'];
-  connectionsColumns: string[] = ['serial', 'name', 'connections'];
-  throughputColumns: string[] = ['serial', 'name', 'inboundMbps', 'outboundMbps'];
+  vSHitsChartOption: EChartsOption = {};
+  vSConnectionsChartOption: EChartsOption = {};
+  vSThroughputChartOption: EChartsOption = {};
 
-  vSHitsDataSource: MatTableDataSource<any> = new MatTableDataSource();
-  vSConnectionsDataSource: MatTableDataSource<any> = new MatTableDataSource();
-  vSThroughputDataSource: MatTableDataSource<any> = new MatTableDataSource();
+  rSHitsChartOption: EChartsOption = {};
+  rSConnectionsChartOption: EChartsOption = {};
+  rSThroughputChartOption: EChartsOption = {};
 
-  rSHitsDataSource: MatTableDataSource<any> = new MatTableDataSource();
-  rSConnectionsDataSource: MatTableDataSource<any> = new MatTableDataSource();
-  rSThroughputDataSource: MatTableDataSource<any> = new MatTableDataSource();
+  lLBHitsChartOption: EChartsOption = {};
+  lLBConnectionsChartOption: EChartsOption = {};
+  lLBThroughputChartOption: EChartsOption = {};
 
-  llbHitsDataSource: MatTableDataSource<any> = new MatTableDataSource();
-  llbConnectionsDataSource: MatTableDataSource<any> = new MatTableDataSource();
-  llbThroughputDataSource: MatTableDataSource<any> = new MatTableDataSource();
+  devices: any[] = [];
+  groups: any[] = [];
+  virtualServiceMetrics: any = [];
+  realServiceMetrics: any = [];
+  llbMetrics: any = [];
 
   constructor(
     private _notification: NotificationService,
     private _system: SystemService,
     private _device: DeviceService,
+    private _chartOptions: ChartOptions,
+    private _utils: UtilsService
   ) {
   }
 
@@ -51,13 +57,9 @@
   }
 
   ngOnChanges(changes: SimpleChanges): void {
-    if (changes['cpuUsagePercentage']) {
-    }
+    if (changes['cpuUsagePercentage']) {}
   }
 
-  devices: any[] = [];
-  groups: any[] = [];
-
   getDeviceGroups(): void {
     this.devices = [];
     this.groups = [];
@@ -91,24 +93,20 @@
       })
   }
 
-  virtualServiceMetrics: any = [];
-
   getTopAPVVirtualServicesMetrics() {
     let payload = {}
     this._system.getTopAPVVirtualServicesMetrics(payload)
       .pipe(take(1))
       .subscribe({
         next: (result: any) => {
-          this.devices.forEach((_device: any) => {
-            result.forEach((_metric: any) => {
-              if (_metric?.agent_host === _device?.ip) {
-                _metric.device_name = _device?.name;
-                _metric.device_type = _device?.type;
-              }
-            })
-          })
-          this.virtualServiceMetrics = result;
-          this.updateVirtualServicesMetricsCharts();
+          this.virtualServiceMetrics = this._device.mapDeviceDetails(this.devices, result);
+          this.updateMetricsCharts(
+            ['connections', 'hits', 'network'],
+            this.virtualServiceMetrics,
+            'vSHitsChartOption',
+            'vSConnectionsChartOption',
+            'vSThroughputChartOption'
+          )
         },
         error: (error: { message: string; }) => {
           console.log(error);
@@ -117,24 +115,20 @@
       });
   }
 
-  realServiceMetrics: any = [];
-
   getTopAPVRealServicesMetrics() {
     let payload = {}
     this._system.getTopAPVRealServicesMetrics(payload)
       .pipe(take(1))
       .subscribe({
         next: (result: any) => {
-          this.devices.forEach((_device: any) => {
-            result.forEach((_metric: any) => {
-              if (_metric?.agent_host === _device?.ip) {
-                _metric.device_name = _device?.name;
-                _metric.device_type = _device?.type;
-              }
-            })
-          })
-          this.realServiceMetrics = result;
-          this.updateRealServicesMetricsCharts();
+          this.realServiceMetrics = this._device.mapDeviceDetails(this.devices, result);
+          this.updateMetricsCharts(
+            ['connections', 'hits', 'network'],
+            this.realServiceMetrics,
+            'rSHitsChartOption',
+            'rSConnectionsChartOption',
+            'rSThroughputChartOption'
+          )
         },
         error: (error: { message: string; }) => {
           console.log(error);
@@ -143,25 +137,20 @@
       });
   }
 
-  llbMetrics: any = [];
-
   getTopAPVLLBMetrics() {
     let payload = {}
     this._system.getTopAPVLLBMetrics(payload)
       .pipe(take(1))
       .subscribe({
         next: (result: any) => {
-          this.devices.forEach((_device: any) => {
-            result.forEach((_metric: any) => {
-              if (_metric?.agent_host === _device?.ip) {
-                _metric.device_name = _device?.name;
-                _metric.service_name = _metric?.link_name;
-                _metric.device_type = _device?.type;
-              }
-            })
-          })
-          this.llbMetrics = result;
-          this.updateLLBMetricsCharts();
+          this.llbMetrics = this._device.mapDeviceDetails(this.devices, result);
+          this.updateMetricsCharts(
+            ['connections', 'hits', 'network'],
+            this.llbMetrics,
+            'lLBHitsChartOption',
+            'lLBConnectionsChartOption',
+            'lLBThroughputChartOption'
+          )
         },
         error: (error: { message: string; }) => {
           console.log(error);
@@ -170,91 +159,11 @@
       });
   }
 
-  convertData(rawData: any): any {
-    const connections = rawData
-      .filter((item: any) => item.metric === 'connections')
-      .map((item: any) => ({name: `${item.service_name} (${item.device_name})`, value: item.value}));
-
-    const hits = rawData
-      .filter((item: any) => item.metric === 'hits' || item.metric === 'requests')
-      .map((item: any) => ({name: `${item.service_name} (${item.device_name})`, value: item.value}));
-
-    const network = rawData
-      .filter((item: any) => item.metric === 'network')
-      .map((item: any) => ({
-        name: `${item.service_name} (${item.device_name})`,
-        inbound: item.received / (1024 * 1024),
-        outbound: item.sent / (1024 * 1024),
-      }));
-
-    return {
-      connections: connections,
-      hits: hits,
-      network: network,
-    };
+  updateMetricsCharts(metricLabels: any, metrics: any, hitsKey: any, connectionsKey: any, throughputKey: any): void {
+    let _data: any = this._utils.formatChartData(metricLabels, metrics);
+    (this as any)[connectionsKey] = this._chartOptions.distributionChartOptions('Connections', _data.connections);
+    (this as any)[hitsKey] = this._chartOptions.distributionChartOptions('Hits', _data.connections);
+    const {names, inbound, outbound} = _data?.network || {};
+    (this as any)[throughputKey] = this._chartOptions.throughputChartOptions(names, inbound, outbound);
   }
-
-  private updateVirtualServicesMetricsCharts(): void {
-    let _data: any = this.convertData(this.virtualServiceMetrics);
-
-    _data?.connections.sort((a: any, b: any) => b.value - a.value);
-    this.vSConnectionsDataSource.data = _data?.connections
-    _data?.hits.sort((a: any, b: any) => b.value - a.value);
-    this.vSHitsDataSource.data = _data?.hits
-
-    _data?.network.sort((a: any, b: any) => (b.inbound + b.outbound) - (a.inbound + a.outbound));
-    const sortedNames = _data?.network.map((item: any) => item.name);
-    const sortedInbound = _data?.network.map((item: any) => item.inbound);
-    const sortedOutbound = _data?.network.map((item: any) => item.outbound);
-
-    this.vSThroughputDataSource.data = sortedNames.map((name: any, index: any) => {
-      return {
-        name: name,
-        inboundMbps: sortedInbound[index],
-        outboundMbps: sortedOutbound[index]
-      };
-    });
-  }
-
-  private updateRealServicesMetricsCharts(): void {
-    let _data: any = this.convertData(this.realServiceMetrics);
-
-    _data?.connections.sort((a: any, b: any) => b.value - a.value);
-    this.rSConnectionsDataSource.data = _data?.connections
-    _data?.hits.sort((a: any, b: any) => b.value - a.value);
-    this.rSHitsDataSource.data = _data?.hits
-    _data?.network.sort((a: any, b: any) => (b.inbound + b.outbound) - (a.inbound + a.outbound));
-    const sortedNames = _data?.network.map((item: any) => item.name);
-    const sortedInbound = _data?.network.map((item: any) => item.inbound);
-    const sortedOutbound = _data?.network.map((item: any) => item.outbound);
-
-    this.rSThroughputDataSource.data = sortedNames.map((name: any, index: any) => {
-      return {
-        name: name,
-        inboundMbps: sortedInbound[index],
-        outboundMbps: sortedOutbound[index]
-      };
-    });
-  }
-
-  private updateLLBMetricsCharts(): void {
-    let _data: any = this.convertData(this.llbMetrics);
-
-    _data?.connections.sort((a: any, b: any) => b.value - a.value);
-    this.llbConnectionsDataSource.data = _data?.connections
-    _data?.hits.sort((a: any, b: any) => b.value - a.value);
-    this.llbHitsDataSource.data = _data?.hits
-    _data?.network.sort((a: any, b: any) => (b.inbound + b.outbound) - (a.inbound + a.outbound));
-    const sortedNames = _data?.network.map((item: any) => item.name);
-    const sortedInbound = _data?.network.map((item: any) => item.inbound);
-    const sortedOutbound = _data?.network.map((item: any) => item.outbound);
-
-    this.llbThroughputDataSource.data = sortedNames.map((name: any, index: any) => {
-      return {
-        name: name,
-        inboundMbps: sortedInbound[index],
-        outboundMbps: sortedOutbound[index]
-      };
-    });
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-devices/dashboard-insights-devices.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-devices/dashboard-insights-devices.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-devices/dashboard-insights-devices.ts	(working copy)
@@ -5,17 +5,9 @@
 import {DeviceService} from '../../../services/device-service';
 import {SystemService} from '../../../services/system-service';
 import {NotificationService} from '../../../services/notification';
+import {UtilsService} from '../../../services/utils-service';
+import {ChartOptions} from '../../../services/chart-options';
 
-export interface DeviceTypeStatus {
-  connected: number;
-  disconnected: number;
-}
-
-export interface DeviceGraphData {
-  total: DeviceTypeStatus;
-  types: Map<string, DeviceTypeStatus>;
-}
-
 @Component({
   selector: 'app-dashboard-insights-devices',
   imports: [
@@ -31,12 +23,22 @@
   devicesThroughputChartOption1: EChartsOption = {};
   connectionChartOption1: EChartsOption = {};
 
+  devices: any[] = [];
+  groups: any[] = [];
+  deviceStats: any = {}
+  deviceConnectionStats: any = {}
+  connectedDevices: number = 0;
+  connectedDevicesMetrics: any = [];
+
   constructor(
     private _device: DeviceService,
     private _system: SystemService,
     private _notification: NotificationService,
+    private _utils: UtilsService,
+    private _chartOptions: ChartOptions
   ) {
   }
+
   ngOnInit(): void {
     setTimeout(() => {
       this.getDeviceGroups();
@@ -44,17 +46,9 @@
   }
 
   ngOnChanges(changes: SimpleChanges): void {
-    if (changes['cpuUsagePercentage']) {
-      this.updateChartOption();
-    }
+    if (changes['cpuUsagePercentage']) {}
   }
 
-  devices: any[] = [];
-  groups: any[] = [];
-  deviceStats: any = {}
-  deviceConnectionStats: any = {}
-  connectedDevices: number = 0;
-
   getDeviceGroups(): void {
     this.devices = [];
     this.groups = [];
@@ -76,13 +70,13 @@
                   this.devices.push(device);
                 })
               })
-              this.deviceStats =  this.getDeviceGraphData(this.devices);
-              this.deviceConnectionStats = this.transformChartData(this.deviceStats.types.entries());
+              this.deviceStats = this._utils.transformTotalConnectedDevicesMetrics(this.devices);
+              this.deviceConnectionStats = this._utils.transformConnectedDevicesMetrics(this.deviceStats.types.entries());
               this.connectedDevices = this.devices.filter((device: any) => device?.connection === true).length;
               this.getConnectedDevicesMetrics();
             }
           }
-          this.updateChartOption();
+          this.connectionChartOption1 = this._chartOptions.connectedDeviceLineChart(this.deviceConnectionStats);
         }, error: (error: { message: string; }) => {
           console.log(error);
           this._notification.showError(error.message);
@@ -90,23 +84,13 @@
       })
   }
 
-  connectedDevicesMetrics: any = [];
-
   getConnectedDevicesMetrics() {
     let payload = {}
     this._system.getConnectedDevicesMetrics(payload)
       .pipe(take(1))
       .subscribe({
         next: (result: any) => {
-          this.devices.forEach((_device: any) => {
-            result.forEach((_metric: any) => {
-              if (_metric?.agent_host === _device?.ip) {
-                _metric.device_name = _device?.name;
-                _metric.device_type = _device?.type;
-              }
-            })
-          })
-          this.connectedDevicesMetrics = result;
+          this.connectedDevicesMetrics = this._device.mapDeviceDetails(this.devices, result);
           this.updateDeviceMetricsCharts();
         },
         error: (error: { message: string; }) => {
@@ -116,260 +100,12 @@
       });
   }
 
-  transformChartData(deviceData: [string, { connected: number, disconnected: number }][]) {
-    const yAxisData: string[] = [];
-    const connectedData: number[] = [];
-    const disconnectedData: number[] = [];
-
-    deviceData.forEach(([type, status]) => {
-      yAxisData.push(type);
-      connectedData.push(status.connected);
-      disconnectedData.push(status.disconnected);
-    });
-
-    return { yAxisData, connectedData, disconnectedData };
-  }
-
-  getDeviceGraphData(devices: any): DeviceGraphData {
-    const totalStatus: DeviceTypeStatus = { connected: 0, disconnected: 0 };
-    const typesStatus = new Map<string, DeviceTypeStatus>();
-
-    if (!devices || devices.length === 0) {
-      return { total: totalStatus, types: typesStatus };
-    }
-
-    devices.forEach((device: any) => {
-      // Update the total counts
-      if (device.connection) {
-        totalStatus.connected++;
-      } else {
-        totalStatus.disconnected++;
-      }
-
-      // Update the per-type counts
-      if (!typesStatus.has(device.type)) {
-        typesStatus.set(device.type, { connected: 0, disconnected: 0 });
-      }
-      const status = typesStatus.get(device.type)!;
-      if (device.connection) {
-        status.connected++;
-      } else {
-        status.disconnected++;
-      }
-    });
-
-    return { total: totalStatus, types: typesStatus };
-  }
-
-  convertData(): any {
-    const cpuData = this.connectedDevicesMetrics
-      .filter((item: any) => item.metric === 'cpu')
-      .map((item: any) => ({ name: item.device_name, value: item.value }));
-
-    const memoryData = this.connectedDevicesMetrics
-      .filter((item: any) => item.metric === 'memory')
-      .map((item: any) => ({ name: item.device_name, value: item.value }));
-
-    const throughputData = this.connectedDevicesMetrics
-      .filter((item: any) => item.metric === 'network')
-      .map((item: any) => ({
-        name: item.device_name,
-        inbound: item.total_in / (1024 * 1024),
-        outbound: item.total_out / (1024 * 1024),
-      }));
-
-    return {
-      cpu: cpuData,
-      memory: memoryData,
-      throughput: throughputData,
-    };
-  }
-
   private updateDeviceMetricsCharts(): void {
-    let _data: any = this.convertData();
-
-    _data?.cpu.sort((a: any, b: any) => b.value - a.value);
-    _data?.memory.sort((a: any, b: any) => b.value - a.value);
-    _data?.throughput.sort((a: any, b: any) => (b.inbound + b.outbound) - (a.inbound + a.outbound));
-    const sortedNames = _data?.throughput.map((item: any) => item.name);
-    const sortedInbound = _data?.throughput.map((item: any) => item.inbound);
-    const sortedOutbound = _data?.throughput.map((item: any) => item.outbound);
-
-    this.devicesCPUChartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      yAxis: {
-        type: 'value',
-        boundaryGap: [0, 0.01],
-        max: 100,
-        name: '%',
-        nameLocation: 'end',
-        nameGap: 20,
-        axisLabel: {
-          formatter: '{value}'
-        }
-      },
-      xAxis: {
-        type: 'category',
-        data: _data?.cpu.map((item: any) => item?.name),
-        inverse: false
-      },
-      series: [
-        {
-          name: 'CPU %',
-          type: 'bar',
-          data: _data?.cpu.map((item: any) => item?.value)
-        }
-      ]
-    };
-
-    this.devicesMemoryChartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      yAxis: {
-        type: 'value',
-        boundaryGap: [0, 0.01],
-        max: 100,
-        name: '%',
-        nameLocation: 'end',
-        nameGap: 20,
-        axisLabel: {
-          formatter: '{value}'
-        }
-      },
-      xAxis: {
-        type: 'category',
-        data: _data?.memory.map((item: any) => item?.name),
-        inverse: false
-      },
-      series: [
-        {
-          name: 'Memory %',
-          type: 'bar',
-          data: _data?.memory.map((item: any) => item?.value)
-        }
-      ]
-    };
-
-    this.devicesThroughputChartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        },
-        formatter: (params: any) => {
-          let tooltipContent = `${params[0].name}<br/>`;
-          params.forEach((param: any) => {
-            const valueInMbps = param.value.toFixed(2);
-            tooltipContent += `${param.marker} ${param.seriesName}: ${valueInMbps} Mbps<br/>`;
-          });
-          return tooltipContent;
-        }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      yAxis: {
-        type: 'value',
-        boundaryGap: [0, 0.01],
-        name: 'Mbps',
-        nameLocation: 'end',
-        nameGap: 20,
-        axisLabel: {
-          formatter: '{value}'
-        }
-      },
-      xAxis: {
-        type: 'category',
-        data: sortedNames,
-      },
-      series: [
-        {
-          name: 'Inbound',
-          type: 'bar',
-          data: sortedInbound
-        },
-        {
-          name: 'Outbound',
-          type: 'bar',
-          data: sortedOutbound
-        }
-      ]
-    };
+    let _data: any = this._utils.formatChartData(['cpu', 'memory', 'device_network'], this.connectedDevicesMetrics)
+    this.devicesCPUChartOption1 = this._chartOptions.lineChartOptions('CPU %', _data?.cpu);
+    this.devicesMemoryChartOption1 = this._chartOptions.lineChartOptions('Memory %', _data?.memory);
+    let {names, inbound, outbound} = _data?.network || {};
+    console.log(names, inbound, outbound);
+    this.devicesThroughputChartOption1 = this._chartOptions.throughputChartOptions(names, inbound, outbound)
   }
-
-  private updateChartOption() {
-
-    this.connectionChartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      legend: {
-        bottom: 'bottom'
-      },
-      yAxis: {
-        type: 'value',
-        axisLabel: {
-          formatter: function (value: number) {
-            if (value % 1 === 0) {
-              return String(value);
-            }
-            return '';
-          },
-        },
-      },
-      xAxis: {
-        type: 'category',
-        data: this.deviceConnectionStats?.yAxisData
-      },
-      series: [
-        {
-          name: 'Connected',
-          type: 'bar',
-          stack: 'total',
-          color: 'green',
-          label: {
-            show: true
-          },
-          emphasis: {
-            focus: 'series'
-          },
-          data: this.deviceConnectionStats?.connectedData
-        },
-        {
-          name: 'Disconnected',
-          type: 'bar',
-          stack: 'total',
-          label: {
-            show: true
-          },
-          color: 'red',
-          emphasis: {
-            focus: 'series'
-          },
-          data: this.deviceConnectionStats?.disconnectedData
-        }
-      ]
-    };
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.html	(working copy)
@@ -8,7 +8,7 @@
           </mat-card-title>
         </div>
         <div class="chart-flex-container">
-          <div echarts [options]="chartOption1" class="chart-container"></div>
+          <div echarts [options]="activeSessionsChartOption" class="chart-container"></div>
         </div>
       </mat-card-content>
     </mat-card>
@@ -22,7 +22,7 @@
           </mat-card-title>
         </div>
         <div class="chart-flex-container">
-          <div echarts [options]="chartOption2" class="chart-container"></div>
+          <div echarts [options]="clientThroughputChartOption" class="chart-container"></div>
         </div>
       </mat-card-content>
     </mat-card>
@@ -36,7 +36,7 @@
           </mat-card-title>
         </div>
         <div class="chart-flex-container">
-          <div echarts [options]="chartOption3" class="chart-container"></div>
+          <div echarts [options]="serverThroughputChartOption" class="chart-container"></div>
         </div>
       </mat-card-content>
     </mat-card>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.scss
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.scss	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.scss	(working copy)
@@ -154,3 +154,55 @@
   text-transform: uppercase;
   margin-top: 5px;
 }
+
+.page-card-1 {
+  width: 100%;
+  border-radius: 0;
+  background-color: inherit;
+  font-size: 14px !important;
+
+  mat-card-header {
+    color: #1170cf;
+  }
+}
+
+mat-card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 4px 10px;
+}
+
+mat-card-title {
+  font-size: medium;
+}
+
+:host {
+  --table-bg: transparent;
+
+  .small-table {
+    background: var(--table-bg);
+
+    .mat-row,
+    .mat-header-row,
+    [mat-row],
+    [mat-header-row] {
+      height: 36px;
+      line-height: 32px;
+      background: var(--table-bg);
+
+      &:hover {
+        background: var(--table-bg);
+      }
+    }
+
+    .mat-cell,
+    .mat-header-cell,
+    [mat-cell],
+    [mat-header-cell] {
+      padding: 4px 8px;
+      line-height: 1.2;
+      background: var(--table-bg);
+    }
+  }
+}
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.ts	(working copy)
@@ -1,6 +1,13 @@
 import {Component, OnChanges, OnInit, SimpleChanges} from '@angular/core';
 import {SharedModule} from '../../../shared/shared-module';
 import {EChartsOption} from 'echarts';
+import {MatTableDataSource} from '@angular/material/table';
+import {NotificationService} from '../../../services/notification';
+import {SystemService} from '../../../services/system-service';
+import {DeviceService} from '../../../services/device-service';
+import {take} from 'rxjs/operators';
+import {ChartOptions} from '../../../services/chart-options';
+import {UtilsService} from '../../../services/utils-service';
 
 @Component({
   selector: 'app-dashboard-insights-sslvpn',
@@ -10,158 +17,87 @@
 })
 export class DashboardInsightsSslvpn implements OnInit, OnChanges {
 
-  chartOption1: EChartsOption = {};
-  chartOption2: EChartsOption = {};
-  chartOption3: EChartsOption = {};
-  chartOption4: EChartsOption = {};
+  activeSessionsChartOption: EChartsOption = {};
+  clientThroughputChartOption: EChartsOption = {};
+  serverThroughputChartOption: EChartsOption = {};
 
+  devices: any[] = [];
+  groups: any[] = [];
+  sslVPNMetrics: any = [];
+
+  constructor(
+    private _notification: NotificationService,
+    private _system: SystemService,
+    private _device: DeviceService,
+    private _chartOptions: ChartOptions,
+    private _utils: UtilsService
+  ) {
+  }
+
   ngOnInit(): void {
-    this.updateChartOption();
+    setTimeout(() => {
+      this.getDeviceGroups();
+    })
   }
 
   ngOnChanges(changes: SimpleChanges): void {
     if (changes['cpuUsagePercentage']) {
-      this.updateChartOption();
     }
   }
 
-  private updateChartOption() {
-    let _data: any = {
-      sessions: [
-        {name: 'vsite1', value: 60},
-        {name: 'vsite2', value: 50},
-        {name: 'vsite3', value: 44},
-        {name: 'vsite4', value: 40},
-        {name: 'vsite5', value: 35}
-      ],
-      clientThroughput: [
-        {name: 'vsite1', inbound: 15, outbound: 20},
-        {name: 'vsite2', inbound: 20, outbound: 5},
-        {name: 'vsite3', inbound: 10, outbound: 8},
-        {name: 'vsite4', inbound: 7, outbound: 12},
-        {name: 'vsite5', inbound: 8, outbound: 11},
-      ],
-      serverThroughput: [
-        {name: 'vsite1', inbound: 10, outbound: 20},
-        {name: 'vsite2', inbound: 20, outbound: 5},
-        {name: 'vsite3', inbound: 15, outbound: 8},
-        {name: 'vsite4', inbound: 7, outbound: 12},
-        {name: 'vsite5', inbound: 8, outbound: 11},
-      ]
-    }
-    _data?.sessions.sort((a: any, b: any) => b.value - a.value);
-    _data?.clientThroughput.sort((a: any, b: any) => (b.inbound + b.outbound) - (a.inbound + a.outbound));
-    _data?.serverThroughput.sort((a: any, b: any) => (b.inbound + b.outbound) - (a.inbound + a.outbound));
-    const sortedCNames = _data?.clientThroughput.map((item: any) => item.name);
-    const sortedCInbound = _data?.clientThroughput.map((item: any) => item.inbound);
-    const sortedCOutbound = _data?.clientThroughput.map((item: any) => item.outbound);
-
-    const sortedSNames = _data?.serverThroughput.map((item: any) => item.name);
-    const sortedSInbound = _data?.serverThroughput.map((item: any) => item.inbound);
-    const sortedSOutbound = _data?.serverThroughput.map((item: any) => item.outbound);
-
-    this.chartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
+  getDeviceGroups(): void {
+    this.devices = [];
+    this.groups = [];
+    // ToDo: Update with actual RoleId with RBAC
+    let roleId = "0"
+    let rawPayload = new FormData();
+    rawPayload.set('action', 'FilterRoleDeviceGroups');
+    rawPayload.set('options', JSON.stringify({"role_id": roleId}));
+    this._device.getDeviceGroups(rawPayload)
+      .pipe(take(1))
+      .subscribe({
+        next: (result: any) => {
+          if (result && result.length > 1) {
+            if (result[1] && 'result' in result[1]) {
+              let groups = result[1].result;
+              groups.forEach((group: any) => {
+                this.groups.push(group?.group_name);
+                group?.device_list.forEach((device: any) => {
+                  this.devices.push(device);
+                })
+              })
+              this.getTopSSLVPNMetrics();
+            }
+          }
+        }, error: (error: { message: string; }) => {
+          console.log(error);
+          this._notification.showError(error.message);
         }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      xAxis: {
-        type: 'value',
-        boundaryGap: [0, 0.01],
-        max: 100
-      },
-      yAxis: {
-        type: 'category',
-        data: _data?.sessions.map((item: any) => item?.name),
-        inverse: true
-      },
-      series: [
-        {
-          name: 'Sessions',
-          type: 'bar',
-          data: _data?.sessions.map((item: any) => item?.value)
-        }
-      ]
-    };
+      })
+  }
 
-    this.chartOption2 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      legend: {
-        bottom: 'bottom'
-      },
-      xAxis: {
-        type: 'value',
-        boundaryGap: [0, 0.01]
-      },
-      yAxis: {
-        type: 'category',
-        data: sortedCNames,
-        inverse: true
-      },
-      series: [
-        {
-          name: 'Received',
-          type: 'bar',
-          data: sortedCInbound
+  getTopSSLVPNMetrics() {
+    let payload = {}
+    this._system.getTopSSLVPNMetrics(payload)
+      .pipe(take(1))
+      .subscribe({
+        next: (result: any) => {
+          this.sslVPNMetrics = this._device.mapDeviceDetails(this.devices, result);
+          this.updateSSLVPNMetricsTables();
         },
-        {
-          name: 'Sent',
-          type: 'bar',
-          data: sortedCOutbound
+        error: (error: { message: string; }) => {
+          console.log(error);
+          this._notification.showError(error.message);
         }
-      ]
-    };
+      });
+  }
 
-    this.chartOption3 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      legend: {
-        bottom: 'bottom'
-      },
-      xAxis: {
-        type: 'value',
-        boundaryGap: [0, 0.01]
-      },
-      yAxis: {
-        type: 'category',
-        data: sortedSNames,
-        inverse: true
-      },
-      series: [
-        {
-          name: 'Received',
-          type: 'bar',
-          data: sortedSInbound
-        },
-        {
-          name: 'Sent',
-          type: 'bar',
-          data: sortedSOutbound
-        }
-      ]
-    };
+  private updateSSLVPNMetricsTables(): void {
+    let _data: any = this._utils.formatChartData(['active_sessions', 'client_network', 'server_network'], this.sslVPNMetrics)
+    this.activeSessionsChartOption = this._chartOptions.distributionChartOptions('Sessions', _data?.active_sessions);
+    let {names, inbound, outbound} = _data?.client_network || {};
+    this.clientThroughputChartOption = this._chartOptions.throughputChartOptions(names, inbound, outbound)
+    let {names: names1, inbound: inbound1, outbound: outbound1} = _data?.server_network || {};
+    this.serverThroughputChartOption = this._chartOptions.throughputChartOptions(names1, inbound1, outbound1)
   }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.html	(working copy)
@@ -98,11 +98,11 @@
             </div>
             <div class="chart-column second-column-1 chart-stack-container">
               <div class="metric-item">
-                <span class="metric-value">{{ networkMetrics?.bytes_sent_bps | bytes }}</span>
+                <span class="metric-value">{{ networkMetrics?.sent | bytes }}</span>
                 <span class="metric-label">Sent</span>
               </div>
               <div class="metric-item">
-                <span class="metric-value">{{ networkMetrics?.bytes_recv_bps | bytes }}</span>
+                <span class="metric-value">{{ networkMetrics?.received | bytes }}</span>
                 <span class="metric-label">Received</span>
               </div>
             </div>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.ts	(working copy)
@@ -7,17 +7,9 @@
 import {NotificationService} from '../../../services/notification';
 import {SystemService} from '../../../services/system-service';
 import {BytesPipe} from '../../../pipes/bytes-pipe';
+import {ChartOptions} from '../../../services/chart-options';
+import {UtilsService} from '../../../services/utils-service';
 
-export interface DeviceTypeStatus {
-  connected: number;
-  disconnected: number;
-}
-
-export interface DeviceGraphData {
-  total: DeviceTypeStatus;
-  types: Map<string, DeviceTypeStatus>;
-}
-
 @Component({
   selector: 'app-dashboard-system-insights',
   imports: [SharedModule, BytesPipe],
@@ -42,6 +34,8 @@
     private _device: DeviceService,
     private _system: SystemService,
     private _notification: NotificationService,
+    private _chartOptions: ChartOptions,
+    private _utils: UtilsService,
   ) {
   }
 
@@ -133,8 +127,8 @@
                   }
                 })
               })
-              this.deviceStats = this.getDeviceGraphData(this.devices);
-              this.deviceConnectionStats = this.transformChartData(this.deviceStats.types.entries());
+              this.deviceStats = this._utils.transformTotalConnectedDevicesMetrics(this.devices);
+              this.deviceConnectionStats = this._utils.transformConnectedDevicesMetrics(this.deviceStats.types.entries());
               this.connectedDevices = this.devices.filter((device: any) => device?.connection === true).length;
             }
           }
@@ -159,51 +153,6 @@
     })
   }
 
-  transformChartData(deviceData: [string, { connected: number, disconnected: number }][]) {
-    const yAxisData: string[] = [];
-    const connectedData: number[] = [];
-    const disconnectedData: number[] = [];
-
-    deviceData.forEach(([type, status]) => {
-      yAxisData.push(type);
-      connectedData.push(status.connected);
-      disconnectedData.push(status.disconnected);
-    });
-
-    return {yAxisData, connectedData, disconnectedData};
-  }
-
-  getDeviceGraphData(devices: any): DeviceGraphData {
-    const totalStatus: DeviceTypeStatus = {connected: 0, disconnected: 0};
-    const typesStatus = new Map<string, DeviceTypeStatus>();
-
-    if (!devices || devices.length === 0) {
-      return {total: totalStatus, types: typesStatus};
-    }
-
-    devices.forEach((device: any) => {
-      // Update the total counts
-      if (device.connection) {
-        totalStatus.connected++;
-      } else {
-        totalStatus.disconnected++;
-      }
-
-      // Update the per-type counts
-      if (!typesStatus.has(device.type)) {
-        typesStatus.set(device.type, {connected: 0, disconnected: 0});
-      }
-      const status = typesStatus.get(device.type)!;
-      if (device.connection) {
-        status.connected++;
-      } else {
-        status.disconnected++;
-      }
-    });
-
-    return {total: totalStatus, types: typesStatus};
-  }
-
   ngOnChanges(changes: SimpleChanges): void {
     if (changes['cpuUsagePercentage']) {
       this.updateChartOption();
@@ -229,375 +178,24 @@
         remaining: 100 - this.currentSystemMetrics?.disk_io?.disk_io_percent,
       }
     }
-    this.systemLoadChartOption2 = {
-      title: {
-        text: `CPU Usage - ${_data?.cpu?.usage.toFixed(1)}%`,
-        left: 'center',
-        top: '80%',
-        textStyle: {
-          fontSize: 14,
-          fontWeight: 'bold',
-          color: '#333'
-        }
-      },
-      tooltip: {
-        trigger: 'item',
-        formatter: '{b}: {c}%'
-      },
-      series: [
-        {
-          type: 'pie',
-          radius: ['70%', '90%'],
-          center: ['50%', '75%'],
-          startAngle: 180,
-          endAngle: 0,
-          label: {
-            show: false
-          },
-          data: [
-            {
-              value: _data?.cpu?.usage,
-              name: 'CPU Usage',
-              itemStyle: {
-                color: '#008ac5'
-              },
-            },
-            {
-              value: _data?.cpu?.remaining,
-              name: 'Remaining',
-              itemStyle: {
-                color: '#E0E0E0'
-              },
-            },
-          ]
-        }
-      ]
-    };
-
-    this.systemLoadChartOption3 = {
-      title: {
-        text: `Memory Usage - ${_data?.memory?.usage.toFixed(1)}%`,
-        left: 'center',
-        top: '80%',
-        textStyle: {
-          fontSize: 14,
-          fontWeight: 'bold',
-          color: '#333'
-        }
-      },
-      tooltip: {
-        trigger: 'item',
-        formatter: '{b}: {c}%'
-      },
-      series: [
-        {
-          type: 'pie',
-          radius: ['70%', '90%'],
-          center: ['50%', '75%'],
-          startAngle: 180,
-          endAngle: 0,
-          label: {
-            show: false
-          },
-          data: [
-            {
-              value: _data?.memory?.usage,
-              name: 'Memory Usage',
-              itemStyle: {
-                color: '#00c698'
-              },
-            },
-            {
-              value: _data?.memory?.remaining,
-              name: 'Remaining',
-              itemStyle: {
-                color: '#E0E0E0'
-              },
-            },
-          ]
-        }
-      ]
-    };
-
-    this.diskChartOption1 = {
-      title: {
-        text: `Usage - ${_data?.disk?.usage.toFixed(1)}%`,
-        left: 'center',
-        top: '80%',
-        textStyle: {
-          fontSize: 12,
-          fontWeight: 'bold',
-          color: '#333'
-        }
-      },
-      tooltip: {
-        trigger: 'item',
-        formatter: '{b}: {c}%'
-      },
-      series: [
-        {
-          type: 'pie',
-          radius: ['70%', '90%'],
-          center: ['50%', '75%'],
-          startAngle: 180,
-          endAngle: 0,
-          label: {
-            show: false
-          },
-          data: [
-            {
-              value: _data?.disk?.usage,
-              name: 'Disk Usage',
-              itemStyle: {
-                color: '#1170cf'
-              },
-            },
-            {
-              value: _data?.disk?.remaining.toFixed(2),
-              name: 'Remaining',
-              itemStyle: {
-                color: '#E0E0E0'
-              },
-            },
-          ]
-        }
-      ]
-    };
-
-    this.connectionChartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      legend: {
-        bottom: 'bottom'
-      },
-      yAxis: {
-        type: 'value',
-        axisLabel: {
-          formatter: function (value: number) {
-            if (value % 1 === 0) {
-              return String(value);
-            }
-            return '';
-          },
-        },
-      },
-      xAxis: {
-        type: 'category',
-        data: this.deviceConnectionStats?.yAxisData
-      },
-      series: [
-        {
-          name: 'Connected',
-          type: 'bar',
-          stack: 'total',
-          color: 'green',
-          label: {
-            show: true
-          },
-          emphasis: {
-            focus: 'series'
-          },
-          data: this.deviceConnectionStats?.connectedData
-        },
-        {
-          name: 'Disconnected',
-          type: 'bar',
-          stack: 'total',
-          label: {
-            show: true
-          },
-          color: 'red',
-          emphasis: {
-            focus: 'series'
-          },
-          data: this.deviceConnectionStats?.disconnectedData
-        }
-      ]
-    };
-
-    this.connectionChartOption2 = {
-      tooltip: {
-        trigger: 'item'
-      },
-      series: [
-        {
-          name: 'Devices Status',
-          type: 'pie',
-          radius: ['40%', '70%'],
-          avoidLabelOverlap: false,
-          label: {
-            show: false,
-            position: 'bottom'
-          },
-          labelLine: {
-            show: false
-          },
-          data: [
-            {value: this.deviceStats?.total?.connected, name: 'Connected', itemStyle: {color: 'green'}},
-            {value: this.deviceStats?.total?.disconnected, name: 'Disconnected', itemStyle: {color: 'red'}}
-          ]
-        }
-      ]
-    }
+    this.systemLoadChartOption2 = this._chartOptions.halfDoughnutChartOptions('CPU', _data?.cpu, ['#008ac5', '#E0E0E0']);
+    this.systemLoadChartOption3 = this._chartOptions.halfDoughnutChartOptions('Memory', _data?.memory, ['#00c698', '#E0E0E0']);
+    this.diskChartOption1 = this._chartOptions.halfDoughnutChartOptions('Disk', _data?.disk, ['#1170cf', '#E0E0E0']);
+    this.connectionChartOption1 = this._chartOptions.connectedDeviceLineChart(this.deviceConnectionStats)
+    this.connectionChartOption2 = this._chartOptions.connectedDevicesDoughnutChartOptions(this.deviceStats?.total);
   }
 
   updateHistoricalCharts() {
+    const factor: any = 1024 * 1024;
     const cpu_data_formatted: any = this.historicalCPUMetrics.map((d: any) => [d.time, d.cpu_percent]);
     const memory_data_formatted: any = this.historicalMemoryMetrics.map((d: any) => [d.time, d.mem_percent]);
-
-    const {unit, factor}: any = {unit: 'MB/s', factor: 1024 * 1024};
-    const sent_data_formatted: any = this.historicalNetworkMetrics.map((d: any) => [d.time, d.bytes_sent_bps / factor]);
-    const received_data_formatted: any = this.historicalNetworkMetrics.map((d: any) => [d.time, d.bytes_recv_bps / factor]);
-
+    const sent_data_formatted: any = this.historicalNetworkMetrics.map((d: any) => [d.time, d.sent / factor]);
+    const received_data_formatted: any = this.historicalNetworkMetrics.map((d: any) => [d.time, d.received / factor]);
     const disk_io_formatted = this.historicalDiskIOMetrics.map((d: any) => [d.time, d.disk_io_percent]);
 
-    this.systemLoadChartOption1 = {
-      grid: {
-        left: '7%',
-        right: '1%',
-        bottom: '1%',
-        containLabel: true
-      },
-      tooltip: {
-        trigger: 'axis',
-        formatter: (params: any) => {
-          const date = new Date(params[0].value[0]);
-          const formattedTime = date.toLocaleTimeString('en-US', {
-            hour: '2-digit',
-            minute: '2-digit',
-            second: '2-digit',
-            hour12: false
-          });
-          let tooltipContent = `Time: ${formattedTime}<br/>`;
-          params.forEach((item: any) => {
-            tooltipContent += `${item.marker} ${item.seriesName}: ${item.value[1]}<br/>`;
-          });
-          return tooltipContent;
-        }
-      },
-      legend: {
-        data: ['CPU', 'Memory']
-      },
-      xAxis: {
-        type: 'time',
-        name: 'Time'
-      },
-      yAxis: {
-        type: 'value',
-        name: 'Percentage'
-      },
-      series: [
-        {
-          name: 'CPU',
-          type: 'line',
-          data: cpu_data_formatted,
-          color: '#008ac5'
-        },
-        {
-          name: 'Memory',
-          type: 'line',
-          data: memory_data_formatted,
-          color: '#00c698'
-        }
-      ]
-    };
-
-    this.networkThroughputChartOption1 = {
-      grid: {
-        left: '7%',
-        right: '1%',
-        bottom: '1%',
-        containLabel: true
-      },
-      tooltip: {
-        trigger: 'axis',
-        formatter: (params: any) => {
-          const date = new Date(params[0].value[0]);
-          const formattedTime = date.toLocaleTimeString('en-US', {
-            hour: '2-digit',
-            minute: '2-digit',
-            second: '2-digit',
-            hour12: false
-          });
-          let tooltipContent = `Time: ${formattedTime}<br/>`;
-          params.forEach((item: any) => {
-            tooltipContent += `${item.marker} ${item.seriesName}: ${item.value[1].toFixed(2)} ${unit}<br/>`;
-          });
-          return tooltipContent;
-        }
-      },
-      legend: {
-        data: ['Sent', 'Received']
-      },
-      xAxis: {
-        type: 'time',
-        name: 'Time'
-      },
-      yAxis: {
-        type: 'value',
-        name: `Bandwidth (${unit})`
-      },
-      series: [
-        {
-          name: 'Sent',
-          type: 'line',
-          data: sent_data_formatted,
-        },
-        {
-          name: 'Received',
-          type: 'line',
-          data: received_data_formatted,
-        }
-      ]
-    };
-
-    this.diskIOChartOption1 = {
-      grid: {
-        left: '7%',
-        right: '1%',
-        bottom: '1%',
-        containLabel: true
-      },
-      tooltip: {
-        trigger: 'axis',
-        formatter: (params: any) => {
-          const date = new Date(params[0].value[0]);
-          const formattedTime = date.toLocaleTimeString('en-US', {
-            hour: '2-digit',
-            minute: '2-digit',
-            second: '2-digit',
-            hour12: false
-          });
-          let tooltipContent = `Time: ${formattedTime}<br/>`;
-          params.forEach((item: any) => {
-            tooltipContent += `${item.marker} ${item.seriesName}: ${item.value[1].toFixed(2)}<br/>`;
-          });
-          return tooltipContent;
-        }
-      },
-      legend: {
-        data: ['Disk I/O',]
-      },
-      xAxis: {
-        type: 'time',
-        name: 'Time'
-      },
-      yAxis: {
-        type: 'value',
-        name: `Percentage %`
-      },
-      series: [
-        {
-          name: 'Disk I/O',
-          type: 'line',
-          data: disk_io_formatted
-        },
-      ]
-    };
+    this.systemLoadChartOption1 = this._chartOptions.historicalCPUMemoryChartOptions(cpu_data_formatted, memory_data_formatted);
+    this.networkThroughputChartOption1 = this._chartOptions.historicalThroughputChartOptions(sent_data_formatted, received_data_formatted)
+    this.diskIOChartOption1 = this._chartOptions.historicalDiskIOChartOptions(disk_io_formatted);
   }
 
   dialog = inject(MatDialog);
@@ -616,21 +214,6 @@
     dialogRef.afterClosed().subscribe((isAdded: boolean) => {
     })
   }
-
-  enlargeSystemDiskUsage() {
-    this.dialogConfig.width = '50%';
-    const dialogRef = this.dialog.open(EnlargeSystemDiskUsage, this.dialogConfig);
-    dialogRef.afterClosed().subscribe((isAdded: boolean) => {
-    })
-  }
-
-  enlargeSystemDevices() {
-    this.dialogConfig.width = '50%';
-    this.dialogConfig.data = this.deviceStats;
-    const dialogRef = this.dialog.open(EnlargeSystemDevices, this.dialogConfig);
-    dialogRef.afterClosed().subscribe((isAdded: boolean) => {
-    })
-  }
 }
 
 @Component({
@@ -648,6 +231,7 @@
   constructor(
     private _system: SystemService,
     private _notification: NotificationService,
+    private _chartOptions: ChartOptions,
   ) {
   }
 
@@ -679,51 +263,7 @@
   updateChartOptions() {
     const cpu_data_formatted = this.cpuMetrics.map((d: any) => [d.time, d.cpu_percent]);
     const memory_data_formatted = this.memoryMetrics.map((d: any) => [d.time, d.mem_percent]);
-
-    this.chartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        formatter: (params: any) => {
-          const date = new Date(params[0].value[0]);
-          const formattedTime = date.toLocaleTimeString('en-US', {
-            hour: '2-digit',
-            minute: '2-digit',
-            second: '2-digit',
-            hour12: false
-          });
-          let tooltipContent = `Time: ${formattedTime}<br/>`;
-          params.forEach((item: any) => {
-            tooltipContent += `${item.marker} ${item.seriesName}: ${item.value[1]}<br/>`;
-          });
-          return tooltipContent;
-        }
-      },
-      legend: {
-        data: ['CPU', 'Memory']
-      },
-      xAxis: {
-        type: 'time',
-        name: 'Time'
-      },
-      yAxis: {
-        type: 'value',
-        name: 'Percentage'
-      },
-      series: [
-        {
-          name: 'CPU',
-          type: 'line',
-          data: cpu_data_formatted,
-          color: '#008ac5'
-        },
-        {
-          name: 'Memory',
-          type: 'line',
-          data: memory_data_formatted,
-          color: '#00c698'
-        }
-      ]
-    };
+    this.chartOption1 = this._chartOptions.historicalCPUMemoryChartOptions(cpu_data_formatted, memory_data_formatted);
   }
 
   onCancel() {
@@ -746,6 +286,7 @@
   constructor(
     private _system: SystemService,
     private _notification: NotificationService,
+    private _chartOptions: ChartOptions,
   ) {
   }
 
@@ -763,104 +304,6 @@
       .subscribe({
         next: (result: any) => {
           this.networkMetrics = result?.net_history;
-          this.updateChartOptions();
-        },
-        error: (error: { message: string; }) => {
-          console.log(error);
-          this._notification.showError(error.message);
-        }
-      });
-  }
-
-  updateChartOptions() {
-    const {unit, factor}: any = {unit: 'MB/s', factor: 1024 * 1024};
-
-    const sent_data_formatted = this.networkMetrics.map((d: any) => [d.time, d.bytes_sent_bps / factor]);
-    const received_data_formatted = this.networkMetrics.map((d: any) => [d.time, d.bytes_recv_bps / factor]);
-
-    this.chartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        formatter: (params: any) => {
-          const date = new Date(params[0].value[0]);
-          const formattedTime = date.toLocaleTimeString('en-US', {
-            hour: '2-digit',
-            minute: '2-digit',
-            second: '2-digit',
-            hour12: false
-          });
-          let tooltipContent = `Time: ${formattedTime}<br/>`;
-          params.forEach((item: any) => {
-            tooltipContent += `${item.marker} ${item.seriesName}: ${item.value[1].toFixed(2)} ${unit}<br/>`;
-          });
-          return tooltipContent;
-        }
-      },
-      legend: {
-        data: ['Sent', 'Received']
-      },
-      xAxis: {
-        type: 'time',
-        name: 'Time'
-      },
-      yAxis: {
-        type: 'value',
-        name: `Bandwidth (${unit})`
-      },
-      series: [
-        {
-          name: 'Sent',
-          type: 'line',
-          data: sent_data_formatted
-        },
-        {
-          name: 'Received',
-          type: 'line',
-          data: received_data_formatted
-        }
-      ]
-    };
-  }
-
-  onCancel() {
-    this.dialogRef.close();
-  }
-}
-
-@Component({
-  selector: 'enlarge-system-disk-usage',
-  templateUrl: './enlarge-system-disk-usage.html',
-  imports: [SharedModule],
-  encapsulation: ViewEncapsulation.None
-})
-export class EnlargeSystemDiskUsage implements OnInit {
-
-  chartOption1: EChartsOption = {};
-  chartOption2: EChartsOption = {};
-
-  readonly data = inject(MAT_DIALOG_DATA);
-  readonly dialogRef = inject(MatDialogRef<EnlargeSystemDiskUsage>);
-
-  constructor(
-    private _system: SystemService,
-    private _notification: NotificationService,
-  ) {
-  }
-
-  ngOnInit() {
-    setTimeout(() => {
-      this.getHistoricalSystemMetrics();
-    })
-  }
-
-  diskIOMetrics: any = {};
-
-  getHistoricalSystemMetrics() {
-    this._system.getHistoricalSystemMetrics()
-      .pipe(take(1))
-      .subscribe({
-        next: (result: any) => {
-          this.diskIOMetrics = result?.disk_io_history;
           this.updateChartOptions();
         },
         error: (error: { message: string; }) => {
@@ -871,161 +314,13 @@
   }
 
   updateChartOptions() {
-    const disk_io_formatted = this.diskIOMetrics.map((d: any) => [d.time, d.disk_io_percent]);
-
-    this.chartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        formatter: (params: any) => {
-          const date = new Date(params[0].value[0]);
-          const formattedTime = date.toLocaleTimeString('en-US', {
-            hour: '2-digit',
-            minute: '2-digit',
-            second: '2-digit',
-            hour12: false
-          });
-          let tooltipContent = `Time: ${formattedTime}<br/>`;
-          params.forEach((item: any) => {
-            tooltipContent += `${item.marker} ${item.seriesName}: ${item.value[1].toFixed(2)}<br/>`;
-          });
-          return tooltipContent;
-        }
-      },
-      legend: {
-        data: ['Disk I/O',]
-      },
-      xAxis: {
-        type: 'time',
-        name: 'Time'
-      },
-      yAxis: {
-        type: 'value',
-        name: `Percentage %`
-      },
-      series: [
-        {
-          name: 'Disk I/O',
-          type: 'line',
-          data: disk_io_formatted
-        },
-      ]
-    };
+    const factor: any = 1024 * 1024;
+    const sent_data_formatted = this.networkMetrics.map((d: any) => [d.time, d.sent / factor]);
+    const received_data_formatted = this.networkMetrics.map((d: any) => [d.time, d.received / factor]);
+    this.chartOption1 = this._chartOptions.historicalThroughputChartOptions(sent_data_formatted, received_data_formatted)
   }
 
   onCancel() {
     this.dialogRef.close();
   }
 }
-
-@Component({
-  selector: 'enlarge-system-devices',
-  templateUrl: './enlarge-system-devices.html',
-  imports: [SharedModule],
-  encapsulation: ViewEncapsulation.None
-})
-export class EnlargeSystemDevices implements OnInit {
-
-  chartOption1: EChartsOption = {};
-  chartOption2: EChartsOption = {};
-
-  readonly data = inject(MAT_DIALOG_DATA);
-  readonly dialogRef = inject(MatDialogRef<EnlargeSystemDevices>);
-
-  ngOnInit() {
-    this.updateChartOptions()
-  }
-
-  transformChartData(deviceData: [string, { connected: number, disconnected: number }][]) {
-    const yAxisData: string[] = [];
-    const connectedData: number[] = [];
-    const disconnectedData: number[] = [];
-
-    deviceData.forEach(([type, status]) => {
-      yAxisData.push(type);
-      connectedData.push(status.connected);
-      disconnectedData.push(status.disconnected);
-    });
-
-    return {yAxisData, connectedData, disconnectedData};
-  }
-
-  updateChartOptions() {
-    const {yAxisData, connectedData, disconnectedData} = this.transformChartData(this.data.types.entries());
-    this.chartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      xAxis: {
-        type: 'value'
-      },
-      yAxis: {
-        type: 'category',
-        data: yAxisData
-      },
-      series: [
-        {
-          name: 'Connected',
-          type: 'bar',
-          stack: 'total',
-          color: 'green',
-          label: {
-            show: true
-          },
-          emphasis: {
-            focus: 'series'
-          },
-          data: connectedData
-        },
-        {
-          name: 'Disconnected',
-          type: 'bar',
-          stack: 'total',
-          label: {
-            show: true
-          },
-          color: 'red',
-          emphasis: {
-            focus: 'series'
-          },
-          data: disconnectedData
-        }
-      ]
-    };
-
-    this.chartOption2 = {
-      tooltip: {
-        trigger: 'item'
-      },
-      series: [
-        {
-          name: 'Devices Status',
-          type: 'pie',
-          radius: ['40%', '70%'],
-          avoidLabelOverlap: false,
-          label: {
-            show: false,
-            position: 'bottom'
-          },
-          labelLine: {
-            show: false
-          },
-          data: [
-            {value: this.data?.total?.connected, name: 'Connected', itemStyle: {color: '#4CAF50'}},
-            {value: this.data?.total?.disconnected, name: 'Disconnected', itemStyle: {color: '#F44336'}}
-          ]
-        }
-      ]
-    }
-  }
-
-  onCancel() {
-    this.dialogRef.close();
-  }
-}
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/constants/api_urls.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/constants/api_urls.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/constants/api_urls.ts	(working copy)
@@ -163,6 +163,7 @@
   GET_TOP_APV_VIRTUAL_METRICS_URL: `${PREFIX}/top-apv-virtual-metrics`,
   GET_TOP_APV_REAL_METRICS_URL: `${PREFIX}/top-apv-real-metrics`,
   GET_TOP_APV_LLB_METRICS_URL: `${PREFIX}/top-apv-llb-metrics`,
+  GET_TOP_SSL_VPN_METRICS_URL: `${PREFIX}/top-ssl-vpn-metrics`,
   GET_DEVICE_MONITORING_METRICS_URL: `${PREFIX}/device_metrics`,
   GET_APV_VS_MONITORING_METRICS_URL: `${PREFIX}/apv/slb/virtual_stats`,
   GET_APV_RS_MONITORING_METRICS_URL: `${PREFIX}/apv/slb/real_stats`,
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/chart-options.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/chart-options.spec.ts	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/chart-options.spec.ts	(working copy)
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { ChartOptions } from './chart-options';
+
+describe('ChartOptions', () => {
+  let service: ChartOptions;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(ChartOptions);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/chart-options.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/chart-options.ts	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/chart-options.ts	(working copy)
@@ -0,0 +1,398 @@
+import {Injectable} from '@angular/core';
+import {EChartsOption} from 'echarts';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ChartOptions {
+
+  throughputChartOptions(sortedNames: any, sortedInbound: any, sortedOutbound: any): EChartsOption {
+    return {
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow'
+        },
+        formatter: (params: any) => {
+          let tooltipContent = `${params[0].name}<br/>`;
+          params.forEach((param: any) => {
+            const valueInMbps = param.value.toFixed(2);
+            tooltipContent += `${param.marker} ${param.seriesName}: ${valueInMbps} Mbps<br/>`;
+          });
+          return tooltipContent;
+        }
+      },
+      grid: {
+        left: '3%',
+        containLabel: true
+      },
+      yAxis: {
+        type: 'value',
+        boundaryGap: [0, 0.01],
+        name: 'Mbps',
+        nameLocation: 'end',
+        nameGap: 20,
+        axisLabel: {
+          formatter: '{value}'
+        }
+      },
+      xAxis: {
+        type: 'category',
+        data: sortedNames,
+      },
+      series: [
+        {
+          name: 'Inbound',
+          type: 'bar',
+          data: sortedInbound
+        },
+        {
+          name: 'Outbound',
+          type: 'bar',
+          data: sortedOutbound
+        }
+      ]
+    };
+  }
+
+  distributionChartOptions(label: any, active_sessions: any): EChartsOption {
+    return {
+      tooltip: {
+        trigger: 'item',
+        formatter: '{a} <br/>{b}: {c}'
+      },
+      series: [
+        {
+          name: label,
+          type: 'pie',
+          radius: [25, 75],
+          center: ['50%', '50%'],
+          roseType: 'area',
+          itemStyle: {
+            borderRadius: 8
+          },
+          data: active_sessions
+        }
+      ]
+    }
+  }
+
+  lineChartOptions(label: any, metrics: any): EChartsOption {
+    return {
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow'
+        }
+      },
+      grid: {
+        left: '3%',
+        containLabel: true
+      },
+      yAxis: {
+        type: 'value',
+        boundaryGap: [0, 0.01],
+        max: 100,
+        name: '%',
+        nameLocation: 'end',
+        nameGap: 20,
+        axisLabel: {
+          formatter: '{value}'
+        }
+      },
+      xAxis: {
+        type: 'category',
+        data: metrics.map((item: any) => item?.name),
+        inverse: false
+      },
+      series: [
+        {
+          name: label,
+          type: 'bar',
+          data: metrics.map((item: any) => item?.value)
+        }
+      ]
+    };
+  }
+
+  connectedDeviceLineChart(metrics: any): EChartsOption {
+    return {
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow'
+        }
+      },
+      legend: {
+        bottom: 'bottom'
+      },
+      yAxis: {
+        type: 'value',
+        axisLabel: {
+          formatter: function (value: number) {
+            if (value % 1 === 0) {
+              return String(value);
+            }
+            return '';
+          },
+        },
+      },
+      xAxis: {
+        type: 'category',
+        data: metrics?.yAxisData
+      },
+      series: [
+        {
+          name: 'Connected',
+          type: 'bar',
+          stack: 'total',
+          color: 'green',
+          label: {
+            show: true
+          },
+          emphasis: {
+            focus: 'series'
+          },
+          data: metrics?.connectedData
+        },
+        {
+          name: 'Disconnected',
+          type: 'bar',
+          stack: 'total',
+          label: {
+            show: true
+          },
+          color: 'red',
+          emphasis: {
+            focus: 'series'
+          },
+          data: metrics?.disconnectedData
+        }
+      ]
+    };
+  }
+
+  historicalThroughputChartOptions(inbound: any, outbound: any): EChartsOption {
+    return {
+      grid: {
+        left: '7%',
+        right: '1%',
+        bottom: '1%',
+        containLabel: true
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: (params: any) => {
+          const date = new Date(params[0].value[0]);
+          const formattedTime = date.toLocaleTimeString('en-US', {
+            hour: '2-digit',
+            minute: '2-digit',
+            second: '2-digit',
+            hour12: false
+          });
+          let tooltipContent = `Time: ${formattedTime}<br/>`;
+          params.forEach((item: any) => {
+            tooltipContent += `${item.marker} ${item.seriesName}: ${item.value[1].toFixed(2)} Mbps<br/>`;
+          });
+          return tooltipContent;
+        }
+      },
+      legend: {
+        data: ['Sent', 'Received']
+      },
+      xAxis: {
+        type: 'time',
+        name: 'Time'
+      },
+      yAxis: {
+        type: 'value',
+        name: `Bandwidth (Mbps)`
+      },
+      series: [
+        {
+          name: 'Sent',
+          type: 'line',
+          data: outbound,
+        },
+        {
+          name: 'Received',
+          type: 'line',
+          data: inbound,
+        }
+      ]
+    }
+  }
+
+  historicalCPUMemoryChartOptions(cpu_data: any, memory_data: any): EChartsOption {
+    return {
+      grid: {
+        left: '7%',
+        right: '1%',
+        bottom: '1%',
+        containLabel: true
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: (params: any) => {
+          const date = new Date(params[0].value[0]);
+          const formattedTime = date.toLocaleTimeString('en-US', {
+            hour: '2-digit',
+            minute: '2-digit',
+            second: '2-digit',
+            hour12: false
+          });
+          let tooltipContent = `Time: ${formattedTime}<br/>`;
+          params.forEach((item: any) => {
+            tooltipContent += `${item.marker} ${item.seriesName}: ${item.value[1]}<br/>`;
+          });
+          return tooltipContent;
+        }
+      },
+      legend: {
+        data: ['CPU', 'Memory']
+      },
+      xAxis: {
+        type: 'time',
+        name: 'Time'
+      },
+      yAxis: {
+        type: 'value',
+        name: 'Percentage'
+      },
+      series: [
+        {
+          name: 'CPU',
+          type: 'line',
+          data: cpu_data,
+          color: '#008ac5'
+        },
+        {
+          name: 'Memory',
+          type: 'line',
+          data: memory_data,
+          color: '#00c698'
+        }
+      ]
+    }
+  }
+
+  historicalDiskIOChartOptions(disk_io_data: any): EChartsOption {
+    return {
+      grid: {
+        left: '7%',
+        right: '1%',
+        bottom: '1%',
+        containLabel: true
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: (params: any) => {
+          const date = new Date(params[0].value[0]);
+          const formattedTime = date.toLocaleTimeString('en-US', {
+            hour: '2-digit',
+            minute: '2-digit',
+            second: '2-digit',
+            hour12: false
+          });
+          let tooltipContent = `Time: ${formattedTime}<br/>`;
+          params.forEach((item: any) => {
+            tooltipContent += `${item.marker} ${item.seriesName}: ${item.value[1].toFixed(2)}<br/>`;
+          });
+          return tooltipContent;
+        }
+      },
+      legend: {
+        data: ['Disk I/O',]
+      },
+      xAxis: {
+        type: 'time',
+        name: 'Time'
+      },
+      yAxis: {
+        type: 'value',
+        name: `Percentage %`
+      },
+      series: [
+        {
+          name: 'Disk I/O',
+          type: 'line',
+          data: disk_io_data
+        },
+      ]
+    }
+  }
+
+  connectedDevicesDoughnutChartOptions(deviceMetrics: any): EChartsOption {
+    return {
+      tooltip: {
+        trigger: 'item'
+      },
+      series: [
+        {
+          name: 'Devices Status',
+          type: 'pie',
+          radius: ['40%', '70%'],
+          avoidLabelOverlap: false,
+          label: {
+            show: false,
+            position: 'bottom'
+          },
+          labelLine: {
+            show: false
+          },
+          data: [
+            {value: deviceMetrics?.connected, name: 'Connected', itemStyle: {color: 'green'}},
+            {value: deviceMetrics?.disconnected, name: 'Disconnected', itemStyle: {color: 'red'}}
+          ]
+        }
+      ]
+    }
+  }
+
+  halfDoughnutChartOptions(label: string, _data: any, colors: any[] = ['#008ac5', '#E0E0E0']): EChartsOption {
+    return {
+      title: {
+        text: `${label} Usage - ${_data?.usage.toFixed(1)}%`,
+        left: 'center',
+        top: '80%',
+        textStyle: {
+          fontSize: 14,
+          fontWeight: 'bold',
+          color: '#333'
+        }
+      },
+      tooltip: {
+        trigger: 'item',
+        formatter: '{b}: {c}%'
+      },
+      series: [
+        {
+          type: 'pie',
+          radius: ['70%', '90%'],
+          center: ['50%', '75%'],
+          startAngle: 180,
+          endAngle: 0,
+          label: {
+            show: false
+          },
+          data: [
+            {
+              value: _data?.usage,
+              name: `${label} Usage`,
+              itemStyle: {
+                color: colors[0]
+              },
+            },
+            {
+              value: _data?.remaining,
+              name: 'Remaining',
+              itemStyle: {
+                color: colors[1]
+              },
+            },
+          ]
+        }
+      ]
+    }
+  }
+}
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/device-service.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/device-service.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/device-service.ts	(working copy)
@@ -141,7 +141,7 @@
   }
 
   getDeviceConfigByConfigFileName(filename: any, filetype: string = 'system') {
-    let baseURL: string =`${URLS.GET_DEVICE_CONFIG_BY_CONFIG_FILE_NAME_URL}`;
+    let baseURL: string = `${URLS.GET_DEVICE_CONFIG_BY_CONFIG_FILE_NAME_URL}`;
     filetype = filetype === 'device' ? 'system' : filetype;
     return this.http.get(`${baseURL}/${filetype}/${filename}`);
   }
@@ -157,7 +157,7 @@
   getAGVSiteConfig(configName: string, vsite: any = 'global') {
     let finalURL: any = `${URLS.GET_AG_VSITE_CA_CONFIG_URL}/${configName}/ca.conf`;
     if (vsite !== 'global') {
-      finalURL =  `${URLS.GET_AG_VSITE_CA_CONFIG_URL}/${configName}/vsites/${vsite}/ca.conf.${vsite}`;
+      finalURL = `${URLS.GET_AG_VSITE_CA_CONFIG_URL}/${configName}/vsites/${vsite}/ca.conf.${vsite}`;
     }
     return this.http.post(finalURL, null, {
       csrf: true,
@@ -167,7 +167,7 @@
   }
 
   updateDeviceConfigByConfigFileName(filename: string, filetype: string, rawPayload: any) {
-    let baseURL: string =`${URLS.UPDATE_DEVICE_CONFIG_BY_CONFIG_FILE_NAME_URL}`;
+    let baseURL: string = `${URLS.UPDATE_DEVICE_CONFIG_BY_CONFIG_FILE_NAME_URL}`;
     filetype = filetype === 'device' ? 'system' : filetype;
     return this.http.post(`${baseURL}/${filetype}/${filename}`, rawPayload, {
       csrf: true,
@@ -179,7 +179,7 @@
   updateAGVSiteConfig(configName: string, vsite: any = 'global', rawPayload: any) {
     let finalURL: any = `${URLS.UPDATE_AG_VSITE_CA_CONFIG_URL}/${configName}/ca.conf`;
     if (vsite !== 'global') {
-      finalURL =  `${URLS.UPDATE_AG_VSITE_CA_CONFIG_URL}/${configName}/vsites/${vsite}/ca.conf.${vsite}`;
+      finalURL = `${URLS.UPDATE_AG_VSITE_CA_CONFIG_URL}/${configName}/vsites/${vsite}/ca.conf.${vsite}`;
     }
     return this.http.post(finalURL, rawPayload, {
       csrf: true,
@@ -528,4 +528,19 @@
       }
     );
   }
+
+  mapDeviceDetails(devices: any[], metrics: any[]): any[] {
+    metrics.forEach((_metric: any) => {
+      const device = devices.find(_device => _metric?.agent_host === _device?.ip);
+      if (device) {
+        _metric.device_name = device?.name;
+        _metric.device_type = device?.type;
+        // Specific for LLB
+        if (!_metric.service_name && _metric?.link_name) {
+          _metric.service_name = _metric.link_name;
+        }
+      }
+    });
+    return metrics;
+  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/system-service.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/system-service.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/system-service.ts	(working copy)
@@ -485,6 +485,14 @@
 
   getTopAPVLLBMetrics(payload: any) {
     return this.http.post(URLS.GET_TOP_APV_LLB_METRICS_URL, payload, {
+      csrf: true,
+      isFormData: false,
+      csrfInFormData: true
+    });
+  }
+
+  getTopSSLVPNMetrics(payload: any) {
+    return this.http.post(URLS.GET_TOP_SSL_VPN_METRICS_URL, payload, {
       csrf: true,
       isFormData: false,
       csrfInFormData: true
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/utils-service.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/utils-service.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/utils-service.ts	(working copy)
@@ -1,7 +1,17 @@
-import { Injectable } from '@angular/core';
+import {Injectable} from '@angular/core';
 import {MatTableDataSource} from '@angular/material/table';
 import {KeyValuePair} from '../components/sub-components/device-details/device-details';
 
+export interface DeviceTypeStatus {
+  connected: number;
+  disconnected: number;
+}
+
+export interface DeviceGraphData {
+  total: DeviceTypeStatus;
+  types: Map<string, DeviceTypeStatus>;
+}
+
 @Injectable({
   providedIn: 'root'
 })
@@ -88,4 +98,155 @@
       "17": "rdp",
     }
   }
+
+  formatChartData(metrics: any, rawData: any): any {
+    let result: any = {};
+
+    if (metrics.includes('connections')) {
+      result.connections = rawData
+        .filter((item: any) => item.metric === 'connections')
+        .map((item: any) => ({
+          name: `${item.service_name} (${item.device_name})`,
+          value: item.value
+        }))
+        .sort((a: any, b: any) => b.value - a.value);
+    }
+
+    if (metrics.includes('hits') || metrics.includes('requests')) {
+      result.hits = rawData
+        .filter((item: any) => item.metric === 'hits' || item.metric === 'requests')
+        .map((item: any) => ({
+          name: `${item.service_name} (${item.device_name})`,
+          value: item.value
+        }))
+        .sort((a: any, b: any) => b.value - a.value);
+    }
+
+    if (metrics.includes('active_sessions')) {
+      result.active_sessions = rawData
+        .filter((item: any) => item.metric === 'active_sessions')
+        .map((item: any) => ({name: `${item.vsite_name} (${item.device_name})`, value: item.value}))
+        .sort((a: any, b: any) => b.value - a.value);
+    }
+
+    if (metrics.includes('cpu')) {
+      result.cpu = rawData
+        .filter((item: any) => item.metric === 'cpu')
+        .map((item: any) => ({name: `${item.device_name}`, value: item.value}))
+        .sort((a: any, b: any) => b.value - a.value);
+    }
+
+    if (metrics.includes('memory')) {
+      result.memory = rawData
+        .filter((item: any) => item.metric === 'memory')
+        .map((item: any) => ({name: `${item.device_name}`, value: item.value}))
+        .sort((a: any, b: any) => b.value - a.value);
+    }
+
+    if (metrics.includes('network')) {
+      const sortedNetworkData = rawData
+        .filter((item: any) => item.metric === 'network')
+        .map((item: any) => ({
+          name: `${item.service_name} (${item.device_name})`,
+          // Convert Bytes to Megabytes (MB)
+          inbound: item.received / (1024 * 1024),
+          outbound: item.sent / (1024 * 1024),
+        }))
+        .sort((a: any, b: any) => (b.inbound + b.outbound) - (a.inbound + a.outbound));
+      result.network = {
+        names: sortedNetworkData.map((item: any) => item.name),
+        inbound: sortedNetworkData.map((item: any) => item.inbound),
+        outbound: sortedNetworkData.map((item: any) => item.outbound)
+      };
+    }
+
+    if (metrics.includes('device_network')) {
+      const sortedNetworkData = rawData
+        .filter((item: any) => item.metric === 'network')
+        .map((item: any) => ({
+          name: `${item.device_name}`,
+          // Convert Bytes to Megabytes (MB)
+          total_in: item.total_in / (1024 * 1024),
+          total_out: item.total_out / (1024 * 1024),
+        }))
+        .sort((a: any, b: any) => (b.total_in + b.total_out) - (a.total_in + a.total_out));
+      result.network = {
+        names: sortedNetworkData.map((item: any) => item.name),
+        inbound: sortedNetworkData.map((item: any) => item.total_in),
+        outbound: sortedNetworkData.map((item: any) => item.total_out)
+      };
+    }
+
+    if (metrics.includes('client_network')) {
+      const sortedNetworkData = rawData
+        .filter((item: any) => item.metric === 'client_network')
+        .map((item: any) => ({
+          name: `${item.vsite_name} (${item.device_name})`,
+          // Convert Bytes to Megabytes (MB)
+          inbound: item.received / (1024 * 1024),
+          outbound: item.sent / (1024 * 1024),
+        }))
+        .sort((a: any, b: any) => (b.inbound + b.outbound) - (a.inbound + a.outbound));
+      result.client_network = {
+        names: sortedNetworkData.map((item: any) => item.name),
+        inbound: sortedNetworkData.map((item: any) => item.inbound),
+        outbound: sortedNetworkData.map((item: any) => item.outbound)
+      };
+    }
+
+    if (metrics.includes('server_network')) {
+      const sortedNetworkData = rawData
+        .filter((item: any) => item.metric === 'server_network')
+        .map((item: any) => ({
+          name: `${item.vsite_name} (${item.device_name})`,
+          // Convert Bytes to Megabytes (MB)
+          inbound: item.received / (1024 * 1024),
+          outbound: item.sent / (1024 * 1024),
+        }))
+        .sort((a: any, b: any) => (b.inbound + b.outbound) - (a.inbound + a.outbound));
+      result.server_network = {
+        names: sortedNetworkData.map((item: any) => item.name),
+        inbound: sortedNetworkData.map((item: any) => item.inbound),
+        outbound: sortedNetworkData.map((item: any) => item.outbound)
+      };
+    }
+    return result;
+  }
+
+  transformConnectedDevicesMetrics(deviceData: [string, { connected: number, disconnected: number }][]) {
+    const yAxisData: string[] = [];
+    const connectedData: number[] = [];
+    const disconnectedData: number[] = [];
+    deviceData.forEach(([type, status]) => {
+      yAxisData.push(type);
+      connectedData.push(status.connected);
+      disconnectedData.push(status.disconnected);
+    });
+    return {yAxisData, connectedData, disconnectedData};
+  }
+
+  transformTotalConnectedDevicesMetrics(devices: any): DeviceGraphData {
+    const totalStatus: DeviceTypeStatus = {connected: 0, disconnected: 0};
+    const typesStatus = new Map<string, DeviceTypeStatus>();
+    if (!devices || devices.length === 0) {
+      return {total: totalStatus, types: typesStatus};
+    }
+    devices.forEach((device: any) => {
+      if (device.connection) {
+        totalStatus.connected++;
+      } else {
+        totalStatus.disconnected++;
+      }
+      if (!typesStatus.has(device.type)) {
+        typesStatus.set(device.type, {connected: 0, disconnected: 0});
+      }
+      const status = typesStatus.get(device.type)!;
+      if (device.connection) {
+        status.connected++;
+      } else {
+        status.disconnected++;
+      }
+    });
+    return {total: totalStatus, types: typesStatus};
+  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/controller/sslvpn_metrics.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/controller/sslvpn_metrics.py	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/controller/sslvpn_metrics.py	(working copy)
@@ -0,0 +1,32 @@
+from django.http import JsonResponse
+
+from cm.lib.libbasic_operation import oper_log
+from hive.custom_exceptions import generic_exception as ge
+from hive.services.sslvpn_metrics import get_top_ssl_vpn_metrics
+
+
+def handle_get_top_ssl_vpn_metrics(request, path=None):
+    try:
+        if request.method != 'POST':
+            return JsonResponse({
+                'error': 400,
+                'message': "Invalid HTTP method"
+            }, status=400)
+
+        payload = getattr(request, 'json', {}) or {}
+        agent_hosts = payload.get('agent_host', None)
+        if agent_hosts is not None:
+            if isinstance(agent_hosts, str):
+                agent_hosts = [agent_hosts]
+
+        services_metrics = get_top_ssl_vpn_metrics(agent_hosts)
+
+        return JsonResponse(services_metrics, safe=False)
+
+    except ge.GenericError as e:
+        oper_log('error', 'system', str(e))
+        return ge.handle_exception(e)
+    except Exception as e:
+        msg = f'Exception while getting top ssl-vpn virtual site metrics: {str(e)}'
+        oper_log('error', 'system', msg)
+        raise ge.GenericError(500, msg)
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/apv_services_metrics.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/apv_services_metrics.py	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/apv_services_metrics.py	(working copy)
@@ -3,9 +3,9 @@
         self.metric = metric  # "connections", "requests", or "network"
         self.agent_host = agent_host
         self.service_name = service_name
-        self.value = float(value) if value is not None else None
-        self.received = int(received) if received is not None else None
-        self.sent = int(sent) if sent is not None else None
+        self.value = int(value) if value is not None else 0
+        self.received = int(received) if received is not None else 0
+        self.sent = int(sent) if sent is not None else 0
 
     def to_dict(self):
         return {
@@ -23,9 +23,9 @@
         self.metric = metric  # "connections", "hits", or "network"
         self.agent_host = agent_host
         self.service_name = service_name
-        self.value = float(value) if value is not None else None
-        self.received = int(received) if received is not None else None
-        self.sent = int(sent) if sent is not None else None
+        self.value = int(value) if value is not None else 0
+        self.received = int(received) if received is not None else 0
+        self.sent = int(sent) if sent is not None else 0
 
     def to_dict(self):
         return {
@@ -40,12 +40,12 @@
 
 class LLBMetrics:
     def __init__(self, metric, agent_host, link_name, value, received=None, sent=None):
-        self.metric = metric            # "hits", "connections", "throughput"
+        self.metric = metric  # "hits", "connections", "throughput"
         self.agent_host = agent_host
         self.link_name = link_name
-        self.value = float(value) if value is not None else None
-        self.received = int(received) if received is not None else None
-        self.sent = int(sent) if sent is not None else None
+        self.value = int(value) if value is not None else 0
+        self.received = int(received) if received is not None else 0
+        self.sent = int(sent) if sent is not None else 0
 
     def to_dict(self):
         return {
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/devices_metrics.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/devices_metrics.py	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/devices_metrics.py	(working copy)
@@ -2,9 +2,9 @@
     def __init__(self, metric, agent_host, value, total_in=None, total_out=None):
         self.metric = metric  # "cpu", "memory", or "network"
         self.agent_host = agent_host
-        self.value = float(value) if value is not None else None
-        self.total_in = int(total_in) if total_in is not None else None
-        self.total_out = int(total_out) if total_out is not None else None
+        self.value = float(value) if value is not None else 0
+        self.total_in = int(total_in) if total_in is not None else 0
+        self.total_out = int(total_out) if total_out is not None else 0
 
     def to_dict(self):
         return {
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/sslvpn_metrics.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/sslvpn_metrics.py	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/sslvpn_metrics.py	(working copy)
@@ -0,0 +1,18 @@
+class VirtualSiteMetrics:
+    def __init__(self, metric, agent_host, vsite_name, value, received=None, sent=None):
+        self.metric = metric  # "active_sessions", "client_network", "server_network"
+        self.agent_host = agent_host
+        self.vsite_name = vsite_name
+        self.value = int(value) if value is not None else 0
+        self.received = int(received) if received is not None else 0
+        self.sent = int(sent) if sent is not None else 0
+
+    def to_dict(self):
+        return {
+            "metric": self.metric,
+            "agent_host": self.agent_host,
+            "vsite_name": self.vsite_name,
+            "value": self.value,
+            "received": self.received,
+            "sent": self.sent
+        }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/system_metrics.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/system_metrics.py	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/db/system_metrics.py	(working copy)
@@ -55,18 +55,18 @@
 
 
 class NetworkMetrics:
-    def __init__(self, time, host, interface, bytes_sent_bps, bytes_recv_bps):
+    def __init__(self, time, host, interface, sent, received):
         self.time = time
         self.host = host
         self.interface = interface
-        self.bytes_sent_bps = bytes_sent_bps
-        self.bytes_recv_bps = bytes_recv_bps
+        self.sent = sent
+        self.received = received
 
     def to_dict(self):
         return {
             'time': self.time.isoformat() if self.time else None,
             'host': self.host,
             'interface': self.interface,
-            'bytes_sent_bps': float(round(self.bytes_sent_bps, 2)) if self.bytes_sent_bps is not None else None,
-            'bytes_recv_bps': float(round(self.bytes_recv_bps, 2)) if self.bytes_recv_bps is not None else None
+            'sent': float(round(self.sent, 2)) if self.sent is not None else 0,
+            'received': float(round(self.received, 2)) if self.received is not None else 0
         }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/repositories/sslvpn_metrics.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/repositories/sslvpn_metrics.py	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/repositories/sslvpn_metrics.py	(working copy)
@@ -0,0 +1,162 @@
+from hive.db.db_client import with_db_connection
+from hive.db.sslvpn_metrics import VirtualSiteMetrics
+
+
+class VirtualSiteMetricsRepo:
+    @staticmethod
+    @with_db_connection
+    def get_top_virtual_site_metrics(cur, interval_seconds=30, limit=5, agent_hosts=None):
+        # Build agent_host filter
+        host_filter = ""
+        params = {"interval": interval_seconds, "limit": limit}
+        if agent_hosts:
+            host_filter = "AND agent_host = ANY(%(agent_hosts)s)"
+            params["agent_hosts"] = agent_hosts
+
+        query = f"""
+        -- Top Active Sessions
+        WITH ActiveSessions AS (
+            SELECT
+                id,
+                agent_host,
+                last(active_sessions, time) AS value
+            FROM ag_virtual_site_stats
+            WHERE time >= NOW() - INTERVAL '%(interval)s seconds' {host_filter}
+            GROUP BY id, agent_host
+        ),
+        ActiveTop AS (
+            SELECT
+                'active_sessions' AS metric,
+                agent_host,
+                id AS vsite_name,
+                value,
+                NULL::BIGINT AS received,
+                NULL::BIGINT AS sent
+            FROM ActiveSessions
+            ORDER BY value DESC
+            LIMIT %(limit)s
+        ),
+
+        -- Client Throughput
+        ClientBytes AS (
+            SELECT
+                time_bucket('10 seconds', time) AS bucket,
+                id,
+                agent_host,
+                first(client_bytes_in, time)  AS first_in,
+                first(client_bytes_out, time) AS first_out
+            FROM ag_virtual_site_stats
+            WHERE time >= NOW() - INTERVAL '%(interval)s seconds' {host_filter}
+            GROUP BY bucket, id, agent_host
+        ),
+        ClientDerivatives AS (
+            SELECT
+                id,
+                agent_host,
+                (first_in - lag(first_in) OVER w) * 8 AS received,
+                (first_out - lag(first_out) OVER w) * 8 AS sent
+            FROM ClientBytes
+            WINDOW w AS (PARTITION BY id, agent_host ORDER BY bucket)
+        ),
+        ClientAgg AS (
+            SELECT
+                id,
+                agent_host,
+                last(received, now()) AS received,
+                last(sent, now())     AS sent,
+                last(received, now()) + last(sent, now()) AS total
+            FROM ClientDerivatives
+            GROUP BY id, agent_host
+        ),
+        ClientTop AS (
+            SELECT
+                'client_network' AS metric,
+                agent_host,
+                id AS vsite_name,
+                total AS value,
+                received,
+                sent
+            FROM ClientAgg
+            ORDER BY total DESC
+            LIMIT %(limit)s
+        ),
+
+        -- Server Throughput
+        ServerBytes AS (
+            SELECT
+                time_bucket('10 seconds', time) AS bucket,
+                id,
+                agent_host,
+                first(server_bytes_in, time)  AS first_in,
+                first(server_bytes_out, time) AS first_out
+            FROM ag_virtual_site_stats
+            WHERE time >= NOW() - INTERVAL '%(interval)s seconds' {host_filter}
+            GROUP BY bucket, id, agent_host
+        ),
+        ServerDerivatives AS (
+            SELECT
+                id,
+                agent_host,
+                (first_in - lag(first_in) OVER w) * 8 AS received,
+                (first_out - lag(first_out) OVER w) * 8 AS sent
+            FROM ServerBytes
+            WINDOW w AS (PARTITION BY id, agent_host ORDER BY bucket)
+        ),
+        ServerAgg AS (
+            SELECT
+                id,
+                agent_host,
+                last(received, now()) AS received,
+                last(sent, now())     AS sent,
+                last(received, now()) + last(sent, now()) AS total
+            FROM ServerDerivatives
+            GROUP BY id, agent_host
+        ),
+        ServerTop AS (
+            SELECT
+                'server_network' AS metric,
+                agent_host,
+                id AS vsite_name,
+                total AS value,
+                received,
+                sent
+            FROM ServerAgg
+            ORDER BY total DESC
+            LIMIT %(limit)s
+        )
+
+        -- Final Union
+        SELECT *
+        FROM (
+            SELECT * FROM ActiveTop
+            UNION ALL
+            SELECT * FROM ClientTop
+            UNION ALL
+            SELECT * FROM ServerTop
+        ) t
+        ORDER BY metric, value DESC;
+        """
+
+        cur.execute(query, params)
+        rows = cur.fetchall()
+
+        metrics_list = []
+        for row in rows:
+            metric_obj = VirtualSiteMetricsRepo.parse_virtual_metric(row)
+            if metric_obj:
+                metrics_list.append(metric_obj)
+        return metrics_list
+
+    @staticmethod
+    def parse_virtual_metric(row):
+        if not row:
+            return None
+        metric, agent_host, vsite_name, value, received, sent = row
+        return VirtualSiteMetrics(
+            metric=metric,
+            agent_host=agent_host,
+            vsite_name=vsite_name,
+            value=value,
+            received=received,
+            sent=sent
+        )
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/repositories/system_metrics.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/repositories/system_metrics.py	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/repositories/system_metrics.py	(working copy)
@@ -107,8 +107,8 @@
                     SELECT
                         bucket_time,
                         interface,
-                        ROUND(SUM(bytes_sent_diff) / GREATEST(1, SUM(time_diff_sec)), 2) AS bytes_sent_bps,
-                        ROUND(SUM(bytes_recv_diff) / GREATEST(1, SUM(time_diff_sec)), 2) AS bytes_recv_bps
+                        ROUND(SUM(bytes_sent_diff) / GREATEST(1, SUM(time_diff_sec)), 2) AS sent,
+                        ROUND(SUM(bytes_recv_diff) / GREATEST(1, SUM(time_diff_sec)), 2) AS received
                     FROM (
                              SELECT
                                  time_bucket('1 minute', time) AS bucket_time,
@@ -129,13 +129,13 @@
         cur.close()
 
         metrics_list = []
-        for time, interface, bytes_sent, bytes_recv in results:
+        for time, interface, sent, received in results:
             metrics_list.append(NetworkMetrics(
                 time=time,
                 host=host,
                 interface=interface,
-                bytes_sent_bps=bytes_sent,
-                bytes_recv_bps=bytes_recv
+                sent=sent,
+                received=received
             ))
         return [m.to_dict() for m in metrics_list]
 
@@ -228,8 +228,8 @@
                     SELECT bucket_time,
                            interface,
                            ROUND(SUM(bytes_sent_diff) * 8 / GREATEST(1, SUM(time_diff_sec)),
-                                 2)                                                                       AS bytes_sent_bps,
-                           ROUND(SUM(bytes_recv_diff) * 8 / GREATEST(1, SUM(time_diff_sec)), 2) AS bytes_recv_bps
+                                 2)                                                                       AS sent,
+                           ROUND(SUM(bytes_recv_diff) * 8 / GREATEST(1, SUM(time_diff_sec)), 2) AS received
                     FROM (SELECT time_bucket('1 minute', time)                                                            AS bucket_time,
                                  interface,
                                  GREATEST(bytes_sent::BIGINT -
@@ -253,12 +253,12 @@
         cur.close()
 
         metrics_list = []
-        for time, interface, bytes_sent, bytes_recv in results:
+        for time, interface, sent, received in results:
             metrics_list.append(NetworkMetrics(
                 time=time,
                 host=host,
                 interface=interface,
-                bytes_sent_bps=bytes_sent,
-                bytes_recv_bps=bytes_recv
+                sent=sent,
+                received=received
             ))
         return [m.to_dict() for m in metrics_list]
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/services/sslvpn_metrics.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/services/sslvpn_metrics.py	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/services/sslvpn_metrics.py	(working copy)
@@ -0,0 +1,35 @@
+from hive.repositories.sslvpn_metrics import VirtualSiteMetricsRepo
+
+
+def sanitize_metrics(metrics_list):
+    sanitized = []
+    for m in metrics_list:
+        d = m.to_dict() if hasattr(m, "to_dict") else dict(m)
+        if d['metric'] == 'active_sessions':
+            d.pop('received', None)
+            d.pop('sent', None)
+        elif d['metric'] in ('client_network', 'server_network'):
+            d.pop('value', None)
+        sanitized.append(d)
+    return sanitized
+
+
+def get_top_ssl_vpn_metrics(agent_hosts=None, interval=30, limit=5):
+    """
+    Wrapper to fetch top devices' metrics.
+
+    :param agent_hosts: list of hostnames to filter (or None for all)
+    :param interval: interval in seconds for network rate calculation
+    :param limit: top N hosts per metric
+    :return: list of dicts representing metrics
+    """
+    metrics_objects = VirtualSiteMetricsRepo.get_top_virtual_site_metrics(
+        agent_hosts=agent_hosts,
+        interval_seconds=interval,
+        limit=limit
+    )
+
+    metrics_list = [m.to_dict() for m in metrics_objects]
+
+    metrics_list = sanitize_metrics(metrics_list)
+    return metrics_list
