Wire fields widget to backend + basic edition (#17965)
Closes https://github.com/twentyhq/core-team-issues/issues/2215 Closes https://github.com/twentyhq/core-team-issues/issues/2216 Closes https://github.com/twentyhq/core-team-issues/issues/2218 ## Fields widget edition demo https://github.com/user-attachments/assets/08626d70-8fcb-4ae2-9222-10ef57e75f90 ## Dashboards still work https://github.com/user-attachments/assets/aa9a9c45-a0a2-481e-b132-bc52254778da
This commit is contained in:
committed by
GitHub
parent
b107020ad3
commit
00209f7e2c
+2
@@ -8,6 +8,7 @@ import { WorkspaceFlatPageLayoutWidgetMapCacheService } from 'src/engine/metadat
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
|
||||
import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -17,6 +18,7 @@ import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-
|
||||
PageLayoutTabEntity,
|
||||
ObjectMetadataEntity,
|
||||
FieldMetadataEntity,
|
||||
ViewEntity,
|
||||
]),
|
||||
],
|
||||
providers: [
|
||||
|
||||
+21
-40
@@ -17,6 +17,7 @@ import { validateGraphFlatPageLayoutWidgetForUpdate } from 'src/engine/metadata-
|
||||
import { validateIframeFlatPageLayoutWidgetForCreation } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-iframe-flat-page-layout-widget-for-creation.util';
|
||||
import { validateIframeFlatPageLayoutWidgetForUpdate } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-iframe-flat-page-layout-widget-for-update.util';
|
||||
import { validateSimpleRecordPageWidgetForCreation } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-simple-record-page-widget-for-creation.util';
|
||||
import { validateSimpleRecordPageWidgetForUpdate } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-simple-record-page-widget-for-update.util';
|
||||
import { validateStandaloneRichTextFlatPageLayoutWidgetForCreation } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-standalone-rich-text-flat-page-layout-widget-for-creation.util';
|
||||
import { validateStandaloneRichTextFlatPageLayoutWidgetForUpdate } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-standalone-rich-text-flat-page-layout-widget-for-update.util';
|
||||
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
|
||||
@@ -110,55 +111,35 @@ export class FlatPageLayoutWidgetTypeValidatorService {
|
||||
STANDALONE_RICH_TEXT:
|
||||
validateStandaloneRichTextFlatPageLayoutWidgetForUpdate,
|
||||
FRONT_COMPONENT: validateFrontComponentFlatPageLayoutWidgetForUpdate,
|
||||
TIMELINE: rejectWidgetType(
|
||||
WidgetType.TIMELINE,
|
||||
'Widget type TIMELINE is not supported yet.',
|
||||
msg`Widget type TIMELINE is not supported yet.`,
|
||||
TIMELINE: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.TIMELINE,
|
||||
),
|
||||
TASKS: rejectWidgetType(
|
||||
WidgetType.TASKS,
|
||||
'Widget type TASKS is not supported yet.',
|
||||
msg`Widget type TASKS is not supported yet.`,
|
||||
TASKS: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.TASKS,
|
||||
),
|
||||
NOTES: rejectWidgetType(
|
||||
WidgetType.NOTES,
|
||||
'Widget type NOTES is not supported yet.',
|
||||
msg`Widget type NOTES is not supported yet.`,
|
||||
NOTES: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.NOTES,
|
||||
),
|
||||
FILES: rejectWidgetType(
|
||||
WidgetType.FILES,
|
||||
'Widget type FILES is not supported yet.',
|
||||
msg`Widget type FILES is not supported yet.`,
|
||||
FILES: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.FILES,
|
||||
),
|
||||
EMAILS: rejectWidgetType(
|
||||
WidgetType.EMAILS,
|
||||
'Widget type EMAILS is not supported yet.',
|
||||
msg`Widget type EMAILS is not supported yet.`,
|
||||
EMAILS: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.EMAILS,
|
||||
),
|
||||
CALENDAR: rejectWidgetType(
|
||||
WidgetType.CALENDAR,
|
||||
'Widget type CALENDAR is not supported yet.',
|
||||
msg`Widget type CALENDAR is not supported yet.`,
|
||||
CALENDAR: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.CALENDAR,
|
||||
),
|
||||
FIELD_RICH_TEXT: rejectWidgetType(
|
||||
WidgetType.FIELD_RICH_TEXT,
|
||||
'Widget type FIELD_RICH_TEXT is not supported yet.',
|
||||
msg`Widget type FIELD_RICH_TEXT is not supported yet.`,
|
||||
FIELD_RICH_TEXT: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.FIELD_RICH_TEXT,
|
||||
),
|
||||
WORKFLOW: rejectWidgetType(
|
||||
WidgetType.WORKFLOW,
|
||||
'Widget type WORKFLOW is not supported yet.',
|
||||
msg`Widget type WORKFLOW is not supported yet.`,
|
||||
WORKFLOW: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.WORKFLOW,
|
||||
),
|
||||
WORKFLOW_VERSION: rejectWidgetType(
|
||||
WidgetType.WORKFLOW_VERSION,
|
||||
'Widget type WORKFLOW_VERSION is not supported yet.',
|
||||
msg`Widget type WORKFLOW_VERSION is not supported yet.`,
|
||||
WORKFLOW_VERSION: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.WORKFLOW_VERSION,
|
||||
),
|
||||
WORKFLOW_RUN: rejectWidgetType(
|
||||
WidgetType.WORKFLOW_RUN,
|
||||
'Widget type WORKFLOW_RUN is not supported yet.',
|
||||
msg`Widget type WORKFLOW_RUN is not supported yet.`,
|
||||
WORKFLOW_RUN: validateSimpleRecordPageWidgetForUpdate(
|
||||
WidgetConfigurationType.WORKFLOW_RUN,
|
||||
),
|
||||
};
|
||||
|
||||
|
||||
+13
@@ -13,6 +13,7 @@ import { fromPageLayoutWidgetEntityToFlatPageLayoutWidget } from 'src/engine/met
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
|
||||
import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
|
||||
import { createIdToUniversalIdentifierMap } from 'src/engine/workspace-cache/utils/create-id-to-universal-identifier-map.util';
|
||||
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
|
||||
@@ -31,6 +32,8 @@ export class WorkspaceFlatPageLayoutWidgetMapCacheService extends WorkspaceCache
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectRepository(FieldMetadataEntity)
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
@InjectRepository(ViewEntity)
|
||||
private readonly viewRepository: Repository<ViewEntity>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -44,6 +47,7 @@ export class WorkspaceFlatPageLayoutWidgetMapCacheService extends WorkspaceCache
|
||||
pageLayoutTabs,
|
||||
objectMetadatas,
|
||||
fieldMetadatas,
|
||||
views,
|
||||
] = await Promise.all([
|
||||
this.pageLayoutWidgetRepository.find({
|
||||
where: { workspaceId },
|
||||
@@ -69,6 +73,11 @@ export class WorkspaceFlatPageLayoutWidgetMapCacheService extends WorkspaceCache
|
||||
select: ['id', 'universalIdentifier'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.viewRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const applicationIdToUniversalIdentifierMap =
|
||||
@@ -80,6 +89,9 @@ export class WorkspaceFlatPageLayoutWidgetMapCacheService extends WorkspaceCache
|
||||
const fieldMetadataUniversalIdentifierById = Object.fromEntries(
|
||||
createIdToUniversalIdentifierMap(fieldMetadatas),
|
||||
);
|
||||
const viewUniversalIdentifierById = Object.fromEntries(
|
||||
createIdToUniversalIdentifierMap(views),
|
||||
);
|
||||
|
||||
const flatPageLayoutWidgetMaps = createEmptyFlatEntityMaps();
|
||||
|
||||
@@ -91,6 +103,7 @@ export class WorkspaceFlatPageLayoutWidgetMapCacheService extends WorkspaceCache
|
||||
pageLayoutTabIdToUniversalIdentifierMap,
|
||||
objectMetadataIdToUniversalIdentifierMap,
|
||||
fieldMetadataUniversalIdentifierById,
|
||||
viewUniversalIdentifierById,
|
||||
});
|
||||
|
||||
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
|
||||
|
||||
+3
@@ -20,6 +20,7 @@ export type FromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreateArgs =
|
||||
| 'flatObjectMetadataMaps'
|
||||
| 'flatFieldMetadataMaps'
|
||||
| 'flatViewFieldGroupMaps'
|
||||
| 'flatViewMaps'
|
||||
>;
|
||||
|
||||
export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({
|
||||
@@ -30,6 +31,7 @@ export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatViewFieldGroupMaps,
|
||||
flatViewMaps,
|
||||
}: FromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreateArgs): FlatPageLayoutWidget => {
|
||||
const { pageLayoutTabId, ...createPageLayoutWidgetInput } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
@@ -82,6 +84,7 @@ export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({
|
||||
flatFieldMetadataMaps.universalIdentifierById,
|
||||
viewFieldGroupUniversalIdentifierById:
|
||||
flatViewFieldGroupMaps.universalIdentifierById,
|
||||
viewUniversalIdentifierById: flatViewMaps.universalIdentifierById,
|
||||
shouldThrowOnMissingIdentifier: true,
|
||||
}),
|
||||
};
|
||||
|
||||
+25
-2
@@ -74,11 +74,13 @@ export const fromPageLayoutWidgetConfigurationToUniversalConfiguration = ({
|
||||
configuration,
|
||||
fieldMetadataUniversalIdentifierById,
|
||||
viewFieldGroupUniversalIdentifierById = {},
|
||||
viewUniversalIdentifierById = {},
|
||||
shouldThrowOnMissingIdentifier = false,
|
||||
}: {
|
||||
configuration: PageLayoutWidgetConfiguration;
|
||||
fieldMetadataUniversalIdentifierById: Partial<Record<string, string>>;
|
||||
viewFieldGroupUniversalIdentifierById?: Partial<Record<string, string>>;
|
||||
viewUniversalIdentifierById?: Partial<Record<string, string>>;
|
||||
shouldThrowOnMissingIdentifier?: boolean;
|
||||
}): UniversalPageLayoutWidgetConfiguration => {
|
||||
switch (configuration.configurationType) {
|
||||
@@ -269,10 +271,30 @@ export const fromPageLayoutWidgetConfigurationToUniversalConfiguration = ({
|
||||
}
|
||||
|
||||
case WidgetConfigurationType.FIELDS: {
|
||||
const { newFieldDefaultConfiguration, ...rest } = configuration;
|
||||
const { viewId, newFieldDefaultConfiguration, ...rest } = configuration;
|
||||
|
||||
let viewUniversalIdentifier: string | null = null;
|
||||
|
||||
if (isDefined(viewId)) {
|
||||
viewUniversalIdentifier = viewUniversalIdentifierById[viewId] ?? null;
|
||||
|
||||
if (
|
||||
!isDefined(viewUniversalIdentifier) &&
|
||||
shouldThrowOnMissingIdentifier
|
||||
) {
|
||||
throw new FlatEntityMapsException(
|
||||
`View universal identifier not found for id: ${viewId}`,
|
||||
FlatEntityMapsExceptionCode.RELATION_UNIVERSAL_IDENTIFIER_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDefined(newFieldDefaultConfiguration)) {
|
||||
return configuration;
|
||||
return {
|
||||
...rest,
|
||||
newFieldDefaultConfiguration,
|
||||
viewId: viewUniversalIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
let viewFieldGroupUniversalIdentifier: string | null = null;
|
||||
@@ -296,6 +318,7 @@ export const fromPageLayoutWidgetConfigurationToUniversalConfiguration = ({
|
||||
|
||||
return {
|
||||
...rest,
|
||||
viewId: viewUniversalIdentifier,
|
||||
newFieldDefaultConfiguration: {
|
||||
isVisible: newFieldDefaultConfiguration.isVisible,
|
||||
viewFieldGroupId: viewFieldGroupUniversalIdentifier,
|
||||
|
||||
+3
@@ -13,6 +13,7 @@ type FromPageLayoutWidgetEntityToFlatPageLayoutWidgetArgs =
|
||||
FromEntityToFlatEntityArgs<'pageLayoutWidget'> & {
|
||||
fieldMetadataUniversalIdentifierById: Partial<Record<string, string>>;
|
||||
viewFieldGroupUniversalIdentifierById?: Partial<Record<string, string>>;
|
||||
viewUniversalIdentifierById?: Partial<Record<string, string>>;
|
||||
};
|
||||
|
||||
export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ({
|
||||
@@ -22,6 +23,7 @@ export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ({
|
||||
objectMetadataIdToUniversalIdentifierMap,
|
||||
fieldMetadataUniversalIdentifierById,
|
||||
viewFieldGroupUniversalIdentifierById,
|
||||
viewUniversalIdentifierById,
|
||||
}: FromPageLayoutWidgetEntityToFlatPageLayoutWidgetArgs): FlatPageLayoutWidget => {
|
||||
const pageLayoutWidgetEntityWithoutRelations = removePropertiesFromRecord(
|
||||
pageLayoutWidgetEntity,
|
||||
@@ -73,6 +75,7 @@ export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ({
|
||||
configuration: pageLayoutWidgetEntityWithoutRelations.configuration,
|
||||
fieldMetadataUniversalIdentifierById,
|
||||
viewFieldGroupUniversalIdentifierById,
|
||||
viewUniversalIdentifierById,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
+3
@@ -31,6 +31,7 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatViewFieldGroupMaps,
|
||||
flatViewMaps,
|
||||
}: {
|
||||
updatePageLayoutWidgetInput: UpdatePageLayoutWidgetInputWithId;
|
||||
flatPageLayoutWidgetMaps: FlatPageLayoutWidgetMaps;
|
||||
@@ -39,6 +40,7 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
|
||||
| 'flatObjectMetadataMaps'
|
||||
| 'flatFieldMetadataMaps'
|
||||
| 'flatViewFieldGroupMaps'
|
||||
| 'flatViewMaps'
|
||||
>): FlatPageLayoutWidget => {
|
||||
const { id: pageLayoutWidgetToUpdateId } =
|
||||
extractAndSanitizeObjectStringFields(rawUpdatePageLayoutWidgetInput, [
|
||||
@@ -102,6 +104,7 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
|
||||
flatFieldMetadataMaps.universalIdentifierById,
|
||||
viewFieldGroupUniversalIdentifierById:
|
||||
flatViewFieldGroupMaps.universalIdentifierById,
|
||||
viewUniversalIdentifierById: flatViewMaps.universalIdentifierById,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { msg, t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs } from 'src/engine/metadata-modules/flat-page-layout-widget/services/flat-page-layout-widget-type-validator.service';
|
||||
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
|
||||
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
|
||||
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
|
||||
|
||||
export const validateSimpleRecordPageWidgetForUpdate =
|
||||
(expectedConfigurationType: WidgetConfigurationType) =>
|
||||
(
|
||||
args: ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs,
|
||||
): FlatPageLayoutWidgetValidationError[] => {
|
||||
const { flatEntityToValidate } = args;
|
||||
const { universalConfiguration, title: widgetTitle } = flatEntityToValidate;
|
||||
|
||||
if (!isDefined(universalConfiguration)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const errors: FlatPageLayoutWidgetValidationError[] = [];
|
||||
|
||||
if (
|
||||
universalConfiguration.configurationType !== expectedConfigurationType
|
||||
) {
|
||||
const expectedString = expectedConfigurationType.toString();
|
||||
const actualString =
|
||||
universalConfiguration.configurationType?.toString() ?? 'undefined';
|
||||
|
||||
errors.push({
|
||||
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
|
||||
message: t`Invalid configuration type for widget "${widgetTitle}". Expected ${expectedString}, got ${actualString}`,
|
||||
userFriendlyMessage: msg`Invalid configuration type for widget`,
|
||||
value: universalConfiguration.configurationType,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
Reference in New Issue
Block a user