diff --git a/client/src/Components/v1/GenericFallback/index.jsx b/client/src/Components/v1/GenericFallback/index.jsx
index 137cdd1da..188c09f37 100644
--- a/client/src/Components/v1/GenericFallback/index.jsx
+++ b/client/src/Components/v1/GenericFallback/index.jsx
@@ -40,13 +40,13 @@ const GenericFallback = ({ children }) => {
[
path: "notifications",
icon: ,
},
+ { name: t("menu.checks"), path: "checks", icon: },
{ name: t("menu.incidents"), path: "incidents", icon: },
{ name: t("menu.statusPages"), path: "status", icon: },
diff --git a/client/src/Pages/v1/Incidents/index.jsx b/client/src/Pages/v1/Incidents/index.jsx
index 3c0d7f60f..91cab2a40 100644
--- a/client/src/Pages/v1/Incidents/index.jsx
+++ b/client/src/Pages/v1/Incidents/index.jsx
@@ -18,13 +18,11 @@ import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
//Constants
-const Incidents = () => {
+const Checks = () => {
// Redux state
const { t } = useTranslation();
- const BREADCRUMBS = [
- { name: t("incidentsPageTitle", "Incidents"), path: "/incidents" },
- ];
+ const BREADCRUMBS = [{ name: t("checksPageTitle"), path: "/checks" }];
// Local state
const [selectedMonitor, setSelectedMonitor] = useState("0");
@@ -86,8 +84,8 @@ const Incidents = () => {
disabled={isLoadingAcknowledge}
>
{selectedMonitor === "0"
- ? t("incidentsPageActionResolveAll")
- : t("incidentsPageActionResolveMonitor")}
+ ? t("checksPageActionResolveAllMonitor")
+ : t("checksPageActionResolveMonitor")}
{
);
};
-export default Incidents;
+export default Checks;
diff --git a/client/src/Pages/v1/Incidents2/Components/ActiveIncidentsPanel/index.jsx b/client/src/Pages/v1/Incidents2/Components/ActiveIncidentsPanel/index.jsx
new file mode 100644
index 000000000..b6024ce53
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/Components/ActiveIncidentsPanel/index.jsx
@@ -0,0 +1,145 @@
+import PropTypes from "prop-types";
+import { Box, Stack, Typography } from "@mui/material";
+import { useTheme } from "@emotion/react";
+import { useTranslation } from "react-i18next";
+import PanelSkeleton from "../IncidentsSummaryPanel/skeleton.jsx";
+import Incidents from "@/assets/icons/incidents.svg?react";
+import CheckIcon from "@/assets/icons/check-icon.svg?react";
+import SummaryCard from "../SummaryCard/index.jsx";
+
+/**
+ * ActiveIncidentsPanel Component
+ *
+ * Displays a quick overview of currently active incidents.
+ * Shows only essential information: monitor name, status badge, and duration.
+ *
+ * @param {Object} props
+ * @param {Array} props.incidents - Array of active incidents
+ * @param {number} props.totalCount - Total count of active incidents (for badge)
+ * @param {boolean} props.isLoading - Loading state
+ * @param {Object} props.error - Error object if any
+ * @param {Function} props.onIncidentClick - Callback when incident is clicked (optional)
+ */
+const ActiveIncidentsPanel = ({ totalCount = 0, isLoading = false, error = null }) => {
+ const theme = useTheme();
+ const { t } = useTranslation();
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (error) {
+ return (
+
+
+ {t("incidentsPage.incidentsActivePanelError")}
+
+
+ );
+ }
+
+ if (!totalCount || totalCount === 0) {
+ return (
+
+
+
+
+
+
+
+ {t("incidentsPage.allSystemsAreOperational")}
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+ {totalCount}
+
+
+ {t("incidentsPage.incidentsActivePanelTitle")}
+
+
+
+ );
+};
+
+ActiveIncidentsPanel.propTypes = {
+ totalCount: PropTypes.number,
+ isLoading: PropTypes.bool,
+ error: PropTypes.object,
+ onIncidentClick: PropTypes.func,
+};
+
+export default ActiveIncidentsPanel;
diff --git a/client/src/Pages/v1/Incidents2/Components/IncidentTable/index.jsx b/client/src/Pages/v1/Incidents2/Components/IncidentTable/index.jsx
new file mode 100644
index 000000000..8cac4e07b
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/Components/IncidentTable/index.jsx
@@ -0,0 +1,204 @@
+//Components
+import DataTable from "@/Components/v1/Table/index.jsx";
+import TableSkeleton from "@/Components/v1/Table/skeleton.jsx";
+import Pagination from "@/Components/v1/Table/TablePagination/index.jsx";
+import { StatusLabel } from "@/Components/v1/Label/index.jsx";
+import { HttpStatusLabel } from "@/Components/v1/HttpStatusLabel/index.jsx";
+import GenericFallback from "@/Components/v1/GenericFallback/index.jsx";
+import NetworkError from "@/Components/v1/GenericFallback/NetworkError.jsx";
+
+//Utils
+import { formatDateWithTz } from "../../../../../Utils/timeUtils.js";
+import { useSelector } from "react-redux";
+import PropTypes from "prop-types";
+import { useTranslation } from "react-i18next";
+import { Button, Typography, useTheme } from "@mui/material";
+
+const IncidentTable = ({
+ incidents = [],
+ incidentsCount = 0,
+ isLoading = false,
+ networkError = false,
+ page = 0,
+ rowsPerPage = 10,
+ handleChangePage,
+ handleChangeRowsPerPage,
+ resolveIncident,
+ handleUpdateTrigger,
+}) => {
+ const uiTimezone = useSelector((state) => state.ui.timezone);
+
+ const { t } = useTranslation();
+ const theme = useTheme();
+
+ const handleResolveIncident = async (incidentId) => {
+ try {
+ await resolveIncident(incidentId);
+ handleUpdateTrigger();
+ } catch (error) {
+ console.error(t("incidentsPage.errorResolvingIncident"), error);
+ }
+ };
+
+ const headers = [
+ {
+ id: "monitorName",
+ content: t("incidentsTableMonitorName"),
+ render: (row) => row.monitorName ?? "N/A",
+ },
+ {
+ id: "status",
+ content: t("incidentsTableStatus"),
+ render: (row) => {
+ const status = row.status === true ? "down" : "up";
+ const statusText =
+ row.status === true ? t("incidentsPage.active") : t("incidentsPage.resolved");
+ return (
+
+ );
+ },
+ },
+ {
+ id: "startTime",
+ content: t("incidentsPage.startTime"),
+ render: (row) => {
+ const formattedDate = formatDateWithTz(
+ row.startTime || row.createdAt,
+ "YYYY-MM-DD HH:mm:ss A",
+ uiTimezone
+ );
+ return formattedDate;
+ },
+ },
+ {
+ id: "endTime",
+ content: t("incidentsPage.endTime"),
+ render: (row) => {
+ if (row.endTime) {
+ return formatDateWithTz(row.endTime, "YYYY-MM-DD HH:mm:ss A", uiTimezone);
+ }
+ return "-";
+ },
+ },
+ {
+ id: "resolutionType",
+ content: t("incidentsPage.resolutionType"),
+ render: (row) => {
+ if (row.resolutionType) {
+ return (
+
+ {row.resolutionType}
+
+ );
+ }
+ return "-";
+ },
+ },
+ {
+ id: "statusCode",
+ content: t("incidentsTableStatusCode"),
+ render: (row) => ,
+ },
+ {
+ id: "message",
+ content: t("incidentsTableMessage"),
+ render: (row) => row.message || "-",
+ },
+ {
+ id: "action",
+ content: t("actions"),
+ render: (row) => {
+ if (row.status === true) {
+ return (
+
+ );
+ } else {
+ return (
+
+ {t("incidentsPage.incidentsTableResolved")}
+
+ );
+ }
+ },
+ },
+ ];
+
+ if (isLoading) return ;
+
+ if (networkError) {
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoading && !networkError && incidents?.length === 0) {
+ return (
+
+ {t("incidentsTableNoIncidents", "No incidents found")}
+
+ );
+ }
+
+ const incidentsData = Array.isArray(incidents) ? incidents : [];
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+IncidentTable.propTypes = {
+ incidents: PropTypes.array.isRequired, // Array of incident objects
+ incidentsCount: PropTypes.number.isRequired, // Total count for pagination
+ isLoading: PropTypes.bool.isRequired, // Loading state
+ networkError: PropTypes.bool, // Network error object
+ page: PropTypes.number.isRequired, // Current page number
+ rowsPerPage: PropTypes.number.isRequired, // Number of rows per page
+ handleChangePage: PropTypes.func.isRequired, // Handler for page change
+ handleChangeRowsPerPage: PropTypes.func.isRequired, // Handler for rows per page change
+ resolveIncident: PropTypes.func.isRequired,
+ handleUpdateTrigger: PropTypes.func.isRequired,
+};
+
+export default IncidentTable;
diff --git a/client/src/Pages/v1/Incidents2/Components/IncidentsSummaryPanel/index.jsx b/client/src/Pages/v1/Incidents2/Components/IncidentsSummaryPanel/index.jsx
new file mode 100644
index 000000000..43071fa03
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/Components/IncidentsSummaryPanel/index.jsx
@@ -0,0 +1,100 @@
+import { useEffect, useState } from "react";
+// Hooks
+import useFetchIncidents from "../../hooks/useFetchIncidents.js";
+import PropTypes from "prop-types";
+// Components
+import { Grid } from "@mui/material";
+import ActiveIncidentsPanel from "../ActiveIncidentsPanel/index.jsx";
+import LatestIncidentsPanel from "../LatestIncidentsPanel/index.jsx";
+import StatisticsPanel from "../StatisticsPanel/index.jsx";
+
+const IncidentsSummaryPanel = ({ updateTrigger }) => {
+ const { fetchIncidentSummary } = useFetchIncidents();
+ const [summary, setSummary] = useState({
+ totalActive: 0,
+ avgResolutionTimeHours: 0,
+ total: 0,
+ topMonitor: null,
+ totalManualResolutions: 0,
+ totalAutomaticResolutions: 0,
+ latestIncidents: [],
+ });
+
+ const [isLoadingSummary, setIsLoadingSummary] = useState(false);
+ const [summaryError, setSummaryError] = useState(null);
+
+ useEffect(() => {
+ const fetchSummary = async () => {
+ setIsLoadingSummary(true);
+ setSummaryError(null);
+
+ try {
+ const summaryData = await fetchIncidentSummary({
+ limit: 3,
+ });
+
+ if (summaryData) {
+ setSummary(summaryData);
+ } else {
+ setSummaryError(new Error("Failed to fetch summary"));
+ }
+ } catch (error) {
+ console.error("Error fetching incident summary:", error);
+ setSummaryError(error);
+ } finally {
+ setIsLoadingSummary(false);
+ }
+ };
+
+ fetchSummary();
+ }, [fetchIncidentSummary, updateTrigger]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+IncidentsSummaryPanel.propTypes = {
+ updateTrigger: PropTypes.bool.isRequired,
+};
+export default IncidentsSummaryPanel;
diff --git a/client/src/Pages/v1/Incidents2/Components/IncidentsSummaryPanel/skeleton.jsx b/client/src/Pages/v1/Incidents2/Components/IncidentsSummaryPanel/skeleton.jsx
new file mode 100644
index 000000000..153cd7c25
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/Components/IncidentsSummaryPanel/skeleton.jsx
@@ -0,0 +1,20 @@
+import { Skeleton } from "@mui/material";
+import SummaryCard from "../SummaryCard/index.jsx";
+
+/**
+ * Simple skeleton loader for summary panels
+ * Reusable for ActiveIncidentsPanel, LatestIncidentsPanel, and StatisticsPanel
+ */
+const PanelSkeleton = () => {
+ return (
+
+
+
+ );
+};
+
+export default PanelSkeleton;
diff --git a/client/src/Pages/v1/Incidents2/Components/LatestIncidentsPanel/IncidentItem.jsx b/client/src/Pages/v1/Incidents2/Components/LatestIncidentsPanel/IncidentItem.jsx
new file mode 100644
index 000000000..c212c8e52
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/Components/LatestIncidentsPanel/IncidentItem.jsx
@@ -0,0 +1,153 @@
+import PropTypes from "prop-types";
+import { Box, Stack, Typography } from "@mui/material";
+import { useTheme } from "@emotion/react";
+import { StatusLabel } from "@/Components/v1/Label/index.jsx";
+import { getHumanReadableDuration } from "@/Utils/timeUtils.js";
+import Monitors from "@/assets/icons/monitors.svg?react";
+import AverageResponseIcon from "@/assets/icons/status-pages.svg?react";
+import { useTranslation } from "react-i18next";
+
+const IncidentItem = ({ incident }) => {
+ const { t } = useTranslation();
+ const theme = useTheme();
+ const isActive = incident.status === true;
+ const calculateDuration = () => {
+ if (!incident.startTime) {
+ return "-";
+ }
+ const startTime = new Date(incident.startTime);
+ const endTime = isActive
+ ? new Date()
+ : incident.endTime
+ ? new Date(incident.endTime)
+ : null;
+
+ if (!endTime) {
+ return "-";
+ }
+
+ const durationMs = endTime.getTime() - startTime.getTime();
+
+ if (durationMs < 0) {
+ return "-";
+ }
+
+ return getHumanReadableDuration(durationMs);
+ };
+
+ const duration = calculateDuration();
+ const iconWrapperStyle = {
+ px: theme.spacing(2),
+ display: "flex",
+ justifyContent: "center",
+ color: theme.palette.primary.contrastTextTertiary,
+ "& svg path": {
+ stroke: "currentColor",
+ },
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ {t("incidentsPage.incidentItemMonitor")}:
+
+
+ {incident.monitorName || t("incidentsPage.unknownMonitor")}
+
+
+
+
+
+
+
+
+
+
+
+ {t("incidentsPage.incidentItemStatus")}:
+
+
+
+
+
+
+
+
+
+ {duration}
+
+
+
+
+ );
+};
+
+IncidentItem.propTypes = {
+ incident: PropTypes.shape({
+ _id: PropTypes.string,
+ id: PropTypes.string,
+ monitorId: PropTypes.string,
+ monitorName: PropTypes.string,
+ status: PropTypes.bool,
+ startTime: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
+ endTime: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
+ resolutionType: PropTypes.oneOf(["automatic", "manual"]),
+ }),
+ onClick: PropTypes.func,
+};
+
+export default IncidentItem;
diff --git a/client/src/Pages/v1/Incidents2/Components/LatestIncidentsPanel/index.jsx b/client/src/Pages/v1/Incidents2/Components/LatestIncidentsPanel/index.jsx
new file mode 100644
index 000000000..21d2330c4
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/Components/LatestIncidentsPanel/index.jsx
@@ -0,0 +1,99 @@
+import PropTypes from "prop-types";
+import { Box, Stack, Typography, Divider } from "@mui/material";
+import { useTheme } from "@emotion/react";
+import { useTranslation } from "react-i18next";
+import PanelSkeleton from "../IncidentsSummaryPanel/skeleton.jsx";
+import IncidentItem from "./IncidentItem.jsx";
+import SummaryCard from "../SummaryCard/index.jsx";
+
+/**
+ * LatestIncidentsPanel Component
+ *
+ * Displays a quick overview of recent incidents (both active and resolved).
+ * Shows only essential information: monitor name, status, duration, and resolution type.
+ *
+ * @param {Object} props
+ * @param {Array} props.incidents - Array of recent incidents
+ * @param {boolean} props.isLoading - Loading state
+ * @param {Object} props.error - Error object if any
+ */
+const LatestIncidentsPanel = ({ incidents = [], isLoading = false, error = null }) => {
+ const theme = useTheme();
+ const { t } = useTranslation();
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (error) {
+ return (
+
+
+
+ {t("incidentsPage.incidentsLatestPanelError")}
+
+
+
+ );
+ }
+
+ return (
+
+ {!incidents || incidents.length === 0 ? (
+
+
+ {t("incidentsPage.incidentsLatestPanelEmpty")}
+
+
+ ) : (
+
+ {incidents.map((incident, index) => (
+
+
+ {index < incidents.length - 1 && }
+
+ ))}
+
+ )}
+
+ );
+};
+
+LatestIncidentsPanel.propTypes = {
+ incidents: PropTypes.arrayOf(
+ PropTypes.shape({
+ _id: PropTypes.string,
+ monitorId: PropTypes.string,
+ monitorName: PropTypes.string,
+ status: PropTypes.bool,
+ duration: PropTypes.string,
+ resolutionType: PropTypes.oneOf(["automatic", "manual"]),
+ })
+ ),
+ isLoading: PropTypes.bool,
+ error: PropTypes.object,
+ onIncidentClick: PropTypes.func,
+};
+
+export default LatestIncidentsPanel;
diff --git a/client/src/Pages/v1/Incidents2/Components/OptionsHeader/index.jsx b/client/src/Pages/v1/Incidents2/Components/OptionsHeader/index.jsx
new file mode 100644
index 000000000..3a0ca1985
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/Components/OptionsHeader/index.jsx
@@ -0,0 +1,138 @@
+// Components
+import { Stack, Typography, Button, ButtonGroup } from "@mui/material";
+import Select from "@/Components/v1/Inputs/Select/index.jsx";
+import PropTypes from "prop-types";
+
+//Utils
+import { useTheme } from "@emotion/react";
+import { useTranslation } from "react-i18next";
+
+const OptionsHeader = ({
+ shouldRender,
+ selectedMonitor = 0,
+ setSelectedMonitor,
+ monitors,
+ filter = "all",
+ setFilter,
+ dateRange = "all",
+ setDateRange,
+}) => {
+ const theme = useTheme();
+ const { t } = useTranslation();
+ const monitorNames = typeof monitors !== "undefined" ? Object.values(monitors) : [];
+
+ const filterOptions = [
+ { _id: "all", name: t("incidentsPage.incidentsOptionsHeaderFilterAll") },
+ { _id: "active", name: t("incidentsPage.incidentsOptionsHeaderFilterActive") },
+ { _id: "resolved", name: t("incidentsPage.incidentsOptionsHeaderFilterResolved") },
+ { _id: "manual", name: t("incidentsPage.incidentsOptionsHeaderFilterManual") },
+ ];
+
+ const stackStyles = {
+ direction: "row",
+ alignItems: "center",
+ gap: theme.spacing(6),
+ };
+
+ if (!shouldRender) return;
+
+ return (
+
+
+
+ {t("incidentsOptionsHeader")}
+
+
+
+
+ {t("incidentsOptionsHeaderFilterBy")}
+
+
+
+
+ {t("incidentsOptionsHeaderShow")}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+OptionsHeader.propTypes = {
+ shouldRender: PropTypes.bool,
+ selectedMonitor: PropTypes.string,
+ setSelectedMonitor: PropTypes.func,
+ monitors: PropTypes.object,
+ filter: PropTypes.string,
+ setFilter: PropTypes.func,
+ dateRange: PropTypes.string,
+ setDateRange: PropTypes.func,
+};
+
+export default OptionsHeader;
diff --git a/client/src/Pages/v1/Incidents2/Components/StatisticsPanel/index.jsx b/client/src/Pages/v1/Incidents2/Components/StatisticsPanel/index.jsx
new file mode 100644
index 000000000..ede9c619a
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/Components/StatisticsPanel/index.jsx
@@ -0,0 +1,234 @@
+import PropTypes from "prop-types";
+import { Box, Stack, Typography } from "@mui/material";
+import { useTheme } from "@emotion/react";
+import PanelSkeleton from "../IncidentsSummaryPanel/skeleton.jsx";
+import { useTranslation } from "react-i18next";
+import { Divider } from "@mui/material";
+import Clock from "@/assets/icons/maintenance.svg?react";
+import Incidents from "@/assets/icons/incidents.svg?react";
+import ResolutionItem from "@/assets/icons/interval-check.svg?react";
+import NotificationIcon from "@/assets/icons/notifications.svg?react";
+import SummaryCard from "../SummaryCard/index.jsx";
+
+/**
+ * StatisticsPanel Component
+ *
+ * Displays key metrics and statistics about incidents.
+ *
+ * @param {Object} props
+ * @param {Object} props.statistics - Statistics data object
+ * @param {number} props.statistics.total - Total incidents count
+ * @param {number} props.statistics.totalTrend - Trend percentage for total (optional)
+ * @param {string} props.statistics.mttr - Mean Time To Resolve (formatted string, e.g., "2h 15m")
+ * @param {number} props.statistics.mttrTrend - Trend percentage for MTTR (optional)
+ * @param {Object} props.statistics.topMonitor - Top monitor with most incidents
+ * @param {string} props.statistics.topMonitor.name - Monitor name
+ * @param {number} props.statistics.topMonitor.count - Incident count
+ * @param {number} props.statistics.topMonitor.percentage - Percentage of total
+ * @param {string} props.statistics.uptimeAffected - Total downtime (formatted string, e.g., "12h 45m")
+ * @param {number} props.statistics.availability - Availability percentage
+ * @param {boolean} props.isLoading - Loading state
+ * @param {Object} props.error - Error object if any
+ */
+const StatisticsPanel = ({ isLoading = false, error = null, summary = {} }) => {
+ const theme = useTheme();
+ const { t } = useTranslation();
+ const totalResolutions =
+ (summary?.totalManualResolutions || 0) + (summary?.totalAutomaticResolutions || 0);
+ const automaticPercentage =
+ totalResolutions > 0
+ ? (summary?.totalAutomaticResolutions / totalResolutions) * 100
+ : 0;
+
+ const iconWrapperStyle = {
+ display: "flex",
+ justifyContent: "center",
+ mx: theme.spacing(3),
+ color: theme.palette.primary.contrastTextTertiary,
+ "& svg path": {
+ stroke: "currentColor",
+ },
+ };
+ if (isLoading) {
+ return ;
+ }
+
+ if (error) {
+ return (
+
+
+
+ {t("incidentsPage.incidentsStatisticsPanelError")}
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ {t("incidentsPage.totalIncidents")} : {summary.total || 0}
+
+
+
+
+
+
+
+
+
+
+ {t("incidentsPage.mostAffectedMonitor")} :{" "}
+ {summary.topMonitor?.monitorName || t("incidentsPage.unknownMonitor")}
+
+
+
+
+
+
+
+
+
+
+ {t("incidentsPage.avgResolutionTime")} :{" "}
+ {summary.avgResolutionTimeHours || 0} {t("incidentsPage.hours")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {t("incidentsPage.resolutions")}: {totalResolutions}
+
+
+
+
+
+
+
+
+ {summary.totalAutomaticResolutions > 0 && (
+
+ {t("incidentsPage.automatic")} ({summary.totalAutomaticResolutions})
+
+ )}
+ {summary.totalManualResolutions > 0 && (
+
+ {t("incidentsPage.manual")} ({summary.totalManualResolutions})
+
+ )}
+
+
+
+
+ );
+};
+
+StatisticsPanel.propTypes = {
+ summary: PropTypes.object,
+ isLoading: PropTypes.bool,
+ error: PropTypes.object,
+};
+
+export default StatisticsPanel;
diff --git a/client/src/Pages/v1/Incidents2/Components/SummaryCard/index.jsx b/client/src/Pages/v1/Incidents2/Components/SummaryCard/index.jsx
new file mode 100644
index 000000000..cb895dc50
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/Components/SummaryCard/index.jsx
@@ -0,0 +1,68 @@
+import { Paper } from "@mui/material";
+import { useTheme } from "@mui/material/styles";
+import PropTypes from "prop-types";
+import { Box, Stack, Typography, Divider } from "@mui/material";
+const SummaryCard = ({ children, isHighPriority = false, sx = {}, title = null }) => {
+ const theme = useTheme();
+ return (
+
+ {title && (
+
+
+
+ {title}
+
+
+
+
+ )}
+
+ {children}
+
+
+ );
+};
+SummaryCard.propTypes = {
+ children: PropTypes.node,
+ isHighPriority: PropTypes.bool,
+ sx: PropTypes.object,
+ title: PropTypes.string,
+};
+export default SummaryCard;
diff --git a/client/src/Pages/v1/Incidents2/hooks/useFetchIncidents.js b/client/src/Pages/v1/Incidents2/hooks/useFetchIncidents.js
new file mode 100644
index 000000000..a67f6b7ee
--- /dev/null
+++ b/client/src/Pages/v1/Incidents2/hooks/useFetchIncidents.js
@@ -0,0 +1,205 @@
+import { useState, useCallback } from "react";
+import { networkService } from "../../../../main.jsx";
+import { createToast } from "../../../../Utils/toastUtils.jsx";
+import { useTranslation } from "react-i18next";
+/**
+ * Hook to fetch and manage incidents
+ *
+ * Provides multiple functions to fetch incidents with different filters.
+ *
+ * @returns {Object} {
+ * incidents,
+ * incidentsCount,
+ * isLoading,
+ * networkError,
+ * fetchIncidents,
+ * fetchActiveIncidents,
+ * fetchResolvedIncidents,
+ * fetchIncidentsByTeam,
+ * fetchIncidentById,
+ * resolveIncident,
+ * fetchIncidentSummary
+ * }
+ */
+const useFetchIncidents = () => {
+ const [incidents, setIncidents] = useState(null);
+ const [incidentsCount, setIncidentsCount] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [networkError, setNetworkError] = useState(false);
+ const { t } = useTranslation();
+ /**
+ * Generic function to fetch incidents by team with custom config
+ *
+ * @param {Object} config - Configuration object
+ * @param {string} [config.monitorId] - Filter by monitor ID
+ * @param {boolean} [config.status] - Filter by status (true=active, false=resolved)
+ * @param {string} [config.resolutionType] - Filter by resolution type (automatic/manual)
+ * @param {string} [config.sortOrder] - Sort order (asc/desc)
+ * @param {string} [config.dateRange] - Date range filter
+ * @param {number} [config.page] - Page number
+ * @param {number} [config.rowsPerPage] - Rows per page
+ */
+ const fetchIncidentsByTeam = useCallback(async (config = {}) => {
+ try {
+ setIsLoading(true);
+ setNetworkError(false);
+
+ const res = await networkService.getIncidentsByTeam(config);
+
+ setIncidents(res.data?.data?.incidents || []);
+ setIncidentsCount(res.data?.data?.incidentsCount || 0);
+ } catch (error) {
+ setNetworkError(true);
+ console.error(t("incidentsPage.errorFetchingIncidents"), error);
+ createToast({ body: error.message || t("incidentsPage.errorFetchingIncidents") });
+ } finally {
+ setIsLoading(false);
+ }
+ }, []);
+
+ /**
+ * Fetch active (open) incidents - Default function
+ *
+ * @param {Object} config - Additional configuration
+ */
+ const fetchActiveIncidents = useCallback(
+ async (config = {}) => {
+ await fetchIncidentsByTeam({
+ status: true,
+ sortOrder: "desc",
+ ...config,
+ });
+ },
+ [fetchIncidentsByTeam]
+ );
+
+ /**
+ * Fetch resolved incidents
+ *
+ * @param {Object} config - Additional configuration
+ */
+ const fetchResolvedIncidents = useCallback(
+ async (config = {}) => {
+ await fetchIncidentsByTeam({
+ status: false,
+ sortOrder: "desc",
+ ...config,
+ });
+ },
+ [fetchIncidentsByTeam]
+ );
+
+ /**
+ * Fetch incidents with custom filters (alias for fetchIncidentsByTeam)
+ *
+ * @param {Object} config - Configuration object
+ */
+ const fetchIncidents = useCallback(
+ async (config = {}) => {
+ await fetchIncidentsByTeam(config);
+ },
+ [fetchIncidentsByTeam]
+ );
+
+ /**
+ * Fetch a single incident by ID
+ *
+ * @param {string} incidentId - The ID of the incident to fetch
+ * @returns {Promise