-
- {presetName ? "Update preset" : "Enter new preset name"}
-
+
{presetName ? "Update preset" : "Enter new preset name"}
diff --git a/keep-ui/app/alerts/alert-push-alert-to-server-modal.tsx b/keep-ui/app/alerts/alert-push-alert-to-server-modal.tsx
index 6362abc54..a842a0641 100644
--- a/keep-ui/app/alerts/alert-push-alert-to-server-modal.tsx
+++ b/keep-ui/app/alerts/alert-push-alert-to-server-modal.tsx
@@ -9,7 +9,7 @@ import {
} from "@tremor/react";
import Modal from "@/components/ui/Modal";
import { useSession } from "next-auth/react";
-import { getApiURL } from "utils/apiUrl";
+import { useApiUrl } from "utils/hooks/useConfig";
import { useProviders } from "utils/hooks/useProviders";
import ImageWithFallback from "@/components/ImageWithFallback";
import { useAlerts } from "utils/hooks/useAlerts";
@@ -45,6 +45,7 @@ const PushAlertToServerModal = ({
const { data: session } = useSession();
const { data: providersData } = useProviders();
+ const apiUrl = useApiUrl();
const providers = providersData?.providers || [];
useEffect(() => {
@@ -82,7 +83,7 @@ const PushAlertToServerModal = ({
try {
const response = await fetch(
- `${getApiURL()}/alerts/event/${selectedSource.type}`,
+ `${apiUrl}/alerts/event/${selectedSource.type}`,
{
method: "POST",
headers: {
diff --git a/keep-ui/app/alerts/alert-run-workflow-modal.tsx b/keep-ui/app/alerts/alert-run-workflow-modal.tsx
new file mode 100644
index 000000000..3ade1e15a
--- /dev/null
+++ b/keep-ui/app/alerts/alert-run-workflow-modal.tsx
@@ -0,0 +1,88 @@
+import { Button, Select, SelectItem } from "@tremor/react";
+import { AlertDto } from "./models";
+import Modal from "@/components/ui/Modal";
+import { useWorkflows } from "utils/hooks/useWorkflows";
+import { useState } from "react";
+import { useSession } from "next-auth/react";
+import { useApiUrl } from "utils/hooks/useConfig";
+import { toast } from "react-toastify";
+import { useRouter } from "next/navigation";
+
+interface Props {
+ alert: AlertDto | null | undefined;
+ handleClose: () => void;
+}
+
+export default function AlertRunWorkflowModal({ alert, handleClose }: Props) {
+ /**
+ *
+ */
+ const [selectedWorkflowId, setSelectedWorkflowId] = useState<
+ string | undefined
+ >(undefined);
+ const { data: workflows } = useWorkflows({});
+ const { data: session } = useSession();
+ const router = useRouter();
+ const apiUrl = useApiUrl();
+
+ const isOpen = !!alert;
+
+ const clearAndClose = () => {
+ setSelectedWorkflowId(undefined);
+ handleClose();
+ };
+
+ const handleRun = async () => {
+ const response = await fetch(
+ `${apiUrl}/workflows/${selectedWorkflowId}/run`,
+ {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${session?.accessToken}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(alert),
+ }
+ );
+
+ if (response.ok) {
+ // Workflow started successfully
+ toast.success("Workflow started successfully", { position: "top-left" });
+ const responseData = await response.json();
+ const { workflow_execution_id } = responseData;
+ router.push(
+ `/workflows/${selectedWorkflowId}/runs/${workflow_execution_id}`
+ );
+ } else {
+ toast.error("Failed to start workflow", { position: "top-left" });
+ }
+ clearAndClose();
+ };
+
+ return (
+
+ {workflows && (
+
+ )}
+
+
+ );
+}
diff --git a/keep-ui/app/alerts/alert-tab-modal.tsx b/keep-ui/app/alerts/alert-tab-modal.tsx
index dd72657c4..d5a99e7d6 100644
--- a/keep-ui/app/alerts/alert-tab-modal.tsx
+++ b/keep-ui/app/alerts/alert-tab-modal.tsx
@@ -3,23 +3,27 @@ import Modal from "@/components/ui/Modal";
import { Button, TextInput } from "@tremor/react";
import { AlertsRulesBuilder } from "app/alerts/alerts-rules-builder";
import { useSession } from "next-auth/react";
-import { getApiURL } from "utils/apiUrl";
+import { useApiUrl } from "utils/hooks/useConfig";
interface AlertTabModalProps {
presetId: string;
isOpen: boolean;
onClose: () => void;
onAddTab: (name: string, filter: string) => void;
-
}
-const AlertTabModal = ({ presetId, isOpen, onClose, onAddTab }: AlertTabModalProps) => {
+const AlertTabModal = ({
+ presetId,
+ isOpen,
+ onClose,
+ onAddTab,
+}: AlertTabModalProps) => {
const [newTabName, setNewTabName] = useState("");
const [newTabFilter, setNewTabFilter] = useState
("");
const [errors, setErrors] = useState({ name: false, filter: false });
const [backendError, setBackendError] = useState(null);
const { data: session } = useSession();
-
+ const apiUrl = useApiUrl();
const handleAddTab = async () => {
if (!newTabName || !newTabFilter) {
setErrors({
@@ -31,18 +35,22 @@ const AlertTabModal = ({ presetId, isOpen, onClose, onAddTab }: AlertTabModalPro
try {
// Send the new tab data to the backend
- const apiUrl = getApiURL();
const response = await fetch(`${apiUrl}/preset/${presetId}/tab`, {
- method: 'POST',
+ method: "POST",
headers: {
- 'Content-Type': 'application/json',
+ "Content-Type": "application/json",
Authorization: `Bearer ${session?.accessToken}`,
},
body: JSON.stringify({ name: newTabName, filter: newTabFilter }),
});
if (!response.ok) {
- throw new Error('Failed to add the new tab: ' + response.status + ' ' + response.statusText);
+ throw new Error(
+ "Failed to add the new tab: " +
+ response.status +
+ " " +
+ response.statusText
+ );
}
onAddTab(newTabName, newTabFilter);
@@ -51,10 +59,9 @@ const AlertTabModal = ({ presetId, isOpen, onClose, onAddTab }: AlertTabModalPro
setBackendError(null); // Clear any previous backend errors
onClose();
} catch (error) {
- if(error instanceof Error) {
+ if (error instanceof Error) {
setBackendError(error.message);
- }
- else{
+ } else {
setBackendError("An error occurred while adding the tab");
}
}
@@ -97,17 +104,21 @@ const AlertTabModal = ({ presetId, isOpen, onClose, onAddTab }: AlertTabModalPro
{errors.filter && (
Filter is required
)}
- {backendError && (
- {backendError}
- )}
+ {backendError && {backendError}
}
diff --git a/keep-ui/app/alerts/alert-tabs.tsx b/keep-ui/app/alerts/alert-tabs.tsx
index 6c60fe9a2..37e93a0c7 100644
--- a/keep-ui/app/alerts/alert-tabs.tsx
+++ b/keep-ui/app/alerts/alert-tabs.tsx
@@ -1,10 +1,18 @@
import { FormEventHandler, useState } from "react";
-import { Button, TextInput, Tab, TabGroup, TabList, TabPanel, TabPanels } from "@tremor/react";
+import {
+ Button,
+ TextInput,
+ Tab,
+ TabGroup,
+ TabList,
+ TabPanel,
+ TabPanels,
+} from "@tremor/react";
import { AlertDto } from "./models";
import AlertTabModal from "./alert-tab-modal";
import { evalWithContext } from "./alerts-rules-builder";
import { XMarkIcon } from "@heroicons/react/24/outline";
-import { getApiURL } from "utils/apiUrl";
+import { useApiUrl } from "utils/hooks/useConfig";
import { useSession } from "next-auth/react";
interface Tab {
id?: string;
@@ -20,16 +28,26 @@ interface Props {
setSelectedTab: (index: number) => void;
}
-const AlertTabs = ({ presetId, tabs, setTabs, selectedTab, setSelectedTab }: Props) => {
+const AlertTabs = ({
+ presetId,
+ tabs,
+ setTabs,
+ selectedTab,
+ setSelectedTab,
+}: Props) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const { data: session } = useSession();
+ const apiUrl = useApiUrl();
const handleTabChange = (index: any) => {
setSelectedTab(index);
};
const addNewTab = (name: string, filter: string) => {
- const newTab = { name: name, filter: (alert: AlertDto) => evalWithContext(alert, filter) };
+ const newTab = {
+ name: name,
+ filter: (alert: AlertDto) => evalWithContext(alert, filter),
+ };
const updatedTabs = [...tabs];
updatedTabs.splice(tabs.length - 1, 0, newTab); // Insert the new tab before the last tab
@@ -38,20 +56,21 @@ const AlertTabs = ({ presetId, tabs, setTabs, selectedTab, setSelectedTab }: Pro
setSelectedTab(0); // Set the selected tab to first tab
};
-
const deleteTab = async (index: number) => {
const tabToDelete = tabs[index];
// if the tab has id, means it already in the database
try {
- const apiUrl = getApiURL();
- const response = await fetch(`${apiUrl}/preset/${presetId}/tab/${tabToDelete.id}`, {
- method: "DELETE",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${session?.accessToken}`,
- },
- });
+ const response = await fetch(
+ `${apiUrl}/preset/${presetId}/tab/${tabToDelete.id}`,
+ {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${session?.accessToken}`,
+ },
+ }
+ );
if (!response.ok) {
throw new Error("Failed to delete the tab");
@@ -72,38 +91,39 @@ const AlertTabs = ({ presetId, tabs, setTabs, selectedTab, setSelectedTab }: Pro
- <>
+ <>
{tabs.slice(0, -1).map((tab, index) => (
-
- {tab.name.toLowerCase()}
- {index !== 0 && (
-
- )}
-
+
+ {tab.name.toLowerCase()}
+ {index !== 0 && (
+
+ )}
+
))}
- setIsModalOpen(true)}>{tabs[tabs.length - 1].name}
- >
+ setIsModalOpen(true)}>
+ {tabs[tabs.length - 1].name}
+
+ >
- {tabs.slice(0, -1).map((tab, index) => (
+ {tabs.slice(0, -1).map((tab, index) => (
- ))}
-
+ ))}
+
-
-
-
setIsModalOpen(false)}
- onAddTab={addNewTab}
- presetId={presetId}
+
- />
+ setIsModalOpen(false)}
+ onAddTab={addNewTab}
+ presetId={presetId}
+ />
);
};
diff --git a/keep-ui/app/dashboard/[id]/dashboard.tsx b/keep-ui/app/dashboard/[id]/dashboard.tsx
index 627b9773e..fef94ac8c 100644
--- a/keep-ui/app/dashboard/[id]/dashboard.tsx
+++ b/keep-ui/app/dashboard/[id]/dashboard.tsx
@@ -1,20 +1,20 @@
-'use client';
-import { useParams } from 'next/navigation';
-import { useState, useEffect, ChangeEvent } from 'react';
-import GridLayout from '../GridLayout';
+"use client";
+import { useParams } from "next/navigation";
+import { useState, useEffect, ChangeEvent } from "react";
+import GridLayout from "../GridLayout";
import { usePresets } from "utils/hooks/usePresets";
-import WidgetModal from '../WidgetModal';
-import { Button, Card, TextInput, Subtitle, Icon } from '@tremor/react';
-import { LayoutItem, WidgetData, Threshold, GenericsMertics } from '../types';
-import { Preset } from 'app/alerts/models';
-import { FiSave, FiEdit2 } from 'react-icons/fi';
-import { useSession } from 'next-auth/react';
-import { useDashboards } from 'utils/hooks/useDashboards';
-import { getApiURL } from 'utils/apiUrl';
-import './../styles.css';
-import { toast } from 'react-toastify';
-import { GenericFilters } from '@/components/filters/GenericFilters';
-import { useDashboardPreset } from 'utils/hooks/useDashboardPresets';
+import WidgetModal from "../WidgetModal";
+import { Button, Card, TextInput, Subtitle, Icon } from "@tremor/react";
+import { LayoutItem, WidgetData, Threshold, GenericsMertics } from "../types";
+import { Preset } from "app/alerts/models";
+import { FiSave, FiEdit2 } from "react-icons/fi";
+import { useSession } from "next-auth/react";
+import { useDashboards } from "utils/hooks/useDashboards";
+import { useApiUrl } from "utils/hooks/useConfig";
+import "./../styles.css";
+import { toast } from "react-toastify";
+import { GenericFilters } from "@/components/filters/GenericFilters";
+import { useDashboardPreset } from "utils/hooks/useDashboardPresets";
const DASHBOARD_FILTERS = [
{
@@ -22,8 +22,8 @@ const DASHBOARD_FILTERS = [
key: "time_stamp",
value: "",
name: "Last received",
- }
-]
+ },
+];
const DashboardPage = () => {
const allPresets = useDashboardPreset();
@@ -36,10 +36,13 @@ const DashboardPage = () => {
const [editingItem, setEditingItem] = useState
(null);
const [dashboardName, setDashboardName] = useState(decodeURIComponent(id));
const [isEditingName, setIsEditingName] = useState(false);
+ const apiUrl = useApiUrl();
useEffect(() => {
if (!isLoading) {
- const dashboard = dashboards?.find(d => d.dashboard_name === decodeURIComponent(id));
+ const dashboard = dashboards?.find(
+ (d) => d.dashboard_name === decodeURIComponent(id)
+ );
if (dashboard) {
setLayout(dashboard.dashboard_config.layout);
setWidgetData(dashboard.dashboard_config.widget_data);
@@ -54,7 +57,13 @@ const DashboardPage = () => {
};
const closeModal = () => setIsModalOpen(false);
- const handleAddWidget = (preset: Preset|null, thresholds: Threshold[], name: string, widgetType?: string, genericMetrics?: GenericsMertics|null) => {
+ const handleAddWidget = (
+ preset: Preset | null,
+ thresholds: Threshold[],
+ name: string,
+ widgetType?: string,
+ genericMetrics?: GenericsMertics | null
+ ) => {
const uniqueId = `w-${Date.now()}`;
const newItem: LayoutItem = {
i: uniqueId,
@@ -64,14 +73,14 @@ const DashboardPage = () => {
h: genericMetrics ? 20 : 3,
minW: genericMetrics ? 10 : 2,
minH: genericMetrics ? 15 : 2,
- static: false
+ static: false,
};
const newWidget: WidgetData = {
...newItem,
thresholds,
preset,
name,
- widgetType: widgetType || 'preset',
+ widgetType: widgetType || "preset",
genericMetrics: genericMetrics || null,
};
setLayout((prevLayout) => [...prevLayout, newItem]);
@@ -79,14 +88,13 @@ const DashboardPage = () => {
};
const handleEditWidget = (id: string, update?: WidgetData) => {
- let itemToEdit = widgetData.find(d => d.i === id) || null;
- if(itemToEdit && update){
- setEditingItem({...itemToEdit, ...update});
- }else {
+ let itemToEdit = widgetData.find((d) => d.i === id) || null;
+ if (itemToEdit && update) {
+ setEditingItem({ ...itemToEdit, ...update });
+ } else {
setEditingItem(itemToEdit);
}
setIsModalOpen(true);
-
};
const handleSaveEdit = (updatedItem: WidgetData) => {
@@ -97,8 +105,8 @@ const DashboardPage = () => {
};
const handleDeleteWidget = (id: string) => {
- setLayout(layout.filter(item => item.i !== id));
- setWidgetData(widgetData.filter(item => item.i !== id));
+ setLayout(layout.filter((item) => item.i !== id));
+ setWidgetData(widgetData.filter((item) => item.i !== id));
};
const handleLayoutChange = (newLayout: LayoutItem[]) => {
@@ -113,24 +121,27 @@ const DashboardPage = () => {
const handleSaveDashboard = async () => {
try {
- const apiUrl = getApiURL();
- let dashboard = dashboards?.find(d => d.dashboard_name === decodeURIComponent(id));
- const method = dashboard ? 'PUT' : 'POST';
- const endpoint = `${apiUrl}/dashboard${dashboard ? `/${encodeURIComponent(dashboard.id)}` : ''}`;
+ let dashboard = dashboards?.find(
+ (d) => d.dashboard_name === decodeURIComponent(id)
+ );
+ const method = dashboard ? "PUT" : "POST";
+ const endpoint = `${apiUrl}/dashboard${
+ dashboard ? `/${encodeURIComponent(dashboard.id)}` : ""
+ }`;
const response = await fetch(endpoint, {
method,
headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${session!.accessToken}`
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${session!.accessToken}`,
},
body: JSON.stringify({
dashboard_name: dashboardName,
dashboard_config: {
layout,
widget_data: widgetData,
- }
- })
+ },
+ }),
});
if (!response.ok) {
@@ -167,7 +178,9 @@ const DashboardPage = () => {
className="border-orange-500 focus:border-orange-600 focus:ring-orange-600"
/>
) : (
- {dashboardName}
+
+ {dashboardName}
+
)}
{
/>