From e3f7a0572e398d5aac6f70dfd690e18b993459b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?=
<71827178+bosiraphael@users.noreply.github.com>
Date: Thu, 2 Jan 2025 13:15:27 +0100
Subject: [PATCH] 9260 refactor multiple record actions and no selection
actions (#9314)
Closes #9260
- Refactored multiple record actions and no selection record actions to
use config file
- Simplified actions registration logic
- Updated tests
---
.../RecordActionMenuEntriesSetter.tsx | 104 ++++----
.../components/RegisterRecordActionEffect.tsx | 55 ++++
.../DefaultActionsConfigV1.ts} | 56 +++-
.../DefaultActionsConfigV2.ts} | 56 +++-
.../WorkflowActionsConfig.ts} | 52 +++-
.../WorkflowRunsActionsConfig.ts} | 36 ++-
.../WorkflowVersionsActionsConfig.ts} | 36 ++-
...ipleRecordsActionMenuEntrySetterEffect.tsx | 24 --
.../useDeleteMultipleRecordsAction.test.tsx | 151 ++++-------
.../useExportMultipleRecordsAction.test.tsx | 124 +++------
.../hooks/useDeleteMultipleRecordsAction.tsx | 241 +++++++-----------
.../hooks/useExportMultipleRecordsAction.tsx | 55 +---
.../hooks/useMultipleRecordsActions.tsx | 38 ---
...NoSelectionActionMenuEntrySetterEffect.tsx | 26 --
...ExportViewNoSelectionRecordAction.test.tsx | 108 --------
.../useExportViewNoSelectionRecordAction.tsx | 54 ----
.../hooks/useNoSelectionRecordActions.tsx | 28 --
...ingleRecordActionMenuEntrySetterEffect.tsx | 34 ---
...eAddToFavoritesSingleRecordAction.test.tsx | 3 -
.../useDeleteSingleRecordAction.test.tsx | 1 -
...veFromFavoritesSingleRecordAction.test.tsx | 3 -
.../useAddToFavoritesSingleRecordAction.ts | 9 +-
.../hooks/useDeleteSingleRecordAction.tsx | 126 ++++-----
.../hooks/useDestroySingleRecordAction.tsx | 98 +++----
.../hooks/useExportNoteAction.ts | 68 ++---
...eNavigateToNextRecordSingleRecordAction.ts | 9 +-
...igateToPreviousRecordSingleRecordAction.ts | 10 +-
...seRemoveFromFavoritesSingleRecordAction.ts | 9 +-
.../hooks/useSelectedRecordIdOrThrow.tsx | 18 ++
.../single-record/utils/getActionConfig.ts | 27 --
...ateDraftWorkflowSingleRecordAction.test.ts | 10 +-
...dVersionWorkflowSingleRecordAction.test.ts | 10 +-
...activateWorkflowSingleRecordAction.test.ts | 15 +-
...ardDraftWorkflowSingleRecordAction.test.ts | 20 +-
...ActivateDraftWorkflowSingleRecordAction.ts | 9 +-
...lishedVersionWorkflowSingleRecordAction.ts | 9 +-
...useDeactivateWorkflowSingleRecordAction.ts | 9 +-
...eDiscardDraftWorkflowSingleRecordAction.ts | 9 +-
...ActiveVersionWorkflowSingleRecordAction.ts | 9 +-
.../useSeeRunsWorkflowSingleRecordAction.ts | 9 +-
...seSeeVersionsWorkflowSingleRecordAction.ts | 9 +-
.../useTestWorkflowSingleRecordAction.ts | 9 +-
...eeRunsWorkflowVersionSingleRecordAction.ts | 9 +-
...rsionsWorkflowVersionSingleRecordAction.ts | 14 +-
...DraftWorkflowVersionSingleRecordAction.tsx | 9 +-
.../action-menu/actions/types/ActionHook.ts | 14 +
.../actions/types/SingleRecordActionHook.ts | 20 --
.../actions/utils/getActionConfig.ts | 25 ++
.../actions/utils/getActionViewType.ts | 32 +++
.../useActionMenuEntriesWithCallbacks.ts | 70 -----
.../testing/jest/JestContextStoreSetter.tsx | 10 +
...taAndApolloMocksAndContextStoreWrapper.tsx | 5 +
52 files changed, 871 insertions(+), 1123 deletions(-)
create mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RegisterRecordActionEffect.tsx
rename packages/twenty-front/src/modules/action-menu/actions/record-actions/{single-record/constants/DefaultSingleRecordActionsConfigV1.ts => constants/DefaultActionsConfigV1.ts} (50%)
rename packages/twenty-front/src/modules/action-menu/actions/record-actions/{single-record/constants/DefaultSingleRecordActionsConfigV2.ts => constants/DefaultActionsConfigV2.ts} (70%)
rename packages/twenty-front/src/modules/action-menu/actions/record-actions/{single-record/workflow-actions/constants/WorkflowSingleRecordActionsConfig.ts => constants/WorkflowActionsConfig.ts} (83%)
rename packages/twenty-front/src/modules/action-menu/actions/record-actions/{single-record/workflow-run-actions/constants/WorkflowRunsSingleRecordActionsConfig.ts => constants/WorkflowRunsActionsConfig.ts} (67%)
rename packages/twenty-front/src/modules/action-menu/actions/record-actions/{single-record/workflow-version-actions/constants/WorkflowVersionsSingleRecordActionsConfig.ts => constants/WorkflowVersionsActionsConfig.ts} (78%)
delete mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect.tsx
delete mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useMultipleRecordsActions.tsx
delete mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect.tsx
delete mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/__tests__/useExportViewNoSelectionRecordAction.test.tsx
delete mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction.tsx
delete mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useNoSelectionRecordActions.tsx
delete mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect.tsx
create mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow.tsx
delete mode 100644 packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/utils/getActionConfig.ts
create mode 100644 packages/twenty-front/src/modules/action-menu/actions/types/ActionHook.ts
delete mode 100644 packages/twenty-front/src/modules/action-menu/actions/types/SingleRecordActionHook.ts
create mode 100644 packages/twenty-front/src/modules/action-menu/actions/utils/getActionConfig.ts
create mode 100644 packages/twenty-front/src/modules/action-menu/actions/utils/getActionViewType.ts
delete mode 100644 packages/twenty-front/src/modules/action-menu/hooks/useActionMenuEntriesWithCallbacks.ts
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx
index 02dfb9cc04c7..6927fe94c3ac 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx
@@ -1,13 +1,10 @@
-import { MultipleRecordsActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect';
-import { NoSelectionActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect';
-import { SingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect';
+import { RegisterRecordActionEffect } from '@/action-menu/actions/record-actions/components/RegisterRecordActionEffect';
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
-import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
+import { getActionConfig } from '@/action-menu/actions/utils/getActionConfig';
+import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
-import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
-import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
@@ -19,33 +16,13 @@ export const RecordActionMenuEntriesSetter = () => {
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataIdComponentState,
);
+
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const objectMetadataItem = objectMetadataItems.find(
(item) => item.id === contextStoreCurrentObjectMetadataId,
);
- if (
- !isDefined(contextStoreCurrentObjectMetadataId) ||
- !isDefined(objectMetadataItem)
- ) {
- return null;
- }
-
- return (
-
- );
-};
-
-const ActionEffects = ({
- objectMetadataItemId,
-}: {
- objectMetadataItemId: string;
-}) => {
- const { objectMetadataItem } = useObjectMetadataItemById({
- objectId: objectMetadataItemId,
- });
-
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
);
@@ -58,43 +35,52 @@ const ActionEffects = ({
FeatureFlagKey.IsWorkflowEnabled,
);
+ const isPageHeaderV2Enabled = useIsFeatureEnabled(
+ FeatureFlagKey.IsPageHeaderV2Enabled,
+ );
+
+ if (
+ !isDefined(contextStoreCurrentObjectMetadataId) ||
+ !isDefined(objectMetadataItem)
+ ) {
+ return null;
+ }
+
+ const viewType = getActionViewType(
+ contextStoreCurrentViewType,
+ contextStoreTargetedRecordsRule,
+ );
+
+ const actionConfig = getActionConfig(
+ objectMetadataItem,
+ isPageHeaderV2Enabled,
+ );
+
+ const actionsToRegister = isDefined(viewType)
+ ? Object.values(actionConfig ?? {}).filter((action) =>
+ action.availableOn?.includes(viewType),
+ )
+ : [];
+
return (
<>
- {contextStoreTargetedRecordsRule.mode === 'selection' &&
- contextStoreTargetedRecordsRule.selectedRecordIds.length === 0 && (
- (
+
+ ))}
+
+ {isWorkflowEnabled &&
+ !(
+ contextStoreTargetedRecordsRule?.mode === 'selection' &&
+ contextStoreTargetedRecordsRule?.selectedRecordIds.length === 0
+ ) && (
+
)}
- {contextStoreTargetedRecordsRule.mode === 'selection' &&
- contextStoreTargetedRecordsRule.selectedRecordIds.length === 1 && (
- <>
- {contextStoreCurrentViewType === ContextStoreViewType.ShowPage && (
-
- )}
- {(contextStoreCurrentViewType === ContextStoreViewType.Table ||
- contextStoreCurrentViewType === ContextStoreViewType.Kanban) && (
-
- )}
- {isWorkflowEnabled && (
-
- )}
- >
- )}
- {(contextStoreTargetedRecordsRule.mode === 'exclusion' ||
- contextStoreTargetedRecordsRule.selectedRecordIds.length > 1) && (
-
- )}
>
);
};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RegisterRecordActionEffect.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RegisterRecordActionEffect.tsx
new file mode 100644
index 000000000000..25159234c624
--- /dev/null
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RegisterRecordActionEffect.tsx
@@ -0,0 +1,55 @@
+import { ActionHook } from '@/action-menu/actions/types/ActionHook';
+import { wrapActionInCallbacks } from '@/action-menu/actions/utils/wrapActionInCallbacks';
+import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
+import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
+import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
+import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
+import { useContext, useEffect } from 'react';
+
+type RegisterRecordActionEffectProps = {
+ action: ActionMenuEntry & {
+ actionHook: ActionHook;
+ };
+ objectMetadataItem: ObjectMetadataItem;
+};
+
+export const RegisterRecordActionEffect = ({
+ action,
+ objectMetadataItem,
+}: RegisterRecordActionEffectProps) => {
+ const { shouldBeRegistered, onClick, ConfirmationModal } = action.actionHook({
+ objectMetadataItem,
+ });
+
+ const { onActionStartedCallback, onActionExecutedCallback } =
+ useContext(ActionMenuContext);
+
+ const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
+
+ const wrappedAction = wrapActionInCallbacks({
+ action: {
+ ...action,
+ onClick,
+ ConfirmationModal,
+ },
+ onActionStartedCallback,
+ onActionExecutedCallback,
+ });
+
+ useEffect(() => {
+ if (shouldBeRegistered) {
+ addActionMenuEntry(wrappedAction);
+ }
+
+ return () => {
+ removeActionMenuEntry(wrappedAction.key);
+ };
+ }, [
+ addActionMenuEntry,
+ removeActionMenuEntry,
+ shouldBeRegistered,
+ wrappedAction,
+ ]);
+
+ return null;
+};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV1.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV1.ts
similarity index 50%
rename from packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV1.ts
rename to packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV1.ts
index ba6584225023..12da07f3f96a 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV1.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV1.ts
@@ -1,20 +1,29 @@
+import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
+import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
+import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
+import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
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 { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
+import { ActionHook } from '@/action-menu/actions/types/ActionHook';
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
-import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
import {
ActionMenuEntry,
ActionMenuEntryScope,
ActionMenuEntryType,
} from '@/action-menu/types/ActionMenuEntry';
-import { IconHeart, IconHeartOff, IconTrash } from 'twenty-ui';
+import {
+ IconDatabaseExport,
+ IconHeart,
+ IconHeartOff,
+ IconTrash,
+} from 'twenty-ui';
-export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1: Record<
+export const DEFAULT_ACTIONS_CONFIG_V1: Record<
string,
ActionMenuEntry & {
- actionHook: SingleRecordActionHook;
+ actionHook: ActionHook;
}
> = {
addToFavoritesSingleRecord: {
@@ -58,4 +67,43 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1: Record<
],
actionHook: useDeleteSingleRecordAction,
},
+ deleteMultipleRecords: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: MultipleRecordsActionKeys.DELETE,
+ label: 'Delete records',
+ shortLabel: 'Delete',
+ position: 3,
+ Icon: IconTrash,
+ accent: 'danger',
+ isPinned: true,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ actionHook: useDeleteMultipleRecordsAction,
+ },
+ exportMultipleRecords: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: MultipleRecordsActionKeys.EXPORT,
+ label: 'Export records',
+ shortLabel: 'Export',
+ position: 4,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
+ exportView: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: NoSelectionRecordActionKeys.EXPORT_VIEW,
+ label: 'Export view',
+ shortLabel: 'Export',
+ position: 5,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV2.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV2.ts
similarity index 70%
rename from packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV2.ts
rename to packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV2.ts
index 6e41b853e495..f7dd5b223500 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/constants/DefaultSingleRecordActionsConfigV2.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV2.ts
@@ -1,3 +1,7 @@
+import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
+import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
+import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
+import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
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';
@@ -6,8 +10,8 @@ import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions
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 { ActionHook } from '@/action-menu/actions/types/ActionHook';
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
-import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
import {
ActionMenuEntry,
ActionMenuEntryScope,
@@ -16,6 +20,7 @@ import {
import {
IconChevronDown,
IconChevronUp,
+ IconDatabaseExport,
IconFileExport,
IconHeart,
IconHeartOff,
@@ -23,10 +28,10 @@ import {
IconTrashX,
} from 'twenty-ui';
-export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
+export const DEFAULT_ACTIONS_CONFIG_V2: Record<
string,
ActionMenuEntry & {
- actionHook: SingleRecordActionHook;
+ actionHook: ActionHook;
}
> = {
exportNoteToPdf: {
@@ -87,13 +92,52 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
],
actionHook: useDeleteSingleRecordAction,
},
+ deleteMultipleRecords: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: MultipleRecordsActionKeys.DELETE,
+ label: 'Delete records',
+ shortLabel: 'Delete',
+ position: 4,
+ Icon: IconTrash,
+ accent: 'danger',
+ isPinned: true,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ actionHook: useDeleteMultipleRecordsAction,
+ },
+ exportMultipleRecords: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: MultipleRecordsActionKeys.EXPORT,
+ label: 'Export records',
+ shortLabel: 'Export',
+ position: 5,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
+ exportView: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: NoSelectionRecordActionKeys.EXPORT_VIEW,
+ label: 'Export view',
+ shortLabel: 'Export',
+ position: 6,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
destroySingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.DESTROY,
label: 'Permanently destroy record',
shortLabel: 'Destroy',
- position: 4,
+ position: 7,
Icon: IconTrashX,
accent: 'danger',
isPinned: true,
@@ -109,7 +153,7 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
key: SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
label: 'Navigate to previous record',
shortLabel: '',
- position: 5,
+ position: 8,
isPinned: true,
Icon: IconChevronUp,
availableOn: [ActionViewType.SHOW_PAGE],
@@ -121,7 +165,7 @@ export const DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2: Record<
key: SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
label: 'Navigate to next record',
shortLabel: '',
- position: 6,
+ position: 9,
isPinned: true,
Icon: IconChevronDown,
availableOn: [ActionViewType.SHOW_PAGE],
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/constants/WorkflowSingleRecordActionsConfig.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.ts
similarity index 83%
rename from packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/constants/WorkflowSingleRecordActionsConfig.ts
rename to packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.ts
index f6d85ae3b5ee..773ff4ce673d 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/constants/WorkflowSingleRecordActionsConfig.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.ts
@@ -1,3 +1,7 @@
+import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
+import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
+import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
+import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
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';
@@ -14,8 +18,8 @@ import { useSeeRunsWorkflowSingleRecordAction } from '@/action-menu/actions/reco
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 { WorkflowSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-actions/types/WorkflowSingleRecordActionsKeys';
+import { ActionHook } from '@/action-menu/actions/types/ActionHook';
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
-import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
import {
ActionMenuEntry,
ActionMenuEntryScope,
@@ -24,6 +28,7 @@ import {
import {
IconChevronDown,
IconChevronUp,
+ IconDatabaseExport,
IconHeart,
IconHeartOff,
IconHistory,
@@ -35,10 +40,10 @@ import {
IconTrashX,
} from 'twenty-ui';
-export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
+export const WORKFLOW_ACTIONS_CONFIG: Record<
string,
ActionMenuEntry & {
- actionHook: SingleRecordActionHook;
+ actionHook: ActionHook;
}
> = {
activateWorkflowDraftSingleRecord: {
@@ -229,13 +234,26 @@ export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
],
actionHook: useDeleteSingleRecordAction,
},
+ deleteMultipleRecords: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: MultipleRecordsActionKeys.DELETE,
+ label: 'Delete records',
+ shortLabel: 'Delete',
+ position: 14,
+ Icon: IconTrash,
+ accent: 'danger',
+ isPinned: true,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ actionHook: useDeleteMultipleRecordsAction,
+ },
destroySingleRecord: {
type: ActionMenuEntryType.Standard,
scope: ActionMenuEntryScope.RecordSelection,
key: SingleRecordActionKeys.DESTROY,
label: 'Permanently destroy record',
shortLabel: 'Destroy',
- position: 14,
+ position: 15,
Icon: IconTrashX,
accent: 'danger',
isPinned: false,
@@ -245,4 +263,30 @@ export const WORKFLOW_SINGLE_RECORD_ACTIONS_CONFIG: Record<
],
actionHook: useDestroySingleRecordAction,
},
+ exportMultipleRecords: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: MultipleRecordsActionKeys.EXPORT,
+ label: 'Export records',
+ shortLabel: 'Export',
+ position: 16,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
+ exportView: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: NoSelectionRecordActionKeys.EXPORT_VIEW,
+ label: 'Export view',
+ shortLabel: 'Export',
+ position: 17,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-run-actions/constants/WorkflowRunsSingleRecordActionsConfig.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.ts
similarity index 67%
rename from packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-run-actions/constants/WorkflowRunsSingleRecordActionsConfig.ts
rename to packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.ts
index 7f6f24633207..ee0f56f972be 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-run-actions/constants/WorkflowRunsSingleRecordActionsConfig.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.ts
@@ -1,10 +1,13 @@
+import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
+import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
+import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
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 { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
+import { ActionHook } from '@/action-menu/actions/types/ActionHook';
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
-import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
import {
ActionMenuEntry,
ActionMenuEntryScope,
@@ -13,14 +16,15 @@ import {
import {
IconChevronDown,
IconChevronUp,
+ IconDatabaseExport,
IconHeart,
IconHeartOff,
} from 'twenty-ui';
-export const WORKFLOW_RUNS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
+export const WORKFLOW_RUNS_ACTIONS_CONFIG: Record<
string,
ActionMenuEntry & {
- actionHook: SingleRecordActionHook;
+ actionHook: ActionHook;
}
> = {
addToFavoritesSingleRecord: {
@@ -77,4 +81,30 @@ export const WORKFLOW_RUNS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
availableOn: [ActionViewType.SHOW_PAGE],
actionHook: useNavigateToNextRecordSingleRecordAction,
},
+ exportMultipleRecords: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: MultipleRecordsActionKeys.EXPORT,
+ label: 'Export records',
+ shortLabel: 'Export',
+ position: 4,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
+ exportView: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: NoSelectionRecordActionKeys.EXPORT_VIEW,
+ label: 'Export view',
+ shortLabel: 'Export',
+ position: 5,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/constants/WorkflowVersionsSingleRecordActionsConfig.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowVersionsActionsConfig.ts
similarity index 78%
rename from packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/constants/WorkflowVersionsSingleRecordActionsConfig.ts
rename to packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowVersionsActionsConfig.ts
index 95f531329704..2fbff23aabe3 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/constants/WorkflowVersionsSingleRecordActionsConfig.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowVersionsActionsConfig.ts
@@ -1,3 +1,6 @@
+import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
+import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
+import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
@@ -7,8 +10,8 @@ import { useSeeRunsWorkflowVersionSingleRecordAction } from '@/action-menu/actio
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 { WorkflowVersionSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/types/WorkflowVersionSingleRecordActionsKeys';
+import { ActionHook } from '@/action-menu/actions/types/ActionHook';
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
-import { SingleRecordActionHook } from '@/action-menu/actions/types/SingleRecordActionHook';
import {
ActionMenuEntry,
ActionMenuEntryScope,
@@ -17,6 +20,7 @@ import {
import {
IconChevronDown,
IconChevronUp,
+ IconDatabaseExport,
IconHeart,
IconHeartOff,
IconHistory,
@@ -24,10 +28,10 @@ import {
IconPencil,
} from 'twenty-ui';
-export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
+export const WORKFLOW_VERSIONS_ACTIONS_CONFIG: Record<
string,
ActionMenuEntry & {
- actionHook: SingleRecordActionHook;
+ actionHook: ActionHook;
}
> = {
useAsDraftWorkflowVersionSingleRecord: {
@@ -125,4 +129,30 @@ export const WORKFLOW_VERSIONS_SINGLE_RECORD_ACTIONS_CONFIG: Record<
],
actionHook: useRemoveFromFavoritesSingleRecordAction,
},
+ exportMultipleRecords: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: MultipleRecordsActionKeys.EXPORT,
+ label: 'Export records',
+ shortLabel: 'Export',
+ position: 8,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
+ exportView: {
+ type: ActionMenuEntryType.Standard,
+ scope: ActionMenuEntryScope.RecordSelection,
+ key: NoSelectionRecordActionKeys.EXPORT_VIEW,
+ label: 'Export view',
+ shortLabel: 'Export',
+ position: 9,
+ Icon: IconDatabaseExport,
+ accent: 'default',
+ isPinned: false,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ actionHook: useExportMultipleRecordsAction,
+ },
};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect.tsx
deleted file mode 100644
index 67b96f1b7ab4..000000000000
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useMultipleRecordsActions } from '@/action-menu/actions/record-actions/multiple-records/hooks/useMultipleRecordsActions';
-import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-import { useEffect } from 'react';
-
-export const MultipleRecordsActionMenuEntrySetterEffect = ({
- objectMetadataItem,
-}: {
- objectMetadataItem: ObjectMetadataItem;
-}) => {
- const { registerMultipleRecordsActions, unregisterMultipleRecordsActions } =
- useMultipleRecordsActions({
- objectMetadataItem,
- });
-
- useEffect(() => {
- registerMultipleRecordsActions();
-
- return () => {
- unregisterMultipleRecordsActions();
- };
- }, [registerMultipleRecordsActions, unregisterMultipleRecordsActions]);
-
- return null;
-};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/__tests__/useDeleteMultipleRecordsAction.test.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/__tests__/useDeleteMultipleRecordsAction.test.tsx
index 447a08447877..9eba7ca23eea 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/__tests__/useDeleteMultipleRecordsAction.test.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/__tests__/useDeleteMultipleRecordsAction.test.tsx
@@ -1,132 +1,87 @@
-import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
-import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
-import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
-import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
-import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
-import { expect } from '@storybook/test';
-import { renderHook } from '@testing-library/react';
+import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
+import { renderHook, waitFor } from '@testing-library/react';
import { act } from 'react';
-import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
+import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
+import { getPeopleMock } from '~/testing/mock-data/people';
import { useDeleteMultipleRecordsAction } from '../useDeleteMultipleRecordsAction';
+const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
+ (item) => item.nameSingular === 'person',
+)!;
+
+const peopleMock = getPeopleMock();
+
+const deleteManyRecordsMock = jest.fn();
+const resetTableRowSelectionMock = jest.fn();
+
jest.mock('@/object-record/hooks/useDeleteManyRecords', () => ({
useDeleteManyRecords: () => ({
- deleteManyRecords: jest.fn(),
+ deleteManyRecords: deleteManyRecordsMock,
}),
}));
-jest.mock('@/favorites/hooks/useDeleteFavorite', () => ({
- useDeleteFavorite: () => ({
- deleteFavorite: jest.fn(),
- }),
-}));
-jest.mock('@/favorites/hooks/useFavorites', () => ({
- useFavorites: () => ({
- sortedFavorites: [],
- }),
+
+jest.mock('@/object-record/hooks/useLazyFetchAllRecords', () => ({
+ useLazyFetchAllRecords: () => {
+ return {
+ fetchAllRecords: () => [peopleMock[0], peopleMock[1]],
+ };
+ },
}));
+
jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({
useRecordTable: () => ({
- resetTableRowSelection: jest.fn(),
+ resetTableRowSelection: resetTableRowSelectionMock,
}),
}));
-const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
- (item) => item.nameSingular === 'company',
-)!;
-
-const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
+const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
apolloMocks: [],
- onInitializeRecoilSnapshot: ({ set }) => {
- set(
- contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
- instanceId: '1',
- }),
- 3,
- );
+ componentInstanceId: '1',
+ contextStoreCurrentObjectMetadataNameSingular:
+ personMockObjectMetadataItem.nameSingular,
+ contextStoreTargetedRecordsRule: {
+ mode: 'selection',
+ selectedRecordIds: [peopleMock[0].id, peopleMock[1].id],
+ },
+ contextStoreNumberOfSelectedRecords: 2,
+ onInitializeRecoilSnapshot: (snapshot) => {
+ snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
+ snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
},
});
describe('useDeleteMultipleRecordsAction', () => {
- const wrapper = ({ children }: { children: React.ReactNode }) => (
-
-
-
- {children}
-
-
-
- );
-
- it('should register delete action', () => {
+ it('should call deleteManyRecords on click', async () => {
const { result } = renderHook(
- () => {
- const actionMenuEntries = useRecoilComponentValueV2(
- actionMenuEntriesComponentState,
- );
-
- return {
- actionMenuEntries,
- useDeleteMultipleRecordsAction: useDeleteMultipleRecordsAction({
- objectMetadataItem: companyMockObjectMetadataItem,
- }),
- };
+ () =>
+ useDeleteMultipleRecordsAction({
+ objectMetadataItem: personMockObjectMetadataItem,
+ }),
+ {
+ wrapper,
},
- { wrapper },
);
+ expect(result.current.ConfirmationModal?.props?.isOpen).toBe(false);
+
act(() => {
- result.current.useDeleteMultipleRecordsAction.registerDeleteMultipleRecordsAction(
- { position: 1 },
- );
+ result.current.onClick();
});
- expect(result.current.actionMenuEntries.size).toBe(1);
- expect(
- result.current.actionMenuEntries.get('delete-multiple-records'),
- ).toBeDefined();
- expect(
- result.current.actionMenuEntries.get('delete-multiple-records')?.position,
- ).toBe(1);
- });
-
- it('should unregister delete action', () => {
- const { result } = renderHook(
- () => {
- const actionMenuEntries = useRecoilComponentValueV2(
- actionMenuEntriesComponentState,
- );
-
- return {
- actionMenuEntries,
- useDeleteMultipleRecordsAction: useDeleteMultipleRecordsAction({
- objectMetadataItem: companyMockObjectMetadataItem,
- }),
- };
- },
- { wrapper },
- );
+ expect(result.current.ConfirmationModal?.props?.isOpen).toBe(true);
act(() => {
- result.current.useDeleteMultipleRecordsAction.registerDeleteMultipleRecordsAction(
- { position: 1 },
- );
+ result.current.ConfirmationModal?.props?.onConfirmClick();
});
- expect(result.current.actionMenuEntries.size).toBe(1);
+ await waitFor(() => {
+ expect(resetTableRowSelectionMock).toHaveBeenCalled();
- act(() => {
- result.current.useDeleteMultipleRecordsAction.unregisterDeleteMultipleRecordsAction();
+ expect(deleteManyRecordsMock).toHaveBeenCalledWith([
+ peopleMock[0].id,
+ peopleMock[1].id,
+ ]);
});
-
- expect(result.current.actionMenuEntries.size).toBe(0);
});
});
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/__tests__/useExportMultipleRecordsAction.test.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/__tests__/useExportMultipleRecordsAction.test.tsx
index 7d9dbff6ef77..46e645080da9 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/__tests__/useExportMultipleRecordsAction.test.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/__tests__/useExportMultipleRecordsAction.test.tsx
@@ -1,105 +1,59 @@
-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 { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
+import { renderHook, waitFor } from '@testing-library/react';
import { act } from 'react';
-import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
-import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
+import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
+import { getPeopleMock } from '~/testing/mock-data/people';
import { useExportMultipleRecordsAction } from '../useExportMultipleRecordsAction';
-const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
- (item) => item.nameSingular === 'company',
+const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
+ (item) => item.nameSingular === 'person',
)!;
-const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
- apolloMocks: [],
-});
+const peopleMock = getPeopleMock();
-describe('useExportMultipleRecordsAction', () => {
- const wrapper = ({ children }: { children: React.ReactNode }) => (
-
-
-
-
- {children}
-
-
-
-
- );
-
- it('should register export multiple records action', () => {
- const { result } = renderHook(
- () => {
- const actionMenuEntries = useRecoilComponentValueV2(
- actionMenuEntriesComponentState,
- );
+const downloadMock = jest.fn();
- return {
- actionMenuEntries,
- useExportMultipleRecordsAction: useExportMultipleRecordsAction({
- objectMetadataItem: companyMockObjectMetadataItem,
- }),
- };
- },
- { wrapper },
- );
-
- act(() => {
- result.current.useExportMultipleRecordsAction.registerExportMultipleRecordsAction(
- { position: 1 },
- );
- });
+jest.mock('@/object-record/record-index/export/hooks/useExportRecords', () => ({
+ useExportRecords: () => ({
+ download: downloadMock,
+ }),
+}));
- expect(result.current.actionMenuEntries.size).toBe(1);
- expect(
- result.current.actionMenuEntries.get('export-multiple-records'),
- ).toBeDefined();
- expect(
- result.current.actionMenuEntries.get('export-multiple-records')?.position,
- ).toBe(1);
- });
+const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
+ apolloMocks: [],
+ componentInstanceId: '1',
+ contextStoreCurrentObjectMetadataNameSingular:
+ personMockObjectMetadataItem.nameSingular,
+ contextStoreTargetedRecordsRule: {
+ mode: 'selection',
+ selectedRecordIds: [peopleMock[0].id, peopleMock[1].id],
+ },
+ contextStoreNumberOfSelectedRecords: 2,
+ onInitializeRecoilSnapshot: (snapshot) => {
+ snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
+ snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
+ },
+});
- it('should unregister export multiple records action', () => {
+describe('useExportMultipleRecordsAction', () => {
+ it('should call exportManyRecords on click', async () => {
const { result } = renderHook(
- () => {
- const actionMenuEntries = useRecoilComponentValueV2(
- actionMenuEntriesComponentState,
- );
-
- return {
- actionMenuEntries,
- useExportMultipleRecordsAction: useExportMultipleRecordsAction({
- objectMetadataItem: companyMockObjectMetadataItem,
- }),
- };
+ () =>
+ useExportMultipleRecordsAction({
+ objectMetadataItem: personMockObjectMetadataItem,
+ }),
+ {
+ wrapper,
},
- { wrapper },
);
act(() => {
- result.current.useExportMultipleRecordsAction.registerExportMultipleRecordsAction(
- { position: 1 },
- );
+ result.current.onClick();
});
- expect(result.current.actionMenuEntries.size).toBe(1);
-
- act(() => {
- result.current.useExportMultipleRecordsAction.unregisterExportMultipleRecordsAction();
+ await waitFor(() => {
+ expect(downloadMock).toHaveBeenCalled();
});
-
- expect(result.current.actionMenuEntries.size).toBe(0);
});
});
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction.tsx
index 7c48a63a0b92..dfcbb5350d48 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction.tsx
@@ -1,15 +1,9 @@
-import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
-import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
-import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
-import {
- ActionMenuEntryScope,
- ActionMenuEntryType,
-} from '@/action-menu/types/ActionMenuEntry';
+import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
+
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
-import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
import { DELETE_MAX_COUNT } from '@/object-record/constants/DeleteMaxCount';
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
@@ -18,140 +12,101 @@ import { FilterOperand } from '@/object-record/object-filter-dropdown/types/Filt
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
-import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
-import { useCallback, useContext, useState } from 'react';
-import { IconTrash, isDefined } from 'twenty-ui';
-
-export const useDeleteMultipleRecordsAction = ({
- objectMetadataItem,
-}: {
- objectMetadataItem: ObjectMetadataItem;
-}) => {
- const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
-
- const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
- useState(false);
-
- const { resetTableRowSelection } = useRecordTable({
- recordTableId: objectMetadataItem.namePlural,
- });
-
- const { deleteManyRecords } = useDeleteManyRecords({
- objectNameSingular: objectMetadataItem.nameSingular,
- });
-
- const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
- contextStoreNumberOfSelectedRecordsComponentState,
- );
-
- const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
- contextStoreTargetedRecordsRuleComponentState,
- );
-
- const contextStoreFilters = useRecoilComponentValueV2(
- contextStoreFiltersComponentState,
- );
-
- const { filterValueDependencies } = useFilterValueDependencies();
-
- const graphqlFilter = computeContextStoreFilters(
- contextStoreTargetedRecordsRule,
- contextStoreFilters,
- objectMetadataItem,
- filterValueDependencies,
- );
-
- const deletedAtFieldMetadata = objectMetadataItem.fields.find(
- (field) => field.name === 'deletedAt',
- );
-
- const isDeletedFilterActive = contextStoreFilters.some(
- (filter) =>
- filter.fieldMetadataId === deletedAtFieldMetadata?.id &&
- filter.operand === FilterOperand.IsNotEmpty,
- );
-
- const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
- objectNameSingular: objectMetadataItem.nameSingular,
- filter: graphqlFilter,
- limit: DEFAULT_QUERY_PAGE_SIZE,
- recordGqlFields: { id: true },
- });
-
- const { closeRightDrawer } = useRightDrawer();
-
- const handleDeleteClick = useCallback(async () => {
- const recordsToDelete = await fetchAllRecordIds();
- const recordIdsToDelete = recordsToDelete.map((record) => record.id);
-
- resetTableRowSelection();
-
- await deleteManyRecords(recordIdsToDelete);
- }, [deleteManyRecords, fetchAllRecordIds, resetTableRowSelection]);
-
- const isRemoteObject = objectMetadataItem.isRemote;
-
- const canDelete =
- !isRemoteObject &&
- !isDeletedFilterActive &&
- isDefined(contextStoreNumberOfSelectedRecords) &&
- contextStoreNumberOfSelectedRecords < DELETE_MAX_COUNT &&
- contextStoreNumberOfSelectedRecords > 0;
-
- const { isInRightDrawer, onActionStartedCallback, onActionExecutedCallback } =
- useContext(ActionMenuContext);
-
- const registerDeleteMultipleRecordsAction = ({
- position,
- }: {
- position: number;
- }) => {
- if (canDelete) {
- addActionMenuEntry({
- type: ActionMenuEntryType.Standard,
- scope: ActionMenuEntryScope.RecordSelection,
- key: MultipleRecordsActionKeys.DELETE,
- label: 'Delete records',
- shortLabel: 'Delete',
- position,
- Icon: IconTrash,
- accent: 'danger',
- isPinned: true,
- onClick: () => {
- setIsDeleteRecordsModalOpen(true);
- },
- ConfirmationModal: (
- {
- onActionStartedCallback?.({
- key: 'delete-multiple-records',
- });
- await handleDeleteClick();
- onActionExecutedCallback?.({
- key: 'delete-multiple-records',
- });
- if (isInRightDrawer) {
- closeRightDrawer();
- }
- }}
- deleteButtonText={'Delete Records'}
- />
- ),
- });
- }
- };
-
- const unregisterDeleteMultipleRecordsAction = () => {
- removeActionMenuEntry('delete-multiple-records');
- };
-
- return {
- registerDeleteMultipleRecordsAction,
- unregisterDeleteMultipleRecordsAction,
+import { useCallback, useState } from 'react';
+import { isDefined } from 'twenty-ui';
+
+export const useDeleteMultipleRecordsAction: ActionHookWithObjectMetadataItem =
+ ({ objectMetadataItem }) => {
+ const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
+ useState(false);
+
+ const { resetTableRowSelection } = useRecordTable({
+ recordTableId: objectMetadataItem.namePlural,
+ });
+
+ const { deleteManyRecords } = useDeleteManyRecords({
+ objectNameSingular: objectMetadataItem.nameSingular,
+ });
+
+ const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
+ contextStoreNumberOfSelectedRecordsComponentState,
+ );
+
+ const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
+ contextStoreTargetedRecordsRuleComponentState,
+ );
+
+ const contextStoreFilters = useRecoilComponentValueV2(
+ contextStoreFiltersComponentState,
+ );
+
+ const { filterValueDependencies } = useFilterValueDependencies();
+
+ const graphqlFilter = computeContextStoreFilters(
+ contextStoreTargetedRecordsRule,
+ contextStoreFilters,
+ objectMetadataItem,
+ filterValueDependencies,
+ );
+
+ const deletedAtFieldMetadata = objectMetadataItem.fields.find(
+ (field) => field.name === 'deletedAt',
+ );
+
+ const isDeletedFilterActive = contextStoreFilters.some(
+ (filter) =>
+ filter.fieldMetadataId === deletedAtFieldMetadata?.id &&
+ filter.operand === FilterOperand.IsNotEmpty,
+ );
+
+ const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
+ objectNameSingular: objectMetadataItem.nameSingular,
+ filter: graphqlFilter,
+ limit: DEFAULT_QUERY_PAGE_SIZE,
+ recordGqlFields: { id: true },
+ });
+
+ const handleDeleteClick = useCallback(async () => {
+ const recordsToDelete = await fetchAllRecordIds();
+ const recordIdsToDelete = recordsToDelete.map((record) => record.id);
+
+ resetTableRowSelection();
+
+ await deleteManyRecords(recordIdsToDelete);
+ }, [deleteManyRecords, fetchAllRecordIds, resetTableRowSelection]);
+
+ const isRemoteObject = objectMetadataItem.isRemote;
+
+ const shouldBeRegistered =
+ !isRemoteObject &&
+ !isDeletedFilterActive &&
+ isDefined(contextStoreNumberOfSelectedRecords) &&
+ contextStoreNumberOfSelectedRecords < DELETE_MAX_COUNT &&
+ contextStoreNumberOfSelectedRecords > 0;
+
+ const onClick = () => {
+ if (!shouldBeRegistered) {
+ return;
+ }
+
+ setIsDeleteRecordsModalOpen(true);
+ };
+
+ const confirmationModal = (
+
+ );
+
+ return {
+ shouldBeRegistered,
+ onClick,
+ ConfirmationModal: confirmationModal,
+ };
};
-};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction.tsx
index 3b4d10bec890..9d07a41c1c22 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction.tsx
@@ -1,68 +1,25 @@
-import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-import { IconDatabaseExport } from 'twenty-ui';
-import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
-import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
-import {
- ActionMenuEntryScope,
- ActionMenuEntryType,
-} from '@/action-menu/types/ActionMenuEntry';
-import {
- displayedExportProgress,
- useExportRecords,
-} from '@/object-record/record-index/export/hooks/useExportRecords';
-import { useContext } from 'react';
+import { useExportRecords } from '@/object-record/record-index/export/hooks/useExportRecords';
export const useExportMultipleRecordsAction = ({
objectMetadataItem,
}: {
objectMetadataItem: ObjectMetadataItem;
}) => {
- const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
-
- const { progress, download } = useExportRecords({
+ const { download } = useExportRecords({
delayMs: 100,
objectMetadataItem,
recordIndexId: objectMetadataItem.namePlural,
filename: `${objectMetadataItem.nameSingular}.csv`,
});
- const { onActionStartedCallback, onActionExecutedCallback } =
- useContext(ActionMenuContext);
-
- const registerExportMultipleRecordsAction = ({
- position,
- }: {
- position: number;
- }) => {
- addActionMenuEntry({
- type: ActionMenuEntryType.Standard,
- scope: ActionMenuEntryScope.RecordSelection,
- key: MultipleRecordsActionKeys.EXPORT,
- position,
- label: displayedExportProgress(progress),
- shortLabel: 'Export',
- Icon: IconDatabaseExport,
- accent: 'default',
- onClick: async () => {
- await onActionStartedCallback?.({
- key: MultipleRecordsActionKeys.EXPORT,
- });
- await download();
- await onActionExecutedCallback?.({
- key: MultipleRecordsActionKeys.EXPORT,
- });
- },
- });
- };
-
- const unregisterExportMultipleRecordsAction = () => {
- removeActionMenuEntry('export-multiple-records');
+ const onClick = async () => {
+ await download();
};
return {
- registerExportMultipleRecordsAction,
- unregisterExportMultipleRecordsAction,
+ shouldBeRegistered: true,
+ onClick,
};
};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useMultipleRecordsActions.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useMultipleRecordsActions.tsx
deleted file mode 100644
index 2b135aaa0e33..000000000000
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/hooks/useMultipleRecordsActions.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
-import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
-import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-
-export const useMultipleRecordsActions = ({
- objectMetadataItem,
-}: {
- objectMetadataItem: ObjectMetadataItem;
-}) => {
- const {
- registerDeleteMultipleRecordsAction,
- unregisterDeleteMultipleRecordsAction,
- } = useDeleteMultipleRecordsAction({
- objectMetadataItem,
- });
-
- const {
- registerExportMultipleRecordsAction,
- unregisterExportMultipleRecordsAction,
- } = useExportMultipleRecordsAction({
- objectMetadataItem,
- });
-
- const registerMultipleRecordsActions = () => {
- registerDeleteMultipleRecordsAction({ position: 1 });
- registerExportMultipleRecordsAction({ position: 2 });
- };
-
- const unregisterMultipleRecordsActions = () => {
- unregisterDeleteMultipleRecordsAction();
- unregisterExportMultipleRecordsAction();
- };
-
- return {
- registerMultipleRecordsActions,
- unregisterMultipleRecordsActions,
- };
-};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect.tsx
deleted file mode 100644
index 2cba1582912f..000000000000
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { useNoSelectionRecordActions } from '@/action-menu/actions/record-actions/no-selection/hooks/useNoSelectionRecordActions';
-import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-import { useEffect } from 'react';
-
-export const NoSelectionActionMenuEntrySetterEffect = ({
- objectMetadataItem,
-}: {
- objectMetadataItem: ObjectMetadataItem;
-}) => {
- const {
- registerNoSelectionRecordActions,
- unregisterNoSelectionRecordActions,
- } = useNoSelectionRecordActions({
- objectMetadataItem,
- });
-
- useEffect(() => {
- registerNoSelectionRecordActions();
-
- return () => {
- unregisterNoSelectionRecordActions();
- };
- }, [registerNoSelectionRecordActions, unregisterNoSelectionRecordActions]);
-
- return null;
-};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/__tests__/useExportViewNoSelectionRecordAction.test.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/__tests__/useExportViewNoSelectionRecordAction.test.tsx
deleted file mode 100644
index 7b00c186536a..000000000000
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/__tests__/useExportViewNoSelectionRecordAction.test.tsx
+++ /dev/null
@@ -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 { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction';
-const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
- (item) => item.nameSingular === 'company',
-)!;
-
-const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
- apolloMocks: [],
-});
-
-describe('useExportViewNoSelectionRecordAction', () => {
- const wrapper = ({ children }: { children: React.ReactNode }) => (
-
-
-
-
- {children}
-
-
-
-
- );
-
- it('should register export view action', () => {
- const { result } = renderHook(
- () => {
- const actionMenuEntries = useRecoilComponentValueV2(
- actionMenuEntriesComponentState,
- );
-
- return {
- actionMenuEntries,
- useExportViewNoSelectionRecordAction:
- useExportViewNoSelectionRecordAction({
- objectMetadataItem: companyMockObjectMetadataItem,
- }),
- };
- },
- { wrapper },
- );
-
- act(() => {
- result.current.useExportViewNoSelectionRecordAction.registerExportViewNoSelectionRecordsAction(
- { position: 1 },
- );
- });
-
- expect(result.current.actionMenuEntries.size).toBe(1);
- expect(
- result.current.actionMenuEntries.get('export-view-no-selection'),
- ).toBeDefined();
- expect(
- result.current.actionMenuEntries.get('export-view-no-selection')
- ?.position,
- ).toBe(1);
- });
-
- it('should unregister export view action', () => {
- const { result } = renderHook(
- () => {
- const actionMenuEntries = useRecoilComponentValueV2(
- actionMenuEntriesComponentState,
- );
-
- return {
- actionMenuEntries,
- useExportViewNoSelectionRecordAction:
- useExportViewNoSelectionRecordAction({
- objectMetadataItem: companyMockObjectMetadataItem,
- }),
- };
- },
- { wrapper },
- );
-
- act(() => {
- result.current.useExportViewNoSelectionRecordAction.registerExportViewNoSelectionRecordsAction(
- { position: 1 },
- );
- });
-
- expect(result.current.actionMenuEntries.size).toBe(1);
-
- act(() => {
- result.current.useExportViewNoSelectionRecordAction.unregisterExportViewNoSelectionRecordsAction();
- });
-
- expect(result.current.actionMenuEntries.size).toBe(0);
- });
-});
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction.tsx
deleted file mode 100644
index e3ace2ee667b..000000000000
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
-import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-import { IconDatabaseExport } from 'twenty-ui';
-
-import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey';
-import {
- ActionMenuEntryScope,
- ActionMenuEntryType,
-} from '@/action-menu/types/ActionMenuEntry';
-import {
- displayedExportProgress,
- useExportRecords,
-} from '@/object-record/record-index/export/hooks/useExportRecords';
-
-export const useExportViewNoSelectionRecordAction = ({
- objectMetadataItem,
-}: {
- objectMetadataItem: ObjectMetadataItem;
-}) => {
- const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
-
- const { progress, download } = useExportRecords({
- delayMs: 100,
- objectMetadataItem,
- recordIndexId: objectMetadataItem.namePlural,
- filename: `${objectMetadataItem.nameSingular}.csv`,
- });
-
- const registerExportViewNoSelectionRecordsAction = ({
- position,
- }: {
- position: number;
- }) => {
- addActionMenuEntry({
- type: ActionMenuEntryType.Standard,
- scope: ActionMenuEntryScope.Global,
- key: NoSelectionRecordActionKeys.EXPORT_VIEW,
- position,
- label: displayedExportProgress(progress),
- Icon: IconDatabaseExport,
- accent: 'default',
- onClick: () => download(),
- });
- };
-
- const unregisterExportViewNoSelectionRecordsAction = () => {
- removeActionMenuEntry('export-view-no-selection');
- };
-
- return {
- registerExportViewNoSelectionRecordsAction,
- unregisterExportViewNoSelectionRecordsAction,
- };
-};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useNoSelectionRecordActions.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useNoSelectionRecordActions.tsx
deleted file mode 100644
index af08e721b404..000000000000
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useNoSelectionRecordActions.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction';
-import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-
-export const useNoSelectionRecordActions = ({
- objectMetadataItem,
-}: {
- objectMetadataItem: ObjectMetadataItem;
-}) => {
- const {
- registerExportViewNoSelectionRecordsAction,
- unregisterExportViewNoSelectionRecordsAction,
- } = useExportViewNoSelectionRecordAction({
- objectMetadataItem,
- });
-
- const registerNoSelectionRecordActions = () => {
- registerExportViewNoSelectionRecordsAction({ position: 1 });
- };
-
- const unregisterNoSelectionRecordActions = () => {
- unregisterExportViewNoSelectionRecordsAction();
- };
-
- return {
- registerNoSelectionRecordActions,
- unregisterNoSelectionRecordActions,
- };
-};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect.tsx
deleted file mode 100644
index 3280eb62cb06..000000000000
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
-import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
-import { useActionMenuEntriesWithCallbacks } from '@/action-menu/hooks/useActionMenuEntriesWithCallbacks';
-import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-import { useEffect } from 'react';
-
-export const SingleRecordActionMenuEntrySetterEffect = ({
- objectMetadataItem,
- viewType,
-}: {
- objectMetadataItem: ObjectMetadataItem;
- viewType: ActionViewType;
-}) => {
- const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
-
- const { actionMenuEntries } = useActionMenuEntriesWithCallbacks(
- objectMetadataItem,
- viewType,
- );
-
- useEffect(() => {
- for (const action of actionMenuEntries) {
- addActionMenuEntry(action);
- }
-
- return () => {
- for (const action of actionMenuEntries) {
- removeActionMenuEntry(action.key);
- }
- };
- }, [actionMenuEntries, addActionMenuEntry, removeActionMenuEntry]);
-
- return null;
-};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useAddToFavoritesSingleRecordAction.test.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useAddToFavoritesSingleRecordAction.test.tsx
index 9137cb8538d3..4e7f6a162d50 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useAddToFavoritesSingleRecordAction.test.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useAddToFavoritesSingleRecordAction.test.tsx
@@ -85,7 +85,6 @@ describe('useAddToFavoritesSingleRecordAction', () => {
const { result } = renderHook(
() =>
useAddToFavoritesSingleRecordAction({
- recordId: peopleMock[1].id,
objectMetadataItem: personMockObjectMetadataItem,
}),
{
@@ -100,7 +99,6 @@ describe('useAddToFavoritesSingleRecordAction', () => {
const { result } = renderHook(
() =>
useAddToFavoritesSingleRecordAction({
- recordId: peopleMock[0].id,
objectMetadataItem: personMockObjectMetadataItem,
}),
{
@@ -115,7 +113,6 @@ describe('useAddToFavoritesSingleRecordAction', () => {
const { result } = renderHook(
() =>
useAddToFavoritesSingleRecordAction({
- recordId: peopleMock[1].id,
objectMetadataItem: personMockObjectMetadataItem,
}),
{
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useDeleteSingleRecordAction.test.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useDeleteSingleRecordAction.test.tsx
index a650d86e4614..9c9f39520266 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useDeleteSingleRecordAction.test.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useDeleteSingleRecordAction.test.tsx
@@ -39,7 +39,6 @@ describe('useDeleteSingleRecordAction', () => {
const { result } = renderHook(
() =>
useDeleteSingleRecordAction({
- recordId: peopleMock[0].id,
objectMetadataItem: personMockObjectMetadataItem,
}),
{
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useRemoveFromFavoritesSingleRecordAction.test.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useRemoveFromFavoritesSingleRecordAction.test.tsx
index 8cf965dd5c2e..6da41f206a83 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useRemoveFromFavoritesSingleRecordAction.test.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/__tests__/useRemoveFromFavoritesSingleRecordAction.test.tsx
@@ -85,7 +85,6 @@ describe('useRemoveFromFavoritesSingleRecordAction', () => {
const { result } = renderHook(
() =>
useRemoveFromFavoritesSingleRecordAction({
- recordId: peopleMock[0].id,
objectMetadataItem: personMockObjectMetadataItem,
}),
{
@@ -100,7 +99,6 @@ describe('useRemoveFromFavoritesSingleRecordAction', () => {
const { result } = renderHook(
() =>
useRemoveFromFavoritesSingleRecordAction({
- recordId: peopleMock[1].id,
objectMetadataItem: personMockObjectMetadataItem,
}),
{
@@ -115,7 +113,6 @@ describe('useRemoveFromFavoritesSingleRecordAction', () => {
const { result } = renderHook(
() =>
useRemoveFromFavoritesSingleRecordAction({
- recordId: peopleMock[0].id,
objectMetadataItem: personMockObjectMetadataItem,
}),
{
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction.ts
index ec3f6ddf042d..eace0a761de8 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction.ts
@@ -1,4 +1,5 @@
-import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
@@ -6,8 +7,10 @@ import { isNull } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui';
-export const useAddToFavoritesSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
- ({ recordId, objectMetadataItem }) => {
+export const useAddToFavoritesSingleRecordAction: ActionHookWithObjectMetadataItem =
+ ({ objectMetadataItem }) => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const { sortedFavorites: favorites } = useFavorites();
const { createFavorite } = useCreateFavorite();
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction.tsx
index e499c72d062a..0fbc2ad3679e 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction.tsx
@@ -1,4 +1,5 @@
-import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
import { useFavorites } from '@/favorites/hooks/useFavorites';
@@ -12,80 +13,83 @@ import { useCallback, useContext, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui';
-export const useDeleteSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
- ({ recordId, objectMetadataItem }) => {
- const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
- useState(false);
+export const useDeleteSingleRecordAction: ActionHookWithObjectMetadataItem = ({
+ objectMetadataItem,
+}) => {
+ const recordId = useSelectedRecordIdOrThrow();
- const { resetTableRowSelection } = useRecordTable({
- recordTableId: objectMetadataItem.namePlural,
- });
+ const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
+ useState(false);
- const { deleteOneRecord } = useDeleteOneRecord({
- objectNameSingular: objectMetadataItem.nameSingular,
- });
+ const { resetTableRowSelection } = useRecordTable({
+ recordTableId: objectMetadataItem.namePlural,
+ });
- const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
+ const { deleteOneRecord } = useDeleteOneRecord({
+ objectNameSingular: objectMetadataItem.nameSingular,
+ });
- const { sortedFavorites: favorites } = useFavorites();
- const { deleteFavorite } = useDeleteFavorite();
+ const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
- const { closeRightDrawer } = useRightDrawer();
+ const { sortedFavorites: favorites } = useFavorites();
+ const { deleteFavorite } = useDeleteFavorite();
- const handleDeleteClick = useCallback(async () => {
- resetTableRowSelection();
+ const { closeRightDrawer } = useRightDrawer();
- const foundFavorite = favorites?.find(
- (favorite) => favorite.recordId === recordId,
- );
+ const handleDeleteClick = useCallback(async () => {
+ resetTableRowSelection();
- if (isDefined(foundFavorite)) {
- deleteFavorite(foundFavorite.id);
- }
+ const foundFavorite = favorites?.find(
+ (favorite) => favorite.recordId === recordId,
+ );
- await deleteOneRecord(recordId);
- }, [
- deleteFavorite,
- deleteOneRecord,
- favorites,
- resetTableRowSelection,
- recordId,
- ]);
+ if (isDefined(foundFavorite)) {
+ deleteFavorite(foundFavorite.id);
+ }
- const isRemoteObject = objectMetadataItem.isRemote;
+ await deleteOneRecord(recordId);
+ }, [
+ deleteFavorite,
+ deleteOneRecord,
+ favorites,
+ resetTableRowSelection,
+ recordId,
+ ]);
- const { isInRightDrawer } = useContext(ActionMenuContext);
+ const isRemoteObject = objectMetadataItem.isRemote;
- const shouldBeRegistered =
- !isRemoteObject && isNull(selectedRecord?.deletedAt);
+ const { isInRightDrawer } = useContext(ActionMenuContext);
- const onClick = () => {
- if (!shouldBeRegistered) {
- return;
- }
+ const shouldBeRegistered =
+ !isRemoteObject && isNull(selectedRecord?.deletedAt);
- setIsDeleteRecordsModalOpen(true);
- };
+ const onClick = () => {
+ if (!shouldBeRegistered) {
+ return;
+ }
- return {
- shouldBeRegistered,
- onClick,
- ConfirmationModal: (
- {
+ handleDeleteClick();
+ if (isInRightDrawer) {
+ closeRightDrawer();
}
- onConfirmClick={() => {
- handleDeleteClick();
- if (isInRightDrawer) {
- closeRightDrawer();
- }
- }}
- deleteButtonText={'Delete Record'}
- />
- ),
- };
+ }}
+ deleteButtonText={'Delete Record'}
+ />
+ ),
};
+};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction.tsx
index 85bcf89e861f..107d68aab3b4 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction.tsx
@@ -1,4 +1,5 @@
-import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
@@ -9,63 +10,66 @@ import { useCallback, useContext, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui';
-export const useDestroySingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
- ({ recordId, objectMetadataItem }) => {
- const [isDestroyRecordsModalOpen, setIsDestroyRecordsModalOpen] =
- useState(false);
+export const useDestroySingleRecordAction: ActionHookWithObjectMetadataItem = ({
+ objectMetadataItem,
+}) => {
+ const recordId = useSelectedRecordIdOrThrow();
- const { resetTableRowSelection } = useRecordTable({
- recordTableId: objectMetadataItem.namePlural,
- });
+ const [isDestroyRecordsModalOpen, setIsDestroyRecordsModalOpen] =
+ useState(false);
- const { destroyOneRecord } = useDestroyOneRecord({
- objectNameSingular: objectMetadataItem.nameSingular,
- });
+ const { resetTableRowSelection } = useRecordTable({
+ recordTableId: objectMetadataItem.namePlural,
+ });
- const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
+ const { destroyOneRecord } = useDestroyOneRecord({
+ objectNameSingular: objectMetadataItem.nameSingular,
+ });
- const { closeRightDrawer } = useRightDrawer();
+ const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
- const handleDeleteClick = useCallback(async () => {
- resetTableRowSelection();
+ const { closeRightDrawer } = useRightDrawer();
- await destroyOneRecord(recordId);
- }, [resetTableRowSelection, destroyOneRecord, recordId]);
+ const handleDeleteClick = useCallback(async () => {
+ resetTableRowSelection();
- const isRemoteObject = objectMetadataItem.isRemote;
+ await destroyOneRecord(recordId);
+ }, [resetTableRowSelection, destroyOneRecord, recordId]);
- const { isInRightDrawer } = useContext(ActionMenuContext);
+ const isRemoteObject = objectMetadataItem.isRemote;
- const shouldBeRegistered =
- !isRemoteObject && isDefined(selectedRecord?.deletedAt);
+ const { isInRightDrawer } = useContext(ActionMenuContext);
- const onClick = () => {
- if (!shouldBeRegistered) {
- return;
- }
+ const shouldBeRegistered =
+ !isRemoteObject && isDefined(selectedRecord?.deletedAt);
- setIsDestroyRecordsModalOpen(true);
- };
+ const onClick = () => {
+ if (!shouldBeRegistered) {
+ return;
+ }
- return {
- shouldBeRegistered,
- onClick,
- ConfirmationModal: (
- {
+ await handleDeleteClick();
+ if (isInRightDrawer) {
+ closeRightDrawer();
}
- onConfirmClick={async () => {
- await handleDeleteClick();
- if (isInRightDrawer) {
- closeRightDrawer();
- }
- }}
- deleteButtonText={'Permanently Destroy Record'}
- />
- ),
- };
+ }}
+ deleteButtonText={'Permanently Destroy Record'}
+ />
+ ),
};
+};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useExportNoteAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useExportNoteAction.ts
index bce0cfdf006e..80f3b196066a 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useExportNoteAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useExportNoteAction.ts
@@ -1,49 +1,51 @@
-import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { BlockNoteEditor } from '@blocknote/core';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui';
-export const useExportNoteAction: SingleRecordActionHookWithObjectMetadataItem =
- ({ recordId, objectMetadataItem }) => {
- const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
+export const useExportNoteAction: ActionHookWithObjectMetadataItem = ({
+ objectMetadataItem,
+}) => {
+ const recordId = useSelectedRecordIdOrThrow();
- const filename = `${(selectedRecord?.title || 'Untitled Note').replace(/[<>:"/\\|?*]/g, '-')}`;
+ const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
- const isNoteOrTask =
- objectMetadataItem?.nameSingular === CoreObjectNameSingular.Note ||
- objectMetadataItem?.nameSingular === CoreObjectNameSingular.Task;
+ const filename = `${(selectedRecord?.title || 'Untitled Note').replace(/[<>:"/\\|?*]/g, '-')}`;
- const shouldBeRegistered =
- isDefined(objectMetadataItem) &&
- isDefined(selectedRecord) &&
- isNoteOrTask;
+ const isNoteOrTask =
+ objectMetadataItem?.nameSingular === CoreObjectNameSingular.Note ||
+ objectMetadataItem?.nameSingular === CoreObjectNameSingular.Task;
- const onClick = async () => {
- if (!shouldBeRegistered || !selectedRecord?.body) {
- return;
- }
+ const shouldBeRegistered =
+ isDefined(objectMetadataItem) && isDefined(selectedRecord) && isNoteOrTask;
- const editor = await BlockNoteEditor.create({
- initialContent: JSON.parse(selectedRecord.body),
- });
+ const onClick = async () => {
+ if (!shouldBeRegistered || !selectedRecord?.body) {
+ return;
+ }
- const { exportBlockNoteEditorToPdf } = await import(
- '@/action-menu/actions/record-actions/single-record/utils/exportBlockNoteEditorToPdf'
- );
+ const editor = await BlockNoteEditor.create({
+ initialContent: JSON.parse(selectedRecord.body),
+ });
- await exportBlockNoteEditorToPdf(editor, filename);
+ const { exportBlockNoteEditorToPdf } = await import(
+ '@/action-menu/actions/record-actions/single-record/utils/exportBlockNoteEditorToPdf'
+ );
- // TODO later: implement DOCX export
- // const { exportBlockNoteEditorToDocx } = await import(
- // '@/action-menu/actions/record-actions/single-record/utils/exportBlockNoteEditorToDocx'
- // );
- // await exportBlockNoteEditorToDocx(editor, filename);
- };
+ await exportBlockNoteEditorToPdf(editor, filename);
- return {
- shouldBeRegistered,
- onClick,
- };
+ // TODO later: implement DOCX export
+ // const { exportBlockNoteEditorToDocx } = await import(
+ // '@/action-menu/actions/record-actions/single-record/utils/exportBlockNoteEditorToDocx'
+ // );
+ // await exportBlockNoteEditorToDocx(editor, filename);
};
+
+ return {
+ shouldBeRegistered,
+ onClick,
+ };
+};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction.ts
index 4e55d6315e73..35757df3ba98 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction.ts
@@ -1,10 +1,13 @@
-import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
import { useContext } from 'react';
-export const useNavigateToNextRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
- ({ recordId, objectMetadataItem }) => {
+export const useNavigateToNextRecordSingleRecordAction: ActionHookWithObjectMetadataItem =
+ ({ objectMetadataItem }) => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const { isInRightDrawer } = useContext(ActionMenuContext);
const { navigateToNextRecord } = useRecordShowPagePagination(
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction.ts
index 127cfa97a9ec..36cec0c5fedb 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction.ts
@@ -1,11 +1,15 @@
-import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
import { useContext } from 'react';
-export const useNavigateToPreviousRecordSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
- ({ recordId, objectMetadataItem }) => {
+export const useNavigateToPreviousRecordSingleRecordAction: ActionHookWithObjectMetadataItem =
+ ({ objectMetadataItem }) => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const { isInRightDrawer } = useContext(ActionMenuContext);
+
const { navigateToPreviousRecord } = useRecordShowPagePagination(
objectMetadataItem.nameSingular,
recordId,
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction.ts
index 57802d53957d..00d924365a44 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction.ts
@@ -1,10 +1,13 @@
-import { SingleRecordActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { isDefined } from 'twenty-ui';
-export const useRemoveFromFavoritesSingleRecordAction: SingleRecordActionHookWithObjectMetadataItem =
- ({ recordId, objectMetadataItem }) => {
+export const useRemoveFromFavoritesSingleRecordAction: ActionHookWithObjectMetadataItem =
+ ({ objectMetadataItem }) => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const { sortedFavorites: favorites } = useFavorites();
const { deleteFavorite } = useDeleteFavorite();
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow.tsx
new file mode 100644
index 000000000000..17f5aab14b48
--- /dev/null
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow.tsx
@@ -0,0 +1,18 @@
+import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
+import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
+
+export const useSelectedRecordIdOrThrow = () => {
+ const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
+ contextStoreTargetedRecordsRuleComponentState,
+ );
+
+ if (
+ contextStoreTargetedRecordsRule.mode === 'exclusion' ||
+ (contextStoreTargetedRecordsRule.mode === 'selection' &&
+ contextStoreTargetedRecordsRule.selectedRecordIds.length === 0)
+ ) {
+ throw new Error('Selected record ID is required');
+ }
+
+ return contextStoreTargetedRecordsRule.selectedRecordIds[0];
+};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/utils/getActionConfig.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/utils/getActionConfig.ts
deleted file mode 100644
index 6c567e0c5155..000000000000
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/utils/getActionConfig.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-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_RUNS_SINGLE_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/constants/WorkflowRunsSingleRecordActionsConfig';
-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;
- }
- if (objectMetadataItem.nameSingular === CoreObjectNameSingular.WorkflowRun) {
- return WORKFLOW_RUNS_SINGLE_RECORD_ACTIONS_CONFIG;
- }
- return isPageHeaderV2Enabled
- ? DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V2
- : DEFAULT_SINGLE_RECORD_ACTIONS_CONFIG_V1;
-};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useActivateDraftWorkflowSingleRecordAction.test.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useActivateDraftWorkflowSingleRecordAction.test.ts
index 19e5e64b1688..279c1f079f6c 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useActivateDraftWorkflowSingleRecordAction.test.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useActivateDraftWorkflowSingleRecordAction.test.ts
@@ -55,10 +55,7 @@ const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
describe('useActivateDraftWorkflowSingleRecordAction', () => {
it('should be registered', () => {
const { result } = renderHook(
- () =>
- useActivateDraftWorkflowSingleRecordAction({
- recordId: workflowMock.id,
- }),
+ () => useActivateDraftWorkflowSingleRecordAction(),
{
wrapper,
},
@@ -69,10 +66,7 @@ describe('useActivateDraftWorkflowSingleRecordAction', () => {
it('should call activateWorkflowVersion on click', () => {
const { result } = renderHook(
- () =>
- useActivateDraftWorkflowSingleRecordAction({
- recordId: workflowMock.id,
- }),
+ () => useActivateDraftWorkflowSingleRecordAction(),
{
wrapper,
},
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useActivateLastPublishedVersionWorkflowSingleRecordAction.test.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useActivateLastPublishedVersionWorkflowSingleRecordAction.test.ts
index 9cac8dfb5d08..e7d3c536d1aa 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useActivateLastPublishedVersionWorkflowSingleRecordAction.test.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useActivateLastPublishedVersionWorkflowSingleRecordAction.test.ts
@@ -56,10 +56,7 @@ const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
describe('useActivateLastPublishedVersionWorkflowSingleRecordAction', () => {
it('should be registered', () => {
const { result } = renderHook(
- () =>
- useActivateLastPublishedVersionWorkflowSingleRecordAction({
- recordId: workflowMock.id,
- }),
+ () => useActivateLastPublishedVersionWorkflowSingleRecordAction(),
{
wrapper,
},
@@ -70,10 +67,7 @@ describe('useActivateLastPublishedVersionWorkflowSingleRecordAction', () => {
it('should call activateWorkflowVersion on click', () => {
const { result } = renderHook(
- () =>
- useActivateLastPublishedVersionWorkflowSingleRecordAction({
- recordId: workflowMock.id,
- }),
+ () => useActivateLastPublishedVersionWorkflowSingleRecordAction(),
{
wrapper,
},
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useDeactivateWorkflowSingleRecordAction.test.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useDeactivateWorkflowSingleRecordAction.test.ts
index 3dd091f46c9f..77bc31ff8103 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useDeactivateWorkflowSingleRecordAction.test.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useDeactivateWorkflowSingleRecordAction.test.ts
@@ -104,10 +104,7 @@ describe('useDeactivateWorkflowSingleRecordAction', () => {
() => deactivatedWorkflowMock,
);
const { result } = renderHook(
- () =>
- useDeactivateWorkflowSingleRecordAction({
- recordId: deactivatedWorkflowMock.id,
- }),
+ () => useDeactivateWorkflowSingleRecordAction(),
{
wrapper: deactivatedWorkflowWrapper,
},
@@ -121,10 +118,7 @@ describe('useDeactivateWorkflowSingleRecordAction', () => {
() => activeWorkflowMock,
);
const { result } = renderHook(
- () =>
- useDeactivateWorkflowSingleRecordAction({
- recordId: activeWorkflowMock.id,
- }),
+ () => useDeactivateWorkflowSingleRecordAction(),
{
wrapper: activeWorkflowWrapper,
},
@@ -138,10 +132,7 @@ describe('useDeactivateWorkflowSingleRecordAction', () => {
() => activeWorkflowMock,
);
const { result } = renderHook(
- () =>
- useDeactivateWorkflowSingleRecordAction({
- recordId: activeWorkflowMock.id,
- }),
+ () => useDeactivateWorkflowSingleRecordAction(),
{
wrapper: activeWorkflowWrapper,
},
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useDiscardDraftWorkflowSingleRecordAction.test.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useDiscardDraftWorkflowSingleRecordAction.test.ts
index cf37a4a0fc23..430954ddd932 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useDiscardDraftWorkflowSingleRecordAction.test.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/__tests__/useDiscardDraftWorkflowSingleRecordAction.test.ts
@@ -178,10 +178,7 @@ describe('useDiscardDraftWorkflowSingleRecordAction', () => {
() => noDraftWorkflowMock,
);
const { result } = renderHook(
- () =>
- useDiscardDraftWorkflowSingleRecordAction({
- recordId: noDraftWorkflowMock.id,
- }),
+ () => useDiscardDraftWorkflowSingleRecordAction(),
{
wrapper: noDraftWorkflowWrapper,
},
@@ -196,10 +193,7 @@ describe('useDiscardDraftWorkflowSingleRecordAction', () => {
);
const { result } = renderHook(
- () =>
- useDiscardDraftWorkflowSingleRecordAction({
- recordId: draftWorkflowMockWithOneVersion.id,
- }),
+ () => useDiscardDraftWorkflowSingleRecordAction(),
{
wrapper: draftWorkflowWithOneVersionWrapper,
},
@@ -213,10 +207,7 @@ describe('useDiscardDraftWorkflowSingleRecordAction', () => {
() => draftWorkflowMock,
);
const { result } = renderHook(
- () =>
- useDiscardDraftWorkflowSingleRecordAction({
- recordId: draftWorkflowMock.id,
- }),
+ () => useDiscardDraftWorkflowSingleRecordAction(),
{
wrapper: draftWorkflowWrapper,
},
@@ -230,10 +221,7 @@ describe('useDiscardDraftWorkflowSingleRecordAction', () => {
() => draftWorkflowMock,
);
const { result } = renderHook(
- () =>
- useDiscardDraftWorkflowSingleRecordAction({
- recordId: draftWorkflowMock.id,
- }),
+ () => useDiscardDraftWorkflowSingleRecordAction(),
{
wrapper: draftWorkflowWrapper,
},
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateDraftWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateDraftWorkflowSingleRecordAction.ts
index 280aa8019c04..99f72c0f8bd2 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateDraftWorkflowSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateDraftWorkflowSingleRecordAction.ts
@@ -1,10 +1,13 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { isDefined } from 'twenty-ui';
-export const useActivateDraftWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useActivateDraftWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const { activateWorkflowVersion } = useActivateWorkflowVersion();
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateLastPublishedVersionWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateLastPublishedVersionWorkflowSingleRecordAction.ts
index 0c68e1a629d0..061569ac103a 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateLastPublishedVersionWorkflowSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateLastPublishedVersionWorkflowSingleRecordAction.ts
@@ -1,10 +1,13 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { isDefined } from 'twenty-ui';
-export const useActivateLastPublishedVersionWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useActivateLastPublishedVersionWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const { activateWorkflowVersion } = useActivateWorkflowVersion();
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDeactivateWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDeactivateWorkflowSingleRecordAction.ts
index dd61f50de5eb..2a51f735b8fb 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDeactivateWorkflowSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDeactivateWorkflowSingleRecordAction.ts
@@ -1,10 +1,13 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { isDefined } from 'twenty-ui';
-export const useDeactivateWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useDeactivateWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion();
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDiscardDraftWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDiscardDraftWorkflowSingleRecordAction.ts
index a1fe61b51eca..d5db87b19cdf 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDiscardDraftWorkflowSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDiscardDraftWorkflowSingleRecordAction.ts
@@ -1,10 +1,13 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { isDefined } from 'twenty-ui';
-export const useDiscardDraftWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useDiscardDraftWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction.ts
index 678a197f8917..527df28b493e 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction.ts
@@ -1,12 +1,15 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
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 }) => {
+export const useSeeActiveVersionWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const workflow = useWorkflowWithCurrentVersion(recordId);
const isDraft = workflow?.statuses?.includes('DRAFT') || false;
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction.ts
index 08e427e612b2..0b245f7f3e9a 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction.ts
@@ -1,4 +1,5 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
@@ -6,8 +7,10 @@ import qs from 'qs';
import { useNavigate } from 'react-router-dom';
import { isDefined } from 'twenty-ui';
-export const useSeeRunsWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useSeeRunsWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
const navigate = useNavigate();
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction.ts
index 4524248048e6..6abcc39932f2 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction.ts
@@ -1,4 +1,5 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
@@ -6,8 +7,10 @@ import qs from 'qs';
import { useNavigate } from 'react-router-dom';
import { isDefined } from 'twenty-ui';
-export const useSeeVersionsWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useSeeVersionsWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
const navigate = useNavigate();
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction.ts
index dd1daeba24f4..5a74e7fd4f25 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction.ts
@@ -1,10 +1,13 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { isDefined } from 'twenty-ui';
-export const useTestWorkflowSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useTestWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
const { runWorkflowVersion } = useRunWorkflowVersion();
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction.ts
index e8c3975544d0..ab5e2795df1b 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction.ts
@@ -1,4 +1,5 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
@@ -8,8 +9,10 @@ import { useNavigate } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui';
-export const useSeeRunsWorkflowVersionSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useSeeRunsWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction.ts
index 738b11fb7d9d..e3d21342d293 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction.ts
@@ -1,21 +1,23 @@
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui';
-export const useSeeVersionsWorkflowVersionSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useSeeVersionsWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
if (!isDefined(workflowVersion)) {
throw new Error('Workflow version not found');
}
+ // TODO: Add recordIds to the hook
const { shouldBeRegistered, onClick } =
- useSeeVersionsWorkflowSingleRecordAction({
- recordId: workflowVersion.workflow.id,
- });
+ useSeeVersionsWorkflowSingleRecordAction();
return {
shouldBeRegistered,
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction.tsx
index 24e0983069db..437d16e129c0 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction.tsx
@@ -1,4 +1,5 @@
-import { SingleRecordActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/SingleRecordActionHook';
+import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
+import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { buildShowPageURL } from '@/object-record/record-show/utils/buildShowPageURL';
import { OverrideWorkflowDraftConfirmationModal } from '@/workflow/components/OverrideWorkflowDraftConfirmationModal';
@@ -10,8 +11,10 @@ import { useNavigate } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-ui';
-export const useUseAsDraftWorkflowVersionSingleRecordAction: SingleRecordActionHookWithoutObjectMetadataItem =
- ({ recordId }) => {
+export const useUseAsDraftWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
+ () => {
+ const recordId = useSelectedRecordIdOrThrow();
+
const workflowVersion = useWorkflowVersion(recordId);
const workflow = useWorkflowWithCurrentVersion(
diff --git a/packages/twenty-front/src/modules/action-menu/actions/types/ActionHook.ts b/packages/twenty-front/src/modules/action-menu/actions/types/ActionHook.ts
new file mode 100644
index 000000000000..2add89e614a7
--- /dev/null
+++ b/packages/twenty-front/src/modules/action-menu/actions/types/ActionHook.ts
@@ -0,0 +1,14 @@
+import { ActionHookResult } from '@/action-menu/actions/types/ActionHookResult';
+import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
+
+export type ActionHook =
+ | ActionHookWithoutObjectMetadataItem
+ | ActionHookWithObjectMetadataItem;
+
+export type ActionHookWithoutObjectMetadataItem = () => ActionHookResult;
+
+export type ActionHookWithObjectMetadataItem = ({
+ objectMetadataItem,
+}: {
+ objectMetadataItem: ObjectMetadataItem;
+}) => ActionHookResult;
diff --git a/packages/twenty-front/src/modules/action-menu/actions/types/SingleRecordActionHook.ts b/packages/twenty-front/src/modules/action-menu/actions/types/SingleRecordActionHook.ts
deleted file mode 100644
index 4bf981332905..000000000000
--- a/packages/twenty-front/src/modules/action-menu/actions/types/SingleRecordActionHook.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-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;
diff --git a/packages/twenty-front/src/modules/action-menu/actions/utils/getActionConfig.ts b/packages/twenty-front/src/modules/action-menu/actions/utils/getActionConfig.ts
new file mode 100644
index 000000000000..9d2cd4b216cf
--- /dev/null
+++ b/packages/twenty-front/src/modules/action-menu/actions/utils/getActionConfig.ts
@@ -0,0 +1,25 @@
+import { DEFAULT_ACTIONS_CONFIG_V1 } from '@/action-menu/actions/record-actions/constants/DefaultActionsConfigV1';
+import { DEFAULT_ACTIONS_CONFIG_V2 } from '@/action-menu/actions/record-actions/constants/DefaultActionsConfigV2';
+import { WORKFLOW_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/WorkflowActionsConfig';
+import { WORKFLOW_RUNS_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig';
+import { WORKFLOW_VERSIONS_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/WorkflowVersionsActionsConfig';
+import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
+import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
+
+export const getActionConfig = (
+ objectMetadataItem: ObjectMetadataItem,
+ isPageHeaderV2Enabled: boolean,
+) => {
+ switch (objectMetadataItem.nameSingular) {
+ case CoreObjectNameSingular.Workflow:
+ return WORKFLOW_ACTIONS_CONFIG;
+ case CoreObjectNameSingular.WorkflowVersion:
+ return WORKFLOW_VERSIONS_ACTIONS_CONFIG;
+ case CoreObjectNameSingular.WorkflowRun:
+ return WORKFLOW_RUNS_ACTIONS_CONFIG;
+ default:
+ return isPageHeaderV2Enabled
+ ? DEFAULT_ACTIONS_CONFIG_V2
+ : DEFAULT_ACTIONS_CONFIG_V1;
+ }
+};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/utils/getActionViewType.ts b/packages/twenty-front/src/modules/action-menu/actions/utils/getActionViewType.ts
new file mode 100644
index 000000000000..435bebe05559
--- /dev/null
+++ b/packages/twenty-front/src/modules/action-menu/actions/utils/getActionViewType.ts
@@ -0,0 +1,32 @@
+import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
+import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
+import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
+
+export const getActionViewType = (
+ contextStoreCurrentViewType: ContextStoreViewType | null,
+ contextStoreTargetedRecordsRule: ContextStoreTargetedRecordsRule,
+) => {
+ if (contextStoreCurrentViewType === null) {
+ return null;
+ }
+
+ if (contextStoreCurrentViewType === ContextStoreViewType.ShowPage) {
+ return ActionViewType.SHOW_PAGE;
+ }
+
+ if (
+ contextStoreTargetedRecordsRule.mode === 'selection' &&
+ contextStoreTargetedRecordsRule.selectedRecordIds.length === 0
+ ) {
+ return ActionViewType.INDEX_PAGE_NO_SELECTION;
+ }
+
+ if (
+ contextStoreTargetedRecordsRule.mode === 'selection' &&
+ contextStoreTargetedRecordsRule.selectedRecordIds.length === 1
+ ) {
+ return ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION;
+ }
+
+ return ActionViewType.INDEX_PAGE_BULK_SELECTION;
+};
diff --git a/packages/twenty-front/src/modules/action-menu/hooks/useActionMenuEntriesWithCallbacks.ts b/packages/twenty-front/src/modules/action-menu/hooks/useActionMenuEntriesWithCallbacks.ts
deleted file mode 100644
index 3bdb784cfab5..000000000000
--- a/packages/twenty-front/src/modules/action-menu/hooks/useActionMenuEntriesWithCallbacks.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import { getActionConfig } from '@/action-menu/actions/record-actions/single-record/utils/getActionConfig';
-import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
-import { wrapActionInCallbacks } from '@/action-menu/actions/utils/wrapActionInCallbacks';
-import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
-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 { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
-import { useContext } from 'react';
-import { isDefined } from 'twenty-ui';
-import { FeatureFlagKey } from '~/generated/graphql';
-
-export const useActionMenuEntriesWithCallbacks = (
- objectMetadataItem: ObjectMetadataItem,
- viewType: ActionViewType,
-) => {
- const isPageHeaderV2Enabled = useIsFeatureEnabled(
- FeatureFlagKey.IsPageHeaderV2Enabled,
- );
-
- const actionConfig = getActionConfig(
- objectMetadataItem,
- isPageHeaderV2Enabled,
- );
-
- 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 { onActionStartedCallback, onActionExecutedCallback } =
- useContext(ActionMenuContext);
-
- const actionMenuEntries = Object.values(actionConfig ?? {})
- .filter((action) => action.availableOn?.includes(viewType))
- .map((action) => {
- const { shouldBeRegistered, onClick, ConfirmationModal } =
- action.actionHook({
- recordId: selectedRecordId,
- objectMetadataItem,
- });
-
- if (!shouldBeRegistered) {
- return undefined;
- }
-
- const wrappedAction = wrapActionInCallbacks({
- action: {
- ...action,
- onClick,
- ConfirmationModal,
- },
- onActionStartedCallback,
- onActionExecutedCallback,
- });
-
- return wrappedAction;
- })
- .filter(isDefined);
-
- return { actionMenuEntries };
-};
diff --git a/packages/twenty-front/src/testing/jest/JestContextStoreSetter.tsx b/packages/twenty-front/src/testing/jest/JestContextStoreSetter.tsx
index 47ac02bcc881..f08377a199ce 100644
--- a/packages/twenty-front/src/testing/jest/JestContextStoreSetter.tsx
+++ b/packages/twenty-front/src/testing/jest/JestContextStoreSetter.tsx
@@ -1,6 +1,7 @@
import { ReactNode, useEffect, useState } from 'react';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
+import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import {
ContextStoreTargetedRecordsRule,
contextStoreTargetedRecordsRuleComponentState,
@@ -13,10 +14,12 @@ export const JestContextStoreSetter = ({
mode: 'selection',
selectedRecordIds: [],
},
+ contextStoreNumberOfSelectedRecords = 0,
contextStoreCurrentObjectMetadataNameSingular = '',
children,
}: {
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
+ contextStoreNumberOfSelectedRecords?: number;
contextStoreCurrentObjectMetadataNameSingular?: string;
children: ReactNode;
}) => {
@@ -28,6 +31,10 @@ export const JestContextStoreSetter = ({
contextStoreCurrentObjectMetadataIdComponentState,
);
+ const setContextStoreNumberOfSelectedRecords = useSetRecoilComponentStateV2(
+ contextStoreNumberOfSelectedRecordsComponentState,
+ );
+
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: contextStoreCurrentObjectMetadataNameSingular,
});
@@ -38,12 +45,15 @@ export const JestContextStoreSetter = ({
useEffect(() => {
setContextStoreTargetedRecordsRule(contextStoreTargetedRecordsRule);
setContextStoreCurrentObjectMetadataId(contextStoreCurrentObjectMetadataId);
+ setContextStoreNumberOfSelectedRecords(contextStoreNumberOfSelectedRecords);
setIsLoaded(true);
}, [
setContextStoreTargetedRecordsRule,
setContextStoreCurrentObjectMetadataId,
contextStoreTargetedRecordsRule,
contextStoreCurrentObjectMetadataId,
+ setContextStoreNumberOfSelectedRecords,
+ contextStoreNumberOfSelectedRecords,
]);
return isLoaded ? <>{children}> : null;
diff --git a/packages/twenty-front/src/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper.tsx b/packages/twenty-front/src/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper.tsx
index 882675cd431d..1946010092bd 100644
--- a/packages/twenty-front/src/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper.tsx
+++ b/packages/twenty-front/src/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper.tsx
@@ -13,6 +13,7 @@ export type GetJestMetadataAndApolloMocksAndActionMenuWrapperProps = {
| undefined;
onInitializeRecoilSnapshot?: (snapshot: MutableSnapshot) => void;
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
+ contextStoreNumberOfSelectedRecords?: number;
contextStoreCurrentObjectMetadataNameSingular?: string;
componentInstanceId: string;
};
@@ -21,6 +22,7 @@ export const getJestMetadataAndApolloMocksAndActionMenuWrapper = ({
apolloMocks,
onInitializeRecoilSnapshot,
contextStoreTargetedRecordsRule,
+ contextStoreNumberOfSelectedRecords,
contextStoreCurrentObjectMetadataNameSingular,
componentInstanceId,
}: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps) => {
@@ -43,6 +45,9 @@ export const getJestMetadataAndApolloMocksAndActionMenuWrapper = ({
>