dashboard workspace standard seeds (#16962)

# Introduction

In this pull request we're introducing new standard page layout, tabs
and widget ( 1 page layout, 1 tab and 8 widgets ) and also a new
opportunity field

Also now prefilling new records, 6 opportunities and a dashboard.

## Standard declaration
### New workspace creation
Relies on existing standard declaration builder

### Backfill command
We've been hacking through the standard builder in order to extract only
the standard page layout entities, updated their entity dependencies to
match the workspace ids so the validation passes


## Remark
- Refactored the `PageLayoutWidget` configuration type to be dynamically
typed through a generic discriminated union

---------

Co-authored-by: prastoin <paul@twenty.com>
This commit is contained in:
nitin
2026-01-12 15:04:15 +05:30
committed by GitHub
parent 89af749b94
commit 46e5420d20
63 changed files with 4930 additions and 101 deletions
@@ -45,7 +45,7 @@ export const deleteFlatEntityFromFlatEntityMapsOrThrow = <
);
if (
isDefined(stillPresentUniversalIdentifiers) ||
!isDefined(stillPresentUniversalIdentifiers) ||
isEmpty(stillPresentUniversalIdentifiers)
) {
return undefined;
@@ -0,0 +1,37 @@
import { isDefined } from 'twenty-shared/utils';
import {
FlatEntityMapsException,
FlatEntityMapsExceptionCode,
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity.type';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
export type FindFlatEntityByUniversalIdentifierOrThrowArgs<
T extends SyncableFlatEntity,
> = {
flatEntityMaps: FlatEntityMaps<T>;
universalIdentifier: string;
};
export const findFlatEntityByUniversalIdentifierOrThrow = <
T extends SyncableFlatEntity,
>({
flatEntityMaps,
universalIdentifier,
}: FindFlatEntityByUniversalIdentifierOrThrowArgs<T>): T => {
const flatEntity = findFlatEntityByUniversalIdentifier({
flatEntityMaps,
universalIdentifier,
});
if (!isDefined(flatEntity)) {
throw new FlatEntityMapsException(
`Could not find flat entity with universal identifier ${universalIdentifier}`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
return flatEntity;
};
@@ -1,4 +1,7 @@
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity.type';
import { type PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
export type FlatPageLayoutWidget = FlatEntityFrom<PageLayoutWidgetEntity>;
export type FlatPageLayoutWidget<
T extends WidgetConfigurationType = WidgetConfigurationType,
> = FlatEntityFrom<PageLayoutWidgetEntity<T>>;
@@ -0,0 +1,12 @@
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
export function isFlatPageLayoutWidgetConfigurationOfType<
Widget extends FlatPageLayoutWidget<WidgetConfigurationType>,
Type extends WidgetConfigurationType,
>(
widget: Pick<Widget, 'configuration'>,
type: Type,
): widget is Widget & FlatPageLayoutWidget<Type> {
return widget.configuration.configurationType === type;
}
@@ -1,24 +1,48 @@
import { AggregateChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/aggregate-chart-configuration.dto';
import { BarChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/bar-chart-configuration.dto';
import { CalendarConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/calendar-configuration.dto';
import { EmailsConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/emails-configuration.dto';
import { FieldConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/field-configuration.dto';
import { FieldRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/field-rich-text-configuration.dto';
import { FieldsConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/fields-configuration.dto';
import { FilesConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/files-configuration.dto';
import { GaugeChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/gauge-chart-configuration.dto';
import { IframeConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/iframe-configuration.dto';
import { LineChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto';
import { NotesConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/notes-configuration.dto';
import { PieChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto';
import { StandaloneRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { TasksConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/tasks-configuration.dto';
import { TimelineConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/timeline-configuration.dto';
import { ViewConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/view-configuration.dto';
import { WorkflowConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/workflow-configuration.dto';
import { WorkflowRunConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/workflow-run-configuration.dto';
import { WorkflowVersionConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/workflow-version-configuration.dto';
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { type PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
export const ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE =
{
[WidgetConfigurationType.AGGREGATE_CHART]: AggregateChartConfigurationDTO,
[WidgetConfigurationType.BAR_CHART]: BarChartConfigurationDTO,
[WidgetConfigurationType.GAUGE_CHART]: GaugeChartConfigurationDTO,
[WidgetConfigurationType.IFRAME]: IframeConfigurationDTO,
[WidgetConfigurationType.LINE_CHART]: LineChartConfigurationDTO,
[WidgetConfigurationType.PIE_CHART]: PieChartConfigurationDTO,
[WidgetConfigurationType.STANDALONE_RICH_TEXT]:
StandaloneRichTextConfigurationDTO,
// TODO: Remove the partial when we have all the widget configuration types in the backend
} as const satisfies Partial<{
AGGREGATE_CHART: AggregateChartConfigurationDTO,
STANDALONE_RICH_TEXT: StandaloneRichTextConfigurationDTO,
PIE_CHART: PieChartConfigurationDTO,
LINE_CHART: LineChartConfigurationDTO,
IFRAME: IframeConfigurationDTO,
GAUGE_CHART: GaugeChartConfigurationDTO,
BAR_CHART: BarChartConfigurationDTO,
CALENDAR: CalendarConfigurationDTO,
EMAILS: EmailsConfigurationDTO,
FIELD: FieldConfigurationDTO,
FIELD_RICH_TEXT: FieldRichTextConfigurationDTO,
FIELDS: FieldsConfigurationDTO,
FILES: FilesConfigurationDTO,
NOTES: NotesConfigurationDTO,
TASKS: TasksConfigurationDTO,
TIMELINE: TimelineConfigurationDTO,
VIEW: ViewConfigurationDTO,
WORKFLOW: WorkflowConfigurationDTO,
WORKFLOW_RUN: WorkflowRunConfigurationDTO,
WORKFLOW_VERSION: WorkflowVersionConfigurationDTO,
} as const satisfies {
[P in WidgetConfigurationType]: new () => PageLayoutWidgetConfigurationBase;
}>;
};
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('CalendarConfiguration')
export class CalendarConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.CALENDAR])
@IsNotEmpty()
configurationType: WidgetConfigurationType.CALENDAR;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('EmailsConfiguration')
export class EmailsConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.EMAILS])
@IsNotEmpty()
configurationType: WidgetConfigurationType.EMAILS;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('FieldConfiguration')
export class FieldConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.FIELD])
@IsNotEmpty()
configurationType: WidgetConfigurationType.FIELD;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('FieldRichTextConfiguration')
export class FieldRichTextConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.FIELD_RICH_TEXT])
@IsNotEmpty()
configurationType: WidgetConfigurationType.FIELD_RICH_TEXT;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('FieldsConfiguration')
export class FieldsConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.FIELDS])
@IsNotEmpty()
configurationType: WidgetConfigurationType.FIELDS;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('FilesConfiguration')
export class FilesConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.FILES])
@IsNotEmpty()
configurationType: WidgetConfigurationType.FILES;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('NotesConfiguration')
export class NotesConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.NOTES])
@IsNotEmpty()
configurationType: WidgetConfigurationType.NOTES;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('TasksConfiguration')
export class TasksConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.TASKS])
@IsNotEmpty()
configurationType: WidgetConfigurationType.TASKS;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('TimelineConfiguration')
export class TimelineConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.TIMELINE])
@IsNotEmpty()
configurationType: WidgetConfigurationType.TIMELINE;
}
@@ -0,0 +1,14 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('ViewConfiguration')
export class ViewConfigurationDTO implements PageLayoutWidgetConfigurationBase {
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.VIEW])
@IsNotEmpty()
configurationType: WidgetConfigurationType.VIEW;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('WorkflowConfiguration')
export class WorkflowConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.WORKFLOW])
@IsNotEmpty()
configurationType: WidgetConfigurationType.WORKFLOW;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('WorkflowRunConfiguration')
export class WorkflowRunConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.WORKFLOW_RUN])
@IsNotEmpty()
configurationType: WidgetConfigurationType.WORKFLOW_RUN;
}
@@ -0,0 +1,16 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsIn, IsNotEmpty } from 'class-validator';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type';
@ObjectType('WorkflowVersionConfiguration')
export class WorkflowVersionConfigurationDTO
implements PageLayoutWidgetConfigurationBase
{
@Field(() => WidgetConfigurationType)
@IsIn([WidgetConfigurationType.WORKFLOW_VERSION])
@IsNotEmpty()
configurationType: WidgetConfigurationType.WORKFLOW_VERSION;
}
@@ -9,15 +9,16 @@ import {
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
type Relation,
UpdateDateColumn,
type Relation,
} from 'typeorm';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
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 { type AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type';
import { type GridPosition } from 'src/engine/metadata-modules/page-layout-widget/types/grid-position.type';
import { PageLayoutWidgetConfigurationTypeSettings } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configuration.type';
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
@Entity({ name: 'pageLayoutWidget', schema: 'core' })
@@ -27,7 +28,10 @@ import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-enti
['workspaceId', 'pageLayoutTabId'],
{ where: '"deletedAt" IS NULL' },
)
export class PageLayoutWidgetEntity
export class PageLayoutWidgetEntity<
TWidgetConfigurationType extends
WidgetConfigurationType = WidgetConfigurationType,
>
extends SyncableEntity
implements Required<PageLayoutWidgetEntity>
{
@@ -68,7 +72,7 @@ export class PageLayoutWidgetEntity
gridPosition: GridPosition;
@Column({ type: 'jsonb', nullable: false })
configuration: AllPageLayoutWidgetConfiguration;
configuration: PageLayoutWidgetConfigurationTypeSettings<TWidgetConfigurationType>;
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@@ -1,5 +1,5 @@
import { type ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE } from 'src/engine/metadata-modules/page-layout-widget/constants/all-widget-configuration-type-validator-by-widget-configuration-type.constant';
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { type PageLayoutWidgetConfigurationTypeSettings } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configuration.type';
export type AllPageLayoutWidgetConfiguration = InstanceType<
(typeof ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE)[keyof typeof ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE]
>;
export type AllPageLayoutWidgetConfiguration =
PageLayoutWidgetConfigurationTypeSettings<WidgetConfigurationType>;
@@ -0,0 +1,8 @@
import { type ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE } from 'src/engine/metadata-modules/page-layout-widget/constants/all-widget-configuration-type-validator-by-widget-configuration-type.constant';
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
export type PageLayoutWidgetConfigurationTypeSettings<
T extends WidgetConfigurationType,
> = InstanceType<
(typeof ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE)[T]
>;