react-dashstream 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,842 +1,985 @@
1
- # React DashStream — AI Coding Assistant Skill
2
-
3
- > **Package**: `react-dashstream` (npm)
4
- > **Version**: 0.0.4
5
- > **Peer deps**: `react` ^18 || ^19, `react-dom` ^18 || ^19
6
- > **License**: MIT
7
-
8
- Use this skill when the user is working with the `react-dashstream` package — a holographic 3D infrastructure monitoring dashboard built entirely with CSS 3D transforms (no WebGL, no canvas).
9
-
10
- ---
11
-
12
- ## What this package does
13
-
14
- React DashStream renders a rotating 3D carousel of infrastructure services. Each service can be clicked to expand into a multi-tiered topology showing servers, databases, dispatchers, message servers, and user nodes connected by animated SVG lines. Components can be drilled into to reveal internal sub-components with sparkline graphs and gauges.
15
-
16
- Two modes:
17
-
18
- 1. **Static mode** — pass props directly for demo or snapshot views.
19
- 2. **Live data mode** — poll an HTTP endpoint with PromQL queries and auto-inject resolved values as component props.
20
-
21
- ---
22
-
23
- ## Installation and CSS
24
-
25
- ```bash
26
- npm install react-dashstream
27
- ```
28
-
29
- ```tsx
30
- import "react-dashstream/dist/index.css";
31
- import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
32
- ```
33
-
34
- **Always import the CSS file** — without it, no styles render.
35
-
36
- ---
37
-
38
- ## Architecture
39
-
40
- ```
41
- AIOPsDashboard ← Shell: header, state, optional DataProvider
42
- └─ Carousel ← 3D orbit, rotation, hosts dialogs
43
- └─ Service ← Per-service container, connections, holo base
44
- ├─ ServerNode ← Compound: ServiceNode + Server3D
45
- ├─ DatabaseNode ← Compound: ServiceNode + Database3D
46
- ├─ WebDispatcherNode ← Compound: ServiceNode + WebDispatcher3D
47
- ├─ MessageServerNode ← Compound: ServiceNode + MessageServer3D
48
- ├─ HumanNode ← Compound: ServiceNode + Human3D (SVG)
49
- ├─ SvgConnection ← Animated dashed topology line
50
- ├─ SyncBridge ← DB replication indicator
51
- ├─ NodeCallout ← Alert callout (auto from thresholds)
52
- └─ HoloBase ← Neon platform (auto-rendered)
53
-
54
- ServiceDialog ← Service-level KPI panel (auto on expand)
55
- ComponentDialog ← Drill-down with internals + graphs
56
- ├─ HistoricalGraphPanel ← Sparkline charts
57
- └─ CPU3D / Memory3D / DriveBay3D / NetworkBlock3D / ThreadPool3D / Platter3D / Port3D
58
-
59
- DataProvider ← Polling engine + credentials modal
60
- ```
61
-
62
- ### Source layout
63
-
64
- ```
65
- src/
66
- ├── index.ts # Public API barrel
67
- ├── AIOPsDashboard.tsx # Dashboard shell + live-data wiring
68
- ├── types.ts # Shared topology/drill-down types
69
- ├── theme.ts # Status tokens, holo palette, 3D face helpers
70
- ├── styles.css / index.css # Library + dashboard styles
71
- ├── data/
72
- ├── DataProvider.tsx # Polling engine, context, hooks
73
- ├── CredentialsModal.tsx # Auth prompt UI
74
- └── index.ts
75
- ├── components/ # ~30 UI components
76
- ├── Carousel.tsx, Service.tsx, ServiceNode.tsx
77
- ├── ServerNode.tsx, DatabaseNode.tsx, WebDispatcherNode.tsx, etc.
78
- │ ├── Server3D.tsx, Database3D.tsx, etc.
79
- │ ├── ServiceDialog.tsx, ComponentDialog.tsx
80
- │ ├── Internal3DComponents.tsx, HistoricalGraphPanel.tsx
81
- │ ├── ComponentDrillView.tsx, SvgConnection.tsx, SyncBridge.tsx
82
- │ ├── NodeCallout.tsx, HoloBase.tsx, CarouselContext.ts
83
- └── index.ts
84
- ├── services/
85
- │ ├── SAPService.tsx, ExchangeService.tsx
86
- │ ├── sapSubComponents.tsx
87
- └── index.ts
88
- example/
89
- ├── Dashboard.tsx
90
- └── services/
91
- ```
92
-
93
- ---
94
-
95
- ## Complete data flow reference
96
-
97
- There are **9 distinct data paths** in this package. Understanding them is critical for building live dashboards.
98
-
99
- ### 1. Data bindings — live props injected into service components
100
-
101
- `dataBindings` maps **service name → prop name → PromQL query**. The dashboard polls each query, applies a transform, and injects the result as a prop on the matched child component.
102
-
103
- ```ts
104
- type DataBinding = string | { query: string; transform?: (raw: unknown) => unknown };
105
- type DataBindings = Record<string, Record<string, DataBinding>>;
106
- ```
107
-
108
- - Bare string → uses global `dataTransform` (defaults to numeric parsing)
109
- - Object `{ query, transform }` uses per-binding transform
110
- - Service name key must match child's `name` prop **exactly**
111
-
112
- ```tsx
113
- const dataBindings: DataBindings = {
114
- "My Service": {
115
- cpuLoad: 'cpu_usage{instance="srv-01"}', // bare string → number
116
- memLoad: 'memory_usage{instance="srv-01"}', // bare string → number
117
- status: {
118
- // object custom transform
119
- query: 'cpu_usage{instance="srv-01"}',
120
- transform: (raw) => {
121
- const n = Number(raw);
122
- if (n >= 85) return "critical";
123
- if (n >= 70) return "warning";
124
- return "online";
125
- },
126
- },
127
- dbCapacity: 'disk_capacity{instance="db-01"}',
128
- dbStatus: { query: 'disk_capacity{instance="db-01"}', transform: statusFromValue },
129
- },
130
- };
131
- ```
132
-
133
- #### Multi-component services
134
-
135
- When a service has multiple nodes of the same type (e.g. three servers), use a flat naming convention with prefixes. Each `DataBinding` maps **one prop → one query** — there is no array or object binding type.
136
-
137
- ```tsx
138
- interface ServiceXProps {
139
- name: string;
140
- status?: ComponentStatus;
141
- srv1CpuLoad?: number;
142
- srv1MemLoad?: number;
143
- srv1Status?: ComponentStatus;
144
- srv2CpuLoad?: number;
145
- srv2MemLoad?: number;
146
- srv2Status?: ComponentStatus;
147
- srv3CpuLoad?: number;
148
- srv3MemLoad?: number;
149
- srv3Status?: ComponentStatus;
150
- dbCapacity?: number;
151
- dbStatus?: ComponentStatus;
152
- }
153
-
154
- function ServiceX({
155
- name, status = "online",
156
- srv1CpuLoad = 54, srv1MemLoad = 58, srv1Status = "online",
157
- srv2CpuLoad = 63, srv2MemLoad = 66, srv2Status = "online",
158
- srv3CpuLoad = 78, srv3MemLoad = 71, srv3Status = "online",
159
- dbCapacity = 68, dbStatus = "online",
160
- }: ServiceXProps) {
161
- return (
162
- <Service name={name} status={status} connections={[/* ... */]}>
163
- <ServerNode name="SRV-X1" status={srv1Status}
164
- cpuLoad={srv1CpuLoad} memLoad={srv1MemLoad} ... />
165
- <ServerNode name="SRV-X2" status={srv2Status}
166
- cpuLoad={srv2CpuLoad} memLoad={srv2MemLoad} ... />
167
- <ServerNode name="SRV-X3" status={srv3Status}
168
- cpuLoad={srv3CpuLoad} memLoad={srv3MemLoad} ... />
169
- <DatabaseNode name="DB-X1" status={dbStatus}
170
- capacity={dbCapacity} ... />
171
- </Service>
172
- );
173
- }
174
- ```
175
-
176
- Bind each prefixed prop individually:
177
-
178
- ```tsx
179
- dataBindings={{
180
- ServiceX: {
181
- status: { query: 'status{instance="svcx"}', transform: statusFromValue },
182
- srv1CpuLoad: 'cpu_usage{instance="srvx-01"}',
183
- srv1MemLoad: 'memory_usage{instance="srvx-01"}',
184
- srv1Status: { query: 'status{instance="srvx-01"}', transform: statusFromValue },
185
- srv2CpuLoad: 'cpu_usage{instance="srvx-02"}',
186
- srv2MemLoad: 'memory_usage{instance="srvx-02"}',
187
- srv2Status: { query: 'status{instance="srvx-02"}', transform: statusFromValue },
188
- srv3CpuLoad: 'cpu_usage{instance="srvx-03"}',
189
- srv3MemLoad: 'memory_usage{instance="srvx-03"}',
190
- srv3Status: { query: 'status{instance="srvx-03"}', transform: statusFromValue },
191
- dbCapacity: 'disk_capacity{instance="dbx-01"}',
192
- dbStatus: { query: 'status{instance="dbx-01"}', transform: statusFromValue },
193
- },
194
- }}
195
- ```
196
-
197
- Use a consistent convention like `srv1CpuLoad`, `srv2CpuLoad`. The service component maps each prefixed prop to the correct node.
198
-
199
- ### 2. Service dialog — static ServiceMeta metrics
200
-
201
- The **ServiceDialog** panel shows KPIs when a service is expanded. Static metrics are passed via `ServiceMeta`:
202
-
203
- ```tsx
204
- const services: ServiceMeta[] = [
205
- {
206
- name: "My Service",
207
- status: "online",
208
- metrics: [
209
- { label: "Uptime", value: "99.99%", color: "#00ff88" },
210
- { label: "Avg Latency", value: "8ms", color: "#00e5ff" },
211
- ],
212
- alerts: [
213
- { level: "info", message: "All Systems Nominal" },
214
- { level: "warning", message: "High CPU on SRV-01" },
215
- { level: "critical", message: "DB connection pool exhausted" },
216
- ],
217
- },
218
- ];
219
- ```
220
-
221
- ### 3. Service dialog live serviceDataBindings
222
-
223
- Replace static metrics with live-fetched values using `serviceDataBindings`:
224
-
225
- ```tsx
226
- serviceDataBindings={{
227
- "My Service": [
228
- { label: "CPU Load", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" },
229
- { label: "Memory", query: 'memory_usage{instance="srv-01"}', unit: "%", color: "#bb55ff" },
230
- { label: "Disk", query: 'disk_capacity{instance="db-01"}', unit: "%", color: "#ff8c00" },
231
- ],
232
- }}
233
- ```
234
-
235
- ```ts
236
- interface ServiceMetricBinding {
237
- label: string; // Row label
238
- query: string; // PromQL query
239
- unit?: string; // Suffix (e.g. "%", "ms")
240
- color?: string; // Accent color
241
- transform?: (raw: unknown) => string; // Custom formatter
242
- }
243
- ```
244
-
245
- When provided, live values **replace** static `ServiceMeta.metrics` for that service.
246
-
247
- ### 4. Component dialog default gauges
248
-
249
- The **ComponentDialog** appears when clicking a node. Default gauges are derived from node type:
250
-
251
- - **Server**: CPU (from `cpuLoad`), Memory (from `memLoad`), Storage
252
- - **Database**: Capacity (from `capacity`), Memory, Storage
253
- - **Dispatcher**: Traffic (from `traffic`), Memory, Storage
254
- - **Message Server**: Queue (from `queueDepth`), Memory, Storage
255
-
256
- Default thresholds: warn at 70, critical at 85.
257
-
258
- ### 5. Component dialog — custom dialogMetrics
259
-
260
- Override default gauges with `dialogMetrics` on any compound node:
261
-
262
- ```tsx
263
- <ServerNode
264
- name="SRV-01"
265
- status="online"
266
- cpuLoad={67}
267
- memLoad={72}
268
- ex={200}
269
- ey={380}
270
- compactOffset={{ x: -30, y: -20 }}
271
- zIndex={8}
272
- dialogMetrics={[
273
- { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67, unit: "%" },
274
- { id: "mem", label: "MEMORY", sublabel: "HEAP USAGE", value: 72, unit: "%" },
275
- { id: "iops", label: "IOPS", sublabel: "DISK OPS", value: 45, unit: "k/s", icon: "disk" },
276
- {
277
- id: "threads",
278
- label: "THREADS",
279
- sublabel: "ACTIVE",
280
- value: 82,
281
- unit: "%",
282
- warnAt: 60,
283
- critAt: 80,
284
- icon: "cpu",
285
- },
286
- ]}
287
- />
288
- ```
289
-
290
- ```ts
291
- interface ComponentDialogMetric {
292
- id: string; // Unique key
293
- label: string; // Upper label
294
- sublabel: string; // Lower label
295
- value: number; // 0–100
296
- unit?: string; // Default "%"
297
- icon?: "cpu" | "mem" | "disk"; // Default "cpu"
298
- warnAt?: number; // Default 70
299
- critAt?: number; // Default 85
300
- color?: string; // Override bar color (bypasses threshold coloring)
301
- }
302
- ```
303
-
304
- All compound nodes accept `dialogMetrics`: `ServerNode`, `DatabaseNode`, `WebDispatcherNode`, `MessageServerNode`.
305
-
306
- **Live component dialog metrics pattern**: To feed live query values into custom gauges, expose each metric as a prop on your service component. Use `dataBindings` to inject them. Then build the `dialogMetrics` array from those props:
307
-
308
- ```tsx
309
- // Service component — accepts individual metric props, builds dialogMetrics from them
310
- function MyService({ name, status = "online", cpuLoad = 42, memLoad = 60, iops = 20 }: any) {
311
- return (
312
- <Service
313
- name={name}
314
- status={status}
315
- connections={
316
- [
317
- /* ... */
318
- ]
319
- }
320
- >
321
- <ServerNode
322
- name="SRV-01"
323
- status={status}
324
- cpuLoad={cpuLoad}
325
- memLoad={memLoad}
326
- ex={200}
327
- ey={380}
328
- compactOffset={{ x: -30, y: -20 }}
329
- zIndex={8}
330
- dialogMetrics={[
331
- { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad },
332
- { id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad },
333
- {
334
- id: "iops",
335
- label: "IOPS",
336
- sublabel: "DISK OPS",
337
- value: iops,
338
- icon: "disk",
339
- warnAt: 50,
340
- critAt: 80,
341
- },
342
- ]}
343
- />
344
- </Service>
345
- );
346
- }
347
-
348
- // Dashboard — bind each prop to a query
349
- <AIOPsDashboard
350
- liveData
351
- dataEndpoint="..."
352
- dataBindings={{
353
- "My Service": {
354
- cpuLoad: 'cpu_usage{instance="srv-01"}',
355
- memLoad: 'memory_usage{instance="srv-01"}',
356
- iops: 'disk_iops{instance="srv-01"}',
357
- status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
358
- },
359
- }}
360
- >
361
- <MyService name="My Service" />
362
- </AIOPsDashboard>;
363
- ```
364
-
365
- The dashboard injects `cpuLoad`, `memLoad`, `iops` as live props → they flow into `dialogMetrics` → gauges update automatically on every poll. Works for any number of custom metrics.
366
-
367
- ### 6. Alerts — automatic threshold detection
368
-
369
- Nodes auto-render `NodeCallout` alerts when metrics breach thresholds:
370
-
371
- - **Warning** at 70% → orange callout
372
- - **Critical** at 85% → red callout
373
-
374
- Sources (checked in priority order):
375
-
376
- 1. `dialogMetrics` values vs their `warnAt`/`critAt`
377
- 2. Context values (`cpuLoad`, `memLoad`, `traffic`, `queueDepth`, `capacity`) vs default thresholds
378
-
379
- ```tsx
380
- // Auto-alert from cpuLoad > 85
381
- <ServerNode cpuLoad={92} memLoad={64}
382
- alert={{ offsetX: -160, offsetY: -60, align: "left" }}
383
- ... />
384
-
385
- // Custom message
386
- <ServerNode cpuLoad={92}
387
- alert={{ msg: "CPU overload — scale out", offsetX: -160, offsetY: -60, align: "left" }}
388
- ... />
389
-
390
- // Custom thresholds via dialogMetrics
391
- <ServerNode
392
- dialogMetrics={[
393
- { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67,
394
- warnAt: 50, critAt: 75 }, // Alert at 67 because critAt=75
395
- ]}
396
- alert={{ offsetX: -160, offsetY: -60, align: "left" }}
397
- ... />
398
- ```
399
-
400
- Alert `align` options: `"left"`, `"right"`, `"top"`, `"bottom"`.
401
-
402
- Alert prop shape:
403
-
404
- ```ts
405
- alert?: {
406
- msg?: string; // Custom message (auto-generated if omitted)
407
- offsetX?: number; // X offset from node (default 110)
408
- offsetY?: number; // Y offset from node (default -30)
409
- align?: "left" | "right" | "top" | "bottom"; // Default "right"
410
- internalRef?: string; // Sub-component ID
411
- }
412
- ```
413
-
414
- ### 7. Drill-down internals custom subComponents
415
-
416
- When clicking a node, the drill-down shows internal 3D sub-components. Default sub-components are auto-generated per type. Override with `subComponents`:
417
-
418
- ```tsx
419
- import { CPU3D, Memory3D, DriveBay3D, ThreadPool3D, NetworkBlock3D, Platter3D, Port3D } from "react-dashstream";
420
- import type { SubComponentConfig } from "react-dashstream";
421
-
422
- const internals: SubComponentConfig[] = [
423
- { id: "cpu-0", label: "CPU-0", status: "online",
424
- element: <CPU3D label="CPU-0" load={67} color="#00e5ff" /> },
425
- { id: "heap", label: "HEAP", status: "warning",
426
- element: <Memory3D label="HEAP" usedPercent={92} color="#8855ee" status="warning" /> },
427
- { id: "drive", label: "DRIVE-0", status: "online",
428
- element: <DriveBay3D label="DRIVE-0" color="#00e5ff" activity={true} /> },
429
- { id: "threads", label: "THREADS", status: "online",
430
- element: <ThreadPool3D label="THREADS" color="#00e5ff" /> },
431
- { id: "net", label: "NET", status: "online",
432
- element: <NetworkBlock3D label="NET" color="#00e5ff" /> },
433
- ];
434
-
435
- <ServerNode subComponents={internals} ... />
436
- ```
437
-
438
- ```ts
439
- interface SubComponentConfig {
440
- id: string; // Unique key
441
- label: string; // Display name
442
- status: ComponentStatus; // LED color
443
- detail?: string; // Fault description
444
- element: ReactNode; // 3D element to render
445
- }
446
- ```
447
-
448
- All compound nodes accept `subComponents`.
449
-
450
- ### 8. Graph series — custom sparklines
451
-
452
- The drill-down's historical panel shows sparkline charts. Default graphs are auto-generated. Override with `graphSeries`:
453
-
454
- ```tsx
455
- import type { GraphSeries } from "react-dashstream";
456
-
457
- const graphs: GraphSeries[] = [
458
- { id: "cpu", label: "CPU-0", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68, 55] },
459
- { id: "mem", label: "HEAP", unit: "%", color: "#8855ee", data: [60, 65, 72, 78, 82, 75] },
460
- { id: "iops", label: "DISK", unit: "k/s", color: "#ff8c00", data: [12, 15, 22, 18, 25, 20] },
461
- ];
462
-
463
- <ServerNode graphSeries={graphs} ... />
464
- ```
465
-
466
- ```ts
467
- interface GraphSeries {
468
- id: string; // Unique key
469
- label: string; // Label above sparkline
470
- unit: string; // Suffix
471
- color: string; // Line color
472
- data: number[]; // Points (most recent last)
473
- }
474
- ```
475
-
476
- All compound nodes accept `graphSeries`.
477
-
478
- ### 9. Data hooks — programmatic access
479
-
480
- Access live data context anywhere inside the dashboard:
481
-
482
- ```tsx
483
- import { useAIOpsData, useAIOpsDataOptional, useQueryResult } from "react-dashstream";
484
-
485
- function CustomWidget() {
486
- const { data, isRefreshing, lastRefreshError, lastRefreshTime, credentialsSet } = useAIOpsData();
487
- const cpu = useQueryResult('cpu_usage{instance="srv-01"}');
488
- return <div>CPU: {String(cpu)}</div>;
489
- }
490
- ```
491
-
492
- | Hook | Returns | Throws? |
493
- | ------------------------ | -------------------------- | ---------------------------- |
494
- | `useAIOpsData()` | `DataContextValue` | Yes outside `DataProvider` |
495
- | `useAIOpsDataOptional()` | `DataContextValue \| null` | No |
496
- | `useQueryResult(query)` | `unknown \| null` | Yes — outside `DataProvider` |
497
-
498
- ```ts
499
- interface DataContextValue {
500
- data: Record<string, unknown>; // Raw responses keyed by query
501
- isRefreshing: boolean;
502
- lastRefreshError: string | null;
503
- lastRefreshTime: Date | null;
504
- credentialsSet: boolean;
505
- setCredentials: (creds: Credentials) => void;
506
- }
507
- ```
508
-
509
- ---
510
-
511
- ## Exports
512
-
513
- Everything below is exported from `react-dashstream`.
514
-
515
- ### Default export
516
-
517
- `AIOPsDashboard` — main dashboard shell component.
518
-
519
- ### Compound nodes
520
-
521
- `ServerNode`, `DatabaseNode`, `WebDispatcherNode`, `MessageServerNode`, `HumanNode`
522
-
523
- ### Layout
524
-
525
- `Carousel`, `Service`, `ServiceNode`
526
-
527
- ### 3D models
528
-
529
- `Server3D`, `Database3D`, `WebDispatcher3D`, `MessageServer3D`, `Human3D`
530
-
531
- ### Internal 3D components (for drill-down)
532
-
533
- `CPU3D`, `Memory3D`, `DriveBay3D`, `NetworkBlock3D`, `ThreadPool3D`, `Platter3D`, `Port3D`
534
-
535
- ### Indicators and connections
536
-
537
- `SvgConnection`, `SyncBridge`, `NodeCallout`, `HoloBase`
538
-
539
- ### Dialogs
540
-
541
- `ServiceDialog`, `ComponentDialog`, `HistoricalGraphPanel`, `ComponentDrillView`
542
-
543
- ### Pre-built services
544
-
545
- `SAPService`, `ExchangeService`
546
-
547
- SAP helpers: `computeSAPServiceStatus`, `computeSAPDialogMetrics`, `computeSAPDialogAlerts`, `SAP_CONNECTIONS`
548
-
549
- Exchange helpers: `computeExchangeServiceStatus`, `computeExchangeDialogMetrics`, `computeExchangeDialogAlerts`, `EXCHANGE_CONNECTIONS`
550
-
551
- SAP sub-component helpers: `getServerSubComponents`, `getServerGraphSeries`, `getDispatcherSubComponents`, `getDispatcherGraphSeries`, `getMessageServerSubComponents`, `getMessageServerGraphSeries`, `getDatabaseSubComponents`, `getDatabaseGraphSeries`
552
-
553
- ### Data layer
554
-
555
- `DataProvider`, `useAIOpsData`, `useAIOpsDataOptional`, `useQueryResult`, `defaultDataTransform`
556
-
557
- ### Theme
558
-
559
- `STATUS_CFG`, `HOLO_CYAN` (`"#00e5ff"`), `HOLO_BLUE` (`"#0055cc"`), `HOLO_SURFACE`, `HOLO_GLASS`, `makeFaceStyles(W, H, D)`
560
-
561
- ### Context hooks
562
-
563
- `CarouselContext`, `CarouselItemContext`, `useCarouselContext`, `useCarouselItemContext`, `ServiceContext`
564
-
565
- ### Type exports
566
-
567
- `AIOPsDashboardProps`, `ServiceMeta`, `ServiceMetricBinding`, `ComponentStatus`, `StatusCfg`, `FaceName`, `Base3DProps`, `ComponentType`, `ComponentContext`, `ComponentDialogMetric`, `SubComponentConfig`, `GraphSeries`, `SelectedComponent`, `ConnectionConfig`, `ViewState`, `DataBinding`, `DataBindings`, `DataProviderConfig`, `Credentials`, `DataContextValue`, `ServiceProps`, `ServiceContextValue`, `ServiceNodeProps`, `CarouselProps`, `ServerNodeProps`, `DatabaseNodeProps`, `HumanNodeProps`, `WebDispatcherNodeProps`, `MessageServerNodeProps`, `SvgConnectionProps`, `SyncBridgeProps`, `NodeCalloutProps`, `ServiceDialogProps`, `ServiceDialogMetric`, `ServiceDialogAlert`, `ComponentDialogProps`, `ComponentDrillViewProps`, `CarouselContextValue`, `CarouselItemContextValue`, `SAPServiceProps`, `SAPServiceOwnProps`, `SAPServiceConfig`, `ExchangeServiceProps`, `ExchangeServiceOwnProps`, `ExchangeServiceConfig`
568
-
569
- ---
570
-
571
- ## Key types quick reference
572
-
573
- ### AIOPsDashboardProps
574
-
575
- ```ts
576
- interface AIOPsDashboardProps {
577
- title?: string;
578
- brandName?: string; // Default: "BUSAUD AIOps"
579
- brandTag?: string; // Default: "3D MONITOR"
580
- services?: ServiceMeta[];
581
- backgroundImage?: string;
582
- logoUrl?: string;
583
- carouselSpeed?: number; // Default: 0.006
584
- fontFamily?: string;
585
- liveData?: boolean; // Default: false
586
- dataEndpoint?: string;
587
- dataBindings?: DataBindings;
588
- dataTransform?: (raw: unknown) => unknown;
589
- dataRefreshInterval?: number; // Default: 60000
590
- serviceDataBindings?: Record<string, ServiceMetricBinding[]>;
591
- children: React.ReactNode;
592
- }
593
- ```
594
-
595
- ### ServiceMeta
596
-
597
- ```ts
598
- interface ServiceMeta {
599
- name: string; // Must match child component name
600
- status: ComponentStatus;
601
- dbSync?: boolean; // Default: true
602
- metrics?: ServiceDialogMetric[];
603
- alerts?: ServiceDialogAlert[];
604
- }
605
-
606
- interface ServiceDialogMetric {
607
- label: string;
608
- value: string;
609
- color: string;
610
- }
611
-
612
- interface ServiceDialogAlert {
613
- level: "info" | "warning" | "critical";
614
- message: string;
615
- }
616
- ```
617
-
618
- ### ConnectionConfig
619
-
620
- ```ts
621
- interface ConnectionConfig {
622
- from: [number, number];
623
- to: [number, number];
624
- visibleAtPhase?: number; // Default: 0
625
- color?: string; // Default: "#00e5ff"
626
- duration?: string; // Default: "1.2s"
627
- }
628
- ```
629
-
630
- ### ComponentStatus
631
-
632
- ```ts
633
- type ComponentStatus = "online" | "warning" | "critical" | "offline";
634
- ```
635
-
636
- | Status | Color | Glow |
637
- | ---------- | --------- | -------- |
638
- | `online` | `#00e5ff` | cyan |
639
- | `warning` | `#ff8c00` | orange |
640
- | `critical` | `#ff2255` | red |
641
- | `offline` | `#1e3a5a` | dim blue |
642
-
643
- ---
644
-
645
- ## Endpoint contract
646
-
647
- | Aspect | Requirement |
648
- | ------------ | -------------------------------------------------------------- |
649
- | **Method** | `GET` |
650
- | **URL** | `<dataEndpoint>?query=<urlEncodedQuery>` |
651
- | **Headers** | `access-key` and `access-secret-key` |
652
- | **Response** | Plain text body (trimmed). Default transform parses as number. |
653
- | **Errors** | Non-2xx → counted as failure per query |
654
-
655
- For JSON responses, provide a custom `dataTransform`:
656
-
657
- ```tsx
658
- dataTransform={(raw) => {
659
- const parsed = JSON.parse(String(raw));
660
- return parsed?.data?.result?.[0]?.value?.[1] ?? raw;
661
- }}
662
- ```
663
-
664
- ---
665
-
666
- ## Standalone DataProvider
667
-
668
- Use the data layer without `AIOPsDashboard`:
669
-
670
- ```tsx
671
- import { DataProvider, useAIOpsData } from "react-dashstream";
672
-
673
- <DataProvider
674
- config={{
675
- endpoint: "https://prometheus.example.com/api/v1/query",
676
- queries: ['cpu_usage{instance="srv-01"}'],
677
- refreshInterval: 15000,
678
- }}
679
- >
680
- <MyContent />
681
- </DataProvider>;
682
- ```
683
-
684
- ---
685
-
686
- ## Node positioning guide
687
-
688
- The topology scene is ~660×640 pixels.
689
-
690
- | Layer | Typical `ey` | `visibleAtPhase` | Component |
691
- | ----------- | ------------ | ---------------- | ------------------- |
692
- | Users (top) | 100 | 2 | `HumanNode` |
693
- | Dispatcher | 230 | 2 | `WebDispatcherNode` |
694
- | App servers | 350–380 | 3 | `ServerNode` |
695
- | Databases | 500–520 | 4 | `DatabaseNode` |
696
-
697
- Center X = `330`. Left/right columns: `200` / `460`. Three-column: `165` / `330` / `495`.
698
-
699
- `compactOffset` controls node position in the compact carousel view relative to center.
700
-
701
- ---
702
-
703
- ## Usage patterns
704
-
705
- ### Minimal static dashboard
706
-
707
- ```tsx
708
- import "react-dashstream/dist/index.css";
709
- import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
710
-
711
- <AIOPsDashboard brandName="MY DASHBOARD" services={[{ name: "Svc", status: "online" }]}>
712
- <Service
713
- name="Svc"
714
- status="online"
715
- connections={[{ from: [330, 200], to: [330, 380], visibleAtPhase: 3 }]}
716
- >
717
- <ServerNode
718
- ex={330}
719
- ey={380}
720
- compactOffset={{ x: 0, y: 0 }}
721
- zIndex={8}
722
- name="SRV-01"
723
- status="online"
724
- cpuLoad={42}
725
- memLoad={60}
726
- />
727
- </Service>
728
- </AIOPsDashboard>;
729
- ```
730
-
731
- ### Full live-data dashboard
732
-
733
- ```tsx
734
- <AIOPsDashboard
735
- brandName="LIVE MONITOR"
736
- services={services}
737
- liveData={true}
738
- dataEndpoint="https://prometheus.example.com/api/v1/query"
739
- dataRefreshInterval={10000}
740
- dataBindings={{
741
- "My Service": {
742
- cpuLoad: 'cpu_usage{instance="srv-01"}',
743
- status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
744
- },
745
- }}
746
- serviceDataBindings={{
747
- "My Service": [{ label: "CPU", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" }],
748
- }}
749
- >
750
- <MyService name="My Service" />
751
- </AIOPsDashboard>
752
- ```
753
-
754
- ### Custom service with all data features
755
-
756
- ```tsx
757
- function MyService({ name, status, cpuLoad, memLoad, dbCapacity, dbStatus }: any) {
758
- return (
759
- <Service
760
- name={name}
761
- status={status ?? "online"}
762
- connections={[
763
- { from: [330, 200], to: [200, 380], visibleAtPhase: 3 },
764
- { from: [330, 200], to: [460, 380], visibleAtPhase: 3 },
765
- ]}
766
- >
767
- <ServerNode
768
- ex={200}
769
- ey={380}
770
- compactOffset={{ x: -30, y: -20 }}
771
- zIndex={8}
772
- name="SRV-01"
773
- subLabel="APP SERVER"
774
- status={status ?? "online"}
775
- cpuLoad={cpuLoad ?? 42}
776
- memLoad={memLoad ?? 60}
777
- alert={{ offsetX: -160, offsetY: -60, align: "left" }}
778
- dialogMetrics={[
779
- { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad ?? 42 },
780
- { id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad ?? 60 },
781
- ]}
782
- subComponents={[
783
- {
784
- id: "cpu",
785
- label: "CPU-0",
786
- status: "online",
787
- element: <CPU3D label="CPU-0" load={cpuLoad ?? 42} />,
788
- },
789
- {
790
- id: "mem",
791
- label: "HEAP",
792
- status: "online",
793
- element: <Memory3D label="HEAP" usedPercent={memLoad ?? 60} />,
794
- },
795
- ]}
796
- graphSeries={[
797
- { id: "cpu", label: "CPU", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68] },
798
- ]}
799
- />
800
- <DatabaseNode
801
- ex={460}
802
- ey={380}
803
- compactOffset={{ x: 30, y: -20 }}
804
- zIndex={7}
805
- name="DB-01"
806
- subLabel="PRIMARY"
807
- status={dbStatus ?? "online"}
808
- capacity={dbCapacity ?? 55}
809
- alert={{ offsetX: 160, offsetY: -60, align: "right" }}
810
- />
811
- </Service>
812
- );
813
- }
814
- ```
815
-
816
- ---
817
-
818
- ## Build and development
819
-
820
- ```bash
821
- npm run dev # Vite dev server (loads example/Dashboard.tsx)
822
- npm run build # TypeScript check + Vite library build
823
- npm run build:lib # Vite library build only
824
- npm run preview # Preview production build
825
- ```
826
-
827
- Output: `dist/index.js` (ESM), `dist/index.d.ts`, `dist/index.css`.
828
-
829
- ---
830
-
831
- ## Common pitfalls
832
-
833
- 1. **Missing CSS import** — Always import `react-dashstream/dist/index.css`.
834
- 2. **Name mismatch** `name` on child components must exactly match keys in `dataBindings`, `serviceDataBindings`, and `ServiceMeta.name`.
835
- 3. **Credentials are in-memory only** Page refresh requires re-entering credentials.
836
- 4. **Default transform is plain-text** — For JSON endpoints, provide a custom `dataTransform`.
837
- 5. **Polling only** No WebSocket/SSE support; uses `setInterval`.
838
- 6. **`visibleAtPhase` matters** — Nodes without proper values won't animate during expansion.
839
- 7. **Connection coordinates** `from`/`to` arrays use `[x, y]` matching `ex`/`ey` of connected nodes.
840
- 8. **`dialogMetrics` replaces defaults** — When provided, all default gauges (CPU/Memory/Storage) are removed.
841
- 9. **`subComponents` replaces defaults** — When provided, all auto-generated sub-components are removed.
842
- 10. **`graphSeries` replaces defaults** — When provided, all auto-generated sparklines are removed.
1
+ # React DashStream — AI Coding Assistant Skill
2
+
3
+ > **Package**: `react-dashstream` (npm)
4
+ > **Version**: 0.0.4
5
+ > **Peer deps**: `react` ^18 || ^19, `react-dom` ^18 || ^19
6
+ > **License**: MIT
7
+
8
+ Use this skill when the user is working with the `react-dashstream` package — a holographic 3D infrastructure monitoring dashboard built entirely with CSS 3D transforms (no WebGL, no canvas).
9
+
10
+ ---
11
+
12
+ ## What this package does
13
+
14
+ React DashStream renders a rotating 3D carousel of infrastructure services. Each service can be clicked to expand into a multi-tiered topology showing servers, databases, dispatchers, message servers, and user nodes connected by animated SVG lines. Components can be drilled into to reveal internal sub-components with sparkline graphs and gauges.
15
+
16
+ Two modes:
17
+
18
+ 1. **Static mode** — pass props directly for demo or snapshot views.
19
+ 2. **Live data mode** — poll an HTTP endpoint with PromQL queries and auto-inject resolved values as component props.
20
+
21
+ ---
22
+
23
+ ## Installation and CSS
24
+
25
+ ```bash
26
+ npm install react-dashstream
27
+ ```
28
+
29
+ ```tsx
30
+ import "react-dashstream/dist/index.css";
31
+ import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
32
+ ```
33
+
34
+ **Always import the CSS file** — without it, no styles render.
35
+
36
+ ---
37
+
38
+ ## Architecture
39
+
40
+ ```
41
+ AIOPsDashboard ← Shell: header, state, optional DataProvider
42
+ └─ Carousel ← 3D orbit, rotation, hosts dialogs
43
+ └─ Service ← Per-service container, connections, holo base
44
+ ├─ ServerNode ← Compound: ServiceNode + Server3D
45
+ ├─ DatabaseNode ← Compound: ServiceNode + Database3D
46
+ ├─ WebDispatcherNode ← Compound: ServiceNode + WebDispatcher3D
47
+ ├─ MessageServerNode ← Compound: ServiceNode + MessageServer3D
48
+ ├─ HumanNode ← Compound: ServiceNode + Human3D (SVG)
49
+ ├─ SvgConnection ← Animated dashed topology line
50
+ ├─ SyncBridge ← DB replication indicator
51
+ ├─ NodeCallout ← Alert callout (auto from thresholds)
52
+ └─ HoloBase ← Neon platform (auto-rendered)
53
+
54
+ ServiceDialog ← Service-level KPI panel (auto on expand)
55
+ ComponentDialog ← Drill-down with internals + graphs
56
+ ├─ HistoricalGraphPanel ← Sparkline charts
57
+ └─ CPU3D / Memory3D / DriveBay3D / NetworkBlock3D / ThreadPool3D / Platter3D / Port3D
58
+
59
+ DataProvider ← Polling engine + credentials modal
60
+
61
+ EventView ← Standalone event console (table view)
62
+ ```
63
+
64
+ ### Source layout
65
+
66
+ ```
67
+ src/
68
+ ├── index.ts # Public API barrel
69
+ ├── AIOPsDashboard.tsx # Dashboard shell + live-data wiring
70
+ ├── types.ts # Shared topology/drill-down types
71
+ ├── theme.ts # Status tokens, holo palette, 3D face helpers
72
+ ├── styles.css / index.css # Library + dashboard styles
73
+ ├── data/
74
+ ├── DataProvider.tsx # Polling engine, context, hooks
75
+ ├── CredentialsModal.tsx # Auth prompt UI
76
+ └── index.ts
77
+ ├── components/ # ~30 UI components
78
+ │ ├── Carousel.tsx, Service.tsx, ServiceNode.tsx
79
+ │ ├── ServerNode.tsx, DatabaseNode.tsx, WebDispatcherNode.tsx, etc.
80
+ │ ├── Server3D.tsx, Database3D.tsx, etc.
81
+ │ ├── ServiceDialog.tsx, ComponentDialog.tsx
82
+ │ ├── Internal3DComponents.tsx, HistoricalGraphPanel.tsx
83
+ ├── ComponentDrillView.tsx, SvgConnection.tsx, SyncBridge.tsx
84
+ ├── NodeCallout.tsx, HoloBase.tsx, CarouselContext.ts
85
+ │ ├── EventView/ # Operations event console
86
+ ├── EventView.tsx # Main component (filter, sort, paginate, search)
87
+ │ ├── EventView.css # Holographic table styles
88
+ │ │ ├── types.ts # AIOpsEvent, EventSeverity, EventClass, etc.
89
+ │ │ ├── mockData.ts # 28 realistic mock events
90
+ │ │ └── index.ts
91
+ │ └── index.ts
92
+ ├── services/
93
+ │ ├── SAPService.tsx, ExchangeService.tsx
94
+ │ ├── sapSubComponents.tsx
95
+ │ └── index.ts
96
+ example/
97
+ ├── Dashboard.tsx
98
+ └── services/
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Complete data flow reference
104
+
105
+ There are **9 distinct data paths** in this package. Understanding them is critical for building live dashboards.
106
+
107
+ ### 1. Data bindings — live props injected into service components
108
+
109
+ `dataBindings` maps **service name → prop name → PromQL query**. The dashboard polls each query, applies a transform, and injects the result as a prop on the matched child component.
110
+
111
+ ```ts
112
+ type DataBinding = string | { query: string; transform?: (raw: unknown) => unknown };
113
+ type DataBindings = Record<string, Record<string, DataBinding>>;
114
+ ```
115
+
116
+ - Bare string → uses global `dataTransform` (defaults to numeric parsing)
117
+ - Object `{ query, transform }` → uses per-binding transform
118
+ - Service name key must match child's `name` prop **exactly**
119
+
120
+ ```tsx
121
+ const dataBindings: DataBindings = {
122
+ "My Service": {
123
+ cpuLoad: 'cpu_usage{instance="srv-01"}', // bare string → number
124
+ memLoad: 'memory_usage{instance="srv-01"}', // bare string → number
125
+ status: {
126
+ // object → custom transform
127
+ query: 'cpu_usage{instance="srv-01"}',
128
+ transform: (raw) => {
129
+ const n = Number(raw);
130
+ if (n >= 85) return "critical";
131
+ if (n >= 70) return "warning";
132
+ return "online";
133
+ },
134
+ },
135
+ dbCapacity: 'disk_capacity{instance="db-01"}',
136
+ dbStatus: { query: 'disk_capacity{instance="db-01"}', transform: statusFromValue },
137
+ },
138
+ };
139
+ ```
140
+
141
+ #### Multi-component services
142
+
143
+ When a service has multiple nodes of the same type (e.g. three servers), use a flat naming convention with prefixes. Each `DataBinding` maps **one prop → one query** — there is no array or object binding type.
144
+
145
+ ```tsx
146
+ interface ServiceXProps {
147
+ name: string;
148
+ status?: ComponentStatus;
149
+ srv1CpuLoad?: number;
150
+ srv1MemLoad?: number;
151
+ srv1Status?: ComponentStatus;
152
+ srv2CpuLoad?: number;
153
+ srv2MemLoad?: number;
154
+ srv2Status?: ComponentStatus;
155
+ srv3CpuLoad?: number;
156
+ srv3MemLoad?: number;
157
+ srv3Status?: ComponentStatus;
158
+ dbCapacity?: number;
159
+ dbStatus?: ComponentStatus;
160
+ }
161
+
162
+ function ServiceX({
163
+ name, status = "online",
164
+ srv1CpuLoad = 54, srv1MemLoad = 58, srv1Status = "online",
165
+ srv2CpuLoad = 63, srv2MemLoad = 66, srv2Status = "online",
166
+ srv3CpuLoad = 78, srv3MemLoad = 71, srv3Status = "online",
167
+ dbCapacity = 68, dbStatus = "online",
168
+ }: ServiceXProps) {
169
+ return (
170
+ <Service name={name} status={status} connections={[/* ... */]}>
171
+ <ServerNode name="SRV-X1" status={srv1Status}
172
+ cpuLoad={srv1CpuLoad} memLoad={srv1MemLoad} ... />
173
+ <ServerNode name="SRV-X2" status={srv2Status}
174
+ cpuLoad={srv2CpuLoad} memLoad={srv2MemLoad} ... />
175
+ <ServerNode name="SRV-X3" status={srv3Status}
176
+ cpuLoad={srv3CpuLoad} memLoad={srv3MemLoad} ... />
177
+ <DatabaseNode name="DB-X1" status={dbStatus}
178
+ capacity={dbCapacity} ... />
179
+ </Service>
180
+ );
181
+ }
182
+ ```
183
+
184
+ Bind each prefixed prop individually:
185
+
186
+ ```tsx
187
+ dataBindings={{
188
+ ServiceX: {
189
+ status: { query: 'status{instance="svcx"}', transform: statusFromValue },
190
+ srv1CpuLoad: 'cpu_usage{instance="srvx-01"}',
191
+ srv1MemLoad: 'memory_usage{instance="srvx-01"}',
192
+ srv1Status: { query: 'status{instance="srvx-01"}', transform: statusFromValue },
193
+ srv2CpuLoad: 'cpu_usage{instance="srvx-02"}',
194
+ srv2MemLoad: 'memory_usage{instance="srvx-02"}',
195
+ srv2Status: { query: 'status{instance="srvx-02"}', transform: statusFromValue },
196
+ srv3CpuLoad: 'cpu_usage{instance="srvx-03"}',
197
+ srv3MemLoad: 'memory_usage{instance="srvx-03"}',
198
+ srv3Status: { query: 'status{instance="srvx-03"}', transform: statusFromValue },
199
+ dbCapacity: 'disk_capacity{instance="dbx-01"}',
200
+ dbStatus: { query: 'status{instance="dbx-01"}', transform: statusFromValue },
201
+ },
202
+ }}
203
+ ```
204
+
205
+ Use a consistent convention like `srv1CpuLoad`, `srv2CpuLoad`. The service component maps each prefixed prop to the correct node.
206
+
207
+ ### 2. Service dialog — static ServiceMeta metrics
208
+
209
+ The **ServiceDialog** panel shows KPIs when a service is expanded. Static metrics are passed via `ServiceMeta`:
210
+
211
+ ```tsx
212
+ const services: ServiceMeta[] = [
213
+ {
214
+ name: "My Service",
215
+ status: "online",
216
+ metrics: [
217
+ { label: "Uptime", value: "99.99%", color: "#00ff88" },
218
+ { label: "Avg Latency", value: "8ms", color: "#00e5ff" },
219
+ ],
220
+ alerts: [
221
+ { level: "info", message: "All Systems Nominal" },
222
+ { level: "warning", message: "High CPU on SRV-01" },
223
+ { level: "critical", message: "DB connection pool exhausted" },
224
+ ],
225
+ },
226
+ ];
227
+ ```
228
+
229
+ ### 3. Service dialog live serviceDataBindings
230
+
231
+ Replace static metrics with live-fetched values using `serviceDataBindings`:
232
+
233
+ ```tsx
234
+ serviceDataBindings={{
235
+ "My Service": [
236
+ { label: "CPU Load", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" },
237
+ { label: "Memory", query: 'memory_usage{instance="srv-01"}', unit: "%", color: "#bb55ff" },
238
+ { label: "Disk", query: 'disk_capacity{instance="db-01"}', unit: "%", color: "#ff8c00" },
239
+ ],
240
+ }}
241
+ ```
242
+
243
+ ```ts
244
+ interface ServiceMetricBinding {
245
+ label: string; // Row label
246
+ query: string; // PromQL query
247
+ unit?: string; // Suffix (e.g. "%", "ms")
248
+ color?: string; // Accent color
249
+ transform?: (raw: unknown) => string; // Custom formatter
250
+ }
251
+ ```
252
+
253
+ When provided, live values **replace** static `ServiceMeta.metrics` for that service.
254
+
255
+ ### 4. Component dialog — default gauges
256
+
257
+ The **ComponentDialog** appears when clicking a node. Default gauges are derived from node type:
258
+
259
+ - **Server**: CPU (from `cpuLoad`), Memory (from `memLoad`), Storage
260
+ - **Database**: Capacity (from `capacity`), Memory, Storage
261
+ - **Dispatcher**: Traffic (from `traffic`), Memory, Storage
262
+ - **Message Server**: Queue (from `queueDepth`), Memory, Storage
263
+
264
+ Default thresholds: warn at 70, critical at 85.
265
+
266
+ ### 5. Component dialog — custom dialogMetrics
267
+
268
+ Override default gauges with `dialogMetrics` on any compound node:
269
+
270
+ ```tsx
271
+ <ServerNode
272
+ name="SRV-01"
273
+ status="online"
274
+ cpuLoad={67}
275
+ memLoad={72}
276
+ ex={200}
277
+ ey={380}
278
+ compactOffset={{ x: -30, y: -20 }}
279
+ zIndex={8}
280
+ dialogMetrics={[
281
+ { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67, unit: "%" },
282
+ { id: "mem", label: "MEMORY", sublabel: "HEAP USAGE", value: 72, unit: "%" },
283
+ { id: "iops", label: "IOPS", sublabel: "DISK OPS", value: 45, unit: "k/s", icon: "disk" },
284
+ {
285
+ id: "threads",
286
+ label: "THREADS",
287
+ sublabel: "ACTIVE",
288
+ value: 82,
289
+ unit: "%",
290
+ warnAt: 60,
291
+ critAt: 80,
292
+ icon: "cpu",
293
+ },
294
+ ]}
295
+ />
296
+ ```
297
+
298
+ ```ts
299
+ interface ComponentDialogMetric {
300
+ id: string; // Unique key
301
+ label: string; // Upper label
302
+ sublabel: string; // Lower label
303
+ value: number; // 0–100
304
+ unit?: string; // Default "%"
305
+ icon?: "cpu" | "mem" | "disk"; // Default "cpu"
306
+ warnAt?: number; // Default 70
307
+ critAt?: number; // Default 85
308
+ color?: string; // Override bar color (bypasses threshold coloring)
309
+ }
310
+ ```
311
+
312
+ All compound nodes accept `dialogMetrics`: `ServerNode`, `DatabaseNode`, `WebDispatcherNode`, `MessageServerNode`.
313
+
314
+ **Live component dialog metrics pattern**: To feed live query values into custom gauges, expose each metric as a prop on your service component. Use `dataBindings` to inject them. Then build the `dialogMetrics` array from those props:
315
+
316
+ ```tsx
317
+ // Service component — accepts individual metric props, builds dialogMetrics from them
318
+ function MyService({ name, status = "online", cpuLoad = 42, memLoad = 60, iops = 20 }: any) {
319
+ return (
320
+ <Service
321
+ name={name}
322
+ status={status}
323
+ connections={
324
+ [
325
+ /* ... */
326
+ ]
327
+ }
328
+ >
329
+ <ServerNode
330
+ name="SRV-01"
331
+ status={status}
332
+ cpuLoad={cpuLoad}
333
+ memLoad={memLoad}
334
+ ex={200}
335
+ ey={380}
336
+ compactOffset={{ x: -30, y: -20 }}
337
+ zIndex={8}
338
+ dialogMetrics={[
339
+ { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad },
340
+ { id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad },
341
+ {
342
+ id: "iops",
343
+ label: "IOPS",
344
+ sublabel: "DISK OPS",
345
+ value: iops,
346
+ icon: "disk",
347
+ warnAt: 50,
348
+ critAt: 80,
349
+ },
350
+ ]}
351
+ />
352
+ </Service>
353
+ );
354
+ }
355
+
356
+ // Dashboard — bind each prop to a query
357
+ <AIOPsDashboard
358
+ liveData
359
+ dataEndpoint="..."
360
+ dataBindings={{
361
+ "My Service": {
362
+ cpuLoad: 'cpu_usage{instance="srv-01"}',
363
+ memLoad: 'memory_usage{instance="srv-01"}',
364
+ iops: 'disk_iops{instance="srv-01"}',
365
+ status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
366
+ },
367
+ }}
368
+ >
369
+ <MyService name="My Service" />
370
+ </AIOPsDashboard>;
371
+ ```
372
+
373
+ The dashboard injects `cpuLoad`, `memLoad`, `iops` as live props → they flow into `dialogMetrics` → gauges update automatically on every poll. Works for any number of custom metrics.
374
+
375
+ ### 6. Alerts — automatic threshold detection
376
+
377
+ Nodes auto-render `NodeCallout` alerts when metrics breach thresholds:
378
+
379
+ - **Warning** at 70% → orange callout
380
+ - **Critical** at 85% → red callout
381
+
382
+ Sources (checked in priority order):
383
+
384
+ 1. `dialogMetrics` values vs their `warnAt`/`critAt`
385
+ 2. Context values (`cpuLoad`, `memLoad`, `traffic`, `queueDepth`, `capacity`) vs default thresholds
386
+
387
+ ```tsx
388
+ // Auto-alert from cpuLoad > 85
389
+ <ServerNode cpuLoad={92} memLoad={64}
390
+ alert={{ offsetX: -160, offsetY: -60, align: "left" }}
391
+ ... />
392
+
393
+ // Custom message
394
+ <ServerNode cpuLoad={92}
395
+ alert={{ msg: "CPU overload — scale out", offsetX: -160, offsetY: -60, align: "left" }}
396
+ ... />
397
+
398
+ // Custom thresholds via dialogMetrics
399
+ <ServerNode
400
+ dialogMetrics={[
401
+ { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67,
402
+ warnAt: 50, critAt: 75 }, // Alert at 67 because critAt=75
403
+ ]}
404
+ alert={{ offsetX: -160, offsetY: -60, align: "left" }}
405
+ ... />
406
+ ```
407
+
408
+ Alert `align` options: `"left"`, `"right"`, `"top"`, `"bottom"`.
409
+
410
+ Alert prop shape:
411
+
412
+ ```ts
413
+ alert?: {
414
+ msg?: string; // Custom message (auto-generated if omitted)
415
+ offsetX?: number; // X offset from node (default 110)
416
+ offsetY?: number; // Y offset from node (default -30)
417
+ align?: "left" | "right" | "top" | "bottom"; // Default "right"
418
+ internalRef?: string; // Sub-component ID
419
+ }
420
+ ```
421
+
422
+ ### 7. Drill-down internals custom subComponents
423
+
424
+ When clicking a node, the drill-down shows internal 3D sub-components. Default sub-components are auto-generated per type. Override with `subComponents`:
425
+
426
+ ```tsx
427
+ import { CPU3D, Memory3D, DriveBay3D, ThreadPool3D, NetworkBlock3D, Platter3D, Port3D } from "react-dashstream";
428
+ import type { SubComponentConfig } from "react-dashstream";
429
+
430
+ const internals: SubComponentConfig[] = [
431
+ { id: "cpu-0", label: "CPU-0", status: "online",
432
+ element: <CPU3D label="CPU-0" load={67} color="#00e5ff" /> },
433
+ { id: "heap", label: "HEAP", status: "warning",
434
+ element: <Memory3D label="HEAP" usedPercent={92} color="#8855ee" status="warning" /> },
435
+ { id: "drive", label: "DRIVE-0", status: "online",
436
+ element: <DriveBay3D label="DRIVE-0" color="#00e5ff" activity={true} /> },
437
+ { id: "threads", label: "THREADS", status: "online",
438
+ element: <ThreadPool3D label="THREADS" color="#00e5ff" /> },
439
+ { id: "net", label: "NET", status: "online",
440
+ element: <NetworkBlock3D label="NET" color="#00e5ff" /> },
441
+ ];
442
+
443
+ <ServerNode subComponents={internals} ... />
444
+ ```
445
+
446
+ ```ts
447
+ interface SubComponentConfig {
448
+ id: string; // Unique key
449
+ label: string; // Display name
450
+ status: ComponentStatus; // LED color
451
+ detail?: string; // Fault description
452
+ element: ReactNode; // 3D element to render
453
+ }
454
+ ```
455
+
456
+ All compound nodes accept `subComponents`.
457
+
458
+ ### 8. Graph series custom sparklines
459
+
460
+ The drill-down's historical panel shows sparkline charts. Default graphs are auto-generated. Override with `graphSeries`:
461
+
462
+ ```tsx
463
+ import type { GraphSeries } from "react-dashstream";
464
+
465
+ const graphs: GraphSeries[] = [
466
+ { id: "cpu", label: "CPU-0", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68, 55] },
467
+ { id: "mem", label: "HEAP", unit: "%", color: "#8855ee", data: [60, 65, 72, 78, 82, 75] },
468
+ { id: "iops", label: "DISK", unit: "k/s", color: "#ff8c00", data: [12, 15, 22, 18, 25, 20] },
469
+ ];
470
+
471
+ <ServerNode graphSeries={graphs} ... />
472
+ ```
473
+
474
+ ```ts
475
+ interface GraphSeries {
476
+ id: string; // Unique key
477
+ label: string; // Label above sparkline
478
+ unit: string; // Suffix
479
+ color: string; // Line color
480
+ data: number[]; // Points (most recent last)
481
+ }
482
+ ```
483
+
484
+ All compound nodes accept `graphSeries`.
485
+
486
+ ### 9. Data hooks programmatic access
487
+
488
+ Access live data context anywhere inside the dashboard:
489
+
490
+ ```tsx
491
+ import { useAIOpsData, useAIOpsDataOptional, useQueryResult } from "react-dashstream";
492
+
493
+ function CustomWidget() {
494
+ const { data, isRefreshing, lastRefreshError, lastRefreshTime, credentialsSet } = useAIOpsData();
495
+ const cpu = useQueryResult('cpu_usage{instance="srv-01"}');
496
+ return <div>CPU: {String(cpu)}</div>;
497
+ }
498
+ ```
499
+
500
+ | Hook | Returns | Throws? |
501
+ | ------------------------ | -------------------------- | ---------------------------- |
502
+ | `useAIOpsData()` | `DataContextValue` | Yes — outside `DataProvider` |
503
+ | `useAIOpsDataOptional()` | `DataContextValue \| null` | No |
504
+ | `useQueryResult(query)` | `unknown \| null` | Yes — outside `DataProvider` |
505
+
506
+ ```ts
507
+ interface DataContextValue {
508
+ data: Record<string, unknown>; // Raw responses keyed by query
509
+ isRefreshing: boolean;
510
+ lastRefreshError: string | null;
511
+ lastRefreshTime: Date | null;
512
+ credentialsSet: boolean;
513
+ setCredentials: (creds: Credentials) => void;
514
+ }
515
+ ```
516
+
517
+ ---
518
+
519
+ ## Exports
520
+
521
+ Everything below is exported from `react-dashstream`.
522
+
523
+ ### Default export
524
+
525
+ `AIOPsDashboard` — main dashboard shell component.
526
+
527
+ ### Compound nodes
528
+
529
+ `ServerNode`, `DatabaseNode`, `WebDispatcherNode`, `MessageServerNode`, `HumanNode`
530
+
531
+ ### Layout
532
+
533
+ `Carousel`, `Service`, `ServiceNode`
534
+
535
+ ### 3D models
536
+
537
+ `Server3D`, `Database3D`, `WebDispatcher3D`, `MessageServer3D`, `Human3D`
538
+
539
+ ### Internal 3D components (for drill-down)
540
+
541
+ `CPU3D`, `Memory3D`, `DriveBay3D`, `NetworkBlock3D`, `ThreadPool3D`, `Platter3D`, `Port3D`
542
+
543
+ ### Indicators and connections
544
+
545
+ `SvgConnection`, `SyncBridge`, `NodeCallout`, `HoloBase`
546
+
547
+ ### Dialogs
548
+
549
+ `ServiceDialog`, `ComponentDialog`, `HistoricalGraphPanel`, `ComponentDrillView`
550
+
551
+ ### Pre-built services
552
+
553
+ `SAPService`, `ExchangeService`
554
+
555
+ SAP helpers: `computeSAPServiceStatus`, `computeSAPDialogMetrics`, `computeSAPDialogAlerts`, `SAP_CONNECTIONS`
556
+
557
+ Exchange helpers: `computeExchangeServiceStatus`, `computeExchangeDialogMetrics`, `computeExchangeDialogAlerts`, `EXCHANGE_CONNECTIONS`
558
+
559
+ SAP sub-component helpers: `getServerSubComponents`, `getServerGraphSeries`, `getDispatcherSubComponents`, `getDispatcherGraphSeries`, `getMessageServerSubComponents`, `getMessageServerGraphSeries`, `getDatabaseSubComponents`, `getDatabaseGraphSeries`
560
+
561
+ ### Data layer
562
+
563
+ `DataProvider`, `useAIOpsData`, `useAIOpsDataOptional`, `useQueryResult`, `defaultDataTransform`
564
+
565
+ ### Theme
566
+
567
+ `STATUS_CFG`, `HOLO_CYAN` (`"#00e5ff"`), `HOLO_BLUE` (`"#0055cc"`), `HOLO_SURFACE`, `HOLO_GLASS`, `makeFaceStyles(W, H, D)`
568
+
569
+ ### Context hooks
570
+
571
+ `CarouselContext`, `CarouselItemContext`, `useCarouselContext`, `useCarouselItemContext`, `ServiceContext`
572
+
573
+ ### Type exports
574
+
575
+ `AIOPsDashboardProps`, `ServiceMeta`, `ServiceMetricBinding`, `ComponentStatus`, `StatusCfg`, `FaceName`, `Base3DProps`, `ComponentType`, `ComponentContext`, `ComponentDialogMetric`, `SubComponentConfig`, `GraphSeries`, `SelectedComponent`, `ConnectionConfig`, `ViewState`, `DataBinding`, `DataBindings`, `DataProviderConfig`, `Credentials`, `DataContextValue`, `ServiceProps`, `ServiceContextValue`, `ServiceNodeProps`, `CarouselProps`, `ServerNodeProps`, `DatabaseNodeProps`, `HumanNodeProps`, `WebDispatcherNodeProps`, `MessageServerNodeProps`, `SvgConnectionProps`, `SyncBridgeProps`, `NodeCalloutProps`, `ServiceDialogProps`, `ServiceDialogMetric`, `ServiceDialogAlert`, `ComponentDialogProps`, `ComponentDrillViewProps`, `CarouselContextValue`, `CarouselItemContextValue`, `SAPServiceProps`, `SAPServiceOwnProps`, `SAPServiceConfig`, `ExchangeServiceProps`, `ExchangeServiceOwnProps`, `ExchangeServiceConfig`
576
+
577
+ ---
578
+
579
+ ## Key types quick reference
580
+
581
+ ### AIOPsDashboardProps
582
+
583
+ ```ts
584
+ interface AIOPsDashboardProps {
585
+ title?: string;
586
+ brandName?: string; // Default: "BUSAUD AIOps"
587
+ brandTag?: string; // Default: "3D MONITOR"
588
+ services?: ServiceMeta[];
589
+ backgroundImage?: string;
590
+ logoUrl?: string;
591
+ carouselSpeed?: number; // Default: 0.006
592
+ fontFamily?: string;
593
+ liveData?: boolean; // Default: false
594
+ dataEndpoint?: string;
595
+ dataBindings?: DataBindings;
596
+ dataTransform?: (raw: unknown) => unknown;
597
+ dataRefreshInterval?: number; // Default: 60000
598
+ serviceDataBindings?: Record<string, ServiceMetricBinding[]>;
599
+ children: React.ReactNode;
600
+ }
601
+ ```
602
+
603
+ ### ServiceMeta
604
+
605
+ ```ts
606
+ interface ServiceMeta {
607
+ name: string; // Must match child component name
608
+ status: ComponentStatus;
609
+ dbSync?: boolean; // Default: true
610
+ metrics?: ServiceDialogMetric[];
611
+ alerts?: ServiceDialogAlert[];
612
+ }
613
+
614
+ interface ServiceDialogMetric {
615
+ label: string;
616
+ value: string;
617
+ color: string;
618
+ }
619
+
620
+ interface ServiceDialogAlert {
621
+ level: "info" | "warning" | "critical";
622
+ message: string;
623
+ }
624
+ ```
625
+
626
+ ### ConnectionConfig
627
+
628
+ ```ts
629
+ interface ConnectionConfig {
630
+ from: [number, number];
631
+ to: [number, number];
632
+ visibleAtPhase?: number; // Default: 0
633
+ color?: string; // Default: "#00e5ff"
634
+ duration?: string; // Default: "1.2s"
635
+ }
636
+ ```
637
+
638
+ ### ComponentStatus
639
+
640
+ ```ts
641
+ type ComponentStatus = "online" | "warning" | "critical" | "offline";
642
+ ```
643
+
644
+ | Status | Color | Glow |
645
+ | ---------- | --------- | -------- |
646
+ | `online` | `#00e5ff` | cyan |
647
+ | `warning` | `#ff8c00` | orange |
648
+ | `critical` | `#ff2255` | red |
649
+ | `offline` | `#1e3a5a` | dim blue |
650
+
651
+ ---
652
+
653
+ ## Endpoint contract
654
+
655
+ | Aspect | Requirement |
656
+ | ------------ | -------------------------------------------------------------- |
657
+ | **Method** | `GET` |
658
+ | **URL** | `<dataEndpoint>?query=<urlEncodedQuery>` |
659
+ | **Headers** | `access-key` and `access-secret-key` |
660
+ | **Response** | Plain text body (trimmed). Default transform parses as number. |
661
+ | **Errors** | Non-2xx → counted as failure per query |
662
+
663
+ For JSON responses, provide a custom `dataTransform`:
664
+
665
+ ```tsx
666
+ dataTransform={(raw) => {
667
+ const parsed = JSON.parse(String(raw));
668
+ return parsed?.data?.result?.[0]?.value?.[1] ?? raw;
669
+ }}
670
+ ```
671
+
672
+ ---
673
+
674
+ ## Standalone DataProvider
675
+
676
+ Use the data layer without `AIOPsDashboard`:
677
+
678
+ ```tsx
679
+ import { DataProvider, useAIOpsData } from "react-dashstream";
680
+
681
+ <DataProvider
682
+ config={{
683
+ endpoint: "https://prometheus.example.com/api/v1/query",
684
+ queries: ['cpu_usage{instance="srv-01"}'],
685
+ refreshInterval: 15000,
686
+ }}
687
+ >
688
+ <MyContent />
689
+ </DataProvider>;
690
+ ```
691
+
692
+ ---
693
+
694
+ ## Node positioning guide
695
+
696
+ The topology scene is ~660×640 pixels.
697
+
698
+ | Layer | Typical `ey` | `visibleAtPhase` | Component |
699
+ | ----------- | ------------ | ---------------- | ------------------- |
700
+ | Users (top) | 100 | 2 | `HumanNode` |
701
+ | Dispatcher | 230 | 2 | `WebDispatcherNode` |
702
+ | App servers | 350–380 | 3 | `ServerNode` |
703
+ | Databases | 500–520 | 4 | `DatabaseNode` |
704
+
705
+ Center X = `330`. Left/right columns: `200` / `460`. Three-column: `165` / `330` / `495`.
706
+
707
+ `compactOffset` controls node position in the compact carousel view relative to center.
708
+
709
+ ---
710
+
711
+ ## Usage patterns
712
+
713
+ ### Minimal static dashboard
714
+
715
+ ```tsx
716
+ import "react-dashstream/dist/index.css";
717
+ import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
718
+
719
+ <AIOPsDashboard brandName="MY DASHBOARD" services={[{ name: "Svc", status: "online" }]}>
720
+ <Service
721
+ name="Svc"
722
+ status="online"
723
+ connections={[{ from: [330, 200], to: [330, 380], visibleAtPhase: 3 }]}
724
+ >
725
+ <ServerNode
726
+ ex={330}
727
+ ey={380}
728
+ compactOffset={{ x: 0, y: 0 }}
729
+ zIndex={8}
730
+ name="SRV-01"
731
+ status="online"
732
+ cpuLoad={42}
733
+ memLoad={60}
734
+ />
735
+ </Service>
736
+ </AIOPsDashboard>;
737
+ ```
738
+
739
+ ### Full live-data dashboard
740
+
741
+ ```tsx
742
+ <AIOPsDashboard
743
+ brandName="LIVE MONITOR"
744
+ services={services}
745
+ liveData={true}
746
+ dataEndpoint="https://prometheus.example.com/api/v1/query"
747
+ dataRefreshInterval={10000}
748
+ dataBindings={{
749
+ "My Service": {
750
+ cpuLoad: 'cpu_usage{instance="srv-01"}',
751
+ status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
752
+ },
753
+ }}
754
+ serviceDataBindings={{
755
+ "My Service": [{ label: "CPU", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" }],
756
+ }}
757
+ >
758
+ <MyService name="My Service" />
759
+ </AIOPsDashboard>
760
+ ```
761
+
762
+ ### Custom service with all data features
763
+
764
+ ```tsx
765
+ function MyService({ name, status, cpuLoad, memLoad, dbCapacity, dbStatus }: any) {
766
+ return (
767
+ <Service
768
+ name={name}
769
+ status={status ?? "online"}
770
+ connections={[
771
+ { from: [330, 200], to: [200, 380], visibleAtPhase: 3 },
772
+ { from: [330, 200], to: [460, 380], visibleAtPhase: 3 },
773
+ ]}
774
+ >
775
+ <ServerNode
776
+ ex={200}
777
+ ey={380}
778
+ compactOffset={{ x: -30, y: -20 }}
779
+ zIndex={8}
780
+ name="SRV-01"
781
+ subLabel="APP SERVER"
782
+ status={status ?? "online"}
783
+ cpuLoad={cpuLoad ?? 42}
784
+ memLoad={memLoad ?? 60}
785
+ alert={{ offsetX: -160, offsetY: -60, align: "left" }}
786
+ dialogMetrics={[
787
+ { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad ?? 42 },
788
+ { id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad ?? 60 },
789
+ ]}
790
+ subComponents={[
791
+ {
792
+ id: "cpu",
793
+ label: "CPU-0",
794
+ status: "online",
795
+ element: <CPU3D label="CPU-0" load={cpuLoad ?? 42} />,
796
+ },
797
+ {
798
+ id: "mem",
799
+ label: "HEAP",
800
+ status: "online",
801
+ element: <Memory3D label="HEAP" usedPercent={memLoad ?? 60} />,
802
+ },
803
+ ]}
804
+ graphSeries={[
805
+ { id: "cpu", label: "CPU", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68] },
806
+ ]}
807
+ />
808
+ <DatabaseNode
809
+ ex={460}
810
+ ey={380}
811
+ compactOffset={{ x: 30, y: -20 }}
812
+ zIndex={7}
813
+ name="DB-01"
814
+ subLabel="PRIMARY"
815
+ status={dbStatus ?? "online"}
816
+ capacity={dbCapacity ?? 55}
817
+ alert={{ offsetX: 160, offsetY: -60, align: "right" }}
818
+ />
819
+ </Service>
820
+ );
821
+ }
822
+ ```
823
+
824
+ ---
825
+
826
+ ## EventView — Operations Event Console
827
+
828
+ Table component for displaying infrastructure events from a live API. Uses the same `access-key` / `access-secret-key` authentication and holographic theme as the 3D dashboard. No built-in default data — requires either `apiConfig` (to fetch from an API) or `events` (pre-fetched array).
829
+
830
+ ### API mode (primary usage)
831
+
832
+ ```tsx
833
+ import "react-dashstream/dist/index.css";
834
+ import { EventView } from "react-dashstream";
835
+ import type { EventApiConfig } from "react-dashstream";
836
+
837
+ const apiConfig: EventApiConfig = {
838
+ baseUrl: "https://your-monitoring-server.example.com",
839
+ payload: { filter: {}, sortBy: "date_reception", sortOrder: "DESC" },
840
+ fieldMapping: {
841
+ id: "mc_ueid",
842
+ occurrence: "date_reception",
843
+ severityLastModified: "severity_last_modified",
844
+ severity: "severity",
845
+ owner: "owner",
846
+ class: "class",
847
+ host: "mc_host",
848
+ message: "msg",
849
+ remedySupportGroup: "ara_remedy_support_group",
850
+ incidentId: "ara_incident_id",
851
+ smsStatus: "ara_sms_status",
852
+ onCallNumber: "ara_on_call_number",
853
+ hostedApplication: "ara_hosted_application",
854
+ monitoringCategory: "ara_hosted_app_monitoring",
855
+ applicationSupportUnit: "ara_application_support_unit",
856
+ },
857
+ };
858
+
859
+ <EventView apiConfig={apiConfig} />;
860
+ ```
861
+
862
+ ### How it works
863
+
864
+ 1. Shows credentials modal (or reuses DataProvider credentials if inside `AIOPsDashboard`)
865
+ 2. Sends `POST {baseUrl}/tsws/monitoring/api/v1.0/events/search` with the payload as JSON body
866
+ 3. Auth via headers: `access-key` and `access-secret-key`
867
+ 4. Expects response: `{ eventSeverityCount: {...}, totalCount: N, eventList: [{...}] }`
868
+ 5. Maps each object in `eventList` to `AIOpsEvent` using `fieldMapping`
869
+ 6. Maps severity strings via `severityMap` (default: `CRITICAL→Critical`, `MAJOR→Major`, `MINOR→Minor`)
870
+ 7. Polls on `refreshInterval` (default 60s)
871
+
872
+ ### Props
873
+
874
+ | Prop | Type | Default | Notes |
875
+ | ------------- | ---------------- | ----------------- | -------------------------------------------------- |
876
+ | `apiConfig` | `EventApiConfig` | — | Required for API mode |
877
+ | `events` | `AIOpsEvent[]` | — | Pre-fetched events (skips API calls) |
878
+ | `credentials` | `Credentials` | — | Explicit creds; falls back to DataProvider → modal |
879
+ | `pageSize` | `number` | `15` | Rows per page |
880
+ | `title` | `string` | `"Event Console"` | Header title |
881
+
882
+ ### EventApiConfig
883
+
884
+ ```ts
885
+ interface EventApiConfig {
886
+ baseUrl: string; // "https://monitoring.example.com"
887
+ endpoint?: string; // default: "/tsws/monitoring/api/v1.0/events/search"
888
+ payload: Record<string, unknown>; // POST body
889
+ fieldMapping: EventFieldMapping; // maps API keys → AIOpsEvent keys
890
+ severityMap?: Record<string, EventSeverity>; // default: { CRITICAL, MAJOR, MINOR }
891
+ refreshInterval?: number; // ms, default 60000
892
+ }
893
+ ```
894
+
895
+ ### EventFieldMapping
896
+
897
+ Maps every `AIOpsEvent` property to the API response field name. All 15 keys required:
898
+
899
+ ```ts
900
+ type EventFieldMapping = Record<keyof AIOpsEvent, string>;
901
+ ```
902
+
903
+ ### AIOpsEvent interface
904
+
905
+ ```ts
906
+ interface AIOpsEvent {
907
+ id: string;
908
+ occurrence: string;
909
+ severityLastModified: string;
910
+ severity: "Minor" | "Major" | "Critical";
911
+ owner: string;
912
+ class: "Self-monitoring" | "SCOM" | "Situation" | "Alarm" | "Event";
913
+ host: string;
914
+ message: string;
915
+ remedySupportGroup: string;
916
+ incidentId: string;
917
+ smsStatus: "" | "SUCCESS" | "FAILED";
918
+ onCallNumber: string;
919
+ hostedApplication: string;
920
+ monitoringCategory: "24/7" | "Office Hours";
921
+ applicationSupportUnit: string;
922
+ }
923
+ ```
924
+
925
+ ### Credential resolution order
926
+
927
+ 1. `credentials` prop (if provided)
928
+ 2. `DataProvider` context credentials (when inside `AIOPsDashboard` with `liveData`)
929
+ 3. Built-in credentials modal (same UI as the 3D dashboard)
930
+
931
+ ### Severity colors
932
+
933
+ - **Minor** → `#ffb800` (amber)
934
+ - **Major** → `#ff6600` (orange)
935
+ - **Critical** → `#ff2255` (red)
936
+
937
+ ### Features
938
+
939
+ - Live API polling with configurable interval and manual refresh button
940
+ - Configurable field mapping — any API response shape can be mapped
941
+ - Shared auth — reuses DataProvider credentials when available
942
+ - Severity filter chips with live counts
943
+ - Free-text search across message, host, owner, incident ID
944
+ - Sortable columns (click header: asc → desc → none)
945
+ - Pagination with selectable page size (10/15/25/50)
946
+ - Loading spinner, error badge, last-refresh timestamp
947
+ - Holographic theme: scan-line header, glowing rows, severity badges
948
+
949
+ ### Generating an EventApiConfig from the AI
950
+
951
+ When asked to set up EventView for a new API, generate the config like this:
952
+
953
+ 1. Set `baseUrl` to the server's protocol + host + port
954
+ 2. Set `endpoint` only if it differs from the default `/tsws/monitoring/api/v1.0/events/search`
955
+ 3. Build `payload` based on the API's expected POST body schema
956
+ 4. Build `fieldMapping` by asking the user which API field maps to each `AIOpsEvent` property
957
+ 5. Set `severityMap` only if the API uses non-standard severity values
958
+
959
+ ---
960
+
961
+ ## Build and development
962
+
963
+ ```bash
964
+ npm run dev # Vite dev server (loads example/Dashboard.tsx)
965
+ npm run build # TypeScript check + Vite library build
966
+ npm run build:lib # Vite library build only
967
+ npm run preview # Preview production build
968
+ ```
969
+
970
+ Output: `dist/index.js` (ESM), `dist/index.d.ts`, `dist/index.css`.
971
+
972
+ ---
973
+
974
+ ## Common pitfalls
975
+
976
+ 1. **Missing CSS import** — Always import `react-dashstream/dist/index.css`.
977
+ 2. **Name mismatch** — `name` on child components must exactly match keys in `dataBindings`, `serviceDataBindings`, and `ServiceMeta.name`.
978
+ 3. **Credentials are in-memory only** — Page refresh requires re-entering credentials.
979
+ 4. **Default transform is plain-text** — For JSON endpoints, provide a custom `dataTransform`.
980
+ 5. **Polling only** — No WebSocket/SSE support; uses `setInterval`.
981
+ 6. **`visibleAtPhase` matters** — Nodes without proper values won't animate during expansion.
982
+ 7. **Connection coordinates** — `from`/`to` arrays use `[x, y]` matching `ex`/`ey` of connected nodes.
983
+ 8. **`dialogMetrics` replaces defaults** — When provided, all default gauges (CPU/Memory/Storage) are removed.
984
+ 9. **`subComponents` replaces defaults** — When provided, all auto-generated sub-components are removed.
985
+ 10. **`graphSeries` replaces defaults** — When provided, all auto-generated sparklines are removed.