@checkstack/healthcheck-frontend 0.7.1 → 0.8.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
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @checkstack/healthcheck-frontend
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d6f7449: Add availability statistics display to HealthCheckSystemOverview
|
|
8
|
+
|
|
9
|
+
- New `getAvailabilityStats` RPC endpoint that calculates availability percentages for 31-day and 365-day periods
|
|
10
|
+
- Availability is calculated as `(healthyRuns / totalRuns) * 100`
|
|
11
|
+
- Data is sourced from both daily aggregates and recent raw runs to include the most up-to-date information
|
|
12
|
+
- Frontend displays availability stats with color-coded badges (green ≥99.9%, yellow ≥99%, red <99%)
|
|
13
|
+
- Shows total run counts for each period
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [d6f7449]
|
|
18
|
+
- @checkstack/healthcheck-common@0.8.0
|
|
19
|
+
- @checkstack/dashboard-frontend@0.3.12
|
|
20
|
+
|
|
21
|
+
## 0.7.2
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- e58e994: Fix runtime error in AutoChartGrid when mapping over values with undefined elements
|
|
26
|
+
|
|
27
|
+
The filter functions `getAllBooleanValuesWithTime` and `getAllStringValuesWithTime` incorrectly checked `v !== null` instead of `v !== undefined`, allowing undefined elements to pass through and crash when accessing `.value`.
|
|
28
|
+
|
|
3
29
|
## 0.7.1
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1202,7 +1202,9 @@ function getAllBooleanValuesWithTime(
|
|
|
1202
1202
|
timeLabel: format(new Date(run.timestamp), "MMM d, HH:mm:ss"),
|
|
1203
1203
|
};
|
|
1204
1204
|
})
|
|
1205
|
-
.filter(
|
|
1205
|
+
.filter(
|
|
1206
|
+
(v): v is { value: boolean; timeLabel: string } => v !== undefined,
|
|
1207
|
+
);
|
|
1206
1208
|
}
|
|
1207
1209
|
return context.buckets
|
|
1208
1210
|
.map((bucket) => {
|
|
@@ -1219,7 +1221,7 @@ function getAllBooleanValuesWithTime(
|
|
|
1219
1221
|
timeLabel: `${format(bucketStart, "MMM d, HH:mm")} - ${format(bucketEnd, "HH:mm")}`,
|
|
1220
1222
|
};
|
|
1221
1223
|
})
|
|
1222
|
-
.filter((v): v is { value: boolean; timeLabel: string } => v !==
|
|
1224
|
+
.filter((v): v is { value: boolean; timeLabel: string } => v !== undefined);
|
|
1223
1225
|
}
|
|
1224
1226
|
|
|
1225
1227
|
/**
|
|
@@ -1242,7 +1244,9 @@ function getAllStringValuesWithTime(
|
|
|
1242
1244
|
timeLabel: format(new Date(run.timestamp), "MMM d, HH:mm:ss"),
|
|
1243
1245
|
};
|
|
1244
1246
|
})
|
|
1245
|
-
.filter(
|
|
1247
|
+
.filter(
|
|
1248
|
+
(v): v is { value: string; timeLabel: string } => v !== undefined,
|
|
1249
|
+
);
|
|
1246
1250
|
}
|
|
1247
1251
|
return context.buckets
|
|
1248
1252
|
.map((bucket) => {
|
|
@@ -1259,5 +1263,5 @@ function getAllStringValuesWithTime(
|
|
|
1259
1263
|
timeLabel: `${format(bucketStart, "MMM d, HH:mm")} - ${format(bucketEnd, "HH:mm")}`,
|
|
1260
1264
|
};
|
|
1261
1265
|
})
|
|
1262
|
-
.filter((v): v is { value: string; timeLabel: string } => v !==
|
|
1266
|
+
.filter((v): v is { value: string; timeLabel: string } => v !== undefined);
|
|
1263
1267
|
}
|
|
@@ -70,6 +70,30 @@ interface ExpandedRowProps {
|
|
|
70
70
|
systemId: string;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// Helper to format availability percentage with color
|
|
74
|
+
const formatAvailability = (
|
|
75
|
+
value: number | null,
|
|
76
|
+
totalRuns: number,
|
|
77
|
+
): { text: string; className: string } => {
|
|
78
|
+
if (value === null || totalRuns === 0) {
|
|
79
|
+
return { text: "N/A", className: "text-muted-foreground" };
|
|
80
|
+
}
|
|
81
|
+
const formatted = value.toFixed(2) + "%";
|
|
82
|
+
if (value >= 99.9) {
|
|
83
|
+
return {
|
|
84
|
+
text: formatted,
|
|
85
|
+
className: "text-green-600 dark:text-green-400",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (value >= 99) {
|
|
89
|
+
return {
|
|
90
|
+
text: formatted,
|
|
91
|
+
className: "text-yellow-600 dark:text-yellow-400",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return { text: formatted, className: "text-red-600 dark:text-red-400" };
|
|
95
|
+
};
|
|
96
|
+
|
|
73
97
|
const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
74
98
|
const healthCheckClient = usePluginClient(HealthCheckApi);
|
|
75
99
|
const navigate = useNavigate();
|
|
@@ -191,6 +215,13 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
191
215
|
: `Window mode (${item.stateThresholds.windowSize} runs): Degraded at ${item.stateThresholds.degraded.minFailureCount}+ failures, Unhealthy at ${item.stateThresholds.unhealthy.minFailureCount}+ failures`
|
|
192
216
|
: "Using default thresholds";
|
|
193
217
|
|
|
218
|
+
// Fetch availability stats
|
|
219
|
+
const { data: availabilityData } =
|
|
220
|
+
healthCheckClient.getAvailabilityStats.useQuery({
|
|
221
|
+
systemId,
|
|
222
|
+
configurationId: item.configurationId,
|
|
223
|
+
});
|
|
224
|
+
|
|
194
225
|
// Render charts - charts handle data transformation internally
|
|
195
226
|
const renderCharts = () => {
|
|
196
227
|
if (chartLoading) {
|
|
@@ -270,6 +301,59 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
|
|
|
270
301
|
</div>
|
|
271
302
|
</div>
|
|
272
303
|
|
|
304
|
+
{/* Availability Stats - Prominent Display */}
|
|
305
|
+
{availabilityData && (
|
|
306
|
+
<div className="grid grid-cols-2 gap-4">
|
|
307
|
+
<div className="flex flex-col gap-1 p-4 rounded-lg border bg-card">
|
|
308
|
+
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
|
309
|
+
31-Day Availability
|
|
310
|
+
</span>
|
|
311
|
+
<div className="flex items-baseline gap-2">
|
|
312
|
+
<span
|
|
313
|
+
className={`text-2xl font-bold ${formatAvailability(availabilityData.availability31Days, availabilityData.totalRuns31Days).className}`}
|
|
314
|
+
>
|
|
315
|
+
{
|
|
316
|
+
formatAvailability(
|
|
317
|
+
availabilityData.availability31Days,
|
|
318
|
+
availabilityData.totalRuns31Days,
|
|
319
|
+
).text
|
|
320
|
+
}
|
|
321
|
+
</span>
|
|
322
|
+
{availabilityData.totalRuns31Days > 0 && (
|
|
323
|
+
<span className="text-sm text-muted-foreground">
|
|
324
|
+
({availabilityData.totalRuns31Days.toLocaleString()} runs)
|
|
325
|
+
</span>
|
|
326
|
+
)}
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
<div className="flex flex-col gap-1 p-4 rounded-lg border bg-card">
|
|
330
|
+
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
|
331
|
+
365-Day Availability
|
|
332
|
+
</span>
|
|
333
|
+
<div className="flex items-baseline gap-2">
|
|
334
|
+
<span
|
|
335
|
+
className={`text-2xl font-bold ${formatAvailability(availabilityData.availability365Days, availabilityData.totalRuns365Days).className}`}
|
|
336
|
+
>
|
|
337
|
+
{
|
|
338
|
+
formatAvailability(
|
|
339
|
+
availabilityData.availability365Days,
|
|
340
|
+
availabilityData.totalRuns365Days,
|
|
341
|
+
).text
|
|
342
|
+
}
|
|
343
|
+
</span>
|
|
344
|
+
{availabilityData.totalRuns365Days > 0 && (
|
|
345
|
+
<span className="text-sm text-muted-foreground">
|
|
346
|
+
({availabilityData.totalRuns365Days.toLocaleString()} runs)
|
|
347
|
+
</span>
|
|
348
|
+
)}
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
)}
|
|
353
|
+
|
|
354
|
+
{/* Divider */}
|
|
355
|
+
<div className="h-px bg-border" />
|
|
356
|
+
|
|
273
357
|
{/* Date Range Filter with Loading Spinner */}
|
|
274
358
|
<div className="flex items-center gap-3 flex-wrap">
|
|
275
359
|
<DateRangeFilter
|