Add Dashboard in Lab, make Calendar available out of lab (#15560)

As per title!
This commit is contained in:
Charles Bochet
2025-11-03 15:40:26 +01:00
committed by GitHub
parent b32ab35db9
commit 20576f2ee7
18 changed files with 51 additions and 131 deletions
@@ -32,11 +32,11 @@ export const PUBLIC_FEATURE_FLAGS: PublicFeatureFlag[] = [
},
},
{
key: FeatureFlagKey.IS_CALENDAR_VIEW_ENABLED,
key: FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED,
metadata: {
label: 'Calendar View',
description: 'View and manage calendar events',
imagePath: 'https://twenty.com/images/lab/is-calendar-view-enabled.png',
label: 'Dashboards',
description: 'Enable dashboards',
imagePath: 'https://twenty.com/images/lab/is-dashboards-enabled.png',
},
},
{
@@ -8,16 +8,11 @@ export enum FeatureFlagKey {
IS_APPLICATION_ENABLED = 'IS_APPLICATION_ENABLED',
IS_IMAP_SMTP_CALDAV_ENABLED = 'IS_IMAP_SMTP_CALDAV_ENABLED',
IS_MORPH_RELATION_ENABLED = 'IS_MORPH_RELATION_ENABLED',
IS_RELATION_CONNECT_ENABLED = 'IS_RELATION_CONNECT_ENABLED',
IS_CORE_VIEW_SYNCING_ENABLED = 'IS_CORE_VIEW_SYNCING_ENABLED',
IS_CORE_VIEW_ENABLED = 'IS_CORE_VIEW_ENABLED',
IS_WORKSPACE_MIGRATION_V2_ENABLED = 'IS_WORKSPACE_MIGRATION_V2_ENABLED',
IS_PAGE_LAYOUT_ENABLED = 'IS_PAGE_LAYOUT_ENABLED',
IS_RECORD_PAGE_LAYOUT_ENABLED = 'IS_RECORD_PAGE_LAYOUT_ENABLED',
IS_MESSAGE_FOLDER_CONTROL_ENABLED = 'IS_MESSAGE_FOLDER_CONTROL_ENABLED',
IS_CALENDAR_VIEW_ENABLED = 'IS_CALENDAR_VIEW_ENABLED',
IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED',
IS_EMAILING_DOMAIN_ENABLED = 'IS_EMAILING_DOMAIN_ENABLED',
IS_DYNAMIC_SEARCH_FIELDS_ENABLED = 'IS_DYNAMIC_SEARCH_FIELDS_ENABLED',
IS_WORKFLOW_RUN_STOPPAGE_ENABLED = 'IS_WORKFLOW_RUN_STOPPAGE_ENABLED',
}
@@ -131,17 +131,12 @@ describe('WorkspaceEntityManager', () => {
IS_APPLICATION_ENABLED: false,
IS_IMAP_SMTP_CALDAV_ENABLED: false,
IS_MORPH_RELATION_ENABLED: false,
IS_RELATION_CONNECT_ENABLED: false,
IS_CORE_VIEW_SYNCING_ENABLED: false,
IS_CORE_VIEW_ENABLED: false,
IS_WORKSPACE_MIGRATION_V2_ENABLED: false,
IS_PAGE_LAYOUT_ENABLED: false,
IS_RECORD_PAGE_LAYOUT_ENABLED: false,
IS_MESSAGE_FOLDER_CONTROL_ENABLED: false,
IS_CALENDAR_VIEW_ENABLED: false,
IS_PUBLIC_DOMAIN_ENABLED: false,
IS_EMAILING_DOMAIN_ENABLED: false,
IS_DYNAMIC_SEARCH_FIELDS_ENABLED: false,
IS_WORKFLOW_RUN_STOPPAGE_ENABLED: false,
},
eventEmitterService: {
@@ -162,17 +157,12 @@ describe('WorkspaceEntityManager', () => {
IS_APPLICATION_ENABLED: false,
IS_IMAP_SMTP_CALDAV_ENABLED: false,
IS_MORPH_RELATION_ENABLED: false,
IS_RELATION_CONNECT_ENABLED: false,
IS_CORE_VIEW_SYNCING_ENABLED: false,
IS_CORE_VIEW_ENABLED: false,
IS_WORKSPACE_MIGRATION_V2_ENABLED: false,
IS_PAGE_LAYOUT_ENABLED: false,
IS_RECORD_PAGE_LAYOUT_ENABLED: false,
IS_MESSAGE_FOLDER_CONTROL_ENABLED: false,
IS_CALENDAR_VIEW_ENABLED: false,
IS_PUBLIC_DOMAIN_ENABLED: false,
IS_EMAILING_DOMAIN_ENABLED: false,
IS_DYNAMIC_SEARCH_FIELDS_ENABLED: false,
},
permissionsPerRoleId: {},
} as WorkspaceDataSource;
@@ -71,11 +71,6 @@ export const seedFeatureFlags = async (
workspaceId: workspaceId,
value: false,
},
{
key: FeatureFlagKey.IS_CALENDAR_VIEW_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_PUBLIC_DOMAIN_ENABLED,
workspaceId: workspaceId,
@@ -86,11 +81,6 @@ export const seedFeatureFlags = async (
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_DYNAMIC_SEARCH_FIELDS_ENABLED,
workspaceId: workspaceId,
value: false,
},
])
.execute();
};
@@ -112,7 +112,7 @@ type RecordSeedConfig = {
// Organize seeds into dependency batches for parallel insertion
const getRecordSeedsBatches = (
workspaceId: string,
featureFlags?: Record<FeatureFlagKey, boolean>,
_featureFlags?: Record<FeatureFlagKey, boolean>,
): RecordSeedConfig[][] => {
// Batch 1: No dependencies
const batch1: RecordSeedConfig[] = [
@@ -140,15 +140,11 @@ const getRecordSeedsBatches = (
pgColumns: CONNECTED_ACCOUNT_DATA_SEED_COLUMNS,
recordSeeds: CONNECTED_ACCOUNT_DATA_SEEDS,
},
...(featureFlags?.[FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED]
? [
{
tableName: 'dashboard',
pgColumns: DASHBOARD_DATA_SEED_COLUMNS,
recordSeeds: getDashboardDataSeeds(workspaceId),
},
]
: []),
{
tableName: 'dashboard',
pgColumns: DASHBOARD_DATA_SEED_COLUMNS,
recordSeeds: getDashboardDataSeeds(workspaceId),
},
];
// Batch 3: Depends on company and connectedAccount
@@ -2,7 +2,6 @@ import { isString } from '@sniptt/guards';
import { type DataSource, type QueryRunner } from 'typeorm';
import { v4 } from 'uuid';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
@@ -41,7 +40,6 @@ export const prefillCoreViews = async ({
coreDataSource,
workspaceId,
objectMetadataItems,
featureFlags,
workspaceSchemaName,
}: PrefillCoreViewsArgs): Promise<ViewEntity[]> => {
const views = [
@@ -58,9 +56,7 @@ export const prefillCoreViews = async ({
workflowRunsAllView(objectMetadataItems, true),
];
if (featureFlags?.[FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED]) {
views.push(dashboardsAllView(objectMetadataItems, true));
}
views.push(dashboardsAllView(objectMetadataItems, true));
const queryRunner = coreDataSource.createQueryRunner();
@@ -1,7 +1,6 @@
import { type EntityManager } from 'typeorm';
import { v4 } from 'uuid';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { type ViewDefinition } from 'src/engine/workspace-manager/standard-objects-prefill-data/types/view-definition.interface';
import { companiesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view';
@@ -22,7 +21,7 @@ export const prefillViews = async (
entityManager: EntityManager,
schemaName: string,
objectMetadataItems: ObjectMetadataEntity[],
featureFlags?: Record<string, boolean>,
_featureFlags?: Record<string, boolean>,
) => {
const customObjectMetadataItems = objectMetadataItems.filter(
(item) => item.isCustom,
@@ -47,9 +46,7 @@ export const prefillViews = async (
...customViews,
];
if (featureFlags?.[FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED]) {
views.push(dashboardsAllView(objectMetadataItems));
}
views.push(dashboardsAllView(objectMetadataItems));
return createWorkspaceViews(entityManager, schemaName, views);
};
@@ -5,14 +5,12 @@ import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metada
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { ActorMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.decorator';
import { WorkspaceIsFieldUIReadOnly } from 'src/engine/twenty-orm/decorators/workspace-is-field-ui-readonly.decorator';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
@@ -246,16 +244,10 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity {
inverseSideFieldKey: 'attachments',
onDelete: RelationOnDeleteAction.CASCADE,
})
@WorkspaceGate({
featureFlag: FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED,
})
@WorkspaceIsNullable()
dashboard: Relation<DashboardWorkspaceEntity> | null;
@WorkspaceJoinColumn('dashboard')
@WorkspaceGate({
featureFlag: FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED,
})
dashboardId: string | null;
@WorkspaceRelation({
@@ -5,7 +5,6 @@ import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metada
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/constants/search-vector-field.constants';
import { ActorMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
@@ -13,7 +12,6 @@ import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceFieldIndex } from 'src/engine/twenty-orm/decorators/workspace-field-index.decorator';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.decorator';
import { WorkspaceIsFieldUIReadOnly } from 'src/engine/twenty-orm/decorators/workspace-is-field-ui-readonly.decorator';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceIsSearchable } from 'src/engine/twenty-orm/decorators/workspace-is-searchable.decorator';
@@ -45,9 +43,6 @@ export const SEARCH_FIELDS_FOR_DASHBOARD: FieldTypeAndNameMetadata[] = [
icon: STANDARD_OBJECT_ICONS.dashboard,
labelIdentifierStandardId: DASHBOARD_STANDARD_FIELD_IDS.title,
})
@WorkspaceGate({
featureFlag: FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED,
})
@WorkspaceIsSearchable()
export class DashboardWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceField({
@@ -4,14 +4,12 @@ import { FieldMetadataType } from 'twenty-shared/types';
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-on-delete-action.type';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.decorator';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
@@ -223,16 +221,10 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity {
inverseSideFieldKey: 'favorites',
onDelete: RelationOnDeleteAction.CASCADE,
})
@WorkspaceGate({
featureFlag: FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED,
})
@WorkspaceIsNullable()
dashboard: Relation<DashboardWorkspaceEntity> | null;
@WorkspaceJoinColumn('dashboard')
@WorkspaceGate({
featureFlag: FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED,
})
dashboardId: string;
@WorkspaceField({
@@ -5,13 +5,11 @@ import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metada
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.decorator';
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
@@ -256,16 +254,10 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
inverseSideFieldKey: 'timelineActivities',
onDelete: RelationOnDeleteAction.SET_NULL,
})
@WorkspaceGate({
featureFlag: FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED,
})
@WorkspaceIsNullable()
dashboard: Relation<DashboardWorkspaceEntity> | null;
@WorkspaceJoinColumn('dashboard')
@WorkspaceGate({
featureFlag: FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED,
})
dashboardId: string | null;
@WorkspaceDynamicRelation({