@checkstack/incident-frontend 0.3.10 → 0.4.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,50 @@
1
1
  # @checkstack/incident-frontend
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [090143b]
8
+ - @checkstack/ui@0.5.1
9
+ - @checkstack/dashboard-frontend@0.3.11
10
+ - @checkstack/auth-frontend@0.5.6
11
+
12
+ ## 0.4.0
13
+
14
+ ### Minor Changes
15
+
16
+ - cce5453: Add notification suppression for incidents
17
+
18
+ - Added `suppressNotifications` field to incidents, allowing active incidents to optionally suppress health check notifications
19
+ - When enabled, health status change notifications will not be sent for affected systems while the incident is active (not resolved)
20
+ - Mirrors the existing maintenance notification suppression pattern
21
+ - Added toggle UI in the IncidentEditor dialog
22
+ - Added `hasActiveIncidentWithSuppression` RPC endpoint for service-to-service queries
23
+
24
+ ### Patch Changes
25
+
26
+ - 223081d: Add icon support to PageLayout and improve mobile responsiveness
27
+
28
+ **PageLayout Icons:**
29
+
30
+ - Added required `icon` prop to `PageLayout` and `PageHeader` components that accepts a Lucide icon component reference
31
+ - Icons are rendered with consistent `h-6 w-6 text-primary` styling
32
+ - Updated all page components to include appropriate icons in their headers
33
+
34
+ **Mobile Layout Improvements:**
35
+
36
+ - Standardized responsive padding in main app shell (`p-3` on mobile, `p-6` on desktop)
37
+ - Added `CardHeaderRow` component for mobile-safe card headers with proper wrapping
38
+ - Improved `DateRangeFilter` responsive behavior with vertical stacking on mobile
39
+ - Migrated pages to use `PageLayout` for consistent responsive behavior
40
+
41
+ - Updated dependencies [cce5453]
42
+ - Updated dependencies [223081d]
43
+ - @checkstack/incident-common@0.4.0
44
+ - @checkstack/ui@0.5.0
45
+ - @checkstack/auth-frontend@0.5.5
46
+ - @checkstack/dashboard-frontend@0.3.10
47
+
3
48
  ## 0.3.10
4
49
 
5
50
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/incident-frontend",
3
- "version": "0.3.10",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "scripts": {
@@ -57,6 +57,7 @@ export const IncidentEditor: React.FC<Props> = ({
57
57
  const [selectedSystemIds, setSelectedSystemIds] = useState<Set<string>>(
58
58
  new Set(),
59
59
  );
60
+ const [suppressNotifications, setSuppressNotifications] = useState(false);
60
61
 
61
62
  // Status update fields
62
63
  const [updates, setUpdates] = useState<IncidentUpdate[]>([]);
@@ -105,11 +106,13 @@ export const IncidentEditor: React.FC<Props> = ({
105
106
  setDescription(incident.description ?? "");
106
107
  setSeverity(incident.severity);
107
108
  setSelectedSystemIds(new Set(incident.systemIds));
109
+ setSuppressNotifications(incident.suppressNotifications);
108
110
  } else {
109
111
  setTitle("");
110
112
  setDescription("");
111
113
  setSeverity("major");
112
114
  setSelectedSystemIds(new Set());
115
+ setSuppressNotifications(false);
113
116
  setUpdates([]);
114
117
  setShowUpdateForm(false);
115
118
  }
@@ -143,6 +146,7 @@ export const IncidentEditor: React.FC<Props> = ({
143
146
  title,
144
147
  description: description || undefined,
145
148
  severity,
149
+ suppressNotifications,
146
150
  systemIds: [...selectedSystemIds],
147
151
  });
148
152
  } else {
@@ -150,6 +154,7 @@ export const IncidentEditor: React.FC<Props> = ({
150
154
  title,
151
155
  description,
152
156
  severity,
157
+ suppressNotifications,
153
158
  systemIds: [...selectedSystemIds],
154
159
  });
155
160
  }
@@ -253,6 +258,31 @@ export const IncidentEditor: React.FC<Props> = ({
253
258
  {selectedSystemIds.size} system(s) selected
254
259
  </p>
255
260
  </div>
261
+
262
+ {/* Notification Suppression Toggle */}
263
+ <div className="border rounded-md p-4 bg-muted/30">
264
+ <div
265
+ className="flex items-center gap-3 cursor-pointer"
266
+ onClick={() => setSuppressNotifications(!suppressNotifications)}
267
+ >
268
+ <Checkbox
269
+ id="suppress-notifications"
270
+ checked={suppressNotifications}
271
+ />
272
+ <div className="flex-1">
273
+ <Label
274
+ htmlFor="suppress-notifications"
275
+ className="cursor-pointer font-medium"
276
+ >
277
+ Suppress health notifications
278
+ </Label>
279
+ <p className="text-xs text-muted-foreground mt-1">
280
+ When enabled, health status change notifications will not be
281
+ sent for affected systems while this incident is active.
282
+ </p>
283
+ </div>
284
+ </div>
285
+ </div>
256
286
  </div>
257
287
 
258
288
  {/* Status Updates Section - Only show when editing */}
@@ -56,11 +56,11 @@ const IncidentConfigPageContent: React.FC = () => {
56
56
  const toast = useToast();
57
57
 
58
58
  const { allowed: canManage, loading: accessLoading } = accessApi.useAccess(
59
- incidentAccess.incident.manage
59
+ incidentAccess.incident.manage,
60
60
  );
61
61
 
62
62
  const [statusFilter, setStatusFilter] = useState<IncidentStatus | "all">(
63
- "all"
63
+ "all",
64
64
  );
65
65
  const [showResolved, setShowResolved] = useState(false);
66
66
 
@@ -84,7 +84,7 @@ const IncidentConfigPageContent: React.FC = () => {
84
84
  } = incidentClient.listIncidents.useQuery(
85
85
  statusFilter === "all"
86
86
  ? { includeResolved: showResolved }
87
- : { status: statusFilter, includeResolved: showResolved }
87
+ : { status: statusFilter, includeResolved: showResolved },
88
88
  );
89
89
 
90
90
  // Fetch systems with useQuery
@@ -205,6 +205,7 @@ const IncidentConfigPageContent: React.FC = () => {
205
205
  <PageLayout
206
206
  title="Incident Management"
207
207
  subtitle="Track and manage incidents affecting your systems"
208
+ icon={AlertTriangle}
208
209
  loading={accessLoading}
209
210
  allowed={canManage}
210
211
  actions={
@@ -18,6 +18,7 @@ import { CatalogApi } from "@checkstack/catalog-common";
18
18
  import {
19
19
  Card,
20
20
  CardHeader,
21
+ CardHeaderRow,
21
22
  CardTitle,
22
23
  CardContent,
23
24
  Button,
@@ -54,7 +55,7 @@ const IncidentDetailPageContent: React.FC = () => {
54
55
  const toast = useToast();
55
56
 
56
57
  const { allowed: canManage } = accessApi.useAccess(
57
- incidentAccess.incident.manage
58
+ incidentAccess.incident.manage,
58
59
  );
59
60
 
60
61
  const [showUpdateForm, setShowUpdateForm] = useState(false);
@@ -66,7 +67,7 @@ const IncidentDetailPageContent: React.FC = () => {
66
67
  refetch: refetchIncident,
67
68
  } = incidentClient.getIncident.useQuery(
68
69
  { id: incidentId ?? "" },
69
- { enabled: !!incidentId }
70
+ { enabled: !!incidentId },
70
71
  );
71
72
 
72
73
  // Fetch systems with useQuery
@@ -138,6 +139,7 @@ const IncidentDetailPageContent: React.FC = () => {
138
139
  <PageLayout
139
140
  title={incident.title}
140
141
  subtitle="Incident details and status history"
142
+ icon={AlertTriangle}
141
143
  loading={false}
142
144
  allowed={true}
143
145
  actions={
@@ -147,7 +149,7 @@ const IncidentDetailPageContent: React.FC = () => {
147
149
  navigate(
148
150
  resolveRoute(incidentRoutes.routes.systemHistory, {
149
151
  systemId: sourceSystemId,
150
- })
152
+ }),
151
153
  )
152
154
  }
153
155
  >
@@ -160,12 +162,12 @@ const IncidentDetailPageContent: React.FC = () => {
160
162
  {/* Incident Info Card */}
161
163
  <Card>
162
164
  <CardHeader className="border-b border-border">
163
- <div className="flex items-center justify-between">
165
+ <CardHeaderRow>
164
166
  <div className="flex items-center gap-2">
165
167
  <AlertTriangle className="h-5 w-5 text-muted-foreground" />
166
168
  <CardTitle>Incident Details</CardTitle>
167
169
  </div>
168
- <div className="flex items-center gap-2">
170
+ <div className="flex flex-wrap items-center gap-2">
169
171
  {getIncidentSeverityBadge(incident.severity)}
170
172
  {getIncidentStatusBadge(incident.status)}
171
173
  {canResolve && (
@@ -180,7 +182,7 @@ const IncidentDetailPageContent: React.FC = () => {
180
182
  </Button>
181
183
  )}
182
184
  </div>
183
- </div>
185
+ </CardHeaderRow>
184
186
  </CardHeader>
185
187
  <CardContent className="p-6 space-y-4">
186
188
  {incident.description && (
@@ -236,7 +238,7 @@ const IncidentDetailPageContent: React.FC = () => {
236
238
  {/* Status Updates Timeline */}
237
239
  <Card>
238
240
  <CardHeader className="border-b border-border">
239
- <div className="flex items-center justify-between">
241
+ <CardHeaderRow>
240
242
  <div className="flex items-center gap-2">
241
243
  <MessageSquare className="h-5 w-5 text-muted-foreground" />
242
244
  <CardTitle>Status Updates</CardTitle>
@@ -251,7 +253,7 @@ const IncidentDetailPageContent: React.FC = () => {
251
253
  Add Update
252
254
  </Button>
253
255
  )}
254
- </div>
256
+ </CardHeaderRow>
255
257
  </CardHeader>
256
258
  <CardContent className="p-6">
257
259
  {/* Add Update Form */}
@@ -12,15 +12,13 @@ import {
12
12
  import { CatalogApi, catalogRoutes } from "@checkstack/catalog-common";
13
13
  import {
14
14
  Card,
15
- CardHeader,
16
- CardTitle,
17
15
  CardContent,
18
16
  Badge,
19
- LoadingSpinner,
17
+ PageLayout,
20
18
  EmptyState,
21
19
  BackLink,
22
20
  } from "@checkstack/ui";
23
- import { AlertTriangle, Clock, ChevronRight } from "lucide-react";
21
+ import { Clock, ChevronRight, AlertTriangle } from "lucide-react";
24
22
  import { formatDistanceToNow } from "date-fns";
25
23
 
26
24
  const SystemIncidentHistoryPageContent: React.FC = () => {
@@ -35,7 +33,7 @@ const SystemIncidentHistoryPageContent: React.FC = () => {
35
33
  refetch: refetchIncidents,
36
34
  } = incidentClient.listIncidents.useQuery(
37
35
  { systemId, includeResolved: true },
38
- { enabled: !!systemId }
36
+ { enabled: !!systemId },
39
37
  );
40
38
 
41
39
  // Fetch systems with useQuery
@@ -91,36 +89,25 @@ const SystemIncidentHistoryPageContent: React.FC = () => {
91
89
  }
92
90
  };
93
91
 
94
- if (loading) {
95
- return (
96
- <div className="p-12 flex justify-center">
97
- <LoadingSpinner />
98
- </div>
99
- );
100
- }
92
+ // Actions for the page header
93
+ const headerActions = system && (
94
+ <BackLink
95
+ to={resolveRoute(catalogRoutes.routes.systemDetail, {
96
+ systemId: system.id,
97
+ })}
98
+ >
99
+ Back to {system.name}
100
+ </BackLink>
101
+ );
101
102
 
102
103
  return (
103
- <div className="space-y-6 p-6">
104
+ <PageLayout
105
+ title={`Incident History${system ? ` - ${system.name}` : ""}`}
106
+ icon={AlertTriangle}
107
+ loading={loading}
108
+ actions={headerActions}
109
+ >
104
110
  <Card>
105
- <CardHeader className="border-b border-border">
106
- <div className="flex items-center justify-between">
107
- <div className="flex items-center gap-2">
108
- <AlertTriangle className="h-5 w-5 text-muted-foreground" />
109
- <CardTitle>
110
- Incident History{system ? ` - ${system.name}` : ""}
111
- </CardTitle>
112
- </div>
113
- {system && (
114
- <BackLink
115
- to={resolveRoute(catalogRoutes.routes.systemDetail, {
116
- systemId: system.id,
117
- })}
118
- >
119
- Back to {system.name}
120
- </BackLink>
121
- )}
122
- </div>
123
- </CardHeader>
124
111
  <CardContent className="p-0">
125
112
  {incidents.length === 0 ? (
126
113
  <EmptyState
@@ -172,10 +159,10 @@ const SystemIncidentHistoryPageContent: React.FC = () => {
172
159
  )}
173
160
  </CardContent>
174
161
  </Card>
175
- </div>
162
+ </PageLayout>
176
163
  );
177
164
  };
178
165
 
179
166
  export const SystemIncidentHistoryPage = wrapInSuspense(
180
- SystemIncidentHistoryPageContent
167
+ SystemIncidentHistoryPageContent,
181
168
  );