diff --git a/src/api/services/playbooks.ts b/src/api/services/playbooks.ts
index d52ae0f79..c4ff791e2 100644
--- a/src/api/services/playbooks.ts
+++ b/src/api/services/playbooks.ts
@@ -219,3 +219,10 @@ export async function getPlaybookRuns({
);
return res;
}
+
+export async function approvePlaybookRun(id: string) {
+ const res = await PlaybookAPI.post<{
+ message: string;
+ }>(`/run/approve/${id}`);
+ return res.data;
+}
diff --git a/src/api/types/playbooks.ts b/src/api/types/playbooks.ts
index 157b79884..98c3aa763 100644
--- a/src/api/types/playbooks.ts
+++ b/src/api/types/playbooks.ts
@@ -12,7 +12,8 @@ export type PlaybookRunStatus =
| "completed"
| "failed"
| "pending"
- | "waiting";
+ | "waiting"
+ | "pending_approval";
export type PlaybookRunActionStatus =
| "completed"
diff --git a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx
index 75fa25ff5..0aadcf24c 100644
--- a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx
+++ b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx
@@ -20,9 +20,13 @@ import ShowPlaybookRunsParams from "./ShowParamaters/ShowPlaybookRunsParams";
type PlaybookRunActionsProps = {
data: PlaybookRunWithActions;
+ refetch?: () => void;
};
-export default function PlaybookRunsActions({ data }: PlaybookRunActionsProps) {
+export default function PlaybookRunsActions({
+ data,
+ refetch = () => {}
+}: PlaybookRunActionsProps) {
const [selectedAction, setSelectedAction] = useState<
PlaybookRunAction | undefined
>(() => {
@@ -69,7 +73,15 @@ export default function PlaybookRunsActions({ data }: PlaybookRunActionsProps) {
}
+ value={
+
+ }
/>
void;
+ open: boolean;
+ playbookTitle: string;
+ playbookRunId: string;
+ refetch?: () => void;
+};
+
+export default function ApprovePlaybookRunModal({
+ onClose,
+ open,
+ playbookTitle,
+ playbookRunId,
+ refetch = () => {}
+}: ApprovePlaybookRunModalProps) {
+ const { mutate: approve, isLoading } = useMutation({
+ mutationFn: (id: string) => {
+ return approvePlaybookRun(id);
+ },
+ onError: (error: AxiosError) => {
+ console.error("Failed to approve playbook run", error);
+ toastError(`Failed to approve playbook run: ${error.message}`);
+ },
+ onSuccess: () => {
+ onClose();
+ refetch();
+ }
+ });
+
+ return (
+ Are you sure you want to approve this playbook run?
}
+ onConfirm={() => approve(playbookRunId)}
+ open={open}
+ onClose={onClose}
+ isOpen={open}
+ yesLabel={isLoading ? "Approving..." : "Approve"}
+ closeLabel="Cancel"
+ />
+ );
+}
diff --git a/src/components/Playbooks/Runs/Filter/PlaybookStatusDropdown.tsx b/src/components/Playbooks/Runs/Filter/PlaybookStatusDropdown.tsx
index fe2936161..bd4e1ffeb 100644
--- a/src/components/Playbooks/Runs/Filter/PlaybookStatusDropdown.tsx
+++ b/src/components/Playbooks/Runs/Filter/PlaybookStatusDropdown.tsx
@@ -32,6 +32,11 @@ const options: StateOption[] = [
icon: statusIconMap["pending"],
label: "Pending",
value: "pending"
+ },
+ {
+ icon: statusIconMap["pending_approval"],
+ label: "Pending Approval",
+ value: "pending_approval"
}
].sort((a, b) => a.label.localeCompare(b.label));
diff --git a/src/components/Playbooks/Runs/PlaybookRunsList.tsx b/src/components/Playbooks/Runs/PlaybookRunsList.tsx
index 2fdcf2e58..4acfe8129 100644
--- a/src/components/Playbooks/Runs/PlaybookRunsList.tsx
+++ b/src/components/Playbooks/Runs/PlaybookRunsList.tsx
@@ -65,9 +65,19 @@ const playbookRunsTableColumns: MRT_ColumnDef[] = [
{
header: "Status",
accessorKey: "status",
- Cell: ({ cell }) => {
+ Cell: ({ cell, row }) => {
const status = cell.getValue();
- return ;
+ const playbookRunId = row.original.id;
+ const title =
+ row.original.playbooks?.title ?? row.original.playbooks?.name!;
+
+ return (
+
+ );
}
},
{
diff --git a/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx b/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx
index 113c9ad37..bcee0c9af 100644
--- a/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx
+++ b/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx
@@ -1,9 +1,55 @@
+import { Button } from "@flanksource-ui/ui/Buttons/Button";
+import { useState } from "react";
+import { Tooltip } from "react-tooltip";
import {
PlaybookRunsStatusProps,
PlaybookStatusIcon
} from "../../../ui/Icons/PlaybookStatusIcon";
+import ApprovePlaybookRunModal from "./ApprovePlaybookRunModal";
+
+export function PlaybookStatusDescription({
+ status,
+ playbookTitle,
+ showApproveButton = false,
+ playbookRunId,
+ refetch = () => {}
+}: PlaybookRunsStatusProps & {
+ playbookTitle: string;
+ showApproveButton?: boolean;
+ playbookRunId: string;
+ refetch?: () => void;
+}) {
+ const [showApprovalModal, setShowApprovalModal] = useState(false);
+
+ if (status === "pending_approval" && showApproveButton) {
+ return (
+ <>
+
+ {
+ setShowApprovalModal(false);
+ }}
+ open={showApprovalModal}
+ playbookTitle={playbookTitle}
+ playbookRunId={playbookRunId}
+ refetch={refetch}
+ />
+
+ >
+ );
+ }
-export function PlaybookStatusDescription({ status }: PlaybookRunsStatusProps) {
return (
diff --git a/src/pages/playbooks/PlaybookRunsDetails.tsx b/src/pages/playbooks/PlaybookRunsDetails.tsx
index 576561a46..7561718a1 100644
--- a/src/pages/playbooks/PlaybookRunsDetails.tsx
+++ b/src/pages/playbooks/PlaybookRunsDetails.tsx
@@ -115,7 +115,7 @@ export default function PlaybookRunsDetailsPage() {
{playbookRun ? (
-
+
) : (
)}
diff --git a/src/ui/AlertDialog/ConfirmationPromptDialog.tsx b/src/ui/AlertDialog/ConfirmationPromptDialog.tsx
index 388c5ca86..00efdb2c0 100644
--- a/src/ui/AlertDialog/ConfirmationPromptDialog.tsx
+++ b/src/ui/AlertDialog/ConfirmationPromptDialog.tsx
@@ -1,13 +1,13 @@
import { Dialog } from "@headlessui/react";
import { XIcon } from "@heroicons/react/outline";
import clsx from "clsx";
-import { ComponentProps } from "react";
+import React, { ComponentProps } from "react";
import { FaCircleNotch } from "react-icons/fa";
type ConfirmationPromptDialogProps = {
isOpen: boolean;
title: string;
- description: string;
+ description: React.ReactNode;
onClose: () => void;
onConfirm: () => void;
yesLabel?: string;
diff --git a/src/ui/Icons/PlaybookStatusIcon.tsx b/src/ui/Icons/PlaybookStatusIcon.tsx
index 9d64025c2..35f4b4a5a 100644
--- a/src/ui/Icons/PlaybookStatusIcon.tsx
+++ b/src/ui/Icons/PlaybookStatusIcon.tsx
@@ -17,31 +17,34 @@ export const statusIconMap: Record<
React.ReactElement
> = {
completed: (
-
+
),
cancelled: (
-
+
),
failed: (
-
+
),
pending: (
+
+ ),
+ pending_approval: (
),
waiting: (
-
+
),
running: (
-
+
),
scheduled: (
-
+
),
sleeping: (
-
+
),
skipped: (
-
+
)
};