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
commit c1cf0deb86bba2b31fe719ccf8f4892cebbddb4e Author: Wu Sheng <[email protected]> AuthorDate: Tue May 12 23:01:43 2026 +0800 admin: scope per component — add dependency / topology / logs tabs DashboardScope union expanded from 5 → 8 entries, matching the component-toggle list 1:1: service, instance, endpoint, dependency, topology, trace, logs, profiling LayerDashboards type + BFF zod schema (route body + admin save) + admin SCOPES const all picked up the three new keys. The scope tab strip in /admin/layer-dashboards now renders one tab per *enabled* component — when an operator toggles a component off, its scope tab disappears from the editor and the active scope snaps to the first remaining visible scope. Layers without those components see fewer tabs; layers with all 8 on (general) see every editable widget grid. Each new scope has its own dashboards.<scope> array — operators can populate widgets for the dependency / topology / logs scopes whenever the per-page UI matures. --- apps/bff/src/dashboard/routes.ts | 14 +++++++- apps/bff/src/layers/loader.ts | 3 ++ apps/ui/src/views/admin/LayerDashboardsAdmin.vue | 41 ++++++++++++++++++++++-- packages/api-client/src/dashboard.ts | 15 ++++++++- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/apps/bff/src/dashboard/routes.ts b/apps/bff/src/dashboard/routes.ts index 778b5c9..cc812e1 100644 --- a/apps/bff/src/dashboard/routes.ts +++ b/apps/bff/src/dashboard/routes.ts @@ -78,7 +78,16 @@ const widgetSchema = z.object({ w: z.number().int().positive().optional(), h: z.number().int().positive().optional(), }); -const scopeSchema = z.enum(['service', 'instance', 'endpoint', 'trace', 'profiling']); +const scopeSchema = z.enum([ + 'service', + 'instance', + 'endpoint', + 'dependency', + 'topology', + 'trace', + 'logs', + 'profiling', +]); const bodySchema = z.object({ service: z.string().optional(), widgets: z.array(widgetSchema).max(40).optional(), @@ -558,7 +567,10 @@ export function registerDashboardRoute(app: FastifyInstance, deps: DashboardRout service: z.array(widgetSchema).max(40).optional(), instance: z.array(widgetSchema).max(40).optional(), endpoint: z.array(widgetSchema).max(40).optional(), + dependency: z.array(widgetSchema).max(40).optional(), + topology: z.array(widgetSchema).max(40).optional(), trace: z.array(widgetSchema).max(40).optional(), + logs: z.array(widgetSchema).max(40).optional(), profiling: z.array(widgetSchema).max(40).optional(), }) .strict() diff --git a/apps/bff/src/layers/loader.ts b/apps/bff/src/layers/loader.ts index 4a8b93d..e6a9c5d 100644 --- a/apps/bff/src/layers/loader.ts +++ b/apps/bff/src/layers/loader.ts @@ -95,7 +95,10 @@ export interface LayerDashboards { service?: DashboardWidget[]; instance?: DashboardWidget[]; endpoint?: DashboardWidget[]; + dependency?: DashboardWidget[]; + topology?: DashboardWidget[]; trace?: DashboardWidget[]; + logs?: DashboardWidget[]; profiling?: DashboardWidget[]; } diff --git a/apps/ui/src/views/admin/LayerDashboardsAdmin.vue b/apps/ui/src/views/admin/LayerDashboardsAdmin.vue index 34f3860..5829877 100644 --- a/apps/ui/src/views/admin/LayerDashboardsAdmin.vue +++ b/apps/ui/src/views/admin/LayerDashboardsAdmin.vue @@ -32,7 +32,16 @@ import type { AdminLayerTemplate } from '@/api/client'; import type { DashboardScope, DashboardWidget } from '@skywalking-horizon-ui/api-client'; import { bffClient } from '@/api/client'; -const SCOPES: DashboardScope[] = ['service', 'instance', 'endpoint', 'trace', 'profiling']; +const SCOPES: DashboardScope[] = [ + 'service', + 'instance', + 'endpoint', + 'dependency', + 'topology', + 'trace', + 'logs', + 'profiling', +]; const templates = ref<AdminLayerTemplate[]>([]); const isLoading = ref(true); @@ -72,6 +81,34 @@ function syncDraft(): void { watch(selectedKey, syncDraft); onMounted(loadAll); +/** + * Map each DashboardScope to its corresponding `components.*` flag. + * Used to filter the scope tab strip so admin only surfaces tabs for + * components the operator has toggled on. + */ +const SCOPE_COMPONENT: Record<DashboardScope, ComponentKey> = { + service: 'service', + instance: 'instances', + endpoint: 'endpoints', + dependency: 'endpointDependency', + topology: 'topology', + trace: 'traces', + logs: 'logs', + profiling: 'profiling', +}; +const visibleScopes = computed<DashboardScope[]>(() => { + const tpl = draft.template; + if (!tpl?.components) return SCOPES; + return SCOPES.filter((s) => tpl.components[SCOPE_COMPONENT[s]]); +}); +watch(visibleScopes, (scopes) => { + // If the currently-active scope was just toggled off, snap to the + // first remaining visible scope so the editor stays on solid ground. + if (!scopes.includes(activeScope.value)) { + activeScope.value = scopes[0] ?? 'service'; + } +}); + const dirty = computed(() => { const original = templates.value.find((t) => t.key === selectedKey.value); if (!original || !draft.template) return false; @@ -429,7 +466,7 @@ function toggleComponent(key: ComponentKey): void { <!-- Scope tabs --> <nav class="scope-tabs sw-card"> <button - v-for="s in SCOPES" + v-for="s in visibleScopes" :key="s" class="scope-tab" :class="{ on: activeScope === s }" diff --git a/packages/api-client/src/dashboard.ts b/packages/api-client/src/dashboard.ts index 5c49311..57d5d5e 100644 --- a/packages/api-client/src/dashboard.ts +++ b/packages/api-client/src/dashboard.ts @@ -42,7 +42,20 @@ export type DashboardWidgetType = 'card' | 'line' | 'top'; * `trace` = trace explorer for the selected entity * `profiling` = flame graphs / sampled stacks */ -export type DashboardScope = 'service' | 'instance' | 'endpoint' | 'trace' | 'profiling'; +/** + * One scope per component on a layer. Each scope owns its own widget + * grid (`dashboards.<scope>` array). The set mirrors the layer's + * component toggles 1:1 — every enabled component is configurable. + */ +export type DashboardScope = + | 'service' + | 'instance' + | 'endpoint' + | 'dependency' + | 'topology' + | 'trace' + | 'logs' + | 'profiling'; export interface DashboardWidget { /** Stable id within the layer's dashboard. */
