This is an automated email from the ASF dual-hosted git repository. zqr10159 pushed a commit to branch 2.0.0 in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
commit c0eb3a0814c6c9e3b4fe7cb99d9f8c9ce73fd6ca Author: Logic <[email protected]> AuthorDate: Sun May 31 09:04:54 2026 +0800 fix(web-next): source topology node icons from catalog --- web-next/app/topology/page.test.tsx | 2 +- web-next/app/topology/topology-page.tsx | 3 ++- web-next/app/ui-lab/page.test.tsx | 8 +++++++- web-next/app/ui-lab/page.tsx | 4 ++-- web-next/lib/i18n-runtime-messages.ts | 4 ++++ web-next/packages/hertzbeat-ui/src/index.test.tsx | 4 ++-- web-next/packages/hertzbeat-ui/src/topology-g6.test.tsx | 14 +++++++++----- web-next/packages/hertzbeat-ui/src/topology-g6.tsx | 16 ++++++++++++---- web-next/test/i18n-test-helper.ts | 4 ++++ 9 files changed, 43 insertions(+), 16 deletions(-) diff --git a/web-next/app/topology/page.test.tsx b/web-next/app/topology/page.test.tsx index 559e979481..9d41367f09 100644 --- a/web-next/app/topology/page.test.tsx +++ b/web-next/app/topology/page.test.tsx @@ -971,7 +971,7 @@ describe('topology page', () => { expect(html).not.toContain('data-hz-topology-legend-item="node-type-resource"'); expect(html).toContain('data-hz-topology-legend-icon-library="lucide-react"'); expect(html).toContain('data-hz-topology-legend-icon-source="entity-type-catalog"'); - expect(html).toContain('data-hz-topology-legend-icon-name="blocks"'); + expect(html).toContain('data-hz-topology-legend-icon-name="component"'); expect(html).toContain('data-hz-topology-legend-icon-name="database"'); expect(html).toContain('data-hz-topology-legend-icon-no-handdrawn="true"'); expect(html).toContain('data-hz-topology-legend-item="healthy-node"'); diff --git a/web-next/app/topology/topology-page.tsx b/web-next/app/topology/topology-page.tsx index c17a439823..81f7f9e5dd 100644 --- a/web-next/app/topology/topology-page.tsx +++ b/web-next/app/topology/topology-page.tsx @@ -74,9 +74,10 @@ function findEdge(edges: TopologyServiceEdge[], id: string) { } function resolveTopologyG6EntityType(node: Pick<TopologyServiceNode, 'entityType' | 'id' | 'label' | 'source'>) { + const inferredIcon = getHzTopologyG6NodeIcon(`${node.entityType} ${node.label} ${node.source} ${node.id}`); + if (inferredIcon.kind === 'cache' || inferredIcon.kind === 'queue') return inferredIcon.kind; const directIcon = getHzTopologyG6NodeIcon(node.entityType); if (directIcon.kind !== 'unknown') return directIcon.kind; - const inferredIcon = getHzTopologyG6NodeIcon(`${node.label} ${node.source} ${node.id}`); return inferredIcon.kind === 'unknown' ? node.entityType : inferredIcon.kind; } diff --git a/web-next/app/ui-lab/page.test.tsx b/web-next/app/ui-lab/page.test.tsx index dbe95c950a..3093c7c6d8 100644 --- a/web-next/app/ui-lab/page.test.tsx +++ b/web-next/app/ui-lab/page.test.tsx @@ -2703,13 +2703,19 @@ describe('HertzBeat UI lab page', () => { expect(html).not.toContain('data-hz-topology-legend-section="confidence"'); expect(html).toContain('data-hz-topology-legend-item="node-type-service"'); expect(html).toContain('data-hz-topology-legend-item="node-type-database"'); - expect(html).toContain('data-hz-topology-legend-item="node-type-middleware"'); + expect(html).toContain('data-hz-topology-legend-item="node-type-cache"'); + expect(html).toContain('data-hz-topology-legend-item="node-type-queue"'); expect(html).toContain('data-hz-topology-legend-item="node-type-monitor"'); + expect(html).not.toContain('data-hz-topology-legend-item="node-type-middleware"'); expect(html).not.toContain('data-hz-topology-legend-item="node-type-application"'); expect(html).not.toContain('data-hz-topology-legend-item="node-type-alert"'); expect(html).not.toContain('data-hz-topology-legend-item="node-type-resource"'); expect(html).toContain('data-hz-topology-legend-icon-library="lucide-react"'); expect(html).toContain('data-hz-topology-legend-icon-source="entity-type-catalog"'); + expect(html).toContain('data-hz-topology-legend-icon-name="component"'); + expect(html).toContain('data-hz-topology-legend-icon-name="database"'); + expect(html).toContain('data-hz-topology-legend-icon-name="memory-stick"'); + expect(html).toContain('data-hz-topology-legend-icon-name="inbox"'); expect(html).toContain('data-hz-topology-legend-icon-no-handdrawn="true"'); expect(html).toContain('data-hz-topology-legend-visual-source="lucide-react"'); expect(html).toContain('data-hz-topology-legend-visual-mode="source-backed-text"'); diff --git a/web-next/app/ui-lab/page.tsx b/web-next/app/ui-lab/page.tsx index 0693307083..4bf30d9a9c 100644 --- a/web-next/app/ui-lab/page.tsx +++ b/web-next/app/ui-lab/page.tsx @@ -361,7 +361,7 @@ const topologyG6LabGraph: HzTopologyG6GraphInput = { { id: 'queue-events', label: 'Events Queue', - entityType: 'middleware', + entityType: 'queue', health: 'healthy', tone: 'blue', focus: 'normal', @@ -383,7 +383,7 @@ const topologyG6LabGraph: HzTopologyG6GraphInput = { { id: 'cache-cart', label: 'Cart Cache', - entityType: 'middleware', + entityType: 'cache', health: 'warning', tone: 'orange', focus: 'normal', diff --git a/web-next/lib/i18n-runtime-messages.ts b/web-next/lib/i18n-runtime-messages.ts index 74fe659faf..c1e4754789 100644 --- a/web-next/lib/i18n-runtime-messages.ts +++ b/web-next/lib/i18n-runtime-messages.ts @@ -1020,6 +1020,8 @@ export const SUPPLEMENTAL_MESSAGES: Partial<Record<LocaleCode, Messages>> = { 'topology.legend.node-type.application': 'Application', 'topology.legend.node-type.service': 'Service', 'topology.legend.node-type.database': 'Database', + 'topology.legend.node-type.cache': 'Cache', + 'topology.legend.node-type.queue': 'Queue', 'topology.legend.node-type.middleware': 'Middleware', 'topology.legend.node-type.k8s-workload': 'Workload', 'topology.legend.node-type.monitor': 'Monitor', @@ -4537,6 +4539,8 @@ export const SUPPLEMENTAL_MESSAGES: Partial<Record<LocaleCode, Messages>> = { 'topology.legend.node-type.application': '应用', 'topology.legend.node-type.service': '服务', 'topology.legend.node-type.database': '数据库', + 'topology.legend.node-type.cache': '缓存', + 'topology.legend.node-type.queue': '队列', 'topology.legend.node-type.middleware': '中间件', 'topology.legend.node-type.k8s-workload': '工作负载', 'topology.legend.node-type.monitor': '监控', diff --git a/web-next/packages/hertzbeat-ui/src/index.test.tsx b/web-next/packages/hertzbeat-ui/src/index.test.tsx index 1613acd63f..4de18901b4 100644 --- a/web-next/packages/hertzbeat-ui/src/index.test.tsx +++ b/web-next/packages/hertzbeat-ui/src/index.test.tsx @@ -5208,7 +5208,7 @@ describe('@hertzbeat/ui', () => { label: 'Service node', iconSrc: 'data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2024%2024%22%3E%3C/svg%3E', iconLibrary: 'lucide-react', - iconName: 'blocks', + iconName: 'component', iconSource: 'entity-type-catalog', visualSource: 'lucide-react' }, @@ -5241,7 +5241,7 @@ describe('@hertzbeat/ui', () => { expect(html).toContain('data-hz-topology-legend-source-label="lucide-react"'); expect(html).toContain('data-hz-topology-legend-icon-owner="hertzbeat-ui-legend-source-icon"'); expect(html).toContain('data-hz-topology-legend-icon-library="lucide-react"'); - expect(html).toContain('data-hz-topology-legend-icon-name="blocks"'); + expect(html).toContain('data-hz-topology-legend-icon-name="component"'); expect(html).toContain('data-hz-topology-legend-icon-source="entity-type-catalog"'); expect(html).toContain('data-hz-topology-legend-icon-no-handdrawn="true"'); expect(html).toContain('data-hz-topology-legend-visual-source="hertzbeat-status-token"'); diff --git a/web-next/packages/hertzbeat-ui/src/topology-g6.test.tsx b/web-next/packages/hertzbeat-ui/src/topology-g6.test.tsx index 04a813fb86..f8f730f6ec 100644 --- a/web-next/packages/hertzbeat-ui/src/topology-g6.test.tsx +++ b/web-next/packages/hertzbeat-ui/src/topology-g6.test.tsx @@ -79,7 +79,7 @@ describe('@hertzbeat/ui topology G6 canvas', () => { entityType: 'service', iconLibrary: 'lucide-react', iconLabel: 'Service', - iconName: 'blocks', + iconName: 'component', iconSource: 'entity-type-catalog', nodeShape: 'circle-icon-label', requestRatePerSecond: 12.34, @@ -127,8 +127,10 @@ describe('@hertzbeat/ui topology G6 canvas', () => { it('uses a shared Lucide node icon catalog for supported topology entity types', () => { expect(HZ_TOPOLOGY_G6_NODE_ICON_CATALOG.map(item => [item.kind, item.iconName, item.label])).toEqual([ ['application', 'app-window', 'Application'], - ['service', 'blocks', 'Service'], + ['service', 'component', 'Service'], ['database', 'database', 'Database'], + ['cache', 'memory-stick', 'Cache'], + ['queue', 'inbox', 'Queue'], ['middleware', 'workflow', 'Middleware'], ['k8s-workload', 'container', 'Workload'], ['monitor', 'activity', 'Monitor'], @@ -137,10 +139,12 @@ describe('@hertzbeat/ui topology G6 canvas', () => { ['unknown', 'circle-help', 'Unknown'] ]); - expect(getHzTopologyG6NodeIcon('service')).toMatchObject({ kind: 'service', iconName: 'blocks' }); - expect(getHzTopologyG6NodeIcon('payment-api')).toMatchObject({ kind: 'service', iconName: 'blocks' }); + expect(getHzTopologyG6NodeIcon('service')).toMatchObject({ kind: 'service', iconName: 'component' }); + expect(getHzTopologyG6NodeIcon('payment-api')).toMatchObject({ kind: 'service', iconName: 'component' }); expect(getHzTopologyG6NodeIcon('orders-db')).toMatchObject({ kind: 'database', iconName: 'database' }); - expect(getHzTopologyG6NodeIcon('redis-cache')).toMatchObject({ kind: 'middleware', iconName: 'workflow' }); + expect(getHzTopologyG6NodeIcon('redis-cache')).toMatchObject({ kind: 'cache', iconName: 'memory-stick' }); + expect(getHzTopologyG6NodeIcon('kafka-events-queue')).toMatchObject({ kind: 'queue', iconName: 'inbox' }); + expect(getHzTopologyG6NodeIcon('middleware')).toMatchObject({ kind: 'middleware', iconName: 'workflow' }); expect(getHzTopologyG6NodeIcon('k8s-workload')).toMatchObject({ kind: 'k8s-workload', iconName: 'container' }); expect(getHzTopologyG6NodeIcon('monitor-bind')).toMatchObject({ kind: 'monitor', iconName: 'activity' }); expect(getHzTopologyG6NodeIcon('linux-host')).toMatchObject({ kind: 'resource', iconName: 'server' }); diff --git a/web-next/packages/hertzbeat-ui/src/topology-g6.tsx b/web-next/packages/hertzbeat-ui/src/topology-g6.tsx index d638b35501..e913a7de1f 100644 --- a/web-next/packages/hertzbeat-ui/src/topology-g6.tsx +++ b/web-next/packages/hertzbeat-ui/src/topology-g6.tsx @@ -4,10 +4,12 @@ import * as React from 'react'; import { Crosshair, Maximize2, RotateCcw, Search, ZoomIn, ZoomOut } from 'lucide-react'; import { __iconNode as activityIconNode } from 'lucide-react/dist/esm/icons/activity.js'; import { __iconNode as appWindowIconNode } from 'lucide-react/dist/esm/icons/app-window.js'; -import { __iconNode as blocksIconNode } from 'lucide-react/dist/esm/icons/blocks.js'; import { __iconNode as circleHelpIconNode } from 'lucide-react/dist/esm/icons/circle-help.js'; +import { __iconNode as componentIconNode } from 'lucide-react/dist/esm/icons/component.js'; import { __iconNode as containerIconNode } from 'lucide-react/dist/esm/icons/container.js'; import { __iconNode as databaseIconNode } from 'lucide-react/dist/esm/icons/database.js'; +import { __iconNode as inboxIconNode } from 'lucide-react/dist/esm/icons/inbox.js'; +import { __iconNode as memoryStickIconNode } from 'lucide-react/dist/esm/icons/memory-stick.js'; import { __iconNode as serverIconNode } from 'lucide-react/dist/esm/icons/server.js'; import { __iconNode as triangleAlertIconNode } from 'lucide-react/dist/esm/icons/triangle-alert.js'; import { __iconNode as workflowIconNode } from 'lucide-react/dist/esm/icons/workflow.js'; @@ -79,6 +81,8 @@ export type HzTopologyG6NodeIconKind = | 'application' | 'service' | 'database' + | 'cache' + | 'queue' | 'middleware' | 'k8s-workload' | 'monitor' @@ -88,8 +92,10 @@ export type HzTopologyG6NodeIconKind = export type HzTopologyG6NodeIconName = | 'app-window' - | 'blocks' + | 'component' | 'database' + | 'memory-stick' + | 'inbox' | 'workflow' | 'container' | 'activity' @@ -360,13 +366,15 @@ function topologyNodeIconSpec( export const HZ_TOPOLOGY_G6_NODE_ICON_CATALOG: readonly HzTopologyG6NodeIconSpec[] = [ topologyNodeIconSpec('application', 'Application', 'app-window', ['application', 'app'], appWindowIconNode), - topologyNodeIconSpec('service', 'Service', 'blocks', ['service', 'api', 'endpoint'], blocksIconNode), + topologyNodeIconSpec('service', 'Service', 'component', ['service', 'api', 'endpoint'], componentIconNode), topologyNodeIconSpec('database', 'Database', 'database', ['database', 'db', 'mysql', 'postgres', 'postgresql', 'mongo'], databaseIconNode), + topologyNodeIconSpec('cache', 'Cache', 'memory-stick', ['cache', 'redis', 'memcached'], memoryStickIconNode), + topologyNodeIconSpec('queue', 'Queue', 'inbox', ['queue', 'mq', 'broker', 'topic', 'messaging', 'kafka', 'rabbit'], inboxIconNode), topologyNodeIconSpec( 'middleware', 'Middleware', 'workflow', - ['middleware', 'queue', 'mq', 'broker', 'topic', 'messaging', 'cache', 'redis', 'kafka', 'rabbit'], + ['middleware'], workflowIconNode ), topologyNodeIconSpec( diff --git a/web-next/test/i18n-test-helper.ts b/web-next/test/i18n-test-helper.ts index 1f955f4fdd..068a2c9301 100644 --- a/web-next/test/i18n-test-helper.ts +++ b/web-next/test/i18n-test-helper.ts @@ -621,6 +621,8 @@ const SUPPLEMENTAL_MESSAGES: Partial<Record<LocaleCode, Messages>> = { 'topology.legend.node-type.application': 'Application', 'topology.legend.node-type.service': 'Service', 'topology.legend.node-type.database': 'Database', + 'topology.legend.node-type.cache': 'Cache', + 'topology.legend.node-type.queue': 'Queue', 'topology.legend.node-type.middleware': 'Middleware', 'topology.legend.node-type.k8s-workload': 'Workload', 'topology.legend.node-type.monitor': 'Monitor', @@ -3411,6 +3413,8 @@ const SUPPLEMENTAL_MESSAGES: Partial<Record<LocaleCode, Messages>> = { 'topology.legend.node-type.application': '应用', 'topology.legend.node-type.service': '服务', 'topology.legend.node-type.database': '数据库', + 'topology.legend.node-type.cache': '缓存', + 'topology.legend.node-type.queue': '队列', 'topology.legend.node-type.middleware': '中间件', 'topology.legend.node-type.k8s-workload': '工作负载', 'topology.legend.node-type.monitor': '监控', --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
