react-dashstream 0.1.3 → 0.3.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.
Files changed (38) hide show
  1. package/README.md +1245 -1123
  2. package/dashstream-skill.md +1148 -1039
  3. package/dist/AIOPsDashboard.d.ts +7 -0
  4. package/dist/AIOPsDashboard.d.ts.map +1 -1
  5. package/dist/ThemeContext.d.ts +4 -0
  6. package/dist/ThemeContext.d.ts.map +1 -0
  7. package/dist/components/ComponentDialog.d.ts.map +1 -1
  8. package/dist/components/Database3D.d.ts.map +1 -1
  9. package/dist/components/DatacenterView/DatacenterView.d.ts +37 -0
  10. package/dist/components/DatacenterView/DatacenterView.d.ts.map +1 -0
  11. package/dist/components/DatacenterView/index.d.ts +3 -0
  12. package/dist/components/DatacenterView/index.d.ts.map +1 -0
  13. package/dist/components/DatacenterView/types.d.ts +55 -0
  14. package/dist/components/DatacenterView/types.d.ts.map +1 -0
  15. package/dist/components/EventView/EventAlertsContext.d.ts +2 -0
  16. package/dist/components/EventView/EventAlertsContext.d.ts.map +1 -1
  17. package/dist/components/EventView/EventView.d.ts +1 -1
  18. package/dist/components/EventView/EventView.d.ts.map +1 -1
  19. package/dist/components/EventView/types.d.ts +3 -0
  20. package/dist/components/EventView/types.d.ts.map +1 -1
  21. package/dist/components/NodeCallout.d.ts.map +1 -1
  22. package/dist/components/Server3D.d.ts.map +1 -1
  23. package/dist/components/Service.d.ts.map +1 -1
  24. package/dist/components/ServiceDialog.d.ts.map +1 -1
  25. package/dist/components/ServiceNode.d.ts.map +1 -1
  26. package/dist/components/WebDispatcher3D.d.ts.map +1 -1
  27. package/dist/components/index.d.ts +4 -0
  28. package/dist/components/index.d.ts.map +1 -1
  29. package/dist/data/CredentialsModal.d.ts.map +1 -1
  30. package/dist/data/DataProvider.d.ts +7 -0
  31. package/dist/data/DataProvider.d.ts.map +1 -1
  32. package/dist/data/index.d.ts +2 -2
  33. package/dist/data/index.d.ts.map +1 -1
  34. package/dist/index.css +1 -1
  35. package/dist/index.d.ts +4 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +3837 -2518
  38. package/package.json +1 -1
@@ -1,1039 +1,1148 @@
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 # Table component (filter, sort, search, infinite scroll)
87
- │ │ ├── EventView.css # Holographic table styles
88
- │ │ ├── EventAlertsContext.tsx # EventAlertsProvider + per-host alert map
89
- │ │ ├── fetchEvents.ts # Shared fetch/mapping logic (used by both EventView & provider)
90
- ├── types.ts # AIOpsEvent, EventSeverity, EventApiConfig, etc.
91
- ├── mockData.ts # 28 realistic mock events
92
- └── index.ts
93
- │ └── index.ts
94
- ├── services/
95
- │ ├── SAPService.tsx, ExchangeService.tsx
96
- │ ├── sapSubComponents.tsx
97
- └── index.ts
98
- example/
99
- ├── Dashboard.tsx
100
- └── services/
101
- ```
102
-
103
- ---
104
-
105
- ## Complete data flow reference
106
-
107
- There are **10 distinct data paths** in this package. Understanding them is critical for building live dashboards.
108
-
109
- ### 1. Data bindings live props injected into service components
110
-
111
- `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.
112
-
113
- ```ts
114
- type DataBinding = string | { query: string; transform?: (raw: unknown) => unknown };
115
- type DataBindings = Record<string, Record<string, DataBinding>>;
116
- ```
117
-
118
- - Bare string → uses global `dataTransform` (defaults to numeric parsing)
119
- - Object `{ query, transform }` → uses per-binding transform
120
- - Service name key must match child's `name` prop **exactly**
121
-
122
- ```tsx
123
- const dataBindings: DataBindings = {
124
- "My Service": {
125
- cpuLoad: 'cpu_usage{instance="srv-01"}', // bare string → number
126
- memLoad: 'memory_usage{instance="srv-01"}', // bare string → number
127
- status: {
128
- // object custom transform
129
- query: 'cpu_usage{instance="srv-01"}',
130
- transform: (raw) => {
131
- const n = Number(raw);
132
- if (n >= 85) return "critical";
133
- if (n >= 70) return "warning";
134
- return "online";
135
- },
136
- },
137
- dbCapacity: 'disk_capacity{instance="db-01"}',
138
- dbStatus: { query: 'disk_capacity{instance="db-01"}', transform: statusFromValue },
139
- },
140
- };
141
- ```
142
-
143
- #### Multi-component services
144
-
145
- 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.
146
-
147
- ```tsx
148
- interface ServiceXProps {
149
- name: string;
150
- status?: ComponentStatus;
151
- srv1CpuLoad?: number;
152
- srv1MemLoad?: number;
153
- srv1Status?: ComponentStatus;
154
- srv2CpuLoad?: number;
155
- srv2MemLoad?: number;
156
- srv2Status?: ComponentStatus;
157
- srv3CpuLoad?: number;
158
- srv3MemLoad?: number;
159
- srv3Status?: ComponentStatus;
160
- dbCapacity?: number;
161
- dbStatus?: ComponentStatus;
162
- }
163
-
164
- function ServiceX({
165
- name, status = "online",
166
- srv1CpuLoad = 54, srv1MemLoad = 58, srv1Status = "online",
167
- srv2CpuLoad = 63, srv2MemLoad = 66, srv2Status = "online",
168
- srv3CpuLoad = 78, srv3MemLoad = 71, srv3Status = "online",
169
- dbCapacity = 68, dbStatus = "online",
170
- }: ServiceXProps) {
171
- return (
172
- <Service name={name} status={status} connections={[/* ... */]}>
173
- <ServerNode name="SRV-X1" status={srv1Status}
174
- cpuLoad={srv1CpuLoad} memLoad={srv1MemLoad} ... />
175
- <ServerNode name="SRV-X2" status={srv2Status}
176
- cpuLoad={srv2CpuLoad} memLoad={srv2MemLoad} ... />
177
- <ServerNode name="SRV-X3" status={srv3Status}
178
- cpuLoad={srv3CpuLoad} memLoad={srv3MemLoad} ... />
179
- <DatabaseNode name="DB-X1" status={dbStatus}
180
- capacity={dbCapacity} ... />
181
- </Service>
182
- );
183
- }
184
- ```
185
-
186
- Bind each prefixed prop individually:
187
-
188
- ```tsx
189
- dataBindings={{
190
- ServiceX: {
191
- status: { query: 'status{instance="svcx"}', transform: statusFromValue },
192
- srv1CpuLoad: 'cpu_usage{instance="srvx-01"}',
193
- srv1MemLoad: 'memory_usage{instance="srvx-01"}',
194
- srv1Status: { query: 'status{instance="srvx-01"}', transform: statusFromValue },
195
- srv2CpuLoad: 'cpu_usage{instance="srvx-02"}',
196
- srv2MemLoad: 'memory_usage{instance="srvx-02"}',
197
- srv2Status: { query: 'status{instance="srvx-02"}', transform: statusFromValue },
198
- srv3CpuLoad: 'cpu_usage{instance="srvx-03"}',
199
- srv3MemLoad: 'memory_usage{instance="srvx-03"}',
200
- srv3Status: { query: 'status{instance="srvx-03"}', transform: statusFromValue },
201
- dbCapacity: 'disk_capacity{instance="dbx-01"}',
202
- dbStatus: { query: 'status{instance="dbx-01"}', transform: statusFromValue },
203
- },
204
- }}
205
- ```
206
-
207
- Use a consistent convention like `srv1CpuLoad`, `srv2CpuLoad`. The service component maps each prefixed prop to the correct node.
208
-
209
- ### 2. Service dialog — static ServiceMeta metrics
210
-
211
- The **ServiceDialog** panel shows KPIs when a service is expanded. Static metrics are passed via `ServiceMeta`:
212
-
213
- ```tsx
214
- const services: ServiceMeta[] = [
215
- {
216
- name: "My Service",
217
- status: "online",
218
- metrics: [
219
- { label: "Uptime", value: "99.99%", color: "#00ff88" },
220
- { label: "Avg Latency", value: "8ms", color: "#00e5ff" },
221
- ],
222
- alerts: [
223
- { level: "info", message: "All Systems Nominal" },
224
- { level: "warning", message: "High CPU on SRV-01" },
225
- { level: "critical", message: "DB connection pool exhausted" },
226
- ],
227
- },
228
- ];
229
- ```
230
-
231
- ### 3. Service dialog — live serviceDataBindings
232
-
233
- Replace static metrics with live-fetched values using `serviceDataBindings`:
234
-
235
- ```tsx
236
- serviceDataBindings={{
237
- "My Service": [
238
- { label: "CPU Load", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" },
239
- { label: "Memory", query: 'memory_usage{instance="srv-01"}', unit: "%", color: "#bb55ff" },
240
- { label: "Disk", query: 'disk_capacity{instance="db-01"}', unit: "%", color: "#ff8c00" },
241
- ],
242
- }}
243
- ```
244
-
245
- ```ts
246
- interface ServiceMetricBinding {
247
- label: string; // Row label
248
- query: string; // PromQL query
249
- unit?: string; // Suffix (e.g. "%", "ms")
250
- color?: string; // Accent color
251
- transform?: (raw: unknown) => string; // Custom formatter
252
- }
253
- ```
254
-
255
- When provided, live values **replace** static `ServiceMeta.metrics` for that service.
256
-
257
- ### 4. Component dialog — default gauges
258
-
259
- The **ComponentDialog** appears when clicking a node. Default gauges are derived from node type:
260
-
261
- - **Server**: CPU (from `cpuLoad`), Memory (from `memLoad`), Storage
262
- - **Database**: Capacity (from `capacity`), Memory, Storage
263
- - **Dispatcher**: Traffic (from `traffic`), Memory, Storage
264
- - **Message Server**: Queue (from `queueDepth`), Memory, Storage
265
-
266
- Default thresholds: warn at 70, critical at 85.
267
-
268
- ### 5. Component dialog — custom dialogMetrics
269
-
270
- Override default gauges with `dialogMetrics` on any compound node:
271
-
272
- ```tsx
273
- <ServerNode
274
- name="SRV-01"
275
- status="online"
276
- cpuLoad={67}
277
- memLoad={72}
278
- ex={200}
279
- ey={380}
280
- compactOffset={{ x: -30, y: -20 }}
281
- zIndex={8}
282
- dialogMetrics={[
283
- { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67, unit: "%" },
284
- { id: "mem", label: "MEMORY", sublabel: "HEAP USAGE", value: 72, unit: "%" },
285
- { id: "iops", label: "IOPS", sublabel: "DISK OPS", value: 45, unit: "k/s", icon: "disk" },
286
- {
287
- id: "threads",
288
- label: "THREADS",
289
- sublabel: "ACTIVE",
290
- value: 82,
291
- unit: "%",
292
- warnAt: 60,
293
- critAt: 80,
294
- icon: "cpu",
295
- },
296
- ]}
297
- />
298
- ```
299
-
300
- ```ts
301
- interface ComponentDialogMetric {
302
- id: string; // Unique key
303
- label: string; // Upper label
304
- sublabel: string; // Lower label
305
- value: number; // 0–100
306
- unit?: string; // Default "%"
307
- icon?: "cpu" | "mem" | "disk"; // Default "cpu"
308
- warnAt?: number; // Default 70
309
- critAt?: number; // Default 85
310
- color?: string; // Override bar color (bypasses threshold coloring)
311
- }
312
- ```
313
-
314
- All compound nodes accept `dialogMetrics`: `ServerNode`, `DatabaseNode`, `WebDispatcherNode`, `MessageServerNode`.
315
-
316
- **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:
317
-
318
- ```tsx
319
- // Service component — accepts individual metric props, builds dialogMetrics from them
320
- function MyService({ name, status = "online", cpuLoad = 42, memLoad = 60, iops = 20 }: any) {
321
- return (
322
- <Service
323
- name={name}
324
- status={status}
325
- connections={
326
- [
327
- /* ... */
328
- ]
329
- }
330
- >
331
- <ServerNode
332
- name="SRV-01"
333
- status={status}
334
- cpuLoad={cpuLoad}
335
- memLoad={memLoad}
336
- ex={200}
337
- ey={380}
338
- compactOffset={{ x: -30, y: -20 }}
339
- zIndex={8}
340
- dialogMetrics={[
341
- { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad },
342
- { id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad },
343
- {
344
- id: "iops",
345
- label: "IOPS",
346
- sublabel: "DISK OPS",
347
- value: iops,
348
- icon: "disk",
349
- warnAt: 50,
350
- critAt: 80,
351
- },
352
- ]}
353
- />
354
- </Service>
355
- );
356
- }
357
-
358
- // Dashboard — bind each prop to a query
359
- <AIOPsDashboard
360
- liveData
361
- dataEndpoint="..."
362
- dataBindings={{
363
- "My Service": {
364
- cpuLoad: 'cpu_usage{instance="srv-01"}',
365
- memLoad: 'memory_usage{instance="srv-01"}',
366
- iops: 'disk_iops{instance="srv-01"}',
367
- status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
368
- },
369
- }}
370
- >
371
- <MyService name="My Service" />
372
- </AIOPsDashboard>;
373
- ```
374
-
375
- 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.
376
-
377
- ### 6. Alerts — automatic threshold detection
378
-
379
- Nodes auto-render `NodeCallout` alerts when metrics breach thresholds:
380
-
381
- - **Warning** at 70% → orange callout
382
- - **Critical** at 85% → red callout
383
-
384
- Sources (checked in priority order):
385
-
386
- 1. `dialogMetrics` values vs their `warnAt`/`critAt`
387
- 2. Context values (`cpuLoad`, `memLoad`, `traffic`, `queueDepth`, `capacity`) vs default thresholds
388
-
389
- ```tsx
390
- // Auto-alert from cpuLoad > 85
391
- <ServerNode cpuLoad={92} memLoad={64}
392
- alert={{ offsetX: -160, offsetY: -60, align: "left" }}
393
- ... />
394
-
395
- // Custom message
396
- <ServerNode cpuLoad={92}
397
- alert={{ msg: "CPU overload — scale out", offsetX: -160, offsetY: -60, align: "left" }}
398
- ... />
399
-
400
- // Custom thresholds via dialogMetrics
401
- <ServerNode
402
- dialogMetrics={[
403
- { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67,
404
- warnAt: 50, critAt: 75 }, // Alert at 67 because critAt=75
405
- ]}
406
- alert={{ offsetX: -160, offsetY: -60, align: "left" }}
407
- ... />
408
- ```
409
-
410
- Alert `align` options: `"left"`, `"right"`, `"top"`, `"bottom"`.
411
-
412
- Alert prop shape:
413
-
414
- ```ts
415
- alert?: {
416
- msg?: string; // Custom message (auto-generated if omitted)
417
- offsetX?: number; // X offset from node (default 110)
418
- offsetY?: number; // Y offset from node (default -30)
419
- align?: "left" | "right" | "top" | "bottom"; // Default "right"
420
- internalRef?: string; // Sub-component ID
421
- }
422
- ```
423
-
424
- ### 7. Event-to-dashboard bridge automatic node alerts from events API
425
-
426
- When `eventApiConfig` is passed to `AIOPsDashboard`, events are fetched in the background and nodes whose `name` (case-insensitive) matches an event `host` automatically show alert callouts.
427
-
428
- ```tsx
429
- <AIOPsDashboard
430
- eventApiConfig={eventApiConfig} // same EventApiConfig used for EventView
431
- liveData
432
- dataEndpoint="..."
433
- dataBindings={dataBindings}
434
- services={services}
435
- >
436
- <MyService name="My Service" />
437
- </AIOPsDashboard>
438
- ```
439
-
440
- **How it works internally:**
441
-
442
- 1. `AIOPsDashboard` wraps children in `EventAlertsProvider` (inside `DataProvider` when `liveData` is set — shares credentials automatically)
443
- 2. `EventAlertsProvider` fetches events using the same `fetchEvents` + `mapRawEvent` logic as `EventView`
444
- 3. Events are grouped by **lower-cased hostname** into a `Map<string, EventHostAlert>`
445
- 4. `ServiceNode` reads from `EventAlertsContext` via `useEventAlertsOptional()`
446
- 5. If `componentInfo.name.toLowerCase()` matches an entry, event severity is factored into the existing `resolveNodeSeverity` pipeline as a **third severity source** (alongside node status and metric breaches)
447
- 6. Highest severity among all three sources wins
448
- 7. `NodeCallout` appears automatically with the event message; multi-event hosts show `"N events – message"`
449
-
450
- **Severity mapping:**
451
-
452
- | Event severity | → ComponentStatus |
453
- | -------------- | ----------------- |
454
- | Critical | `"critical"` |
455
- | Major | `"warning"` |
456
- | Minor | `"warning"` |
457
-
458
- **Key requirement:** Node `name` props must match event `host` values from the API (case-insensitive). Example: if the API returns `host: "SAP-PRD-APP01"`, the node must be `<ServerNode name="SAP-PRD-APP01" ... />`.
459
-
460
- **Credential resolution order** (for EventAlertsProvider inside AIOPsDashboard):
461
-
462
- 1. `DataProvider` context credentials (when `liveData` is enabled — same credentials, one login)
463
- 2. Built-in `CredentialsModal` (when `liveData` is not enabled)
464
-
465
- **Opt-in:** Without `eventApiConfig`, no events are fetched zero overhead.
466
-
467
- **When generating an `AIOPsDashboard` with event bridge:** Reuse the same `EventApiConfig` object that the user already has for their `EventView`. Just add it as `eventApiConfig` on `AIOPsDashboard`.
468
-
469
- ### 8. Drill-down internals custom subComponents
470
-
471
- When clicking a node, the drill-down shows internal 3D sub-components. Default sub-components are auto-generated per type. Override with `subComponents`:
472
-
473
- ```tsx
474
- import { CPU3D, Memory3D, DriveBay3D, ThreadPool3D, NetworkBlock3D, Platter3D, Port3D } from "react-dashstream";
475
- import type { SubComponentConfig } from "react-dashstream";
476
-
477
- const internals: SubComponentConfig[] = [
478
- { id: "cpu-0", label: "CPU-0", status: "online",
479
- element: <CPU3D label="CPU-0" load={67} color="#00e5ff" /> },
480
- { id: "heap", label: "HEAP", status: "warning",
481
- element: <Memory3D label="HEAP" usedPercent={92} color="#8855ee" status="warning" /> },
482
- { id: "drive", label: "DRIVE-0", status: "online",
483
- element: <DriveBay3D label="DRIVE-0" color="#00e5ff" activity={true} /> },
484
- { id: "threads", label: "THREADS", status: "online",
485
- element: <ThreadPool3D label="THREADS" color="#00e5ff" /> },
486
- { id: "net", label: "NET", status: "online",
487
- element: <NetworkBlock3D label="NET" color="#00e5ff" /> },
488
- ];
489
-
490
- <ServerNode subComponents={internals} ... />
491
- ```
492
-
493
- ```ts
494
- interface SubComponentConfig {
495
- id: string; // Unique key
496
- label: string; // Display name
497
- status: ComponentStatus; // LED color
498
- detail?: string; // Fault description
499
- element: ReactNode; // 3D element to render
500
- }
501
- ```
502
-
503
- All compound nodes accept `subComponents`.
504
-
505
- ### 9. Graph series custom sparklines
506
-
507
- The drill-down's historical panel shows sparkline charts. Default graphs are auto-generated. Override with `graphSeries`:
508
-
509
- ```tsx
510
- import type { GraphSeries } from "react-dashstream";
511
-
512
- const graphs: GraphSeries[] = [
513
- { id: "cpu", label: "CPU-0", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68, 55] },
514
- { id: "mem", label: "HEAP", unit: "%", color: "#8855ee", data: [60, 65, 72, 78, 82, 75] },
515
- { id: "iops", label: "DISK", unit: "k/s", color: "#ff8c00", data: [12, 15, 22, 18, 25, 20] },
516
- ];
517
-
518
- <ServerNode graphSeries={graphs} ... />
519
- ```
520
-
521
- ```ts
522
- interface GraphSeries {
523
- id: string; // Unique key
524
- label: string; // Label above sparkline
525
- unit: string; // Suffix
526
- color: string; // Line color
527
- data: number[]; // Points (most recent last)
528
- }
529
- ```
530
-
531
- All compound nodes accept `graphSeries`.
532
-
533
- ### 10. Data hooks — programmatic access
534
-
535
- Access live data context anywhere inside the dashboard:
536
-
537
- ```tsx
538
- import { useAIOpsData, useAIOpsDataOptional, useQueryResult } from "react-dashstream";
539
-
540
- function CustomWidget() {
541
- const { data, isRefreshing, lastRefreshError, lastRefreshTime, credentialsSet } = useAIOpsData();
542
- const cpu = useQueryResult('cpu_usage{instance="srv-01"}');
543
- return <div>CPU: {String(cpu)}</div>;
544
- }
545
- ```
546
-
547
- | Hook | Returns | Throws? |
548
- | ------------------------ | -------------------------- | ---------------------------- |
549
- | `useAIOpsData()` | `DataContextValue` | Yes — outside `DataProvider` |
550
- | `useAIOpsDataOptional()` | `DataContextValue \| null` | No |
551
- | `useQueryResult(query)` | `unknown \| null` | Yes — outside `DataProvider` |
552
-
553
- ```ts
554
- interface DataContextValue {
555
- data: Record<string, unknown>; // Raw responses keyed by query
556
- isRefreshing: boolean;
557
- lastRefreshError: string | null;
558
- lastRefreshTime: Date | null;
559
- credentialsSet: boolean;
560
- setCredentials: (creds: Credentials) => void;
561
- }
562
- ```
563
-
564
- ---
565
-
566
- ## Exports
567
-
568
- Everything below is exported from `react-dashstream`.
569
-
570
- ### Default export
571
-
572
- `AIOPsDashboard` — main dashboard shell component.
573
-
574
- ### Compound nodes
575
-
576
- `ServerNode`, `DatabaseNode`, `WebDispatcherNode`, `MessageServerNode`, `HumanNode`
577
-
578
- ### Layout
579
-
580
- `Carousel`, `Service`, `ServiceNode`
581
-
582
- ### 3D models
583
-
584
- `Server3D`, `Database3D`, `WebDispatcher3D`, `MessageServer3D`, `Human3D`
585
-
586
- ### Internal 3D components (for drill-down)
587
-
588
- `CPU3D`, `Memory3D`, `DriveBay3D`, `NetworkBlock3D`, `ThreadPool3D`, `Platter3D`, `Port3D`
589
-
590
- ### Indicators and connections
591
-
592
- `SvgConnection`, `SyncBridge`, `NodeCallout`, `HoloBase`
593
-
594
- ### Dialogs
595
-
596
- `ServiceDialog`, `ComponentDialog`, `HistoricalGraphPanel`, `ComponentDrillView`
597
-
598
- ### Pre-built services
599
-
600
- `SAPService`, `ExchangeService`
601
-
602
- SAP helpers: `computeSAPServiceStatus`, `computeSAPDialogMetrics`, `computeSAPDialogAlerts`, `SAP_CONNECTIONS`
603
-
604
- Exchange helpers: `computeExchangeServiceStatus`, `computeExchangeDialogMetrics`, `computeExchangeDialogAlerts`, `EXCHANGE_CONNECTIONS`
605
-
606
- SAP sub-component helpers: `getServerSubComponents`, `getServerGraphSeries`, `getDispatcherSubComponents`, `getDispatcherGraphSeries`, `getMessageServerSubComponents`, `getMessageServerGraphSeries`, `getDatabaseSubComponents`, `getDatabaseGraphSeries`
607
-
608
- ### Data layer
609
-
610
- `DataProvider`, `useAIOpsData`, `useAIOpsDataOptional`, `useQueryResult`, `defaultDataTransform`
611
-
612
- ### Event alerts
613
-
614
- `EventAlertsProvider`, `useEventAlerts`, `useEventAlertsOptional`
615
-
616
- ### Theme
617
-
618
- `STATUS_CFG`, `HOLO_CYAN` (`"#00e5ff"`), `HOLO_BLUE` (`"#0055cc"`), `HOLO_SURFACE`, `HOLO_GLASS`, `makeFaceStyles(W, H, D)`
619
-
620
- ### Context hooks
621
-
622
- `CarouselContext`, `CarouselItemContext`, `useCarouselContext`, `useCarouselItemContext`, `ServiceContext`
623
-
624
- ### Type exports
625
-
626
- `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`, `EventHostAlert`, `EventAlertsContextValue`, `EventAlertsProviderProps`
627
-
628
- ---
629
-
630
- ## Key types quick reference
631
-
632
- ### AIOPsDashboardProps
633
-
634
- ```ts
635
- interface AIOPsDashboardProps {
636
- title?: string;
637
- brandName?: string; // Default: "BUSAUD AIOps"
638
- brandTag?: string; // Default: "3D MONITOR"
639
- services?: ServiceMeta[];
640
- backgroundImage?: string;
641
- logoUrl?: string;
642
- carouselSpeed?: number; // Default: 0.006
643
- fontFamily?: string;
644
- liveData?: boolean; // Default: false
645
- dataEndpoint?: string;
646
- dataBindings?: DataBindings;
647
- dataTransform?: (raw: unknown) => unknown;
648
- dataRefreshInterval?: number; // Default: 60000
649
- serviceDataBindings?: Record<string, ServiceMetricBinding[]>;
650
- eventApiConfig?: EventApiConfig; // Enable event-to-dashboard bridge
651
- children: React.ReactNode;
652
- }
653
- ```
654
-
655
- ### ServiceMeta
656
-
657
- ```ts
658
- interface ServiceMeta {
659
- name: string; // Must match child component name
660
- status: ComponentStatus;
661
- dbSync?: boolean; // Default: true
662
- metrics?: ServiceDialogMetric[];
663
- alerts?: ServiceDialogAlert[];
664
- }
665
-
666
- interface ServiceDialogMetric {
667
- label: string;
668
- value: string;
669
- color: string;
670
- }
671
-
672
- interface ServiceDialogAlert {
673
- level: "info" | "warning" | "critical";
674
- message: string;
675
- }
676
- ```
677
-
678
- ### ConnectionConfig
679
-
680
- ```ts
681
- interface ConnectionConfig {
682
- from: [number, number];
683
- to: [number, number];
684
- visibleAtPhase?: number; // Default: 0
685
- color?: string; // Default: "#00e5ff"
686
- duration?: string; // Default: "1.2s"
687
- }
688
- ```
689
-
690
- ### ComponentStatus
691
-
692
- ```ts
693
- type ComponentStatus = "online" | "warning" | "critical" | "offline";
694
- ```
695
-
696
- | Status | Color | Glow |
697
- | ---------- | --------- | -------- |
698
- | `online` | `#00e5ff` | cyan |
699
- | `warning` | `#ff8c00` | orange |
700
- | `critical` | `#ff2255` | red |
701
- | `offline` | `#1e3a5a` | dim blue |
702
-
703
- ---
704
-
705
- ## Endpoint contract
706
-
707
- | Aspect | Requirement |
708
- | ------------ | -------------------------------------------------------------- |
709
- | **Method** | `GET` |
710
- | **URL** | `<dataEndpoint>?query=<urlEncodedQuery>` |
711
- | **Headers** | `access-key` and `access-secret-key` |
712
- | **Response** | Plain text body (trimmed). Default transform parses as number. |
713
- | **Errors** | Non-2xx → counted as failure per query |
714
-
715
- For JSON responses, provide a custom `dataTransform`:
716
-
717
- ```tsx
718
- dataTransform={(raw) => {
719
- const parsed = JSON.parse(String(raw));
720
- return parsed?.data?.result?.[0]?.value?.[1] ?? raw;
721
- }}
722
- ```
723
-
724
- ---
725
-
726
- ## Standalone DataProvider
727
-
728
- Use the data layer without `AIOPsDashboard`:
729
-
730
- ```tsx
731
- import { DataProvider, useAIOpsData } from "react-dashstream";
732
-
733
- <DataProvider
734
- config={{
735
- endpoint: "https://prometheus.example.com/api/v1/query",
736
- queries: ['cpu_usage{instance="srv-01"}'],
737
- refreshInterval: 15000,
738
- }}
739
- >
740
- <MyContent />
741
- </DataProvider>;
742
- ```
743
-
744
- ---
745
-
746
- ## Node positioning guide
747
-
748
- The topology scene is ~660×640 pixels.
749
-
750
- | Layer | Typical `ey` | `visibleAtPhase` | Component |
751
- | ----------- | ------------ | ---------------- | ------------------- |
752
- | Users (top) | 100 | 2 | `HumanNode` |
753
- | Dispatcher | 230 | 2 | `WebDispatcherNode` |
754
- | App servers | 350–380 | 3 | `ServerNode` |
755
- | Databases | 500–520 | 4 | `DatabaseNode` |
756
-
757
- Center X = `330`. Left/right columns: `200` / `460`. Three-column: `165` / `330` / `495`.
758
-
759
- `compactOffset` controls node position in the compact carousel view relative to center.
760
-
761
- ---
762
-
763
- ## Usage patterns
764
-
765
- ### Minimal static dashboard
766
-
767
- ```tsx
768
- import "react-dashstream/dist/index.css";
769
- import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
770
-
771
- <AIOPsDashboard brandName="MY DASHBOARD" services={[{ name: "Svc", status: "online" }]}>
772
- <Service
773
- name="Svc"
774
- status="online"
775
- connections={[{ from: [330, 200], to: [330, 380], visibleAtPhase: 3 }]}
776
- >
777
- <ServerNode
778
- ex={330}
779
- ey={380}
780
- compactOffset={{ x: 0, y: 0 }}
781
- zIndex={8}
782
- name="SRV-01"
783
- status="online"
784
- cpuLoad={42}
785
- memLoad={60}
786
- />
787
- </Service>
788
- </AIOPsDashboard>;
789
- ```
790
-
791
- ### Full live-data dashboard
792
-
793
- ```tsx
794
- <AIOPsDashboard
795
- brandName="LIVE MONITOR"
796
- services={services}
797
- liveData={true}
798
- dataEndpoint="https://prometheus.example.com/api/v1/query"
799
- dataRefreshInterval={10000}
800
- dataBindings={{
801
- "My Service": {
802
- cpuLoad: 'cpu_usage{instance="srv-01"}',
803
- status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
804
- },
805
- }}
806
- serviceDataBindings={{
807
- "My Service": [{ label: "CPU", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" }],
808
- }}
809
- >
810
- <MyService name="My Service" />
811
- </AIOPsDashboard>
812
- ```
813
-
814
- ### Custom service with all data features
815
-
816
- ```tsx
817
- function MyService({ name, status, cpuLoad, memLoad, dbCapacity, dbStatus }: any) {
818
- return (
819
- <Service
820
- name={name}
821
- status={status ?? "online"}
822
- connections={[
823
- { from: [330, 200], to: [200, 380], visibleAtPhase: 3 },
824
- { from: [330, 200], to: [460, 380], visibleAtPhase: 3 },
825
- ]}
826
- >
827
- <ServerNode
828
- ex={200}
829
- ey={380}
830
- compactOffset={{ x: -30, y: -20 }}
831
- zIndex={8}
832
- name="SRV-01"
833
- subLabel="APP SERVER"
834
- status={status ?? "online"}
835
- cpuLoad={cpuLoad ?? 42}
836
- memLoad={memLoad ?? 60}
837
- alert={{ offsetX: -160, offsetY: -60, align: "left" }}
838
- dialogMetrics={[
839
- { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad ?? 42 },
840
- { id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad ?? 60 },
841
- ]}
842
- subComponents={[
843
- {
844
- id: "cpu",
845
- label: "CPU-0",
846
- status: "online",
847
- element: <CPU3D label="CPU-0" load={cpuLoad ?? 42} />,
848
- },
849
- {
850
- id: "mem",
851
- label: "HEAP",
852
- status: "online",
853
- element: <Memory3D label="HEAP" usedPercent={memLoad ?? 60} />,
854
- },
855
- ]}
856
- graphSeries={[
857
- { id: "cpu", label: "CPU", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68] },
858
- ]}
859
- />
860
- <DatabaseNode
861
- ex={460}
862
- ey={380}
863
- compactOffset={{ x: 30, y: -20 }}
864
- zIndex={7}
865
- name="DB-01"
866
- subLabel="PRIMARY"
867
- status={dbStatus ?? "online"}
868
- capacity={dbCapacity ?? 55}
869
- alert={{ offsetX: 160, offsetY: -60, align: "right" }}
870
- />
871
- </Service>
872
- );
873
- }
874
- ```
875
-
876
- ---
877
-
878
- ## EventView — Operations Event Console
879
-
880
- 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).
881
-
882
- ### API mode (primary usage)
883
-
884
- ```tsx
885
- import "react-dashstream/dist/index.css";
886
- import { EventView } from "react-dashstream";
887
- import type { EventApiConfig } from "react-dashstream";
888
-
889
- const apiConfig: EventApiConfig = {
890
- baseUrl: "https://your-monitoring-server.example.com",
891
- payload: { filter: {}, sortBy: "date_reception", sortOrder: "DESC" },
892
- fieldMapping: {
893
- id: "mc_ueid",
894
- occurrence: "date_reception",
895
- severityLastModified: "severity_last_modified",
896
- severity: "severity",
897
- owner: "owner",
898
- class: "class",
899
- host: "mc_host",
900
- message: "msg",
901
- remedySupportGroup: "ara_remedy_support_group",
902
- incidentId: "ara_incident_id",
903
- smsStatus: "ara_sms_status",
904
- onCallNumber: "ara_on_call_number",
905
- hostedApplication: "ara_hosted_application",
906
- monitoringCategory: "ara_hosted_app_monitoring",
907
- applicationSupportUnit: "ara_application_support_unit",
908
- },
909
- };
910
-
911
- <EventView apiConfig={apiConfig} />;
912
- ```
913
-
914
- ### How it works
915
-
916
- 1. Shows credentials modal (or reuses DataProvider credentials if inside `AIOPsDashboard`)
917
- 2. Sends `POST {baseUrl}/tsws/monitoring/api/v1.0/events/search` with the payload as JSON body
918
- 3. Auth via headers: `access-key` and `access-secret-key`
919
- 4. Expects response: `{ eventSeverityCount: {...}, totalCount: N, eventList: [{...}] }`
920
- 5. Maps each object in `eventList` to `AIOpsEvent` using `fieldMapping`
921
- 6. Maps severity strings via `severityMap` (default: `CRITICAL→Critical`, `MAJOR→Major`, `MINOR→Minor`)
922
- 7. Polls on `refreshInterval` (default 60s)
923
-
924
- ### Props
925
-
926
- | Prop | Type | Default | Notes |
927
- | -------------------- | ---------------- | ----------------- | -------------------------------------------------- |
928
- | `apiConfig` | `EventApiConfig` | — | Required for API mode |
929
- | `events` | `AIOpsEvent[]` | — | Pre-fetched events (skips API calls) |
930
- | `credentials` | `Credentials` | — | Explicit creds; falls back to DataProvider → modal |
931
- | `columnWidthsCookie` | `string` | `"ev_col_widths"` | Cookie name for persisting column widths |
932
- | `title` | `string` | `"Event Console"` | Header title |
933
-
934
- ### EventApiConfig
935
-
936
- ```ts
937
- interface EventApiConfig {
938
- baseUrl: string; // "https://monitoring.example.com"
939
- endpoint?: string; // default: "/tsws/monitoring/api/v1.0/events/search"
940
- payload: Record<string, unknown>; // POST body
941
- fieldMapping: EventFieldMapping; // maps API keys → AIOpsEvent keys
942
- severityMap?: Record<string, EventSeverity>; // default: { CRITICAL, MAJOR, MINOR }
943
- refreshInterval?: number; // ms, default 60000
944
- }
945
- ```
946
-
947
- ### EventFieldMapping
948
-
949
- Maps every `AIOpsEvent` property to the API response field name. All 15 keys required:
950
-
951
- ```ts
952
- type EventFieldMapping = Record<keyof AIOpsEvent, string>;
953
- ```
954
-
955
- ### AIOpsEvent interface
956
-
957
- ```ts
958
- interface AIOpsEvent {
959
- id: string;
960
- occurrence: string;
961
- severityLastModified: string;
962
- severity: "Minor" | "Major" | "Critical";
963
- owner: string;
964
- class: "Self-monitoring" | "SCOM" | "Situation" | "Alarm" | "Event";
965
- host: string;
966
- message: string;
967
- remedySupportGroup: string;
968
- incidentId: string;
969
- smsStatus: "" | "SUCCESS" | "FAILED";
970
- onCallNumber: string;
971
- hostedApplication: string;
972
- monitoringCategory: "24/7" | "Office Hours";
973
- applicationSupportUnit: string;
974
- }
975
- ```
976
-
977
- ### Credential resolution order
978
-
979
- 1. `credentials` prop (if provided)
980
- 2. `DataProvider` context credentials (when inside `AIOPsDashboard` with `liveData`)
981
- 3. Built-in credentials modal (same UI as the 3D dashboard)
982
-
983
- ### Severity colors
984
-
985
- - **Minor** `#ffb800` (amber)
986
- - **Major** → `#ff6600` (orange)
987
- - **Critical** `#ff2255` (red)
988
-
989
- ### Features
990
-
991
- - Live API polling with configurable interval and manual refresh button
992
- - Configurable field mapping — any API response shape can be mapped
993
- - Shared auth reuses DataProvider credentials when available
994
- - Severity filter chips with live counts
995
- - Free-text search across message, host, owner, incident ID
996
- - Sortable columns (click header: asc desc → none)
997
- - Infinite scroll — all matching events in a single scrollable table
998
- - Resizable columns drag column edges; widths persist in cookies
999
- - Loading spinner, error badge, last-refresh timestamp
1000
- - Holographic theme: scan-line header, glowing rows, severity badges
1001
-
1002
- ### Generating an EventApiConfig from the AI
1003
-
1004
- When asked to set up EventView for a new API, generate the config like this:
1005
-
1006
- 1. Set `baseUrl` to the server's protocol + host + port
1007
- 2. Set `endpoint` only if it differs from the default `/tsws/monitoring/api/v1.0/events/search`
1008
- 3. Build `payload` based on the API's expected POST body schema
1009
- 4. Build `fieldMapping` by asking the user which API field maps to each `AIOpsEvent` property
1010
- 5. Set `severityMap` only if the API uses non-standard severity values
1011
-
1012
- ---
1013
-
1014
- ## Build and development
1015
-
1016
- ```bash
1017
- npm run dev # Vite dev server (loads example/Dashboard.tsx)
1018
- npm run build # TypeScript check + Vite library build
1019
- npm run build:lib # Vite library build only
1020
- npm run preview # Preview production build
1021
- ```
1022
-
1023
- Output: `dist/index.js` (ESM), `dist/index.d.ts`, `dist/index.css`.
1024
-
1025
- ---
1026
-
1027
- ## Common pitfalls
1028
-
1029
- 1. **Missing CSS import** Always import `react-dashstream/dist/index.css`.
1030
- 2. **Name mismatch** — `name` on child components must exactly match keys in `dataBindings`, `serviceDataBindings`, and `ServiceMeta.name`.
1031
- 3. **Credentials are in-memory only** — Page refresh requires re-entering credentials.
1032
- 4. **Default transform is plain-text** — For JSON endpoints, provide a custom `dataTransform`.
1033
- 5. **Polling only** No WebSocket/SSE support; uses `setInterval`.
1034
- 6. **`visibleAtPhase` matters** Nodes without proper values won't animate during expansion.
1035
- 7. **Connection coordinates** — `from`/`to` arrays use `[x, y]` matching `ex`/`ey` of connected nodes.
1036
- 8. **`dialogMetrics` replaces defaults** When provided, all default gauges (CPU/Memory/Storage) are removed.
1037
- 9. **`subComponents` replaces defaults** When provided, all auto-generated sub-components are removed.
1038
- 10. **`graphSeries` replaces defaults** When provided, all auto-generated sparklines are removed.
1039
- 11. **Event bridge matching** — Node `name` must match event `host` (case-insensitive) for `eventApiConfig` to highlight nodes automatically.
1
+ # React DashStream — AI Coding Assistant Skill
2
+
3
+ > **Package**: `react-dashstream` (npm)
4
+ > **Version**: 0.2.0
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
+ ## Light / dark theme
39
+
40
+ The dashboard supports **`"light"`** and **`"dark"`** modes.
41
+
42
+ - **`AIOPsDashboard`** defaults to **light** and exposes a header toggle. Use **`backgroundImage`** (dark) and **`lightBackgroundImage`** (light when set).
43
+ - **Controlled mode:** pass **`theme`** and **`onThemeChange`** (`DashboardTheme`) so parent state owns the mode (e.g. sync app chrome + `EventView`).
44
+ - **`ThemeProvider`** (export) + **`useTheme()`** — wrap subtrees so **`EventView`**, **`CredentialsModal`**, **`ServiceDialog`**, and **`ComponentDialog`** match the dashboard. Without a provider, `useTheme()` defaults to **`"dark"`**; prefer wrapping when using `EventView` or the lock screen next to a light dashboard.
45
+ - **`EventView`** accepts optional **`theme`** to override context.
46
+
47
+ When generating multi-view apps (3D + Event Console + credentials), wrap the root in **`ThemeProvider`** with the same value passed to **`AIOPsDashboard`**’s controlled props.
48
+
49
+ ---
50
+
51
+ ## Architecture
52
+
53
+ ```
54
+ AIOPsDashboard Shell: header, state, optional DataProvider
55
+ └─ Carousel 3D orbit, rotation, hosts dialogs
56
+ └─ Service Per-service container, connections, holo base
57
+ ├─ ServerNode ← Compound: ServiceNode + Server3D
58
+ ├─ DatabaseNode ← Compound: ServiceNode + Database3D
59
+ ├─ WebDispatcherNode Compound: ServiceNode + WebDispatcher3D
60
+ ├─ MessageServerNode ← Compound: ServiceNode + MessageServer3D
61
+ ├─ HumanNode Compound: ServiceNode + Human3D (SVG)
62
+ ├─ SvgConnection ← Animated dashed topology line
63
+ ├─ SyncBridge ← DB replication indicator
64
+ ├─ NodeCallout ← Alert callout (auto from thresholds)
65
+ └─ HoloBase ← Neon platform (auto-rendered)
66
+
67
+ ServiceDialog ← Service-level KPI panel (auto on expand)
68
+ ComponentDialog ← Drill-down with internals + graphs
69
+ ├─ HistoricalGraphPanel ← Sparkline charts
70
+ └─ CPU3D / Memory3D / DriveBay3D / NetworkBlock3D / ThreadPool3D / Platter3D / Port3D
71
+
72
+ DataProvider ← Polling engine + credentials modal
73
+
74
+ EventView ← Standalone event console (table view)
75
+
76
+ DatacenterView ← SVG topology / optional geography map + drill-down dashboard
77
+ ```
78
+
79
+ ### Source layout
80
+
81
+ ```
82
+ src/
83
+ ├── index.ts # Public API barrel
84
+ ├── AIOPsDashboard.tsx # Dashboard shell + live-data wiring
85
+ ├── types.ts # Shared topology/drill-down types
86
+ ├── theme.ts # Status tokens, holo palette, 3D face helpers
87
+ ├── ThemeContext.ts # DashboardTheme, ThemeProvider, useTheme
88
+ ├── styles.css / index.css # Library + dashboard styles
89
+ ├── data/
90
+ │ ├── DataProvider.tsx # Polling engine, context, hooks
91
+ │ ├── CredentialsModal.tsx # Auth prompt UI
92
+ │ └── index.ts
93
+ ├── components/ # ~30 UI components
94
+ ├── Carousel.tsx, Service.tsx, ServiceNode.tsx
95
+ │ ├── ServerNode.tsx, DatabaseNode.tsx, WebDispatcherNode.tsx, etc.
96
+ │ ├── Server3D.tsx, Database3D.tsx, etc.
97
+ ├── ServiceDialog.tsx, ComponentDialog.tsx
98
+ │ ├── Internal3DComponents.tsx, HistoricalGraphPanel.tsx
99
+ ├── ComponentDrillView.tsx, SvgConnection.tsx, SyncBridge.tsx
100
+ │ ├── NodeCallout.tsx, HoloBase.tsx, CarouselContext.ts
101
+ │ ├── EventView/ # Operations event console
102
+ │ │ ├── EventView.tsx # Table component (filter, sort, search, infinite scroll)
103
+ │ │ ├── EventView.css # Holographic table styles
104
+ │ │ ├── EventAlertsContext.tsx # EventAlertsProvider + per-host alert map
105
+ │ │ ├── fetchEvents.ts # Shared fetch/mapping logic (used by both EventView & provider)
106
+ │ │ ├── types.ts # AIOpsEvent, EventSeverity, EventApiConfig, etc.
107
+ │ │ ├── mockData.ts # 28 realistic mock events
108
+ │ │ └── index.ts
109
+ │ ├── DatacenterView/ # Topology map + CSS 3D building markers
110
+ │ │ ├── DatacenterView.tsx
111
+ │ │ ├── DatacenterView.css
112
+ │ │ ├── types.ts
113
+ │ │ └── index.ts
114
+ │ └── index.ts
115
+ ├── services/
116
+ │ ├── SAPService.tsx, ExchangeService.tsx
117
+ │ ├── sapSubComponents.tsx
118
+ │ └── index.ts
119
+ example/
120
+ ├── Dashboard.tsx
121
+ └── services/
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Complete data flow reference
127
+
128
+ There are **10 distinct data paths** in this package. Understanding them is critical for building live dashboards.
129
+
130
+ ### 1. Data bindings — live props injected into service components
131
+
132
+ `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.
133
+
134
+ ```ts
135
+ type DataBinding = string | { query: string; transform?: (raw: unknown) => unknown };
136
+ type DataBindings = Record<string, Record<string, DataBinding>>;
137
+ ```
138
+
139
+ - Bare string → uses global `dataTransform` (defaults to numeric parsing)
140
+ - Object `{ query, transform }` → uses per-binding transform
141
+ - Service name key must match child's `name` prop **exactly**
142
+
143
+ ```tsx
144
+ const dataBindings: DataBindings = {
145
+ "My Service": {
146
+ cpuLoad: 'cpu_usage{instance="srv-01"}', // bare string → number
147
+ memLoad: 'memory_usage{instance="srv-01"}', // bare string → number
148
+ status: {
149
+ // object → custom transform
150
+ query: 'cpu_usage{instance="srv-01"}',
151
+ transform: (raw) => {
152
+ const n = Number(raw);
153
+ if (n >= 85) return "critical";
154
+ if (n >= 70) return "warning";
155
+ return "online";
156
+ },
157
+ },
158
+ dbCapacity: 'disk_capacity{instance="db-01"}',
159
+ dbStatus: { query: 'disk_capacity{instance="db-01"}', transform: statusFromValue },
160
+ },
161
+ };
162
+ ```
163
+
164
+ #### Multi-component services
165
+
166
+ 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.
167
+
168
+ ```tsx
169
+ interface ServiceXProps {
170
+ name: string;
171
+ status?: ComponentStatus;
172
+ srv1CpuLoad?: number;
173
+ srv1MemLoad?: number;
174
+ srv1Status?: ComponentStatus;
175
+ srv2CpuLoad?: number;
176
+ srv2MemLoad?: number;
177
+ srv2Status?: ComponentStatus;
178
+ srv3CpuLoad?: number;
179
+ srv3MemLoad?: number;
180
+ srv3Status?: ComponentStatus;
181
+ dbCapacity?: number;
182
+ dbStatus?: ComponentStatus;
183
+ }
184
+
185
+ function ServiceX({
186
+ name, status = "online",
187
+ srv1CpuLoad = 54, srv1MemLoad = 58, srv1Status = "online",
188
+ srv2CpuLoad = 63, srv2MemLoad = 66, srv2Status = "online",
189
+ srv3CpuLoad = 78, srv3MemLoad = 71, srv3Status = "online",
190
+ dbCapacity = 68, dbStatus = "online",
191
+ }: ServiceXProps) {
192
+ return (
193
+ <Service name={name} status={status} connections={[/* ... */]}>
194
+ <ServerNode name="SRV-X1" status={srv1Status}
195
+ cpuLoad={srv1CpuLoad} memLoad={srv1MemLoad} ... />
196
+ <ServerNode name="SRV-X2" status={srv2Status}
197
+ cpuLoad={srv2CpuLoad} memLoad={srv2MemLoad} ... />
198
+ <ServerNode name="SRV-X3" status={srv3Status}
199
+ cpuLoad={srv3CpuLoad} memLoad={srv3MemLoad} ... />
200
+ <DatabaseNode name="DB-X1" status={dbStatus}
201
+ capacity={dbCapacity} ... />
202
+ </Service>
203
+ );
204
+ }
205
+ ```
206
+
207
+ Bind each prefixed prop individually:
208
+
209
+ ```tsx
210
+ dataBindings={{
211
+ ServiceX: {
212
+ status: { query: 'status{instance="svcx"}', transform: statusFromValue },
213
+ srv1CpuLoad: 'cpu_usage{instance="srvx-01"}',
214
+ srv1MemLoad: 'memory_usage{instance="srvx-01"}',
215
+ srv1Status: { query: 'status{instance="srvx-01"}', transform: statusFromValue },
216
+ srv2CpuLoad: 'cpu_usage{instance="srvx-02"}',
217
+ srv2MemLoad: 'memory_usage{instance="srvx-02"}',
218
+ srv2Status: { query: 'status{instance="srvx-02"}', transform: statusFromValue },
219
+ srv3CpuLoad: 'cpu_usage{instance="srvx-03"}',
220
+ srv3MemLoad: 'memory_usage{instance="srvx-03"}',
221
+ srv3Status: { query: 'status{instance="srvx-03"}', transform: statusFromValue },
222
+ dbCapacity: 'disk_capacity{instance="dbx-01"}',
223
+ dbStatus: { query: 'status{instance="dbx-01"}', transform: statusFromValue },
224
+ },
225
+ }}
226
+ ```
227
+
228
+ Use a consistent convention like `srv1CpuLoad`, `srv2CpuLoad`. The service component maps each prefixed prop to the correct node.
229
+
230
+ ### 2. Service dialog — static ServiceMeta metrics
231
+
232
+ The **ServiceDialog** panel shows KPIs when a service is expanded. Static metrics are passed via `ServiceMeta`:
233
+
234
+ ```tsx
235
+ const services: ServiceMeta[] = [
236
+ {
237
+ name: "My Service",
238
+ status: "online",
239
+ metrics: [
240
+ { label: "Uptime", value: "99.99%", color: "#00ff88" },
241
+ { label: "Avg Latency", value: "8ms", color: "#00e5ff" },
242
+ ],
243
+ alerts: [
244
+ { level: "info", message: "All Systems Nominal" },
245
+ { level: "warning", message: "High CPU on SRV-01" },
246
+ { level: "critical", message: "DB connection pool exhausted" },
247
+ ],
248
+ },
249
+ ];
250
+ ```
251
+
252
+ ### 3. Service dialog — live serviceDataBindings
253
+
254
+ Replace static metrics with live-fetched values using `serviceDataBindings`:
255
+
256
+ ```tsx
257
+ serviceDataBindings={{
258
+ "My Service": [
259
+ { label: "CPU Load", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" },
260
+ { label: "Memory", query: 'memory_usage{instance="srv-01"}', unit: "%", color: "#bb55ff" },
261
+ { label: "Disk", query: 'disk_capacity{instance="db-01"}', unit: "%", color: "#ff8c00" },
262
+ ],
263
+ }}
264
+ ```
265
+
266
+ ```ts
267
+ interface ServiceMetricBinding {
268
+ label: string; // Row label
269
+ query: string; // PromQL query
270
+ unit?: string; // Suffix (e.g. "%", "ms")
271
+ color?: string; // Accent color
272
+ transform?: (raw: unknown) => string; // Custom formatter
273
+ }
274
+ ```
275
+
276
+ When provided, live values **replace** static `ServiceMeta.metrics` for that service.
277
+
278
+ ### 4. Component dialog — default gauges
279
+
280
+ The **ComponentDialog** appears when clicking a node. Default gauges are derived from node type:
281
+
282
+ - **Server**: CPU (from `cpuLoad`), Memory (from `memLoad`), Storage
283
+ - **Database**: Capacity (from `capacity`), Memory, Storage
284
+ - **Dispatcher**: Traffic (from `traffic`), Memory, Storage
285
+ - **Message Server**: Queue (from `queueDepth`), Memory, Storage
286
+
287
+ Default thresholds: warn at 70, critical at 85.
288
+
289
+ ### 5. Component dialog — custom dialogMetrics
290
+
291
+ Override default gauges with `dialogMetrics` on any compound node:
292
+
293
+ ```tsx
294
+ <ServerNode
295
+ name="SRV-01"
296
+ status="online"
297
+ cpuLoad={67}
298
+ memLoad={72}
299
+ ex={200}
300
+ ey={380}
301
+ compactOffset={{ x: -30, y: -20 }}
302
+ zIndex={8}
303
+ dialogMetrics={[
304
+ { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67, unit: "%" },
305
+ { id: "mem", label: "MEMORY", sublabel: "HEAP USAGE", value: 72, unit: "%" },
306
+ { id: "iops", label: "IOPS", sublabel: "DISK OPS", value: 45, unit: "k/s", icon: "disk" },
307
+ {
308
+ id: "threads",
309
+ label: "THREADS",
310
+ sublabel: "ACTIVE",
311
+ value: 82,
312
+ unit: "%",
313
+ warnAt: 60,
314
+ critAt: 80,
315
+ icon: "cpu",
316
+ },
317
+ ]}
318
+ />
319
+ ```
320
+
321
+ ```ts
322
+ interface ComponentDialogMetric {
323
+ id: string; // Unique key
324
+ label: string; // Upper label
325
+ sublabel: string; // Lower label
326
+ value: number; // 0–100
327
+ unit?: string; // Default "%"
328
+ icon?: "cpu" | "mem" | "disk"; // Default "cpu"
329
+ warnAt?: number; // Default 70
330
+ critAt?: number; // Default 85
331
+ color?: string; // Override bar color (bypasses threshold coloring)
332
+ }
333
+ ```
334
+
335
+ All compound nodes accept `dialogMetrics`: `ServerNode`, `DatabaseNode`, `WebDispatcherNode`, `MessageServerNode`.
336
+
337
+ **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:
338
+
339
+ ```tsx
340
+ // Service component — accepts individual metric props, builds dialogMetrics from them
341
+ function MyService({ name, status = "online", cpuLoad = 42, memLoad = 60, iops = 20 }: any) {
342
+ return (
343
+ <Service
344
+ name={name}
345
+ status={status}
346
+ connections={
347
+ [
348
+ /* ... */
349
+ ]
350
+ }
351
+ >
352
+ <ServerNode
353
+ name="SRV-01"
354
+ status={status}
355
+ cpuLoad={cpuLoad}
356
+ memLoad={memLoad}
357
+ ex={200}
358
+ ey={380}
359
+ compactOffset={{ x: -30, y: -20 }}
360
+ zIndex={8}
361
+ dialogMetrics={[
362
+ { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad },
363
+ { id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad },
364
+ {
365
+ id: "iops",
366
+ label: "IOPS",
367
+ sublabel: "DISK OPS",
368
+ value: iops,
369
+ icon: "disk",
370
+ warnAt: 50,
371
+ critAt: 80,
372
+ },
373
+ ]}
374
+ />
375
+ </Service>
376
+ );
377
+ }
378
+
379
+ // Dashboard bind each prop to a query
380
+ <AIOPsDashboard
381
+ liveData
382
+ dataEndpoint="..."
383
+ dataBindings={{
384
+ "My Service": {
385
+ cpuLoad: 'cpu_usage{instance="srv-01"}',
386
+ memLoad: 'memory_usage{instance="srv-01"}',
387
+ iops: 'disk_iops{instance="srv-01"}',
388
+ status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
389
+ },
390
+ }}
391
+ >
392
+ <MyService name="My Service" />
393
+ </AIOPsDashboard>;
394
+ ```
395
+
396
+ 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.
397
+
398
+ ### 6. Alerts — automatic threshold detection
399
+
400
+ Nodes auto-render `NodeCallout` alerts when metrics breach thresholds:
401
+
402
+ - **Warning** at 70% → orange callout
403
+ - **Critical** at 85% red callout
404
+
405
+ Sources (checked in priority order):
406
+
407
+ 1. `dialogMetrics` values vs their `warnAt`/`critAt`
408
+ 2. Context values (`cpuLoad`, `memLoad`, `traffic`, `queueDepth`, `capacity`) vs default thresholds
409
+
410
+ ```tsx
411
+ // Auto-alert from cpuLoad > 85
412
+ <ServerNode cpuLoad={92} memLoad={64}
413
+ alert={{ offsetX: -160, offsetY: -60, align: "left" }}
414
+ ... />
415
+
416
+ // Custom message
417
+ <ServerNode cpuLoad={92}
418
+ alert={{ msg: "CPU overload scale out", offsetX: -160, offsetY: -60, align: "left" }}
419
+ ... />
420
+
421
+ // Custom thresholds via dialogMetrics
422
+ <ServerNode
423
+ dialogMetrics={[
424
+ { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67,
425
+ warnAt: 50, critAt: 75 }, // Alert at 67 because critAt=75
426
+ ]}
427
+ alert={{ offsetX: -160, offsetY: -60, align: "left" }}
428
+ ... />
429
+ ```
430
+
431
+ Alert `align` options: `"left"`, `"right"`, `"top"`, `"bottom"`.
432
+
433
+ Alert prop shape:
434
+
435
+ ```ts
436
+ alert?: {
437
+ msg?: string; // Custom message (auto-generated if omitted)
438
+ offsetX?: number; // X offset from node (default 110)
439
+ offsetY?: number; // Y offset from node (default -30)
440
+ align?: "left" | "right" | "top" | "bottom"; // Default "right"
441
+ internalRef?: string; // Sub-component ID
442
+ }
443
+ ```
444
+
445
+ ### 7. Event-to-dashboard bridge — automatic node alerts from events API
446
+
447
+ When `eventApiConfig` is passed to `AIOPsDashboard`, events are fetched in the background and nodes whose `name` (case-insensitive) matches an event `host` automatically show alert callouts.
448
+
449
+ ```tsx
450
+ <AIOPsDashboard
451
+ eventApiConfig={eventApiConfig} // same EventApiConfig used for EventView
452
+ liveData
453
+ dataEndpoint="..."
454
+ dataBindings={dataBindings}
455
+ services={services}
456
+ >
457
+ <MyService name="My Service" />
458
+ </AIOPsDashboard>
459
+ ```
460
+
461
+ **How it works internally:**
462
+
463
+ 1. `AIOPsDashboard` wraps children in `EventAlertsProvider` (inside `DataProvider` when `liveData` is set — shares credentials automatically)
464
+ 2. `EventAlertsProvider` fetches events using the same `fetchEvents` + `mapRawEvent` logic as `EventView`
465
+ 3. Events are grouped by **lower-cased hostname** into a `Map<string, EventHostAlert>`
466
+ 4. `ServiceNode` reads from `EventAlertsContext` via `useEventAlertsOptional()`
467
+ 5. If `componentInfo.name.toLowerCase()` matches an entry, event severity is factored into the existing `resolveNodeSeverity` pipeline as a **third severity source** (alongside node status and metric breaches)
468
+ 6. Highest severity among all three sources wins
469
+ 7. `NodeCallout` appears automatically with the event message; multi-event hosts show `"N events – message"`
470
+
471
+ **Severity mapping:**
472
+
473
+ | Event severity | → ComponentStatus |
474
+ | -------------- | ----------------- |
475
+ | Critical | `"critical"` |
476
+ | Major | `"warning"` |
477
+ | Minor | `"warning"` |
478
+
479
+ **Key requirement:** Node `name` props must match event `host` values from the API (case-insensitive). Example: if the API returns `host: "SAP-PRD-APP01"`, the node must be `<ServerNode name="SAP-PRD-APP01" ... />`.
480
+
481
+ **Credential resolution order** (for EventAlertsProvider inside AIOPsDashboard):
482
+
483
+ 1. `DataProvider` context credentials (when `liveData` is enabled — same credentials, one login)
484
+ 2. Built-in `CredentialsModal` (when `liveData` is not enabled)
485
+
486
+ **Opt-in:** Without `eventApiConfig`, no events are fetched — zero overhead.
487
+
488
+ **When generating an `AIOPsDashboard` with event bridge:** Reuse the same `EventApiConfig` object that the user already has for their `EventView`. Just add it as `eventApiConfig` on `AIOPsDashboard`.
489
+
490
+ ### 8. Drill-down internals custom subComponents
491
+
492
+ When clicking a node, the drill-down shows internal 3D sub-components. Default sub-components are auto-generated per type. Override with `subComponents`:
493
+
494
+ ```tsx
495
+ import { CPU3D, Memory3D, DriveBay3D, ThreadPool3D, NetworkBlock3D, Platter3D, Port3D } from "react-dashstream";
496
+ import type { SubComponentConfig } from "react-dashstream";
497
+
498
+ const internals: SubComponentConfig[] = [
499
+ { id: "cpu-0", label: "CPU-0", status: "online",
500
+ element: <CPU3D label="CPU-0" load={67} color="#00e5ff" /> },
501
+ { id: "heap", label: "HEAP", status: "warning",
502
+ element: <Memory3D label="HEAP" usedPercent={92} color="#8855ee" status="warning" /> },
503
+ { id: "drive", label: "DRIVE-0", status: "online",
504
+ element: <DriveBay3D label="DRIVE-0" color="#00e5ff" activity={true} /> },
505
+ { id: "threads", label: "THREADS", status: "online",
506
+ element: <ThreadPool3D label="THREADS" color="#00e5ff" /> },
507
+ { id: "net", label: "NET", status: "online",
508
+ element: <NetworkBlock3D label="NET" color="#00e5ff" /> },
509
+ ];
510
+
511
+ <ServerNode subComponents={internals} ... />
512
+ ```
513
+
514
+ ```ts
515
+ interface SubComponentConfig {
516
+ id: string; // Unique key
517
+ label: string; // Display name
518
+ status: ComponentStatus; // LED color
519
+ detail?: string; // Fault description
520
+ element: ReactNode; // 3D element to render
521
+ }
522
+ ```
523
+
524
+ All compound nodes accept `subComponents`.
525
+
526
+ ### 9. Graph series — custom sparklines
527
+
528
+ The drill-down's historical panel shows sparkline charts. Default graphs are auto-generated. Override with `graphSeries`:
529
+
530
+ ```tsx
531
+ import type { GraphSeries } from "react-dashstream";
532
+
533
+ const graphs: GraphSeries[] = [
534
+ { id: "cpu", label: "CPU-0", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68, 55] },
535
+ { id: "mem", label: "HEAP", unit: "%", color: "#8855ee", data: [60, 65, 72, 78, 82, 75] },
536
+ { id: "iops", label: "DISK", unit: "k/s", color: "#ff8c00", data: [12, 15, 22, 18, 25, 20] },
537
+ ];
538
+
539
+ <ServerNode graphSeries={graphs} ... />
540
+ ```
541
+
542
+ ```ts
543
+ interface GraphSeries {
544
+ id: string; // Unique key
545
+ label: string; // Label above sparkline
546
+ unit: string; // Suffix
547
+ color: string; // Line color
548
+ data: number[]; // Points (most recent last)
549
+ }
550
+ ```
551
+
552
+ All compound nodes accept `graphSeries`.
553
+
554
+ ### 10. Data hooks — programmatic access
555
+
556
+ Access live data context anywhere inside the dashboard:
557
+
558
+ ```tsx
559
+ import { useAIOpsData, useAIOpsDataOptional, useQueryResult } from "react-dashstream";
560
+
561
+ function CustomWidget() {
562
+ const { data, isRefreshing, lastRefreshError, lastRefreshTime, credentialsSet } = useAIOpsData();
563
+ const cpu = useQueryResult('cpu_usage{instance="srv-01"}');
564
+ return <div>CPU: {String(cpu)}</div>;
565
+ }
566
+ ```
567
+
568
+ | Hook | Returns | Throws? |
569
+ | ------------------------ | -------------------------- | ---------------------------- |
570
+ | `useAIOpsData()` | `DataContextValue` | Yes — outside `DataProvider` |
571
+ | `useAIOpsDataOptional()` | `DataContextValue \| null` | No |
572
+ | `useQueryResult(query)` | `unknown \| null` | Yes outside `DataProvider` |
573
+
574
+ ```ts
575
+ interface DataContextValue {
576
+ data: Record<string, unknown>; // Raw responses keyed by query
577
+ isRefreshing: boolean;
578
+ lastRefreshError: string | null;
579
+ lastRefreshTime: Date | null;
580
+ credentialsSet: boolean;
581
+ setCredentials: (creds: Credentials) => void;
582
+ }
583
+ ```
584
+
585
+ ---
586
+
587
+ ## Exports
588
+
589
+ Everything below is exported from `react-dashstream`.
590
+
591
+ ### Default export
592
+
593
+ `AIOPsDashboard` — main dashboard shell component.
594
+
595
+ ### Compound nodes
596
+
597
+ `ServerNode`, `DatabaseNode`, `WebDispatcherNode`, `MessageServerNode`, `HumanNode`
598
+
599
+ ### Layout
600
+
601
+ `Carousel`, `Service`, `ServiceNode`
602
+
603
+ ### 3D models
604
+
605
+ `Server3D`, `Database3D`, `WebDispatcher3D`, `MessageServer3D`, `Human3D`
606
+
607
+ ### Internal 3D components (for drill-down)
608
+
609
+ `CPU3D`, `Memory3D`, `DriveBay3D`, `NetworkBlock3D`, `ThreadPool3D`, `Platter3D`, `Port3D`
610
+
611
+ ### Indicators and connections
612
+
613
+ `SvgConnection`, `SyncBridge`, `NodeCallout`, `HoloBase`
614
+
615
+ ### Dialogs
616
+
617
+ `ServiceDialog`, `ComponentDialog`, `HistoricalGraphPanel`, `ComponentDrillView`
618
+
619
+ ### Pre-built services
620
+
621
+ `SAPService`, `ExchangeService`
622
+
623
+ SAP helpers: `computeSAPServiceStatus`, `computeSAPDialogMetrics`, `computeSAPDialogAlerts`, `SAP_CONNECTIONS`
624
+
625
+ Exchange helpers: `computeExchangeServiceStatus`, `computeExchangeDialogMetrics`, `computeExchangeDialogAlerts`, `EXCHANGE_CONNECTIONS`
626
+
627
+ SAP sub-component helpers: `getServerSubComponents`, `getServerGraphSeries`, `getDispatcherSubComponents`, `getDispatcherGraphSeries`, `getMessageServerSubComponents`, `getMessageServerGraphSeries`, `getDatabaseSubComponents`, `getDatabaseGraphSeries`
628
+
629
+ ### Data layer
630
+
631
+ `DataProvider`, `useAIOpsData`, `useAIOpsDataOptional`, `useQueryResult`, `extractUniqueQueries`, `extractDatacenterMetricQueries`, `defaultDataTransform`
632
+
633
+ ### Datacenter topology map
634
+
635
+ `DatacenterView` plus types `DatacenterViewProps`, `DatacenterBuildingConfig`, `DatacenterBuildingVariant`, `DatacenterConnection`, `DatacenterGeography`, `DatacenterMetrics`, `DatacenterSiteLabels`, `DatacenterViewPhase`
636
+
637
+ ### Event alerts
638
+
639
+ `EventAlertsProvider`, `useEventAlerts`, `useEventAlertsOptional`
640
+
641
+ ### Theme
642
+
643
+ `STATUS_CFG`, `HOLO_CYAN` (`"#00e5ff"`), `HOLO_BLUE` (`"#0055cc"`), `HOLO_SURFACE`, `HOLO_GLASS`, `makeFaceStyles(W, H, D)`
644
+
645
+ `ThemeProvider`, `useTheme`, type `DashboardTheme` (`"light" | "dark"`)
646
+
647
+ ### Context hooks
648
+
649
+ `CarouselContext`, `CarouselItemContext`, `useCarouselContext`, `useCarouselItemContext`, `ServiceContext`
650
+
651
+ ### Type exports
652
+
653
+ `AIOPsDashboardProps`, `ServiceMeta`, `ServiceMetricBinding`, `DashboardTheme`, `ComponentStatus`, `StatusCfg`, `FaceName`, `Base3DProps`, `ComponentType`, `ComponentContext`, `ComponentDialogMetric`, `SubComponentConfig`, `GraphSeries`, `SelectedComponent`, `ConnectionConfig`, `ViewState`, `DataBinding`, `DataBindings`, `DatacenterMetricBindings`, `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`, `EventHostAlert`, `EventAlertsContextValue`, `EventAlertsProviderProps`, `DatacenterViewProps`, `DatacenterBuildingConfig`, `DatacenterBuildingVariant`, `DatacenterConnection`, `DatacenterGeography`, `DatacenterMetrics`, `DatacenterSiteLabels`, `DatacenterViewPhase`
654
+
655
+ ---
656
+
657
+ ## Key types quick reference
658
+
659
+ ### AIOPsDashboardProps
660
+
661
+ `DashboardTheme` is `"light" | "dark"` (exported).
662
+
663
+ ```ts
664
+ interface AIOPsDashboardProps {
665
+ title?: string;
666
+ brandName?: string; // Default: "BUSAUD AIOps"
667
+ brandTag?: string; // Default: "3D MONITOR"
668
+ services?: ServiceMeta[];
669
+ backgroundImage?: string;
670
+ lightBackgroundImage?: string; // Used in light mode when set
671
+ theme?: DashboardTheme; // Controlled theme (pair with onThemeChange)
672
+ onThemeChange?: (theme: DashboardTheme) => void;
673
+ logoUrl?: string;
674
+ carouselSpeed?: number; // Default: 0.006
675
+ fontFamily?: string;
676
+ liveData?: boolean; // Default: false
677
+ dataEndpoint?: string;
678
+ dataBindings?: DataBindings;
679
+ dataTransform?: (raw: unknown) => unknown;
680
+ dataRefreshInterval?: number; // Default: 60000
681
+ serviceDataBindings?: Record<string, ServiceMetricBinding[]>;
682
+ eventApiConfig?: EventApiConfig; // Enable event-to-dashboard bridge
683
+ children: React.ReactNode;
684
+ }
685
+ ```
686
+
687
+ ### ServiceMeta
688
+
689
+ ```ts
690
+ interface ServiceMeta {
691
+ name: string; // Must match child component name
692
+ status: ComponentStatus;
693
+ dbSync?: boolean; // Default: true
694
+ metrics?: ServiceDialogMetric[];
695
+ alerts?: ServiceDialogAlert[];
696
+ }
697
+
698
+ interface ServiceDialogMetric {
699
+ label: string;
700
+ value: string;
701
+ color: string;
702
+ }
703
+
704
+ interface ServiceDialogAlert {
705
+ level: "info" | "warning" | "critical";
706
+ message: string;
707
+ }
708
+ ```
709
+
710
+ ### ConnectionConfig
711
+
712
+ ```ts
713
+ interface ConnectionConfig {
714
+ from: [number, number];
715
+ to: [number, number];
716
+ visibleAtPhase?: number; // Default: 0
717
+ color?: string; // Default: "#00e5ff"
718
+ duration?: string; // Default: "1.2s"
719
+ }
720
+ ```
721
+
722
+ ### ComponentStatus
723
+
724
+ ```ts
725
+ type ComponentStatus = "online" | "warning" | "critical" | "offline";
726
+ ```
727
+
728
+ | Status | Color | Glow |
729
+ | ---------- | --------- | -------- |
730
+ | `online` | `#00e5ff` | cyan |
731
+ | `warning` | `#ff8c00` | orange |
732
+ | `critical` | `#ff2255` | red |
733
+ | `offline` | `#1e3a5a` | dim blue |
734
+
735
+ ---
736
+
737
+ ## Endpoint contract
738
+
739
+ | Aspect | Requirement |
740
+ | ------------ | -------------------------------------------------------------- |
741
+ | **Method** | `GET` |
742
+ | **URL** | `<dataEndpoint>?query=<urlEncodedQuery>` |
743
+ | **Headers** | `access-key` and `access-secret-key` |
744
+ | **Response** | Plain text body (trimmed). Default transform parses as number. |
745
+ | **Errors** | Non-2xx → counted as failure per query |
746
+
747
+ For JSON responses, provide a custom `dataTransform`:
748
+
749
+ ```tsx
750
+ dataTransform={(raw) => {
751
+ const parsed = JSON.parse(String(raw));
752
+ return parsed?.data?.result?.[0]?.value?.[1] ?? raw;
753
+ }}
754
+ ```
755
+
756
+ ---
757
+
758
+ ## Standalone DataProvider
759
+
760
+ Use the data layer without `AIOPsDashboard`:
761
+
762
+ ```tsx
763
+ import { DataProvider, useAIOpsData } from "react-dashstream";
764
+
765
+ <DataProvider
766
+ config={{
767
+ endpoint: "https://prometheus.example.com/api/v1/query",
768
+ queries: ['cpu_usage{instance="srv-01"}'],
769
+ refreshInterval: 15000,
770
+ }}
771
+ >
772
+ <MyContent />
773
+ </DataProvider>;
774
+ ```
775
+
776
+ ---
777
+
778
+ ## Node positioning guide
779
+
780
+ The topology scene is ~660×640 pixels.
781
+
782
+ | Layer | Typical `ey` | `visibleAtPhase` | Component |
783
+ | ----------- | ------------ | ---------------- | ------------------- |
784
+ | Users (top) | 100 | 2 | `HumanNode` |
785
+ | Dispatcher | 230 | 2 | `WebDispatcherNode` |
786
+ | App servers | 350–380 | 3 | `ServerNode` |
787
+ | Databases | 500–520 | 4 | `DatabaseNode` |
788
+
789
+ Center X = `330`. Left/right columns: `200` / `460`. Three-column: `165` / `330` / `495`.
790
+
791
+ `compactOffset` controls node position in the compact carousel view relative to center.
792
+
793
+ ---
794
+
795
+ ## Usage patterns
796
+
797
+ ### Minimal static dashboard
798
+
799
+ ```tsx
800
+ import "react-dashstream/dist/index.css";
801
+ import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
802
+
803
+ <AIOPsDashboard brandName="MY DASHBOARD" services={[{ name: "Svc", status: "online" }]}>
804
+ <Service
805
+ name="Svc"
806
+ status="online"
807
+ connections={[{ from: [330, 200], to: [330, 380], visibleAtPhase: 3 }]}
808
+ >
809
+ <ServerNode
810
+ ex={330}
811
+ ey={380}
812
+ compactOffset={{ x: 0, y: 0 }}
813
+ zIndex={8}
814
+ name="SRV-01"
815
+ status="online"
816
+ cpuLoad={42}
817
+ memLoad={60}
818
+ />
819
+ </Service>
820
+ </AIOPsDashboard>;
821
+ ```
822
+
823
+ ### Full live-data dashboard
824
+
825
+ ```tsx
826
+ <AIOPsDashboard
827
+ brandName="LIVE MONITOR"
828
+ services={services}
829
+ liveData={true}
830
+ dataEndpoint="https://prometheus.example.com/api/v1/query"
831
+ dataRefreshInterval={10000}
832
+ dataBindings={{
833
+ "My Service": {
834
+ cpuLoad: 'cpu_usage{instance="srv-01"}',
835
+ status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
836
+ },
837
+ }}
838
+ serviceDataBindings={{
839
+ "My Service": [{ label: "CPU", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" }],
840
+ }}
841
+ >
842
+ <MyService name="My Service" />
843
+ </AIOPsDashboard>
844
+ ```
845
+
846
+ ### Custom service with all data features
847
+
848
+ ```tsx
849
+ function MyService({ name, status, cpuLoad, memLoad, dbCapacity, dbStatus }: any) {
850
+ return (
851
+ <Service
852
+ name={name}
853
+ status={status ?? "online"}
854
+ connections={[
855
+ { from: [330, 200], to: [200, 380], visibleAtPhase: 3 },
856
+ { from: [330, 200], to: [460, 380], visibleAtPhase: 3 },
857
+ ]}
858
+ >
859
+ <ServerNode
860
+ ex={200}
861
+ ey={380}
862
+ compactOffset={{ x: -30, y: -20 }}
863
+ zIndex={8}
864
+ name="SRV-01"
865
+ subLabel="APP SERVER"
866
+ status={status ?? "online"}
867
+ cpuLoad={cpuLoad ?? 42}
868
+ memLoad={memLoad ?? 60}
869
+ alert={{ offsetX: -160, offsetY: -60, align: "left" }}
870
+ dialogMetrics={[
871
+ { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad ?? 42 },
872
+ { id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad ?? 60 },
873
+ ]}
874
+ subComponents={[
875
+ {
876
+ id: "cpu",
877
+ label: "CPU-0",
878
+ status: "online",
879
+ element: <CPU3D label="CPU-0" load={cpuLoad ?? 42} />,
880
+ },
881
+ {
882
+ id: "mem",
883
+ label: "HEAP",
884
+ status: "online",
885
+ element: <Memory3D label="HEAP" usedPercent={memLoad ?? 60} />,
886
+ },
887
+ ]}
888
+ graphSeries={[
889
+ { id: "cpu", label: "CPU", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68] },
890
+ ]}
891
+ />
892
+ <DatabaseNode
893
+ ex={460}
894
+ ey={380}
895
+ compactOffset={{ x: 30, y: -20 }}
896
+ zIndex={7}
897
+ name="DB-01"
898
+ subLabel="PRIMARY"
899
+ status={dbStatus ?? "online"}
900
+ capacity={dbCapacity ?? 55}
901
+ alert={{ offsetX: 160, offsetY: -60, align: "right" }}
902
+ />
903
+ </Service>
904
+ );
905
+ }
906
+ ```
907
+
908
+ ---
909
+
910
+ ## DatacenterView — topology / geography map
911
+
912
+ SVG-based **network topology** (grid) or optional **geographic outline** (consumer-supplied SVG path `d`), CSS **3D building** markers, animated links, and **drill-down** into a nested `AIOPsDashboard` when the user selects a site or building.
913
+
914
+ ### Data model
915
+
916
+ - **`DatacenterBuildingConfig`** one marker on the map (`id`, `name`, `subtitle`, `x` / `y` as **percent** of the 800×600 scene, `status`, `metrics`, `services`, `renderServices`, `variant`, optional `brand`, `isExternal`).
917
+ - **`siteId`** — optional; multiple buildings with the same `siteId` share **one marker**, **combined metrics** (sums / averages for PUE, temperature, uptime), and a **multi-building cluster** visualization (each building’s `variant` is shown). Optional **`siteLabels`** (`Record<string, { label: string; subtitle?: string }>`) overrides auto-generated titles.
918
+ - **`DatacenterConnection`** `from` / `to` endpoint ids (building `id` or `siteId`), `throughput` label, optional `isExternal`.
919
+ - **`DatacenterMetrics`** numeric KPIs used on markers and for aggregation.
920
+
921
+ ### Mock metrics
922
+
923
+ Pass **`dataCenters`** with embedded **`metrics`**. No `DataProvider` required.
924
+
925
+ ### PromQL (live) metrics on markers
926
+
927
+ 1. Wrap the tree in **`DataProvider`** with `config.queries` including every query used by **`metricBindings`**, collected via **`extractDatacenterMetricQueries(metricBindings)`** (and merge with queries for any nested `AIOPsDashboard` if you use `aiOpsDashboardProps`).
928
+ 2. Pass **`metricBindings`**: `Record<buildingId, Partial<Record<keyof DatacenterMetrics, DataBinding>>>` — same **`DataBinding`** shape as service-level bindings (`string` or `{ query, transform? }`).
929
+ 3. **`DatacenterView`** calls **`useAIOpsDataOptional()`** and merges transformed numbers over each building’s static `metrics` (site-level rows aggregate the merged per-building values).
930
+
931
+ This path is **independent** of `AIOPsDashboard`’s **`dataBindings`** (which key off **service `name`** on carousel children).
932
+
933
+ ### Drill-down dashboard
934
+
935
+ **`aiOpsDashboardProps`** is spread onto the inner **`AIOPsDashboard`**; **`services`**, **`theme`**, **`children`** (from `renderServices()`), and branding are forced from the selected building so the map stays consistent. Use **`aiOpsDashboardProps`** for **`liveData`**, **`dataEndpoint`**, **`dataBindings`**, **`serviceDataBindings`**, etc., for **service-level** PromQL inside the opened datacenter.
936
+
937
+ ### Geography (optional)
938
+
939
+ **`showGeography`** and **`geography: { outlinePathD, referencePoints? }`** — the package does **not** ship country assets; the app parses or provides an SVG path `d` string (see `example/SaudiMapView.tsx`).
940
+
941
+ ### DatacenterViewProps (reference)
942
+
943
+ | Prop | Type | Default | Notes |
944
+ |------|------|---------|--------|
945
+ | `dataCenters` | `DatacenterBuildingConfig[]` | (required) | Mock baseline metrics in each config; merged with live values when `metricBindings` + `DataProvider` present |
946
+ | `connections` | `DatacenterConnection[]` | `[]` | Link lines between building or site ids |
947
+ | `metricBindings` | `DatacenterMetricBindings` | — | Building **id** → metric field → PromQL `DataBinding`; needs `DataProvider` + `extractDatacenterMetricQueries` |
948
+ | `dataTransform` | `(raw: unknown) => unknown` | `defaultDataTransform` | Applied to each binding unless overridden per binding |
949
+ | `geography` | `DatacenterGeography` | | `outlinePathD` + optional `referencePoints` (SVG coords) |
950
+ | `showGeography` | `boolean` | `false` | When true, draws outline + reference points |
951
+ | `mapTitle` | `string` | `"Geography — Operations"` | Header when `showGeography` |
952
+ | `topologyTitle` | `string` | `"Network topology"` | Header when not showing geography |
953
+ | `topologyAffiliatesLabel` | `string` | `"AFFILIATES"` | Label in topology-only mode (affiliates zone) |
954
+ | `brandText` | `string` | `"AIOps"` | Header brand |
955
+ | `headerTag` | `string` | `"COMMAND CENTER"` | Corner tag |
956
+ | `initialBuildingId` | `string` | — | Building `id` to open when `openDashboardOnMount` is true |
957
+ | `openDashboardOnMount` | `boolean` | `false` | Auto-zoom into `initialBuildingId` |
958
+ | `onNavigateEvents` | `() => void` | — | e.g. switch app view to event console |
959
+ | `theme` | `DashboardTheme` | — | Passed through to drill-down dashboard |
960
+ | `onThemeChange` | `(t) => void` | — | |
961
+ | `backgroundImage` / `lightBackgroundImage` | `string` | — | Drill-down `AIOPsDashboard` backgrounds |
962
+ | `aiOpsDashboardProps` | `Partial<AIOPsDashboardProps>` | — | Spread first; `services`, children, theme, branding overridden from selection |
963
+ | `defaultBuildingBrand` | `string` | `"AIOps"` | Fallback label on 3D building faces |
964
+ | `siteLabels` | `Record<string, DatacenterSiteLabels>` | | Per-`siteId` titles for multi-building markers |
965
+ | `showMapScaleControls` | `boolean` | `true` | Size / hover sliders in header (map mode) |
966
+ | `mapFooter` | `ReactNode` | — | Rendered below the map when drill-down is **not** active |
967
+
968
+ ### Minimal usage
969
+
970
+ ```tsx
971
+ import "react-dashstream/dist/index.css";
972
+ import { DatacenterView } from "react-dashstream";
973
+ import type { DatacenterBuildingConfig, DatacenterConnection } from "react-dashstream";
974
+
975
+ const dataCenters: DatacenterBuildingConfig[] = [
976
+ /* id, x, y (%), metrics, services, renderServices, variant, siteId? … */
977
+ ];
978
+ const connections: DatacenterConnection[] = [];
979
+
980
+ <DatacenterView dataCenters={dataCenters} connections={connections} />;
981
+ ```
982
+
983
+ ---
984
+
985
+ ## EventView Operations Event Console
986
+
987
+ 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).
988
+
989
+ ### API mode (primary usage)
990
+
991
+ ```tsx
992
+ import "react-dashstream/dist/index.css";
993
+ import { EventView } from "react-dashstream";
994
+ import type { EventApiConfig } from "react-dashstream";
995
+
996
+ const apiConfig: EventApiConfig = {
997
+ baseUrl: "https://your-monitoring-server.example.com",
998
+ payload: { filter: {}, sortBy: "date_reception", sortOrder: "DESC" },
999
+ fieldMapping: {
1000
+ id: "mc_ueid",
1001
+ occurrence: "date_reception",
1002
+ severityLastModified: "severity_last_modified",
1003
+ severity: "severity",
1004
+ owner: "owner",
1005
+ class: "class",
1006
+ host: "mc_host",
1007
+ message: "msg",
1008
+ remedySupportGroup: "ara_remedy_support_group",
1009
+ incidentId: "ara_incident_id",
1010
+ smsStatus: "ara_sms_status",
1011
+ onCallNumber: "ara_on_call_number",
1012
+ hostedApplication: "ara_hosted_application",
1013
+ monitoringCategory: "ara_hosted_app_monitoring",
1014
+ applicationSupportUnit: "ara_application_support_unit",
1015
+ },
1016
+ };
1017
+
1018
+ <EventView apiConfig={apiConfig} />;
1019
+ ```
1020
+
1021
+ ### How it works
1022
+
1023
+ 1. Shows credentials modal (or reuses DataProvider credentials if inside `AIOPsDashboard`)
1024
+ 2. Sends `POST {baseUrl}/tsws/monitoring/api/v1.0/events/search` with the payload as JSON body
1025
+ 3. Auth via headers: `access-key` and `access-secret-key`
1026
+ 4. Expects response: `{ eventSeverityCount: {...}, totalCount: N, eventList: [{...}] }`
1027
+ 5. Maps each object in `eventList` to `AIOpsEvent` using `fieldMapping`
1028
+ 6. Maps severity strings via `severityMap` (default: `CRITICAL→Critical`, `MAJOR→Major`, `MINOR→Minor`)
1029
+ 7. Polls on `refreshInterval` (default 60s)
1030
+
1031
+ ### Props
1032
+
1033
+ | Prop | Type | Default | Notes |
1034
+ | -------------------- | ---------------- | ----------------- | -------------------------------------------------- |
1035
+ | `apiConfig` | `EventApiConfig` | — | Required for API mode |
1036
+ | `events` | `AIOpsEvent[]` | | Pre-fetched events (skips API calls) |
1037
+ | `credentials` | `Credentials` | | Explicit creds; falls back to DataProvider → modal |
1038
+ | `columnWidthsCookie` | `string` | `"ev_col_widths"` | Cookie name for persisting column widths |
1039
+ | `title` | `string` | `"Event Console"` | Header title |
1040
+ | `theme` | `DashboardTheme` | — | Optional override; else `ThemeProvider` / default |
1041
+
1042
+ ### EventApiConfig
1043
+
1044
+ ```ts
1045
+ interface EventApiConfig {
1046
+ baseUrl: string; // "https://monitoring.example.com"
1047
+ endpoint?: string; // default: "/tsws/monitoring/api/v1.0/events/search"
1048
+ payload: Record<string, unknown>; // POST body
1049
+ fieldMapping: EventFieldMapping; // maps API keys → AIOpsEvent keys
1050
+ severityMap?: Record<string, EventSeverity>; // default: { CRITICAL, MAJOR, MINOR }
1051
+ refreshInterval?: number; // ms, default 60000
1052
+ }
1053
+ ```
1054
+
1055
+ ### EventFieldMapping
1056
+
1057
+ Maps every `AIOpsEvent` property to the API response field name. All 15 keys required:
1058
+
1059
+ ```ts
1060
+ type EventFieldMapping = Record<keyof AIOpsEvent, string>;
1061
+ ```
1062
+
1063
+ ### AIOpsEvent interface
1064
+
1065
+ ```ts
1066
+ interface AIOpsEvent {
1067
+ id: string;
1068
+ occurrence: string;
1069
+ severityLastModified: string;
1070
+ severity: "Minor" | "Major" | "Critical";
1071
+ owner: string;
1072
+ class: "Self-monitoring" | "SCOM" | "Situation" | "Alarm" | "Event";
1073
+ host: string;
1074
+ message: string;
1075
+ remedySupportGroup: string;
1076
+ incidentId: string;
1077
+ smsStatus: "" | "SUCCESS" | "FAILED";
1078
+ onCallNumber: string;
1079
+ hostedApplication: string;
1080
+ monitoringCategory: "24/7" | "Office Hours";
1081
+ applicationSupportUnit: string;
1082
+ }
1083
+ ```
1084
+
1085
+ ### Credential resolution order
1086
+
1087
+ 1. `credentials` prop (if provided)
1088
+ 2. `DataProvider` context credentials (when inside `AIOPsDashboard` with `liveData`)
1089
+ 3. Built-in credentials modal (same UI as the 3D dashboard)
1090
+
1091
+ ### Severity colors
1092
+
1093
+ - **Minor** → `#ffb800` (amber)
1094
+ - **Major** → `#ff6600` (orange)
1095
+ - **Critical** → `#ff2255` (red)
1096
+
1097
+ ### Features
1098
+
1099
+ - Live API polling with configurable interval and manual refresh button
1100
+ - Configurable field mapping — any API response shape can be mapped
1101
+ - Shared auth — reuses DataProvider credentials when available
1102
+ - Severity filter chips with live counts
1103
+ - Free-text search across message, host, owner, incident ID
1104
+ - Sortable columns (click header: asc → desc → none)
1105
+ - Infinite scroll — all matching events in a single scrollable table
1106
+ - Resizable columns — drag column edges; widths persist in cookies
1107
+ - Loading spinner, error badge, last-refresh timestamp
1108
+ - Light / dark table chrome (via `ThemeProvider` or `theme` prop), scan-line header, severity badges
1109
+
1110
+ ### Generating an EventApiConfig from the AI
1111
+
1112
+ When asked to set up EventView for a new API, generate the config like this:
1113
+
1114
+ 1. Set `baseUrl` to the server's protocol + host + port
1115
+ 2. Set `endpoint` only if it differs from the default `/tsws/monitoring/api/v1.0/events/search`
1116
+ 3. Build `payload` based on the API's expected POST body schema
1117
+ 4. Build `fieldMapping` by asking the user which API field maps to each `AIOpsEvent` property
1118
+ 5. Set `severityMap` only if the API uses non-standard severity values
1119
+
1120
+ ---
1121
+
1122
+ ## Build and development
1123
+
1124
+ ```bash
1125
+ npm run dev # Vite dev server (loads example/Dashboard.tsx)
1126
+ npm run build # TypeScript check + Vite library build
1127
+ npm run build:lib # Vite library build only
1128
+ npm run preview # Preview production build
1129
+ ```
1130
+
1131
+ Output: `dist/index.js` (ESM), `dist/index.d.ts`, `dist/index.css`.
1132
+
1133
+ ---
1134
+
1135
+ ## Common pitfalls
1136
+
1137
+ 1. **Missing CSS import** — Always import `react-dashstream/dist/index.css`.
1138
+ 2. **Name mismatch** — `name` on child components must exactly match keys in `dataBindings`, `serviceDataBindings`, and `ServiceMeta.name`.
1139
+ 3. **Credentials are in-memory only** — Page refresh requires re-entering credentials.
1140
+ 4. **Default transform is plain-text** — For JSON endpoints, provide a custom `dataTransform`.
1141
+ 5. **Polling only** — No WebSocket/SSE support; uses `setInterval`.
1142
+ 6. **`visibleAtPhase` matters** — Nodes without proper values won't animate during expansion.
1143
+ 7. **Connection coordinates** — `from`/`to` arrays use `[x, y]` matching `ex`/`ey` of connected nodes.
1144
+ 8. **`dialogMetrics` replaces defaults** — When provided, all default gauges (CPU/Memory/Storage) are removed.
1145
+ 9. **`subComponents` replaces defaults** — When provided, all auto-generated sub-components are removed.
1146
+ 10. **`graphSeries` replaces defaults** — When provided, all auto-generated sparklines are removed.
1147
+ 11. **Event bridge matching** — Node `name` must match event `host` (case-insensitive) for `eventApiConfig` to highlight nodes automatically.
1148
+ 12. **Theme context** — `EventView` and `CredentialsModal` use `useTheme()`. Without `ThemeProvider`, context defaults to `"dark"`. Wrap the app (or use `EventView`’s `theme` prop) so light dashboards are not paired with a dark event console or lock screen.