From 33024623d8852162b761e76e66bcdbad497f7c03 Mon Sep 17 00:00:00 2001 From: MiriSafra Date: Wed, 18 Sep 2024 16:52:35 +0300 Subject: [PATCH 1/6] Add ability to cancel all running apps from selected items Signed-off-by: MiriSafra --- .../applications-table/applications-table.tsx | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/client/src/app/pages/applications/applications-table/applications-table.tsx b/client/src/app/pages/applications/applications-table/applications-table.tsx index d102d69a2a..747b91b225 100644 --- a/client/src/app/pages/applications/applications-table/applications-table.tsx +++ b/client/src/app/pages/applications/applications-table/applications-table.tsx @@ -116,7 +116,6 @@ export const ApplicationsTable: React.FC = () => { const history = useHistory(); const token = keycloak.tokenParsed; - // ----- State for the modals const [saveApplicationModalState, setSaveApplicationModalState] = useState< "create" | DecoratedApplication | null @@ -156,7 +155,9 @@ export const ApplicationsTable: React.FC = () => { const [applicationsToDelete, setApplicationsToDelete] = useState< DecoratedApplication[] >([]); - + const [applicationsToCancel, setApplicationsToCancel] = useState< + DecoratedApplication[] + >([]); const [assessmentToDiscard, setAssessmentToDiscard] = useState(null); @@ -272,7 +273,6 @@ export const ApplicationsTable: React.FC = () => { }); } ); - const discardReview = async (application: DecoratedApplication) => { if (application.review) { deleteReview({ @@ -297,7 +297,6 @@ export const ApplicationsTable: React.FC = () => { }); } ); - const discardAssessment = async (application: DecoratedApplication) => { if (application.assessments) { application.assessments.forEach((assessment) => { @@ -575,6 +574,23 @@ export const ApplicationsTable: React.FC = () => { > {t("actions.delete")} , + ...(tasksReadAccess && + tasksWriteAccess && + selectedRows.some((application: DecoratedApplication) => + isTaskCancellable(application) + ) + ? [ + { + handleCancelBulkAnalysis(); + }} + > + {t("actions.cancelAnalysis")} + , + ] + : []), ...(credentialsReadAccess ? [ { }) ); }; + const handleCancelBulkAnalysis = () => { + const runningAppsToCancel = selectedRows.filter((application) => + isTaskCancellable(application) + ); + setApplicationsToCancel(runningAppsToCancel); + runningAppsToCancel.forEach((application) => { + cancelAnalysis(application); + }); + }; const assessSelectedApp = async (application: DecoratedApplication) => { setApplicationToAssess(application); From 505282a3d8c7459f8e212a876b59314df1678455 Mon Sep 17 00:00:00 2001 From: MiriSafra Date: Thu, 19 Sep 2024 10:30:31 +0300 Subject: [PATCH 2/6] Fix: Cancel button disabled until active analysis selected Signed-off-by: MiriSafra --- .../applications-table/applications-table.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/client/src/app/pages/applications/applications-table/applications-table.tsx b/client/src/app/pages/applications/applications-table/applications-table.tsx index 747b91b225..6d2bc93a0b 100644 --- a/client/src/app/pages/applications/applications-table/applications-table.tsx +++ b/client/src/app/pages/applications/applications-table/applications-table.tsx @@ -574,15 +574,14 @@ export const ApplicationsTable: React.FC = () => { > {t("actions.delete")} , - ...(tasksReadAccess && - tasksWriteAccess && - selectedRows.some((application: DecoratedApplication) => - isTaskCancellable(application) - ) + ...(tasksReadAccess && tasksWriteAccess ? [ + isTaskCancellable(application) + )} onClick={() => { handleCancelBulkAnalysis(); }} From 0f1a5efdcd4eea7d78d3d4d1fa08e40d3b50be29 Mon Sep 17 00:00:00 2001 From: MiriSafra Date: Sun, 22 Sep 2024 09:56:14 +0300 Subject: [PATCH 3/6] fix condition of disable status Signed-off-by: MiriSafra --- .../applications/applications-table/applications-table.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/src/app/pages/applications/applications-table/applications-table.tsx b/client/src/app/pages/applications/applications-table/applications-table.tsx index 6d2bc93a0b..5a6061ae94 100644 --- a/client/src/app/pages/applications/applications-table/applications-table.tsx +++ b/client/src/app/pages/applications/applications-table/applications-table.tsx @@ -578,10 +578,11 @@ export const ApplicationsTable: React.FC = () => { ? [ + isDisabled={ + !selectedRows.some((application: DecoratedApplication) => isTaskCancellable(application) - )} + ) + } onClick={() => { handleCancelBulkAnalysis(); }} From 1341508ca6855b4dead5e3774e89188b67449e55 Mon Sep 17 00:00:00 2001 From: MiriSafra Date: Wed, 30 Oct 2024 11:47:32 +0200 Subject: [PATCH 4/6] add confirm dialog Signed-off-by: MiriSafra --- client/public/locales/en/translation.json | 6 +++++ .../applications-table/applications-table.tsx | 23 ++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/client/public/locales/en/translation.json b/client/public/locales/en/translation.json index 6942e06e7d..c47a362674 100644 --- a/client/public/locales/en/translation.json +++ b/client/public/locales/en/translation.json @@ -14,6 +14,7 @@ "accept": "Accept", "back": "Back", "cancel": "Cancel", + "cancelTasks": "Cancel tasks", "checkDocumentation": "Check documentation", "clearAllFilters": "Clear all filters", "clearRepositoryNotSupported": "This action is disabled when RWX volumes are not available", @@ -107,7 +108,9 @@ "dialog": { "message": { "applicationsBulkDelete": "The selected application(s) will be deleted.", + "TasksBulkCancel": "The selected task(s) will be canceled.", "delete": "This action cannot be undone.", + "cancel": "This action cannot be undone.", "discardAssessment": "The assessment(s) for <1>{{applicationName}} will be discarded. Do you wish to continue?", "discardReview": "The review for <1>{{applicationName}} will be discarded. Do you wish to continue?", "leavePage": "Are you sure you want to leave this page? Be sure to save your changes, or they will be lost.", @@ -124,7 +127,9 @@ "copyApplicationAssessmentAndReviewFrom": "Copy {{what}} assessment and review", "copyApplicationAssessmentFrom": "Copy {{what}} assessment", "delete": "Delete {{what}}?", + "cancel": "Cancel {{what}}?", "deleteWithName": "Delete {{what}} \"{{name}}\"?", + "cancelWithName": "Cancel {{what}} \"{{name}}\"?", "discard": "Discard {{what}}?", "download": "Download {{what}}", "edit": "Edit {{what}}", @@ -481,6 +486,7 @@ "tagCategoryDeleted": "Tag category deleted", "tagCategories": "Tag categories", "tasks": "Tasks", + "task": "Task", "teamMember": "team member", "terminated": "Terminated", "ticket": "Ticket", diff --git a/client/src/app/pages/applications/applications-table/applications-table.tsx b/client/src/app/pages/applications/applications-table/applications-table.tsx index 5a6061ae94..6fc873e0df 100644 --- a/client/src/app/pages/applications/applications-table/applications-table.tsx +++ b/client/src/app/pages/applications/applications-table/applications-table.tsx @@ -155,9 +155,9 @@ export const ApplicationsTable: React.FC = () => { const [applicationsToDelete, setApplicationsToDelete] = useState< DecoratedApplication[] >([]); - const [applicationsToCancel, setApplicationsToCancel] = useState< - DecoratedApplication[] - >([]); + const [tasksToCancel, setTasksToCancel] = useState( + [] + ); const [assessmentToDiscard, setAssessmentToDiscard] = useState(null); @@ -655,13 +655,10 @@ export const ApplicationsTable: React.FC = () => { ); }; const handleCancelBulkAnalysis = () => { - const runningAppsToCancel = selectedRows.filter((application) => + const runningTasksToCancel = selectedRows.filter((application) => isTaskCancellable(application) ); - setApplicationsToCancel(runningAppsToCancel); - runningAppsToCancel.forEach((application) => { - cancelAnalysis(application); - }); + setTasksToCancel(runningTasksToCancel); }; const assessSelectedApp = async (application: DecoratedApplication) => { @@ -1174,6 +1171,16 @@ export const ApplicationsTable: React.FC = () => { if (ids) bulkDeleteApplication({ ids: ids }); }} /> + setTasksToCancel([])} + onClose={() => setTasksToCancel([])} + onConfirm={() => { + tasksToCancel.forEach((application) => { + cancelAnalysis(application); + }); + setTasksToCancel([]); + }} + /> Date: Wed, 30 Oct 2024 12:15:41 +0200 Subject: [PATCH 5/6] fix/cancel-tasks-dialog Signed-off-by: shevijacobson --- .../applications-table/applications-table.tsx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/client/src/app/pages/applications/applications-table/applications-table.tsx b/client/src/app/pages/applications/applications-table/applications-table.tsx index 6fc873e0df..727c5a98cc 100644 --- a/client/src/app/pages/applications/applications-table/applications-table.tsx +++ b/client/src/app/pages/applications/applications-table/applications-table.tsx @@ -1172,6 +1172,27 @@ export const ApplicationsTable: React.FC = () => { }} /> 1 + ? "dialog.title.cancel" + : "dialog.title.cancelWithName", + { + what: + tasksToCancel.length > 1 + ? t("terms.tasks").toLowerCase() + : t("terms.task").toLowerCase(), + name: tasksToCancel.length === 1 && tasksToCancel[0].name, + } + )} + titleIconVariant={"warning"} + isOpen={tasksToCancel.length > 0} + message={`${ + tasksToCancel.length > 1 ? t("dialog.message.TasksBulkCancel") : "" + } ${t("dialog.message.cancel")}`} + aria-label="Tasks bulk cancel" + confirmBtnVariant={ButtonVariant.danger} + confirmBtnLabel={t("actions.cancelTasks")} + cancelBtnLabel={t("actions.cancel")} onCancel={() => setTasksToCancel([])} onClose={() => setTasksToCancel([])} onConfirm={() => { From 55a46d5940aeb6df65a3218ebcd295bcf31ec5c9 Mon Sep 17 00:00:00 2001 From: MiriSafra Date: Wed, 30 Oct 2024 19:25:12 +0200 Subject: [PATCH 6/6] add check to disable cancel button in isTaskCancellabl Signed-off-by: MiriSafra --- .../applications/applications-table/applications-table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/app/pages/applications/applications-table/applications-table.tsx b/client/src/app/pages/applications/applications-table/applications-table.tsx index 727c5a98cc..a7542103c7 100644 --- a/client/src/app/pages/applications/applications-table/applications-table.tsx +++ b/client/src/app/pages/applications/applications-table/applications-table.tsx @@ -215,7 +215,7 @@ export const ApplicationsTable: React.FC = () => { const isTaskCancellable = (application: DecoratedApplication) => { const task = application.tasks.currentAnalyzer; - return !TaskStates.Terminal.includes(task?.state ?? ""); + return !!task && !TaskStates.Terminal.includes(task?.state ?? ""); }; // TODO: Review the refetchInterval calculation for the application list