Skip to content

Commit 3875586

Browse files
committed
added get monitor incidents subpage
1 parent d6a1eef commit 3875586

File tree

6 files changed

+206
-2
lines changed

6 files changed

+206
-2
lines changed

apps/dashboard/src/api/endpoints/incidents/incidents.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface Incident {
1111
title: string;
1212
description: string;
1313
createdAt: string;
14+
updatedAt: string;
1415
}
1516

1617
/*
@@ -37,3 +38,22 @@ export async function getIncidents(
3738

3839
return response?.data;
3940
}
41+
42+
43+
export async function getMonitorIncidents(
44+
teamId: number,
45+
monitorId: number,
46+
offset?: number,
47+
limit?: number,
48+
): Promise<GetIncidentsResponse> {
49+
const response = await client.get(`/v1/teams/${teamId}/incidents/monitor/${monitorId}`,
50+
{
51+
params: {
52+
offset,
53+
limit,
54+
},
55+
},
56+
);
57+
58+
return response?.data;
59+
}

apps/dashboard/src/hooks/incidents.query.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const queryClient = getQueryClient();
99
export const useIncidents = (offset = 0, limit = 5) => {
1010
const teamId = useAuthenticationStore((state) => state.currentTeamId);
1111

12-
return useQuery(["team", teamId, "checks",
12+
return useQuery(["team", teamId, "incidents",
1313
{
1414
offset,
1515
limit,
@@ -23,3 +23,20 @@ export const useIncidents = (offset = 0, limit = 5) => {
2323
});
2424
};
2525

26+
export const useMonitorIncidents = (monitorId: number, offset = 0, limit = 5) => {
27+
const teamId = useAuthenticationStore((state) => state.currentTeamId);
28+
29+
return useQuery(["team", teamId, "monitor", monitorId, "incidents",
30+
{
31+
offset,
32+
limit,
33+
},
34+
], () => {
35+
if (!teamId) {
36+
return Promise.resolve(null);
37+
}
38+
39+
return IncidentsAPI.getMonitorIncidents(teamId, monitorId, offset, limit);
40+
});
41+
};
42+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { FunctionComponent } from "react";
2+
import { useMonitorIncidents } from "../../../../hooks/incidents.query";
3+
import { Helmet } from "react-helmet";
4+
import { Link, NavLink, useParams } from "react-router-dom";
5+
import { useMonitor } from "../../../../hooks/monitors.query";
6+
interface IncidentsWindowDetailViewProps {}
7+
import Container from "../../../../components/Container";
8+
import { Button, Skeleton } from "@mui/material";
9+
import Placeholder from "../../../../components/Placeholder";
10+
import IncidentsList from "../components/IncidentsList";
11+
12+
const IncidentsWindowDetailView: FunctionComponent<
13+
IncidentsWindowDetailViewProps
14+
> = () => {
15+
let params = useParams();
16+
const monitorId = (params.id as number | undefined) || 0;
17+
18+
const { data, error, isLoading } = useMonitor(monitorId);
19+
20+
// Get incidents
21+
const {
22+
data: monitorIncidents,
23+
error: incidentsError,
24+
isLoading: incidentsAreLoading,
25+
} = useMonitorIncidents(monitorId);
26+
return (
27+
<>
28+
<Helmet>
29+
<title>{isLoading ? "Monitors" : `Monitors | ${data?.name}`}</title>
30+
</Helmet>
31+
32+
<Container
33+
breadcrumbs={[
34+
<Link to="/incidents">Incidents</Link>,
35+
isLoading ? (
36+
<Skeleton variant="text" width={150} />
37+
) : (
38+
<span>{data?.name}</span>
39+
),
40+
]}
41+
>
42+
{incidentsAreLoading ? (
43+
<Placeholder />
44+
) : (
45+
<IncidentsList incidents={monitorIncidents?.incidents} />
46+
)}
47+
</Container>
48+
</>
49+
);
50+
};
51+
52+
export default IncidentsWindowDetailView;
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {
2+
Box,
3+
Button,
4+
Card,
5+
Chip,
6+
Grid,
7+
Stack,
8+
Tooltip,
9+
Typography,
10+
} from "@mui/material";
11+
import { FunctionComponent } from "react";
12+
import { IoOpenOutline, IoStatsChart } from "react-icons/io5";
13+
import { useNavigate } from "react-router-dom";
14+
import { Incident } from "../../../../../src/api/endpoints/incidents";
15+
16+
interface MonitorIncidentPageIncidentsListProps {
17+
incidents?: Incident[];
18+
}
19+
20+
const IncidentPageIncidentsList: FunctionComponent<
21+
MonitorIncidentPageIncidentsListProps
22+
> = ({
23+
incidents
24+
}) => {
25+
return (
26+
<Stack spacing={2}>
27+
{incidents?.map((incident) => (
28+
<IncidentsListItem incident={incident} />
29+
))}
30+
</Stack>
31+
);
32+
};
33+
34+
interface IncidentsListItemProps {
35+
incident: Incident;
36+
}
37+
38+
const IncidentsListItem: FunctionComponent<IncidentsListItemProps> = (
39+
{
40+
incident,
41+
}
42+
) => {
43+
const navigate = useNavigate();
44+
45+
return (
46+
<Stack direction="row" spacing={0.5}>
47+
<Card
48+
component={Button}
49+
sx={{
50+
display: "block",
51+
textAlign: "left",
52+
padding: 0,
53+
flex: 1,
54+
}}
55+
>
56+
<Stack direction="row">
57+
<Grid
58+
container
59+
alignItems="center"
60+
p={2}
61+
columns={{ xs: 1, md: 2 }}
62+
justifyContent="space-between"
63+
gap={{ xs: 2, md: 4 }}
64+
onClick={() => navigate(`/incidents/${incident.id}`)} // Navigate to the incident page
65+
>
66+
<Grid item>
67+
<Stack spacing={1}>
68+
<Typography variant="body2">{incident.title}</Typography>
69+
70+
<Stack direction="row" spacing={1}>
71+
<Chip size="small" label="api" color="info" />
72+
<Chip size="small" label="login" color="info" />
73+
<Chip size="small" label="monitors" color="info" />
74+
<Chip size="small" label="foo" color="info" />
75+
<Chip size="small" label="bar" color="info" />
76+
</Stack>
77+
</Stack>
78+
</Grid>
79+
80+
<Grid
81+
item
82+
direction="row"
83+
display="flex"
84+
justifyContent="flex-end"
85+
alignItems="center"
86+
gap={2}
87+
>
88+
<Stack textAlign={{ xs: "left", md: "right" }}>
89+
<Typography variant="body2" color="text.secondary">
90+
first occurrence {incident.createdAt}
91+
</Typography>
92+
<Typography variant="body2" color="text.secondary">
93+
latest occurrence {incident.updatedAt}
94+
</Typography>
95+
</Stack>
96+
97+
<Box sx={{ display: { xs: "none", md: "block" } }}>
98+
<IoStatsChart size={38} />
99+
</Box>
100+
</Grid>
101+
</Grid>
102+
</Stack>
103+
</Card>
104+
105+
<Tooltip title="Open in a new tab">
106+
<Card component={Button} color="primary">
107+
<IoOpenOutline size={18} />
108+
</Card>
109+
</Tooltip>
110+
</Stack>
111+
);
112+
};
113+
114+
export default IncidentPageIncidentsList;

apps/dashboard/src/pages/Dashboard/Incidents/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Helmet } from "react-helmet";
33
import Container from "../../../components/Container";
44
import Placeholder from "../../../components/Placeholder";
55
import IncidentOverviewList from "./components/OverviewList";
6-
// import { useIncidents } from "../../../hooks/incidents.query";
76
import { useMonitorsIncidents } from "../../../hooks/monitors.query";
87

98

apps/dashboard/src/routes/routes.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ const MaintenanceDetailView = lazy(
7777
);
7878

7979
const IncidentsView = lazy(() => import("../pages/Dashboard/Incidents"));
80+
const IncidentsDetailView = lazy(() => import("../pages/Dashboard/Incidents/Detail"));
8081

8182
const AlertingView = lazy(() => import("../pages/Dashboard/Alerting"));
8283

@@ -157,6 +158,7 @@ const Routes: FunctionComponent = () => {
157158
{/* Incidents */}
158159
<Route path="incidents">
159160
<Route index path="" element={<IncidentsView />} />
161+
<Route path=":id" element={<IncidentsDetailView />} />
160162
</Route>
161163

162164
{/* Maintenance */}

0 commit comments

Comments
 (0)