diff --git a/frontend/src/_stores/appDataStore.js b/frontend/src/_stores/appDataStore.js index 4456bd0b64..7af5264dc3 100644 --- a/frontend/src/_stores/appDataStore.js +++ b/frontend/src/_stores/appDataStore.js @@ -36,7 +36,7 @@ export const useAppDataStore = create( updateState: (state) => set((prev) => ({ ...prev, ...state })), updateAppDefinitionDiff: (appDefinitionDiff) => set(() => ({ appDefinitionDiff: appDefinitionDiff })), updateAppVersion: (appId, versionId, pageId, appDefinitionDiff, isUserSwitchedVersion = false) => { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { useAppDataStore.getState().actions.setIsSaving(true); const isComponentCutProcess = get().appDiffOptions?.componentCut === true; @@ -54,6 +54,10 @@ export const useAppDataStore = create( .then(() => { useAppDataStore.getState().actions.setIsSaving(false); }) + .catch((error) => { + useAppDataStore.getState().actions.setIsSaving(false); + reject(error); + }) .finally(() => resolve()); }); }, diff --git a/server/src/dto/component.dto.ts b/server/src/dto/component.dto.ts index 593e9db928..92ff0a0040 100644 --- a/server/src/dto/component.dto.ts +++ b/server/src/dto/component.dto.ts @@ -1,5 +1,18 @@ import { Type } from 'class-transformer'; -import { IsArray, IsBoolean, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString, IsUUID } from 'class-validator'; +import { + IsArray, + IsBoolean, + IsNotEmpty, + IsNumber, + IsObject, + IsOptional, + IsString, + IsUUID, + ValidationArguments, + ValidatorConstraint, + ValidatorConstraintInterface, + Validate, +} from 'class-validator'; export class ComponentLayoutDto { @IsNumber() @@ -29,6 +42,37 @@ export class LayoutData { mobile?: ComponentLayoutDto; } +@ValidatorConstraint({ name: 'LayoutDataValidator', async: false }) +class LayoutDataValidator implements ValidatorConstraintInterface { + validate(value: any) { + if (value) { + for (const key in value) { + if (!value[key] || typeof value[key] !== 'object' || !value[key].layouts) { + return false; + } + } + } + return true; + } + + defaultMessage(args: ValidationArguments) { + return `Each key in "diff" must have the structure { layouts: LayoutData }`; + } +} + +export class LayoutUpdateDto { + @IsBoolean() + is_user_switched_version: boolean; + + @IsUUID() + pageId: string; + + @IsObject() + @IsNotEmpty() + @Validate(LayoutDataValidator, { each: true }) + diff: Record; +} + class ComponentDto { @IsString() name: string; @@ -92,15 +136,3 @@ export class DeleteComponentDto { @IsOptional() is_component_cut: boolean; } - -export class LayoutUpdateDto { - @IsBoolean() - is_user_switched_version: boolean; - - @IsUUID() - pageId: string; - - @IsObject() - @IsNotEmpty() - diff: Record; -} diff --git a/server/src/services/components.service.ts b/server/src/services/components.service.ts index b87bfcc936..713a885f81 100644 --- a/server/src/services/components.service.ts +++ b/server/src/services/components.service.ts @@ -7,6 +7,7 @@ import { Page } from 'src/entities/page.entity'; import { dbTransactionForAppVersionAssociationsUpdate, dbTransactionWrap } from 'src/helpers/utils.helper'; import { EventsService } from './events_handler.service'; +import { LayoutData } from '@dto/component.dto'; @Injectable() export class ComponentsService { @@ -131,7 +132,7 @@ export class ComponentsService { }, appVersionId); } - async componentLayoutChange(componenstLayoutDiff: object, appVersionId: string) { + async componentLayoutChange(componenstLayoutDiff: Record, appVersionId: string) { return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { for (const componentId in componenstLayoutDiff) { const doesComponentExist = await manager.findAndCount(Component, { id: componentId });