diff --git a/web/src/components/Playbooks/workflows/BasicDetails.jsx b/web/src/components/Playbooks/workflows/BasicDetails.jsx
new file mode 100644
index 000000000..11b7f76c6
--- /dev/null
+++ b/web/src/components/Playbooks/workflows/BasicDetails.jsx
@@ -0,0 +1,110 @@
+import React from "react";
+import ValueComponent from "../../ValueComponent";
+import SelectComponent from "../../SelectComponent";
+import { RefreshRounded } from "@mui/icons-material";
+import { CircularProgress } from "@mui/material";
+import { useGetPlaybooksQuery } from "../../../store/features/playbook/api/index.ts";
+import { useSelector } from "react-redux";
+import { currentWorkflowSelector } from "../../../store/features/workflow/workflowSlice.ts";
+import SlackTriggerForm from "./triggers/SlackTriggerForm.jsx";
+import { triggerTypes } from "../../../utils/workflow/triggerTypes.ts";
+import { handleInput, handleSelect } from "./utils/handleInputs.ts";
+
+function BasicDetails() {
+ const {
+ data,
+ isFetching: playbooksLoading,
+ refetch,
+ } = useGetPlaybooksQuery({});
+ const currentWorkflow = useSelector(currentWorkflowSelector);
+
+ return (
+ <>
+
+
+
+ Workflow Name
+
+ handleInput("name", val)}
+ />
+
+
+
+ Select Playbook
+
+ + Create New
+
+
+
+ {
+ return {
+ id: e.id,
+ label: e.name,
+ playbook: e,
+ };
+ })}
+ placeholder={`Select Playbook`}
+ onSelectionChange={(_, val) => {
+ handleSelect("playbookId", val);
+ }}
+ selected={currentWorkflow?.playbookId}
+ searchable={true}
+ />
+ {playbooksLoading && }
+
+
+
+
+
+
+
+
+
+ Trigger Type
+
+
+ {
+ return {
+ id: e.id,
+ label: e.label,
+ };
+ })}
+ placeholder={`Select Workflow Type`}
+ onSelectionChange={(_, val) => {
+ handleSelect("workflowType", val);
+ }}
+ selected={currentWorkflow?.workflowType}
+ searchable={true}
+ />
+
+
+ {currentWorkflow.workflowType === "slack" && (
+
+ )}
+
+ >
+ );
+}
+
+export default BasicDetails;
diff --git a/web/src/components/Playbooks/workflows/CreateWorkflow.jsx b/web/src/components/Playbooks/workflows/CreateWorkflow.jsx
new file mode 100644
index 000000000..c90ad9410
--- /dev/null
+++ b/web/src/components/Playbooks/workflows/CreateWorkflow.jsx
@@ -0,0 +1,29 @@
+import React from "react";
+import Heading from "../../Heading.js";
+import BasicDetails from "./BasicDetails.jsx";
+import ScheduleDetails from "./ScheduleDetails.jsx";
+import NotificationDetails from "./NotificationDetails.jsx";
+
+function CreateTrigger() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+
+
+ );
+}
+
+export default CreateTrigger;
diff --git a/web/src/components/Playbooks/workflows/NotificationDetails.jsx b/web/src/components/Playbooks/workflows/NotificationDetails.jsx
new file mode 100644
index 000000000..1ef48551b
--- /dev/null
+++ b/web/src/components/Playbooks/workflows/NotificationDetails.jsx
@@ -0,0 +1,19 @@
+import React from "react";
+import { notificationOptions } from "../../../utils/workflow/notificationOptions.ts";
+import { HandleInputRender } from "../../common/HandleInputRender/HandleInputRender.jsx";
+
+function NotificationDetails() {
+ return (
+
+
+ Notifications
+
+
+ {notificationOptions.map((option) => (
+
+ ))}
+
+ );
+}
+
+export default NotificationDetails;
diff --git a/web/src/components/Playbooks/workflows/ScheduleDetails.jsx b/web/src/components/Playbooks/workflows/ScheduleDetails.jsx
new file mode 100644
index 000000000..b818ad37e
--- /dev/null
+++ b/web/src/components/Playbooks/workflows/ScheduleDetails.jsx
@@ -0,0 +1,53 @@
+import React from "react";
+import { useSelector } from "react-redux";
+import { currentWorkflowSelector } from "../../../store/features/workflow/workflowSlice.ts";
+import { scheduleOptions } from "../../../utils/workflow/scheduleOptions.tsx";
+import { HandleInputRender } from "../../common/HandleInputRender/HandleInputRender.jsx";
+import { handleSelect } from "./utils/handleInputs.ts";
+
+function ScheduleDetails() {
+ const currentWorkflow = useSelector(currentWorkflowSelector);
+
+ return (
+
+
+ What happens when the workflow is triggered?
+ Select one of these
+
+
+ {scheduleOptions.map((option) => (
+ handleSelect(e, option)}
+ className={`${
+ currentWorkflow.schedule === option.id
+ ? "!bg-white !text-violet-500 border-violet-500"
+ : "text-gray-500 bg-gray-50 border-gray-200"
+ } p-2 text-sm hover:bg-gray-100 cursor-pointer transition-all rounded border`}>
+ {option.label}
+
+ ))}
+
+
+ {scheduleOptions
+ .find((e) => e.id === currentWorkflow.schedule)
+ ?.options.map((option) => (
+
+ ))}
+
+ {(currentWorkflow["cron-schedule"] ||
+ (currentWorkflow.interval && currentWorkflow.duration)) && (
+
{`This configuration means that this workflow will run ${
+ currentWorkflow["cron-schedule"]
+ ? `as per {${currentWorkflow["cron-schedule"]}} schedule`
+ : ""
+ } for the next ${currentWorkflow.duration} ${
+ currentWorkflow.interval
+ }`}
+ )}
+
+ );
+}
+
+export default ScheduleDetails;
diff --git a/web/src/components/Playbooks/workflows/triggers/AlertsTable.jsx b/web/src/components/Playbooks/workflows/triggers/AlertsTable.jsx
new file mode 100644
index 000000000..d8003ed46
--- /dev/null
+++ b/web/src/components/Playbooks/workflows/triggers/AlertsTable.jsx
@@ -0,0 +1,81 @@
+import {
+ Accordion,
+ AccordionDetails,
+ AccordionSummary,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+} from "@mui/material";
+import NoExistingTrigger from "./NoExistingTrigger";
+import { renderTimestamp } from "../../../../utils/DateUtils";
+import { ExpandMore } from "@mui/icons-material";
+
+const AlertsTable = ({ data }) => {
+ return (
+ <>
+
+ Alerts matching the search criteria
+
+
+
+
+ Title
+ Timestamp
+ Alert Tags
+
+
+
+ {data?.map((item, index) => (
+
+
+ {item.alert_title}
+
+
+ {renderTimestamp(item.alert_timestamp)}
+
+
+
+ }
+ aria-controls="panel1a-content"
+ id="panel1a-header">
+ Details
+
+
+
+
+
+ Key
+ Value
+
+
+
+ {item.alert_tags.map((tag, index) => (
+
+ {tag.key}
+ {tag.value}
+
+ ))}
+
+
+
+
+
+
+ ))}
+
+
+ {!data?.length ? : null}
+ >
+ );
+};
+
+export default AlertsTable;
diff --git a/web/src/components/Playbooks/workflows/triggers/NoExistingTrigger.jsx b/web/src/components/Playbooks/workflows/triggers/NoExistingTrigger.jsx
new file mode 100644
index 000000000..226af33e5
--- /dev/null
+++ b/web/src/components/Playbooks/workflows/triggers/NoExistingTrigger.jsx
@@ -0,0 +1,25 @@
+import { Link } from "react-router-dom";
+
+const NoExistingTrigger = () => {
+ return (
+ <>
+
+
+
+ No Alerts found
+
+
+
+
+ Check Documentation
+
+
+
+
+ >
+ );
+};
+
+export default NoExistingTrigger;
diff --git a/web/src/components/Playbooks/workflows/triggers/SlackTriggerForm.jsx b/web/src/components/Playbooks/workflows/triggers/SlackTriggerForm.jsx
new file mode 100644
index 000000000..d3379a93f
--- /dev/null
+++ b/web/src/components/Playbooks/workflows/triggers/SlackTriggerForm.jsx
@@ -0,0 +1,105 @@
+import React from "react";
+import SelectComponent from "../../../SelectComponent/index.jsx";
+import ValueComponent from "../../../ValueComponent/index.jsx";
+import { useSelector } from "react-redux";
+import { currentWorkflowSelector } from "../../../../store/features/workflow/workflowSlice.ts";
+import { useGetTriggerOptionsQuery } from "../../../../store/features/triggers/api/getTriggerOptionsApi.ts";
+import {
+ handleTriggerInput,
+ handleTriggerSelect,
+} from "../utils/handleInputs.ts";
+import { useLazyGetSearchTriggersQuery } from "../../../../store/features/triggers/api/searchTriggerApi.ts";
+import AlertsTable from "./AlertsTable.jsx";
+import { CircularProgress } from "@mui/material";
+
+function SlackTriggerForm() {
+ const { data: options } = useGetTriggerOptionsQuery();
+ const currentWorkflow = useSelector(currentWorkflowSelector);
+ const [
+ triggerSearchTrigger,
+ { data: searchTriggerResult, isFetching: searchLoading },
+ ] = useLazyGetSearchTriggersQuery();
+
+ const handleSubmit = (e) => {
+ e?.preventDefault();
+ triggerSearchTrigger({
+ workspaceId: currentWorkflow?.trigger?.workspaceId,
+ channel_id: currentWorkflow?.trigger?.channel_id,
+ alert_type: currentWorkflow?.trigger?.alert_type,
+ filter_string: currentWorkflow?.trigger?.filterString,
+ });
+ };
+
+ const sources = options?.alert_types?.filter(
+ (e) => e.channel_connector_key_id === currentWorkflow.channel?.id,
+ );
+ const data = searchTriggerResult?.alerts ?? null;
+
+ return (
+
+ );
+}
+
+export default SlackTriggerForm;
diff --git a/web/src/components/Playbooks/workflows/utils/handleInputs.ts b/web/src/components/Playbooks/workflows/utils/handleInputs.ts
new file mode 100644
index 000000000..f73957cfd
--- /dev/null
+++ b/web/src/components/Playbooks/workflows/utils/handleInputs.ts
@@ -0,0 +1,42 @@
+import { store } from "../../../../store/index.ts";
+import {
+ setCurrentWorkflowKey,
+ setCurrentWorkflowTriggerKey,
+} from "../../../../store/features/workflow/workflowSlice.ts";
+
+export const handleSelect = (e, option) => {
+ const type = e.target?.getAttribute("data-type") ?? e;
+ store.dispatch(
+ setCurrentWorkflowKey({
+ key: type,
+ value: option.id,
+ }),
+ );
+};
+
+export const handleInput = (key, value) => {
+ store.dispatch(
+ setCurrentWorkflowKey({
+ key,
+ value,
+ }),
+ );
+};
+
+export const handleTriggerSelect = (key, value) => {
+ store.dispatch(
+ setCurrentWorkflowTriggerKey({
+ key,
+ value,
+ }),
+ );
+};
+
+export const handleTriggerInput = (key, value) => {
+ store.dispatch(
+ setCurrentWorkflowTriggerKey({
+ key,
+ value,
+ }),
+ );
+};
diff --git a/web/src/store/features/triggers/api/getTriggerOptionsApi.ts b/web/src/store/features/triggers/api/getTriggerOptionsApi.ts
new file mode 100644
index 000000000..d798ed528
--- /dev/null
+++ b/web/src/store/features/triggers/api/getTriggerOptionsApi.ts
@@ -0,0 +1,38 @@
+import { GET_TRIGGER_OPTIONS } from "../../../../constants/index.ts";
+import { apiSlice } from "../../../app/apiSlice.ts";
+import { setCurrentWorkflowTriggerKey } from "../../workflow/workflowSlice.ts";
+
+export const getTriggerOptionsApi = apiSlice.injectEndpoints({
+ endpoints: (builder) => ({
+ getTriggerOptions: builder.query({
+ query: () => ({
+ url: GET_TRIGGER_OPTIONS,
+ method: "POST",
+ body: {
+ connector_type_requests: [
+ {
+ connector_type: "SLACK",
+ },
+ ],
+ },
+ }),
+ transformResponse: (response) => {
+ if (response?.alert_ops_options?.comm_options?.workspaces?.length > 0)
+ return response?.alert_ops_options?.comm_options?.workspaces[0];
+ return [];
+ },
+ onQueryStarted: async (_, { dispatch, queryFulfilled }) => {
+ try {
+ const { data } = await queryFulfilled;
+ dispatch(
+ setCurrentWorkflowTriggerKey({ key: "workflowId", value: data.id }),
+ );
+ } catch (error) {
+ console.log(error);
+ }
+ },
+ }),
+ }),
+});
+
+export const { useGetTriggerOptionsQuery } = getTriggerOptionsApi;
diff --git a/web/src/store/features/triggers/api/index.ts b/web/src/store/features/triggers/api/index.ts
new file mode 100644
index 000000000..63857eb1c
--- /dev/null
+++ b/web/src/store/features/triggers/api/index.ts
@@ -0,0 +1 @@
+export * from "./getTriggerOptionsApi.ts";
diff --git a/web/src/store/features/triggers/api/searchTriggerApi.ts b/web/src/store/features/triggers/api/searchTriggerApi.ts
new file mode 100644
index 000000000..c82fbb5f6
--- /dev/null
+++ b/web/src/store/features/triggers/api/searchTriggerApi.ts
@@ -0,0 +1,50 @@
+import { SEARCH_TRIGGER } from '../../../../constants/index.ts';
+import { apiSlice } from '../../../app/apiSlice.ts';
+
+type SearchTriggerApiArgTypes = {
+ workspace_id: number;
+ channel_id: number;
+ alert_type: string;
+ filter_string: string;
+};
+
+const currentTimestamp = Math.floor(Date.now() / 1000);
+
+export const searchTriggerApi = apiSlice.injectEndpoints({
+ endpoints: builder => ({
+ getSearchTriggers: builder.query({
+ query: ({ workspace_id, channel_id, alert_type, filter_string }) => ({
+ url: SEARCH_TRIGGER,
+ method: 'POST',
+ body: {
+ meta: {
+ page: {
+ limit: 5,
+ offset: 0
+ },
+ time_range: {
+ time_geq: currentTimestamp - 259200,
+ time_lt: currentTimestamp
+ }
+ },
+ workspace_id,
+ channel_id,
+ alert_type,
+ fuzzy_search_request: {
+ context: 'SLACK_ALERT',
+ pattern: filter_string
+ }
+ }
+ }),
+ transformResponse: (response: any) => {
+ const data = {
+ alerts: response?.slack_alerts ?? [],
+ total: response?.meta?.total_count ?? 0
+ };
+ return data;
+ }
+ })
+ })
+});
+
+export const { useLazyGetSearchTriggersQuery } = searchTriggerApi;