diff --git a/client/src/Components/Fallback/index.jsx b/client/src/Components/Fallback/index.jsx index f35229494..e5a212c00 100644 --- a/client/src/Components/Fallback/index.jsx +++ b/client/src/Components/Fallback/index.jsx @@ -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), }} diff --git a/client/src/Hooks/checkHooks.js b/client/src/Hooks/checkHooks.js index a7dae8b9a..f23e1eaea 100644 --- a/client/src/Hooks/checkHooks.js +++ b/client/src/Hooks/checkHooks.js @@ -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, +}; diff --git a/client/src/Pages/Incidents/Components/IncidentTable/index.jsx b/client/src/Pages/Incidents/Components/IncidentTable/index.jsx index bc5eaa5ca..e8929d9ed 100644 --- a/client/src/Pages/Incidents/Components/IncidentTable/index.jsx +++ b/client/src/Pages/Incidents/Components/IncidentTable/index.jsx @@ -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) => , }, { 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 +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; 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")} - - - - - - +