Files
Checkmate/client/src/Pages/Incidents/Components/StatisticsPanel/index.jsx
T
2026-01-20 19:12:05 +00:00

169 lines
4.3 KiB
React

import PropTypes from "prop-types";
import { Box, Stack, Typography, Divider } from "@mui/material";
import { useTheme } from "@emotion/react";
import PanelSkeleton from "../IncidentsSummaryPanel/skeleton.jsx";
import { useTranslation } from "react-i18next";
import Icon from "@/Components/v1/Icon";
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 iconWrapperStyle = {
display: "flex",
justifyContent: "center",
mx: theme.spacing(2),
color: theme.palette.primary.contrastTextTertiary,
"& svg": {
width: 18,
height: 18,
},
"& svg path": {
stroke: "currentColor",
strokeWidth: 1.5,
},
};
if (isLoading) {
return <PanelSkeleton />;
}
if (error) {
return (
<SummaryCard>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "100%",
minHeight: 200,
}}
>
<Typography
color="error"
align="center"
>
{t("incidentsPage.incidentsStatisticsPanelError")}
</Typography>
</Box>
</SummaryCard>
);
}
const getMostAffectedMonitor = () => {
if (!summary.total || summary.total === 0) {
return t("incidentsPage.none");
}
return summary.topMonitor?.monitorName || t("incidentsPage.none");
};
const rowStyle = {
py: theme.spacing(0.5),
minHeight: 32,
};
return (
<SummaryCard title={t("incidentsPage.incidentsStatisticsPanelTitle")}>
<Stack gap={theme.spacing(4)}>
<Box>
<Stack
direction="row"
alignItems="center"
gap={theme.spacing(2)}
sx={rowStyle}
>
<Box sx={iconWrapperStyle}>
<Icon
name="Bell"
size={18}
/>
</Box>
<Typography
variant="body1"
fontWeight={500}
>
{t("incidentsPage.totalIncidents")}: {summary.total || 0}
</Typography>
</Stack>
<Divider sx={{ mt: theme.spacing(2) }} />
</Box>
<Box>
<Stack
direction="row"
alignItems="center"
gap={theme.spacing(2)}
sx={rowStyle}
>
<Box sx={iconWrapperStyle}>
<Icon
name="AlertTriangle"
size={18}
/>
</Box>
<Typography
variant="body1"
fontWeight={500}
>
{t("incidentsPage.mostAffectedMonitor")}: {getMostAffectedMonitor()}
</Typography>
</Stack>
<Divider sx={{ mt: theme.spacing(2) }} />
</Box>
<Stack
direction="row"
alignItems="center"
gap={theme.spacing(2)}
sx={rowStyle}
>
<Box sx={iconWrapperStyle}>
<Icon
name="Wrench"
size={18}
/>
</Box>
<Typography
variant="body1"
fontWeight={500}
>
{t("incidentsPage.avgResolutionTime")}:{" "}
{summary.total > 0
? `${summary.avgResolutionTimeHours || 0} ${t("incidentsPage.hours")}`
: "N/A"}
</Typography>
</Stack>
</Stack>
</SummaryCard>
);
};
StatisticsPanel.propTypes = {
summary: PropTypes.object,
isLoading: PropTypes.bool,
error: PropTypes.object,
};
export default StatisticsPanel;