Skip to content

Commit

Permalink
feat: chnage status from menu
Browse files Browse the repository at this point in the history
  • Loading branch information
shahargl committed Jun 17, 2024
1 parent 1f8da48 commit c076651
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,5 @@ storage

# otel files
tempo-data/

scripts/automatic_extraction_rules.py
139 changes: 139 additions & 0 deletions keep-ui/app/alerts/alert-change-status-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { Button, Title, Subtitle } from "@tremor/react";
import Modal from "@/components/ui/Modal";
import Select, { CSSObjectWithLabel, ControlProps, OptionProps, GroupBase } from "react-select";
import { useState } from "react";
import { AlertDto, Status } from "./models";
import { getApiURL } from "utils/apiUrl";
import { useSession } from "next-auth/react";
import { toast } from "react-toastify";
import {
CheckCircleIcon,
ExclamationCircleIcon,
PauseIcon,
XCircleIcon,
QuestionMarkCircleIcon,
} from "@heroicons/react/24/outline";
import { usePresets } from "utils/hooks/usePresets";
import { useAlerts } from "utils/hooks/useAlerts";

const statusIcons = {
[Status.Firing]: <ExclamationCircleIcon className="w-4 h-4 mr-2" />,
[Status.Resolved]: <CheckCircleIcon className="w-4 h-4 mr-2" />,
[Status.Acknowledged]: <PauseIcon className="w-4 h-4 mr-2" />,
[Status.Suppressed]: <XCircleIcon className="w-4 h-4 mr-2" />,
[Status.Pending]: <QuestionMarkCircleIcon className="w-4 h-4 mr-2" />,
};

const customSelectStyles = {
control: (base: CSSObjectWithLabel, state: ControlProps<{ value: Status; label: JSX.Element; }, false, GroupBase<{ value: Status; label: JSX.Element; }>>) => ({
...base,
borderColor: state.isFocused ? 'orange' : base.borderColor,
boxShadow: state.isFocused ? '0 0 0 1px orange' : base.boxShadow,
'&:hover': {
borderColor: 'orange',
},
}),
option: (base: CSSObjectWithLabel, { isFocused }: OptionProps<{ value: Status; label: JSX.Element; }, false, GroupBase<{ value: Status; label: JSX.Element; }>>) => ({
...base,
backgroundColor: isFocused ? 'rgba(255,165,0,0.1)' : base.backgroundColor,
'&:hover': {
backgroundColor: 'rgba(255,165,0,0.2)',
},
}),
};


interface Props {
alert: AlertDto | null | undefined;
handleClose: () => void;
}

export default function AlertChangeStatusModal({ alert, handleClose }: Props) {
const { data: session } = useSession();
const [selectedStatus, setSelectedStatus] = useState<Status | null>(null);
const { useAllPresets } = usePresets();
const { mutate: presetsMutator } = useAllPresets();
const { useAllAlerts } = useAlerts();
const { mutate: alertsMutator } = useAllAlerts({ revalidateOnMount: false });

if (!alert) return null;

const statusOptions = Object.values(Status)
.filter((status) => status !== alert.status) // Exclude current status
.map((status) => ({
value: status,
label: (
<div className="flex items-center">
{statusIcons[status]}
<span>{status.charAt(0).toUpperCase() + status.slice(1)}</span>
</div>
),
}));

const clearAndClose = () => {
setSelectedStatus(null);
handleClose();
};

const handleChangeStatus = async () => {
if (!selectedStatus) {
toast.error("Please select a new status.");
return;
}

try {
const response = await fetch(`${getApiURL()}/alerts/enrich`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session?.accessToken}`,
},
body: JSON.stringify({
enrichments: {
fingerprint: alert.fingerprint,
status: selectedStatus,
},
fingerprint: alert.fingerprint,
}),
});

if (response.ok) {
toast.success("Alert status changed successfully!");
clearAndClose();
await alertsMutator();
await presetsMutator();
} else {
toast.error("Failed to change alert status.");
}
} catch (error) {
toast.error("An error occurred while changing alert status.");
}
};

return (
<Modal onClose={handleClose} isOpen={!!alert}>
<Title>Change Alert Status</Title>
<Subtitle className="flex items-center">
Change status from <strong className="mx-2">{alert.status}</strong> to:
<div className="flex-1">
<Select
options={statusOptions}
value={statusOptions.find((option) => option.value === selectedStatus)}
onChange={(option) => setSelectedStatus(option?.value || null)}
placeholder="Select new status"
className="ml-2"
styles={customSelectStyles}
/>
</div>
</Subtitle>
<div className="flex justify-end mt-4 space-x-2">
<Button onClick={handleChangeStatus} color="orange">
Change Status
</Button>
<Button onClick={handleClose} color="orange" variant="secondary">
Cancel
</Button>
</div>
</Modal>
);
}
25 changes: 24 additions & 1 deletion keep-ui/app/alerts/alert-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Menu, Portal, Transition } from "@headlessui/react";
import { Fragment, useState } from "react";
import { Icon } from "@tremor/react";
import {
ChevronDoubleRightIcon,
ArchiveBoxIcon,
EllipsisHorizontalIcon,
PlusIcon,
Expand All @@ -27,6 +28,7 @@ interface Props {
setIsMenuOpen: (key: string) => void;
setRunWorkflowModalAlert?: (alert: AlertDto) => void;
setDismissModalAlert?: (alert: AlertDto[]) => void;
setChangeStatusAlert?: (alert: AlertDto) => void;
presetName: string;
}

Expand All @@ -36,6 +38,7 @@ export default function AlertMenu({
setIsMenuOpen,
setRunWorkflowModalAlert,
setDismissModalAlert,
setChangeStatusAlert, // Added prop
presetName,
}: Props) {
const router = useRouter();
Expand Down Expand Up @@ -87,7 +90,7 @@ export default function AlertMenu({
const callAssignEndpoint = async (unassign: boolean = false) => {
if (
confirm(
"After assiging this alert to yourself, you won't be able to unassign it until someone else assigns it to himself. Are you sure you want to continue?"
"After assigning this alert to yourself, you won't be able to unassign it until someone else assigns it to himself. Are you sure you want to continue?"
)
) {
const res = await fetch(
Expand Down Expand Up @@ -337,6 +340,26 @@ export default function AlertMenu({
</button>
)}
</Menu.Item>
{/* Change Status */}
<Menu.Item>
{({ active }) => (
<button
onClick={() => {
setChangeStatusAlert?.(alert);
handleCloseMenu();
}}
className={`${
active ? "bg-slate-200" : "text-gray-900"
} group flex w-full items-center rounded-md px-2 py-2 text-xs`}
>
<ChevronDoubleRightIcon
className="mr-2 h-4 w-4"
aria-hidden="true"
/>
Change Status
</button>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
Expand Down
3 changes: 3 additions & 0 deletions keep-ui/app/alerts/alert-table-tab-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
setNoteModalAlert: (alert: AlertDto | null) => void;
setRunWorkflowModalAlert: (alert: AlertDto | null) => void;
setDismissModalAlert: (alert: AlertDto[] | null) => void;
setChangeStatusAlert: (alert: AlertDto | null) => void;
}

export default function AlertTableTabPanel({
Expand All @@ -40,6 +41,7 @@
setNoteModalAlert,
setRunWorkflowModalAlert,
setDismissModalAlert,
setChangeStatusAlert,
}: Props) {
const sortedPresetAlerts = alerts
.filter((alert) => getPresetAlerts(alert, preset.name))
Expand Down Expand Up @@ -74,6 +76,7 @@
setNoteModalAlert: setNoteModalAlert,
setRunWorkflowModalAlert: setRunWorkflowModalAlert,
setDismissModalAlert: setDismissModalAlert,
setChangeStatusAlert: setChangeStatusAlert,
presetName: preset.name,
presetNoisy: preset.is_noisy,
});
Expand Down
3 changes: 3 additions & 0 deletions keep-ui/app/alerts/alert-table-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ interface GenerateAlertTableColsArg {
setTicketModalAlert?: (alert: AlertDto) => void;
setRunWorkflowModalAlert?: (alert: AlertDto) => void;
setDismissModalAlert?: (alert: AlertDto[]) => void;
setChangeStatusAlert?: (alert: AlertDto) => void;
presetName: string;
presetNoisy?: boolean;
}
Expand All @@ -122,6 +123,7 @@ export const useAlertTableCols = (
setTicketModalAlert,
setRunWorkflowModalAlert,
setDismissModalAlert,
setChangeStatusAlert,
presetName,
presetNoisy = false,
}: GenerateAlertTableColsArg = { presetName: "feed" }
Expand Down Expand Up @@ -330,6 +332,7 @@ export const useAlertTableCols = (
setIsMenuOpen={setCurrentOpenMenu}
setRunWorkflowModalAlert={setRunWorkflowModalAlert}
setDismissModalAlert={setDismissModalAlert}
setChangeStatusAlert={setChangeStatusAlert}
/>
),
}),
Expand Down
7 changes: 7 additions & 0 deletions keep-ui/app/alerts/alerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import AlertRunWorkflowModal from "./alert-run-workflow-modal";
import AlertDismissModal from "./alert-dismiss-modal";
import { ViewAlertModal } from "./ViewAlertModal";
import { useRouter, useSearchParams } from "next/navigation";
import AlertChangeStatusModal from "./alert-change-status-modal";

const defaultPresets: Preset[] = [
{
Expand Down Expand Up @@ -71,6 +72,7 @@ export default function Alerts({ presetName }: AlertsProps) {
const [dismissModalAlert, setDismissModalAlert] = useState<
AlertDto[] | null
>();
const [changeStatusAlert, setChangeStatusAlert] = useState<AlertDto | null>();
const [viewAlertModal, setViewAlertModal] = useState<AlertDto | null>();
const { useAllPresets } = usePresets();

Expand Down Expand Up @@ -117,6 +119,7 @@ export default function Alerts({ presetName }: AlertsProps) {
setNoteModalAlert={setNoteModalAlert}
setRunWorkflowModalAlert={setRunWorkflowModalAlert}
setDismissModalAlert={setDismissModalAlert}
setChangeStatusAlert={setChangeStatusAlert}
/>

{selectedPreset && (
Expand All @@ -140,6 +143,10 @@ export default function Alerts({ presetName }: AlertsProps) {
alert={dismissModalAlert}
handleClose={() => setDismissModalAlert(null)}
/>
<AlertChangeStatusModal
alert={changeStatusAlert}
handleClose={() => setChangeStatusAlert(null)}
/>
<ViewAlertModal
alert={viewAlertModal}
handleClose={() => router.replace(`/alerts/${presetName}`)}
Expand Down
11 changes: 10 additions & 1 deletion keep-ui/app/alerts/models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ export const severityMapping: { [id: number]: string } = {
5: Severity.Critical,
};

export enum Status {
Firing = "firing",
Resolved = "resolved",
Acknowledged = "acknowledged",
Suppressed = "suppressed",
Pending = "pending",
}


export interface AlertDto {
id: string;
name: string;
status: string;
status: Status;
lastReceived: Date;
environment: string;
isDuplicate?: boolean;
Expand Down

0 comments on commit c076651

Please sign in to comment.