Introducing view field group syncable entity (#17867)

## Context
Introduces a new viewFieldGroup entity that allows grouping view fields
into sections (e.g. "General", "Additional", "Other") within a view.

The page layout fields widget needs a way to organize fields into
sections. Today, views have no concept of field grouping. This PR
introduces the viewFieldGroup entity which sits between a view and its
viewFields, enabling section-based organization.

<img width="401" height="724" alt="Layout - V2 (customize visibility)"
src="https://github.com/user-attachments/assets/6376e2ab-44db-42bf-9d2c-758f56f6b548"
/>

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Weiko
2026-02-11 22:02:58 +01:00
committed by GitHub
parent ed66fbd71b
commit 6aca1dd013
126 changed files with 3593 additions and 115 deletions
@@ -143,6 +143,7 @@ export const enrichCreateWorkspaceMigrationActionsWithIds = ({
case 'view':
case 'viewField':
case 'viewGroup':
case 'viewFieldGroup':
case 'rowLevelPermissionPredicate':
case 'rowLevelPermissionPredicateGroup':
case 'viewFilterGroup':
@@ -31,6 +31,7 @@ import { WorkspaceMigrationRowLevelPermissionPredicateGroupActionsBuilderService
import { WorkspaceMigrationRowLevelPermissionPredicateActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/row-level-permission-predicate/workspace-migration-row-level-permission-predicate-actions-builder.service';
import { WorkspaceMigrationSkillActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/skill/workspace-migration-skill-actions-builder.service';
import { WorkspaceMigrationViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field/workspace-migration-view-field-actions-builder.service';
import { WorkspaceMigrationViewFieldGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field-group/workspace-migration-view-field-group-actions-builder.service';
import { WorkspaceMigrationViewFilterGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-filter-group/workspace-migration-view-filter-group-actions-builder.service';
import { WorkspaceMigrationViewFilterActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-filter/workspace-migration-view-filter-actions-builder.service';
import { WorkspaceMigrationViewGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-group/workspace-migration-view-group-actions-builder.service';
@@ -47,6 +48,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
private readonly workspaceMigrationViewFilterActionsBuilderService: WorkspaceMigrationViewFilterActionsBuilderService,
private readonly workspaceMigrationViewFilterGroupActionsBuilderService: WorkspaceMigrationViewFilterGroupActionsBuilderService,
private readonly workspaceMigrationViewGroupActionsBuilderService: WorkspaceMigrationViewGroupActionsBuilderService,
private readonly workspaceMigrationViewFieldGroupActionsBuilderService: WorkspaceMigrationViewFieldGroupActionsBuilderService,
private readonly workspaceMigrationLogicFunctionActionsBuilderService: WorkspaceMigrationLogicFunctionActionsBuilderService,
private readonly workspaceMigrationRoleTargetActionsBuilderService: WorkspaceMigrationRoleTargetActionsBuilderService,
private readonly workspaceMigrationFieldActionsBuilderService: WorkspaceMigrationFieldActionsBuilderService,
@@ -150,6 +152,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
flatViewFilterMaps,
flatViewFilterGroupMaps,
flatViewGroupMaps,
flatViewFieldGroupMaps,
flatRowLevelPermissionPredicateMaps,
flatRowLevelPermissionPredicateGroupMaps,
flatRoleMaps,
@@ -324,6 +327,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
flatFieldMetadataMaps:
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
flatViewFieldGroupMaps:
optimisticAllFlatEntityMaps.flatViewFieldGroupMaps,
},
workspaceId,
},
@@ -452,6 +457,41 @@ export class WorkspaceMigrationBuildOrchestratorService {
}
}
if (isDefined(flatViewFieldGroupMaps)) {
const { from: fromFlatViewFieldGroupMaps, to: toFlatViewFieldGroupMaps } =
flatViewFieldGroupMaps;
const viewFieldGroupResult =
await this.workspaceMigrationViewFieldGroupActionsBuilderService.validateAndBuild(
{
additionalCacheDataMaps,
from: fromFlatViewFieldGroupMaps,
to: toFlatViewFieldGroupMaps,
buildOptions,
dependencyOptimisticFlatEntityMaps: {
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
},
workspaceId,
},
);
this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation(
{
allFlatEntityMaps: optimisticAllFlatEntityMaps,
flatEntityMapsAndRelatedFlatEntityMaps:
viewFieldGroupResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps,
},
);
if (viewFieldGroupResult.status === 'fail') {
orchestratorFailureReport.viewFieldGroup.push(
...viewFieldGroupResult.errors,
);
} else {
orchestratorActionsReport.viewFieldGroup = viewFieldGroupResult.actions;
}
}
if (isDefined(flatRowLevelPermissionPredicateGroupMaps)) {
const {
from: fromFlatRowLevelPermissionPredicateGroupMaps,
@@ -1003,6 +1043,9 @@ export class WorkspaceMigrationBuildOrchestratorService {
...aggregatedOrchestratorActionsReport.viewGroup.delete,
...aggregatedOrchestratorActionsReport.viewGroup.create,
...aggregatedOrchestratorActionsReport.viewGroup.update,
...aggregatedOrchestratorActionsReport.viewFieldGroup.delete,
...aggregatedOrchestratorActionsReport.viewFieldGroup.create,
...aggregatedOrchestratorActionsReport.viewFieldGroup.update,
///
// Logic functions