[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:infrastructure-topology-dashboard":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Building an Infrastructure Topology Viewer with Typed Node Cards\n\n## What This Example Builds\nThis example builds a compact infrastructure topology viewer with one web server at the root and linked database, cache, and service nodes around it. The visible result is a dark, monitoring-style scene where the main server appears as a detailed status card, database nodes appear as appliance tiles, and service nodes appear as smaller utility blocks.\n\nUsers mainly inspect the prepared topology through normal graph navigation. The most notable part is the scene composition: relation-graph still renders ordinary nodes and lines, but a canvas-level VPC annotation and custom node bodies make the graph read like an operations board instead of a generic tree.\n\n## How the Data Is Organized\nThe data is an inline JSON topology object created inside `initializeGraph()`. It defines `rootId`, a `nodes` array, and a `lines` array, then loads that structure directly with `setJsonData(...)`. There is no separate `doLayout` preprocessing stage.\n\nThe important preprocessing choice is in the node metadata. Each node stores a `data.type`, and some nodes carry additional fields such as server status, IP, port, CPU, memory, disk, or database engine. The renderer then branches on `node.data.type` to choose which node body to display. In a real system, the same shape can represent application servers, cache layers, database instances, internal APIs, or other infrastructure assets loaded from an inventory service.\n\n## How relation-graph Is Used\nThe example uses `RGProvider` in `index.tsx` so `RGHooks.useGraphInstance()` can control the graph from `MyGraph.tsx`. The graph options configure a left-to-right tree with fixed node gaps, rectangular node geometry, standard curved links, and left-right junction handling. Built-in node fill and border are turned off so the visible appearance comes from the custom slot content, while the default link styling stays cyan and slightly emphasized.\n\n`RelationGraph` is used as a normal node-link graph rather than as a pure connector layer. `RGSlotOnNode` renders three different node bodies from one dataset: a large monitoring card for the server, a compact database tile, and a simple service tile. `RGSlotOnCanvas` adds a labeled `Internal Isolation Zone (VPC-1)` rectangle behind the graph scene, and that overlay is explicitly non-interactive because it uses `pointer-events-none`.\n\nThe instance API handles startup. On mount, the component builds the inline dataset, schedules the loading state to clear, calls `setJsonData(...)`, then runs `moveToCenter()` and `zoomToFit()`. The SCSS file finishes the scene by forcing a dark map background and keeping the built-in node shell transparent with rounded corners.\n\n## Key Interactions\n- A loading overlay appears while `g_loading` is true, dimming the graph and showing a spinner.\n- After mount, the graph loads the prepared topology and automatically recenters and fits it into view.\n- The VPC backdrop does not block graph interaction because the overlay is marked with `pointer-events-none`.\n- Server cards raise their shadow on hover, which gives the most information-dense node a small emphasis cue.\n\n## Key Code Fragments\nThis options block shows that the scene is still a regular tree graph, but the built-in node chrome is stripped back so custom node markup can define the visible UI.\n\n```tsx\nconst userGraphOptions: RGOptions = {\n    debug: false,\n    layout: {\n        layoutName: 'tree',\n        from: 'left',\n        treeNodeGapH: 150,\n        treeNodeGapV: 20\n    },\n    defaultNodeShape: RGNodeShape.rect,\n    defaultLineShape: RGLineShape.StandardCurve,\n```\n\nThis fragment shows that the topology is loaded from inline JSON and that node metadata is prepared to drive different renderers.\n\n```tsx\nconst myJsonData = {\n    rootId: 'server-1',\n    nodes: [\n        { id: 'server-1', text: 'Web Server', data: { type: 'server', ip: '192.168.1.10', port: '80', status: 'Running', cpu: '45%', mem: '6.2GB', disk: '128GB/512GB' } },\n        { id: 'db-1', text: 'User DB', data: { type: 'database', dbType: 'PostgreSQL' } },\n        { id: 'db-2', text: 'Cache', data: { type: 'database', dbType: 'Redis' } },\n        { id: 'svc-1', text: 'Auth Service', data: { type: 'service' } },\n        { id: 'svc-2', text: 'Payment API', data: { type: 'service' } },\n    ],\n```\n\nThis startup sequence is the core runtime pattern: load the inline dataset, then center and fit the viewport.\n\n```tsx\nsetGLoading(false);\nawait graphInstance.setJsonData(myJsonData);\ngraphInstance.moveToCenter();\ngraphInstance.zoomToFit();\n```\n\nThis slot fragment shows that one node slot branches by `data.type` so the same graph can render different infrastructure roles with different HTML bodies.\n\n```tsx\nconst NodeSlot = ({ node }: RGNodeSlotProps) => {\n    const data = node.data;\n\n    if (data.type === 'server') {\n        return (\n            \u003Cdiv className=\"w-[300px] border border-gray-300 rounded-lg shadow-md bg-white overflow-hidden transition-all hover:shadow-lg\">\n```\n\nThis canvas slot fragment proves that the VPC zone is an annotation layer, not the source of the actual graph nodes and lines.\n\n```tsx\n\u003CRGSlotOnCanvas>\n    \u003Cdiv className=\"absolute left-[-50px] top-[-80px] w-[400px] h-[400px] bg-[rgba(116,255,5,0.1)] border border-dashed border-green-300 pointer-events-none\">\n        \u003Cdiv className=\"w-fit px-5 h-10 leading-10 -mt-0 bg-[rgba(116,255,5,0.2)] text-gray-700 rounded-tr-lg flex items-center justify-center gap-1.5 font-medium\">\n            \u003CLayout className=\"w-4 h-4\" />\n            Internal Isolation Zone (VPC-1)\n        \u003C/div>\n    \u003C/div>\n\u003C/RGSlotOnCanvas>\n```\n\n## What Makes This Example Distinct\nThe comparison records make the main distinction clear: this example keeps the topology in ordinary graph nodes and standard loaded `lines`, but uses typed node slots and a canvas-only backdrop to make the result look like an infrastructure dashboard. That is a different pattern from `scene-network-use-canvas-slot`, where most of the visible board is built directly in `RGSlotOnCanvas` and relation-graph acts more like a connector layer for DOM endpoints.\n\nCompared with `scene-network`, this example is smaller and more tree-structured. It does not build a full fixed dashboard shell with legend overlays, toolbar framing, or manually staged status columns. Instead, it focuses on one left-to-right infrastructure branch with a VPC label and mixed node types.\n\nThe rare combination is also important: a left-to-right tree layout, mount-time `setJsonData()` plus center-plus-fit initialization, node-data-driven renderer branching, cyan curved links, a dark graph canvas, and a non-interactive VPC zone annotation all appear together in one compact viewer. That makes it a strong starting point when a team wants scene-specific infrastructure presentation without moving away from standard relation-graph nodes and lines.\n\n## Where Else This Pattern Applies\n- Infrastructure inventory viewers that need one dataset to render servers, databases, caches, and services with different card bodies.\n- Operations dashboards that need a labeled environment zone or security boundary behind a normal node-link topology.\n- Internal platform maps that should open already centered and fitted instead of asking users to arrange the first view manually.\n- Technical dependency boards where scene styling matters, but the implementation should still rely on standard `nodes` and `lines` rather than absolute-positioned HTML panels.\n",false,500,1782615400186]