react-dashstream 0.2.0 → 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.
- package/README.md +1245 -1193
- package/dashstream-skill.md +1148 -1062
- package/dist/components/DatacenterView/DatacenterView.d.ts +37 -0
- package/dist/components/DatacenterView/DatacenterView.d.ts.map +1 -0
- package/dist/components/DatacenterView/index.d.ts +3 -0
- package/dist/components/DatacenterView/index.d.ts.map +1 -0
- package/dist/components/DatacenterView/types.d.ts +55 -0
- package/dist/components/DatacenterView/types.d.ts.map +1 -0
- package/dist/components/EventView/EventAlertsContext.d.ts +2 -0
- package/dist/components/EventView/EventAlertsContext.d.ts.map +1 -1
- package/dist/components/EventView/EventView.d.ts.map +1 -1
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/data/DataProvider.d.ts +7 -0
- package/dist/data/DataProvider.d.ts.map +1 -1
- package/dist/data/index.d.ts +2 -2
- package/dist/data/index.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2548 -1698
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,1193 +1,1245 @@
|
|
|
1
|
-
# React DashStream
|
|
2
|
-
|
|
3
|
-
Holographic 3D infrastructure monitoring dashboard for React. Pure CSS-3D — no WebGL, no canvas, no dependencies beyond React.
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
npm install react-dashstream
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Quick start
|
|
12
|
-
|
|
13
|
-
```tsx
|
|
14
|
-
import "react-dashstream/dist/index.css";
|
|
15
|
-
import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
|
|
16
|
-
import type { ServiceMeta } from "react-dashstream";
|
|
17
|
-
|
|
18
|
-
const services: ServiceMeta[] = [
|
|
19
|
-
{
|
|
20
|
-
name: "My Service",
|
|
21
|
-
status: "online",
|
|
22
|
-
metrics: [
|
|
23
|
-
{ label: "Service Health", value: "99.9%", color: "#00ff88" },
|
|
24
|
-
{ label: "Avg Response Time", value: "14ms", color: "#00e5ff" },
|
|
25
|
-
],
|
|
26
|
-
alerts: [{ level: "info", message: "All Systems Nominal" }],
|
|
27
|
-
},
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
export default function App() {
|
|
31
|
-
return (
|
|
32
|
-
<AIOPsDashboard brandName="MY DASHBOARD" services={services}>
|
|
33
|
-
<Service
|
|
34
|
-
name="My Service"
|
|
35
|
-
status="online"
|
|
36
|
-
connections={[
|
|
37
|
-
{ from: [330, 200], to: [200, 380], visibleAtPhase: 3 },
|
|
38
|
-
{ from: [330, 200], to: [460, 380], visibleAtPhase: 3 },
|
|
39
|
-
]}
|
|
40
|
-
>
|
|
41
|
-
<ServerNode
|
|
42
|
-
ex={200}
|
|
43
|
-
ey={380}
|
|
44
|
-
compactOffset={{ x: -30, y: -20 }}
|
|
45
|
-
zIndex={8}
|
|
46
|
-
name="SRV-01"
|
|
47
|
-
subLabel="APP SERVER"
|
|
48
|
-
status="online"
|
|
49
|
-
cpuLoad={42}
|
|
50
|
-
memLoad={60}
|
|
51
|
-
/>
|
|
52
|
-
<DatabaseNode
|
|
53
|
-
ex={460}
|
|
54
|
-
ey={380}
|
|
55
|
-
compactOffset={{ x: 30, y: -20 }}
|
|
56
|
-
zIndex={7}
|
|
57
|
-
name="DB-01"
|
|
58
|
-
subLabel="PRIMARY"
|
|
59
|
-
status="online"
|
|
60
|
-
capacity={55}
|
|
61
|
-
/>
|
|
62
|
-
</Service>
|
|
63
|
-
</AIOPsDashboard>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Click a service to expand its topology. Click a component to drill into its internals.
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## Theme (light / dark)
|
|
73
|
-
|
|
74
|
-
The dashboard ships with **light** and **dark** visual modes. `AIOPsDashboard` defaults to **light**; the header includes a control to toggle modes.
|
|
75
|
-
|
|
76
|
-
### Background images
|
|
77
|
-
|
|
78
|
-
| Prop | When used |
|
|
79
|
-
| ---------------------- | -------------------------------------------------- |
|
|
80
|
-
| `backgroundImage` | Dark mode, and light mode if no light asset is set |
|
|
81
|
-
| `lightBackgroundImage` | Light mode when provided |
|
|
82
|
-
|
|
83
|
-
### Controlled theme (sync with your app shell)
|
|
84
|
-
|
|
85
|
-
Pass **`theme`** and **`onThemeChange`** to drive the dashboard from parent state (for example, to keep tabs, sidebars, and `EventView` in sync):
|
|
86
|
-
|
|
87
|
-
```tsx
|
|
88
|
-
import { useState } from "react";
|
|
89
|
-
import { AIOPsDashboard, ThemeProvider, type DashboardTheme, type ServiceMeta } from "react-dashstream";
|
|
90
|
-
import lightBg from "./light.webp";
|
|
91
|
-
import darkBg from "./dark.webp";
|
|
92
|
-
|
|
93
|
-
const services: ServiceMeta[] = [
|
|
94
|
-
/* …same shape as Quick start… */
|
|
95
|
-
];
|
|
96
|
-
|
|
97
|
-
export default function App() {
|
|
98
|
-
const [theme, setTheme] = useState<DashboardTheme>("light");
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<ThemeProvider value={theme}>
|
|
102
|
-
<AIOPsDashboard
|
|
103
|
-
theme={theme}
|
|
104
|
-
onThemeChange={setTheme}
|
|
105
|
-
lightBackgroundImage={lightBg}
|
|
106
|
-
backgroundImage={darkBg}
|
|
107
|
-
brandName="MY DASHBOARD"
|
|
108
|
-
services={services}
|
|
109
|
-
>
|
|
110
|
-
{/* Service / node tree — see Quick start */}
|
|
111
|
-
</AIOPsDashboard>
|
|
112
|
-
</ThemeProvider>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
When both props are set, the dashboard is **controlled**; the header toggle calls `onThemeChange`. Omit them to use **internal** theme state.
|
|
118
|
-
|
|
119
|
-
### `ThemeProvider`, `EventView`, and the credentials modal
|
|
120
|
-
|
|
121
|
-
**`ServiceDialog`**, **`ComponentDialog`**, **`EventView`**, and **`CredentialsModal`** (access-key prompt) read the active mode from **`useTheme()`**.
|
|
122
|
-
|
|
123
|
-
- Wrap any subtree that includes **`EventView`** or standalone credential prompts in **`ThemeProvider`** with the same `value` as your dashboard so the event console and lock screen match.
|
|
124
|
-
- Optionally pass **`theme="light"` \| `"dark"`** on **`EventView`** to override context (useful when embedding without a provider).
|
|
125
|
-
|
|
126
|
-
Exports: `ThemeProvider`, `useTheme`, type `DashboardTheme`.
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
## Full example — multi-service, multi-layer
|
|
131
|
-
|
|
132
|
-
See `example/Dashboard.tsx` in this package for a complete two-service example with Payment Gateway and Auth Service topologies rotating in a 3D carousel.
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
##
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
###
|
|
257
|
-
|
|
258
|
-
|
|
|
259
|
-
|
|
|
260
|
-
|
|
|
261
|
-
|
|
|
262
|
-
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
|
311
|
-
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
|
360
|
-
|
|
|
361
|
-
|
|
|
362
|
-
|
|
|
363
|
-
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
```tsx
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
<
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
name
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
label: string; //
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
{
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
{
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
###
|
|
1124
|
-
|
|
1125
|
-
| Component
|
|
1126
|
-
|
|
|
1127
|
-
| `
|
|
1128
|
-
| `
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
|
1159
|
-
|
|
|
1160
|
-
| `
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1
|
+
# React DashStream
|
|
2
|
+
|
|
3
|
+
Holographic 3D infrastructure monitoring dashboard for React. Pure CSS-3D — no WebGL, no canvas, no dependencies beyond React.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npm install react-dashstream
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import "react-dashstream/dist/index.css";
|
|
15
|
+
import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
|
|
16
|
+
import type { ServiceMeta } from "react-dashstream";
|
|
17
|
+
|
|
18
|
+
const services: ServiceMeta[] = [
|
|
19
|
+
{
|
|
20
|
+
name: "My Service",
|
|
21
|
+
status: "online",
|
|
22
|
+
metrics: [
|
|
23
|
+
{ label: "Service Health", value: "99.9%", color: "#00ff88" },
|
|
24
|
+
{ label: "Avg Response Time", value: "14ms", color: "#00e5ff" },
|
|
25
|
+
],
|
|
26
|
+
alerts: [{ level: "info", message: "All Systems Nominal" }],
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export default function App() {
|
|
31
|
+
return (
|
|
32
|
+
<AIOPsDashboard brandName="MY DASHBOARD" services={services}>
|
|
33
|
+
<Service
|
|
34
|
+
name="My Service"
|
|
35
|
+
status="online"
|
|
36
|
+
connections={[
|
|
37
|
+
{ from: [330, 200], to: [200, 380], visibleAtPhase: 3 },
|
|
38
|
+
{ from: [330, 200], to: [460, 380], visibleAtPhase: 3 },
|
|
39
|
+
]}
|
|
40
|
+
>
|
|
41
|
+
<ServerNode
|
|
42
|
+
ex={200}
|
|
43
|
+
ey={380}
|
|
44
|
+
compactOffset={{ x: -30, y: -20 }}
|
|
45
|
+
zIndex={8}
|
|
46
|
+
name="SRV-01"
|
|
47
|
+
subLabel="APP SERVER"
|
|
48
|
+
status="online"
|
|
49
|
+
cpuLoad={42}
|
|
50
|
+
memLoad={60}
|
|
51
|
+
/>
|
|
52
|
+
<DatabaseNode
|
|
53
|
+
ex={460}
|
|
54
|
+
ey={380}
|
|
55
|
+
compactOffset={{ x: 30, y: -20 }}
|
|
56
|
+
zIndex={7}
|
|
57
|
+
name="DB-01"
|
|
58
|
+
subLabel="PRIMARY"
|
|
59
|
+
status="online"
|
|
60
|
+
capacity={55}
|
|
61
|
+
/>
|
|
62
|
+
</Service>
|
|
63
|
+
</AIOPsDashboard>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Click a service to expand its topology. Click a component to drill into its internals.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Theme (light / dark)
|
|
73
|
+
|
|
74
|
+
The dashboard ships with **light** and **dark** visual modes. `AIOPsDashboard` defaults to **light**; the header includes a control to toggle modes.
|
|
75
|
+
|
|
76
|
+
### Background images
|
|
77
|
+
|
|
78
|
+
| Prop | When used |
|
|
79
|
+
| ---------------------- | -------------------------------------------------- |
|
|
80
|
+
| `backgroundImage` | Dark mode, and light mode if no light asset is set |
|
|
81
|
+
| `lightBackgroundImage` | Light mode when provided |
|
|
82
|
+
|
|
83
|
+
### Controlled theme (sync with your app shell)
|
|
84
|
+
|
|
85
|
+
Pass **`theme`** and **`onThemeChange`** to drive the dashboard from parent state (for example, to keep tabs, sidebars, and `EventView` in sync):
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { useState } from "react";
|
|
89
|
+
import { AIOPsDashboard, ThemeProvider, type DashboardTheme, type ServiceMeta } from "react-dashstream";
|
|
90
|
+
import lightBg from "./light.webp";
|
|
91
|
+
import darkBg from "./dark.webp";
|
|
92
|
+
|
|
93
|
+
const services: ServiceMeta[] = [
|
|
94
|
+
/* …same shape as Quick start… */
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
export default function App() {
|
|
98
|
+
const [theme, setTheme] = useState<DashboardTheme>("light");
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<ThemeProvider value={theme}>
|
|
102
|
+
<AIOPsDashboard
|
|
103
|
+
theme={theme}
|
|
104
|
+
onThemeChange={setTheme}
|
|
105
|
+
lightBackgroundImage={lightBg}
|
|
106
|
+
backgroundImage={darkBg}
|
|
107
|
+
brandName="MY DASHBOARD"
|
|
108
|
+
services={services}
|
|
109
|
+
>
|
|
110
|
+
{/* Service / node tree — see Quick start */}
|
|
111
|
+
</AIOPsDashboard>
|
|
112
|
+
</ThemeProvider>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
When both props are set, the dashboard is **controlled**; the header toggle calls `onThemeChange`. Omit them to use **internal** theme state.
|
|
118
|
+
|
|
119
|
+
### `ThemeProvider`, `EventView`, and the credentials modal
|
|
120
|
+
|
|
121
|
+
**`ServiceDialog`**, **`ComponentDialog`**, **`EventView`**, and **`CredentialsModal`** (access-key prompt) read the active mode from **`useTheme()`**.
|
|
122
|
+
|
|
123
|
+
- Wrap any subtree that includes **`EventView`** or standalone credential prompts in **`ThemeProvider`** with the same `value` as your dashboard so the event console and lock screen match.
|
|
124
|
+
- Optionally pass **`theme="light"` \| `"dark"`** on **`EventView`** to override context (useful when embedding without a provider).
|
|
125
|
+
|
|
126
|
+
Exports: `ThemeProvider`, `useTheme`, type `DashboardTheme`.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Full example — multi-service, multi-layer
|
|
131
|
+
|
|
132
|
+
See `example/Dashboard.tsx` in this package for a complete two-service example with Payment Gateway and Auth Service topologies rotating in a 3D carousel.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Datacenter topology map (`DatacenterView`)
|
|
137
|
+
|
|
138
|
+
`DatacenterView` renders an SVG **topology** (or optional **geographic outline** if you pass an SVG path `d` string), **CSS 3D** datacenter markers, link lines, and a **zoom transition** into a nested **`AIOPsDashboard`** for the selected building. Define **multiple buildings** under one **`siteId`** to get a single site marker, aggregated KPIs, and a multi-building cluster. Metrics can stay **mock** (`dataCenters[].metrics`) or use **PromQL** via **`metricBindings`** (building id → metric key → `DataBinding`) together with **`DataProvider`** and **`extractDatacenterMetricQueries`**.
|
|
139
|
+
|
|
140
|
+
The demo app (`src/App.tsx`) uses `example/SaudiMapView.tsx`, a thin wrapper around `DatacenterView`. Scenario data lives in `example/saudiMapDemoData.tsx`; optional mock metric sliders use `example/DemoMetricPanel.tsx` + `example/DemoMetricPanel.css` (not part of the published package).
|
|
141
|
+
|
|
142
|
+
**Agent-oriented reference:** see **`dashstream-skill.md`** (section **DatacenterView — topology / geography map**) for props, types, mock vs live data, and composition with `DataProvider`.
|
|
143
|
+
|
|
144
|
+
### Minimal import
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import "react-dashstream/dist/index.css";
|
|
148
|
+
import { DatacenterView, DataProvider, extractDatacenterMetricQueries } from "react-dashstream";
|
|
149
|
+
import type { DatacenterBuildingConfig, DatacenterMetricBindings } from "react-dashstream";
|
|
150
|
+
|
|
151
|
+
const dataCenters: DatacenterBuildingConfig[] = [
|
|
152
|
+
{
|
|
153
|
+
id: "dc-a",
|
|
154
|
+
name: "DC A",
|
|
155
|
+
subtitle: "Primary",
|
|
156
|
+
x: 30,
|
|
157
|
+
y: 40,
|
|
158
|
+
status: "online",
|
|
159
|
+
variant: "tower",
|
|
160
|
+
metrics: {
|
|
161
|
+
carbonEmissions: 100,
|
|
162
|
+
powerUtilization: 5000,
|
|
163
|
+
cooling: 1200,
|
|
164
|
+
pue: 1.2,
|
|
165
|
+
uptime: 99.9,
|
|
166
|
+
activeServers: 200,
|
|
167
|
+
temperature: 22,
|
|
168
|
+
networkThroughput: 100,
|
|
169
|
+
},
|
|
170
|
+
services: [],
|
|
171
|
+
renderServices: () => null,
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
const metricBindings: DatacenterMetricBindings = {
|
|
176
|
+
"dc-a": { powerUtilization: 'avg(scrape_duration_seconds)' },
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const queries = extractDatacenterMetricQueries(metricBindings);
|
|
180
|
+
|
|
181
|
+
<DataProvider config={{ endpoint: "https://prom.example.com/api/v1/query", queries }}>
|
|
182
|
+
<DatacenterView dataCenters={dataCenters} metricBindings={metricBindings} />
|
|
183
|
+
</DataProvider>;
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Event Console
|
|
189
|
+
|
|
190
|
+
The `EventView` component provides a full-screen operations event console with severity filtering, sortable columns, search, and pagination — styled to match the holographic theme. It fetches events from an external API using the same `access-key` / `access-secret-key` authentication as the 3D dashboard.
|
|
191
|
+
|
|
192
|
+
### Quick start — API mode
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
import "react-dashstream/dist/index.css";
|
|
196
|
+
import { EventView } from "react-dashstream";
|
|
197
|
+
import type { EventApiConfig } from "react-dashstream";
|
|
198
|
+
|
|
199
|
+
const eventApiConfig: EventApiConfig = {
|
|
200
|
+
baseUrl: "https://your-monitoring-server.example.com",
|
|
201
|
+
// endpoint defaults to "/tsws/monitoring/api/v1.0/events/search"
|
|
202
|
+
payload: {
|
|
203
|
+
filter: {},
|
|
204
|
+
sortBy: "date_reception",
|
|
205
|
+
sortOrder: "DESC",
|
|
206
|
+
},
|
|
207
|
+
fieldMapping: {
|
|
208
|
+
id: "mc_ueid",
|
|
209
|
+
occurrence: "date_reception",
|
|
210
|
+
severityLastModified: "severity_last_modified",
|
|
211
|
+
severity: "severity",
|
|
212
|
+
owner: "owner",
|
|
213
|
+
class: "class",
|
|
214
|
+
host: "mc_host",
|
|
215
|
+
message: "msg",
|
|
216
|
+
remedySupportGroup: "ara_remedy_support_group",
|
|
217
|
+
incidentId: "ara_incident_id",
|
|
218
|
+
smsStatus: "ara_sms_status",
|
|
219
|
+
onCallNumber: "ara_on_call_number",
|
|
220
|
+
hostedApplication: "ara_hosted_application",
|
|
221
|
+
monitoringCategory: "ara_hosted_app_monitoring",
|
|
222
|
+
applicationSupportUnit: "ara_application_support_unit",
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
export default function Events() {
|
|
227
|
+
return <EventView apiConfig={eventApiConfig} />;
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
On first load a credentials modal appears (same as the 3D dashboard). After authentication, EventView sends a `POST` to `{baseUrl}/tsws/monitoring/api/v1.0/events/search` with the payload and polls every 60 seconds.
|
|
232
|
+
|
|
233
|
+
### How it works
|
|
234
|
+
|
|
235
|
+
1. **Authentication** — uses `access-key` and `access-secret-key` HTTP headers, identical to the 3D dashboard. If EventView is inside a `DataProvider` (e.g. alongside `AIOPsDashboard` with `liveData`), it reuses those credentials automatically — no second login.
|
|
236
|
+
2. **POST request** — sends `apiConfig.payload` as the JSON body.
|
|
237
|
+
3. **Response format** — expects:
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"eventSeverityCount": { "MAJOR": 1, "CRITICAL": 2, "MINOR": 3 },
|
|
241
|
+
"totalCount": 6,
|
|
242
|
+
"eventList": [{ "mc_ueid": "...", "msg": "...", ... }]
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
4. **Field mapping** — each object in `eventList` is mapped to an `AIOpsEvent` using `apiConfig.fieldMapping`. Every `AIOpsEvent` field must have a corresponding API field name.
|
|
246
|
+
5. **Severity mapping** — raw severity strings (e.g. `"CRITICAL"`) are mapped to `EventSeverity` via `apiConfig.severityMap` (defaults to `{ CRITICAL: "Critical", MAJOR: "Major", MINOR: "Minor" }`).
|
|
247
|
+
|
|
248
|
+
### Pre-fetched events mode
|
|
249
|
+
|
|
250
|
+
If you already have events data, pass them directly — no API call is made:
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
<EventView events={myEvents} title="My Event Console" />
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### EventView props
|
|
257
|
+
|
|
258
|
+
| Prop | Type | Default | Description |
|
|
259
|
+
| -------------------- | --------------------- | ----------------- | ---------------------------------------------------------------------------- |
|
|
260
|
+
| `apiConfig` | `EventApiConfig` | — | API configuration (base URL, payload, field mapping). Required for API mode |
|
|
261
|
+
| `events` | `AIOpsEvent[]` | — | Pre-fetched events. When provided, `apiConfig` is not used |
|
|
262
|
+
| `credentials` | `Credentials` | — | Explicit credentials. Falls back to DataProvider context, then modal |
|
|
263
|
+
| `columnWidthsCookie` | `string` | `"ev_col_widths"` | Cookie name used to persist user-defined column widths |
|
|
264
|
+
| `title` | `string` | `"Event Console"` | Title displayed in the component header |
|
|
265
|
+
| `theme` | `"light"` \| `"dark"` | — | Optional override; otherwise uses `ThemeProvider` / defaults to dark context |
|
|
266
|
+
|
|
267
|
+
### EventApiConfig
|
|
268
|
+
|
|
269
|
+
| Field | Type | Default | Description |
|
|
270
|
+
| ----------------- | ------------------------------- | ---------------------------------------------------------- | ------------------------------------------------- |
|
|
271
|
+
| `baseUrl` | `string` | (required) | Protocol + host, e.g. `"https://mon.example.com"` |
|
|
272
|
+
| `endpoint` | `string` | `"/tsws/monitoring/api/v1.0/events/search"` | Path appended to `baseUrl` |
|
|
273
|
+
| `payload` | `Record<string, unknown>` | (required) | POST body sent with every request |
|
|
274
|
+
| `fieldMapping` | `EventFieldMapping` | (required) | Maps API field names → `AIOpsEvent` fields |
|
|
275
|
+
| `severityMap` | `Record<string, EventSeverity>` | `{ CRITICAL: "Critical", MAJOR: "Major", MINOR: "Minor" }` | Maps raw severity → `EventSeverity` |
|
|
276
|
+
| `refreshInterval` | `number` | `60000` | Polling interval in milliseconds |
|
|
277
|
+
|
|
278
|
+
### Field mapping
|
|
279
|
+
|
|
280
|
+
The `fieldMapping` object maps every `AIOpsEvent` property to the key name the API returns. All fields are required:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
const fieldMapping: EventFieldMapping = {
|
|
284
|
+
id: "mc_ueid", // unique event identifier
|
|
285
|
+
occurrence: "date_reception", // datetime
|
|
286
|
+
severityLastModified: "severity_last_modified",
|
|
287
|
+
severity: "severity", // mapped through severityMap
|
|
288
|
+
owner: "owner",
|
|
289
|
+
class: "class", // Self-monitoring, SCOM, etc.
|
|
290
|
+
host: "mc_host",
|
|
291
|
+
message: "msg",
|
|
292
|
+
remedySupportGroup: "ara_remedy_support_group",
|
|
293
|
+
incidentId: "ara_incident_id",
|
|
294
|
+
smsStatus: "ara_sms_status", // "", "SUCCESS", "FAILED"
|
|
295
|
+
onCallNumber: "ara_on_call_number",
|
|
296
|
+
hostedApplication: "ara_hosted_application",
|
|
297
|
+
monitoringCategory: "ara_hosted_app_monitoring", // "24/7" or "Office Hours"
|
|
298
|
+
applicationSupportUnit: "ara_application_support_unit",
|
|
299
|
+
};
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Credential resolution order
|
|
303
|
+
|
|
304
|
+
1. `credentials` prop (if provided)
|
|
305
|
+
2. `DataProvider` context credentials (when inside `AIOPsDashboard` with `liveData`)
|
|
306
|
+
3. Built-in credentials modal (same UI as the 3D dashboard)
|
|
307
|
+
|
|
308
|
+
### Severity colors
|
|
309
|
+
|
|
310
|
+
| Severity | Color | Usage |
|
|
311
|
+
| -------- | --------- | ------------------ |
|
|
312
|
+
| Minor | `#ffb800` | Amber, low urgency |
|
|
313
|
+
| Major | `#ff6600` | Orange, attention |
|
|
314
|
+
| Critical | `#ff2255` | Red, immediate |
|
|
315
|
+
|
|
316
|
+
### Features
|
|
317
|
+
|
|
318
|
+
- **Live API polling** — POST to configurable endpoint with auto-refresh
|
|
319
|
+
- **Field mapping** — maps arbitrary API response keys to typed event columns
|
|
320
|
+
- **Shared authentication** — reuses credentials from `DataProvider` context when available
|
|
321
|
+
- **Severity filter chips** — toggle Critical, Major, Minor with live counts
|
|
322
|
+
- **Free-text search** — filters across message, host, owner, and incident ID
|
|
323
|
+
- **Sortable columns** — click any header to cycle ascending / descending / none
|
|
324
|
+
- **Infinite scroll** — all matching events render in a single scrollable table
|
|
325
|
+
- **Resizable columns** — drag column edges to resize; widths are persisted in cookies
|
|
326
|
+
- **Loading / error states** — spinner, error badge, last-refresh timestamp, manual refresh button
|
|
327
|
+
- **Theming** — dark holographic styling or **light** mode (panels, table, and credentials modal follow `ThemeProvider` / optional `theme` prop)
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Event-to-dashboard bridge
|
|
332
|
+
|
|
333
|
+
When the same `EventApiConfig` is passed to `AIOPsDashboard`, events are fetched in the background and **automatically highlight nodes** in the 3D topology whose hostname matches an open event. No extra code, no manual wiring — just pass the config.
|
|
334
|
+
|
|
335
|
+
### Quick start
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
<AIOPsDashboard
|
|
339
|
+
brandName="MY DASHBOARD"
|
|
340
|
+
services={services}
|
|
341
|
+
liveData
|
|
342
|
+
dataEndpoint="https://prometheus.example.com/api/v1/query"
|
|
343
|
+
dataBindings={dataBindings}
|
|
344
|
+
eventApiConfig={eventApiConfig} // ← same config used for EventView
|
|
345
|
+
>
|
|
346
|
+
<MyService name="My Service" />
|
|
347
|
+
</AIOPsDashboard>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### How it works
|
|
351
|
+
|
|
352
|
+
1. `AIOPsDashboard` wraps its children in an `EventAlertsProvider` that fetches events in the background (using the same polling interval and credentials as the Event Console).
|
|
353
|
+
2. Each fetched event has a `host` field (mapped via `fieldMapping`).
|
|
354
|
+
3. Every `ServiceNode` in the topology checks if its `componentInfo.name` matches any event host (**case-insensitive**).
|
|
355
|
+
4. If a match is found, the node's severity is updated and a `NodeCallout` appears with the event message.
|
|
356
|
+
|
|
357
|
+
### Severity mapping
|
|
358
|
+
|
|
359
|
+
| Event severity | Dashboard status | Callout color |
|
|
360
|
+
| -------------- | ---------------- | ------------- |
|
|
361
|
+
| Critical | `"critical"` | Red |
|
|
362
|
+
| Major | `"warning"` | Orange |
|
|
363
|
+
| Minor | `"warning"` | Orange |
|
|
364
|
+
|
|
365
|
+
When multiple events exist on the same host, the highest severity wins and the callout shows `"N events – <message>"`.
|
|
366
|
+
|
|
367
|
+
The event alert is a **third severity source** alongside the node's own `status` prop and metric threshold breaches. The highest severity among all three always wins.
|
|
368
|
+
|
|
369
|
+
### Node name matching
|
|
370
|
+
|
|
371
|
+
For the bridge to work, the `name` prop on your nodes must match the `host` field in the event data:
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
// If your events API returns host: "SAP-PRD-APP01", name your node:
|
|
375
|
+
<ServerNode name="SAP-PRD-APP01" ... />
|
|
376
|
+
|
|
377
|
+
// Matching is case-insensitive, so these also work:
|
|
378
|
+
<ServerNode name="sap-prd-app01" ... />
|
|
379
|
+
<ServerNode name="Sap-Prd-App01" ... />
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Credential resolution
|
|
383
|
+
|
|
384
|
+
The `EventAlertsProvider` inside `AIOPsDashboard` resolves credentials in this order:
|
|
385
|
+
|
|
386
|
+
1. Parent `DataProvider` context (when `liveData` is enabled — **same credentials, single login**)
|
|
387
|
+
2. Built-in credentials modal (when `liveData` is not enabled)
|
|
388
|
+
|
|
389
|
+
### Opt-in behavior
|
|
390
|
+
|
|
391
|
+
- **Without `eventApiConfig`** — no events are fetched, no alerts are injected, zero overhead.
|
|
392
|
+
- **Without `EventView`** — the bridge still works. You can use event-based alerts on the 3D dashboard without ever rendering an `EventView`.
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## External data source monitoring
|
|
397
|
+
|
|
398
|
+
DashStream can connect to any HTTP monitoring endpoint (Prometheus, Grafana, custom APIs) and feed live values into your dashboard. This section covers **every** data path — from node props to service dialogs, component dialogs, alerts, drill-down internals, and graphs.
|
|
399
|
+
|
|
400
|
+
### How it works
|
|
401
|
+
|
|
402
|
+
1. Set `liveData={true}` on `AIOPsDashboard` with `dataEndpoint` and `dataBindings`.
|
|
403
|
+
2. A **credentials modal** appears on first load asking for `access-key` and `access-secret-key` (stored in memory only).
|
|
404
|
+
3. The dashboard polls `GET <endpoint>?query=<encodedQuery>` for each unique query with credentials as HTTP headers.
|
|
405
|
+
4. Resolved values are injected as **props** into child service components matched by `name`.
|
|
406
|
+
5. The header shows **"DATA REFRESH FAILED"** if any queries fail.
|
|
407
|
+
|
|
408
|
+
### Live data props
|
|
409
|
+
|
|
410
|
+
| Prop | Type | Default | Description |
|
|
411
|
+
| --------------------- | ---------------------------------------- | ------------- | ------------------------------------------------------ |
|
|
412
|
+
| `liveData` | `boolean` | `false` | Enable the live data pipeline. |
|
|
413
|
+
| `dataEndpoint` | `string` | — | Base URL. Queries sent as `GET <url>?query=<encoded>`. |
|
|
414
|
+
| `dataBindings` | `DataBindings` | — | Maps service → prop → query. See below. |
|
|
415
|
+
| `dataTransform` | `(raw) => unknown` | Numeric parse | Global transform for raw responses. |
|
|
416
|
+
| `dataRefreshInterval` | `number` | `60000` | Polling interval in ms. |
|
|
417
|
+
| `serviceDataBindings` | `Record<string, ServiceMetricBinding[]>` | — | Live metrics for the service stats dialog. |
|
|
418
|
+
|
|
419
|
+
### Endpoint contract
|
|
420
|
+
|
|
421
|
+
| Aspect | Requirement |
|
|
422
|
+
| ------------ | --------------------------------------------------------------- |
|
|
423
|
+
| **Method** | `GET` |
|
|
424
|
+
| **URL** | `<dataEndpoint>?query=<urlEncodedQuery>` |
|
|
425
|
+
| **Headers** | `access-key` and `access-secret-key` |
|
|
426
|
+
| **Response** | Plain text body (trimmed). Default transform parses as number. |
|
|
427
|
+
| **Errors** | Non-2xx → counted as failure. Partial failures shown in header. |
|
|
428
|
+
|
|
429
|
+
For JSON responses (e.g. Prometheus), provide a custom `dataTransform`:
|
|
430
|
+
|
|
431
|
+
```tsx
|
|
432
|
+
dataTransform={(raw) => {
|
|
433
|
+
const parsed = JSON.parse(String(raw));
|
|
434
|
+
return parsed?.data?.result?.[0]?.value?.[1] ?? raw;
|
|
435
|
+
}}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
### 1. Data bindings — injecting live props into nodes
|
|
441
|
+
|
|
442
|
+
`dataBindings` maps **service name → prop name → query**. Each key in the inner object must match a prop on your service component. The service name must match the child's `name` prop.
|
|
443
|
+
|
|
444
|
+
A binding can be a bare query string or an object with a custom transform:
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
type DataBinding = string | { query: string; transform?: (raw: unknown) => unknown };
|
|
448
|
+
type DataBindings = Record<string, Record<string, DataBinding>>;
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**Example — one service with a server and database:**
|
|
452
|
+
|
|
453
|
+
```tsx
|
|
454
|
+
import "react-dashstream/dist/index.css";
|
|
455
|
+
import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
|
|
456
|
+
|
|
457
|
+
function statusFromValue(raw: unknown) {
|
|
458
|
+
const n = Number(raw);
|
|
459
|
+
if (n >= 85) return "critical";
|
|
460
|
+
if (n >= 70) return "warning";
|
|
461
|
+
return "online";
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export default function App() {
|
|
465
|
+
return (
|
|
466
|
+
<AIOPsDashboard
|
|
467
|
+
brandName="LIVE DASHBOARD"
|
|
468
|
+
services={[{ name: "My Service", status: "online" }]}
|
|
469
|
+
liveData={true}
|
|
470
|
+
dataEndpoint="https://prometheus.example.com/api/v1/query"
|
|
471
|
+
dataRefreshInterval={10000}
|
|
472
|
+
dataBindings={{
|
|
473
|
+
"My Service": {
|
|
474
|
+
// bare string → parsed as number automatically
|
|
475
|
+
cpuLoad: 'cpu_usage{instance="srv-01"}',
|
|
476
|
+
memLoad: 'memory_usage{instance="srv-01"}',
|
|
477
|
+
|
|
478
|
+
// object with transform → convert number to status string
|
|
479
|
+
status: {
|
|
480
|
+
query: 'up{instance="srv-01"}',
|
|
481
|
+
transform: statusFromValue,
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
// bind database props too
|
|
485
|
+
dbCapacity: 'disk_capacity{instance="db-01"}',
|
|
486
|
+
dbStatus: {
|
|
487
|
+
query: 'disk_capacity{instance="db-01"}',
|
|
488
|
+
transform: statusFromValue,
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
}}
|
|
492
|
+
>
|
|
493
|
+
<MyService name="My Service" />
|
|
494
|
+
</AIOPsDashboard>
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
The component receives `cpuLoad`, `memLoad`, `status`, `dbCapacity`, and `dbStatus` as live props, overriding their defaults.
|
|
500
|
+
|
|
501
|
+
#### Multi-component services
|
|
502
|
+
|
|
503
|
+
When a service has multiple nodes of the same type (e.g. three servers), use a flat naming convention with prefixes. Each binding maps one prop to one query — there is no array or object binding support.
|
|
504
|
+
|
|
505
|
+
```tsx
|
|
506
|
+
// Service component — accepts prefixed props for each server
|
|
507
|
+
interface ServiceXProps {
|
|
508
|
+
name: string;
|
|
509
|
+
status?: ComponentStatus;
|
|
510
|
+
srv1CpuLoad?: number;
|
|
511
|
+
srv1MemLoad?: number;
|
|
512
|
+
srv1Status?: ComponentStatus;
|
|
513
|
+
srv2CpuLoad?: number;
|
|
514
|
+
srv2MemLoad?: number;
|
|
515
|
+
srv2Status?: ComponentStatus;
|
|
516
|
+
srv3CpuLoad?: number;
|
|
517
|
+
srv3MemLoad?: number;
|
|
518
|
+
srv3Status?: ComponentStatus;
|
|
519
|
+
dbCapacity?: number;
|
|
520
|
+
dbStatus?: ComponentStatus;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function ServiceX({
|
|
524
|
+
name, status = "online",
|
|
525
|
+
srv1CpuLoad = 54, srv1MemLoad = 58, srv1Status = "online",
|
|
526
|
+
srv2CpuLoad = 63, srv2MemLoad = 66, srv2Status = "online",
|
|
527
|
+
srv3CpuLoad = 78, srv3MemLoad = 71, srv3Status = "online",
|
|
528
|
+
dbCapacity = 68, dbStatus = "online",
|
|
529
|
+
}: ServiceXProps) {
|
|
530
|
+
return (
|
|
531
|
+
<Service name={name} status={status} connections={[/* ... */]}>
|
|
532
|
+
<ServerNode name="SRV-X1" status={srv1Status}
|
|
533
|
+
cpuLoad={srv1CpuLoad} memLoad={srv1MemLoad} ... />
|
|
534
|
+
<ServerNode name="SRV-X2" status={srv2Status}
|
|
535
|
+
cpuLoad={srv2CpuLoad} memLoad={srv2MemLoad} ... />
|
|
536
|
+
<ServerNode name="SRV-X3" status={srv3Status}
|
|
537
|
+
cpuLoad={srv3CpuLoad} memLoad={srv3MemLoad} ... />
|
|
538
|
+
<DatabaseNode name="DB-X1" status={dbStatus}
|
|
539
|
+
capacity={dbCapacity} ... />
|
|
540
|
+
</Service>
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Dashboard — bind each prefixed prop to its query
|
|
545
|
+
<AIOPsDashboard
|
|
546
|
+
liveData={true}
|
|
547
|
+
dataEndpoint="https://prometheus.example.com/api/v1/query"
|
|
548
|
+
dataBindings={{
|
|
549
|
+
ServiceX: {
|
|
550
|
+
status: { query: 'status{instance="svcx"}', transform: statusFromValue },
|
|
551
|
+
srv1CpuLoad: 'cpu_usage{instance="srvx-01"}',
|
|
552
|
+
srv1MemLoad: 'memory_usage{instance="srvx-01"}',
|
|
553
|
+
srv1Status: { query: 'status{instance="srvx-01"}', transform: statusFromValue },
|
|
554
|
+
srv2CpuLoad: 'cpu_usage{instance="srvx-02"}',
|
|
555
|
+
srv2MemLoad: 'memory_usage{instance="srvx-02"}',
|
|
556
|
+
srv2Status: { query: 'status{instance="srvx-02"}', transform: statusFromValue },
|
|
557
|
+
srv3CpuLoad: 'cpu_usage{instance="srvx-03"}',
|
|
558
|
+
srv3MemLoad: 'memory_usage{instance="srvx-03"}',
|
|
559
|
+
srv3Status: { query: 'status{instance="srvx-03"}', transform: statusFromValue },
|
|
560
|
+
dbCapacity: 'disk_capacity{instance="dbx-01"}',
|
|
561
|
+
dbStatus: { query: 'status{instance="dbx-01"}', transform: statusFromValue },
|
|
562
|
+
},
|
|
563
|
+
}}
|
|
564
|
+
services={[{ name: "ServiceX", status: "online" }]}
|
|
565
|
+
>
|
|
566
|
+
<ServiceX name="ServiceX" />
|
|
567
|
+
</AIOPsDashboard>
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
Use a consistent naming convention like `srv1CpuLoad`, `srv2CpuLoad`, etc. The dashboard injects each prefixed prop individually. The service component maps each prop to the correct node.
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
### 2. Service dialog — live KPI metrics
|
|
575
|
+
|
|
576
|
+
The **ServiceDialog** is the stats panel that appears when you click a service. It shows KPI rows and alerts.
|
|
577
|
+
|
|
578
|
+
**Static way** — pass `ServiceMeta` with hardcoded values:
|
|
579
|
+
|
|
580
|
+
```tsx
|
|
581
|
+
const services: ServiceMeta[] = [
|
|
582
|
+
{
|
|
583
|
+
name: "My Service",
|
|
584
|
+
status: "online",
|
|
585
|
+
metrics: [
|
|
586
|
+
{ label: "Uptime", value: "99.99%", color: "#00ff88" },
|
|
587
|
+
{ label: "Avg Latency", value: "8ms", color: "#00e5ff" },
|
|
588
|
+
{ label: "Error Rate", value: "0.02%", color: "#00ff88" },
|
|
589
|
+
],
|
|
590
|
+
alerts: [{ level: "info", message: "All Systems Nominal" }],
|
|
591
|
+
},
|
|
592
|
+
];
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**Live way** — use `serviceDataBindings` to fetch values from your endpoint:
|
|
596
|
+
|
|
597
|
+
```tsx
|
|
598
|
+
<AIOPsDashboard
|
|
599
|
+
services={[{ name: "My Service", status: "online" }]}
|
|
600
|
+
liveData={true}
|
|
601
|
+
dataEndpoint="https://prometheus.example.com/api/v1/query"
|
|
602
|
+
dataBindings={{ /* ... */ }}
|
|
603
|
+
serviceDataBindings={{
|
|
604
|
+
"My Service": [
|
|
605
|
+
{ label: "CPU Load", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" },
|
|
606
|
+
{ label: "Memory", query: 'memory_usage{instance="srv-01"}', unit: "%", color: "#bb55ff" },
|
|
607
|
+
{ label: "Disk", query: 'disk_capacity{instance="db-01"}', unit: "%", color: "#ff8c00" },
|
|
608
|
+
],
|
|
609
|
+
}}
|
|
610
|
+
>
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Each `ServiceMetricBinding`:
|
|
614
|
+
|
|
615
|
+
```ts
|
|
616
|
+
interface ServiceMetricBinding {
|
|
617
|
+
label: string; // Row label
|
|
618
|
+
query: string; // PromQL query
|
|
619
|
+
unit?: string; // Suffix (e.g. "%", "ms")
|
|
620
|
+
color?: string; // Accent color
|
|
621
|
+
transform?: (raw: unknown) => string; // Custom formatter
|
|
622
|
+
}
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
When `serviceDataBindings` is provided for a service, live values **replace** the static `ServiceMeta.metrics`.
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
### 3. Component dialog — custom gauges
|
|
630
|
+
|
|
631
|
+
The **ComponentDialog** appears when you click a node (server, database, etc.) inside an expanded service. By default it shows CPU, Memory, and Storage gauges derived from the node's props.
|
|
632
|
+
|
|
633
|
+
**Override these gauges** with the `dialogMetrics` prop on any compound node:
|
|
634
|
+
|
|
635
|
+
```tsx
|
|
636
|
+
<ServerNode
|
|
637
|
+
ex={200}
|
|
638
|
+
ey={380}
|
|
639
|
+
compactOffset={{ x: -30, y: -20 }}
|
|
640
|
+
zIndex={8}
|
|
641
|
+
name="SRV-01"
|
|
642
|
+
subLabel="APP SERVER"
|
|
643
|
+
status="online"
|
|
644
|
+
cpuLoad={67}
|
|
645
|
+
memLoad={72}
|
|
646
|
+
dialogMetrics={[
|
|
647
|
+
{ id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67, unit: "%" },
|
|
648
|
+
{ id: "mem", label: "MEMORY", sublabel: "HEAP USAGE", value: 72, unit: "%" },
|
|
649
|
+
{ id: "iops", label: "IOPS", sublabel: "DISK OPS", value: 45, unit: "k/s", icon: "disk" },
|
|
650
|
+
{
|
|
651
|
+
id: "threads",
|
|
652
|
+
label: "THREADS",
|
|
653
|
+
sublabel: "ACTIVE",
|
|
654
|
+
value: 82,
|
|
655
|
+
unit: "%",
|
|
656
|
+
warnAt: 60,
|
|
657
|
+
critAt: 80,
|
|
658
|
+
icon: "cpu",
|
|
659
|
+
},
|
|
660
|
+
]}
|
|
661
|
+
/>
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
Each `ComponentDialogMetric`:
|
|
665
|
+
|
|
666
|
+
```ts
|
|
667
|
+
interface ComponentDialogMetric {
|
|
668
|
+
id: string; // Unique key
|
|
669
|
+
label: string; // Upper label (e.g. "CPU")
|
|
670
|
+
sublabel: string; // Lower label (e.g. "PROCESSOR")
|
|
671
|
+
value: number; // 0–100 gauge value
|
|
672
|
+
unit?: string; // Suffix (default "%")
|
|
673
|
+
icon?: "cpu" | "mem" | "disk"; // Gauge icon (default "cpu")
|
|
674
|
+
warnAt?: number; // Orange threshold (default 70)
|
|
675
|
+
critAt?: number; // Red threshold (default 85)
|
|
676
|
+
color?: string; // Override bar color (bypasses thresholds)
|
|
677
|
+
}
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
All compound nodes (`ServerNode`, `DatabaseNode`, `WebDispatcherNode`, `MessageServerNode`) accept `dialogMetrics`.
|
|
681
|
+
|
|
682
|
+
**Live component dialog metrics** — to feed live values into custom gauges, expose each metric value as a prop on your service component and use `dataBindings` to inject them. Then build the `dialogMetrics` array from those live props:
|
|
683
|
+
|
|
684
|
+
```tsx
|
|
685
|
+
// 1. Define your service component with props for each custom metric
|
|
686
|
+
import { Service, ServerNode } from "react-dashstream";
|
|
687
|
+
import type { ComponentStatus } from "react-dashstream";
|
|
688
|
+
|
|
689
|
+
interface MyServiceProps {
|
|
690
|
+
name: string;
|
|
691
|
+
status?: ComponentStatus;
|
|
692
|
+
cpuLoad?: number;
|
|
693
|
+
memLoad?: number;
|
|
694
|
+
iops?: number;
|
|
695
|
+
threadCount?: number;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function MyService({
|
|
699
|
+
name,
|
|
700
|
+
status = "online",
|
|
701
|
+
cpuLoad = 42,
|
|
702
|
+
memLoad = 60,
|
|
703
|
+
iops = 20,
|
|
704
|
+
threadCount = 45,
|
|
705
|
+
}: MyServiceProps) {
|
|
706
|
+
return (
|
|
707
|
+
<Service
|
|
708
|
+
name={name}
|
|
709
|
+
status={status}
|
|
710
|
+
connections={
|
|
711
|
+
[
|
|
712
|
+
/* ... */
|
|
713
|
+
]
|
|
714
|
+
}
|
|
715
|
+
>
|
|
716
|
+
<ServerNode
|
|
717
|
+
ex={200}
|
|
718
|
+
ey={380}
|
|
719
|
+
compactOffset={{ x: -30, y: -20 }}
|
|
720
|
+
zIndex={8}
|
|
721
|
+
name="SRV-01"
|
|
722
|
+
subLabel="APP SERVER"
|
|
723
|
+
status={status}
|
|
724
|
+
cpuLoad={cpuLoad}
|
|
725
|
+
memLoad={memLoad}
|
|
726
|
+
dialogMetrics={[
|
|
727
|
+
{ id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad },
|
|
728
|
+
{ id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad },
|
|
729
|
+
{
|
|
730
|
+
id: "iops",
|
|
731
|
+
label: "IOPS",
|
|
732
|
+
sublabel: "DISK OPS",
|
|
733
|
+
value: iops,
|
|
734
|
+
icon: "disk",
|
|
735
|
+
warnAt: 50,
|
|
736
|
+
critAt: 80,
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
id: "threads",
|
|
740
|
+
label: "THREADS",
|
|
741
|
+
sublabel: "ACTIVE",
|
|
742
|
+
value: threadCount,
|
|
743
|
+
icon: "cpu",
|
|
744
|
+
warnAt: 60,
|
|
745
|
+
critAt: 85,
|
|
746
|
+
},
|
|
747
|
+
]}
|
|
748
|
+
alert={{ offsetX: -160, offsetY: -60, align: "left" }}
|
|
749
|
+
/>
|
|
750
|
+
</Service>
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// 2. Bind each prop to a live query
|
|
755
|
+
<AIOPsDashboard
|
|
756
|
+
liveData={true}
|
|
757
|
+
dataEndpoint="https://prometheus.example.com/api/v1/query"
|
|
758
|
+
dataBindings={{
|
|
759
|
+
"My Service": {
|
|
760
|
+
cpuLoad: 'cpu_usage{instance="srv-01"}',
|
|
761
|
+
memLoad: 'memory_usage{instance="srv-01"}',
|
|
762
|
+
iops: 'disk_iops{instance="srv-01"}',
|
|
763
|
+
threadCount: 'active_threads{instance="srv-01"}',
|
|
764
|
+
status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
|
|
765
|
+
},
|
|
766
|
+
}}
|
|
767
|
+
services={[{ name: "My Service", status: "online" }]}
|
|
768
|
+
>
|
|
769
|
+
<MyService name="My Service" />
|
|
770
|
+
</AIOPsDashboard>;
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
The dashboard injects `cpuLoad`, `memLoad`, `iops`, and `threadCount` as live props into `MyService`. Those flow into the `dialogMetrics` array, so the component dialog gauges update automatically on every poll cycle. This works for any number of custom metrics — just add a prop for each one.
|
|
774
|
+
|
|
775
|
+
---
|
|
776
|
+
|
|
777
|
+
### 4. Alerts — automatic threshold detection
|
|
778
|
+
|
|
779
|
+
Nodes **automatically** show alert callouts when metrics breach thresholds:
|
|
780
|
+
|
|
781
|
+
- **Warning** at 70% (orange callout)
|
|
782
|
+
- **Critical** at 85% (red callout)
|
|
783
|
+
|
|
784
|
+
This works from `cpuLoad`, `memLoad`, `traffic`, `queueDepth`, or `capacity` — no extra code needed:
|
|
785
|
+
|
|
786
|
+
```tsx
|
|
787
|
+
// This server shows a critical alert automatically because cpuLoad > 85
|
|
788
|
+
<ServerNode
|
|
789
|
+
ex={200}
|
|
790
|
+
ey={380}
|
|
791
|
+
compactOffset={{ x: -30, y: -20 }}
|
|
792
|
+
zIndex={8}
|
|
793
|
+
name="SRV-01"
|
|
794
|
+
subLabel="PROCESSOR"
|
|
795
|
+
status="online"
|
|
796
|
+
cpuLoad={92}
|
|
797
|
+
memLoad={64}
|
|
798
|
+
/>
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
**Position the callout:**
|
|
802
|
+
|
|
803
|
+
```tsx
|
|
804
|
+
<ServerNode
|
|
805
|
+
cpuLoad={92}
|
|
806
|
+
alert={{ offsetX: -160, offsetY: -60, align: "left" }}
|
|
807
|
+
...
|
|
808
|
+
/>
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
**Custom alert message:**
|
|
812
|
+
|
|
813
|
+
```tsx
|
|
814
|
+
<ServerNode
|
|
815
|
+
cpuLoad={92}
|
|
816
|
+
alert={{ msg: "CPU overload — scale out", offsetX: -160, offsetY: -60, align: "left" }}
|
|
817
|
+
...
|
|
818
|
+
/>
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
**Custom thresholds** via `dialogMetrics`:
|
|
822
|
+
|
|
823
|
+
```tsx
|
|
824
|
+
<ServerNode
|
|
825
|
+
dialogMetrics={[
|
|
826
|
+
{ id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: 67,
|
|
827
|
+
warnAt: 50, critAt: 75 }, // Alert triggers at 67% because critAt is 75
|
|
828
|
+
]}
|
|
829
|
+
alert={{ offsetX: -160, offsetY: -60, align: "left" }}
|
|
830
|
+
...
|
|
831
|
+
/>
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
Alert `align` options: `"left"`, `"right"`, `"top"`, `"bottom"`.
|
|
835
|
+
|
|
836
|
+
---
|
|
837
|
+
|
|
838
|
+
### 5. Drill-down internals — custom sub-components
|
|
839
|
+
|
|
840
|
+
When you click a node in expanded view, a **drill-down** opens showing internal sub-components (CPUs, memory sticks, drives, etc.). By default these are auto-generated based on node type.
|
|
841
|
+
|
|
842
|
+
Provide **custom sub-components** via the `subComponents` prop:
|
|
843
|
+
|
|
844
|
+
```tsx
|
|
845
|
+
import { ServerNode, CPU3D, Memory3D, DriveBay3D, ThreadPool3D } from "react-dashstream";
|
|
846
|
+
import type { SubComponentConfig } from "react-dashstream";
|
|
847
|
+
|
|
848
|
+
const internals: SubComponentConfig[] = [
|
|
849
|
+
{
|
|
850
|
+
id: "cpu-0",
|
|
851
|
+
label: "CPU-0",
|
|
852
|
+
status: "online",
|
|
853
|
+
element: <CPU3D label="CPU-0" load={67} color="#00e5ff" />,
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
id: "cpu-1",
|
|
857
|
+
label: "CPU-1",
|
|
858
|
+
status: "warning",
|
|
859
|
+
element: <CPU3D label="CPU-1" load={88} color="#ff8c00" status="warning" />,
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
id: "heap",
|
|
863
|
+
label: "HEAP",
|
|
864
|
+
status: "online",
|
|
865
|
+
element: <Memory3D label="HEAP" usedPercent={72} color="#8855ee" />,
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
id: "drive",
|
|
869
|
+
label: "DRIVE-0",
|
|
870
|
+
status: "online",
|
|
871
|
+
element: <DriveBay3D label="DRIVE-0" color="#00e5ff" activity={true} />,
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
id: "threads",
|
|
875
|
+
label: "THREADS",
|
|
876
|
+
status: "online",
|
|
877
|
+
element: <ThreadPool3D label="THREADS" color="#00e5ff" />,
|
|
878
|
+
},
|
|
879
|
+
];
|
|
880
|
+
|
|
881
|
+
<ServerNode
|
|
882
|
+
ex={200}
|
|
883
|
+
ey={380}
|
|
884
|
+
compactOffset={{ x: -30, y: -20 }}
|
|
885
|
+
zIndex={8}
|
|
886
|
+
name="SRV-01"
|
|
887
|
+
subLabel="APP SERVER"
|
|
888
|
+
status="online"
|
|
889
|
+
cpuLoad={67}
|
|
890
|
+
memLoad={72}
|
|
891
|
+
subComponents={internals}
|
|
892
|
+
/>;
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
Each `SubComponentConfig`:
|
|
896
|
+
|
|
897
|
+
```ts
|
|
898
|
+
interface SubComponentConfig {
|
|
899
|
+
id: string; // Unique key
|
|
900
|
+
label: string; // Display name
|
|
901
|
+
status: ComponentStatus; // Drives LED color
|
|
902
|
+
detail?: string; // Shown on fault
|
|
903
|
+
element: ReactNode; // The 3D element to render
|
|
904
|
+
}
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
Available internal 3D components: `CPU3D`, `Memory3D`, `DriveBay3D`, `NetworkBlock3D`, `ThreadPool3D`, `Platter3D`, `Port3D`.
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
### 6. Graph series — custom sparklines
|
|
912
|
+
|
|
913
|
+
The drill-down also shows a **historical graph panel** with sparklines. By default, graphs are auto-generated from mock data.
|
|
914
|
+
|
|
915
|
+
Provide **custom graph data** via the `graphSeries` prop:
|
|
916
|
+
|
|
917
|
+
```tsx
|
|
918
|
+
import type { GraphSeries } from "react-dashstream";
|
|
919
|
+
|
|
920
|
+
const graphs: GraphSeries[] = [
|
|
921
|
+
{ id: "cpu", label: "CPU-0", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68, 55, 62] },
|
|
922
|
+
{ id: "mem", label: "HEAP", unit: "%", color: "#8855ee", data: [60, 65, 72, 78, 82, 75, 70] },
|
|
923
|
+
{ id: "iops", label: "DISK", unit: "k/s", color: "#ff8c00", data: [12, 15, 22, 18, 25, 20, 16] },
|
|
924
|
+
];
|
|
925
|
+
|
|
926
|
+
<ServerNode
|
|
927
|
+
ex={200}
|
|
928
|
+
ey={380}
|
|
929
|
+
compactOffset={{ x: -30, y: -20 }}
|
|
930
|
+
zIndex={8}
|
|
931
|
+
name="SRV-01"
|
|
932
|
+
subLabel="APP SERVER"
|
|
933
|
+
status="online"
|
|
934
|
+
cpuLoad={67}
|
|
935
|
+
memLoad={72}
|
|
936
|
+
graphSeries={graphs}
|
|
937
|
+
/>;
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
Each `GraphSeries`:
|
|
941
|
+
|
|
942
|
+
```ts
|
|
943
|
+
interface GraphSeries {
|
|
944
|
+
id: string; // Unique key
|
|
945
|
+
label: string; // Label above the sparkline
|
|
946
|
+
unit: string; // Suffix (e.g. "%", "kbps")
|
|
947
|
+
color: string; // Sparkline color
|
|
948
|
+
data: number[]; // Data points (most recent last)
|
|
949
|
+
}
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
---
|
|
953
|
+
|
|
954
|
+
### 7. Putting it all together
|
|
955
|
+
|
|
956
|
+
A complete example with live data, service dialog metrics, component dialog gauges, sub-components, graphs, and alert positioning:
|
|
957
|
+
|
|
958
|
+
```tsx
|
|
959
|
+
import "react-dashstream/dist/index.css";
|
|
960
|
+
import { AIOPsDashboard, Service, ServerNode, DatabaseNode, CPU3D, Memory3D } from "react-dashstream";
|
|
961
|
+
import type { ServiceMeta, DataBindings, ServiceMetricBinding } from "react-dashstream";
|
|
962
|
+
|
|
963
|
+
const services: ServiceMeta[] = [
|
|
964
|
+
{
|
|
965
|
+
name: "My Service",
|
|
966
|
+
status: "online",
|
|
967
|
+
metrics: [{ label: "Uptime", value: "99.99%", color: "#00ff88" }],
|
|
968
|
+
alerts: [{ level: "info", message: "All Systems Nominal" }],
|
|
969
|
+
},
|
|
970
|
+
];
|
|
971
|
+
|
|
972
|
+
function statusFromValue(raw: unknown) {
|
|
973
|
+
const n = Number(raw);
|
|
974
|
+
if (n >= 85) return "critical";
|
|
975
|
+
if (n >= 70) return "warning";
|
|
976
|
+
return "online";
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
const dataBindings: DataBindings = {
|
|
980
|
+
"My Service": {
|
|
981
|
+
cpuLoad: 'cpu_usage{instance="srv-01"}',
|
|
982
|
+
memLoad: 'memory_usage{instance="srv-01"}',
|
|
983
|
+
status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
|
|
984
|
+
dbCapacity: 'disk_capacity{instance="db-01"}',
|
|
985
|
+
dbStatus: { query: 'disk_capacity{instance="db-01"}', transform: statusFromValue },
|
|
986
|
+
},
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
const serviceDataBindings: Record<string, ServiceMetricBinding[]> = {
|
|
990
|
+
"My Service": [
|
|
991
|
+
{ label: "CPU Load", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" },
|
|
992
|
+
{ label: "Memory", query: 'memory_usage{instance="srv-01"}', unit: "%", color: "#bb55ff" },
|
|
993
|
+
{ label: "Disk", query: 'disk_capacity{instance="db-01"}', unit: "%", color: "#ff8c00" },
|
|
994
|
+
],
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
function MyService({ name, status, cpuLoad, memLoad, dbCapacity, dbStatus }: any) {
|
|
998
|
+
return (
|
|
999
|
+
<Service
|
|
1000
|
+
name={name}
|
|
1001
|
+
status={status ?? "online"}
|
|
1002
|
+
connections={[
|
|
1003
|
+
{ from: [330, 200], to: [200, 380], visibleAtPhase: 3 },
|
|
1004
|
+
{ from: [330, 200], to: [460, 380], visibleAtPhase: 3 },
|
|
1005
|
+
]}
|
|
1006
|
+
>
|
|
1007
|
+
<ServerNode
|
|
1008
|
+
ex={200}
|
|
1009
|
+
ey={380}
|
|
1010
|
+
compactOffset={{ x: -30, y: -20 }}
|
|
1011
|
+
zIndex={8}
|
|
1012
|
+
name="SRV-01"
|
|
1013
|
+
subLabel="APP SERVER"
|
|
1014
|
+
status={status ?? "online"}
|
|
1015
|
+
cpuLoad={cpuLoad ?? 42}
|
|
1016
|
+
memLoad={memLoad ?? 60}
|
|
1017
|
+
alert={{ offsetX: -160, offsetY: -60, align: "left" }}
|
|
1018
|
+
subComponents={[
|
|
1019
|
+
{
|
|
1020
|
+
id: "cpu",
|
|
1021
|
+
label: "CPU-0",
|
|
1022
|
+
status: "online",
|
|
1023
|
+
element: <CPU3D label="CPU-0" load={cpuLoad ?? 42} />,
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
id: "mem",
|
|
1027
|
+
label: "HEAP",
|
|
1028
|
+
status: "online",
|
|
1029
|
+
element: <Memory3D label="HEAP" usedPercent={memLoad ?? 60} />,
|
|
1030
|
+
},
|
|
1031
|
+
]}
|
|
1032
|
+
graphSeries={[
|
|
1033
|
+
{ id: "cpu", label: "CPU", unit: "%", color: "#00e5ff", data: [45, 52, 67, 71, 68] },
|
|
1034
|
+
]}
|
|
1035
|
+
/>
|
|
1036
|
+
<DatabaseNode
|
|
1037
|
+
ex={460}
|
|
1038
|
+
ey={380}
|
|
1039
|
+
compactOffset={{ x: 30, y: -20 }}
|
|
1040
|
+
zIndex={7}
|
|
1041
|
+
name="DB-01"
|
|
1042
|
+
subLabel="PRIMARY"
|
|
1043
|
+
status={dbStatus ?? "online"}
|
|
1044
|
+
capacity={dbCapacity ?? 55}
|
|
1045
|
+
alert={{ offsetX: 160, offsetY: -60, align: "right" }}
|
|
1046
|
+
/>
|
|
1047
|
+
</Service>
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
export default function App() {
|
|
1052
|
+
return (
|
|
1053
|
+
<AIOPsDashboard
|
|
1054
|
+
brandName="MY DASHBOARD"
|
|
1055
|
+
services={services}
|
|
1056
|
+
liveData={true}
|
|
1057
|
+
dataEndpoint="https://prometheus.example.com/api/v1/query"
|
|
1058
|
+
dataRefreshInterval={10000}
|
|
1059
|
+
dataBindings={dataBindings}
|
|
1060
|
+
serviceDataBindings={serviceDataBindings}
|
|
1061
|
+
>
|
|
1062
|
+
<MyService name="My Service" />
|
|
1063
|
+
</AIOPsDashboard>
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
---
|
|
1069
|
+
|
|
1070
|
+
### 8. Data hooks
|
|
1071
|
+
|
|
1072
|
+
Access live data anywhere inside the dashboard tree:
|
|
1073
|
+
|
|
1074
|
+
```tsx
|
|
1075
|
+
import { useAIOpsData, useAIOpsDataOptional, useQueryResult } from "react-dashstream";
|
|
1076
|
+
|
|
1077
|
+
function CustomWidget() {
|
|
1078
|
+
const { data, isRefreshing, lastRefreshError } = useAIOpsData();
|
|
1079
|
+
const cpu = useQueryResult('cpu_usage{instance="srv-01"}');
|
|
1080
|
+
|
|
1081
|
+
return (
|
|
1082
|
+
<div>
|
|
1083
|
+
{isRefreshing && <span>Refreshing...</span>}
|
|
1084
|
+
{lastRefreshError && <span>{lastRefreshError}</span>}
|
|
1085
|
+
<span>CPU: {String(cpu)}</span>
|
|
1086
|
+
</div>
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
| Hook | Returns | Throws? |
|
|
1092
|
+
| ------------------------ | -------------------------- | ---------------------------- |
|
|
1093
|
+
| `useAIOpsData()` | Full `DataContextValue` | Yes — outside `DataProvider` |
|
|
1094
|
+
| `useAIOpsDataOptional()` | `DataContextValue \| null` | No |
|
|
1095
|
+
| `useQueryResult(query)` | Raw response or `null` | Yes — outside `DataProvider` |
|
|
1096
|
+
|
|
1097
|
+
### 9. Standalone DataProvider
|
|
1098
|
+
|
|
1099
|
+
Use the data layer without the full dashboard shell:
|
|
1100
|
+
|
|
1101
|
+
```tsx
|
|
1102
|
+
import { DataProvider, useAIOpsData } from "react-dashstream";
|
|
1103
|
+
|
|
1104
|
+
function MyApp() {
|
|
1105
|
+
return (
|
|
1106
|
+
<DataProvider
|
|
1107
|
+
config={{
|
|
1108
|
+
endpoint: "https://prometheus.example.com/api/v1/query",
|
|
1109
|
+
queries: ['cpu_usage{instance="srv-01"}'],
|
|
1110
|
+
refreshInterval: 15000,
|
|
1111
|
+
}}
|
|
1112
|
+
>
|
|
1113
|
+
<MyContent />
|
|
1114
|
+
</DataProvider>
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
---
|
|
1120
|
+
|
|
1121
|
+
## Components
|
|
1122
|
+
|
|
1123
|
+
### Layout
|
|
1124
|
+
|
|
1125
|
+
| Component | Description |
|
|
1126
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
1127
|
+
| `AIOPsDashboard` | Full dashboard shell — header, carousel, and state management. Drop services in as children. |
|
|
1128
|
+
| `Service` | Service container — positions child nodes on a 3D orbit and draws connection lines between them. |
|
|
1129
|
+
| `ServiceNode` | Low-level positioned wrapper with floating animation, scan line, and labels. Used by the compound nodes below. |
|
|
1130
|
+
|
|
1131
|
+
### Compound nodes (recommended)
|
|
1132
|
+
|
|
1133
|
+
These combine `ServiceNode` + 3D model + `componentInfo` into a single element:
|
|
1134
|
+
|
|
1135
|
+
| Component | Key props | Description |
|
|
1136
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
1137
|
+
| `ServerNode` | `status`, `cpuLoad`, `memLoad`, `brandLabel`, `dialogMetrics`, `subComponents`, `graphSeries`, `alert` | App server tower with LEDs and CPU/memory bars. |
|
|
1138
|
+
| `DatabaseNode` | `status`, `capacity`, `dialogMetrics`, `subComponents`, `graphSeries`, `alert` | Three-platter database cylinder with capacity bar. |
|
|
1139
|
+
| `WebDispatcherNode` | `status`, `traffic`, `activeRoutes`, `dialogMetrics`, `subComponents`, `graphSeries`, `alert` | Network appliance with 8 port LEDs and traffic metrics. |
|
|
1140
|
+
| `MessageServerNode` | `status`, `queueDepth`, `msgsPerSec`, `instances`, `dialogMetrics`, `subComponents`, `graphSeries`, `alert` | Message server with instance LEDs and queue metrics. |
|
|
1141
|
+
| `HumanNode` | `status`, `scale` | SVG wireframe person icon for user/actor nodes. |
|
|
1142
|
+
|
|
1143
|
+
All compound nodes share: `ex`, `ey`, `compactOffset`, `zIndex`, `name`, `subLabel`, `color`, `delay`, `visibleAtPhase`.
|
|
1144
|
+
|
|
1145
|
+
### 3D models (low-level)
|
|
1146
|
+
|
|
1147
|
+
If you need full control, use the raw 3D models inside a `ServiceNode`:
|
|
1148
|
+
|
|
1149
|
+
`Server3D`, `Database3D`, `WebDispatcher3D`, `MessageServer3D`, `Human3D`
|
|
1150
|
+
|
|
1151
|
+
All 3D models accept: `rotateX`, `rotateY`, `rotateZ`, `scale`, `autoRotate`.
|
|
1152
|
+
|
|
1153
|
+
### Status indicators
|
|
1154
|
+
|
|
1155
|
+
| Component | Props | Description |
|
|
1156
|
+
| --------------- | ------------------------------------ | ---------------------------------------------------------------- |
|
|
1157
|
+
| `SyncBridge` | `synced`, `latencyMs` | Database replication bridge between primary and standby. |
|
|
1158
|
+
| `NodeCallout` | `status`, `title`, `msg`, `ex`, `ey` | Alert callout with leader line (auto-rendered by `ServiceNode`). |
|
|
1159
|
+
| `HoloBase` | `size`, `color`, `widthRatio` | Neon holographic base platform (auto-rendered by `Service`). |
|
|
1160
|
+
| `SvgConnection` | `x1`, `y1`, `x2`, `y2`, `show` | Animated dashed SVG connection line. |
|
|
1161
|
+
|
|
1162
|
+
### Dialogs
|
|
1163
|
+
|
|
1164
|
+
| Component | Description |
|
|
1165
|
+
| ----------------- | ----------------------------------------------------------------------------------------------- |
|
|
1166
|
+
| `ServiceDialog` | Service-level stats panel — shows metrics and alerts. Auto-rendered when a service is expanded. |
|
|
1167
|
+
| `ComponentDialog` | Component drill-down with sub-component internals and sparkline graphs. |
|
|
1168
|
+
|
|
1169
|
+
### Drill-down internals
|
|
1170
|
+
|
|
1171
|
+
Rendered inside `ComponentDialog` when a component is inspected:
|
|
1172
|
+
|
|
1173
|
+
`CPU3D`, `Memory3D`, `DriveBay3D`, `NetworkBlock3D`, `ThreadPool3D`, `Platter3D`, `Port3D`, `HistoricalGraphPanel`, `ComponentDrillView`
|
|
1174
|
+
|
|
1175
|
+
### Pre-built services
|
|
1176
|
+
|
|
1177
|
+
| Component | Topology |
|
|
1178
|
+
| ----------------- | --------------------------------------------------------------------------------- |
|
|
1179
|
+
| `SAPService` | Users → Web Dispatcher + Message Server → 3 App Servers → Primary DB + Standby DB |
|
|
1180
|
+
| `ExchangeService` | Users → Dispatcher → 3 App Servers → Primary DB + Standby DB |
|
|
1181
|
+
|
|
1182
|
+
---
|
|
1183
|
+
|
|
1184
|
+
## Building a custom service
|
|
1185
|
+
|
|
1186
|
+
Compose compound nodes inside a `Service` container. Each node needs:
|
|
1187
|
+
|
|
1188
|
+
- **`ex`, `ey`** — Position in the expanded topology (pixels from top-left of scene).
|
|
1189
|
+
- **`compactOffset`** — Offset from the service center in the compact carousel view.
|
|
1190
|
+
- **`zIndex`** — Stacking order (higher tiers get higher values).
|
|
1191
|
+
- **`visibleAtPhase`** — When the node fades in during expansion (0–6). Use `2` for top-tier, `3` for middle, etc.
|
|
1192
|
+
|
|
1193
|
+
Define connection lines between nodes via the `connections` prop on `Service`:
|
|
1194
|
+
|
|
1195
|
+
```tsx
|
|
1196
|
+
connections={[
|
|
1197
|
+
{ from: [x1, y1], to: [x2, y2], visibleAtPhase: 3 },
|
|
1198
|
+
{ from: [x1, y1], to: [x2, y2], visibleAtPhase: 4, color: "#ff8c00" },
|
|
1199
|
+
]}
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
---
|
|
1203
|
+
|
|
1204
|
+
## Status types
|
|
1205
|
+
|
|
1206
|
+
```ts
|
|
1207
|
+
type ComponentStatus = "online" | "warning" | "critical" | "offline";
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
| Status | Color | Glow |
|
|
1211
|
+
| ---------- | --------- | -------- |
|
|
1212
|
+
| `online` | `#00e5ff` | cyan |
|
|
1213
|
+
| `warning` | `#ff8c00` | orange |
|
|
1214
|
+
| `critical` | `#ff2255` | red |
|
|
1215
|
+
| `offline` | `#1e3a5a` | dim blue |
|
|
1216
|
+
|
|
1217
|
+
---
|
|
1218
|
+
|
|
1219
|
+
## Theme constants
|
|
1220
|
+
|
|
1221
|
+
```ts
|
|
1222
|
+
import {
|
|
1223
|
+
STATUS_CFG,
|
|
1224
|
+
HOLO_CYAN,
|
|
1225
|
+
HOLO_BLUE,
|
|
1226
|
+
HOLO_SURFACE,
|
|
1227
|
+
HOLO_GLASS,
|
|
1228
|
+
makeFaceStyles,
|
|
1229
|
+
ThemeProvider,
|
|
1230
|
+
useTheme,
|
|
1231
|
+
type DashboardTheme,
|
|
1232
|
+
} from "react-dashstream";
|
|
1233
|
+
```
|
|
1234
|
+
|
|
1235
|
+
- `STATUS_CFG` — status-to-color lookup table
|
|
1236
|
+
- `HOLO_CYAN` / `HOLO_BLUE` — accent colors
|
|
1237
|
+
- `HOLO_SURFACE` / `HOLO_GLASS` — CSS gradient backgrounds for 3D faces
|
|
1238
|
+
- `makeFaceStyles(W, H, D)` — generates CSS transforms for the 6 faces of a 3D box
|
|
1239
|
+
- `ThemeProvider`, `useTheme`, `DashboardTheme` — light/dark for dashboard UI, dialogs, `EventView`, and credentials modal (see **Theme (light / dark)** above)
|
|
1240
|
+
|
|
1241
|
+
---
|
|
1242
|
+
|
|
1243
|
+
## License
|
|
1244
|
+
|
|
1245
|
+
MIT
|