@checkstack/healthcheck-frontend 0.7.2 → 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,23 @@
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
+
3
21
  ## 0.7.2
4
22
 
5
23
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/healthcheck-frontend",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "scripts": {
@@ -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