Command to deduplicate command menu items (#19202)

- Deduplicate standard record command menu items by merging
single-record and multi-record delete, restore, destroy, and export
actions into shared record commands.
- Update frontend command registrations and engine component mappings to
use the new unified keys, while keeping deprecated engine keys
temporarily mapped for backward compatibility.
- Add a 1-21 upgrade command that removes legacy command menu items from
workspaces and creates the new unified standard items.
This commit is contained in:
Raphaël Bosi
2026-04-02 11:51:15 +02:00
committed by GitHub
parent 16033e9f99
commit f688db30a9
18 changed files with 388 additions and 384 deletions
@@ -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)}',