Refactored all single record actions (#9045)
## Context Refactored all single record actions so they can be defined by a config file. This refactoring is made with the idea that later the actions will be stored in the database, so we needed a way to serialize them. For each object we can define a config file, if an object has no config file, it falls back to the default config. I introduced action hooks, which return: - `shouldBeRegistered`: `boolean` Whether the action should be registered. - `onClick`: `() => void` The code that will be executed when we click on an action - `ConfirmationModal`?: `React.ReactNode` (optional) The confirmation modal which will be displayed on click This PR also closes #8973 ## Next steps - Refactor multiple records actions - Refactor no selection actions - Add tests
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import { MultipleRecordsActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect';
|
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 { NoSelectionActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect';
|
||||||
import { SingleRecordActionMenuEntrySetter } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetter';
|
import { SingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect';
|
||||||
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
|
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
|
||||||
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
||||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
@ -59,7 +59,7 @@ const ActionEffects = ({
|
|||||||
{contextStoreTargetedRecordsRule.mode === 'selection' &&
|
{contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||||
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1 && (
|
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1 && (
|
||||||
<>
|
<>
|
||||||
<SingleRecordActionMenuEntrySetter
|
<SingleRecordActionMenuEntrySetterEffect
|
||||||
objectMetadataItem={objectMetadataItem}
|
objectMetadataItem={objectMetadataItem}
|
||||||
/>
|
/>
|
||||||
{isWorkflowEnabled && (
|
{isWorkflowEnabled && (
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
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 />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,24 +1,72 @@
|
|||||||
|
import { getActionConfig } from '@/action-menu/actions/record-actions/single-record/utils/getActionConfig';
|
||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useSingleRecordActions } from '../hooks/useSingleRecordActions';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const SingleRecordActionMenuEntrySetterEffect = ({
|
export const SingleRecordActionMenuEntrySetterEffect = ({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}: {
|
}: {
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
}) => {
|
}) => {
|
||||||
const { registerSingleRecordActions, unregisterSingleRecordActions } =
|
const isPageHeaderV2Enabled = useIsFeatureEnabled(
|
||||||
useSingleRecordActions({
|
'IS_PAGE_HEADER_V2_ENABLED',
|
||||||
objectMetadataItem,
|
);
|
||||||
});
|
|
||||||
|
const actionConfig = getActionConfig(
|
||||||
|
objectMetadataItem,
|
||||||
|
isPageHeaderV2Enabled,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
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 actionMenuEntries = Object.values(actionConfig ?? {})
|
||||||
|
.map((action) => {
|
||||||
|
const { shouldBeRegistered, onClick, ConfirmationModal } =
|
||||||
|
action.actionHook({
|
||||||
|
recordId: selectedRecordId,
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (shouldBeRegistered) {
|
||||||
|
return {
|
||||||
|
...action,
|
||||||
|
onClick,
|
||||||
|
ConfirmationModal,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
})
|
||||||
|
.filter(isDefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
registerSingleRecordActions();
|
for (const action of actionMenuEntries) {
|
||||||
|
addActionMenuEntry(action);
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unregisterSingleRecordActions();
|
for (const action of actionMenuEntries) {
|
||||||
|
removeActionMenuEntry(action.key);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [registerSingleRecordActions, unregisterSingleRecordActions]);
|
}, [actionMenuEntries, addActionMenuEntry, removeActionMenuEntry]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
||||||
|
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
||||||
|
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
|
||||||
|
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import {
|
||||||
|
ActionMenuEntry,
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { IconHeart, IconHeartOff, IconTrash } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1: Record<
|
||||||
|
string,
|
||||||
|
ActionMenuEntry & {
|
||||||
|
actionHook: SingleRecordActionHook;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
addToFavoritesSingleRecord: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: 'add-to-favorites-single-record',
|
||||||
|
label: 'Add to favorites',
|
||||||
|
position: 0,
|
||||||
|
Icon: IconHeart,
|
||||||
|
actionHook: useAddToFavoritesSingleRecordAction,
|
||||||
|
},
|
||||||
|
removeFromFavoritesSingleRecord: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: 'remove-from-favorites-single-record',
|
||||||
|
label: 'Remove from favorites',
|
||||||
|
position: 1,
|
||||||
|
Icon: IconHeartOff,
|
||||||
|
actionHook: useRemoveFromFavoritesSingleRecordAction,
|
||||||
|
},
|
||||||
|
deleteSingleRecord: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: 'delete-single-record',
|
||||||
|
label: 'Delete',
|
||||||
|
position: 2,
|
||||||
|
Icon: IconTrash,
|
||||||
|
accent: 'danger',
|
||||||
|
isPinned: true,
|
||||||
|
actionHook: useDeleteSingleRecordAction,
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
||||||
|
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
||||||
|
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
|
||||||
|
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import {
|
||||||
|
ActionMenuEntry,
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { IconHeart, IconHeartOff, IconTrash } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
|
||||||
|
string,
|
||||||
|
ActionMenuEntry & {
|
||||||
|
actionHook: SingleRecordActionHook;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
addToFavoritesSingleRecord: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: 'add-to-favorites-single-record',
|
||||||
|
label: 'Add to favorites',
|
||||||
|
position: 0,
|
||||||
|
isPinned: true,
|
||||||
|
Icon: IconHeart,
|
||||||
|
actionHook: useAddToFavoritesSingleRecordAction,
|
||||||
|
},
|
||||||
|
removeFromFavoritesSingleRecord: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: 'remove-from-favorites-single-record',
|
||||||
|
label: 'Remove from favorites',
|
||||||
|
isPinned: true,
|
||||||
|
position: 1,
|
||||||
|
Icon: IconHeartOff,
|
||||||
|
actionHook: useRemoveFromFavoritesSingleRecordAction,
|
||||||
|
},
|
||||||
|
deleteSingleRecord: {
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
key: 'delete-single-record',
|
||||||
|
label: 'Delete',
|
||||||
|
position: 2,
|
||||||
|
Icon: IconTrash,
|
||||||
|
accent: 'danger',
|
||||||
|
isPinned: true,
|
||||||
|
actionHook: useDeleteSingleRecordAction,
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -1 +0,0 @@
|
|||||||
export const NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS = 2;
|
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import {
|
||||||
|
GetJestMetadataAndApolloMocksAndActionMenuWrapperProps,
|
||||||
|
getJestMetadataAndApolloMocksAndActionMenuWrapper,
|
||||||
|
} from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { getPeopleMock } from '~/testing/mock-data/people';
|
||||||
|
import { useAddToFavoritesSingleRecordAction } from '../useAddToFavoritesSingleRecordAction';
|
||||||
|
|
||||||
|
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'person',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const peopleMock = getPeopleMock();
|
||||||
|
|
||||||
|
const favoritesMock = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
recordId: peopleMock[0].id,
|
||||||
|
position: 0,
|
||||||
|
avatarType: 'rounded',
|
||||||
|
avatarUrl: '',
|
||||||
|
labelIdentifier: ' ',
|
||||||
|
link: `/object/${personMockObjectMetadataItem.nameSingular}/${peopleMock[0].id}`,
|
||||||
|
objectNameSingular: personMockObjectMetadataItem.nameSingular,
|
||||||
|
workspaceMemberId: '1',
|
||||||
|
favoriteFolderId: undefined,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
jest.mock('@/favorites/hooks/useFavorites', () => ({
|
||||||
|
useFavorites: () => ({
|
||||||
|
favorites: favoritesMock,
|
||||||
|
sortedFavorites: favoritesMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const createFavoriteMock = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@/favorites/hooks/useCreateFavorite', () => ({
|
||||||
|
useCreateFavorite: () => ({
|
||||||
|
createFavorite: createFavoriteMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const wrapperConfigWithSelectedRecordAsFavorite: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps =
|
||||||
|
{
|
||||||
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
personMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [peopleMock[0].id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||||
|
snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapperConfigWithSelectedRecordNotAsFavorite: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps =
|
||||||
|
{
|
||||||
|
...wrapperConfigWithSelectedRecordAsFavorite,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [peopleMock[1].id],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapperWithSelectedRecordAsFavorite =
|
||||||
|
getJestMetadataAndApolloMocksAndActionMenuWrapper(
|
||||||
|
wrapperConfigWithSelectedRecordAsFavorite,
|
||||||
|
);
|
||||||
|
|
||||||
|
const wrapperWithSelectedRecordNotAsFavorite =
|
||||||
|
getJestMetadataAndApolloMocksAndActionMenuWrapper(
|
||||||
|
wrapperConfigWithSelectedRecordNotAsFavorite,
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('useAddToFavoritesSingleRecordAction', () => {
|
||||||
|
it('should be registered when the record is not a favorite', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useAddToFavoritesSingleRecordAction({
|
||||||
|
recordId: peopleMock[1].id,
|
||||||
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: wrapperWithSelectedRecordNotAsFavorite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be registered when the record is a favorite', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useAddToFavoritesSingleRecordAction({
|
||||||
|
recordId: peopleMock[0].id,
|
||||||
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: wrapperWithSelectedRecordAsFavorite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call createFavorite on click', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useAddToFavoritesSingleRecordAction({
|
||||||
|
recordId: peopleMock[1].id,
|
||||||
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: wrapperWithSelectedRecordNotAsFavorite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createFavoriteMock).toHaveBeenCalledWith(
|
||||||
|
peopleMock[1],
|
||||||
|
personMockObjectMetadataItem.nameSingular,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,121 +1,64 @@
|
|||||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
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 { renderHook } from '@testing-library/react';
|
||||||
import { act } from 'react';
|
import { act } from 'react';
|
||||||
import { RecoilRoot } from 'recoil';
|
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { getPeopleMock } from '~/testing/mock-data/people';
|
||||||
import { useDeleteSingleRecordAction } from '../useDeleteSingleRecordAction';
|
import { useDeleteSingleRecordAction } from '../useDeleteSingleRecordAction';
|
||||||
|
|
||||||
|
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'person',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const peopleMock = getPeopleMock();
|
||||||
|
|
||||||
|
const deleteOneRecordMock = jest.fn();
|
||||||
|
|
||||||
jest.mock('@/object-record/hooks/useDeleteOneRecord', () => ({
|
jest.mock('@/object-record/hooks/useDeleteOneRecord', () => ({
|
||||||
useDeleteOneRecord: () => ({
|
useDeleteOneRecord: () => ({
|
||||||
deleteOneRecord: jest.fn(),
|
deleteOneRecord: deleteOneRecordMock,
|
||||||
}),
|
|
||||||
}));
|
|
||||||
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(
|
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
(item) => item.nameSingular === 'company',
|
apolloMocks: [],
|
||||||
)!;
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
personMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [peopleMock[0].id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
describe('useDeleteSingleRecordAction', () => {
|
describe('useDeleteSingleRecordAction', () => {
|
||||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
it('should call deleteOneRecord on click', () => {
|
||||||
<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 { result } = renderHook(
|
||||||
() => {
|
() =>
|
||||||
const actionMenuEntries = useRecoilComponentValueV2(
|
useDeleteSingleRecordAction({
|
||||||
actionMenuEntriesComponentState,
|
recordId: peopleMock[0].id,
|
||||||
);
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
return {
|
{
|
||||||
actionMenuEntries,
|
wrapper,
|
||||||
useDeleteSingleRecordAction: useDeleteSingleRecordAction({
|
|
||||||
recordId: 'record1',
|
|
||||||
objectMetadataItem: companyMockObjectMetadataItem,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
{ wrapper },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
act(() => {
|
expect(result.current.ConfirmationModal?.props?.isOpen).toBe(false);
|
||||||
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(() => {
|
act(() => {
|
||||||
result.current.useDeleteSingleRecordAction.registerDeleteSingleRecordAction(
|
result.current.onClick();
|
||||||
{ position: 1 },
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(1);
|
expect(result.current.ConfirmationModal?.props?.isOpen).toBe(true);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.useDeleteSingleRecordAction.unregisterDeleteSingleRecordAction();
|
result.current.ConfirmationModal?.props?.onConfirmClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.actionMenuEntries.size).toBe(0);
|
expect(deleteOneRecordMock).toHaveBeenCalledWith(peopleMock[0].id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,108 +0,0 @@
|
|||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import {
|
||||||
|
GetJestMetadataAndApolloMocksAndActionMenuWrapperProps,
|
||||||
|
getJestMetadataAndApolloMocksAndActionMenuWrapper,
|
||||||
|
} from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { getPeopleMock } from '~/testing/mock-data/people';
|
||||||
|
import { useRemoveFromFavoritesSingleRecordAction } from '../useRemoveFromFavoritesSingleRecordAction';
|
||||||
|
|
||||||
|
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'person',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const peopleMock = getPeopleMock();
|
||||||
|
|
||||||
|
const favoritesMock = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
recordId: peopleMock[0].id,
|
||||||
|
position: 0,
|
||||||
|
avatarType: 'rounded',
|
||||||
|
avatarUrl: '',
|
||||||
|
labelIdentifier: ' ',
|
||||||
|
link: `/object/${personMockObjectMetadataItem.nameSingular}/${peopleMock[0].id}`,
|
||||||
|
objectNameSingular: personMockObjectMetadataItem.nameSingular,
|
||||||
|
workspaceMemberId: '1',
|
||||||
|
favoriteFolderId: undefined,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
jest.mock('@/favorites/hooks/useFavorites', () => ({
|
||||||
|
useFavorites: () => ({
|
||||||
|
favorites: favoritesMock,
|
||||||
|
sortedFavorites: favoritesMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const deleteFavoriteMock = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@/favorites/hooks/useDeleteFavorite', () => ({
|
||||||
|
useDeleteFavorite: () => ({
|
||||||
|
deleteFavorite: deleteFavoriteMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const wrapperConfigWithSelectedRecordAsFavorite: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps =
|
||||||
|
{
|
||||||
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
personMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [peopleMock[0].id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||||
|
snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapperConfigWithSelectedRecordNotAsFavorite: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps =
|
||||||
|
{
|
||||||
|
...wrapperConfigWithSelectedRecordAsFavorite,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [peopleMock[1].id],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapperWithSelectedRecordAsFavorite =
|
||||||
|
getJestMetadataAndApolloMocksAndActionMenuWrapper(
|
||||||
|
wrapperConfigWithSelectedRecordAsFavorite,
|
||||||
|
);
|
||||||
|
|
||||||
|
const wrapperWithSelectedRecordNotAsFavorite =
|
||||||
|
getJestMetadataAndApolloMocksAndActionMenuWrapper(
|
||||||
|
wrapperConfigWithSelectedRecordNotAsFavorite,
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('useRemoveFromFavoritesSingleRecordAction', () => {
|
||||||
|
it('should be registered when the record is a favorite', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useRemoveFromFavoritesSingleRecordAction({
|
||||||
|
recordId: peopleMock[0].id,
|
||||||
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: wrapperWithSelectedRecordAsFavorite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be registered when the record is not a favorite', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useRemoveFromFavoritesSingleRecordAction({
|
||||||
|
recordId: peopleMock[1].id,
|
||||||
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: wrapperWithSelectedRecordNotAsFavorite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call deleteFavorite on click', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useRemoveFromFavoritesSingleRecordAction({
|
||||||
|
recordId: peopleMock[0].id,
|
||||||
|
objectMetadataItem: personMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: wrapperWithSelectedRecordAsFavorite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(deleteFavoriteMock).toHaveBeenCalledWith(favoritesMock[0].id);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||||
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useAddToFavoritesSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
|
||||||
|
({ recordId, objectMetadataItem }) => {
|
||||||
|
const { sortedFavorites: favorites } = useFavorites();
|
||||||
|
|
||||||
|
const { createFavorite } = useCreateFavorite();
|
||||||
|
|
||||||
|
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||||
|
|
||||||
|
const foundFavorite = favorites?.find(
|
||||||
|
(favorite) => favorite.recordId === recordId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isFavorite = !!foundFavorite;
|
||||||
|
|
||||||
|
const shouldBeRegistered =
|
||||||
|
isDefined(objectMetadataItem) &&
|
||||||
|
isDefined(selectedRecord) &&
|
||||||
|
!objectMetadataItem.isRemote &&
|
||||||
|
!isFavorite;
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
createFavorite(selectedRecord, objectMetadataItem.nameSingular);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,90 +1,70 @@
|
|||||||
|
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
|
||||||
import {
|
|
||||||
ActionMenuEntryScope,
|
|
||||||
ActionMenuEntryType,
|
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
|
||||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||||
import { useCallback, useContext, useState } from 'react';
|
import { useCallback, useContext, useState } from 'react';
|
||||||
import { IconTrash, isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useDeleteSingleRecordAction = ({
|
export const useDeleteSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
|
||||||
recordId,
|
({ recordId, objectMetadataItem }) => {
|
||||||
objectMetadataItem,
|
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
|
||||||
}: {
|
useState(false);
|
||||||
recordId: string;
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
|
||||||
}) => {
|
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
|
||||||
|
|
||||||
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
|
const { resetTableRowSelection } = useRecordTable({
|
||||||
useState(false);
|
recordTableId: objectMetadataItem.namePlural,
|
||||||
|
});
|
||||||
|
|
||||||
const { resetTableRowSelection } = useRecordTable({
|
const { deleteOneRecord } = useDeleteOneRecord({
|
||||||
recordTableId: objectMetadataItem.namePlural,
|
objectNameSingular: objectMetadataItem.nameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { deleteOneRecord } = useDeleteOneRecord({
|
const { sortedFavorites: favorites } = useFavorites();
|
||||||
objectNameSingular: objectMetadataItem.nameSingular,
|
const { deleteFavorite } = useDeleteFavorite();
|
||||||
});
|
|
||||||
|
|
||||||
const { sortedFavorites: favorites } = useFavorites();
|
const { closeRightDrawer } = useRightDrawer();
|
||||||
const { deleteFavorite } = useDeleteFavorite();
|
|
||||||
|
|
||||||
const { closeRightDrawer } = useRightDrawer();
|
const handleDeleteClick = useCallback(async () => {
|
||||||
|
resetTableRowSelection();
|
||||||
|
|
||||||
const handleDeleteClick = useCallback(async () => {
|
const foundFavorite = favorites?.find(
|
||||||
resetTableRowSelection();
|
(favorite) => favorite.recordId === recordId,
|
||||||
|
);
|
||||||
|
|
||||||
const foundFavorite = favorites?.find(
|
if (isDefined(foundFavorite)) {
|
||||||
(favorite) => favorite.recordId === recordId,
|
deleteFavorite(foundFavorite.id);
|
||||||
);
|
}
|
||||||
|
|
||||||
if (isDefined(foundFavorite)) {
|
await deleteOneRecord(recordId);
|
||||||
deleteFavorite(foundFavorite.id);
|
}, [
|
||||||
}
|
deleteFavorite,
|
||||||
|
deleteOneRecord,
|
||||||
|
favorites,
|
||||||
|
resetTableRowSelection,
|
||||||
|
recordId,
|
||||||
|
]);
|
||||||
|
|
||||||
await deleteOneRecord(recordId);
|
const isRemoteObject = objectMetadataItem.isRemote;
|
||||||
}, [
|
|
||||||
deleteFavorite,
|
|
||||||
deleteOneRecord,
|
|
||||||
favorites,
|
|
||||||
resetTableRowSelection,
|
|
||||||
recordId,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const isRemoteObject = objectMetadataItem.isRemote;
|
const { isInRightDrawer, onActionExecutedCallback } =
|
||||||
|
useContext(ActionMenuContext);
|
||||||
|
|
||||||
const { isInRightDrawer, onActionExecutedCallback } =
|
const shouldBeRegistered = !isRemoteObject;
|
||||||
useContext(ActionMenuContext);
|
|
||||||
|
|
||||||
const registerDeleteSingleRecordAction = ({
|
const onClick = () => {
|
||||||
position,
|
if (!shouldBeRegistered) {
|
||||||
}: {
|
return;
|
||||||
position: number;
|
}
|
||||||
}) => {
|
|
||||||
if (isRemoteObject) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addActionMenuEntry({
|
setIsDeleteRecordsModalOpen(true);
|
||||||
type: ActionMenuEntryType.Standard,
|
};
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
|
||||||
key: 'delete-single-record',
|
return {
|
||||||
label: 'Delete',
|
shouldBeRegistered,
|
||||||
position,
|
onClick,
|
||||||
Icon: IconTrash,
|
|
||||||
accent: 'danger',
|
|
||||||
isPinned: true,
|
|
||||||
onClick: () => {
|
|
||||||
setIsDeleteRecordsModalOpen(true);
|
|
||||||
},
|
|
||||||
ConfirmationModal: (
|
ConfirmationModal: (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
isOpen={isDeleteRecordsModalOpen}
|
isOpen={isDeleteRecordsModalOpen}
|
||||||
@ -103,15 +83,5 @@ export const useDeleteSingleRecordAction = ({
|
|||||||
deleteButtonText={'Delete Record'}
|
deleteButtonText={'Delete Record'}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const unregisterDeleteSingleRecordAction = () => {
|
|
||||||
removeActionMenuEntry('delete-single-record');
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerDeleteSingleRecordAction,
|
|
||||||
unregisterDeleteSingleRecordAction,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,77 +0,0 @@
|
|||||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
|
||||||
import {
|
|
||||||
ActionMenuEntryScope,
|
|
||||||
ActionMenuEntryType,
|
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
|
||||||
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 { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
import { IconHeart, IconHeartOff, isDefined } from 'twenty-ui';
|
|
||||||
|
|
||||||
export const useManageFavoritesSingleRecordAction = ({
|
|
||||||
recordId,
|
|
||||||
objectMetadataItem,
|
|
||||||
}: {
|
|
||||||
recordId: string;
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
|
||||||
}) => {
|
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
|
||||||
|
|
||||||
const { sortedFavorites: favorites } = useFavorites();
|
|
||||||
|
|
||||||
const { createFavorite } = useCreateFavorite();
|
|
||||||
|
|
||||||
const { deleteFavorite } = useDeleteFavorite();
|
|
||||||
|
|
||||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
|
||||||
|
|
||||||
const foundFavorite = favorites?.find(
|
|
||||||
(favorite) => favorite.recordId === recordId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const isFavorite = !!foundFavorite;
|
|
||||||
|
|
||||||
const isPageHeaderV2Enabled = useIsFeatureEnabled(
|
|
||||||
'IS_PAGE_HEADER_V2_ENABLED',
|
|
||||||
);
|
|
||||||
|
|
||||||
const registerManageFavoritesSingleRecordAction = ({
|
|
||||||
position,
|
|
||||||
}: {
|
|
||||||
position: number;
|
|
||||||
}) => {
|
|
||||||
if (!isDefined(objectMetadataItem) || objectMetadataItem.isRemote) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addActionMenuEntry({
|
|
||||||
type: ActionMenuEntryType.Standard,
|
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
|
||||||
key: 'manage-favorites-single-record',
|
|
||||||
isPinned: isPageHeaderV2Enabled,
|
|
||||||
label: isFavorite ? 'Remove from favorites' : 'Add to favorites',
|
|
||||||
position,
|
|
||||||
Icon: isFavorite ? IconHeartOff : IconHeart,
|
|
||||||
onClick: () => {
|
|
||||||
if (isFavorite && isDefined(foundFavorite?.id)) {
|
|
||||||
deleteFavorite(foundFavorite.id);
|
|
||||||
} else if (isDefined(selectedRecord)) {
|
|
||||||
createFavorite(selectedRecord, objectMetadataItem.nameSingular);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const unregisterManageFavoritesSingleRecordAction = () => {
|
|
||||||
removeActionMenuEntry('manage-favorites-single-record');
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerManageFavoritesSingleRecordAction,
|
|
||||||
unregisterManageFavoritesSingleRecordAction,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||||
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useRemoveFromFavoritesSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
|
||||||
|
({ recordId, objectMetadataItem }) => {
|
||||||
|
const { sortedFavorites: favorites } = useFavorites();
|
||||||
|
|
||||||
|
const { deleteFavorite } = useDeleteFavorite();
|
||||||
|
|
||||||
|
const foundFavorite = favorites?.find(
|
||||||
|
(favorite) => favorite.recordId === recordId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isFavorite = !!foundFavorite;
|
||||||
|
|
||||||
|
const shouldBeRegistered =
|
||||||
|
isDefined(objectMetadataItem) &&
|
||||||
|
!objectMetadataItem.isRemote &&
|
||||||
|
isFavorite;
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteFavorite(foundFavorite.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,56 +0,0 @@
|
|||||||
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({
|
|
||||||
recordId: selectedRecordId,
|
|
||||||
objectMetadataItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
registerDeleteSingleRecordAction,
|
|
||||||
unregisterDeleteSingleRecordAction,
|
|
||||||
} = useDeleteSingleRecordAction({
|
|
||||||
recordId: selectedRecordId,
|
|
||||||
objectMetadataItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
const registerSingleRecordActions = () => {
|
|
||||||
registerManageFavoritesSingleRecordAction({ position: 1 });
|
|
||||||
registerDeleteSingleRecordAction({ position: 2 });
|
|
||||||
};
|
|
||||||
|
|
||||||
const unregisterSingleRecordActions = () => {
|
|
||||||
unregisterManageFavoritesSingleRecordAction();
|
|
||||||
unregisterDeleteSingleRecordAction();
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerSingleRecordActions,
|
|
||||||
unregisterSingleRecordActions,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1 } from '@/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV1';
|
||||||
|
import { DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2 } from '@/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV2';
|
||||||
|
import { WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/single-record/workflow-actions/constants/WorkflowSingleRecordActionsConfig';
|
||||||
|
import { WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/constants/WorkflowVersionsSingleRecordActionsConfig';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
|
||||||
|
export const getActionConfig = (
|
||||||
|
objectMetadataItem: ObjectMetadataItem,
|
||||||
|
isPageHeaderV2Enabled: boolean,
|
||||||
|
) => {
|
||||||
|
if (objectMetadataItem.nameSingular === CoreObjectNameSingular.Workflow) {
|
||||||
|
return WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
objectMetadataItem.nameSingular === CoreObjectNameSingular.WorkflowVersion
|
||||||
|
) {
|
||||||
|
return WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG;
|
||||||
|
}
|
||||||
|
return isPageHeaderV2Enabled
|
||||||
|
? DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2
|
||||||
|
: DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1;
|
||||||
|
};
|
||||||
@ -1,21 +0,0 @@
|
|||||||
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,110 @@
|
|||||||
|
import { useActivateDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateDraftWorkflowSingleRecordAction';
|
||||||
|
import { useActivateLastPublishedVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateLastPublishedVersionWorkflowSingleRecordAction';
|
||||||
|
import { useDeactivateWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDeactivateWorkflowSingleRecordAction';
|
||||||
|
import { useDiscardDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDiscardDraftWorkflowSingleRecordAction';
|
||||||
|
import { useSeeActiveVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction';
|
||||||
|
import { useSeeRunsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction';
|
||||||
|
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
|
||||||
|
import { useTestWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction';
|
||||||
|
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import {
|
||||||
|
ActionMenuEntry,
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import {
|
||||||
|
IconHistory,
|
||||||
|
IconHistoryToggle,
|
||||||
|
IconPlayerPause,
|
||||||
|
IconPlayerPlay,
|
||||||
|
IconPower,
|
||||||
|
IconTrash,
|
||||||
|
} from 'twenty-ui';
|
||||||
|
|
||||||
|
export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
|
||||||
|
string,
|
||||||
|
ActionMenuEntry & {
|
||||||
|
actionHook: SingleRecordActionHook;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
activateWorkflowDraftSingleRecord: {
|
||||||
|
key: 'activate-workflow-draft-single-record',
|
||||||
|
label: 'Activate Draft',
|
||||||
|
isPinned: true,
|
||||||
|
position: 1,
|
||||||
|
Icon: IconPower,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
actionHook: useActivateDraftWorkflowSingleRecordAction,
|
||||||
|
},
|
||||||
|
activateWorkflowLastPublishedVersionSingleRecord: {
|
||||||
|
key: 'activate-workflow-last-published-version-single-record',
|
||||||
|
label: 'Activate last published version',
|
||||||
|
isPinned: true,
|
||||||
|
position: 2,
|
||||||
|
Icon: IconPower,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
actionHook: useActivateLastPublishedVersionWorkflowSingleRecordAction,
|
||||||
|
},
|
||||||
|
deactivateWorkflowSingleRecord: {
|
||||||
|
key: 'deactivate-workflow-single-record',
|
||||||
|
label: 'Deactivate Workflow',
|
||||||
|
isPinned: true,
|
||||||
|
position: 3,
|
||||||
|
Icon: IconPlayerPause,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
actionHook: useDeactivateWorkflowSingleRecordAction,
|
||||||
|
},
|
||||||
|
discardWorkflowDraftSingleRecord: {
|
||||||
|
key: 'discard-workflow-draft-single-record',
|
||||||
|
label: 'Discard Draft',
|
||||||
|
isPinned: true,
|
||||||
|
position: 4,
|
||||||
|
Icon: IconTrash,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
actionHook: useDiscardDraftWorkflowSingleRecordAction,
|
||||||
|
},
|
||||||
|
seeWorkflowActiveVersionSingleRecord: {
|
||||||
|
key: 'see-workflow-active-version-single-record',
|
||||||
|
label: 'See active version',
|
||||||
|
isPinned: false,
|
||||||
|
position: 5,
|
||||||
|
Icon: IconHistory,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
actionHook: useSeeActiveVersionWorkflowSingleRecordAction,
|
||||||
|
},
|
||||||
|
seeWorkflowRunsSingleRecord: {
|
||||||
|
key: 'see-workflow-runs-single-record',
|
||||||
|
label: 'See runs',
|
||||||
|
isPinned: false,
|
||||||
|
position: 6,
|
||||||
|
Icon: IconHistoryToggle,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
actionHook: useSeeRunsWorkflowSingleRecordAction,
|
||||||
|
},
|
||||||
|
seeWorkflowVersionsHistorySingleRecord: {
|
||||||
|
key: 'see-workflow-versions-history-single-record',
|
||||||
|
label: 'See versions history',
|
||||||
|
isPinned: false,
|
||||||
|
position: 7,
|
||||||
|
Icon: IconHistory,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
actionHook: useSeeVersionsWorkflowSingleRecordAction,
|
||||||
|
},
|
||||||
|
testWorkflowSingleRecord: {
|
||||||
|
key: 'test-workflow-single-record',
|
||||||
|
label: 'Test Workflow',
|
||||||
|
isPinned: true,
|
||||||
|
position: 8,
|
||||||
|
Icon: IconPlayerPlay,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
actionHook: useTestWorkflowSingleRecordAction,
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { useActivateDraftWorkflowSingleRecordAction } from '../useActivateDraftWorkflowSingleRecordAction';
|
||||||
|
|
||||||
|
const workflowMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'workflow',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const workflowMock = {
|
||||||
|
__typename: 'Workflow',
|
||||||
|
id: 'workflowId',
|
||||||
|
currentVersion: {
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'DRAFT',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||||
|
useWorkflowWithCurrentVersion: () => workflowMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const activateWorkflowVersionMock = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useActivateWorkflowVersion', () => ({
|
||||||
|
useActivateWorkflowVersion: () => ({
|
||||||
|
activateWorkflowVersion: activateWorkflowVersionMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
workflowMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [workflowMock.id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(recordStoreFamilyState(workflowMock.id), workflowMock);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useActivateDraftWorkflowSingleRecordAction', () => {
|
||||||
|
it('should be registered', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useActivateDraftWorkflowSingleRecordAction({
|
||||||
|
recordId: workflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call activateWorkflowVersion on click', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useActivateDraftWorkflowSingleRecordAction({
|
||||||
|
recordId: workflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(activateWorkflowVersionMock).toHaveBeenCalledWith({
|
||||||
|
workflowId: workflowMock.id,
|
||||||
|
workflowVersionId: workflowMock.currentVersion.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { useActivateLastPublishedVersionWorkflowSingleRecordAction } from '../useActivateLastPublishedVersionWorkflowSingleRecordAction';
|
||||||
|
|
||||||
|
const workflowMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'workflow',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const workflowMock = {
|
||||||
|
__typename: 'Workflow',
|
||||||
|
id: 'workflowId',
|
||||||
|
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||||
|
currentVersion: {
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'lastPublishedVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'DEACTIVATED',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||||
|
useWorkflowWithCurrentVersion: () => workflowMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const activateWorkflowVersionMock = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useActivateWorkflowVersion', () => ({
|
||||||
|
useActivateWorkflowVersion: () => ({
|
||||||
|
activateWorkflowVersion: activateWorkflowVersionMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
workflowMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [workflowMock.id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(recordStoreFamilyState(workflowMock.id), workflowMock);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useActivateLastPublishedVersionWorkflowSingleRecordAction', () => {
|
||||||
|
it('should be registered', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useActivateLastPublishedVersionWorkflowSingleRecordAction({
|
||||||
|
recordId: workflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call activateWorkflowVersion on click', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useActivateLastPublishedVersionWorkflowSingleRecordAction({
|
||||||
|
recordId: workflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(activateWorkflowVersionMock).toHaveBeenCalledWith({
|
||||||
|
workflowId: workflowMock.id,
|
||||||
|
workflowVersionId: workflowMock.currentVersion.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,123 +0,0 @@
|
|||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,124 +0,0 @@
|
|||||||
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,158 @@
|
|||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { useDeactivateWorkflowSingleRecordAction } from '../useDeactivateWorkflowSingleRecordAction';
|
||||||
|
const workflowMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'workflow',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const activeWorkflowMock = {
|
||||||
|
__typename: 'Workflow',
|
||||||
|
id: 'workflowId',
|
||||||
|
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||||
|
currentVersion: {
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'ACTIVE',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const deactivatedWorkflowMock = {
|
||||||
|
__typename: 'Workflow',
|
||||||
|
id: 'workflowId',
|
||||||
|
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||||
|
currentVersion: {
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'DEACTIVATED',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||||
|
useWorkflowWithCurrentVersion: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const deactivateWorkflowVersionMock = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useDeactivateWorkflowVersion', () => ({
|
||||||
|
useDeactivateWorkflowVersion: () => ({
|
||||||
|
deactivateWorkflowVersion: deactivateWorkflowVersionMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const activeWorkflowWrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper(
|
||||||
|
{
|
||||||
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
workflowMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [activeWorkflowMock.id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(
|
||||||
|
recordStoreFamilyState(activeWorkflowMock.id),
|
||||||
|
activeWorkflowMock,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const deactivatedWorkflowWrapper =
|
||||||
|
getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
workflowMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [deactivatedWorkflowMock.id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(
|
||||||
|
recordStoreFamilyState(deactivatedWorkflowMock.id),
|
||||||
|
deactivatedWorkflowMock,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useDeactivateWorkflowSingleRecordAction', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be registered when the workflow is deactivated', () => {
|
||||||
|
(useWorkflowWithCurrentVersion as jest.Mock).mockImplementation(
|
||||||
|
() => deactivatedWorkflowMock,
|
||||||
|
);
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useDeactivateWorkflowSingleRecordAction({
|
||||||
|
recordId: deactivatedWorkflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: deactivatedWorkflowWrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be registered when the workflow is active', () => {
|
||||||
|
(useWorkflowWithCurrentVersion as jest.Mock).mockImplementation(
|
||||||
|
() => activeWorkflowMock,
|
||||||
|
);
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useDeactivateWorkflowSingleRecordAction({
|
||||||
|
recordId: activeWorkflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: activeWorkflowWrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call deactivateWorkflowVersion on click', () => {
|
||||||
|
(useWorkflowWithCurrentVersion as jest.Mock).mockImplementation(
|
||||||
|
() => activeWorkflowMock,
|
||||||
|
);
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useDeactivateWorkflowSingleRecordAction({
|
||||||
|
recordId: activeWorkflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: activeWorkflowWrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(deactivateWorkflowVersionMock).toHaveBeenCalledWith(
|
||||||
|
activeWorkflowMock.currentVersion.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,113 +0,0 @@
|
|||||||
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,250 @@
|
|||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { useDiscardDraftWorkflowSingleRecordAction } from '../useDiscardDraftWorkflowSingleRecordAction';
|
||||||
|
|
||||||
|
const workflowMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'workflow',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const noDraftWorkflowMock = {
|
||||||
|
__typename: 'Workflow',
|
||||||
|
id: 'workflowId',
|
||||||
|
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||||
|
currentVersion: {
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'ACTIVE',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
versions: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'ACTIVE',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'versionId2',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'ACTIVE',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const draftWorkflowMock = {
|
||||||
|
__typename: 'Workflow',
|
||||||
|
id: 'workflowId',
|
||||||
|
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||||
|
currentVersion: {
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'DRAFT',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
versions: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'DRAFT',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
id: 'versionId2',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'ACTIVE',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
__typename: 'WorkflowStep',
|
||||||
|
id: 'stepId2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const draftWorkflowMockWithOneVersion = {
|
||||||
|
...draftWorkflowMock,
|
||||||
|
versions: [draftWorkflowMock.currentVersion],
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||||
|
useWorkflowWithCurrentVersion: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const deleteOneWorkflowVersionMock = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useDeleteOneWorkflowVersion', () => ({
|
||||||
|
useDeleteOneWorkflowVersion: () => ({
|
||||||
|
deleteOneWorkflowVersion: deleteOneWorkflowVersionMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const noDraftWorkflowWrapper =
|
||||||
|
getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
workflowMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [noDraftWorkflowMock.id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(
|
||||||
|
recordStoreFamilyState(noDraftWorkflowMock.id),
|
||||||
|
noDraftWorkflowMock,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const draftWorkflowWrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
workflowMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [draftWorkflowMock.id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(
|
||||||
|
recordStoreFamilyState(draftWorkflowMock.id),
|
||||||
|
draftWorkflowMock,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const draftWorkflowWithOneVersionWrapper =
|
||||||
|
getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
componentInstanceId: '1',
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular:
|
||||||
|
workflowMockObjectMetadataItem.nameSingular,
|
||||||
|
contextStoreTargetedRecordsRule: {
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [draftWorkflowMockWithOneVersion.id],
|
||||||
|
},
|
||||||
|
onInitializeRecoilSnapshot: (snapshot) => {
|
||||||
|
snapshot.set(
|
||||||
|
recordStoreFamilyState(draftWorkflowMockWithOneVersion.id),
|
||||||
|
draftWorkflowMockWithOneVersion,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useDiscardDraftWorkflowSingleRecordAction', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be registered when there is no draft', () => {
|
||||||
|
(useWorkflowWithCurrentVersion as jest.Mock).mockImplementation(
|
||||||
|
() => noDraftWorkflowMock,
|
||||||
|
);
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useDiscardDraftWorkflowSingleRecordAction({
|
||||||
|
recordId: noDraftWorkflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: noDraftWorkflowWrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be registered when there is only one version', () => {
|
||||||
|
(useWorkflowWithCurrentVersion as jest.Mock).mockImplementation(
|
||||||
|
() => draftWorkflowMockWithOneVersion,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useDiscardDraftWorkflowSingleRecordAction({
|
||||||
|
recordId: draftWorkflowMockWithOneVersion.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: draftWorkflowWithOneVersionWrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be registered when the workflow is draft', () => {
|
||||||
|
(useWorkflowWithCurrentVersion as jest.Mock).mockImplementation(
|
||||||
|
() => draftWorkflowMock,
|
||||||
|
);
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useDiscardDraftWorkflowSingleRecordAction({
|
||||||
|
recordId: draftWorkflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: draftWorkflowWrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.shouldBeRegistered).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call deactivateWorkflowVersion on click', () => {
|
||||||
|
(useWorkflowWithCurrentVersion as jest.Mock).mockImplementation(
|
||||||
|
() => draftWorkflowMock,
|
||||||
|
);
|
||||||
|
const { result } = renderHook(
|
||||||
|
() =>
|
||||||
|
useDiscardDraftWorkflowSingleRecordAction({
|
||||||
|
recordId: draftWorkflowMock.id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
wrapper: draftWorkflowWrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(deleteOneWorkflowVersionMock).toHaveBeenCalledWith({
|
||||||
|
workflowVersionId: draftWorkflowMock.currentVersion.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,128 +0,0 @@
|
|||||||
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,32 @@
|
|||||||
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useActivateDraftWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
|
({ recordId }) => {
|
||||||
|
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
|
const shouldBeRegistered =
|
||||||
|
isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) &&
|
||||||
|
isDefined(workflowWithCurrentVersion.currentVersion?.steps) &&
|
||||||
|
workflowWithCurrentVersion.currentVersion.status === 'DRAFT';
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activateWorkflowVersion({
|
||||||
|
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||||
|
workflowId: workflowWithCurrentVersion.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useActivateLastPublishedVersionWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
|
({ recordId }) => {
|
||||||
|
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
|
const shouldBeRegistered =
|
||||||
|
isDefined(workflowWithCurrentVersion) &&
|
||||||
|
isDefined(workflowWithCurrentVersion.currentVersion.trigger) &&
|
||||||
|
isDefined(workflowWithCurrentVersion.lastPublishedVersionId) &&
|
||||||
|
workflowWithCurrentVersion.currentVersion.status !== 'ACTIVE' &&
|
||||||
|
isDefined(workflowWithCurrentVersion.currentVersion?.steps) &&
|
||||||
|
workflowWithCurrentVersion.currentVersion?.steps.length !== 0;
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activateWorkflowVersion({
|
||||||
|
workflowVersionId: workflowWithCurrentVersion.lastPublishedVersionId,
|
||||||
|
workflowId: workflowWithCurrentVersion.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,64 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
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,28 @@
|
|||||||
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useDeactivateWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
|
({ recordId }) => {
|
||||||
|
const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
|
const shouldBeRegistered =
|
||||||
|
isDefined(workflowWithCurrentVersion) &&
|
||||||
|
workflowWithCurrentVersion.currentVersion.status === 'ACTIVE';
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivateWorkflowVersion(workflowWithCurrentVersion.currentVersion.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,55 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,63 +1,31 @@
|
|||||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
import {
|
|
||||||
ActionMenuEntryScope,
|
|
||||||
ActionMenuEntryType,
|
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
|
||||||
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
|
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { IconTrash, isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useDiscardDraftWorkflowSingleRecordAction = ({
|
export const useDiscardDraftWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
workflowId,
|
({ recordId }) => {
|
||||||
}: {
|
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
|
||||||
workflowId: string;
|
|
||||||
}) => {
|
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
|
||||||
|
|
||||||
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
const shouldBeRegistered =
|
||||||
|
isDefined(workflowWithCurrentVersion) &&
|
||||||
const registerDiscardDraftWorkflowSingleRecordAction = ({
|
workflowWithCurrentVersion.versions.length > 1 &&
|
||||||
position,
|
|
||||||
}: {
|
|
||||||
position: number;
|
|
||||||
}) => {
|
|
||||||
if (
|
|
||||||
!isDefined(workflowWithCurrentVersion) ||
|
|
||||||
workflowWithCurrentVersion.versions.length < 2
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDraft =
|
|
||||||
workflowWithCurrentVersion.currentVersion.status === 'DRAFT';
|
workflowWithCurrentVersion.currentVersion.status === 'DRAFT';
|
||||||
|
|
||||||
if (!isDraft) {
|
const onClick = () => {
|
||||||
return;
|
if (!shouldBeRegistered) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
addActionMenuEntry({
|
deleteOneWorkflowVersion({
|
||||||
key: 'discard-workflow-draft-single-record',
|
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||||
label: 'Discard Draft',
|
});
|
||||||
position,
|
};
|
||||||
Icon: IconTrash,
|
|
||||||
type: ActionMenuEntryType.Standard,
|
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
|
||||||
onClick: () => {
|
|
||||||
deleteOneWorkflowVersion({
|
|
||||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const unregisterDiscardDraftWorkflowSingleRecordAction = () => {
|
return {
|
||||||
removeActionMenuEntry('discard-workflow-draft-single-record');
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
|
||||||
registerDiscardDraftWorkflowSingleRecordAction,
|
|
||||||
unregisterDiscardDraftWorkflowSingleRecordAction,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useSeeActiveVersionWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
|
({ recordId }) => {
|
||||||
|
const workflow = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
|
const isDraft = workflow?.statuses?.includes('DRAFT') || false;
|
||||||
|
|
||||||
|
const workflowActiveVersion = useActiveWorkflowVersion(recordId);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const shouldBeRegistered = isDefined(workflowActiveVersion) && isDraft;
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigate(
|
||||||
|
`/object/${CoreObjectNameSingular.WorkflowVersion}/${workflowActiveVersion.id}`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
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 { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useSeeRunsWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
|
({ recordId }) => {
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const shouldBeRegistered = isDefined(workflowWithCurrentVersion);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterQueryParams: FilterQueryParams = {
|
||||||
|
filter: {
|
||||||
|
workflow: {
|
||||||
|
[ViewFilterOperand.Is]: [workflowWithCurrentVersion.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowRun}?${qs.stringify(
|
||||||
|
filterQueryParams,
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
navigate(filterLinkHref);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
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 { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useSeeVersionsWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
|
({ recordId }) => {
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const shouldBeRegistered = isDefined(workflowWithCurrentVersion);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterQueryParams: FilterQueryParams = {
|
||||||
|
filter: {
|
||||||
|
workflow: {
|
||||||
|
[ViewFilterOperand.Is]: [workflowWithCurrentVersion.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowVersion}?${qs.stringify(
|
||||||
|
filterQueryParams,
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
navigate(filterLinkHref);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,60 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
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,34 @@
|
|||||||
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useTestWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
|
({ recordId }) => {
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||||
|
|
||||||
|
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||||
|
|
||||||
|
const shouldBeRegistered =
|
||||||
|
isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) &&
|
||||||
|
workflowWithCurrentVersion.currentVersion.trigger.type === 'MANUAL' &&
|
||||||
|
!isDefined(
|
||||||
|
workflowWithCurrentVersion.currentVersion.trigger.settings.objectType,
|
||||||
|
);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runWorkflowVersion({
|
||||||
|
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||||
|
workflowName: workflowWithCurrentVersion.name,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,60 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
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,46 @@
|
|||||||
|
import { useSeeExecutionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeExecutionsWorkflowVersionSingleRecordAction';
|
||||||
|
import { useSeeVersionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction';
|
||||||
|
import { useUseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction';
|
||||||
|
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import {
|
||||||
|
ActionMenuEntry,
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { IconHistory, IconHistoryToggle, IconPencil } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
|
||||||
|
string,
|
||||||
|
ActionMenuEntry & {
|
||||||
|
actionHook: SingleRecordActionHook;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
useAsDraftWorkflowVersionSingleRecord: {
|
||||||
|
key: 'use-as-draft-workflow-version-single-record',
|
||||||
|
label: 'Use as draft',
|
||||||
|
position: 1,
|
||||||
|
isPinned: true,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
Icon: IconPencil,
|
||||||
|
actionHook: useUseAsDraftWorkflowVersionSingleRecordAction,
|
||||||
|
},
|
||||||
|
seeWorkflowExecutionsSingleRecord: {
|
||||||
|
key: 'see-workflow-executions-single-record',
|
||||||
|
label: 'See executions',
|
||||||
|
position: 2,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
Icon: IconHistoryToggle,
|
||||||
|
actionHook: useSeeExecutionsWorkflowVersionSingleRecordAction,
|
||||||
|
},
|
||||||
|
seeWorkflowVersionsHistorySingleRecord: {
|
||||||
|
key: 'see-workflow-versions-history-single-record',
|
||||||
|
label: 'See versions history',
|
||||||
|
position: 3,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
Icon: IconHistory,
|
||||||
|
actionHook: useSeeVersionsWorkflowVersionSingleRecordAction,
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
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 { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useSeeExecutionsWorkflowVersionSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
|
({ recordId }) => {
|
||||||
|
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
|
||||||
|
workflowVersion?.workflow.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const shouldBeRegistered = isDefined(workflowWithCurrentVersion);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!shouldBeRegistered) return;
|
||||||
|
|
||||||
|
const filterQueryParams: FilterQueryParams = {
|
||||||
|
filter: {
|
||||||
|
workflow: {
|
||||||
|
[ViewFilterOperand.Is]: [workflowWithCurrentVersion.id],
|
||||||
|
},
|
||||||
|
workflowVersion: {
|
||||||
|
[ViewFilterOperand.Is]: [recordId],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowRun}?${qs.stringify(
|
||||||
|
filterQueryParams,
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
navigate(filterLinkHref);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
|
||||||
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useSeeVersionsWorkflowVersionSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
|
({ recordId }) => {
|
||||||
|
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||||
|
|
||||||
|
if (!isDefined(workflowVersion)) {
|
||||||
|
throw new Error('Workflow version not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { shouldBeRegistered, onClick } =
|
||||||
|
useSeeVersionsWorkflowSingleRecordAction({
|
||||||
|
recordId: workflowVersion.workflow.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldBeRegistered,
|
||||||
|
onClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,78 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,92 +1,66 @@
|
|||||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook';
|
||||||
import {
|
|
||||||
ActionMenuEntryScope,
|
|
||||||
ActionMenuEntryType,
|
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
|
||||||
import { OverrideWorkflowDraftConfirmationModal } from '@/workflow/components/OverrideWorkflowDraftConfirmationModal';
|
import { OverrideWorkflowDraftConfirmationModal } from '@/workflow/components/OverrideWorkflowDraftConfirmationModal';
|
||||||
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
||||||
|
import { useWorkflowVersion } from '@/workflow/hooks/useWorkflowVersion';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { openOverrideWorkflowDraftConfirmationModalState } from '@/workflow/states/openOverrideWorkflowDraftConfirmationModalState';
|
import { openOverrideWorkflowDraftConfirmationModalState } from '@/workflow/states/openOverrideWorkflowDraftConfirmationModalState';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
import { IconPencil, isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useUseAsDraftWorkflowVersionSingleRecordAction = ({
|
export const useUseAsDraftWorkflowVersionSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
|
||||||
workflowVersionId,
|
({ recordId }) => {
|
||||||
}: {
|
const workflowVersion = useWorkflowVersion(recordId);
|
||||||
workflowVersionId: string;
|
|
||||||
}) => {
|
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
|
||||||
|
|
||||||
const workflowVersion = useRecoilValue(
|
const workflow = useWorkflowWithCurrentVersion(
|
||||||
recordStoreFamilyState(workflowVersionId),
|
workflowVersion?.workflow?.id ?? '',
|
||||||
);
|
);
|
||||||
|
|
||||||
const workflow = useWorkflowWithCurrentVersion(
|
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion();
|
||||||
workflowVersion?.workflow?.id ?? '',
|
|
||||||
);
|
|
||||||
|
|
||||||
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion();
|
const setOpenOverrideWorkflowDraftConfirmationModal = useSetRecoilState(
|
||||||
|
openOverrideWorkflowDraftConfirmationModalState,
|
||||||
|
);
|
||||||
|
|
||||||
const setOpenOverrideWorkflowDraftConfirmationModal = useSetRecoilState(
|
const workflowStatuses = workflow?.statuses;
|
||||||
openOverrideWorkflowDraftConfirmationModalState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const registerUseAsDraftWorkflowVersionSingleRecordAction = ({
|
const shouldBeRegistered =
|
||||||
position,
|
isDefined(workflowVersion) &&
|
||||||
}: {
|
isDefined(workflow) &&
|
||||||
position: number;
|
isDefined(workflowStatuses) &&
|
||||||
}) => {
|
workflowVersion.status !== 'DRAFT';
|
||||||
if (
|
|
||||||
!isDefined(workflowVersion) ||
|
|
||||||
!isDefined(workflow) ||
|
|
||||||
!isDefined(workflow.statuses) ||
|
|
||||||
workflowVersion.status === 'DRAFT'
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasAlreadyDraftVersion = workflow.statuses.includes('DRAFT');
|
const onClick = async () => {
|
||||||
|
if (!shouldBeRegistered) return;
|
||||||
|
|
||||||
addActionMenuEntry({
|
const hasAlreadyDraftVersion = workflowStatuses.includes('DRAFT');
|
||||||
key: 'use-workflow-version-as-draft-single-record',
|
|
||||||
label: 'Use as draft',
|
if (hasAlreadyDraftVersion) {
|
||||||
position,
|
setOpenOverrideWorkflowDraftConfirmationModal(true);
|
||||||
Icon: IconPencil,
|
} else {
|
||||||
type: ActionMenuEntryType.Standard,
|
await createNewWorkflowVersion({
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
workflowId: workflowVersion.workflow.id,
|
||||||
onClick: async () => {
|
name: `v${workflow.versions.length + 1}`,
|
||||||
if (hasAlreadyDraftVersion) {
|
status: 'DRAFT',
|
||||||
setOpenOverrideWorkflowDraftConfirmationModal(true);
|
trigger: workflowVersion.trigger,
|
||||||
} else {
|
steps: workflowVersion.steps,
|
||||||
await createNewWorkflowVersion({
|
});
|
||||||
workflowId: workflowVersion.workflow.id,
|
}
|
||||||
name: `v${workflow.versions.length + 1}`,
|
};
|
||||||
status: 'DRAFT',
|
|
||||||
trigger: workflowVersion.trigger,
|
const ConfirmationModal = shouldBeRegistered ? (
|
||||||
steps: workflowVersion.steps,
|
<OverrideWorkflowDraftConfirmationModal
|
||||||
});
|
draftWorkflowVersionId={workflow?.currentVersion?.id ?? ''}
|
||||||
}
|
workflowId={workflow?.id ?? ''}
|
||||||
},
|
workflowVersionUpdateInput={{
|
||||||
ConfirmationModal: (
|
steps: workflowVersion.steps,
|
||||||
<OverrideWorkflowDraftConfirmationModal
|
trigger: workflowVersion.trigger,
|
||||||
draftWorkflowVersionId={workflow?.currentVersion?.id ?? ''}
|
}}
|
||||||
workflowId={workflow?.id ?? ''}
|
/>
|
||||||
workflowVersionUpdateInput={{
|
) : undefined;
|
||||||
steps: workflowVersion.steps,
|
|
||||||
trigger: workflowVersion.trigger,
|
return {
|
||||||
}}
|
shouldBeRegistered,
|
||||||
/>
|
onClick,
|
||||||
),
|
ConfirmationModal,
|
||||||
});
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const unregisterUseAsDraftWorkflowVersionSingleRecordAction = () => {
|
|
||||||
removeActionMenuEntry('use-workflow-version-as-draft-single-record');
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerUseAsDraftWorkflowVersionSingleRecordAction,
|
|
||||||
unregisterUseAsDraftWorkflowVersionSingleRecordAction,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,70 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export type ActionHookResult = {
|
||||||
|
shouldBeRegistered: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
ConfirmationModal?: React.ReactElement;
|
||||||
|
};
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { ActionHookResult } from '@/action-menu/actions/types/actionHookResult';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
|
||||||
|
export type SingleRecordActionHook =
|
||||||
|
| SingleRecordActionHookWithoutObjectMetadataItem
|
||||||
|
| SingleRecordActionHookWithObjectMetadataItem;
|
||||||
|
|
||||||
|
export type SingleRecordActionHookWithoutObjectMetadataItem = ({
|
||||||
|
recordId,
|
||||||
|
}: {
|
||||||
|
recordId: string;
|
||||||
|
}) => ActionHookResult;
|
||||||
|
|
||||||
|
export type SingleRecordActionHookWithObjectMetadataItem = ({
|
||||||
|
recordId,
|
||||||
|
objectMetadataItem,
|
||||||
|
}: {
|
||||||
|
recordId: string;
|
||||||
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
|
}) => ActionHookResult;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { MouseEvent, ReactNode } from 'react';
|
import { MouseEvent, ReactElement } from 'react';
|
||||||
import { IconComponent, MenuItemAccent } from 'twenty-ui';
|
import { IconComponent, MenuItemAccent } from 'twenty-ui';
|
||||||
|
|
||||||
export enum ActionMenuEntryType {
|
export enum ActionMenuEntryType {
|
||||||
@ -21,5 +21,5 @@ export type ActionMenuEntry = {
|
|||||||
isPinned?: boolean;
|
isPinned?: boolean;
|
||||||
accent?: MenuItemAccent;
|
accent?: MenuItemAccent;
|
||||||
onClick?: (event?: MouseEvent<HTMLElement>) => void;
|
onClick?: (event?: MouseEvent<HTMLElement>) => void;
|
||||||
ConfirmationModal?: ReactNode;
|
ConfirmationModal?: ReactElement;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -121,12 +121,50 @@ export const useCommandMenu = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const closeCommandMenu = useRecoilCallback(
|
const closeCommandMenu = useRecoilCallback(
|
||||||
({ snapshot }) =>
|
({ snapshot, set }) =>
|
||||||
() => {
|
() => {
|
||||||
const isCommandMenuOpened = snapshot
|
const isCommandMenuOpened = snapshot
|
||||||
.getLoadable(isCommandMenuOpenedState)
|
.getLoadable(isCommandMenuOpenedState)
|
||||||
.getValue();
|
.getValue();
|
||||||
|
|
||||||
|
set(
|
||||||
|
contextStoreCurrentObjectMetadataIdComponentState.atomFamily({
|
||||||
|
instanceId: 'command-menu',
|
||||||
|
}),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
set(
|
||||||
|
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||||
|
instanceId: 'command-menu',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: [],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
set(
|
||||||
|
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||||
|
instanceId: 'command-menu',
|
||||||
|
}),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
set(
|
||||||
|
contextStoreFiltersComponentState.atomFamily({
|
||||||
|
instanceId: 'command-menu',
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
set(
|
||||||
|
contextStoreCurrentViewIdComponentState.atomFamily({
|
||||||
|
instanceId: 'command-menu',
|
||||||
|
}),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
if (isCommandMenuOpened) {
|
if (isCommandMenuOpened) {
|
||||||
setIsCommandMenuOpened(false);
|
setIsCommandMenuOpened(false);
|
||||||
resetSelectedItem();
|
resetSelectedItem();
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import { ViewType } from '@/views/types/ViewType';
|
|||||||
import { MockedResponse } from '@apollo/client/testing';
|
import { MockedResponse } from '@apollo/client/testing';
|
||||||
import { expect } from '@storybook/test';
|
import { expect } from '@storybook/test';
|
||||||
import gql from 'graphql-tag';
|
import gql from 'graphql-tag';
|
||||||
import { getJestMetadataAndApolloMocksAndContextStoreWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
|
||||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
|
||||||
const defaultResponseData = {
|
const defaultResponseData = {
|
||||||
@ -130,17 +130,15 @@ const mocks: MockedResponse[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const WrapperWithResponse = getJestMetadataAndApolloMocksAndContextStoreWrapper(
|
const WrapperWithResponse = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
{
|
apolloMocks: mocks,
|
||||||
apolloMocks: mocks,
|
componentInstanceId: 'recordIndexId',
|
||||||
componentInstanceId: 'recordIndexId',
|
contextStoreTargetedRecordsRule: {
|
||||||
contextStoreTargetedRecordsRule: {
|
mode: 'selection',
|
||||||
mode: 'selection',
|
selectedRecordIds: [],
|
||||||
selectedRecordIds: [],
|
|
||||||
},
|
|
||||||
contextStoreCurrentObjectMetadataNameSingular: 'person',
|
|
||||||
},
|
},
|
||||||
);
|
contextStoreCurrentObjectMetadataNameSingular: 'person',
|
||||||
|
});
|
||||||
|
|
||||||
const graphqlEmptyResponse = [
|
const graphqlEmptyResponse = [
|
||||||
{
|
{
|
||||||
@ -157,7 +155,7 @@ const graphqlEmptyResponse = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const WrapperWithEmptyResponse =
|
const WrapperWithEmptyResponse =
|
||||||
getJestMetadataAndApolloMocksAndContextStoreWrapper({
|
getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||||
apolloMocks: graphqlEmptyResponse,
|
apolloMocks: graphqlEmptyResponse,
|
||||||
componentInstanceId: 'recordIndexId',
|
componentInstanceId: 'recordIndexId',
|
||||||
contextStoreTargetedRecordsRule: {
|
contextStoreTargetedRecordsRule: {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { PageContainer } from '@/ui/layout/page/components/PageContainer';
|
|||||||
import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
|
import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
|
||||||
import { RecordShowPageWorkflowHeader } from '@/workflow/components/RecordShowPageWorkflowHeader';
|
import { RecordShowPageWorkflowHeader } from '@/workflow/components/RecordShowPageWorkflowHeader';
|
||||||
import { RecordShowPageWorkflowVersionHeader } from '@/workflow/components/RecordShowPageWorkflowVersionHeader';
|
import { RecordShowPageWorkflowVersionHeader } from '@/workflow/components/RecordShowPageWorkflowVersionHeader';
|
||||||
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
import { RecordShowPageHeader } from '~/pages/object-record/RecordShowPageHeader';
|
import { RecordShowPageHeader } from '~/pages/object-record/RecordShowPageHeader';
|
||||||
|
|
||||||
export const RecordShowPage = () => {
|
export const RecordShowPage = () => {
|
||||||
@ -38,6 +39,10 @@ export const RecordShowPage = () => {
|
|||||||
parameters.objectRecordId ?? '',
|
parameters.objectRecordId ?? '',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isPageHeaderV2Enabled = useIsFeatureEnabled(
|
||||||
|
'IS_PAGE_HEADER_V2_ENABLED',
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecordFieldValueSelectorContextProvider>
|
<RecordFieldValueSelectorContextProvider>
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
@ -57,25 +62,30 @@ export const RecordShowPage = () => {
|
|||||||
headerIcon={headerIcon}
|
headerIcon={headerIcon}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{objectNameSingular === CoreObjectNameSingular.Workflow ? (
|
{!isPageHeaderV2Enabled &&
|
||||||
<RecordShowPageWorkflowHeader workflowId={objectRecordId} />
|
objectNameSingular === CoreObjectNameSingular.Workflow && (
|
||||||
) : objectNameSingular ===
|
<RecordShowPageWorkflowHeader workflowId={objectRecordId} />
|
||||||
CoreObjectNameSingular.WorkflowVersion ? (
|
)}
|
||||||
<RecordShowPageWorkflowVersionHeader
|
{!isPageHeaderV2Enabled &&
|
||||||
workflowVersionId={objectRecordId}
|
objectNameSingular ===
|
||||||
/>
|
CoreObjectNameSingular.WorkflowVersion && (
|
||||||
) : (
|
<RecordShowPageWorkflowVersionHeader
|
||||||
<>
|
workflowVersionId={objectRecordId}
|
||||||
<RecordShowActionMenu
|
|
||||||
{...{
|
|
||||||
isFavorite,
|
|
||||||
record,
|
|
||||||
handleFavoriteButtonClick,
|
|
||||||
objectMetadataItem,
|
|
||||||
objectNameSingular,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</>
|
)}
|
||||||
|
{(isPageHeaderV2Enabled ||
|
||||||
|
(objectNameSingular !== CoreObjectNameSingular.Workflow &&
|
||||||
|
objectNameSingular !==
|
||||||
|
CoreObjectNameSingular.WorkflowVersion)) && (
|
||||||
|
<RecordShowActionMenu
|
||||||
|
{...{
|
||||||
|
isFavorite,
|
||||||
|
record,
|
||||||
|
handleFavoriteButtonClick,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectNameSingular,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
</RecordShowPageHeader>
|
</RecordShowPageHeader>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
import { MockedResponse } from '@apollo/client/testing';
|
import { MockedResponse } from '@apollo/client/testing';
|
||||||
@ -6,13 +7,7 @@ import { MutableSnapshot } from 'recoil';
|
|||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter';
|
import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter';
|
||||||
|
|
||||||
export const getJestMetadataAndApolloMocksAndContextStoreWrapper = ({
|
export type GetJestMetadataAndApolloMocksAndActionMenuWrapperProps = {
|
||||||
apolloMocks,
|
|
||||||
onInitializeRecoilSnapshot,
|
|
||||||
contextStoreTargetedRecordsRule,
|
|
||||||
contextStoreCurrentObjectMetadataNameSingular,
|
|
||||||
componentInstanceId,
|
|
||||||
}: {
|
|
||||||
apolloMocks:
|
apolloMocks:
|
||||||
| readonly MockedResponse<Record<string, any>, Record<string, any>>[]
|
| readonly MockedResponse<Record<string, any>, Record<string, any>>[]
|
||||||
| undefined;
|
| undefined;
|
||||||
@ -20,11 +15,20 @@ export const getJestMetadataAndApolloMocksAndContextStoreWrapper = ({
|
|||||||
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
|
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
|
||||||
contextStoreCurrentObjectMetadataNameSingular?: string;
|
contextStoreCurrentObjectMetadataNameSingular?: string;
|
||||||
componentInstanceId: string;
|
componentInstanceId: string;
|
||||||
}) => {
|
};
|
||||||
|
|
||||||
|
export const getJestMetadataAndApolloMocksAndActionMenuWrapper = ({
|
||||||
|
apolloMocks,
|
||||||
|
onInitializeRecoilSnapshot,
|
||||||
|
contextStoreTargetedRecordsRule,
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular,
|
||||||
|
componentInstanceId,
|
||||||
|
}: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps) => {
|
||||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
apolloMocks,
|
apolloMocks,
|
||||||
onInitializeRecoilSnapshot,
|
onInitializeRecoilSnapshot,
|
||||||
});
|
});
|
||||||
|
|
||||||
return ({ children }: { children: ReactNode }) => (
|
return ({ children }: { children: ReactNode }) => (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
@ -32,14 +36,20 @@ export const getJestMetadataAndApolloMocksAndContextStoreWrapper = ({
|
|||||||
instanceId: componentInstanceId,
|
instanceId: componentInstanceId,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<JestContextStoreSetter
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
contextStoreTargetedRecordsRule={contextStoreTargetedRecordsRule}
|
value={{
|
||||||
contextStoreCurrentObjectMetadataNameSingular={
|
instanceId: componentInstanceId,
|
||||||
contextStoreCurrentObjectMetadataNameSingular
|
}}
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{children}
|
<JestContextStoreSetter
|
||||||
</JestContextStoreSetter>
|
contextStoreTargetedRecordsRule={contextStoreTargetedRecordsRule}
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular={
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</JestContextStoreSetter>
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user