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:
@@ -7,12 +7,16 @@ import {
|
||||
type MorphRelationLoaderPayload,
|
||||
type ObjectMetadataLoaderPayload,
|
||||
type RelationLoaderPayload,
|
||||
type ViewFieldGroupsByViewIdLoaderPayload,
|
||||
type ViewFieldsByViewFieldGroupIdLoaderPayload,
|
||||
} from 'src/engine/dataloaders/dataloader.service';
|
||||
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
import { type RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto';
|
||||
import { type IndexFieldMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-field-metadata.dto';
|
||||
import { type IndexMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-metadata.dto';
|
||||
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
import { type ViewFieldGroupDTO } from 'src/engine/metadata-modules/view-field-group/dtos/view-field-group.dto';
|
||||
import { type ViewFieldDTO } from 'src/engine/metadata-modules/view-field/dtos/view-field.dto';
|
||||
|
||||
export interface IDataloaders {
|
||||
relationLoader: DataLoader<RelationLoaderPayload, RelationDTO | null>;
|
||||
@@ -41,4 +45,14 @@ export interface IDataloaders {
|
||||
ObjectMetadataLoaderPayload,
|
||||
ObjectMetadataDTO | null
|
||||
>;
|
||||
|
||||
viewFieldGroupsByViewIdLoader: DataLoader<
|
||||
ViewFieldGroupsByViewIdLoaderPayload,
|
||||
ViewFieldGroupDTO[]
|
||||
>;
|
||||
|
||||
viewFieldsByViewFieldGroupIdLoader: DataLoader<
|
||||
ViewFieldsByViewFieldGroupIdLoaderPayload,
|
||||
ViewFieldDTO[]
|
||||
>;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-module
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findAllOthersMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-all-others-morph-relation-flat-field-metadatas-or-throw.util';
|
||||
import { fromFlatFieldMetadataToFieldMetadataDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-flat-field-metadata-to-field-metadata-dto.util';
|
||||
@@ -26,6 +27,8 @@ import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-fiel
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { fromFlatObjectMetadataToObjectMetadataDto } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-object-metadata-dto.util';
|
||||
import { getMorphNameFromMorphFieldMetadataName } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-morph-name-from-morph-field-metadata-name.util';
|
||||
import { fromFlatViewFieldGroupToViewFieldGroupDto } from 'src/engine/metadata-modules/view-field-group/utils/from-flat-view-field-group-to-view-field-group-dto.util';
|
||||
import { fromFlatViewFieldToViewFieldDto } from 'src/engine/metadata-modules/view-field/utils/from-flat-view-field-to-view-field-dto.util';
|
||||
import { type IndexFieldMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-field-metadata.dto';
|
||||
import { type IndexMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-metadata.dto';
|
||||
import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
@@ -70,6 +73,16 @@ export type ObjectMetadataLoaderPayload = {
|
||||
objectMetadataId: string;
|
||||
};
|
||||
|
||||
export type ViewFieldGroupsByViewIdLoaderPayload = {
|
||||
workspaceId: string;
|
||||
viewId: string;
|
||||
};
|
||||
|
||||
export type ViewFieldsByViewFieldGroupIdLoaderPayload = {
|
||||
workspaceId: string;
|
||||
viewFieldGroupId: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class DataloaderService {
|
||||
constructor(
|
||||
@@ -84,6 +97,10 @@ export class DataloaderService {
|
||||
const indexMetadataLoader = this.createIndexMetadataLoader();
|
||||
const indexFieldMetadataLoader = this.createIndexFieldMetadataLoader();
|
||||
const objectMetadataLoader = this.createObjectMetadataLoader();
|
||||
const viewFieldGroupsByViewIdLoader =
|
||||
this.createViewFieldGroupsByViewIdLoader();
|
||||
const viewFieldsByViewFieldGroupIdLoader =
|
||||
this.createViewFieldsByViewFieldGroupIdLoader();
|
||||
|
||||
return {
|
||||
relationLoader,
|
||||
@@ -92,6 +109,8 @@ export class DataloaderService {
|
||||
indexMetadataLoader,
|
||||
indexFieldMetadataLoader,
|
||||
objectMetadataLoader,
|
||||
viewFieldGroupsByViewIdLoader,
|
||||
viewFieldsByViewFieldGroupIdLoader,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -515,4 +534,74 @@ export class DataloaderService {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private createViewFieldGroupsByViewIdLoader() {
|
||||
return new DataLoader<
|
||||
ViewFieldGroupsByViewIdLoaderPayload,
|
||||
ReturnType<typeof fromFlatViewFieldGroupToViewFieldGroupDto>[]
|
||||
>(async (dataLoaderParams: ViewFieldGroupsByViewIdLoaderPayload[]) => {
|
||||
const workspaceId = dataLoaderParams[0].workspaceId;
|
||||
|
||||
const { flatViewMaps, flatViewFieldGroupMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatViewMaps', 'flatViewFieldGroupMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return dataLoaderParams.map(({ viewId }) => {
|
||||
const flatView = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: viewId,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatView)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return findManyFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityIds: flatView.viewFieldGroupIds,
|
||||
flatEntityMaps: flatViewFieldGroupMaps,
|
||||
})
|
||||
.filter((flatViewFieldGroup) => flatViewFieldGroup.deletedAt === null)
|
||||
.map(fromFlatViewFieldGroupToViewFieldGroupDto);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private createViewFieldsByViewFieldGroupIdLoader() {
|
||||
return new DataLoader<
|
||||
ViewFieldsByViewFieldGroupIdLoaderPayload,
|
||||
ReturnType<typeof fromFlatViewFieldToViewFieldDto>[]
|
||||
>(async (dataLoaderParams: ViewFieldsByViewFieldGroupIdLoaderPayload[]) => {
|
||||
const workspaceId = dataLoaderParams[0].workspaceId;
|
||||
|
||||
const { flatViewFieldGroupMaps, flatViewFieldMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatViewFieldGroupMaps', 'flatViewFieldMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return dataLoaderParams.map(({ viewFieldGroupId }) => {
|
||||
const flatViewFieldGroup = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: viewFieldGroupId,
|
||||
flatEntityMaps: flatViewFieldGroupMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatViewFieldGroup)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return findManyFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityIds: flatViewFieldGroup.viewFieldIds,
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
})
|
||||
.filter((flatViewField) => flatViewField.deletedAt === null)
|
||||
.map(fromFlatViewFieldToViewFieldDto);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user