@checkstack/incident-frontend 0.4.25 → 0.5.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,47 @@
1
1
  # @checkstack/incident-frontend
2
2
 
3
+ ## 0.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - @checkstack/dashboard-frontend@0.4.1
8
+
9
+ ## 0.5.0
10
+
11
+ ### Minor Changes
12
+
13
+ - bb1fea0: Redesign system detail page with hero banner, two-column layout, plugin metric tiles, and health check slide-over drawer.
14
+
15
+ ### New Components
16
+
17
+ - **MetricTile** (`@checkstack/ui`): Compact stat tile with icon, label, value, variant coloring
18
+ - **Sheet** (`@checkstack/ui`): Slide-over drawer built on Radix Dialog primitives
19
+
20
+ ### New Extension Slot
21
+
22
+ - **SystemOverviewMetricsSlot** (`@checkstack/catalog-common`): Plugin-contributed at-a-glance metric tiles in the system detail hero banner
23
+
24
+ ### Layout Changes
25
+
26
+ - System detail page now uses a hero banner with breadcrumb, status badges, and metric tile strip
27
+ - Two-column layout: monitoring content (left) and system context (right)
28
+ - Health checks rendered as compact card rows instead of heavy accordions
29
+ - Clicking a health check opens a slide-over drawer with summary tiles, timeline charts, and recent runs
30
+ - Right column uses lightweight borderless sections with dividers instead of heavy Card wrappers
31
+
32
+ ### Plugin Extensions
33
+
34
+ - Health check, SLO, Incident, and Maintenance plugins each contribute a metric tile to the hero banner
35
+
36
+ ### Patch Changes
37
+
38
+ - Updated dependencies [bb1fea0]
39
+ - Updated dependencies [bb1fea0]
40
+ - @checkstack/dashboard-frontend@0.4.0
41
+ - @checkstack/ui@1.4.0
42
+ - @checkstack/catalog-common@1.4.0
43
+ - @checkstack/auth-frontend@0.5.26
44
+
3
45
  ## 0.4.25
4
46
 
5
47
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/incident-frontend",
3
- "version": "0.4.25",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "checkstack": {
@@ -10,49 +10,13 @@ import {
10
10
  INCIDENT_UPDATED,
11
11
  type IncidentWithSystems,
12
12
  } from "@checkstack/incident-common";
13
- import {
14
- Card,
15
- CardHeader,
16
- CardTitle,
17
- CardContent,
18
- Badge,
19
- LoadingSpinner,
20
- Button,
21
- } from "@checkstack/ui";
22
- import { AlertTriangle, Clock, History, ChevronRight } from "lucide-react";
23
- import { formatDistanceToNow } from "date-fns";
13
+ import { Badge, LoadingSpinner, Button } from "@checkstack/ui";
14
+ import { AlertTriangle, History } from "lucide-react";
24
15
 
25
16
  type Props = SlotContext<typeof SystemDetailsTopSlot>;
26
17
 
27
18
  const SEVERITY_WEIGHTS = { critical: 3, major: 2, minor: 1 } as const;
28
19
 
29
- function getSeverityColor(severity: string): string {
30
- switch (severity) {
31
- case "critical": {
32
- return "border-destructive/30 bg-destructive/5";
33
- }
34
- case "major": {
35
- return "border-warning/30 bg-warning/5";
36
- }
37
- default: {
38
- return "border-info/30 bg-info/5";
39
- }
40
- }
41
- }
42
-
43
- function getSeverityHeaderColor(severity: string): string {
44
- switch (severity) {
45
- case "critical": {
46
- return "bg-destructive/10";
47
- }
48
- case "major": {
49
- return "bg-warning/10";
50
- }
51
- default: {
52
- return "bg-info/10";
53
- }
54
- }
55
- }
56
20
 
57
21
  function findMostSevereIncident(
58
22
  incidents: IncidentWithSystems[]
@@ -97,137 +61,78 @@ export const SystemIncidentPanel: React.FC<Props> = ({ system }) => {
97
61
 
98
62
  if (loading) {
99
63
  return (
100
- <Card>
101
- <CardContent className="p-6 flex justify-center">
102
- <LoadingSpinner />
103
- </CardContent>
104
- </Card>
64
+ <div className="flex items-center justify-center rounded-md border border-border/50 bg-card px-3 py-2">
65
+ <LoadingSpinner />
66
+ </div>
105
67
  );
106
68
  }
107
69
 
108
70
  if (incidents.length === 0) {
109
- // Show a subtle card with just the history button when no active incidents
110
71
  return (
111
- <Card className="border-border/50">
112
- <CardContent className="p-4">
113
- <div className="flex items-center justify-between">
114
- <div className="flex items-center gap-2 text-muted-foreground">
115
- <AlertTriangle className="h-4 w-4" />
116
- <span className="text-sm">No active incidents</span>
117
- </div>
118
- <Button variant="ghost" size="sm" asChild>
119
- <Link
120
- to={resolveRoute(incidentRoutes.routes.systemHistory, {
121
- systemId: system.id,
122
- })}
123
- >
124
- <History className="h-4 w-4 mr-1" />
125
- View History
126
- </Link>
127
- </Button>
128
- </div>
129
- </CardContent>
130
- </Card>
72
+ <div className="flex items-center justify-between rounded-md border border-border/50 bg-card px-3 py-2">
73
+ <div className="flex items-center gap-2 text-muted-foreground">
74
+ <AlertTriangle className="h-3.5 w-3.5" />
75
+ <span className="text-sm">No active incidents</span>
76
+ </div>
77
+ <Button variant="ghost" size="sm" className="h-7 text-xs" asChild>
78
+ <Link
79
+ to={resolveRoute(incidentRoutes.routes.systemHistory, {
80
+ systemId: system.id,
81
+ })}
82
+ >
83
+ <History className="h-3 w-3 mr-1" />
84
+ History
85
+ </Link>
86
+ </Button>
87
+ </div>
131
88
  );
132
89
  }
133
90
 
134
- const getStatusBadge = (status: string) => {
135
- switch (status) {
136
- case "investigating": {
137
- return <Badge variant="destructive">Investigating</Badge>;
138
- }
139
- case "identified": {
140
- return <Badge variant="warning">Identified</Badge>;
141
- }
142
- case "fixing": {
143
- return <Badge variant="warning">Fixing</Badge>;
144
- }
145
- case "monitoring": {
146
- return <Badge variant="info">Monitoring</Badge>;
147
- }
148
- default: {
149
- return <Badge>{status}</Badge>;
150
- }
151
- }
152
- };
153
-
154
- // Use the most severe incident for the card styling
155
91
  const mostSevere = findMostSevereIncident(incidents);
92
+ const severityColor =
93
+ mostSevere.severity === "critical"
94
+ ? "border-destructive/30 bg-destructive/5"
95
+ : mostSevere.severity === "major"
96
+ ? "border-warning/30 bg-warning/5"
97
+ : "border-info/30 bg-info/5";
156
98
 
157
99
  return (
158
- <Card className={getSeverityColor(mostSevere.severity)}>
159
- <CardHeader
160
- className={`border-b border-border ${getSeverityHeaderColor(
161
- mostSevere.severity
162
- )}`}
163
- >
164
- <div className="flex items-center justify-between">
165
- <div className="flex items-center gap-2">
166
- <AlertTriangle className="h-5 w-5 text-destructive" />
167
- <CardTitle className="text-lg font-semibold">
168
- Active Incidents ({incidents.length})
169
- </CardTitle>
170
- </div>
171
- <Button variant="ghost" size="sm" asChild>
172
- <Link
173
- to={resolveRoute(incidentRoutes.routes.systemHistory, {
174
- systemId: system.id,
175
- })}
100
+ <div
101
+ className={`flex items-center justify-between rounded-md border px-3 py-2 ${severityColor}`}
102
+ >
103
+ <div className="flex items-center gap-2 min-w-0">
104
+ <AlertTriangle className="h-3.5 w-3.5 text-destructive shrink-0" />
105
+ <span className="text-sm font-medium truncate">
106
+ {incidents.length} active incident{incidents.length > 1 ? "s" : ""}
107
+ </span>
108
+ <div className="flex items-center gap-1.5">
109
+ {incidents.map((i) => (
110
+ <Badge
111
+ key={i.id}
112
+ variant={
113
+ i.severity === "critical"
114
+ ? "destructive"
115
+ : i.severity === "major"
116
+ ? "warning"
117
+ : "secondary"
118
+ }
119
+ className="text-xs"
176
120
  >
177
- <History className="h-4 w-4 mr-1" />
178
- View History
179
- </Link>
180
- </Button>
121
+ {i.severity}
122
+ </Badge>
123
+ ))}
181
124
  </div>
182
- </CardHeader>
183
- <CardContent className="p-4 space-y-3">
184
- {incidents.map((i) => (
185
- <Link
186
- key={i.id}
187
- to={`${resolveRoute(incidentRoutes.routes.detail, {
188
- incidentId: i.id,
189
- })}?from=${system.id}`}
190
- className="block p-3 rounded-lg border border-border bg-background hover:bg-muted/50 transition-colors"
191
- >
192
- <div className="flex items-start justify-between mb-2">
193
- <div className="flex items-center gap-2">
194
- <h4 className="font-medium text-foreground">{i.title}</h4>
195
- <ChevronRight className="h-4 w-4 text-muted-foreground" />
196
- </div>
197
- <div className="flex items-center gap-2">
198
- <Badge
199
- variant={
200
- i.severity === "critical"
201
- ? "destructive"
202
- : i.severity === "major"
203
- ? "warning"
204
- : "secondary"
205
- }
206
- >
207
- {i.severity}
208
- </Badge>
209
- {getStatusBadge(i.status)}
210
- </div>
211
- </div>
212
- {i.description && (
213
- <p className="text-sm text-muted-foreground mb-2">
214
- {i.description}
215
- </p>
216
- )}
217
- <div className="flex gap-4 text-xs text-muted-foreground">
218
- <div className="flex items-center gap-1">
219
- <Clock className="h-3 w-3" />
220
- <span>
221
- Started{" "}
222
- {formatDistanceToNow(new Date(i.createdAt), {
223
- addSuffix: true,
224
- })}
225
- </span>
226
- </div>
227
- </div>
228
- </Link>
229
- ))}
230
- </CardContent>
231
- </Card>
125
+ </div>
126
+ <Button variant="ghost" size="sm" className="h-7 text-xs shrink-0" asChild>
127
+ <Link
128
+ to={resolveRoute(incidentRoutes.routes.systemHistory, {
129
+ systemId: system.id,
130
+ })}
131
+ >
132
+ <History className="h-3 w-3 mr-1" />
133
+ View
134
+ </Link>
135
+ </Button>
136
+ </div>
232
137
  );
233
138
  };