@checkstack/healthcheck-frontend 0.17.0 → 0.17.1

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,11 @@
1
1
  # @checkstack/healthcheck-frontend
2
2
 
3
+ ## 0.17.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 42b0832: Refactor auto-chart layout to make collector grouping more dominant. Chart titles now show only the metric label (e.g. "Avg Response Time") instead of the prefixed "{collectorId}: Metric" form. Collector groups display the collector name as a heading with a badge containing the full collector id. Cards now stack at full width and their contents are center-aligned.
8
+
3
9
  ## 0.17.0
4
10
 
5
11
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/healthcheck-frontend",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "checkstack": {
@@ -10,7 +10,7 @@ import { extractChartFields, getFieldValue } from "./schema-parser";
10
10
  import { useStrategySchemas } from "./useStrategySchemas";
11
11
  import type { HealthCheckDiagramSlotContext } from "../slots";
12
12
  import { SparklineTooltip } from "../components/SparklineTooltip";
13
- import { Card, CardContent, CardHeader, CardTitle } from "@checkstack/ui";
13
+ import { Badge, Card, CardContent, CardHeader, CardTitle } from "@checkstack/ui";
14
14
  import {
15
15
  PieChart,
16
16
  Pie,
@@ -86,7 +86,7 @@ export function AutoChartGrid({ context }: AutoChartGridProps) {
86
86
  <div className="space-y-6 mt-4">
87
87
  {/* Strategy-level fields */}
88
88
  {strategyFields.length > 0 && (
89
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
89
+ <div className="space-y-4">
90
90
  {strategyFields.map((field) => (
91
91
  <AutoChartCard
92
92
  key={field.name}
@@ -118,6 +118,7 @@ interface CollectorGroupData {
118
118
  instanceKey: string;
119
119
  collectorId: string;
120
120
  displayName: string;
121
+ instanceLabel?: string;
121
122
  fields: ExpandedChartField[];
122
123
  }
123
124
 
@@ -140,15 +141,15 @@ function buildCollectorGroups(
140
141
 
141
142
  // Create a group for each instance
142
143
  for (const [index, instanceKey] of instanceKeys.entries()) {
143
- const displayName =
144
- instanceKeys.length === 1
145
- ? collectorId.split(".").pop() || collectorId
146
- : `${collectorId.split(".").pop() || collectorId} #${index + 1}`;
144
+ const displayName = collectorId.split(".").pop() || collectorId;
145
+ const instanceLabel =
146
+ instanceKeys.length > 1 ? `#${index + 1}` : undefined;
147
147
 
148
148
  groups.push({
149
149
  instanceKey,
150
150
  collectorId,
151
151
  displayName,
152
+ instanceLabel,
152
153
  fields: collectorFields.map((field) => ({
153
154
  ...field,
154
155
  instanceKey,
@@ -174,7 +175,8 @@ function CollectorGroup({
174
175
  context: HealthCheckDiagramSlotContext;
175
176
  baselines: AnomalyBaselineDto[];
176
177
  }) {
177
- // Separate fields into narrow (grid) and wide (full-width) categories
178
+ // Order: narrow (summary) cards first, then wide timeline cards.
179
+ // Layout is now fully stacked at 100% width.
178
180
  const narrowFields = group.fields.filter(
179
181
  (f) => !WIDE_CHART_TYPES.has(f.chartType),
180
182
  );
@@ -184,26 +186,30 @@ function CollectorGroup({
184
186
 
185
187
  return (
186
188
  <div className="space-y-4">
187
- <h4 className="text-sm font-medium text-muted-foreground uppercase tracking-wide">
188
- {group.displayName}
189
- </h4>
190
-
191
- {/* Narrow cards grid - these pack together nicely */}
192
- {narrowFields.length > 0 && (
193
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
194
- {narrowFields.map((field) => (
195
- <AutoChartCard
196
- key={`${field.instanceKey}-${field.name}`}
197
- field={field}
198
- context={context}
199
- baselines={baselines}
200
- />
201
- ))}
202
- </div>
203
- )}
189
+ <div className="flex items-center gap-2 flex-wrap border-b pb-2">
190
+ <h3 className="text-lg font-semibold capitalize">
191
+ {group.displayName}
192
+ </h3>
193
+ {group.instanceLabel && (
194
+ <span className="text-sm font-medium text-muted-foreground">
195
+ {group.instanceLabel}
196
+ </span>
197
+ )}
198
+ <Badge variant="outline" className="font-mono">
199
+ {group.collectorId}
200
+ </Badge>
201
+ </div>
204
202
 
205
- {/* Wide timeline cards - assertion plus timeline fields */}
206
203
  <div className="space-y-4">
204
+ {narrowFields.map((field) => (
205
+ <AutoChartCard
206
+ key={`${field.instanceKey}-${field.name}`}
207
+ field={field}
208
+ context={context}
209
+ baselines={baselines}
210
+ />
211
+ ))}
212
+
207
213
  <AssertionStatusCard
208
214
  context={context}
209
215
  instanceKey={group.instanceKey}
@@ -275,9 +281,11 @@ function AssertionStatusCard({
275
281
  return (
276
282
  <Card>
277
283
  <CardHeader className="pb-2">
278
- <CardTitle className="text-sm font-medium">Assertion</CardTitle>
284
+ <CardTitle className="text-sm font-medium text-center">
285
+ Assertion
286
+ </CardTitle>
279
287
  </CardHeader>
280
- <CardContent>
288
+ <CardContent className="text-center">
281
289
  <div className="text-sm text-muted-foreground">No data</div>
282
290
  </CardContent>
283
291
  </Card>
@@ -298,14 +306,14 @@ function AssertionStatusCard({
298
306
  >
299
307
  <CardHeader className="pb-2">
300
308
  <CardTitle
301
- className={`text-sm font-medium ${latestResult.passed ? "" : "text-red-600"}`}
309
+ className={`text-sm font-medium text-center ${latestResult.passed ? "" : "text-red-600"}`}
302
310
  >
303
311
  {latestResult.passed ? "Assertion" : "Assertion Failed"}
304
312
  </CardTitle>
305
313
  </CardHeader>
306
- <CardContent className="space-y-2">
314
+ <CardContent className="space-y-2 text-center">
307
315
  {/* Current status with rate */}
308
- <div className="flex items-center gap-2">
316
+ <div className="flex items-center justify-center gap-2">
309
317
  <div
310
318
  className={`w-3 h-3 rounded-full ${
311
319
  latestResult.passed ? "bg-green-500" : "bg-red-500"
@@ -449,9 +457,11 @@ function AutoChartCard({ field, context, baselines }: AutoChartCardProps) {
449
457
  return (
450
458
  <Card>
451
459
  <CardHeader className="pb-2">
452
- <CardTitle className="text-sm font-medium">{field.label}</CardTitle>
460
+ <CardTitle className="text-sm font-medium text-center">
461
+ {field.label}
462
+ </CardTitle>
453
463
  </CardHeader>
454
- <CardContent>
464
+ <CardContent className="flex flex-col items-center text-center [&>*]:w-full">
455
465
  <ChartRenderer field={field} context={context} baseline={baseline} />
456
466
  </CardContent>
457
467
  </Card>
@@ -592,8 +602,8 @@ function GaugeRenderer({ field, context, baseline }: ChartRendererProps) {
592
602
  const data = [{ name: field.label, value: numValue, fill: fillColor }];
593
603
 
594
604
  return (
595
- <div className="flex flex-col gap-2">
596
- <div className="flex items-center gap-3">
605
+ <div className="flex flex-col gap-2 items-center">
606
+ <div className="flex items-center justify-center gap-3">
597
607
  <ResponsiveContainer width={80} height={80}>
598
608
  <RadialBarChart
599
609
  cx="50%"
@@ -681,7 +691,7 @@ function BooleanRenderer({ field, context, baseline }: ChartRendererProps) {
681
691
  return (
682
692
  <div className="space-y-2">
683
693
  {/* Current status with rate */}
684
- <div className="flex items-center gap-2">
694
+ <div className="flex items-center justify-center gap-2">
685
695
  <div
686
696
  className={`w-3 h-3 rounded-full ${
687
697
  latestValue ? "bg-green-500" : "bg-red-500"
@@ -753,7 +763,7 @@ function TextRenderer({ field, context, baseline }: ChartRendererProps) {
753
763
  return (
754
764
  <div className="space-y-2">
755
765
  {/* Current value with count */}
756
- <div className="flex items-center gap-2">
766
+ <div className="flex items-center justify-center gap-2">
757
767
  <span className="text-sm font-mono">{latestValue || "—"}</span>
758
768
  {!allSame && (
759
769
  <span className="text-xs text-muted-foreground">
@@ -8,7 +8,7 @@
8
8
  import type { ChartField } from "./schema-parser";
9
9
  import { extractChartFields, getFieldValue } from "./schema-parser";
10
10
  import { useStrategySchemas } from "./useStrategySchemas";
11
- import { Card, CardContent, CardHeader, CardTitle } from "@checkstack/ui";
11
+ import { Badge, Card, CardContent, CardHeader, CardTitle } from "@checkstack/ui";
12
12
  import { RadialBarChart, RadialBar, ResponsiveContainer } from "recharts";
13
13
 
14
14
  interface SingleRunChartGridProps {
@@ -62,7 +62,7 @@ export function SingleRunChartGrid({
62
62
  <div className="space-y-6">
63
63
  {/* Strategy-level fields */}
64
64
  {strategyFields.length > 0 && (
65
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
65
+ <div className="space-y-4">
66
66
  {strategyFields.map((field) => (
67
67
  <SingleValueCard
68
68
  key={field.name}
@@ -124,12 +124,13 @@ function CollectorSection({
124
124
 
125
125
  return (
126
126
  <div className="space-y-4">
127
- <div className="flex items-center gap-2">
128
- <h4 className="text-sm font-medium text-muted-foreground uppercase tracking-wide">
129
- {displayName}
130
- </h4>
131
- <span className="text-xs text-muted-foreground">
132
- ({instanceId.slice(0, 8)})
127
+ <div className="flex items-center gap-2 flex-wrap border-b pb-2">
128
+ <h3 className="text-lg font-semibold capitalize">{displayName}</h3>
129
+ <Badge variant="outline" className="font-mono">
130
+ {collectorId}
131
+ </Badge>
132
+ <span className="text-xs text-muted-foreground font-mono">
133
+ {instanceId.slice(0, 8)}
133
134
  </span>
134
135
  </div>
135
136
 
@@ -165,7 +166,7 @@ function CollectorSection({
165
166
  </Card>
166
167
  )}
167
168
 
168
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
169
+ <div className="space-y-4">
169
170
  {fields.map((field) => (
170
171
  <SingleValueCard
171
172
  key={field.name}
@@ -190,9 +191,11 @@ function SingleValueCard({ field, value }: SingleValueCardProps) {
190
191
  return (
191
192
  <Card>
192
193
  <CardHeader className="pb-2">
193
- <CardTitle className="text-sm font-medium">{field.label}</CardTitle>
194
+ <CardTitle className="text-sm font-medium text-center">
195
+ {field.label}
196
+ </CardTitle>
194
197
  </CardHeader>
195
- <CardContent>
198
+ <CardContent className="flex flex-col items-center text-center [&>*]:w-full">
196
199
  <SingleValueRenderer field={field} value={value} />
197
200
  </CardContent>
198
201
  </Card>
@@ -292,7 +295,7 @@ function GaugeRenderer({ value, unit }: { value: unknown; unit?: string }) {
292
295
  const data = [{ name: "value", value: clampedValue, fill: fillColor }];
293
296
 
294
297
  return (
295
- <div className="flex items-center gap-3">
298
+ <div className="flex items-center justify-center gap-3">
296
299
  <ResponsiveContainer width={80} height={80}>
297
300
  <RadialBarChart
298
301
  cx="50%"
@@ -330,7 +333,7 @@ function BooleanRenderer({ value }: { value: unknown }) {
330
333
  const boolValue = Boolean(value);
331
334
 
332
335
  return (
333
- <div className="flex items-center gap-2">
336
+ <div className="flex items-center justify-center gap-2">
334
337
  <div
335
338
  className={`w-3 h-3 rounded-full ${
336
339
  boolValue ? "bg-green-500" : "bg-red-500"
@@ -108,12 +108,9 @@ function extractFieldsFromProperties(
108
108
  if (!chartType) continue;
109
109
 
110
110
  // Use just field name - collectorId is stored separately for data lookup
111
+ // and surfaced via a separate badge in the group header
111
112
  const field = extractSingleField(fieldName, prop);
112
113
  field.collectorId = collectorId;
113
- // Prefix label with collector ID for clarity
114
- if (!prop["x-chart-label"]?.includes(collectorId)) {
115
- field.label = `${collectorId}: ${field.label}`;
116
- }
117
114
  fields.push(field);
118
115
  }
119
116