Implement contextual actions for the workflows (#8814)
Implemented the following actions for the workflows: - Test Workflow - Discard Draft - Activate Draft - Activate Workflow Last Published Version - Deactivate Workflow - See Workflow Executions - See Workflow Active Version - See Workflow Versions History And the following actions for the workflow versions: - Use As Draft - See Workflow Versions History - See Workflow Executions Video example: https://github.com/user-attachments/assets/016956a6-6f2e-4de5-9605-d6e14526cbb3 A few of these actions are links to the relations of the workflow object (link to a filtered table). To generalize this behavior, I will create an hook named `useSeeRelationsActionSingleRecordAction` to automatically generate links to a showPage if the relation is a Many To One or to a filtered table if the relation is a One to Many for all the record types. I will also create actions which will allow to create a relation. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,9 +1,9 @@
|
||||
import { MultipleRecordsActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect';
|
||||
import { NoSelectionActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect';
|
||||
import { SingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect';
|
||||
import { SingleRecordActionMenuEntrySetter } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetter';
|
||||
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
|
||||
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
@ -32,30 +32,35 @@ const ActionEffects = ({
|
||||
objectId: objectMetadataItemId,
|
||||
});
|
||||
|
||||
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const isWorkflowEnabled = useIsFeatureEnabled('IS_WORKFLOW_ENABLED');
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextStoreNumberOfSelectedRecords === 0 && (
|
||||
<NoSelectionActionMenuEntrySetterEffect
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
)}
|
||||
{contextStoreNumberOfSelectedRecords === 1 && (
|
||||
<SingleRecordActionMenuEntrySetterEffect
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
)}
|
||||
{contextStoreNumberOfSelectedRecords === 1 && isWorkflowEnabled && (
|
||||
<WorkflowRunRecordActionMenuEntrySetterEffect
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
)}
|
||||
{contextStoreNumberOfSelectedRecords > 1 && (
|
||||
{contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||
contextStoreTargetedRecordsRule.selectedRecordIds.length === 0 && (
|
||||
<NoSelectionActionMenuEntrySetterEffect
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
)}
|
||||
{contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1 && (
|
||||
<>
|
||||
<SingleRecordActionMenuEntrySetter
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
{isWorkflowEnabled && (
|
||||
<WorkflowRunRecordActionMenuEntrySetterEffect
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{(contextStoreTargetedRecordsRule.mode === 'exclusion' ||
|
||||
contextStoreTargetedRecordsRule.selectedRecordIds.length > 1) && (
|
||||
<MultipleRecordsActionMenuEntrySetterEffect
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { useDeleteMultipleRecordsAction } from '../useDeleteMultipleRecordsAction';
|
||||
|
||||
jest.mock('@/object-record/hooks/useDeleteManyRecords', () => ({
|
||||
useDeleteManyRecords: () => ({
|
||||
deleteManyRecords: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
jest.mock('@/favorites/hooks/useDeleteFavorite', () => ({
|
||||
useDeleteFavorite: () => ({
|
||||
deleteFavorite: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
jest.mock('@/favorites/hooks/useFavorites', () => ({
|
||||
useFavorites: () => ({
|
||||
sortedFavorites: [],
|
||||
}),
|
||||
}));
|
||||
jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({
|
||||
useRecordTable: () => ({
|
||||
resetTableRowSelection: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
)!;
|
||||
|
||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
onInitializeRecoilSnapshot: ({ set }) => {
|
||||
set(
|
||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||
instanceId: '1',
|
||||
}),
|
||||
3,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
describe('useDeleteMultipleRecordsAction', () => {
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JestMetadataAndApolloMocksWrapper>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JestMetadataAndApolloMocksWrapper>
|
||||
);
|
||||
|
||||
it('should register delete action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useDeleteMultipleRecordsAction: useDeleteMultipleRecordsAction({
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useDeleteMultipleRecordsAction.registerDeleteMultipleRecordsAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('delete-multiple-records'),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('delete-multiple-records')?.position,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('should unregister delete action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useDeleteMultipleRecordsAction: useDeleteMultipleRecordsAction({
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useDeleteMultipleRecordsAction.registerDeleteMultipleRecordsAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
|
||||
act(() => {
|
||||
result.current.useDeleteMultipleRecordsAction.unregisterDeleteMultipleRecordsAction();
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,105 @@
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { useExportMultipleRecordsAction } from '../useExportMultipleRecordsAction';
|
||||
|
||||
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
)!;
|
||||
|
||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
describe('useExportMultipleRecordsAction', () => {
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JestMetadataAndApolloMocksWrapper>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</JestMetadataAndApolloMocksWrapper>
|
||||
);
|
||||
|
||||
it('should register export multiple records action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useExportMultipleRecordsAction: useExportMultipleRecordsAction({
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useExportMultipleRecordsAction.registerExportMultipleRecordsAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('export-multiple-records'),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('export-multiple-records')?.position,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('should unregister export multiple records action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useExportMultipleRecordsAction: useExportMultipleRecordsAction({
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useExportMultipleRecordsAction.registerExportMultipleRecordsAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
|
||||
act(() => {
|
||||
result.current.useExportMultipleRecordsAction.unregisterExportMultipleRecordsAction();
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -22,10 +22,8 @@ import { useCallback, useContext, useState } from 'react';
|
||||
import { IconTrash, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useDeleteMultipleRecordsAction = ({
|
||||
position,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
position: number;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
@ -106,7 +104,11 @@ export const useDeleteMultipleRecordsAction = ({
|
||||
const { isInRightDrawer, onActionExecutedCallback } =
|
||||
useContext(ActionMenuContext);
|
||||
|
||||
const registerDeleteMultipleRecordsAction = () => {
|
||||
const registerDeleteMultipleRecordsAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (canDelete) {
|
||||
addActionMenuEntry({
|
||||
type: ActionMenuEntryType.Standard,
|
||||
|
||||
@ -12,10 +12,8 @@ import {
|
||||
} from '@/object-record/record-index/export/hooks/useExportRecords';
|
||||
|
||||
export const useExportMultipleRecordsAction = ({
|
||||
position,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
position: number;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
@ -27,7 +25,11 @@ export const useExportMultipleRecordsAction = ({
|
||||
filename: `${objectMetadataItem.nameSingular}.csv`,
|
||||
});
|
||||
|
||||
const registerExportMultipleRecordsAction = () => {
|
||||
const registerExportMultipleRecordsAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
addActionMenuEntry({
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
|
||||
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportMultipleRecordsAction';
|
||||
import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
export const useMultipleRecordsActions = ({
|
||||
@ -11,26 +11,24 @@ export const useMultipleRecordsActions = ({
|
||||
registerDeleteMultipleRecordsAction,
|
||||
unregisterDeleteMultipleRecordsAction,
|
||||
} = useDeleteMultipleRecordsAction({
|
||||
position: 0,
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const {
|
||||
registerExportViewNoSelectionRecordsAction,
|
||||
unregisterExportViewNoSelectionRecordsAction,
|
||||
} = useExportViewNoSelectionRecordAction({
|
||||
position: 1,
|
||||
registerExportMultipleRecordsAction,
|
||||
unregisterExportMultipleRecordsAction,
|
||||
} = useExportMultipleRecordsAction({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const registerMultipleRecordsActions = () => {
|
||||
registerDeleteMultipleRecordsAction();
|
||||
registerExportViewNoSelectionRecordsAction();
|
||||
registerDeleteMultipleRecordsAction({ position: 1 });
|
||||
registerExportMultipleRecordsAction({ position: 2 });
|
||||
};
|
||||
|
||||
const unregisterMultipleRecordsActions = () => {
|
||||
unregisterDeleteMultipleRecordsAction();
|
||||
unregisterExportViewNoSelectionRecordsAction();
|
||||
unregisterExportMultipleRecordsAction();
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -0,0 +1,108 @@
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
|
||||
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction';
|
||||
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
)!;
|
||||
|
||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
describe('useExportViewNoSelectionRecordAction', () => {
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JestMetadataAndApolloMocksWrapper>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</JestMetadataAndApolloMocksWrapper>
|
||||
);
|
||||
|
||||
it('should register export view action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useExportViewNoSelectionRecordAction:
|
||||
useExportViewNoSelectionRecordAction({
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useExportViewNoSelectionRecordAction.registerExportViewNoSelectionRecordsAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('export-view-no-selection'),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('export-view-no-selection')
|
||||
?.position,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('should unregister export view action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useExportViewNoSelectionRecordAction:
|
||||
useExportViewNoSelectionRecordAction({
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useExportViewNoSelectionRecordAction.registerExportViewNoSelectionRecordsAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
|
||||
act(() => {
|
||||
result.current.useExportViewNoSelectionRecordAction.unregisterExportViewNoSelectionRecordsAction();
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -12,10 +12,8 @@ import {
|
||||
} from '@/object-record/record-index/export/hooks/useExportRecords';
|
||||
|
||||
export const useExportViewNoSelectionRecordAction = ({
|
||||
position,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
position: number;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
@ -27,7 +25,11 @@ export const useExportViewNoSelectionRecordAction = ({
|
||||
filename: `${objectMetadataItem.nameSingular}.csv`,
|
||||
});
|
||||
|
||||
const registerExportViewNoSelectionRecordsAction = () => {
|
||||
const registerExportViewNoSelectionRecordsAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
addActionMenuEntry({
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
@ -1,4 +1,4 @@
|
||||
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportMultipleRecordsAction';
|
||||
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
export const useNoSelectionRecordActions = ({
|
||||
@ -10,12 +10,11 @@ export const useNoSelectionRecordActions = ({
|
||||
registerExportViewNoSelectionRecordsAction,
|
||||
unregisterExportViewNoSelectionRecordsAction,
|
||||
} = useExportViewNoSelectionRecordAction({
|
||||
position: 0,
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const registerNoSelectionRecordActions = () => {
|
||||
registerExportViewNoSelectionRecordsAction();
|
||||
registerExportViewNoSelectionRecordsAction({ position: 1 });
|
||||
};
|
||||
|
||||
const unregisterNoSelectionRecordActions = () => {
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
import { SingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect';
|
||||
import { WorkflowSingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/WorkflowSingleRecordActionMenuEntrySetterEffect';
|
||||
import { WorkflowVersionsSingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/WorkflowVersionsSingleRecordActionMenuEntrySetterEffect';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
export const SingleRecordActionMenuEntrySetter = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<SingleRecordActionMenuEntrySetterEffect
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
{objectMetadataItem.nameSingular === CoreObjectNameSingular.Workflow && (
|
||||
<WorkflowSingleRecordActionMenuEntrySetterEffect />
|
||||
)}
|
||||
{objectMetadataItem.nameSingular ===
|
||||
CoreObjectNameSingular.WorkflowVersion && (
|
||||
<WorkflowVersionsSingleRecordActionMenuEntrySetterEffect />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
export const NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS = 2;
|
||||
@ -0,0 +1,121 @@
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { useDeleteSingleRecordAction } from '../useDeleteSingleRecordAction';
|
||||
|
||||
jest.mock('@/object-record/hooks/useDeleteOneRecord', () => ({
|
||||
useDeleteOneRecord: () => ({
|
||||
deleteOneRecord: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
jest.mock('@/favorites/hooks/useDeleteFavorite', () => ({
|
||||
useDeleteFavorite: () => ({
|
||||
deleteFavorite: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
jest.mock('@/favorites/hooks/useFavorites', () => ({
|
||||
useFavorites: () => ({
|
||||
sortedFavorites: [],
|
||||
}),
|
||||
}));
|
||||
jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({
|
||||
useRecordTable: () => ({
|
||||
resetTableRowSelection: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
)!;
|
||||
|
||||
describe('useDeleteSingleRecordAction', () => {
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
it('should register delete action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useDeleteSingleRecordAction: useDeleteSingleRecordAction({
|
||||
recordId: 'record1',
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useDeleteSingleRecordAction.registerDeleteSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('delete-single-record'),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('delete-single-record')?.position,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('should unregister delete action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useDeleteSingleRecordAction: useDeleteSingleRecordAction({
|
||||
recordId: 'record1',
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useDeleteSingleRecordAction.registerDeleteSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
|
||||
act(() => {
|
||||
result.current.useDeleteSingleRecordAction.unregisterDeleteSingleRecordAction();
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,108 @@
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { useManageFavoritesSingleRecordAction } from '../useManageFavoritesSingleRecordAction';
|
||||
|
||||
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
)!;
|
||||
|
||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
describe('useManageFavoritesSingleRecordAction', () => {
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JestMetadataAndApolloMocksWrapper>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</JestMetadataAndApolloMocksWrapper>
|
||||
);
|
||||
|
||||
it('should register manage favorites action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useManageFavoritesSingleRecordAction:
|
||||
useManageFavoritesSingleRecordAction({
|
||||
recordId: 'record1',
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useManageFavoritesSingleRecordAction.registerManageFavoritesSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('manage-favorites-single-record'),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('manage-favorites-single-record')
|
||||
?.position,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('should unregister manage favorites action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useManageFavoritesSingleRecordAction:
|
||||
useManageFavoritesSingleRecordAction({
|
||||
recordId: 'record1',
|
||||
objectMetadataItem: companyMockObjectMetadataItem,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useManageFavoritesSingleRecordAction.registerManageFavoritesSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.useManageFavoritesSingleRecordAction.unregisterManageFavoritesSingleRecordAction();
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -4,7 +4,6 @@ import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
@ -12,15 +11,14 @@ import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useCallback, useContext, useState } from 'react';
|
||||
import { IconTrash, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useDeleteSingleRecordAction = ({
|
||||
position,
|
||||
recordId,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
position: number;
|
||||
recordId: string;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
@ -39,39 +37,26 @@ export const useDeleteSingleRecordAction = ({
|
||||
const { sortedFavorites: favorites } = useFavorites();
|
||||
const { deleteFavorite } = useDeleteFavorite();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const { closeRightDrawer } = useRightDrawer();
|
||||
|
||||
const recordIdToDelete =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds?.[0]
|
||||
: undefined;
|
||||
|
||||
const handleDeleteClick = useCallback(async () => {
|
||||
if (!isDefined(recordIdToDelete)) {
|
||||
return;
|
||||
}
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
const foundFavorite = favorites?.find(
|
||||
(favorite) => favorite.recordId === recordIdToDelete,
|
||||
(favorite) => favorite.recordId === recordId,
|
||||
);
|
||||
|
||||
if (isDefined(foundFavorite)) {
|
||||
deleteFavorite(foundFavorite.id);
|
||||
}
|
||||
|
||||
await deleteOneRecord(recordIdToDelete);
|
||||
await deleteOneRecord(recordId);
|
||||
}, [
|
||||
deleteFavorite,
|
||||
deleteOneRecord,
|
||||
favorites,
|
||||
recordIdToDelete,
|
||||
resetTableRowSelection,
|
||||
recordId,
|
||||
]);
|
||||
|
||||
const isRemoteObject = objectMetadataItem.isRemote;
|
||||
@ -79,8 +64,12 @@ export const useDeleteSingleRecordAction = ({
|
||||
const { isInRightDrawer, onActionExecutedCallback } =
|
||||
useContext(ActionMenuContext);
|
||||
|
||||
const registerDeleteSingleRecordAction = () => {
|
||||
if (isRemoteObject || !isDefined(recordIdToDelete)) {
|
||||
const registerDeleteSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (isRemoteObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -3,51 +3,42 @@ import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconHeart, IconHeartOff, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useManageFavoritesSingleRecordAction = ({
|
||||
position,
|
||||
recordId,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
position: number;
|
||||
recordId: string;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const { sortedFavorites: favorites } = useFavorites();
|
||||
|
||||
const { createFavorite } = useCreateFavorite();
|
||||
|
||||
const { deleteFavorite } = useDeleteFavorite();
|
||||
|
||||
const selectedRecordId =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
||||
: undefined;
|
||||
|
||||
const selectedRecord = useRecoilValue(
|
||||
recordStoreFamilyState(selectedRecordId ?? ''),
|
||||
);
|
||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
const foundFavorite = favorites?.find(
|
||||
(favorite) => favorite.recordId === selectedRecordId,
|
||||
(favorite) => favorite.recordId === recordId,
|
||||
);
|
||||
|
||||
const isFavorite = !!selectedRecordId && !!foundFavorite;
|
||||
const isFavorite = !!foundFavorite;
|
||||
|
||||
const registerManageFavoritesSingleRecordAction = () => {
|
||||
const registerManageFavoritesSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (!isDefined(objectMetadataItem) || objectMetadataItem.isRemote) {
|
||||
return;
|
||||
}
|
||||
@ -1,17 +1,33 @@
|
||||
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
||||
import { useManageFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useManageFavoritesSingleRecordAction';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const useSingleRecordActions = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const selectedRecordId =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(selectedRecordId)) {
|
||||
throw new Error('Selected record ID is required');
|
||||
}
|
||||
|
||||
const {
|
||||
registerManageFavoritesSingleRecordAction,
|
||||
unregisterManageFavoritesSingleRecordAction,
|
||||
} = useManageFavoritesSingleRecordAction({
|
||||
position: 0,
|
||||
recordId: selectedRecordId,
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
@ -19,13 +35,13 @@ export const useSingleRecordActions = ({
|
||||
registerDeleteSingleRecordAction,
|
||||
unregisterDeleteSingleRecordAction,
|
||||
} = useDeleteSingleRecordAction({
|
||||
position: 1,
|
||||
recordId: selectedRecordId,
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const registerSingleRecordActions = () => {
|
||||
registerManageFavoritesSingleRecordAction();
|
||||
registerDeleteSingleRecordAction();
|
||||
registerManageFavoritesSingleRecordAction({ position: 1 });
|
||||
registerDeleteSingleRecordAction({ position: 2 });
|
||||
};
|
||||
|
||||
const unregisterSingleRecordActions = () => {
|
||||
@ -0,0 +1,21 @@
|
||||
import { NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS } from '@/action-menu/actions/record-actions/single-record/constants/NumberOfStandardSingleRecordActionsOnAllObjects';
|
||||
import { useEffect } from 'react';
|
||||
import { useWorkflowSingleRecordActions } from '../hooks/useWorkflowSingleRecordActions';
|
||||
|
||||
export const WorkflowSingleRecordActionMenuEntrySetterEffect = () => {
|
||||
const { registerSingleRecordActions, unregisterSingleRecordActions } =
|
||||
useWorkflowSingleRecordActions();
|
||||
|
||||
useEffect(() => {
|
||||
registerSingleRecordActions({
|
||||
startPosition:
|
||||
NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS + 1,
|
||||
});
|
||||
|
||||
return () => {
|
||||
unregisterSingleRecordActions();
|
||||
};
|
||||
}, [registerSingleRecordActions, unregisterSingleRecordActions]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@ -0,0 +1,123 @@
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { useActivateWorkflowDraftWorkflowSingleRecordAction } from '../useActivateWorkflowDraftWorkflowSingleRecordAction';
|
||||
|
||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||
useWorkflowWithCurrentVersion: () => ({
|
||||
id: 'workflowId',
|
||||
currentVersion: {
|
||||
id: 'currentVersionId',
|
||||
trigger: 'trigger',
|
||||
status: 'DRAFT',
|
||||
steps: [
|
||||
{
|
||||
id: 'stepId1',
|
||||
},
|
||||
{
|
||||
id: 'stepId2',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useActivateWorkflowDraftWorkflowSingleRecordAction', () => {
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JestMetadataAndApolloMocksWrapper>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</JestMetadataAndApolloMocksWrapper>
|
||||
);
|
||||
|
||||
it('should register activate workflow draft workflow action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useActivateWorkflowDraftWorkflowSingleRecordAction:
|
||||
useActivateWorkflowDraftWorkflowSingleRecordAction({
|
||||
workflowId: 'workflowId',
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useActivateWorkflowDraftWorkflowSingleRecordAction.registerActivateWorkflowDraftWorkflowSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
expect(
|
||||
result.current.actionMenuEntries.get(
|
||||
'activate-workflow-draft-single-record',
|
||||
),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
result.current.actionMenuEntries.get(
|
||||
'activate-workflow-draft-single-record',
|
||||
)?.position,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('should unregister activate workflow draft workflow action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useActivateWorkflowDraftWorkflowSingleRecordAction:
|
||||
useActivateWorkflowDraftWorkflowSingleRecordAction({
|
||||
workflowId: 'workflow1',
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useActivateWorkflowDraftWorkflowSingleRecordAction.registerActivateWorkflowDraftWorkflowSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.useActivateWorkflowDraftWorkflowSingleRecordAction.unregisterActivateWorkflowDraftWorkflowSingleRecordAction();
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,124 @@
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction } from '../useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction';
|
||||
|
||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||
useWorkflowWithCurrentVersion: () => ({
|
||||
id: 'workflowId',
|
||||
currentVersion: {
|
||||
id: 'currentVersionId',
|
||||
trigger: 'trigger',
|
||||
status: 'DEACTIVATED',
|
||||
steps: [
|
||||
{
|
||||
id: 'stepId1',
|
||||
},
|
||||
{
|
||||
id: 'stepId2',
|
||||
},
|
||||
],
|
||||
},
|
||||
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction', () => {
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JestMetadataAndApolloMocksWrapper>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</JestMetadataAndApolloMocksWrapper>
|
||||
);
|
||||
|
||||
it('should register activate workflow last published version workflow action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction:
|
||||
useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction({
|
||||
workflowId: 'workflowId',
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction.registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
expect(
|
||||
result.current.actionMenuEntries.get(
|
||||
'activate-workflow-last-published-version-single-record',
|
||||
),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
result.current.actionMenuEntries.get(
|
||||
'activate-workflow-last-published-version-single-record',
|
||||
)?.position,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('should unregister activate workflow last published version workflow action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction:
|
||||
useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction({
|
||||
workflowId: 'workflow1',
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction.registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction.unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction();
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,113 @@
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { useDeactivateWorkflowWorkflowSingleRecordAction } from '../useDeactivateWorkflowWorkflowSingleRecordAction';
|
||||
|
||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||
useWorkflowWithCurrentVersion: () => ({
|
||||
id: 'workflowId',
|
||||
currentVersion: {
|
||||
id: 'currentVersionId',
|
||||
trigger: 'trigger',
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useDeactivateWorkflowWorkflowSingleRecordAction', () => {
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JestMetadataAndApolloMocksWrapper>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</JestMetadataAndApolloMocksWrapper>
|
||||
);
|
||||
|
||||
it('should register activate workflow last published version workflow action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useDeactivateWorkflowWorkflowSingleRecordAction:
|
||||
useDeactivateWorkflowWorkflowSingleRecordAction({
|
||||
workflowId: 'workflowId',
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useDeactivateWorkflowWorkflowSingleRecordAction.registerDeactivateWorkflowWorkflowSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('deactivate-workflow-single-record'),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
result.current.actionMenuEntries.get('deactivate-workflow-single-record')
|
||||
?.position,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('should unregister deactivate workflow workflow action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useDeactivateWorkflowWorkflowSingleRecordAction:
|
||||
useDeactivateWorkflowWorkflowSingleRecordAction({
|
||||
workflowId: 'workflow1',
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useDeactivateWorkflowWorkflowSingleRecordAction.registerDeactivateWorkflowWorkflowSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.useDeactivateWorkflowWorkflowSingleRecordAction.unregisterDeactivateWorkflowWorkflowSingleRecordAction();
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,128 @@
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { useDiscardDraftWorkflowSingleRecordAction } from '../useDiscardDraftWorkflowSingleRecordAction';
|
||||
|
||||
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||
useWorkflowWithCurrentVersion: () => ({
|
||||
id: 'workflowId',
|
||||
currentVersion: {
|
||||
id: 'currentVersionId',
|
||||
trigger: 'trigger',
|
||||
status: 'DRAFT',
|
||||
},
|
||||
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||
versions: [
|
||||
{
|
||||
id: 'currentVersionId',
|
||||
trigger: 'trigger',
|
||||
status: 'DRAFT',
|
||||
},
|
||||
{
|
||||
id: 'lastPublishedVersionId',
|
||||
trigger: 'trigger',
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
],
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useDiscardDraftWorkflowSingleRecordAction', () => {
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JestMetadataAndApolloMocksWrapper>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: '1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</JestMetadataAndApolloMocksWrapper>
|
||||
);
|
||||
|
||||
it('should register discard workflow draft workflow action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useDiscardDraftWorkflowSingleRecordAction:
|
||||
useDiscardDraftWorkflowSingleRecordAction({
|
||||
workflowId: 'workflowId',
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useDiscardDraftWorkflowSingleRecordAction.registerDiscardDraftWorkflowSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||
expect(
|
||||
result.current.actionMenuEntries.get(
|
||||
'discard-workflow-draft-single-record',
|
||||
),
|
||||
).toBeDefined();
|
||||
expect(
|
||||
result.current.actionMenuEntries.get(
|
||||
'discard-workflow-draft-single-record',
|
||||
)?.position,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it('should unregister deactivate workflow workflow action', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
actionMenuEntries,
|
||||
useDiscardDraftWorkflowSingleRecordAction:
|
||||
useDiscardDraftWorkflowSingleRecordAction({
|
||||
workflowId: 'workflow1',
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.useDiscardDraftWorkflowSingleRecordAction.registerDiscardDraftWorkflowSingleRecordAction(
|
||||
{ position: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.useDiscardDraftWorkflowSingleRecordAction.unregisterDiscardDraftWorkflowSingleRecordAction();
|
||||
});
|
||||
|
||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,64 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { IconPower, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useActivateWorkflowDraftWorkflowSingleRecordAction = ({
|
||||
workflowId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
const registerActivateWorkflowDraftWorkflowSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (
|
||||
!isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) ||
|
||||
!isDefined(workflowWithCurrentVersion.currentVersion?.steps)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isDraft =
|
||||
workflowWithCurrentVersion.currentVersion.status === 'DRAFT';
|
||||
|
||||
if (!isDraft) {
|
||||
return;
|
||||
}
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'activate-workflow-draft-single-record',
|
||||
label: 'Activate Draft',
|
||||
position,
|
||||
Icon: IconPower,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
onClick: () => {
|
||||
activateWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
workflowId: workflowWithCurrentVersion.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterActivateWorkflowDraftWorkflowSingleRecordAction = () => {
|
||||
removeActionMenuEntry('activate-workflow-draft-single-record');
|
||||
};
|
||||
|
||||
return {
|
||||
registerActivateWorkflowDraftWorkflowSingleRecordAction,
|
||||
unregisterActivateWorkflowDraftWorkflowSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,61 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { IconPower, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction =
|
||||
({ workflowId }: { workflowId: string }) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } =
|
||||
useActionMenuEntries();
|
||||
|
||||
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||
|
||||
const workflowWithCurrentVersion =
|
||||
useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
const registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction =
|
||||
({ position }: { position: number }) => {
|
||||
if (
|
||||
!isDefined(workflowWithCurrentVersion) ||
|
||||
!isDefined(workflowWithCurrentVersion.currentVersion.trigger) ||
|
||||
!isDefined(workflowWithCurrentVersion.lastPublishedVersionId) ||
|
||||
workflowWithCurrentVersion.currentVersion.status === 'ACTIVE' ||
|
||||
!isDefined(workflowWithCurrentVersion.currentVersion?.steps) ||
|
||||
workflowWithCurrentVersion.currentVersion?.steps.length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'activate-workflow-last-published-version-single-record',
|
||||
label: 'Activate last published version',
|
||||
position,
|
||||
Icon: IconPower,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
onClick: () => {
|
||||
activateWorkflowVersion({
|
||||
workflowVersionId:
|
||||
workflowWithCurrentVersion.lastPublishedVersionId,
|
||||
workflowId: workflowWithCurrentVersion.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction =
|
||||
() => {
|
||||
removeActionMenuEntry(
|
||||
'activate-workflow-last-published-version-single-record',
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction,
|
||||
unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,55 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { IconPlayerPause, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useDeactivateWorkflowWorkflowSingleRecordAction = ({
|
||||
workflowId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
const isWorkflowActive =
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
workflowWithCurrentVersion.currentVersion.status === 'ACTIVE';
|
||||
|
||||
const registerDeactivateWorkflowWorkflowSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (!isDefined(workflowWithCurrentVersion) || !isWorkflowActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'deactivate-workflow-single-record',
|
||||
label: 'Deactivate Workflow',
|
||||
position,
|
||||
Icon: IconPlayerPause,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
onClick: () => {
|
||||
deactivateWorkflowVersion(workflowWithCurrentVersion.currentVersion.id);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterDeactivateWorkflowWorkflowSingleRecordAction = () => {
|
||||
removeActionMenuEntry('deactivate-workflow-single-record');
|
||||
};
|
||||
|
||||
return {
|
||||
registerDeactivateWorkflowWorkflowSingleRecordAction,
|
||||
unregisterDeactivateWorkflowWorkflowSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,63 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { IconTrash, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useDiscardDraftWorkflowSingleRecordAction = ({
|
||||
workflowId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
const registerDiscardDraftWorkflowSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (
|
||||
!isDefined(workflowWithCurrentVersion) ||
|
||||
workflowWithCurrentVersion.versions.length < 2
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isDraft =
|
||||
workflowWithCurrentVersion.currentVersion.status === 'DRAFT';
|
||||
|
||||
if (!isDraft) {
|
||||
return;
|
||||
}
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'discard-workflow-draft-single-record',
|
||||
label: 'Discard Draft',
|
||||
position,
|
||||
Icon: IconTrash,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
onClick: () => {
|
||||
deleteOneWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterDiscardDraftWorkflowSingleRecordAction = () => {
|
||||
removeActionMenuEntry('discard-workflow-draft-single-record');
|
||||
};
|
||||
|
||||
return {
|
||||
registerDiscardDraftWorkflowSingleRecordAction,
|
||||
unregisterDiscardDraftWorkflowSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,60 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconHistory, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useSeeWorkflowActiveVersionWorkflowSingleRecordAction = ({
|
||||
workflowId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const workflow = useRecoilValue(recordStoreFamilyState(workflowId));
|
||||
|
||||
const isDraft = workflow?.statuses?.includes('DRAFT');
|
||||
|
||||
const workflowActiveVersion = useActiveWorkflowVersion(workflowId);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const registerSeeWorkflowActiveVersionWorkflowSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (!isDefined(workflowActiveVersion) || !isDraft) {
|
||||
return;
|
||||
}
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'see-workflow-active-version-single-record',
|
||||
label: 'See active version',
|
||||
position,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
Icon: IconHistory,
|
||||
onClick: () => {
|
||||
navigate(
|
||||
`/object/${CoreObjectNameSingular.WorkflowVersion}/${workflowActiveVersion.id}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterSeeWorkflowActiveVersionWorkflowSingleRecordAction = () => {
|
||||
removeActionMenuEntry('see-workflow-active-version-single-record');
|
||||
};
|
||||
|
||||
return {
|
||||
registerSeeWorkflowActiveVersionWorkflowSingleRecordAction,
|
||||
unregisterSeeWorkflowActiveVersionWorkflowSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,66 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import qs from 'qs';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { IconHistoryToggle, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useSeeWorkflowRunsWorkflowSingleRecordAction = ({
|
||||
workflowId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const registerSeeWorkflowRunsWorkflowSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filterQueryParams: FilterQueryParams = {
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: [workflowWithCurrentVersion.id],
|
||||
},
|
||||
},
|
||||
};
|
||||
const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowRun}?${qs.stringify(
|
||||
filterQueryParams,
|
||||
)}`;
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'see-workflow-runs-single-record',
|
||||
label: 'See runs',
|
||||
position,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
Icon: IconHistoryToggle,
|
||||
onClick: () => {
|
||||
navigate(filterLinkHref);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterSeeWorkflowRunsWorkflowSingleRecordAction = () => {
|
||||
removeActionMenuEntry('see-workflow-runs-single-record');
|
||||
};
|
||||
|
||||
return {
|
||||
registerSeeWorkflowRunsWorkflowSingleRecordAction,
|
||||
unregisterSeeWorkflowRunsWorkflowSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,66 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import qs from 'qs';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { IconHistory, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction = ({
|
||||
workflowId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filterQueryParams: FilterQueryParams = {
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: [workflowWithCurrentVersion.id],
|
||||
},
|
||||
},
|
||||
};
|
||||
const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowVersion}?${qs.stringify(
|
||||
filterQueryParams,
|
||||
)}`;
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'see-workflow-versions-history-single-record',
|
||||
label: 'See versions history',
|
||||
position,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
Icon: IconHistory,
|
||||
onClick: () => {
|
||||
navigate(filterLinkHref);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction = () => {
|
||||
removeActionMenuEntry('see-workflow-versions-history-single-record');
|
||||
};
|
||||
|
||||
return {
|
||||
registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction,
|
||||
unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,60 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { IconPlayerPlay, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useTestWorkflowWorkflowSingleRecordAction = ({
|
||||
workflowId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||
|
||||
const registerTestWorkflowWorkflowSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (
|
||||
!isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) ||
|
||||
workflowWithCurrentVersion.currentVersion.trigger.type !== 'MANUAL' ||
|
||||
isDefined(
|
||||
workflowWithCurrentVersion.currentVersion.trigger.settings.objectType,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'test-workflow-single-record',
|
||||
label: 'Test workflow',
|
||||
position,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
Icon: IconPlayerPlay,
|
||||
onClick: () => {
|
||||
runWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
workflowName: workflowWithCurrentVersion.name,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterTestWorkflowWorkflowSingleRecordAction = () => {
|
||||
removeActionMenuEntry('test-workflow-single-record');
|
||||
};
|
||||
|
||||
return {
|
||||
registerTestWorkflowWorkflowSingleRecordAction,
|
||||
unregisterTestWorkflowWorkflowSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,127 @@
|
||||
import { useActivateWorkflowDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateWorkflowDraftWorkflowSingleRecordAction';
|
||||
import { useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction';
|
||||
import { useDeactivateWorkflowWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDeactivateWorkflowWorkflowSingleRecordAction';
|
||||
import { useDiscardDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDiscardDraftWorkflowSingleRecordAction';
|
||||
import { useSeeWorkflowActiveVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeWorkflowActiveVersionWorkflowSingleRecordAction';
|
||||
import { useSeeWorkflowRunsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeWorkflowRunsWorkflowSingleRecordAction';
|
||||
import { useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction';
|
||||
import { useTestWorkflowWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowWorkflowSingleRecordAction';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const useWorkflowSingleRecordActions = () => {
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const selectedRecordId =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds?.[0]
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(selectedRecordId)) {
|
||||
throw new Error('Selected record ID is required');
|
||||
}
|
||||
|
||||
const {
|
||||
registerTestWorkflowWorkflowSingleRecordAction,
|
||||
unregisterTestWorkflowWorkflowSingleRecordAction,
|
||||
} = useTestWorkflowWorkflowSingleRecordAction({
|
||||
workflowId: selectedRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction,
|
||||
unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction,
|
||||
} = useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction({
|
||||
workflowId: selectedRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
registerDeactivateWorkflowWorkflowSingleRecordAction,
|
||||
unregisterDeactivateWorkflowWorkflowSingleRecordAction,
|
||||
} = useDeactivateWorkflowWorkflowSingleRecordAction({
|
||||
workflowId: selectedRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
registerSeeWorkflowRunsWorkflowSingleRecordAction,
|
||||
unregisterSeeWorkflowRunsWorkflowSingleRecordAction,
|
||||
} = useSeeWorkflowRunsWorkflowSingleRecordAction({
|
||||
workflowId: selectedRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction,
|
||||
unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction,
|
||||
} = useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction({
|
||||
workflowId: selectedRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
registerSeeWorkflowActiveVersionWorkflowSingleRecordAction,
|
||||
unregisterSeeWorkflowActiveVersionWorkflowSingleRecordAction,
|
||||
} = useSeeWorkflowActiveVersionWorkflowSingleRecordAction({
|
||||
workflowId: selectedRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
registerActivateWorkflowDraftWorkflowSingleRecordAction,
|
||||
unregisterActivateWorkflowDraftWorkflowSingleRecordAction,
|
||||
} = useActivateWorkflowDraftWorkflowSingleRecordAction({
|
||||
workflowId: selectedRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
registerDiscardDraftWorkflowSingleRecordAction,
|
||||
unregisterDiscardDraftWorkflowSingleRecordAction,
|
||||
} = useDiscardDraftWorkflowSingleRecordAction({
|
||||
workflowId: selectedRecordId,
|
||||
});
|
||||
|
||||
const registerSingleRecordActions = ({
|
||||
startPosition,
|
||||
}: {
|
||||
startPosition: number;
|
||||
}) => {
|
||||
registerTestWorkflowWorkflowSingleRecordAction({ position: startPosition });
|
||||
registerDiscardDraftWorkflowSingleRecordAction({
|
||||
position: startPosition + 1,
|
||||
});
|
||||
registerActivateWorkflowDraftWorkflowSingleRecordAction({
|
||||
position: startPosition + 2,
|
||||
});
|
||||
registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction({
|
||||
position: startPosition + 3,
|
||||
});
|
||||
registerDeactivateWorkflowWorkflowSingleRecordAction({
|
||||
position: startPosition + 4,
|
||||
});
|
||||
registerSeeWorkflowRunsWorkflowSingleRecordAction({
|
||||
position: startPosition + 5,
|
||||
});
|
||||
registerSeeWorkflowActiveVersionWorkflowSingleRecordAction({
|
||||
position: startPosition + 6,
|
||||
});
|
||||
registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction({
|
||||
position: startPosition + 7,
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterSingleRecordActions = () => {
|
||||
unregisterTestWorkflowWorkflowSingleRecordAction();
|
||||
unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction();
|
||||
unregisterDiscardDraftWorkflowSingleRecordAction();
|
||||
unregisterActivateWorkflowDraftWorkflowSingleRecordAction();
|
||||
unregisterDeactivateWorkflowWorkflowSingleRecordAction();
|
||||
unregisterSeeWorkflowRunsWorkflowSingleRecordAction();
|
||||
unregisterSeeWorkflowActiveVersionWorkflowSingleRecordAction();
|
||||
unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction();
|
||||
};
|
||||
|
||||
return {
|
||||
registerSingleRecordActions,
|
||||
unregisterSingleRecordActions,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import { NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS } from '@/action-menu/actions/record-actions/single-record/constants/NumberOfStandardSingleRecordActionsOnAllObjects';
|
||||
import { useWorkflowVersionsSingleRecordActions } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useWorkflowVersionsSingleRecordActions';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const WorkflowVersionsSingleRecordActionMenuEntrySetterEffect = () => {
|
||||
const { registerSingleRecordActions, unregisterSingleRecordActions } =
|
||||
useWorkflowVersionsSingleRecordActions();
|
||||
|
||||
useEffect(() => {
|
||||
registerSingleRecordActions({
|
||||
startPosition:
|
||||
NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS + 1,
|
||||
});
|
||||
|
||||
return () => {
|
||||
unregisterSingleRecordActions();
|
||||
};
|
||||
}, [registerSingleRecordActions, unregisterSingleRecordActions]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@ -0,0 +1,78 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import qs from 'qs';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconHistoryToggle, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useSeeWorkflowExecutionsWorkflowVersionSingleRecordAction = ({
|
||||
workflowVersionId,
|
||||
}: {
|
||||
workflowVersionId: string;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const workflowVersion = useRecoilValue(
|
||||
recordStoreFamilyState(workflowVersionId),
|
||||
);
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
|
||||
workflowVersion?.workflow.id,
|
||||
);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const registerSeeWorkflowExecutionsWorkflowVersionSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filterQueryParams: FilterQueryParams = {
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: [workflowWithCurrentVersion.id],
|
||||
},
|
||||
workflowVersion: {
|
||||
[ViewFilterOperand.Is]: [workflowVersionId],
|
||||
},
|
||||
},
|
||||
};
|
||||
const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowRun}?${qs.stringify(
|
||||
filterQueryParams,
|
||||
)}`;
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'see-workflow-executions-single-record',
|
||||
label: 'See executions',
|
||||
position,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
Icon: IconHistoryToggle,
|
||||
onClick: () => {
|
||||
navigate(filterLinkHref);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterSeeWorkflowExecutionsWorkflowVersionSingleRecordAction =
|
||||
() => {
|
||||
removeActionMenuEntry('see-workflow-executions-single-record');
|
||||
};
|
||||
|
||||
return {
|
||||
registerSeeWorkflowExecutionsWorkflowVersionSingleRecordAction,
|
||||
unregisterSeeWorkflowExecutionsWorkflowVersionSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,27 @@
|
||||
import { useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const useSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction = ({
|
||||
workflowVersionId,
|
||||
}: {
|
||||
workflowVersionId: string;
|
||||
}) => {
|
||||
const workflowVersion = useRecoilValue(
|
||||
recordStoreFamilyState(workflowVersionId),
|
||||
);
|
||||
|
||||
const {
|
||||
registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction:
|
||||
registerSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||
unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction:
|
||||
unregisterSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||
} = useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction({
|
||||
workflowId: workflowVersion?.workflow.id,
|
||||
});
|
||||
|
||||
return {
|
||||
registerSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||
unregisterSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,92 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { OverrideWorkflowDraftConfirmationModal } from '@/workflow/components/OverrideWorkflowDraftConfirmationModal';
|
||||
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { openOverrideWorkflowDraftConfirmationModalState } from '@/workflow/states/openOverrideWorkflowDraftConfirmationModalState';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { IconPencil, isDefined } from 'twenty-ui';
|
||||
|
||||
export const useUseAsDraftWorkflowVersionSingleRecordAction = ({
|
||||
workflowVersionId,
|
||||
}: {
|
||||
workflowVersionId: string;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const workflowVersion = useRecoilValue(
|
||||
recordStoreFamilyState(workflowVersionId),
|
||||
);
|
||||
|
||||
const workflow = useWorkflowWithCurrentVersion(
|
||||
workflowVersion?.workflow?.id ?? '',
|
||||
);
|
||||
|
||||
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion();
|
||||
|
||||
const setOpenOverrideWorkflowDraftConfirmationModal = useSetRecoilState(
|
||||
openOverrideWorkflowDraftConfirmationModalState,
|
||||
);
|
||||
|
||||
const registerUseAsDraftWorkflowVersionSingleRecordAction = ({
|
||||
position,
|
||||
}: {
|
||||
position: number;
|
||||
}) => {
|
||||
if (
|
||||
!isDefined(workflowVersion) ||
|
||||
!isDefined(workflow) ||
|
||||
!isDefined(workflow.statuses) ||
|
||||
workflowVersion.status === 'DRAFT'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasAlreadyDraftVersion = workflow.statuses.includes('DRAFT');
|
||||
|
||||
addActionMenuEntry({
|
||||
key: 'use-workflow-version-as-draft-single-record',
|
||||
label: 'Use as draft',
|
||||
position,
|
||||
Icon: IconPencil,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
onClick: async () => {
|
||||
if (hasAlreadyDraftVersion) {
|
||||
setOpenOverrideWorkflowDraftConfirmationModal(true);
|
||||
} else {
|
||||
await createNewWorkflowVersion({
|
||||
workflowId: workflowVersion.workflow.id,
|
||||
name: `v${workflow.versions.length + 1}`,
|
||||
status: 'DRAFT',
|
||||
trigger: workflowVersion.trigger,
|
||||
steps: workflowVersion.steps,
|
||||
});
|
||||
}
|
||||
},
|
||||
ConfirmationModal: (
|
||||
<OverrideWorkflowDraftConfirmationModal
|
||||
draftWorkflowVersionId={workflow?.currentVersion?.id ?? ''}
|
||||
workflowId={workflow?.id ?? ''}
|
||||
workflowVersionUpdateInput={{
|
||||
steps: workflowVersion.steps,
|
||||
trigger: workflowVersion.trigger,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterUseAsDraftWorkflowVersionSingleRecordAction = () => {
|
||||
removeActionMenuEntry('use-workflow-version-as-draft-single-record');
|
||||
};
|
||||
|
||||
return {
|
||||
registerUseAsDraftWorkflowVersionSingleRecordAction,
|
||||
unregisterUseAsDraftWorkflowVersionSingleRecordAction,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,70 @@
|
||||
import { useSeeWorkflowExecutionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeWorkflowExecutionsWorkflowVersionSingleRecordAction';
|
||||
import { useSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction';
|
||||
import { useUseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const useWorkflowVersionsSingleRecordActions = () => {
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const selectedRecordId =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds?.[0]
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(selectedRecordId)) {
|
||||
throw new Error('Selected record ID is required');
|
||||
}
|
||||
|
||||
const {
|
||||
registerSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||
unregisterSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||
} = useSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction({
|
||||
workflowVersionId: selectedRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
registerUseAsDraftWorkflowVersionSingleRecordAction,
|
||||
unregisterUseAsDraftWorkflowVersionSingleRecordAction,
|
||||
} = useUseAsDraftWorkflowVersionSingleRecordAction({
|
||||
workflowVersionId: selectedRecordId,
|
||||
});
|
||||
|
||||
const {
|
||||
registerSeeWorkflowExecutionsWorkflowVersionSingleRecordAction,
|
||||
unregisterSeeWorkflowExecutionsWorkflowVersionSingleRecordAction,
|
||||
} = useSeeWorkflowExecutionsWorkflowVersionSingleRecordAction({
|
||||
workflowVersionId: selectedRecordId,
|
||||
});
|
||||
|
||||
const registerSingleRecordActions = ({
|
||||
startPosition,
|
||||
}: {
|
||||
startPosition: number;
|
||||
}) => {
|
||||
registerUseAsDraftWorkflowVersionSingleRecordAction({
|
||||
position: startPosition,
|
||||
});
|
||||
registerSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction({
|
||||
position: startPosition + 1,
|
||||
});
|
||||
|
||||
registerSeeWorkflowExecutionsWorkflowVersionSingleRecordAction({
|
||||
position: startPosition + 2,
|
||||
});
|
||||
};
|
||||
|
||||
const unregisterSingleRecordActions = () => {
|
||||
unregisterUseAsDraftWorkflowVersionSingleRecordAction();
|
||||
unregisterSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction();
|
||||
unregisterSeeWorkflowExecutionsWorkflowVersionSingleRecordAction();
|
||||
};
|
||||
|
||||
return {
|
||||
registerSingleRecordActions,
|
||||
unregisterSingleRecordActions,
|
||||
};
|
||||
};
|
||||
@ -6,13 +6,10 @@ import {
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useAllActiveWorkflowVersions } from '@/workflow/hooks/useAllActiveWorkflowVersions';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconSettingsAutomation, isDefined } from 'twenty-ui';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
@ -33,8 +30,12 @@ export const useWorkflowRunRecordActions = ({
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(selectedRecordId)) {
|
||||
throw new Error('Selected record ID is required');
|
||||
}
|
||||
|
||||
const selectedRecord = useRecoilValue(
|
||||
recordStoreFamilyState(selectedRecordId ?? ''),
|
||||
recordStoreFamilyState(selectedRecordId),
|
||||
);
|
||||
|
||||
const { records: activeWorkflowVersions } = useAllActiveWorkflowVersions({
|
||||
@ -44,10 +45,6 @@ export const useWorkflowRunRecordActions = ({
|
||||
|
||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const registerWorkflowRunRecordActions = () => {
|
||||
if (!isDefined(objectMetadataItem) || objectMetadataItem.isRemote) {
|
||||
return;
|
||||
@ -57,6 +54,9 @@ export const useWorkflowRunRecordActions = ({
|
||||
index,
|
||||
activeWorkflowVersion,
|
||||
] of activeWorkflowVersions.entries()) {
|
||||
if (!isDefined(activeWorkflowVersion.workflow)) {
|
||||
continue;
|
||||
}
|
||||
const name = capitalize(activeWorkflowVersion.workflow.name);
|
||||
addActionMenuEntry({
|
||||
type: ActionMenuEntryType.WorkflowRun,
|
||||
@ -72,19 +72,9 @@ export const useWorkflowRunRecordActions = ({
|
||||
|
||||
await runWorkflowVersion({
|
||||
workflowVersionId: activeWorkflowVersion.id,
|
||||
workflowName: name,
|
||||
payload: selectedRecord,
|
||||
});
|
||||
|
||||
enqueueSnackBar('', {
|
||||
variant: SnackBarVariant.Success,
|
||||
title: `${name} starting...`,
|
||||
icon: (
|
||||
<IconSettingsAutomation
|
||||
size={16}
|
||||
color={theme.snackBar.success.color}
|
||||
/>
|
||||
),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -3,14 +3,11 @@ import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useAllActiveWorkflowVersions } from '@/workflow/hooks/useAllActiveWorkflowVersions';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { IconSettingsAutomation } from 'twenty-ui';
|
||||
import { IconSettingsAutomation, isDefined } from 'twenty-ui';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useWorkflowRunActions = () => {
|
||||
@ -24,10 +21,6 @@ export const useWorkflowRunActions = () => {
|
||||
|
||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const addWorkflowRunActions = () => {
|
||||
if (!isWorkflowEnabled) {
|
||||
return;
|
||||
@ -37,7 +30,12 @@ export const useWorkflowRunActions = () => {
|
||||
index,
|
||||
activeWorkflowVersion,
|
||||
] of activeWorkflowVersions.entries()) {
|
||||
if (!isDefined(activeWorkflowVersion.workflow)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = capitalize(activeWorkflowVersion.workflow.name);
|
||||
|
||||
addActionMenuEntry({
|
||||
type: ActionMenuEntryType.WorkflowRun,
|
||||
key: `workflow-run-${activeWorkflowVersion.id}`,
|
||||
@ -48,17 +46,7 @@ export const useWorkflowRunActions = () => {
|
||||
onClick: async () => {
|
||||
await runWorkflowVersion({
|
||||
workflowVersionId: activeWorkflowVersion.id,
|
||||
});
|
||||
|
||||
enqueueSnackBar('', {
|
||||
variant: SnackBarVariant.Success,
|
||||
title: `${name} starting...`,
|
||||
icon: (
|
||||
<IconSettingsAutomation
|
||||
size={16}
|
||||
color={theme.snackBar.success.color}
|
||||
/>
|
||||
),
|
||||
workflowName: name,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -7,14 +7,17 @@ import {
|
||||
} from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { openOverrideWorkflowDraftConfirmationModalState } from '@/workflow/states/openOverrideWorkflowDraftConfirmationModalState';
|
||||
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
export const OverrideWorkflowDraftConfirmationModal = ({
|
||||
draftWorkflowVersionId,
|
||||
workflowVersionUpdateInput,
|
||||
workflowId,
|
||||
}: {
|
||||
draftWorkflowVersionId: string;
|
||||
workflowVersionUpdateInput: Pick<WorkflowVersion, 'trigger' | 'steps'>;
|
||||
workflowId: string;
|
||||
}) => {
|
||||
const [
|
||||
openOverrideWorkflowDraftConfirmationModal,
|
||||
@ -26,11 +29,15 @@ export const OverrideWorkflowDraftConfirmationModal = ({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
});
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleOverrideDraft = async () => {
|
||||
await updateOneWorkflowVersion({
|
||||
idToUpdate: draftWorkflowVersionId,
|
||||
updateOneRecordInput: workflowVersionUpdateInput,
|
||||
});
|
||||
|
||||
navigate(buildShowPageURL(CoreObjectNameSingular.Workflow, workflowId));
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -71,6 +71,7 @@ export const RecordShowPageWorkflowHeader = ({
|
||||
|
||||
await runWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
workflowName: workflowWithCurrentVersion.name,
|
||||
});
|
||||
|
||||
enqueueSnackBar('', {
|
||||
|
||||
@ -131,6 +131,7 @@ export const RecordShowPageWorkflowVersionHeader = ({
|
||||
{isDefined(workflowVersion) && isDefined(draftWorkflowVersion) ? (
|
||||
<OverrideWorkflowDraftConfirmationModal
|
||||
draftWorkflowVersionId={draftWorkflowVersion.id}
|
||||
workflowId={workflowVersion.workflowId}
|
||||
workflowVersionUpdateInput={{
|
||||
steps: workflowVersion.steps,
|
||||
trigger: workflowVersion.trigger,
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { Workflow, WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
|
||||
export const useActiveWorkflowVersion = (workflowId: string) => {
|
||||
const { records: workflowVersions } = useFindManyRecords<
|
||||
WorkflowVersion & {
|
||||
workflow: Omit<Workflow, 'versions'> & {
|
||||
versions: Array<{ __typename: string }>;
|
||||
};
|
||||
}
|
||||
>({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
filter: {
|
||||
workflowId: {
|
||||
eq: workflowId,
|
||||
},
|
||||
status: {
|
||||
eq: 'ACTIVE',
|
||||
},
|
||||
},
|
||||
recordGqlFields: {
|
||||
id: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
workflowId: true,
|
||||
trigger: true,
|
||||
steps: true,
|
||||
status: true,
|
||||
workflow: {
|
||||
id: true,
|
||||
name: true,
|
||||
statuses: true,
|
||||
versions: {
|
||||
totalCount: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return workflowVersions?.[0];
|
||||
};
|
||||
@ -1,10 +1,15 @@
|
||||
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { RUN_WORKFLOW_VERSION } from '@/workflow/graphql/mutations/runWorkflowVersion';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { IconSettingsAutomation } from 'twenty-ui';
|
||||
import {
|
||||
RunWorkflowVersionMutation,
|
||||
RunWorkflowVersionMutationVariables,
|
||||
} from '~/generated/graphql';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useRunWorkflowVersion = () => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
@ -15,16 +20,33 @@ export const useRunWorkflowVersion = () => {
|
||||
client: apolloMetadataClient,
|
||||
});
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const runWorkflowVersion = async ({
|
||||
workflowVersionId,
|
||||
workflowName,
|
||||
payload,
|
||||
}: {
|
||||
workflowVersionId: string;
|
||||
workflowName: string;
|
||||
payload?: Record<string, any>;
|
||||
}) => {
|
||||
await mutate({
|
||||
variables: { input: { workflowVersionId, payload } },
|
||||
});
|
||||
|
||||
enqueueSnackBar('', {
|
||||
variant: SnackBarVariant.Success,
|
||||
title: `${capitalize(workflowName)} starting...`,
|
||||
icon: (
|
||||
<IconSettingsAutomation
|
||||
size={16}
|
||||
color={theme.snackBar.success.color}
|
||||
/>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
return { runWorkflowVersion };
|
||||
@ -17,6 +17,7 @@ export const useWorkflowWithCurrentVersion = (
|
||||
id: true,
|
||||
name: true,
|
||||
statuses: true,
|
||||
lastPublishedVersionId: true,
|
||||
versions: true,
|
||||
},
|
||||
skip: !isDefined(workflowId),
|
||||
|
||||
Reference in New Issue
Block a user