[COMMAND MENU ITEMS] Add engine component key (#18554)

## PR Description

In the process of migrating all the existing commands to the backend, we
stumbled across a couple of problems that made us reconsider the full
migration. This PR introduces a way for command menu items to bypass
front components and to directly reference a frontend component from
twenty front.

It:
- Introduces a `engineFrontComponentKey` field on `CommandMenuItem` as
an alternative to `frontComponentId` and `workflowVersionId`, allowing
command menu items to reference frontend components by key directly
rather than requiring a FrontComponent entity
- Updates the DB constraint to allow exactly one of `workflowVersionId`,
`frontComponentId`, or `engineFrontComponentKey`

### All standard command menu items from the frontend which use
`standardFrontComponentKey`

These are all commands that execute a GraphQL query or a mutation.
Two mains concerned have been raised that made us go with this
(temporary) architecture instead:
- If those commands are part of the standard application, they can only
alter objects from that application and not custom objects.
- We would need to implement a way to trigger optimistic rendering from
the front components, which might take some time to implement.

List:
- Create new record
- Delete (single record)
- Delete records (multiple)
- Restore record
- Restore records (multiple)
- Permanently destroy record
- Permanently destroy records (multiple)
- Add to favorites
- Remove from favorites
- Merge records
- Duplicate Dashboard
- Save Dashboard
- Save Page Layout
- Activate Workflow
- Deactivate Workflow
- Discard Draft (workflow)
- Test Workflow
- Tidy up workflow
- Duplicate Workflow
- Stop (workflow run)
- Use as draft (workflow version)

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Raphaël Bosi
2026-03-12 13:14:45 +01:00
committed by GitHub
parent 78473a606a
commit e8f8189167
55 changed files with 857 additions and 1152 deletions
File diff suppressed because one or more lines are too long
@@ -0,0 +1,77 @@
import { DeleteMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/DeleteMultipleRecordsCommand';
import { DestroyMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/DestroyMultipleRecordsCommand';
import { MergeMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/MergeMultipleRecordsCommand';
import { RestoreMultipleRecordsCommand } from '@/command-menu-item/record/multiple-records/components/RestoreMultipleRecordsCommand';
import { CreateNewIndexRecordNoSelectionRecordCommand } from '@/command-menu-item/record/no-selection/components/CreateNewIndexRecordNoSelectionRecordCommand';
import { AddToFavoritesSingleRecordCommand } from '@/command-menu-item/record/single-record/components/AddToFavoritesSingleRecordCommand';
import { DeleteSingleRecordCommand } from '@/command-menu-item/record/single-record/components/DeleteSingleRecordCommand';
import { DestroySingleRecordCommand } from '@/command-menu-item/record/single-record/components/DestroySingleRecordCommand';
import { RemoveFromFavoritesSingleRecordCommand } from '@/command-menu-item/record/single-record/components/RemoveFromFavoritesSingleRecordCommand';
import { RestoreSingleRecordCommand } from '@/command-menu-item/record/single-record/components/RestoreSingleRecordCommand';
import { DuplicateDashboardSingleRecordCommand } from '@/command-menu-item/record/single-record/dashboard/components/DuplicateDashboardSingleRecordCommand';
import { SaveDashboardSingleRecordCommand } from '@/command-menu-item/record/single-record/dashboard/components/SaveDashboardSingleRecordCommand';
import { SaveRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/SaveRecordPageLayoutSingleRecordCommand';
import { ActivateWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/ActivateWorkflowSingleRecordCommand';
import { DeactivateWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/DeactivateWorkflowSingleRecordCommand';
import { DiscardDraftWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/DiscardDraftWorkflowSingleRecordCommand';
import { DuplicateWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/DuplicateWorkflowSingleRecordCommand';
import { TestWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/TestWorkflowSingleRecordCommand';
import { TidyUpWorkflowSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow/components/TidyUpWorkflowSingleRecordCommand';
import { StopWorkflowRunSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-runs/components/StopWorkflowRunSingleRecordCommand';
import { UseAsDraftWorkflowVersionSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-versions/components/UseAsDraftWorkflowVersionSingleRecordCommand';
import { EngineComponentKey } from '~/generated-metadata/graphql';
export const ENGINE_COMPONENT_KEY_COMPONENT_MAP: Record<
EngineComponentKey,
React.ReactNode
> = {
[EngineComponentKey.CREATE_NEW_RECORD]: (
<CreateNewIndexRecordNoSelectionRecordCommand />
),
[EngineComponentKey.DELETE_SINGLE_RECORD]: <DeleteSingleRecordCommand />,
[EngineComponentKey.DELETE_MULTIPLE_RECORDS]: (
<DeleteMultipleRecordsCommand />
),
[EngineComponentKey.RESTORE_SINGLE_RECORD]: <RestoreSingleRecordCommand />,
[EngineComponentKey.RESTORE_MULTIPLE_RECORDS]: (
<RestoreMultipleRecordsCommand />
),
[EngineComponentKey.DESTROY_SINGLE_RECORD]: <DestroySingleRecordCommand />,
[EngineComponentKey.DESTROY_MULTIPLE_RECORDS]: (
<DestroyMultipleRecordsCommand />
),
[EngineComponentKey.ADD_TO_FAVORITES]: <AddToFavoritesSingleRecordCommand />,
[EngineComponentKey.REMOVE_FROM_FAVORITES]: (
<RemoveFromFavoritesSingleRecordCommand />
),
[EngineComponentKey.MERGE_MULTIPLE_RECORDS]: <MergeMultipleRecordsCommand />,
[EngineComponentKey.DUPLICATE_DASHBOARD]: (
<DuplicateDashboardSingleRecordCommand />
),
[EngineComponentKey.DUPLICATE_WORKFLOW]: (
<DuplicateWorkflowSingleRecordCommand />
),
[EngineComponentKey.ACTIVATE_WORKFLOW]: (
<ActivateWorkflowSingleRecordCommand />
),
[EngineComponentKey.DEACTIVATE_WORKFLOW]: (
<DeactivateWorkflowSingleRecordCommand />
),
[EngineComponentKey.DISCARD_DRAFT_WORKFLOW]: (
<DiscardDraftWorkflowSingleRecordCommand />
),
[EngineComponentKey.TEST_WORKFLOW]: <TestWorkflowSingleRecordCommand />,
[EngineComponentKey.STOP_WORKFLOW_RUN]: (
<StopWorkflowRunSingleRecordCommand />
),
[EngineComponentKey.USE_AS_DRAFT_WORKFLOW_VERSION]: (
<UseAsDraftWorkflowVersionSingleRecordCommand />
),
[EngineComponentKey.SAVE_RECORD_PAGE_LAYOUT]: (
<SaveRecordPageLayoutSingleRecordCommand />
),
[EngineComponentKey.SAVE_DASHBOARD_LAYOUT]: (
<SaveDashboardSingleRecordCommand />
),
[EngineComponentKey.TIDY_UP_WORKFLOW]: <TidyUpWorkflowSingleRecordCommand />,
};
@@ -10,6 +10,7 @@ export const COMMAND_MENU_ITEM_FRAGMENT = gql`
name
isHeadless
}
engineComponentKey
label
icon
shortLabel
@@ -1,3 +1,4 @@
import { ENGINE_COMPONENT_KEY_COMPONENT_MAP } from '@/command-menu-item/constants/EngineComponentKeyComponentMap';
import { Command } from '@/command-menu-item/display/components/Command';
import { HeadlessFrontComponentCommandMenuItem } from '@/command-menu-item/display/components/HeadlessFrontComponentCommandMenuItem';
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
@@ -22,6 +23,7 @@ import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants
import {
CommandMenuItemAvailabilityType,
type CommandMenuItemFieldsFragment,
type EngineComponentKey,
useFindManyCommandMenuItemsQuery,
} from '~/generated-metadata/graphql';
@@ -30,6 +32,10 @@ type CommandMenuItemWithFrontComponent = CommandMenuItemFieldsFragment & {
conditionalAvailabilityExpression?: string | null;
};
type CommandMenuItemWithSource = CommandMenuItemFieldsFragment & {
conditionalAvailabilityExpression?: string | null;
};
type BuildCommandMenuItemFromFrontComponentParams = {
item: CommandMenuItemWithFrontComponent;
type?: CommandMenuItemType;
@@ -53,8 +59,6 @@ type BuildCommandMenuItemFromFrontComponentParams = {
commandMenuContextApi: CommandMenuContextApi;
};
// TODO: we should remove this backward compatibility logic in the future
// once we have migrated all command menu items
const buildCommandMenuItemFromFrontComponent = ({
item,
type = CommandMenuItemType.FrontComponent,
@@ -115,6 +119,47 @@ const buildCommandMenuItemFromFrontComponent = ({
};
};
type BuildCommandMenuItemFromStandardKeyParams = {
item: CommandMenuItemWithSource;
engineComponentKey: EngineComponentKey;
type?: CommandMenuItemType;
scope: CommandMenuItemScope;
isPinned: boolean;
getIcon: ReturnType<typeof useIcons>['getIcon'];
commandMenuContextApi: CommandMenuContextApi;
};
const buildCommandItemFromEngineKey = ({
item,
engineComponentKey,
type = CommandMenuItemType.Standard,
scope,
isPinned,
getIcon,
commandMenuContextApi,
}: BuildCommandMenuItemFromStandardKeyParams) => {
const Icon = getIcon(item.icon, COMMAND_MENU_DEFAULT_ICON);
const component = ENGINE_COMPONENT_KEY_COMPONENT_MAP[engineComponentKey];
return {
type,
key: `command-menu-item-engine-${item.id}`,
scope,
label: item.label,
shortLabel: item.shortLabel ?? undefined,
position: item.position,
isPinned,
Icon,
shouldBeRegistered: () =>
evaluateConditionalAvailabilityExpression(
item.conditionalAvailabilityExpression,
commandMenuContextApi,
),
component,
};
};
export const useCommandMenuItemFrontComponentCommands = (
commandMenuContextApi: CommandMenuContextApi,
) => {
@@ -159,74 +204,102 @@ export const useCommandMenuItemFrontComponentCommands = (
const { data } = useFindManyCommandMenuItemsQuery();
const frontComponentItems =
data?.commandMenuItems?.filter(
(item): item is CommandMenuItemWithFrontComponent =>
isDefined(item.frontComponentId),
) ?? [];
const allItems = data?.commandMenuItems ?? [];
const objectMatches = (item: CommandMenuItemWithFrontComponent) =>
const objectMatches = (item: CommandMenuItemFieldsFragment) =>
!isDefined(item.availabilityObjectMetadataId) ||
item.availabilityObjectMetadataId ===
contextStoreCurrentObjectMetadataItemId;
const frontComponentItemsWithObjectMatches =
frontComponentItems.filter(objectMatches);
const itemsWithObjectMatches = allItems.filter(objectMatches);
const globalItems = frontComponentItemsWithObjectMatches.filter(
const buildCommandMenuItem = ({
item,
scope,
isPinned,
typeOverride,
}: {
item: CommandMenuItemFieldsFragment;
scope: CommandMenuItemScope;
isPinned: boolean;
typeOverride?: CommandMenuItemType;
}) => {
if (isDefined(item.engineComponentKey)) {
return buildCommandItemFromEngineKey({
item,
engineComponentKey: item.engineComponentKey,
type: typeOverride,
scope,
isPinned,
getIcon,
commandMenuContextApi,
});
}
if (isDefined(item.frontComponentId)) {
return buildCommandMenuItemFromFrontComponent({
item: item as CommandMenuItemWithFrontComponent,
type: typeOverride,
scope,
isPinned,
getIcon,
openFrontComponentInSidePanel,
mountHeadlessFrontComponent,
commandMenuContextApi,
mountContext,
});
}
return null;
};
const globalItems = itemsWithObjectMatches.filter(
(item) => item.availabilityType === CommandMenuItemAvailabilityType.GLOBAL,
);
const recordScopedItems = frontComponentItemsWithObjectMatches.filter(
const recordScopedItems = itemsWithObjectMatches.filter(
(item) =>
item.availabilityType ===
CommandMenuItemAvailabilityType.RECORD_SELECTION,
);
const fallbackItems = frontComponentItemsWithObjectMatches.filter(
const fallbackItems = itemsWithObjectMatches.filter(
(item) =>
item.availabilityType === CommandMenuItemAvailabilityType.FALLBACK,
);
const globalCommandMenuItems = globalItems.map((item) =>
buildCommandMenuItemFromFrontComponent({
item,
scope: CommandMenuItemScope.Global,
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
getIcon,
openFrontComponentInSidePanel,
mountHeadlessFrontComponent,
commandMenuContextApi,
}),
);
const globalCommandMenuItems = globalItems
.map((item) =>
buildCommandMenuItem({
item,
scope: CommandMenuItemScope.Global,
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
}),
)
.filter(isDefined);
const recordScopedCommandMenuItems = hasRecordSelection
? recordScopedItems.map((item) =>
buildCommandMenuItemFromFrontComponent({
item,
scope: CommandMenuItemScope.RecordSelection,
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
getIcon,
openFrontComponentInSidePanel,
mountHeadlessFrontComponent,
commandMenuContextApi,
mountContext,
}),
)
? recordScopedItems
.map((item) =>
buildCommandMenuItem({
item,
scope: CommandMenuItemScope.RecordSelection,
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
}),
)
.filter(isDefined)
: [];
const fallbackCommandMenuItems = fallbackItems.map((item) =>
buildCommandMenuItemFromFrontComponent({
item,
type: CommandMenuItemType.Fallback,
scope: CommandMenuItemScope.Global,
isPinned: false,
getIcon,
openFrontComponentInSidePanel,
mountHeadlessFrontComponent,
commandMenuContextApi,
}),
);
const fallbackCommandMenuItems = fallbackItems
.map((item) =>
buildCommandMenuItem({
item,
scope: CommandMenuItemScope.Global,
isPinned: false,
typeOverride: CommandMenuItemType.Fallback,
}),
)
.filter(isDefined);
return [
...globalCommandMenuItems,
@@ -2037,6 +2037,7 @@ type CommandMenuItem {
workflowVersionId: UUID
frontComponentId: UUID
frontComponent: FrontComponent
engineComponentKey: EngineComponentKey
label: String!
icon: String
shortLabel: String
@@ -2050,6 +2051,30 @@ type CommandMenuItem {
updatedAt: DateTime!
}
enum EngineComponentKey {
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
MERGE_MULTIPLE_RECORDS
DUPLICATE_DASHBOARD
DUPLICATE_WORKFLOW
ACTIVATE_WORKFLOW
DEACTIVATE_WORKFLOW
DISCARD_DRAFT_WORKFLOW
TEST_WORKFLOW
STOP_WORKFLOW_RUN
USE_AS_DRAFT_WORKFLOW_VERSION
SAVE_RECORD_PAGE_LAYOUT
SAVE_DASHBOARD_LAYOUT
TIDY_UP_WORKFLOW
}
enum CommandMenuItemAvailabilityType {
GLOBAL
RECORD_SELECTION
@@ -3650,6 +3675,7 @@ input UpsertFieldsWidgetFieldInput {
input CreateCommandMenuItemInput {
workflowVersionId: UUID
frontComponentId: UUID
engineComponentKey: EngineComponentKey
label: String!
icon: String
shortLabel: String
@@ -3669,6 +3695,7 @@ input UpdateCommandMenuItemInput {
isPinned: Boolean
availabilityType: CommandMenuItemAvailabilityType
availabilityObjectMetadataId: UUID
engineComponentKey: EngineComponentKey
}
input CreateFrontComponentInput {
@@ -1771,6 +1771,7 @@ export interface CommandMenuItem {
workflowVersionId?: Scalars['UUID']
frontComponentId?: Scalars['UUID']
frontComponent?: FrontComponent
engineComponentKey?: EngineComponentKey
label: Scalars['String']
icon?: Scalars['String']
shortLabel?: Scalars['String']
@@ -1785,6 +1786,8 @@ export interface CommandMenuItem {
__typename: 'CommandMenuItem'
}
export type EngineComponentKey = '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' | 'MERGE_MULTIPLE_RECORDS' | 'DUPLICATE_DASHBOARD' | 'DUPLICATE_WORKFLOW' | 'ACTIVATE_WORKFLOW' | 'DEACTIVATE_WORKFLOW' | 'DISCARD_DRAFT_WORKFLOW' | 'TEST_WORKFLOW' | 'STOP_WORKFLOW_RUN' | 'USE_AS_DRAFT_WORKFLOW_VERSION' | 'SAVE_RECORD_PAGE_LAYOUT' | 'SAVE_DASHBOARD_LAYOUT' | 'TIDY_UP_WORKFLOW'
export type CommandMenuItemAvailabilityType = 'GLOBAL' | 'RECORD_SELECTION' | 'FALLBACK'
export interface AgentChatThread {
@@ -4697,6 +4700,7 @@ export interface CommandMenuItemGenqlSelection{
workflowVersionId?: boolean | number
frontComponentId?: boolean | number
frontComponent?: FrontComponentGenqlSelection
engineComponentKey?: boolean | number
label?: boolean | number
icon?: boolean | number
shortLabel?: boolean | number
@@ -5992,9 +5996,9 @@ export interface UpsertFieldsWidgetFieldInput {
/** The id of the view field */
viewFieldId: Scalars['UUID'],isVisible: Scalars['Boolean'],position: Scalars['Float']}
export interface CreateCommandMenuItemInput {workflowVersionId?: (Scalars['UUID'] | null),frontComponentId?: (Scalars['UUID'] | null),label: Scalars['String'],icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),conditionalAvailabilityExpression?: (Scalars['String'] | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null)}
export interface CreateCommandMenuItemInput {workflowVersionId?: (Scalars['UUID'] | null),frontComponentId?: (Scalars['UUID'] | null),engineComponentKey?: (EngineComponentKey | null),label: Scalars['String'],icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),conditionalAvailabilityExpression?: (Scalars['String'] | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null)}
export interface UpdateCommandMenuItemInput {id: Scalars['UUID'],label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null)}
export interface UpdateCommandMenuItemInput {id: Scalars['UUID'],label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null),engineComponentKey?: (EngineComponentKey | null)}
export interface CreateFrontComponentInput {id?: (Scalars['UUID'] | null),name: Scalars['String'],description?: (Scalars['String'] | null),sourceComponentPath: Scalars['String'],builtComponentPath: Scalars['String'],componentName: Scalars['String'],builtComponentChecksum: Scalars['String']}
@@ -8513,6 +8517,30 @@ export const enumLogicFunctionExecutionStatus = {
ERROR: 'ERROR' as const
}
export const enumEngineComponentKey = {
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,
ADD_TO_FAVORITES: 'ADD_TO_FAVORITES' as const,
REMOVE_FROM_FAVORITES: 'REMOVE_FROM_FAVORITES' as const,
MERGE_MULTIPLE_RECORDS: 'MERGE_MULTIPLE_RECORDS' as const,
DUPLICATE_DASHBOARD: 'DUPLICATE_DASHBOARD' as const,
DUPLICATE_WORKFLOW: 'DUPLICATE_WORKFLOW' as const,
ACTIVATE_WORKFLOW: 'ACTIVATE_WORKFLOW' as const,
DEACTIVATE_WORKFLOW: 'DEACTIVATE_WORKFLOW' as const,
DISCARD_DRAFT_WORKFLOW: 'DISCARD_DRAFT_WORKFLOW' as const,
TEST_WORKFLOW: 'TEST_WORKFLOW' as const,
STOP_WORKFLOW_RUN: 'STOP_WORKFLOW_RUN' as const,
USE_AS_DRAFT_WORKFLOW_VERSION: 'USE_AS_DRAFT_WORKFLOW_VERSION' as const,
SAVE_RECORD_PAGE_LAYOUT: 'SAVE_RECORD_PAGE_LAYOUT' as const,
SAVE_DASHBOARD_LAYOUT: 'SAVE_DASHBOARD_LAYOUT' as const,
TIDY_UP_WORKFLOW: 'TIDY_UP_WORKFLOW' as const
}
export const enumCommandMenuItemAvailabilityType = {
GLOBAL: 'GLOBAL' as const,
RECORD_SELECTION: 'RECORD_SELECTION' as const,
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,37 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddEngineComponentKeyToCommandMenuItem1773311456455
implements MigrationInterface
{
name = 'AddEngineComponentKeyToCommandMenuItem1773311456455';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."commandMenuItem" DROP CONSTRAINT "CHK_command_menu_item_workflow_or_front_component"`,
);
await queryRunner.query(
`CREATE TYPE "core"."commandMenuItem_enginecomponentkey_enum" AS ENUM('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', 'MERGE_MULTIPLE_RECORDS', 'DUPLICATE_DASHBOARD', 'DUPLICATE_WORKFLOW', 'ACTIVATE_WORKFLOW', 'DEACTIVATE_WORKFLOW', 'DISCARD_DRAFT_WORKFLOW', 'TEST_WORKFLOW', 'STOP_WORKFLOW_RUN', 'USE_AS_DRAFT_WORKFLOW_VERSION', 'SAVE_RECORD_PAGE_LAYOUT', 'SAVE_DASHBOARD_LAYOUT', 'TIDY_UP_WORKFLOW')`,
);
await queryRunner.query(
`ALTER TABLE "core"."commandMenuItem" ADD "engineComponentKey" "core"."commandMenuItem_enginecomponentkey_enum"`,
);
await queryRunner.query(
`ALTER TABLE "core"."commandMenuItem" ADD CONSTRAINT "CHK_CMD_MENU_ITEM_WF_OR_FC_OR_ENGINE_KEY" CHECK (("workflowVersionId" IS NOT NULL AND "frontComponentId" IS NULL AND "engineComponentKey" IS NULL) OR ("workflowVersionId" IS NULL AND "frontComponentId" IS NOT NULL AND "engineComponentKey" IS NULL) OR ("workflowVersionId" IS NULL AND "frontComponentId" IS NULL AND "engineComponentKey" IS NOT NULL))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."commandMenuItem" DROP CONSTRAINT "CHK_CMD_MENU_ITEM_WF_OR_FC_OR_ENGINE_KEY"`,
);
await queryRunner.query(
`ALTER TABLE "core"."commandMenuItem" DROP COLUMN "engineComponentKey"`,
);
await queryRunner.query(
`DROP TYPE "core"."commandMenuItem_enginecomponentkey_enum"`,
);
await queryRunner.query(
`ALTER TABLE "core"."commandMenuItem" ADD CONSTRAINT "CHK_command_menu_item_workflow_or_front_component" CHECK (((("workflowVersionId" IS NOT NULL) AND ("frontComponentId" IS NULL)) OR (("workflowVersionId" IS NULL) AND ("frontComponentId" IS NOT NULL))))`,
);
}
}
@@ -1,6 +1,6 @@
import { type CommandMenuItemManifest } from 'twenty-shared/application';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { type UniversalFlatCommandMenuItem } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-command-menu-item.type';
const AVAILABILITY_TYPE_MAP: Record<
@@ -38,6 +38,7 @@ export const fromCommandMenuItemManifestToUniversalFlatCommandMenuItem = ({
commandMenuItemManifest.frontComponentUniversalIdentifier,
availabilityObjectMetadataUniversalIdentifier:
commandMenuItemManifest.availabilityObjectUniversalIdentifier ?? null,
engineComponentKey: null,
workflowVersionId: null,
createdAt: now,
updatedAt: now,
@@ -1,10 +1,4 @@
import {
Field,
Float,
HideField,
ObjectType,
registerEnumType,
} from '@nestjs/graphql';
import { Field, Float, HideField, ObjectType } from '@nestjs/graphql';
import {
IsBoolean,
@@ -18,13 +12,10 @@ import {
} from 'class-validator';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
import { FrontComponentDTO } from 'src/engine/metadata-modules/front-component/dtos/front-component.dto';
registerEnumType(CommandMenuItemAvailabilityType, {
name: 'CommandMenuItemAvailabilityType',
});
@ObjectType('CommandMenuItem')
export class CommandMenuItemDTO {
@IsUUID()
@@ -45,6 +36,11 @@ export class CommandMenuItemDTO {
@Field(() => FrontComponentDTO, { nullable: true })
frontComponent?: FrontComponentDTO | null;
@IsEnum(EngineComponentKey)
@IsOptional()
@Field(() => EngineComponentKey, { nullable: true })
engineComponentKey?: EngineComponentKey;
@IsString()
@IsNotEmpty()
@Field()
@@ -11,7 +11,8 @@ import {
} from 'class-validator';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
@InputType()
export class CreateCommandMenuItemInput {
@@ -25,6 +26,11 @@ export class CreateCommandMenuItemInput {
@Field(() => UUIDScalarType, { nullable: true })
frontComponentId?: string;
@IsEnum(EngineComponentKey)
@IsOptional()
@Field(() => EngineComponentKey, { nullable: true })
engineComponentKey?: EngineComponentKey;
@IsString()
@IsNotEmpty()
@Field()
@@ -11,7 +11,8 @@ import {
} from 'class-validator';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
@InputType()
export class UpdateCommandMenuItemInput {
@@ -54,4 +55,9 @@ export class UpdateCommandMenuItemInput {
@IsOptional()
@Field(() => UUIDScalarType, { nullable: true })
availabilityObjectMetadataId?: string;
@IsEnum(EngineComponentKey)
@IsOptional()
@Field(() => EngineComponentKey, { nullable: true })
engineComponentKey?: EngineComponentKey;
}
@@ -11,16 +11,12 @@ import {
UpdateDateColumn,
} from 'typeorm';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
import { FrontComponentEntity } from 'src/engine/metadata-modules/front-component/entities/front-component.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
export enum CommandMenuItemAvailabilityType {
GLOBAL = 'GLOBAL',
RECORD_SELECTION = 'RECORD_SELECTION',
FALLBACK = 'FALLBACK',
}
@Entity({ name: 'commandMenuItem', schema: 'core' })
@Index('IDX_COMMAND_MENU_ITEM_WORKFLOW_VERSION_ID_WORKSPACE_ID', [
'workflowVersionId',
@@ -34,8 +30,8 @@ export enum CommandMenuItemAvailabilityType {
'availabilityObjectMetadataId',
])
@Check(
'CHK_command_menu_item_workflow_or_front_component',
'("workflowVersionId" IS NOT NULL AND "frontComponentId" IS NULL) OR ("workflowVersionId" IS NULL AND "frontComponentId" IS NOT NULL)',
'CHK_CMD_MENU_ITEM_WF_OR_FC_OR_ENGINE_KEY',
'("workflowVersionId" IS NOT NULL AND "frontComponentId" IS NULL AND "engineComponentKey" IS NULL) OR ("workflowVersionId" IS NULL AND "frontComponentId" IS NOT NULL AND "engineComponentKey" IS NULL) OR ("workflowVersionId" IS NULL AND "frontComponentId" IS NULL AND "engineComponentKey" IS NOT NULL)',
)
export class CommandMenuItemEntity
extends SyncableEntity
@@ -57,6 +53,13 @@ export class CommandMenuItemEntity
@JoinColumn({ name: 'frontComponentId' })
frontComponent: Relation<FrontComponentEntity> | null;
@Column({
type: 'enum',
enum: Object.values(EngineComponentKey),
nullable: true,
})
engineComponentKey: EngineComponentKey | null;
@Column({ nullable: false })
label: string;
@@ -74,7 +77,7 @@ export class CommandMenuItemEntity
@Column({
type: 'enum',
enum: CommandMenuItemAvailabilityType,
enum: Object.values(CommandMenuItemAvailabilityType),
nullable: false,
default: CommandMenuItemAvailabilityType.GLOBAL,
})
@@ -0,0 +1,11 @@
import { registerEnumType } from '@nestjs/graphql';
export enum CommandMenuItemAvailabilityType {
GLOBAL = 'GLOBAL',
RECORD_SELECTION = 'RECORD_SELECTION',
FALLBACK = 'FALLBACK',
}
registerEnumType(CommandMenuItemAvailabilityType, {
name: 'CommandMenuItemAvailabilityType',
});
@@ -0,0 +1,29 @@
import { registerEnumType } from '@nestjs/graphql';
export enum EngineComponentKey {
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',
ADD_TO_FAVORITES = 'ADD_TO_FAVORITES',
REMOVE_FROM_FAVORITES = 'REMOVE_FROM_FAVORITES',
MERGE_MULTIPLE_RECORDS = 'MERGE_MULTIPLE_RECORDS',
DUPLICATE_DASHBOARD = 'DUPLICATE_DASHBOARD',
DUPLICATE_WORKFLOW = 'DUPLICATE_WORKFLOW',
ACTIVATE_WORKFLOW = 'ACTIVATE_WORKFLOW',
DEACTIVATE_WORKFLOW = 'DEACTIVATE_WORKFLOW',
DISCARD_DRAFT_WORKFLOW = 'DISCARD_DRAFT_WORKFLOW',
TEST_WORKFLOW = 'TEST_WORKFLOW',
STOP_WORKFLOW_RUN = 'STOP_WORKFLOW_RUN',
USE_AS_DRAFT_WORKFLOW_VERSION = 'USE_AS_DRAFT_WORKFLOW_VERSION',
SAVE_RECORD_PAGE_LAYOUT = 'SAVE_RECORD_PAGE_LAYOUT',
SAVE_DASHBOARD_LAYOUT = 'SAVE_DASHBOARD_LAYOUT',
TIDY_UP_WORKFLOW = 'TIDY_UP_WORKFLOW',
}
registerEnumType(EngineComponentKey, {
name: 'EngineComponentKey',
});
@@ -8,4 +8,5 @@ export const FLAT_COMMAND_MENU_ITEM_EDITABLE_PROPERTIES = [
'isPinned',
'availabilityType',
'availabilityObjectMetadataId',
'engineComponentKey',
] as const satisfies MetadataEntityPropertyName<'commandMenuItem'>[];
@@ -61,6 +61,7 @@ export const fromCommandMenuItemEntityToFlatCommandMenuItem = ({
id: commandMenuItemEntity.id,
workflowVersionId: commandMenuItemEntity.workflowVersionId,
frontComponentId: commandMenuItemEntity.frontComponentId,
engineComponentKey: commandMenuItemEntity.engineComponentKey,
label: commandMenuItemEntity.label,
icon: commandMenuItemEntity.icon,
shortLabel: commandMenuItemEntity.shortLabel,
@@ -7,7 +7,7 @@ import {
CommandMenuItemExceptionCode,
} from 'src/engine/metadata-modules/command-menu-item/command-menu-item.exception';
import { type CreateCommandMenuItemInput } from 'src/engine/metadata-modules/command-menu-item/dtos/create-command-menu-item.input';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util';
@@ -32,10 +32,19 @@ export const fromCreateCommandMenuItemInputToFlatCommandMenuItemToCreate = ({
const hasFrontComponentId = isDefined(
createCommandMenuItemInput.frontComponentId,
);
const hasEngineComponentKey = isDefined(
createCommandMenuItemInput.engineComponentKey,
);
if (hasWorkflowVersionId === hasFrontComponentId) {
const sourceCount = [
hasWorkflowVersionId,
hasFrontComponentId,
hasEngineComponentKey,
].filter(Boolean).length;
if (sourceCount !== 1) {
throw new CommandMenuItemException(
'Exactly one of workflowVersionId or frontComponentId is required',
'Exactly one of workflowVersionId, frontComponentId or engineComponentKey is required',
CommandMenuItemExceptionCode.WORKFLOW_OR_FRONT_COMPONENT_REQUIRED,
);
}
@@ -62,6 +71,7 @@ export const fromCreateCommandMenuItemInputToFlatCommandMenuItemToCreate = ({
workflowVersionId: createCommandMenuItemInput.workflowVersionId ?? null,
frontComponentId: createCommandMenuItemInput.frontComponentId ?? null,
frontComponentUniversalIdentifier,
engineComponentKey: createCommandMenuItemInput.engineComponentKey ?? null,
label: createCommandMenuItemInput.label,
icon: createCommandMenuItemInput.icon ?? null,
shortLabel: createCommandMenuItemInput.shortLabel ?? null,
@@ -7,6 +7,7 @@ export const fromFlatCommandMenuItemToCommandMenuItemDto = (
id: flatCommandMenuItem.id,
workflowVersionId: flatCommandMenuItem.workflowVersionId ?? undefined,
frontComponentId: flatCommandMenuItem.frontComponentId ?? undefined,
engineComponentKey: flatCommandMenuItem.engineComponentKey ?? undefined,
label: flatCommandMenuItem.label,
icon: flatCommandMenuItem.icon ?? undefined,
shortLabel: flatCommandMenuItem.shortLabel ?? undefined,
@@ -30,6 +30,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
"availabilityType",
"conditionalAvailabilityExpression",
"availabilityObjectMetadataUniversalIdentifier",
"engineComponentKey",
],
"propertiesToStringify": [],
},
@@ -1032,6 +1032,11 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toStringify: false,
universalProperty: 'frontComponentUniversalIdentifier',
},
engineComponentKey: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
},
workflowVersionId: {
toCompare: false,
toStringify: false,
@@ -74,7 +74,8 @@ export const fromPageLayoutWidgetConfigurationToUniversalConfiguration = ({
configuration,
fieldMetadataUniversalIdentifierById,
frontComponentUniversalIdentifierById = {},
viewFieldGroupUniversalIdentifierById = {},
viewFieldGroupUniversalIdentifierById:
_viewFieldGroupUniversalIdentifierById = {},
viewUniversalIdentifierById = {},
shouldThrowOnMissingIdentifier = false,
}: {
@@ -1,4 +1,4 @@
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import {
COMMAND_MENU_ITEM_SEEDS,
FRONT_COMPONENT_SEEDS,
@@ -1,6 +1,7 @@
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
import { STANDARD_FRONT_COMPONENTS } from './standard-front-component.constant';
@@ -18,6 +19,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.navigateToNextRecord.universalIdentifier,
engineComponentKey: null,
},
navigateToPreviousRecord: {
universalIdentifier: 'ec10f871-415b-420b-8150-7e09f6f04833',
@@ -32,6 +34,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.navigateToPreviousRecord.universalIdentifier,
engineComponentKey: null,
},
createNewRecord: {
universalIdentifier: '08d255bf-58cd-47a5-bd82-78c5c58592f1',
@@ -44,8 +47,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'pageType == "INDEX_PAGE" and objectPermissions.canUpdateObjectRecords and not hasAnySoftDeleteFilterOnView',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.createNewRecord.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.CREATE_NEW_RECORD,
},
deleteSingleRecord: {
universalIdentifier: '6652773f-b9a9-4fa3-a52c-e2f2e259e430',
@@ -58,8 +61,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'numberOfSelectedRecords == 1 and not hasAnySoftDeleteFilterOnView and objectPermissions.canSoftDeleteObjectRecords and noneDefined(selectedRecords, "deletedAt")',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.deleteSingleRecord.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.DELETE_SINGLE_RECORD,
},
deleteMultipleRecords: {
universalIdentifier: 'cde86f1f-2c13-42b1-812b-f2b2b468cb83',
@@ -72,8 +75,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'numberOfSelectedRecords >= 2 and objectPermissions.canSoftDeleteObjectRecords and not hasAnySoftDeleteFilterOnView and numberOfSelectedRecords < 10000',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.deleteMultipleRecords.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.DELETE_MULTIPLE_RECORDS,
},
restoreSingleRecord: {
universalIdentifier: '8b3a1cae-3e4d-43c1-a71f-48592b2e47ff',
@@ -86,8 +89,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'numberOfSelectedRecords == 1 and everyDefined(selectedRecords, "deletedAt") and objectPermissions.canSoftDeleteObjectRecords and (pageType == "RECORD_PAGE" or hasAnySoftDeleteFilterOnView)',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.restoreSingleRecord.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.RESTORE_SINGLE_RECORD,
},
restoreMultipleRecords: {
universalIdentifier: '8b740c9d-d99a-45a8-812f-809caaf420ac',
@@ -100,8 +103,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'numberOfSelectedRecords >= 2 and objectPermissions.canSoftDeleteObjectRecords and hasAnySoftDeleteFilterOnView and numberOfSelectedRecords < 10000',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.restoreMultipleRecords.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.RESTORE_MULTIPLE_RECORDS,
},
destroySingleRecord: {
universalIdentifier: '44a78417-c394-4bc8-961f-98b503030ddb',
@@ -114,8 +117,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'numberOfSelectedRecords == 1 and objectPermissions.canDestroyObjectRecords and everyDefined(selectedRecords, "deletedAt")',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.destroySingleRecord.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.DESTROY_SINGLE_RECORD,
},
destroyMultipleRecords: {
universalIdentifier: 'c630b3fb-7920-40d1-9906-77d0aa797608',
@@ -128,8 +131,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'numberOfSelectedRecords >= 2 and objectPermissions.canDestroyObjectRecords and hasAnySoftDeleteFilterOnView and numberOfSelectedRecords < 10000',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.destroyMultipleRecords.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.DESTROY_MULTIPLE_RECORDS,
},
addToFavorites: {
universalIdentifier: '38bf80c3-bd55-4753-80ba-38aa66429a03',
@@ -142,8 +145,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'arrayLength(favoriteRecordIds) < numberOfSelectedRecords and noneDefined(selectedRecords, "deletedAt") and not hasAnySoftDeleteFilterOnView',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.addToFavorites.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.ADD_TO_FAVORITES,
},
removeFromFavorites: {
universalIdentifier: '3ea42507-44fa-4895-a36d-cbfef7355a50',
@@ -156,8 +159,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'arrayLength(favoriteRecordIds) == numberOfSelectedRecords and noneDefined(selectedRecords, "deletedAt") and not hasAnySoftDeleteFilterOnView',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.removeFromFavorites.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.REMOVE_FROM_FAVORITES,
},
exportNoteToPdf: {
universalIdentifier: '86c8f3aa-9276-4c16-8cff-e295e34fbaf0',
@@ -172,6 +175,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.exportNoteToPdf.universalIdentifier,
engineComponentKey: null,
},
exportFromRecordIndex: {
universalIdentifier: 'a934ba8a-ac8f-487d-9cd9-06dfdaec1f49',
@@ -185,6 +189,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.exportFromRecordIndex.universalIdentifier,
engineComponentKey: null,
},
exportFromRecordShow: {
universalIdentifier: 'ba339455-f3c2-4ed1-bf77-3e316d7d6a66',
@@ -198,6 +203,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.exportFromRecordShow.universalIdentifier,
engineComponentKey: null,
},
updateMultipleRecords: {
universalIdentifier: '2e080651-f098-4a78-bea9-7a70002dc57c',
@@ -212,6 +218,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.updateMultipleRecords.universalIdentifier,
engineComponentKey: null,
},
mergeMultipleRecords: {
universalIdentifier: '6c14eb04-8e7e-4d47-93c0-8ec4834e2e60',
@@ -224,8 +231,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'numberOfSelectedRecords >= 2 and isDefined(objectMetadataItem.duplicateCriteria) and objectPermissions.canUpdateObjectRecords and objectPermissions.canDestroyObjectRecords and numberOfSelectedRecords <= 9',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.mergeMultipleRecords.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.MERGE_MULTIPLE_RECORDS,
},
exportMultipleRecords: {
universalIdentifier: 'f71f68e5-7b6e-4c03-8161-c48434d7777c',
@@ -239,6 +246,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.exportMultipleRecords.universalIdentifier,
engineComponentKey: null,
},
importRecords: {
universalIdentifier: 'a2dc9de7-4798-422e-bb55-bfad7b9bdbe8',
@@ -252,6 +260,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.importRecords.universalIdentifier,
engineComponentKey: null,
},
exportView: {
universalIdentifier: '80680f2a-c426-48b3-a839-c63a6183dc4b',
@@ -265,6 +274,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.exportView.universalIdentifier,
engineComponentKey: null,
},
seeDeletedRecords: {
universalIdentifier: 'd63c21c3-9785-4750-be87-5f36269b8e0d',
@@ -278,6 +288,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.seeDeletedRecords.universalIdentifier,
engineComponentKey: null,
},
createNewView: {
universalIdentifier: '6ec7c339-e167-431d-bec6-d1c737df677c',
@@ -291,6 +302,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.createNewView.universalIdentifier,
engineComponentKey: null,
},
hideDeletedRecords: {
universalIdentifier: '1420db7f-0fba-49e2-b23e-4b7caa0fafa0',
@@ -304,6 +316,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.hideDeletedRecords.universalIdentifier,
engineComponentKey: null,
},
goToPeople: {
universalIdentifier: 'dfe5fef8-d42c-40f0-941f-8e3b5eb01daa',
@@ -317,6 +330,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.goToPeople.universalIdentifier,
engineComponentKey: null,
},
goToCompanies: {
universalIdentifier: '196e4eec-bfdd-48a6-bcbb-6707ef11951a',
@@ -330,6 +344,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.goToCompanies.universalIdentifier,
engineComponentKey: null,
},
goToDashboards: {
universalIdentifier: '11dc07d1-21b2-4f86-af8c-6a664c02f00c',
@@ -343,6 +358,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.goToDashboards.universalIdentifier,
engineComponentKey: null,
},
goToOpportunities: {
universalIdentifier: 'f04f5e00-a208-422f-acf2-ff189769510d',
@@ -357,6 +373,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.goToOpportunities.universalIdentifier,
engineComponentKey: null,
},
goToSettings: {
universalIdentifier: 'ef9aba44-0068-453e-930a-f8c182af18ee',
@@ -370,6 +387,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.goToSettings.universalIdentifier,
engineComponentKey: null,
},
goToTasks: {
universalIdentifier: 'e8e3bd0b-5ce9-4577-bb61-588c0b1ad063',
@@ -383,6 +401,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.goToTasks.universalIdentifier,
engineComponentKey: null,
},
goToNotes: {
universalIdentifier: '08e0f0cc-ac2d-46cd-9bca-39a409b9addf',
@@ -396,6 +415,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.goToNotes.universalIdentifier,
engineComponentKey: null,
},
editRecordPageLayout: {
universalIdentifier: 'd9794c67-1799-424f-8871-5ea771dd4a6d',
@@ -410,6 +430,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.editRecordPageLayout.universalIdentifier,
engineComponentKey: null,
},
saveRecordPageLayout: {
universalIdentifier: 'a3363589-e2a6-4451-a53c-b8c2710785e2',
@@ -422,8 +443,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
conditionalAvailabilityExpression:
'pageType == "RECORD_PAGE" and isPageInEditMode and featureFlags.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED and noneDefined(selectedRecords, "deletedAt") and objectPermissions.canUpdateObjectRecords',
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.saveRecordPageLayout.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.SAVE_RECORD_PAGE_LAYOUT,
},
cancelRecordPageLayout: {
universalIdentifier: '0b9b4e93-2b4e-4ab0-908e-83ed1d674df7',
@@ -438,6 +459,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.cancelRecordPageLayout.universalIdentifier,
engineComponentKey: null,
},
editDashboardLayout: {
universalIdentifier: 'b9b53bbc-3129-4eb9-8344-c3f9628ffa7d',
@@ -453,6 +475,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.dashboard.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.editDashboardLayout.universalIdentifier,
engineComponentKey: null,
},
saveDashboardLayout: {
universalIdentifier: '18b23908-f816-42ab-bc0a-eb5fae29c695',
@@ -466,8 +489,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'pageType == "RECORD_PAGE" and isPageInEditMode and noneDefined(selectedRecords, "deletedAt") and everyDefined(selectedRecords, "pageLayoutId") and objectPermissions.canUpdateObjectRecords',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.dashboard.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.saveDashboardLayout.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.SAVE_DASHBOARD_LAYOUT,
},
cancelDashboardLayout: {
universalIdentifier: '030ecd01-0aaf-4e6d-8400-105996548887',
@@ -483,6 +506,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.dashboard.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.cancelDashboardLayout.universalIdentifier,
engineComponentKey: null,
},
duplicateDashboard: {
universalIdentifier: '2ee07307-60ce-41ef-bfee-7c718f67557e',
@@ -496,8 +520,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'noneDefined(selectedRecords, "deletedAt") and objectPermissions.canUpdateObjectRecords',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.dashboard.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.duplicateDashboard.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.DUPLICATE_DASHBOARD,
},
goToWorkflows: {
universalIdentifier: '4fa778f9-7931-4d18-b895-929e1ef9c31f',
@@ -511,6 +535,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.goToWorkflows.universalIdentifier,
engineComponentKey: null,
},
activateWorkflow: {
universalIdentifier: '44f19c85-0fd0-482f-a14e-da513c60b1b3',
@@ -524,8 +549,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'everyDefined(selectedRecords, "currentVersion.trigger") and everyDefined(selectedRecords, "currentVersion.steps") and every(selectedRecords, "currentVersion.steps.length") and (everyEquals(selectedRecords, "currentVersion.status", "DRAFT") or includesNone(selectedRecords, "statuses", "ACTIVE")) and noneDefined(selectedRecords, "deletedAt")',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.activateWorkflow.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.ACTIVATE_WORKFLOW,
},
deactivateWorkflow: {
universalIdentifier: '57f21a06-a17a-47b1-a123-90d90dbdf0b7',
@@ -539,8 +564,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'everyEquals(selectedRecords, "currentVersion.status", "ACTIVE") and noneDefined(selectedRecords, "deletedAt")',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.deactivateWorkflow.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.DEACTIVATE_WORKFLOW,
},
discardDraftWorkflow: {
universalIdentifier: '4c227f2e-03bb-4a66-9b13-49f263264f4a',
@@ -554,8 +579,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'every(selectedRecords, "versions.length") and everyEquals(selectedRecords, "currentVersion.status", "DRAFT") and noneDefined(selectedRecords, "deletedAt")',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.discardDraftWorkflow.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.DISCARD_DRAFT_WORKFLOW,
},
testWorkflow: {
universalIdentifier: 'f85d552a-87a3-4667-99f7-71b47917539c',
@@ -569,8 +594,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'everyDefined(selectedRecords, "currentVersion.trigger") and everyDefined(selectedRecords, "currentVersion.steps") and every(selectedRecords, "currentVersion.steps.length") and ((everyEquals(selectedRecords, "currentVersion.trigger.type", "MANUAL") and noneDefined(selectedRecords, "currentVersion.trigger.settings.objectType")) or everyEquals(selectedRecords, "currentVersion.trigger.type", "WEBHOOK") or everyEquals(selectedRecords, "currentVersion.trigger.type", "CRON")) and noneDefined(selectedRecords, "deletedAt")',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.testWorkflow.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.TEST_WORKFLOW,
},
seeActiveVersionWorkflow: {
universalIdentifier: '31790508-75ff-4e4c-a768-83bd1b0718e0',
@@ -586,6 +611,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.seeActiveVersionWorkflow.universalIdentifier,
engineComponentKey: null,
},
seeRunsWorkflow: {
universalIdentifier: 'e57efc2d-00a2-493a-b76c-f2dabd23a5eb',
@@ -601,6 +627,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.seeRunsWorkflow.universalIdentifier,
engineComponentKey: null,
},
seeVersionsWorkflow: {
universalIdentifier: '92781d24-b875-4282-8cdb-d127f04a5c7d',
@@ -616,6 +643,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.seeVersionsWorkflow.universalIdentifier,
engineComponentKey: null,
},
addNodeWorkflow: {
universalIdentifier: '818117fa-6cad-4ebc-83c1-40f4afc28d94',
@@ -631,6 +659,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.addNodeWorkflow.universalIdentifier,
engineComponentKey: null,
},
tidyUpWorkflow: {
universalIdentifier: '1f3a3cab-161a-4775-af47-11be4d0bf411',
@@ -644,8 +673,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'pageType == "RECORD_PAGE" and everyDefined(selectedRecords, "currentVersion.trigger") and everyDefined(selectedRecords, "currentVersion.steps") and every(selectedRecords, "currentVersion.steps.length") and noneDefined(selectedRecords, "deletedAt")',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.tidyUpWorkflow.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.TIDY_UP_WORKFLOW,
},
duplicateWorkflow: {
universalIdentifier: '91094438-b4c2-46ad-a23b-8af4b23ba514',
@@ -659,8 +688,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'everyDefined(selectedRecords, "currentVersion") and noneDefined(selectedRecords, "deletedAt")',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.workflow.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.duplicateWorkflow.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.DUPLICATE_WORKFLOW,
},
goToRuns: {
universalIdentifier: '1ba959da-ff49-4c1f-a517-2b78ee200508',
@@ -675,6 +704,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.goToRuns.universalIdentifier,
engineComponentKey: null,
},
seeVersionWorkflowRun: {
universalIdentifier: 'cc3a065c-c89e-40ac-9449-4272c55b1bb8',
@@ -689,6 +719,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.workflowRun.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.seeVersionWorkflowRun.universalIdentifier,
engineComponentKey: null,
},
seeWorkflowWorkflowRun: {
universalIdentifier: '9d9cc62d-3543-45c3-93f3-23d2d8979f2b',
@@ -703,6 +734,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.workflowRun.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.seeWorkflowWorkflowRun.universalIdentifier,
engineComponentKey: null,
},
stopWorkflowRun: {
universalIdentifier: '4c186606-9515-4561-a1eb-9a072b4f5e58',
@@ -716,8 +748,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'isSelectAll or someEquals(selectedRecords, "status", "NOT_STARTED") or someEquals(selectedRecords, "status", "ENQUEUED") or someEquals(selectedRecords, "status", "RUNNING")',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.workflowRun.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.stopWorkflowRun.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.STOP_WORKFLOW_RUN,
},
seeRunsWorkflowVersion: {
universalIdentifier: '44e305c7-4f0a-45ec-803f-6471b56455cb',
@@ -733,6 +765,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.workflowVersion.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.seeRunsWorkflowVersion.universalIdentifier,
engineComponentKey: null,
},
seeWorkflowWorkflowVersion: {
universalIdentifier: 'b43052db-023e-4083-9b63-2c2dfbfd1320',
@@ -748,6 +781,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.workflowVersion.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.seeWorkflowWorkflowVersion.universalIdentifier,
engineComponentKey: null,
},
useAsDraftWorkflowVersion: {
universalIdentifier: '483c0c1d-ea4d-4a4d-8a59-2dcf9f8e38f6',
@@ -761,8 +795,8 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
'noneEquals(selectedRecords, "status", "DRAFT")',
availabilityObjectMetadataUniversalIdentifier:
STANDARD_OBJECTS.workflowVersion.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.useAsDraftWorkflowVersion.universalIdentifier,
frontComponentUniversalIdentifier: null,
engineComponentKey: EngineComponentKey.USE_AS_DRAFT_WORKFLOW_VERSION,
},
seeVersionsWorkflowVersion: {
universalIdentifier: '1d4abeb7-2750-4af7-9a92-fbadd2a9e4ba',
@@ -778,6 +812,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
STANDARD_OBJECTS.workflowVersion.universalIdentifier,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.seeVersionsWorkflowVersion.universalIdentifier,
engineComponentKey: null,
},
searchRecords: {
universalIdentifier: 'fa24e25e-68f8-4548-82ff-c7b5168b7c7d',
@@ -791,6 +826,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.searchRecords.universalIdentifier,
engineComponentKey: null,
},
searchRecordsFallback: {
universalIdentifier: 'c659890c-7266-46c9-bfe1-75cefff8b6d0',
@@ -804,6 +840,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.searchRecordsFallback.universalIdentifier,
engineComponentKey: null,
},
askAi: {
universalIdentifier: 'ce5fb54d-2b19-4dd1-b7b4-9532a1761a41',
@@ -817,6 +854,7 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.askAi.universalIdentifier,
engineComponentKey: null,
},
viewPreviousAiChats: {
universalIdentifier: '3084c3c9-cc23-4dad-9e00-92025f5cba7a',
@@ -830,5 +868,6 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
availabilityObjectMetadataUniversalIdentifier: null,
frontComponentUniversalIdentifier:
STANDARD_FRONT_COMPONENTS.viewPreviousAiChats.universalIdentifier,
engineComponentKey: null,
},
} as const;
@@ -29,131 +29,6 @@ export const STANDARD_FRONT_COMPONENTS = {
.builtComponentChecksum,
isHeadless: true,
},
createNewRecord: {
universalIdentifier: '1e6e57bb-89c4-482e-bf44-d62ad1c05d2f',
name: 'Create new record',
componentName: 'CreateNewRecord',
sourceComponentPath:
'front-components/record/create-new-record.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.createNewRecord
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.createNewRecord
.builtComponentChecksum,
isHeadless: true,
},
deleteSingleRecord: {
universalIdentifier: 'cbc7e92f-d4fe-4956-ba66-a81b7f9713c6',
name: 'Delete single record',
componentName: 'DeleteSingleRecord',
sourceComponentPath:
'front-components/record/delete-single-record.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.deleteSingleRecord
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.deleteSingleRecord
.builtComponentChecksum,
isHeadless: true,
},
deleteMultipleRecords: {
universalIdentifier: '35c25fd2-6060-440a-b734-aa3016c11f47',
name: 'Delete multiple records',
componentName: 'DeleteMultipleRecords',
sourceComponentPath:
'front-components/record/delete-multiple-records.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.deleteMultipleRecords
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.deleteMultipleRecords
.builtComponentChecksum,
isHeadless: true,
},
restoreSingleRecord: {
universalIdentifier: '88262225-1253-4dfe-9bf7-563b73e6d9ea',
name: 'Restore single record',
componentName: 'RestoreSingleRecord',
sourceComponentPath:
'front-components/record/restore-single-record.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.restoreSingleRecord
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.restoreSingleRecord
.builtComponentChecksum,
isHeadless: true,
},
restoreMultipleRecords: {
universalIdentifier: '105ff959-838c-44bc-8a92-60d61093ebc4',
name: 'Restore multiple records',
componentName: 'RestoreMultipleRecords',
sourceComponentPath:
'front-components/record/restore-multiple-records.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.restoreMultipleRecords
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.restoreMultipleRecords
.builtComponentChecksum,
isHeadless: true,
},
destroySingleRecord: {
universalIdentifier: '212d9e7d-e149-417b-9a0f-5024835349e6',
name: 'Destroy single record',
componentName: 'DestroySingleRecord',
sourceComponentPath:
'front-components/record/destroy-single-record.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.destroySingleRecord
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.destroySingleRecord
.builtComponentChecksum,
isHeadless: true,
},
destroyMultipleRecords: {
universalIdentifier: 'f647d17d-b4ae-4a4c-99a9-4e5e4e3068a2',
name: 'Destroy multiple records',
componentName: 'DestroyMultipleRecords',
sourceComponentPath:
'front-components/record/destroy-multiple-records.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.destroyMultipleRecords
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.destroyMultipleRecords
.builtComponentChecksum,
isHeadless: true,
},
addToFavorites: {
universalIdentifier: '5694b053-1f42-416c-a26a-375f826aa9b3',
name: 'Add to favorites',
componentName: 'AddToFavorites',
sourceComponentPath:
'front-components/record/add-to-favorites.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.addToFavorites.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.addToFavorites
.builtComponentChecksum,
isHeadless: true,
},
removeFromFavorites: {
universalIdentifier: '21105d6a-33a2-4ae3-8edb-c33c38d2e091',
name: 'Remove from favorites',
componentName: 'RemoveFromFavorites',
sourceComponentPath:
'front-components/record/remove-from-favorites.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.removeFromFavorites
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.removeFromFavorites
.builtComponentChecksum,
isHeadless: true,
},
exportNoteToPdf: {
universalIdentifier: '980399e9-e530-4430-bf47-7f3f482434b4',
name: 'Export note to PDF',
@@ -210,20 +85,6 @@ export const STANDARD_FRONT_COMPONENTS = {
.builtComponentChecksum,
isHeadless: true,
},
mergeMultipleRecords: {
universalIdentifier: '9c6757aa-ecdd-4b21-8466-fb1e8f0bfcb8',
name: 'Merge multiple records',
componentName: 'MergeMultipleRecords',
sourceComponentPath:
'front-components/record/merge-multiple-records.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.mergeMultipleRecords
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.mergeMultipleRecords
.builtComponentChecksum,
isHeadless: true,
},
exportMultipleRecords: {
universalIdentifier: '05381918-8c9c-421f-ab89-10e365a463f8',
name: 'Export multiple records',
@@ -407,20 +268,6 @@ export const STANDARD_FRONT_COMPONENTS = {
.builtComponentChecksum,
isHeadless: true,
},
saveRecordPageLayout: {
universalIdentifier: '7956ba3a-fd4d-466e-96b5-9e3e8b637c44',
name: 'Save record page layout',
componentName: 'SaveRecordPageLayout',
sourceComponentPath:
'front-components/page-layout/save-record-page-layout.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.saveRecordPageLayout
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.saveRecordPageLayout
.builtComponentChecksum,
isHeadless: true,
},
cancelRecordPageLayout: {
universalIdentifier: 'd392762b-4c42-4471-9c45-c92e66728380',
name: 'Cancel record page layout edition',
@@ -449,20 +296,6 @@ export const STANDARD_FRONT_COMPONENTS = {
.builtComponentChecksum,
isHeadless: true,
},
saveDashboardLayout: {
universalIdentifier: '5d7b4510-79e8-440f-8707-21741c00d262',
name: 'Save dashboard layout',
componentName: 'SaveDashboardLayout',
sourceComponentPath:
'front-components/dashboard/save-dashboard-layout.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.saveDashboardLayout
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.saveDashboardLayout
.builtComponentChecksum,
isHeadless: true,
},
cancelDashboardLayout: {
universalIdentifier: 'dac81512-3890-4ba5-8471-bd94738ab80a',
name: 'Cancel dashboard layout edition',
@@ -477,20 +310,6 @@ export const STANDARD_FRONT_COMPONENTS = {
.builtComponentChecksum,
isHeadless: true,
},
duplicateDashboard: {
universalIdentifier: 'e2bec30e-a6b0-47ff-9708-45855ae96fe7',
name: 'Duplicate dashboard',
componentName: 'DuplicateDashboard',
sourceComponentPath:
'front-components/dashboard/duplicate-dashboard.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.duplicateDashboard
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.duplicateDashboard
.builtComponentChecksum,
isHeadless: true,
},
goToWorkflows: {
universalIdentifier: '9d313f79-170f-47cc-b9c4-a2076a86232f',
name: 'Go to Workflows',
@@ -504,61 +323,6 @@ export const STANDARD_FRONT_COMPONENTS = {
.builtComponentChecksum,
isHeadless: true,
},
activateWorkflow: {
universalIdentifier: '97b89dc4-cef9-4439-9358-35c98616eb1e',
name: 'Activate workflow',
componentName: 'ActivateWorkflow',
sourceComponentPath:
'front-components/workflow/activate-workflow.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.activateWorkflow
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.activateWorkflow
.builtComponentChecksum,
isHeadless: true,
},
deactivateWorkflow: {
universalIdentifier: 'cbf92077-1892-47e0-9435-14ea8f50a510',
name: 'Deactivate workflow',
componentName: 'DeactivateWorkflow',
sourceComponentPath:
'front-components/workflow/deactivate-workflow.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.deactivateWorkflow
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.deactivateWorkflow
.builtComponentChecksum,
isHeadless: true,
},
discardDraftWorkflow: {
universalIdentifier: '972dc871-7f9c-4035-957c-e6662f4df7c5',
name: 'Discard draft workflow',
componentName: 'DiscardDraftWorkflow',
sourceComponentPath:
'front-components/workflow/discard-draft-workflow.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.discardDraftWorkflow
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.discardDraftWorkflow
.builtComponentChecksum,
isHeadless: true,
},
testWorkflow: {
universalIdentifier: '39e9aa5a-cacc-4543-9053-f1fbf923e170',
name: 'Test workflow',
componentName: 'TestWorkflow',
sourceComponentPath:
'front-components/workflow/test-workflow.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.testWorkflow.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.testWorkflow
.builtComponentChecksum,
isHeadless: true,
},
seeActiveVersionWorkflow: {
universalIdentifier: '6259a4a5-428e-41b9-a032-333c2d51e15f',
name: 'See active version',
@@ -615,33 +379,6 @@ export const STANDARD_FRONT_COMPONENTS = {
.builtComponentChecksum,
isHeadless: true,
},
tidyUpWorkflow: {
universalIdentifier: '3dac631e-dfb7-4570-ac27-15d98ee8ec43',
name: 'Tidy up workflow',
componentName: 'TidyUpWorkflow',
sourceComponentPath:
'front-components/workflow/tidy-up-workflow.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.tidyUpWorkflow.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.tidyUpWorkflow
.builtComponentChecksum,
isHeadless: true,
},
duplicateWorkflow: {
universalIdentifier: '566a48c5-5342-446f-a041-9db59bcdab6b',
name: 'Duplicate workflow',
componentName: 'DuplicateWorkflow',
sourceComponentPath:
'front-components/workflow/duplicate-workflow.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.duplicateWorkflow
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.duplicateWorkflow
.builtComponentChecksum,
isHeadless: true,
},
goToRuns: {
universalIdentifier: '0bd0ccfc-1909-4d19-b694-610878f07a3a',
name: 'Go to runs',
@@ -682,20 +419,6 @@ export const STANDARD_FRONT_COMPONENTS = {
.builtComponentChecksum,
isHeadless: true,
},
stopWorkflowRun: {
universalIdentifier: '110fb676-2a09-4ac0-bd19-7261bc588967',
name: 'Stop workflow run',
componentName: 'StopWorkflowRun',
sourceComponentPath:
'front-components/workflow-run/stop-workflow-run.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.stopWorkflowRun
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.stopWorkflowRun
.builtComponentChecksum,
isHeadless: true,
},
seeRunsWorkflowVersion: {
universalIdentifier: '3d673f94-ecf9-4e38-8eac-684cf4cad617',
name: 'See runs (workflow version)',
@@ -724,20 +447,6 @@ export const STANDARD_FRONT_COMPONENTS = {
.builtComponentChecksum,
isHeadless: true,
},
useAsDraftWorkflowVersion: {
universalIdentifier: 'cb11ab5c-974a-4942-bb8a-77efa6b5bb26',
name: 'Use as draft (workflow version)',
componentName: 'UseAsDraftWorkflowVersion',
sourceComponentPath:
'front-components/workflow-version/use-as-draft-workflow-version.front-component.tsx',
builtComponentPath:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.useAsDraftWorkflowVersion
.builtComponentPath,
builtComponentChecksum:
STANDARD_FRONT_COMPONENT_BUILD_MANIFEST.useAsDraftWorkflowVersion
.builtComponentChecksum,
isHeadless: true,
},
seeVersionsWorkflowVersion: {
universalIdentifier: 'a93c9f09-7a7f-4665-982a-0709a652c5bd',
name: 'See versions history (workflow version)',
@@ -28,15 +28,24 @@ export const createStandardCommandMenuItemFlatMetadata = ({
}): FlatCommandMenuItem => {
const definition = STANDARD_COMMAND_MENU_ITEMS[commandMenuItemName];
const flatFrontComponent = findFlatEntityByUniversalIdentifier({
flatEntityMaps: flatFrontComponentMaps,
universalIdentifier: definition.frontComponentUniversalIdentifier,
});
let resolvedFrontComponentId: string | null = null;
let resolvedFrontComponentUniversalIdentifier: string | null = null;
if (!isDefined(flatFrontComponent)) {
throw new Error(
`Front component not found for universal identifier ${definition.frontComponentUniversalIdentifier}`,
);
if (isDefined(definition.frontComponentUniversalIdentifier)) {
const flatFrontComponent = findFlatEntityByUniversalIdentifier({
flatEntityMaps: flatFrontComponentMaps,
universalIdentifier: definition.frontComponentUniversalIdentifier,
});
if (!isDefined(flatFrontComponent)) {
throw new Error(
`Front component not found for universal identifier ${definition.frontComponentUniversalIdentifier}`,
);
}
resolvedFrontComponentId = flatFrontComponent.id;
resolvedFrontComponentUniversalIdentifier =
flatFrontComponent.universalIdentifier;
}
let resolvedObjectMetadataId: string | null = null;
@@ -75,8 +84,10 @@ export const createStandardCommandMenuItemFlatMetadata = ({
availabilityType: definition.availabilityType,
conditionalAvailabilityExpression:
definition.conditionalAvailabilityExpression ?? null,
frontComponentId: flatFrontComponent.id,
frontComponentUniversalIdentifier: flatFrontComponent.universalIdentifier,
frontComponentId: resolvedFrontComponentId,
frontComponentUniversalIdentifier:
resolvedFrontComponentUniversalIdentifier,
engineComponentKey: definition.engineComponentKey,
workflowVersionId: null,
availabilityObjectMetadataId: resolvedObjectMetadataId,
availabilityObjectMetadataUniversalIdentifier:
@@ -1,102 +0,0 @@
import { isDefined } from 'twenty-shared/utils';
import { type CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { type FlatFrontComponent } from 'src/engine/metadata-modules/flat-front-component/types/flat-front-component.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
export const createStandardCommandMenuItemFolderFlatMetadata = ({
universalIdentifier,
label,
shortLabel,
icon,
position,
isPinned,
availabilityType,
frontComponentUniversalIdentifier,
availabilityObjectMetadataUniversalIdentifier,
conditionalAvailabilityExpression = null,
commandMenuItemId,
workspaceId,
twentyStandardApplicationId,
dependencyFlatEntityMaps: { flatFrontComponentMaps, flatObjectMetadataMaps },
now,
}: {
universalIdentifier: string;
label: string;
shortLabel: string | null;
icon: string;
position: number;
isPinned: boolean;
availabilityType: CommandMenuItemAvailabilityType;
frontComponentUniversalIdentifier: string;
availabilityObjectMetadataUniversalIdentifier: string | null;
conditionalAvailabilityExpression?: string | null;
commandMenuItemId: string;
workspaceId: string;
twentyStandardApplicationId: string;
dependencyFlatEntityMaps: {
flatFrontComponentMaps: FlatEntityMaps<FlatFrontComponent>;
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
};
now: string;
}): FlatCommandMenuItem => {
const flatFrontComponent = findFlatEntityByUniversalIdentifier({
flatEntityMaps: flatFrontComponentMaps,
universalIdentifier: frontComponentUniversalIdentifier,
});
if (!isDefined(flatFrontComponent)) {
throw new Error(
`Front component not found for universal identifier ${frontComponentUniversalIdentifier}`,
);
}
let resolvedObjectMetadataId: string | null = null;
let resolvedObjectMetadataUniversalIdentifier: string | null = null;
if (isDefined(availabilityObjectMetadataUniversalIdentifier)) {
const flatObjectMetadata = findFlatEntityByUniversalIdentifier({
flatEntityMaps: flatObjectMetadataMaps,
universalIdentifier: availabilityObjectMetadataUniversalIdentifier,
});
if (!isDefined(flatObjectMetadata)) {
throw new Error(
`Object metadata not found for universal identifier ${availabilityObjectMetadataUniversalIdentifier}`,
);
}
resolvedObjectMetadataId = flatObjectMetadata.id;
resolvedObjectMetadataUniversalIdentifier =
flatObjectMetadata.universalIdentifier;
}
return {
id: commandMenuItemId,
universalIdentifier,
applicationId: twentyStandardApplicationId,
applicationUniversalIdentifier:
TWENTY_STANDARD_APPLICATION.universalIdentifier,
workspaceId,
label,
shortLabel,
icon,
position,
isPinned,
availabilityType,
conditionalAvailabilityExpression:
conditionalAvailabilityExpression ?? null,
frontComponentId: flatFrontComponent.id,
frontComponentUniversalIdentifier: flatFrontComponent.universalIdentifier,
workflowVersionId: null,
availabilityObjectMetadataId: resolvedObjectMetadataId,
availabilityObjectMetadataUniversalIdentifier:
resolvedObjectMetadataUniversalIdentifier,
createdAt: now,
updatedAt: now,
};
};
@@ -41,12 +41,21 @@ export class FlatCommandMenuItemValidatorService {
const hasFrontComponentUniversalIdentifier = isDefined(
flatCommandMenuItem.frontComponentUniversalIdentifier,
);
const hasEngineComponentKey = isDefined(
flatCommandMenuItem.engineComponentKey,
);
if (hasWorkflowVersionId === hasFrontComponentUniversalIdentifier) {
const sourceCount = [
hasWorkflowVersionId,
hasFrontComponentUniversalIdentifier,
hasEngineComponentKey,
].filter(Boolean).length;
if (sourceCount !== 1) {
validationResult.errors.push({
code: CommandMenuItemExceptionCode.WORKFLOW_OR_FRONT_COMPONENT_REQUIRED,
message: t`Exactly one of workflowVersionId or frontComponentUniversalIdentifier is required`,
userFriendlyMessage: msg`Exactly one of workflow version or front component is required`,
message: t`Exactly one of workflowVersionId, frontComponentUniversalIdentifier or engineComponentKey is required`,
userFriendlyMessage: msg`Exactly one of workflow version, front component or engine component key is required`,
});
}
@@ -72,7 +72,7 @@ export const fromUniversalConfigurationToFlatPageLayoutWidgetConfiguration = ({
flatFieldMetadataMaps,
flatFrontComponentMaps,
flatViewMaps,
flatViewFieldGroupMaps,
flatViewFieldGroupMaps: _flatViewFieldGroupMaps,
}: {
universalConfiguration: FlatPageLayoutWidget['universalConfiguration'];
flatFieldMetadataMaps: MetadataFlatEntityMaps<'fieldMetadata'>;
@@ -6,7 +6,7 @@ import { type ActorMetadata, FeatureFlagKey } from 'twenty-shared/types';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { CommandMenuItemService } from 'src/engine/metadata-modules/command-menu-item/command-menu-item.service';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
@@ -7,7 +7,7 @@ exports[`CommandMenuItem creation should fail when creating with both workflowVe
"subCode": "WORKFLOW_OR_FRONT_COMPONENT_REQUIRED",
"userFriendlyMessage": "Either workflow version or front component is required.",
},
"message": "Exactly one of workflowVersionId or frontComponentId is required",
"message": "Exactly one of workflowVersionId, frontComponentId or engineComponentKey is required",
"name": "UserInputError",
}
`;
@@ -98,7 +98,7 @@ exports[`CommandMenuItem creation should fail when creating with missing workflo
"subCode": "WORKFLOW_OR_FRONT_COMPONENT_REQUIRED",
"userFriendlyMessage": "Either workflow version or front component is required.",
},
"message": "Exactly one of workflowVersionId or frontComponentId is required",
"message": "Exactly one of workflowVersionId, frontComponentId or engineComponentKey is required",
"name": "UserInputError",
}
`;
@@ -9,7 +9,7 @@ import { updateFeatureFlag } from 'test/integration/metadata/suites/utils/update
import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test';
import { FeatureFlagKey } from 'twenty-shared/types';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
describe('CommandMenuItem creation should succeed', () => {
let createdCommandMenuItemId: string;
@@ -7,7 +7,7 @@ import { updateFeatureFlag } from 'test/integration/metadata/suites/utils/update
import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test';
import { FeatureFlagKey } from 'twenty-shared/types';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
describe('CommandMenuItem update should succeed', () => {
let createdCommandMenuItemId: string;
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const DuplicateDashboard = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: 'e2bec30e-a6b0-47ff-9708-45855ae96fe7',
name: 'Duplicate dashboard',
component: DuplicateDashboard,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const SaveDashboardLayout = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '5d7b4510-79e8-440f-8707-21741c00d262',
name: 'Save dashboard layout',
component: SaveDashboardLayout,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const SaveRecordPageLayout = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '7956ba3a-fd4d-466e-96b5-9e3e8b637c44',
name: 'Save record page layout',
component: SaveRecordPageLayout,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const AddToFavorites = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '5694b053-1f42-416c-a26a-375f826aa9b3',
name: 'Add to favorites',
component: AddToFavorites,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const CreateNewRecord = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '1e6e57bb-89c4-482e-bf44-d62ad1c05d2f',
name: 'Create new record',
component: CreateNewRecord,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const DeleteMultipleRecords = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '35c25fd2-6060-440a-b734-aa3016c11f47',
name: 'Delete multiple records',
component: DeleteMultipleRecords,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const DeleteSingleRecord = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: 'cbc7e92f-d4fe-4956-ba66-a81b7f9713c6',
name: 'Delete single record',
component: DeleteSingleRecord,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const DestroyMultipleRecords = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: 'f647d17d-b4ae-4a4c-99a9-4e5e4e3068a2',
name: 'Destroy multiple records',
component: DestroyMultipleRecords,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const DestroySingleRecord = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '212d9e7d-e149-417b-9a0f-5024835349e6',
name: 'Destroy single record',
component: DestroySingleRecord,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const MergeMultipleRecords = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '9c6757aa-ecdd-4b21-8466-fb1e8f0bfcb8',
name: 'Merge multiple records',
component: MergeMultipleRecords,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const RemoveFromFavorites = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '21105d6a-33a2-4ae3-8edb-c33c38d2e091',
name: 'Remove from favorites',
component: RemoveFromFavorites,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const RestoreMultipleRecords = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '105ff959-838c-44bc-8a92-60d61093ebc4',
name: 'Restore multiple records',
component: RestoreMultipleRecords,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const RestoreSingleRecord = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '88262225-1253-4dfe-9bf7-563b73e6d9ea',
name: 'Restore single record',
component: RestoreSingleRecord,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const StopWorkflowRun = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '110fb676-2a09-4ac0-bd19-7261bc588967',
name: 'Stop workflow run',
component: StopWorkflowRun,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const UseAsDraftWorkflowVersion = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: 'cb11ab5c-974a-4942-bb8a-77efa6b5bb26',
name: 'Use as draft (workflow version)',
component: UseAsDraftWorkflowVersion,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const ActivateWorkflow = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '97b89dc4-cef9-4439-9358-35c98616eb1e',
name: 'Activate workflow',
component: ActivateWorkflow,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const DeactivateWorkflow = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: 'cbf92077-1892-47e0-9435-14ea8f50a510',
name: 'Deactivate workflow',
component: DeactivateWorkflow,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const DiscardDraftWorkflow = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '972dc871-7f9c-4035-957c-e6662f4df7c5',
name: 'Discard draft workflow',
component: DiscardDraftWorkflow,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const DuplicateWorkflow = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '566a48c5-5342-446f-a041-9db59bcdab6b',
name: 'Duplicate workflow',
component: DuplicateWorkflow,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const TestWorkflow = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '39e9aa5a-cacc-4543-9053-f1fbf923e170',
name: 'Test workflow',
component: TestWorkflow,
isHeadless: true,
});
@@ -1,11 +0,0 @@
import { Command, defineFrontComponent } from 'twenty-sdk';
// TODO: implement execute logic
const TidyUpWorkflow = () => <Command execute={async () => {}} />;
export default defineFrontComponent({
universalIdentifier: '3dac631e-dfb7-4570-ac27-15d98ee8ec43',
name: 'Tidy up workflow',
component: TidyUpWorkflow,
isHeadless: true,
});
@@ -12,18 +12,10 @@ export const STANDARD_FRONT_COMPONENT_BUILD_MANIFEST = {
"builtComponentPath": "dashboard/cancel-dashboard-layout.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"duplicateDashboard": {
"builtComponentPath": "dashboard/duplicate-dashboard.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"editDashboardLayout": {
"builtComponentPath": "dashboard/edit-dashboard-layout.front-component.mjs",
"builtComponentChecksum": "2d8373187861b98be2b46922308787b0"
},
"saveDashboardLayout": {
"builtComponentPath": "dashboard/save-dashboard-layout.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"goToCompanies": {
"builtComponentPath": "navigation/go-to-companies.front-component.mjs",
"builtComponentChecksum": "26828ed9d08676e9ea1ab53ca4379c8e"
@@ -68,38 +60,10 @@ export const STANDARD_FRONT_COMPONENT_BUILD_MANIFEST = {
"builtComponentPath": "page-layout/edit-record-page-layout.front-component.mjs",
"builtComponentChecksum": "2d8373187861b98be2b46922308787b0"
},
"saveRecordPageLayout": {
"builtComponentPath": "page-layout/save-record-page-layout.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"addToFavorites": {
"builtComponentPath": "record/add-to-favorites.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"createNewRecord": {
"builtComponentPath": "record/create-new-record.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"createNewView": {
"builtComponentPath": "record/create-new-view.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"deleteMultipleRecords": {
"builtComponentPath": "record/delete-multiple-records.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"deleteSingleRecord": {
"builtComponentPath": "record/delete-single-record.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"destroyMultipleRecords": {
"builtComponentPath": "record/destroy-multiple-records.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"destroySingleRecord": {
"builtComponentPath": "record/destroy-single-record.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"exportFromRecordIndex": {
"builtComponentPath": "record/export-from-record-index.front-component.mjs",
"builtComponentChecksum": "2d8373187861b98be2b46922308787b0"
@@ -128,10 +92,6 @@ export const STANDARD_FRONT_COMPONENT_BUILD_MANIFEST = {
"builtComponentPath": "record/import-records.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"mergeMultipleRecords": {
"builtComponentPath": "record/merge-multiple-records.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"navigateToNextRecord": {
"builtComponentPath": "record/navigate-to-next-record.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
@@ -140,18 +100,6 @@ export const STANDARD_FRONT_COMPONENT_BUILD_MANIFEST = {
"builtComponentPath": "record/navigate-to-previous-record.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"removeFromFavorites": {
"builtComponentPath": "record/remove-from-favorites.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"restoreMultipleRecords": {
"builtComponentPath": "record/restore-multiple-records.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"restoreSingleRecord": {
"builtComponentPath": "record/restore-single-record.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"seeDeletedRecords": {
"builtComponentPath": "record/see-deleted-records.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
@@ -184,10 +132,6 @@ export const STANDARD_FRONT_COMPONENT_BUILD_MANIFEST = {
"builtComponentPath": "workflow-run/see-workflow-workflow-run.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"stopWorkflowRun": {
"builtComponentPath": "workflow-run/stop-workflow-run.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"seeRunsWorkflowVersion": {
"builtComponentPath": "workflow-version/see-runs-workflow-version.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
@@ -200,30 +144,10 @@ export const STANDARD_FRONT_COMPONENT_BUILD_MANIFEST = {
"builtComponentPath": "workflow-version/see-workflow-workflow-version.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"useAsDraftWorkflowVersion": {
"builtComponentPath": "workflow-version/use-as-draft-workflow-version.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"activateWorkflow": {
"builtComponentPath": "workflow/activate-workflow.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"addNodeWorkflow": {
"builtComponentPath": "workflow/add-node-workflow.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"deactivateWorkflow": {
"builtComponentPath": "workflow/deactivate-workflow.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"discardDraftWorkflow": {
"builtComponentPath": "workflow/discard-draft-workflow.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"duplicateWorkflow": {
"builtComponentPath": "workflow/duplicate-workflow.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"seeActiveVersionWorkflow": {
"builtComponentPath": "workflow/see-active-version-workflow.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
@@ -235,13 +159,5 @@ export const STANDARD_FRONT_COMPONENT_BUILD_MANIFEST = {
"seeVersionsWorkflow": {
"builtComponentPath": "workflow/see-versions-workflow.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"testWorkflow": {
"builtComponentPath": "workflow/test-workflow.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
},
"tidyUpWorkflow": {
"builtComponentPath": "workflow/tidy-up-workflow.front-component.mjs",
"builtComponentChecksum": "1233c1c6b371e623c29b58aa3ca9dfec"
}
} as const;