Command menu actions fixes (#9169)

- Fix command menu not closing after executing an action
- Add delete and favorite actions to workflow and workflow versions
This commit is contained in:
Raphaël Bosi
2024-12-20 14:42:01 +01:00
committed by GitHub
parent f65a90d137
commit d08075f610
31 changed files with 269 additions and 55 deletions

View File

@ -3,6 +3,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { IconDatabaseExport } from 'twenty-ui'; import { IconDatabaseExport } from 'twenty-ui';
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys'; import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { import {
ActionMenuEntryScope, ActionMenuEntryScope,
ActionMenuEntryType, ActionMenuEntryType,
@ -11,6 +12,7 @@ import {
displayedExportProgress, displayedExportProgress,
useExportRecords, useExportRecords,
} from '@/object-record/record-index/export/hooks/useExportRecords'; } from '@/object-record/record-index/export/hooks/useExportRecords';
import { useContext } from 'react';
export const useExportMultipleRecordsAction = ({ export const useExportMultipleRecordsAction = ({
objectMetadataItem, objectMetadataItem,
@ -26,6 +28,9 @@ export const useExportMultipleRecordsAction = ({
filename: `${objectMetadataItem.nameSingular}.csv`, filename: `${objectMetadataItem.nameSingular}.csv`,
}); });
const { onActionStartedCallback, onActionExecutedCallback } =
useContext(ActionMenuContext);
const registerExportMultipleRecordsAction = ({ const registerExportMultipleRecordsAction = ({
position, position,
}: { }: {
@ -40,7 +45,15 @@ export const useExportMultipleRecordsAction = ({
shortLabel: 'Export', shortLabel: 'Export',
Icon: IconDatabaseExport, Icon: IconDatabaseExport,
accent: 'default', accent: 'default',
onClick: () => download(), onClick: async () => {
await onActionStartedCallback?.({
key: MultipleRecordsActionKeys.EXPORT,
});
await download();
await onActionExecutedCallback?.({
key: MultipleRecordsActionKeys.EXPORT,
});
},
}); });
}; };

View File

@ -1,11 +1,13 @@
import { getActionConfig } from '@/action-menu/actions/record-actions/single-record/utils/getActionConfig'; import { getActionConfig } from '@/action-menu/actions/record-actions/single-record/utils/getActionConfig';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn'; import { ActionAvailableOn } from '@/action-menu/actions/types/ActionAvailableOn';
import { wrapActionInCallbacks } from '@/action-menu/actions/utils/wrapActionInCallbacks';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries'; import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; 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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';
export const ShowPageSingleRecordActionMenuEntrySetterEffect = ({ export const ShowPageSingleRecordActionMenuEntrySetterEffect = ({
@ -36,6 +38,8 @@ export const ShowPageSingleRecordActionMenuEntrySetterEffect = ({
if (!isDefined(selectedRecordId)) { if (!isDefined(selectedRecordId)) {
throw new Error('Selected record ID is required'); throw new Error('Selected record ID is required');
} }
const { onActionStartedCallback, onActionExecutedCallback } =
useContext(ActionMenuContext);
const actionMenuEntries = Object.values(actionConfig ?? {}) const actionMenuEntries = Object.values(actionConfig ?? {})
.filter((action) => .filter((action) =>
@ -48,15 +52,21 @@ export const ShowPageSingleRecordActionMenuEntrySetterEffect = ({
objectMetadataItem, objectMetadataItem,
}); });
if (shouldBeRegistered) { if (!shouldBeRegistered) {
return { return undefined;
}
const wrappedAction = wrapActionInCallbacks({
action: {
...action, ...action,
onClick, onClick,
ConfirmationModal, ConfirmationModal,
}; },
} onActionStartedCallback,
onActionExecutedCallback,
});
return undefined; return wrappedAction;
}) })
.filter(isDefined); .filter(isDefined);

View File

@ -1,11 +1,13 @@
import { getActionConfig } from '@/action-menu/actions/record-actions/single-record/utils/getActionConfig'; import { getActionConfig } from '@/action-menu/actions/record-actions/single-record/utils/getActionConfig';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn'; import { ActionAvailableOn } from '@/action-menu/actions/types/ActionAvailableOn';
import { wrapActionInCallbacks } from '@/action-menu/actions/utils/wrapActionInCallbacks';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries'; import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; 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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';
export const SingleRecordActionMenuEntrySetterEffect = ({ export const SingleRecordActionMenuEntrySetterEffect = ({
@ -37,6 +39,9 @@ export const SingleRecordActionMenuEntrySetterEffect = ({
throw new Error('Selected record ID is required'); throw new Error('Selected record ID is required');
} }
const { onActionStartedCallback, onActionExecutedCallback } =
useContext(ActionMenuContext);
const actionMenuEntries = Object.values(actionConfig ?? {}) const actionMenuEntries = Object.values(actionConfig ?? {})
.filter((action) => .filter((action) =>
action.availableOn?.includes( action.availableOn?.includes(
@ -50,15 +55,21 @@ export const SingleRecordActionMenuEntrySetterEffect = ({
objectMetadataItem, objectMetadataItem,
}); });
if (shouldBeRegistered) { if (!shouldBeRegistered) {
return { return undefined;
}
const wrappedAction = wrapActionInCallbacks({
action: {
...action, ...action,
onClick, onClick,
ConfirmationModal, ConfirmationModal,
}; },
} onActionStartedCallback,
onActionExecutedCallback,
});
return undefined; return wrappedAction;
}) })
.filter(isDefined); .filter(isDefined);

View File

@ -2,8 +2,8 @@ import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/recor
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction'; 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 { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey'; import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn'; import { ActionAvailableOn } from '@/action-menu/actions/types/ActionAvailableOn';
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
import { import {
ActionMenuEntry, ActionMenuEntry,
ActionMenuEntryScope, ActionMenuEntryScope,

View File

@ -5,8 +5,8 @@ import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction'; import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction'; import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey'; import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn'; import { ActionAvailableOn } from '@/action-menu/actions/types/ActionAvailableOn';
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
import { import {
ActionMenuEntry, ActionMenuEntry,
ActionMenuEntryScope, ActionMenuEntryScope,

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite'; import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useFavorites } from '@/favorites/hooks/useFavorites';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite'; import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useFavorites } from '@/favorites/hooks/useFavorites';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord'; import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
@ -34,8 +34,7 @@ export const useDestroySingleRecordAction: SingleRecordActionHookWithObjectMetad
const isRemoteObject = objectMetadataItem.isRemote; const isRemoteObject = objectMetadataItem.isRemote;
const { isInRightDrawer, onActionExecutedCallback } = const { isInRightDrawer } = useContext(ActionMenuContext);
useContext(ActionMenuContext);
const shouldBeRegistered = const shouldBeRegistered =
!isRemoteObject && isDefined(selectedRecord?.deletedAt); !isRemoteObject && isDefined(selectedRecord?.deletedAt);
@ -61,7 +60,6 @@ export const useDestroySingleRecordAction: SingleRecordActionHookWithObjectMetad
} }
onConfirmClick={async () => { onConfirmClick={async () => {
await handleDeleteClick(); await handleDeleteClick();
onActionExecutedCallback?.({ key: 'destroy-single-record' });
if (isInRightDrawer) { if (isInRightDrawer) {
closeRightDrawer(); closeRightDrawer();
} }

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination'; import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
export const useNavigateToNextRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem = export const useNavigateToNextRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination'; import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
export const useNavigateToPreviousRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem = export const useNavigateToPreviousRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
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 { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';

View File

@ -1,5 +1,9 @@
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 { useDestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction';
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction'; import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction'; import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey'; import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
import { useActivateDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateDraftWorkflowSingleRecordAction'; 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 { useActivateLastPublishedVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateLastPublishedVersionWorkflowSingleRecordAction';
@ -10,8 +14,8 @@ import { useSeeRunsWorkflowSingleRecordAction } from '@/action-menu/actions/reco
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction'; 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 { useTestWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction';
import { WorkflowSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-actions/types/WorkflowSingleRecordActionsKeys'; import { WorkflowSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-actions/types/WorkflowSingleRecordActionsKeys';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn'; import { ActionAvailableOn } from '@/action-menu/actions/types/ActionAvailableOn';
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
import { import {
ActionMenuEntry, ActionMenuEntry,
ActionMenuEntryScope, ActionMenuEntryScope,
@ -20,12 +24,15 @@ import {
import { import {
IconChevronDown, IconChevronDown,
IconChevronUp, IconChevronUp,
IconHeart,
IconHeartOff,
IconHistory, IconHistory,
IconHistoryToggle, IconHistoryToggle,
IconPlayerPause, IconPlayerPause,
IconPlayerPlay, IconPlayerPlay,
IconPower, IconPower,
IconTrash, IconTrash,
IconTrashX,
} from 'twenty-ui'; } from 'twenty-ui';
export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record< export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
@ -176,4 +183,66 @@ export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
availableOn: [ActionAvailableOn.SHOW_PAGE], availableOn: [ActionAvailableOn.SHOW_PAGE],
actionHook: useNavigateToNextRecordSingleRecordAction, actionHook: useNavigateToNextRecordSingleRecordAction,
}, },
addToFavoritesSingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.ADD_TO_FAVORITES,
label: 'Add to favorites',
shortLabel: 'Add to favorites',
position: 11,
isPinned: false,
Icon: IconHeart,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useAddToFavoritesSingleRecordAction,
},
removeFromFavoritesSingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
label: 'Remove from favorites',
shortLabel: 'Remove from favorites',
isPinned: false,
position: 12,
Icon: IconHeartOff,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useRemoveFromFavoritesSingleRecordAction,
},
deleteSingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.DELETE,
label: 'Delete record',
shortLabel: 'Delete',
position: 13,
Icon: IconTrash,
accent: 'danger',
isPinned: false,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useDeleteSingleRecordAction,
},
destroySingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.DESTROY,
label: 'Permanently destroy record',
shortLabel: 'Destroy',
position: 14,
Icon: IconTrashX,
accent: 'danger',
isPinned: false,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useDestroySingleRecordAction,
},
}; };

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion'; import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion'; import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion'; import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
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 { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion'; import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams'; import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams'; import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion'; import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';

View File

@ -1,12 +1,16 @@
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 { useDestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction';
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction'; import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction'; import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey'; import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
import { useSeeRunsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction'; import { useSeeRunsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction';
import { useSeeVersionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction'; 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 { useUseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction';
import { WorkflowVersionSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/types/WorkflowVersionSingleRecordActionsKeys'; import { WorkflowVersionSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/types/WorkflowVersionSingleRecordActionsKeys';
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn'; import { ActionAvailableOn } from '@/action-menu/actions/types/ActionAvailableOn';
import { SingleRecordActionHook } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
import { import {
ActionMenuEntry, ActionMenuEntry,
ActionMenuEntryScope, ActionMenuEntryScope,
@ -15,9 +19,13 @@ import {
import { import {
IconChevronDown, IconChevronDown,
IconChevronUp, IconChevronUp,
IconHeart,
IconHeartOff,
IconHistory, IconHistory,
IconHistoryToggle, IconHistoryToggle,
IconPencil, IconPencil,
IconTrash,
IconTrashX,
} from 'twenty-ui'; } from 'twenty-ui';
export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record< export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
@ -72,7 +80,7 @@ export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
key: SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD, key: SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
label: 'Navigate to previous version', label: 'Navigate to previous version',
shortLabel: '', shortLabel: '',
position: 9, position: 4,
Icon: IconChevronUp, Icon: IconChevronUp,
availableOn: [ActionAvailableOn.SHOW_PAGE], availableOn: [ActionAvailableOn.SHOW_PAGE],
actionHook: useNavigateToPreviousRecordSingleRecordAction, actionHook: useNavigateToPreviousRecordSingleRecordAction,
@ -83,9 +91,71 @@ export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
key: SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD, key: SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
label: 'Navigate to next version', label: 'Navigate to next version',
shortLabel: '', shortLabel: '',
position: 10, position: 5,
Icon: IconChevronDown, Icon: IconChevronDown,
availableOn: [ActionAvailableOn.SHOW_PAGE], availableOn: [ActionAvailableOn.SHOW_PAGE],
actionHook: useNavigateToNextRecordSingleRecordAction, actionHook: useNavigateToNextRecordSingleRecordAction,
}, },
addToFavoritesSingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.ADD_TO_FAVORITES,
label: 'Add to favorites',
shortLabel: 'Add to favorites',
position: 6,
isPinned: false,
Icon: IconHeart,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useAddToFavoritesSingleRecordAction,
},
removeFromFavoritesSingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
label: 'Remove from favorites',
shortLabel: 'Remove from favorites',
isPinned: false,
position: 7,
Icon: IconHeartOff,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useRemoveFromFavoritesSingleRecordAction,
},
deleteSingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.DELETE,
label: 'Delete record',
shortLabel: 'Delete',
position: 8,
Icon: IconTrash,
accent: 'danger',
isPinned: false,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useDeleteSingleRecordAction,
},
destroySingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.DESTROY,
label: 'Permanently destroy record',
shortLabel: 'Destroy',
position: 9,
Icon: IconTrashX,
accent: 'danger',
isPinned: false,
availableOn: [
ActionAvailableOn.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionAvailableOn.SHOW_PAGE,
],
actionHook: useDestroySingleRecordAction,
},
}; };

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams'; import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';

View File

@ -1,5 +1,5 @@
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction'; import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';

View File

@ -1,4 +1,4 @@
import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/singleRecordActionHook'; import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
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 { useWorkflowVersion } from '@/workflow/hooks/useWorkflowVersion';

View File

@ -0,0 +1,7 @@
import { ConfirmationModalProps } from '@/ui/layout/modal/components/ConfirmationModal';
export type ActionHookResult = {
shouldBeRegistered: boolean;
onClick: () => Promise<void> | void;
ConfirmationModal?: React.ReactElement<ConfirmationModalProps>;
};

View File

@ -1,4 +1,4 @@
import { ActionHookResult } from '@/action-menu/actions/types/actionHookResult'; import { ActionHookResult } from '@/action-menu/actions/types/ActionHookResult';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export type SingleRecordActionHook = export type SingleRecordActionHook =

View File

@ -1,5 +0,0 @@
export type ActionHookResult = {
shouldBeRegistered: boolean;
onClick: () => void;
ConfirmationModal?: React.ReactElement;
};

View File

@ -0,0 +1,40 @@
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { isDefined } from 'twenty-ui';
export const wrapActionInCallbacks = ({
action,
onActionStartedCallback,
onActionExecutedCallback,
}: {
action: ActionMenuEntry;
onActionStartedCallback?: (action: { key: string }) => Promise<void> | void;
onActionExecutedCallback?: (action: { key: string }) => Promise<void> | void;
}) => {
const onClickWithCallbacks = isDefined(action.ConfirmationModal)
? action.onClick
: async () => {
await onActionStartedCallback?.({ key: action.key });
await action.onClick?.();
await onActionExecutedCallback?.({ key: action.key });
};
const ConfirmationModalWithCallbacks = isDefined(action.ConfirmationModal)
? {
...action.ConfirmationModal,
props: {
...action.ConfirmationModal.props,
onConfirmClick: async () => {
await onActionStartedCallback?.({ key: action.key });
await action.ConfirmationModal?.props.onConfirmClick?.();
await onActionExecutedCallback?.({ key: action.key });
},
},
}
: undefined;
return {
...action,
onClick: onClickWithCallbacks,
ConfirmationModal: ConfirmationModalWithCallbacks,
};
};

View File

@ -2,8 +2,8 @@ import { createContext } from 'react';
type ActionMenuContextType = { type ActionMenuContextType = {
isInRightDrawer: boolean; isInRightDrawer: boolean;
onActionStartedCallback?: (action: { key: string }) => void; onActionStartedCallback?: (action: { key: string }) => Promise<void> | void;
onActionExecutedCallback?: (action: { key: string }) => void; onActionExecutedCallback?: (action: { key: string }) => Promise<void> | void;
}; };
export const ActionMenuContext = createContext<ActionMenuContextType>({ export const ActionMenuContext = createContext<ActionMenuContextType>({

View File

@ -1,4 +1,5 @@
import { ActionAvailableOn } from '@/action-menu/actions/types/actionAvailableOn'; import { ActionAvailableOn } from '@/action-menu/actions/types/ActionAvailableOn';
import { ConfirmationModalProps } from '@/ui/layout/modal/components/ConfirmationModal';
import { MouseEvent, ReactElement } from 'react'; import { MouseEvent, ReactElement } from 'react';
import { IconComponent, MenuItemAccent } from 'twenty-ui'; import { IconComponent, MenuItemAccent } from 'twenty-ui';
@ -24,5 +25,5 @@ export type ActionMenuEntry = {
accent?: MenuItemAccent; accent?: MenuItemAccent;
availableOn?: ActionAvailableOn[]; availableOn?: ActionAvailableOn[];
onClick?: (event?: MouseEvent<HTMLElement>) => void; onClick?: (event?: MouseEvent<HTMLElement>) => void;
ConfirmationModal?: ReactElement; ConfirmationModal?: ReactElement<ConfirmationModalProps>;
}; };