diff --git a/keep-ui/app/alerts/alert-actions.tsx b/keep-ui/app/alerts/alert-actions.tsx index d6ca4e316b..ec26b7ca41 100644 --- a/keep-ui/app/alerts/alert-actions.tsx +++ b/keep-ui/app/alerts/alert-actions.tsx @@ -4,6 +4,10 @@ import { getSession } from "next-auth/react"; import { getApiURL } from "utils/apiUrl"; import { AlertDto } from "./models"; import { useAlerts } from "utils/hooks/useAlerts"; +import { PlusIcon } from "@radix-ui/react-icons"; +import { toast } from "react-toastify"; +import { usePresets } from "utils/hooks/usePresets"; +import { usePathname, useRouter } from "next/navigation"; interface Props { selectedRowIds: string[]; @@ -11,8 +15,18 @@ interface Props { } export default function AlertActions({ selectedRowIds, alerts }: Props) { + const pathname = usePathname(); + const router = useRouter(); const { useAllAlerts } = useAlerts(); - const { mutate } = useAllAlerts(); + const { mutate } = useAllAlerts({ revalidateOnFocus: false }); + const { useAllPresets } = usePresets(); + const { mutate: presetsMutator } = useAllPresets({ + revalidateOnFocus: false, + }); + + const selectedAlerts = alerts.filter((_alert, index) => + selectedRowIds.includes(index.toString()) + ); const onDelete = async () => { const confirmed = confirm( @@ -23,10 +37,6 @@ export default function AlertActions({ selectedRowIds, alerts }: Props) { const session = await getSession(); const apiUrl = getApiURL(); - const selectedAlerts = alerts.filter((_alert, index) => - selectedRowIds.includes(index.toString()) - ); - for await (const alert of selectedAlerts) { const { fingerprint } = alert; @@ -51,6 +61,36 @@ export default function AlertActions({ selectedRowIds, alerts }: Props) { } }; + async function addOrUpdatePreset() { + const presetName = prompt("Enter new preset name"); + if (presetName) { + const distinctAlertNames = Array.from( + new Set(selectedAlerts.map((alert) => alert.name)) + ); + const options = distinctAlertNames.map((name) => { + return { value: `name=${name}`, label: `name=${name}` }; + }); + const session = await getSession(); + const apiUrl = getApiURL(); + const response = await fetch(`${apiUrl}/preset`, { + method: "POST", + headers: { + Authorization: `Bearer ${session?.accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ name: presetName, options: options }), + }); + if (response.ok) { + toast(`Preset ${presetName} created!`, { + position: "top-left", + type: "success", + }); + presetsMutator(); + router.replace(`${pathname}?selectedPreset=${presetName}`); + } + } + } + return (
+
); } diff --git a/keep-ui/app/alerts/alert-assign-ticket-modal.tsx b/keep-ui/app/alerts/alert-assign-ticket-modal.tsx new file mode 100644 index 0000000000..be21071326 --- /dev/null +++ b/keep-ui/app/alerts/alert-assign-ticket-modal.tsx @@ -0,0 +1,199 @@ +import React from 'react'; +import Select, { components } from 'react-select'; +import { Dialog } from '@headlessui/react'; +import { Button, TextInput } from '@tremor/react'; +import { PlusIcon } from '@heroicons/react/20/solid' +import { useForm, Controller, SubmitHandler } from 'react-hook-form'; +import { Providers } from "./../providers/providers"; +import { useSession } from "next-auth/react"; +import { getApiURL } from 'utils/apiUrl'; + +interface AlertAssignTicketModalProps { + isOpen: boolean; + onClose: () => void; + ticketingProviders: Providers; // Replace 'ProviderType' with the actual type of ticketingProviders + alertFingerprint: string; // Replace 'string' with the actual type of alertFingerprint +} + +interface OptionType { + value: string; + label: string; + id: string; + type: string; + icon?: string; + isAddProvider?: boolean; +} + +interface FormData { + provider: { + id: string; + value: string; + type: string; + }; + ticket_url: string; +} + +const AlertAssignTicketModal = ({ isOpen, onClose, ticketingProviders, alertFingerprint }: AlertAssignTicketModalProps) => { + const { handleSubmit, control, formState: { errors } } = useForm(); + // get the token + const { data: session } = useSession(); + + const onSubmit: SubmitHandler = async (data) => { + try { + // build the formData + const requestData = { + enrichments: { + ticket_type: data.provider.type, + ticket_url: data.ticket_url, + ticket_provider_id: data.provider.value, + }, + fingerprint: alertFingerprint, + }; + + + const response = await fetch(`${getApiURL()}/alerts/enrich`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${session?.accessToken}`, + }, + body: JSON.stringify(requestData), + }); + + if (response.ok) { + // Handle success + console.log("Ticket assigned successfully"); + onClose(); + } else { + // Handle error + console.error("Failed to assign ticket"); + } + } catch (error) { + // Handle unexpected error + console.error("An unexpected error occurred"); + } + }; + + const providerOptions: OptionType[] = ticketingProviders.map((provider) => ({ + id: provider.id, + value: provider.id, + label: provider.details.name || '', + type: provider.type, + })); + + const customOptions: OptionType[] = [ + ...providerOptions, + { + value: 'add_provider', + label: 'Add another ticketing provider', + icon: 'plus', + isAddProvider: true, + id: 'add_provider', + type: '', + }, + ]; + + const handleOnChange = (option: any) => { + if (option.value === 'add_provider') { + window.open('/providers?labels=ticketing', '_blank'); + } + }; + + + const Option = (props: any) => { + // Check if the option is 'add_provider' + const isAddProvider = props.data.isAddProvider; + + return ( + +
+ {isAddProvider ? ( + + ) : ( + props.data.type && + )} + {props.data.label} +
+
+ ); + }; + + const SingleValue = (props: any) => { + const { children, data } = props; + + return ( + +
+ {data.isAddProvider ? ( + + ) : ( + data.type && + )} + {children} +
+
+ ); + }; + + return ( + +
+ +
+ Assign Ticket + {ticketingProviders.length > 0 ? ( +
+
+ + ( +