diff --git a/CHANGES.rst b/CHANGES.rst index 5784f53a..d51e7c70 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,7 @@ Changes Version 0.9.2 (UNRELEASED) -------------------------- +- Adds option to delete all the runs of a workflow. - Changes Docker image Node version from 16 to 18. Version 0.9.1 (2023-09-27) diff --git a/reana-ui/src/actions.js b/reana-ui/src/actions.js index 211ae68c..38e85e84 100644 --- a/reana-ui/src/actions.js +++ b/reana-ui/src/actions.js @@ -411,11 +411,11 @@ export function fetchWorkflowRetentionRules(id) { }; } -export function deleteWorkflow(id, workspace = true) { +export function deleteWorkflow(id, { workspace = true, allRuns = false } = {}) { return async (dispatch) => { dispatch({ type: WORKFLOW_DELETE_INIT }); return await client - .deleteWorkflow(id, { workspace }) + .deleteWorkflow(id, { workspace, allRuns }) .then((resp) => { dispatch({ type: WORKFLOW_DELETED, ...resp.data }); dispatch({ type: WORKFLOW_LIST_REFRESH }); diff --git a/reana-ui/src/client.js b/reana-ui/src/client.js index e0179f98..ebf275a3 100644 --- a/reana-ui/src/client.js +++ b/reana-ui/src/client.js @@ -151,9 +151,9 @@ class Client { return this._request(WORKFLOW_RETENTION_RULES_URL(id)); } - deleteWorkflow(id, data) { + deleteWorkflow(id, { workspace, allRuns }) { return this._request(WORKFLOW_SET_STATUS_URL(id, { status: "deleted" }), { - data, + data: { workspace, all_runs: allRuns }, method: "put", }); } diff --git a/reana-ui/src/components/WorkflowDeleteModal.js b/reana-ui/src/components/WorkflowDeleteModal.js index f5dd75ee..b7780cab 100644 --- a/reana-ui/src/components/WorkflowDeleteModal.js +++ b/reana-ui/src/components/WorkflowDeleteModal.js @@ -8,19 +8,53 @@ under the terms of the MIT License; see LICENSE file for more details. */ +import { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { Button, Modal, Message, Icon } from "semantic-ui-react"; +import { Button, Checkbox, Icon, Message, Modal } from "semantic-ui-react"; -import { deleteWorkflow, closeDeleteWorkflowModal } from "~/actions"; +import { closeDeleteWorkflowModal, deleteWorkflow } from "~/actions"; +import client from "~/client"; +import { NON_DELETED_STATUSES } from "~/config"; import { - getWorkflowDeleteModalOpen, getWorkflowDeleteModalItem, + getWorkflowDeleteModalOpen, } from "~/selectors"; export default function WorkflowDeleteModal() { const dispatch = useDispatch(); const open = useSelector(getWorkflowDeleteModalOpen); const workflow = useSelector(getWorkflowDeleteModalItem); + const [allRuns, setAllRuns] = useState(false); + const [allRunsTotal, setAllRunsTotal] = useState(); + + const { id, name, run, size } = workflow ?? {}; + + useEffect(() => { + // reset local state on workflow change + setAllRuns(false); + setAllRunsTotal(null); + }, [id]); + + useEffect(() => { + if (!name) { + return; + } + + client + .getWorkflows({ + workflowIdOrName: name, + status: NON_DELETED_STATUSES, + // request minimum number of workflows as we are only interested in `total` + pagination: { size: 1, page: 1 }, + }) + .then((resp) => setAllRunsTotal(resp.data.total)) + .catch((err) => + console.log( + `Error while fetching number of workflows with name ${name}`, + err + ) + ); + }, [name]); if (!workflow) return null; @@ -28,7 +62,10 @@ export default function WorkflowDeleteModal() { dispatch(closeDeleteWorkflowModal()); }; - const { id, name, run, size } = workflow; + const deleteButtonLabel = allRuns + ? `Delete ${allRunsTotal ?? "all"} workflows named "${name}"` + : `Delete workflow "${name}#${run}"`; + return ( Delete workflow @@ -45,20 +82,27 @@ export default function WorkflowDeleteModal() { download all the files you want to keep before proceeding. -

- Are you sure you want to delete workflow "{name} #{run}"? -

+ + Delete all the runs of the workflow{" "} + {allRunsTotal ? `(${allRunsTotal})` : ""} + + } + onChange={(e, data) => setAllRuns(data.checked)} + checked={allRuns} + />