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
@@ -0,0 +1,65 @@
import { FieldActorSource } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type EntityManager } from 'typeorm';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
import { STANDARD_PAGE_LAYOUTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout.constant';
export const MY_FIRST_DASHBOARD_ID = 'f31ecf3b-87d3-4e8a-a84b-b6f0f3f8c7e2';
export const prefillDashboards = async (
entityManager: EntityManager,
schemaName: string,
flatPageLayoutMaps: FlatEntityMaps<FlatPageLayout>,
) => {
const myFirstDashboardPageLayout = findFlatEntityByUniversalIdentifier({
flatEntityMaps: flatPageLayoutMaps,
universalIdentifier:
STANDARD_PAGE_LAYOUTS.myFirstDashboard.universalIdentifier,
});
if (!isDefined(myFirstDashboardPageLayout)) {
throw new Error(
`Page layout with universalIdentifier '${STANDARD_PAGE_LAYOUTS.myFirstDashboard.universalIdentifier}' not found`,
);
}
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.dashboard`, [
'id',
'title',
'pageLayoutId',
'position',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'createdByContext',
'updatedBySource',
'updatedByWorkspaceMemberId',
'updatedByName',
'updatedByContext',
])
.orIgnore()
.values([
{
id: MY_FIRST_DASHBOARD_ID,
title: 'My First Dashboard',
pageLayoutId: myFirstDashboardPageLayout.id,
position: 0,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
createdByContext: {},
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
updatedByContext: {},
},
])
.returning('*')
.execute();
};
@@ -0,0 +1,179 @@
import { FieldActorSource } from 'twenty-shared/types';
import { type EntityManager } from 'typeorm';
import {
AIRBNB_ID,
ANTHROPIC_ID,
FIGMA_ID,
NOTION_ID,
STRIPE_ID,
} from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-companies';
import {
BRIAN_CHESKY_ID,
DARIO_AMODEI_ID,
DYLAN_FIELD_ID,
IVAN_ZHAO_ID,
PATRICK_COLLISON_ID,
} from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-people';
export const OPPORTUNITY_STRIPE_PLATFORM_MIGRATION_ID =
'822639e5-9bf7-40f1-8882-a11140362339';
export const OPPORTUNITY_ANTHROPIC_AI_MODEL_ID =
'fc747edc-cb00-4078-8d6b-1fab2611dae4';
export const OPPORTUNITY_NOTION_WORKSPACE_ID =
'75de302f-1044-4957-8da4-1f67ebefd52b';
export const OPPORTUNITY_STRIPE_API_INTEGRATION_ID =
'2beb07b0-340c-41d7-be33-5aa91757f329';
export const OPPORTUNITY_AIRBNB_ENTERPRISE_ID =
'9543adcf-ec03-44e2-9233-3c2d3ebae98a';
export const OPPORTUNITY_FIGMA_DESIGN_ID =
'9457f8e9-16ae-43b9-92ee-cbd21f3dded5';
export const prefillOpportunities = async (
entityManager: EntityManager,
schemaName: string,
) => {
const workspaceMember = await entityManager
.createQueryBuilder()
.select('id')
.from(`${schemaName}.workspaceMember`, 'workspaceMember')
.limit(1)
.getRawOne();
const ownerId = workspaceMember?.id ?? null;
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.opportunity`, [
'id',
'name',
'amountAmountMicros',
'amountCurrencyCode',
'closeDate',
'stage',
'position',
'companyId',
'pointOfContactId',
'ownerId',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'updatedBySource',
'updatedByWorkspaceMemberId',
'updatedByName',
])
.orIgnore()
.values([
{
id: OPPORTUNITY_STRIPE_PLATFORM_MIGRATION_ID,
name: 'Platform Migration',
amountAmountMicros: 60000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-01-31T16:25:00.000Z'),
stage: 'PROPOSAL',
position: 1,
companyId: STRIPE_ID,
pointOfContactId: PATRICK_COLLISON_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_ANTHROPIC_AI_MODEL_ID,
name: 'AI Model Training',
amountAmountMicros: 100000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-02-15T16:25:00.000Z'),
stage: 'CUSTOMER',
position: 2,
companyId: ANTHROPIC_ID,
pointOfContactId: DARIO_AMODEI_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_NOTION_WORKSPACE_ID,
name: 'Workspace Expansion',
amountAmountMicros: 45000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-01-20T16:26:00.000Z'),
stage: 'MEETING',
position: 3,
companyId: NOTION_ID,
pointOfContactId: IVAN_ZHAO_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_STRIPE_API_INTEGRATION_ID,
name: 'API Integration Deal',
amountAmountMicros: 75000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-01-25T16:26:00.000Z'),
stage: 'SCREENING',
position: 4,
companyId: STRIPE_ID,
pointOfContactId: PATRICK_COLLISON_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_AIRBNB_ENTERPRISE_ID,
name: 'Enterprise Plan Upgrade',
amountAmountMicros: 50000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-03-10T16:26:00.000Z'),
stage: 'NEW',
position: 5,
companyId: AIRBNB_ID,
pointOfContactId: BRIAN_CHESKY_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_FIGMA_DESIGN_ID,
name: 'Design Partnership',
amountAmountMicros: 30000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-01-15T16:27:00.000Z'),
stage: 'NEW',
position: 6,
companyId: FIGMA_ID,
pointOfContactId: DYLAN_FIELD_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
])
.returning('*')
.execute();
};
@@ -1,5 +1,5 @@
import { type EntityManager } from 'typeorm';
import { FieldActorSource } from 'twenty-shared/types';
import { type EntityManager } from 'typeorm';
import {
AIRBNB_ID,
@@ -9,6 +9,12 @@ import {
STRIPE_ID,
} from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-companies';
export const BRIAN_CHESKY_ID = 'a2e78a5e-338b-46df-8811-fa08c7d19d35'; // Airbnb
export const DARIO_AMODEI_ID = '93c72d2e-e65c-44c4-99ad-f87f50349dcf'; // Anthropic
export const PATRICK_COLLISON_ID = 'edf6d445-13a7-4373-9a47-8f89e8c0a877'; // Stripe
export const DYLAN_FIELD_ID = 'b1e26fa6-c757-4c88-abfa-4b11f5cf3acf'; // Figma
export const IVAN_ZHAO_ID = '7a93d1e5-3f74-4945-8a65-d7f996083f72'; // Notion
export const prefillPeople = async (
entityManager: EntityManager,
schemaName: string,
@@ -17,6 +23,7 @@ export const prefillPeople = async (
.createQueryBuilder()
.insert()
.into(`${schemaName}.person`, [
'id',
'nameFirstName',
'nameLastName',
'city',
@@ -36,6 +43,7 @@ export const prefillPeople = async (
.orIgnore()
.values([
{
id: BRIAN_CHESKY_ID,
nameFirstName: 'Brian',
nameLastName: 'Chesky',
city: 'San Francisco',
@@ -54,6 +62,7 @@ export const prefillPeople = async (
companyId: AIRBNB_ID,
},
{
id: DARIO_AMODEI_ID,
nameFirstName: 'Dario',
nameLastName: 'Amodei',
city: 'San Francisco',
@@ -72,6 +81,7 @@ export const prefillPeople = async (
companyId: ANTHROPIC_ID,
},
{
id: PATRICK_COLLISON_ID,
nameFirstName: 'Patrick',
nameLastName: 'Collison',
city: 'San Francisco',
@@ -90,6 +100,7 @@ export const prefillPeople = async (
companyId: STRIPE_ID,
},
{
id: DYLAN_FIELD_ID,
nameFirstName: 'Dylan',
nameLastName: 'Field',
city: 'San Francisco',
@@ -108,6 +119,7 @@ export const prefillPeople = async (
companyId: FIGMA_ID,
},
{
id: IVAN_ZHAO_ID,
nameFirstName: 'Ivan',
nameLastName: 'Zhao',
city: 'San Francisco',