Skip to content
Snippets Groups Projects
Commit e0dcba05 authored by Johanna Szepanski's avatar Johanna Szepanski
Browse files

Merge branch 'CE-2775-alert-type-ioc-status' into 'develop'

CE-2775: Differentiate IOC status warning from error

See merge request !477
parents 16d6c128 e26489cd
No related branches found
No related tags found
2 merge requests!497CE-2790: Prepare for 4.0.0,!477CE-2775: Differentiate IOC status warning from error
Pipeline #190966 waiting for manual action
...@@ -5,7 +5,12 @@ import { IOCStatusIcon } from "../IOCIcons"; ...@@ -5,7 +5,12 @@ import { IOCStatusIcon } from "../IOCIcons";
export function IOCBadge({ ioc, ...rest }) { export function IOCBadge({ ioc, ...rest }) {
return ( return (
<IconBadge <IconBadge
icon={<IOCStatusIcon ioc={ioc} />} icon={
<IOCStatusIcon
ioc={ioc}
hideAlerts
/>
}
title={ioc.namingName ?? ioc.name} title={ioc.namingName ?? ioc.name}
subtitle={ioc.activeDeployment?.host?.fqdn || "---"} subtitle={ioc.activeDeployment?.host?.fqdn || "---"}
{...rest} {...rest}
......
import React from "react"; import React from "react";
import { Typography, useTheme, Stack, Badge } from "@mui/material"; import { LabeledIcon } from "@ess-ics/ce-ui-common";
import {
Brightness1,
StopCircle,
RadioButtonUnchecked,
HelpOutline,
PlayCircleFilled,
PauseCircleFilled
} from "@mui/icons-material";
import Popover from "../../common/Popover"; import Popover from "../../common/Popover";
import { AlertBanner, LabeledIcon } from "@ess-ics/ce-ui-common"; import { STATUS, statusConfig, getIocStatus } from "./iocStatusData";
import { object, bool } from "prop-types";
const STATUS = { import { useTheme } from "@mui/material";
active: "active", import { PlayCircleFilled, PauseCircleFilled } from "@mui/icons-material";
disabled: "disabled", import { StatusIcon } from "./StatusIcon";
alert: "alert", import { IOCStatusPopoverContent } from "./IOCStatusPopoverContent";
inactive: "inactive", import { IOCStatusBadge } from "./IOCStatusBadge";
inactiveAlert: "inactive alert"
}; const propsTypes = {
ioc: object,
const SEVERITY = { hideAlerts: bool
info: "info"
}; };
function AlertMessagesPopoverContents({ title, alerts }) { export function IOCStatusIcon({ ioc, hideAlerts = false }) {
// Filter out INFO level alerts const theme = useTheme();
// And for now filter out links on alerts due to issues with let { alerts = [], id } = ioc;
// focus behavior of popovers const iocStatus = getIocStatus(ioc);
// Also normalize the type to lowercase since AlertBanner expects lowercase const displayBadge =
const sanitizedAlerts = ( !hideAlerts &&
alerts?.filter((alert) => alert?.type.toLowerCase() !== SEVERITY.info) || [] (iocStatus === STATUS.alert ||
).map((alert) => ({ iocStatus === STATUS.warning ||
...alert, iocStatus === STATUS.inactiveAlert);
type: alert?.type?.toLowerCase(),
link: undefined
}));
return ( return (
<Stack <Popover
gap={1} id={`ioc-status-popover-${id}`}
sx={{ minWidth: 0, maxWidth: "50vw" }} renderOwningComponent={({
> ariaOwns,
<Typography ariaHasPopup,
sx={{ handlePopoverOpen,
fontWeight: sanitizedAlerts.length > 0 ? "bold" : "normal" handlePopoverClose
}} }) => (
> <div
{title} aria-owns={ariaOwns}
</Typography> aria-haspopup={ariaHasPopup}
{sanitizedAlerts.length > 0 onMouseEnter={handlePopoverOpen}
? sanitizedAlerts.map((alert, index) => ( onMouseLeave={handlePopoverClose}
<AlertBanner style={{ color: theme.palette.status.icons }}
{...alert} >
key={index} {displayBadge ? (
<IOCStatusBadge
status={iocStatus}
theme={theme}
>
<StatusIcon
ioc={ioc}
status={iocStatus}
/>
</IOCStatusBadge>
) : (
<StatusIcon
ioc={ioc}
status={iocStatus}
/> />
)) )}
: null} </div>
</Stack> )}
); popoverContents={
} <IOCStatusPopoverContent
title={statusConfig[iocStatus].title}
export function IOCStatusIcon({ ioc }) { alerts={hideAlerts ? [] : alerts}
const theme = useTheme(); />
let { active, alerts = [], activeDeployment, id } = ioc; }
const alertSeverity = ioc?.alertSeverity?.toLowerCase(); anchorOrigin={{
vertical: "top",
const iconConfig = { horizontal: "right"
[STATUS.active]: { }}
title: "Active",
icon: Brightness1
},
[STATUS.disabled]: {
title: "Disabled",
icon: StopCircle
},
[STATUS.alert]: {
title: "Alert",
icon: Brightness1
},
[STATUS.inactive]: {
title: "Inactive",
icon: RadioButtonUnchecked
},
[STATUS.inactiveAlert]: {
title: "Alert",
icon: RadioButtonUnchecked
},
null: {
title: "Unknown",
icon: HelpOutline
}
};
let status = null;
if (active === null) {
active = undefined;
}
if (
active &&
(alertSeverity === undefined || alertSeverity === SEVERITY.info)
) {
// Active status / running
status = STATUS.active;
} else if (
active === false &&
(alertSeverity === undefined || alertSeverity === SEVERITY.info)
) {
// stopped/paused
status = STATUS.disabled;
} else if (
active !== undefined &&
alertSeverity !== undefined &&
alertSeverity !== SEVERITY.info
) {
// warning/error
status = STATUS.alert;
} else if (
(active === undefined || activeDeployment === null) &&
(alertSeverity === undefined || alertSeverity === SEVERITY.info)
) {
// Inactive status
status = STATUS.inactive;
} else if (
(active === undefined || activeDeployment === null) &&
alertSeverity !== undefined &&
alertSeverity !== SEVERITY.info
) {
// inactive with warning/error
status = STATUS.inactiveAlert;
} else {
// unknown fallback state
status = null;
}
const iconTitle = iconConfig[status].title;
const getLabel = () => {
if (status === STATUS.alert || status === STATUS.inactiveAlert) {
const warnings = alerts.filter((alert) => alert.type === "WARNING");
const errors = alerts.filter((alert) => alert.type === "ERROR");
const hasWarnings = Boolean(warnings.length);
const hasErrors = Boolean(errors.length);
return `${iconTitle} ${hasWarnings ? warnings.length + " warnings" : ""}${
hasErrors ? errors.length + " errors" : ""
}`;
}
return iconTitle;
};
const statusIcon = (
<LabeledIcon
label={getLabel()}
Icon={iconConfig[status].icon}
labelPosition="none"
/> />
); );
return (
<div>
<Popover
id={`ioc-status-popover-${id}`}
renderOwningComponent={({
ariaOwns,
ariaHasPopup,
handlePopoverOpen,
handlePopoverClose
}) => (
<div
aria-owns={ariaOwns}
aria-haspopup={ariaHasPopup}
onMouseEnter={handlePopoverOpen}
onMouseLeave={handlePopoverClose}
style={{ color: theme.palette.status.icons }}
>
{status === STATUS.alert || status === STATUS.inactiveAlert ? (
<Badge
badgeContent={"!"}
color="error"
sx={{
"& .MuiBadge-badge": {
minWidth: "17px",
height: "17px",
fontSize: "0.7rem"
}
}}
>
{statusIcon}
</Badge>
) : (
statusIcon
)}
</div>
)}
popoverContents={
<AlertMessagesPopoverContents
title={iconTitle}
alerts={alerts}
/>
}
anchorOrigin={{
vertical: "top",
horizontal: "right"
}}
/>
</div>
);
} }
IOCStatusIcon.propsTypes = propsTypes;
export function CommandTypeIcon({ export function CommandTypeIcon({
type, type,
colorStyle = "colors", colorStyle = "colors",
......
import React from "react";
import { string, object, arrayOf, oneOfType, node } from "prop-types";
import { STATUS } from "./iocStatusData";
import { Stack } from "@mui/material";
import WarningAmberIcon from "@mui/icons-material/WarningAmber";
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
const propTypes = {
status: string,
theme: object,
children: oneOfType([arrayOf(node), node])
};
const commonStyles = {
position: "absolute",
top: "-10px",
left: "14px",
fontSize: "20px"
};
export const IOCStatusBadge = ({ status, theme, children }) => (
<Stack sx={{ position: "relative" }}>
{children}
{status === STATUS.warning ? (
<WarningAmberIcon
sx={{ fill: theme.palette.warning.main, ...commonStyles }}
/>
) : (
<>
{status === STATUS.alert || status === STATUS.inactiveAlert ? (
<ErrorOutlineIcon
sx={{ fill: theme.palette.error.main, ...commonStyles }}
/>
) : null}
</>
)}
</Stack>
);
IOCStatusBadge.propTypes = propTypes;
import React from "react";
import { string, arrayOf, object } from "prop-types";
import { SEVERITY } from "./iocStatusData";
import { Typography, Stack } from "@mui/material";
import { AlertBanner } from "@ess-ics/ce-ui-common";
const propsTypes = {
title: string,
alerts: arrayOf(object),
object
};
export const IOCStatusPopoverContent = ({ title, alerts }) => {
// Filter out INFO level alerts
// And for now filter out links on alerts due to issues with
// focus behavior of popovers
// Also normalize the type to lowercase since AlertBanner expects lowercase
const sanitizedAlerts = (
alerts?.filter((alert) => alert?.type.toLowerCase() !== SEVERITY.info) || []
).map((alert) => ({
...alert,
type: alert?.type?.toLowerCase(),
link: undefined
}));
return (
<Stack
gap={1}
sx={{ minWidth: 0, maxWidth: "50vw" }}
>
<Typography
sx={{
fontWeight: sanitizedAlerts.length > 0 ? "bold" : "normal"
}}
>
{title}
</Typography>
{sanitizedAlerts.length > 0
? sanitizedAlerts.map((alert, index) => (
<AlertBanner
{...alert}
key={index}
/>
))
: null}
</Stack>
);
};
IOCStatusPopoverContent.propsTypes = propsTypes;
import React from "react";
import { object, string } from "prop-types";
import { STATUS, statusConfig } from "./iocStatusData";
import { LabeledIcon } from "@ess-ics/ce-ui-common";
const propTypes = {
ioc: object,
status: string
};
const getLabel = (alerts, status) => {
if (
status === STATUS.alert ||
status === STATUS.warning ||
status === STATUS.inactiveAlert
) {
const warnings = alerts.filter((alert) => alert.type === "WARNING");
const errors = alerts.filter((alert) => alert.type === "ERROR");
const hasWarnings = Boolean(warnings.length);
const hasErrors = Boolean(errors.length);
return `${statusConfig[status].title} ${
hasWarnings ? warnings.length + " warnings" : ""
}${hasErrors ? errors.length + " errors" : ""} !`;
}
return statusConfig[status].title;
};
export const StatusIcon = ({ ioc, status }) => {
const { alerts = [] } = ioc;
return (
<LabeledIcon
label={getLabel(alerts, status)}
Icon={statusConfig[status].icon}
labelPosition="none"
/>
);
};
StatusIcon.propTypes = propTypes;
import {
Brightness1,
StopCircle,
RadioButtonUnchecked,
HelpOutline
} from "@mui/icons-material";
export const STATUS = {
active: "active",
disabled: "disabled",
alert: "alert",
warning: "warning",
inactive: "inactive",
inactiveAlert: "inactive alert"
};
export const SEVERITY = {
info: "info",
alert: "error",
warning: "warning"
};
export const statusConfig = {
[STATUS.active]: {
title: "Active",
icon: Brightness1
},
[STATUS.disabled]: {
title: "Disabled",
icon: StopCircle
},
[STATUS.alert]: {
title: "Alert",
icon: Brightness1
},
[STATUS.warning]: {
title: "Warning",
icon: Brightness1
},
[STATUS.inactive]: {
title: "Inactive",
icon: RadioButtonUnchecked
},
[STATUS.inactiveAlert]: {
title: "Alert",
icon: RadioButtonUnchecked
},
null: {
title: "Unknown",
icon: HelpOutline
}
};
export const getIocStatus = (ioc) => {
let { active, activeDeployment } = ioc;
const alertSeverity = ioc?.alertSeverity?.toLowerCase();
if (
active &&
(alertSeverity === undefined || alertSeverity === SEVERITY.info)
) {
// Active status / running
return STATUS.active;
} else if (
active === false &&
(alertSeverity === undefined || alertSeverity === SEVERITY.info)
) {
// stopped/paused
return STATUS.disabled;
} else if (
active !== undefined &&
alertSeverity !== undefined &&
alertSeverity === SEVERITY.alert
) {
// error
return STATUS.alert;
} else if (
active !== undefined &&
alertSeverity !== undefined &&
alertSeverity === SEVERITY.warning
) {
// warning
return STATUS.warning;
} else if (
(!active || activeDeployment === null) &&
(alertSeverity === undefined || alertSeverity === SEVERITY.info)
) {
// Inactive status
return STATUS.inactive;
} else if (
(!active || activeDeployment === null) &&
alertSeverity !== undefined &&
alertSeverity !== SEVERITY.info
) {
// inactive with warning/error
return STATUS.inactiveAlert;
} else {
// unknown fallback state
return null;
}
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment