@checkstack/healthcheck-frontend 0.12.1 → 0.13.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/CHANGELOG.md +63 -0
- package/package.json +11 -10
- package/src/components/HealthCheckRunsTable.tsx +19 -1
- package/src/components/HealthCheckSystemOverview.tsx +79 -6
- package/src/components/SystemHealthCheckAssignment.tsx +34 -825
- package/src/components/assignments/AssignmentTree.tsx +123 -0
- package/src/components/assignments/ExecutionPanel.tsx +135 -0
- package/src/components/assignments/GeneralPanel.tsx +100 -0
- package/src/components/assignments/RetentionPanel.tsx +157 -0
- package/src/components/assignments/ThresholdsPanel.tsx +254 -0
- package/src/components/editor/EditorTree.tsx +10 -75
- package/src/components/editor/IDEStatusBar.tsx +5 -58
- package/src/hooks/useHealthCheckData.ts +4 -0
- package/src/index.tsx +7 -0
- package/src/pages/AssignmentIDEPage.tsx +477 -0
- package/src/pages/HealthCheckHistoryDetailPage.tsx +53 -6
- package/src/pages/HealthCheckIDEPage.tsx +7 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# @checkstack/healthcheck-frontend
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 26d8bae: Distributed satellite health checks and Assignment IDE page
|
|
8
|
+
|
|
9
|
+
**Satellite System**
|
|
10
|
+
|
|
11
|
+
- New `satellite-backend`, `satellite-common`, `satellite-frontend`, and `satellite` agent packages for distributed health check execution
|
|
12
|
+
- WebSocket-based satellite connectivity with authentication, heartbeats, and live configuration push
|
|
13
|
+
- Satellite management UI with create dialog, status badges, and list page
|
|
14
|
+
|
|
15
|
+
**Live Configuration Updates**
|
|
16
|
+
|
|
17
|
+
- Added `assignmentChanged` hook to `healthcheck-backend` for cross-plugin communication
|
|
18
|
+
- `satellite-backend` subscribes to assignment changes and pushes config updates to connected satellites in real-time
|
|
19
|
+
|
|
20
|
+
**Assignment IDE Page**
|
|
21
|
+
|
|
22
|
+
- Replaced the 1028-line modal-based `SystemHealthCheckAssignment` component with a full-page IDE layout
|
|
23
|
+
- New modular components: `AssignmentTree`, `GeneralPanel`, `ThresholdsPanel`, `RetentionPanel`, `ExecutionPanel`
|
|
24
|
+
- Added unassign capability and sorted assignment lists for stable ordering
|
|
25
|
+
|
|
26
|
+
**Shared IDE Primitives**
|
|
27
|
+
|
|
28
|
+
- Extracted `IDETreeNode`, `IDETreeSection`, `IDEStatusBar`, `IDELayout` to `@checkstack/ui` for cross-plugin reuse
|
|
29
|
+
- Migrated existing health check IDE editor to use shared primitives
|
|
30
|
+
|
|
31
|
+
**Infrastructure**
|
|
32
|
+
|
|
33
|
+
- Added `Dockerfile.satellite` for containerized satellite deployment
|
|
34
|
+
- WebSocket route registry in `@checkstack/backend` and `@checkstack/backend-api`
|
|
35
|
+
|
|
36
|
+
- 26d8bae: Source attribution and filtering for satellite health checks
|
|
37
|
+
|
|
38
|
+
**Source Attribution**
|
|
39
|
+
|
|
40
|
+
- Fixed satellite result attribution: runs from satellites now correctly display their source instead of defaulting to "Local"
|
|
41
|
+
- Added `sourceId` and `sourceLabel` to both public and detailed history API responses
|
|
42
|
+
|
|
43
|
+
**Source Filtering**
|
|
44
|
+
|
|
45
|
+
- Added `sourceFilter` parameter to `getHistory`, `getDetailedHistory`, and `getDetailedAggregatedHistory` RPC endpoints
|
|
46
|
+
- Source filter supports "local" (core-only), specific satellite UUID, or all sources
|
|
47
|
+
- Filter applies to all three aggregation tiers (raw, hourly, daily)
|
|
48
|
+
|
|
49
|
+
**Frontend**
|
|
50
|
+
|
|
51
|
+
- System detail accordion shows source filter buttons (All / Local / per-satellite) next to date range filter
|
|
52
|
+
- Filter applies to both charts and recent runs table
|
|
53
|
+
- Source column added to the recent runs table with Local/Remote badges
|
|
54
|
+
- Health check history detail page includes per-satellite source filter buttons
|
|
55
|
+
|
|
56
|
+
### Patch Changes
|
|
57
|
+
|
|
58
|
+
- Updated dependencies [26d8bae]
|
|
59
|
+
- Updated dependencies [26d8bae]
|
|
60
|
+
- @checkstack/ui@1.3.0
|
|
61
|
+
- @checkstack/healthcheck-common@0.11.0
|
|
62
|
+
- @checkstack/satellite-common@0.2.0
|
|
63
|
+
- @checkstack/auth-frontend@0.5.19
|
|
64
|
+
- @checkstack/dashboard-frontend@0.3.27
|
|
65
|
+
|
|
3
66
|
## 0.12.1
|
|
4
67
|
|
|
5
68
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/healthcheck-frontend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.tsx",
|
|
6
6
|
"checkstack": {
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
"lint:code": "eslint . --max-warnings 0"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@checkstack/auth-frontend": "0.5.
|
|
16
|
-
"@checkstack/catalog-common": "1.3.
|
|
17
|
-
"@checkstack/common": "0.6.
|
|
18
|
-
"@checkstack/dashboard-frontend": "0.3.
|
|
19
|
-
"@checkstack/frontend-api": "0.3.
|
|
20
|
-
"@checkstack/healthcheck-common": "0.10.
|
|
21
|
-
"@checkstack/signal-frontend": "0.0.
|
|
22
|
-
"@checkstack/ui": "1.2.
|
|
15
|
+
"@checkstack/auth-frontend": "0.5.18",
|
|
16
|
+
"@checkstack/catalog-common": "1.3.1",
|
|
17
|
+
"@checkstack/common": "0.6.5",
|
|
18
|
+
"@checkstack/dashboard-frontend": "0.3.26",
|
|
19
|
+
"@checkstack/frontend-api": "0.3.9",
|
|
20
|
+
"@checkstack/healthcheck-common": "0.10.1",
|
|
21
|
+
"@checkstack/signal-frontend": "0.0.15",
|
|
22
|
+
"@checkstack/ui": "1.2.1",
|
|
23
23
|
"ajv": "^8.18.0",
|
|
24
24
|
"ajv-formats": "^3.0.1",
|
|
25
25
|
"date-fns": "^4.1.0",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"react-router-dom": "^6.20.0",
|
|
29
29
|
"recharts": "^3.6.0",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
|
-
"zod": "^4.2.1"
|
|
31
|
+
"zod": "^4.2.1",
|
|
32
|
+
"@checkstack/satellite-common": "0.1.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@checkstack/scripts": "0.1.2",
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
Pagination,
|
|
11
11
|
} from "@checkstack/ui";
|
|
12
12
|
import { formatDistanceToNow, format } from "date-fns";
|
|
13
|
-
import { ExternalLink, Loader2 } from "lucide-react";
|
|
13
|
+
import { ExternalLink, Loader2, Satellite, Server } from "lucide-react";
|
|
14
14
|
import { useNavigate } from "react-router-dom";
|
|
15
15
|
import { healthcheckRoutes } from "@checkstack/healthcheck-common";
|
|
16
16
|
import { resolveRoute } from "@checkstack/common";
|
|
@@ -22,6 +22,10 @@ export interface HealthCheckRunDetailed {
|
|
|
22
22
|
status: "healthy" | "unhealthy" | "degraded";
|
|
23
23
|
result: Record<string, unknown>;
|
|
24
24
|
timestamp: Date;
|
|
25
|
+
/** Source ID for result attribution (undefined = local core, UUID = satellite) */
|
|
26
|
+
sourceId?: string;
|
|
27
|
+
/** Human-readable source label (e.g. "Local" or "EU West (eu-west-1)") */
|
|
28
|
+
sourceLabel?: string;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
export interface HealthCheckRunsTableProps {
|
|
@@ -93,6 +97,7 @@ export const HealthCheckRunsTable: React.FC<HealthCheckRunsTableProps> = ({
|
|
|
93
97
|
</>
|
|
94
98
|
)}
|
|
95
99
|
<TableHead>Timestamp</TableHead>
|
|
100
|
+
<TableHead>Source</TableHead>
|
|
96
101
|
{showFilterColumns && <TableHead className="w-16"></TableHead>}
|
|
97
102
|
</TableRow>
|
|
98
103
|
</TableHeader>
|
|
@@ -123,6 +128,19 @@ export const HealthCheckRunsTable: React.FC<HealthCheckRunsTableProps> = ({
|
|
|
123
128
|
})}
|
|
124
129
|
</span>
|
|
125
130
|
</TableCell>
|
|
131
|
+
<TableCell>
|
|
132
|
+
{run.sourceId ? (
|
|
133
|
+
<span className="inline-flex items-center gap-1 text-xs px-1.5 py-0.5 rounded-full bg-orange-500/10 text-orange-600">
|
|
134
|
+
<Satellite className="h-3 w-3" />
|
|
135
|
+
{run.sourceLabel ?? "Remote"}
|
|
136
|
+
</span>
|
|
137
|
+
) : (
|
|
138
|
+
<span className="inline-flex items-center gap-1 text-xs px-1.5 py-0.5 rounded-full bg-muted text-muted-foreground">
|
|
139
|
+
<Server className="h-3 w-3" />
|
|
140
|
+
{run.sourceLabel ?? "Local"}
|
|
141
|
+
</span>
|
|
142
|
+
)}
|
|
143
|
+
</TableCell>
|
|
126
144
|
{showFilterColumns && (
|
|
127
145
|
<TableCell>
|
|
128
146
|
<ExternalLink className="h-4 w-4 text-muted-foreground" />
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
healthCheckAccess,
|
|
16
16
|
healthcheckRoutes,
|
|
17
17
|
} from "@checkstack/healthcheck-common";
|
|
18
|
+
import { SatelliteApi, satelliteAccess } from "@checkstack/satellite-common";
|
|
18
19
|
import { resolveRoute } from "@checkstack/common";
|
|
19
20
|
import {
|
|
20
21
|
HealthBadge,
|
|
@@ -36,10 +37,14 @@ import {
|
|
|
36
37
|
CardContent,
|
|
37
38
|
CardHeader,
|
|
38
39
|
CardTitle,
|
|
39
|
-
|
|
40
40
|
} from "@checkstack/ui";
|
|
41
41
|
import { formatDistanceToNow } from "date-fns";
|
|
42
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
ChevronDown,
|
|
44
|
+
ChevronRight,
|
|
45
|
+
Satellite as SatelliteIcon,
|
|
46
|
+
Server,
|
|
47
|
+
} from "lucide-react";
|
|
43
48
|
import { useNavigate } from "react-router-dom";
|
|
44
49
|
import { HealthCheckSparkline } from "./HealthCheckSparkline";
|
|
45
50
|
import { HealthCheckLatencyChart } from "./HealthCheckLatencyChart";
|
|
@@ -71,14 +76,24 @@ interface ExpandedRowProps {
|
|
|
71
76
|
systemId: string;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
|
-
|
|
75
79
|
const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
76
80
|
const healthCheckClient = usePluginClient(HealthCheckApi);
|
|
81
|
+
const satelliteClient = usePluginClient(SatelliteApi);
|
|
77
82
|
const navigate = useNavigate();
|
|
78
83
|
const accessApi = useApi(accessApiRef);
|
|
79
84
|
const { allowed: canViewDetails } = accessApi.useAccess(
|
|
80
85
|
healthCheckAccess.details,
|
|
81
86
|
);
|
|
87
|
+
const { allowed: canReadSatellites } = accessApi.useAccess(
|
|
88
|
+
satelliteAccess.satellite.read,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Fetch satellites for source filter (only if user has access)
|
|
92
|
+
const { data: satellitesData } = satelliteClient.listSatellites.useQuery(
|
|
93
|
+
{},
|
|
94
|
+
{ enabled: canReadSatellites },
|
|
95
|
+
);
|
|
96
|
+
const satellites = satellitesData?.satellites ?? [];
|
|
82
97
|
|
|
83
98
|
// Date range state for filtering - default to last 24 hours
|
|
84
99
|
const [dateRange, setDateRange] = useState(() =>
|
|
@@ -86,6 +101,7 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
86
101
|
);
|
|
87
102
|
// Track if a rolling preset is active (vs custom range)
|
|
88
103
|
const [isRollingPreset, setIsRollingPreset] = useState(true);
|
|
104
|
+
const [sourceFilter, setSourceFilter] = useState<string | undefined>();
|
|
89
105
|
|
|
90
106
|
// Callback to handle date range changes from the filter
|
|
91
107
|
const handleDateRangeChange = useCallback(
|
|
@@ -139,6 +155,7 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
139
155
|
configurationId: item.configurationId,
|
|
140
156
|
strategyId: item.strategyId,
|
|
141
157
|
dateRange,
|
|
158
|
+
sourceFilter,
|
|
142
159
|
isRollingPreset,
|
|
143
160
|
// Update endDate to current time when new runs are detected (only for rolling presets)
|
|
144
161
|
onDateRangeRefresh: (newEndDate) => {
|
|
@@ -161,6 +178,7 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
161
178
|
offset: pagination.offset,
|
|
162
179
|
startDate: dateRange.startDate,
|
|
163
180
|
// Don't pass endDate - backend defaults to 'now' so new runs are included
|
|
181
|
+
sourceFilter,
|
|
164
182
|
sortOrder: "desc",
|
|
165
183
|
});
|
|
166
184
|
|
|
@@ -191,7 +209,6 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
191
209
|
: `Window mode (${item.stateThresholds.windowSize} runs): Degraded at ${item.stateThresholds.degraded.minFailureCount}+ failures, Unhealthy at ${item.stateThresholds.unhealthy.minFailureCount}+ failures`
|
|
192
210
|
: "Using default thresholds";
|
|
193
211
|
|
|
194
|
-
|
|
195
212
|
// Render charts - charts handle data transformation internally
|
|
196
213
|
const renderCharts = () => {
|
|
197
214
|
if (chartLoading) {
|
|
@@ -266,8 +283,6 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
266
283
|
</div>
|
|
267
284
|
</div>
|
|
268
285
|
|
|
269
|
-
|
|
270
|
-
|
|
271
286
|
{/* Date Range Filter with Loading Spinner */}
|
|
272
287
|
<div className="flex items-center gap-3 flex-wrap">
|
|
273
288
|
<DateRangeFilter
|
|
@@ -292,6 +307,49 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
292
307
|
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
|
|
293
308
|
)}
|
|
294
309
|
</div>
|
|
310
|
+
{/* Source filter (visible when satellites exist and user has read access) */}
|
|
311
|
+
{canReadSatellites && satellites.length > 0 && (
|
|
312
|
+
<div className="flex items-center gap-2">
|
|
313
|
+
<span className="text-xs text-muted-foreground">Source:</span>
|
|
314
|
+
<div className="flex items-center gap-1">
|
|
315
|
+
<button
|
|
316
|
+
onClick={() => setSourceFilter(undefined)}
|
|
317
|
+
className={`inline-flex items-center gap-1 text-xs px-2 py-1 rounded-full transition-colors ${
|
|
318
|
+
sourceFilter === undefined
|
|
319
|
+
? "bg-primary text-primary-foreground"
|
|
320
|
+
: "bg-muted text-muted-foreground hover:bg-muted/80"
|
|
321
|
+
}`}
|
|
322
|
+
>
|
|
323
|
+
All
|
|
324
|
+
</button>
|
|
325
|
+
<button
|
|
326
|
+
onClick={() => setSourceFilter("local")}
|
|
327
|
+
className={`inline-flex items-center gap-1 text-xs px-2 py-1 rounded-full transition-colors ${
|
|
328
|
+
sourceFilter === "local"
|
|
329
|
+
? "bg-primary text-primary-foreground"
|
|
330
|
+
: "bg-muted text-muted-foreground hover:bg-muted/80"
|
|
331
|
+
}`}
|
|
332
|
+
>
|
|
333
|
+
<Server className="h-3 w-3" />
|
|
334
|
+
Local
|
|
335
|
+
</button>
|
|
336
|
+
{satellites.map((sat) => (
|
|
337
|
+
<button
|
|
338
|
+
key={sat.id}
|
|
339
|
+
onClick={() => setSourceFilter(sat.id)}
|
|
340
|
+
className={`inline-flex items-center gap-1 text-xs px-2 py-1 rounded-full transition-colors ${
|
|
341
|
+
sourceFilter === sat.id
|
|
342
|
+
? "bg-orange-500 text-white"
|
|
343
|
+
: "bg-orange-500/10 text-orange-600 hover:bg-orange-500/20"
|
|
344
|
+
}`}
|
|
345
|
+
>
|
|
346
|
+
<SatelliteIcon className="h-3 w-3" />
|
|
347
|
+
{sat.name}
|
|
348
|
+
</button>
|
|
349
|
+
))}
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
)}
|
|
295
353
|
|
|
296
354
|
{/* Charts Section */}
|
|
297
355
|
{renderCharts()}
|
|
@@ -309,12 +367,14 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
309
367
|
</span>
|
|
310
368
|
<div className="flex-1 h-px bg-border" />
|
|
311
369
|
</div>
|
|
370
|
+
|
|
312
371
|
<div className="rounded-md border">
|
|
313
372
|
<Table>
|
|
314
373
|
<TableHeader>
|
|
315
374
|
<TableRow>
|
|
316
375
|
<TableHead className="w-24">Status</TableHead>
|
|
317
376
|
<TableHead>Time</TableHead>
|
|
377
|
+
<TableHead>Source</TableHead>
|
|
318
378
|
</TableRow>
|
|
319
379
|
</TableHeader>
|
|
320
380
|
<TableBody>
|
|
@@ -348,6 +408,19 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
348
408
|
addSuffix: true,
|
|
349
409
|
})}
|
|
350
410
|
</TableCell>
|
|
411
|
+
<TableCell>
|
|
412
|
+
{run.sourceId ? (
|
|
413
|
+
<span className="inline-flex items-center gap-1 text-xs px-1.5 py-0.5 rounded-full bg-orange-500/10 text-orange-600">
|
|
414
|
+
<SatelliteIcon className="h-3 w-3" />
|
|
415
|
+
{run.sourceLabel ?? "Remote"}
|
|
416
|
+
</span>
|
|
417
|
+
) : (
|
|
418
|
+
<span className="inline-flex items-center gap-1 text-xs px-1.5 py-0.5 rounded-full bg-muted text-muted-foreground">
|
|
419
|
+
<Server className="h-3 w-3" />
|
|
420
|
+
{run.sourceLabel ?? "Local"}
|
|
421
|
+
</span>
|
|
422
|
+
)}
|
|
423
|
+
</TableCell>
|
|
351
424
|
</TableRow>
|
|
352
425
|
))}
|
|
353
426
|
</TableBody>
|