From d9ccc72a38acb6a92828d7a97bb1abbff0ba8974 Mon Sep 17 00:00:00 2001 From: Br0wnHammer Date: Thu, 3 Jul 2025 02:15:37 +0530 Subject: [PATCH 1/4] Feat: Resolve Incidents All and One --- client/src/Hooks/checkHooks.js | 20 ++++++- .../Components/IncidentTable/index.jsx | 55 +++++++++++++++++-- .../Components/OptionsHeader/index.jsx | 46 ++++++---------- client/src/Pages/Incidents/index.jsx | 31 ++++++++++- client/src/Utils/NetworkService.js | 6 ++ client/src/locales/en.json | 3 + server/db/mongo/modules/checkModuleQueries.js | 6 +- 7 files changed, 126 insertions(+), 41 deletions(-) diff --git a/client/src/Hooks/checkHooks.js b/client/src/Hooks/checkHooks.js index a7dae8b9a..db920c7ad 100644 --- a/client/src/Hooks/checkHooks.js +++ b/client/src/Hooks/checkHooks.js @@ -12,6 +12,7 @@ const useFetchChecksTeam = ({ page, rowsPerPage, enabled = true, + updateTrigger, }) => { const [checks, setChecks] = useState(undefined); const [checksCount, setChecksCount] = useState(undefined); @@ -49,7 +50,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 +78,7 @@ const useFetchChecksByMonitor = ({ page, rowsPerPage, enabled = true, + updateTrigger, }) => { const [checks, setChecks] = useState(undefined); const [checksCount, setChecksCount] = useState(undefined); @@ -117,12 +130,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,7 +157,7 @@ const useFetchChecksSummaryByTeamId = () => { }; fetchSummary(); - }, []); + }, [updateTrigger]); return [summary, isLoading, networkError]; }; diff --git a/client/src/Pages/Incidents/Components/IncidentTable/index.jsx b/client/src/Pages/Incidents/Components/IncidentTable/index.jsx index bc5eaa5ca..eb9f6fe0b 100644 --- a/client/src/Pages/Incidents/Components/IncidentTable/index.jsx +++ b/client/src/Pages/Incidents/Components/IncidentTable/index.jsx @@ -15,13 +15,18 @@ import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; import { useFetchChecksTeam } from "../../../../Hooks/checkHooks"; import { useFetchChecksByMonitor } from "../../../../Hooks/checkHooks"; +import { Button, Typography } from "@mui/material"; +import { networkService } from "../../../../Utils/NetworkService"; +import { createToast } from "../../../../Utils/toastUtils"; const IncidentTable = ({ - shouldRender, + isLoading, monitors, selectedMonitor, filter, dateRange, + updateTrigger, + setUpdateTrigger, }) => { //Redux state const uiTimezone = useSelector((state) => state.ui.timezone); @@ -31,6 +36,7 @@ const IncidentTable = ({ const [rowsPerPage, setRowsPerPage] = useState(10); const selectedMonitorDetails = monitors?.[selectedMonitor]; const selectedMonitorType = selectedMonitorDetails?.type; + const [resolveLoading, setResolveLoading] = useState(false); const [checksMonitor, checksCountMonitor, isLoadingMonitor, networkErrorMonitor] = useFetchChecksByMonitor({ @@ -45,6 +51,7 @@ const IncidentTable = ({ page: page, rowsPerPage: rowsPerPage, enabled: selectedMonitor !== "0", + updateTrigger, }); const [checksTeam, checksCountTeam, isLoadingTeam, networkErrorTeam] = @@ -58,11 +65,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 +84,21 @@ const IncidentTable = ({ setRowsPerPage(event.target.value); }; + const handleResolveIncident = async (checkId) => { + try { + setResolveLoading(true); + await networkService.updateCheckStatus({ + checkId, + ack: true, + }); + setUpdateTrigger((prev) => !prev); + } catch (error) { + createToast({ body: "Failed to resolve incident." }); + } finally { + setResolveLoading(false); + } + }; + const headers = [ { id: "monitorName", @@ -114,9 +137,31 @@ const IncidentTable = ({ render: (row) => , }, { id: "message", content: t("incidentsTableMessage"), render: (row) => row.message }, + { + id: "action", + content: t("actions"), + render: (row) => { + return row.ack === false ? ( + + ) : ( + + {t("incidentsTableResolvedAt")}{" "} + {formatDateWithTz(row.ackAt, "YYYY-MM-DD HH:mm:ss A", uiTimezone)} + + ); + }, + }, ]; - if (!shouldRender || isLoading) return ; + if (isLoading || resolveLoading) return ; if (networkError) { return ( @@ -149,10 +194,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; diff --git a/client/src/Pages/Incidents/Components/OptionsHeader/index.jsx b/client/src/Pages/Incidents/Components/OptionsHeader/index.jsx index 08a9bcf1c..259880223 100644 --- a/client/src/Pages/Incidents/Components/OptionsHeader/index.jsx +++ b/client/src/Pages/Incidents/Components/OptionsHeader/index.jsx @@ -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")} - - - - - - +