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/admin-tools/admin-tools.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/admin-tools/admin-tools.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/admin-tools/admin-tools.html	(working copy)
@@ -1,32 +1 @@
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="User Management">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-user-management/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Role Management">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-role-management/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Operational Logs">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-operation-logs/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="System Reboot/Shutdown">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-system-control/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/admin-tools/admin-tools.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/admin-tools/admin-tools.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/admin-tools/admin-tools.ts	(working copy)
@@ -1,60 +1,23 @@
-import {Component, OnInit} from '@angular/core';
+import {Component} from '@angular/core';
 import {SharedModule} from '../../shared/shared-module';
 import {UserManagement} from '../sub-components/user-management/user-management';
 import {RoleManagement} from '../sub-components/role-management/role-management';
 import {OperationLogs} from '../sub-components/operation-logs/operation-logs';
 import {SystemControl} from '../sub-components/system-control/system-control';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
+import {TabContainer, TabDefinition} from '../tab-container/tab-container';
 
 @Component({
   selector: 'app-admin-tools',
-  imports: [SharedModule, UserManagement, RoleManagement, OperationLogs, SystemControl],
+  imports: [SharedModule, TabContainer],
   templateUrl: './admin-tools.html',
   styleUrl: './admin-tools.scss'
 })
-export class AdminTools implements OnInit {
+export class AdminTools {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'User Management',
-    'Role Management',
-    'Operational Logs',
-    'System Reboot/Shutdown'
-  ];
-
-  constructor(private route: ActivatedRoute, private router: Router) {
-  }
-
-  ngOnInit(): void {
-    this.route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this.router.navigate([], {
-        relativeTo: this.route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
+  tabDefinitions: TabDefinition[] = [
+    {label: 'User Management', component: UserManagement, paramName: 'User Management'},
+    {label: 'Role Management', component: RoleManagement, paramName: 'Role Management'},
+    {label: 'Operational Logs', component: OperationLogs, paramName: 'Operational Logs'},
+    {label: 'System Reboot/Shutdown', component: SystemControl, paramName: 'System Control'},
+  ]
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/config-hub/config-hub.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/config-hub/config-hub.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/config-hub/config-hub.html	(working copy)
@@ -1,32 +1 @@
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Device Configuration Files">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-config-overview/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Cloned Files">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-cloned-files/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Custom Configuration">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-custom-config-overview/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Custom Templates">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-custom-config-templates/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/config-hub/config-hub.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/config-hub/config-hub.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/config-hub/config-hub.ts	(working copy)
@@ -1,7 +1,5 @@
-import {Component, OnInit} from '@angular/core';
+import {Component} from '@angular/core';
 import {SharedModule} from '../../shared/shared-module';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {
   DeviceCustomConfigOverview
 } from '../sub-components/device-custom-config-overview/device-custom-config-overview';
@@ -10,55 +8,20 @@
 import {
   DeviceCustomConfigTemplates
 } from '../sub-components/device-custom-config-templates/device-custom-config-templates';
+import {TabContainer, TabDefinition} from '../tab-container/tab-container';
 
 @Component({
   selector: 'app-config-hub',
-  imports: [SharedModule, DeviceCustomConfigOverview, DeviceClonedFiles, DeviceConfigOverview, DeviceCustomConfigTemplates],
+  imports: [SharedModule, TabContainer],
   templateUrl: './config-hub.html',
   styleUrl: './config-hub.scss'
 })
-export class ConfigHub implements OnInit {
+export class ConfigHub {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Device Configuration Files',
-    'Cloned Files',
-    'Custom Configuration',
-    'Custom Templates',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Device Configuration Files', component: DeviceCustomConfigOverview, paramName: 'device-config'},
+    {label: 'Cloned Files', component: DeviceClonedFiles, paramName: 'cloned-files'},
+    {label: 'Custom Configuration', component: DeviceConfigOverview, paramName: 'custom-config'},
+    {label: 'Custom Templates', component: DeviceCustomConfigTemplates, paramName: 'custom-templates'},
   ];
-
-  constructor(private route: ActivatedRoute, private router: Router) {
-  }
-
-  ngOnInit(): void {
-    this.route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this.router.navigate([], {
-        relativeTo: this.route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/dashboard/dashboard.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/dashboard/dashboard.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/dashboard/dashboard.html	(working copy)
@@ -1,18 +1 @@
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="System">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-dashboard-system-insights/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Devices & Services Overview">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-dashboard-device-insights/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/dashboard/dashboard.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/dashboard/dashboard.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/dashboard/dashboard.ts	(working copy)
@@ -4,53 +4,23 @@
 import {MatTabChangeEvent} from '@angular/material/tabs';
 import {DashboardSystemInsights} from '../sub-components/dashboard-system-insights/dashboard-system-insights';
 import {DashboardDeviceInsights} from '../sub-components/dashboard-device-insights/dashboard-device-insights';
+import {TabContainer, TabDefinition} from '../tab-container/tab-container';
+import {DashboardInsightsDevices} from '../sub-components/dashboard-insights-devices/dashboard-insights-devices';
 
 @Component({
   selector: 'app-dashboard',
-  imports: [SharedModule, DashboardSystemInsights, DashboardDeviceInsights],
+  imports: [SharedModule, TabContainer],
   templateUrl: './dashboard.html',
   styleUrl: './dashboard.scss'
 })
-export class Dashboard implements OnInit {
+export class Dashboard {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'System',
-    'Devices & Services Overview',
-  ];
-
-  constructor(private _route: ActivatedRoute, private _router: Router) {
-  }
-
-  ngOnInit(): void {
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
+  tabDefinitions: TabDefinition[] = [
+    {label: 'System', component: DashboardSystemInsights, paramName: 'system'},
+    {
+      label: 'Devices & Services Overview',
+      component: DashboardDeviceInsights,
+      paramName: 'devices'
+    },
+  ]
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/general-settings/general-settings.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/general-settings/general-settings.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/general-settings/general-settings.html	(working copy)
@@ -1,60 +1 @@
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Host">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-host></app-host>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="License">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-license></app-license>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Date & Time">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-system-time></app-system-time>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="System Update">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-system-update></app-system-update>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Network">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-network></app-network>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="AAA">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-admin-aaa></app-admin-aaa>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Log Settings">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-log-settings></app-log-settings>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Backup & Restore">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-system-backup-restore></app-system-backup-restore>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/general-settings/general-settings.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/general-settings/general-settings.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/general-settings/general-settings.ts	(working copy)
@@ -8,62 +8,25 @@
 import {Network} from '../sub-components/network/network';
 import {AdminAaa} from '../sub-components/admin-aaa/admin-aaa';
 import {SystemBackupRestore} from '../sub-components/system-backup-restore/system-backup-restore';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
+import {TabContainer, TabDefinition} from '../tab-container/tab-container';
 
 @Component({
   selector: 'app-general-settings',
   standalone: true,
-  imports: [SharedModule, Host, LogSettings, License, SystemTime, SystemUpdate, Network, AdminAaa, SystemBackupRestore],
+  imports: [SharedModule, TabContainer],
   templateUrl: './general-settings.html',
   styleUrl: './general-settings.scss',
 })
-export class GeneralSettings implements OnInit {
+export class GeneralSettings {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Host',
-    'License',
-    'Date & Time',
-    'System Update',
-    'Network',
-    'AAA',
-    'Log Settings',
-    'Backup & Restore',
+  tabDefinitions: TabDefinition[] = [
+    { label: 'Host', component: Host, paramName: 'Host' },
+    { label: 'License', component: License, paramName: 'License' },
+    { label: 'Date & Time', component: SystemTime, paramName: 'Date & Time' },
+    { label: 'System Update', component: SystemUpdate, paramName: 'System Update' },
+    { label: 'Network', component: Network, paramName: 'Network' },
+    { label: 'AAA', component: AdminAaa, paramName: 'AAA' },
+    { label: 'Log Settings', component: LogSettings, paramName: 'Log Settings' },
+    { label: 'Backup & Restore', component: SystemBackupRestore, paramName: 'Backup & Restore' },
   ];
-
-  constructor(private route: ActivatedRoute, private router: Router) {
-  }
-
-  ngOnInit(): void {
-    this.route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this.router.navigate([], {
-        relativeTo: this.route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/log-analysis/log-analysis.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/log-analysis/log-analysis.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/log-analysis/log-analysis.html	(working copy)
@@ -1,19 +1 @@
-<div class="tab-container">
-  <mat-tab-group mat-stretch-tabs="false" animationDuration="0ms" [selectedIndex]="selectedTabIndex"
-                 (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="APV - SLB - Virtual Services">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-log-analysis-slb-overview/>
-        </div>
-      </ng-template>
-    </mat-tab>
-<!--    <mat-tab label="SSLVPN Services">-->
-<!--      <ng-template matTabContent>-->
-<!--        <div class="tab-content">-->
-<!--          <app-log-analysis-ssl-vpn-overview/>-->
-<!--        </div>-->
-<!--      </ng-template>-->
-<!--    </mat-tab>-->
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/log-analysis/log-analysis.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/log-analysis/log-analysis.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/log-analysis/log-analysis.ts	(working copy)
@@ -1,55 +1,17 @@
 import {Component, OnInit} from '@angular/core';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {SharedModule} from '../../shared/shared-module';
 import {LogAnalysisSlbOverview} from '../sub-components/log-analysis-slb-overview/log-analysis-slb-overview';
+import {TabContainer, TabDefinition} from '../tab-container/tab-container';
 
 @Component({
   selector: 'app-log-analysis',
-  imports: [SharedModule, LogAnalysisSlbOverview],
+  imports: [SharedModule, TabContainer],
   templateUrl: './log-analysis.html',
   styleUrl: './log-analysis.scss'
 })
-export class LogAnalysis implements OnInit {
+export class LogAnalysis {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'APV - SLB - Virtual Services',
-    'SSLVPN Services',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'APV - SLB - Virtual Services', component: LogAnalysisSlbOverview, paramName: 'apv-slb-vs'},
   ];
-
-  constructor(private _route: ActivatedRoute, private _router: Router) {
-  }
-
-  ngOnInit(): void {
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/monitoring/monitoring.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/monitoring/monitoring.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/monitoring/monitoring.html	(working copy)
@@ -1,33 +1 @@
-<div class="tab-container">
-  <mat-tab-group mat-stretch-tabs="false" animationDuration="0ms" [selectedIndex]="selectedTabIndex"
-                 (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Devices">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-resource-monitoring-devices/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="APV">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-resource-monitoring-apv/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="ASF">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-resource-monitoring-asf/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="SSL VPN">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-resource-monitoring-ssl-vpn/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/monitoring/monitoring.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/monitoring/monitoring.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/monitoring/monitoring.ts	(working copy)
@@ -1,60 +1,23 @@
 import {Component, OnInit} from '@angular/core';
 import {SharedModule} from '../../shared/shared-module';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {ResourceMonitoringDevices} from '../sub-components/resource-monitoring-devices/resource-monitoring-devices';
 import {ResourceMonitoringApv} from '../sub-components/resource-monitoring-apv/resource-monitoring-apv';
 import {ResourceMonitoringSslVpn} from '../sub-components/resource-monitoring-ssl-vpn/resource-monitoring-ssl-vpn';
 import {ResourceMonitoringAsf} from '../sub-components/resource-monitoring-asf/resource-monitoring-asf';
+import {TabContainer, TabDefinition} from '../tab-container/tab-container';
 
 @Component({
   selector: 'app-monitoring',
-  imports: [SharedModule, ResourceMonitoringDevices, ResourceMonitoringApv, ResourceMonitoringSslVpn, ResourceMonitoringAsf],
+  imports: [SharedModule, TabContainer],
   templateUrl: './monitoring.html',
   styleUrl: './monitoring.scss'
 })
-export class Monitoring implements OnInit {
+export class Monitoring {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Devices',
-    'APV',
-    'ASF',
-    'SSL VPN',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Devices', component: ResourceMonitoringDevices, paramName: 'devices'},
+    {label: 'APV', component: ResourceMonitoringApv, paramName: 'apv'},
+    // {label: 'ASF', component: ResourceMonitoringAsf, paramName: 'asf'},
+    {label: 'SSL VPN', component: ResourceMonitoringSslVpn, paramName: 'ssl-vpn'},
   ];
-
-  constructor(private _route: ActivatedRoute, private _router: Router) {
-  }
-
-  ngOnInit(): void {
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/notification/notification.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/notification/notification.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/notification/notification.html	(working copy)
@@ -1,18 +1 @@
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Notification Channel">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-notification-channels/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Notification Settings">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-notification-settings/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/notification/notification.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/notification/notification.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/notification/notification.ts	(working copy)
@@ -1,59 +1,21 @@
-import {Component, OnInit} from '@angular/core';
-import {NotificationChannels} from '../sub-components/notification-channels/notification-channels';
+import {Component} from '@angular/core';
 import {NotificationSettings} from '../sub-components/notification-settings/notification-settings';
 import {SharedModule} from '../../shared/shared-module';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
+import {TabContainer, TabDefinition} from '../tab-container/tab-container';
+import {UserManagement} from '../sub-components/user-management/user-management';
 
 @Component({
   selector: 'app-notification',
   imports: [
     SharedModule,
-    NotificationChannels,
-    NotificationSettings
+    TabContainer
   ],
   templateUrl: './notification.html',
   styleUrl: './notification.scss'
 })
-export class Notification implements OnInit {
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Notification Channel',
-    'Notification Settings',
-  ];
-
-  constructor(private route: ActivatedRoute, private router: Router) {
-  }
-
-  ngOnInit(): void {
-    this.route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this.router.navigate([], {
-        relativeTo: this.route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
+export class Notification {
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Notification Channel', component: UserManagement, paramName: 'User Management'},
+    {label: 'Notification Settings', component: NotificationSettings, paramName: 'User Management'},
+  ]
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-device-details/avx-device-details.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-device-details/avx-device-details.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-device-details/avx-device-details.html	(working copy)
@@ -8,35 +8,4 @@
     </mat-card-title>
   </mat-card-header>
 </mat-card>
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Overview">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-avx-device-detail-overview/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="VA Deployments">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-avx-device-detail-deployments/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Configuration">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-avx-device-detail-configuration/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Log">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-avx-device-detail-logs/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-device-details/avx-device-details.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-device-details/avx-device-details.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-device-details/avx-device-details.ts	(working copy)
@@ -1,20 +1,17 @@
 import {Component, OnInit} from '@angular/core';
 import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {AvxDeviceDetailOverview} from '../avx-device-detail-overview/avx-device-detail-overview';
 import {AvxDeviceDetailDeployments} from '../avx-device-detail-deployments/avx-device-detail-deployments';
 import {AvxDeviceDetailConfiguration} from '../avx-device-detail-configuration/avx-device-detail-configuration';
 import {AvxDeviceDetailLogs} from '../avx-device-detail-logs/avx-device-detail-logs';
 import {SharedModule} from '../../../shared/shared-module';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-avx-device-details',
   imports: [
     SharedModule,
-    AvxDeviceDetailOverview,
-    AvxDeviceDetailDeployments,
-    AvxDeviceDetailConfiguration,
-    AvxDeviceDetailLogs
+    TabContainer
   ],
   templateUrl: './avx-device-details.html',
   styleUrl: './avx-device-details.scss'
@@ -23,12 +20,12 @@
 
   deviceName: any = '';
   deviceId: any = '';
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Overview',
-    'VA Deployments',
-    'Configuration',
-    'Log',
+
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Overview', component: AvxDeviceDetailOverview, paramName: 'overview'},
+    {label: 'VA Deployments', component: AvxDeviceDetailDeployments, paramName: 'va-deployments'},
+    {label: 'Configuration', component: AvxDeviceDetailConfiguration, paramName: 'configuration'},
+    {label: 'Log', component: AvxDeviceDetailLogs, paramName: 'log'},
   ];
 
   constructor(
@@ -40,39 +37,9 @@
   ngOnInit() {
     this.deviceName = this._route.snapshot.paramMap.get('deviceName');
     this.deviceId = this._route.snapshot.paramMap.get('deviceId');
-
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
   }
 
   navigateToAVXDevices() {
     this._router.navigate(['/avx/devices']);
   }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-instances-overview/avx-instances-overview.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-instances-overview/avx-instances-overview.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-instances-overview/avx-instances-overview.html	(working copy)
@@ -1,18 +1 @@
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="VA Instances">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-avx-va-instances/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="VA Backup">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-avx-va-backup/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-instances-overview/avx-instances-overview.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-instances-overview/avx-instances-overview.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-instances-overview/avx-instances-overview.ts	(working copy)
@@ -1,56 +1,19 @@
-import {Component, OnInit} from '@angular/core';
+import {Component} from '@angular/core';
 import {SharedModule} from '../../../shared/shared-module';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {AvxVaInstances} from '../avx-va-instances/avx-va-instances';
 import {AvxVaBackup} from '../avx-va-backup/avx-va-backup';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-avx-instances-overview',
-  imports: [SharedModule, AvxVaInstances, AvxVaBackup],
+  imports: [SharedModule, TabContainer],
   templateUrl: './avx-instances-overview.html',
   styleUrl: './avx-instances-overview.scss'
 })
-export class AvxInstancesOverview implements OnInit {
+export class AvxInstancesOverview {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'VA Instances',
-    'VA Backup',
-  ];
-
-  constructor(private route: ActivatedRoute, private router: Router) {
-  }
-
-  ngOnInit(): void {
-    this.route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this.router.navigate([], {
-        relativeTo: this.route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
+  tabDefinitions: TabDefinition[] = [
+    {label: 'VA Instances', component: AvxVaInstances, paramName: 'va-instances'},
+    {label: 'VA Backup', component: AvxVaBackup, paramName: 'va-backup'},
+  ]
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-va-instance-details/avx-va-instance-details.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-va-instance-details/avx-va-instance-details.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-va-instance-details/avx-va-instance-details.html	(working copy)
@@ -9,29 +9,4 @@
     <mat-card-subtitle>Device:{{ deviceName }} - Instance: {{ instanceName }}</mat-card-subtitle>
   </mat-card-header>
 </mat-card>
-<div class="tab-container">
-  <mat-tab-group mat-stretch-tabs="false" animationDuration="0ms" [selectedIndex]="selectedTabIndex"
-                 (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Overview">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-avx-va-instance-overview/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Configuration">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-avx-va-instance-configuration/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Logs">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-avx-va-instance-logs/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-va-instance-details/avx-va-instance-details.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-va-instance-details/avx-va-instance-details.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/avx-va-instance-details/avx-va-instance-details.ts	(working copy)
@@ -5,10 +5,11 @@
 import {AvxVaInstanceOverview} from '../avx-va-instance-overview/avx-va-instance-overview';
 import {AvxVaInstanceConfiguration} from '../avx-va-instance-configuration/avx-va-instance-configuration';
 import {AvxVaInstanceLogs} from '../avx-va-instance-logs/avx-va-instance-logs';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-avx-va-instance-details',
-  imports: [SharedModule, AvxVaInstanceOverview, AvxVaInstanceConfiguration, AvxVaInstanceLogs],
+  imports: [SharedModule, TabContainer],
   templateUrl: './avx-va-instance-details.html',
   styleUrl: './avx-va-instance-details.scss'
 })
@@ -18,11 +19,10 @@
   instanceName: any = '';
   deviceId: any = '';
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Overview',
-    'Configuration',
-    'Logs',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Overview', component: AvxVaInstanceOverview, paramName: 'overview'},
+    {label: 'Configuration', component: AvxVaInstanceOverview, paramName: 'configuration'},
+    {label: 'Logs', component: AvxVaInstanceOverview, paramName: 'logs'},
   ];
 
   constructor(
@@ -35,38 +35,8 @@
     this.deviceName = this._route.snapshot.paramMap.get('deviceName');
     this.deviceId = this._route.snapshot.paramMap.get('deviceId');
     this.instanceName = this._route.snapshot.paramMap.get('instanceName');
-
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
   }
 
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
-
   navigateToAVXInstances() {
     this._router.navigate(['/avx/instances']);
   }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-device-insights/dashboard-device-insights.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-device-insights/dashboard-device-insights.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-device-insights/dashboard-device-insights.html	(working copy)
@@ -1,39 +1 @@
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Devices">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-dashboard-insights-devices/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="APV">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-dashboard-insights-apv/>
-        </div>
-      </ng-template>
-    </mat-tab>
-<!--    <mat-tab label="ASF">-->
-<!--      <ng-template matTabContent>-->
-<!--        <div class="tab-content">-->
-<!--          <app-dashboard-insights-asf/>-->
-<!--        </div>-->
-<!--      </ng-template>-->
-<!--    </mat-tab>-->
-    <mat-tab label="SSL VPN">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-dashboard-insights-sslvpn/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="AVX">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-dashboard-insights-avx/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="subTab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-device-insights/dashboard-device-insights.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-device-insights/dashboard-device-insights.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-device-insights/dashboard-device-insights.ts	(working copy)
@@ -1,62 +1,24 @@
-import {Component, OnInit} from '@angular/core';
+import {Component} from '@angular/core';
 import {SharedModule} from '../../../shared/shared-module';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {DashboardInsightsDevices} from '../dashboard-insights-devices/dashboard-insights-devices';
 import {DashboardInsightsApv} from '../dashboard-insights-apv/dashboard-insights-apv';
 import {DashboardInsightsSslvpn} from '../dashboard-insights-sslvpn/dashboard-insights-sslvpn';
 import {DashboardInsightsAvx} from '../dashboard-insights-avx/dashboard-insights-avx';
-import {SystemService} from '../../../services/system-service';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-dashboard-device-insights',
-  imports: [SharedModule, DashboardInsightsDevices, DashboardInsightsApv, DashboardInsightsSslvpn, DashboardInsightsAvx],
+  imports: [SharedModule, TabContainer],
   templateUrl: './dashboard-device-insights.html',
   styleUrl: './dashboard-device-insights.scss'
 })
-export class DashboardDeviceInsights implements OnInit {
+export class DashboardDeviceInsights {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Devices',
-    'APV',
-    // 'ASF',
-    'SSL VPN',
-    'AVX',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Devices', component: DashboardInsightsDevices, paramName: 'devices'},
+    {label: 'APV', component: DashboardInsightsApv, paramName: 'apv'},
+    // { label: 'ASF', component: /* ASF Component */, paramName: 'asf' }, // Easily uncommented
+    {label: 'SSL VPN', component: DashboardInsightsSslvpn, paramName: 'ssl-vpn'},
+    {label: 'AVX', component: DashboardInsightsAvx, paramName: 'avx'},
   ];
-
-  constructor(private _route: ActivatedRoute, private _router: Router) {
-  }
-
-  ngOnInit(): void {
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['subTab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {subTab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
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)
@@ -1,4 +1,4 @@
-import {Component, OnChanges, OnInit, SimpleChanges} from '@angular/core';
+import {Component, DestroyRef, inject, OnChanges, OnInit, SimpleChanges} from '@angular/core';
 import {FaIconComponent} from "@fortawesome/angular-fontawesome";
 import {MatCard, MatCardContent, MatCardTitle} from "@angular/material/card";
 import {MatGridList, MatGridTile} from "@angular/material/grid-list";
@@ -7,9 +7,13 @@
 import {EChartsOption} from 'echarts';
 import {NotificationService} from '../../../services/notification';
 import {SystemService} from '../../../services/system-service';
-import {take} from 'rxjs/operators';
+import {startWith, take, tap} 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';
+import {interval} from 'rxjs';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
 
 @Component({
   selector: 'app-dashboard-insights-apv',
@@ -21,26 +25,32 @@
 })
 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 = [];
 
+  private destroyRef = inject(DestroyRef);
+
   constructor(
     private _notification: NotificationService,
     private _system: SystemService,
     private _device: DeviceService,
+    private _chartOptions: ChartOptions,
+    private _utils: UtilsService
   ) {
   }
 
@@ -51,13 +61,9 @@
   }
 
   ngOnChanges(changes: SimpleChanges): void {
-    if (changes['cpuUsagePercentage']) {
-    }
+    if (changes['cpuUsagePercentage']) {}
   }
 
-  devices: any[] = [];
-  groups: any[] = [];
-
   getDeviceGroups(): void {
     this.devices = [];
     this.groups = [];
@@ -79,9 +85,17 @@
                   this.devices.push(device);
                 })
               })
-              this.getTopAPVVirtualServicesMetrics();
-              this.getTopAPVRealServicesMetrics();
-              this.getTopAPVLLBMetrics();
+              interval(this._utils.AUTO_REFRESH_INTERVAL)
+                .pipe(
+                  startWith(0),
+                  tap(() => {
+                    this.getTopAPVVirtualServicesMetrics();
+                    this.getTopAPVRealServicesMetrics();
+                    this.getTopAPVLLBMetrics();
+                  }),
+                  takeUntilDestroyed(this.destroyRef)
+                )
+                .subscribe();
             }
           }
         }, error: (error: { message: string; }) => {
@@ -91,24 +105,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 +127,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 +149,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 +171,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-avx/dashboard-insights-avx.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-avx/dashboard-insights-avx.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-avx/dashboard-insights-avx.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]="cpuChartOption" 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]="memoryChartOption" class="chart-container"></div>
         </div>
       </mat-card-content>
     </mat-card>
@@ -32,11 +32,11 @@
       <mat-card-content class="card-content-wrapper">
         <div class="card-header-row">
           <mat-card-title class="card-title">
-            <span class="card-title-text">Throughput - Top 5</span>
+            <span class="card-title-text">Disk Usage - Top 5</span>
           </mat-card-title>
         </div>
         <div class="chart-flex-container">
-          <div echarts [options]="chartOption3" class="chart-container"></div>
+          <div echarts [options]="diskChartOption" class="chart-container"></div>
         </div>
       </mat-card-content>
     </mat-card>
@@ -46,11 +46,12 @@
       <mat-card-content class="card-content-wrapper">
         <div class="card-header-row">
           <mat-card-title class="card-title">
-            <span class="card-title-text">Connection Status</span>
+            <span class="card-title-text">Connection Status ( {{ deviceStats?.total?.connected }}
+              /{{ deviceStats?.total?.connected + deviceStats?.total?.disconnected }})</span>
           </mat-card-title>
         </div>
         <div class="chart-flex-container">
-          <div echarts [options]="chartOption4" class="chart-container"></div>
+          <div echarts [options]="connectionChartOption" 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-avx/dashboard-insights-avx.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-avx/dashboard-insights-avx.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-avx/dashboard-insights-avx.ts	(working copy)
@@ -1,7 +1,19 @@
-import {Component, OnChanges, OnInit, SimpleChanges} from '@angular/core';
+import {Component, DestroyRef, inject, OnChanges, OnInit, SimpleChanges} from '@angular/core';
 import {SharedModule} from '../../../shared/shared-module';
 import {EChartsOption} from 'echarts';
+import {AvxService} from '../../../services/avx-service';
+import {NotificationService} from '../../../services/notification';
+import {startWith, take, tap} from 'rxjs/operators';
+import {UtilsService} from '../../../services/utils-service';
+import {ChartOptions} from '../../../services/chart-options';
+import {interval} from 'rxjs';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
 
+interface DeviceMetric {
+  name: string;
+  value: number;
+}
+
 @Component({
   selector: 'app-dashboard-insights-avx',
   imports: [
@@ -12,160 +24,84 @@
 })
 export class DashboardInsightsAvx implements OnInit, OnChanges {
 
-  chartOption1: EChartsOption = {};
-  chartOption2: EChartsOption = {};
-  chartOption3: EChartsOption = {};
-  chartOption4: EChartsOption = {};
+  connectionChartOption: EChartsOption = {};
+  cpuChartOption: EChartsOption = {};
+  memoryChartOption: EChartsOption = {};
+  diskChartOption: EChartsOption = {};
 
-  ngOnInit(): void {
-    this.updateChartOption();
+  private destroyRef = inject(DestroyRef);
+
+  constructor(
+    private _avx: AvxService,
+    private _notification: NotificationService,
+    private _utils: UtilsService,
+    private _chartOptions: ChartOptions
+  ) {
   }
 
-  ngOnChanges(changes: SimpleChanges): void {
-    if (changes['cpuUsagePercentage']) {
-      this.updateChartOption();
-    }
+  ngOnInit(): void {
+    setTimeout(() => {
+      interval(this._utils.AUTO_REFRESH_INTERVAL)
+        .pipe(
+          startWith(0),
+          tap(() => {
+            this.getAVXDevices();
+          }),
+          takeUntilDestroyed(this.destroyRef)
+        )
+        .subscribe();
+    })
   }
 
-  private updateChartOption() {
-    let _data: any = {
-      cpu: [
-        {name: 'AVX1', value: 60},
-        {name: 'AVX2', value: 50},
-        {name: 'AVX3', value: 44},
-      ],
-      memory: [
-        {name: 'AVX1', value: 40},
-        {name: 'AVX2', value: 50},
-        {name: 'AVX3', value: 64},
-      ],
-      disk: [
-        {name: 'AVX1', value: 10},
-        {name: 'AVX2', value: 80},
-        {name: 'AVX3', value: 10},
-      ]
-    }
-    _data?.cpu.sort((a: any, b: any) => b.value - a.value);
-    _data?.memory.sort((a: any, b: any) => b.value - a.value);
-    _data?.disk.sort((a: any, b: any) => b.value - a.value);
+  avxDevices: any = [];
+  deviceStats: any = [];
+  deviceConnectionStats: any = [];
 
-    this.chartOption1 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
+  getAVXDevices() {
+    this.avxDevices = [];
+    this._avx.getAVXDevices().pipe(take(1)).subscribe({
+      next: (result: any) => {
+        if (!result || result.length === 0) {
+          return;
         }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      xAxis: {
-        type: 'value',
-        boundaryGap: [0, 0.01],
-        max: 100
-      },
-      yAxis: {
-        type: 'category',
-        data: _data?.cpu.map((item: any) => item?.name),
-        inverse: true
-      },
-      series: [
-        {
-          name: 'CPU %',
-          type: 'bar',
-          data: _data?.cpu.map((item: any) => item?.value)
-        }
-      ]
-    };
+        this.avxDevices = result;
+        const metrics: { cpu: DeviceMetric[], memory: DeviceMetric[], disk: DeviceMetric[] } = {
+          cpu: [],
+          memory: [],
+          disk: [],
+        };
 
-    this.chartOption2 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      xAxis: {
-        type: 'value',
-        boundaryGap: [0, 0.01],
-        max: 100
-      },
-      yAxis: {
-        type: 'category',
-        data: _data?.memory.map((item: any) => item?.name),
-        inverse: true
-      },
-      series: [
-        {
-          name: 'Memory %',
-          type: 'bar',
-          data: _data?.memory.map((item: any) => item?.value)
-        }
-      ]
-    };
+        this.avxDevices.forEach((_device: any) => {
+          const name = _device?.name;
+          if (name) {
+            metrics.cpu.push({name, value: _device?.cpu_usage});
+            metrics.memory.push({name, value: _device?.memory_usage});
+            metrics.disk.push({name, value: _device?.disk_usage});
+          }
+        })
+        const sortByValueDesc = (a: DeviceMetric, b: DeviceMetric) => b.value - a.value;
 
-    this.chartOption3 = {
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          type: 'shadow'
-        }
-      },
-      grid: {
-        left: '3%',
-        containLabel: true
-      },
-      xAxis: {
-        type: 'value',
-        boundaryGap: [0, 0.01],
-        max: 100
-      },
-      yAxis: {
-        type: 'category',
-        data: _data?.disk.map((item: any) => item?.name),
-        inverse: true
-      },
-      series: [
-        {
-          name: 'Disk %',
-          type: 'bar',
-          data: _data?.disk.map((item: any) => item?.value)
-        }
-      ]
-    };
+        this.deviceStats = this._utils.transformTotalConnectedDevicesMetrics(result);
+        this.deviceConnectionStats = this._utils.transformConnectedDevicesMetrics(this.deviceStats.types.entries());
 
-    this.chartOption4 = {
-      tooltip: {
-        trigger: 'item'
+        const sortedCpuMetrics = metrics.cpu.sort(sortByValueDesc);
+        const sortedMemoryMetrics = metrics.memory.sort(sortByValueDesc);
+        const sortedDiskMetrics = metrics.disk.sort(sortByValueDesc);
+
+        this.connectionChartOption = this._chartOptions.connectedDevicesDoughnutChartOptions(this.deviceStats?.total, true);
+        this.cpuChartOption = this._chartOptions.lineChartOptions('CPU %', sortedCpuMetrics);
+        this.memoryChartOption = this._chartOptions.lineChartOptions('Memory %', sortedMemoryMetrics);
+        this.diskChartOption = this._chartOptions.lineChartOptions('Disk %', sortedDiskMetrics);
       },
-      legend: {
-        top: '5%',
-        left: 'center'
-      },
-      series: [
-        {
-          name: 'Devices Status',
-          type: 'pie',
-          radius: ['40%', '70%'],
-          avoidLabelOverlap: false,
-          label: {
-            show: false,
-            position: 'bottom'
-          },
-          labelLine: {
-            show: false
-          },
-          data: [
-            {value: 3, name: 'Connected', itemStyle: {color: '#4CAF50'}},
-            {value: 1, name: 'Disconnected', itemStyle: {color: '#F44336'}}
-          ]
-        }
-      ]
+      error: error => {
+        this._notification.showError(`Error: ${error?.message}`);
+        console.log(error);
+      }
+    });
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes['cpuUsagePercentage']) {
     }
   }
 }
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)
@@ -1,21 +1,15 @@
-import {Component, OnChanges, OnInit, SimpleChanges} from '@angular/core';
+import {Component, DestroyRef, inject, OnChanges, OnInit, SimpleChanges} from '@angular/core';
 import {EChartsOption} from 'echarts';
 import {SharedModule} from '../../../shared/shared-module';
-import {take} from 'rxjs/operators';
+import {startWith, take, tap} from 'rxjs/operators';
 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';
+import {interval} from 'rxjs';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
 
-export interface DeviceTypeStatus {
-  connected: number;
-  disconnected: number;
-}
-
-export interface DeviceGraphData {
-  total: DeviceTypeStatus;
-  types: Map<string, DeviceTypeStatus>;
-}
-
 @Component({
   selector: 'app-dashboard-insights-devices',
   imports: [
@@ -31,30 +25,40 @@
   devicesThroughputChartOption1: EChartsOption = {};
   connectionChartOption1: EChartsOption = {};
 
+  devices: any[] = [];
+  groups: any[] = [];
+  deviceStats: any = {}
+  deviceConnectionStats: any = {}
+  connectedDevices: number = 0;
+  connectedDevicesMetrics: any = [];
+
+  private destroyRef = inject(DestroyRef);
+
   constructor(
     private _device: DeviceService,
     private _system: SystemService,
     private _notification: NotificationService,
+    private _utils: UtilsService,
+    private _chartOptions: ChartOptions
   ) {
   }
+
   ngOnInit(): void {
     setTimeout(() => {
-      this.getDeviceGroups();
+      interval(this._utils.AUTO_REFRESH_INTERVAL)
+        .pipe(
+          startWith(0),
+          tap(() => this.getDeviceGroups()),
+          takeUntilDestroyed(this.destroyRef)
+        )
+        .subscribe();
     })
   }
 
   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 +80,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 +94,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 +110,11 @@
       });
   }
 
-  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 || {};
+    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,15 @@
-import {Component, OnChanges, OnInit, SimpleChanges} from '@angular/core';
+import {Component, DestroyRef, inject, 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 {startWith, take, tap} from 'rxjs/operators';
+import {ChartOptions} from '../../../services/chart-options';
+import {UtilsService} from '../../../services/utils-service';
+import {interval} from 'rxjs';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
 
 @Component({
   selector: 'app-dashboard-insights-sslvpn',
@@ -10,158 +19,97 @@
 })
 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 = [];
+
+  private destroyRef = inject(DestroyRef);
+
+  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);
+                })
+              })
+              interval(this._utils.AUTO_REFRESH_INTERVAL)
+                .pipe(
+                  startWith(0),
+                  tap(() => {
+                    this.getTopSSLVPNMetrics();
+                  }),
+                  takeUntilDestroyed(this.destroyRef)
+                )
+                .subscribe();
+            }
+          }
+        }, 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>
@@ -144,6 +144,8 @@
         <div class="card-header-row">
           <mat-card-title class="card-title">
             <span class="card-title-text">Devices</span>
+            <fa-icon [icon]="['fas', 'expand']" class="fa-1x a-link" matTooltip="Expand"
+                     (click)="enlargeSystemDevices()"></fa-icon>
           </mat-card-title>
         </div>
         <div class="chart-flex-container">
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)
@@ -1,23 +1,21 @@
-import {Component, inject, OnChanges, OnInit, SimpleChanges, ViewEncapsulation} from '@angular/core';
+import {Component, DestroyRef, inject, OnChanges, OnInit, SimpleChanges, ViewEncapsulation} from '@angular/core';
 import {SharedModule} from '../../../shared/shared-module';
 import {EChartsOption} from 'echarts';
 import {MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material/dialog';
-import {take} from 'rxjs/operators';
+import {startWith, take, tap} from 'rxjs/operators';
 import {DeviceService} from '../../../services/device-service';
 import {NotificationService} from '../../../services/notification';
 import {SystemService} from '../../../services/system-service';
 import {BytesPipe} from '../../../pipes/bytes-pipe';
+import {ChartOptions} from '../../../services/chart-options';
+import {KeyValuePair, UtilsService} from '../../../services/utils-service';
+import {interval} from 'rxjs';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
+import {MatTableDataSource} from '@angular/material/table';
+import {GlobalSerialPipe} from '../../../pipes/global-serial-pipe';
+import {Router} from '@angular/router';
 
-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],
@@ -38,18 +36,27 @@
   connectionChartOption1: EChartsOption = {};
   connectionChartOption2: EChartsOption = {};
 
+  private destroyRef = inject(DestroyRef);
+
   constructor(
     private _device: DeviceService,
     private _system: SystemService,
     private _notification: NotificationService,
+    private _chartOptions: ChartOptions,
+    private _utils: UtilsService,
   ) {
   }
 
   ngOnInit(): void {
     setTimeout(() => {
       this.getDeviceGroups();
-      this.getLatestSystemMetrics();
-      this.getHistoricalSystemMetrics();
+      interval(this._utils.AUTO_REFRESH_INTERVAL)
+        .pipe(
+          startWith(0),
+          tap(() => this.getSystemMetrics()),
+          takeUntilDestroyed(this.destroyRef)
+        )
+        .subscribe();
     })
   }
 
@@ -60,6 +67,11 @@
   diskMetrics: any = {};
   networkMetrics: any = {};
 
+  getSystemMetrics(): void {
+    this.getLatestSystemMetrics();
+    this.getHistoricalSystemMetrics();
+  }
+
   getLatestSystemMetrics() {
     this._system.getLatestSystemMetrics()
       .pipe(take(1))
@@ -128,13 +140,13 @@
                 this.groups.push(group?.group_name);
                 group?.device_list.forEach((device: any) => {
                   this.devices.push(device);
-                  if (device?.type.toLowerCase() === 'apv' || device?.type.toLowerCase() === 'vapv') {
+                  if (device.connection && (device?.type.toLowerCase() === 'apv' || device?.type.toLowerCase() === 'vapv')) {
                     this.getAPVVirtualServices(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;
             }
           }
@@ -159,51 +171,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 +196,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);
@@ -617,16 +233,8 @@
     })
   }
 
-  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;
+    // this.dialogConfig.width = '50%';
     const dialogRef = this.dialog.open(EnlargeSystemDevices, this.dialogConfig);
     dialogRef.afterClosed().subscribe((isAdded: boolean) => {
     })
@@ -634,135 +242,136 @@
 }
 
 @Component({
-  selector: 'enlarge-system-load',
-  templateUrl: './enlarge-system-load.html',
-  imports: [SharedModule]
+  selector: 'enlarge-system-devices',
+  templateUrl: './enlarge-system-devices.html',
+  imports: [SharedModule, GlobalSerialPipe]
 })
-export class EnlargeSystemLoad implements OnInit {
-
-  chartOption1: EChartsOption = {};
-
+export class EnlargeSystemDevices implements OnInit {
   readonly data = inject(MAT_DIALOG_DATA);
-  readonly dialogRef = inject(MatDialogRef<EnlargeSystemLoad>);
+  readonly dialogRef = inject(MatDialogRef<EnlargeSystemDevices>);
+  private destroyRef = inject(DestroyRef);
 
   constructor(
-    private _system: SystemService,
+    private _router: Router,
     private _notification: NotificationService,
+    private _utils: UtilsService,
+    private _device: DeviceService,
   ) {
   }
 
   ngOnInit() {
     setTimeout(() => {
-      this.getHistoricalSystemMetrics();
+      interval(this._utils.AUTO_REFRESH_INTERVAL)
+        .pipe(
+          startWith(0),
+          tap(() => {
+            this.getDeviceStatus();
+          }),
+          takeUntilDestroyed(this.destroyRef)
+        )
+        .subscribe();
     })
   }
 
-  cpuMetrics: any = {};
-  memoryMetrics: any = {};
+  devices: any[] = [];
+  groups: any[] = [];
+  deviceStats: any = {};
+  deviceStatusColumns: Array<string> = ['serial', 'deviceName', 'deviceGroup', 'ip'];
+  deviceStatusDataSource: MatTableDataSource<any> = new MatTableDataSource();
 
-  getHistoricalSystemMetrics() {
-    this._system.getHistoricalSystemMetrics()
+  getDeviceStatus() {
+    this.devices = [];
+    this.groups = [];
+    this.deviceStatusDataSource.data = [];
+    let offlineDevices: any = [];
+    // ToDo: Update with actual RoleId
+    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) => {
-          this.cpuMetrics = result?.cpu_history;
-          this.memoryMetrics = result?.mem_history;
-          this.updateChartOptions();
-        },
-        error: (error: { message: string; }) => {
+          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);
+                  if (!device?.connection) {
+                    offlineDevices.push(device);
+                  }
+                })
+              })
+              this.deviceStats = this._utils.transformTotalConnectedDevicesMetrics(this.devices);
+              this.deviceStatusDataSource.data = offlineDevices;
+            }
+          }
+        }, error: (error: { message: string; }) => {
           console.log(error);
           this._notification.showError(error.message);
         }
-      });
+      })
   }
 
-  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'
-        }
-      ]
-    };
+  navigateToDevice(device: any) {
+    this._router.navigate(['/inventory/devices', device.name], {
+      state: {deviceData: device, groups: this.groups}
+    });
+    this.dialogRef.close();
   }
-
   onCancel() {
     this.dialogRef.close();
   }
 }
 
 @Component({
-  selector: 'enlarge-system-throughput',
-  templateUrl: './enlarge-system-throughput.html',
+  selector: 'enlarge-system-load',
+  templateUrl: './enlarge-system-load.html',
   imports: [SharedModule]
 })
-export class EnlargeSystemThroughput implements OnInit {
+export class EnlargeSystemLoad implements OnInit {
 
   chartOption1: EChartsOption = {};
 
   readonly data = inject(MAT_DIALOG_DATA);
-  readonly dialogRef = inject(MatDialogRef<EnlargeSystemThroughput>);
+  readonly dialogRef = inject(MatDialogRef<EnlargeSystemLoad>);
+  private destroyRef = inject(DestroyRef);
 
   constructor(
     private _system: SystemService,
     private _notification: NotificationService,
+    private _chartOptions: ChartOptions,
+    private _utils: UtilsService
   ) {
   }
 
   ngOnInit() {
     setTimeout(() => {
-      this.getHistoricalSystemMetrics();
+      interval(this._utils.AUTO_REFRESH_INTERVAL)
+        .pipe(
+          startWith(0),
+          tap(() => {
+            this.getHistoricalSystemMetrics();
+          }),
+          takeUntilDestroyed(this.destroyRef)
+        )
+        .subscribe();
     })
   }
 
-  networkMetrics: any = {};
+  cpuMetrics: any = {};
+  memoryMetrics: any = {};
 
   getHistoricalSystemMetrics() {
     this._system.getHistoricalSystemMetrics()
       .pipe(take(1))
       .subscribe({
         next: (result: any) => {
-          this.networkMetrics = result?.net_history;
+          this.cpuMetrics = result?.cpu_history;
+          this.memoryMetrics = result?.mem_history;
           this.updateChartOptions();
         },
         error: (error: { message: string; }) => {
@@ -773,53 +382,9 @@
   }
 
   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
-        }
-      ]
-    };
+    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 = this._chartOptions.historicalCPUMemoryChartOptions(cpu_data_formatted, memory_data_formatted);
   }
 
   onCancel() {
@@ -828,39 +393,48 @@
 }
 
 @Component({
-  selector: 'enlarge-system-disk-usage',
-  templateUrl: './enlarge-system-disk-usage.html',
-  imports: [SharedModule],
-  encapsulation: ViewEncapsulation.None
+  selector: 'enlarge-system-throughput',
+  templateUrl: './enlarge-system-throughput.html',
+  imports: [SharedModule]
 })
-export class EnlargeSystemDiskUsage implements OnInit {
+export class EnlargeSystemThroughput implements OnInit {
 
   chartOption1: EChartsOption = {};
-  chartOption2: EChartsOption = {};
 
   readonly data = inject(MAT_DIALOG_DATA);
-  readonly dialogRef = inject(MatDialogRef<EnlargeSystemDiskUsage>);
+  readonly dialogRef = inject(MatDialogRef<EnlargeSystemThroughput>);
+  private destroyRef = inject(DestroyRef);
 
   constructor(
     private _system: SystemService,
     private _notification: NotificationService,
+    private _chartOptions: ChartOptions,
+    private _utils: UtilsService
   ) {
   }
 
   ngOnInit() {
     setTimeout(() => {
-      this.getHistoricalSystemMetrics();
+      interval(this._utils.AUTO_REFRESH_INTERVAL)
+        .pipe(
+          startWith(0),
+          tap(() => {
+            this.getHistoricalSystemMetrics();
+          }),
+          takeUntilDestroyed(this.destroyRef)
+        )
+        .subscribe();
     })
   }
 
-  diskIOMetrics: any = {};
+  networkMetrics: any = {};
 
   getHistoricalSystemMetrics() {
     this._system.getHistoricalSystemMetrics()
       .pipe(take(1))
       .subscribe({
         next: (result: any) => {
-          this.diskIOMetrics = result?.disk_io_history;
+          this.networkMetrics = result?.net_history;
           this.updateChartOptions();
         },
         error: (error: { message: string; }) => {
@@ -871,161 +445,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/components/sub-components/dashboard-system-insights/enlarge-system-devices.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/enlarge-system-devices.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/enlarge-system-devices.html	(working copy)
@@ -1,12 +1,74 @@
-<h2 mat-dialog-title>System Devices Status ({{data?.total?.connected}}/{{data?.total?.connected + data?.total?.disconnected}})</h2>
+<h2 mat-dialog-title>Disconnected Devices - ({{ deviceStats?.total?.disconnected }}
+  /{{ deviceStats?.total?.connected + deviceStats?.total?.disconnected }})</h2>
 <mat-dialog-content>
   <div class="dialog-charts-wrapper">
-    <div echarts [options]="chartOption1" class="dialog-chart-container"></div>
-    <div echarts [options]="chartOption2" class="dialog-chart-container"></div>
+    @if (deviceStats?.total?.disconnected === 0) {
+      <div class="center">
+        <p>All devices are connected.</p>
+      </div>
+    } @else {
+      <mat-card class="page-card-1" appearance="filled">
+        <mat-card-content>
+          <div class="table-container">
+            <div class="mat-elevation-z0">
+              <table mat-table [dataSource]="deviceStatusDataSource">
+                <ng-container matColumnDef="serial">
+                  <th mat-header-cell *matHeaderCellDef>ID</th>
+                  <td mat-cell *matCellDef="let element; let i = index;"> {{ i | globalSerial }}</td>
+                </ng-container>
+                <ng-container matColumnDef="deviceName">
+                  <th mat-header-cell *matHeaderCellDef>Device Name</th>
+                  <td mat-cell *matCellDef="let element">
+                    <a class="details-page-link" (click)="navigateToDevice(element)">
+                      {{ element?.name }}
+                    </a>
+                  </td>
+                </ng-container>
+                <ng-container matColumnDef="deviceGroup">
+                  <th mat-header-cell *matHeaderCellDef>Model</th>
+                  <td mat-cell *matCellDef="let element">{{ element?.model }}</td>
+                </ng-container>
+                <ng-container matColumnDef="ip">
+                  <th mat-header-cell *matHeaderCellDef>IP Address</th>
+                  <td mat-cell *matCellDef="let element">{{ element?.ip }}</td>
+                </ng-container>
+                <tr mat-header-row *matHeaderRowDef="deviceStatusColumns"></tr>
+                <tr mat-row *matRowDef="let row; columns: deviceStatusColumns;"></tr>
+                <tr class="mat-row table-no-data" *matNoDataRow>
+                  <td class="mat-cell" colspan="11">No results found.</td>
+                </tr>
+              </table>
+            </div>
+          </div>
+        </mat-card-content>
+      </mat-card>
+    }
   </div>
 </mat-dialog-content>
 <mat-dialog-actions>
-  <button mat-button color="basic" (click)="onCancel()">
-    Close
-  </button>
+  <button mat-button color="basic" (click)="onCancel()">Close</button>
 </mat-dialog-actions>
+
+<style>
+  .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;
+  }
+</style>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-details/device-details.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-details/device-details.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-details/device-details.ts	(working copy)
@@ -7,11 +7,7 @@
 import {take} from 'rxjs/operators';
 import {NotificationService} from '../../../services/notification';
 import {DeviceService} from '../../../services/device-service';
-
-export interface KeyValuePair {
-  key: string;
-  value: any;
-}
+import {KeyValuePair} from '../../../services/utils-service';
 
 @Component({
   selector: 'app-device-details',
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ha-overview/device-ha-overview.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ha-overview/device-ha-overview.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ha-overview/device-ha-overview.html	(working copy)
@@ -8,28 +8,4 @@
     </mat-card-title>
   </mat-card-header>
 </mat-card>
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Base Settings">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-ha-base-settings/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="HA Unit">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-ha-unit/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="HA Group">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-ha-group/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ha-overview/device-ha-overview.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ha-overview/device-ha-overview.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ha-overview/device-ha-overview.ts	(working copy)
@@ -1,31 +1,27 @@
 import {Component, OnInit} from '@angular/core';
 import {SharedModule} from '../../../shared/shared-module';
 import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {DeviceHaBaseSettings} from '../device-ha-base-settings/device-ha-base-settings';
 import {DeviceHaUnit} from '../device-ha-unit/device-ha-unit';
 import {DeviceHaGroup} from '../device-ha-group/device-ha-group';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-device-ha-overview',
   imports: [
     SharedModule,
-    DeviceHaBaseSettings,
-    DeviceHaUnit,
-    DeviceHaGroup,
-
+    TabContainer,
   ],
   templateUrl: './device-ha-overview.html',
   styleUrl: './device-ha-overview.scss'
 })
 export class DeviceHaOverview implements OnInit {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Base Settings',
-    'HA Unit',
-    'HA Group',
-  ];
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Base Settings', component: DeviceHaBaseSettings, paramName: 'settings'},
+    {label: 'HA Unit', component: DeviceHaUnit, paramName: 'unit'},
+    {label: 'HA Group', component: DeviceHaGroup, paramName: 'group'},
+  ]
 
   deviceName: string | null = null;
 
@@ -38,35 +34,6 @@
   }
 
   ngOnInit(): void {
-    this.route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
   }
 
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this.router.navigate([], {
-        relativeTo: this.route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
-
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ssl-overview/device-ssl-overview.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ssl-overview/device-ssl-overview.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ssl-overview/device-ssl-overview.html	(working copy)
@@ -8,28 +8,4 @@
     </mat-card-title>
   </mat-card-header>
 </mat-card>
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Certificate List">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-ssl-certificates/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Intermediate CA Certificate List">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-ssl-inter-ca-certificates/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Certificate Backup/Restore">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-device-ssl-certificate-backup-restore/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ssl-overview/device-ssl-overview.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ssl-overview/device-ssl-overview.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-ssl-overview/device-ssl-overview.ts	(working copy)
@@ -1,5 +1,4 @@
 import {Component, OnInit} from '@angular/core';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {SharedModule} from '../../../shared/shared-module';
 import {ActivatedRoute, Router} from '@angular/router';
 import {DeviceSslCertificates} from '../device-ssl-certificates/device-ssl-certificates';
@@ -7,23 +6,27 @@
 import {
   DeviceSslCertificateBackupRestore
 } from '../device-ssl-certificate-backup-restore/device-ssl-certificate-backup-restore';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-device-ssl-overview',
-  imports: [SharedModule, DeviceSslCertificates, DeviceSslInterCaCertificates, DeviceSslCertificateBackupRestore],
+  imports: [SharedModule, TabContainer],
   templateUrl: './device-ssl-overview.html',
   styleUrl: './device-ssl-overview.scss'
 })
 export class DeviceSslOverview implements OnInit {
 
-  selectedTabIndex: number = 0;
   deviceName: string | null = '';
   serviceName: string | null = '';
   hostName: string | null = '';
-  private tabNames: string[] = [
-    'Certificate List',
-    'Intermediate CA Certificate List',
-    'Certificate Backup/Restore',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Certificate List', component: DeviceSslCertificates, paramName: 'certificates'},
+    {label: 'Intermediate CA Certificate List', component: DeviceSslInterCaCertificates, paramName: 'intermediate-ca'},
+    {
+      label: 'Certificate Backup/Restore',
+      component: DeviceSslCertificateBackupRestore,
+      paramName: 'certificate-backup-restore'
+    },
   ];
 
   constructor(private _route: ActivatedRoute, private _router: Router) {
@@ -33,39 +36,9 @@
     this.deviceName = this._route.snapshot.paramMap.get('deviceName');
     this.serviceName = this._route.snapshot.paramMap.get('serviceName');
     this.hostName = this._route.snapshot.paramMap.get('hostName');
-
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
   }
 
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
   backToSSLCertificateManagement(): void {
     this._router.navigate(['/device/apv/ssl']);
   }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/devices/devices.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/devices/devices.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/devices/devices.ts	(working copy)
@@ -11,11 +11,7 @@
 import {CustomValidators} from '../../../utils/custom-validators';
 import {Router} from '@angular/router';
 import {GlobalSerialPipe} from '../../../pipes/global-serial-pipe';
-
-export interface KeyValuePair {
-  key: string;
-  value: any;
-}
+import {KeyValuePair} from '../../../services/utils-service';
 
 @Component({
   selector: 'app-devices',
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/license/license.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/license/license.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/license/license.ts	(working copy)
@@ -3,16 +3,11 @@
 import {take} from 'rxjs/operators';
 import {MatTableDataSource} from '@angular/material/table';
 import {NotificationService} from '../../../services/notification';
-import {UtilsService} from '../../../services/utils-service';
+import {KeyValuePair, UtilsService} from '../../../services/utils-service';
 import {SharedModule} from '../../../shared/shared-module';
 import {MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material/dialog';
 import {FormBuilder, FormGroup, Validators} from '@angular/forms';
 
-export interface KeyValuePair {
-  key: string;
-  value: any;
-}
-
 @Component({
   selector: 'app-license',
   imports: [
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/resource-monitoring-apv/resource-monitoring-apv.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/resource-monitoring-apv/resource-monitoring-apv.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/resource-monitoring-apv/resource-monitoring-apv.html	(working copy)
@@ -1,26 +1 @@
-<div class="tab-container">
-  <mat-tab-group mat-stretch-tabs="false" animationDuration="0ms" [selectedIndex]="selectedTabIndex"
-                 (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="SLB - Virtual Services">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-resource-monitoring-slb-virtual/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="SLB - Real Services">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-resource-monitoring-slb-real/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="LLB">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-resource-monitoring-llb/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="subTab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/resource-monitoring-apv/resource-monitoring-apv.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/resource-monitoring-apv/resource-monitoring-apv.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/resource-monitoring-apv/resource-monitoring-apv.ts	(working copy)
@@ -1,58 +1,21 @@
-import {Component, OnInit} from '@angular/core';
+import {Component} from '@angular/core';
 import {SharedModule} from '../../../shared/shared-module';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {ResourceMonitoringLlb} from '../resource-monitoring-llb/resource-monitoring-llb';
 import {ResourceMonitoringSlbVirtual} from '../resource-monitoring-slb-virtual/resource-monitoring-slb-virtual';
 import {ResourceMonitoringSlbReal} from '../resource-monitoring-slb-real/resource-monitoring-slb-real';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-resource-monitoring-apv',
-  imports: [SharedModule, ResourceMonitoringLlb, ResourceMonitoringSlbVirtual, ResourceMonitoringSlbReal,],
+  imports: [SharedModule, TabContainer,],
   templateUrl: './resource-monitoring-apv.html',
   styleUrl: './resource-monitoring-apv.scss'
 })
-export class ResourceMonitoringApv implements OnInit {
+export class ResourceMonitoringApv {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'SLB - Virtual Services',
-    'SLB - Real Services',
-    'LLB',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'SLB - Virtual Services', component: ResourceMonitoringSlbVirtual, paramName: 'slb-virtual'},
+    {label: 'SLB - Real Services', component: ResourceMonitoringSlbReal, paramName: 'slb-real'},
+    {label: 'LLB', component: ResourceMonitoringLlb, paramName: 'llb'},
   ];
-
-  constructor(private _route: ActivatedRoute, private _router: Router) {
-  }
-
-  ngOnInit(): void {
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['subTab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {subTab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-acl-groups/vpn-acl-groups.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-acl-groups/vpn-acl-groups.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-acl-groups/vpn-acl-groups.html	(working copy)
@@ -8,21 +8,22 @@
     </mat-card-title>
   </mat-card-header>
 </mat-card>
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="ACL Resource">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vpn-acl-resources/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Rule">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vpn-acl-rules/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<!--<div class="tab-container">-->
+<!--  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">-->
+<!--    <mat-tab label="ACL Resource">-->
+<!--      <ng-template matTabContent>-->
+<!--        <div class="tab-content">-->
+<!--          <app-vpn-acl-resources/>-->
+<!--        </div>-->
+<!--      </ng-template>-->
+<!--    </mat-tab>-->
+<!--    <mat-tab label="Rule">-->
+<!--      <ng-template matTabContent>-->
+<!--        <div class="tab-content">-->
+<!--          <app-vpn-acl-rules/>-->
+<!--        </div>-->
+<!--      </ng-template>-->
+<!--    </mat-tab>-->
+<!--  </mat-tab-group>-->
+<!--</div>-->
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-acl-groups/vpn-acl-groups.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-acl-groups/vpn-acl-groups.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-acl-groups/vpn-acl-groups.ts	(working copy)
@@ -1,21 +1,15 @@
 import {Component, OnInit} from '@angular/core';
-import {FaIconComponent} from '@fortawesome/angular-fontawesome';
-import {MatCard, MatCardHeader, MatCardTitle} from '@angular/material/card';
-import {MatTab, MatTabChangeEvent, MatTabContent, MatTabGroup} from '@angular/material/tabs';
-import {VpnAclResourceGroups} from '../vpn-acl-resource-groups/vpn-acl-resource-groups';
-import {VpnHardwareId} from '../vpn-hardware-id/vpn-hardware-id';
-import {VpnResourceGroups} from '../vpn-resource-groups/vpn-resource-groups';
 import {SharedModule} from '../../../shared/shared-module';
 import {ActivatedRoute, Router} from '@angular/router';
 import {VpnAclResources} from '../vpn-acl-resources/vpn-acl-resources';
 import {VpnAclRules} from '../vpn-acl-rules/vpn-acl-rules';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-vpn-acl-groups',
   imports: [
     SharedModule,
-    VpnAclResources,
-    VpnAclRules,
+    TabContainer,
   ],
   templateUrl: './vpn-acl-groups.html',
   styleUrl: './vpn-acl-groups.scss'
@@ -26,10 +20,9 @@
   groupName: string | null = '';
   serviceDetails: any = null;
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'ACL Resource',
-    'Rule',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'ACL Resource', component: VpnAclResources, paramName: 'resource'},
+    {label: 'Rule', component: VpnAclRules, paramName: 'resource'},
   ];
 
   constructor(
@@ -43,39 +36,9 @@
     this.serviceName = this._route.snapshot.paramMap.get('serviceName');
     this.groupName = this._route.snapshot.paramMap.get('groupName');
     this.serviceDetails = history.state.serviceDetails;
-
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
   }
 
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
   backToACLGroups(): void {
     this._router.navigate(['/device/ssl-vpn/details', this.deviceName, this.serviceName]);
   }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-acl-resource-groups/vpn-acl-resource-groups.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-acl-resource-groups/vpn-acl-resource-groups.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-acl-resource-groups/vpn-acl-resource-groups.ts	(working copy)
@@ -58,7 +58,7 @@
     this._vpn.executeAGCLICommand(this.deviceName, payload)
       .pipe(take(1)).subscribe({
       next: (result: any) => {
-        if (result && result.contents && result.contents.length > 0) {
+        if (result && result.contents && Array.isArray(result.contents) && result.contents.length > 0) {
           result?.contents.forEach((content: any) => {
             if (content?.cmdid === 'acl resourcegroup network') {
               this.dataSource.data.push({
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-details-overview/vpn-details-overview.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-details-overview/vpn-details-overview.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-details-overview/vpn-details-overview.html	(working copy)
@@ -8,28 +8,4 @@
     </mat-card-title>
   </mat-card-header>
 </mat-card>
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="VPN Resource Groups">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vpn-resource-groups/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="ACL Resource Groups">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vpn-acl-resource-groups/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Hardware Id">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vpn-hardware-id/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-details-overview/vpn-details-overview.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-details-overview/vpn-details-overview.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-details-overview/vpn-details-overview.ts	(working copy)
@@ -1,18 +1,16 @@
 import {Component, OnInit} from '@angular/core';
 import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {SharedModule} from '../../../shared/shared-module';
 import {VpnResourceGroups} from '../vpn-resource-groups/vpn-resource-groups';
 import {VpnAclResourceGroups} from '../vpn-acl-resource-groups/vpn-acl-resource-groups';
 import {VpnHardwareId} from '../vpn-hardware-id/vpn-hardware-id';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-vpn-details-overview',
   imports: [
     SharedModule,
-    VpnResourceGroups,
-    VpnAclResourceGroups,
-    VpnHardwareId
+    TabContainer
   ],
   templateUrl: './vpn-details-overview.html',
   styleUrl: './vpn-details-overview.scss'
@@ -23,11 +21,10 @@
   serviceName: string | null = '';
   serviceDetails: any = null;
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'VPN Resource Groups',
-    'ACL Resource Groups',
-    'Hardware Id',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'VPN Resource Groups', component: VpnResourceGroups, paramName: 'vpnResourceGroups'},
+    {label: 'ACL Resource Groups', component: VpnAclResourceGroups, paramName: 'aclResourceGroups'},
+    {label: 'Hardware Id', component: VpnHardwareId, paramName: 'hardwareId'},
   ];
 
   constructor(
@@ -40,41 +37,9 @@
     this.deviceName = this._route.snapshot.paramMap.get('deviceName');
     this.serviceName = this._route.snapshot.paramMap.get('serviceName');
     this.serviceDetails = history.state.serviceDetails;
-
-    console.log(this.serviceDetails, this.deviceName, this.serviceName);
-
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
   }
 
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
   backToVirtualSites(): void {
     this._router.navigate(['/device/ssl-vpn']);
   }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-hardware-id/vpn-hardware-id.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-hardware-id/vpn-hardware-id.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-hardware-id/vpn-hardware-id.ts	(working copy)
@@ -78,7 +78,7 @@
     this._vpn.getHardwareIds(this.deviceName, payload)
       .pipe(take(1)).subscribe({
       next: (res: any) => {
-        if (res && res.contents && res.contents.length > 0) {
+        if (res && res.contents && Array.isArray(res.contents) && res.contents.length > 0) {
           const conf_list: any[] = res['contents'];
           let total = 0;
           const result: any[] = [];
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-resource-group-overview/vpn-resource-group-overview.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-resource-group-overview/vpn-resource-group-overview.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-resource-group-overview/vpn-resource-group-overview.html	(working copy)
@@ -8,35 +8,4 @@
     </mat-card-title>
   </mat-card-header>
 </mat-card>
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Application-Type VPN Resources">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vpn-resource-group-app-resources/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Application-Type VPN Resources Excluded">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vpn-resource-group-app-resources-excluded/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Network-Type VPN Resources">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vpn-resource-group-network-resources/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Network-Type VPN Resources Excluded">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vpn-resource-group-network-resources-excluded/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-resource-group-overview/vpn-resource-group-overview.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-resource-group-overview/vpn-resource-group-overview.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-resource-group-overview/vpn-resource-group-overview.ts	(working copy)
@@ -1,9 +1,4 @@
-import { Component } from '@angular/core';
-import {FaIconComponent} from '@fortawesome/angular-fontawesome';
-import {MatCard, MatCardHeader, MatCardTitle} from '@angular/material/card';
-import {MatTab, MatTabChangeEvent, MatTabContent, MatTabGroup} from '@angular/material/tabs';
-import {VpnAclResources} from '../vpn-acl-resources/vpn-acl-resources';
-import {VpnAclRules} from '../vpn-acl-rules/vpn-acl-rules';
+import {Component, OnInit} from '@angular/core';
 import {ActivatedRoute, Router} from '@angular/router';
 import {SharedModule} from '../../../shared/shared-module';
 import {VpnResourceGroupAppResources} from '../vpn-resource-group-app-resources/vpn-resource-group-app-resources';
@@ -16,31 +11,28 @@
 import {
   VpnResourceGroupNetworkResourcesExcluded
 } from '../vpn-resource-group-network-resources-excluded/vpn-resource-group-network-resources-excluded';
+import {TabContainer, TabDefinition} from '../../tab-container/tab-container';
 
 @Component({
   selector: 'app-vpn-resource-group-overview',
   imports: [
     SharedModule,
-    VpnResourceGroupAppResources,
-    VpnResourceGroupAppResourcesExcluded,
-    VpnResourceGroupNetworkResources,
-    VpnResourceGroupNetworkResourcesExcluded
+    TabContainer
   ],
   templateUrl: './vpn-resource-group-overview.html',
   styleUrl: './vpn-resource-group-overview.scss'
 })
-export class VpnResourceGroupOverview {
+export class VpnResourceGroupOverview implements OnInit {
   deviceName: string | null = '';
   serviceName: string | null = '';
   groupName: string | null = '';
   serviceDetails: any = null;
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Application-Type VPN Resources',
-    'Application-Type VPN Resources Excluded',
-    'Network-Type VPN Resources',
-    'Network-Type VPN Resources Excluded',
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Application-Type VPN Resources', component: VpnResourceGroupAppResources, paramName: ''},
+    {label: 'Application-Type VPN Resources Excluded', component: VpnResourceGroupAppResourcesExcluded, paramName: ''},
+    {label: 'Network-Type VPN Resources', component: VpnResourceGroupNetworkResources, paramName: ''},
+    {label: 'Network-Type VPN Resources Excluded', component: VpnResourceGroupNetworkResourcesExcluded, paramName: ''},
   ];
 
   constructor(
@@ -54,39 +46,9 @@
     this.serviceName = this._route.snapshot.paramMap.get('serviceName');
     this.groupName = this._route.snapshot.paramMap.get('groupName');
     this.serviceDetails = history.state.serviceDetails;
-
-    this._route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
   }
 
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
   backToVPNResourceGroups(): void {
     this._router.navigate(['/device/ssl-vpn/details', this.deviceName, this.serviceName]);
   }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this._router.navigate([], {
-        relativeTo: this._route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-resource-groups/vpn-resource-groups.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-resource-groups/vpn-resource-groups.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vpn-resource-groups/vpn-resource-groups.ts	(working copy)
@@ -60,7 +60,7 @@
     this._vpn.executeAGCLICommand(this.deviceName, payload)
       .pipe(take(1)).subscribe({
       next: (result: any) => {
-        if (result && result.contents && result.contents.length > 0) {
+        if (result && result.contents && Array.isArray(result.contents) && result.contents.length > 0) {
           result?.contents.forEach((content: any) => {
             if (content?.cmdid === 'vpn resource group') {
               this.vpnGroups.push({
@@ -88,7 +88,7 @@
     this._vpn.executeAGCLICommand(this.deviceName, payload)
       .pipe(take(1)).subscribe({
       next: (result: any) => {
-        if (result && result.contents && result.contents.length > 0) {
+        if (result && result.contents && Array.isArray(result.contents) && result.contents.length > 0) {
           result?.contents.forEach((content: any) => {
             if (content?.cmdid === 'role resource vpnresourcegroup') {
               if (this.vpnGroupsMap[content?.groupname]) {
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.html	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.html	(working copy)
@@ -0,0 +1,16 @@
+<div class="tab-container">
+  <mat-tab-group
+    animationDuration="0ms"
+    [selectedIndex]="selectedTabIndex"
+    (selectedTabChange)="onTabChange($event)">
+    @for (tab of tabDefinitions; track tab.label) {
+      <mat-tab [label]="tab.label">
+        <ng-template matTabContent>
+          <div class="tab-content">
+            <ng-container *ngComponentOutlet="tab.component"></ng-container>
+          </div>
+        </ng-template>
+      </mat-tab>
+    }
+  </mat-tab-group>
+</div>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.scss	(added)
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.scss	(revision 0)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.scss	(revision 0)
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.spec.ts	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.spec.ts	(working copy)
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TabContainer } from './tab-container';
+
+describe('TabContainer', () => {
+  let component: TabContainer;
+  let fixture: ComponentFixture<TabContainer>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [TabContainer]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(TabContainer);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.ts	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/tab-container/tab-container.ts	(working copy)
@@ -0,0 +1,57 @@
+import {Component, OnInit, Input, Type, OnChanges, SimpleChanges} from '@angular/core';
+import {SharedModule} from '../../shared/shared-module';
+import {MatTabChangeEvent} from '@angular/material/tabs';
+import {ActivatedRoute, Router} from '@angular/router';
+
+export interface TabDefinition {
+  label: string;
+  component: Type<any>;
+  paramName?: string;
+}
+
+@Component({
+  selector: 'app-tab-container',
+  imports: [SharedModule],
+  standalone: true,
+  templateUrl: './tab-container.html',
+  styleUrl: './tab-container.scss'
+})
+export class TabContainer implements OnInit {
+
+  @Input() tabDefinitions: TabDefinition[] = [];
+  @Input() paramKey: string = 'tab';
+  selectedTabIndex: number = 0;
+  private tabNames: string[] = [];
+
+  constructor(private _route: ActivatedRoute, private _router: Router) {
+  }
+
+  ngOnInit(): void {
+    this.tabNames = this.tabDefinitions.map(t => t.paramName || t.label);
+    this._route.queryParams.subscribe(params => {
+      const tabParam = params[this.paramKey];
+      if (tabParam) {
+        const index = this.tabNames.indexOf(tabParam);
+        this.selectedTabIndex = (index !== -1) ? index : 0;
+      } else {
+        this.updateQueryParams(this.selectedTabIndex);
+      }
+    });
+  }
+
+  onTabChange(event: MatTabChangeEvent): void {
+    this.selectedTabIndex = event.index;
+    this.updateQueryParams(event.index);
+  }
+
+  private updateQueryParams(tabIndex: number): void {
+    const tabName = this.tabNames[tabIndex];
+    if (tabName) {
+      this._router.navigate([], {
+        relativeTo: this._route,
+        queryParams: {[this.paramKey]: tabName},
+        queryParamsHandling: 'merge'
+      });
+    }
+  }
+}
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/topology/topology.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/topology/topology.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/topology/topology.html	(working copy)
@@ -1,18 +1 @@
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="SLB Virtual Services">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-topology-slb-virtual-service/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="SLB Real Services">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-topology-slb-real-service/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/topology/topology.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/topology/topology.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/topology/topology.ts	(working copy)
@@ -1,60 +1,22 @@
 import {Component, OnInit} from '@angular/core';
 import {SharedModule} from '../../shared/shared-module';
-import {ActivatedRoute, Router} from '@angular/router';
-import {MatTabChangeEvent} from '@angular/material/tabs';
 import {TopologySlbVirtualService} from '../sub-components/topology-slb-virtual-service/topology-slb-virtual-service';
 import {TopologySlbRealService} from '../sub-components/topology-slb-real-service/topology-slb-real-service';
+import {TabContainer, TabDefinition} from '../tab-container/tab-container';
 
 @Component({
   selector: 'app-topology',
   imports: [
     SharedModule,
-    TopologySlbVirtualService,
-    TopologySlbRealService
+    TabContainer
   ],
   templateUrl: './topology.html',
   styleUrl: './topology.scss'
 })
-export class Topology implements OnInit {
+export class Topology {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'SLB Virtual Services',
-    'SLB Real Services',
-  ];
-
-  constructor(private route: ActivatedRoute, private router: Router) {
-  }
-
-  ngOnInit(): void {
-    this.route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this.router.navigate([], {
-        relativeTo: this.route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
+  tabDefinitions: TabDefinition[] = [
+    {label: 'SLB Virtual Services', component: TopologySlbVirtualService, paramName: 'VS'},
+    {label: 'SLB Real Services', component: TopologySlbRealService, paramName: 'RS'},
+  ]
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/volume-license-devices-overview/volume-license-devices-overview.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/volume-license-devices-overview/volume-license-devices-overview.html	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/volume-license-devices-overview/volume-license-devices-overview.html	(working copy)
@@ -1,18 +1 @@
-<div class="tab-container">
-  <mat-tab-group animationDuration="0ms" [selectedIndex]="selectedTabIndex" (selectedTabChange)="onTabChange($event)">
-    <mat-tab label="Managed Devices">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vl-managed-devices/>
-        </div>
-      </ng-template>
-    </mat-tab>
-    <mat-tab label="Discovered Devices">
-      <ng-template matTabContent>
-        <div class="tab-content">
-          <app-vl-discovered-devices/>
-        </div>
-      </ng-template>
-    </mat-tab>
-  </mat-tab-group>
-</div>
+<app-tab-container [tabDefinitions]="tabDefinitions" paramKey="tab"/>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/volume-license-devices-overview/volume-license-devices-overview.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/volume-license-devices-overview/volume-license-devices-overview.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/volume-license-devices-overview/volume-license-devices-overview.ts	(working copy)
@@ -4,53 +4,19 @@
 import {VlManagedDevices} from '../sub-components/vl-managed-devices/vl-managed-devices';
 import {ActivatedRoute, Router} from '@angular/router';
 import {MatTabChangeEvent} from '@angular/material/tabs';
+import {TabContainer, TabDefinition} from '../tab-container/tab-container';
+import {UserManagement} from '../sub-components/user-management/user-management';
 
 @Component({
   selector: 'app-volume-license-devices-overview',
-  imports: [SharedModule, VlDiscoveredDevices, VlManagedDevices],
+  imports: [SharedModule, TabContainer],
   templateUrl: './volume-license-devices-overview.html',
   styleUrl: './volume-license-devices-overview.scss'
 })
-export class VolumeLicenseDevicesOverview implements OnInit {
+export class VolumeLicenseDevicesOverview {
 
-  selectedTabIndex: number = 0;
-  private tabNames: string[] = [
-    'Managed Devices',
-    'Discovered Devices',
-  ];
-
-  constructor(private route: ActivatedRoute, private router: Router) {
-  }
-
-  ngOnInit(): void {
-    this.route.queryParams.subscribe(params => {
-      const tabParam = params['tab'];
-      if (tabParam) {
-        const index = this.tabNames.indexOf(tabParam);
-        if (index !== -1) {
-          this.selectedTabIndex = index;
-        } else {
-          this.selectedTabIndex = 0;
-        }
-      } else {
-        this.updateQueryParams(this.selectedTabIndex);
-      }
-    });
-  }
-
-  onTabChange(event: MatTabChangeEvent): void {
-    this.selectedTabIndex = event.index;
-    this.updateQueryParams(event.index);
-  }
-
-  private updateQueryParams(tabIndex: number): void {
-    const tabName = this.tabNames[tabIndex];
-    if (tabName) {
-      this.router.navigate([], {
-        relativeTo: this.route,
-        queryParams: {tab: tabName},
-        queryParamsHandling: 'merge'
-      });
-    }
-  }
+  tabDefinitions: TabDefinition[] = [
+    {label: 'Managed Devices', component: UserManagement, paramName: 'Managed'},
+    {label: 'Discovered Devices', component: UserManagement, paramName: 'Discovered'},
+  ]
 }
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/interceptors/loading-interceptor-interceptor.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/interceptors/loading-interceptor-interceptor.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/interceptors/loading-interceptor-interceptor.ts	(working copy)
@@ -3,28 +3,31 @@
 import {finalize} from 'rxjs';
 import {Loading} from '../services/loading';
 
+const BYPASS_URLS: string[] = [
+  '/cm/ajax/webconsole/_handle_request',
+  '/system-metrics-latest',
+  '/system-metrics-history',
+  '/devices-metrics',
+  '/top-apv-virtual-metrics',
+  '/top-apv-real-metrics',
+  '/top-apv-llb-metrics',
+  '/top-ssl-vpn-metrics',
+  '/api/api/cm/virtualization/Host/_get_list_data',
+];
+
 export const loadingInterceptorFn: HttpInterceptorFn = (req, next) => {
   const loadingService = inject(Loading);
 
-  const BYPASS_URL_SEGMENT = '/cm/ajax/webconsole/_handle_request';
-  const shouldBypassLoading = req.url.includes(BYPASS_URL_SEGMENT);
+  const shouldBypassLoading = BYPASS_URLS.some(urlSegment => req.url.includes(urlSegment));
 
-  let activeRequestsForThisCall = 0;
-
   if (!shouldBypassLoading) {
-    if (activeRequestsForThisCall === 0) {
-      loadingService.show();
-    }
-    activeRequestsForThisCall++;
+    loadingService.startRequest();
   }
 
   return next(req).pipe(
     finalize(() => {
       if (!shouldBypassLoading) {
-        activeRequestsForThisCall--;
-        if (activeRequestsForThisCall === 0) {
-          loadingService.hide();
-        }
+        loadingService.endRequest();
       }
     })
   );
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,404 @@
+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, showLegend: boolean = false): EChartsOption {
+    let options: EChartsOption = {
+      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'}}
+          ]
+        }
+      ]
+    }
+    if (showLegend) {
+      options.legend = {
+        bottom: 'bottom'
+      }
+    }
+    return options;
+  }
+
+  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/loading.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/loading.ts	(revision 2709)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/loading.ts	(working copy)
@@ -6,18 +6,29 @@
 })
 export class Loading {
 
-
+  private activeRequestCount = 0;
   private loadingSubject = new BehaviorSubject<boolean>(false);
   loading$ = this.loadingSubject.asObservable();
 
   constructor() {
   }
 
-  show() {
-    this.loadingSubject.next(true);
+  startRequest(): void {
+    this.activeRequestCount++;
+    if (this.activeRequestCount === 1) {
+      this.loadingSubject.next(true);
+    }
   }
 
-  hide() {
-    this.loadingSubject.next(false);
+  endRequest(): void {
+    this.activeRequestCount--;
+
+    if (this.activeRequestCount < 0) {
+      this.activeRequestCount = 0;
+    }
+
+    if (this.activeRequestCount === 0) {
+      this.loadingSubject.next(false);
+    }
   }
 }
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,12 +1,28 @@
-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>;
+}
+
+export interface KeyValuePair {
+  key: string;
+  value: any;
+}
+
 @Injectable({
   providedIn: 'root'
 })
 export class UtilsService {
 
+  AUTO_REFRESH_INTERVAL: number = 10000;
+
   constructor() {
   }
 
@@ -88,4 +104,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
