From 28de78bfc03cb6bb291a000eef861cb1da130c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:34:02 +0200 Subject: [PATCH] Create PageLayoutEntity (#14099) Closes https://github.com/twentyhq/core-team-issues/issues/1389 --- .../1756289507594-createPageLayoutEntity.ts | 37 +++++++++++ .../engine/core-modules/core-engine.module.ts | 4 +- .../entities/page-layout.entity.ts | 65 +++++++++++++++++++ .../enums/page-layout-type.enum.ts | 5 ++ .../page-layout/page-layout.module.ts | 10 +++ 5 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/common/1756289507594-createPageLayoutEntity.ts create mode 100644 packages/twenty-server/src/engine/core-modules/page-layout/entities/page-layout.entity.ts create mode 100644 packages/twenty-server/src/engine/core-modules/page-layout/enums/page-layout-type.enum.ts create mode 100644 packages/twenty-server/src/engine/core-modules/page-layout/page-layout.module.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1756289507594-createPageLayoutEntity.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1756289507594-createPageLayoutEntity.ts new file mode 100644 index 0000000000..adf6ccee31 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1756289507594-createPageLayoutEntity.ts @@ -0,0 +1,37 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +export class CreatePageLayoutEntity1756289507594 implements MigrationInterface { + name = 'CreatePageLayoutEntity1756289507594'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "core"."pageLayout_type_enum" AS ENUM('RECORD_INDEX', 'RECORD_PAGE', 'DASHBOARD')`, + ); + await queryRunner.query( + `CREATE TABLE "core"."pageLayout" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "workspaceId" uuid NOT NULL, "type" "core"."pageLayout_type_enum" NOT NULL DEFAULT 'RECORD_PAGE', "objectMetadataId" uuid, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_5028ccb46ffa0c945d2f9246dfa" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_PAGE_LAYOUT_WORKSPACE_ID_OBJECT_METADATA_ID" ON "core"."pageLayout" ("workspaceId", "objectMetadataId") `, + ); + await queryRunner.query( + `ALTER TABLE "core"."pageLayout" ADD CONSTRAINT "FK_760ec8b78721991220b76accd55" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."pageLayout" ADD CONSTRAINT "FK_dd63ca42614bacf58971aabdcbb" FOREIGN KEY ("objectMetadataId") REFERENCES "core"."objectMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."pageLayout" DROP CONSTRAINT "FK_dd63ca42614bacf58971aabdcbb"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."pageLayout" DROP CONSTRAINT "FK_760ec8b78721991220b76accd55"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_PAGE_LAYOUT_WORKSPACE_ID_OBJECT_METADATA_ID"`, + ); + await queryRunner.query(`DROP TABLE "core"."pageLayout"`); + await queryRunner.query(`DROP TYPE "core"."pageLayout_type_enum"`); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts index 55c5c7b860..f22c778a5f 100644 --- a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts +++ b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts @@ -22,6 +22,7 @@ import { exceptionHandlerModuleFactory } from 'src/engine/core-modules/exception import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { FileStorageModule } from 'src/engine/core-modules/file-storage/file-storage.module'; import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service'; +import { GeoMapModule } from 'src/engine/core-modules/geo-map/geo-map-module'; import { HealthModule } from 'src/engine/core-modules/health/health.module'; import { ImapSmtpCaldavModule } from 'src/engine/core-modules/imap-smtp-caldav-connection/imap-smtp-caldav-connection.module'; import { LabModule } from 'src/engine/core-modules/lab/lab.module'; @@ -31,6 +32,7 @@ import { MessageQueueModule } from 'src/engine/core-modules/message-queue/messag import { messageQueueModuleFactory } from 'src/engine/core-modules/message-queue/message-queue.module-factory'; import { TimelineMessagingModule } from 'src/engine/core-modules/messaging/timeline-messaging.module'; import { OpenApiModule } from 'src/engine/core-modules/open-api/open-api.module'; +import { PageLayoutModule } from 'src/engine/core-modules/page-layout/page-layout.module'; import { PostgresCredentialsModule } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.module'; import { RedisClientModule } from 'src/engine/core-modules/redis-client/redis-client.module'; import { RedisClientService } from 'src/engine/core-modules/redis-client/redis-client.service'; @@ -50,7 +52,6 @@ import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.mod import { RoleModule } from 'src/engine/metadata-modules/role/role.module'; import { SubscriptionsModule } from 'src/engine/subscriptions/subscriptions.module'; import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module'; -import { GeoMapModule } from 'src/engine/core-modules/geo-map/geo-map-module'; import { AuditModule } from './audit/audit.module'; import { ClientConfigModule } from './client-config/client-config.module'; @@ -120,6 +121,7 @@ import { FileModule } from './file/file.module'; ApiKeyModule, WebhookModule, CoreViewModule, + PageLayoutModule, ], exports: [ AuditModule, diff --git a/packages/twenty-server/src/engine/core-modules/page-layout/entities/page-layout.entity.ts b/packages/twenty-server/src/engine/core-modules/page-layout/entities/page-layout.entity.ts new file mode 100644 index 0000000000..baed1396c4 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/page-layout/entities/page-layout.entity.ts @@ -0,0 +1,65 @@ +import { + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + Index, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + Relation, + UpdateDateColumn, +} from 'typeorm'; + +import { PageLayoutType } from 'src/engine/core-modules/page-layout/enums/page-layout-type.enum'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; + +@Entity({ name: 'pageLayout', schema: 'core' }) +@Index('IDX_PAGE_LAYOUT_WORKSPACE_ID_OBJECT_METADATA_ID', [ + 'workspaceId', + 'objectMetadataId', +]) +export class PageLayoutEntity implements Required { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ nullable: false }) + name: string; + + @Column({ nullable: false, type: 'uuid' }) + workspaceId: string; + + @ManyToOne(() => Workspace, { + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'workspaceId' }) + workspace: Relation; + + @Column({ + type: 'enum', + enum: Object.values(PageLayoutType), + nullable: false, + default: PageLayoutType.RECORD_PAGE, + }) + type: PageLayoutType; + + @Column({ nullable: true, type: 'uuid' }) + objectMetadataId: string | null; + + @ManyToOne(() => ObjectMetadataEntity, { + onDelete: 'CASCADE', + nullable: true, + }) + @JoinColumn({ name: 'objectMetadataId' }) + objectMetadata: Relation | null; + + @CreateDateColumn({ type: 'timestamptz' }) + createdAt: Date; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt: Date; + + @DeleteDateColumn({ type: 'timestamptz' }) + deletedAt: Date | null; +} diff --git a/packages/twenty-server/src/engine/core-modules/page-layout/enums/page-layout-type.enum.ts b/packages/twenty-server/src/engine/core-modules/page-layout/enums/page-layout-type.enum.ts new file mode 100644 index 0000000000..66952029ce --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/page-layout/enums/page-layout-type.enum.ts @@ -0,0 +1,5 @@ +export enum PageLayoutType { + RECORD_INDEX = 'RECORD_INDEX', + RECORD_PAGE = 'RECORD_PAGE', + DASHBOARD = 'DASHBOARD', +} diff --git a/packages/twenty-server/src/engine/core-modules/page-layout/page-layout.module.ts b/packages/twenty-server/src/engine/core-modules/page-layout/page-layout.module.ts new file mode 100644 index 0000000000..3c6874f5e3 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/page-layout/page-layout.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { PageLayoutEntity } from 'src/engine/core-modules/page-layout/entities/page-layout.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([PageLayoutEntity], 'core')], + exports: [], +}) +export class PageLayoutModule {}