[DASHBOARDS] Allow dashboards to be restored (#17042)

This PR introduces a few changes:
- Add three actions: see deleted dashboards, destroy dashboard and
restore dashboard
- Remove the soft delete and restore on all the page layout entities
- Cascade the destruction of a dashboard to a page layout

Video QA:


https://github.com/user-attachments/assets/ab993b11-dd9c-4e88-880c-92691a521cc2
This commit is contained in:
Raphaël Bosi
2026-01-12 13:59:17 +01:00
committed by GitHub
parent 3ada8e5168
commit 655f1eef5f
60 changed files with 1178 additions and 2067 deletions
@@ -1,44 +0,0 @@
import { t } from '@lingui/core/macro';
import {
extractAndSanitizeObjectStringFields,
isDefined,
} from 'twenty-shared/utils';
import { type FlatPageLayoutTabMaps } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab-maps.type';
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
import {
PageLayoutTabException,
PageLayoutTabExceptionCode,
} from 'src/engine/metadata-modules/page-layout-tab/exceptions/page-layout-tab.exception';
export type DeletePageLayoutTabInput = {
id: string;
};
export const fromDeletePageLayoutTabInputToFlatPageLayoutTabOrThrow = ({
deletePageLayoutTabInput: rawDeletePageLayoutTabInput,
flatPageLayoutTabMaps,
}: {
deletePageLayoutTabInput: DeletePageLayoutTabInput;
flatPageLayoutTabMaps: FlatPageLayoutTabMaps;
}): FlatPageLayoutTab => {
const { id: pageLayoutTabId } = extractAndSanitizeObjectStringFields(
rawDeletePageLayoutTabInput,
['id'],
);
const existingFlatPageLayoutTabToDelete =
flatPageLayoutTabMaps.byId[pageLayoutTabId];
if (!isDefined(existingFlatPageLayoutTabToDelete)) {
throw new PageLayoutTabException(
t`Page layout tab to delete not found`,
PageLayoutTabExceptionCode.PAGE_LAYOUT_TAB_NOT_FOUND,
);
}
return {
...existingFlatPageLayoutTabToDelete,
deletedAt: new Date().toISOString(),
};
};
@@ -1,51 +0,0 @@
import { t } from '@lingui/core/macro';
import {
extractAndSanitizeObjectStringFields,
isDefined,
} from 'twenty-shared/utils';
import { type FlatPageLayoutTabMaps } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab-maps.type';
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
import {
PageLayoutTabException,
PageLayoutTabExceptionCode,
} from 'src/engine/metadata-modules/page-layout-tab/exceptions/page-layout-tab.exception';
export type RestorePageLayoutTabInput = {
id: string;
};
export const fromRestorePageLayoutTabInputToFlatPageLayoutTabOrThrow = ({
restorePageLayoutTabInput,
flatPageLayoutTabMaps,
}: {
restorePageLayoutTabInput: RestorePageLayoutTabInput;
flatPageLayoutTabMaps: FlatPageLayoutTabMaps;
}): FlatPageLayoutTab => {
const { id: pageLayoutTabId } = extractAndSanitizeObjectStringFields(
restorePageLayoutTabInput,
['id'],
);
const existingFlatPageLayoutTabToRestore =
flatPageLayoutTabMaps.byId[pageLayoutTabId];
if (!isDefined(existingFlatPageLayoutTabToRestore)) {
throw new PageLayoutTabException(
t`Page layout tab to restore not found`,
PageLayoutTabExceptionCode.PAGE_LAYOUT_TAB_NOT_FOUND,
);
}
if (!isDefined(existingFlatPageLayoutTabToRestore.deletedAt)) {
throw new PageLayoutTabException(
t`Page layout tab is not deleted and cannot be restored`,
PageLayoutTabExceptionCode.INVALID_PAGE_LAYOUT_TAB_DATA,
);
}
return {
...existingFlatPageLayoutTabToRestore,
deletedAt: null,
};
};