mirror of
https://github.com/prymitive/karma
synced 2026-05-11 03:46:48 +00:00
191 lines
5.4 KiB
TypeScript
191 lines
5.4 KiB
TypeScript
import React, {
|
|
FC,
|
|
Ref,
|
|
CSSProperties,
|
|
useRef,
|
|
useState,
|
|
useCallback,
|
|
} from "react";
|
|
|
|
import { observer } from "mobx-react-lite";
|
|
|
|
import { Manager, Reference, Popper } from "react-popper";
|
|
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import { faCaretDown } from "@fortawesome/free-solid-svg-icons/faCaretDown";
|
|
import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
|
|
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalLinkAlt";
|
|
|
|
import { APIAlertT, APIAlertGroupT } from "Models/APITypes";
|
|
import { AlertStore } from "Stores/AlertStore";
|
|
import {
|
|
SilenceFormStore,
|
|
AlertmanagerClustersToOption,
|
|
} from "Stores/SilenceFormStore";
|
|
import { CommonPopperModifiers } from "Common/Popper";
|
|
import { FetchPauser } from "Components/FetchPauser";
|
|
import { DropdownSlide } from "Components/Animations/DropdownSlide";
|
|
import { DateFromNow } from "Components/DateFromNow";
|
|
import { useOnClickOutside } from "Hooks/useOnClickOutside";
|
|
|
|
const PopperModifiers = [
|
|
...CommonPopperModifiers,
|
|
{ name: "offset", options: { offset: "-5px, 0px" } },
|
|
];
|
|
|
|
const onSilenceClick = (
|
|
alertStore: AlertStore,
|
|
silenceFormStore: SilenceFormStore,
|
|
group: APIAlertGroupT,
|
|
alert: APIAlertT
|
|
) => {
|
|
const clusters: { [cluster: string]: string[] } = {};
|
|
Object.entries(alertStore.data.clustersWithoutReadOnly).forEach(
|
|
([cluster, members]) => {
|
|
if (alert.alertmanager.map((am) => am.cluster).includes(cluster)) {
|
|
clusters[cluster] = members;
|
|
}
|
|
}
|
|
);
|
|
|
|
silenceFormStore.data.resetProgress();
|
|
silenceFormStore.data.fillMatchersFromGroup(
|
|
group,
|
|
alertStore.settings.values.silenceForm.strip.labels,
|
|
AlertmanagerClustersToOption(clusters),
|
|
[alert]
|
|
);
|
|
silenceFormStore.tab.setTab("editor");
|
|
silenceFormStore.toggle.show();
|
|
};
|
|
|
|
const MenuContent: FC<{
|
|
popperPlacement?: string;
|
|
popperRef?: Ref<HTMLDivElement>;
|
|
popperStyle?: CSSProperties;
|
|
group: APIAlertGroupT;
|
|
alert: APIAlertT;
|
|
afterClick: () => void;
|
|
alertStore: AlertStore;
|
|
silenceFormStore: SilenceFormStore;
|
|
}> = ({
|
|
popperPlacement,
|
|
popperRef,
|
|
popperStyle,
|
|
group,
|
|
alert,
|
|
afterClick,
|
|
alertStore,
|
|
silenceFormStore,
|
|
}) => {
|
|
return (
|
|
<FetchPauser alertStore={alertStore}>
|
|
<div
|
|
className="dropdown-menu d-block shadow"
|
|
ref={popperRef}
|
|
style={popperStyle}
|
|
data-placement={popperPlacement}
|
|
>
|
|
<h6 className="dropdown-header">Alert source links:</h6>
|
|
{alert.alertmanager.map((am) => (
|
|
<a
|
|
key={am.name}
|
|
className="dropdown-item"
|
|
href={am.source}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
onClick={afterClick}
|
|
>
|
|
<FontAwesomeIcon className="mr-1" icon={faExternalLinkAlt} />
|
|
{am.name}
|
|
</a>
|
|
))}
|
|
<div className="dropdown-divider" />
|
|
<div
|
|
className={`dropdown-item ${
|
|
Object.keys(alertStore.data.clustersWithoutReadOnly).length === 0
|
|
? "disabled"
|
|
: "cursor-pointer"
|
|
}`}
|
|
onClick={() => {
|
|
if (Object.keys(alertStore.data.clustersWithoutReadOnly).length) {
|
|
onSilenceClick(alertStore, silenceFormStore, group, alert);
|
|
afterClick();
|
|
}
|
|
}}
|
|
>
|
|
<FontAwesomeIcon className="mr-1" icon={faBellSlash} />
|
|
Silence this alert
|
|
</div>
|
|
</div>
|
|
</FetchPauser>
|
|
);
|
|
};
|
|
|
|
const AlertMenu: FC<{
|
|
group: APIAlertGroupT;
|
|
alert: APIAlertT;
|
|
alertStore: AlertStore;
|
|
silenceFormStore: SilenceFormStore;
|
|
setIsMenuOpen: (isOpen: boolean) => void;
|
|
}> = observer(
|
|
({ group, alert, alertStore, silenceFormStore, setIsMenuOpen }) => {
|
|
const [isHidden, setIsHidden] = useState<boolean>(true);
|
|
|
|
const toggle = useCallback(() => {
|
|
setIsMenuOpen(isHidden);
|
|
setIsHidden(!isHidden);
|
|
}, [isHidden, setIsMenuOpen]);
|
|
|
|
const hide = useCallback(() => {
|
|
setIsHidden(true);
|
|
setIsMenuOpen(false);
|
|
}, [setIsMenuOpen]);
|
|
|
|
const rootRef = useRef<HTMLSpanElement | null>(null);
|
|
useOnClickOutside(rootRef, hide, !isHidden);
|
|
|
|
return (
|
|
<span ref={rootRef}>
|
|
<Manager>
|
|
<Reference>
|
|
{({ ref }) => (
|
|
<span
|
|
className="components-label components-label-with-hover px-1 mr-1 badge badge-secondary cursor-pointer"
|
|
ref={ref}
|
|
onClick={toggle}
|
|
data-toggle="dropdown"
|
|
>
|
|
<FontAwesomeIcon
|
|
className="pr-1"
|
|
style={{ width: "0.8rem" }}
|
|
icon={faCaretDown}
|
|
/>
|
|
<DateFromNow timestamp={alert.startsAt} />
|
|
</span>
|
|
)}
|
|
</Reference>
|
|
<DropdownSlide in={!isHidden} unmountOnExit>
|
|
<Popper placement="bottom-start" modifiers={PopperModifiers}>
|
|
{({ placement, ref, style }) => (
|
|
<MenuContent
|
|
popperPlacement={placement}
|
|
popperRef={ref}
|
|
popperStyle={style}
|
|
group={group}
|
|
alert={alert}
|
|
alertStore={alertStore}
|
|
silenceFormStore={silenceFormStore}
|
|
afterClick={hide}
|
|
/>
|
|
)}
|
|
</Popper>
|
|
</DropdownSlide>
|
|
</Manager>
|
|
</span>
|
|
);
|
|
}
|
|
);
|
|
|
|
export { AlertMenu, MenuContent };
|