diff --git a/client/src/Pages/Incidents/Components/CardSummary.tsx b/client/src/Pages/Incidents/Components/CardSummary.tsx
index f5ba2619b..12d1f87cb 100644
--- a/client/src/Pages/Incidents/Components/CardSummary.tsx
+++ b/client/src/Pages/Incidents/Components/CardSummary.tsx
@@ -1,22 +1,23 @@
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import Divider from "@mui/material/Divider";
-import { BaseBox } from "@/Components/v2/design-elements";
-import { CircleCheck, TriangleAlert, Bell, Wrench } from "lucide-react";
+import Grid from "@mui/material/Grid";
+import { BaseBox, ValueLabel } from "@/Components/v2/design-elements";
+import { CircleCheck, TriangleAlert, Bell, Wrench, Globe } from "lucide-react";
import Box from "@mui/material/Box";
-import { IncidentItem, SummaryItem } from "@/Pages/Incidents/Components/IncidentItem";
+import { SummaryItem } from "@/Pages/Incidents/Components/IncidentItem";
import { useTranslation } from "react-i18next";
import { useTheme } from "@mui/material";
-import type { Incident, IncidentSummary } from "@/Types/Incident";
-import type { Monitor } from "@/Types/Monitor";
+import type { IncidentSummary, IncidentSummaryItem } from "@/Types/Incident";
+import { getIncidentsDuration } from "@/Pages/Incidents/utils";
interface SummaryCardProps {
title: string;
sx?: React.CSSProperties;
}
-const SummaryCard = ({
+export const SummaryCard = ({
title,
sx,
children,
@@ -49,18 +50,17 @@ const SummaryCard = ({
);
};
-interface SummaryCardIncidentsProps {
- incidents?: Incident[];
- monitors?: Monitor[] | null;
+interface SummaryCardActiveIncidentsProps {
+ summary?: IncidentSummary | null;
}
-export const SummaryCardActiveIncidents = ({ incidents }: SummaryCardIncidentsProps) => {
+export const SummaryCardActiveIncidents = ({ summary }: SummaryCardActiveIncidentsProps) => {
const { t } = useTranslation();
const theme = useTheme();
- if (!incidents) return null;
+ if (!summary) return null;
- const activeCount = incidents.filter((incident) => incident.status === true).length;
+ const activeCount = summary.totalActive;
const hasActive = activeCount > 0;
const color = hasActive ? theme.palette.error.main : theme.palette.success.main;
const icon = hasActive ? (
@@ -90,22 +90,82 @@ export const SummaryCardActiveIncidents = ({ incidents }: SummaryCardIncidentsPr
);
};
-export const SummaryCardLatestIncidents = ({
- incidents,
- monitors,
-}: SummaryCardIncidentsProps) => {
+const SummaryIncidentItem = ({ incident }: { incident: IncidentSummaryItem }) => {
+ const theme = useTheme();
+ const duration = getIncidentsDuration(incident);
+ return (
+
+
+
+
+ {incident.monitorName ?? "N/A"}
+
+
+
+
+
+
+
+
+ {duration}
+
+
+ );
+};
+
+interface SummaryCardLatestIncidentsProps {
+ summary?: IncidentSummary | null;
+}
+
+export const SummaryCardLatestIncidents = ({ summary }: SummaryCardLatestIncidentsProps) => {
const { t } = useTranslation();
const theme = useTheme();
+
+ const latestIncidents = summary?.latestIncidents ?? [];
+
return (
- {incidents?.slice(0, 3).map((incident, index) => (
+ {latestIncidents.slice(0, 3).map((incident, index) => (
- m.id === incident.monitorId)}
- />
- {index < incidents.length - 1 && }
+
+ {index < latestIncidents.length - 1 && (
+
+ )}
))}
diff --git a/client/src/Pages/Incidents/Components/IncidentTable.tsx b/client/src/Pages/Incidents/Components/IncidentTable.tsx
index 957f30dfc..df82764d9 100644
--- a/client/src/Pages/Incidents/Components/IncidentTable.tsx
+++ b/client/src/Pages/Incidents/Components/IncidentTable.tsx
@@ -18,6 +18,7 @@ import { useTheme } from "@mui/material/styles";
import Box from "@mui/material/Box";
interface IncidentsTableProps {
+ title?: string;
incidents?: Incident[];
monitors?: Monitor[];
incidentsCount: number;
@@ -30,6 +31,7 @@ interface IncidentsTableProps {
}
export const IncidentsTable = ({
+ title,
incidents,
monitors,
incidentsCount,
@@ -200,6 +202,14 @@ export const IncidentsTable = ({
return (
+ {title && (
+
+ {title}
+
+ )}
{
+ const { t } = useTranslation();
const theme = useTheme();
const { monitorId } = useParams();
@@ -27,6 +29,8 @@ const IncidentsPage = () => {
const [dateRange, setDateRange] = useState("recent");
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
+ const [activeIncidentsPage, setActiveIncidentsPage] = useState(0);
+ const [activeIncidentsRowsPerPage, setActiveIncidentsRowsPerPage] = useState(5);
// Resolve dialog state
const [isResolveDialogOpen, setIsResolveDialogOpen] = useState(false);
@@ -73,6 +77,27 @@ const IncidentsPage = () => {
error: monitorsError,
} = useGet("/monitors/team");
+ // Build active incidents URL (always fetch all active, no date filter)
+ const activeIncidentsUrl = useMemo(() => {
+ const params = new URLSearchParams();
+ if (selectedMonitor !== "0") params.append("monitorId", selectedMonitor);
+ params.append("status", "true");
+ params.append("sortOrder", "desc");
+ params.append("page", String(activeIncidentsPage));
+ params.append("rowsPerPage", String(activeIncidentsRowsPerPage));
+ return `/incidents/team?${params.toString()}`;
+ }, [selectedMonitor, activeIncidentsPage, activeIncidentsRowsPerPage]);
+
+ // Fetch active incidents
+ const {
+ data: activeIncidentsData,
+ refetch: refetchActiveIncidents,
+ } = useGet(
+ activeIncidentsUrl,
+ {},
+ { keepPreviousData: true, refreshInterval: 10000 }
+ );
+
// Fetch incident summary
const {
data: summaryData,
@@ -89,6 +114,8 @@ const IncidentsPage = () => {
// Derived state
const incidents = incidentsData?.incidents ?? [];
const incidentsCount = incidentsData?.count ?? 0;
+ const activeIncidents = activeIncidentsData?.incidents ?? [];
+ const activeIncidentsCount = activeIncidentsData?.count ?? 0;
const networkError = !!incidentsError || !!monitorsError || !!summaryError;
// Expose state and handlers for future UI use
@@ -102,7 +129,7 @@ const IncidentsPage = () => {
void networkError;
const handleOpenDetails = (incidentId: string) => {
- const incident = incidents.find((i) => i.id === incidentId) ?? null;
+ const incident = incidents.find((i) => i.id === incidentId) ?? activeIncidents.find((i) => i.id === incidentId) ?? null;
const monitor = monitorsData?.find((m) => m.id === incident?.monitorId) ?? null;
setIsDetailsDialogOpen(true);
setSelectedIncident(incident);
@@ -132,6 +159,7 @@ const IncidentsPage = () => {
const handleResolved = () => {
refetchIncidents();
+ refetchActiveIncidents();
refetchSummary();
};
@@ -141,11 +169,8 @@ const IncidentsPage = () => {
direction={{ xs: "column", md: "row" }}
gap={theme.spacing(8)}
>
-
-
+
+
{
setDateRange={setDateRange}
isLoading={isLoadingIncidents}
/>
+ {activeIncidentsCount > 0 && (
+
+ )}
{
+
+type IncidentLike = Pick;
+
+export const getIncidentsDuration = (incident: IncidentLike | IncidentSummaryItem) => {
if (!incident?.startTime) {
return "-";
}