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 ab74538  layer: separate layer-aggregate vs selected-service KPIs in 
the header
ab74538 is described below

commit ab74538ee2cb7555ddb9f354d0c6089e96e1d0e9
Author: Wu Sheng <[email protected]>
AuthorDate: Tue May 12 21:11:30 2026 +0800

    layer: separate layer-aggregate vs selected-service KPIs in the header
    
    Two distinct KPI strips in the General header now:
    
    - Row 1 (top-right): LAYER aggregates — sum/avg of every column from
      general.json/metrics.columns, with per-KPI sparkline. cpm shows sum
      across all services, p99/sla/err show avg. The whole-layer summary.
    
    - Row 2 (Switch row, right): SELECTED-service KPIs — the picked
      service's individual values. Compact inline format (label · value),
      no sparkline. The service-scope dashboard below carries trend
      charts, so the header strip stays a glance read.
    
    Previously these were collapsed into one strip and the user couldn't
    tell aggregate from selected. Same colors per metric in both strips,
    different layout signals which is which.
---
 apps/ui/src/views/layer/LayerShell.vue | 117 +++++++++++++++++++++++++--------
 1 file changed, 90 insertions(+), 27 deletions(-)

diff --git a/apps/ui/src/views/layer/LayerShell.vue 
b/apps/ui/src/views/layer/LayerShell.vue
index 0c6cda0..8ebc5fa 100644
--- a/apps/ui/src/views/layer/LayerShell.vue
+++ b/apps/ui/src/views/layer/LayerShell.vue
@@ -129,12 +129,12 @@ interface HeaderKpi {
   spark?: Array<number | null>;
 }
 /**
- * Header KPIs are layer-wide aggregates — sum or avg across the topN
- * services per the operator's setup config. The Switch button below
- * carries the *selected service* context for the widget grid; the
- * header summary is the layer rollup.
+ * Layer-wide aggregate KPIs — sum or avg across the topN services per
+ * the column's `aggregation` field in the JSON template. Rendered
+ * top-right of the General header. Sparkline = the aggregated series
+ * for the same metric.
  */
-const headerKpis = computed<HeaderKpi[]>(() => {
+const layerKpis = computed<HeaderKpi[]>(() => {
   const L = layer.value;
   if (!L) return [];
   const c = cfg.value;
@@ -154,12 +154,40 @@ const headerKpis = computed<HeaderKpi[]>(() => {
   return out;
 });
 
+/**
+ * Selected-service KPIs — the per-row values for the currently picked
+ * service. Rendered on the Switch row beneath the layer aggregates.
+ * No sparkline here (the per-service spark series is the service-scope
+ * dashboard's job).
+ */
+const serviceKpis = computed<HeaderKpi[]>(() => {
+  const L = layer.value;
+  if (!L) return [];
+  const c = cfg.value;
+  if (!c) return [];
+  const row = selectedRow.value;
+  if (!row) return [];
+  const out: HeaderKpi[] = [];
+  for (const col of c.landing.columns.slice(0, 5)) {
+    const m = metricMeta(col.metric);
+    out.push({
+      label: col.label || m.label,
+      value: row.metrics[col.metric] ?? null,
+      unit: col.unit || m.unit,
+      color: colorForMetric(col.metric),
+    });
+  }
+  return out;
+});
+
 </script>
 
 <template>
   <div class="layer-shell">
     <header v-if="layer" class="sw-card layer-head">
-      <!-- Row 1: layer identity. -->
+      <!-- Row 1: layer identity (left) + LAYER aggregate KPI strip
+           (right). The aggregates use sum/avg per the JSON columns'
+           aggregation field — sum cpm across services, avg p99 etc. -->
       <div class="layer-id-row">
         <div class="icon-tile" :style="{ background: layer.color }">{{ 
initials }}</div>
         <div class="identity-text">
@@ -175,23 +203,8 @@ const headerKpis = computed<HeaderKpi[]>(() => {
             </span>
           </div>
         </div>
-      </div>
-
-      <!-- Row 2: Switch button + selected service name + KPI strip.
-           Merged into the same card as the layer header so there's no
-           duplicate "Selected service" zone elsewhere. -->
-      <div v-if="sampledServices.length > 0" class="service-row">
-        <button
-          class="sw-btn switch"
-          type="button"
-          :class="{ open: pickerOpen }"
-          @click="togglePicker"
-        >
-          <span class="caret">▾</span>
-          <span class="svc-name">{{ selectedName }}</span>
-        </button>
-        <div class="kpi-strip">
-          <div v-for="(k, i) in headerKpis" :key="i" class="kpi">
+        <div class="kpi-strip layer-kpis">
+          <div v-for="(k, i) in layerKpis" :key="i" class="kpi">
             <div class="kpi-label">{{ k.label }}</div>
             <div class="kpi-value" :style="{ color: k.color }">
               <span :class="{ muted: k.value == null }">{{ fmtMetric(k.value) 
}}</span>
@@ -210,6 +223,31 @@ const headerKpis = computed<HeaderKpi[]>(() => {
           </div>
         </div>
       </div>
+
+      <!-- Row 2: Switch button + selected-service KPIs. Distinct from
+           the layer aggregates above — these are the picked service's
+           per-row metric values (no sparklines; the service-scope
+           dashboard below carries the trend charts). -->
+      <div v-if="sampledServices.length > 0" class="service-row">
+        <button
+          class="sw-btn switch"
+          type="button"
+          :class="{ open: pickerOpen }"
+          @click="togglePicker"
+        >
+          <span class="caret">▾</span>
+          <span class="svc-name">{{ selectedName }}</span>
+        </button>
+        <div class="kpi-strip service-kpis">
+          <div v-for="(k, i) in serviceKpis" :key="i" class="kpi compact">
+            <span class="kpi-label inline">{{ k.label }}</span>
+            <span class="kpi-value inline" :style="{ color: k.color }">
+              <span :class="{ muted: k.value == null }">{{ fmtMetric(k.value) 
}}</span>
+              <span v-if="k.unit" class="kpi-unit">{{ k.unit }}</span>
+            </span>
+          </div>
+        </div>
+      </div>
     </header>
 
     <!-- Picker dropdown — only visible when the Switch button is open.
@@ -266,13 +304,38 @@ const headerKpis = computed<HeaderKpi[]>(() => {
 }
 .service-row {
   display: flex;
-  align-items: flex-end;
-  gap: 18px;
+  align-items: center;
+  gap: 14px;
   flex-wrap: wrap;
-  margin-top: 14px;
-  padding-top: 14px;
+  margin-top: 12px;
+  padding-top: 12px;
   border-top: 1px dashed var(--sw-line);
 }
+.service-kpis {
+  margin-left: auto;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 14px;
+  align-items: baseline;
+}
+.kpi.compact {
+  display: inline-flex;
+  align-items: baseline;
+  gap: 5px;
+  text-align: left;
+  min-width: 0;
+}
+.kpi-label.inline {
+  font-size: 9.5px;
+  margin-bottom: 0;
+  text-transform: uppercase;
+  letter-spacing: 0.06em;
+  color: var(--sw-fg-3);
+}
+.kpi-value.inline {
+  font-size: 13px;
+  font-weight: 600;
+}
 .switch {
   /* Merged Switch button — sits at the start of the service row, ahead
    * of the KPI strip. Click opens the picker dropdown below. */

Reply via email to