Update updatedat field on dashboards after edition (#16964)

Closes https://github.com/twentyhq/core-team-issues/issues/1896
This commit is contained in:
Raphaël Bosi
2026-01-07 18:14:24 +01:00
committed by GitHub
parent a19ed92429
commit 7af98a9c15
11 changed files with 872 additions and 69 deletions
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { DashboardSyncService } from 'src/engine/metadata-modules/dashboard/services/dashboard-sync.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
@Module({
imports: [TwentyORMModule, WorkspaceManyOrAllFlatEntityMapsCacheModule],
providers: [DashboardSyncService],
exports: [DashboardSyncService],
})
export class DashboardModule {}
@@ -0,0 +1,256 @@
import { Injectable, Logger } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
@Injectable()
export class DashboardSyncService {
private readonly logger = new Logger(DashboardSyncService.name);
constructor(
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
) {}
private async isPageLayoutOfTypeDashboard({
pageLayoutId,
workspaceId,
}: {
pageLayoutId: string;
workspaceId: string;
}): Promise<boolean> {
const { flatPageLayoutMaps } =
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId,
flatMapsKeys: ['flatPageLayoutMaps'],
},
);
const pageLayout = flatPageLayoutMaps.byId[pageLayoutId];
return (
isDefined(pageLayout) && pageLayout.type === PageLayoutType.DASHBOARD
);
}
async updateLinkedDashboardsUpdatedAtByPageLayoutId({
pageLayoutId,
workspaceId,
updatedAt,
}: {
pageLayoutId: string;
workspaceId: string;
updatedAt: Date;
}): Promise<void> {
const isDashboard = await this.isPageLayoutOfTypeDashboard({
pageLayoutId,
workspaceId,
});
if (!isDashboard) {
return;
}
const authContext = buildSystemAuthContext(workspaceId);
try {
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const dashboardRepository =
await this.globalWorkspaceOrmManager.getRepository(
workspaceId,
'dashboard',
{ shouldBypassPermissionChecks: true },
);
await dashboardRepository.update({ pageLayoutId }, { updatedAt });
},
);
} catch (error) {
this.logger.error(
`Failed to update dashboard updatedAt for page layout ${pageLayoutId}: ${error}`,
);
}
}
async softDeleteLinkedDashboardsByPageLayoutId({
pageLayoutId,
workspaceId,
deletedAt,
}: {
pageLayoutId: string;
workspaceId: string;
deletedAt: Date;
}): Promise<void> {
const isDashboard = await this.isPageLayoutOfTypeDashboard({
pageLayoutId,
workspaceId,
});
if (!isDashboard) {
return;
}
const authContext = buildSystemAuthContext(workspaceId);
try {
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const dashboardRepository =
await this.globalWorkspaceOrmManager.getRepository(
workspaceId,
'dashboard',
{ shouldBypassPermissionChecks: true },
);
await dashboardRepository.update({ pageLayoutId }, { deletedAt });
},
);
} catch (error) {
this.logger.error(
`Failed to soft delete dashboards for page layout ${pageLayoutId}: ${error}`,
);
}
}
async restoreLinkedDashboardsByPageLayoutId({
pageLayoutId,
workspaceId,
}: {
pageLayoutId: string;
workspaceId: string;
}): Promise<void> {
const isDashboard = await this.isPageLayoutOfTypeDashboard({
pageLayoutId,
workspaceId,
});
if (!isDashboard) {
return;
}
const authContext = buildSystemAuthContext(workspaceId);
try {
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const dashboardRepository =
await this.globalWorkspaceOrmManager.getRepository(
workspaceId,
'dashboard',
{ shouldBypassPermissionChecks: true },
);
await dashboardRepository.update(
{ pageLayoutId },
{ deletedAt: null },
);
},
);
} catch (error) {
this.logger.error(
`Failed to restore dashboards for page layout ${pageLayoutId}: ${error}`,
);
}
}
async updateLinkedDashboardsUpdatedAtByTabId({
tabId,
workspaceId,
updatedAt,
}: {
tabId: string;
workspaceId: string;
updatedAt: Date;
}): Promise<void> {
const { flatPageLayoutTabMaps, flatPageLayoutMaps } =
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId,
flatMapsKeys: ['flatPageLayoutTabMaps', 'flatPageLayoutMaps'],
},
);
const tab = flatPageLayoutTabMaps.byId[tabId];
if (!isDefined(tab)) {
return;
}
const pageLayout = flatPageLayoutMaps.byId[tab.pageLayoutId];
if (
!isDefined(pageLayout) ||
pageLayout.type !== PageLayoutType.DASHBOARD
) {
return;
}
await this.updateLinkedDashboardsUpdatedAtByPageLayoutId({
pageLayoutId: tab.pageLayoutId,
workspaceId,
updatedAt,
});
}
async updateLinkedDashboardsUpdatedAtByWidgetId({
widgetId,
workspaceId,
updatedAt,
}: {
widgetId: string;
workspaceId: string;
updatedAt: Date;
}): Promise<void> {
const {
flatPageLayoutWidgetMaps,
flatPageLayoutTabMaps,
flatPageLayoutMaps,
} =
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId,
flatMapsKeys: [
'flatPageLayoutWidgetMaps',
'flatPageLayoutTabMaps',
'flatPageLayoutMaps',
],
},
);
const widget = flatPageLayoutWidgetMaps.byId[widgetId];
if (!isDefined(widget)) {
return;
}
const tab = flatPageLayoutTabMaps.byId[widget.pageLayoutTabId];
if (!isDefined(tab)) {
return;
}
const pageLayout = flatPageLayoutMaps.byId[tab.pageLayoutId];
if (
!isDefined(pageLayout) ||
pageLayout.type !== PageLayoutType.DASHBOARD
) {
return;
}
await this.updateLinkedDashboardsUpdatedAtByPageLayoutId({
pageLayoutId: tab.pageLayoutId,
workspaceId,
updatedAt,
});
}
}
@@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DashboardModule } from 'src/engine/metadata-modules/dashboard/dashboard.module';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
import { FlatPageLayoutTabModule } from 'src/engine/metadata-modules/flat-page-layout-tab/flat-page-layout-tab.module';
import { FlatPageLayoutWidgetModule } from 'src/engine/metadata-modules/flat-page-layout-widget/flat-page-layout-widget.module';
@@ -29,6 +30,7 @@ import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspa
FlatPageLayoutTabModule,
FlatPageLayoutWidgetModule,
ApplicationModule,
DashboardModule,
],
controllers: [PageLayoutTabController],
providers: [
@@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { DashboardSyncService } from 'src/engine/metadata-modules/dashboard/services/dashboard-sync.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { FlatPageLayoutTabMaps } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab-maps.type';
@@ -36,6 +37,7 @@ export class PageLayoutTabService {
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly applicationService: ApplicationService,
private readonly dashboardSyncService: DashboardSyncService,
) {}
async findByPageLayoutId(
@@ -164,12 +166,18 @@ export class PageLayoutTabService {
},
);
return fromFlatPageLayoutTabToPageLayoutTabDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: flatPageLayoutTabToCreate.id,
flatEntityMaps: recomputedFlatPageLayoutTabMaps,
}),
);
const createdTab = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: flatPageLayoutTabToCreate.id,
flatEntityMaps: recomputedFlatPageLayoutTabMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByTabId({
tabId: flatPageLayoutTabToCreate.id,
workspaceId,
updatedAt: new Date(createdTab.updatedAt),
});
return fromFlatPageLayoutTabToPageLayoutTabDto(createdTab);
}
async update(
@@ -226,12 +234,18 @@ export class PageLayoutTabService {
},
);
return fromFlatPageLayoutTabToPageLayoutTabDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutTabMaps,
}),
);
const updatedTab = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutTabMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByTabId({
tabId: id,
workspaceId,
updatedAt: new Date(updatedTab.updatedAt),
});
return fromFlatPageLayoutTabToPageLayoutTabDto(updatedTab);
}
async delete(
@@ -282,12 +296,18 @@ export class PageLayoutTabService {
},
);
return fromFlatPageLayoutTabToPageLayoutTabDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutTabMaps,
}),
);
const deletedTab = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutTabMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByTabId({
tabId: id,
workspaceId,
updatedAt: new Date(deletedTab.updatedAt),
});
return fromFlatPageLayoutTabToPageLayoutTabDto(deletedTab);
}
async destroy(id: string, workspaceId: string): Promise<boolean> {
@@ -327,6 +347,12 @@ export class PageLayoutTabService {
);
}
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByTabId({
tabId: id,
workspaceId,
updatedAt: new Date(),
});
return true;
}
@@ -378,11 +404,17 @@ export class PageLayoutTabService {
},
);
return fromFlatPageLayoutTabToPageLayoutTabDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutTabMaps,
}),
);
const restoredTab = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutTabMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByTabId({
tabId: id,
workspaceId,
updatedAt: new Date(restoredTab.updatedAt),
});
return fromFlatPageLayoutTabToPageLayoutTabDto(restoredTab);
}
}
@@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DashboardModule } from 'src/engine/metadata-modules/dashboard/dashboard.module';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
import { FlatPageLayoutWidgetModule } from 'src/engine/metadata-modules/flat-page-layout-widget/flat-page-layout-widget.module';
import { PageLayoutWidgetController } from 'src/engine/metadata-modules/page-layout-widget/controllers/page-layout-widget.controller';
@@ -27,6 +28,7 @@ import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspa
WorkspaceManyOrAllFlatEntityMapsCacheModule,
FlatPageLayoutWidgetModule,
ApplicationModule,
DashboardModule,
],
controllers: [PageLayoutWidgetController],
providers: [
@@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { DashboardSyncService } from 'src/engine/metadata-modules/dashboard/services/dashboard-sync.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { FlatPageLayoutWidgetMaps } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-maps.type';
@@ -41,6 +42,7 @@ export class PageLayoutWidgetService {
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly applicationService: ApplicationService,
private readonly dashboardSyncService: DashboardSyncService,
) {}
private async getFlatPageLayoutWidgetMaps(
@@ -158,12 +160,18 @@ export class PageLayoutWidgetService {
const recomputedMaps = await this.getFlatPageLayoutWidgetMaps(workspaceId);
return fromFlatPageLayoutWidgetToPageLayoutWidgetDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: flatPageLayoutWidgetToCreate.id,
flatEntityMaps: recomputedMaps,
}),
);
const createdWidget = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: flatPageLayoutWidgetToCreate.id,
flatEntityMaps: recomputedMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByWidgetId({
widgetId: flatPageLayoutWidgetToCreate.id,
workspaceId,
updatedAt: new Date(createdWidget.updatedAt),
});
return fromFlatPageLayoutWidgetToPageLayoutWidgetDto(createdWidget);
}
async update(
@@ -211,12 +219,18 @@ export class PageLayoutWidgetService {
const recomputedMaps = await this.getFlatPageLayoutWidgetMaps(workspaceId);
return fromFlatPageLayoutWidgetToPageLayoutWidgetDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedMaps,
}),
);
const updatedWidget = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByWidgetId({
widgetId: id,
workspaceId,
updatedAt: new Date(updatedWidget.updatedAt),
});
return fromFlatPageLayoutWidgetToPageLayoutWidgetDto(updatedWidget);
}
private getExistingWidgetOrThrow(
@@ -261,12 +275,18 @@ export class PageLayoutWidgetService {
const recomputedMaps = await this.getFlatPageLayoutWidgetMaps(workspaceId);
return fromFlatPageLayoutWidgetToPageLayoutWidgetDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedMaps,
}),
);
const deletedWidget = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByWidgetId({
widgetId: id,
workspaceId,
updatedAt: new Date(deletedWidget.updatedAt),
});
return fromFlatPageLayoutWidgetToPageLayoutWidgetDto(deletedWidget);
}
async destroy(id: string, workspaceId: string): Promise<boolean> {
@@ -290,6 +310,12 @@ export class PageLayoutWidgetService {
'Multiple validation errors occurred while destroying page layout widget',
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByWidgetId({
widgetId: id,
workspaceId,
updatedAt: new Date(),
});
return true;
}
@@ -316,11 +342,17 @@ export class PageLayoutWidgetService {
const recomputedMaps = await this.getFlatPageLayoutWidgetMaps(workspaceId);
return fromFlatPageLayoutWidgetToPageLayoutWidgetDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedMaps,
}),
);
const restoredWidget = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByWidgetId({
widgetId: id,
workspaceId,
updatedAt: new Date(restoredWidget.updatedAt),
});
return fromFlatPageLayoutWidgetToPageLayoutWidgetDto(restoredWidget);
}
}
@@ -5,18 +5,19 @@ import { ApplicationModule } from 'src/engine/core-modules/application/applicati
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { I18nModule } from 'src/engine/core-modules/i18n/i18n.module';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DashboardModule } from 'src/engine/metadata-modules/dashboard/dashboard.module';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
import { FlatPageLayoutTabModule } from 'src/engine/metadata-modules/flat-page-layout-tab/flat-page-layout-tab.module';
import { FlatPageLayoutWidgetModule } from 'src/engine/metadata-modules/flat-page-layout-widget/flat-page-layout-widget.module';
import { FlatPageLayoutModule } from 'src/engine/metadata-modules/flat-page-layout/flat-page-layout.module';
import { PageLayoutTabModule } from 'src/engine/metadata-modules/page-layout-tab/page-layout-tab.module';
import { PageLayoutWidgetModule } from 'src/engine/metadata-modules/page-layout-widget/page-layout-widget.module';
import { PageLayoutController } from 'src/engine/metadata-modules/page-layout/controllers/page-layout.controller';
import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
import { PageLayoutResolver } from 'src/engine/metadata-modules/page-layout/resolvers/page-layout.resolver';
import { PageLayoutDuplicationService } from 'src/engine/metadata-modules/page-layout/services/page-layout-duplication.service';
import { PageLayoutUpdateService } from 'src/engine/metadata-modules/page-layout/services/page-layout-update.service';
import { PageLayoutService } from 'src/engine/metadata-modules/page-layout/services/page-layout.service';
import { PageLayoutTabModule } from 'src/engine/metadata-modules/page-layout-tab/page-layout-tab.module';
import { PageLayoutWidgetModule } from 'src/engine/metadata-modules/page-layout-widget/page-layout-widget.module';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
@@ -39,6 +40,7 @@ import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspa
ApplicationModule,
PageLayoutTabModule,
PageLayoutWidgetModule,
DashboardModule,
],
controllers: [PageLayoutController],
providers: [
@@ -4,6 +4,7 @@ import { computeDiffBetweenObjects, isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { DashboardSyncService } from 'src/engine/metadata-modules/dashboard/services/dashboard-sync.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-page-layout-tab/constants/flat-page-layout-tab-editable-properties.constant';
@@ -40,6 +41,7 @@ export class PageLayoutUpdateService {
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly applicationService: ApplicationService,
private readonly dashboardSyncService: DashboardSyncService,
) {}
async updatePageLayoutWithTabs({
@@ -164,6 +166,14 @@ export class PageLayoutUpdateService {
flatEntityMaps: recomputedFlatPageLayoutMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByPageLayoutId(
{
pageLayoutId: id,
workspaceId,
updatedAt: new Date(flatLayout.updatedAt),
},
);
return fromFlatPageLayoutWithTabsAndWidgetsToPageLayoutDto(
reconstructFlatPageLayoutWithTabsAndWidgets({
layout: flatLayout,
@@ -4,6 +4,7 @@ import { isNonEmptyString } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { DashboardSyncService } from 'src/engine/metadata-modules/dashboard/services/dashboard-sync.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { type FlatPageLayoutTabMaps } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab-maps.type';
@@ -44,6 +45,7 @@ export class PageLayoutService {
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly applicationService: ApplicationService,
private readonly dashboardSyncService: DashboardSyncService,
) {}
async findByWorkspaceId(workspaceId: string): Promise<PageLayoutDTO[]> {
@@ -262,12 +264,20 @@ export class PageLayoutService {
},
);
return fromFlatPageLayoutToPageLayoutDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutMaps,
}),
const updatedLayout = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutMaps,
});
await this.dashboardSyncService.updateLinkedDashboardsUpdatedAtByPageLayoutId(
{
pageLayoutId: id,
workspaceId,
updatedAt: new Date(updatedLayout.updatedAt),
},
);
return fromFlatPageLayoutToPageLayoutDto(updatedLayout);
}
async delete(
@@ -318,12 +328,20 @@ export class PageLayoutService {
},
);
return fromFlatPageLayoutToPageLayoutDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutMaps,
}),
);
const deletedLayout = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutMaps,
});
await this.dashboardSyncService.softDeleteLinkedDashboardsByPageLayoutId({
pageLayoutId: id,
workspaceId,
deletedAt: isDefined(deletedLayout.deletedAt)
? new Date(deletedLayout.deletedAt)
: new Date(),
});
return fromFlatPageLayoutToPageLayoutDto(deletedLayout);
}
async destroy(
@@ -456,11 +474,16 @@ export class PageLayoutService {
},
);
return fromFlatPageLayoutToPageLayoutDto(
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutMaps,
}),
);
const restoredLayout = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: id,
flatEntityMaps: recomputedFlatPageLayoutMaps,
});
await this.dashboardSyncService.restoreLinkedDashboardsByPageLayoutId({
pageLayoutId: id,
workspaceId,
});
return fromFlatPageLayoutToPageLayoutDto(restoredLayout);
}
}