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 74b657d  layer: drop overview cap; services is the default entry per 
layer
74b657d is described below

commit 74b657d7d056fd288662fe2dd69b2a55e5bd1c61
Author: Wu Sheng <[email protected]>
AuthorDate: Tue May 12 15:09:48 2026 +0800

    layer: drop overview cap; services is the default entry per layer
    
    There's no per-layer Overview page. The global Overview at / already
    composes all layers; visiting a layer means going straight to its
    Services (or its renamed equivalent โ€” Workloads / Functions / Pages /
    Clusters / Databases / Brokers).
    
    - LayerCaps.overview removed from packages/api-client/menu.ts; BFF
      LAYER_DEFAULTS scrubbed to match.
    - BFF setup zod schema no longer accepts an 'overview' cap field.
    - AppSidebar drops the per-layer Overview row; Services becomes the
      first child under each expanded layer.
    - Router: /layer/:layerKey now redirects to /layer/:layerKey/services
      (the bare layer route was an orphan).
    - Overview LayerLandingCard's 'View all' link points at the services
      slot route.
---
 apps/bff/src/oap/menu-routes.ts                 | 46 ++++++++++++-------------
 apps/bff/src/setup/routes.ts                    |  1 -
 apps/ui/src/components/shell/AppSidebar.vue     | 11 +-----
 apps/ui/src/router/index.ts                     | 10 +++---
 apps/ui/src/views/overview/LayerLandingCard.vue |  2 +-
 apps/ui/src/views/setup/LayerSetupCard.vue      |  4 ++-
 packages/api-client/src/menu.ts                 |  1 -
 7 files changed, 32 insertions(+), 43 deletions(-)

diff --git a/apps/bff/src/oap/menu-routes.ts b/apps/bff/src/oap/menu-routes.ts
index 4d19cee..6f157fe 100644
--- a/apps/bff/src/oap/menu-routes.ts
+++ b/apps/bff/src/oap/menu-routes.ts
@@ -95,7 +95,7 @@ const LAYER_DEFAULTS: Record<string, { color: string; slots: 
LayerSlots; caps: L
     color: 'var(--sw-accent)',
     slots: { services: 'Services', instances: 'Instances', endpoints: 'API', 
endpointDependency: 'API dependency' },
     caps: {
-      overview: true, serviceMap: true, endpointDependency: true, 
instanceTopology: true, processTopology: true,
+      serviceMap: true, endpointDependency: true, instanceTopology: true, 
processTopology: true,
       dashboards: true, traces: true, logs: true, profiling: true, events: 
true,
     },
   },
@@ -103,36 +103,36 @@ const LAYER_DEFAULTS: Record<string, { color: string; 
slots: LayerSlots; caps: L
     color: 'var(--sw-info)',
     slots: { services: 'Services', instances: 'Sidecars', endpoints: 
'Endpoints' },
     caps: {
-      overview: true, serviceMap: true, endpointDependency: true, 
instanceTopology: true,
+      serviceMap: true, endpointDependency: true, instanceTopology: true,
       dashboards: true, traces: true, logs: true, events: true,
     },
   },
-  MESH_CP: { color: 'var(--sw-info)', slots: { services: 'Control-plane 
services' }, caps: { overview: true, dashboards: true } },
-  MESH_DP: { color: 'var(--sw-info)', slots: { services: 'Data-plane 
services', instances: 'Sidecars' }, caps: { overview: true, dashboards: true, 
instanceTopology: true } },
-  K8S: { color: 'var(--sw-purple)', slots: { services: 'Workloads', instances: 
'Pods' }, caps: { overview: true, serviceMap: true, instanceTopology: true, 
dashboards: true, events: true } },
-  K8S_SERVICE: { color: 'var(--sw-purple)', slots: { services: 'K8s services', 
instances: 'Pods' }, caps: { overview: true, serviceMap: true, 
instanceTopology: true, dashboards: true } },
-  BROWSER: { color: 'var(--sw-cyan)', slots: { services: 'Applications', 
instances: 'Versions', endpoints: 'Pages' }, caps: { overview: true, 
dashboards: true, traces: true, logs: true } },
-  MYSQL: { color: 'var(--sw-warn)', slots: { services: 'Instances' }, caps: { 
overview: true, dashboards: true } },
-  POSTGRESQL: { color: 'var(--sw-warn)', slots: { services: 'Instances' }, 
caps: { overview: true, dashboards: true } },
-  ELASTICSEARCH: { color: 'var(--sw-warn)', slots: { services: 'Clusters', 
instances: 'Nodes' }, caps: { overview: true, dashboards: true } },
-  REDIS: { color: 'var(--sw-warn)', slots: { services: 'Instances' }, caps: { 
overview: true, dashboards: true } },
-  MONGODB: { color: 'var(--sw-warn)', slots: { services: 'Clusters', 
instances: 'Nodes' }, caps: { overview: true, dashboards: true } },
-  CLICKHOUSE: { color: 'var(--sw-warn)', slots: { services: 'Services', 
instances: 'Instances' }, caps: { overview: true, dashboards: true } },
-  KAFKA: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Brokers' }, caps: { overview: true, dashboards: true } },
-  PULSAR: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Brokers' }, caps: { overview: true, dashboards: true } },
-  ROCKETMQ: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Brokers', endpoints: 'Topics' }, caps: { overview: true, dashboards: true } },
-  RABBITMQ: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Nodes' }, caps: { overview: true, dashboards: true } },
-  ACTIVEMQ: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Brokers', endpoints: 'Destinations' }, caps: { overview: true, dashboards: 
true } },
-  VIRTUAL_DATABASE: { color: 'var(--sw-warn)', slots: { services: 'Databases' 
}, caps: { overview: true, dashboards: true } },
-  VIRTUAL_CACHE: { color: 'var(--sw-warn)', slots: { services: 'Caches' }, 
caps: { overview: true, dashboards: true } },
-  VIRTUAL_MQ: { color: 'var(--sw-ok)', slots: { services: 'Queues' }, caps: { 
overview: true, dashboards: true } },
-  VIRTUAL_GENAI: { color: 'var(--sw-purple)', slots: { services: 'Providers', 
instances: 'Models' }, caps: { overview: true, dashboards: true } },
+  MESH_CP: { color: 'var(--sw-info)', slots: { services: 'Control-plane 
services' }, caps: { dashboards: true } },
+  MESH_DP: { color: 'var(--sw-info)', slots: { services: 'Data-plane 
services', instances: 'Sidecars' }, caps: { dashboards: true, instanceTopology: 
true } },
+  K8S: { color: 'var(--sw-purple)', slots: { services: 'Workloads', instances: 
'Pods' }, caps: { serviceMap: true, instanceTopology: true, dashboards: true, 
events: true } },
+  K8S_SERVICE: { color: 'var(--sw-purple)', slots: { services: 'K8s services', 
instances: 'Pods' }, caps: { serviceMap: true, instanceTopology: true, 
dashboards: true } },
+  BROWSER: { color: 'var(--sw-cyan)', slots: { services: 'Applications', 
instances: 'Versions', endpoints: 'Pages' }, caps: { dashboards: true, traces: 
true, logs: true } },
+  MYSQL: { color: 'var(--sw-warn)', slots: { services: 'Instances' }, caps: { 
dashboards: true } },
+  POSTGRESQL: { color: 'var(--sw-warn)', slots: { services: 'Instances' }, 
caps: { dashboards: true } },
+  ELASTICSEARCH: { color: 'var(--sw-warn)', slots: { services: 'Clusters', 
instances: 'Nodes' }, caps: { dashboards: true } },
+  REDIS: { color: 'var(--sw-warn)', slots: { services: 'Instances' }, caps: { 
dashboards: true } },
+  MONGODB: { color: 'var(--sw-warn)', slots: { services: 'Clusters', 
instances: 'Nodes' }, caps: { dashboards: true } },
+  CLICKHOUSE: { color: 'var(--sw-warn)', slots: { services: 'Services', 
instances: 'Instances' }, caps: { dashboards: true } },
+  KAFKA: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Brokers' }, caps: { dashboards: true } },
+  PULSAR: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Brokers' }, caps: { dashboards: true } },
+  ROCKETMQ: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Brokers', endpoints: 'Topics' }, caps: { dashboards: true } },
+  RABBITMQ: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Nodes' }, caps: { dashboards: true } },
+  ACTIVEMQ: { color: 'var(--sw-ok)', slots: { services: 'Clusters', instances: 
'Brokers', endpoints: 'Destinations' }, caps: { dashboards: true } },
+  VIRTUAL_DATABASE: { color: 'var(--sw-warn)', slots: { services: 'Databases' 
}, caps: { dashboards: true } },
+  VIRTUAL_CACHE: { color: 'var(--sw-warn)', slots: { services: 'Caches' }, 
caps: { dashboards: true } },
+  VIRTUAL_MQ: { color: 'var(--sw-ok)', slots: { services: 'Queues' }, caps: { 
dashboards: true } },
+  VIRTUAL_GENAI: { color: 'var(--sw-purple)', slots: { services: 'Providers', 
instances: 'Models' }, caps: { dashboards: true } },
 };
 
 const DEFAULT_FOR_UNKNOWN_LAYER = {
   color: 'var(--sw-fg-2)',
   slots: { services: 'Services' } as LayerSlots,
-  caps: { overview: true, dashboards: true } as LayerCaps,
+  caps: {dashboards: true } as LayerCaps,
 };
 
 function deriveLayer(
diff --git a/apps/bff/src/setup/routes.ts b/apps/bff/src/setup/routes.ts
index 03efc4b..df0e5a5 100644
--- a/apps/bff/src/setup/routes.ts
+++ b/apps/bff/src/setup/routes.ts
@@ -70,7 +70,6 @@ const layerConfigSchema = z
       .strict(),
     caps: z
       .object({
-        overview: z.boolean().optional(),
         serviceMap: z.boolean().optional(),
         endpointDependency: z.boolean().optional(),
         instanceTopology: z.boolean().optional(),
diff --git a/apps/ui/src/components/shell/AppSidebar.vue 
b/apps/ui/src/components/shell/AppSidebar.vue
index 2c9b254..b207468 100644
--- a/apps/ui/src/components/shell/AppSidebar.vue
+++ b/apps/ui/src/components/shell/AppSidebar.vue
@@ -174,20 +174,11 @@ const sections: NavSection[] = [
           </span>
         </div>
         <div v-if="expandedLayer === L.key" class="layer-children">
-          <RouterLink
-            v-if="L.caps.overview"
-            :to="`/layer/${L.key}`"
-            class="sw-nav-item"
-            :class="{ 'is-active': isActive(`/layer/${L.key}`) && route.path 
=== `/layer/${L.key}` }"
-          >
-            <Icon name="dash" /><span>Overview</span>
-          </RouterLink>
-
           <RouterLink
             v-if="L.slots.services"
             :to="`/layer/${L.key}/services`"
             class="sw-nav-item"
-            :class="{ 'is-active': isActive(`/layer/${L.key}/services`) }"
+            :class="{ 'is-active': isActive(`/layer/${L.key}/services`) || 
route.path === `/layer/${L.key}` }"
           >
             <Icon name="svc" /><span>{{ L.slots.services }}</span>
             <span class="sw-badge" style="margin-left: auto">{{ L.serviceCount 
}}</span>
diff --git a/apps/ui/src/router/index.ts b/apps/ui/src/router/index.ts
index e81da7a..3fc988d 100644
--- a/apps/ui/src/router/index.ts
+++ b/apps/ui/src/router/index.ts
@@ -29,14 +29,12 @@ function humanKey(k: string): string {
 // only needs the raw key + the feature label.
 function layerSubRoutes(): RouteRecordRaw[] {
   const sub: RouteRecordRaw[] = [];
+  // Bare /layer/:layerKey redirects to /layer/:layerKey/services โ€” the
+  // default entry point per layer. There is no per-layer 'overview'
+  // (the global Overview at / handles that).
   sub.push({
     path: 'layer/:layerKey',
-    component: placeholder,
-    props: (r) => ({
-      title: `${humanKey(String(r.params.layerKey))} ยท Overview`,
-      phase: 'Phase 2',
-      note: 'Per-layer landing: KPIs, throughput, service constellation, 
services table.',
-    }),
+    redirect: (to) => ({ path: `/layer/${to.params.layerKey}/services` }),
   });
 
   const features: { path: string; label: string; phase: string }[] = [
diff --git a/apps/ui/src/views/overview/LayerLandingCard.vue 
b/apps/ui/src/views/overview/LayerLandingCard.vue
index 43f08d9..308e08a 100644
--- a/apps/ui/src/views/overview/LayerLandingCard.vue
+++ b/apps/ui/src/views/overview/LayerLandingCard.vue
@@ -26,7 +26,7 @@ const props = defineProps<{ layer: LayerDef }>();
 const store = useSetupStore();
 const cfg = computed(() => store.ensure(props.layer.key, { slots: 
props.layer.slots, caps: props.layer.caps }));
 const slotName = computed(() => cfg.value.slots.services ?? 'Services');
-const detailHref = computed(() => `/layer/${props.layer.key}`);
+const detailHref = computed(() => `/layer/${props.layer.key}/services`);
 </script>
 
 <template>
diff --git a/apps/ui/src/views/setup/LayerSetupCard.vue 
b/apps/ui/src/views/setup/LayerSetupCard.vue
index 1176b87..3f30c48 100644
--- a/apps/ui/src/views/setup/LayerSetupCard.vue
+++ b/apps/ui/src/views/setup/LayerSetupCard.vue
@@ -55,7 +55,9 @@ const summary = computed<string>(() => {
   return base;
 });
 
-// Default cap labels with the "Topology" trio collapsed for compact display.
+// Cap rows the operator can toggle. The per-layer page always opens on
+// Services โ€” there's no `overview` cap; the global Overview already
+// composes layers automatically.
 const capRows: Array<{ key: keyof typeof cfg.value.caps; label: string }> = [
   { key: 'serviceMap', label: 'Service map' },
   { key: 'endpointDependency', label: 'API dependency' },
diff --git a/packages/api-client/src/menu.ts b/packages/api-client/src/menu.ts
index 640dbc4..a8cfec2 100644
--- a/packages/api-client/src/menu.ts
+++ b/packages/api-client/src/menu.ts
@@ -39,7 +39,6 @@ export interface LayerSlots {
 }
 
 export interface LayerCaps {
-  overview?: boolean;
   serviceMap?: boolean;
   endpointDependency?: boolean;
   instanceTopology?: boolean;

Reply via email to