From 08274d53ff6de8487e62265f888602cd5e850f5c Mon Sep 17 00:00:00 2001 From: arpitnath Date: Sun, 13 Aug 2023 01:42:56 +0530 Subject: [PATCH] new controllers for components and layouts --- frontend/src/Editor/Container.jsx | 3 +- frontend/src/Editor/EditorFunc.jsx | 65 +++++++++++++++---- frontend/src/Editor/EditorKeyHooks.jsx | 2 - frontend/src/_helpers/appUtils.js | 13 ++-- frontend/src/_services/appVersion.service.js | 2 - frontend/src/_stores/appDataStore.js | 1 - frontend/src/_stores/utils.js | 17 ++++- .../1691006952074-CreateComponentTable.ts | 5 ++ server/src/controllers/apps.controller.ts | 36 +++++++--- server/src/entities/component.entity.ts | 3 + server/src/services/apps.service.ts | 4 +- server/src/services/components.service.ts | 20 +++++- 12 files changed, 130 insertions(+), 41 deletions(-) diff --git a/frontend/src/Editor/Container.jsx b/frontend/src/Editor/Container.jsx index 3362897c45..6731c4611a 100644 --- a/frontend/src/Editor/Container.jsx +++ b/frontend/src/Editor/Container.jsx @@ -60,8 +60,6 @@ export const Container = ({ [JSON.stringify(appDefinition), currentPageId] ); - console.log('----mohaaan components', { components }); - const currentState = useCurrentState(); const { appVersionsId, enableReleasedVersionPopupState, isVersionReleased } = useAppVersionStore( (state) => ({ @@ -149,6 +147,7 @@ export const Container = ({ const noOfBoxs = Object.values(boxes || []).length; useEffect(() => { updateCanvasHeight(boxes); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [noOfBoxs]); const moveBox = useCallback( diff --git a/frontend/src/Editor/EditorFunc.jsx b/frontend/src/Editor/EditorFunc.jsx index b2460694e6..a0c856156e 100644 --- a/frontend/src/Editor/EditorFunc.jsx +++ b/frontend/src/Editor/EditorFunc.jsx @@ -64,9 +64,10 @@ import { shallow } from 'zustand/shallow'; import { useEditorActions, useEditorState, useEditorStore } from '@/_stores/editorStore'; import { useAppDataActions, useAppDataStore, useAppInfo } from '@/_stores/appDataStore'; import { useMounted } from '@/_hooks/use-mount'; + // eslint-disable-next-line import/no-unresolved import { diff } from 'deep-object-diff'; -import { camelizeKeys } from 'humps'; +import { camelizeKeys, decamelizeKeys } from 'humps'; setAutoFreeze(false); enablePatches(); @@ -227,7 +228,6 @@ const EditorComponent = (props) => { } if (mounted && didAppDefinitionChanged && currentPageId) { - console.log('----mohaaan: useEffecr', { appDefinition }); const components = appDefinition?.pages[currentPageId]?.components || {}; computeComponentState(components); @@ -236,6 +236,7 @@ const EditorComponent = (props) => { autoSave(); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify({ appDefinition, currentPageId, dataQueries })]); const editorRef = { @@ -630,6 +631,47 @@ const EditorComponent = (props) => { }; //!-------- + + const buildComponentMetaDefinition = (components = {}) => { + for (const componentId in components) { + const currentComponentData = components[componentId]; + const componentMeta = componentTypes.find((comp) => currentComponentData.component.component === comp.component); + + const mergedDefinition = { + ...componentMeta.definition, + properties: { + ...componentMeta.definition.properties, + ...currentComponentData?.component.definition.properties, + }, + + styles: { + ...componentMeta.definition.styles, + ...currentComponentData?.component.definition.styles, + }, + validations: { + ...componentMeta.definition.validations, + ...currentComponentData?.component.definition.validations, + }, + }; + + const mergedComponent = { + component: { + ...componentMeta, + ...currentComponentData.component, + }, + layouts: { + ...currentComponentData.layouts, + }, + withDefaultChildren: componentMeta.withDefaultChildren ?? false, + }; + + mergedComponent.component.definition = mergedDefinition; + + components[componentId] = mergedComponent; + } + return components; + }; + const buildAppDefinition = (data) => { const editingVersion = _.omit(camelizeKeys(data.editing_version), ['definition', 'updatedAt', 'createdAt', 'name']); @@ -637,6 +679,10 @@ const EditorComponent = (props) => { _.unset(editingVersion, 'id'); const pages = data.pages.reduce((acc, page) => { + const currentComponents = buildComponentMetaDefinition(_.cloneDeep(page?.components)); + + page.components = currentComponents; + acc[page.id] = page; return acc; @@ -649,6 +695,8 @@ const EditorComponent = (props) => { pages: pages, }; + // const componentMeta = componentTypes.find((comp) => component.component === comp.component); + return appJSON; }; @@ -677,7 +725,7 @@ const EditorComponent = (props) => { const homePageId = !startingPageId || startingPageId === 'null' ? appJson.homePageId : startingPageId; const currentComponents = appJson.pages[homePageId]?.components ?? {}; - console.log('---piku [fetching app] [pages] ==> ', { currentComponents }); + console.log('---arpit [fetching app] [pages] ==> ', { currentComponents }); const currentpageData = { handle: appJson.pages[homePageId]?.handle, name: appJson.pages[homePageId]?.name, @@ -727,7 +775,6 @@ const EditorComponent = (props) => { // !-------- const setAppDefinitionFromVersion = (version, shouldWeEditVersion = true) => { - console.log('---arpit [setAppFromVersion]--', version); if (version?.id !== props.editingVersion?.id) { appDefinitionChanged(defaults(version.definition, defaultDefinition(props.darkMode)), { skipAutoSave: true, @@ -783,11 +830,6 @@ const EditorComponent = (props) => { let updatedAppDefinition; const copyOfAppDefinition = JSON.parse(JSON.stringify(appDefinition)); - console.log('--arpit | appDefinitionChanged func()', { - opts, - newDefinition, - }); - updatedAppDefinition = produce(copyOfAppDefinition, (draft) => { if (_.isEmpty(draft)) return; @@ -822,6 +864,7 @@ const EditorComponent = (props) => { setUndoStack((prev) => [...prev, undoPatches]); updateAppDefinitionDiff(diffPatches); + updateState({ appDiffOptions: opts, }); @@ -839,7 +882,6 @@ const EditorComponent = (props) => { }; const saveEditingVersion = (isUserSwitchedVersion = false) => { - console.log('---arpit [saving - editionversion]--', { appDefinitionDiff }); if (props.isVersionReleased && !isUserSwitchedVersion) { updateEditorState({ isSaving: false, @@ -952,10 +994,10 @@ const EditorComponent = (props) => { canUndo: undoStack.length > 0, canRedo: redoStack.length > 0, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(undoStack), JSON.stringify(redoStack)]); const componentDefinitionChanged = (componentDefinition, props) => { - console.log('---arpit checking:::: ', { props }); if (props?.isVersionReleased) { useAppVersionStore.getState().actions.enableReleasedVersionPopupState(); return; @@ -1024,6 +1066,7 @@ const EditorComponent = (props) => { } appDefinitionChanged(newDefinition, { componentDefinitionChanged: true, + componentDeleted: true, }); handleInspectorView(); } else { diff --git a/frontend/src/Editor/EditorKeyHooks.jsx b/frontend/src/Editor/EditorKeyHooks.jsx index df79bf779b..1f9b6cb797 100644 --- a/frontend/src/Editor/EditorKeyHooks.jsx +++ b/frontend/src/Editor/EditorKeyHooks.jsx @@ -10,7 +10,6 @@ export const EditorKeyHooks = ({ removeMultipleComponents, }) => { const handleHotKeysCallback = (key) => { - console.log('---arpit-- hotkeys', { key }); switch (key) { case 'Escape': handleEditorEscapeKeyPress(); @@ -19,7 +18,6 @@ export const EditorKeyHooks = ({ removeMultipleComponents(); break; case 'KeyD': - console.log('---arpit-- paste component'); cloneComponents(); break; case 'KeyC': diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js index 373a6bd57a..6ff61378c7 100644 --- a/frontend/src/_helpers/appUtils.js +++ b/frontend/src/_helpers/appUtils.js @@ -1130,10 +1130,11 @@ export function renderTooltip({ props, text }) { export function computeComponentState(components = {}) { let componentState = {}; const currentComponents = getCurrentState().components; + Object.keys(components).forEach((key) => { - const component = components[key]; - const componentMeta = componentTypes.find((comp) => component.component.component === comp.component); - console.log('------tj: computeComponentState', { component, currentComponents }); + const { component } = components[key]; + const componentMeta = componentTypes.find((comp) => component.component === comp.component); + const existingComponentName = Object.keys(currentComponents).find((comp) => currentComponents[comp].id === key); const existingValues = currentComponents[existingComponentName]; @@ -1149,14 +1150,14 @@ export function computeComponentState(components = {}) { } if (!isListView && !isForm) { - componentState[component.component.name] = { + componentState[component.name] = { ...componentMeta.exposedVariables, id: key, ...existingValues, }; } } else { - componentState[component.component.name] = { + componentState[component.name] = { ...componentMeta.exposedVariables, id: key, ...existingValues, @@ -1586,7 +1587,7 @@ export const removeSelectedComponent = (pageId, newDefinition, selectedComponent delete newDefinition.pages[pageId].components[component.id]; }); - updateAppDefinition(newDefinition, { componentDefinitionChanged: true }); + updateAppDefinition(newDefinition, { componentDefinitionChanged: true, componentDeleted: true }); }; const getSelectedText = () => { diff --git a/frontend/src/_services/appVersion.service.js b/frontend/src/_services/appVersion.service.js index 07b9036bd6..9af120a865 100644 --- a/frontend/src/_services/appVersion.service.js +++ b/frontend/src/_services/appVersion.service.js @@ -60,8 +60,6 @@ function save(appId, versionId, values, isUserSwitchedVersion = false) { return fetch(`${config.apiUrl}/apps/${appId}/versions/${versionId}`, requestOptions).then(handleResponse); } function autoSaveApp(appId, versionId, diff, type, pageId, operation, isUserSwitchedVersion = false) { - console.log('---piku [version saved] [v2]', { operation, type, diff }); - const OPERATION = Object.freeze({ create: 'POST', update: 'PUT', diff --git a/frontend/src/_stores/appDataStore.js b/frontend/src/_stores/appDataStore.js index 778142f29b..a7f06dbe91 100644 --- a/frontend/src/_stores/appDataStore.js +++ b/frontend/src/_stores/appDataStore.js @@ -32,7 +32,6 @@ export const useAppDataStore = create( updateState: (state) => set((prev) => ({ ...prev, ...state })), updateAppDefinitionDiff: (appDefinitionDiff) => set(() => ({ appDefinitionDiff: appDefinitionDiff })), updateAppVersion: async (appId, versionId, pageId, appDefinitionDiff, isUserSwitchedVersion = false) => { - // console.log('-piku :: from store', { appDefinitionDiff }); return await appVersionService.autoSaveApp( appId, versionId, diff --git a/frontend/src/_stores/utils.js b/frontend/src/_stores/utils.js index 78ee079f4e..428fda719d 100644 --- a/frontend/src/_stores/utils.js +++ b/frontend/src/_stores/utils.js @@ -32,6 +32,7 @@ const defaultComponent = { styles: {}, validation: {}, events: [], + type: '', }; const updateType = Object.freeze({ @@ -39,6 +40,7 @@ const updateType = Object.freeze({ containerChanges: 'components/layout', componentAdded: 'components', componentDefinitionChanged: 'components', + componentDeleted: 'components', }); export const computeAppDiff = (appDiff, currentPageId, opts) => { @@ -46,10 +48,20 @@ export const computeAppDiff = (appDiff, currentPageId, opts) => { let updateDiff; let operation = 'update'; + console.log('---piku [computeAppDiff]', { appDiff, currentPageId, opts }); + if (opts?.pageDefinitionChanged) { updateDiff = appDiff?.pages[currentPageId]; type = updateType.pageDefinitionChanged; + } else if (opts?.componentDeleted) { + const currentPageComponents = appDiff?.pages[currentPageId]?.components; + + updateDiff = _.keys(currentPageComponents); + + type = updateType.componentDeleted; + + operation = 'delete'; } else if ((opts?.containerChanges || opts?.componentDefinitionChanged) && !opts?.componentAdded) { const currentPageComponents = appDiff?.pages[currentPageId]?.components; @@ -65,16 +77,15 @@ export const computeAppDiff = (appDiff, currentPageId, opts) => { result[id] = _.defaultsDeep(metaDiff, defaultComponent); + result[id].type = componentMeta.component; result[id].layouts = appDiff.pages[currentPageId].components[id].layouts; - operation = 'create'; + return result; }, {}); type = updateType.componentDefinitionChanged; } - console.log('---piku [currentPageComponents]', { updateDiff, opts, type }); - return { updateDiff, type, operation }; }; diff --git a/server/migrations/1691006952074-CreateComponentTable.ts b/server/migrations/1691006952074-CreateComponentTable.ts index 3aab390d2a..2016b55eb2 100644 --- a/server/migrations/1691006952074-CreateComponentTable.ts +++ b/server/migrations/1691006952074-CreateComponentTable.ts @@ -17,6 +17,11 @@ export class CreateComponentTable1691006952074 implements MigrationInterface { type: 'varchar', isNullable: false, }, + { + name: 'type', + type: 'varchar', + isNullable: false, + }, { name: 'page_id', type: 'uuid', diff --git a/server/src/controllers/apps.controller.ts b/server/src/controllers/apps.controller.ts index e06d0e834f..f449fb572e 100644 --- a/server/src/controllers/apps.controller.ts +++ b/server/src/controllers/apps.controller.ts @@ -101,7 +101,7 @@ export class AppsController { response['data_queries'] = seralizedQueries; response['definition'] = app.editingVersion?.definition; - response['pages'] = decamelizeKeys(pagesForVersion); + response['pages'] = pagesForVersion; //! if editing version exists, camelize the definition if (app.editingVersion && app.editingVersion.definition) { @@ -320,9 +320,6 @@ export class AppsController { throw new ForbiddenException('You do not have permissions to perform this action'); } - // const updateType = versionEditDto.app_diff?.type; - // console.log('----arpit apps controller => ', { updateType }); - await this.appsService.updateVersion(version, versionEditDto, app.organizationId); return; } @@ -347,8 +344,6 @@ export class AppsController { throw new ForbiddenException('You do not have permissions to perform this action'); } - // const updateType = versionEditDto.app_diff?.type; - console.log('----arpit apps controller v2 => ', { versionEditDto }); await this.componentsService.create(versionEditDto.diff, versionEditDto.pageId); } @UseGuards(JwtAuthGuard) @@ -372,10 +367,33 @@ export class AppsController { throw new ForbiddenException('You do not have permissions to perform this action'); } - // const updateType = versionEditDto.app_diff?.type; - console.log('----arpit apps controller v2 [update] => ', { versionEditDto }); await this.componentsService.update(versionEditDto.diff); } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Delete(':id/versions/:versionId/components') + async deleteComponents( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() versionEditDto: VersionEditDto + ) { + 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'); + } + + await this.componentsService.delete(versionEditDto.diff); + } + @UseGuards(JwtAuthGuard) @UseInterceptors(ValidAppInterceptor) @Put(':id/versions/:versionId/components/layout') @@ -397,8 +415,6 @@ export class AppsController { throw new ForbiddenException('You do not have permissions to perform this action'); } - // const updateType = versionEditDto.app_diff?.type; - console.log('----arpit apps controller v2 |layput | [update] => ', { versionEditDto }); await this.componentsService.componentLayoutChange(versionEditDto.diff); } diff --git a/server/src/entities/component.entity.ts b/server/src/entities/component.entity.ts index ec01f4baa5..b32d4fae80 100644 --- a/server/src/entities/component.entity.ts +++ b/server/src/entities/component.entity.ts @@ -10,6 +10,9 @@ export class Component { @Column({ name: 'name' }) name: string; + @Column({ name: 'type' }) + type: string; + @Column({ name: 'page_id' }) pageId: string; diff --git a/server/src/services/apps.service.ts b/server/src/services/apps.service.ts index d1bb036c18..b4a53a5cde 100644 --- a/server/src/services/apps.service.ts +++ b/server/src/services/apps.service.ts @@ -122,8 +122,8 @@ export class AppsService { const defaultHomePage = await manager.save( manager.create(Page, { - name: 'Home', - pageHandle: 'home', + name: 'Defualt Page', + pageHandle: 'defaultpage', appVersionId: appVersion.id, }) ); diff --git a/server/src/services/components.service.ts b/server/src/services/components.service.ts index 07fca7f185..03019ce66b 100644 --- a/server/src/services/components.service.ts +++ b/server/src/services/components.service.ts @@ -97,6 +97,22 @@ export class ComponentsService { }); } + async delete(componentIds: string[]) { + return dbTransactionWrap(async (manager: EntityManager) => { + const components = await manager.findByIds(Component, componentIds); + + if (!components.length) { + return { + error: { + message: `Components with ids ${componentIds} do not exist`, + }, + }; + } + + await manager.delete(Component, componentIds); + }); + } + async componentLayoutChange(componenstLayoutDiff: object) { return dbTransactionWrap(async (manager: EntityManager) => { for (const componentId in componenstLayoutDiff) { @@ -124,8 +140,6 @@ export class ComponentsService { ...layout, }; - console.log('--arpit [layput changed]', { type, layout, componentLayout, newLayout }); - await manager.update(Layout, { id: componentLayout.id }, newLayout); } } @@ -166,6 +180,7 @@ export class ComponentsService { const transformedComponent: Component = new Component(); transformedComponent.id = componentId; transformedComponent.name = componentData.name; + transformedComponent.type = componentData.type; transformedComponent.properties = componentData.properties || {}; transformedComponent.styles = componentData.styles || {}; transformedComponent.validations = componentData.validation || {}; @@ -184,6 +199,7 @@ export class ComponentsService { [id]: { component: { name, + component: componentData.type, definition: { properties, styles,