View field override (#18572)

## Context
This PR introduces overrides for view fields which will be useful for
page layout FIELDS widgets fields position/groups/visibility override +
restore logic.
This commit is contained in:
Weiko
2026-03-14 12:40:15 +01:00
committed by GitHub
parent 0b0ffcb8fa
commit 48172d60fd
50 changed files with 864 additions and 114 deletions
@@ -69,6 +69,7 @@ export const createStandardViewFieldGroupFlatMetadata = <
name,
position,
isVisible,
overrides: null,
createdAt: now,
updatedAt: now,
deletedAt: null,
@@ -120,6 +120,8 @@ export const createStandardViewFieldFlatMetadata = <
isVisible,
size,
aggregateOperation,
overrides: null,
universalOverrides: null,
createdAt: now,
updatedAt: now,
deletedAt: null,
@@ -13,7 +13,9 @@ export const ALL_JSONB_PROPERTIES_WITH_SERIALIZED_RELATION_BY_METADATA_NAME = {
},
objectMetadata: {},
view: {},
viewField: {},
viewField: {
overrides: 'overrides',
},
viewGroup: {},
viewFieldGroup: {},
viewFilter: {},
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { v4 } from 'uuid';
import { isDefined } from 'twenty-shared/utils';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
@@ -10,6 +11,7 @@ import {
FlatCreateViewFieldAction,
UniversalCreateViewFieldAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field/types/workspace-migration-view-field-action.type';
import { fromUniversalOverridesToViewFieldOverrides } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-field/services/utils/from-universal-overrides-to-view-field-overrides.util';
import {
WorkspaceMigrationActionRunnerArgs,
WorkspaceMigrationActionRunnerContext,
@@ -37,6 +39,13 @@ export class CreateViewFieldActionHandlerService extends WorkspaceMigrationRunne
universalForeignKeyValues: action.flatEntity,
});
const overrides = isDefined(action.flatEntity.universalOverrides)
? fromUniversalOverridesToViewFieldOverrides({
universalOverrides: action.flatEntity.universalOverrides,
flatViewFieldGroupMaps: allFlatEntityMaps.flatViewFieldGroupMaps,
})
: null;
const emptyUniversalForeignKeyAggregators =
getUniversalFlatEntityEmptyForeignKeyAggregators({
metadataName: 'viewField',
@@ -49,6 +58,7 @@ export class CreateViewFieldActionHandlerService extends WorkspaceMigrationRunne
fieldMetadataId,
viewId,
viewFieldGroupId,
overrides,
id: action.id ?? v4(),
applicationId: flatApplication.id,
workspaceId,
@@ -9,6 +9,7 @@ import {
FlatUpdateViewFieldAction,
UniversalUpdateViewFieldAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field/types/workspace-migration-view-field-action.type';
import { fromUniversalOverridesToViewFieldOverrides } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-field/services/utils/from-universal-overrides-to-view-field-overrides.util';
import {
WorkspaceMigrationActionRunnerArgs,
WorkspaceMigrationActionRunnerContext,
@@ -33,11 +34,26 @@ export class UpdateViewFieldActionHandlerService extends WorkspaceMigrationRunne
universalIdentifier: action.universalIdentifier,
});
const update = resolveUniversalUpdateRelationIdentifiersToIds({
metadataName: 'viewField',
universalUpdate: action.update,
allFlatEntityMaps,
});
const { universalOverrides, ...updateWithResolvedForeignKeys } =
resolveUniversalUpdateRelationIdentifiersToIds({
metadataName: 'viewField',
universalUpdate: action.update,
allFlatEntityMaps,
});
const update =
universalOverrides === undefined
? updateWithResolvedForeignKeys
: universalOverrides === null
? { ...updateWithResolvedForeignKeys, overrides: null }
: {
...updateWithResolvedForeignKeys,
overrides: fromUniversalOverridesToViewFieldOverrides({
universalOverrides,
flatViewFieldGroupMaps:
allFlatEntityMaps.flatViewFieldGroupMaps,
}),
};
return {
type: 'update',
@@ -0,0 +1,41 @@
import { type FormatRecordSerializedRelationProperties } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { type FlatViewFieldGroupMaps } from 'src/engine/metadata-modules/flat-view-field-group/types/flat-view-field-group-maps.type';
import { type FlatViewFieldGroup } from 'src/engine/metadata-modules/flat-view-field-group/types/flat-view-field-group.type';
import { type ViewFieldOverrides } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
type UniversalViewFieldOverrides =
FormatRecordSerializedRelationProperties<ViewFieldOverrides>;
export const fromUniversalOverridesToViewFieldOverrides = ({
universalOverrides,
flatViewFieldGroupMaps,
}: {
universalOverrides: UniversalViewFieldOverrides;
flatViewFieldGroupMaps: FlatViewFieldGroupMaps;
}): ViewFieldOverrides => {
const { viewFieldGroupUniversalIdentifier, ...scalarOverrides } =
universalOverrides;
if (!isDefined(viewFieldGroupUniversalIdentifier)) {
return {
...scalarOverrides,
...(viewFieldGroupUniversalIdentifier === null
? { viewFieldGroupId: null }
: {}),
};
}
const flatViewFieldGroup =
findFlatEntityByUniversalIdentifier<FlatViewFieldGroup>({
flatEntityMaps: flatViewFieldGroupMaps,
universalIdentifier: viewFieldGroupUniversalIdentifier,
});
return {
...scalarOverrides,
viewFieldGroupId: flatViewFieldGroup?.id ?? null,
};
};