diff --git a/.version b/.version index db65e2167e..3fe7dd19b6 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.21.0 +2.21.1 diff --git a/frontend/.version b/frontend/.version index db65e2167e..3fe7dd19b6 100644 --- a/frontend/.version +++ b/frontend/.version @@ -1 +1 @@ -2.21.0 +2.21.1 diff --git a/frontend/src/Editor/Header/EditAppName.jsx b/frontend/src/Editor/Header/EditAppName.jsx index 8ac727e5be..304e964eb2 100644 --- a/frontend/src/Editor/Header/EditAppName.jsx +++ b/frontend/src/Editor/Header/EditAppName.jsx @@ -1,7 +1,7 @@ import React, { useRef, useEffect, useState } from 'react'; import { ToolTip } from '@/_components'; import { appService } from '@/_services'; -import { handleHttpErrorMessages, validateAppName, validateName } from '@/_helpers/utils'; +import { handleHttpErrorMessages, validateName } from '@/_helpers/utils'; import InfoOrErrorBox from './InfoOrErrorBox'; import { toast } from 'react-hot-toast'; @@ -32,7 +32,7 @@ function EditAppName({ appId, appName = '', onNameChanged }) { const saveAppName = async (newName) => { const trimmedName = newName.trim(); - if (validateName(trimmedName, 'App name', true)?.errorMsg) { + if (validateName(trimmedName, 'App', false, true)?.errorMsg) { setName(appName); clearError(); setIsEditing(false); diff --git a/frontend/src/HomePage/AppCard.jsx b/frontend/src/HomePage/AppCard.jsx index 79cd2b88e6..380f1281e9 100644 --- a/frontend/src/HomePage/AppCard.jsx +++ b/frontend/src/HomePage/AppCard.jsx @@ -20,7 +20,6 @@ export default function AppCard({ canCreateApp, canDeleteApp, deleteApp, - cloneApp, exportApp, appActionModal, canUpdateApp, @@ -85,7 +84,6 @@ export default function AppCard({ canDeleteApp={canDeleteApp(app)} canUpdateApp={canUpdateApp(app)} deleteApp={() => deleteApp(app)} - cloneApp={() => cloneApp(app)} exportApp={() => exportApp(app)} isMenuOpen={isMenuOpen} darkMode={darkMode} diff --git a/frontend/src/HomePage/AppList.jsx b/frontend/src/HomePage/AppList.jsx index 0ff33cc6b0..80efa14b0f 100644 --- a/frontend/src/HomePage/AppList.jsx +++ b/frontend/src/HomePage/AppList.jsx @@ -39,7 +39,6 @@ const AppList = (props) => { canDeleteApp={props.canDeleteApp} canUpdateApp={props.canUpdateApp} deleteApp={props.deleteApp} - cloneApp={props.cloneApp} exportApp={props.exportApp} appActionModal={props.appActionModal} /> diff --git a/frontend/src/HomePage/HomePage.jsx b/frontend/src/HomePage/HomePage.jsx index 84b8130b8a..0c16707f54 100644 --- a/frontend/src/HomePage/HomePage.jsx +++ b/frontend/src/HomePage/HomePage.jsx @@ -133,14 +133,14 @@ class HomePageComponent extends React.Component { _self.setState({ creatingApp: true }); try { const data = await appService.createApp({ icon: sample(iconList), name: appName }); - const workspaceId = getWorkspaceId(); _self.props.navigate(`/${workspaceId}/apps/${data.id}`); toast.success('App created successfully!'); + _self.setState({ creatingApp: false }); return true; } catch (errorResponse) { + _self.setState({ creatingApp: false }); if (errorResponse.statusCode === 409) { - _self.setState({ creatingApp: false }); return false; } else { throw errorResponse; @@ -155,11 +155,11 @@ class HomePageComponent extends React.Component { await appService.saveApp(appId, { name: newAppName }); await this.fetchApps(); toast.success('App name has been updated!'); + _self.setState({ renamingApp: false }); return true; } catch (errorResponse) { + _self.setState({ renamingApp: false }); if (errorResponse.statusCode === 409) { - console.log(errorResponse); - _self.setState({ renamingApp: false }); return false; } else { throw errorResponse; @@ -171,13 +171,16 @@ class HomePageComponent extends React.Component { this.setState({ showAppDeletionConfirmation: true, appToBeDeleted: app }); }; - cloneApp = async (appId, appName) => { + cloneApp = async (appName, appId) => { this.setState({ isCloningApp: true }); try { - const data = await appService.cloneApp(appName, appId); + const data = await appService.cloneResource({ + app: [{ id: appId, name: appName }], + organization_id: getWorkspaceId(), + }); toast.success('App cloned successfully!'); + this.props.navigate(`/${getWorkspaceId()}/apps/${data?.imports?.app[0]?.id}`); this.setState({ isCloningApp: false }); - this.props.navigate(`/${getWorkspaceId()}/apps/${data.id}`); return true; } catch (_error) { this.setState({ isCloningApp: false }); @@ -221,9 +224,9 @@ class HomePageComponent extends React.Component { const organization_id = getWorkspaceId(); const isLegacyImport = isEmpty(importJSON.tooljet_version); if (isLegacyImport) { - importJSON = { app: [{ definition: importJSON }], tooljet_version: importJSON.tooljetVersion }; + importJSON = { app: [{ definition: importJSON, appName: appName }], tooljet_version: importJSON.tooljetVersion }; } - const requestBody = { organization_id, appName, ...importJSON }; + const requestBody = { organization_id, ...importJSON }; try { const data = await appService.importResource(requestBody); toast.success('App imported successfully.'); @@ -841,7 +844,6 @@ class HomePageComponent extends React.Component { canDeleteApp={this.canDeleteApp} canUpdateApp={this.canUpdateApp} deleteApp={this.deleteApp} - cloneApp={this.cloneApp} exportApp={this.exportApp} meta={meta} currentFolder={currentFolder} diff --git a/frontend/src/_components/AppModal.jsx b/frontend/src/_components/AppModal.jsx index 6ff81c6b79..ab9773a5d8 100644 --- a/frontend/src/_components/AppModal.jsx +++ b/frontend/src/_components/AppModal.jsx @@ -3,7 +3,7 @@ import { toast } from 'react-hot-toast'; import Modal from '../HomePage/Modal'; import { ButtonSolid } from '@/_ui/AppButton/AppButton'; import _ from 'lodash'; -import { validateAppName } from '@/_helpers/utils'; +import { validateName } from '@/_helpers/utils'; export function AppModal({ closeModal, @@ -105,7 +105,7 @@ export function AppModal({ setInfoText('Maximum length has been reached'); } else { setInfoText(''); - const error = validateAppName(trimmedName); + const error = validateName(trimmedName, 'App', false); setErrorText(error?.errorMsg || ''); } }; diff --git a/frontend/src/_helpers/utils.js b/frontend/src/_helpers/utils.js index f3fb99001d..fd562ce24f 100644 --- a/frontend/src/_helpers/utils.js +++ b/frontend/src/_helpers/utils.js @@ -920,26 +920,10 @@ export function isExpectedDataType(data, expectedDataType) { return data; } -export const validateAppName = (name, showError = false) => { +export const validateName = (name, nameType, emptyCheck = true, showError = false, allowSpecialChars = true) => { const newName = name.trim(); let errorMsg = ''; - if (newName.length > 50) { - errorMsg = `Maximum length has been reached`; - showError && - toast.error(errorMsg, { - id: '1', - }); - } - return { - status: !(errorMsg.length > 0), - errorMsg, - }; -}; - -export const validateName = (name, nameType, showError = false, allowSpecialChars = true) => { - const newName = name.trim(); - let errorMsg = ''; - if (!newName) { + if (emptyCheck && !newName) { errorMsg = `${nameType} can't be empty`; showError && toast.error(errorMsg, { diff --git a/server/.version b/server/.version index db65e2167e..3fe7dd19b6 100644 --- a/server/.version +++ b/server/.version @@ -1 +1 @@ -2.21.0 +2.21.1 diff --git a/server/src/dto/clone-resources.dto.ts b/server/src/dto/clone-resources.dto.ts index 5dc6c3ab7a..4641410b87 100644 --- a/server/src/dto/clone-resources.dto.ts +++ b/server/src/dto/clone-resources.dto.ts @@ -1,4 +1,4 @@ -import { IsUUID, IsOptional } from 'class-validator'; +import { IsUUID, IsOptional, IsString } from 'class-validator'; export class CloneResourcesDto { @IsOptional() @@ -14,6 +14,9 @@ export class CloneResourcesDto { export class CloneAppDto { @IsUUID() id: string; + + @IsString() + name: string; } export class CloneTooljetDatabaseDto { diff --git a/server/src/dto/import-resources.dto.ts b/server/src/dto/import-resources.dto.ts index 9d6257d4af..723da20405 100644 --- a/server/src/dto/import-resources.dto.ts +++ b/server/src/dto/import-resources.dto.ts @@ -10,9 +10,6 @@ export class ImportResourcesDto { @IsOptional() app: ImportAppDto[]; - @IsOptional() - appName: string; - @IsOptional() tooljet_database: ImportTooljetDatabaseDto[]; } @@ -20,6 +17,9 @@ export class ImportResourcesDto { export class ImportAppDto { @IsDefined() definition: any; + + @IsString() + appName: string; } export class ImportTooljetDatabaseDto { diff --git a/server/src/services/import_export_resources.service.ts b/server/src/services/import_export_resources.service.ts index fce758c0cf..30e6740064 100644 --- a/server/src/services/import_export_resources.service.ts +++ b/server/src/services/import_export_resources.service.ts @@ -59,12 +59,16 @@ export class ImportExportResourcesService { } if (importResourcesDto.app) { - const appName = importResourcesDto.appName; for (const appImportDto of importResourcesDto.app) { user.organizationId = importResourcesDto.organization_id; - const createdApp = await this.appImportExportService.import(user, appImportDto.definition, appName, { - tooljet_database: tableNameMapping, - }); + const createdApp = await this.appImportExportService.import( + user, + appImportDto.definition, + appImportDto.appName, + { + tooljet_database: tableNameMapping, + } + ); imports.app.push({ id: createdApp.id, name: createdApp.name }); } } @@ -82,6 +86,7 @@ export class ImportExportResourcesService { const resourceExport = await this.export(user, exportResourcesDto); resourceExport['organization_id'] = cloneResourcesDto.organization_id; + resourceExport['app'][0]['appName'] = cloneResourcesDto.app[0].name; const clonedResource = await this.import(user, resourceExport as ImportResourcesDto, true); return clonedResource;