mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-24 02:29:35 -06:00
Fix: change height and witdth and Merge branch 'develop' into fix/placeholder-center-align
This commit is contained in:
@@ -88,14 +88,15 @@ const Fallback = ({
|
||||
display: "flex",
|
||||
borderStyle: "dashed",
|
||||
height: {
|
||||
xs: theme.spacing(200),
|
||||
sm: theme.spacing(300),
|
||||
md: theme.spacing(300),
|
||||
sm: "50vh",
|
||||
md: "70vh",
|
||||
lg: "60vh",
|
||||
xl: "55vh",
|
||||
},
|
||||
width: {
|
||||
xs: theme.spacing(100),
|
||||
sm: theme.spacing(210),
|
||||
md: theme.spacing(250),
|
||||
sm: "90%",
|
||||
md: "50%",
|
||||
lg: "40%",
|
||||
},
|
||||
padding: theme.spacing(10),
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { networkService } from "../main";
|
||||
import { createToast } from "../Utils/toastUtils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const useFetchChecksTeam = ({
|
||||
status,
|
||||
@@ -12,6 +13,7 @@ const useFetchChecksTeam = ({
|
||||
page,
|
||||
rowsPerPage,
|
||||
enabled = true,
|
||||
updateTrigger,
|
||||
}) => {
|
||||
const [checks, setChecks] = useState(undefined);
|
||||
const [checksCount, setChecksCount] = useState(undefined);
|
||||
@@ -49,7 +51,18 @@ const useFetchChecksTeam = ({
|
||||
};
|
||||
|
||||
fetchChecks();
|
||||
}, [status, sortOrder, limit, dateRange, filter, ack, page, rowsPerPage, enabled]);
|
||||
}, [
|
||||
status,
|
||||
sortOrder,
|
||||
limit,
|
||||
dateRange,
|
||||
filter,
|
||||
ack,
|
||||
page,
|
||||
rowsPerPage,
|
||||
enabled,
|
||||
updateTrigger,
|
||||
]);
|
||||
|
||||
return [checks, checksCount, isLoading, networkError];
|
||||
};
|
||||
@@ -66,6 +79,7 @@ const useFetchChecksByMonitor = ({
|
||||
page,
|
||||
rowsPerPage,
|
||||
enabled = true,
|
||||
updateTrigger,
|
||||
}) => {
|
||||
const [checks, setChecks] = useState(undefined);
|
||||
const [checksCount, setChecksCount] = useState(undefined);
|
||||
@@ -117,12 +131,13 @@ const useFetchChecksByMonitor = ({
|
||||
page,
|
||||
rowsPerPage,
|
||||
enabled,
|
||||
updateTrigger,
|
||||
]);
|
||||
|
||||
return [checks, checksCount, isLoading, networkError];
|
||||
};
|
||||
|
||||
const useFetchChecksSummaryByTeamId = () => {
|
||||
const useFetchChecksSummaryByTeamId = ({ updateTrigger } = {}) => {
|
||||
const [summary, setSummary] = useState(undefined);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [networkError, setNetworkError] = useState(false);
|
||||
@@ -143,9 +158,56 @@ const useFetchChecksSummaryByTeamId = () => {
|
||||
};
|
||||
|
||||
fetchSummary();
|
||||
}, []);
|
||||
}, [updateTrigger]);
|
||||
|
||||
return [summary, isLoading, networkError];
|
||||
};
|
||||
|
||||
export { useFetchChecksByMonitor, useFetchChecksTeam, useFetchChecksSummaryByTeamId };
|
||||
const useResolveIncident = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const resolveIncident = async (checkId, setUpdateTrigger) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await networkService.updateCheckStatus({
|
||||
checkId,
|
||||
ack: true,
|
||||
});
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
} catch (error) {
|
||||
createToast({ body: t("checkHooks.failureResolveOne") });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [resolveIncident, isLoading];
|
||||
};
|
||||
|
||||
const useAckAllChecks = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const ackAllChecks = async (setUpdateTrigger) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await networkService.updateAllChecksStatus({ ack: true });
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
} catch (error) {
|
||||
createToast({ body: t("checkHooks.failureResolveAll") });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [ackAllChecks, isLoading];
|
||||
};
|
||||
|
||||
export {
|
||||
useFetchChecksByMonitor,
|
||||
useFetchChecksTeam,
|
||||
useFetchChecksSummaryByTeamId,
|
||||
useResolveIncident,
|
||||
useAckAllChecks,
|
||||
};
|
||||
|
||||
@@ -15,13 +15,17 @@ import PropTypes from "prop-types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useFetchChecksTeam } from "../../../../Hooks/checkHooks";
|
||||
import { useFetchChecksByMonitor } from "../../../../Hooks/checkHooks";
|
||||
import { useResolveIncident } from "../../../../Hooks/checkHooks";
|
||||
import { Button, Typography } from "@mui/material";
|
||||
|
||||
const IncidentTable = ({
|
||||
shouldRender,
|
||||
isLoading,
|
||||
monitors,
|
||||
selectedMonitor,
|
||||
filter,
|
||||
dateRange,
|
||||
updateTrigger,
|
||||
setUpdateTrigger,
|
||||
}) => {
|
||||
//Redux state
|
||||
const uiTimezone = useSelector((state) => state.ui.timezone);
|
||||
@@ -32,6 +36,9 @@ const IncidentTable = ({
|
||||
const selectedMonitorDetails = monitors?.[selectedMonitor];
|
||||
const selectedMonitorType = selectedMonitorDetails?.type;
|
||||
|
||||
//Hooks
|
||||
const [resolveIncident, resolveLoading] = useResolveIncident();
|
||||
|
||||
const [checksMonitor, checksCountMonitor, isLoadingMonitor, networkErrorMonitor] =
|
||||
useFetchChecksByMonitor({
|
||||
monitorId: selectedMonitor === "0" ? undefined : selectedMonitor,
|
||||
@@ -45,6 +52,7 @@ const IncidentTable = ({
|
||||
page: page,
|
||||
rowsPerPage: rowsPerPage,
|
||||
enabled: selectedMonitor !== "0",
|
||||
updateTrigger,
|
||||
});
|
||||
|
||||
const [checksTeam, checksCountTeam, isLoadingTeam, networkErrorTeam] =
|
||||
@@ -58,11 +66,12 @@ const IncidentTable = ({
|
||||
page: page,
|
||||
rowsPerPage: rowsPerPage,
|
||||
enabled: selectedMonitor === "0",
|
||||
updateTrigger,
|
||||
});
|
||||
|
||||
const checks = selectedMonitor === "0" ? checksTeam : checksMonitor;
|
||||
const checksCount = selectedMonitor === "0" ? checksCountTeam : checksCountMonitor;
|
||||
const isLoading = isLoadingTeam || isLoadingMonitor;
|
||||
isLoading = isLoadingTeam || isLoadingMonitor;
|
||||
const networkError = selectedMonitor === "0" ? networkErrorTeam : networkErrorMonitor;
|
||||
|
||||
const { t } = useTranslation();
|
||||
@@ -76,6 +85,10 @@ const IncidentTable = ({
|
||||
setRowsPerPage(event.target.value);
|
||||
};
|
||||
|
||||
const handleResolveIncident = (checkId) => {
|
||||
resolveIncident(checkId, setUpdateTrigger);
|
||||
};
|
||||
|
||||
const headers = [
|
||||
{
|
||||
id: "monitorName",
|
||||
@@ -114,9 +127,31 @@ const IncidentTable = ({
|
||||
render: (row) => <HttpStatusLabel status={row.statusCode} />,
|
||||
},
|
||||
{ id: "message", content: t("incidentsTableMessage"), render: (row) => row.message },
|
||||
{
|
||||
id: "action",
|
||||
content: t("actions"),
|
||||
render: (row) => {
|
||||
return row.ack === false ? (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
onClick={() => {
|
||||
handleResolveIncident(row._id);
|
||||
}}
|
||||
>
|
||||
{t("incidentsTableActionResolve")}
|
||||
</Button>
|
||||
) : (
|
||||
<Typography>
|
||||
{t("incidentsTableResolvedAt")}{" "}
|
||||
{formatDateWithTz(row.ackAt, "YYYY-MM-DD HH:mm:ss A", uiTimezone)}
|
||||
</Typography>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (!shouldRender || isLoading) return <TableSkeleton />;
|
||||
if (isLoading || resolveLoading) return <TableSkeleton />;
|
||||
|
||||
if (networkError) {
|
||||
return (
|
||||
@@ -149,10 +184,12 @@ const IncidentTable = ({
|
||||
};
|
||||
|
||||
IncidentTable.propTypes = {
|
||||
shouldRender: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
monitors: PropTypes.object,
|
||||
selectedMonitor: PropTypes.string,
|
||||
filter: PropTypes.string,
|
||||
dateRange: PropTypes.string,
|
||||
updateTrigger: PropTypes.bool,
|
||||
setUpdateTrigger: PropTypes.func,
|
||||
};
|
||||
export default IncidentTable;
|
||||
|
||||
@@ -21,6 +21,12 @@ const OptionsHeader = ({
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const monitorNames = typeof monitors !== "undefined" ? Object.values(monitors) : [];
|
||||
const filterOptions = [
|
||||
{ _id: "all", name: t("incidentsOptionsHeaderFilterAll") },
|
||||
{ _id: "down", name: t("incidentsOptionsHeaderFilterDown") },
|
||||
{ _id: "resolve", name: t("incidentsOptionsHeaderFilterCannotResolve") },
|
||||
{ _id: "resolved", name: t("incidentsOptionsHeaderFilterResolved") },
|
||||
];
|
||||
|
||||
// The stacks below which are three in number have the same style so
|
||||
const stackStyles = {
|
||||
@@ -65,36 +71,16 @@ const OptionsHeader = ({
|
||||
>
|
||||
{t("incidentsOptionsHeaderFilterBy")}
|
||||
</Typography>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(filter === "all").toString()}
|
||||
onClick={() => setFilter("all")}
|
||||
>
|
||||
{t("incidentsOptionsHeaderFilterAll")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(filter === "down").toString()}
|
||||
onClick={() => setFilter("down")}
|
||||
>
|
||||
{t("incidentsOptionsHeaderFilterDown")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(filter === "resolve").toString()}
|
||||
onClick={() => setFilter("resolve")}
|
||||
>
|
||||
{t("incidentsOptionsHeaderFilterCannotResolve")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(filter === "resolved").toString()}
|
||||
onClick={() => setFilter("resolved")}
|
||||
>
|
||||
{t("incidentsOptionsHeaderFilterResolved")}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<Select
|
||||
id="incidents-select-filter"
|
||||
value={filter}
|
||||
onChange={(e) => setFilter(e.target.value)}
|
||||
items={filterOptions}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastTextSecondary,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack {...stackStyles}>
|
||||
<Typography
|
||||
|
||||
@@ -5,11 +5,13 @@ import GenericFallback from "../../Components/GenericFallback";
|
||||
import IncidentTable from "./Components/IncidentTable";
|
||||
import OptionsHeader from "./Components/OptionsHeader";
|
||||
import StatusBoxes from "./Components/StatusBoxes";
|
||||
import { Box, Button } from "@mui/material";
|
||||
|
||||
//Utils
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useFetchMonitorsByTeamId } from "../../Hooks/monitorHooks";
|
||||
import { useFetchChecksSummaryByTeamId } from "../../Hooks/checkHooks";
|
||||
import { useAckAllChecks } from "../../Hooks/checkHooks";
|
||||
import { useState, useEffect } from "react";
|
||||
import NetworkError from "../../Components/GenericFallback/NetworkError";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -29,12 +31,17 @@ const Incidents = () => {
|
||||
const [filter, setFilter] = useState(undefined);
|
||||
const [dateRange, setDateRange] = useState(undefined);
|
||||
const [monitorLookup, setMonitorLookup] = useState(undefined);
|
||||
const [updateTrigger, setUpdateTrigger] = useState(false);
|
||||
|
||||
//Hooks
|
||||
const [ackAllChecks, ackAllLoading] = useAckAllChecks();
|
||||
|
||||
//Utils
|
||||
const theme = useTheme();
|
||||
const [monitors, , isLoading, networkError] = useFetchMonitorsByTeamId({});
|
||||
const [summary, isLoadingSummary, networkErrorSummary] =
|
||||
useFetchChecksSummaryByTeamId();
|
||||
const [summary, isLoadingSummary, networkErrorSummary] = useFetchChecksSummaryByTeamId({
|
||||
updateTrigger,
|
||||
});
|
||||
const { monitorId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -55,6 +62,10 @@ const Incidents = () => {
|
||||
setMonitorLookup(monitorLookup);
|
||||
}, [monitors]);
|
||||
|
||||
const handleAckAllChecks = () => {
|
||||
ackAllChecks(setUpdateTrigger);
|
||||
};
|
||||
|
||||
if (networkError || networkErrorSummary) {
|
||||
return (
|
||||
<GenericFallback>
|
||||
@@ -66,6 +77,16 @@ const Incidents = () => {
|
||||
return (
|
||||
<Stack gap={theme.spacing(10)}>
|
||||
<Breadcrumbs list={BREADCRUMBS} />
|
||||
<Box alignSelf="flex-end">
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
onClick={handleAckAllChecks}
|
||||
disabled={ackAllLoading}
|
||||
>
|
||||
{t("incidentsPageActionResolve")}
|
||||
</Button>
|
||||
</Box>
|
||||
<StatusBoxes
|
||||
isLoading={isLoadingSummary}
|
||||
summary={summary}
|
||||
@@ -81,11 +102,13 @@ const Incidents = () => {
|
||||
setDateRange={setDateRange}
|
||||
/>
|
||||
<IncidentTable
|
||||
shouldRender={!isLoading}
|
||||
isLoading={isLoading}
|
||||
monitors={monitorLookup ? monitorLookup : {}}
|
||||
selectedMonitor={selectedMonitor}
|
||||
filter={filter}
|
||||
dateRange={dateRange}
|
||||
updateTrigger={updateTrigger}
|
||||
setUpdateTrigger={setUpdateTrigger}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -631,6 +631,12 @@ class NetworkService {
|
||||
});
|
||||
}
|
||||
|
||||
async updateAllChecksStatus(config) {
|
||||
return this.axiosInstance.put(`/checks/team/`, {
|
||||
ack: config.ack,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* Get all checks for a given user
|
||||
|
||||
@@ -390,7 +390,10 @@
|
||||
"incidentsOptionsHeaderFilterBy": "Filter by:",
|
||||
"incidentsOptionsHeaderTotalIncidents": "Total Incidents",
|
||||
"incidentsOptionsHeaderFilterCannotResolve": "Cannot Resolve",
|
||||
"incidentsTableResolvedAt": "Resolved at",
|
||||
"incidentsOptionsHeaderFilterDown": "Down",
|
||||
"incidentsTableActionResolve": "Resolve",
|
||||
"incidentsPageActionResolve": "Resolve all incidents",
|
||||
"incidentsOptionsHeaderFilterResolved": "Resolved",
|
||||
"incidentsOptionsHeaderLastDay": "Last day",
|
||||
"incidentsOptionsHeaderLastHour": "Last hour",
|
||||
@@ -513,6 +516,10 @@
|
||||
"monitorStatusUp": "Monitor {name} ({url}) is now UP and responding",
|
||||
"monitors": "monitors",
|
||||
"monitorsToApply": "Monitors to apply maintenance window to",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "Failed to resolve incident.",
|
||||
"failureResolveAll": "Failed to resolve all incidents."
|
||||
},
|
||||
"ms": "ms",
|
||||
"navControls": "Controls",
|
||||
"nextWindow": "Next window",
|
||||
|
||||
@@ -10,7 +10,11 @@ const buildChecksSummaryByTeamIdPipeline = ({ matchStage }) => {
|
||||
totalChecks: { $sum: { $cond: [{ $eq: ["$status", false] }, 1, 0] } },
|
||||
resolvedChecks: {
|
||||
$sum: {
|
||||
$cond: [{ $eq: ["$ack", true] }, 1, 0],
|
||||
$cond: [
|
||||
{ $and: [{ $eq: ["$ack", true] }, { $eq: ["$status", false] }] },
|
||||
1,
|
||||
0,
|
||||
],
|
||||
},
|
||||
},
|
||||
downChecks: {
|
||||
|
||||
Reference in New Issue
Block a user