Index: /branches/amp_4_0/platform/tools/container/.env
===================================================================
--- /branches/amp_4_0/platform/tools/container/.env	(revision 2944)
+++ /branches/amp_4_0/platform/tools/container/.env	(working copy)
@@ -36,6 +36,11 @@
 # Uncomment to override auto-detection (e.g. for specific domain or IP).
 # AMP_DOMAIN_OR_IP=192.168.162.139
 
+# --- AMP WebUI Session Timeout (Idle) ---
+# Session expires after this many idle minutes.
+AMP_SSO_TOKEN_TTL_MINUTES=120
+AMP_SESSION_IDLE_TIMEOUT_MINUTES=120
+
 # --- OpenSearch Memory Allocation ---
 # By default, manage_amp.sh calculates this based on 50% of Host RAM.
 # Uncomment to override (e.g. for testing constraints).
Index: /branches/amp_4_0/platform/tools/container/DEPLOYMENT.md
===================================================================
--- /branches/amp_4_0/platform/tools/container/DEPLOYMENT.md	(revision 2944)
+++ /branches/amp_4_0/platform/tools/container/DEPLOYMENT.md	(working copy)
@@ -152,6 +152,20 @@
 ```
 
 This waits for services to be ready and initializes security, databases, and dashboards.
+
+**Engineer shortcut (single command):**
+
+```bash
+./manage_amp.sh deploy --auto --post
+```
+
+**Alternate shortcut:**
+
+```bash
+./manage_amp.sh deploy_full
+```
+
+> Note: Post-deploy includes built-in waits and retries for OpenSearch, Dashboards, and Postgres.
 
 ### Step 7: Verify Deployment
 
@@ -161,6 +175,20 @@
 
 Access the GUI at `https://<NODE_IP>/` or `https://<VIP>/`
 
+### Common Operations (Minimal Commands)
+
+Restart all services:
+
+```bash
+./manage_amp.sh restart
+```
+
+Restart a single service (example: OpenSearch):
+
+```bash
+./manage_amp.sh restart opensearch
+```
+
 ---
 
 ## Part 3: Offline Deployment (Air-Gapped)
Index: /branches/amp_4_0/platform/tools/container/manage_amp.sh
===================================================================
--- /branches/amp_4_0/platform/tools/container/manage_amp.sh	(revision 2944)
+++ /branches/amp_4_0/platform/tools/container/manage_amp.sh	(working copy)
@@ -820,6 +820,26 @@
         fi
 
         echo "✅ Generated $HP_OUTPUT"
+    fi
+
+    # Generate Grafana JWT JWKS from the shared OPENSEARCH_JWT_SECRET
+    GRAFANA_JWT_DIR="$SERVICES_DIR/grafana/jwt"
+    GRAFANA_JWKS_FILE="$GRAFANA_JWT_DIR/jwks.json"
+    mkdir -p "$GRAFANA_JWT_DIR"
+
+    if [ -z "$OPENSEARCH_JWT_SECRET" ]; then
+        echo "⚠️  OPENSEARCH_JWT_SECRET is empty; skipping Grafana JWKS generation."
+    else
+        if command -v openssl >/dev/null 2>&1; then
+            SECRET_B64URL=$(printf '%s' "$OPENSEARCH_JWT_SECRET" | openssl base64 -A | tr '+/' '-_' | tr -d '=')
+        else
+            SECRET_B64URL=$(printf '%s' "$OPENSEARCH_JWT_SECRET" | base64 | tr -d '\n=' | tr '+/' '-_')
+        fi
+
+        cat > "$GRAFANA_JWKS_FILE" <<EOF
+{"keys":[{"kty":"oct","kid":"amp-grafana-hs256","use":"sig","alg":"HS256","k":"$SECRET_B64URL"}]}
+EOF
+        echo "✅ Generated $GRAFANA_JWKS_FILE"
     fi
 }
 
@@ -1425,6 +1445,9 @@
     echo "  sudo useradd -m -s /ca/bin/ca_shell amp_operator"
     echo "  sudo passwd amp_operator"
     echo ""
+    echo "Next step for end users:"
+    echo "  ./manage_amp.sh post_deploy"
+    echo ""
 }
 
 rm_stack() {
@@ -1434,6 +1457,8 @@
 
 run_setup() {
     echo "--- Running Setup (Generating Certificates) ---"
+    # Ensure secrets are loaded so JWT signing key is consistent
+    load_secrets_file
     # Ensure volumes exist
     if ! docker volume ls | grep -q "certs-vol"; then
         docker volume create certs-vol
@@ -1564,6 +1589,8 @@
     echo "--- Running Configurator (OpenSearch Setup) ---"
     
     check_swarm
+    # Ensure secrets are loaded for standalone configurator runs.
+    load_secrets_file
     
     # Find a running OpenSearch container (it already has curl installed)
     CONTAINER_ID=$(docker ps --format '{{.ID}} {{.Names}}' | grep "amp_opensearch" | grep -v "dashboards" | awk '{print $1}' | head -n 1)
@@ -2045,7 +2072,38 @@
     echo "✅ Post-Deployment Configuration Complete!"
     echo "========================================================="
 }
+
+restart_services() {
+    check_swarm
 
+    local target="$1"
+    local namespace="com.docker.stack.namespace=${STACK_NAME}"
+
+    if [ -n "$target" ]; then
+        # Allow short names (e.g., opensearch) or full service names (e.g., amp_opensearch)
+        local service_name="$target"
+        if ! docker service ls --format '{{.Name}}' | grep -q "^${service_name}$"; then
+            service_name="${STACK_NAME}_${target}"
+        fi
+        if ! docker service ls --format '{{.Name}}' | grep -q "^${service_name}$"; then
+            echo "❌ Service not found: $target"
+            exit 1
+        fi
+        echo "Restarting service: $service_name"
+        docker service update --force "$service_name"
+        echo "✅ Restart requested for $service_name"
+        return
+    fi
+
+    echo "Restarting all services in stack: $STACK_NAME"
+    docker service ls --filter "label=$namespace" --format '{{.Name}}' | while read -r svc; do
+        [ -n "$svc" ] || continue
+        echo "  - $svc"
+        docker service update --force "$svc" >/dev/null
+    done
+    echo "✅ Restart requested for all services in $STACK_NAME"
+}
+
 case $ACTION in
     init)
         init_swarm "$2"  # Pass optional advertise-addr as second argument
@@ -2077,10 +2135,18 @@
         ;;
     deploy)
         if [ "$2" == "--auto" ]; then
-             auto_configure
+            auto_configure
         fi
         deploy_stack
+        if [ "$2" == "--post" ] || [ "$3" == "--post" ]; then
+            post_deploy_config
+        fi
         ;;
+    deploy_full)
+        auto_configure
+        deploy_stack
+        post_deploy_config
+        ;;
     auto-config)
         auto_configure
         ;;
@@ -2104,6 +2170,9 @@
     rotate-secrets)
         rotate_secrets "$2"
         ;;
+    restart)
+        restart_services "$2"
+        ;;
     rm|remove)
         rm_stack
         ;;
@@ -2111,11 +2180,15 @@
         docker stack services $STACK_NAME
         ;;
     *)
-        echo "Usage: $0 {init|init-secrets|build|bundle|load_offline|setup|deploy|post_deploy|security_init|status|configurator|system_tune|rotate-secrets|rm|vip}"
+        echo "Usage: $0 {init|init-secrets|build|bundle|load_offline|setup|deploy|deploy_full|post_deploy|security_init|status|configurator|system_tune|rotate-secrets|restart|rm|vip}"
         echo "  init-secrets        : Interactive password setup (before first deploy)"
         echo "  rotate-secrets      : Rotate passwords (after stopping stack)"
         echo "  deploy --auto       : Auto-configure cluster and deploy"
+        echo "  deploy --post       : Deploy and run post-deploy steps"
+        echo "  deploy --auto --post: Auto-configure, deploy, then post-deploy"
+        echo "  deploy_full         : Same as --auto --post"
         echo "  post_deploy         : Run all post-deployment configuration steps (Wait & Init)"
+        echo "  restart [service]   : Restart all services or a single service (e.g., opensearch)"
         echo "  vip commands        : ./manage_amp.sh vip --vip <IP> --priority <INT> [--interface <IFACE>]"
         exit 1
 esac
Index: /branches/amp_4_0/platform/tools/container/services/grafana/jwt/jwks.json
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/grafana/jwt/jwks.json	(nonexistent)
+++ /branches/amp_4_0/platform/tools/container/services/grafana/jwt/jwks.json	(working copy)
@@ -0,0 +1 @@
+{"keys":[]}
Index: /branches/amp_4_0/platform/tools/container/services/nginx/conf.d/app.conf
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/nginx/conf.d/app.conf	(revision 2944)
+++ /branches/amp_4_0/platform/tools/container/services/nginx/conf.d/app.conf	(working copy)
@@ -6,6 +6,11 @@
     "~access_token=([^;]+)" $1;
 }
 
+map $http_cookie $grafana_jwt_token {
+    default "";
+    "~monitoring_access_token=([^;]+)" $1;
+}
+
 # --- HTTP Server Block (Redirects to HTTPS) ---
 server {
     listen 80;
@@ -57,11 +62,16 @@
         proxy_pass https://host.docker.internal:5601;
         proxy_http_version 1.1;
         proxy_set_header Host $host;
+        proxy_set_header X-Forwarded-Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-Forwarded-Prefix /visualization;
         proxy_set_header Authorization "Bearer $jwt_token";
+        proxy_set_header securitytenant "global_tenant";
         proxy_ssl_verify off;
+        proxy_redirect ~^(/app/.*)$ /visualization$1;
+        proxy_redirect ~^(/login.*)$ /visualization$1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
         proxy_cache_bypass $http_upgrade;
@@ -73,11 +83,16 @@
         proxy_pass https://host.docker.internal:5601;
         proxy_http_version 1.1;
         proxy_set_header Host $host;
+        proxy_set_header X-Forwarded-Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-Forwarded-Prefix /visualization;
         proxy_set_header Authorization "Bearer $jwt_token";
+        proxy_set_header securitytenant "global_tenant";
         proxy_ssl_verify off;
+        proxy_redirect ~^(/app/.*)$ /visualization$1;
+        proxy_redirect ~^(/login.*)$ /visualization$1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
         proxy_cache_bypass $http_upgrade;
@@ -89,11 +104,16 @@
         proxy_pass https://host.docker.internal:5601;
         proxy_http_version 1.1;
         proxy_set_header Host $host;
+        proxy_set_header X-Forwarded-Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-Forwarded-Prefix /visualization;
         proxy_set_header Authorization "Bearer $jwt_token";
+        proxy_set_header securitytenant "global_tenant";
         proxy_ssl_verify off;
+        proxy_redirect ~^(/app/.*)$ /visualization$1;
+        proxy_redirect ~^(/login.*)$ /visualization$1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
         proxy_cache_bypass $http_upgrade;
@@ -102,6 +122,23 @@
     }
 
     # Grafana at /monitoring/
+    location /monitoring {
+        if ($arg_access_token) {
+            add_header Set-Cookie "monitoring_access_token=$arg_access_token; Path=/monitoring/; HttpOnly";
+            return 302 /monitoring/;
+        }
+        proxy_pass http://host.docker.internal:3000;
+        proxy_http_version 1.1;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-JWT-Assertion $grafana_jwt_token;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection "upgrade";
+        proxy_cache_bypass $http_upgrade;
+    }
+
     location /monitoring/ {
         # set $grafana "http://host.docker.internal:3000";
         proxy_pass http://host.docker.internal:3000; # Static pass allows system resolver to read /etc/hosts
@@ -110,6 +147,7 @@
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-JWT-Assertion $grafana_jwt_token;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
         proxy_cache_bypass $http_upgrade;
Index: /branches/amp_4_0/platform/tools/container/services/setup/configure_opensearch.sh
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/setup/configure_opensearch.sh	(revision 2944)
+++ /branches/amp_4_0/platform/tools/container/services/setup/configure_opensearch.sh	(working copy)
@@ -36,7 +36,10 @@
 
 while [ $COUNT -lt $MAX_RETRIES ]; do
   # Capture output and exit code to debug failure
-  OUTPUT=$(curl -v -k -u "$ADMIN_USER:$ADMIN_PASS" "$OPENSEARCH_URL/_cluster/health" 2>&1)
+  OUTPUT=$(curl -v -k \
+    --cert /usr/share/opensearch/config/certs/admin.pem \
+    --key /usr/share/opensearch/config/certs/admin-key.pem \
+    "$OPENSEARCH_URL/_cluster/health" 2>&1)
   RET=$?
   
   if [ $RET -eq 0 ]; then
@@ -94,7 +97,11 @@
 log "Role mapping 'jwt_users' updated."
 
 log "Applying Index Template..."
-RESPONSE=$(curl -s -k -u "$ADMIN_USER:$ADMIN_PASS" -w "%{http_code}" -X PUT "$OPENSEARCH_URL/_template/acm_template" -H 'Content-Type: application/json' -d @/usr/share/opensearch/config/amplog_template.json)
+RESPONSE=$(curl -s -k \
+  --cert /usr/share/opensearch/config/certs/admin.pem \
+  --key /usr/share/opensearch/config/certs/admin-key.pem \
+  -w "%{http_code}" -X PUT "$OPENSEARCH_URL/_template/acm_template" \
+  -H 'Content-Type: application/json' -d @/usr/share/opensearch/config/amplog_template.json)
 log "Done. HTTP Response: $RESPONSE"
 
 MAX_RETRIES=60
@@ -102,7 +109,9 @@
 log "Waiting for OpenSearch Dashboards ($OPENSEARCH_DASHBOARDS_URL/visualization/api/status) (timeout: 300s)..."
 while [ $COUNT -lt $MAX_RETRIES ]; do
   # Capture HTTP Status and body - using -w to separate status code
-  HTTP_CODE=$(curl -s -k -o /tmp/dashboards_status.txt -w "%{http_code}" -u "$ADMIN_USER:$ADMIN_PASS" "$OPENSEARCH_DASHBOARDS_URL/visualization/api/status")
+  HTTP_CODE=$(curl -s -k -o /tmp/dashboards_status.txt -w "%{http_code}" -u "$ADMIN_USER:$ADMIN_PASS" \
+    -H "securitytenant: global_tenant" \
+    "$OPENSEARCH_DASHBOARDS_URL/visualization/api/status")
   STATUS_RES=$(cat /tmp/dashboards_status.txt)
 
   if [ "$HTTP_CODE" -eq 200 ] && echo "$STATUS_RES" | grep -q '"state":"green"'; then
@@ -177,7 +186,9 @@
 # Using -w %{http_code} to catch errors and -o to avoid dumping large JSON to logs
 # Set longer timeout (300s) for first-time import which can be slow
 log "Testing connectivity to $OPENSEARCH_DASHBOARDS_URL first..."
-curl -s -k -u "$ADMIN_USER:$ADMIN_PASS" -w "%{http_code}\n" -o /dev/null --connect-timeout 60 "$OPENSEARCH_DASHBOARDS_URL/visualization/api/status"
+curl -s -k -u "$ADMIN_USER:$ADMIN_PASS" -w "%{http_code}\n" -o /dev/null --connect-timeout 60 \
+  -H "securitytenant: global_tenant" \
+  "$OPENSEARCH_DASHBOARDS_URL/visualization/api/status"
 
 log "Starting import (this may take several minutes)..."
 HTTP_CODE=$(curl -v -k -u "$ADMIN_USER:$ADMIN_PASS" -o /tmp/import_res.json -w "%{http_code}" \
@@ -185,6 +196,7 @@
   --max-time 600 \
   -X POST "$OPENSEARCH_DASHBOARDS_URL/visualization/api/saved_objects/_import?overwrite=true" \
   -H "osd-xsrf: true" \
+  -H "securitytenant: global_tenant" \
   --form file=@/usr/share/opensearch/config/export.ndjson 2> /tmp/curl_debug.log)
 
 log "Import finished. HTTP Response: $HTTP_CODE"
@@ -198,6 +210,7 @@
   if [ -f /tmp/import_res.json ]; then
     cat /tmp/import_res.json
   fi
+  exit 1
 fi
 
 log "Configuration Complete!"
Index: /branches/amp_4_0/platform/tools/container/services/setup/setup.sh
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/setup/setup.sh	(revision 2944)
+++ /branches/amp_4_0/platform/tools/container/services/setup/setup.sh	(working copy)
@@ -93,6 +93,9 @@
           challenge: true
         authentication_backend:
           type: intern
+    kibana:
+      # Land users in Global tenant by default so shared dashboards/index patterns are visible.
+      default_tenant: "global_tenant"
 EOF
 
 # --- 3. Internal Users Generation (internal_users.yml) ---
Index: /branches/amp_4_0/platform/tools/container/stack.yml
===================================================================
--- /branches/amp_4_0/platform/tools/container/stack.yml	(revision 2944)
+++ /branches/amp_4_0/platform/tools/container/stack.yml	(working copy)
@@ -27,6 +27,8 @@
     file: services/pgbouncer/userlist.txt
   grafana_datasources:
     file: services/grafana/provisioning/datasources/datasources.yaml
+  grafana_jwt_jwks:
+    file: services/grafana/jwt/jwks.json
   opensearch_config:
     file: services/opensearch/opensearch.yml
   haproxy_cfg:
@@ -101,6 +103,7 @@
     command: /usr/share/opensearch/opensearch-docker-entrypoint.sh opensearch
     secrets:
       - opensearch_initial_admin_password
+      - opensearch_jwt_secret
     volumes:
       - opensearch-data:/usr/share/opensearch/data
       - security-config-vol:/usr/share/opensearch/config/opensearch-security-mount:ro
@@ -344,11 +347,15 @@
       OPENSEARCH_SSL_VERIFICATIONMODE: certificate
       OPENSEARCH_USERNAME: admin
       OPENSEARCH_SSL_CERTIFICATEAUTHORITIES: '["/usr/share/opensearch-dashboards/config/certs/root-ca.pem"]'
+      OPENSEARCH_SECURITY_AUTH_TYPE: jwt
+      OPENSEARCH_SECURITY_JWT_HEADER: Authorization
+      OPENSEARCH_SECURITY_JWT_URL_PARAMETER: access_token
       SERVER_SSL_ENABLED: "true"
       SERVER_SSL_KEY: /usr/share/opensearch-dashboards/config/certs/node-key.pem
       SERVER_SSL_CERTIFICATE: /usr/share/opensearch-dashboards/config/certs/node.pem
       SERVER_BASEPATH: /visualization
       SERVER_REWRITEBASEPATH: "true"
+      SERVER_PUBLICBASEURL: https://${AMP_DOMAIN_OR_IP:-localhost}/visualization
     command: >
       bash -c "export OPENSEARCH_PASSWORD=\$$(cat /run/secrets/opensearch_initial_admin_password) && /usr/share/opensearch-dashboards/opensearch-dashboards-docker-entrypoint.sh opensearch-dashboards"
     secrets:
@@ -389,6 +396,11 @@
       - GF_AUTH_TOKEN_ROTATION_INTERVAL_MINUTES=1440
       - GF_AUTH_LOGIN_MAXIMUM_INACTIVE_LIFETIME_DAYS=30
       - GF_AUTH_LOGIN_MAXIMUM_LIFETIME_DAYS=30
+      - GF_AUTH_JWT_ENABLED=true
+      - GF_AUTH_JWT_HEADER_NAME=X-JWT-Assertion
+      - GF_AUTH_JWT_USERNAME_CLAIM=sub
+      - GF_AUTH_JWT_AUTO_SIGN_UP=true
+      - GF_AUTH_JWT_JWK_SET_FILE=/etc/grafana/jwt/jwks.json
       - GF_SECURITY_ADMIN_USER=admin
       - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD:-GArr@y2050}
       - GF_INSTALL_PLUGINS=grafana-opensearch-datasource
@@ -403,6 +415,8 @@
     configs:
       - source: grafana_datasources
         target: /etc/grafana/provisioning/datasources/datasources.yaml
+      - source: grafana_jwt_jwks
+        target: /etc/grafana/jwt/jwks.json
     volumes:
       # Removed local sqlite volume, data is now in Postgres
       # - grafana-data:/var/lib/grafana
Index: /branches/amp_4_0/platform/tools/container/stack.yml.template
===================================================================
--- /branches/amp_4_0/platform/tools/container/stack.yml.template	(revision 2944)
+++ /branches/amp_4_0/platform/tools/container/stack.yml.template	(working copy)
@@ -23,6 +23,8 @@
     file: services/pgbouncer/userlist.txt
   grafana_datasources:
     file: services/grafana/provisioning/datasources/datasources.yaml
+  grafana_jwt_jwks:
+    file: services/grafana/jwt/jwks.json
   opensearch_config:
     file: services/opensearch/opensearch.yml
   haproxy_cfg:
@@ -105,6 +107,7 @@
 
     secrets:
       - opensearch_initial_admin_password
+      - opensearch_jwt_secret
     volumes:
       - opensearch-data:/usr/share/opensearch/data
       - security-config-vol:/usr/share/opensearch/config/opensearch-security-mount:ro
@@ -289,11 +292,16 @@
       OPENSEARCH_SSL_VERIFICATIONMODE: certificate
       OPENSEARCH_USERNAME: admin
       OPENSEARCH_SSL_CERTIFICATEAUTHORITIES: '["/usr/share/opensearch-dashboards/config/certs/root-ca.pem"]'
+      OPENSEARCH_SECURITY_AUTH_TYPE: jwt
+      OPENSEARCH_SECURITY_JWT_HEADER: Authorization
+      OPENSEARCH_SECURITY_JWT_URL_PARAMETER: access_token
+      OPENSEARCH_SECURITY_MULTITENANCY_TENANTS_PREFERRED: '["Global","Private"]'
       SERVER_SSL_ENABLED: "true"
       SERVER_SSL_KEY: /usr/share/opensearch-dashboards/config/certs/node-key.pem
       SERVER_SSL_CERTIFICATE: /usr/share/opensearch-dashboards/config/certs/node.pem
       SERVER_BASEPATH: /visualization
       SERVER_REWRITEBASEPATH: "true"
+      SERVER_PUBLICBASEURL: https://${AMP_DOMAIN_OR_IP:-localhost}/visualization
     extra_hosts:
       - "host.docker.internal:host-gateway"
     configs:
@@ -346,6 +354,11 @@
       - GF_AUTH_TOKEN_ROTATION_INTERVAL_MINUTES=1440
       - GF_AUTH_LOGIN_MAXIMUM_INACTIVE_LIFETIME_DAYS=30
       - GF_AUTH_LOGIN_MAXIMUM_LIFETIME_DAYS=30
+      - GF_AUTH_JWT_ENABLED=true
+      - GF_AUTH_JWT_HEADER_NAME=X-JWT-Assertion
+      - GF_AUTH_JWT_USERNAME_CLAIM=sub
+      - GF_AUTH_JWT_AUTO_SIGN_UP=true
+      - GF_AUTH_JWT_JWK_SET_FILE=/etc/grafana/jwt/jwks.json
       - GF_SECURITY_ADMIN_USER=admin
       - GF_SECURITY_ADMIN_PASSWORD__FILE=/run/secrets/grafana_admin_password
       - GF_INSTALL_PLUGINS=grafana-opensearch-datasource
@@ -364,6 +377,8 @@
     configs:
       - source: grafana_datasources
         target: /etc/grafana/provisioning/datasources/datasources.yaml
+      - source: grafana_jwt_jwks
+        target: /etc/grafana/jwt/jwks.json
     volumes:
       - /dev/null:/dev/null # Placeholder to keep yaml valid if empty
     networks:
@@ -472,6 +487,7 @@
       - OPENSEARCH_PASSWORD_FILE=/run/secrets/opensearch_initial_admin_password
       - DJANGO_SETTINGS_MODULE=djproject.settings
       - PYTHONPATH=/ca/webui/htdocs/new/src
+      - AMP_SESSION_IDLE_TIMEOUT_MINUTES=${AMP_SESSION_IDLE_TIMEOUT_MINUTES:-15}
     volumes:
       # Mount configuration directories from host
       - type: bind
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/models/configuration/config_file/__init__.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/models/configuration/config_file/__init__.py	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/models/configuration/config_file/__init__.py	(working copy)
@@ -438,9 +438,14 @@
             else:
                 os.mknod(file_path + '/' + data['name'])
 
-            insert_sql = "INSERT INTO file_list(name, create_time, modify_time, type, comment, device_type) values('%s', '%s', '%s', '%s', '%s', '%s')" % (
-                data['name'] + ".cfg", create_time, create_time, file_type, data['comment'], data['device_type'])
             db = DB.get_connected_db()
+            
+            sql = "SELECT id FROM file_type WHERE name = '%s'" % file_type
+            res = db.fetchall(sql)
+            file_type_id = res[0][0] if res else 0
+
+            insert_sql = "INSERT INTO file_list(name, create_time, modify_time, type, comment, device_type, file_type_id) values('%s', '%s', '%s', '%s', '%s', '%s', %d)" % (
+                data['name'] + ".cfg", create_time, create_time, file_type, data['comment'], data['device_type'], file_type_id)
             db.execute_sql(insert_sql)
             db.close()
             return
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/models/device_mgmt/device/__init__.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/models/device_mgmt/device/__init__.py	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/models/device_mgmt/device/__init__.py	(working copy)
@@ -665,10 +665,14 @@
             ) % data
             db.execute_sql(save_sql)
             create_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+            sql = "SELECT id FROM file_type WHERE name = 'device'"
+            res = db.fetchall(sql)
+            file_type_id = res[0][0] if res else 0
+
             insert_config_sql = (
-                "INSERT INTO file_list(name, create_time, modify_time, type, comment, device_type) "
-                "values('%s', '%s', '%s', '%s', '%s', '%s')" % (
-                    data['name'], create_time, create_time, 'device', '', data['type'])
+                "INSERT INTO file_list(name, create_time, modify_time, type, comment, device_type, file_type_id) "
+                "values('%s', '%s', '%s', '%s', '%s', '%s', %d)" % (
+                    data['name'], create_time, create_time, 'device', '', data['type'], file_type_id)
             )
             db.execute_sql(insert_config_sql)
             db.close()
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 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/djproject/urls.py	(working copy)
@@ -26,7 +26,7 @@
 from hive.controller.backup_controller import handle_backup_req
 from hive.controller.restore_controller import handle_restore_req
 from hive.controller.utils import handle_observability_status_req, handle_observability_restart_req
-from hive.an_opensearch import opensearch_proxy, get_opensearch_sso_token
+from hive.an_opensearch import opensearch_proxy, get_opensearch_sso_token, get_grafana_sso_token
 from hive.controller.system_metrics import handle_get_latest_system_metrics, handle_get_historical_system_metrics
 from hive.controller.generic_controller import handle_service_query_req
 from hive.controller.notification_controller import handle_notification_req
@@ -98,6 +98,7 @@
     re_path(r'^observability-status$', handle_observability_status_req),
     re_path(r'^observability-restart$', handle_observability_restart_req),
     re_path(r'^opensearch-sso-token$', get_opensearch_sso_token),
+    re_path(r'^grafana-sso-token$', get_grafana_sso_token),
     re_path(r'^reporting/(?P<app>\w+)/(?P<filename>.*)$', reporting_downloading_handler),
     re_path(r'^reporting_logo$', reporting_logo_handler),
     re_path(r'^cm/save_setting$', save_setting),
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/app.config.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/app.config.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/app.config.ts	(working copy)
@@ -16,8 +16,11 @@
 import { echarts } from './charts-config';
 import { authInterceptorFn } from './interceptors/auth-interceptor-interceptor';
 
+import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
+
 export const appConfig: ApplicationConfig = {
   providers: [
+    provideAnimationsAsync(),
     provideBrowserGlobalErrorListeners(),
     provideZoneChangeDetection({ eventCoalescing: true }),
     provideEnvironmentInitializer(() => inject(AuthService).init()),
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.html	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.html	(working copy)
@@ -13,6 +13,11 @@
             <a (click)="openSearchDashboard()" class="a-link-color">Logs Dashboard <fa-icon
                     [icon]="['far', 'window-maximize']" size="sm"></fa-icon></a>
         </div>
+        <div class="dashboard-link-container">
+            <span>Visit Grafana Dashboard: </span>
+            <a (click)="openGrafanaDashboard()" class="a-link-color">Metrics Dashboard <fa-icon
+                    [icon]="['far', 'window-maximize']" size="sm"></fa-icon></a>
+        </div>
         <div class="table-container">
             <table mat-table [dataSource]="dataSource" class="an-table">
                 <ng-container matColumnDef="serial">
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.ts	(working copy)
@@ -64,9 +64,7 @@
     this.system.getOpenSearchAuthToken().pipe(take(1)).subscribe({
       next: (result: any) => {
         if (result && result.token) {
-          const protocol = window.location.protocol;
-          const hostname = window.location.hostname;
-          const opensearchUrl = `${protocol}://${hostname}/visualization?access_token=${result?.token}`;
+          const opensearchUrl = `${window.location.origin}/visualization?access_token=${result?.token}`;
           window.open(opensearchUrl, '_blank');
         }
       },
@@ -77,6 +75,21 @@
     });
   }
 
+  openGrafanaDashboard() {
+    this.system.getGrafanaAuthToken().pipe(take(1)).subscribe({
+      next: (result: any) => {
+        if (result && result.token) {
+          const grafanaUrl = `${window.location.origin}/monitoring/?access_token=${result?.token}`;
+          window.open(grafanaUrl, '_blank');
+        }
+      },
+      error: error => {
+        this.ui.notifyError(`Error: ${error?.message}`);
+        console.log(error);
+      }
+    });
+  }
+
   restartService(element: any) {
     this.ui.confirmationService.openConfirmDialog({
       title: 'Restart Service',
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/apv-ssl-client-verification/apv-ssl-client-verification.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/apv-ssl-client-verification/apv-ssl-client-verification.html	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/apv-ssl-client-verification/apv-ssl-client-verification.html	(working copy)
@@ -4,15 +4,24 @@
   <div class="settings-section">
     <div class="section-title">Client Verification Settings</div>
     <form (ngSubmit)="updateClientVerificationSettings()" [formGroup]="configForm" class="common-form">
-      <div class="form-field-wrapper">
-        <label for="client_auth">Enable Client Verification</label>
-        <mat-slide-toggle id="client_auth" formControlName="client_auth"></mat-slide-toggle>
+      <div class="setting-item">
+        <div class="setting-label">
+          <label for="client_auth">Enable Client Verification</label>
+          <div class="setting-description">Enable or disable client certificate verification for this virtual site.
+          </div>
+        </div>
+        <mat-slide-toggle id="client_auth" formControlName="client_auth" color="primary"></mat-slide-toggle>
       </div>
-      <div class="form-field-wrapper">
-        <label for="host_status">Virtual Host Status ({{ hostName }})</label>
-        <mat-slide-toggle id="host_status" formControlName="host_status"></mat-slide-toggle>
+
+      <div class="setting-item">
+        <div class="setting-label">
+          <label for="host_status">Virtual Host Status ({{ hostName }})</label>
+          <div class="setting-description">Toggle the active status of the virtual host.</div>
+        </div>
+        <mat-slide-toggle id="host_status" formControlName="host_status" color="primary"></mat-slide-toggle>
       </div>
-      <div>
+
+      <div class="form-actions">
         <button mat-flat-button color="primary" type="submit">Update Settings</button>
       </div>
     </form>
@@ -31,8 +40,11 @@
         </mat-form-field>
 
         <!-- Action -->
-        <button mat-flat-button color="primary" (click)="importClientCertificate()">
-          <fa-icon [icon]="['fas', 'file-import']"></fa-icon> Import
+        <button mat-flat-button color="primary" class="action-button" (click)="importClientCertificate()">
+          <div class="button-content">
+            <fa-icon [icon]="['fas', 'file-import']"></fa-icon>
+            <span>Import</span>
+          </div>
         </button>
       </div>
     </div>
@@ -94,8 +106,11 @@
         </mat-form-field>
 
         <!-- Action -->
-        <button mat-flat-button color="primary" (click)="importRootCACertificate()">
-          <fa-icon [icon]="['fas', 'file-import']"></fa-icon> Import
+        <button mat-flat-button color="primary" class="action-button" (click)="importRootCACertificate()">
+          <div class="button-content">
+            <fa-icon [icon]="['fas', 'file-import']"></fa-icon>
+            <span>Import</span>
+          </div>
         </button>
       </div>
     </div>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/apv-ssl-client-verification/apv-ssl-client-verification.scss
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/apv-ssl-client-verification/apv-ssl-client-verification.scss	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/apv-ssl-client-verification/apv-ssl-client-verification.scss	(working copy)
@@ -19,35 +19,68 @@
     // Settings Section
     .settings-section {
         background: white;
-        padding: 16px;
+        padding: 24px;
         border-radius: 8px;
         border: 1px solid #e0e0e0;
+        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
 
         .section-title {
-            font-size: 16px;
+            font-size: 18px;
             font-weight: 500;
-            color: #424242;
-            margin-bottom: 16px;
+            color: #212121;
+            margin-bottom: 24px;
+            padding-bottom: 12px;
+            border-bottom: 1px solid #eeeeee;
         }
 
         .common-form {
             display: flex;
             flex-direction: column;
-            gap: 12px;
+            gap: 0; // Gap handled by padding/border of items
+            max-width: 800px;
 
-            .form-field-wrapper {
+            .setting-item {
                 display: flex;
+                justify-content: space-between;
                 align-items: center;
-                justify-content: flex-start;
-                gap: 24px;
-                // max-width: 400px; // Remove fixed max-width restriction or adjust if needed
+                padding: 16px 0;
+                border-bottom: 1px solid #f5f5f5;
 
-                label {
-                    font-size: 14px;
-                    color: #616161;
-                    margin-bottom: 0;
+                &:first-child {
+                    padding-top: 0;
                 }
+
+                &:last-of-type {
+                    border-bottom: none;
+                }
+
+                .setting-label {
+                    display: flex;
+                    flex-direction: column;
+                    gap: 4px;
+                    max-width: 70%;
+
+                    label {
+                        font-size: 15px;
+                        font-weight: 500;
+                        color: #333;
+                        margin-bottom: 0;
+                        cursor: pointer;
+                    }
+
+                    .setting-description {
+                        font-size: 13px;
+                        color: #757575;
+                        line-height: 1.4;
+                    }
+                }
             }
+
+            .form-actions {
+                margin-top: 32px;
+                display: flex;
+                justify-content: flex-start;
+            }
         }
     }
 
@@ -63,12 +96,33 @@
             display: flex;
             align-items: center;
             gap: 16px;
+
+            .action-button {
+                height: 40px; // Standard height to match input
+                padding: 0 16px; // Proper Horizontal padding
+
+                .button-content {
+                    display: flex;
+                    align-items: center;
+                    gap: 8px;
+
+                    fa-icon {
+                        font-size: 14px;
+                    }
+
+                    span {
+                        white-space: nowrap;
+                        font-size: 14px;
+                        font-weight: 500;
+                    }
+                }
+            }
         }
 
         .section-header {
-            font-size: 16px;
+            font-size: 18px;
             font-weight: 500;
-            color: #424242;
+            color: #212121;
         }
 
         .filter-field {
@@ -110,6 +164,7 @@
         overflow: hidden;
         border: 1px solid #e0e0e0;
         background: white;
+        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
         display: flex;
         flex-direction: column;
 
@@ -156,11 +211,11 @@
                 color: #c62828;
             }
         }
-    }
 
-    mat-paginator {
-        background: transparent;
-        border-top: 1px solid #e0e0e0;
-        flex-shrink: 0;
+        mat-paginator {
+            background: transparent;
+            border-top: 1px solid #e0e0e0;
+            flex-shrink: 0;
+        }
     }
 }
\ No newline at end of file
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/apv-ssl-client-verification/apv-ssl-client-verification.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/apv-ssl-client-verification/apv-ssl-client-verification.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/apv-ssl-client-verification/apv-ssl-client-verification.ts	(working copy)
@@ -10,6 +10,142 @@
 import { take } from 'rxjs/operators';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { MatSort } from '@angular/material/sort';
+
+@Component({
+  selector: 'import-ssl-client-cert',
+  templateUrl: './import-ssl-client-cert.html',
+  imports: [SharedModule]
+})
+export class ImportAPVSSLClientCertificateDialog implements OnInit {
+
+  readonly data = inject(MAT_DIALOG_DATA);
+  readonly dialogRef = inject(MatDialogRef<ImportAPVSSLClientCertificateDialog>);
+
+  private fb = inject(FormBuilder);
+  private device = inject(DeviceFacade);
+  private cdRef = inject(ChangeDetectorRef);
+  private ui = inject(CoreUiFacade);
+
+  configForm!: FormGroup;
+  domains: any = [];
+
+  ngOnInit() {
+    this.configForm = this.fb.group({
+      content: ['', Validators.required],
+      private_key: ['', [Validators.required]],
+      password: [''],
+    },)
+    this.domains = this.data?.domains;
+  }
+
+  onCancel() {
+    this.dialogRef.close(false);
+  }
+
+  onSubmit() {
+    if (this.configForm.invalid) {
+      console.log(this.configForm.value);
+      this.configForm.markAllAsTouched();
+      return;
+    }
+    let importClientKey = `ssl import clientkey "${this.data.hostName}" \nYES\n${this.configForm?.value?.private_key}\n...\n${this.configForm?.value?.password || ""}\n`;
+    let importClientCert = `ssl import clientcert "${this.data.hostName}" \nYES\n${this.configForm?.value?.content}\n...\n${this.configForm?.value?.password || ""}\n`;
+    this.device.executeAPVCLICommand(this.data?.deviceName, importClientKey)
+      .pipe(take(1))
+      .subscribe({
+        next: (res: any) => {
+          const responseContent = res.contents;
+          if (responseContent && typeof responseContent === 'string' && responseContent.includes('Failed')) {
+            this.ui.notifyError(responseContent);
+          } else {
+            this.device.executeAPVCLICommand(this.data?.deviceName, importClientCert)
+              .pipe(take(1))
+              .subscribe({
+                next: (res: any) => {
+                  const responseContent = res.contents;
+                  if (responseContent && typeof responseContent === 'string' && responseContent.includes('Failed')) {
+                    this.ui.notifyError(responseContent);
+                  } else {
+                    this.dialogRef.close(true);
+                    this.ui.notifySuccess(`The SSL client certificate has been imported successfully.`);
+                  }
+                },
+                error: (error: any) => {
+                  this.ui.notifyError(error.message);
+                  this.cdRef.detectChanges();
+                }
+              });
+          }
+        },
+        error: (error: any) => {
+          this.ui.notifyError(error.message);
+          this.cdRef.detectChanges();
+        }
+      });
+  }
+}
+
+@Component({
+  selector: 'import-ssl-rootca-cert',
+  templateUrl: './import-ssl-rootca-cert.html',
+  imports: [SharedModule]
+})
+export class ImportAPVSSLRootCACertificateDialog implements OnInit {
+
+  readonly data = inject(MAT_DIALOG_DATA);
+  readonly dialogRef = inject(MatDialogRef<ImportAPVSSLRootCACertificateDialog>);
+
+  private fb = inject(FormBuilder);
+  private device = inject(DeviceFacade);
+  private cdRef = inject(ChangeDetectorRef);
+  private ui = inject(CoreUiFacade);
+
+  configForm!: FormGroup;
+  domains: any = [];
+
+  ngOnInit() {
+    this.configForm = this.fb.group({
+      content: ['', Validators.required],
+      domain_name: [''],
+    },)
+    this.domains = this.data?.domains;
+  }
+
+  onCancel() {
+    this.dialogRef.close(false);
+  }
+
+  onSubmit() {
+    if (this.configForm.invalid) {
+      console.log(this.configForm.value);
+      this.configForm.markAllAsTouched();
+      return;
+    }
+    let importRootCA;
+    if (this.configForm?.value?.domain_name && this.configForm?.value?.domain_name !== '') {
+      importRootCA = `ssl import rootca "${this.data.hostName}" "${this.configForm?.value?.domain_name}" \n${this.configForm?.value?.content}\n...\n`;
+    } else {
+      importRootCA = `ssl import rootca "${this.data.hostName}" \n${this.configForm?.value?.content}\n...\n`;
+    }
+    this.device.executeAPVCLICommand(this.data?.deviceName, importRootCA)
+      .pipe(take(1))
+      .subscribe({
+        next: (res: any) => {
+          const responseContent = res.contents;
+          if (responseContent && typeof responseContent === 'string' && responseContent.includes('Failed')) {
+            this.ui.notifyError(responseContent);
+          } else {
+            this.dialogRef.close(true);
+            this.ui.notifySuccess(`The SSL RootCA certificate has been imported successfully.`);
+          }
+        },
+        error: (error: any) => {
+          this.ui.notifyError(error.message);
+          this.cdRef.detectChanges();
+        }
+      });
+  }
+}
 
 @Component({
   selector: 'app-apv-ssl-client-verification',
@@ -229,141 +365,7 @@
         this.getClientVerificationSettings();
       }
     })
-  }
-}
-
-@Component({
-  selector: 'import-ssl-client-cert',
-  templateUrl: './import-ssl-client-cert.html',
-  imports: [SharedModule]
-})
-export class ImportAPVSSLClientCertificateDialog implements OnInit {
-
-  readonly data = inject(MAT_DIALOG_DATA);
-  readonly dialogRef = inject(MatDialogRef<ImportAPVSSLClientCertificateDialog>);
-
-  private fb = inject(FormBuilder);
-  private device = inject(DeviceFacade);
-  private cdRef = inject(ChangeDetectorRef);
-  private ui = inject(CoreUiFacade);
-
-  configForm!: FormGroup;
-  domains: any = [];
-
-  ngOnInit() {
-    this.configForm = this.fb.group({
-      content: ['', Validators.required],
-      private_key: ['', [Validators.required]],
-      password: [''],
-    },)
-    this.domains = this.data?.domains;
-  }
-
-  onCancel() {
-    this.dialogRef.close(false);
-  }
-
-  onSubmit() {
-    if (this.configForm.invalid) {
-      console.log(this.configForm.value);
-      this.configForm.markAllAsTouched();
-      return;
-    }
-    let importClientKey = `ssl import clientkey "${this.data.hostName}" \nYES\n${this.configForm?.value?.private_key}\n...\n${this.configForm?.value?.password || ""}\n`;
-    let importClientCert = `ssl import clientcert "${this.data.hostName}" \nYES\n${this.configForm?.value?.content}\n...\n${this.configForm?.value?.password || ""}\n`;
-    this.device.executeAPVCLICommand(this.data?.deviceName, importClientKey)
-      .pipe(take(1))
-      .subscribe({
-        next: (res: any) => {
-          const responseContent = res.contents;
-          if (responseContent && typeof responseContent === 'string' && responseContent.includes('Failed')) {
-            this.ui.notifyError(responseContent);
-          } else {
-            this.device.executeAPVCLICommand(this.data?.deviceName, importClientCert)
-              .pipe(take(1))
-              .subscribe({
-                next: (res: any) => {
-                  const responseContent = res.contents;
-                  if (responseContent && typeof responseContent === 'string' && responseContent.includes('Failed')) {
-                    this.ui.notifyError(responseContent);
-                  } else {
-                    this.dialogRef.close(true);
-                    this.ui.notifySuccess(`The SSL client certificate has been imported successfully.`);
-                  }
-                },
-                error: (error: any) => {
-                  this.ui.notifyError(error.message);
-                  this.cdRef.detectChanges();
-                }
-              });
-          }
-        },
-        error: (error: any) => {
-          this.ui.notifyError(error.message);
-          this.cdRef.detectChanges();
-        }
-      });
   }
 }
 
-@Component({
-  selector: 'import-ssl-rootca-cert',
-  templateUrl: './import-ssl-rootca-cert.html',
-  imports: [SharedModule]
-})
-export class ImportAPVSSLRootCACertificateDialog implements OnInit {
 
-  readonly data = inject(MAT_DIALOG_DATA);
-  readonly dialogRef = inject(MatDialogRef<ImportAPVSSLRootCACertificateDialog>);
-
-  private fb = inject(FormBuilder);
-  private device = inject(DeviceFacade);
-  private cdRef = inject(ChangeDetectorRef);
-  private ui = inject(CoreUiFacade);
-
-  configForm!: FormGroup;
-  domains: any = [];
-
-  ngOnInit() {
-    this.configForm = this.fb.group({
-      content: ['', Validators.required],
-      domain_name: [''],
-    },)
-    this.domains = this.data?.domains;
-  }
-
-  onCancel() {
-    this.dialogRef.close(false);
-  }
-
-  onSubmit() {
-    if (this.configForm.invalid) {
-      console.log(this.configForm.value);
-      this.configForm.markAllAsTouched();
-      return;
-    }
-    let importRootCA;
-    if (this.configForm?.value?.domain_name && this.configForm?.value?.domain_name !== '') {
-      importRootCA = `ssl import rootca "${this.data.hostName}" "${this.configForm?.value?.domain_name}" \n${this.configForm?.value?.content}\n...\n`;
-    } else {
-      importRootCA = `ssl import rootca "${this.data.hostName}" \n${this.configForm?.value?.content}\n...\n`;
-    }
-    this.device.executeAPVCLICommand(this.data?.deviceName, importRootCA)
-      .pipe(take(1))
-      .subscribe({
-        next: (res: any) => {
-          const responseContent = res.contents;
-          if (responseContent && typeof responseContent === 'string' && responseContent.includes('Failed')) {
-            this.ui.notifyError(responseContent);
-          } else {
-            this.dialogRef.close(true);
-            this.ui.notifySuccess(`The SSL RootCA certificate has been imported successfully.`);
-          }
-        },
-        error: (error: any) => {
-          this.ui.notifyError(error.message);
-          this.cdRef.detectChanges();
-        }
-      });
-  }
-}
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-device-insights/dashboard-device-insights.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-device-insights/dashboard-device-insights.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-device-insights/dashboard-device-insights.spec.ts	(working copy)
@@ -1,32 +1,72 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
 import { DashboardDeviceInsights } from './dashboard-device-insights';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
 import { DeviceFacade } from '../../../services/facades/device.facade';
-import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
-import { MockDeviceFacade } from '../../../testing/mock-device.facade';
+import { of, throwError } from 'rxjs';
 
 describe('DashboardDeviceInsights', () => {
   let component: DashboardDeviceInsights;
   let fixture: ComponentFixture<DashboardDeviceInsights>;
+  let deviceFacade: any;
+  let uiFacade: any;
 
   beforeEach(async () => {
+    deviceFacade = {
+      getDeviceGroups: jest.fn().mockReturnValue(of({ devices: [] })),
+      getAVXDevices: jest.fn().mockReturnValue(of([]))
+    };
+    uiFacade = {
+      notifyError: jest.fn()
+    };
+
     await TestBed.configureTestingModule({
-      imports: [DashboardDeviceInsights, NoopAnimationsModule],
+      imports: [DashboardDeviceInsights],
       providers: [
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() }
+        { provide: CoreUiFacade, useValue: uiFacade },
+        { provide: DeviceFacade, useValue: deviceFacade }
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(DashboardDeviceInsights);
     component = fixture.componentInstance;
-    fixture.detectChanges();
+
+    // Forced override
+    (component as any).deviceFacade = deviceFacade;
+    (component as any).ui = uiFacade;
   });
 
   it('should create', () => {
+    fixture.detectChanges();
     expect(component).toBeTruthy();
   });
-});
 
+  it('should detect AVX, APV and SSLVPN devices', fakeAsync(() => {
+    deviceFacade.getDeviceGroups.mockReturnValue(of({
+      devices: [
+        { type: 'APV' },
+        { type: 'AG' }
+      ]
+    }));
+    deviceFacade.getAVXDevices.mockReturnValue(of([{ id: 1 }]));
+
+    component.ngOnInit();
+    tick();
+
+    expect(component.hasAvx).toBe(true);
+    expect(component.hasApv).toBe(true);
+    expect(component.hasSslVpn).toBe(true);
+    expect(component.isLoading).toBe(false);
+  }));
+
+  it('should handle API errors', fakeAsync(() => {
+    deviceFacade.getDeviceGroups.mockReturnValue(throwError(() => new Error('API Error')));
+    deviceFacade.getAVXDevices.mockReturnValue(of([])); // for forkJoin
+
+    component.ngOnInit();
+    tick();
+
+    expect(uiFacade.notifyError).toHaveBeenCalled();
+    expect(component.isLoading).toBe(false);
+  }));
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-apv/dashboard-insights-apv.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-apv/dashboard-insights-apv.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-apv/dashboard-insights-apv.spec.ts	(working copy)
@@ -1,34 +1,39 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { provideRouter } from '@angular/router';
-import { provideNoopAnimations } from '@angular/platform-browser/animations';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
 import { DashboardInsightsApv } from './dashboard-insights-apv';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
-import { DeviceFacade } from '../../../services/facades/device.facade';
 import { SystemFacade } from '../../../services/facades/system.facade';
-import { ChartOptionsService } from '../../../services/chart-options.service';
-import { UtilsService } from '../../../services/utils.service';
+import { DeviceFacade } from '../../../services/facades/device.facade';
 import { LayoutService } from '../../../services/layout.service';
-import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
-import { MockDeviceFacade } from '../../../testing/mock-device.facade';
-import { MockSystemFacade } from '../../../testing/mock-system.facade';
+import { UtilsService } from '../../../services/utils.service';
+import { ChartOptionsService } from '../../../services/chart-options.service';
 import { NgxEchartsModule } from 'ngx-echarts';
-import { of } from 'rxjs';
+import { of, throwError } from 'rxjs';
 
 describe('DashboardInsightsApv', () => {
   let component: DashboardInsightsApv;
   let fixture: ComponentFixture<DashboardInsightsApv>;
+  let systemFacade: any;
+  let deviceFacade: any;
+  let uiFacade: any;
+  let utilsMock: any;
 
   beforeEach(async () => {
-    const mockLayoutService = {
-      isDarkMode$: of(false)
+    systemFacade = {
+      getTopAPVVirtualServicesMetrics: jest.fn().mockReturnValue(of([])),
+      getTopAPVRealServicesMetrics: jest.fn().mockReturnValue(of([])),
+      getTopAPVLLBMetrics: jest.fn().mockReturnValue(of([]))
     };
-    const mockUtilsService = {
-      AUTO_REFRESH_INTERVAL: 60000,
-      formatChartData: jest.fn().mockReturnValue({})
+    deviceFacade = {
+      getDeviceGroups: jest.fn().mockReturnValue(of({ devices: [], deviceGroups: [] })),
+      mapDeviceDetails: jest.fn().mockReturnValue([])
     };
-    const mockChartOptionsService = {
-      getNoDataChartOptions: jest.fn().mockReturnValue({})
+    uiFacade = {
+      notifyError: jest.fn()
     };
+    utilsMock = {
+      AUTO_REFRESH_INTERVAL: 30000,
+      formatChartData: jest.fn().mockReturnValue({})
+    };
 
     await TestBed.configureTestingModule({
       imports: [
@@ -38,25 +43,72 @@
         })
       ],
       providers: [
-        provideRouter([]),
-        provideNoopAnimations(),
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() },
-        { provide: SystemFacade, useValue: new MockSystemFacade() },
-        { provide: LayoutService, useValue: mockLayoutService },
-        { provide: UtilsService, useValue: mockUtilsService },
-        { provide: ChartOptionsService, useValue: mockChartOptionsService }
+        { provide: CoreUiFacade, useValue: uiFacade },
+        { provide: SystemFacade, useValue: systemFacade },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: LayoutService, useValue: { isDarkMode$: of(false) } },
+        { provide: UtilsService, useValue: utilsMock },
+        { provide: ChartOptionsService, useValue: { getNoDataChartOptions: () => ({}) } }
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(DashboardInsightsApv);
     component = fixture.componentInstance;
-    fixture.detectChanges();
+
+    // Force overrides
+    (component as any).systemFacade = systemFacade;
+    (component as any).deviceFacade = deviceFacade;
+    (component as any).ui = uiFacade;
+    (component as any)._utils = utilsMock;
   });
 
   it('should create', () => {
+    fixture.detectChanges();
     expect(component).toBeTruthy();
   });
-});
 
+  it('should initialize and fetch all metrics', fakeAsync(() => {
+    deviceFacade.getDeviceGroups.mockReturnValue(of({
+      devices: [{ id: 1 }],
+      deviceGroups: []
+    }));
+
+    component.ngOnInit();
+    tick(); // setTimeout
+    fixture.detectChanges();
+    tick(); // interval startWith(0)
+
+    expect(deviceFacade.getDeviceGroups).toHaveBeenCalled();
+    expect(systemFacade.getTopAPVVirtualServicesMetrics).toHaveBeenCalled();
+    expect(systemFacade.getTopAPVRealServicesMetrics).toHaveBeenCalled();
+    expect(systemFacade.getTopAPVLLBMetrics).toHaveBeenCalled();
+  }));
+
+  it('should handle API errors', fakeAsync(() => {
+    deviceFacade.getDeviceGroups.mockReturnValue(throwError(() => ({ message: 'Error' })));
+
+    component.ngOnInit();
+    tick();
+    fixture.detectChanges();
+
+    expect(uiFacade.notifyError).toHaveBeenCalledWith('Error');
+  }));
+
+  it('should update charts when metrics are received', fakeAsync(() => {
+    const mockMetrics = [{ name: 'S1', connections: 10, hits: 20 }];
+    systemFacade.getTopAPVVirtualServicesMetrics.mockReturnValue(of(mockMetrics));
+    deviceFacade.mapDeviceDetails.mockReturnValue(mockMetrics);
+    utilsMock.formatChartData.mockReturnValue({
+      connections: [{ name: 'S1', value: 10 }],
+      hits: [{ name: 'S1', value: 20 }],
+      network: { names: ['S1'], inbound: [100], outbound: [50] }
+    });
+
+    component.getTopAPVVirtualServicesMetrics();
+
+    expect(component.vSHitsChartOption).toBeDefined();
+    expect(component.vSConnectionsChartOption).toBeDefined();
+    expect(component.vSThroughputChartOption).toBeDefined();
+  }));
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-avx/dashboard-insights-avx.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-avx/dashboard-insights-avx.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-avx/dashboard-insights-avx.spec.ts	(working copy)
@@ -1,33 +1,31 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { provideRouter } from '@angular/router';
-import { provideNoopAnimations } from '@angular/platform-browser/animations';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
 import { DashboardInsightsAvx } from './dashboard-insights-avx';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
 import { DeviceFacade } from '../../../services/facades/device.facade';
-import { ChartOptionsService } from '../../../services/chart-options.service';
 import { UtilsService } from '../../../services/utils.service';
+import { ChartOptionsService } from '../../../services/chart-options.service';
 import { LayoutService } from '../../../services/layout.service';
-import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
-import { MockDeviceFacade } from '../../../testing/mock-device.facade';
 import { NgxEchartsModule } from 'ngx-echarts';
-import { of } from 'rxjs';
+import { of, throwError } from 'rxjs';
 
 describe('DashboardInsightsAvx', () => {
   let component: DashboardInsightsAvx;
   let fixture: ComponentFixture<DashboardInsightsAvx>;
+  let deviceFacade: any;
+  let uiFacade: any;
+  let utilsMock: any;
 
   beforeEach(async () => {
-    const mockLayoutService = {
-      isDarkMode$: of(false)
+    deviceFacade = {
+      getAVXDevices: jest.fn().mockReturnValue(of([]))
     };
-    const mockUtilsService = {
-      AUTO_REFRESH_INTERVAL: 60000,
-      transformTotalConnectedDevicesMetrics: jest.fn().mockReturnValue({ total: 0, types: new Map() }),
-      transformConnectedDevicesMetrics: jest.fn().mockReturnValue([])
+    uiFacade = {
+      notifyError: jest.fn()
     };
-    const mockChartOptionsService = {
-      getNoDataChartOptions: jest.fn().mockReturnValue({}),
-      connectedDevicesDoughnutChartOptions: jest.fn().mockReturnValue({})
+    utilsMock = {
+      AUTO_REFRESH_INTERVAL: 30000,
+      transformTotalConnectedDevicesMetrics: jest.fn().mockReturnValue({ total: { connected: 0, disconnected: 0 }, types: new Map() }),
+      transformConnectedDevicesMetrics: jest.fn().mockReturnValue({ yAxisData: [] })
     };
 
     await TestBed.configureTestingModule({
@@ -38,24 +36,59 @@
         })
       ],
       providers: [
-        provideRouter([]),
-        provideNoopAnimations(),
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() },
-        { provide: LayoutService, useValue: mockLayoutService },
-        { provide: UtilsService, useValue: mockUtilsService },
-        { provide: ChartOptionsService, useValue: mockChartOptionsService }
+        { provide: CoreUiFacade, useValue: uiFacade },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: UtilsService, useValue: utilsMock },
+        { provide: ChartOptionsService, useValue: { getNoDataChartOptions: () => ({}), connectedDevicesDoughnutChartOptions: () => ({}) } },
+        { provide: LayoutService, useValue: { isDarkMode$: of(false) } }
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(DashboardInsightsAvx);
     component = fixture.componentInstance;
-    fixture.detectChanges();
+
+    // Force overrides
+    (component as any).deviceFacade = deviceFacade;
+    (component as any).ui = uiFacade;
+    (component as any)._utils = utilsMock;
   });
 
   it('should create', () => {
+    fixture.detectChanges();
     expect(component).toBeTruthy();
   });
-});
 
+  it('should initialize and fetch metrics', fakeAsync(() => {
+    deviceFacade.getAVXDevices.mockReturnValue(of([{ id: 1, name: 'AVX1', cpu_usage: 10, memory_usage: 20, disk_usage: 30 }]));
+
+    component.ngOnInit();
+    tick(); // setTimeout
+    fixture.detectChanges();
+    tick(); // interval startWith(0)
+
+    expect(deviceFacade.getAVXDevices).toHaveBeenCalled();
+    expect(component.avxDevices.length).toBe(1);
+  }));
+
+  it('should handle API errors', fakeAsync(() => {
+    deviceFacade.getAVXDevices.mockReturnValue(throwError(() => ({ message: 'Error' })));
+
+    component.ngOnInit();
+    tick();
+    fixture.detectChanges();
+
+    expect(uiFacade.notifyError).toHaveBeenCalledWith('Error: Error');
+  }));
+
+  it('should update chart options when data is received', fakeAsync(() => {
+    const mockAVX = [{ name: 'AVX1', cpu_usage: 10, memory_usage: 20, disk_usage: 30 }];
+    deviceFacade.getAVXDevices.mockReturnValue(of(mockAVX));
+
+    component.getAVXDevices();
+
+    expect(component.cpuChartOption).toBeDefined();
+    expect(component.memoryChartOption).toBeDefined();
+    expect(component.diskChartOption).toBeDefined();
+  }));
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-devices/dashboard-insights-devices.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-devices/dashboard-insights-devices.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-devices/dashboard-insights-devices.spec.ts	(working copy)
@@ -1,31 +1,38 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { provideRouter } from '@angular/router';
-import { provideNoopAnimations } from '@angular/platform-browser/animations';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
 import { DashboardInsightsDevices } from './dashboard-insights-devices';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
-import { DeviceFacade } from '../../../services/facades/device.facade';
 import { SystemFacade } from '../../../services/facades/system.facade';
-import { ChartOptionsService } from '../../../services/chart-options.service';
+import { DeviceFacade } from '../../../services/facades/device.facade';
 import { UtilsService } from '../../../services/utils.service';
-import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
-import { MockDeviceFacade } from '../../../testing/mock-device.facade';
-import { MockSystemFacade } from '../../../testing/mock-system.facade';
+import { ChartOptionsService } from '../../../services/chart-options.service';
 import { NgxEchartsModule } from 'ngx-echarts';
+import { of, throwError } from 'rxjs';
 
 describe('DashboardInsightsDevices', () => {
   let component: DashboardInsightsDevices;
   let fixture: ComponentFixture<DashboardInsightsDevices>;
+  let systemFacade: any;
+  let deviceFacade: any;
+  let uiFacade: any;
+  let utilsMock: any;
 
   beforeEach(async () => {
-    const mockUtilsService = {
-      AUTO_REFRESH_INTERVAL: 60000,
-      transformTotalConnectedDevicesMetrics: jest.fn().mockReturnValue({ types: new Map() }),
-      transformConnectedDevicesMetrics: jest.fn().mockReturnValue([]),
-      formatChartData: jest.fn().mockReturnValue({})
+    systemFacade = {
+      getConnectedDevicesMetrics: jest.fn().mockReturnValue(of({}))
     };
-    const mockChartOptionsService = {
-      connectedDeviceLineChart: jest.fn().mockReturnValue({})
+    deviceFacade = {
+      getDeviceGroups: jest.fn().mockReturnValue(of({ devices: [], groups: [] })),
+      mapDeviceDetails: jest.fn().mockReturnValue([])
     };
+    uiFacade = {
+      notifyError: jest.fn()
+    };
+    utilsMock = {
+      AUTO_REFRESH_INTERVAL: 30000,
+      transformTotalConnectedDevicesMetrics: jest.fn().mockReturnValue({ total: { connected: 0, disconnected: 0 }, types: new Map() }),
+      transformConnectedDevicesMetrics: jest.fn().mockReturnValue({ yAxisData: [] }),
+      formatChartData: jest.fn().mockReturnValue({})
+    };
 
     await TestBed.configureTestingModule({
       imports: [
@@ -35,24 +42,53 @@
         })
       ],
       providers: [
-        provideRouter([]),
-        provideNoopAnimations(),
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() },
-        { provide: SystemFacade, useValue: new MockSystemFacade() },
-        { provide: UtilsService, useValue: mockUtilsService },
-        { provide: ChartOptionsService, useValue: mockChartOptionsService }
+        { provide: CoreUiFacade, useValue: uiFacade },
+        { provide: SystemFacade, useValue: systemFacade },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: UtilsService, useValue: utilsMock },
+        { provide: ChartOptionsService, useValue: { connectedDeviceLineChart: () => ({}) } }
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(DashboardInsightsDevices);
     component = fixture.componentInstance;
-    fixture.detectChanges();
+
+    // Force overrides
+    (component as any).systemFacade = systemFacade;
+    (component as any).deviceFacade = deviceFacade;
+    (component as any).ui = uiFacade;
+    (component as any)._utils = utilsMock;
   });
 
   it('should create', () => {
+    fixture.detectChanges();
     expect(component).toBeTruthy();
   });
-});
 
+  it('should initialize and fetch metrics', fakeAsync(() => {
+    component.ngOnInit();
+    tick(); // setTimeout
+    fixture.detectChanges();
+    tick(); // interval startWith(0)
+
+    expect(deviceFacade.getDeviceGroups).toHaveBeenCalled();
+    expect(systemFacade.getConnectedDevicesMetrics).toHaveBeenCalled();
+  }));
+
+  it('should update charts when data is received', fakeAsync(() => {
+    const mockDevices = [{ id: 1, connection: true }];
+    deviceFacade.getDeviceGroups.mockReturnValue(of({ devices: mockDevices, groups: [] }));
+    systemFacade.getConnectedDevicesMetrics.mockReturnValue(of([{ id: 1 }]));
+    utilsMock.formatChartData.mockReturnValue({
+      cpu: [{ name: 'D1', value: 10 }],
+      memory: [{ name: 'D1', value: 20 }],
+      network: { names: ['D1'], inbound: [100], outbound: [50] }
+    });
+
+    component.getDeviceGroups();
+
+    expect(component.connectedDevices).toBe(1);
+    expect(component.devicesCPUChartOption1).toBeDefined();
+  }));
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-insights-sslvpn/dashboard-insights-sslvpn.spec.ts	(working copy)
@@ -1,32 +1,37 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { provideRouter } from '@angular/router';
-import { provideNoopAnimations } from '@angular/platform-browser/animations';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
 import { DashboardInsightsSslvpn } from './dashboard-insights-sslvpn';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
 import { SystemFacade } from '../../../services/facades/system.facade';
-import { ChartOptionsService } from '../../../services/chart-options.service';
-import { UtilsService } from '../../../services/utils.service';
+import { DeviceFacade } from '../../../services/facades/device.facade';
 import { LayoutService } from '../../../services/layout.service';
-import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
-import { MockSystemFacade } from '../../../testing/mock-system.facade';
+import { UtilsService } from '../../../services/utils.service';
+import { ChartOptionsService } from '../../../services/chart-options.service';
 import { NgxEchartsModule } from 'ngx-echarts';
-import { of } from 'rxjs';
+import { of, throwError } from 'rxjs';
 
 describe('DashboardInsightsSslvpn', () => {
   let component: DashboardInsightsSslvpn;
   let fixture: ComponentFixture<DashboardInsightsSslvpn>;
+  let systemFacade: any;
+  let deviceFacade: any;
+  let uiFacade: any;
+  let utilsMock: any;
 
   beforeEach(async () => {
-    const mockLayoutService = {
-      isDarkMode$: of(false)
+    systemFacade = {
+      getTopSSLVPNMetrics: jest.fn().mockReturnValue(of([]))
     };
-    const mockUtilsService = {
-      AUTO_REFRESH_INTERVAL: 60000,
-      formatChartData: jest.fn().mockReturnValue({})
+    deviceFacade = {
+      getDeviceGroups: jest.fn().mockReturnValue(of({ devices: [], groups: [] })),
+      mapDeviceDetails: jest.fn().mockReturnValue([])
     };
-    const mockChartOptionsService = {
-      getNoDataChartOptions: jest.fn().mockReturnValue({})
+    uiFacade = {
+      notifyError: jest.fn()
     };
+    utilsMock = {
+      AUTO_REFRESH_INTERVAL: 30000,
+      formatChartData: jest.fn().mockReturnValue({})
+    };
 
     await TestBed.configureTestingModule({
       imports: [
@@ -36,24 +41,70 @@
         })
       ],
       providers: [
-        provideRouter([]),
-        provideNoopAnimations(),
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: SystemFacade, useValue: new MockSystemFacade() },
-        { provide: LayoutService, useValue: mockLayoutService },
-        { provide: UtilsService, useValue: mockUtilsService },
-        { provide: ChartOptionsService, useValue: mockChartOptionsService }
+        { provide: CoreUiFacade, useValue: uiFacade },
+        { provide: SystemFacade, useValue: systemFacade },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: LayoutService, useValue: { isDarkMode$: of(false) } },
+        { provide: UtilsService, useValue: utilsMock },
+        { provide: ChartOptionsService, useValue: { getNoDataChartOptions: () => ({}) } }
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(DashboardInsightsSslvpn);
     component = fixture.componentInstance;
-    fixture.detectChanges();
+
+    // Force overrides
+    (component as any).systemFacade = systemFacade;
+    (component as any).deviceFacade = deviceFacade;
+    (component as any).ui = uiFacade;
+    (component as any)._utils = utilsMock;
   });
 
   it('should create', () => {
+    fixture.detectChanges();
     expect(component).toBeTruthy();
   });
-});
 
+  it('should initialize and fetch metrics', fakeAsync(() => {
+    deviceFacade.getDeviceGroups.mockReturnValue(of({
+      devices: [{ id: 1 }],
+      groups: []
+    }));
+
+    component.ngOnInit();
+    tick(); // setTimeout
+    fixture.detectChanges();
+    tick(); // interval startWith(0)
+
+    expect(deviceFacade.getDeviceGroups).toHaveBeenCalled();
+    expect(systemFacade.getTopSSLVPNMetrics).toHaveBeenCalled();
+  }));
+
+  it('should handle API errors', fakeAsync(() => {
+    deviceFacade.getDeviceGroups.mockReturnValue(throwError(() => ({ message: 'Error' })));
+
+    component.ngOnInit();
+    tick();
+    fixture.detectChanges();
+
+    expect(uiFacade.notifyError).toHaveBeenCalledWith('Error');
+  }));
+
+  it('should update charts when metrics are received', fakeAsync(() => {
+    const mockMetrics = [{ name: 'VPN1', active_sessions: 10 }];
+    systemFacade.getTopSSLVPNMetrics.mockReturnValue(of(mockMetrics));
+    deviceFacade.mapDeviceDetails.mockReturnValue(mockMetrics);
+    utilsMock.formatChartData.mockReturnValue({
+      active_sessions: [{ name: 'VPN1', value: 10 }],
+      client_network: { names: ['VPN1'], inbound: [100], outbound: [50] },
+      server_network: { names: ['VPN1'], inbound: [200], outbound: [100] }
+    });
+
+    component.getTopSSLVPNMetrics();
+
+    expect(component.activeSessionsChartOption).toBeDefined();
+    expect(component.clientThroughputChartOption).toBeDefined();
+    expect(component.serverThroughputChartOption).toBeDefined();
+  }));
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.scss
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.scss	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.scss	(working copy)
@@ -2,10 +2,11 @@
 .dashboard-grid {
   display: grid;
   grid-template-columns: repeat(4, 1fr);
+  grid-template-rows: auto minmax(200px, 1fr) minmax(200px, 1fr);
   gap: 16px;
   padding: 16px;
-  height: 100%;
-  overflow-y: auto;
+  height: calc(100vh - 135px);
+  overflow: hidden;
   scrollbar-width: none;
   /* Firefox */
   -ms-overflow-style: none;
@@ -160,7 +161,7 @@
   height: 100%;
   width: 100%;
   flex-grow: 1;
-  min-height: 250px;
+  min-height: 0;
   /* Ensure chart has height */
 }
 
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/dashboard-system-insights/dashboard-system-insights.spec.ts	(working copy)
@@ -1,47 +1,231 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { RouterTestingModule } from '@angular/router/testing';
-import { DashboardSystemInsights } from './dashboard-system-insights';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
+import { provideRouter } from '@angular/router';
+import { DashboardSystemInsights, EnlargeSystemLoad, EnlargeSystemDevices, EnlargeSystemThroughput, EnlargeSystemDiskUsage } from './dashboard-system-insights';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
 import { DeviceFacade } from '../../../services/facades/device.facade';
 import { SystemFacade } from '../../../services/facades/system.facade';
 import { LayoutService } from '../../../services/layout.service';
-import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
-import { MockDeviceFacade } from '../../../testing/mock-device.facade';
-import { MockSystemFacade } from '../../../testing/mock-system.facade';
-import { MockLayoutService } from '../../../testing/mock-layout.service';
 import { NgxEchartsModule } from 'ngx-echarts';
+import { of, throwError } from 'rxjs';
+import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
+import { Router } from '@angular/router';
+import { UtilsService } from '../../../services/utils.service';
+import { ChartOptionsService } from '../../../services/chart-options.service';
 
 describe('DashboardSystemInsights', () => {
   let component: DashboardSystemInsights;
   let fixture: ComponentFixture<DashboardSystemInsights>;
+  let systemFacade: any;
+  let deviceFacade: any;
+  let uiFacade: any;
+  let dialogMock: any;
+  let utilsMock: any;
 
   beforeEach(async () => {
+    systemFacade = {
+      getLatestSystemMetrics: jest.fn().mockReturnValue(of({})),
+      getHistoricalSystemMetrics: jest.fn().mockReturnValue(of({})),
+      getConnectedDevicesMetrics: jest.fn().mockReturnValue(of([])),
+      getConnectedDevicesHistoricalMetrics: jest.fn().mockReturnValue(of({}))
+    };
+    deviceFacade = {
+      getDeviceGroups: jest.fn().mockReturnValue(of({ devices: [], groups: [] })),
+      getAPVVirtualServices: jest.fn().mockReturnValue(of([])),
+      mapDeviceDetails: jest.fn().mockReturnValue([])
+    };
+    uiFacade = {
+      notifyError: jest.fn()
+    };
+    dialogMock = {
+      open: jest.fn().mockReturnValue({
+        afterClosed: () => of(true),
+        close: jest.fn()
+      })
+    };
+    utilsMock = {
+      AUTO_REFRESH_INTERVAL: 30000,
+      transformTotalConnectedDevicesMetrics: jest.fn().mockReturnValue({ total: { connected: 0, disconnected: 0 }, types: new Map() }),
+      transformConnectedDevicesMetrics: jest.fn().mockReturnValue({ yAxisData: [] })
+    };
+
     await TestBed.configureTestingModule({
       imports: [
         DashboardSystemInsights,
-        NoopAnimationsModule,
-        RouterTestingModule,
         NgxEchartsModule.forRoot({
           echarts: () => import('echarts'),
         })
       ],
       providers: [
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() },
-        { provide: SystemFacade, useValue: new MockSystemFacade() },
-        { provide: LayoutService, useValue: new MockLayoutService() }
+        provideRouter([]),
+        { provide: CoreUiFacade, useValue: uiFacade },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: SystemFacade, useValue: systemFacade },
+        { provide: LayoutService, useValue: { isDarkMode$: of(false) } },
+        { provide: MatDialog, useValue: dialogMock },
+        { provide: Router, useValue: { navigate: jest.fn() } },
+        { provide: UtilsService, useValue: utilsMock },
+        {
+          provide: ChartOptionsService, useValue: {
+            getNoDataChartOptions: () => ({}),
+            connectedDevicesDoughnutChartOptions: () => ({}),
+            getCPUGaugeOptions: () => ({}),
+            getMemoryGaugeOptions: () => ({})
+          }
+        }
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(DashboardSystemInsights);
     component = fixture.componentInstance;
-    fixture.detectChanges();
+
+    (component as any).dialog = dialogMock;
+    (component as any).systemFacade = systemFacade;
+    (component as any).deviceFacade = deviceFacade;
+    (component as any)._utils = utilsMock;
+    (component as any).ui = uiFacade;
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should initialize and fetch metrics on interval', fakeAsync(() => {
+    const mockLatest = {
+      cpu: [{ host: 'node1', cpu_percent: 10 }],
+      memory: [{ host: 'node1', mem_percent: 20 }],
+      disk_usage: [{ host: 'node1', total_bytes: 1000, used_bytes: 500, free_bytes: 500 }],
+      net_metrics: [{ host: 'node1', sent: 10, received: 20 }]
+    };
+    systemFacade.getLatestSystemMetrics.mockReturnValue(of(mockLatest));
+    systemFacade.getHistoricalSystemMetrics.mockReturnValue(of({ cpu_history: [] }));
+
+    component.ngOnInit();
+    tick(); // for setTimeout
+    fixture.detectChanges();
+    tick(0); // for startWith(0)
+
+    expect(systemFacade.getLatestSystemMetrics).toHaveBeenCalled();
+    expect(component.nodeMetrics.length).toBe(1);
+
+    tick(30000); // Trigger interval
+    expect(systemFacade.getLatestSystemMetrics).toHaveBeenCalledTimes(2);
+  }));
+
+  it('should handle API errors with notifications', fakeAsync(() => {
+    systemFacade.getLatestSystemMetrics.mockReturnValue(throwError(() => ({ message: 'Failed to fetch' })));
+
+    component.getLatestSystemMetrics();
+
+    expect(uiFacade.notifyError).toHaveBeenCalledWith('Failed to fetch');
+  }));
+
+  it('should test enlargement methods', () => {
+    component.enlargeSystemLoad();
+    expect(dialogMock.open).toHaveBeenCalledWith(EnlargeSystemLoad, expect.any(Object));
+
+    component.enlargeSystemThroughput();
+    expect(dialogMock.open).toHaveBeenCalledWith(EnlargeSystemThroughput, expect.any(Object));
+
+    component.enlargeSystemDiskUsage();
+    expect(dialogMock.open).toHaveBeenCalledWith(EnlargeSystemDiskUsage, expect.any(Object));
+
+    component.enlargeSystemDevices();
+    expect(dialogMock.open).toHaveBeenCalledWith(EnlargeSystemDevices, expect.any(Object));
+  });
+
+  it('should format bytes and bits correctly', () => {
+    expect(component.formatBytes(1024)).toBe('1 KB');
+    expect(component.formatBytes(1048576)).toBe('1 MB');
+    expect(component.formatBits(1024)).toBe('1.02 Kbps');
+    expect(component.formatBits(1048576)).toBe('1.05 Mbps');
+  });
+
+  it('should handle multi-node metrics aggregation', fakeAsync(() => {
+    const mockMultiLatest = {
+      cpu: [{ host: 'n1', cpu_percent: 10 }, { host: 'n2', cpu_percent: 20 }],
+      memory: [{ host: 'n1', mem_percent: 30 }, { host: 'n2', mem_percent: 40 }],
+      disk_usage: [
+        { host: 'n1', total_bytes: 100, used_bytes: 50, free_bytes: 50 },
+        { host: 'n2', total_bytes: 100, used_bytes: 60, free_bytes: 40 }
+      ]
+    };
+    systemFacade.getLatestSystemMetrics.mockReturnValue(of(mockMultiLatest));
+
+    component.getLatestSystemMetrics();
+
+    expect(component.isMultiNode).toBe(true);
+    expect(component.nodeMetrics.length).toBe(2);
+  }));
+
+  it('should update chart theme', () => {
+    const spy = jest.spyOn(component, 'applyThemeOverrides');
+    component.updateChartTheme(true);
+    expect(spy).toHaveBeenCalledWith(true);
+  });
 });
 
+describe('EnlargeComponents', () => {
+  let systemFacade: any;
+  let deviceFacade: any;
+  let uiFacade: any;
+  let utilsMock: any;
+
+  beforeEach(() => {
+    systemFacade = {
+      getHistoricalSystemMetrics: jest.fn().mockReturnValue(of({ cpu_history: [], mem_history: [], net_history: [], disk_io_history: [] })),
+      getLatestSystemMetrics: jest.fn().mockReturnValue(of({ cpu: [], memory: [], disk_usage: [] })),
+      getConnectedDevicesHistoricalMetrics: jest.fn().mockReturnValue(of({}))
+    };
+    deviceFacade = {
+      getDeviceGroups: jest.fn().mockReturnValue(of({ devices: [] })),
+      mapDeviceDetails: jest.fn().mockReturnValue([])
+    };
+    uiFacade = {
+      notifyError: jest.fn()
+    };
+    utilsMock = {
+      AUTO_REFRESH_INTERVAL: 100,
+      transformHistoricalSeries: jest.fn().mockReturnValue([]),
+      transformTotalConnectedDevicesMetrics: jest.fn().mockReturnValue({ total: { connected: 0, disconnected: 0 }, types: new Map() }),
+      transformConnectedDevicesMetrics: jest.fn().mockReturnValue({ yAxisData: [] })
+    };
+  });
+
+  async function runEnlargeTest(comp: any, data: any = {}) {
+    await TestBed.configureTestingModule({
+      imports: [comp, NgxEchartsModule.forRoot({ echarts: () => import('echarts') })],
+      providers: [
+        provideRouter([]),
+        { provide: MAT_DIALOG_DATA, useValue: data },
+        { provide: MatDialogRef, useValue: { close: jest.fn() } },
+        { provide: SystemFacade, useValue: systemFacade },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: CoreUiFacade, useValue: uiFacade },
+        { provide: ChartOptionsService, useValue: { getNoDataChartOptions: () => ({}) } },
+        { provide: UtilsService, useValue: utilsMock },
+        { provide: LayoutService, useValue: { isDarkMode$: of(false) } }
+      ]
+    }).compileComponents();
+
+    const fixture = TestBed.createComponent(comp);
+    fixture.detectChanges();
+    expect(fixture.componentInstance).toBeTruthy();
+  }
+
+  it('should create EnlargeSystemLoad', async () => {
+    await runEnlargeTest(EnlargeSystemLoad, { host: 'node1' });
+  });
+
+  it('should create EnlargeSystemDevices', async () => {
+    await runEnlargeTest(EnlargeSystemDevices, { devices: [] });
+  });
+
+  it('should create EnlargeSystemThroughput', async () => {
+    await runEnlargeTest(EnlargeSystemThroughput, { host: 'node1' });
+  });
+
+  it('should create EnlargeSystemDiskUsage', async () => {
+    await runEnlargeTest(EnlargeSystemDiskUsage, { host: 'node1' });
+  });
+});
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 2944)
+++ /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)
@@ -276,9 +276,6 @@
             });
           });
 
-          console.log('Multi-node detected:', this.isMultiNode);
-          console.log('Node metrics:', this.nodeMetrics);
-
           // Handle single-node specific legacy variable `networkMetrics` (sum of all interfaces)
           if (this.currentSystemMetrics?.net_metrics && this.currentSystemMetrics.net_metrics.length > 0) {
             this.networkMetrics = this.currentSystemMetrics.net_metrics.reduce((acc: any, net: any) => {
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-details/device-details.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-details/device-details.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-details/device-details.spec.ts	(working copy)
@@ -1,4 +1,4 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
 import { provideRouter } from '@angular/router';
 import { provideNoopAnimations } from '@angular/platform-browser/animations';
 import { DeviceDetails } from './device-details';
@@ -7,15 +7,20 @@
 import { UtilsService } from '../../../services/utils.service';
 import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
 import { MockDeviceFacade } from '../../../testing/mock-device.facade';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { MatTableDataSource } from '@angular/material/table';
+import { of, throwError } from 'rxjs';
 
 describe('DeviceDetails', () => {
   let component: DeviceDetails;
   let fixture: ComponentFixture<DeviceDetails>;
+  let deviceFacade: MockDeviceFacade;
+  let coreUiFacade: MockCoreUiFacade;
+  let router: Router;
+  let utilsService: any;
 
   beforeEach(async () => {
-    const mockUtilsService = {
+    utilsService = {
       processConfigData: jest.fn().mockReturnValue(new MatTableDataSource([]))
     };
 
@@ -23,8 +28,22 @@
     Object.defineProperty(window, 'history', {
       value: {
         state: {
-          deviceData: { name: 'test', snmp_general: { snmp_enable: false } },
-          groups: []
+          deviceData: {
+            id: 1,
+            name: 'test-device',
+            type: 'APV',
+            restapi_port: '9997',
+            gateway_domain: 'test.com',
+            location: 'test-loc',
+            webui_port: '8888',
+            device_group: 'G1',
+            enable_password: 'pass',
+            log_enable: 1,
+            snmp_general: { snmp_enable: false },
+            license_key: 'LK123',
+            host_name: 'host1'
+          },
+          groups: [{ name: 'G1' }]
         }
       },
       writable: true
@@ -35,15 +54,15 @@
       providers: [
         provideRouter([]),
         provideNoopAnimations(),
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() },
-        { provide: UtilsService, useValue: mockUtilsService },
+        { provide: CoreUiFacade, useClass: MockCoreUiFacade },
+        { provide: DeviceFacade, useClass: MockDeviceFacade },
+        { provide: UtilsService, useValue: utilsService },
         {
           provide: ActivatedRoute,
           useValue: {
             snapshot: {
               paramMap: {
-                get: (key: string) => 'test'
+                get: (key: string) => 'test-device'
               }
             }
           }
@@ -54,11 +73,145 @@
 
     fixture = TestBed.createComponent(DeviceDetails);
     component = fixture.componentInstance;
+    deviceFacade = TestBed.inject(DeviceFacade) as any;
+    coreUiFacade = TestBed.inject(CoreUiFacade) as any;
+    router = TestBed.inject(Router);
+
+    deviceFacade.getDeviceConfiguration.mockReturnValue(of(['', 'Mock Config Content']));
+
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
-});
 
+  it('should initialize data from history state', () => {
+    expect(component.deviceName).toBe('test-device');
+    expect(component.deviceData.name).toBe('test-device');
+    expect(utilsService.processConfigData).toHaveBeenCalled();
+  });
+
+  it('should handle stringified deviceData in history state', () => {
+    (window.history as any).state.deviceData = JSON.stringify({ name: 'stringified' });
+    component.ngOnInit();
+    expect(component.deviceData.name).toBe('stringified');
+  });
+
+  it('should navigate back to managed devices', () => {
+    const spy = jest.spyOn(router, 'navigate');
+    component.backToManagedDevices();
+    expect(spy).toHaveBeenCalledWith(['/inventory/devices']);
+  });
+
+  it('should toggle monitoring state successfully', () => {
+    deviceFacade.updateDeviceMonitoringState = jest.fn().mockReturnValue(of([true, 'Success']));
+    const spy = jest.spyOn(component, 'backToManagedDevices');
+
+    component.onMonitorToggleChange({ checked: true });
+
+    expect(deviceFacade.updateDeviceMonitoringState).toHaveBeenCalled();
+    expect(coreUiFacade.notifySuccess).toHaveBeenCalledWith('test-device updated successfully!');
+    expect(spy).toHaveBeenCalled();
+  });
+
+  it('should handle error when toggling monitoring', () => {
+    deviceFacade.updateDeviceMonitoringState = jest.fn().mockReturnValue(of([false, 'Failed']));
+
+    component.onMonitorToggleChange({ checked: true });
+
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Failed to update device: Failed');
+  });
+
+  it('should fetch device configuration', () => {
+    component.getDeviceConfiguration('test-device');
+    expect(deviceFacade.getDeviceConfiguration).toHaveBeenCalledWith('test-device');
+    expect(component.deviceConfig).toBe('Mock Config Content');
+  });
+
+  it('should handle error when fetching configuration', () => {
+    deviceFacade.getDeviceConfiguration.mockReturnValue(throwError(() => ({ message: 'Error' })));
+    component.getDeviceConfiguration('test-device');
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Error: Error');
+  });
+
+  it('should update device settings successfully', () => {
+    deviceFacade.updateDevice = jest.fn().mockReturnValue(of([true, 'Success']));
+    const spy = jest.spyOn(component, 'backToManagedDevices');
+
+    component.deviceSettingsForm.patchValue({
+      portNumber: '9997',
+      apiUsername: 'admin',
+      apiPassword: 'password',
+      webuiUsername: 'admin',
+      webuiPassword: 'password',
+      groupName: 'G1'
+    });
+
+    component.updateDeviceSettings();
+
+    expect(deviceFacade.updateDevice).toHaveBeenCalled();
+    expect(coreUiFacade.notifySuccess).toHaveBeenCalledWith('test-device updated successfully!');
+    expect(spy).toHaveBeenCalled();
+  });
+
+  it('should not update if form is invalid', () => {
+    component.deviceSettingsForm.patchValue({
+      portNumber: '', // Invalid
+    });
+    const spy = jest.spyOn(console, 'log').mockImplementation();
+
+    component.updateDeviceSettings();
+
+    expect(deviceFacade.updateDevice).not.toHaveBeenCalled();
+    spy.mockRestore();
+  });
+
+  it('should handle error when updating device', () => {
+    deviceFacade.updateDevice = jest.fn().mockReturnValue(throwError(() => new Error('Error')));
+
+    component.deviceSettingsForm.patchValue({
+      portNumber: '9997',
+      apiUsername: 'admin',
+      apiPassword: 'password',
+      webuiUsername: 'admin',
+      webuiPassword: 'password',
+      groupName: 'G1'
+    });
+
+    component.updateDeviceSettings();
+
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Update Failed.');
+  });
+
+  it('should download config file', () => {
+    const spyUrl = jest.fn().mockReturnValue('mock-url');
+    const spyRevoke = jest.fn();
+
+    // Using a safe way to mock URL
+    const originalURL = window.URL;
+    (window as any).URL = {
+      createObjectURL: spyUrl,
+      revokeObjectURL: spyRevoke
+    };
+
+    // Mock anchor element
+    const mockAnchor = {
+      href: '',
+      download: '',
+      click: jest.fn()
+    } as any;
+    const spyCreate = jest.spyOn(document, 'createElement').mockReturnValue(mockAnchor);
+
+    component.deviceConfig = 'Mock Config Content';
+    component.downloadConfigFile();
+
+    expect(spyUrl).toHaveBeenCalled();
+    expect(mockAnchor.download).toBe('test-device_config.txt');
+    expect(mockAnchor.click).toHaveBeenCalled();
+    expect(spyRevoke).toHaveBeenCalled();
+
+    spyCreate.mockRestore();
+    window.URL = originalURL;
+  });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-groups/device-groups.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-groups/device-groups.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/device-groups/device-groups.spec.ts	(working copy)
@@ -1,35 +1,143 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { DeviceGroups } from './device-groups';
+import { provideRouter } from '@angular/router';
+import { provideNoopAnimations } from '@angular/platform-browser/animations';
+import { DeviceGroups, AddDeviceGroupsDialog } from './device-groups';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
 import { DeviceFacade } from '../../../services/facades/device.facade';
 import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
 import { MockDeviceFacade } from '../../../testing/mock-device.facade';
 import { GlobalSerialPipe } from '../../../pipes/global-serial-pipe';
 import { SearchFromResults } from '../../common/search-from-results/search-from-results';
+import { of, throwError } from 'rxjs';
+import { MatDialog } from '@angular/material/dialog';
 
 describe('DeviceGroups', () => {
   let component: DeviceGroups;
   let fixture: ComponentFixture<DeviceGroups>;
+  let deviceFacade: MockDeviceFacade;
+  let coreUiFacade: MockCoreUiFacade;
+  let mockDialog: any;
 
   beforeEach(async () => {
+    mockDialog = {
+      open: jest.fn().mockReturnValue({
+        afterClosed: () => of(true)
+      })
+    };
+
     await TestBed.configureTestingModule({
-      imports: [DeviceGroups, RouterTestingModule, NoopAnimationsModule, GlobalSerialPipe, SearchFromResults],
+      imports: [DeviceGroups, GlobalSerialPipe, SearchFromResults],
       providers: [
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() }
+        provideRouter([]),
+        provideNoopAnimations(),
+        { provide: CoreUiFacade, useClass: MockCoreUiFacade },
+        { provide: DeviceFacade, useClass: MockDeviceFacade },
+        { provide: MatDialog, useValue: mockDialog }
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(DeviceGroups);
     component = fixture.componentInstance;
+    deviceFacade = TestBed.inject(DeviceFacade) as any;
+    coreUiFacade = TestBed.inject(CoreUiFacade) as any;
+
+    // Forcing the mock into the component instance
+    (component as any).dialog = mockDialog;
+
+    deviceFacade.getDeviceGroups.mockReturnValue(of({ groups: [], devices: [] }));
+
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
-});
 
+  it('should fetch device groups on init', () => {
+    const mockGroups = [{ group_name: 'G1', device_list: [] }];
+    deviceFacade.getDeviceGroups.mockReturnValue(of({ groups: mockGroups }));
+
+    component.getDeviceGroups();
+
+    expect(deviceFacade.getDeviceGroups).toHaveBeenCalled();
+    expect(component.deviceGroups.length).toBe(1);
+    expect(component.totalRecords).toBe(1);
+  });
+
+  it('should handle error when fetching device groups', () => {
+    const spy = jest.spyOn(console, 'log').mockImplementation();
+    deviceFacade.getDeviceGroups.mockReturnValue(throwError(() => ({ message: 'Error' })));
+
+    component.getDeviceGroups();
+
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Error');
+    spy.mockRestore();
+  });
+
+  it('should apply filter to data source', () => {
+    component.applyFilter('test');
+    expect(component.searchKey).toBe('test');
+    expect(component.dataSource.filter).toBe('test');
+  });
+
+  it('should format truncated array string correctly', () => {
+    const devices = [{ ip: '1.1.1.1' }, { ip: '2.2.2.2' }];
+    const result = component.getTruncatedArrayString(devices, 10);
+    expect(result.display).toBe('1.1.1.1, 2...');
+    expect(result.full).toBe('1.1.1.1, 2.2.2.2');
+  });
+
+  it('should return empty strings for empty array in getTruncatedArrayString', () => {
+    const result = component.getTruncatedArrayString([], 10);
+    expect(result.display).toBe('');
+    expect(result.full).toBe('');
+  });
+
+  it('should delete device group when confirmed', () => {
+    coreUiFacade.confirm = jest.fn().mockReturnValue(of(true));
+    deviceFacade.deleteDeviceGroup.mockReturnValue(of([true, 'Deleted']));
+    const mockGroup = { group_name: 'G1', device_list: [] };
+
+    component.confirmDelete(mockGroup);
+
+    expect(coreUiFacade.confirm).toHaveBeenCalled();
+    expect(deviceFacade.deleteDeviceGroup).toHaveBeenCalled();
+    expect(coreUiFacade.notifySuccess).toHaveBeenCalledWith('The device group G1 deleted successfully!');
+  });
+
+  it('should use special message when deleting group with devices', () => {
+    coreUiFacade.confirm = jest.fn().mockReturnValue(of(false));
+    const mockGroup = { group_name: 'G1', device_list: [{ id: 1 }] };
+
+    component.confirmDelete(mockGroup);
+
+    expect(coreUiFacade.confirm).toHaveBeenCalledWith(expect.objectContaining({
+      message: 'Deleting this device group will also remove all devices associated with it. Are you sure you want to proceed?'
+    }));
+  });
+
+  it('should add device group via dialog', () => {
+    mockDialog.open.mockReturnValue({
+      afterClosed: () => of({ groupName: 'NewGroup' })
+    });
+    deviceFacade.addDeviceGroup.mockReturnValue(of({}));
+
+    component.addDeviceGroup();
+
+    expect(mockDialog.open).toHaveBeenCalled();
+    expect(deviceFacade.addDeviceGroup).toHaveBeenCalled();
+    expect(coreUiFacade.notifySuccess).toHaveBeenCalledWith('The device group NewGroup added successfully!');
+  });
+
+  it('should handle error when adding device group', () => {
+    mockDialog.open.mockReturnValue({
+      afterClosed: () => of({ groupName: 'NewGroup' })
+    });
+    deviceFacade.addDeviceGroup.mockReturnValue(throwError(() => ({ message: 'Error' })));
+
+    component.addDeviceGroup();
+
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Failed to create the device group.');
+  });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/devices/devices.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/devices/devices.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/devices/devices.spec.ts	(working copy)
@@ -1,7 +1,7 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
 import { provideRouter } from '@angular/router';
 import { provideNoopAnimations } from '@angular/platform-browser/animations';
-import { Devices } from './devices';
+import { Devices, AddDeviceDialog, DevicesBuildInfoDialog, DeviceLicenseDialog, DeviceUpdateLicenseDialog } from './devices';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
 import { DeviceFacade } from '../../../services/facades/device.facade';
 import { StorageService } from '../../../services/storage.service';
@@ -9,36 +9,201 @@
 import { MockDeviceFacade } from '../../../testing/mock-device.facade';
 import { GlobalSerialPipe } from '../../../pipes/global-serial-pipe';
 import { SearchFromResults } from '../../common/search-from-results/search-from-results';
+import { of, throwError } from 'rxjs';
+import { MatDialog, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { Router } from '@angular/router';
 
 describe('Devices', () => {
   let component: Devices;
   let fixture: ComponentFixture<Devices>;
+  let deviceFacade: MockDeviceFacade;
+  let coreUiFacade: MockCoreUiFacade;
+  let storageService: any;
+  let mockDialog: any;
+  let router: Router;
 
   beforeEach(async () => {
-    const mockStorageService = {
+    storageService = {
       getItem: jest.fn().mockReturnValue(null),
       setItem: jest.fn()
     };
 
+    mockDialog = {
+      open: jest.fn().mockReturnValue({
+        afterClosed: () => of(true)
+      })
+    };
+
     await TestBed.configureTestingModule({
       imports: [Devices, GlobalSerialPipe, SearchFromResults],
       providers: [
         provideRouter([]),
         provideNoopAnimations(),
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() },
-        { provide: StorageService, useValue: mockStorageService }
+        { provide: CoreUiFacade, useClass: MockCoreUiFacade },
+        { provide: DeviceFacade, useClass: MockDeviceFacade },
+        { provide: StorageService, useValue: storageService },
+        { provide: MatDialog, useValue: mockDialog }
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(Devices);
     component = fixture.componentInstance;
+
+    // Forcing the mock into the component instance manually to be absolutely sure
+    (component as any).dialog = mockDialog;
+
+    deviceFacade = TestBed.inject(DeviceFacade) as any;
+    coreUiFacade = TestBed.inject(CoreUiFacade) as any;
+    router = TestBed.inject(Router);
+
+    // Default mock data
+    deviceFacade.getDeviceGroups.mockReturnValue(of({ devices: [], groups: [] }));
+    deviceFacade.getDeviceTypeList.mockReturnValue(of({ DEVICE_STD_LIST: [] }));
+
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should fetch device types if not in storage', () => {
+    const typeList = { DEVICE_STD_LIST: ['APV', 'AG'] };
+    deviceFacade.getDeviceTypeList.mockReturnValue(of(typeList));
+
+    component.getDevicesType();
+
+    expect(deviceFacade.getDeviceTypeList).toHaveBeenCalled();
+    expect(storageService.setItem).toHaveBeenCalledWith('deviceType', JSON.stringify(typeList));
+  });
+
+  it('should fetch groups and devices on init', () => {
+    const mockData = {
+      groups: [{ name: 'G1' }],
+      devices: [{ name: 'D1' }]
+    };
+    deviceFacade.getDeviceGroups.mockReturnValue(of(mockData));
+
+    component.getDeviceGroups();
+
+    expect(component.groups.length).toBe(1);
+    expect(component.devices.length).toBe(1);
+    expect(component.dataSource.data.length).toBe(1);
+  });
+
+  it('should handle error when fetching device groups', () => {
+    const spy = jest.spyOn(console, 'log').mockImplementation();
+    deviceFacade.getDeviceGroups.mockReturnValue(throwError(() => ({ message: 'Error' })));
+
+    component.getDeviceGroups();
+
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Error');
+    spy.mockRestore();
+  });
+
+  it('should apply filter to data source', () => {
+    component.dataSource.data = [{ name: 'Test' } as any];
+    component.applyFilter('test');
+    expect(component.searchKey).toBe('test');
+    expect(component.dataSource.filter).toBe('test');
+  });
+
+  it('should open build info dialog', () => {
+    component.getDeviceBuildInfo();
+    expect(mockDialog.open).toHaveBeenCalled();
+  });
+
+  it('should delete device when confirmed', () => {
+    coreUiFacade.confirm = jest.fn().mockReturnValue(of(true));
+    deviceFacade.deleteDevice.mockReturnValue(of([true, 'Deleted']));
+    const mockDevice = { id: 1, name: 'D1', type: 'APV' };
+
+    component.confirmDelete(mockDevice);
+
+    expect(coreUiFacade.confirm).toHaveBeenCalled();
+    expect(deviceFacade.deleteDevice).toHaveBeenCalled();
+    expect(coreUiFacade.notifySuccess).toHaveBeenCalledWith('D1 deleted successfully!');
+  });
+
+  it('should save device config when confirmed', () => {
+    coreUiFacade.confirm = jest.fn().mockReturnValue(of(true));
+    deviceFacade.saveDeviceConfig.mockReturnValue(of([true, 'Saved']));
+    const mockDevice = { id: 1, name: 'D1', type: 'APV' };
+
+    component.confirmSave(mockDevice);
+
+    expect(coreUiFacade.confirm).toHaveBeenCalled();
+    expect(deviceFacade.saveDeviceConfig).toHaveBeenCalled();
+    expect(coreUiFacade.notifySuccess).toHaveBeenCalledWith('D1 saved successfully!');
+  });
+
+  it('should open add device dialog', () => {
+    component.deviceType = JSON.stringify({ DEVICE_STD_LIST: [] });
+    component.addDevice();
+    expect(mockDialog.open).toHaveBeenCalled();
+  });
+
+  it('should open license dialog', () => {
+    const mockDevice = { id: 1, name: 'D1' };
+    component.showLicenseDialog(mockDevice);
+    expect(mockDialog.open).toHaveBeenCalled();
+  });
+
+  it('should navigate to details', () => {
+    const spy = jest.spyOn(router, 'navigate');
+    const mockDevice = { name: 'D1' };
+    component.goToDetails(mockDevice);
+    expect(spy).toHaveBeenCalledWith(['/inventory/devices', 'D1'], expect.any(Object));
+  });
+
+  it('should open web console in new tab', () => {
+    const spy = jest.spyOn(window, 'open').mockImplementation();
+    const mockDevice = { id: 1, name: 'D1' };
+    component.openWebConsole(mockDevice);
+    expect(spy).toHaveBeenCalled();
+    spy.mockRestore();
+  });
 });
 
+describe('Devices Dialogs', () => {
+  let deviceFacade: MockDeviceFacade;
+  let coreUiFacade: MockCoreUiFacade;
+
+  beforeEach(() => {
+    deviceFacade = new MockDeviceFacade() as any;
+    coreUiFacade = new MockCoreUiFacade() as any;
+  });
+
+  it('should create AddDeviceDialog', async () => {
+    await TestBed.configureTestingModule({
+      imports: [AddDeviceDialog],
+      providers: [
+        provideNoopAnimations(),
+        { provide: MAT_DIALOG_DATA, useValue: { otherDeviceType: [], groups: [] } },
+        { provide: MatDialogRef, useValue: { close: jest.fn() } },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: CoreUiFacade, useValue: coreUiFacade }
+      ]
+    }).compileComponents();
+    const fixture = TestBed.createComponent(AddDeviceDialog);
+    expect(fixture.componentInstance).toBeTruthy();
+    fixture.detectChanges();
+  });
+
+  it('should create DevicesBuildInfoDialog', async () => {
+    deviceFacade.getDeviceBuildInfo.mockReturnValue(of([{}]));
+    await TestBed.configureTestingModule({
+      imports: [DevicesBuildInfoDialog],
+      providers: [
+        provideNoopAnimations(),
+        { provide: MatDialogRef, useValue: { close: jest.fn() } },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: CoreUiFacade, useValue: coreUiFacade }
+      ]
+    }).compileComponents();
+    const fixture = TestBed.createComponent(DevicesBuildInfoDialog);
+    expect(fixture.componentInstance).toBeTruthy();
+    fixture.detectChanges();
+  });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/resource-monitoring-devices/resource-monitoring-devices.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/resource-monitoring-devices/resource-monitoring-devices.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/resource-monitoring-devices/resource-monitoring-devices.spec.ts	(working copy)
@@ -1,49 +1,165 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { ResourceMonitoringDevices } from './resource-monitoring-devices';
+import { provideRouter } from '@angular/router';
+import { provideNoopAnimations } from '@angular/platform-browser/animations';
+import { ResourceMonitoringDevices, ShowDeviceInfoDialog } from './resource-monitoring-devices';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
 import { DeviceFacade } from '../../../services/facades/device.facade';
-import { UtilsService } from '../../../services/utils.service';
 import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
 import { MockDeviceFacade } from '../../../testing/mock-device.facade';
 import { GlobalSerialPipe } from '../../../pipes/global-serial-pipe';
+import { of, throwError } from 'rxjs';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { NgxEchartsModule } from 'ngx-echarts';
+import { UtilsService } from '../../../services/utils.service';
+import { MatTableDataSource } from '@angular/material/table';
 
 describe('ResourceMonitoringDevices', () => {
   let component: ResourceMonitoringDevices;
   let fixture: ComponentFixture<ResourceMonitoringDevices>;
+  let deviceFacade: MockDeviceFacade;
+  let coreUiFacade: MockCoreUiFacade;
+  let mockDialog: any;
 
   beforeEach(async () => {
-    const mockUtilsService = {
-      processConfigData: jest.fn().mockReturnValue([])
+    mockDialog = {
+      open: jest.fn().mockReturnValue({
+        afterClosed: () => of(true)
+      })
     };
 
     await TestBed.configureTestingModule({
       imports: [
         ResourceMonitoringDevices,
-        RouterTestingModule,
-        NoopAnimationsModule,
         GlobalSerialPipe,
-        NgxEchartsModule.forRoot({
-          echarts: () => import('echarts'),
-        })
+        NgxEchartsModule.forRoot({ echarts: () => import('echarts') })
       ],
       providers: [
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() },
-        { provide: UtilsService, useValue: mockUtilsService }
+        provideRouter([]),
+        provideNoopAnimations(),
+        { provide: CoreUiFacade, useClass: MockCoreUiFacade },
+        { provide: DeviceFacade, useClass: MockDeviceFacade },
+        { provide: MatDialog, useValue: mockDialog }
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(ResourceMonitoringDevices);
     component = fixture.componentInstance;
+    deviceFacade = TestBed.inject(DeviceFacade) as any;
+    coreUiFacade = TestBed.inject(CoreUiFacade) as any;
+
+    // Forcing the mock into the component instance
+    (component as any).dialog = mockDialog;
+
+    deviceFacade.getDeviceMonitoringMetrics.mockReturnValue(of({ device_stats: [] }));
+    deviceFacade.getDeviceGroups.mockReturnValue(of({ groups: [], devices: [] }));
+
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should fetch and merge data on init', () => {
+    const mockMetrics = [{ agent_host: '1.1.1.1', cpu: 10, mem: 20 }];
+    const mockGroups = {
+      groupNames: ['G1'],
+      devices: [{ ip: '1.1.1.1', name: 'D1' }, { ip: '2.2.2.2', name: 'D2' }]
+    };
+
+    deviceFacade.getDeviceMonitoringMetrics.mockReturnValue(of({ device_stats: mockMetrics }));
+    deviceFacade.getDeviceGroups.mockReturnValue(of(mockGroups));
+
+    component.getDeviceMonitoringMetrics();
+
+    expect(component.devices.length).toBe(1); // Only D1 matches 1.1.1.1
+    expect(component.devices[0].cpu).toBe(10);
+    expect(component.totalRecords).toBe(1);
+  });
+
+  it('should handle error in getDeviceMonitoringMetrics', () => {
+    deviceFacade.getDeviceMonitoringMetrics.mockReturnValue(throwError(() => ({ message: 'Metric Error' })));
+    const spy = jest.spyOn(component, 'getDeviceGroups');
+
+    component.getDeviceMonitoringMetrics();
+
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Metric Error');
+    expect(spy).toHaveBeenCalled();
+  });
+
+  it('should handle error in getDeviceGroups', () => {
+    const spyLog = jest.spyOn(console, 'log').mockImplementation();
+    deviceFacade.getDeviceGroups.mockReturnValue(throwError(() => ({ message: 'Group Error' })));
+
+    component.getDeviceGroups();
+
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Group Error');
+    spyLog.mockRestore();
+  });
+
+  it('should generate mini bar options', () => {
+    const options = component.getMiniBarOptions(50);
+    expect(options.series[1].data).toEqual([50]);
+    expect(options.series[1].itemStyle.color).toBe('#66bb6a'); // Green for 50
+  });
+
+  it('should navigate to details', () => {
+    const mockDevice = { name: 'D1', ip: '1.1.1.1' };
+    component.goToDetails(mockDevice);
+    expect(coreUiFacade.navigate).toHaveBeenCalledWith(
+      ['/monitoring/resources/devices', 'D1', '1.1.1.1'],
+      expect.any(Object)
+    );
+  });
+
+  it('should open device info dialog', () => {
+    const mockDevice = { name: 'D1', ip: '1.1.1.1' };
+    component.showDeviceInfo(mockDevice);
+    expect(mockDialog.open).toHaveBeenCalledWith(ShowDeviceInfoDialog, expect.any(Object));
+  });
+
+  it('should track by ip', () => {
+    const result = component.trackById(0, { ip: '1.1.1.1' });
+    expect(result).toBe('1.1.1.1');
+  });
 });
 
+describe('ShowDeviceInfoDialog', () => {
+  let utilsService: any;
+  let fixture: ComponentFixture<ShowDeviceInfoDialog>;
+  let component: ShowDeviceInfoDialog;
+
+  beforeEach(async () => {
+    utilsService = {
+      processConfigData: jest.fn().mockReturnValue(new MatTableDataSource([]))
+    };
+
+    await TestBed.configureTestingModule({
+      imports: [ShowDeviceInfoDialog],
+      providers: [
+        { provide: MAT_DIALOG_DATA, useValue: { device: {} } },
+        { provide: MatDialogRef, useValue: { close: jest.fn() } },
+        { provide: UtilsService, useValue: utilsService }
+      ]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(ShowDeviceInfoDialog);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should initialize data source on init', () => {
+    expect(utilsService.processConfigData).toHaveBeenCalled();
+  });
+
+  it('should close dialog on cancel', () => {
+    const dialogRef = TestBed.inject(MatDialogRef);
+    component.onCancel();
+    expect(dialogRef.close).toHaveBeenCalledWith(false);
+  });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vl-managed-devices/vl-managed-devices.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vl-managed-devices/vl-managed-devices.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/vl-managed-devices/vl-managed-devices.spec.ts	(working copy)
@@ -1,34 +1,187 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { VlManagedDevices } from './vl-managed-devices';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
+import { provideRouter } from '@angular/router';
+import { provideNoopAnimations } from '@angular/platform-browser/animations';
+import { VlManagedDevices, AddVLManagedDeviceDialog, ShowVLManagedDeviceDialog, EnableVLManagedDeviceDialog, EditVLManagedDeviceBandwidthDialog } from './vl-managed-devices';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
 import { DeviceFacade } from '../../../services/facades/device.facade';
 import { MockCoreUiFacade } from '../../../testing/mock-core-ui.facade';
 import { MockDeviceFacade } from '../../../testing/mock-device.facade';
-import { NewlineToBrPipe } from '../../../pipes/newline-to-br-pipe';
+import { of, throwError } from 'rxjs';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { ChangeDetectorRef } from '@angular/core';
 
 describe('VlManagedDevices', () => {
   let component: VlManagedDevices;
   let fixture: ComponentFixture<VlManagedDevices>;
+  let deviceFacade: MockDeviceFacade;
+  let coreUiFacade: MockCoreUiFacade;
+  let mockDialog: any;
 
   beforeEach(async () => {
+    mockDialog = {
+      open: jest.fn().mockReturnValue({
+        afterClosed: () => of(true)
+      })
+    };
+
     await TestBed.configureTestingModule({
-      imports: [VlManagedDevices, RouterTestingModule, NoopAnimationsModule, NewlineToBrPipe],
+      imports: [VlManagedDevices],
       providers: [
-        { provide: CoreUiFacade, useValue: new MockCoreUiFacade() },
-        { provide: DeviceFacade, useValue: new MockDeviceFacade() }
+        provideRouter([]),
+        provideNoopAnimations(),
+        { provide: CoreUiFacade, useClass: MockCoreUiFacade },
+        { provide: DeviceFacade, useClass: MockDeviceFacade },
+        { provide: MatDialog, useValue: mockDialog },
+        ChangeDetectorRef
       ]
     })
       .compileComponents();
 
     fixture = TestBed.createComponent(VlManagedDevices);
     component = fixture.componentInstance;
+    deviceFacade = TestBed.inject(DeviceFacade) as any;
+    coreUiFacade = TestBed.inject(CoreUiFacade) as any;
+
+    // Forcing the mock into the component instance
+    (component as any).dialog = mockDialog;
+
+    deviceFacade.getManagedDevices.mockReturnValue(of([]));
+    deviceFacade.getVolumeLicenses.mockReturnValue(of([]));
+    deviceFacade.getDeviceGroups.mockReturnValue(of({ devices: [], groups: [] }));
+
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should fetch all data on init', fakeAsync(() => {
+    component.ngOnInit();
+    tick();
+    expect(deviceFacade.getManagedDevices).toHaveBeenCalled();
+    expect(deviceFacade.getVolumeLicenses).toHaveBeenCalled();
+    expect(deviceFacade.getDeviceGroups).toHaveBeenCalled();
+  }));
+
+  it('should handle error in getManagedDevices', () => {
+    deviceFacade.getManagedDevices.mockReturnValue(throwError(() => ({ message: 'Error' })));
+    component.getManagedDevices();
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Error: Error');
+  });
+
+  it('should handle error in getVolumeLicenses', () => {
+    deviceFacade.getVolumeLicenses.mockReturnValue(throwError(() => ({ message: 'Error' })));
+    component.getVolumeLicenses();
+    expect(coreUiFacade.notifyError).toHaveBeenCalledWith('Error: Error');
+  });
+
+  it('should open add device dialog', () => {
+    component.addDevice();
+    expect(mockDialog.open).toHaveBeenCalledWith(AddVLManagedDeviceDialog, expect.any(Object));
+  });
+
+  it('should open show details dialog', () => {
+    component.goToDetails({ id: 1 });
+    expect(mockDialog.open).toHaveBeenCalledWith(ShowVLManagedDeviceDialog, expect.any(Object));
+  });
+
+  it('should open edit bandwidth dialog', () => {
+    component.editDeviceBandwidth({ id: 1 });
+    expect(mockDialog.open).toHaveBeenCalledWith(EditVLManagedDeviceBandwidthDialog, expect.any(Object));
+  });
+
+  it('should open enable license dialog', () => {
+    component.enableDeviceVolumeLicense({ id: 1 });
+    expect(mockDialog.open).toHaveBeenCalledWith(EnableVLManagedDeviceDialog, expect.any(Object));
+  });
+
+  it('should deactivate device license when confirmed', () => {
+    coreUiFacade.confirm = jest.fn().mockReturnValue(of(true));
+    deviceFacade.deactivateDeviceVolumeLicense.mockReturnValue(of([true, 'Success']));
+
+    component.disableDeviceVolumeLicense({ id: 'D1' });
+
+    expect(coreUiFacade.confirm).toHaveBeenCalled();
+    expect(deviceFacade.deactivateDeviceVolumeLicense).toHaveBeenCalled();
+    expect(coreUiFacade.notifySuccess).toHaveBeenCalledWith('The device license has been disabled successfully!');
+  });
+
+  it('should remove VL managed device when confirmed', () => {
+    coreUiFacade.confirm = jest.fn().mockReturnValue(of(true));
+    deviceFacade.removeVLManagedDevice.mockReturnValue(of([true, 'Deleted']));
+
+    component.removeDevice({ id: 'D1' });
+
+    expect(coreUiFacade.confirm).toHaveBeenCalled();
+    expect(deviceFacade.removeVLManagedDevice).toHaveBeenCalled();
+  });
+
+  it('should show cancelled message if not confirmed in removeDevice', () => {
+    coreUiFacade.confirm = jest.fn().mockReturnValue(of(false));
+    component.removeDevice({ id: 'D1' });
+    expect(coreUiFacade.notifySuccess).toHaveBeenCalledWith('Deletion cancelled.');
+  });
 });
 
+describe('VlManagedDevices Dialogs', () => {
+  let deviceFacade: MockDeviceFacade;
+  let coreUiFacade: MockCoreUiFacade;
+
+  beforeEach(() => {
+    deviceFacade = new MockDeviceFacade() as any;
+    coreUiFacade = new MockCoreUiFacade() as any;
+  });
+
+  it('should create AddVLManagedDeviceDialog', async () => {
+    await TestBed.configureTestingModule({
+      imports: [AddVLManagedDeviceDialog],
+      providers: [
+        provideNoopAnimations(),
+        { provide: MAT_DIALOG_DATA, useValue: { devices: [], licenses: [] } },
+        { provide: MatDialogRef, useValue: { close: jest.fn() } },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: CoreUiFacade, useValue: coreUiFacade }
+      ]
+    }).compileComponents();
+    const fixture = TestBed.createComponent(AddVLManagedDeviceDialog);
+    expect(fixture.componentInstance).toBeTruthy();
+    fixture.detectChanges();
+  });
+
+  it('should create EnableVLManagedDeviceDialog', async () => {
+    await TestBed.configureTestingModule({
+      imports: [EnableVLManagedDeviceDialog],
+      providers: [
+        provideNoopAnimations(),
+        { provide: MAT_DIALOG_DATA, useValue: { device: {}, licenses: [] } },
+        { provide: MatDialogRef, useValue: { close: jest.fn() } },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: CoreUiFacade, useValue: coreUiFacade }
+      ]
+    }).compileComponents();
+    const fixture = TestBed.createComponent(EnableVLManagedDeviceDialog);
+    expect(fixture.componentInstance).toBeTruthy();
+    fixture.detectChanges();
+  });
+
+  it('should create ShowVLManagedDeviceDialog', async () => {
+    deviceFacade.getVLManagedDeviceVersion.mockReturnValue(of({ version: '1.0' }));
+    await TestBed.configureTestingModule({
+      imports: [ShowVLManagedDeviceDialog],
+      providers: [
+        provideNoopAnimations(),
+        { provide: MAT_DIALOG_DATA, useValue: { device: { id: 1 } } },
+        { provide: MatDialogRef, useValue: { close: jest.fn() } },
+        { provide: DeviceFacade, useValue: deviceFacade },
+        { provide: CoreUiFacade, useValue: coreUiFacade }
+      ]
+    }).compileComponents();
+    const fixture = TestBed.createComponent(ShowVLManagedDeviceDialog);
+    expect(fixture.componentInstance).toBeTruthy();
+    fixture.detectChanges();
+    // Wait for setTimeout in ngOnInit
+    await fixture.whenStable();
+    expect(deviceFacade.getVLManagedDeviceVersion).toHaveBeenCalled();
+  });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/auth.service.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/auth.service.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/auth.service.spec.ts	(working copy)
@@ -6,7 +6,8 @@
 import { StorageService } from './storage.service';
 import { HttpService } from './http.service';
 import { NotificationService } from './notification.service';
-import { User, ErrorResponse } from '../models/user';
+import { User } from '../models/user';
+import { URLS } from '../constants/api_urls';
 
 describe('AuthService', () => {
   let service: AuthService;
@@ -18,7 +19,7 @@
 
   beforeEach(() => {
     routerMock = {
-      navigate: jest.fn().mockResolvedValue(true)
+      navigate: jest.fn().mockReturnValue({ finally: (cb: any) => cb() })
     };
     storageMock = {
       getItem: jest.fn(),
@@ -62,7 +63,6 @@
         token: 'fake-token'
       };
 
-      // Mocking successful response
       httpMock.post.mockReturnValue(of(mockUser));
 
       service.login('admin', 'password').subscribe(user => {
@@ -73,19 +73,40 @@
       });
     });
 
-    it('should throw error if API returns error code', (done) => {
-      const errorResponse = { error_code: 1, message: 'Invalid credentials' };
+    it('should assign default roles and permissions if missing', (done) => {
+      const response = { username: 'admin', token: 'fake' };
+      httpMock.post.mockReturnValue(of(response));
+      service.login('admin', 'password').subscribe(user => {
+        expect(user.roles).toBeDefined();
+        expect(user.permissions).toBeDefined();
+        done();
+      });
+    });
+
+    it('should throw error if API returns error code (msg)', (done) => {
+      const errorResponse = { error_code: 1, msg: 'Invalid' };
       httpMock.post.mockReturnValue(of(errorResponse));
 
       service.login('admin', 'password').subscribe({
         error: (error) => {
-          expect(error.message).toBe('Invalid credentials');
-          expect(service.currentUserValue).toBeNull();
+          expect(error.message).toBe('Invalid');
           done();
         }
       });
     });
 
+    it('should throw error if API returns code (message)', (done) => {
+      const errorResponse = { code: 1, message: 'Invalid' };
+      httpMock.post.mockReturnValue(of(errorResponse));
+
+      service.login('admin', 'password').subscribe({
+        error: (error) => {
+          expect(error.message).toBe('Invalid');
+          done();
+        }
+      });
+    });
+
     it('should handle HTTP error', (done) => {
       const httpError = { status: 500, message: 'Server error' };
       httpMock.post.mockReturnValue(throwError(() => httpError));
@@ -112,15 +133,30 @@
       service.logout(true, 'expired');
       expect(notificationMock.showError).toHaveBeenCalledWith('Your session has expired. Please log in again.');
     });
+
+    it('should show manual logout message', () => {
+      service.logout(true, 'manual');
+      expect(notificationMock.showError).toHaveBeenCalledWith('You have been logged out successfully.');
+    });
+
+    it('should not logout if logoutInProgress is true', () => {
+      (service as any).logoutInProgress = true;
+      service.logout();
+      expect(storageMock.removeItem).not.toHaveBeenCalled();
+    });
   });
 
   describe('hasRole', () => {
     it('should return true if user has the role', () => {
-      // Use cast to any to access private/protected members for test setup
       (service as any).currentUserSubject.next({ roles: ['admin'] } as User);
       expect(service.hasRole('admin')).toBe(true);
       expect(service.hasRole('user')).toBe(false);
     });
+
+    it('should return false if no user', () => {
+      (service as any).currentUserSubject.next(null);
+      expect(service.hasRole('admin')).toBe(false);
+    });
   });
 
   describe('hasPermission', () => {
@@ -129,6 +165,11 @@
       expect(service.hasPermission('Dashboard')).toBe(true);
       expect(service.hasPermission('Storage')).toBe(false);
     });
+
+    it('should return false if no user', () => {
+      (service as any).currentUserSubject.next(null);
+      expect(service.hasPermission('Dashboard')).toBe(false);
+    });
   });
 
   describe('init', () => {
@@ -148,5 +189,23 @@
 
       expect(service.currentUserValue).toBeNull();
     });
+
+    it('should handle no stored user', async () => {
+      storageMock.getItem.mockReturnValue(null);
+      await service.init();
+      expect(service.currentUserValue).toBeNull();
+    });
   });
+
+  describe('getUserAuthorization', () => {
+    it('should call API with username', () => {
+      httpMock.post.mockReturnValue(of({}));
+      service.getUserAuthorization('admin');
+      expect(httpMock.post).toHaveBeenCalledWith(
+        URLS.GET_USER_AUTHORIZATION_URL,
+        expect.any(FormData),
+        expect.any(Object)
+      );
+    });
+  });
 });
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/avx.service.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/avx.service.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/avx.service.spec.ts	(working copy)
@@ -1,8 +1,8 @@
 import { TestBed } from '@angular/core/testing';
-import { of, throwError } from 'rxjs';
 import { AvxService } from './avx.service';
 import { HttpService } from './http.service';
 import { URLS } from '../constants/api_urls';
+import { of } from 'rxjs';
 
 describe('AvxService', () => {
   let service: AvxService;
@@ -11,9 +11,7 @@
   beforeEach(() => {
     httpMock = {
       get: jest.fn(),
-      post: jest.fn(),
-      put: jest.fn(),
-      delete: jest.fn()
+      post: jest.fn()
     };
 
     TestBed.configureTestingModule({
@@ -22,6 +20,7 @@
         { provide: HttpService, useValue: httpMock }
       ]
     });
+
     service = TestBed.inject(AvxService);
   });
 
@@ -29,117 +28,102 @@
     expect(service).toBeTruthy();
   });
 
-  it('should fetch AVX devices', () => {
-    httpMock.get.mockReturnValue(of([]));
+  it('should test simple GET methods', () => {
+    httpMock.get.mockReturnValue(of({}));
     service.getAVXDevices();
     expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_AVX_DEVICES_URL);
   });
 
-  it('should fetch AVX device images with headers', () => {
+  it('should test parameterized GET methods', () => {
     httpMock.get.mockReturnValue(of({}));
-    const deviceId = 'device123';
+    const deviceId = 'dev1';
+    const vaName = 'va1';
+
     service.getAVXDeviceImages(deviceId);
-    expect(httpMock.get).toHaveBeenCalledWith(
-      URLS.GET_AVX_IMAGES_URL,
-      {},
-      expect.arrayContaining([
-        { name: 'Cm-Data', value: deviceId },
-        { name: 'Cm-Type', value: 'host' }
-      ])
-    );
-  });
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_AVX_IMAGES_URL, {}, expect.any(Array));
 
-  it('should fetch VA instances', () => {
-    httpMock.get.mockReturnValue(of({}));
-    const deviceId = 'device123';
     service.getAVXVAInstances(deviceId);
-    expect(httpMock.get).toHaveBeenCalledWith(
-      URLS.GET_AVX_VA_HOST_BY_DEVICE_ID_URL,
-      {},
-      expect.arrayContaining([
-        { name: 'Cm-Data', value: deviceId },
-        { name: 'Cm-Type', value: 'host' }
-      ])
-    );
-  });
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_AVX_VA_HOST_BY_DEVICE_ID_URL, {}, expect.any(Array));
 
-  it('should fetch VA instance config', () => {
-    httpMock.get.mockReturnValue(of({}));
-    const deviceId = 'device123';
-    const vaName = 'va1';
     service.getAVXVAInstanceConfig(deviceId, vaName);
+    expect(httpMock.get).toHaveBeenCalledWith(expect.stringContaining(vaName), {}, expect.any(Array));
 
-    // Check URL contains va_name
-    const expectedUrl = `${URLS.GET_AVX_INSTANCE_CONFIG_URL}?initial_filter=%7B%22va_name%22%3A%22${vaName}%22%7D`;
+    service.getAVXVAInstancePortMapping(deviceId, vaName);
+    expect(httpMock.get).toHaveBeenCalledWith(expect.stringContaining(vaName), {}, expect.any(Array));
 
-    expect(httpMock.get).toHaveBeenCalledWith(
-      expectedUrl,
-      {},
-      expect.arrayContaining([
-        { name: 'Cm-Data', value: deviceId }
-      ])
-    );
-  });
+    service.getAVXVAInstancePlatformResource(deviceId, vaName);
+    expect(httpMock.get).toHaveBeenCalledWith(expect.stringContaining(vaName), {}, expect.any(Array));
 
-  it('should create AVX VA backup', () => {
-    httpMock.post.mockReturnValue(of({ success: true }));
-    const deviceId = 'device123';
-    const payload = { test: 'data' };
+    service.getAVXVAInstanceShowVersion(deviceId, vaName);
+    expect(httpMock.get).toHaveBeenCalledWith(expect.stringContaining(vaName), {}, expect.any(Array));
 
-    service.createAVXVABackup(deviceId, payload);
+    service.getAVXVABackups(deviceId);
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_AVX_VA_BACKUP_BY_DEVICE_ID_URL, {}, expect.any(Array));
 
-    expect(httpMock.post).toHaveBeenCalledWith(
-      URLS.CREATE_AVX_BACKUP_URL,
-      payload,
-      expect.objectContaining({ csrf: true, isFormData: true }),
-      expect.arrayContaining([
-        { name: 'Cm-Data', value: deviceId }
-      ])
-    );
-  });
+    service.getAVXDeviceLogSettings(deviceId);
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_AVX_DEVICE_LOG_SETTINGS_URL, {}, expect.any(Array));
 
-  it('should update AVX instance power status', () => {
-    httpMock.post.mockReturnValue(of({}));
-    const deviceId = 'device123';
-    const instanceName = 'va1';
-    const status = 1;
+    service.getAVXDeviceLog(deviceId);
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_AVX_DEVICE_LOGS_URL, {}, expect.any(Array));
 
-    service.updateAVXInstancePowerStatus(deviceId, instanceName, status);
+    service.getAVXDeviceConfig(deviceId, 'cpu');
+    expect(httpMock.get).toHaveBeenCalledWith(expect.stringContaining('cpu'), {}, expect.any(Array));
 
-    const expectedUrl = `${URLS.POWER_ON_OFF_AVX_VA_INSTANCE_URL}/%22${instanceName}%22`;
-    const expectedPayload = `post_data=%7B%22status%22%3A${status}%7D`;
+    service.getAVXDeviceSystemInfo(deviceId);
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_AVX_DEVICE_SYSTEM_INFO_URL, {}, expect.any(Array));
 
-    expect(httpMock.post).toHaveBeenCalledWith(
-      expectedUrl,
-      expectedPayload,
-      expect.objectContaining({ isFormData: false }),
-      expect.arrayContaining([
-        { name: 'Cm-Data', value: deviceId }
-      ])
-    );
+    service.getAVXDeviceSystemInfoOverview(deviceId);
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_AVX_DEVICE_SYSTEM_INFO_OVERVIEW_URL, {}, expect.any(Array));
+
+    service.getAVXDeviceResourceOccupation(deviceId);
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_AVX_DEVICE_RESOURCE_OCCUPATION_URL, {}, expect.any(Array));
   });
 
-  it('should add AVX device', () => {
+  it('should test POST methods', () => {
     httpMock.post.mockReturnValue(of({}));
-    const payload = { ip: '1.2.3.4' };
+    const deviceId = 'dev1';
+    const rawPayload = { test: 1 };
 
-    service.addAVXDevice(payload);
+    service.getAVXVAInstanceBasicConfig(deviceId, rawPayload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.GET_AVX_VA_INSTANCE_BASIC_CONFIG_URL, rawPayload, expect.any(Object), expect.any(Array));
 
-    expect(httpMock.post).toHaveBeenCalledWith(
-      URLS.ADD_AVX_DEVICE_URL,
-      payload,
-      expect.objectContaining({ csrf: true, isFormData: true })
-    );
-  });
+    service.createAVXVABackup(deviceId, rawPayload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.CREATE_AVX_BACKUP_URL, rawPayload, expect.any(Object), expect.any(Array));
 
-  it('should handle errors gracefully', () => {
-    const error = { status: 404 };
-    httpMock.get.mockReturnValue(throwError(() => error));
+    service.deleteAVXVABackup(deviceId, rawPayload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DELETE_AVX_VA_BACKUP_URL, rawPayload, expect.any(Object), expect.any(Array));
 
-    service.getAVXDevices().subscribe({
-      error: (err) => {
-        expect(err).toEqual(error);
-      }
-    });
+    service.importAVXVABackup(deviceId, rawPayload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.IMPORT_AVX_VA_BACKUP_URL, rawPayload, expect.any(Object), expect.any(Array));
+
+    service.exportAVXVABackup(deviceId, rawPayload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.EXPORT_AVX_VA_BACKUP_URL, rawPayload, expect.any(Object), expect.any(Array));
+
+    service.restoreAVXVABackup(deviceId, rawPayload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.RESTORE_AVX_VA_BACKUP_URL, rawPayload, expect.any(Object), expect.any(Array));
+
+    service.createInstanceFromBackup(deviceId, { newName: 'new', pipeId: '1' });
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('1'), expect.stringContaining('new'), expect.any(Object), expect.any(Array));
+
+    service.updateAVXInstancePowerStatus(deviceId, 'inst1', 'on');
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('inst1'), expect.stringContaining('on'), expect.any(Object), expect.any(Array));
+
+    service.updateVAPVLicenseKey(deviceId, 'inst1', 'key1');
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.UPDATE_VAPV_LICENSE_KEY_URL, expect.stringContaining('key1'), expect.any(Object), expect.any(Array));
+
+    service.createVAPVLicenseRequestFile(deviceId, 'inst1', 'size1');
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.CREATE_VAPV_LICENSE_REQUEST_FILE_URL, expect.stringContaining('size1'), expect.any(Object), expect.any(Array));
+
+    service.deleteVAInstance(deviceId, 'inst1');
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DELETE_AVX_VA_INSTANCE_URL, expect.stringContaining('inst1'), expect.any(Object), expect.any(Array));
+
+    service.getAutoAssignResources(deviceId, rawPayload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.GET_AUTO_ASSIGN_RESOURCES_URL, rawPayload, expect.any(Object), expect.any(Array));
+
+    service.addAVXDevice(rawPayload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.ADD_AVX_DEVICE_URL, rawPayload, expect.any(Object));
+
+    service.deleteAVXDevice(rawPayload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DELETE_AVX_DEVICE_URL, rawPayload, expect.any(Object));
   });
 });
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/device.service.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/device.service.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/device.service.spec.ts	(working copy)
@@ -1,9 +1,9 @@
 import { TestBed } from '@angular/core/testing';
-import { of, throwError } from 'rxjs';
 import { DeviceService } from './device.service';
 import { HttpService } from './http.service';
 import { StorageService } from './storage.service';
 import { URLS } from '../constants/api_urls';
+import { of, throwError } from 'rxjs';
 
 describe('DeviceService', () => {
   let service: DeviceService;
@@ -28,6 +28,7 @@
         { provide: StorageService, useValue: storageMock }
       ]
     });
+
     service = TestBed.inject(DeviceService);
   });
 
@@ -44,10 +45,6 @@
             {
               group_name: 'Group 1',
               device_list: [{ id: 1, name: 'Device 1' }]
-            },
-            {
-              group_name: 'Group 2',
-              device_list: [{ id: 2, name: 'Device 2' }]
             }
           ]
         }
@@ -56,22 +53,14 @@
       httpMock.post.mockReturnValue(of(mockResult));
 
       service.getDeviceGroups().subscribe(data => {
-        expect(data.groupNames).toEqual(['Group 1', 'Group 2']);
-        expect(data.devices.length).toBe(2);
-        expect(data.groups.length).toBe(2);
-        expect(httpMock.post).toHaveBeenCalledWith(
-          URLS.GET_DEVICE_GROUPS_URL,
-          expect.any(FormData),
-          expect.objectContaining({ csrf: true, isFormData: true })
-        );
+        expect(data.groupNames).toEqual(['Group 1']);
+        expect(data.devices).toEqual([{ id: 1, name: 'Device 1' }]);
         done();
       });
     });
 
     it('should handle API errors', (done) => {
-      const error = { error: { message: 'Failed' } };
-      httpMock.post.mockReturnValue(throwError(() => error));
-
+      httpMock.post.mockReturnValue(throwError(() => ({ error: { message: 'Failed' } })));
       service.getDeviceGroups().subscribe({
         error: (err) => {
           expect(err.message).toBe('Failed');
@@ -81,98 +70,226 @@
     });
   });
 
-  describe('Device Operations', () => {
-    it('should add a device', () => {
-      httpMock.post.mockReturnValue(of({ success: true }));
-      const payload = { name: 'NewDevice' };
-      service.addDevice(payload);
-      expect(httpMock.post).toHaveBeenCalledWith(
-        URLS.ADD_DEVICE_URL,
-        payload,
-        expect.objectContaining({ csrf: true, isFormData: true })
-      );
-    });
+  it('should test simple GET methods', () => {
+    const getMethods = [
+      { name: 'getDeviceBuildInfo', url: URLS.CHECK_DEVICE_BUILD_INFO_URL },
+      { name: 'getAPVVSSSLInfo', url: URLS.CHECK_APV_VS_SSL_CERT_INFO_URL },
+      { name: 'getDeviceTypeList', url: URLS.GET_DEVICE_TYPE_URL },
+      { name: 'getDeviceConfigFiles', url: URLS.GET_DEVICE_CONFIG_FILES_URL },
+      { name: 'getScheduleDeviceBackup', url: URLS.GET_SCHEDULE_DEVICE_BACKUP_URL },
+      { name: 'getAMPDevicesList', url: URLS.GET_AMP_DEVICES_LIST_URL },
+      { name: 'getConfigTemplateKey', url: URLS.GET_CONFIG_TEMPLATE_KEY_URL },
+      { name: 'getTemplateKeyPairs', url: URLS.GET_CONFIG_TEMPLATE_KEY_PAIRS_URL },
+      { name: 'getAvailableBuilds', url: URLS.GET_AVAILABLE_DEVICE_UPGRADE_BUILDS_URL },
+      { name: 'getDeviceHAList', url: URLS.GET_DEVICE_HA_LIST_URL },
+      { name: 'getReports', url: URLS.GET_REPORTS_LOG_URL },
+      { name: 'getReportTasks', url: URLS.GET_REPORT_TASKS_URL }
+    ];
 
-    it('should delete a device', () => {
-      httpMock.post.mockReturnValue(of({ success: true }));
-      const payload = { id: '123' };
-      service.deleteDevice(payload);
-      expect(httpMock.post).toHaveBeenCalledWith(
-        URLS.DELETE_DEVICE_URL,
-        payload,
-        expect.objectContaining({ csrf: true, isFormData: true })
-      );
+    getMethods.forEach(m => {
+      httpMock.get.mockReturnValue(of({}));
+      (service as any)[m.name]();
+      expect(httpMock.get).toHaveBeenCalledWith(m.url);
     });
+  });
 
-    it('should update device', () => {
-      httpMock.post.mockReturnValue(of({ success: true }));
-      service.updateDevice('1', 'apv', 'dev1', 'data');
-      // Verify precise URL construction
-      const expectedUrl = `${URLS.UPDATE_DEVICE_CREDS_URL}/id/%221%22/type/%22apv%22/name/%22dev1%22?post_data=data`;
-      expect(httpMock.post).toHaveBeenCalledWith(
-        expectedUrl,
-        {},
-        expect.objectContaining({ csrf: true })
-      );
-    });
+  it('should test parameterized GET methods', () => {
+    httpMock.get.mockReturnValue(of({}));
+
+    service.getDeviceConfiguration('dev1');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_DEVICE_CONFIG_URL}/dev1`);
+
+    service.getAPVSLBVirtualServices('id1');
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_APV_VIRTUAL_SERVICES_URL, {}, expect.any(Array));
+
+    service.getAPVSLBRealServices('id1');
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_APV_REAL_SERVICES_URL, {}, expect.any(Array));
+
+    service.getDeviceConfigByConfigFileName('file.conf', 'system');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_DEVICE_CONFIG_BY_CONFIG_FILE_NAME_URL}/system/file.conf`);
+
+    service.getSSLCertificates('dev1', 'host1');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_SSL_VHOST_CERTIFICATES_URL}/host1/certs`, {}, expect.any(Array));
+
+    service.getSSLBackupCertificates('dev1', 'host1');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_SSL_VHOST_BACKUP_CERTIFICATES_URL}/host1/backup_certs`, {}, expect.any(Array));
+
+    service.getSSLInterCACertificates('dev1', 'host1');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_SSL_VHOST_INTERMEDIATE_CA_CERTIFICATES_URL}/host1/interca_certs`, {}, expect.any(Array));
+
+    service.getSSLVHostSNI('dev1', 'host1');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_SSL_VHOST_SNI_URL}/host1/ssl_sni`, {}, expect.any(Array));
+
+    service.getSLBServices('id1');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_SLB_SERVICES_URL}`, {}, expect.any(Array));
+
+    service.getClientVerificationSettings('id1', 'host1');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_CLIENT_VERIFICATION_SETTINGS_URL}/host1`, expect.any(Object), expect.any(Array));
+
+    service.downloadReport('file.pdf');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.DOWNLOAD_REPORT_URL}/file.pdf`, expect.any(Object));
   });
 
-  describe('Config Files', () => {
-    it('should get device config by filename', () => {
-      httpMock.get.mockReturnValue(of({}));
-      service.getDeviceConfigByConfigFileName('test.conf', 'device');
-      const expectedUrl = `${URLS.GET_DEVICE_CONFIG_BY_CONFIG_FILE_NAME_URL}/system/test.conf`;
-      expect(httpMock.get).toHaveBeenCalledWith(expectedUrl);
-    });
+  it('should test simple POST methods', () => {
+    const postMethods = [
+      { name: 'addDeviceGroup', url: URLS.ADD_DEVICE_GROUP_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteDeviceGroup', url: URLS.DELETE_DEVICE_GROUPS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addDevice', url: URLS.ADD_DEVICE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteDevice', url: URLS.DELETE_DEVICE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateDeviceMonitoringState', url: URLS.UPDATE_DEVICE_MONITORING_STATE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'saveDevice', url: URLS.SAVE_DEVICE_CONFIG_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateDeviceLicense', url: URLS.UPDATE_DEVICE_LICENSE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'cloneDeviceBackupConfig', url: URLS.CLONE_DEVICE_BACKUP_CONFIG_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteDeviceConfigFile', url: URLS.DELETE_DEVICE_CONFIG_FILE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'performDeviceConfigOperation', url: URLS.PERFORM_DEVICE_CONFIG_OPS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'clearScheduleDeviceBackup', url: URLS.CLEAR_SCHEDULE_DEVICE_BACKUP_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'getAGConfigAssociatedVsites', url: URLS.GET_AG_CONFIG_ASSOCIATED_VSITE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addCustomConfigFile', url: URLS.ADD_CUSTOM_CONFIG_FILE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addConfigTemplateKey', url: URLS.ADD_CONFIG_TEMPLATE_KEY_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateConfigTemplateKey', url: URLS.UPDATE_CONFIG_TEMPLATE_KEY_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteConfigTemplateKey', url: URLS.DELETE_CONFIG_TEMPLATE_KEY_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateDeviceConfigTemplateDefaultValue', url: URLS.UPDATE_DEVICE_CONFIG_TEMPLATE_DEFAULT_VALUE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addDeviceBuildUpgradeConfig', url: URLS.ADD_DEVICE_BUILD_UPGRADE_CONFIG_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteDeviceBuildUpgradeConfig', url: URLS.DELETE_DEVICE_BUILD_UPGRADE_CONFIG_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'switchHAGroup', url: URLS.SWITCH_DEVICE_HA_GROUP_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateRealService', url: URLS.UPDATE_SLB_REAL_SERVICE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'getDeviceMonitoringMetrics', url: URLS.GET_DEVICE_MONITORING_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getAPVVSMonitoringMetrics', url: URLS.GET_APV_VS_MONITORING_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getAPVRSMonitoringMetrics', url: URLS.GET_APV_RS_MONITORING_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getSSLVPNMonitoringMetrics', url: URLS.GET_SSL_VPN_MONITORING_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getLLBMonitoringMetrics', url: URLS.GET_LLB_MONITORING_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getDevicesNetworkMetrics', url: URLS.GET_DEVICES_NETWORK_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getDeviceServices', url: URLS.GET_DEVICE_SERVICES_URL, options: { csrf: true, isFormData: false, csrfInFormData: false } },
+      { name: 'createReportTask', url: URLS.CREATE_REPORT_TASK_URL, options: { csrf: true, isFormData: false, csrfInFormData: false } }
+    ];
 
-    it('should update device config by filename', () => {
+    postMethods.forEach(m => {
       httpMock.post.mockReturnValue(of({}));
-      service.updateDeviceConfigByConfigFileName('test.conf', 'device', { content: 'test' });
-      const expectedUrl = `${URLS.UPDATE_DEVICE_CONFIG_BY_CONFIG_FILE_NAME_URL}/system/test.conf`;
-      expect(httpMock.post).toHaveBeenCalledWith(
-        expectedUrl,
-        { content: 'test' },
-        expect.objectContaining({ csrf: true })
-      );
+      const payload = { test: 1 };
+      (service as any)[m.name](payload);
+      expect(httpMock.post).toHaveBeenCalled();
+      const lastCall = httpMock.post.mock.calls[httpMock.post.mock.calls.length - 1];
+      expect(lastCall[0]).toBe(m.url);
+      expect(lastCall[1]).toEqual(payload);
+      if (m.options) {
+        expect(lastCall[2]).toEqual(m.options);
+      }
     });
   });
 
+  it('should test parameterized POST/PUT methods', () => {
+    httpMock.post.mockReturnValue(of({}));
+    httpMock.put.mockReturnValue(of({}));
+
+    service.updateDevice('id1', 'type1', 'name1', 'data1');
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('id1'), {}, expect.any(Object));
+
+    service.getAGVSiteConfig('conf1', 'global');
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('conf1'), null, expect.any(Object));
+
+    service.updateDeviceConfigByConfigFileName('file.conf', 'system', {});
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('file.conf'), {}, expect.any(Object));
+
+    service.updateAGVSiteConfig('conf1', 'vsite1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('vsite1'), {}, expect.any(Object));
+
+    service.updateDeviceBuildUpgradeConfig({ build_version: '1', id: '2', app_name: 'app' }, {});
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('build_version'), {}, expect.any(Object));
+
+    service.uploadDeviceBuild({});
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('action=Upload'), {}, expect.any(Object));
+
+    service.performDeviceBuildUpdate({});
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('action=System_update'), {}, expect.any(Object));
+
+    service.executeCLICommand('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DEVICE_CLI_EXTEND_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.updateSSLVHost('dev1', 'host1', {});
+    expect(httpMock.put).toHaveBeenCalledWith(expect.stringContaining('host1'), {}, expect.any(Object), expect.any(Array));
+
+    service.backupSSLCertificate('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.BACKUP_SSL_VHOST_CERTIFICATE_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.restoreSSLCertificate('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.RESTORE_SSL_VHOST_CERTIFICATE_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.deleteSSLCertificate('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DELETE_SSL_VHOST_BACKUP_CERTIFICATE_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.importSSLCertificate('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.IMPORT_SSL_CERTIFICATE_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.activateSSLCertificate('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.ACTIVATE_SSL_CERTIFICATE_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.deactivateSSLCertificate('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DEACTIVATE_SSL_CERTIFICATE_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.deleteVHostSSLCertificate('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DELETE_SSL_CERTIFICATE_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.downloadSSLCertInfo('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DOWNLOAD_SSL_VHOST_BACKUP_CERTIFICATE_INFO_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.getDeviceHAConfigByName('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.GET_DEVICE_HA_CONFIG_BY_NAME_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.getDeviceHAUnitsByName('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.GET_DEVICE_HA_UNITS_BY_NAME_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.getDeviceHAGroupsByName('dev1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.GET_DEVICE_HA_GROUPS_BY_NAME_URL, {}, expect.any(Object), expect.any(Array));
+
+    service.updateClientVerificationSettings('dev1', 'host1', {});
+    expect(httpMock.put).toHaveBeenCalledWith(expect.stringContaining('host1'), {}, expect.any(Object), expect.any(Array));
+
+    service.executeAPVCLICommand('dev1', 'cmd');
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.APV_CLI_EXTEND_URL, { cmd: 'cmd' }, expect.any(Object), expect.any(Array));
+
+    service.updateReportTask('id1', {});
+    expect(httpMock.put).toHaveBeenCalledWith(expect.stringContaining('id1'), {}, expect.any(Object));
+
+    service.createReport('id1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('id1'), {}, expect.any(Object));
+  });
+
+  it('should test DELETE methods', () => {
+    httpMock.delete.mockReturnValue(of({}));
+    service.deleteReportTask('id1');
+    expect(httpMock.delete).toHaveBeenCalledWith(expect.stringContaining('id1'));
+
+    service.deleteReport('id1');
+    expect(httpMock.delete).toHaveBeenCalledWith(expect.stringContaining('id1'));
+  });
+
+  it('should test downloadSSLCertificate with BLOB', () => {
+    httpMock.get.mockReturnValue(of(new Blob()));
+    service.downloadSSLCertificate('dev1', 'host1', 'file1');
+    expect(httpMock.get).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ responseType: 'blob' }), expect.any(Array));
+  });
+
   describe('mapDeviceDetails', () => {
-    it('should map device details to metrics', () => {
-      const devices = [
-        { ip: '192.168.1.1', name: 'Device Alpha', type: 'APV' }
-      ];
+    it('should filter out metrics for unknown devices', () => {
+      const devices = [{ ip: '1.1.1.1', name: 'D1', type: 'APV' }];
       const metrics = [
-        { agent_host: '192.168.1.1', value: 100 }
+        { agent_host: '1.1.1.1', value: 10 },
+        { agent_host: '2.2.2.2', value: 20 }
       ];
-
       const result = service.mapDeviceDetails(devices, metrics);
-      expect(result[0].device_name).toBe('Device Alpha');
-      expect(result[0].device_type).toBe('APV');
+      expect(result).toHaveLength(1);
+      expect(result[0].device_name).toBe('D1');
     });
 
-    it('should map link_name to service_name for LLB if service_name is missing', () => {
-      const devices = [
-        { ip: '192.168.1.1', name: 'Device Alpha', type: 'APV' }
-      ];
-      const metrics = [
-        { agent_host: '192.168.1.1', link_name: 'Link1' }
-      ];
+    it('should map link_name to service_name for LLB', () => {
+      const devices = [{ ip: '1.1.1.1', name: 'D1', type: 'APV' }];
+      const metrics = [{ agent_host: '1.1.1.1', link_name: 'L1' }];
       const result = service.mapDeviceDetails(devices, metrics);
-      expect(result[0].service_name).toBe('Link1');
+      expect(result[0].service_name).toBe('L1');
     });
-  });
 
-  describe('SLB Services', () => {
-    it('should get APV SLB Virtual Services with headers', () => {
-      httpMock.get.mockReturnValue(of([]));
-      const deviceId = 'dev1';
-      service.getAPVSLBVirtualServices(deviceId);
-      expect(httpMock.get).toHaveBeenCalledWith(
-        URLS.GET_APV_VIRTUAL_SERVICES_URL,
-        {},
-        expect.arrayContaining([{ name: 'Cm-Data', value: deviceId }])
-      );
+    it('should return empty for null metrics', () => {
+      expect(service.mapDeviceDetails([], null as any)).toEqual([]);
     });
   });
 });
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 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/device.service.ts	(working copy)
@@ -606,7 +606,8 @@
   }
 
   mapDeviceDetails(devices: any[], metrics: any[]): any[] {
-    metrics.forEach((_metric: any) => {
+    if (!metrics) return [];
+    return metrics.filter((_metric: any) => {
       const device = devices.find(_device => _metric?.agent_host === _device?.ip);
       if (device) {
         _metric.device_name = device.name || 'N/A';
@@ -615,9 +616,10 @@
         if (!_metric.service_name && _metric?.link_name) {
           _metric.service_name = _metric.link_name;
         }
+        return true;
       }
+      return false;
     });
-    return metrics;
   }
 
   getClientVerificationSettings(deviceId: any, hostName: any) {
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/core-ui.facade.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/core-ui.facade.spec.ts	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/core-ui.facade.spec.ts	(working copy)
@@ -0,0 +1,216 @@
+import { TestBed } from '@angular/core/testing';
+import { CoreUiFacade } from './core-ui.facade';
+import { AlertMsgService } from '../alert-msg.service';
+import { NotificationService } from '../notification.service';
+import { LoadingService } from '../loading.service';
+import { LayoutService } from '../layout.service';
+import { ConfirmationService } from '../confirmation.service';
+import { AuthService } from '../auth.service';
+import { Router, ActivatedRoute } from '@angular/router';
+import { StorageService } from '../storage.service';
+import { of } from 'rxjs';
+
+describe('CoreUiFacade', () => {
+    let facade: CoreUiFacade;
+    let alertMsgServiceMock: any;
+    let notificationServiceMock: any;
+    let loadingServiceMock: any;
+    let layoutServiceMock: any;
+    let confirmationServiceMock: any;
+    let authServiceMock: any;
+    let routerMock: any;
+    let routeMock: any;
+    let storageServiceMock: any;
+
+    beforeEach(() => {
+        alertMsgServiceMock = { openAlertMsgDialog: jest.fn() };
+        notificationServiceMock = { showSuccess: jest.fn(), showError: jest.fn() };
+        loadingServiceMock = { startRequest: jest.fn(), endRequest: jest.fn() };
+        layoutServiceMock = {
+            isSidebarCollapsed$: of(false),
+            isDarkMode$: of(false),
+            setSidebarCollapsed: jest.fn(),
+            toggleTheme: jest.fn()
+        };
+        confirmationServiceMock = { openConfirmDialog: jest.fn() };
+        authServiceMock = {
+            currentUser: of({}),
+            currentUserValue: {},
+            login: jest.fn(),
+            getUserAuthorization: jest.fn(),
+            logout: jest.fn(),
+            hasRole: jest.fn(),
+            hasPermission: jest.fn()
+        };
+        routerMock = { navigate: jest.fn(), navigateByUrl: jest.fn(), events: of({}), url: '/test' };
+        routeMock = {
+            queryParams: of({}),
+            snapshot: { queryParams: { returnUrl: '/dashboard' } }
+        };
+        storageServiceMock = { setItem: jest.fn(), getItem: jest.fn() };
+
+        TestBed.configureTestingModule({
+            providers: [
+                CoreUiFacade,
+                { provide: AlertMsgService, useValue: alertMsgServiceMock },
+                { provide: NotificationService, useValue: notificationServiceMock },
+                { provide: LoadingService, useValue: loadingServiceMock },
+                { provide: LayoutService, useValue: layoutServiceMock },
+                { provide: ConfirmationService, useValue: confirmationServiceMock },
+                { provide: AuthService, useValue: authServiceMock },
+                { provide: Router, useValue: routerMock },
+                { provide: ActivatedRoute, useValue: routeMock },
+                { provide: StorageService, useValue: storageServiceMock }
+            ]
+        });
+
+        facade = TestBed.inject(CoreUiFacade);
+    });
+
+    it('should be created', () => {
+        expect(facade).toBeTruthy();
+    });
+
+    it('should delegate auth methods', () => {
+        facade.login('user', 'pass');
+        expect(authServiceMock.login).toHaveBeenCalledWith('user', 'pass');
+
+        facade.logout();
+        expect(authServiceMock.logout).toHaveBeenCalled();
+
+        facade.getUserAuthorization('user');
+        expect(authServiceMock.getUserAuthorization).toHaveBeenCalledWith('user');
+
+        facade.hasRole('admin');
+        expect(authServiceMock.hasRole).toHaveBeenCalledWith('admin');
+
+        facade.hasPermission('read');
+        expect(authServiceMock.hasPermission).toHaveBeenCalledWith('read');
+    });
+
+    it('should save user session', () => {
+        facade.saveUserSession({ role_id: 1, root: true, user_type: 'admin' });
+        expect(storageServiceMock.setItem).toHaveBeenCalledWith('roleId', 1);
+        expect(storageServiceMock.setItem).toHaveBeenCalledWith('isRoot', true);
+        expect(storageServiceMock.setItem).toHaveBeenCalledWith('userType', 'admin');
+
+        storageServiceMock.setItem.mockClear();
+        facade.saveUserSession(null);
+        expect(storageServiceMock.setItem).not.toHaveBeenCalled();
+    });
+
+    it('should delegate router methods', () => {
+        facade.navigate(['/home']);
+        expect(routerMock.navigate).toHaveBeenCalledWith(['/home'], undefined);
+
+        facade.navigateByUrl('/home');
+        expect(routerMock.navigateByUrl).toHaveBeenCalledWith('/home');
+
+        facade.navigateHome();
+        expect(routerMock.navigate).toHaveBeenCalledWith(['/dashboard']);
+
+        facade.navigateToUrl('/test');
+        expect(routerMock.navigateByUrl).toHaveBeenCalledWith('/test');
+
+        expect(facade.routerUrl).toBe('/test');
+        expect(facade.activeRoute).toBe(routeMock);
+    });
+
+    it('should delegate layout methods', () => {
+        facade.setSidebarCollapsed(true);
+        expect(layoutServiceMock.setSidebarCollapsed).toHaveBeenCalledWith(true);
+
+        facade.toggleTheme();
+        expect(layoutServiceMock.toggleTheme).toHaveBeenCalled();
+    });
+
+    it('should delegate notification and alert methods', () => {
+        facade.error('error msg');
+        expect(alertMsgServiceMock.openAlertMsgDialog).toHaveBeenCalledWith({ message: 'error msg' });
+
+        facade.notifySuccess('success');
+        expect(notificationServiceMock.showSuccess).toHaveBeenCalledWith('success');
+
+        facade.notifyError('err');
+        expect(notificationServiceMock.showError).toHaveBeenCalledWith('err');
+    });
+
+    it('should delegate loading methods', () => {
+        facade.showLoading();
+        expect(loadingServiceMock.startRequest).toHaveBeenCalled();
+
+        facade.hideLoading();
+        expect(loadingServiceMock.endRequest).toHaveBeenCalled();
+    });
+
+    it('should delegate confirmation method', () => {
+        confirmationServiceMock.openConfirmDialog.mockReturnValue(of(true));
+        facade.confirm({ title: 'T', message: 'M' }).subscribe(res => {
+            expect(res).toBe(true);
+        });
+        expect(confirmationServiceMock.openConfirmDialog).toHaveBeenCalledWith({
+            title: 'T',
+            message: 'M',
+            confirmButtonText: 'Yes',
+            cancelButtonText: 'No',
+            confirmButtonColor: 'warn',
+            cancelButtonColor: 'primary'
+        });
+    });
+
+    it('should use custom confirmation options', () => {
+        confirmationServiceMock.openConfirmDialog.mockReturnValue(of(true));
+        facade.confirm({
+            title: 'T',
+            message: 'M',
+            confirmText: 'OK',
+            cancelText: 'Back',
+            confirmColor: 'primary',
+            cancelColor: 'accent'
+        });
+        expect(confirmationServiceMock.openConfirmDialog).toHaveBeenCalledWith({
+            title: 'T',
+            message: 'M',
+            confirmButtonText: 'OK',
+            cancelButtonText: 'Back',
+            confirmButtonColor: 'primary',
+            cancelButtonColor: 'accent'
+        });
+    });
+
+    it('should get return url', () => {
+        expect(facade.getReturnUrl()).toBe('/dashboard');
+    });
+
+    it('should get storage item', () => {
+        storageServiceMock.getItem.mockReturnValue('val');
+        expect(facade.getStorageItem('key')).toBe('val');
+        expect(storageServiceMock.getItem).toHaveBeenCalledWith('key');
+    });
+
+    it('should have getters for services and properties', (done) => {
+        expect(facade.confirmationService).toBe(confirmationServiceMock);
+        expect(facade.currentUserValue).toBe(authServiceMock.currentUserValue);
+
+        facade.currentUser.subscribe(val => {
+            expect(val).toBe(authServiceMock.currentUserValue);
+        });
+
+        facade.isSidebarCollapsed$.subscribe(val => {
+            expect(val).toBe(false);
+        });
+
+        facade.isDarkMode$.subscribe(val => {
+            expect(val).toBe(false);
+        });
+
+        facade.routerEvents.subscribe(val => {
+            expect(val).toEqual({});
+        });
+
+        facade.queryParams.subscribe(val => {
+            expect(val).toEqual({});
+            done();
+        });
+    });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/device.facade.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/device.facade.spec.ts	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/device.facade.spec.ts	(working copy)
@@ -0,0 +1,363 @@
+import { TestBed } from '@angular/core/testing';
+import { DeviceFacade } from './device.facade';
+import { DeviceService } from '../device.service';
+import { AvxService } from '../avx.service';
+import { VpnService } from '../vpn.service';
+import { VolumeLicenseService } from '../volume-license.service';
+import { of } from 'rxjs';
+
+describe('DeviceFacade', () => {
+    let facade: DeviceFacade;
+    let deviceServiceMock: any;
+    let avxServiceMock: any;
+    let vpnServiceMock: any;
+    let vlServiceMock: any;
+
+    beforeEach(() => {
+        deviceServiceMock = {
+            getDeviceMonitoringMetrics: jest.fn(),
+            getDevicesNetworkMetrics: jest.fn(),
+            mapDeviceDetails: jest.fn(),
+            getDeviceHAList: jest.fn(),
+            executeCLICommand: jest.fn(),
+            updateSSLVHost: jest.fn(),
+            getAPVVSSSLInfo: jest.fn(),
+            getAPVSLBVirtualServices: jest.fn(),
+            getDeviceGroups: jest.fn(),
+            addDeviceGroup: jest.fn(),
+            deleteDeviceGroup: jest.fn(),
+            addDevice: jest.fn(),
+            deleteDevice: jest.fn(),
+            updateDevice: jest.fn(),
+            getDeviceConfiguration: jest.fn(),
+            saveDevice: jest.fn(),
+            getDeviceConfigFiles: jest.fn(),
+            getAvailableBuilds: jest.fn(),
+            addDeviceBuildUpgradeConfig: jest.fn(),
+            updateDeviceBuildUpgradeConfig: jest.fn(),
+            deleteDeviceBuildUpgradeConfig: jest.fn(),
+            uploadDeviceBuild: jest.fn(),
+            performDeviceBuildUpdate: jest.fn(),
+            deleteDeviceConfigFile: jest.fn(),
+            getDeviceTypeList: jest.fn(),
+            getDeviceBuildInfo: jest.fn(),
+            updateDeviceLicense: jest.fn(),
+            cloneDeviceBackupConfig: jest.fn(),
+            performDeviceConfigOperation: jest.fn(),
+            getAMPDevicesList: jest.fn(),
+            getDeviceConfigByConfigFileName: jest.fn(),
+            getAGConfigAssociatedVsites: jest.fn(),
+            getAGVSiteConfig: jest.fn(),
+            updateDeviceConfigByConfigFileName: jest.fn(),
+            updateAGVSiteConfig: jest.fn(),
+            addCustomConfigFile: jest.fn(),
+            addConfigTemplateKey: jest.fn(),
+            updateConfigTemplateKey: jest.fn(),
+            deleteConfigTemplateKey: jest.fn(),
+            updateDeviceConfigTemplateDefaultValue: jest.fn(),
+            getConfigTemplateKey: jest.fn(),
+            getTemplateKeyPairs: jest.fn(),
+            updateDeviceMonitoringState: jest.fn(),
+            getScheduleDeviceBackup: jest.fn(),
+            clearScheduleDeviceBackup: jest.fn(),
+            getDeviceHAConfigByName: jest.fn(),
+            getDeviceHAUnitsByName: jest.fn(),
+            getDeviceHAGroupsByName: jest.fn(),
+            switchHAGroup: jest.fn(),
+            getSSLBackupCertificates: jest.fn(),
+            getSSLVHostSNI: jest.fn(),
+            downloadSSLCertInfo: jest.fn(),
+            downloadSSLCertificate: jest.fn(),
+            deleteSSLCertificate: jest.fn(),
+            backupSSLCertificate: jest.fn(),
+            restoreSSLCertificate: jest.fn(),
+            getSSLCertificates: jest.fn(),
+            activateSSLCertificate: jest.fn(),
+            deactivateSSLCertificate: jest.fn(),
+            deleteVHostSSLCertificate: jest.fn(),
+            importSSLCertificate: jest.fn(),
+            getClientVerificationSettings: jest.fn(),
+            updateClientVerificationSettings: jest.fn(),
+            executeAPVCLICommand: jest.fn(),
+            getSSLInterCACertificates: jest.fn(),
+            deleteAPVSSLInterCACertificate: jest.fn(),
+            importInterCACertificate: jest.fn(),
+            getReports: jest.fn(),
+            getReportTasks: jest.fn(),
+            getDeviceServices: jest.fn(),
+            createReportTask: jest.fn(),
+            updateReportTask: jest.fn(),
+            deleteReportTask: jest.fn(),
+            createReport: jest.fn(),
+            downloadReport: jest.fn(),
+            deleteReport: jest.fn(),
+            getAPVVSMonitoringMetrics: jest.fn(),
+            getAPVRSMonitoringMetrics: jest.fn(),
+            getSSLVPNMonitoringMetrics: jest.fn(),
+            getLLBMonitoringMetrics: jest.fn(),
+            getAPVSLBRealServices: jest.fn(),
+            getSLBServices: jest.fn(),
+            updateRealService: jest.fn()
+        };
+        avxServiceMock = {
+            getAVXDevices: jest.fn(),
+            getAVXDeviceImages: jest.fn(),
+            getAVXVAInstances: jest.fn(),
+            getAVXVAInstanceConfig: jest.fn(),
+            getAVXVAInstancePortMapping: jest.fn(),
+            getAVXVAInstancePlatformResource: jest.fn(),
+            getAVXVAInstanceShowVersion: jest.fn(),
+            getAVXVAInstanceBasicConfig: jest.fn(),
+            getAVXVABackups: jest.fn(),
+            createAVXVABackup: jest.fn(),
+            deleteAVXVABackup: jest.fn(),
+            importAVXVABackup: jest.fn(),
+            exportAVXVABackup: jest.fn(),
+            restoreAVXVABackup: jest.fn(),
+            createInstanceFromBackup: jest.fn(),
+            updateAVXInstancePowerStatus: jest.fn(),
+            updateVAPVLicenseKey: jest.fn(),
+            createVAPVLicenseRequestFile: jest.fn(),
+            deleteVAInstance: jest.fn(),
+            getAutoAssignResources: jest.fn(),
+            getAVXDeviceLogSettings: jest.fn(),
+            getAVXDeviceLog: jest.fn(),
+            getAVXDeviceConfig: jest.fn(),
+            getAVXDeviceSystemInfo: jest.fn(),
+            getAVXDeviceSystemInfoOverview: jest.fn(),
+            getAVXDeviceResourceOccupation: jest.fn(),
+            addAVXDevice: jest.fn(),
+            deleteAVXDevice: jest.fn()
+        };
+        vpnServiceMock = {
+            getVPNServices: jest.fn(),
+            executeAGCLICommand: jest.fn(),
+            getHardwareIds: jest.fn(),
+            batchAGCLICommand: jest.fn()
+        };
+        vlServiceMock = {
+            getVolumeLicenses: jest.fn(),
+            addVolumeLicense: jest.fn(),
+            deleteVolumeLicense: jest.fn(),
+            getManagedDevices: jest.fn(),
+            activateDeviceVolumeLicense: jest.fn(),
+            deactivateDeviceVolumeLicense: jest.fn(),
+            removeVLManagedDevice: jest.fn(),
+            importVLManagedDevice: jest.fn(),
+            addVLManagedDevice: jest.fn(),
+            updateVLManagedDeviceBandwidth: jest.fn(),
+            getVLManagedDeviceVersion: jest.fn(),
+            getVLDiscoverDevices: jest.fn(),
+            getVLDiscoverDevicesMode: jest.fn(),
+            updateVLDiscoverDeviceMode: jest.fn(),
+            deleteVLDiscoverDevice: jest.fn(),
+            activateVLDiscoverDevice: jest.fn()
+        };
+
+        TestBed.configureTestingModule({
+            providers: [
+                DeviceFacade,
+                { provide: DeviceService, useValue: deviceServiceMock },
+                { provide: AvxService, useValue: avxServiceMock },
+                { provide: VpnService, useValue: vpnServiceMock },
+                { provide: VolumeLicenseService, useValue: vlServiceMock }
+            ]
+        });
+
+        facade = TestBed.inject(DeviceFacade);
+    });
+
+    it('should be created', () => {
+        expect(facade).toBeTruthy();
+    });
+
+    it('should delegate all DeviceService methods', () => {
+        const testMethods = [
+            { name: 'getDeviceMonitoringMetrics', args: [{}] },
+            { name: 'getDevicesNetworkMetrics', args: [{}] },
+            { name: 'mapDeviceDetails', args: [[], []], isSync: true },
+            { name: 'getDeviceHAList', args: [] },
+            { name: 'executeCLICommand', args: ['name', {}] },
+            { name: 'updateSSLVHost', args: ['name', 'host', {}] },
+            { name: 'getAPVVSSSLInfo', args: [] },
+            { name: 'getAPVSLBVirtualServices', args: ['id'] },
+            { name: 'getDeviceGroups', args: [] },
+            { name: 'addDeviceGroup', args: [{}] },
+            { name: 'deleteDeviceGroup', args: [{}] },
+            { name: 'addDevice', args: [{}] },
+            { name: 'deleteDevice', args: [{}] },
+            { name: 'updateDevice', args: [1, 'type', 'name', 'data'] },
+            { name: 'getDeviceConfiguration', args: ['name'] },
+            { name: 'saveDeviceConfig', args: [{}], serviceMethod: 'saveDevice' },
+            { name: 'getDeviceConfigFiles', args: [] },
+            { name: 'getAvailableBuilds', args: [] },
+            { name: 'addDeviceBuildUpgradeConfig', args: [{}] },
+            { name: 'updateDeviceBuildUpgradeConfig', args: ['build', {}] },
+            { name: 'deleteDeviceBuildUpgradeConfig', args: [{}] },
+            { name: 'uploadDeviceBuild', args: [{}] },
+            { name: 'performDeviceBuildUpdate', args: [{}] },
+            { name: 'deleteDeviceConfigFile', args: [{}] },
+            { name: 'getDeviceTypeList', args: [] },
+            { name: 'getDeviceBuildInfo', args: [] },
+            { name: 'updateDeviceLicense', args: [{}] },
+            { name: 'cloneDeviceBackupConfig', args: [{}] },
+            { name: 'performDeviceConfigOperation', args: [{}] },
+            { name: 'getAMPDevicesList', args: [] },
+            { name: 'getDeviceConfigByConfigFileName', args: ['file', 'type'] },
+            { name: 'getAGConfigAssociatedVsites', args: [{}] },
+            { name: 'getAGVSiteConfig', args: ['config', 'vsite'] },
+            { name: 'updateDeviceConfigByConfigFileName', args: ['file', 'type', {}] },
+            { name: 'updateAGVSiteConfig', args: ['config', 'vsite', {}] },
+            { name: 'addCustomConfigFile', args: [{}] },
+            { name: 'addConfigTemplateKey', args: [{}] },
+            { name: 'updateConfigTemplateKey', args: [{}] },
+            { name: 'deleteConfigTemplateKey', args: [{}] },
+            { name: 'updateDeviceConfigTemplateDefaultValue', args: [{}] },
+            { name: 'getConfigTemplateKey', args: [] },
+            { name: 'getTemplateKeyPairs', args: [] },
+            { name: 'updateDeviceMonitoringState', args: [{}] },
+            { name: 'getScheduleDeviceBackup', args: [] },
+            { name: 'clearScheduleDeviceBackup', args: [{}] },
+            { name: 'getDeviceHAConfigByName', args: ['name', {}] },
+            { name: 'getDeviceHAUnitsByName', args: ['name', {}] },
+            { name: 'getDeviceHAGroupsByName', args: ['name', {}] },
+            { name: 'switchHAGroup', args: [{}] },
+            { name: 'getSSLBackupCertificates', args: ['name', 'host'] },
+            { name: 'getSSLVHostSNI', args: ['name', 'host'] },
+            { name: 'downloadSSLCertInfo', args: ['name', {}] },
+            { name: 'downloadSSLCertificate', args: ['name', 'app', 'file'] },
+            { name: 'deleteSSLCertificate', args: ['name', {}] },
+            { name: 'backupSSLCertificate', args: ['name', {}] },
+            { name: 'restoreSSLCertificate', args: ['name', {}] },
+            { name: 'getSSLCertificates', args: ['name', 'host'] },
+            { name: 'activateSSLCertificate', args: ['name', {}] },
+            { name: 'deactivateSSLCertificate', args: ['name', {}] },
+            { name: 'deleteVHostSSLCertificate', args: ['name', {}] },
+            { name: 'importSSLCertificate', args: ['name', {}] },
+            { name: 'getClientVerificationSettings', args: ['id', 'host'] },
+            { name: 'updateClientVerificationSettings', args: ['id', 'host', {}] },
+            { name: 'executeAPVCLICommand', args: ['name', 'cmd'] },
+            { name: 'getSSLInterCACertificates', args: ['name', 'host'] },
+            { name: 'deleteAPVSSLInterCACertificate', args: ['name', {}] },
+            { name: 'importInterCACertificate', args: ['name', {}] },
+            { name: 'getReports', args: [] },
+            { name: 'getReportTasks', args: [] },
+            { name: 'getDeviceServices', args: [{}] },
+            { name: 'createReportTask', args: [{}] },
+            { name: 'updateReportTask', args: ['id', {}] },
+            { name: 'deleteReportTask', args: ['id'] },
+            { name: 'createReport', args: ['id', {}] },
+            { name: 'downloadReport', args: ['file'] },
+            { name: 'deleteReport', args: ['id'] },
+            { name: 'getAPVVSMonitoringMetrics', args: [{}] },
+            { name: 'getAPVRSMonitoringMetrics', args: [{}] },
+            { name: 'getSSLVPNMonitoringMetrics', args: [{}] },
+            { name: 'getLLBMonitoringMetrics', args: [{}] },
+            { name: 'getAPVSLBRealServices', args: ['id'] },
+            { name: 'getSLBServices', args: ['id'] },
+            { name: 'updateRealService', args: [{}] }
+        ];
+
+        testMethods.forEach(m => {
+            const sName = m.serviceMethod || m.name;
+            if (m.isSync) {
+                deviceServiceMock[sName].mockReturnValue({ success: true });
+                const res = (facade as any)[m.name](...m.args);
+                expect(res.success).toBe(true);
+            } else {
+                deviceServiceMock[sName].mockReturnValue(of({ success: true }));
+                (facade as any)[m.name](...m.args).subscribe((res: any) => {
+                    expect(res.success).toBe(true);
+                });
+            }
+            expect(deviceServiceMock[sName]).toHaveBeenCalledWith(...m.args);
+        });
+    });
+
+    it('should delegate all AvxService methods', () => {
+        const testMethods = [
+            { name: 'getAVXDevices', args: [] },
+            { name: 'getAVXDeviceImages', args: ['id'] },
+            { name: 'getAVXVAInstances', args: ['id'] },
+            { name: 'getAVXVAInstanceConfig', args: ['id', 'name'] },
+            { name: 'getAVXVAInstancePortMapping', args: ['id', 'name'] },
+            { name: 'getAVXVAInstancePlatformResource', args: ['id', 'name'] },
+            { name: 'getAVXVAInstanceShowVersion', args: ['id', 'name'] },
+            { name: 'getAVXVAInstanceBasicConfig', args: ['id', {}] },
+            { name: 'getAVXVABackups', args: ['id'] },
+            { name: 'createAVXVABackup', args: ['id', {}] },
+            { name: 'deleteAVXVABackup', args: ['id', {}] },
+            { name: 'importAVXVABackup', args: ['id', {}] },
+            { name: 'exportAVXVABackup', args: ['id', {}] },
+            { name: 'restoreAVXVABackup', args: ['id', {}] },
+            { name: 'createInstanceFromBackup', args: ['id', {}] },
+            { name: 'updateAVXInstancePowerStatus', args: ['id', 'name', 'status'] },
+            { name: 'updateVAPVLicenseKey', args: ['id', 'name', 'key'] },
+            { name: 'createVAPVLicenseRequestFile', args: ['id', 'name', 'size'] },
+            { name: 'deleteVAInstance', args: ['id', 'name'] },
+            { name: 'getAutoAssignResources', args: ['id', {}] },
+            { name: 'getAVXDeviceLogSettings', args: ['id'] },
+            { name: 'getAVXDeviceLog', args: ['id'] },
+            { name: 'getAVXDeviceConfig', args: ['id', 'config'] },
+            { name: 'getAVXDeviceSystemInfo', args: ['id'] },
+            { name: 'getAVXDeviceSystemInfoOverview', args: ['id'] },
+            { name: 'getAVXDeviceResourceOccupation', args: ['id'] },
+            { name: 'addAVXDevice', args: [{}] },
+            { name: 'deleteAVXDevice', args: [{}] }
+        ];
+
+        testMethods.forEach(m => {
+            avxServiceMock[m.name].mockReturnValue(of({ success: true }));
+            (facade as any)[m.name](...m.args).subscribe((res: any) => {
+                expect(res.success).toBe(true);
+            });
+            expect(avxServiceMock[m.name]).toHaveBeenCalledWith(...m.args);
+        });
+    });
+
+    it('should delegate all VpnService methods', () => {
+        const testMethods = [
+            { name: 'getVPNServices', args: ['name', {}] },
+            { name: 'executeAGCLICommand', args: ['name', {}] },
+            { name: 'getHardwareIds', args: ['name', {}] },
+            { name: 'batchAGCLICommand', args: ['name', {}] }
+        ];
+
+        testMethods.forEach(m => {
+            vpnServiceMock[m.name].mockReturnValue(of({ success: true }));
+            (facade as any)[m.name](...m.args).subscribe((res: any) => {
+                expect(res.success).toBe(true);
+            });
+            expect(vpnServiceMock[m.name]).toHaveBeenCalledWith(...m.args);
+        });
+    });
+
+    it('should delegate all VolumeLicenseService methods', () => {
+        const testMethods = [
+            { name: 'getVolumeLicenses', args: [] },
+            { name: 'addVolumeLicense', args: [{}] },
+            { name: 'deleteVolumeLicense', args: [{}] },
+            { name: 'getManagedDevices', args: [] },
+            { name: 'activateDeviceVolumeLicense', args: [{}] },
+            { name: 'deactivateDeviceVolumeLicense', args: [{}] },
+            { name: 'removeVLManagedDevice', args: [{}] },
+            { name: 'importVLManagedDevice', args: [{}] },
+            { name: 'addVLManagedDevice', args: [{}] },
+            { name: 'updateVLManagedDeviceBandwidth', args: ['id', {}] },
+            { name: 'getVLManagedDeviceVersion', args: [{}] },
+            { name: 'getVLDiscoverDevices', args: [] },
+            { name: 'getVLDiscoverDevicesMode', args: [] },
+            { name: 'updateVLDiscoverDeviceMode', args: [{}] },
+            { name: 'deleteVLDiscoverDevice', args: [{}] },
+            { name: 'activateVLDiscoverDevice', args: [{}] }
+        ];
+
+        testMethods.forEach(m => {
+            vlServiceMock[m.name].mockReturnValue(of({ success: true }));
+            (facade as any)[m.name](...m.args).subscribe((res: any) => {
+                expect(res.success).toBe(true);
+            });
+            expect(vlServiceMock[m.name]).toHaveBeenCalledWith(...m.args);
+        });
+    });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/monitoring.facade.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/monitoring.facade.spec.ts	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/monitoring.facade.spec.ts	(working copy)
@@ -0,0 +1,97 @@
+import { TestBed } from '@angular/core/testing';
+import { MonitoringFacade } from './monitoring.facade';
+import { DeviceService } from '../device.service';
+import { of } from 'rxjs';
+
+describe('MonitoringFacade', () => {
+    let facade: MonitoringFacade;
+    let deviceServiceMock: any;
+
+    beforeEach(() => {
+        deviceServiceMock = {
+            getDeviceMonitoringMetrics: jest.fn(),
+            getAPVVSMonitoringMetrics: jest.fn(),
+            getAPVRSMonitoringMetrics: jest.fn(),
+            getSSLVPNMonitoringMetrics: jest.fn(),
+            getLLBMonitoringMetrics: jest.fn(),
+            getDevicesNetworkMetrics: jest.fn(),
+            mapDeviceDetails: jest.fn()
+        };
+
+        TestBed.configureTestingModule({
+            providers: [
+                MonitoringFacade,
+                { provide: DeviceService, useValue: deviceServiceMock }
+            ]
+        });
+
+        facade = TestBed.inject(MonitoringFacade);
+    });
+
+    it('should be created', () => {
+        expect(facade).toBeTruthy();
+    });
+
+    it('should delegate getDeviceMetrics', () => {
+        const payload = { test: 1 };
+        deviceServiceMock.getDeviceMonitoringMetrics.mockReturnValue(of({ data: 2 }));
+        facade.getDeviceMetrics(payload).subscribe(res => {
+            expect(res.data).toBe(2);
+        });
+        expect(deviceServiceMock.getDeviceMonitoringMetrics).toHaveBeenCalledWith(payload);
+    });
+
+    it('should delegate getAPVVSMetrics', () => {
+        const payload = { test: 1 };
+        deviceServiceMock.getAPVVSMonitoringMetrics.mockReturnValue(of({ data: 2 }));
+        facade.getAPVVSMetrics(payload).subscribe(res => {
+            expect(res.data).toBe(2);
+        });
+        expect(deviceServiceMock.getAPVVSMonitoringMetrics).toHaveBeenCalledWith(payload);
+    });
+
+    it('should delegate getAPVRSMetrics', () => {
+        const payload = { test: 1 };
+        deviceServiceMock.getAPVRSMonitoringMetrics.mockReturnValue(of({ data: 2 }));
+        facade.getAPVRSMetrics(payload).subscribe(res => {
+            expect(res.data).toBe(2);
+        });
+        expect(deviceServiceMock.getAPVRSMonitoringMetrics).toHaveBeenCalledWith(payload);
+    });
+
+    it('should delegate getSSLVPNMetrics', () => {
+        const payload = { test: 1 };
+        deviceServiceMock.getSSLVPNMonitoringMetrics.mockReturnValue(of({ data: 2 }));
+        facade.getSSLVPNMetrics(payload).subscribe(res => {
+            expect(res.data).toBe(2);
+        });
+        expect(deviceServiceMock.getSSLVPNMonitoringMetrics).toHaveBeenCalledWith(payload);
+    });
+
+    it('should delegate getLLBMetrics', () => {
+        const payload = { test: 1 };
+        deviceServiceMock.getLLBMonitoringMetrics.mockReturnValue(of({ data: 2 }));
+        facade.getLLBMetrics(payload).subscribe(res => {
+            expect(res.data).toBe(2);
+        });
+        expect(deviceServiceMock.getLLBMonitoringMetrics).toHaveBeenCalledWith(payload);
+    });
+
+    it('should delegate getNetworkMetrics', () => {
+        const payload = { test: 1 };
+        deviceServiceMock.getDevicesNetworkMetrics.mockReturnValue(of({ data: 2 }));
+        facade.getNetworkMetrics(payload).subscribe(res => {
+            expect(res.data).toBe(2);
+        });
+        expect(deviceServiceMock.getDevicesNetworkMetrics).toHaveBeenCalledWith(payload);
+    });
+
+    it('should delegate mapDeviceDetails', () => {
+        const devices = [1];
+        const metrics = [2];
+        deviceServiceMock.mapDeviceDetails.mockReturnValue([3]);
+        const res = facade.mapDeviceDetails(devices, metrics);
+        expect(res).toEqual([3]);
+        expect(deviceServiceMock.mapDeviceDetails).toHaveBeenCalledWith(devices, metrics);
+    });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/system.facade.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/system.facade.spec.ts	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/facades/system.facade.spec.ts	(working copy)
@@ -0,0 +1,257 @@
+import { TestBed } from '@angular/core/testing';
+import { SystemFacade } from './system.facade';
+import { SystemService } from '../system.service';
+import { OpenSearchService } from '../open-search.service';
+import { of } from 'rxjs';
+
+describe('SystemFacade', () => {
+    let facade: SystemFacade;
+    let systemServiceMock: any;
+    let openSearchServiceMock: any;
+
+    beforeEach(() => {
+        systemServiceMock = {
+            getSystemHostConfig: jest.fn(),
+            updateSystemHostConfig: jest.fn(),
+            getSystemLogSettings: jest.fn(),
+            updateSystemLogSettings: jest.fn(),
+            getRemoteSyslogHosts: jest.fn(),
+            addRemoteSyslogHost: jest.fn(),
+            deleteRemoteSyslogHost: jest.fn(),
+            getAppLicense: jest.fn(),
+            updateAppLicense: jest.fn(),
+            getNTPConfig: jest.fn(),
+            updateNTPStatus: jest.fn(),
+            addNTPServer: jest.fn(),
+            deleteNTPServer: jest.fn(),
+            getSystemDateTime: jest.fn(),
+            updateSystemDateTime: jest.fn(),
+            getSystemBackupFiles: jest.fn(),
+            deleteSystemBackupFile: jest.fn(),
+            getScheduledBackupFiles: jest.fn(),
+            deleteBackupSchedule: jest.fn(),
+            getSystemRestoreStatus: jest.fn(),
+            createSystemBackup: jest.fn(),
+            createSystemRestore: jest.fn(),
+            getSystemBackupRemoteStorage: jest.fn(),
+            updateSystemBackupRemoteStorage: jest.fn(),
+            createSystemScheduledBackup: jest.fn(),
+            getSystemVersion: jest.fn(),
+            getNetworkInterfaces: jest.fn(),
+            getDNSServers: jest.fn(),
+            getDefaultRoutes: jest.fn(),
+            updateNetworkInterface: jest.fn(),
+            getAAASettings: jest.fn(),
+            deleteAAAESSettings: jest.fn(),
+            addAAAESServer: jest.fn(),
+            updateAAAESServer: jest.fn(),
+            updateAAASettings: jest.fn(),
+            updateDefaultRoute: jest.fn(),
+            clearDefaultRoute: jest.fn(),
+            deleteDNSServer: jest.fn(),
+            addDNSServer: jest.fn(),
+            addNetworkInterfaceIPv6: jest.fn(),
+            getTasks: jest.fn(),
+            deleteTask: jest.fn(),
+            deleteAllTasks: jest.fn(),
+            getSMTPSettings: jest.fn(),
+            updateSMTPSettings: jest.fn(),
+            getNotificationChannels: jest.fn(),
+            deleteNotificationChannel: jest.fn(),
+            getAdminUsers: jest.fn(),
+            sendTestEmail: jest.fn(),
+            sendTestWebhook: jest.fn(),
+            addNotificationChannel: jest.fn(),
+            performSystemReboot: jest.fn(),
+            performSystemShutdown: jest.fn(),
+            getOperationalLogs: jest.fn(),
+            deleteOperationalLog: jest.fn(),
+            deleteAllOperationalLogs: jest.fn(),
+            getUserRoles: jest.fn(),
+            renameUserRole: jest.fn(),
+            addUserRole: jest.fn(),
+            deleteUserRole: jest.fn(),
+            getDeviceGroupsByRoleId: jest.fn(),
+            getAllDeviceGroups: jest.fn(),
+            updateDeviceGroupsForRoleId: jest.fn(),
+            getUsers: jest.fn(),
+            deleteUser: jest.fn(),
+            addUser: jest.fn(),
+            updateUser: jest.fn(),
+            getTaskByName: jest.fn(),
+            upload: jest.fn(),
+            getObservabilityStatus: jest.fn(),
+            restartObservabilityService: jest.fn(),
+            getOpenSearchAuthToken: jest.fn(),
+            getGrafanaAuthToken: jest.fn(),
+            getLatestSystemMetrics: jest.fn(),
+            getHistoricalSystemMetrics: jest.fn(),
+            getConnectedDevicesMetrics: jest.fn(),
+            getTopAPVVirtualServicesMetrics: jest.fn(),
+            getTopAPVRealServicesMetrics: jest.fn(),
+            getTopAPVLLBMetrics: jest.fn(),
+            getTopSSLVPNMetrics: jest.fn(),
+            getStorage: jest.fn(),
+            getStorageCleanupConfig: jest.fn(),
+            getStorageAllocation: jest.fn(),
+            updateLogLocation: jest.fn(),
+            getStorageQuery: jest.fn(),
+            getStorageArchives: jest.fn(),
+            createLogsArchive: jest.fn(),
+            updateStorageCleanupConfig: jest.fn(),
+            initiateStorageCleanup: jest.fn()
+        };
+        openSearchServiceMock = {
+            queryOSLogs: jest.fn()
+        };
+
+        TestBed.configureTestingModule({
+            providers: [
+                SystemFacade,
+                { provide: SystemService, useValue: systemServiceMock },
+                { provide: OpenSearchService, useValue: openSearchServiceMock }
+            ]
+        });
+
+        facade = TestBed.inject(SystemFacade);
+    });
+
+    it('should be created', () => {
+        expect(facade).toBeTruthy();
+    });
+
+    it('should delegate all SystemService methods', () => {
+        const testMethods = [
+            { name: 'getSystemHostConfig', args: [] },
+            { name: 'updateSystemHostConfig', args: [{}] },
+            { name: 'getSystemLogSettings', args: [] },
+            { name: 'updateSystemLogSettings', args: [{}] },
+            { name: 'getRemoteSyslogHosts', args: [] },
+            { name: 'addRemoteSyslogHost', args: [{}] },
+            { name: 'deleteRemoteSyslogHost', args: [{}] },
+            { name: 'getAppLicense', args: [] },
+            { name: 'updateAppLicense', args: [{}] },
+            { name: 'getNTPConfig', args: [] },
+            { name: 'updateNTPStatus', args: [{}] },
+            { name: 'addNTPServer', args: [{}] },
+            { name: 'deleteNTPServer', args: [{}] },
+            { name: 'getSystemDateTime', args: [] },
+            { name: 'updateSystemDateTime', args: [{}] },
+            { name: 'getSystemBackupFiles', args: [] },
+            { name: 'deleteSystemBackupFile', args: ['file'] },
+            { name: 'getScheduledBackupFiles', args: [] },
+            { name: 'deleteBackupSchedule', args: [] },
+            { name: 'getSystemRestoreStatus', args: [] },
+            { name: 'createSystemBackup', args: [{}] },
+            { name: 'createSystemRestore', args: [{}] },
+            { name: 'getSystemBackupRemoteStorage', args: [] },
+            { name: 'updateSystemBackupRemoteStorage', args: [{}] },
+            { name: 'createSystemScheduledBackup', args: [{}] },
+            { name: 'getSystemVersion', args: [] },
+            { name: 'getNetworkInterfaces', args: [] },
+            { name: 'getDNSServers', args: [] },
+            { name: 'getDefaultRoutes', args: [] },
+            { name: 'updateNetworkInterface', args: ['eth0', 'ipv4', '1.1.1.1', '24', {}] },
+            { name: 'getAAASettings', args: [] },
+            { name: 'deleteAAAESSettings', args: [{}] },
+            { name: 'addAAAESServer', args: [{}] },
+            { name: 'updateAAAESServer', args: ['id', {}] },
+            { name: 'updateAAASettings', args: [{}] },
+            { name: 'updateDefaultRoute', args: [{}] },
+            { name: 'clearDefaultRoute', args: [{}] },
+            { name: 'deleteDNSServer', args: [{}] },
+            { name: 'addDNSServer', args: [{}] },
+            { name: 'addNetworkInterfaceIPv6', args: [{}] },
+            { name: 'getTasks', args: [{}] },
+            { name: 'deleteTask', args: [{}] },
+            { name: 'deleteAllTasks', args: [{}] },
+            { name: 'getSMTPSettings', args: [] },
+            { name: 'updateSMTPSettings', args: [{}] },
+            { name: 'getNotificationChannels', args: [10] },
+            { name: 'deleteNotificationChannel', args: [1] },
+            { name: 'getAdminUsers', args: [] },
+            { name: 'sendTestEmail', args: [{}] },
+            { name: 'sendTestWebhook', args: [{}] },
+            { name: 'addNotificationChannel', args: [{}] },
+            { name: 'performSystemReboot', args: [{}] },
+            { name: 'performSystemShutdown', args: [{}] },
+            { name: 'getOperationalLogs', args: [{}] },
+            { name: 'deleteOperationalLog', args: [{}] },
+            { name: 'deleteAllOperationalLogs', args: [] },
+            { name: 'getUserRoles', args: [] },
+            { name: 'renameUserRole', args: [1, {}] },
+            { name: 'addUserRole', args: [{}] },
+            { name: 'deleteUserRole', args: [{}] },
+            { name: 'getDeviceGroupsByRoleId', args: [{}] },
+            { name: 'getAllDeviceGroups', args: [{}] },
+            { name: 'updateDeviceGroupsForRoleId', args: [{}] },
+            { name: 'getUsers', args: [] },
+            { name: 'deleteUser', args: [{}] },
+            { name: 'addUser', args: [{}] },
+            { name: 'updateUser', args: ['user', {}] },
+            { name: 'getTaskByName', args: [{}] },
+            { name: 'upload', args: ['param', {}] },
+            { name: 'getObservabilityStatus', args: [] },
+            { name: 'restartObservabilityService', args: [{}] },
+            { name: 'getOpenSearchAuthToken', args: [] },
+            { name: 'getGrafanaAuthToken', args: [] },
+            { name: 'getLatestSystemMetrics', args: [] },
+            { name: 'getHistoricalSystemMetrics', args: [] },
+            { name: 'getConnectedDevicesMetrics', args: [{}] },
+            { name: 'getTopAPVVirtualServicesMetrics', args: [{}] },
+            { name: 'getTopAPVRealServicesMetrics', args: [{}] },
+            { name: 'getTopAPVLLBMetrics', args: [{}] },
+            { name: 'getTopSSLVPNMetrics', args: [{}] },
+            { name: 'getStorage', args: ['type'] },
+            { name: 'getStorageCleanupConfig', args: ['primary'] },
+            { name: 'getStorageAllocation', args: [] },
+            { name: 'updateLogLocation', args: [{}] },
+            { name: 'getStorageQuery', args: [{}] },
+            { name: 'getStorageArchives', args: ['primary'] },
+            { name: 'createLogsArchive', args: [{}] },
+            { name: 'updateStorageCleanupConfig', args: [{}] },
+            { name: 'initiateStorageCleanup', args: [{}] }
+        ];
+
+        testMethods.forEach(m => {
+            systemServiceMock[m.name].mockReturnValue(of({ success: true }));
+            (facade as any)[m.name](...m.args).subscribe((res: any) => {
+                expect(res.success).toBe(true);
+            });
+            expect(systemServiceMock[m.name]).toHaveBeenCalledWith(...m.args);
+        });
+    });
+
+    it('should execute getNotificationChannels with default value', () => {
+        systemServiceMock.getNotificationChannels.mockReturnValue(of({}));
+        facade.getNotificationChannels();
+        expect(systemServiceMock.getNotificationChannels).toHaveBeenCalledWith(-1);
+    });
+
+    it('should execute getStorageCleanupConfig with default value', () => {
+        systemServiceMock.getStorageCleanupConfig.mockReturnValue(of({}));
+        facade.getStorageCleanupConfig();
+        expect(systemServiceMock.getStorageCleanupConfig).toHaveBeenCalledWith('primary');
+    });
+
+    it('should execute getStorageArchives with default value', () => {
+        systemServiceMock.getStorageArchives.mockReturnValue(of({}));
+        facade.getStorageArchives();
+        expect(systemServiceMock.getStorageArchives).toHaveBeenCalledWith('primary');
+    });
+
+    it('should execute upload with default value', () => {
+        systemServiceMock.upload.mockReturnValue(of({}));
+        facade.upload(null, {});
+        expect(systemServiceMock.upload).toHaveBeenCalledWith(null, {});
+    });
+
+    it('should delegate OpenSearchService methods', () => {
+        const payload = { q: '*' };
+        openSearchServiceMock.queryOSLogs.mockReturnValue(of({ hits: [] }));
+        facade.queryOSLogs(payload).subscribe(res => {
+            expect(res.hits).toEqual([]);
+        });
+        expect(openSearchServiceMock.queryOSLogs).toHaveBeenCalledWith(payload);
+    });
+});
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/http.service.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/http.service.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/http.service.spec.ts	(working copy)
@@ -35,32 +35,32 @@
     req.flush(testData);
   });
 
-  it('should perform a GET request with headers', () => {
-    service.get('/api/test', {}, [{ name: 'Custom-Header', value: 'Value' }]).subscribe();
+  it('should perform a GET request with headers (as param) and options params', () => {
+    service.get('/api/test', { params: { q: 'search' } }, [{ name: 'Custom-Header', value: 'Value' }]).subscribe();
 
-    const req = httpMock.expectOne('/api/test');
+    const req = httpMock.expectOne(req => req.url === '/api/test' && req.params.has('q'));
     expect(req.request.headers.get('Custom-Header')).toBe('Value');
     req.flush({});
   });
 
-  it('should perform a POST request with JSON', () => {
+  it('should perform a POST request with JSON and headers in options', () => {
     const payload = { id: 1 };
-    service.post('/api/post', payload).subscribe();
+    service.post('/api/post', payload, { headers: { 'X-Opt': 'Val' } }).subscribe();
 
     const req = httpMock.expectOne('/api/post');
     expect(req.request.method).toBe('POST');
     expect(req.request.headers.get('Content-Type')).toBe('application/json');
-    expect(req.request.body).toEqual(payload);
+    expect(req.request.headers.get('X-Opt')).toBe('Val');
     req.flush({});
   });
 
   it('should perform a POST request with FormData and set HttpContext', () => {
     const payload = new FormData();
-    service.post('/api/upload', payload, { isFormData: true, csrf: true }).subscribe();
+    service.post('/api/upload', payload, { isFormData: true, csrf: true, csrfInFormData: true }).subscribe();
 
     const req = httpMock.expectOne('/api/upload');
-    expect(req.request.headers.get('Content-Type')).toBeNull(); // Boundary is set automatically
     expect(req.request.context.get(ADD_CSRF_TO_PAYLOAD)).toBe(true);
+    expect(req.request.context.get(ADD_CSRF_TO_FORMDATA)).toBe(true);
     req.flush({});
   });
 
@@ -74,13 +74,29 @@
     req.flush({});
   });
 
-  it('should perform a PUT request', () => {
-    service.put('/api/put', {}).subscribe();
+  it('should perform a PUT request with varied options', () => {
+    service.put('/api/put', { a: 1 }, { responseType: 'text', withCredentials: true }).subscribe();
     const req = httpMock.expectOne('/api/put');
     expect(req.request.method).toBe('PUT');
+    expect(req.request.responseType).toBe('text');
+    expect(req.request.withCredentials).toBe(true);
+    req.flush('done');
+  });
+
+  it('should perform a PATCH request with JSON', () => {
+    service.patch('/api/patch', { a: 1 }).subscribe();
+    const req = httpMock.expectOne('/api/patch');
+    expect(req.request.method).toBe('PATCH');
     req.flush({});
   });
 
+  it('should perform a PATCH request with form', () => {
+    service.patch('/api/patch-form', { a: 1 }, { isForm: true }).subscribe();
+    const req = httpMock.expectOne('/api/patch-form');
+    expect(req.request.headers.get('Content-Type')).toBe('application/x-www-form-urlencoded');
+    req.flush({});
+  });
+
   it('should perform a DELETE request', () => {
     service.delete('/api/delete').subscribe();
     const req = httpMock.expectOne('/api/delete');
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/system.service.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/system.service.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/system.service.spec.ts	(working copy)
@@ -1,8 +1,8 @@
 import { TestBed } from '@angular/core/testing';
-import { of } from 'rxjs';
 import { SystemService } from './system.service';
 import { HttpService } from './http.service';
 import { URLS } from '../constants/api_urls';
+import { of } from 'rxjs';
 
 describe('SystemService', () => {
   let service: SystemService;
@@ -12,7 +12,6 @@
     httpMock = {
       get: jest.fn(),
       post: jest.fn(),
-      put: jest.fn(),
       delete: jest.fn()
     };
 
@@ -22,6 +21,7 @@
         { provide: HttpService, useValue: httpMock }
       ]
     });
+
     service = TestBed.inject(SystemService);
   });
 
@@ -29,50 +29,168 @@
     expect(service).toBeTruthy();
   });
 
-  it('should get system host config', () => {
-    httpMock.get.mockReturnValue(of({}));
-    service.getSystemHostConfig();
-    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_SYSTEM_HOST_CONFIG_URL);
-  });
+  it('should test simple GET methods', () => {
+    const getMethods = [
+      { name: 'getSystemHostConfig', url: URLS.GET_SYSTEM_HOST_CONFIG_URL },
+      { name: 'getSystemLogSettings', url: URLS.GET_SYSTEM_LOG_SETTINGS_URL },
+      { name: 'getRemoteSyslogHosts', url: URLS.GET_REMOTE_SYSTEM_LOG_HOST_URL },
+      { name: 'getAppLicense', url: URLS.GET_APP_LICENSE_URL },
+      { name: 'getSystemDateTime', url: URLS.GET_SYSTEM_TIME_URL },
+      { name: 'getSystemBackupFiles', url: URLS.GET_SYSTEM_BACKUP_FILES_URL },
+      { name: 'getScheduledBackupFiles', url: URLS.GET_SYSTEM_BACKUP_SCHEDULE_URL },
+      { name: 'getSystemRestoreStatus', url: URLS.GET_SYSTEM_RESTORE_STATUS_URL },
+      { name: 'getSystemBackupRemoteStorage', url: URLS.GET_SYSTEM_BACKUP_REMOTE_STORAGE_URL },
+      { name: 'getSystemVersion', url: URLS.GET_SYSTEM_VERSION_URL },
+      { name: 'getNetworkInterfaces', url: URLS.GET_NETWORK_INTERFACES_URL },
+      { name: 'getDNSServers', url: URLS.GET_DNS_SERVERS_URL },
+      { name: 'getDefaultRoutes', url: URLS.GET_DEFAULT_ROUTE_URL },
+      { name: 'getAAASettings', url: URLS.GET_AAA_SETTINGS_URL },
+      { name: 'getSMTPSettings', url: URLS.GET_SMTP_SETTINGS_URL },
+      { name: 'getAdminUsers', url: URLS.GET_ADMIN_USERS_URL },
+      { name: 'deleteAllOperationalLogs', url: URLS.DELETE_ALL_OPERATIONAL_LOGS_URL },
+      { name: 'getUserRoles', url: URLS.GET_USER_ROLES_URL },
+      { name: 'getUsers', url: URLS.GET_USERS_URLS },
+      { name: 'getObservabilityStatus', url: URLS.GET_OBSERVABILITY_STATUS_URL },
+      { name: 'getOpenSearchAuthToken', url: URLS.GET_OPENSEARCH_SSO_TOKEN_URL },
+      { name: 'getGrafanaAuthToken', url: URLS.GET_GRAFANA_SSO_TOKEN_URL },
+      { name: 'getLatestSystemMetrics', url: URLS.GET_SYSTEM_LATEST_METRICS_URL },
+      { name: 'getHistoricalSystemMetrics', url: URLS.GET_SYSTEM_HISTORICAL_METRICS_URL },
+      { name: 'getStorageAllocation', url: URLS.GET_STORAGE_ALLOCATION_URL }
+    ];
 
-  it('should update system host config', () => {
-    httpMock.post.mockReturnValue(of({}));
-    const payload = { hostname: 'test' };
-    service.updateSystemHostConfig(payload);
-    expect(httpMock.post).toHaveBeenCalledWith(
-      URLS.UPDATE_SYSTEM_HOST_CONFIG_URL,
-      payload,
-      expect.objectContaining({ csrf: true, isFormData: true })
-    );
+    getMethods.forEach(m => {
+      httpMock.get.mockReturnValue(of({}));
+      (service as any)[m.name]();
+      expect(httpMock.get).toHaveBeenCalledWith(m.url);
+    });
   });
 
-  it('should get NTP config with correct fields', () => {
+  it('should test parameterized GET methods', () => {
     httpMock.get.mockReturnValue(of({}));
+
     service.getNTPConfig();
-    const expectedUrl = `${URLS.GET_NTP_CONFIG_URL}?fields=[%22enable_ntp%22,%20%22ntp_server%22,%20%22ntp_stats%22]`;
-    expect(httpMock.get).toHaveBeenCalledWith(expectedUrl);
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_NTP_CONFIG_URL}?fields=[%22enable_ntp%22,%20%22ntp_server%22,%20%22ntp_stats%22]`);
+
+    service.getNotificationChannels(10);
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_NOTIFICATION_CHANNELS_URL}?page_size=10`);
+
+    service.getStorage('primary');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_STORAGE_URL}?type=primary`);
+
+    service.getStorageCleanupConfig('secondary');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_STORAGE_CLEANUP_CONFIG_URL}?storage=secondary`);
+
+    service.getStorageArchives('primary');
+    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_LOGS_ARCHIVES_URL}?location=primary`);
   });
 
-  it('should update network interface', () => {
+  it('should test simple POST methods', () => {
+    const postMethods = [
+      { name: 'updateSystemHostConfig', url: URLS.UPDATE_SYSTEM_HOST_CONFIG_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateSystemLogSettings', url: URLS.UPDATE_SYSTEM_LOG_SETTINGS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addRemoteSyslogHost', url: URLS.ADD_REMOTE_SYSTEM_LOG_HOST_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteRemoteSyslogHost', url: URLS.DELETE_REMOTE_SYSTEM_LOG_HOST_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateAppLicense', url: URLS.UPDATE_APP_LICENSE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateNTPStatus', url: URLS.UPDATE_NTP_STATUS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addNTPServer', url: URLS.ADD_NTP_SERVER_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteNTPServer', url: URLS.DELETE_NTP_SERVER_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateSystemDateTime', url: URLS.UPDATE_SYSTEM_TIME_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'createSystemBackup', url: URLS.CREATE_SYSTEM_BACKUP_URL },
+      { name: 'createSystemRestore', url: URLS.CREATE_SYSTEM_RESTORE_URL },
+      { name: 'updateSystemBackupRemoteStorage', url: URLS.UPDATE_SYSTEM_BACKUP_REMOTE_STORAGE_URL },
+      { name: 'createSystemScheduledBackup', url: URLS.CREATE_SYSTEM_SCHEDULED_BACKUP_URL },
+      { name: 'deleteAAAESSettings', url: URLS.DELETE_AAA_ES_SETTINGS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addAAAESServer', url: URLS.ADD_AAA_ES_SERVER_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateAAASettings', url: URLS.UPDATE_AAA_SETTINGS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateDefaultRoute', url: URLS.UPDATE_DEFAULT_ROUTE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'clearDefaultRoute', url: URLS.CLEAR_DEFAULT_ROUTE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteDNSServer', url: URLS.DELETE_DNS_SERVER_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addDNSServer', url: URLS.ADD_DNS_SERVER_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addNetworkInterfaceIPv6', url: URLS.ADD_INTERFACE_IP_DETAILS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'getTasks', url: URLS.GET_TASK_AND_LOG_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteTask', url: URLS.DELETE_TASK_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteAllTasks', url: URLS.DELETE_ALL_TASKS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateSMTPSettings', url: URLS.UPDATE_SMTP_SETTINGS_URL },
+      { name: 'sendTestEmail', url: URLS.SEND_TEST_EMAIL_URL },
+      { name: 'sendTestWebhook', url: URLS.SEND_TEST_WEBHOOK_URL },
+      { name: 'addNotificationChannel', url: URLS.ADD_NOTIFICATION_CHANNEL_URL },
+      { name: 'performSystemReboot', url: URLS.PERFORM_SYSTEM_REBOOT_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'performSystemShutdown', url: URLS.PERFORM_SYSTEM_SHUTDOWN_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'getOperationalLogs', url: URLS.GET_OPERATIONAL_LOGS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteOperationalLog', url: URLS.DELETE_OPERATIONAL_LOG_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addUserRole', url: URLS.ADD_USER_ROLE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteUserRole', url: URLS.DELETE_USER_ROLE_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'getDeviceGroupsByRoleId', url: URLS.GET_DEVICE_GROUPS_BY_ROLE_ID_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'getAllDeviceGroups', url: URLS.GET_DEVICE_GROUPS_LIST_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'updateDeviceGroupsForRoleId', url: URLS.UPDATE_USER_ROLE_DEVICE_GROUPS_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'deleteUser', url: URLS.DELETE_USER_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'addUser', url: URLS.ADD_USER_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'getTaskByName', url: URLS.GET_TASK_BY_NAME_URL, options: { csrf: true, isFormData: true, csrfInFormData: true } },
+      { name: 'restartObservabilityService', url: URLS.RESTART_OBSERVABILITY_SERVICE_URL },
+      { name: 'getConnectedDevicesMetrics', url: URLS.GET_DEVICES_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getTopAPVVirtualServicesMetrics', url: URLS.GET_TOP_APV_VIRTUAL_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getTopAPVRealServicesMetrics', url: URLS.GET_TOP_APV_REAL_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getTopAPVLLBMetrics', url: URLS.GET_TOP_APV_LLB_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getTopSSLVPNMetrics', url: URLS.GET_TOP_SSL_VPN_METRICS_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'updateLogLocation', url: URLS.UPDATE_LOG_LOCATION_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'getStorageQuery', url: URLS.GET_STORAGE_QUERY_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'createLogsArchive', url: URLS.CREATE_LOGS_ARCHIVE_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'updateStorageCleanupConfig', url: URLS.UPDATE_STORAGE_CLEANUP_CONFIG_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } },
+      { name: 'initiateStorageCleanup', url: URLS.PERFORM_STORAGE_CLEANUP_URL, options: { csrf: true, isFormData: false, csrfInFormData: true } }
+    ];
+
+    postMethods.forEach(m => {
+      httpMock.post.mockReturnValue(of({}));
+      const payload = { test: 1 };
+      (service as any)[m.name](payload);
+      if (m.options) {
+        expect(httpMock.post).toHaveBeenCalledWith(m.url, payload, m.options);
+      } else {
+        expect(httpMock.post).toHaveBeenCalledWith(m.url, payload);
+      }
+    });
+  });
+
+  it('should test parameterized POST methods', () => {
     httpMock.post.mockReturnValue(of({}));
-    service.updateNetworkInterface('eth0', 'ipv4', '1.2.3.4', '24', {});
-    const expectedUrl = `${URLS.UPDATE_INTERFACE_URL}/%5B%7B%22interface_name%22%3A%20%22eth0%22%2C%20%22_asso_idx%22%3A%200%7D%5D/ip/%7B%22ipv4%22%3A%20%221.2.3.4%22%7D/mask_prefix/%7B%22mask%22%3A%20%2224%22%7D`;
+
+    service.updateNetworkInterface('eth0', 'ipv4', '1.1.1.1', '24', {});
     expect(httpMock.post).toHaveBeenCalledWith(
-      expectedUrl,
+      expect.stringContaining('eth0'),
       {},
       expect.objectContaining({ csrf: true })
     );
-  });
 
-  it('should get notification channels with page size', () => {
-    httpMock.get.mockReturnValue(of({}));
-    service.getNotificationChannels(10);
-    expect(httpMock.get).toHaveBeenCalledWith(`${URLS.GET_NOTIFICATION_CHANNELS_URL}?page_size=10`);
+    service.updateAAAESServer('server1', {});
+    expect(httpMock.post).toHaveBeenCalledWith(
+      expect.stringContaining('server1'),
+      {},
+      expect.objectContaining({ csrf: true })
+    );
+
+    service.deleteNotificationChannel(1);
+    expect(httpMock.post).toHaveBeenCalledWith(`${URLS.DELETE_NOTIFICATION_CHANNEL_URL}/1`, {});
+
+    service.renameUserRole(1, {});
+    expect(httpMock.post).toHaveBeenCalledWith(`${URLS.RENAME_USER_ROLE_URL}/1`, {}, expect.any(Object));
+
+    service.updateUser('admin', {});
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('admin'), {}, expect.any(Object));
+
+    service.upload('param=1', { data: 1 });
+    expect(httpMock.post).toHaveBeenCalledWith(`${URLS.COMMON_UPLOAD_URL}?param=1`, { data: 1 }, { isFormData: true });
+
+    service.upload(null, { data: 1 });
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.COMMON_UPLOAD_URL, { data: 1 }, { isFormData: true });
   });
 
-  it('should delete system backup file', () => {
+  it('should test DELETE methods', () => {
     httpMock.delete.mockReturnValue(of({}));
-    service.deleteSystemBackupFile('backup.tar.gz');
-    expect(httpMock.delete).toHaveBeenCalledWith(`${URLS.DELETE_SYSTEM_BACKUP_FILE_URL}?filename=backup.tar.gz`);
+
+    service.deleteSystemBackupFile('test.bak');
+    expect(httpMock.delete).toHaveBeenCalledWith(`${URLS.DELETE_SYSTEM_BACKUP_FILE_URL}?filename=test.bak`);
+
+    service.deleteBackupSchedule();
+    expect(httpMock.delete).toHaveBeenCalledWith(URLS.DELETE_SYSTEM_SCHEDULED_BACKUP_URL);
   });
 });
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/utils.service.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/utils.service.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/utils.service.spec.ts	(working copy)
@@ -1,6 +1,11 @@
 import { TestBed } from '@angular/core/testing';
 import { UtilsService } from './utils.service';
+import * as fileSaver from 'file-saver';
 
+jest.mock('file-saver', () => ({
+  saveAs: jest.fn()
+}));
+
 describe('UtilsService', () => {
   let service: UtilsService;
 
@@ -22,6 +27,14 @@
       expect(service.formatKey('cpu_usage')).toBe('CPU usage');
       expect(service.formatKey('hw_version')).toBe('Hardware version');
       expect(service.formatKey('adc_num')).toBe('Number of licensed ADC devices');
+      expect(service.formatKey('vpn_num')).toBe('Number of licensed VPN devices');
+      expect(service.formatKey('vpn_lic_sess')).toBe('Number of licensed VPN sessions');
+      expect(service.formatKey('waf_num')).toBe('Number of licensed WAF devices');
+      expect(service.formatKey('compressionhard')).toBe('Compression Hardware');
+      expect(service.formatKey('curtime')).toBe('Current Time');
+      expect(service.formatKey('boottime')).toBe('Boot Time');
+      expect(service.formatKey('Blddate')).toBe('Platform Build Date');
+      expect(service.formatKey('sslhard')).toBe('SSL Hardware');
     });
 
     it('should handle camelCase', () => {
@@ -31,6 +44,7 @@
     it('should capitalize forced uppercase words', () => {
       expect(service.formatKey('vpn_status')).toBe('VPN status');
       expect(service.formatKey('apv_config')).toBe('APV config');
+      expect(service.formatKey('asf_settings')).toBe('ASF settings');
     });
   });
 
@@ -59,16 +73,54 @@
       const metrics = ['connections'];
       const rawData = [
         { metric: 'connections', service_name: 'S1', device_name: 'D1', value: 100 },
-        { metric: 'connections', service_name: 'S2', device_name: 'D2', value: 50 },
+        { metric: 'connections', service_name: 'S2', value: 50 },
         { metric: 'other', value: 10 }
       ];
 
       const result = service.formatChartData(metrics, rawData);
       expect(result.connections).toHaveLength(2);
       expect(result.connections[0].name).toBe('S1 (D1)');
-      expect(result.connections[0].value).toBe(100);
+      expect(result.connections[1].name).toBe('S2 (N/A)');
     });
 
+    it('should format hits and requests metrics correctly', () => {
+      const metrics = ['hits', 'requests'];
+      const rawData = [
+        { metric: 'hits', service_name: 'H1', value: 200 },
+        { metric: 'requests', service_name: 'R1', value: 300 }
+      ];
+      const result = service.formatChartData(metrics, rawData);
+      expect(result.hits).toHaveLength(2);
+      expect(result.hits[0].name).toBe('R1 (N/A)');
+      expect(result.hits[0].value).toBe(300);
+      expect(result.hits[1].name).toBe('H1 (N/A)');
+    });
+
+    it('should format active_sessions metrics correctly', () => {
+      const metrics = ['active_sessions'];
+      const rawData = [
+        { metric: 'active_sessions', vsite_name: 'V1', value: 50 }
+      ];
+      const result = service.formatChartData(metrics, rawData);
+      expect(result.active_sessions[0].name).toBe('V1 (N/A)');
+    });
+
+    it('should format cpu metrics correctly', () => {
+      const metrics = ['cpu'];
+      const rawData = [
+        { metric: 'cpu', device_name: 'D1', value: 20 }
+      ];
+      const result = service.formatChartData(metrics, rawData);
+      expect(result.cpu[0].name).toBe('D1');
+    });
+
+    it('should format memory metrics correctly', () => {
+      const metrics = ['memory'];
+      const rawData = [{ metric: 'memory', device_name: 'D1', value: 30 }];
+      const result = service.formatChartData(metrics, rawData);
+      expect(result.memory[0].name).toBe('D1');
+    });
+
     it('should format network metrics correctly with conversion', () => {
       const metrics = ['network'];
       const rawData = [
@@ -80,8 +132,56 @@
       expect(result.network.inbound[0]).toBe(1); // 1 MB
       expect(result.network.outbound[0]).toBe(2); // 2 MB
     });
+
+    it('should format device_network metrics correctly', () => {
+      const metrics = ['device_network'];
+      const rawData = [
+        { metric: 'network', device_name: 'D1', total_in: 1048576, total_out: 2097152 }
+      ];
+      const result = service.formatChartData(metrics, rawData);
+      expect(result.network.names).toEqual(['D1']);
+      expect(result.network.inbound[0]).toBe(1);
+    });
+
+    it('should format client_network metrics correctly', () => {
+      const metrics = ['client_network'];
+      const rawData = [
+        { metric: 'client_network', vsite_name: 'V1', received: 1048576, sent: 0 }
+      ];
+      const result = service.formatChartData(metrics, rawData);
+      expect(result.client_network.names).toEqual(['V1 (N/A)']);
+    });
+
+    it('should format server_network metrics correctly', () => {
+      const metrics = ['server_network'];
+      const rawData = [
+        { metric: 'server_network', vsite_name: 'V1', received: 1048576, sent: 0 }
+      ];
+      const result = service.formatChartData(metrics, rawData);
+      expect(result.server_network.names).toEqual(['V1 (N/A)']);
+    });
   });
 
+  describe('getProtocolRepo', () => {
+    it('should return protocol mapping', () => {
+      const repo = service.getProtocolRepo();
+      expect(repo["0"]).toBe('tcp');
+      expect(repo["17"]).toBe('rdp');
+    });
+  });
+
+  describe('transformConnectedDevicesMetrics', () => {
+    it('should transform device data for chart', () => {
+      const data: [string, { connected: number, disconnected: number }][] = [
+        ['APV', { connected: 5, disconnected: 2 }]
+      ];
+      const result = service.transformConnectedDevicesMetrics(data);
+      expect(result.yAxisData).toEqual(['APV']);
+      expect(result.connectedData).toEqual([5]);
+      expect(result.disconnectedData).toEqual([2]);
+    });
+  });
+
   describe('transformTotalConnectedDevicesMetrics', () => {
     it('should count connected and disconnected devices correctly', () => {
       const devices = [
@@ -103,6 +203,68 @@
       const result = service.transformTotalConnectedDevicesMetrics([]);
       expect(result.total.connected).toBe(0);
       expect(result.types.size).toBe(0);
+
+      const resultNull = service.transformTotalConnectedDevicesMetrics(null);
+      expect(resultNull.total.connected).toBe(0);
     });
   });
+
+  describe('convertBytesForChart', () => {
+    it('should format bytes values in names', () => {
+      const data = [{ name: 'Test', value: 1024 }];
+      const result = service.convertBytesForChart(data);
+      expect(result[0].name).toContain('1.00 KB');
+      expect(result[0].value).toBe(1024);
+    });
+  });
+
+  describe('downloadTextFile', () => {
+    it('should call saveAs with blob', () => {
+      const data = { a: 1 };
+      service.downloadTextFile(data, 'test.json');
+      expect(fileSaver.saveAs).toHaveBeenCalled();
+
+      service.downloadTextFile('simple text', 'test.txt');
+      expect(fileSaver.saveAs).toHaveBeenCalled();
+    });
+  });
+
+  describe('formatResponseTimePercentiles', () => {
+    it('should format elasticsearch buckets to chart format', () => {
+      const buckets = [
+        {
+          key_as_string: '2023-01-01T00:00:00.000Z',
+          resp_time: { values: { "50.0": 10.123, "95.0": 20.456, "99.0": 30.789 } }
+        }
+      ];
+      const result = service.formatResponseTimePercentiles(buckets, 'resp_time');
+      const timestamp = new Date('2023-01-01T00:00:00.000Z').getTime();
+      expect(result.p50_formatted[0]).toEqual([timestamp, 10.12]);
+      expect(result.p95_formatted[0]).toEqual([timestamp, 20.46]);
+      expect(result.p99_formatted[0]).toEqual([timestamp, 30.79]);
+    });
+
+    it('should handle missing values', () => {
+      const buckets = [{ key_as_string: '2023-01-01T00:00:00.000Z', resp_time: { values: {} } }];
+      const result = service.formatResponseTimePercentiles(buckets, 'resp_time');
+      expect(result.p50_formatted[0][1]).toBe(0);
+    });
+  });
+
+  describe('getAssociatedDevices', () => {
+    it('should group associated device types', () => {
+      const grouped = {
+        'APV': [{ id: 1 }],
+        'vAPV': [{ id: 2 }],
+        'AG': [{ id: 3 }],
+        'vxAG': [{ id: 4 }],
+        'ASF': [{ id: 5 }],
+        'vASF': [{ id: 6 }]
+      };
+      expect(service.getAssociatedDevices('APV', grouped)).toHaveLength(2);
+      expect(service.getAssociatedDevices('AG', grouped)).toHaveLength(2);
+      expect(service.getAssociatedDevices('ASF', grouped)).toHaveLength(2);
+      expect(service.getAssociatedDevices('Other', grouped)).toHaveLength(0);
+    });
+  });
 });
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/volume-license.service.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/volume-license.service.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/volume-license.service.spec.ts	(working copy)
@@ -1,8 +1,8 @@
 import { TestBed } from '@angular/core/testing';
-import { of } from 'rxjs';
 import { VolumeLicenseService } from './volume-license.service';
 import { HttpService } from './http.service';
 import { URLS } from '../constants/api_urls';
+import { of } from 'rxjs';
 
 describe('VolumeLicenseService', () => {
   let service: VolumeLicenseService;
@@ -13,12 +13,14 @@
       get: jest.fn(),
       post: jest.fn()
     };
+
     TestBed.configureTestingModule({
       providers: [
         VolumeLicenseService,
         { provide: HttpService, useValue: httpMock }
       ]
     });
+
     service = TestBed.inject(VolumeLicenseService);
   });
 
@@ -26,33 +28,60 @@
     expect(service).toBeTruthy();
   });
 
-  it('should get volume licenses', () => {
-    httpMock.get.mockReturnValue(of([]));
+  it('should test GET methods', () => {
+    httpMock.get.mockReturnValue(of({}));
     service.getVolumeLicenses();
     expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_VOLUME_LICENSES_URL);
+
+    service.getManagedDevices();
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_VL_DEVICES_URL);
+
+    service.getVLDiscoverDevices();
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_VL_DISCOVER_DEVICES_URL);
+
+    service.getVLDiscoverDevicesMode();
+    expect(httpMock.get).toHaveBeenCalledWith(URLS.GET_VL_DISCOVER_DEVICES_MODE_URL);
   });
 
-  it('should add volume license', () => {
+  it('should test POST methods', () => {
     httpMock.post.mockReturnValue(of({}));
-    const payload = { key: 'test' };
+    const payload = { test: 1 };
+    const options = { csrf: true, isFormData: true, csrfInFormData: true };
+
     service.addVolumeLicense(payload);
-    expect(httpMock.post).toHaveBeenCalledWith(
-      URLS.ADD_VOLUME_LICENSE_URL,
-      payload,
-      expect.objectContaining({ csrf: true, isFormData: true })
-    );
-  });
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.ADD_VOLUME_LICENSE_URL, payload, options);
 
-  it('should update VL managed device bandwidth', () => {
-    httpMock.post.mockReturnValue(of({}));
-    const deviceId = '123';
-    const payload = { bw: 100 };
-    service.updateVLManagedDeviceBandwidth(deviceId, payload);
-    const expectedUrl = `${URLS.UPDATE_VL_DEVICE_BANDWIDTH_URL}/%22123%22`;
-    expect(httpMock.post).toHaveBeenCalledWith(
-      expectedUrl,
-      payload,
-      expect.objectContaining({ csrf: true })
-    );
+    service.deleteVolumeLicense(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DELETE_VOLUME_LICENSE_URL, payload, options);
+
+    service.activateDeviceVolumeLicense(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.ACTIVATE_DEVICE_VL_URL, payload, options);
+
+    service.deactivateDeviceVolumeLicense(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DEACTIVATE_DEVICE_VL_URL, payload, options);
+
+    service.removeVLManagedDevice(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.REMOVE_VL_MANAGED_DEVICE_URL, payload, options);
+
+    service.importVLManagedDevice(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.IMPORT_DEVICE_TO_VL_MANAGED_DEVICES_URL, payload, options);
+
+    service.addVLManagedDevice(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.ADD_VL_MANAGED_DEVICE_URL, payload, options);
+
+    service.updateVLManagedDeviceBandwidth('dev1', payload);
+    expect(httpMock.post).toHaveBeenCalledWith(expect.stringContaining('dev1'), payload, options);
+
+    service.getVLManagedDeviceVersion(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.GET_VL_DEVICE_VERSION_URL, payload, options);
+
+    service.updateVLDiscoverDeviceMode(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.UPDATE_VL_DISCOVER_DEVICES_URL, payload, options);
+
+    service.deleteVLDiscoverDevice(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.DELETE_VL_DISCOVER_DEVICE_URL, payload, options);
+
+    service.activateVLDiscoverDevice(payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.ACTIVATE_VL_DISCOVER_DEVICE_URL, payload, options);
   });
 });
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/vpn.service.spec.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/vpn.service.spec.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/vpn.service.spec.ts	(working copy)
@@ -1,8 +1,8 @@
 import { TestBed } from '@angular/core/testing';
-import { of } from 'rxjs';
 import { VpnService } from './vpn.service';
 import { HttpService } from './http.service';
 import { URLS } from '../constants/api_urls';
+import { of } from 'rxjs';
 
 describe('VpnService', () => {
   let service: VpnService;
@@ -10,14 +10,16 @@
 
   beforeEach(() => {
     httpMock = {
-      post: jest.fn().mockReturnValue(of({}))
+      post: jest.fn()
     };
+
     TestBed.configureTestingModule({
       providers: [
         VpnService,
         { provide: HttpService, useValue: httpMock }
       ]
     });
+
     service = TestBed.inject(VpnService);
   });
 
@@ -25,45 +27,22 @@
     expect(service).toBeTruthy();
   });
 
-  it('should get VPN services', () => {
-    const aGName = 'ag1';
-    const payload = { test: 'data' };
-    service.getVPNServices(aGName, payload);
+  it('should test POST methods', () => {
+    httpMock.post.mockReturnValue(of({}));
+    const name = 'dev1';
+    const payload = { test: 1 };
+    const headers = [{ name: 'Cm-Data', value: name }, { name: 'Cm-Type', value: 'device' }];
 
-    expect(httpMock.post).toHaveBeenCalledWith(
-      URLS.GET_VPN_SERVICES_LIST_URL,
-      payload,
-      expect.objectContaining({ csrf: true, isFormData: true }),
-      expect.arrayContaining([
-        { name: 'Cm-Data', value: aGName },
-        { name: 'Cm-Type', value: 'device' }
-      ])
-    );
-  });
+    service.getVPNServices(name, payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.GET_VPN_SERVICES_LIST_URL, payload, expect.any(Object), headers);
 
-  it('should execute AG CLI command', () => {
-    const aGName = 'ag1';
-    const payload = { cmd: 'show version' };
-    service.executeAGCLICommand(aGName, payload);
+    service.executeAGCLICommand(name, payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.GET_VPN_SERVICES_LIST_URL, payload, expect.any(Object), headers);
 
-    expect(httpMock.post).toHaveBeenCalledWith(
-      URLS.GET_VPN_SERVICES_LIST_URL, // As per current implementation in service
-      payload,
-      expect.anything(),
-      expect.anything()
-    );
-  });
+    service.getHardwareIds(name, payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.GET_VPN_SERVICES_LIST_URL, payload, expect.any(Object), headers);
 
-  it('should batch AG CLI command', () => {
-    const aGName = 'ag1';
-    const payload = { cmds: [] };
-    service.batchAGCLICommand(aGName, payload);
-
-    expect(httpMock.post).toHaveBeenCalledWith(
-      URLS.BATCH_VPN_CLI_URL,
-      payload,
-      expect.objectContaining({ csrf: true }),
-      expect.arrayContaining([{ name: 'Cm-Data', value: aGName }])
-    );
+    service.batchAGCLICommand(name, payload);
+    expect(httpMock.post).toHaveBeenCalledWith(URLS.BATCH_VPN_CLI_URL, payload, expect.any(Object), headers);
   });
 });
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/testing/mock-device.facade.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/testing/mock-device.facade.ts	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/testing/mock-device.facade.ts	(working copy)
@@ -36,14 +36,25 @@
     getVolumeLicenses = jest.fn().mockReturnValue(of([]));
     addVolumeLicense = jest.fn().mockReturnValue(of([true, '']));
     deleteVolumeLicense = jest.fn().mockReturnValue(of([true, '']));
-    getVolumeLicenseManagedDevices = jest.fn().mockReturnValue(of([]));
-    getVolumeLicenseDiscoveredDevices = jest.fn().mockReturnValue(of([]));
+    getManagedDevices = jest.fn().mockReturnValue(of([]));
+    activateDeviceVolumeLicense = jest.fn().mockReturnValue(of([true, '']));
+    deactivateDeviceVolumeLicense = jest.fn().mockReturnValue(of([true, '']));
+    removeVLManagedDevice = jest.fn().mockReturnValue(of([true, '']));
+    importVLManagedDevice = jest.fn().mockReturnValue(of([true, '']));
+    addVLManagedDevice = jest.fn().mockReturnValue(of([true, '']));
+    updateVLManagedDeviceBandwidth = jest.fn().mockReturnValue(of([true, '']));
+    getVLManagedDeviceVersion = jest.fn().mockReturnValue(of({}));
 
-    // Other
-    getVpnUserGroups = jest.fn().mockReturnValue(of([]));
-    getVpnUsers = jest.fn().mockReturnValue(of([]));
-    getVpnPolicies = jest.fn().mockReturnValue(of([]));
-    getVpnResources = jest.fn().mockReturnValue(of([]));
+    // Other Methods
+    deleteDevice = jest.fn().mockReturnValue(of([true, '']));
+    saveDeviceConfig = jest.fn().mockReturnValue(of([true, '']));
+    addDevice = jest.fn().mockReturnValue(of([true, '']));
+    updateDeviceLicense = jest.fn().mockReturnValue(of([true, '']));
+    getDeviceBuildInfo = jest.fn().mockReturnValue(of([]));
+    updateDeviceMonitoringState = jest.fn().mockReturnValue(of([true, '']));
+    updateDevice = jest.fn().mockReturnValue(of([true, '']));
+    addDeviceGroup = jest.fn().mockReturnValue(of([true, '']));
+    deleteDeviceGroup = jest.fn().mockReturnValue(of([true, '']));
 
     // Monitoring
     getAPVRSMonitoringMetrics = jest.fn().mockReturnValue(of({}));
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/an_opensearch.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/an_opensearch.py	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/an_opensearch.py	(working copy)
@@ -7,29 +7,63 @@
 import yaml
 import os
 from hive.utils import andebug
+import ssl
 
 CONFIG_YML_PATH = '/etc/opensearch/opensearch-security/config.yml'
 
 HOST = '127.0.0.1'
 PORT = 9200
-OPENSEARCH_PASSWORD = os.environ.get('OPENSEARCH_PASSWORD', 'admin')
-AUTH = ('admin', OPENSEARCH_PASSWORD)
 
 
-def opensearch_proxy(request):
+
+
+def _get_sso_token_ttl_minutes():
+    # Keep SSO token TTL aligned with AMP session timeout by default.
+    raw = os.environ.get('AMP_SSO_TOKEN_TTL_MINUTES') or os.environ.get('AMP_SESSION_IDLE_TIMEOUT_MINUTES') or '120'
+    try:
+        minutes = int(raw)
+    except (TypeError, ValueError):
+        minutes = 120
+
+    if minutes < 5:
+        minutes = 5
+    elif minutes > 1440:
+        minutes = 1440
+    return minutes
+
+
+def opensearch_proxy(request, path=None):
+    # Determine the password file path from env or default secret location
+    password_file = os.environ.get('OPENSEARCH_PASSWORD_FILE', '/run/secrets/opensearch_initial_admin_password')
+    
+    try:
+        with open(password_file, 'r') as f:
+            password = f.read().strip()
+    except FileNotFoundError:
+        password = os.environ.get('OPENSEARCH_PASSWORD', 'admin')
+
+    username = os.environ.get('OPENSEARCH_USER', 'admin')
+    auth = (username, password)
+
+    # Create SSL context to disable verification for self-signed certs
+    ssl_context = ssl.create_default_context()
+    ssl_context.check_hostname = False
+    ssl_context.verify_mode = ssl.CERT_NONE
+
     client = OpenSearch(
         hosts=[{'host': HOST, 'port': PORT}],
-        http_auth=AUTH,
+        http_auth=auth,
         use_ssl=True,
         verify_certs=False,
         ssl_assert_hostname=False,
         ssl_show_warn=False,
+        ssl_context=ssl_context
     )
     res = client.search(index='acm-*', body=json.loads(request.body), request_timeout=300)
     return HttpResponse(json.dumps(res), content_type='application/json')
 
 
-def get_opensearch_sso_token(request):
+def _generate_sso_token(request, default_roles='jwt_users', key_id=None):
     # Load the signing key from Docker secret
     JWT_SECRET_PATH = '/run/secrets/opensearch_jwt_secret'
     
@@ -42,11 +76,38 @@
 
     # Use timezone-aware UTC datetime objects
     now = datetime.datetime.now(datetime.timezone.utc)
+    token_ttl_minutes = _get_sso_token_ttl_minutes()
 
-    # ToDo: Implement RBAC
-    payload = {'sub': 'admin', 'exp': now + datetime.timedelta(hours=1), 'iat': now, }
+    # Use logged-in username when available; fallback to admin for legacy flows.
+    username = getattr(getattr(request, 'user', None), 'username', None) or 'admin'
 
-    # Generate token
-    token = jwt.encode(payload, secret_key, algorithm='HS256')
+    # JWT settings should mirror opensearch security config (issuer/roles/subject keys).
+    issuer = os.environ.get('OPENSEARCH_JWT_ISSUER', 'amp.com')
+    roles = os.environ.get('OPENSEARCH_JWT_ROLES', default_roles).split(',')
+    roles = [role.strip() for role in roles if role.strip()]
 
+    # ToDo: Implement RBAC for dynamic role assignment.
+    payload = {
+        'sub': username,
+        'iss': issuer,
+        'roles': roles,
+        'exp': now + datetime.timedelta(minutes=token_ttl_minutes),
+        'iat': now,
+    }
+
+    jwt_headers = {}
+    if key_id:
+        jwt_headers['kid'] = key_id
+
+    return jwt.encode(payload, secret_key, algorithm='HS256', headers=jwt_headers if jwt_headers else None)
+
+
+def get_opensearch_sso_token(request):
+    token = _generate_sso_token(request, default_roles='jwt_users')
     return JsonResponse({'token': token})
+
+
+def get_grafana_sso_token(request):
+    # Grafana SSO uses HS256 JWT validated against the mounted JWKS.
+    token = _generate_sso_token(request, default_roles='jwt_users', key_id='amp-grafana-hs256')
+    return JsonResponse({'token': token})
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/controller/utils.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/controller/utils.py	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/controller/utils.py	(working copy)
@@ -10,6 +10,8 @@
     try:
         if request.method == 'GET':
             status_data = get_observability_services_status()
+            if isinstance(status_data, HttpResponse):
+                return status_data
             return JsonResponse(status_data, safe=False)
         else:
             return HttpResponse(json.dumps({
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/services/utils.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/services/utils.py	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/services/utils.py	(working copy)
@@ -12,9 +12,15 @@
 
 # Service health check configuration
 # amp-core runs on host network mode, so services are accessible via localhost
-# OpenSearch password is read from environment variable
-OPENSEARCH_PASSWORD = os.environ.get('OPENSEARCH_PASSWORD', 'admin')
+# Determine the password file path from env or default secret location
+password_file = os.environ.get('OPENSEARCH_PASSWORD_FILE', '/run/secrets/opensearch_initial_admin_password')
 
+try:
+    with open(password_file, 'r') as f:
+        OPENSEARCH_PASSWORD = f.read().strip()
+except FileNotFoundError:
+    OPENSEARCH_PASSWORD = os.environ.get('OPENSEARCH_PASSWORD', 'admin')
+
 SERVICE_HEALTH_CONFIG = {
     'opensearch': {
         'host': '127.0.0.1',
@@ -155,26 +161,25 @@
 
 
 def get_observability_services_status():
+    services_list = [
+        {'value': 'opensearch', 'label': 'Search & Analytics Engine'},
+        {'value': 'opensearch-dashboards', 'label': 'Logs Dashboards'},
+        {'value': 'logstash', 'label': 'Syslog Collector'},
+        {'value': 'telegraf', 'label': 'Metrics Collector'},
+    ]
     try:
-        services_list = [
-            {'value': 'opensearch', 'label': 'Search & Analytics Engine'},
-            {'value': 'opensearch-dashboards', 'label': 'Logs Dashboards'},
-            {'value': 'logstash', 'label': 'Syslog Collector'},
-            {'value': 'telegraf', 'label': 'Metrics Collector'},
-        ]
         result = []
         for service in services_list:
             is_running = check_service_health(service['value'])
             result.append({'value': is_running, 'label': service['label'], 'service': service['value']})
         return result
     except Exception as e:
-        oper_log('error', 'system', "Exception while fetching observability services status.")
-        message = str(e).replace("'", "")
-        message = 'Error while fetching observability services status, details: {}'.format(message)
-        return HttpResponse(json.dumps({
-            "message": "while fetching observability services status",
-            "details": "{}".format(message)
-        }), content_type="application/json", status=500)
+        oper_log('error', 'system', "Exception while fetching observability services status. details: {}".format(str(e)))
+        # Fallback to a deterministic payload to avoid bubbling 500 to the UI.
+        result = []
+        for service in services_list:
+            result.append({'value': False, 'label': service['label'], 'service': service['value']})
+        return result
 
 
 def perform_observability_services_restart(service_name):
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/session.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/session.py	(revision 2944)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/session.py	(working copy)
@@ -1,4 +1,5 @@
 import base64
+import os
 import time
 import uuid
 import traceback
@@ -22,6 +23,25 @@
 from hive.preference import *
 from hive.utils import HiveEnvironment, andebug, _thread_locals
 from . import auth
+
+
+def _get_session_idle_timeout_seconds():
+    # Configurable idle timeout with safe fallback.
+    raw = os.environ.get('AMP_SESSION_IDLE_TIMEOUT_MINUTES', '120')
+    try:
+        minutes = int(raw)
+    except (TypeError, ValueError):
+        minutes = 120
+
+    # Clamp to avoid accidental insecure/extreme values.
+    if minutes < 5:
+        minutes = 5
+    elif minutes > 1440:
+        minutes = 1440
+    return minutes * 60
+
+
+SESSION_IDLE_TIMEOUT_SECONDS = _get_session_idle_timeout_seconds()
 
 
 class ANSession(object):
@@ -488,7 +508,7 @@
                 else:
                     timestamp = sess.timestamp
                     difference = time.time() - timestamp
-                    if difference > 15 * 60 and timestamp != -1:
+                    if difference > SESSION_IDLE_TIMEOUT_SECONDS and timestamp != -1:
                         sess.logout()
                         _thread_locals.session = None
                         sess = None
