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]

Reply via email to