From 07a4cf2d266b76d25ac99eb3f21699e2c2cb3fc3 Mon Sep 17 00:00:00 2001 From: Baptiste Devessier Date: Mon, 23 Mar 2026 17:49:05 +0100 Subject: [PATCH] Ensure command backfills all relations as Field widget (#18858) \+ add missing Field widget for workflow relations Copies the logic of the injectRelationWidgetsIntoLayout front-end function --- .../field/utils/assertFieldWidgetOrThrow.ts | 5 +- .../1-20-backfill-field-widgets.command.ts | 411 +++++++++++++++--- ...adata-related-entity-ids.util.spec.ts.snap | 22 +- ...tandard-workflow-run-page-layout.config.ts | 12 + ...ard-workflow-version-page-layout.config.ts | 12 + 5 files changed, 392 insertions(+), 70 deletions(-) diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/utils/assertFieldWidgetOrThrow.ts b/packages/twenty-front/src/modules/page-layout/widgets/field/utils/assertFieldWidgetOrThrow.ts index bdd06fa19e6..12fd605b09e 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/utils/assertFieldWidgetOrThrow.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/utils/assertFieldWidgetOrThrow.ts @@ -1,7 +1,7 @@ import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; -import { isNonEmptyString } from '@sniptt/guards'; import { assertIsDefinedOrThrow } from 'twenty-shared/utils'; +import { WidgetConfigurationType } from '~/generated-metadata/graphql'; type AssertFieldWidgetOrThrow = ( widget: PageLayoutWidget, @@ -18,8 +18,7 @@ export const assertFieldWidgetOrThrow: AssertFieldWidgetOrThrow = ( ); if ( - !isNonEmptyString(widget.configuration.__typename) || - widget.configuration.__typename !== 'FieldConfiguration' + widget.configuration.configurationType !== WidgetConfigurationType.FIELD ) { throw new Error( `Expected FieldConfiguration but got ${widget.configuration.__typename}`, diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-backfill-field-widgets.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-backfill-field-widgets.command.ts index c7873496a29..a8741e4dd64 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-backfill-field-widgets.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-backfill-field-widgets.command.ts @@ -1,24 +1,84 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Command } from 'nest-commander'; +import { + CoreObjectNameSingular, + FieldMetadataType, + PageLayoutTabLayoutMode, +} from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; +import { v4 } from 'uuid'; import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; +import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type'; +import { FieldDisplayMode } from 'src/engine/metadata-modules/page-layout-widget/enums/field-display-mode.enum'; import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; +import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; -import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant'; +import { + GRID_POSITIONS, + VERTICAL_LIST_LAYOUT_POSITIONS, +} from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout-tabs.template'; import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; +const HOME_TAB_POSITION = 10; + +const isActivityTargetField = ( + fieldName: string, + objectNameSingular: string, +): boolean => + (objectNameSingular === CoreObjectNameSingular.Note && + fieldName === 'noteTargets') || + (objectNameSingular === CoreObjectNameSingular.Task && + fieldName === 'taskTargets'); + +const isJunctionRelationField = (field: FlatFieldMetadata): boolean => { + const settings = field.settings as + | { junctionTargetFieldId?: string } + | undefined; + + return ( + isDefined(settings?.junctionTargetFieldId) && + typeof settings?.junctionTargetFieldId === 'string' && + settings.junctionTargetFieldId.length > 0 + ); +}; + +const isRelationTargetAvailable = ( + targetObject: FlatObjectMetadata | undefined, +): boolean => { + if (!isDefined(targetObject)) { + return false; + } + + if (targetObject.isRemote) { + return false; + } + + if ( + targetObject.isSystem && + targetObject.nameSingular !== CoreObjectNameSingular.WorkspaceMember + ) { + return false; + } + + return true; +}; + @Command({ name: 'upgrade:1-20:backfill-field-widgets', description: - 'Backfill FIELD widgets for standard object relation fields in existing page layouts', + 'Backfill FIELD widgets for relation fields in existing page layouts', }) export class BackfillFieldWidgetsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { constructor( @@ -43,50 +103,254 @@ export class BackfillFieldWidgetsCommand extends ActiveOrSuspendedWorkspacesMigr `${isDryRun ? '[DRY RUN] ' : ''}Starting backfill of FIELD widgets for workspace ${workspaceId}`, ); - const { twentyStandardFlatApplication } = + const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( { workspaceId }, ); - const { allFlatEntityMaps: standardAllFlatEntityMaps } = - computeTwentyStandardApplicationAllFlatEntityMaps({ - shouldIncludeRecordPageLayouts: true, - now: new Date().toISOString(), - workspaceId, - twentyStandardApplicationId: twentyStandardFlatApplication.id, - }); + const { + flatObjectMetadataMaps, + flatFieldMetadataMaps, + flatPageLayoutMaps, + flatPageLayoutTabMaps, + flatPageLayoutWidgetMaps, + } = await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'flatObjectMetadataMaps', + 'flatFieldMetadataMaps', + 'flatPageLayoutMaps', + 'flatPageLayoutTabMaps', + 'flatPageLayoutWidgetMaps', + ]); - const standardFieldWidgets = Object.values( - standardAllFlatEntityMaps.flatPageLayoutWidgetMaps.byUniversalIdentifier, - ) - .filter(isDefined) - .filter( - (widget) => - widget.configuration?.configurationType === - WidgetConfigurationType.FIELD, - ); + // Build a set of fieldMetadataIds that already have a FIELD widget + const existingFieldWidgetFieldIds = new Set(); - if (standardFieldWidgets.length === 0) { - this.logger.log( - `No FIELD widgets found in standard configs for workspace ${workspaceId}`, - ); - - return; + for (const widget of Object.values( + flatPageLayoutWidgetMaps.byUniversalIdentifier, + )) { + if ( + isDefined(widget) && + widget.configuration?.configurationType === + WidgetConfigurationType.FIELD && + isDefined(widget.configuration.fieldMetadataId) + ) { + existingFieldWidgetFieldIds.add(widget.configuration.fieldMetadataId); + } } - const { flatPageLayoutWidgetMaps: existingWidgetMaps } = - await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'flatPageLayoutWidgetMaps', - ]); + // Build object ID → objectMetadata map + const objectById = new Map(); - const widgetsToCreate = standardFieldWidgets.filter( - (widget) => - !isDefined( - existingWidgetMaps.byUniversalIdentifier[widget.universalIdentifier], - ), - ); + for (const obj of Object.values( + flatObjectMetadataMaps.byUniversalIdentifier, + )) { + if (isDefined(obj)) { + objectById.set(obj.id, obj); + } + } - if (widgetsToCreate.length === 0) { + // Build object ID → RECORD_PAGE layout map + const recordPageLayoutByObjectId = new Map< + string, + { id: string; universalIdentifier: string } + >(); + + for (const layout of Object.values( + flatPageLayoutMaps.byUniversalIdentifier, + )) { + if ( + isDefined(layout) && + layout.type === PageLayoutType.RECORD_PAGE && + isDefined(layout.objectMetadataId) + ) { + recordPageLayoutByObjectId.set(layout.objectMetadataId, { + id: layout.id, + universalIdentifier: layout.universalIdentifier, + }); + } + } + + // Build pageLayoutId → home tab map + const homeTabByPageLayoutId = new Map< + string, + { + id: string; + universalIdentifier: string; + widgetCount: number; + } + >(); + + for (const tab of Object.values( + flatPageLayoutTabMaps.byUniversalIdentifier, + )) { + if ( + isDefined(tab) && + tab.position === HOME_TAB_POSITION && + tab.layoutMode === PageLayoutTabLayoutMode.VERTICAL_LIST + ) { + homeTabByPageLayoutId.set(tab.pageLayoutId, { + id: tab.id, + universalIdentifier: tab.universalIdentifier, + widgetCount: tab.widgetIds?.length ?? 0, + }); + } + } + + // Group fields by objectMetadataId + const fieldsByObjectId = new Map(); + + // Map morphId → all field IDs sharing that morphId (for dedup) + const fieldIdsByMorphId = new Map(); + + for (const field of Object.values( + flatFieldMetadataMaps.byUniversalIdentifier, + )) { + if (!isDefined(field) || !isDefined(field.objectMetadataId)) { + continue; + } + + const list = fieldsByObjectId.get(field.objectMetadataId) ?? []; + + list.push(field); + fieldsByObjectId.set(field.objectMetadataId, list); + + if ( + field.type === FieldMetadataType.MORPH_RELATION && + isDefined(field.morphId) + ) { + const morphFieldIds = fieldIdsByMorphId.get(field.morphId) ?? []; + + morphFieldIds.push(field.id); + fieldIdsByMorphId.set(field.morphId, morphFieldIds); + } + } + + const now = new Date().toISOString(); + + const standardWidgetsToCreate: FlatPageLayoutWidget[] = []; + const customWidgetsToCreate: FlatPageLayoutWidget[] = []; + const processedMorphIds = new Set(); + + for (const object of objectById.values()) { + const pageLayout = recordPageLayoutByObjectId.get(object.id); + + if (!isDefined(pageLayout)) { + continue; + } + + const homeTab = homeTabByPageLayoutId.get(pageLayout.id); + + if (!isDefined(homeTab)) { + continue; + } + + const flatApplication: FlatApplication = object.isCustom + ? workspaceCustomFlatApplication + : twentyStandardFlatApplication; + + const targetList: FlatPageLayoutWidget[] = object.isCustom + ? customWidgetsToCreate + : standardWidgetsToCreate; + + const fields = fieldsByObjectId.get(object.id) ?? []; + let nextWidgetIndex = homeTab.widgetCount; + + for (const field of fields) { + if ( + field.type !== FieldMetadataType.RELATION && + field.type !== FieldMetadataType.MORPH_RELATION + ) { + continue; + } + + if (isActivityTargetField(field.name, object.nameSingular)) { + continue; + } + + if (isJunctionRelationField(field)) { + continue; + } + + if (field.type === FieldMetadataType.RELATION) { + const targetObject = isDefined(field.relationTargetObjectMetadataId) + ? objectById.get(field.relationTargetObjectMetadataId) + : undefined; + + if (!isRelationTargetAvailable(targetObject)) { + continue; + } + } + + // For morph relations, skip if any sibling field (same morphId) + // already has a widget or was already processed in this run + if ( + field.type === FieldMetadataType.MORPH_RELATION && + isDefined(field.morphId) + ) { + if (processedMorphIds.has(field.morphId)) { + continue; + } + + const siblingFieldIds = fieldIdsByMorphId.get(field.morphId) ?? []; + const alreadyHasWidget = siblingFieldIds.some((id) => + existingFieldWidgetFieldIds.has(id), + ); + + if (alreadyHasWidget) { + continue; + } + + processedMorphIds.add(field.morphId); + } + + if (existingFieldWidgetFieldIds.has(field.id)) { + continue; + } + + const widget: FlatPageLayoutWidget = { + id: v4(), + universalIdentifier: v4(), + applicationId: flatApplication.id, + applicationUniversalIdentifier: flatApplication.universalIdentifier, + workspaceId, + pageLayoutTabId: homeTab.id, + pageLayoutTabUniversalIdentifier: homeTab.universalIdentifier, + title: field.label, + type: WidgetType.FIELD, + gridPosition: { ...GRID_POSITIONS.FULL_WIDTH }, + position: { + ...VERTICAL_LIST_LAYOUT_POSITIONS.SECOND, + index: nextWidgetIndex, + }, + configuration: { + configurationType: WidgetConfigurationType.FIELD, + fieldMetadataId: field.id, + fieldDisplayMode: FieldDisplayMode.CARD, + }, + universalConfiguration: { + configurationType: WidgetConfigurationType.FIELD, + fieldMetadataId: field.universalIdentifier, + fieldDisplayMode: FieldDisplayMode.CARD, + }, + objectMetadataId: object.id, + objectMetadataUniversalIdentifier: object.universalIdentifier, + createdAt: now, + updatedAt: now, + deletedAt: null, + conditionalDisplay: null, + overrides: null, + }; + + targetList.push(widget); + existingFieldWidgetFieldIds.add(field.id); + nextWidgetIndex++; + } + } + + const totalWidgets = + standardWidgetsToCreate.length + customWidgetsToCreate.length; + + if (totalWidgets === 0) { this.logger.log( `All FIELD widgets already exist for workspace ${workspaceId}, skipping`, ); @@ -95,44 +359,73 @@ export class BackfillFieldWidgetsCommand extends ActiveOrSuspendedWorkspacesMigr } this.logger.log( - `Found ${widgetsToCreate.length} FIELD widget(s) to create for workspace ${workspaceId}`, + `Found ${totalWidgets} FIELD widget(s) to create for workspace ${workspaceId} (${standardWidgetsToCreate.length} standard, ${customWidgetsToCreate.length} custom)`, ); if (isDryRun) { this.logger.log( - `[DRY RUN] Would create ${widgetsToCreate.length} FIELD widget(s) for workspace ${workspaceId}`, + `[DRY RUN] Would create ${totalWidgets} FIELD widget(s) for workspace ${workspaceId}`, ); return; } - const validateAndBuildResult = - await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( - { - allFlatEntityOperationByMetadataName: { - pageLayoutWidget: { - flatEntityToCreate: widgetsToCreate, - flatEntityToDelete: [], - flatEntityToUpdate: [], + if (standardWidgetsToCreate.length > 0) { + const result = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + pageLayoutWidget: { + flatEntityToCreate: standardWidgetsToCreate, + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, }, + workspaceId, + applicationUniversalIdentifier: + twentyStandardFlatApplication.universalIdentifier, }, - workspaceId, - applicationUniversalIdentifier: - twentyStandardFlatApplication.universalIdentifier, - }, - ); + ); - if (validateAndBuildResult.status === 'fail') { - this.logger.error( - `Failed to create FIELD widgets:\n${JSON.stringify(validateAndBuildResult, null, 2)}`, - ); - throw new Error( - `Failed to create FIELD widgets for workspace ${workspaceId}`, - ); + if (result.status === 'fail') { + this.logger.error( + `Failed to create standard FIELD widgets:\n${JSON.stringify(result, null, 2)}`, + ); + throw new Error( + `Failed to create standard FIELD widgets for workspace ${workspaceId}`, + ); + } + } + + if (customWidgetsToCreate.length > 0) { + const result = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + pageLayoutWidget: { + flatEntityToCreate: customWidgetsToCreate, + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + }, + workspaceId, + applicationUniversalIdentifier: + workspaceCustomFlatApplication.universalIdentifier, + }, + ); + + if (result.status === 'fail') { + this.logger.error( + `Failed to create custom FIELD widgets:\n${JSON.stringify(result, null, 2)}`, + ); + throw new Error( + `Failed to create custom FIELD widgets for workspace ${workspaceId}`, + ); + } } this.logger.log( - `Successfully created ${widgetsToCreate.length} FIELD widget(s) for workspace ${workspaceId}`, + `Successfully created ${totalWidgets} FIELD widget(s) for workspace ${workspaceId}`, ); } } diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-page-layout-metadata-related-entity-ids.util.spec.ts.snap b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-page-layout-metadata-related-entity-ids.util.spec.ts.snap index 1f6a0393140..9c87a5ba366 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-page-layout-metadata-related-entity-ids.util.spec.ts.snap +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-page-layout-metadata-related-entity-ids.util.spec.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Jest Snapshot v1, https://goo.gl/fbAQLP exports[`getStandardPageLayoutMetadataRelatedEntityIds should return standard page layout metadata related entity ids 1`] = ` { @@ -340,21 +340,24 @@ exports[`getStandardPageLayoutMetadataRelatedEntityIds should return standard pa }, }, "workflowRunRecordPage": { - "id": "00000000-0000-0000-0000-000000000093", + "id": "00000000-0000-0000-0000-000000000094", "tabs": { "flow": { - "id": "00000000-0000-0000-0000-000000000096", + "id": "00000000-0000-0000-0000-000000000098", "widgets": { "workflowRun": { - "id": "00000000-0000-0000-0000-000000000097", + "id": "00000000-0000-0000-0000-000000000099", }, }, }, "home": { - "id": "00000000-0000-0000-0000-000000000094", + "id": "00000000-0000-0000-0000-000000000095", "widgets": { "fields": { - "id": "00000000-0000-0000-0000-000000000095", + "id": "00000000-0000-0000-0000-000000000096", + }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000097", }, }, }, @@ -364,10 +367,10 @@ exports[`getStandardPageLayoutMetadataRelatedEntityIds should return standard pa "id": "00000000-0000-0000-0000-000000000088", "tabs": { "flow": { - "id": "00000000-0000-0000-0000-000000000091", + "id": "00000000-0000-0000-0000-000000000092", "widgets": { "workflowVersion": { - "id": "00000000-0000-0000-0000-000000000092", + "id": "00000000-0000-0000-0000-000000000093", }, }, }, @@ -377,6 +380,9 @@ exports[`getStandardPageLayoutMetadataRelatedEntityIds should return standard pa "fields": { "id": "00000000-0000-0000-0000-000000000090", }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000091", + }, }, }, }, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-run-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-run-page-layout.config.ts index 0475d2bb7b8..4ea526806ab 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-run-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-run-page-layout.config.ts @@ -1,8 +1,11 @@ import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; +import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; import { + GRID_POSITIONS, TAB_PROPS, + VERTICAL_LIST_LAYOUT_POSITIONS, WIDGET_PROPS, } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout-tabs.template'; import { @@ -19,6 +22,15 @@ const WORKFLOW_RUN_PAGE_TABS = { universalIdentifier: '20202020-ac08-4008-8008-a0bcf10a8811', ...WIDGET_PROPS.fields, }, + workflow: { + universalIdentifier: '20202020-ac08-4008-8008-a0bcf10a8813', + title: 'Workflow', + type: WidgetType.FIELD, + gridPosition: GRID_POSITIONS.FULL_WIDTH, + position: VERTICAL_LIST_LAYOUT_POSITIONS.THIRD, + fieldUniversalIdentifier: + STANDARD_OBJECTS.workflowRun.fields.workflow.universalIdentifier, + }, }, }, flow: { diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-version-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-version-page-layout.config.ts index 5e6b29dde02..ad2bfa4416c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-version-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-version-page-layout.config.ts @@ -1,8 +1,11 @@ import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; +import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; import { + GRID_POSITIONS, TAB_PROPS, + VERTICAL_LIST_LAYOUT_POSITIONS, WIDGET_PROPS, } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout-tabs.template'; import { @@ -19,6 +22,15 @@ const WORKFLOW_VERSION_PAGE_TABS = { universalIdentifier: '20202020-ac07-4007-8007-a0bcf10a7711', ...WIDGET_PROPS.fields, }, + workflow: { + universalIdentifier: '20202020-ac07-4007-8007-a0bcf10a7712', + title: 'Workflow', + type: WidgetType.FIELD, + gridPosition: GRID_POSITIONS.FULL_WIDTH, + position: VERTICAL_LIST_LAYOUT_POSITIONS.SECOND, + fieldUniversalIdentifier: + STANDARD_OBJECTS.workflowVersion.fields.workflow.universalIdentifier, + }, }, }, flow: {