This is an automated email from the ASF dual-hosted git repository.

wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git


The following commit(s) were added to refs/heads/main by this push:
     new 26aa105  ui: preserve URL ?instance= on first service auto-fill + 
don't flag empty widgets as errors
26aa105 is described below

commit 26aa10558aafd1d1d24eae26f4732c19631b0a1c
Author: Wu Sheng <[email protected]>
AuthorDate: Sun May 17 17:50:57 2026 +0800

    ui: preserve URL ?instance= on first service auto-fill + don't flag empty 
widgets as errors
    
    Two fixes:
    
    1. /layer/general/instance?instance=X used to land with ?instance=
       stripped — the service auto-pick (URL had no ?service=) called
       `setSelected(firstServiceId)`, which unconditionally cleared
       the narrower picks. Now `setSelected` only drops `?instance=` /
       `?endpoint=` when it's REPLACING an existing service (current
       was already truthy). First-time auto-fill (current === null)
       preserves whatever the URL carried — operators who deep-link
       to a specific instance now keep their pick after the auto-
       service settles.
    
       This doesn't reopen the mesh_dp "sticky pick" issue: that was
       about REPLACING a service the operator already had set; this
       change only affects the null-current path.
    
    2. The per-widget event glyph went red (✕) when the BFF returned
       `error: "no data"` — which is a non-fatal sentinel meaning the
       widget's MQE has nothing to plot for this entity right now, not
       an actual backend error. Now treated as `info` so the ticker
       doesn't flash red for ordinary empty cells. Real errors (OAP
       unreachable, MQE parse failures, etc.) keep the ✕ kind.
---
 apps/ui/src/layer/useSelectedService.ts                 | 17 ++++++++++++-----
 .../render/layer-dashboard/useLayerPageOrchestrator.ts  |  7 ++++++-
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/apps/ui/src/layer/useSelectedService.ts 
b/apps/ui/src/layer/useSelectedService.ts
index a8d84b5..d090144 100644
--- a/apps/ui/src/layer/useSelectedService.ts
+++ b/apps/ui/src/layer/useSelectedService.ts
@@ -42,11 +42,18 @@ export function useSelectedService() {
     if (id === current) return;
     if (id) next.service = id;
     else delete next.service;
-    // Instance / endpoint choices are derived from the selected service.
-    // When the service changes, drop the narrower entity so each
-    // dashboard can auto-pick from the new service's own list.
-    delete next.instance;
-    delete next.endpoint;
+    // Drop the narrower picks (?instance=, ?endpoint=) ONLY when
+    // we're replacing an existing service. First-time auto-fill
+    // (`current === null` — URL arrived with no ?service= at all)
+    // preserves any `?instance=` / `?endpoint=` URL hints the
+    // operator typed or that a sharable link carried, because
+    // those were the explicit intent. Without this, hitting
+    // `/layer/general/instance?instance=X` dropped X immediately
+    // and the auto-picked service's first instance won the race.
+    if (current !== null) {
+      delete next.instance;
+      delete next.endpoint;
+    }
     // `replace` instead of `push` — switching services shouldn't bloat
     // the browser back stack with N entries.
     void router.replace({ path: route.path, query: next });
diff --git a/apps/ui/src/render/layer-dashboard/useLayerPageOrchestrator.ts 
b/apps/ui/src/render/layer-dashboard/useLayerPageOrchestrator.ts
index 89a00f4..146c96a 100644
--- a/apps/ui/src/render/layer-dashboard/useLayerPageOrchestrator.ts
+++ b/apps/ui/src/render/layer-dashboard/useLayerPageOrchestrator.ts
@@ -268,7 +268,12 @@ export function useLayerPageOrchestrator(refs: 
OrchestratorRefs): {
         const label = titleById.get(r.id) ?? r.id;
         let kind: 'ok' | 'err' | 'info' = 'info';
         let detail = 'no data';
-        if (r.error) {
+        // The BFF uses `error: "no data"` as a non-fatal sentinel
+        // when a widget's MQE returned nothing (metric not reporting
+        // for this entity right now). Treat that as `info`, not
+        // `err`, so the ticker doesn't light up red for ordinary
+        // empty cells. Real backend errors keep the ✕ kind.
+        if (r.error && r.error !== 'no data') {
           kind = 'err';
           detail = `error: ${r.error}`;
         } else if (r.value != null) {

Reply via email to