diff --git a/frontend/src/Editor/EditorFunc.jsx b/frontend/src/Editor/EditorFunc.jsx index 255e3bb690..b6cd82805a 100644 --- a/frontend/src/Editor/EditorFunc.jsx +++ b/frontend/src/Editor/EditorFunc.jsx @@ -858,7 +858,6 @@ const EditorComponent = (props) => { const shouldUpdate = !_.isEmpty(diffPatches) && !isEqual(appDefinitionDiff, diffPatches); if (shouldUpdate) { - // const redoPatch = diffToPatches(diffPatches); const undoPatches = diffToPatches(inversePatches); setUndoStack((prev) => [...prev, undoPatches]); diff --git a/frontend/src/_services/appVersion.service.js b/frontend/src/_services/appVersion.service.js index 2fca17429a..131702c17c 100644 --- a/frontend/src/_services/appVersion.service.js +++ b/frontend/src/_services/appVersion.service.js @@ -68,7 +68,7 @@ function autoSaveApp(appId, versionId, diff, type, pageId, operation, isUserSwit let body = {}; - if ((type === 'pages' && operation === 'create') || operation === 'delete') { + if (!type || (type === 'pages' && operation === 'create') || operation === 'delete') { body = { ...diff, }; @@ -82,5 +82,7 @@ function autoSaveApp(appId, versionId, diff, type, pageId, operation, isUserSwit credentials: 'include', body: JSON.stringify(body), }; - return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/${type}`, requestOptions).then(handleResponse); + return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/${type ?? ''}`, requestOptions).then( + handleResponse + ); } diff --git a/frontend/src/_stores/utils.js b/frontend/src/_stores/utils.js index a391055ce4..7ab32bf566 100644 --- a/frontend/src/_stores/utils.js +++ b/frontend/src/_stores/utils.js @@ -52,6 +52,7 @@ export const computeAppDiff = (appDiff, currentPageId, opts) => { const updateFor = (appDiff, currentPageId, opts) => { const componentUpdates = ['componentAdded', 'componentDefinitionChanged', 'componentDeleted', 'containerChanges']; const pageUpdates = ['pageDefinitionChanged', 'pageSortingChanged', 'deletePageRequest', 'addNewPage']; + const appUpdates = ['homePageChanged']; const options = _.keys(opts); @@ -59,6 +60,12 @@ const updateFor = (appDiff, currentPageId, opts) => { return computeComponentDiff(appDiff, currentPageId, opts); } else if (_.intersection(options, pageUpdates).length > 0) { return computePageUpdate(appDiff, currentPageId, opts); + } else if (_.intersection(options, appUpdates).length > 0) { + return { + updateDiff: appDiff, + type: null, + operation: 'update', + }; } }; diff --git a/server/src/controllers/apps.controller.v2.ts b/server/src/controllers/apps.controller.v2.ts index f3247c0bd5..6b3660812c 100644 --- a/server/src/controllers/apps.controller.v2.ts +++ b/server/src/controllers/apps.controller.v2.ts @@ -28,6 +28,7 @@ import { AppDecorator } from 'src/decorators/app.decorator'; import { ComponentsService } from '@services/components.service'; import { PageService } from '@services/page.service'; +import { AppVersionUpdateDto } from '@dto/app-version-update.dto'; @Controller({ path: 'apps', @@ -92,6 +93,30 @@ export class AppsControllerV2 { return response; } + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Put(':id/versions/:versionId') + async updateVersion( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() appVersionUpdateDto: AppVersionUpdateDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + return await this.appsService.updateAppVersion(version, appVersionUpdateDto); + } + //components api @UseGuards(JwtAuthGuard) @UseInterceptors(ValidAppInterceptor) diff --git a/server/src/dto/app-version-update.dto.ts b/server/src/dto/app-version-update.dto.ts new file mode 100644 index 0000000000..00e52d4f6c --- /dev/null +++ b/server/src/dto/app-version-update.dto.ts @@ -0,0 +1,26 @@ +import { IsBoolean, IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator'; +import { Transform } from 'class-transformer'; +import { sanitizeInput } from '../helpers/utils.helper'; + +export class AppVersionUpdateDto { + @IsString() + @IsOptional() + @Transform(({ value }) => { + const newValue = sanitizeInput(value); + return newValue.trim(); + }) + @IsNotEmpty() + @MaxLength(50, { message: 'Maximum length has been reached.' }) + name: string; + + @IsBoolean() + @IsOptional() + showViewerNavigation: boolean; + + @IsString() + @IsOptional() + homePageId: string; + + @IsOptional() + globalSettings: any; +} diff --git a/server/src/services/apps.service.ts b/server/src/services/apps.service.ts index 0dceab44f2..535c8891b0 100644 --- a/server/src/services/apps.service.ts +++ b/server/src/services/apps.service.ts @@ -29,6 +29,7 @@ import { decode } from 'js-base64'; import { DataSourceScopes } from 'src/helpers/data_source.constants'; import { DataBaseConstraints } from 'src/helpers/db_constraints.constants'; import { Page } from 'src/entities/page.entity'; +import { AppVersionUpdateDto } from '@dto/app-version-update.dto'; @Injectable() export class AppsService { @@ -667,6 +668,16 @@ export class AppsService { return await this.appVersionsRepository.update(version.id, editableParams); } + async updateAppVersion(version: AppVersion, body: AppVersionUpdateDto) { + const editableParams = {}; + + if (body?.homePageId) { + editableParams['homePageId'] = body.homePageId; + } + + return await this.appVersionsRepository.update(version.id, editableParams); + } + convertToArrayOfKeyValuePairs(options): Array { if (!options) return; return Object.keys(options).map((key) => {