Files
twenty/packages/twenty-server/src/engine/metadata-modules/flat-view-field-group/services/workspace-flat-view-field-group-map-cache.service.ts
T
Weiko 6aca1dd013 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>
2026-02-11 22:02:58 +01:00

93 lines
4.0 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
import { FlatViewFieldGroupMaps } from 'src/engine/metadata-modules/flat-view-field-group/types/flat-view-field-group-maps.type';
import { fromViewFieldGroupEntityToFlatViewFieldGroup } from 'src/engine/metadata-modules/flat-view-field-group/utils/from-view-field-group-entity-to-flat-view-field-group.util';
import { ViewFieldGroupEntity } from 'src/engine/metadata-modules/view-field-group/entities/view-field-group.entity';
import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
import { createIdToUniversalIdentifierMap } from 'src/engine/workspace-cache/utils/create-id-to-universal-identifier-map.util';
import { regroupEntitiesByRelatedEntityId } from 'src/engine/workspace-cache/utils/regroup-entities-by-related-entity-id';
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
@Injectable()
@WorkspaceCache('flatViewFieldGroupMaps')
export class WorkspaceFlatViewFieldGroupMapCacheService extends WorkspaceCacheProvider<FlatViewFieldGroupMaps> {
constructor(
@InjectRepository(ViewFieldGroupEntity)
private readonly viewFieldGroupRepository: Repository<ViewFieldGroupEntity>,
@InjectRepository(ApplicationEntity)
private readonly applicationRepository: Repository<ApplicationEntity>,
@InjectRepository(ViewEntity)
private readonly viewRepository: Repository<ViewEntity>,
@InjectRepository(ViewFieldEntity)
private readonly viewFieldRepository: Repository<ViewFieldEntity>,
) {
super();
}
async computeForCache(workspaceId: string): Promise<FlatViewFieldGroupMaps> {
const [viewFieldGroups, applications, views, viewFields] =
await Promise.all([
this.viewFieldGroupRepository.find({
where: { workspaceId },
withDeleted: true,
}),
this.applicationRepository.find({
where: { workspaceId },
select: ['id', 'universalIdentifier'],
withDeleted: true,
}),
this.viewRepository.find({
where: { workspaceId },
select: ['id', 'universalIdentifier'],
withDeleted: true,
}),
this.viewFieldRepository.find({
where: { workspaceId },
select: ['id', 'universalIdentifier', 'viewFieldGroupId'],
withDeleted: true,
}),
]);
const applicationIdToUniversalIdentifierMap =
createIdToUniversalIdentifierMap(applications);
const viewIdToUniversalIdentifierMap =
createIdToUniversalIdentifierMap(views);
const viewFieldsByViewFieldGroupId =
regroupEntitiesByRelatedEntityId<'viewField'>({
entities: viewFields,
foreignKey: 'viewFieldGroupId',
});
const flatViewFieldGroupMaps = createEmptyFlatEntityMaps();
for (const viewFieldGroupEntity of viewFieldGroups) {
const flatViewFieldGroup = fromViewFieldGroupEntityToFlatViewFieldGroup({
entity: {
...viewFieldGroupEntity,
viewFields:
viewFieldsByViewFieldGroupId.get(viewFieldGroupEntity.id) || [],
},
applicationIdToUniversalIdentifierMap,
viewIdToUniversalIdentifierMap,
});
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
flatEntity: flatViewFieldGroup,
flatEntityMapsToMutate: flatViewFieldGroupMaps,
});
}
return flatViewFieldGroupMaps;
}
}