[Dashboards] Server: Rich Text Widget (#16296)

closes https://github.com/twentyhq/core-team-issues/issues/1893

sync in the transformation layer - 
with just the blocknote format -

<img width="1532" height="1058" alt="CleanShot 2025-12-04 at 16 43 27"
src="https://github.com/user-attachments/assets/081483fb-0bd0-4f14-81fc-8ea2261367a2"
/>

with just the mardown format - 
<img width="1534" height="1056" alt="CleanShot 2025-12-04 at 16 45 03"
src="https://github.com/user-attachments/assets/4eae513b-423d-4597-b79c-97d1f33ab994"
/>

attaching response since in screen shot its getting cut - 
```
{
  "id": "7189a10a-177c-4cc2-8946-950b8c125913",
  "pageLayoutTabId": "82ac56dd-4351-43ad-bf3d-49624c057e17",
  "workspaceId": "20202020-1c25-4d02-bf25-6aeccf7ea419",
  "title": "awdawd awdawdawd",
  "type": "STANDALONE_RICH_TEXT",
  "objectMetadataId": null,
  "gridPosition": {
    "row": 1,
    "column": 1,
    "rowSpan": 1,
    "columnSpan": 1
  },
  "configuration": {
    "body": {
      "markdown": "# Hello World",
      "blocknote": "[{\"id\":\"2e744f21-7c71-414d-b87f-fb48400c593c\",\"type\":\"heading\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\",\"level\":1},\"content\":[{\"type\":\"text\",\"text\":\"Hello World\",\"styles\":{}}],\"children\":[]}]"
    }
  },
  "createdAt": "2025-12-04T11:14:52.212Z",
  "updatedAt": "2025-12-04T11:14:52.212Z",
  "deletedAt": null
}
```
This commit is contained in:
nitin
2025-12-04 18:28:33 +05:30
committed by GitHub
parent 193be72289
commit 2f36c2e82e
24 changed files with 366 additions and 99 deletions
@@ -0,0 +1,17 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsOptional, IsString } from 'class-validator';
import { type RichTextV2Metadata } from 'twenty-shared/types';
@ObjectType('RichTextV2Body')
export class RichTextV2BodyDTO implements RichTextV2Metadata {
@Field(() => String, { nullable: true })
@IsString()
@IsOptional()
blocknote?: string | null;
@Field(() => String, { nullable: true })
@IsString()
@IsOptional()
markdown: string | null;
}
@@ -0,0 +1,15 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Type } from 'class-transformer';
import { IsNotEmpty, ValidateNested } from 'class-validator';
import { RichTextV2BodyDTO } from 'src/engine/core-modules/page-layout/dtos/rich-text-v2-body.dto';
@ObjectType('StandaloneRichTextConfiguration')
export class StandaloneRichTextConfigurationDTO {
@Field(() => RichTextV2BodyDTO)
@ValidateNested()
@Type(() => RichTextV2BodyDTO)
@IsNotEmpty()
body: RichTextV2BodyDTO;
}
@@ -6,6 +6,7 @@ import { GaugeChartConfigurationDTO } from 'src/engine/core-modules/page-layout/
import { IframeConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/iframe-configuration.dto';
import { LineChartConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/line-chart-configuration.dto';
import { PieChartConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/pie-chart-configuration.dto';
import { StandaloneRichTextConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/standalone-rich-text-configuration.dto';
import { GraphType } from 'src/engine/core-modules/page-layout/enums/graph-type.enum';
import { WidgetConfigurationType } from 'src/engine/core-modules/page-layout/enums/widget-configuration-type.enum';
@@ -18,6 +19,7 @@ export const WidgetConfiguration = createUnionType({
AggregateChartConfigurationDTO,
GaugeChartConfigurationDTO,
IframeConfigurationDTO,
StandaloneRichTextConfigurationDTO,
],
resolveType(configuration: Record<string, unknown>) {
if (!('configurationType' in configuration)) {
@@ -52,6 +54,13 @@ export const WidgetConfiguration = createUnionType({
return IframeConfigurationDTO;
}
if (
configuration.configurationType ===
WidgetConfigurationType.STANDALONE_RICH_TEXT_CONFIG
) {
return StandaloneRichTextConfigurationDTO;
}
throw new Error(
`Unknown widget configuration type: ${configuration.configurationType}`,
);
@@ -64,4 +73,5 @@ export type WidgetConfigurationInterface =
| PieChartConfigurationDTO
| AggregateChartConfigurationDTO
| GaugeChartConfigurationDTO
| IframeConfigurationDTO;
| IframeConfigurationDTO
| StandaloneRichTextConfigurationDTO;
@@ -1,4 +1,5 @@
export enum WidgetConfigurationType {
CHART_CONFIG = 'CHART_CONFIG',
IFRAME_CONFIG = 'IFRAME_CONFIG',
STANDALONE_RICH_TEXT_CONFIG = 'STANDALONE_RICH_TEXT_CONFIG',
}
@@ -3,13 +3,14 @@ export enum WidgetType {
IFRAME = 'IFRAME',
FIELDS = 'FIELDS',
GRAPH = 'GRAPH',
STANDALONE_RICH_TEXT = 'STANDALONE_RICH_TEXT',
TIMELINE = 'TIMELINE',
TASKS = 'TASKS',
NOTES = 'NOTES',
FILES = 'FILES',
EMAILS = 'EMAILS',
CALENDAR = 'CALENDAR',
RICH_TEXT = 'RICH_TEXT',
FIELD_RICH_TEXT = 'FIELD_RICH_TEXT',
WORKFLOW = 'WORKFLOW',
WORKFLOW_VERSION = 'WORKFLOW_VERSION',
WORKFLOW_RUN = 'WORKFLOW_RUN',
@@ -142,7 +142,7 @@ export class PageLayoutWidgetService {
);
try {
validatedConfig = validateAndTransformWidgetConfiguration({
validatedConfig = await validateAndTransformWidgetConfiguration({
type: pageLayoutWidgetData.type,
configuration: pageLayoutWidgetData.configuration,
isDashboardV2Enabled,
@@ -246,7 +246,7 @@ export class PageLayoutWidgetService {
);
try {
validatedConfig = validateAndTransformWidgetConfiguration({
validatedConfig = await validateAndTransformWidgetConfiguration({
type: typeForValidation,
configuration: updateData.configuration,
isDashboardV2Enabled,
@@ -4,6 +4,9 @@ import {
INVALID_IFRAME_CONFIG_EMPTY_URL,
INVALID_NUMBER_CHART_CONFIG_BAD_UUID,
INVALID_NUMBER_CHART_CONFIG_MISSING_FIELDS,
INVALID_STANDALONE_RICH_TEXT_CONFIG_BODY_WRONG_TYPE,
INVALID_STANDALONE_RICH_TEXT_CONFIG_INVALID_SUBFIELDS,
INVALID_STANDALONE_RICH_TEXT_CONFIG_MISSING_BODY,
INVALID_VERTICAL_BAR_CHART_CONFIG_MISSING_GROUP_BY,
TEST_GAUGE_CHART_CONFIG,
TEST_HORIZONTAL_BAR_CHART_CONFIG,
@@ -13,6 +16,8 @@ import {
TEST_NUMBER_CHART_CONFIG,
TEST_NUMBER_CHART_CONFIG_MINIMAL,
TEST_PIE_CHART_CONFIG,
TEST_STANDALONE_RICH_TEXT_CONFIG,
TEST_STANDALONE_RICH_TEXT_CONFIG_MINIMAL,
TEST_VERTICAL_BAR_CHART_CONFIG,
TEST_VERTICAL_BAR_CHART_CONFIG_MINIMAL,
} from 'test/integration/constants/widget-configuration-test-data.constants';
@@ -20,10 +25,22 @@ import {
import { WidgetType } from 'src/engine/core-modules/page-layout/enums/widget-type.enum';
import { validateAndTransformWidgetConfiguration } from 'src/engine/core-modules/page-layout/utils/validate-and-transform-widget-configuration.util';
jest.mock(
'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util',
() => ({
transformRichTextV2Value: jest.fn((value) =>
Promise.resolve({
blocknote: value.blocknote ?? null,
markdown: value.markdown ?? null,
}),
),
}),
);
describe('validateAndTransformWidgetConfiguration', () => {
describe('IFRAME widget', () => {
it('should validate and transform valid iframe configuration', () => {
const result = validateAndTransformWidgetConfiguration({
it('should validate and transform valid iframe configuration', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.IFRAME,
configuration: TEST_IFRAME_CONFIG,
isDashboardV2Enabled: false,
@@ -32,31 +49,86 @@ describe('validateAndTransformWidgetConfiguration', () => {
expect(result).toMatchObject(TEST_IFRAME_CONFIG);
});
it('should throw error for invalid URL', () => {
expect(() =>
it('should throw error for invalid URL', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.IFRAME,
configuration: INVALID_IFRAME_CONFIG_BAD_URL,
isDashboardV2Enabled: false,
}),
).toThrow(/url must be a URL address/);
).rejects.toThrow(/url must be a URL address/);
});
it('should throw error for empty URL', () => {
expect(() =>
it('should throw error for empty URL', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.IFRAME,
configuration: INVALID_IFRAME_CONFIG_EMPTY_URL,
isDashboardV2Enabled: false,
}),
).toThrow(/url must be a URL address/);
).rejects.toThrow(/url must be a URL address/);
});
});
describe('STANDALONE_RICH_TEXT widget', () => {
it('should validate and transform valid standalone rich text configuration', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.STANDALONE_RICH_TEXT,
configuration: TEST_STANDALONE_RICH_TEXT_CONFIG,
isDashboardV2Enabled: false,
});
expect(result).toMatchObject(TEST_STANDALONE_RICH_TEXT_CONFIG);
});
it('should validate minimal standalone rich text configuration', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.STANDALONE_RICH_TEXT,
configuration: TEST_STANDALONE_RICH_TEXT_CONFIG_MINIMAL,
isDashboardV2Enabled: false,
});
expect(result).toMatchObject(TEST_STANDALONE_RICH_TEXT_CONFIG_MINIMAL);
});
it('should throw error for missing body', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.STANDALONE_RICH_TEXT,
configuration: INVALID_STANDALONE_RICH_TEXT_CONFIG_MISSING_BODY,
isDashboardV2Enabled: false,
}),
).rejects.toThrow(/body/);
});
it('should throw error when body is wrong type', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.STANDALONE_RICH_TEXT,
configuration: INVALID_STANDALONE_RICH_TEXT_CONFIG_BODY_WRONG_TYPE,
isDashboardV2Enabled: false,
}),
).rejects.toThrow();
});
it('should strip invalid subfields from body', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.STANDALONE_RICH_TEXT,
configuration: INVALID_STANDALONE_RICH_TEXT_CONFIG_INVALID_SUBFIELDS,
isDashboardV2Enabled: false,
});
expect(result).toBeDefined();
expect((result as any).body.blocknote).toBeDefined();
expect((result as any).body.markdown).toBe('valid');
expect((result as any).body.invalidField).toBeUndefined();
});
});
describe('GRAPH widget', () => {
describe('NUMBER graph', () => {
it('should validate full number graph configuration', () => {
const result = validateAndTransformWidgetConfiguration({
it('should validate full number graph configuration', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_NUMBER_CHART_CONFIG,
isDashboardV2Enabled: false,
@@ -65,8 +137,8 @@ describe('validateAndTransformWidgetConfiguration', () => {
expect(result).toMatchObject(TEST_NUMBER_CHART_CONFIG);
});
it('should validate minimal number graph configuration', () => {
const result = validateAndTransformWidgetConfiguration({
it('should validate minimal number graph configuration', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_NUMBER_CHART_CONFIG_MINIMAL,
isDashboardV2Enabled: false,
@@ -75,30 +147,30 @@ describe('validateAndTransformWidgetConfiguration', () => {
expect(result).toMatchObject(TEST_NUMBER_CHART_CONFIG_MINIMAL);
});
it('should throw error for partial number graph configuration with missing required fields', () => {
expect(() =>
it('should throw error for partial number graph configuration with missing required fields', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: INVALID_NUMBER_CHART_CONFIG_MISSING_FIELDS,
isDashboardV2Enabled: false,
}),
).toThrow(/aggregateFieldMetadataId.*aggregateOperation/);
).rejects.toThrow(/aggregateFieldMetadataId.*aggregateOperation/);
});
it('should throw error for invalid UUID', () => {
expect(() =>
it('should throw error for invalid UUID', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: INVALID_NUMBER_CHART_CONFIG_BAD_UUID,
isDashboardV2Enabled: false,
}),
).toThrow(/aggregateFieldMetadataId must be a UUID/);
).rejects.toThrow(/aggregateFieldMetadataId must be a UUID/);
});
});
describe('VERTICAL_BAR graph', () => {
it('should validate full vertical bar graph configuration', () => {
const result = validateAndTransformWidgetConfiguration({
it('should validate full vertical bar graph configuration', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_VERTICAL_BAR_CHART_CONFIG,
isDashboardV2Enabled: false,
@@ -107,8 +179,8 @@ describe('validateAndTransformWidgetConfiguration', () => {
expect(result).toMatchObject(TEST_VERTICAL_BAR_CHART_CONFIG);
});
it('should validate minimal vertical bar graph configuration', () => {
const result = validateAndTransformWidgetConfiguration({
it('should validate minimal vertical bar graph configuration', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_VERTICAL_BAR_CHART_CONFIG_MINIMAL,
isDashboardV2Enabled: false,
@@ -117,20 +189,20 @@ describe('validateAndTransformWidgetConfiguration', () => {
expect(result).toMatchObject(TEST_VERTICAL_BAR_CHART_CONFIG_MINIMAL);
});
it('should throw error for partial vertical bar graph configuration with missing required fields', () => {
expect(() =>
it('should throw error for partial vertical bar graph configuration with missing required fields', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: INVALID_VERTICAL_BAR_CHART_CONFIG_MISSING_GROUP_BY,
isDashboardV2Enabled: false,
}),
).toThrow(/primaryAxisGroupByFieldMetadataId/);
).rejects.toThrow(/primaryAxisGroupByFieldMetadataId/);
});
});
describe('HORIZONTAL_BAR graph', () => {
it('should validate full horizontal bar graph configuration', () => {
const result = validateAndTransformWidgetConfiguration({
it('should validate full horizontal bar graph configuration', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_HORIZONTAL_BAR_CHART_CONFIG,
isDashboardV2Enabled: false,
@@ -139,8 +211,8 @@ describe('validateAndTransformWidgetConfiguration', () => {
expect(result).toMatchObject(TEST_HORIZONTAL_BAR_CHART_CONFIG);
});
it('should validate minimal horizontal bar graph configuration', () => {
const result = validateAndTransformWidgetConfiguration({
it('should validate minimal horizontal bar graph configuration', async () => {
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_HORIZONTAL_BAR_CHART_CONFIG_MINIMAL,
isDashboardV2Enabled: false,
@@ -149,24 +221,24 @@ describe('validateAndTransformWidgetConfiguration', () => {
expect(result).toMatchObject(TEST_HORIZONTAL_BAR_CHART_CONFIG_MINIMAL);
});
it('should throw error for partial horizontal bar graph configuration with missing required fields', () => {
expect(() =>
it('should throw error for partial horizontal bar graph configuration with missing required fields', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: INVALID_HORIZONTAL_BAR_CHART_CONFIG_MISSING_GROUP_BY,
isDashboardV2Enabled: false,
}),
).toThrow(/primaryAxisGroupByFieldMetadataId/);
).rejects.toThrow(/primaryAxisGroupByFieldMetadataId/);
});
});
it('should return null for unsupported graph type', () => {
it('should return null for unsupported graph type', async () => {
const configuration = {
graphType: 'UNSUPPORTED',
viewId: '550e8400-e29b-41d4-a716-446655440000',
};
const result = validateAndTransformWidgetConfiguration({
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: configuration,
isDashboardV2Enabled: false,
@@ -175,12 +247,12 @@ describe('validateAndTransformWidgetConfiguration', () => {
expect(result).toBeNull();
});
it('should return null for missing graph type', () => {
it('should return null for missing graph type', async () => {
const configuration = {
viewId: '550e8400-e29b-41d4-a716-446655440000',
};
const result = validateAndTransformWidgetConfiguration({
const result = await validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: configuration,
isDashboardV2Enabled: false,
@@ -191,40 +263,40 @@ describe('validateAndTransformWidgetConfiguration', () => {
});
describe('Edge cases', () => {
it('should throw error for null configuration', () => {
expect(() =>
it('should throw error for null configuration', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.IFRAME,
configuration: null,
isDashboardV2Enabled: false,
}),
).toThrow('Invalid configuration: not an object');
).rejects.toThrow('Invalid configuration: not an object');
});
it('should throw error for undefined configuration', () => {
expect(() =>
it('should throw error for undefined configuration', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.IFRAME,
configuration: undefined,
isDashboardV2Enabled: false,
}),
).toThrow('Invalid configuration: not an object');
).rejects.toThrow('Invalid configuration: not an object');
});
it('should throw error for non-object configuration', () => {
expect(() =>
it('should throw error for non-object configuration', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.IFRAME,
configuration: 'string',
isDashboardV2Enabled: false,
}),
).toThrow('Invalid configuration: not an object');
).rejects.toThrow('Invalid configuration: not an object');
});
it('should return null for unsupported widget type', () => {
it('should return null for unsupported widget type', async () => {
const configuration = { someField: 'value' };
const result = validateAndTransformWidgetConfiguration({
const result = await validateAndTransformWidgetConfiguration({
type: 'UNSUPPORTED' as WidgetType,
configuration: configuration,
isDashboardV2Enabled: false,
@@ -235,72 +307,72 @@ describe('validateAndTransformWidgetConfiguration', () => {
});
describe('Error messages', () => {
it('should include validation details in error message', () => {
expect(() =>
it('should include validation details in error message', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: INVALID_NUMBER_CHART_CONFIG_BAD_UUID,
isDashboardV2Enabled: false,
}),
).toThrow(/aggregateFieldMetadataId must be a UUID/);
).rejects.toThrow(/aggregateFieldMetadataId must be a UUID/);
});
});
describe('Feature flags', () => {
it('should throw error for GAUGE chart type when IS_DASHBOARD_V2_ENABLED is false', () => {
expect(() =>
it('should throw error for GAUGE chart type when IS_DASHBOARD_V2_ENABLED is false', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_GAUGE_CHART_CONFIG,
isDashboardV2Enabled: false,
}),
).toThrow(/IS_DASHBOARD_V2_ENABLED feature flag/);
).rejects.toThrow(/IS_DASHBOARD_V2_ENABLED feature flag/);
});
it('should not throw error for GAUGE chart type when IS_DASHBOARD_V2_ENABLED is true', () => {
expect(() =>
it('should not throw error for GAUGE chart type when IS_DASHBOARD_V2_ENABLED is true', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_GAUGE_CHART_CONFIG,
isDashboardV2Enabled: true,
}),
).not.toThrow();
).resolves.not.toThrow();
});
it('should not throw error for PIE chart type regardless of IS_DASHBOARD_V2_ENABLED', () => {
expect(() =>
it('should not throw error for PIE chart type regardless of IS_DASHBOARD_V2_ENABLED', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_PIE_CHART_CONFIG,
isDashboardV2Enabled: false,
}),
).not.toThrow();
).resolves.not.toThrow();
expect(() =>
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_PIE_CHART_CONFIG,
isDashboardV2Enabled: true,
}),
).not.toThrow();
).resolves.not.toThrow();
});
it('should not throw error for LINE chart type regardless of IS_DASHBOARD_V2_ENABLED', () => {
expect(() =>
it('should not throw error for LINE chart type regardless of IS_DASHBOARD_V2_ENABLED', async () => {
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_LINE_CHART_CONFIG,
isDashboardV2Enabled: false,
}),
).not.toThrow();
).resolves.not.toThrow();
expect(() =>
await expect(
validateAndTransformWidgetConfiguration({
type: WidgetType.GRAPH,
configuration: TEST_LINE_CHART_CONFIG,
isDashboardV2Enabled: true,
}),
).not.toThrow();
).resolves.not.toThrow();
});
});
});
@@ -18,14 +18,21 @@ export const injectWidgetConfigurationDiscriminator = (
return {
...configuration,
configurationType: WidgetConfigurationType.IFRAME_CONFIG,
} as ConfigurationWithDiscriminator;
} satisfies ConfigurationWithDiscriminator;
}
if (widgetType === WidgetType.GRAPH && 'graphType' in configuration) {
return {
...configuration,
configurationType: WidgetConfigurationType.CHART_CONFIG,
} as ConfigurationWithDiscriminator;
} satisfies ConfigurationWithDiscriminator;
}
if (widgetType === WidgetType.STANDALONE_RICH_TEXT) {
return {
...configuration,
configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT_CONFIG,
} satisfies ConfigurationWithDiscriminator;
}
return configuration as ConfigurationWithDiscriminator;
@@ -3,11 +3,13 @@ import { validateSync, type ValidationError } from 'class-validator';
import { isDefined } from 'twenty-shared/utils';
import { AggregateChartConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/aggregate-chart-configuration.dto';
import { transformRichTextV2Value } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util';
import { BarChartConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/bar-chart-configuration.dto';
import { GaugeChartConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/gauge-chart-configuration.dto';
import { IframeConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/iframe-configuration.dto';
import { LineChartConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/line-chart-configuration.dto';
import { PieChartConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/pie-chart-configuration.dto';
import { StandaloneRichTextConfigurationDTO } from 'src/engine/core-modules/page-layout/dtos/standalone-rich-text-configuration.dto';
import { type WidgetConfigurationInterface } from 'src/engine/core-modules/page-layout/dtos/widget-configuration.interface';
import { BarChartGroupMode } from 'src/engine/core-modules/page-layout/enums/bar-chart-group-mode.enum';
import { GraphType } from 'src/engine/core-modules/page-layout/enums/graph-type.enum';
@@ -161,7 +163,31 @@ const validateIframeConfiguration = (
return instance;
};
export const validateAndTransformWidgetConfiguration = ({
const validateStandaloneRichTextConfiguration = async (
configuration: unknown,
): Promise<WidgetConfigurationInterface | null> => {
const instance = plainToInstance(
StandaloneRichTextConfigurationDTO,
configuration,
);
const errors = validateSync(instance, {
whitelist: true,
forbidUnknownValues: true,
});
if (errors.length > 0) {
throw errors;
}
if (instance.body) {
instance.body = await transformRichTextV2Value(instance.body);
}
return instance;
};
export const validateAndTransformWidgetConfiguration = async ({
type,
configuration,
isDashboardV2Enabled,
@@ -169,7 +195,7 @@ export const validateAndTransformWidgetConfiguration = ({
type: WidgetType;
configuration: unknown;
isDashboardV2Enabled: boolean;
}): WidgetConfigurationInterface | null => {
}): Promise<WidgetConfigurationInterface | null> => {
if (!configuration || typeof configuration !== 'object') {
throw new Error('Invalid configuration: not an object');
}
@@ -183,6 +209,8 @@ export const validateAndTransformWidgetConfiguration = ({
});
case WidgetType.IFRAME:
return validateIframeConfiguration(configuration);
case WidgetType.STANDALONE_RICH_TEXT:
return await validateStandaloneRichTextConfiguration(configuration);
default:
return null;
}