Fix moving a widget to another tab (#19450)

https://github.com/user-attachments/assets/aac81e79-7f2f-4a34-bf68-c76061086821

---------

Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
Baptiste Devessier
2026-04-09 15:59:04 +02:00
committed by GitHub
parent 1e908e5f0f
commit 16e145b036
25 changed files with 415 additions and 132 deletions
@@ -8,4 +8,5 @@ export const FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES = [
'position',
'configuration',
'conditionalDisplay',
'pageLayoutTabId',
] as const satisfies MetadataEntityPropertyName<'pageLayoutWidget'>[];
@@ -75,6 +75,7 @@ export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({
applicationUniversalIdentifier: flatApplication.universalIdentifier,
conditionalDisplay: null,
overrides: null,
universalOverrides: null,
universalConfiguration:
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
configuration: createPageLayoutWidgetInput.configuration,
@@ -7,6 +7,7 @@ import {
import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { fromPageLayoutWidgetConfigurationToUniversalConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-configuration-to-universal-configuration.util';
import { fromPageLayoutWidgetOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-overrides-to-universal-overrides.util';
import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type';
type FromPageLayoutWidgetEntityToFlatPageLayoutWidgetArgs =
@@ -81,6 +82,18 @@ export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ({
viewUniversalIdentifierById,
});
const pageLayoutTabUniversalIdentifierById = Object.fromEntries(
pageLayoutTabIdToUniversalIdentifierMap.entries(),
);
const universalOverrides = isDefined(pageLayoutWidgetEntity.overrides)
? fromPageLayoutWidgetOverridesToUniversalOverrides({
overrides: pageLayoutWidgetEntity.overrides,
pageLayoutTabUniversalIdentifierById,
shouldThrowOnMissingIdentifier: false,
})
: null;
return {
...pageLayoutWidgetEntityWithoutRelations,
createdAt: pageLayoutWidgetEntity.createdAt.toISOString(),
@@ -93,5 +106,6 @@ export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ({
pageLayoutTabUniversalIdentifier,
objectMetadataUniversalIdentifier,
universalConfiguration: configurationWithUniversalIdentifiers,
universalOverrides,
};
};
@@ -0,0 +1,54 @@
import { type FormatRecordSerializedRelationProperties } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
FlatEntityMapsException,
FlatEntityMapsExceptionCode,
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
import { type PageLayoutWidgetOverrides } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
type UniversalPageLayoutWidgetOverrides =
FormatRecordSerializedRelationProperties<PageLayoutWidgetOverrides>;
export const fromPageLayoutWidgetOverridesToUniversalOverrides = ({
overrides,
pageLayoutTabUniversalIdentifierById,
shouldThrowOnMissingIdentifier = true,
}: {
overrides: PageLayoutWidgetOverrides;
pageLayoutTabUniversalIdentifierById: Partial<Record<string, string>>;
shouldThrowOnMissingIdentifier?: boolean;
}): UniversalPageLayoutWidgetOverrides => {
const { pageLayoutTabId, ...scalarOverrides } = overrides;
if (!isDefined(pageLayoutTabId)) {
return {
...scalarOverrides,
...(pageLayoutTabId === null
? { pageLayoutTabUniversalIdentifier: null }
: {}),
};
}
const pageLayoutTabUniversalIdentifier =
pageLayoutTabUniversalIdentifierById[pageLayoutTabId];
if (!isDefined(pageLayoutTabUniversalIdentifier)) {
if (shouldThrowOnMissingIdentifier) {
throw new FlatEntityMapsException(
`PageLayoutTab universal identifier not found for id: ${pageLayoutTabId}`,
FlatEntityMapsExceptionCode.RELATION_UNIVERSAL_IDENTIFIER_NOT_FOUND,
);
}
return {
...scalarOverrides,
pageLayoutTabUniversalIdentifier: null,
};
}
return {
...scalarOverrides,
pageLayoutTabUniversalIdentifier,
};
};
@@ -11,6 +11,7 @@ import { FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES } from 'src/engine/metadata
import { type FlatPageLayoutWidgetMaps } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-maps.type';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { fromPageLayoutWidgetConfigurationToUniversalConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-configuration-to-universal-configuration.util';
import { fromPageLayoutWidgetOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-overrides-to-universal-overrides.util';
import { type UpdatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget.input';
import {
PageLayoutWidgetException,
@@ -35,6 +36,7 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
flatFrontComponentMaps,
flatViewFieldGroupMaps,
flatViewMaps,
flatPageLayoutTabMaps,
callerApplicationUniversalIdentifier,
workspaceCustomApplicationUniversalIdentifier,
}: {
@@ -49,6 +51,7 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
| 'flatFrontComponentMaps'
| 'flatViewFieldGroupMaps'
| 'flatViewMaps'
| 'flatPageLayoutTabMaps'
>): FlatPageLayoutWidget => {
const { id: pageLayoutWidgetToUpdateId } =
extractAndSanitizeObjectStringFields(rawUpdatePageLayoutWidgetInput, [
@@ -103,7 +106,21 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
update: updatedEditableProperties,
}),
overrides,
};
} as FlatPageLayoutWidget;
if (updatedEditableProperties.pageLayoutTabId !== undefined) {
const { pageLayoutTabUniversalIdentifier } =
resolveEntityRelationUniversalIdentifiers({
metadataName: 'pageLayoutWidget',
foreignKeyValues: {
pageLayoutTabId: flatPageLayoutWidgetToUpdate.pageLayoutTabId,
},
flatEntityMaps: { flatPageLayoutTabMaps },
});
flatPageLayoutWidgetToUpdate.pageLayoutTabUniversalIdentifier =
pageLayoutTabUniversalIdentifier;
}
if (updatedEditableProperties.objectMetadataId !== undefined) {
const { objectMetadataUniversalIdentifier } =
@@ -133,5 +150,16 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
});
}
if (isDefined(overrides)) {
flatPageLayoutWidgetToUpdate.universalOverrides =
fromPageLayoutWidgetOverridesToUniversalOverrides({
overrides,
pageLayoutTabUniversalIdentifierById:
flatPageLayoutTabMaps.universalIdentifierById,
});
} else {
flatPageLayoutWidgetToUpdate.universalOverrides = null;
}
return flatPageLayoutWidgetToUpdate;
};