diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.tsx index 5a0ca25328..87f6a1767e 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.tsx @@ -60,8 +60,13 @@ export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({ type: ActionType.Standard, scope: ActionScope.RecordSelection, Icon: IconPlayerStop, - shouldBeRegistered: ({ selectedRecord }) => { - return selectedRecord?.status === 'RUNNING'; + shouldBeRegistered: ({ selectedRecord, isSelectAll }) => { + if (isSelectAll === true) { + return true; + } + + const stoppableStatuses = ['NOT_STARTED', 'ENQUEUED', 'RUNNING']; + return stoppableStatuses.includes(selectedRecord?.status); }, availableOn: [ ActionViewType.SHOW_PAGE, diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-run-actions/components/StopWorkflowRunSingleRecordAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-run-actions/components/StopWorkflowRunSingleRecordAction.tsx index a8eb6650d3..232aa4c8e9 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-run-actions/components/StopWorkflowRunSingleRecordAction.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-run-actions/components/StopWorkflowRunSingleRecordAction.tsx @@ -1,27 +1,67 @@ import { Action } from '@/action-menu/actions/components/Action'; +import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState'; +import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState'; +import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; +import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize'; +import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords'; +import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies'; +import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useStopWorkflowRun } from '@/workflow/hooks/useStopWorkflowRun'; export const StopWorkflowRunSingleRecordAction = () => { + const { objectMetadataItem } = useRecordIndexIdFromCurrentContextStore(); + const contextStoreTargetedRecordsRule = useRecoilComponentValue( contextStoreTargetedRecordsRuleComponentState, ); - if ( - contextStoreTargetedRecordsRule.mode === 'exclusion' || - (contextStoreTargetedRecordsRule.mode === 'selection' && - contextStoreTargetedRecordsRule.selectedRecordIds.length === 0) - ) { - throw new Error('Selected record ID is required'); - } + const contextStoreFilters = useRecoilComponentValue( + contextStoreFiltersComponentState, + ); + + const contextStoreFilterGroups = useRecoilComponentValue( + contextStoreFilterGroupsComponentState, + ); + + const contextStoreAnyFieldFilterValue = useRecoilComponentValue( + contextStoreAnyFieldFilterValueComponentState, + ); + + const { filterValueDependencies } = useFilterValueDependencies(); + + const graphqlFilter = computeContextStoreFilters({ + contextStoreTargetedRecordsRule, + contextStoreFilters, + contextStoreFilterGroups, + objectMetadataItem, + filterValueDependencies, + contextStoreAnyFieldFilterValue, + }); + + const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({ + objectNameSingular: CoreObjectNameSingular.WorkflowRun, + filter: graphqlFilter, + limit: DEFAULT_QUERY_PAGE_SIZE, + recordGqlFields: { id: true }, + }); - const selectedRecordIds = contextStoreTargetedRecordsRule.selectedRecordIds; const { stopWorkflowRun } = useStopWorkflowRun(); const handleClick = async () => { - for (const selectedRecordId of selectedRecordIds) { - await stopWorkflowRun(selectedRecordId); + if (contextStoreTargetedRecordsRule.mode === 'selection') { + for (const selectedRecordId of contextStoreTargetedRecordsRule.selectedRecordIds) { + await stopWorkflowRun(selectedRecordId); + } + } else { + const records = await fetchAllRecordIds(); + + for (const record of records) { + await stopWorkflowRun(record.id); + } } }; diff --git a/packages/twenty-front/src/modules/action-menu/actions/types/ShouldBeRegisteredFunctionParams.ts b/packages/twenty-front/src/modules/action-menu/actions/types/ShouldBeRegisteredFunctionParams.ts index 0ff0a125dc..f349eaba83 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/types/ShouldBeRegisteredFunctionParams.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/types/ShouldBeRegisteredFunctionParams.ts @@ -15,6 +15,8 @@ export type ShouldBeRegisteredFunctionParams = { isFavorite?: boolean; isRemote?: boolean; isNoteOrTask?: boolean; + isSelectAll?: boolean; + loadedRecords?: ObjectRecord[]; selectedRecord?: ObjectRecord; numberOfSelectedRecords?: number; workflowWithCurrentVersion?: WorkflowWithCurrentVersion; diff --git a/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts b/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts index 62d3b1a9ad..5178f3850b 100644 --- a/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts +++ b/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts @@ -73,6 +73,8 @@ export const useShouldActionBeRegisteredParams = ({ contextStoreTargetedRecordsRule, ); + const isSelectAll = contextStoreTargetedRecordsRule.mode === 'exclusion'; + const getObjectReadPermission = useRecoilCallback( ({ snapshot }) => (objectMetadataNameSingular: string) => { @@ -113,6 +115,7 @@ export const useShouldActionBeRegisteredParams = ({ isInRightDrawer, hasAnySoftDeleteFilterOnView, isShowPage, + isSelectAll, selectedRecord, numberOfSelectedRecords, viewType: viewType ?? undefined, diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts index 9d52ed8e71..40892886f8 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts @@ -80,6 +80,13 @@ export class RunWorkflowJob { workspaceId, }); + if ( + workflowRun.status !== WorkflowRunStatus.ENQUEUED && + workflowRun.status !== WorkflowRunStatus.NOT_STARTED + ) { + return; + } + const workflowVersion = await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({ workspaceId, diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts index 5b111b17a5..d38438fc77 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts @@ -156,7 +156,10 @@ export class WorkflowRunWorkspaceService { workflowRunToUpdate.status !== WorkflowRunStatus.ENQUEUED && workflowRunToUpdate.status !== WorkflowRunStatus.NOT_STARTED ) { - return; + throw new WorkflowRunException( + 'Workflow run is not enqueued or not started', + WorkflowRunExceptionCode.INVALID_OPERATION, + ); } const partialUpdate = { diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service.ts index c3ee4e023f..fdd5fd8507 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service.ts @@ -200,12 +200,18 @@ export class WorkflowRunnerWorkspaceService { workspaceId, }); - if (workflowRun.status !== WorkflowRunStatus.RUNNING) { + const stoppableStatuses = [ + WorkflowRunStatus.NOT_STARTED, + WorkflowRunStatus.ENQUEUED, + WorkflowRunStatus.RUNNING, + ]; + + if (!stoppableStatuses.includes(workflowRun.status)) { throw new WorkflowRunException( - 'Workflow run is not running', + 'Workflow run cannot be stopped', WorkflowRunExceptionCode.INVALID_OPERATION, { - userFriendlyMessage: msg`Workflow run is not running`, + userFriendlyMessage: msg`Workflow run cannot be stopped in its current status`, }, ); }