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:
co-authored by
Weiko
parent
1e908e5f0f
commit
16e145b036
+1
-1
@@ -36,6 +36,6 @@ export const fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget = ({
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
};
|
||||
};
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should match snapshot 1`] = `
|
||||
{
|
||||
@@ -195,14 +195,14 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
"deletedAt",
|
||||
"conditionalDisplay",
|
||||
"isActive",
|
||||
"overrides",
|
||||
"universalOverrides",
|
||||
],
|
||||
"propertiesToStringify": [
|
||||
"gridPosition",
|
||||
"position",
|
||||
"universalConfiguration",
|
||||
"conditionalDisplay",
|
||||
"overrides",
|
||||
"universalOverrides",
|
||||
],
|
||||
},
|
||||
"permissionFlag": {
|
||||
|
||||
+2
-1
@@ -921,6 +921,7 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
toCompare: false,
|
||||
toStringify: false,
|
||||
universalProperty: 'pageLayoutTabUniversalIdentifier',
|
||||
isOverridable: true,
|
||||
},
|
||||
conditionalDisplay: {
|
||||
toCompare: true,
|
||||
@@ -937,7 +938,7 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
overrides: {
|
||||
toCompare: true,
|
||||
toStringify: true,
|
||||
universalProperty: undefined,
|
||||
universalProperty: 'universalOverrides',
|
||||
},
|
||||
},
|
||||
pageLayoutTab: {
|
||||
|
||||
+1
@@ -36,6 +36,7 @@ export const splitEntitiesByResetStrategy = <
|
||||
...entity,
|
||||
isActive: true as const,
|
||||
overrides: null,
|
||||
...('universalOverrides' in entity ? { universalOverrides: null } : {}),
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
|
||||
+1
@@ -8,4 +8,5 @@ export const FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES = [
|
||||
'position',
|
||||
'configuration',
|
||||
'conditionalDisplay',
|
||||
'pageLayoutTabId',
|
||||
] as const satisfies MetadataEntityPropertyName<'pageLayoutWidget'>[];
|
||||
|
||||
+1
@@ -75,6 +75,7 @@ export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({
|
||||
applicationUniversalIdentifier: flatApplication.universalIdentifier,
|
||||
conditionalDisplay: null,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
universalConfiguration:
|
||||
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
|
||||
configuration: createPageLayoutWidgetInput.configuration,
|
||||
|
||||
+14
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
+54
@@ -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,
|
||||
};
|
||||
};
|
||||
+29
-1
@@ -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;
|
||||
};
|
||||
|
||||
+8
-1
@@ -5,6 +5,7 @@ import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-la
|
||||
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 { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
|
||||
import { resolveOverridableEntityProperty } from 'src/engine/metadata-modules/utils/resolve-overridable-entity-property.util';
|
||||
|
||||
export type FlatPageLayoutTabWithWidgets = FlatPageLayoutTab & {
|
||||
widgets: FlatPageLayoutWidget[];
|
||||
@@ -14,6 +15,9 @@ export type FlatPageLayoutWithTabsAndWidgets = FlatPageLayout & {
|
||||
tabs: FlatPageLayoutTabWithWidgets[];
|
||||
};
|
||||
|
||||
const getResolvedPageLayoutTabId = (widget: FlatPageLayoutWidget): string =>
|
||||
resolveOverridableEntityProperty(widget, 'pageLayoutTabId');
|
||||
|
||||
export const reconstructFlatPageLayoutWithTabsAndWidgets = ({
|
||||
layout,
|
||||
flatPageLayoutTabMaps,
|
||||
@@ -33,7 +37,10 @@ export const reconstructFlatPageLayoutWithTabsAndWidgets = ({
|
||||
flatPageLayoutWidgetMaps.byUniversalIdentifier,
|
||||
)
|
||||
.filter(isDefined)
|
||||
.filter((widget) => widget.pageLayoutTabId === tab.id && widget.isActive);
|
||||
.filter(
|
||||
(widget) =>
|
||||
getResolvedPageLayoutTabId(widget) === tab.id && widget.isActive,
|
||||
);
|
||||
|
||||
return {
|
||||
...tab,
|
||||
|
||||
+5
@@ -22,6 +22,11 @@ import { AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/pa
|
||||
|
||||
@InputType()
|
||||
export class UpdatePageLayoutWidgetInput {
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
pageLayoutTabId?: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
|
||||
+2
@@ -4,6 +4,7 @@ import {
|
||||
PageLayoutWidgetConditionalDisplay,
|
||||
PageLayoutWidgetPosition,
|
||||
type GridPosition,
|
||||
type SerializedRelation,
|
||||
} from 'twenty-shared/types';
|
||||
import {
|
||||
Column,
|
||||
@@ -30,6 +31,7 @@ export type PageLayoutWidgetOverrides = {
|
||||
title?: string;
|
||||
position?: PageLayoutWidgetPosition | null;
|
||||
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
|
||||
pageLayoutTabId?: SerializedRelation;
|
||||
};
|
||||
|
||||
@Entity({ name: 'pageLayoutWidget', schema: 'core' })
|
||||
|
||||
+6
-2
@@ -18,6 +18,7 @@ import {
|
||||
import { CreatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input';
|
||||
import { UpdatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget.input';
|
||||
import { type PageLayoutWidgetDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto';
|
||||
import { resolveOverridableEntityProperty } from 'src/engine/metadata-modules/utils/resolve-overridable-entity-property.util';
|
||||
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
|
||||
import {
|
||||
PageLayoutWidgetException,
|
||||
@@ -162,8 +163,8 @@ export class PageLayoutWidgetService {
|
||||
.filter(isDefined)
|
||||
.filter(
|
||||
(widget) =>
|
||||
widget.pageLayoutTabId === pageLayoutTabId &&
|
||||
!isDefined(widget.deletedAt),
|
||||
resolveOverridableEntityProperty(widget, 'pageLayoutTabId') ===
|
||||
pageLayoutTabId && !isDefined(widget.deletedAt),
|
||||
)
|
||||
.sort(
|
||||
(widgetA, widgetB) =>
|
||||
@@ -317,6 +318,7 @@ export class PageLayoutWidgetService {
|
||||
flatFrontComponentMaps: existingFlatFrontComponentMaps,
|
||||
flatViewFieldGroupMaps: existingFlatViewFieldGroupMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatPageLayoutTabMaps: existingFlatPageLayoutTabMaps,
|
||||
},
|
||||
{ workspaceCustomFlatApplication },
|
||||
] = await Promise.all([
|
||||
@@ -329,6 +331,7 @@ export class PageLayoutWidgetService {
|
||||
'flatFrontComponentMaps',
|
||||
'flatViewFieldGroupMaps',
|
||||
'flatViewMaps',
|
||||
'flatPageLayoutTabMaps',
|
||||
],
|
||||
},
|
||||
),
|
||||
@@ -368,6 +371,7 @@ export class PageLayoutWidgetService {
|
||||
flatFrontComponentMaps: existingFlatFrontComponentMaps,
|
||||
flatViewFieldGroupMaps: existingFlatViewFieldGroupMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatPageLayoutTabMaps: existingFlatPageLayoutTabMaps,
|
||||
callerApplicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier:
|
||||
|
||||
+2
-6
@@ -116,6 +116,7 @@ export class PageLayoutResetService {
|
||||
const widgetToUpdate: FlatPageLayoutWidget = {
|
||||
...widget,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
@@ -547,15 +548,10 @@ export class PageLayoutResetService {
|
||||
now,
|
||||
});
|
||||
|
||||
const viewFieldsToReset = fieldsToReset.map((field) => ({
|
||||
...field,
|
||||
universalOverrides: null,
|
||||
}));
|
||||
|
||||
return {
|
||||
viewFieldGroupsToUpdate: groupsToReset,
|
||||
viewFieldGroupsToDelete: groupsToDelete,
|
||||
viewFieldsToUpdate: viewFieldsToReset,
|
||||
viewFieldsToUpdate: fieldsToReset,
|
||||
viewFieldsToDelete: fieldsToDelete,
|
||||
};
|
||||
}
|
||||
|
||||
+202
-107
@@ -10,6 +10,7 @@ import { AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util';
|
||||
import { splitEntitiesByRemovalStrategy } from 'src/engine/metadata-modules/flat-entity/utils/split-entities-by-removal-strategy.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';
|
||||
import { type FlatPageLayoutTabMaps } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab-maps.type';
|
||||
@@ -18,6 +19,7 @@ import { FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES } from 'src/engine/metadata
|
||||
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
|
||||
import { buildFlatPageLayoutWidgetCommonProperties } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/build-flat-page-layout-widget-common-properties.util';
|
||||
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 FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
|
||||
import { reconstructFlatPageLayoutWithTabsAndWidgets } from 'src/engine/metadata-modules/flat-page-layout/utils/reconstruct-flat-page-layout-with-tabs-and-widgets.util';
|
||||
import { UpdatePageLayoutTabWithWidgetsInput } from 'src/engine/metadata-modules/page-layout-tab/dtos/inputs/update-page-layout-tab-with-widgets.input';
|
||||
@@ -468,11 +470,16 @@ export class PageLayoutUpdateService {
|
||||
const allWidgetsToUpdate: FlatPageLayoutWidget[] = [];
|
||||
const allWidgetsToDelete: FlatPageLayoutWidget[] = [];
|
||||
|
||||
const widgetIdsAcrossAllTabs = new Set(
|
||||
tabs.flatMap((tab) => tab.widgets.map((widget) => widget.id)),
|
||||
);
|
||||
|
||||
for (const tabInput of tabs) {
|
||||
const { widgetsToCreate, widgetsToUpdate, widgetsToDelete } =
|
||||
this.computeWidgetOperationsForTab({
|
||||
tabId: tabInput.id,
|
||||
widgets: tabInput.widgets,
|
||||
widgetIdsAcrossAllTabs,
|
||||
flatPageLayoutWidgetMaps,
|
||||
flatPageLayoutTabMaps,
|
||||
flatObjectMetadataMaps,
|
||||
@@ -500,6 +507,7 @@ export class PageLayoutUpdateService {
|
||||
private computeWidgetOperationsForTab({
|
||||
tabId,
|
||||
widgets,
|
||||
widgetIdsAcrossAllTabs,
|
||||
flatPageLayoutWidgetMaps,
|
||||
flatPageLayoutTabMaps,
|
||||
flatObjectMetadataMaps,
|
||||
@@ -513,6 +521,7 @@ export class PageLayoutUpdateService {
|
||||
}: {
|
||||
tabId: string;
|
||||
widgets: UpdatePageLayoutWidgetWithIdInput[];
|
||||
widgetIdsAcrossAllTabs: Set<string>;
|
||||
workspaceId: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
workspaceCustomApplicationUniversalIdentifier: string;
|
||||
@@ -538,11 +547,15 @@ export class PageLayoutUpdateService {
|
||||
});
|
||||
}
|
||||
|
||||
const existingWidgets = Object.values(
|
||||
flatPageLayoutWidgetMaps.byUniversalIdentifier,
|
||||
)
|
||||
.filter(isDefined)
|
||||
.filter((widget) => widget.pageLayoutTabId === tabId);
|
||||
const widgetIdsInCurrentTabInput = new Set(
|
||||
widgets.map((widget) => widget.id),
|
||||
);
|
||||
|
||||
const existingWidgets = this.findWidgetsInTabOrMovingToTab({
|
||||
tabId,
|
||||
widgetIdsInCurrentTabInput,
|
||||
flatPageLayoutWidgetMaps,
|
||||
});
|
||||
|
||||
const resolvedExistingWidgets = existingWidgets.map(
|
||||
resolveFlatEntityOverridableProperties,
|
||||
@@ -587,6 +600,7 @@ export class PageLayoutUpdateService {
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
conditionalDisplay: null,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
isActive: true,
|
||||
universalConfiguration:
|
||||
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
|
||||
@@ -604,120 +618,45 @@ export class PageLayoutUpdateService {
|
||||
);
|
||||
|
||||
const widgetsToUpdate: FlatPageLayoutWidget[] = entitiesToUpdate.map(
|
||||
(widgetInput) => {
|
||||
const existingWidget = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: widgetInput.id,
|
||||
flatEntityMaps: flatPageLayoutWidgetMaps,
|
||||
});
|
||||
|
||||
const shouldOverride = isCallerOverridingEntity({
|
||||
callerApplicationUniversalIdentifier:
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
entityApplicationUniversalIdentifier:
|
||||
existingWidget.applicationUniversalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
const commonProperties = buildFlatPageLayoutWidgetCommonProperties({
|
||||
(widgetInput) =>
|
||||
this.buildUpdatedFlatPageLayoutWidget({
|
||||
widgetInput,
|
||||
flatPageLayoutWidgetMaps,
|
||||
flatPageLayoutTabMaps,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedConfiguration = widgetInput.configuration ?? null;
|
||||
|
||||
const { overrides, updatedEditableProperties } =
|
||||
sanitizeOverridableEntityInput({
|
||||
metadataName: 'pageLayoutWidget',
|
||||
existingFlatEntity: existingWidget,
|
||||
updatedEditableProperties: {
|
||||
...commonProperties,
|
||||
configuration: updatedConfiguration,
|
||||
},
|
||||
shouldOverride,
|
||||
});
|
||||
|
||||
return {
|
||||
...existingWidget,
|
||||
...updatedEditableProperties,
|
||||
overrides,
|
||||
updatedAt: now.toISOString(),
|
||||
...(isDefined(updatedConfiguration) && {
|
||||
universalConfiguration:
|
||||
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
|
||||
configuration: updatedConfiguration,
|
||||
fieldMetadataUniversalIdentifierById:
|
||||
flatFieldMetadataMaps.universalIdentifierById,
|
||||
frontComponentUniversalIdentifierById:
|
||||
flatFrontComponentMaps.universalIdentifierById,
|
||||
viewFieldGroupUniversalIdentifierById:
|
||||
flatViewFieldGroupMaps.universalIdentifierById,
|
||||
viewUniversalIdentifierById:
|
||||
flatViewMaps.universalIdentifierById,
|
||||
}),
|
||||
}),
|
||||
};
|
||||
},
|
||||
flatFieldMetadataMaps,
|
||||
flatFrontComponentMaps,
|
||||
flatViewFieldGroupMaps,
|
||||
flatViewMaps,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
now,
|
||||
}),
|
||||
);
|
||||
|
||||
const widgetsToRestoreAndUpdate: FlatPageLayoutWidget[] =
|
||||
entitiesToRestoreAndUpdate.map((widgetInput) => {
|
||||
const existingWidget = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: widgetInput.id,
|
||||
flatEntityMaps: flatPageLayoutWidgetMaps,
|
||||
});
|
||||
|
||||
const shouldOverride = isCallerOverridingEntity({
|
||||
callerApplicationUniversalIdentifier:
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
entityApplicationUniversalIdentifier:
|
||||
existingWidget.applicationUniversalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
const commonProperties = buildFlatPageLayoutWidgetCommonProperties({
|
||||
entitiesToRestoreAndUpdate.map((widgetInput) => ({
|
||||
...this.buildUpdatedFlatPageLayoutWidget({
|
||||
widgetInput,
|
||||
flatPageLayoutWidgetMaps,
|
||||
flatPageLayoutTabMaps,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
flatFieldMetadataMaps,
|
||||
flatFrontComponentMaps,
|
||||
flatViewFieldGroupMaps,
|
||||
flatViewMaps,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
now,
|
||||
}),
|
||||
isActive: true,
|
||||
}));
|
||||
|
||||
const restoredConfiguration = widgetInput.configuration ?? null;
|
||||
|
||||
const { overrides, updatedEditableProperties } =
|
||||
sanitizeOverridableEntityInput({
|
||||
metadataName: 'pageLayoutWidget',
|
||||
existingFlatEntity: existingWidget,
|
||||
updatedEditableProperties: {
|
||||
...commonProperties,
|
||||
configuration: restoredConfiguration,
|
||||
},
|
||||
shouldOverride,
|
||||
});
|
||||
|
||||
return {
|
||||
...existingWidget,
|
||||
...updatedEditableProperties,
|
||||
overrides,
|
||||
isActive: true,
|
||||
updatedAt: now.toISOString(),
|
||||
...(isDefined(restoredConfiguration) && {
|
||||
universalConfiguration:
|
||||
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
|
||||
configuration: restoredConfiguration,
|
||||
fieldMetadataUniversalIdentifierById:
|
||||
flatFieldMetadataMaps.universalIdentifierById,
|
||||
frontComponentUniversalIdentifierById:
|
||||
flatFrontComponentMaps.universalIdentifierById,
|
||||
viewFieldGroupUniversalIdentifierById:
|
||||
flatViewFieldGroupMaps.universalIdentifierById,
|
||||
viewUniversalIdentifierById:
|
||||
flatViewMaps.universalIdentifierById,
|
||||
}),
|
||||
}),
|
||||
};
|
||||
const widgetIdsToRemoveExcludingMovedToOtherTabs =
|
||||
this.excludeWidgetsMovedToOtherTabs({
|
||||
idsToRemove,
|
||||
widgetIdsAcrossAllTabs,
|
||||
});
|
||||
|
||||
const widgetsToRemove = idsToRemove
|
||||
const widgetsToRemove = widgetIdsToRemoveExcludingMovedToOtherTabs
|
||||
.map((widgetId) =>
|
||||
findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: widgetId,
|
||||
@@ -743,6 +682,162 @@ export class PageLayoutUpdateService {
|
||||
};
|
||||
}
|
||||
|
||||
private buildUpdatedFlatPageLayoutWidget({
|
||||
widgetInput,
|
||||
flatPageLayoutWidgetMaps,
|
||||
flatPageLayoutTabMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatFrontComponentMaps,
|
||||
flatViewFieldGroupMaps,
|
||||
flatViewMaps,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
now,
|
||||
}: {
|
||||
widgetInput: UpdatePageLayoutWidgetWithIdInput;
|
||||
workspaceCustomApplicationUniversalIdentifier: string;
|
||||
now: Date;
|
||||
} & Pick<
|
||||
AllFlatEntityMaps,
|
||||
| 'flatObjectMetadataMaps'
|
||||
| 'flatFieldMetadataMaps'
|
||||
| 'flatFrontComponentMaps'
|
||||
| 'flatViewFieldGroupMaps'
|
||||
| 'flatViewMaps'
|
||||
| 'flatPageLayoutTabMaps'
|
||||
| 'flatPageLayoutWidgetMaps'
|
||||
>): FlatPageLayoutWidget {
|
||||
const existingWidget = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: widgetInput.id,
|
||||
flatEntityMaps: flatPageLayoutWidgetMaps,
|
||||
});
|
||||
|
||||
const shouldOverride = isCallerOverridingEntity({
|
||||
callerApplicationUniversalIdentifier:
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
entityApplicationUniversalIdentifier:
|
||||
existingWidget.applicationUniversalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
const configuration = widgetInput.configuration ?? null;
|
||||
|
||||
const editableProperties: Partial<FlatPageLayoutWidget> = {
|
||||
title: widgetInput.title,
|
||||
type: widgetInput.type,
|
||||
objectMetadataId: widgetInput.objectMetadataId ?? null,
|
||||
gridPosition: widgetInput.gridPosition,
|
||||
position: widgetInput.position ?? null,
|
||||
configuration,
|
||||
pageLayoutTabId: widgetInput.pageLayoutTabId,
|
||||
};
|
||||
|
||||
if (widgetInput.conditionalDisplay !== undefined) {
|
||||
editableProperties.conditionalDisplay =
|
||||
widgetInput.conditionalDisplay ?? null;
|
||||
}
|
||||
|
||||
const { overrides, updatedEditableProperties } =
|
||||
sanitizeOverridableEntityInput({
|
||||
metadataName: 'pageLayoutWidget',
|
||||
existingFlatEntity: existingWidget,
|
||||
updatedEditableProperties: editableProperties,
|
||||
shouldOverride,
|
||||
});
|
||||
|
||||
const updatedWidget: FlatPageLayoutWidget = {
|
||||
...existingWidget,
|
||||
...updatedEditableProperties,
|
||||
overrides,
|
||||
updatedAt: now.toISOString(),
|
||||
};
|
||||
|
||||
if (updatedEditableProperties.pageLayoutTabId !== undefined) {
|
||||
const { pageLayoutTabUniversalIdentifier } =
|
||||
resolveEntityRelationUniversalIdentifiers({
|
||||
metadataName: 'pageLayoutWidget',
|
||||
foreignKeyValues: {
|
||||
pageLayoutTabId: updatedWidget.pageLayoutTabId,
|
||||
},
|
||||
flatEntityMaps: { flatPageLayoutTabMaps },
|
||||
});
|
||||
|
||||
updatedWidget.pageLayoutTabUniversalIdentifier =
|
||||
pageLayoutTabUniversalIdentifier;
|
||||
}
|
||||
|
||||
if (updatedEditableProperties.objectMetadataId !== undefined) {
|
||||
const { objectMetadataUniversalIdentifier } =
|
||||
resolveEntityRelationUniversalIdentifiers({
|
||||
metadataName: 'pageLayoutWidget',
|
||||
foreignKeyValues: {
|
||||
objectMetadataId: updatedWidget.objectMetadataId,
|
||||
},
|
||||
flatEntityMaps: { flatObjectMetadataMaps },
|
||||
});
|
||||
|
||||
updatedWidget.objectMetadataUniversalIdentifier =
|
||||
objectMetadataUniversalIdentifier;
|
||||
}
|
||||
|
||||
if (isDefined(overrides)) {
|
||||
updatedWidget.universalOverrides =
|
||||
fromPageLayoutWidgetOverridesToUniversalOverrides({
|
||||
overrides,
|
||||
pageLayoutTabUniversalIdentifierById:
|
||||
flatPageLayoutTabMaps.universalIdentifierById,
|
||||
});
|
||||
} else {
|
||||
updatedWidget.universalOverrides = null;
|
||||
}
|
||||
|
||||
if (isDefined(configuration)) {
|
||||
updatedWidget.universalConfiguration =
|
||||
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
|
||||
configuration,
|
||||
fieldMetadataUniversalIdentifierById:
|
||||
flatFieldMetadataMaps.universalIdentifierById,
|
||||
frontComponentUniversalIdentifierById:
|
||||
flatFrontComponentMaps.universalIdentifierById,
|
||||
viewFieldGroupUniversalIdentifierById:
|
||||
flatViewFieldGroupMaps.universalIdentifierById,
|
||||
viewUniversalIdentifierById: flatViewMaps.universalIdentifierById,
|
||||
});
|
||||
}
|
||||
|
||||
return updatedWidget;
|
||||
}
|
||||
|
||||
private findWidgetsInTabOrMovingToTab({
|
||||
tabId,
|
||||
widgetIdsInCurrentTabInput,
|
||||
flatPageLayoutWidgetMaps,
|
||||
}: {
|
||||
tabId: string;
|
||||
widgetIdsInCurrentTabInput: Set<string>;
|
||||
flatPageLayoutWidgetMaps: AllFlatEntityMaps['flatPageLayoutWidgetMaps'];
|
||||
}): FlatPageLayoutWidget[] {
|
||||
return Object.values(flatPageLayoutWidgetMaps.byUniversalIdentifier)
|
||||
.filter(isDefined)
|
||||
.filter(
|
||||
(widget) =>
|
||||
widget.pageLayoutTabId === tabId ||
|
||||
widgetIdsInCurrentTabInput.has(widget.id),
|
||||
);
|
||||
}
|
||||
|
||||
private excludeWidgetsMovedToOtherTabs({
|
||||
idsToRemove,
|
||||
widgetIdsAcrossAllTabs,
|
||||
}: {
|
||||
idsToRemove: string[];
|
||||
widgetIdsAcrossAllTabs: Set<string>;
|
||||
}): string[] {
|
||||
return idsToRemove.filter(
|
||||
(widgetId) => !widgetIdsAcrossAllTabs.has(widgetId),
|
||||
);
|
||||
}
|
||||
|
||||
private validateChartFieldReferences({
|
||||
widgetInput,
|
||||
flatFieldMetadataMaps,
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
type GridPosition,
|
||||
type PageLayoutWidgetConditionalDisplay,
|
||||
type PageLayoutWidgetPosition,
|
||||
type GridPosition,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -110,5 +110,6 @@ export const createStandardPageLayoutWidgetFlatMetadata = ({
|
||||
deletedAt: null,
|
||||
conditionalDisplay: conditionalDisplay ?? null,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
};
|
||||
};
|
||||
|
||||
+1
@@ -34,6 +34,7 @@ export const ALL_JSONB_PROPERTIES_WITH_SERIALIZED_RELATION_BY_METADATA_NAME = {
|
||||
pageLayoutTab: {},
|
||||
pageLayoutWidget: {
|
||||
configuration: 'configuration',
|
||||
overrides: 'overrides',
|
||||
},
|
||||
commandMenuItem: {},
|
||||
navigationMenuItem: {},
|
||||
|
||||
+10
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
UniversalCreatePageLayoutWidgetAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout-widget/types/workspace-migration-page-layout-widget-action.type';
|
||||
import { fromUniversalConfigurationToFlatPageLayoutWidgetConfiguration } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-widget/services/utils/from-universal-configuration-to-flat-page-layout-widget-configuration.util';
|
||||
import { fromUniversalOverridesToPageLayoutWidgetOverrides } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-widget/services/utils/from-universal-overrides-to-page-layout-widget-overrides.util';
|
||||
import {
|
||||
WorkspaceMigrationActionRunnerArgs,
|
||||
WorkspaceMigrationActionRunnerContext,
|
||||
@@ -47,6 +49,13 @@ export class CreatePageLayoutWidgetActionHandlerService extends WorkspaceMigrati
|
||||
flatViewFieldGroupMaps: allFlatEntityMaps.flatViewFieldGroupMaps,
|
||||
});
|
||||
|
||||
const overrides = isDefined(action.flatEntity.universalOverrides)
|
||||
? fromUniversalOverridesToPageLayoutWidgetOverrides({
|
||||
universalOverrides: action.flatEntity.universalOverrides,
|
||||
flatPageLayoutTabMaps: allFlatEntityMaps.flatPageLayoutTabMaps,
|
||||
})
|
||||
: null;
|
||||
|
||||
const emptyUniversalForeignKeyAggregators =
|
||||
getUniversalFlatEntityEmptyForeignKeyAggregators({
|
||||
metadataName: 'pageLayoutWidget',
|
||||
@@ -57,6 +66,7 @@ export class CreatePageLayoutWidgetActionHandlerService extends WorkspaceMigrati
|
||||
flatEntity: {
|
||||
...action.flatEntity,
|
||||
configuration,
|
||||
overrides,
|
||||
pageLayoutTabId,
|
||||
objectMetadataId,
|
||||
applicationId: flatApplication.id,
|
||||
|
||||
+24
-7
@@ -10,6 +10,7 @@ import {
|
||||
UniversalUpdatePageLayoutWidgetAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout-widget/types/workspace-migration-page-layout-widget-action.type';
|
||||
import { fromUniversalConfigurationToFlatPageLayoutWidgetConfiguration } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-widget/services/utils/from-universal-configuration-to-flat-page-layout-widget-configuration.util';
|
||||
import { fromUniversalOverridesToPageLayoutWidgetOverrides } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-widget/services/utils/from-universal-overrides-to-page-layout-widget-overrides.util';
|
||||
import {
|
||||
WorkspaceMigrationActionRunnerArgs,
|
||||
WorkspaceMigrationActionRunnerContext,
|
||||
@@ -34,14 +35,17 @@ export class UpdatePageLayoutWidgetActionHandlerService extends WorkspaceMigrati
|
||||
universalIdentifier: action.universalIdentifier,
|
||||
});
|
||||
|
||||
const { universalConfiguration, ...updateWithResolvedForeignKeys } =
|
||||
resolveUniversalUpdateRelationIdentifiersToIds({
|
||||
metadataName: 'pageLayoutWidget',
|
||||
universalUpdate: action.update,
|
||||
allFlatEntityMaps,
|
||||
});
|
||||
const {
|
||||
universalConfiguration,
|
||||
universalOverrides,
|
||||
...updateWithResolvedForeignKeys
|
||||
} = resolveUniversalUpdateRelationIdentifiersToIds({
|
||||
metadataName: 'pageLayoutWidget',
|
||||
universalUpdate: action.update,
|
||||
allFlatEntityMaps,
|
||||
});
|
||||
|
||||
const update =
|
||||
const updateWithConfiguration =
|
||||
universalConfiguration === undefined
|
||||
? updateWithResolvedForeignKeys
|
||||
: {
|
||||
@@ -58,6 +62,19 @@ export class UpdatePageLayoutWidgetActionHandlerService extends WorkspaceMigrati
|
||||
}),
|
||||
};
|
||||
|
||||
const update =
|
||||
universalOverrides === undefined
|
||||
? updateWithConfiguration
|
||||
: universalOverrides === null
|
||||
? { ...updateWithConfiguration, overrides: null }
|
||||
: {
|
||||
...updateWithConfiguration,
|
||||
overrides: fromUniversalOverridesToPageLayoutWidgetOverrides({
|
||||
universalOverrides,
|
||||
flatPageLayoutTabMaps: allFlatEntityMaps.flatPageLayoutTabMaps,
|
||||
}),
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'update',
|
||||
metadataName: 'pageLayoutWidget',
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { type FormatRecordSerializedRelationProperties } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
|
||||
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 { type PageLayoutWidgetOverrides } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
|
||||
type UniversalPageLayoutWidgetOverrides =
|
||||
FormatRecordSerializedRelationProperties<PageLayoutWidgetOverrides>;
|
||||
|
||||
export const fromUniversalOverridesToPageLayoutWidgetOverrides = ({
|
||||
universalOverrides,
|
||||
flatPageLayoutTabMaps,
|
||||
}: {
|
||||
universalOverrides: UniversalPageLayoutWidgetOverrides;
|
||||
flatPageLayoutTabMaps: FlatPageLayoutTabMaps;
|
||||
}): PageLayoutWidgetOverrides => {
|
||||
const { pageLayoutTabUniversalIdentifier, ...scalarOverrides } =
|
||||
universalOverrides;
|
||||
|
||||
if (!isDefined(pageLayoutTabUniversalIdentifier)) {
|
||||
return {
|
||||
...scalarOverrides,
|
||||
};
|
||||
}
|
||||
|
||||
const flatPageLayoutTab =
|
||||
findFlatEntityByUniversalIdentifierOrThrow<FlatPageLayoutTab>({
|
||||
flatEntityMaps: flatPageLayoutTabMaps,
|
||||
universalIdentifier: pageLayoutTabUniversalIdentifier,
|
||||
});
|
||||
|
||||
return {
|
||||
...scalarOverrides,
|
||||
pageLayoutTabId:
|
||||
flatPageLayoutTab.id as PageLayoutWidgetOverrides['pageLayoutTabId'],
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user