diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
index b3a49b4beb..17c12ee169 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
+++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
@@ -2567,20 +2567,15 @@ enum EngineComponentKey {
NAVIGATE_TO_NEXT_RECORD
NAVIGATE_TO_PREVIOUS_RECORD
CREATE_NEW_RECORD
- DELETE_SINGLE_RECORD
- DELETE_MULTIPLE_RECORDS
- RESTORE_SINGLE_RECORD
- RESTORE_MULTIPLE_RECORDS
- DESTROY_SINGLE_RECORD
- DESTROY_MULTIPLE_RECORDS
+ DELETE_RECORDS
+ RESTORE_RECORDS
+ DESTROY_RECORDS
ADD_TO_FAVORITES
REMOVE_FROM_FAVORITES
EXPORT_NOTE_TO_PDF
- EXPORT_FROM_RECORD_INDEX
- EXPORT_FROM_RECORD_SHOW
+ EXPORT_RECORDS
UPDATE_MULTIPLE_RECORDS
MERGE_MULTIPLE_RECORDS
- EXPORT_MULTIPLE_RECORDS
IMPORT_RECORDS
EXPORT_VIEW
SEE_DELETED_RECORDS
@@ -2623,6 +2618,15 @@ enum EngineComponentKey {
VIEW_PREVIOUS_AI_CHATS
TRIGGER_WORKFLOW_VERSION
FRONT_COMPONENT_RENDERER
+ DELETE_SINGLE_RECORD
+ DELETE_MULTIPLE_RECORDS
+ RESTORE_SINGLE_RECORD
+ RESTORE_MULTIPLE_RECORDS
+ DESTROY_SINGLE_RECORD
+ DESTROY_MULTIPLE_RECORDS
+ EXPORT_FROM_RECORD_INDEX
+ EXPORT_FROM_RECORD_SHOW
+ EXPORT_MULTIPLE_RECORDS
}
enum CommandMenuItemAvailabilityType {
diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts
index c1f4731c2d..28984f6949 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts
+++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts
@@ -2272,7 +2272,7 @@ export interface CommandMenuItem {
__typename: 'CommandMenuItem'
}
-export type EngineComponentKey = 'NAVIGATE_TO_NEXT_RECORD' | 'NAVIGATE_TO_PREVIOUS_RECORD' | 'CREATE_NEW_RECORD' | 'DELETE_SINGLE_RECORD' | 'DELETE_MULTIPLE_RECORDS' | 'RESTORE_SINGLE_RECORD' | 'RESTORE_MULTIPLE_RECORDS' | 'DESTROY_SINGLE_RECORD' | 'DESTROY_MULTIPLE_RECORDS' | 'ADD_TO_FAVORITES' | 'REMOVE_FROM_FAVORITES' | 'EXPORT_NOTE_TO_PDF' | 'EXPORT_FROM_RECORD_INDEX' | 'EXPORT_FROM_RECORD_SHOW' | 'UPDATE_MULTIPLE_RECORDS' | 'MERGE_MULTIPLE_RECORDS' | 'EXPORT_MULTIPLE_RECORDS' | 'IMPORT_RECORDS' | 'EXPORT_VIEW' | 'SEE_DELETED_RECORDS' | 'CREATE_NEW_VIEW' | 'HIDE_DELETED_RECORDS' | 'GO_TO_PEOPLE' | 'GO_TO_COMPANIES' | 'GO_TO_DASHBOARDS' | 'GO_TO_OPPORTUNITIES' | 'GO_TO_SETTINGS' | 'GO_TO_TASKS' | 'GO_TO_NOTES' | 'EDIT_RECORD_PAGE_LAYOUT' | 'EDIT_DASHBOARD_LAYOUT' | 'SAVE_DASHBOARD_LAYOUT' | 'CANCEL_DASHBOARD_LAYOUT' | 'DUPLICATE_DASHBOARD' | 'GO_TO_WORKFLOWS' | 'ACTIVATE_WORKFLOW' | 'DEACTIVATE_WORKFLOW' | 'DISCARD_DRAFT_WORKFLOW' | 'TEST_WORKFLOW' | 'SEE_ACTIVE_VERSION_WORKFLOW' | 'SEE_RUNS_WORKFLOW' | 'SEE_VERSIONS_WORKFLOW' | 'ADD_NODE_WORKFLOW' | 'TIDY_UP_WORKFLOW' | 'DUPLICATE_WORKFLOW' | 'GO_TO_RUNS' | 'SEE_VERSION_WORKFLOW_RUN' | 'SEE_WORKFLOW_WORKFLOW_RUN' | 'STOP_WORKFLOW_RUN' | 'SEE_RUNS_WORKFLOW_VERSION' | 'SEE_WORKFLOW_WORKFLOW_VERSION' | 'USE_AS_DRAFT_WORKFLOW_VERSION' | 'SEE_VERSIONS_WORKFLOW_VERSION' | 'SEARCH_RECORDS' | 'SEARCH_RECORDS_FALLBACK' | 'ASK_AI' | 'VIEW_PREVIOUS_AI_CHATS' | 'TRIGGER_WORKFLOW_VERSION' | 'FRONT_COMPONENT_RENDERER'
+export type EngineComponentKey = 'NAVIGATE_TO_NEXT_RECORD' | 'NAVIGATE_TO_PREVIOUS_RECORD' | 'CREATE_NEW_RECORD' | 'DELETE_RECORDS' | 'RESTORE_RECORDS' | 'DESTROY_RECORDS' | 'ADD_TO_FAVORITES' | 'REMOVE_FROM_FAVORITES' | 'EXPORT_NOTE_TO_PDF' | 'EXPORT_RECORDS' | 'UPDATE_MULTIPLE_RECORDS' | 'MERGE_MULTIPLE_RECORDS' | 'IMPORT_RECORDS' | 'EXPORT_VIEW' | 'SEE_DELETED_RECORDS' | 'CREATE_NEW_VIEW' | 'HIDE_DELETED_RECORDS' | 'GO_TO_PEOPLE' | 'GO_TO_COMPANIES' | 'GO_TO_DASHBOARDS' | 'GO_TO_OPPORTUNITIES' | 'GO_TO_SETTINGS' | 'GO_TO_TASKS' | 'GO_TO_NOTES' | 'EDIT_RECORD_PAGE_LAYOUT' | 'EDIT_DASHBOARD_LAYOUT' | 'SAVE_DASHBOARD_LAYOUT' | 'CANCEL_DASHBOARD_LAYOUT' | 'DUPLICATE_DASHBOARD' | 'GO_TO_WORKFLOWS' | 'ACTIVATE_WORKFLOW' | 'DEACTIVATE_WORKFLOW' | 'DISCARD_DRAFT_WORKFLOW' | 'TEST_WORKFLOW' | 'SEE_ACTIVE_VERSION_WORKFLOW' | 'SEE_RUNS_WORKFLOW' | 'SEE_VERSIONS_WORKFLOW' | 'ADD_NODE_WORKFLOW' | 'TIDY_UP_WORKFLOW' | 'DUPLICATE_WORKFLOW' | 'GO_TO_RUNS' | 'SEE_VERSION_WORKFLOW_RUN' | 'SEE_WORKFLOW_WORKFLOW_RUN' | 'STOP_WORKFLOW_RUN' | 'SEE_RUNS_WORKFLOW_VERSION' | 'SEE_WORKFLOW_WORKFLOW_VERSION' | 'USE_AS_DRAFT_WORKFLOW_VERSION' | 'SEE_VERSIONS_WORKFLOW_VERSION' | 'SEARCH_RECORDS' | 'SEARCH_RECORDS_FALLBACK' | 'ASK_AI' | 'VIEW_PREVIOUS_AI_CHATS' | 'TRIGGER_WORKFLOW_VERSION' | 'FRONT_COMPONENT_RENDERER' | 'DELETE_SINGLE_RECORD' | 'DELETE_MULTIPLE_RECORDS' | 'RESTORE_SINGLE_RECORD' | 'RESTORE_MULTIPLE_RECORDS' | 'DESTROY_SINGLE_RECORD' | 'DESTROY_MULTIPLE_RECORDS' | 'EXPORT_FROM_RECORD_INDEX' | 'EXPORT_FROM_RECORD_SHOW' | 'EXPORT_MULTIPLE_RECORDS'
export type CommandMenuItemAvailabilityType = 'GLOBAL' | 'RECORD_SELECTION' | 'FALLBACK'
@@ -9140,20 +9140,15 @@ export const enumEngineComponentKey = {
NAVIGATE_TO_NEXT_RECORD: 'NAVIGATE_TO_NEXT_RECORD' as const,
NAVIGATE_TO_PREVIOUS_RECORD: 'NAVIGATE_TO_PREVIOUS_RECORD' as const,
CREATE_NEW_RECORD: 'CREATE_NEW_RECORD' as const,
- DELETE_SINGLE_RECORD: 'DELETE_SINGLE_RECORD' as const,
- DELETE_MULTIPLE_RECORDS: 'DELETE_MULTIPLE_RECORDS' as const,
- RESTORE_SINGLE_RECORD: 'RESTORE_SINGLE_RECORD' as const,
- RESTORE_MULTIPLE_RECORDS: 'RESTORE_MULTIPLE_RECORDS' as const,
- DESTROY_SINGLE_RECORD: 'DESTROY_SINGLE_RECORD' as const,
- DESTROY_MULTIPLE_RECORDS: 'DESTROY_MULTIPLE_RECORDS' as const,
+ DELETE_RECORDS: 'DELETE_RECORDS' as const,
+ RESTORE_RECORDS: 'RESTORE_RECORDS' as const,
+ DESTROY_RECORDS: 'DESTROY_RECORDS' as const,
ADD_TO_FAVORITES: 'ADD_TO_FAVORITES' as const,
REMOVE_FROM_FAVORITES: 'REMOVE_FROM_FAVORITES' as const,
EXPORT_NOTE_TO_PDF: 'EXPORT_NOTE_TO_PDF' as const,
- EXPORT_FROM_RECORD_INDEX: 'EXPORT_FROM_RECORD_INDEX' as const,
- EXPORT_FROM_RECORD_SHOW: 'EXPORT_FROM_RECORD_SHOW' as const,
+ EXPORT_RECORDS: 'EXPORT_RECORDS' as const,
UPDATE_MULTIPLE_RECORDS: 'UPDATE_MULTIPLE_RECORDS' as const,
MERGE_MULTIPLE_RECORDS: 'MERGE_MULTIPLE_RECORDS' as const,
- EXPORT_MULTIPLE_RECORDS: 'EXPORT_MULTIPLE_RECORDS' as const,
IMPORT_RECORDS: 'IMPORT_RECORDS' as const,
EXPORT_VIEW: 'EXPORT_VIEW' as const,
SEE_DELETED_RECORDS: 'SEE_DELETED_RECORDS' as const,
@@ -9195,7 +9190,16 @@ export const enumEngineComponentKey = {
ASK_AI: 'ASK_AI' as const,
VIEW_PREVIOUS_AI_CHATS: 'VIEW_PREVIOUS_AI_CHATS' as const,
TRIGGER_WORKFLOW_VERSION: 'TRIGGER_WORKFLOW_VERSION' as const,
- FRONT_COMPONENT_RENDERER: 'FRONT_COMPONENT_RENDERER' as const
+ FRONT_COMPONENT_RENDERER: 'FRONT_COMPONENT_RENDERER' as const,
+ DELETE_SINGLE_RECORD: 'DELETE_SINGLE_RECORD' as const,
+ DELETE_MULTIPLE_RECORDS: 'DELETE_MULTIPLE_RECORDS' as const,
+ RESTORE_SINGLE_RECORD: 'RESTORE_SINGLE_RECORD' as const,
+ RESTORE_MULTIPLE_RECORDS: 'RESTORE_MULTIPLE_RECORDS' as const,
+ DESTROY_SINGLE_RECORD: 'DESTROY_SINGLE_RECORD' as const,
+ DESTROY_MULTIPLE_RECORDS: 'DESTROY_MULTIPLE_RECORDS' as const,
+ EXPORT_FROM_RECORD_INDEX: 'EXPORT_FROM_RECORD_INDEX' as const,
+ EXPORT_FROM_RECORD_SHOW: 'EXPORT_FROM_RECORD_SHOW' as const,
+ EXPORT_MULTIPLE_RECORDS: 'EXPORT_MULTIPLE_RECORDS' as const
}
export const enumCommandMenuItemAvailabilityType = {
diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts
index 64f8ee53e5..2d2f59ee40 100644
--- a/packages/twenty-front/src/generated-metadata/graphql.ts
+++ b/packages/twenty-front/src/generated-metadata/graphql.ts
@@ -1563,8 +1563,10 @@ export enum EngineComponentKey {
CREATE_NEW_VIEW = 'CREATE_NEW_VIEW',
DEACTIVATE_WORKFLOW = 'DEACTIVATE_WORKFLOW',
DELETE_MULTIPLE_RECORDS = 'DELETE_MULTIPLE_RECORDS',
+ DELETE_RECORDS = 'DELETE_RECORDS',
DELETE_SINGLE_RECORD = 'DELETE_SINGLE_RECORD',
DESTROY_MULTIPLE_RECORDS = 'DESTROY_MULTIPLE_RECORDS',
+ DESTROY_RECORDS = 'DESTROY_RECORDS',
DESTROY_SINGLE_RECORD = 'DESTROY_SINGLE_RECORD',
DISCARD_DRAFT_WORKFLOW = 'DISCARD_DRAFT_WORKFLOW',
DUPLICATE_DASHBOARD = 'DUPLICATE_DASHBOARD',
@@ -1575,6 +1577,7 @@ export enum EngineComponentKey {
EXPORT_FROM_RECORD_SHOW = 'EXPORT_FROM_RECORD_SHOW',
EXPORT_MULTIPLE_RECORDS = 'EXPORT_MULTIPLE_RECORDS',
EXPORT_NOTE_TO_PDF = 'EXPORT_NOTE_TO_PDF',
+ EXPORT_RECORDS = 'EXPORT_RECORDS',
EXPORT_VIEW = 'EXPORT_VIEW',
FRONT_COMPONENT_RENDERER = 'FRONT_COMPONENT_RENDERER',
GO_TO_COMPANIES = 'GO_TO_COMPANIES',
@@ -1593,6 +1596,7 @@ export enum EngineComponentKey {
NAVIGATE_TO_PREVIOUS_RECORD = 'NAVIGATE_TO_PREVIOUS_RECORD',
REMOVE_FROM_FAVORITES = 'REMOVE_FROM_FAVORITES',
RESTORE_MULTIPLE_RECORDS = 'RESTORE_MULTIPLE_RECORDS',
+ RESTORE_RECORDS = 'RESTORE_RECORDS',
RESTORE_SINGLE_RECORD = 'RESTORE_SINGLE_RECORD',
SAVE_DASHBOARD_LAYOUT = 'SAVE_DASHBOARD_LAYOUT',
SEARCH_RECORDS = 'SEARCH_RECORDS',
diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/constants/EngineComponentKeyHeadlessComponentMap.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/constants/EngineComponentKeyHeadlessComponentMap.tsx
index c85a0c8ba9..57101f4477 100644
--- a/packages/twenty-front/src/modules/command-menu-item/engine-command/constants/EngineComponentKeyHeadlessComponentMap.tsx
+++ b/packages/twenty-front/src/modules/command-menu-item/engine-command/constants/EngineComponentKeyHeadlessComponentMap.tsx
@@ -1,25 +1,21 @@
import { HeadlessNavigateEngineCommand } from '@/command-menu-item/engine-command/components/HeadlessNavigateEngineCommand';
import { HeadlessOpenSidePanelPageEngineCommand } from '@/command-menu-item/engine-command/components/HeadlessOpenSidePanelPageEngineCommand';
-import { DeleteMultipleRecordsCommand } from '@/command-menu-item/engine-command/record/multiple-records/components/DeleteMultipleRecordsCommand';
-import { DestroyMultipleRecordsCommand } from '@/command-menu-item/engine-command/record/multiple-records/components/DestroyMultipleRecordsCommand';
-import { ExportMultipleRecordsCommand } from '@/command-menu-item/engine-command/record/multiple-records/components/ExportMultipleRecordsCommand';
import { MergeMultipleRecordsCommand } from '@/command-menu-item/engine-command/record/multiple-records/components/MergeMultipleRecordsCommand';
-import { RestoreMultipleRecordsCommand } from '@/command-menu-item/engine-command/record/multiple-records/components/RestoreMultipleRecordsCommand';
import { UpdateMultipleRecordsCommand } from '@/command-menu-item/engine-command/record/multiple-records/components/UpdateMultipleRecordsCommand';
import { CreateNewIndexRecordNoSelectionRecordCommand } from '@/command-menu-item/engine-command/record/no-selection/components/CreateNewIndexRecordNoSelectionRecordCommand';
import { CreateNewViewNoSelectionRecordCommand } from '@/command-menu-item/engine-command/record/no-selection/components/CreateNewViewNoSelectionRecordCommand';
import { HideDeletedRecordsNoSelectionRecordCommand } from '@/command-menu-item/engine-command/record/no-selection/components/HideDeletedRecordsNoSelectionRecordCommand';
import { ImportRecordsNoSelectionRecordCommand } from '@/command-menu-item/engine-command/record/no-selection/components/ImportRecordsNoSelectionRecordCommand';
import { SeeDeletedRecordsNoSelectionRecordCommand } from '@/command-menu-item/engine-command/record/no-selection/components/SeeDeletedRecordsNoSelectionRecordCommand';
+import { DeleteRecordsCommand } from '@/command-menu-item/engine-command/record/components/DeleteRecordsCommand';
+import { DestroyRecordsCommand } from '@/command-menu-item/engine-command/record/components/DestroyRecordsCommand';
+import { ExportRecordsCommand } from '@/command-menu-item/engine-command/record/components/ExportRecordsCommand';
+import { RestoreRecordsCommand } from '@/command-menu-item/engine-command/record/components/RestoreRecordsCommand';
import { AddToFavoritesSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/components/AddToFavoritesSingleRecordCommand';
-import { DeleteSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/components/DeleteSingleRecordCommand';
-import { DestroySingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/components/DestroySingleRecordCommand';
import { ExportNoteSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/components/ExportNoteSingleRecordCommand';
-import { ExportSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/components/ExportSingleRecordCommand';
import { NavigateToNextRecordSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/components/NavigateToNextRecordSingleRecordCommand';
import { NavigateToPreviousRecordSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/components/NavigateToPreviousRecordSingleRecordCommand';
import { RemoveFromFavoritesSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/components/RemoveFromFavoritesSingleRecordCommand';
-import { RestoreSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/components/RestoreSingleRecordCommand';
import { CancelDashboardSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/dashboard/components/CancelDashboardSingleRecordCommand';
import { DuplicateDashboardSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/dashboard/components/DuplicateDashboardSingleRecordCommand';
import { EditDashboardSingleRecordCommand } from '@/command-menu-item/engine-command/record/single-record/dashboard/components/EditDashboardSingleRecordCommand';
@@ -57,18 +53,9 @@ export const ENGINE_COMPONENT_KEY_COMPONENT_MAP: Record<
[EngineComponentKey.CREATE_NEW_RECORD]: (
),
- [EngineComponentKey.DELETE_SINGLE_RECORD]: ,
- [EngineComponentKey.DELETE_MULTIPLE_RECORDS]: (
-
- ),
- [EngineComponentKey.RESTORE_SINGLE_RECORD]: ,
- [EngineComponentKey.RESTORE_MULTIPLE_RECORDS]: (
-
- ),
- [EngineComponentKey.DESTROY_SINGLE_RECORD]: ,
- [EngineComponentKey.DESTROY_MULTIPLE_RECORDS]: (
-
- ),
+ [EngineComponentKey.DELETE_RECORDS]: ,
+ [EngineComponentKey.RESTORE_RECORDS]: ,
+ [EngineComponentKey.DESTROY_RECORDS]: ,
[EngineComponentKey.ADD_TO_FAVORITES]: ,
[EngineComponentKey.REMOVE_FROM_FAVORITES]: (
@@ -107,20 +94,14 @@ export const ENGINE_COMPONENT_KEY_COMPONENT_MAP: Record<
),
[EngineComponentKey.EXPORT_NOTE_TO_PDF]: ,
- [EngineComponentKey.EXPORT_FROM_RECORD_INDEX]: (
-
- ),
- [EngineComponentKey.EXPORT_FROM_RECORD_SHOW]: ,
- [EngineComponentKey.EXPORT_MULTIPLE_RECORDS]: (
-
- ),
+ [EngineComponentKey.EXPORT_RECORDS]: ,
[EngineComponentKey.UPDATE_MULTIPLE_RECORDS]: (
),
[EngineComponentKey.IMPORT_RECORDS]: (
),
- [EngineComponentKey.EXPORT_VIEW]: ,
+ [EngineComponentKey.EXPORT_VIEW]: ,
[EngineComponentKey.SEE_DELETED_RECORDS]: (
),
@@ -259,4 +240,15 @@ export const ENGINE_COMPONENT_KEY_COMPONENT_MAP: Record<
[EngineComponentKey.FRONT_COMPONENT_RENDERER]: (
),
+
+ // Deprecated keys kept for backward compatibility until migration runs
+ [EngineComponentKey.DELETE_SINGLE_RECORD]: ,
+ [EngineComponentKey.DELETE_MULTIPLE_RECORDS]: ,
+ [EngineComponentKey.RESTORE_SINGLE_RECORD]: ,
+ [EngineComponentKey.RESTORE_MULTIPLE_RECORDS]: ,
+ [EngineComponentKey.DESTROY_SINGLE_RECORD]: ,
+ [EngineComponentKey.DESTROY_MULTIPLE_RECORDS]: ,
+ [EngineComponentKey.EXPORT_FROM_RECORD_INDEX]: ,
+ [EngineComponentKey.EXPORT_FROM_RECORD_SHOW]: ,
+ [EngineComponentKey.EXPORT_MULTIPLE_RECORDS]: ,
};
diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/DeleteSingleRecordCommand.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/components/DeleteRecordsCommand.tsx
similarity index 55%
rename from packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/DeleteSingleRecordCommand.tsx
rename to packages/twenty-front/src/modules/command-menu-item/engine-command/record/components/DeleteRecordsCommand.tsx
index 75f84b29ac..d58632be4d 100644
--- a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/DeleteSingleRecordCommand.tsx
+++ b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/components/DeleteRecordsCommand.tsx
@@ -2,34 +2,37 @@ import { HeadlessEngineCommandWrapperEffect } from '@/command-menu-item/engine-c
import { useHeadlessCommandContextApi } from '@/command-menu-item/engine-command/hooks/useHeadlessCommandContextApi';
import { useRemoveNavigationMenuItemByTargetRecordId } from '@/navigation-menu-item/common/hooks/useRemoveNavigationMenuItemByTargetRecordId';
import { useNavigationMenuItemsData } from '@/navigation-menu-item/display/hooks/useNavigationMenuItemsData';
-import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
+import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
+import { useIncrementalDeleteManyRecords } from '@/object-record/hooks/useIncrementalDeleteManyRecords';
import { useRemoveSelectedRecordsFromRecordBoard } from '@/object-record/record-board/hooks/useRemoveSelectedRecordsFromRecordBoard';
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
+import { type RecordGqlOperationFilter } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
-export const DeleteSingleRecordCommand = () => {
- const { recordIndexId, objectMetadataItem, selectedRecords } =
+export const DeleteRecordsCommand = () => {
+ const { recordIndexId, objectMetadataItem, selectedRecords, graphqlFilter } =
useHeadlessCommandContextApi();
- const recordId = selectedRecords[0]?.id;
-
- if (
- !isDefined(recordId) ||
- !isDefined(recordIndexId) ||
- !isDefined(objectMetadataItem)
- ) {
+ if (!isDefined(recordIndexId) || !isDefined(objectMetadataItem)) {
throw new Error(
- 'Record ID, record index ID, and object metadata are required to delete single record',
+ 'Record index ID and object metadata are required to delete records',
);
}
+ const recordId = selectedRecords[0]?.id;
+
const { resetTableRowSelection } = useResetTableRowSelection(recordIndexId);
const { removeSelectedRecordsFromRecordBoard } =
useRemoveSelectedRecordsFromRecordBoard(recordIndexId);
- const { deleteOneRecord } = useDeleteOneRecord({
+ const noMatchFilter: RecordGqlOperationFilter = { id: { in: [] } };
+
+ const { incrementalDeleteManyRecords } = useIncrementalDeleteManyRecords({
objectNameSingular: objectMetadataItem.nameSingular,
+ filter: graphqlFilter ?? noMatchFilter,
+ pageSize: DEFAULT_QUERY_PAGE_SIZE,
+ delayInMsBetweenMutations: 50,
});
const { navigationMenuItems, workspaceNavigationMenuItems } =
@@ -40,19 +43,24 @@ export const DeleteSingleRecordCommand = () => {
const handleExecute = async () => {
removeSelectedRecordsFromRecordBoard();
-
resetTableRowSelection();
- const foundNavigationMenuItem = [
- ...navigationMenuItems,
- ...workspaceNavigationMenuItems,
- ].find((item) => item.targetRecordId === recordId);
+ if (isDefined(recordId)) {
+ const foundNavigationMenuItem = [
+ ...navigationMenuItems,
+ ...workspaceNavigationMenuItems,
+ ].find((item) => item.targetRecordId === recordId);
- if (isDefined(foundNavigationMenuItem)) {
- removeNavigationMenuItemsByTargetRecordIds([recordId]);
+ if (isDefined(foundNavigationMenuItem)) {
+ removeNavigationMenuItemsByTargetRecordIds([recordId]);
+ }
}
- await deleteOneRecord(recordId);
+ if (!isDefined(graphqlFilter)) {
+ throw new Error('Cannot delete records without a valid filter');
+ }
+
+ await incrementalDeleteManyRecords();
};
return ;
diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/multiple-records/components/DestroyMultipleRecordsCommand.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/components/DestroyRecordsCommand.tsx
similarity index 52%
rename from packages/twenty-front/src/modules/command-menu-item/engine-command/record/multiple-records/components/DestroyMultipleRecordsCommand.tsx
rename to packages/twenty-front/src/modules/command-menu-item/engine-command/record/components/DestroyRecordsCommand.tsx
index 13ed419b0e..1fde0f7f98 100644
--- a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/multiple-records/components/DestroyMultipleRecordsCommand.tsx
+++ b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/components/DestroyRecordsCommand.tsx
@@ -5,33 +5,36 @@ import { useIncrementalDestroyManyRecords } from '@/object-record/hooks/useIncre
import { useRemoveSelectedRecordsFromRecordBoard } from '@/object-record/record-board/hooks/useRemoveSelectedRecordsFromRecordBoard';
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
import { t } from '@lingui/core/macro';
-import { type RecordGqlOperationFilter } from 'twenty-shared/types';
+import { AppPath, type RecordGqlOperationFilter } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
+import { useNavigateApp } from '~/hooks/useNavigateApp';
-export const DestroyMultipleRecordsCommand = () => {
- const { recordIndexId, objectMetadataItem, graphqlFilter } =
+export const DestroyRecordsCommand = () => {
+ const { recordIndexId, objectMetadataItem, selectedRecords, graphqlFilter } =
useHeadlessCommandContextApi();
- if (
- !isDefined(recordIndexId) ||
- !isDefined(objectMetadataItem) ||
- !isDefined(graphqlFilter)
- ) {
+ if (!isDefined(recordIndexId) || !isDefined(objectMetadataItem)) {
throw new Error(
- 'Record index ID, object metadata item, and graphql filter are required to destroy multiple records',
+ 'Record index ID and object metadata are required to destroy records',
);
}
+ const isSingleRecord = selectedRecords.length === 1;
+
+ const navigateApp = useNavigateApp();
+
const { resetTableRowSelection } = useResetTableRowSelection(recordIndexId);
const { removeSelectedRecordsFromRecordBoard } =
useRemoveSelectedRecordsFromRecordBoard(recordIndexId);
+ const noMatchFilter: RecordGqlOperationFilter = { id: { in: [] } };
+
const deletedAtFilter: RecordGqlOperationFilter = {
deletedAt: { is: 'NOT_NULL' },
};
- const combinedFilter = {
- ...graphqlFilter,
+ const combinedFilter: RecordGqlOperationFilter = {
+ ...(graphqlFilter ?? noMatchFilter),
...deletedAtFilter,
};
@@ -44,17 +47,36 @@ export const DestroyMultipleRecordsCommand = () => {
const handleExecute = async () => {
removeSelectedRecordsFromRecordBoard();
-
resetTableRowSelection();
+ if (!isDefined(graphqlFilter)) {
+ throw new Error('Cannot destroy records without a valid filter');
+ }
+
await incrementalDestroyManyRecords();
+
+ if (isSingleRecord) {
+ navigateApp(AppPath.RecordIndexPage, {
+ objectNamePlural: objectMetadataItem.namePlural,
+ });
+ }
};
+ const objectLabel = isSingleRecord
+ ? objectMetadataItem.labelSingular
+ : objectMetadataItem.labelPlural;
+
+ const title = t`Permanently Destroy ${objectLabel}`;
+ const subtitle = isSingleRecord
+ ? t`Are you sure you want to destroy this ${objectMetadataItem.labelSingular}? It cannot be recovered anymore.`
+ : t`Are you sure you want to destroy these ${objectMetadataItem.labelPlural}? They won't be recoverable anymore.`;
+ const confirmButtonText = `${t`Permanently Destroy`} ${objectLabel}`;
+
return (
);
diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/multiple-records/components/ExportMultipleRecordsCommand.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/components/ExportRecordsCommand.tsx
similarity index 68%
rename from packages/twenty-front/src/modules/command-menu-item/engine-command/record/multiple-records/components/ExportMultipleRecordsCommand.tsx
rename to packages/twenty-front/src/modules/command-menu-item/engine-command/record/components/ExportRecordsCommand.tsx
index fc1260dfa6..5fd590fd0e 100644
--- a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/multiple-records/components/ExportMultipleRecordsCommand.tsx
+++ b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/components/ExportRecordsCommand.tsx
@@ -4,13 +4,14 @@ import { CommandComponentInstanceContext } from '@/command-menu-item/engine-comm
import { commandMenuItemProgressFamilyState } from '@/command-menu-item/states/commandMenuItemProgressFamilyState';
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
import { useRecordIndexExportRecords } from '@/object-record/record-index/export/hooks/useRecordIndexExportRecords';
+import { useExportSingleRecord } from '@/object-record/record-show/hooks/useExportSingleRecord';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { useEffect } from 'react';
import { isDefined } from 'twenty-shared/utils';
-const ExportMultipleRecordsCommandContent = ({
+const ExportIndexRecordsContent = ({
objectMetadataItem,
recordIndexId,
setCommandMenuItemProgress,
@@ -42,8 +43,26 @@ const ExportMultipleRecordsCommandContent = ({
return ;
};
-export const ExportMultipleRecordsCommand = () => {
- const { objectMetadataItem, recordIndexId } = useHeadlessCommandContextApi();
+const ExportShowRecordContent = ({
+ objectMetadataItem,
+ recordId,
+}: {
+ objectMetadataItem: EnrichedObjectMetadataItem;
+ recordId: string;
+}) => {
+ const filename = `${objectMetadataItem.nameSingular}.csv`;
+ const { download } = useExportSingleRecord({
+ filename,
+ objectMetadataItem,
+ recordId,
+ });
+
+ return ;
+};
+
+export const ExportRecordsCommand = () => {
+ const { objectMetadataItem, recordIndexId, selectedRecords } =
+ useHeadlessCommandContextApi();
const engineCommandId = useAvailableComponentInstanceIdOrThrow(
CommandComponentInstanceContext,
@@ -54,9 +73,25 @@ export const ExportMultipleRecordsCommand = () => {
engineCommandId,
);
- if (!isDefined(objectMetadataItem) || !isDefined(recordIndexId)) {
+ if (!isDefined(objectMetadataItem)) {
+ throw new Error('Object metadata item is required to export records');
+ }
+
+ const recordId = selectedRecords[0]?.id;
+ const isShowPageExport = !isDefined(recordIndexId) && isDefined(recordId);
+
+ if (isShowPageExport) {
+ return (
+
+ );
+ }
+
+ if (!isDefined(recordIndexId)) {
throw new Error(
- 'Object metadata item and record index ID are required to export multiple records',
+ 'Record index ID is required to export records from index page',
);
}
@@ -64,7 +99,7 @@ export const ExportMultipleRecordsCommand = () => {
- {
- const { recordIndexId, objectMetadataItem, graphqlFilter } =
+export const RestoreRecordsCommand = () => {
+ const { recordIndexId, objectMetadataItem, selectedRecords, graphqlFilter } =
useHeadlessCommandContextApi();
- if (
- !isDefined(recordIndexId) ||
- !isDefined(objectMetadataItem) ||
- !isDefined(graphqlFilter)
- ) {
+ if (!isDefined(recordIndexId) || !isDefined(objectMetadataItem)) {
throw new Error(
- 'Record index ID, object metadata item, and graphql filter are required to restore multiple records',
+ 'Record index ID and object metadata are required to restore records',
);
}
+ const isSingleRecord = selectedRecords.length === 1;
+
const { resetTableRowSelection } = useResetTableRowSelection(recordIndexId);
const { removeSelectedRecordsFromRecordBoard } =
useRemoveSelectedRecordsFromRecordBoard(recordIndexId);
@@ -31,12 +29,14 @@ export const RestoreMultipleRecordsCommand = () => {
objectNameSingular: objectMetadataItem.nameSingular,
});
+ const noMatchFilter: RecordGqlOperationFilter = { id: { in: [] } };
+
const deletedAtFilter: RecordGqlOperationFilter = {
deletedAt: { is: 'NOT_NULL' },
};
- const combinedFilter = {
- ...graphqlFilter,
+ const combinedFilter: RecordGqlOperationFilter = {
+ ...(graphqlFilter ?? noMatchFilter),
...deletedAtFilter,
};
@@ -49,6 +49,11 @@ export const RestoreMultipleRecordsCommand = () => {
const handleExecute = async () => {
removeSelectedRecordsFromRecordBoard();
+
+ if (!isDefined(graphqlFilter)) {
+ throw new Error('Cannot restore records without a valid filter');
+ }
+
const recordsToRestore = await fetchAllRecordIds();
const recordIdsToRestore = recordsToRestore.map((record) => record.id);
@@ -59,11 +64,20 @@ export const RestoreMultipleRecordsCommand = () => {
});
};
+ const objectLabel = isSingleRecord
+ ? objectMetadataItem.labelSingular
+ : objectMetadataItem.labelPlural;
+
+ const title = t`Restore ${objectLabel}`;
+ const subtitle = isSingleRecord
+ ? t`Are you sure you want to restore this ${objectMetadataItem.labelSingular}?`
+ : t`Are you sure you want to restore these ${objectMetadataItem.labelPlural}?`;
+
return (
diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/multiple-records/components/DeleteMultipleRecordsCommand.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/multiple-records/components/DeleteMultipleRecordsCommand.tsx
deleted file mode 100644
index 9c3b92ff20..0000000000
--- a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/multiple-records/components/DeleteMultipleRecordsCommand.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { HeadlessEngineCommandWrapperEffect } from '@/command-menu-item/engine-command/components/HeadlessEngineCommandWrapperEffect';
-import { useHeadlessCommandContextApi } from '@/command-menu-item/engine-command/hooks/useHeadlessCommandContextApi';
-import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
-import { useIncrementalDeleteManyRecords } from '@/object-record/hooks/useIncrementalDeleteManyRecords';
-import { useRemoveSelectedRecordsFromRecordBoard } from '@/object-record/record-board/hooks/useRemoveSelectedRecordsFromRecordBoard';
-import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
-import { isDefined } from 'twenty-shared/utils';
-
-export const DeleteMultipleRecordsCommand = () => {
- const { recordIndexId, objectMetadataItem, graphqlFilter } =
- useHeadlessCommandContextApi();
-
- if (
- !isDefined(recordIndexId) ||
- !isDefined(objectMetadataItem) ||
- !isDefined(graphqlFilter)
- ) {
- throw new Error(
- 'Record index ID, object metadata item, and graphql filter are required to delete multiple records',
- );
- }
-
- const { resetTableRowSelection } = useResetTableRowSelection(recordIndexId);
-
- const { removeSelectedRecordsFromRecordBoard } =
- useRemoveSelectedRecordsFromRecordBoard(recordIndexId);
-
- const { incrementalDeleteManyRecords } = useIncrementalDeleteManyRecords({
- objectNameSingular: objectMetadataItem.nameSingular,
- filter: graphqlFilter,
- pageSize: DEFAULT_QUERY_PAGE_SIZE,
- delayInMsBetweenMutations: 50,
- });
-
- const handleExecute = async () => {
- removeSelectedRecordsFromRecordBoard();
- resetTableRowSelection();
- await incrementalDeleteManyRecords();
- };
-
- return ;
-};
diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/DestroySingleRecordCommand.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/DestroySingleRecordCommand.tsx
deleted file mode 100644
index 822ead630a..0000000000
--- a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/DestroySingleRecordCommand.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { HeadlessConfirmationModalEngineCommandEffect } from '@/command-menu-item/engine-command/components/HeadlessConfirmationModalEngineCommandEffect';
-import { useHeadlessCommandContextApi } from '@/command-menu-item/engine-command/hooks/useHeadlessCommandContextApi';
-import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';
-import { useRemoveSelectedRecordsFromRecordBoard } from '@/object-record/record-board/hooks/useRemoveSelectedRecordsFromRecordBoard';
-import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
-import { t } from '@lingui/core/macro';
-import { AppPath } from 'twenty-shared/types';
-import { isDefined } from 'twenty-shared/utils';
-import { useNavigateApp } from '~/hooks/useNavigateApp';
-
-export const DestroySingleRecordCommand = () => {
- const { recordIndexId, objectMetadataItem, selectedRecords } =
- useHeadlessCommandContextApi();
-
- const recordId = selectedRecords[0]?.id;
-
- if (
- !isDefined(recordId) ||
- !isDefined(recordIndexId) ||
- !isDefined(objectMetadataItem)
- ) {
- throw new Error(
- 'Record ID, record index ID, and object metadata are required to destroy single record',
- );
- }
-
- const navigateApp = useNavigateApp();
-
- const { resetTableRowSelection } = useResetTableRowSelection(recordIndexId);
-
- const { removeSelectedRecordsFromRecordBoard } =
- useRemoveSelectedRecordsFromRecordBoard(recordIndexId);
-
- const { destroyOneRecord } = useDestroyOneRecord({
- objectNameSingular: objectMetadataItem.nameSingular,
- });
-
- const handleExecute = async () => {
- removeSelectedRecordsFromRecordBoard();
- resetTableRowSelection();
-
- await destroyOneRecord(recordId);
- navigateApp(AppPath.RecordIndexPage, {
- objectNamePlural: objectMetadataItem.namePlural,
- });
- };
-
- return (
-
- );
-};
diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/ExportSingleRecordCommand.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/ExportSingleRecordCommand.tsx
deleted file mode 100644
index 6b3d34cb03..0000000000
--- a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/ExportSingleRecordCommand.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { HeadlessEngineCommandWrapperEffect } from '@/command-menu-item/engine-command/components/HeadlessEngineCommandWrapperEffect';
-import { useHeadlessCommandContextApi } from '@/command-menu-item/engine-command/hooks/useHeadlessCommandContextApi';
-import { useExportSingleRecord } from '@/object-record/record-show/hooks/useExportSingleRecord';
-import { isDefined } from 'twenty-shared/utils';
-
-export const ExportSingleRecordCommand = () => {
- const { objectMetadataItem, currentViewId, selectedRecords } =
- useHeadlessCommandContextApi();
-
- const recordId = selectedRecords[0]?.id;
-
- if (
- !isDefined(currentViewId) ||
- !isDefined(recordId) ||
- !isDefined(objectMetadataItem)
- ) {
- throw new Error(
- 'Current view ID, record ID, and object metadata are required to export single record',
- );
- }
-
- const filename = `${objectMetadataItem.nameSingular}.csv`;
- const { download } = useExportSingleRecord({
- filename,
- objectMetadataItem,
- recordId,
- });
-
- return ;
-};
diff --git a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/RestoreSingleRecordCommand.tsx b/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/RestoreSingleRecordCommand.tsx
deleted file mode 100644
index a4fcd70878..0000000000
--- a/packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/RestoreSingleRecordCommand.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { HeadlessConfirmationModalEngineCommandEffect } from '@/command-menu-item/engine-command/components/HeadlessConfirmationModalEngineCommandEffect';
-import { useHeadlessCommandContextApi } from '@/command-menu-item/engine-command/hooks/useHeadlessCommandContextApi';
-import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
-import { useRemoveSelectedRecordsFromRecordBoard } from '@/object-record/record-board/hooks/useRemoveSelectedRecordsFromRecordBoard';
-import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
-import { t } from '@lingui/core/macro';
-import { isDefined } from 'twenty-shared/utils';
-
-export const RestoreSingleRecordCommand = () => {
- const { recordIndexId, objectMetadataItem, selectedRecords } =
- useHeadlessCommandContextApi();
-
- const recordId = selectedRecords[0]?.id;
-
- if (
- !isDefined(recordId) ||
- !isDefined(recordIndexId) ||
- !isDefined(objectMetadataItem)
- ) {
- throw new Error(
- 'Record ID, record index ID, and object metadata are required to restore single record',
- );
- }
-
- const { resetTableRowSelection } = useResetTableRowSelection(recordIndexId);
- const { removeSelectedRecordsFromRecordBoard } =
- useRemoveSelectedRecordsFromRecordBoard(recordIndexId);
-
- const { restoreManyRecords } = useRestoreManyRecords({
- objectNameSingular: objectMetadataItem.nameSingular,
- });
-
- const handleExecute = async () => {
- removeSelectedRecordsFromRecordBoard();
- resetTableRowSelection();
-
- await restoreManyRecords({
- idsToRestore: [recordId],
- });
- };
-
- return (
-
- );
-};
diff --git a/packages/twenty-front/src/modules/command-menu-item/record/types/RecordCommandKeys.ts b/packages/twenty-front/src/modules/command-menu-item/record/types/RecordCommandKeys.ts
new file mode 100644
index 0000000000..efcfcff71b
--- /dev/null
+++ b/packages/twenty-front/src/modules/command-menu-item/record/types/RecordCommandKeys.ts
@@ -0,0 +1,6 @@
+export enum RecordCommandKeys {
+ DELETE = 'delete-records',
+ RESTORE = 'restore-records',
+ DESTROY = 'destroy-records',
+ EXPORT = 'export-records',
+}
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-deduplicate-engine-commands.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-deduplicate-engine-commands.command.ts
new file mode 100644
index 0000000000..28fde94772
--- /dev/null
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-deduplicate-engine-commands.command.ts
@@ -0,0 +1,156 @@
+import { InjectRepository } from '@nestjs/typeorm';
+
+import { Command } from 'nest-commander';
+import { isDefined } from 'twenty-shared/utils';
+import { Repository } from 'typeorm';
+
+import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
+import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
+import { ApplicationService } from 'src/engine/core-modules/application/application.service';
+import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
+import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
+import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
+import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
+import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant';
+import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
+
+const OLD_UNIVERSAL_IDENTIFIERS_TO_DELETE = new Set([
+ '6652773f-b9a9-4fa3-a52c-e2f2e259e430', // deleteSingleRecord
+ 'cde86f1f-2c13-42b1-812b-f2b2b468cb83', // deleteMultipleRecords
+ '8b3a1cae-3e4d-43c1-a71f-48592b2e47ff', // restoreSingleRecord
+ '8b740c9d-d99a-45a8-812f-809caaf420ac', // restoreMultipleRecords
+ '44a78417-c394-4bc8-961f-98b503030ddb', // destroySingleRecord
+ 'c630b3fb-7920-40d1-9906-77d0aa797608', // destroyMultipleRecords
+ 'a934ba8a-ac8f-487d-9cd9-06dfdaec1f49', // exportFromRecordIndex
+ 'ba339455-f3c2-4ed1-bf77-3e316d7d6a66', // exportFromRecordShow
+ 'f71f68e5-7b6e-4c03-8161-c48434d7777c', // exportMultipleRecords
+]);
+
+const NEW_UNIVERSAL_IDENTIFIERS = new Set([
+ 'd5a55d57-ed1d-4791-89b8-53b7e121d69d', // deleteRecords
+ '2d733846-8cc5-4314-ab79-916ae0801baa', // restoreRecords
+ '0ea2ebc4-02ca-4d15-b424-5352b9e487df', // destroyRecords
+ 'c6f5c54d-d52b-4e75-8188-2190d77126f2', // exportRecords
+]);
+
+@Command({
+ name: 'upgrade:1-21:deduplicate-engine-commands',
+ description:
+ 'Merge single/multiple record engine command menu items into unified commands (delete, restore, destroy, export)',
+})
+export class DeduplicateEngineCommandsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
+ constructor(
+ @InjectRepository(WorkspaceEntity)
+ protected readonly workspaceRepository: Repository,
+ protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
+ protected readonly dataSourceService: DataSourceService,
+ private readonly applicationService: ApplicationService,
+ private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
+ private readonly workspaceCacheService: WorkspaceCacheService,
+ ) {
+ super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
+ }
+
+ override async runOnWorkspace({
+ workspaceId,
+ options,
+ }: RunOnWorkspaceArgs): Promise {
+ const isDryRun = options.dryRun ?? false;
+
+ this.logger.log(
+ `${isDryRun ? '[DRY RUN] ' : ''}Starting deduplication of engine commands for workspace ${workspaceId}`,
+ );
+
+ const { twentyStandardFlatApplication } =
+ await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
+ { workspaceId },
+ );
+
+ const { flatCommandMenuItemMaps: existingFlatCommandMenuItemMaps } =
+ await this.workspaceCacheService.getOrRecompute(workspaceId, [
+ 'flatCommandMenuItemMaps',
+ ]);
+
+ const itemsToDelete = Object.values(
+ existingFlatCommandMenuItemMaps.byUniversalIdentifier,
+ )
+ .filter(isDefined)
+ .filter((item) =>
+ OLD_UNIVERSAL_IDENTIFIERS_TO_DELETE.has(item.universalIdentifier),
+ );
+
+ const { allFlatEntityMaps: standardAllFlatEntityMaps } =
+ computeTwentyStandardApplicationAllFlatEntityMaps({
+ shouldIncludeRecordPageLayouts: true,
+ now: new Date().toISOString(),
+ workspaceId,
+ twentyStandardApplicationId: twentyStandardFlatApplication.id,
+ });
+
+ const itemsToCreate = Object.values(
+ standardAllFlatEntityMaps.flatCommandMenuItemMaps.byUniversalIdentifier,
+ )
+ .filter(isDefined)
+ .filter((item) => NEW_UNIVERSAL_IDENTIFIERS.has(item.universalIdentifier))
+ .filter(
+ (item) =>
+ !isDefined(
+ existingFlatCommandMenuItemMaps.byUniversalIdentifier[
+ item.universalIdentifier
+ ],
+ ),
+ );
+
+ const totalChanges = itemsToDelete.length + itemsToCreate.length;
+
+ if (totalChanges === 0) {
+ this.logger.log(
+ `No engine command deduplication needed for workspace ${workspaceId}`,
+ );
+
+ return;
+ }
+
+ this.logger.log(
+ `Found ${itemsToDelete.length} old command menu item(s) to delete and ${itemsToCreate.length} unified item(s) to create for workspace ${workspaceId}`,
+ );
+
+ if (isDryRun) {
+ this.logger.log(
+ `[DRY RUN] Would delete ${itemsToDelete.length} and create ${itemsToCreate.length} command menu item(s) for workspace ${workspaceId}`,
+ );
+
+ return;
+ }
+
+ const validateAndBuildResult =
+ await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
+ {
+ allFlatEntityOperationByMetadataName: {
+ commandMenuItem: {
+ flatEntityToCreate: itemsToCreate,
+ flatEntityToDelete: itemsToDelete,
+ flatEntityToUpdate: [],
+ },
+ },
+ workspaceId,
+ applicationUniversalIdentifier:
+ twentyStandardFlatApplication.universalIdentifier,
+ },
+ );
+
+ if (validateAndBuildResult.status === 'fail') {
+ this.logger.error(
+ `Failed to deduplicate engine commands:\n${JSON.stringify(validateAndBuildResult, null, 2)}`,
+ );
+
+ throw new Error(
+ `Failed to deduplicate engine commands for workspace ${workspaceId}`,
+ );
+ }
+
+ this.logger.log(
+ `Successfully deduplicated engine commands for workspace ${workspaceId} (deleted ${itemsToDelete.length}, created ${itemsToCreate.length})`,
+ );
+ }
+}
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-upgrade-version-command.module.ts
index e20f3f6e8d..33c2ad1ba2 100644
--- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-upgrade-version-command.module.ts
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-upgrade-version-command.module.ts
@@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { BackfillDatasourceToWorkspaceCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-datasource-to-workspace.command';
import { BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-page-layouts-and-fields-widget-view-fields.command';
+import { DeduplicateEngineCommandsCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-deduplicate-engine-commands.command';
import { MigrateAiAgentTextToJsonResponseFormatCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-migrate-ai-agent-text-to-json-response-format.command';
import { UpdateEditLayoutCommandMenuItemLabelCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-update-edit-layout-command-menu-item-label.command';
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
@@ -25,12 +26,14 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
providers: [
BackfillDatasourceToWorkspaceCommand,
BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
+ DeduplicateEngineCommandsCommand,
MigrateAiAgentTextToJsonResponseFormatCommand,
UpdateEditLayoutCommandMenuItemLabelCommand,
],
exports: [
BackfillDatasourceToWorkspaceCommand,
BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
+ DeduplicateEngineCommandsCommand,
MigrateAiAgentTextToJsonResponseFormatCommand,
UpdateEditLayoutCommandMenuItemLabelCommand,
],
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts
index 12f1a45a48..154b64b80a 100644
--- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts
@@ -27,6 +27,7 @@ import { SeedCliApplicationRegistrationCommand } from 'src/database/commands/upg
import { UpdateStandardIndexViewNamesCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-update-standard-index-view-names.command';
import { BackfillDatasourceToWorkspaceCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-datasource-to-workspace.command';
import { BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-page-layouts-and-fields-widget-view-fields.command';
+import { DeduplicateEngineCommandsCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-deduplicate-engine-commands.command';
import { MigrateAiAgentTextToJsonResponseFormatCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-migrate-ai-agent-text-to-json-response-format.command';
import { UpdateEditLayoutCommandMenuItemLabelCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-update-edit-layout-command-menu-item-label.command';
import { CoreEngineVersionService } from 'src/engine/core-engine-version/services/core-engine-version.service';
@@ -73,6 +74,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
// 1.21 Commands
protected readonly backfillDatasourceToWorkspaceCommand: BackfillDatasourceToWorkspaceCommand,
protected readonly backfillPageLayoutsAndFieldsWidgetViewFieldsCommand: BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
+ protected readonly deduplicateEngineCommandsCommand: DeduplicateEngineCommandsCommand,
protected readonly migrateAiAgentTextToJsonResponseFormatCommand: MigrateAiAgentTextToJsonResponseFormatCommand,
protected readonly updateEditLayoutCommandMenuItemLabelCommand: UpdateEditLayoutCommandMenuItemLabelCommand,
) {
@@ -110,6 +112,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
const commands_1210: VersionCommands = [
this.backfillDatasourceToWorkspaceCommand,
this.backfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
+ this.deduplicateEngineCommandsCommand,
this.migrateAiAgentTextToJsonResponseFormatCommand,
this.updateEditLayoutCommandMenuItemLabelCommand,
];
diff --git a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum.ts b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum.ts
index 6509fd1fd6..6b751bf568 100644
--- a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum.ts
@@ -4,20 +4,15 @@ export enum EngineComponentKey {
NAVIGATE_TO_NEXT_RECORD = 'NAVIGATE_TO_NEXT_RECORD',
NAVIGATE_TO_PREVIOUS_RECORD = 'NAVIGATE_TO_PREVIOUS_RECORD',
CREATE_NEW_RECORD = 'CREATE_NEW_RECORD',
- DELETE_SINGLE_RECORD = 'DELETE_SINGLE_RECORD',
- DELETE_MULTIPLE_RECORDS = 'DELETE_MULTIPLE_RECORDS',
- RESTORE_SINGLE_RECORD = 'RESTORE_SINGLE_RECORD',
- RESTORE_MULTIPLE_RECORDS = 'RESTORE_MULTIPLE_RECORDS',
- DESTROY_SINGLE_RECORD = 'DESTROY_SINGLE_RECORD',
- DESTROY_MULTIPLE_RECORDS = 'DESTROY_MULTIPLE_RECORDS',
+ DELETE_RECORDS = 'DELETE_RECORDS',
+ RESTORE_RECORDS = 'RESTORE_RECORDS',
+ DESTROY_RECORDS = 'DESTROY_RECORDS',
ADD_TO_FAVORITES = 'ADD_TO_FAVORITES',
REMOVE_FROM_FAVORITES = 'REMOVE_FROM_FAVORITES',
EXPORT_NOTE_TO_PDF = 'EXPORT_NOTE_TO_PDF',
- EXPORT_FROM_RECORD_INDEX = 'EXPORT_FROM_RECORD_INDEX',
- EXPORT_FROM_RECORD_SHOW = 'EXPORT_FROM_RECORD_SHOW',
+ EXPORT_RECORDS = 'EXPORT_RECORDS',
UPDATE_MULTIPLE_RECORDS = 'UPDATE_MULTIPLE_RECORDS',
MERGE_MULTIPLE_RECORDS = 'MERGE_MULTIPLE_RECORDS',
- EXPORT_MULTIPLE_RECORDS = 'EXPORT_MULTIPLE_RECORDS',
IMPORT_RECORDS = 'IMPORT_RECORDS',
EXPORT_VIEW = 'EXPORT_VIEW',
SEE_DELETED_RECORDS = 'SEE_DELETED_RECORDS',
@@ -60,6 +55,17 @@ export enum EngineComponentKey {
VIEW_PREVIOUS_AI_CHATS = 'VIEW_PREVIOUS_AI_CHATS',
TRIGGER_WORKFLOW_VERSION = 'TRIGGER_WORKFLOW_VERSION',
FRONT_COMPONENT_RENDERER = 'FRONT_COMPONENT_RENDERER',
+
+ // Deprecated keys kept for backward compatibility until migration runs
+ DELETE_SINGLE_RECORD = 'DELETE_SINGLE_RECORD',
+ DELETE_MULTIPLE_RECORDS = 'DELETE_MULTIPLE_RECORDS',
+ RESTORE_SINGLE_RECORD = 'RESTORE_SINGLE_RECORD',
+ RESTORE_MULTIPLE_RECORDS = 'RESTORE_MULTIPLE_RECORDS',
+ DESTROY_SINGLE_RECORD = 'DESTROY_SINGLE_RECORD',
+ DESTROY_MULTIPLE_RECORDS = 'DESTROY_MULTIPLE_RECORDS',
+ EXPORT_FROM_RECORD_INDEX = 'EXPORT_FROM_RECORD_INDEX',
+ EXPORT_FROM_RECORD_SHOW = 'EXPORT_FROM_RECORD_SHOW',
+ EXPORT_MULTIPLE_RECORDS = 'EXPORT_MULTIPLE_RECORDS',
}
registerEnumType(EngineComponentKey, {
diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-command-menu-item.constant.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-command-menu-item.constant.ts
index 91b5725b68..ab8f06bd35 100644
--- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-command-menu-item.constant.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-command-menu-item.constant.ts
@@ -50,95 +50,49 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
engineComponentKey: EngineComponentKey.CREATE_NEW_RECORD,
hotKeys: null,
},
- deleteSingleRecord: {
- universalIdentifier: '6652773f-b9a9-4fa3-a52c-e2f2e259e430',
- label: 'Delete',
+ deleteRecords: {
+ universalIdentifier: 'd5a55d57-ed1d-4791-89b8-53b7e121d69d',
+ label: 'Delete ${capitalize(objectMetadataLabel)}',
icon: 'IconTrash',
isPinned: false,
position: 3,
shortLabel: 'Delete',
availabilityType: CommandMenuItemAvailabilityType.RECORD_SELECTION,
conditionalAvailabilityExpression:
- 'numberOfSelectedRecords == 1 and not hasAnySoftDeleteFilterOnView and objectPermissions.canSoftDeleteObjectRecords and noneDefined(selectedRecords, "deletedAt")',
+ 'numberOfSelectedRecords >= 1 and not hasAnySoftDeleteFilterOnView and objectPermissions.canSoftDeleteObjectRecords and noneDefined(selectedRecords, "deletedAt") and numberOfSelectedRecords < 10000',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier: null,
- engineComponentKey: EngineComponentKey.DELETE_SINGLE_RECORD,
+ engineComponentKey: EngineComponentKey.DELETE_RECORDS,
hotKeys: null,
},
- deleteMultipleRecords: {
- universalIdentifier: 'cde86f1f-2c13-42b1-812b-f2b2b468cb83',
- label: 'Delete ${capitalize(objectMetadataItem.labelPlural)}',
- icon: 'IconTrash',
- isPinned: false,
- position: 4,
- shortLabel: 'Delete',
- availabilityType: CommandMenuItemAvailabilityType.RECORD_SELECTION,
- conditionalAvailabilityExpression:
- 'numberOfSelectedRecords >= 2 and objectPermissions.canSoftDeleteObjectRecords and not hasAnySoftDeleteFilterOnView and numberOfSelectedRecords < 10000',
- availabilityObjectMetadataUniversalIdentifier: null,
- frontComponentUniversalIdentifier: null,
- engineComponentKey: EngineComponentKey.DELETE_MULTIPLE_RECORDS,
- hotKeys: null,
- },
- restoreSingleRecord: {
- universalIdentifier: '8b3a1cae-3e4d-43c1-a71f-48592b2e47ff',
- label: 'Restore ${capitalize(objectMetadataItem.labelSingular)}',
+ restoreRecords: {
+ universalIdentifier: '2d733846-8cc5-4314-ab79-916ae0801baa',
+ label: 'Restore ${capitalize(objectMetadataLabel)}',
icon: 'IconRefresh',
isPinned: true,
position: 5,
shortLabel: 'Restore',
availabilityType: CommandMenuItemAvailabilityType.RECORD_SELECTION,
conditionalAvailabilityExpression:
- 'numberOfSelectedRecords == 1 and everyDefined(selectedRecords, "deletedAt") and objectPermissions.canSoftDeleteObjectRecords and (pageType == "RECORD_PAGE" or hasAnySoftDeleteFilterOnView)',
+ 'numberOfSelectedRecords >= 1 and everyDefined(selectedRecords, "deletedAt") and objectPermissions.canSoftDeleteObjectRecords and (pageType == "RECORD_PAGE" or hasAnySoftDeleteFilterOnView) and numberOfSelectedRecords < 10000',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier: null,
- engineComponentKey: EngineComponentKey.RESTORE_SINGLE_RECORD,
+ engineComponentKey: EngineComponentKey.RESTORE_RECORDS,
hotKeys: null,
},
- restoreMultipleRecords: {
- universalIdentifier: '8b740c9d-d99a-45a8-812f-809caaf420ac',
- label: 'Restore ${capitalize(objectMetadataItem.labelPlural)}',
- icon: 'IconRefresh',
- isPinned: true,
- position: 6,
- shortLabel: 'Restore',
- availabilityType: CommandMenuItemAvailabilityType.RECORD_SELECTION,
- conditionalAvailabilityExpression:
- 'numberOfSelectedRecords >= 2 and objectPermissions.canSoftDeleteObjectRecords and hasAnySoftDeleteFilterOnView and numberOfSelectedRecords < 10000',
- availabilityObjectMetadataUniversalIdentifier: null,
- frontComponentUniversalIdentifier: null,
- engineComponentKey: EngineComponentKey.RESTORE_MULTIPLE_RECORDS,
- hotKeys: null,
- },
- destroySingleRecord: {
- universalIdentifier: '44a78417-c394-4bc8-961f-98b503030ddb',
- label:
- 'Permanently destroy ${capitalize(objectMetadataItem.labelSingular)}',
+ destroyRecords: {
+ universalIdentifier: '0ea2ebc4-02ca-4d15-b424-5352b9e487df',
+ label: 'Permanently destroy ${capitalize(objectMetadataLabel)}',
icon: 'IconTrashX',
isPinned: false,
position: 7,
shortLabel: 'Destroy',
availabilityType: CommandMenuItemAvailabilityType.RECORD_SELECTION,
conditionalAvailabilityExpression:
- 'numberOfSelectedRecords == 1 and objectPermissions.canDestroyObjectRecords and everyDefined(selectedRecords, "deletedAt")',
+ 'numberOfSelectedRecords >= 1 and objectPermissions.canDestroyObjectRecords and everyDefined(selectedRecords, "deletedAt") and numberOfSelectedRecords < 10000',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier: null,
- engineComponentKey: EngineComponentKey.DESTROY_SINGLE_RECORD,
- hotKeys: null,
- },
- destroyMultipleRecords: {
- universalIdentifier: 'c630b3fb-7920-40d1-9906-77d0aa797608',
- label: 'Permanently destroy ${capitalize(objectMetadataItem.labelPlural)}',
- icon: 'IconTrashX',
- isPinned: false,
- position: 8,
- shortLabel: 'Destroy',
- availabilityType: CommandMenuItemAvailabilityType.RECORD_SELECTION,
- conditionalAvailabilityExpression:
- 'numberOfSelectedRecords >= 2 and objectPermissions.canDestroyObjectRecords and hasAnySoftDeleteFilterOnView and numberOfSelectedRecords < 10000',
- availabilityObjectMetadataUniversalIdentifier: null,
- frontComponentUniversalIdentifier: null,
- engineComponentKey: EngineComponentKey.DESTROY_MULTIPLE_RECORDS,
+ engineComponentKey: EngineComponentKey.DESTROY_RECORDS,
hotKeys: null,
},
addToFavorites: {
@@ -186,32 +140,18 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
engineComponentKey: EngineComponentKey.EXPORT_NOTE_TO_PDF,
hotKeys: null,
},
- exportFromRecordIndex: {
- universalIdentifier: 'a934ba8a-ac8f-487d-9cd9-06dfdaec1f49',
- label: 'Export',
+ exportRecords: {
+ universalIdentifier: 'c6f5c54d-d52b-4e75-8188-2190d77126f2',
+ label: 'Export ${capitalize(objectMetadataLabel)}',
icon: 'IconFileExport',
isPinned: false,
position: 12,
shortLabel: 'Export',
availabilityType: CommandMenuItemAvailabilityType.RECORD_SELECTION,
- conditionalAvailabilityExpression: 'pageType == "INDEX_PAGE"',
+ conditionalAvailabilityExpression: null,
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier: null,
- engineComponentKey: EngineComponentKey.EXPORT_FROM_RECORD_INDEX,
- hotKeys: null,
- },
- exportFromRecordShow: {
- universalIdentifier: 'ba339455-f3c2-4ed1-bf77-3e316d7d6a66',
- label: 'Export',
- icon: 'IconFileExport',
- isPinned: false,
- position: 13,
- shortLabel: 'Export',
- availabilityType: CommandMenuItemAvailabilityType.RECORD_SELECTION,
- conditionalAvailabilityExpression: 'pageType == "RECORD_PAGE"',
- availabilityObjectMetadataUniversalIdentifier: null,
- frontComponentUniversalIdentifier: null,
- engineComponentKey: EngineComponentKey.EXPORT_FROM_RECORD_SHOW,
+ engineComponentKey: EngineComponentKey.EXPORT_RECORDS,
hotKeys: null,
},
updateMultipleRecords: {
@@ -244,20 +184,6 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
engineComponentKey: EngineComponentKey.MERGE_MULTIPLE_RECORDS,
hotKeys: null,
},
- exportMultipleRecords: {
- universalIdentifier: 'f71f68e5-7b6e-4c03-8161-c48434d7777c',
- label: 'Export ${capitalize(objectMetadataItem.labelPlural)}',
- icon: 'IconFileExport',
- isPinned: false,
- position: 16,
- shortLabel: 'Export',
- availabilityType: CommandMenuItemAvailabilityType.RECORD_SELECTION,
- conditionalAvailabilityExpression: 'numberOfSelectedRecords >= 2',
- availabilityObjectMetadataUniversalIdentifier: null,
- frontComponentUniversalIdentifier: null,
- engineComponentKey: EngineComponentKey.EXPORT_MULTIPLE_RECORDS,
- hotKeys: null,
- },
importRecords: {
universalIdentifier: 'a2dc9de7-4798-422e-bb55-bfad7b9bdbe8',
label: 'Import ${capitalize(objectMetadataItem.labelPlural)}',