queryParamChangeHandler(index, 1, value)}
mode="javascript"
className="form-control codehinter-query-editor-input"
diff --git a/frontend/src/Editor/Inspector/Components/Chart.jsx b/frontend/src/Editor/Inspector/Components/Chart.jsx
index 4c2c20c890..88e55d1e01 100644
--- a/frontend/src/Editor/Inspector/Components/Chart.jsx
+++ b/frontend/src/Editor/Inspector/Components/Chart.jsx
@@ -56,17 +56,16 @@ class Chart extends React.Component {
}
render() {
- const { dataQueries, component, paramUpdated, componentMeta, components, currentState } = this.state;
- const data = this.state.component.component.definition.properties.data;
+ const { dataQueries, component, paramUpdated, componentMeta, components, currentState } = this.props;
+ const data = this.props.component.component.definition.properties.data; // since component is not unmounting on every render in current scenario
- const jsonDescription = this.state.component.component.definition.properties.jsonDescription;
+ const jsonDescription = this.props.component.component.definition.properties.jsonDescription;
const plotFromJson = resolveReferences(
- this.state.component.component.definition.properties.plotFromJson?.value,
+ this.props.component.component.definition.properties.plotFromJson?.value,
currentState
);
-
- const chartType = this.state.component.component.definition.properties.type.value;
+ const chartType = this.props.component.component.definition.properties.type.value;
let items = [];
diff --git a/frontend/src/Editor/Inspector/EventManager.jsx b/frontend/src/Editor/Inspector/EventManager.jsx
index 55559b6898..8a4ba36e59 100644
--- a/frontend/src/Editor/Inspector/EventManager.jsx
+++ b/frontend/src/Editor/Inspector/EventManager.jsx
@@ -909,7 +909,6 @@ export const EventManager = ({
{events.map((event, index) => {
const actionMeta = ActionTypes.find((action) => action.id === event.event.actionId);
-
// const rowClassName = `card-body p-0 ${focusedEventIndex === index ? ' bg-azure-lt' : ''}`;
return (
diff --git a/frontend/src/Editor/Viewer.jsx b/frontend/src/Editor/Viewer.jsx
index e87724e976..4583505b90 100644
--- a/frontend/src/Editor/Viewer.jsx
+++ b/frontend/src/Editor/Viewer.jsx
@@ -226,7 +226,7 @@ class ViewerComponent extends React.Component {
runQueries = (data_queries) => {
data_queries.forEach((query) => {
if (query.options.runOnPageLoad && isQueryRunnable(query)) {
- runQuery(this, query.id, query.name, undefined, 'view');
+ runQuery(this.getViewerRef(), query.id, query.name, undefined, 'view');
}
});
};
@@ -553,17 +553,19 @@ class ViewerComponent extends React.Component {
);
};
- handleEvent = (eventName, events, options) => {
- const { appDefinition, currentPageId } = this.state;
- const viewerRef = {
- appDefinition: appDefinition,
+ getViewerRef() {
+ return {
+ appDefinition: this.state.appDefinition,
queryConfirmationList: this.props.queryConfirmationList,
updateQueryConfirmationList: this.updateQueryConfirmationList,
navigate: this.props.navigate,
switchPage: this.switchPage,
- currentPageId: currentPageId,
+ currentPageId: this.state.currentPageId,
};
- onEvent(viewerRef, eventName, events, options, 'view');
+ }
+
+ handleEvent = (eventName, events, options) => {
+ onEvent(this.getViewerRef(), eventName, events, options, 'view');
};
computeCanvasMaxWidth = () => {
diff --git a/frontend/src/HomePage/ExportAppModal.jsx b/frontend/src/HomePage/ExportAppModal.jsx
index fa2b74e0fc..a52fdd5eae 100644
--- a/frontend/src/HomePage/ExportAppModal.jsx
+++ b/frontend/src/HomePage/ExportAppModal.jsx
@@ -54,7 +54,7 @@ export default function ExportAppModal({ title, show, closeModal, customClassNam
const requestBody = {
...appOpts,
...(exportTjDb && { tooljet_database: tables }),
- organization_id: app.organization_id,
+ organization_id: app.organization_id ?? app.organizationId,
};
appService
diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js
index 7744e1380a..a0b852ca72 100644
--- a/frontend/src/_helpers/appUtils.js
+++ b/frontend/src/_helpers/appUtils.js
@@ -1703,24 +1703,34 @@ export function snapToGrid(canvasWidth, x, y) {
return [snappedX, snappedY];
}
export const removeSelectedComponent = (pageId, newDefinition, selectedComponents, updateAppDefinition) => {
- selectedComponents.forEach((component) => {
- let childComponents = [];
+ const toDeleteComponents = [];
- if (newDefinition.pages[pageId].components[component.id]?.component?.component === 'Tabs') {
- childComponents = Object.keys(newDefinition.pages[pageId].components).filter((key) =>
- newDefinition.pages[pageId].components[key].parent?.startsWith(component.id)
- );
- } else {
- childComponents = Object.keys(newDefinition.pages[pageId].components).filter(
- (key) => newDefinition.pages[pageId].components[key].parent === component.id
- );
+ if (selectedComponents.length < 1) return getSelectedText();
+
+ const { components: allComponents } = newDefinition.pages[pageId];
+
+ const findAllChildComponents = (componentId) => {
+ if (!toDeleteComponents.includes(componentId)) {
+ toDeleteComponents.push(componentId);
+
+ // Find the children of this component
+ const children = getAllChildComponents(allComponents, componentId).map((child) => child.componentId);
+
+ if (children.length > 0) {
+ // Recursively find children of children
+ children.forEach((child) => {
+ findAllChildComponents(child);
+ });
+ }
}
+ };
- childComponents.forEach((componentId) => {
- delete newDefinition.pages[pageId].components[componentId];
- });
+ selectedComponents.forEach((component) => {
+ findAllChildComponents(component.id);
+ });
- delete newDefinition.pages[pageId].components[component.id];
+ toDeleteComponents.forEach((componentId) => {
+ delete newDefinition.pages[pageId].components[componentId];
});
updateAppDefinition(newDefinition, { componentDefinitionChanged: true, componentDeleted: true });
diff --git a/frontend/src/_stores/dataQueriesStore.js b/frontend/src/_stores/dataQueriesStore.js
index 557e690435..72f4e22c99 100644
--- a/frontend/src/_stores/dataQueriesStore.js
+++ b/frontend/src/_stores/dataQueriesStore.js
@@ -31,7 +31,7 @@ export const useDataQueriesStore = create(
...initialState,
actions: {
// TODO: Remove editor state while changing currentState
- fetchDataQueries: async (appVersionId, selectFirstQuery = false, runQueriesOnAppLoad = false) => {
+ fetchDataQueries: async (appVersionId, selectFirstQuery = false, runQueriesOnAppLoad = false, ref) => {
set({ loadingDataQueries: true });
const data = await dataqueryService.getAll(appVersionId);
set((state) => ({
@@ -62,7 +62,7 @@ export const useDataQueriesStore = create(
}
// Runs query on loading application
- if (runQueriesOnAppLoad) runQueries(data.data_queries, {});
+ if (runQueriesOnAppLoad) runQueries(data.data_queries, ref);
},
setDataQueries: (dataQueries) => set({ dataQueries }),
deleteDataQueries: (queryId) => {
diff --git a/frontend/src/_stores/editorStore.js b/frontend/src/_stores/editorStore.js
index 27c2c898d1..a42dc2ae88 100644
--- a/frontend/src/_stores/editorStore.js
+++ b/frontend/src/_stores/editorStore.js
@@ -38,57 +38,58 @@ const initialState = {
defaultComponentStateComputed: false,
showLeftSidebar: true,
queryConfirmationList: [],
+ currentPageId: null,
};
export const useEditorStore = create(
- zustandDevTools(
- (set, get) => ({
- ...initialState,
- actions: {
- setShowComments: (showComments) =>
- set({ showComments }, false, {
- type: ACTIONS.SET_HOVERED_COMPONENT,
- showComments,
- }),
- toggleComments: () =>
- set({ showComments: !get().showComments }, false, {
- type: ACTIONS.TOGGLE_COMMENTS,
- }),
- toggleCurrentLayout: (currentLayout) =>
- set({ currentLayout }, false, {
- type: ACTIONS.TOGGLE_CURRENT_LAYOUT,
- currentLayout,
- }),
- setIsEditorActive: (isEditorActive) => set(() => ({ isEditorActive })),
- updateEditorState: (state) => set((prev) => ({ ...prev, ...state })),
- updateQueryConfirmationList: (queryConfirmationList) => set({ queryConfirmationList }),
- setHoveredComponent: (hoveredComponent) =>
- set({ hoveredComponent }, false, {
- type: ACTIONS.SET_HOVERED_COMPONENT,
- hoveredComponent,
- }),
- setSelectionInProgress: (isSelectionInProgress) => {
- set(
- {
- isSelectionInProgress,
- },
- false,
- { type: ACTIONS.SET_SELECTION_IN_PROGRESS }
- );
- },
- setSelectedComponents: (selectedComponents, isMulti = false) => {
- const newSelectedComponents = isMulti
- ? [...get().selectedComponents, ...selectedComponents]
- : selectedComponents;
-
- set({
- selectedComponents: newSelectedComponents,
- });
- },
+ // Dev tools for this store are disabled comments since its freezing chrome tab
+ (set, get) => ({
+ ...initialState,
+ actions: {
+ setShowComments: (showComments) =>
+ set({ showComments }, false, {
+ type: ACTIONS.SET_HOVERED_COMPONENT,
+ showComments,
+ }),
+ toggleComments: () =>
+ set({ showComments: !get().showComments }, false, {
+ type: ACTIONS.TOGGLE_COMMENTS,
+ }),
+ toggleCurrentLayout: (currentLayout) =>
+ set({ currentLayout }, false, {
+ type: ACTIONS.TOGGLE_CURRENT_LAYOUT,
+ currentLayout,
+ }),
+ setIsEditorActive: (isEditorActive) => set(() => ({ isEditorActive })),
+ updateEditorState: (state) => set((prev) => ({ ...prev, ...state })),
+ updateQueryConfirmationList: (queryConfirmationList) => set({ queryConfirmationList }),
+ setHoveredComponent: (hoveredComponent) =>
+ set({ hoveredComponent }, false, {
+ type: ACTIONS.SET_HOVERED_COMPONENT,
+ hoveredComponent,
+ }),
+ setSelectionInProgress: (isSelectionInProgress) => {
+ set(
+ {
+ isSelectionInProgress,
+ },
+ false,
+ { type: ACTIONS.SET_SELECTION_IN_PROGRESS }
+ );
},
- }),
- { name: STORE_NAME }
- )
+ setSelectedComponents: (selectedComponents, isMulti = false) => {
+ const newSelectedComponents = isMulti
+ ? [...get().selectedComponents, ...selectedComponents]
+ : selectedComponents;
+
+ set({
+ selectedComponents: newSelectedComponents,
+ });
+ },
+ setCurrentPageId: (currentPageId) => set({ currentPageId }),
+ },
+ }),
+ { name: STORE_NAME }
);
export const useEditorActions = () => useEditorStore((state) => state.actions);
diff --git a/server/data-migrations/1695914619976-MigrateAppsDefinitionSchemaTransition.ts b/server/data-migrations/1695914619976-MigrateAppsDefinitionSchemaTransition.ts
deleted file mode 100644
index bf3d5a9471..0000000000
--- a/server/data-migrations/1695914619976-MigrateAppsDefinitionSchemaTransition.ts
+++ /dev/null
@@ -1,225 +0,0 @@
-import { MigrationInterface, QueryRunner } from 'typeorm';
-import { AppVersion } from '../src/entities/app_version.entity';
-import { Component } from 'src/entities/component.entity';
-import { Page } from 'src/entities/page.entity';
-import { Layout } from 'src/entities/layout.entity';
-import { EventHandler, Target } from 'src/entities/event_handler.entity';
-
-export class MigrateAppsDefinitionSchemaTransition1695914619976 implements MigrationInterface {
- public async up(queryRunner: QueryRunner): Promise {
- let progress = 0;
-
- const entityManager = queryRunner.manager;
-
- const queryBuilder = queryRunner.connection.createQueryBuilder();
-
- const appVersionRepository = entityManager.getRepository(AppVersion);
-
- const appVersions = await appVersionRepository.find();
-
- console.log(`MigrateAppsDefinitionSchemaTransition1695902112489 Progress ${progress} %`);
-
- for (const version of appVersions) {
- progress++;
- const definition = version['definition'];
-
- const dataQueries = await queryBuilder
- .select()
- .from('data_queries', 'data_queries')
- .where('app_version_id = :appVersionId', { appVersionId: version.id })
- .getRawMany();
-
- let updateHomepageId = null;
-
- if (definition?.pages) {
- for (const pageId of Object.keys(definition?.pages)) {
- const page = definition.pages[pageId];
-
- const pageEvents = page.events || [];
-
- const componentEvents = [];
-
- const pagePostionIntheList = Object.keys(definition?.pages).indexOf(pageId);
-
- const isHompage = (definition['homePageId'] as any) === pageId;
-
- const pageComponents = page.components;
-
- const componentLayouts = [];
-
- const mappedComponents = this.transformComponentData(pageComponents, componentEvents);
-
- const newPage = entityManager.create(Page, {
- name: page.name,
-
- handle: page.handle,
-
- appVersionId: version.id,
-
- index: pagePostionIntheList,
- });
-
- const pageCreated = await entityManager.save(newPage);
-
- mappedComponents.forEach((component) => {
- component.page = pageCreated;
- });
-
- const savedComponents = await entityManager.save(Component, mappedComponents);
-
- savedComponents.forEach((component) => {
- const componentLayout = pageComponents[component.id]['layouts'];
-
- if (componentLayout) {
- for (const type in componentLayout) {
- const layout = componentLayout[type];
-
- const newLayout = new Layout();
-
- newLayout.type = type;
-
- newLayout.top = layout.top;
-
- newLayout.left = layout.left;
-
- newLayout.width = layout.width;
-
- newLayout.height = layout.height;
-
- newLayout.component = component;
-
- componentLayouts.push(newLayout);
- }
- }
- });
-
- await entityManager.save(Layout, componentLayouts);
-
- if (pageEvents.length > 0) {
- pageEvents.forEach(async (event, index) => {
- const newEvent = {
- name: event.eventId,
-
- sourceId: pageCreated.id,
-
- target: Target.page,
-
- event: event,
-
- index: pageEvents.index || index,
-
- appVersionId: version.id,
- };
-
- await entityManager.save(EventHandler, newEvent);
- });
- }
-
- componentEvents.forEach((eventObj) => {
- if (eventObj.event?.length === 0) return;
-
- eventObj.event.forEach(async (event, index) => {
- const newEvent = {
- name: event.eventId,
-
- sourceId: eventObj.componentId,
-
- target: Target.component,
-
- event: event,
-
- index: eventObj.index || index,
-
- appVersionId: version.id,
- };
-
- await entityManager.save(EventHandler, newEvent);
- });
- });
-
- if (isHompage) {
- updateHomepageId = pageCreated.id;
- }
- }
- }
-
- for (const dataQuery of dataQueries) {
- const queryEvents = dataQuery?.options?.events || [];
-
- if (queryEvents.length > 0) {
- queryEvents.forEach(async (event, index) => {
- const newEvent = {
- name: event.eventId,
- sourceId: dataQuery.id,
- target: Target.dataQuery,
- event: event,
- index: queryEvents.index || index,
- appVersionId: version.id,
- };
-
- await entityManager.save(EventHandler, newEvent);
- });
- }
- }
- console.log(
- `MigrateAppsDefinitionSchemaTransition1695902112489 Progress ${Math.round(
- (progress / appVersions.length) * 100
- )} %`
- );
- await entityManager.update(
- AppVersion,
- { id: version.id },
- {
- homePageId: updateHomepageId,
- showViewerNavigation: definition.showViewerNavigation || true,
- globalSettings: definition.globalSettings,
- }
- );
- }
- }
-
- private transformComponentData(data: object, componentEvents: any[]): Component[] {
- const transformedComponents: Component[] = [];
-
- for (const componentId in data) {
- const componentData = data[componentId]['component'];
-
- const transformedComponent: Component = new Component();
-
- transformedComponent.id = componentId;
-
- transformedComponent.name = componentData.name;
-
- transformedComponent.type = componentData.component;
-
- transformedComponent.properties = componentData.definition.properties || {};
-
- transformedComponent.styles = componentData.definition.styles || {};
-
- transformedComponent.validation = componentData.definition.validation || {};
-
- transformedComponent.parent = data[componentId].parent || null;
-
- transformedComponents.push(transformedComponent);
-
- componentEvents.push({
- componentId: componentId,
-
- event: componentData.definition.events,
- });
- }
-
- return transformedComponents;
- }
-
- public async down(queryRunner: QueryRunner): Promise {
- await queryRunner.query('DELETE FROM page');
- await queryRunner.query('DELETE FROM component');
- await queryRunner.query('DELETE FROM layout');
- await queryRunner.query('DELETE FROM event_handler');
-
- await queryRunner.query('ALTER TABLE app_version DROP COLUMN IF EXISTS homePageId');
- await queryRunner.query('ALTER TABLE app_version DROP COLUMN IF EXISTS globalSettings');
- await queryRunner.query('ALTER TABLE app_version DROP COLUMN IF EXISTS showViewerNavigation');
- }
-}
diff --git a/server/data-migrations/1697473340856-MigrateAppsDefinitionSchemaTransition.ts b/server/data-migrations/1697473340856-MigrateAppsDefinitionSchemaTransition.ts
new file mode 100644
index 0000000000..d90faa240a
--- /dev/null
+++ b/server/data-migrations/1697473340856-MigrateAppsDefinitionSchemaTransition.ts
@@ -0,0 +1,240 @@
+import { In, MigrationInterface, QueryRunner } from 'typeorm';
+import { AppVersion } from '../src/entities/app_version.entity';
+import { Component } from 'src/entities/component.entity';
+import { Page } from 'src/entities/page.entity';
+import { Layout } from 'src/entities/layout.entity';
+import { EventHandler, Target } from 'src/entities/event_handler.entity';
+import { DataQuery } from 'src/entities/data_query.entity';
+import { MigrationProgress, processDataInBatches } from 'src/helpers/utils.helper';
+
+export class MigrateAppsDefinitionSchemaTransition1697473340856 implements MigrationInterface {
+ public async up(queryRunner: QueryRunner): Promise {
+ // let progress = 0;
+ const entityManager = queryRunner.manager;
+ const appVersionRepository = entityManager.getRepository(AppVersion);
+ const appVersions = await appVersionRepository.find();
+ const totalApps = appVersions.length;
+
+ const migrationProgress = new MigrationProgress('MigrateAppsDefinitionSchemaTransition1697473340856', totalApps);
+
+ const batchSize = 100; // Number of apps to migrate at a time
+
+ await processDataInBatches(
+ entityManager,
+ async (entityManager, skip, take) => {
+ return entityManager.find(AppVersion, {
+ where: { id: In(appVersions.map((appVersion) => appVersion.id)) },
+ take,
+ skip,
+ });
+ },
+ async (entityManager, versions: AppVersion[]) => {
+ for (const version of versions) {
+ const definition = version['definition'];
+
+ if (!definition) return;
+
+ const dataQueriesRepository = entityManager.getRepository(DataQuery);
+ const dataQueries = await dataQueriesRepository.find({
+ where: { appVersionId: version.id },
+ });
+
+ let updateHomepageId = null;
+
+ if (definition?.pages) {
+ for (const pageId of Object.keys(definition?.pages)) {
+ const page = definition.pages[pageId];
+ const pageEvents = page.events || [];
+ const componentEvents = [];
+ const pagePositionInTheList = Object.keys(definition?.pages).indexOf(pageId);
+ const isHomepage = (definition['homePageId'] as any) === pageId;
+ const pageComponents = page.components;
+ const componentLayouts = [];
+ const mappedComponents = this.transformComponentData(pageComponents, componentEvents);
+ const newPage = entityManager.create(Page, {
+ name: page.name,
+ handle: page.handle,
+ appVersionId: version.id,
+ disabled: page.disabled || false,
+ hidden: page.hidden || false,
+ index: pagePositionInTheList,
+ });
+
+ const pageCreated = await entityManager.save(newPage);
+ mappedComponents.forEach((component) => {
+ component.page = pageCreated;
+ });
+
+ const savedComponents = await entityManager.save(Component, mappedComponents);
+
+ savedComponents.forEach((component) => {
+ const componentLayout = pageComponents[component.id]['layouts'];
+
+ if (componentLayout) {
+ for (const type in componentLayout) {
+ const layout = componentLayout[type];
+ const newLayout = new Layout();
+ newLayout.type = type;
+ newLayout.top = layout.top;
+ newLayout.left = layout.left;
+ newLayout.width = layout.width;
+ newLayout.height = layout.height;
+ newLayout.component = component;
+ componentLayouts.push(newLayout);
+ }
+ }
+ });
+
+ await entityManager.save(Layout, componentLayouts);
+
+ if (pageEvents.length > 0) {
+ pageEvents.forEach(async (event, index) => {
+ const newEvent = {
+ name: event.eventId,
+ sourceId: pageCreated.id,
+ target: Target.page,
+ event: event,
+ index: pageEvents.index || index,
+ appVersionId: version.id,
+ };
+
+ await entityManager.save(EventHandler, newEvent);
+ });
+ }
+
+ componentEvents.forEach((eventObj) => {
+ if (eventObj.event?.length === 0) return;
+
+ eventObj.event.forEach(async (event, index) => {
+ const newEvent = {
+ name: event.eventId,
+ sourceId: eventObj.componentId,
+ target: Target.component,
+ event: event,
+ index: eventObj.index || index,
+ appVersionId: version.id,
+ };
+
+ await entityManager.save(EventHandler, newEvent);
+ });
+ });
+
+ savedComponents.forEach(async (component) => {
+ if (component.type === 'Table') {
+ const tableActions = component.properties?.actions?.value || [];
+ const tableColumns = component.properties?.columns?.value || [];
+ const tableActionAndColumnEvents = [];
+
+ tableActions.forEach((action) => {
+ const actionEvents = action.events || [];
+
+ actionEvents.forEach((event, index) => {
+ tableActionAndColumnEvents.push({
+ name: event.eventId,
+ sourceId: component.id,
+ target: Target.tableAction,
+ event: { ...event, ref: action.name },
+ index: event.index ?? index,
+ appVersionId: version.id,
+ });
+ });
+ });
+
+ tableColumns.forEach((column) => {
+ if (column?.columnType !== 'toggle') return;
+ const columnEvents = column.events || [];
+
+ columnEvents.forEach((event, index) => {
+ tableActionAndColumnEvents.push({
+ name: event.eventId,
+ sourceId: component.id,
+ target: Target.tableColumn,
+ event: { ...event, ref: column.name },
+ index: event.index ?? index,
+ appVersionId: version.id,
+ });
+ });
+ });
+
+ await entityManager.save(EventHandler, tableActionAndColumnEvents);
+ }
+ });
+
+ if (isHomepage) {
+ updateHomepageId = pageCreated.id;
+ }
+ }
+ }
+
+ for (const dataQuery of dataQueries) {
+ const queryEvents = dataQuery?.options?.events || [];
+
+ if (queryEvents.length > 0) {
+ queryEvents.forEach(async (event, index) => {
+ const newEvent = {
+ name: event.eventId,
+ sourceId: dataQuery.id,
+ target: Target.dataQuery,
+ event: event,
+ index: queryEvents.index || index,
+ appVersionId: version.id,
+ };
+
+ await entityManager.save(EventHandler, newEvent);
+ });
+ }
+ }
+
+ migrationProgress.show();
+ await entityManager.update(
+ AppVersion,
+ { id: version.id },
+ {
+ homePageId: updateHomepageId,
+ showViewerNavigation: definition?.showViewerNavigation || true,
+ globalSettings: definition.globalSettings,
+ }
+ );
+ }
+ },
+ batchSize
+ );
+ }
+
+ private transformComponentData(data: object, componentEvents: any[]): Component[] {
+ const transformedComponents: Component[] = [];
+
+ for (const componentId in data) {
+ const componentData = data[componentId]['component'];
+
+ const transformedComponent: Component = new Component();
+
+ transformedComponent.id = componentId;
+ transformedComponent.name = componentData.name;
+ transformedComponent.type = componentData.component;
+ transformedComponent.properties = componentData.definition.properties || {};
+ transformedComponent.styles = componentData.definition.styles || {};
+ transformedComponent.validation = componentData.definition.validation || {};
+ transformedComponent.parent = data[componentId].parent || null;
+ transformedComponents.push(transformedComponent);
+
+ componentEvents.push({
+ componentId: componentId,
+ event: componentData.definition.events,
+ });
+ }
+
+ return transformedComponents;
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query('DELETE FROM page');
+ await queryRunner.query('DELETE FROM component');
+ await queryRunner.query('DELETE FROM layout');
+ await queryRunner.query('DELETE FROM event_handler');
+
+ await queryRunner.query('ALTER TABLE app_version DROP COLUMN IF EXISTS homePageId');
+ await queryRunner.query('ALTER TABLE app_version DROP COLUMN IF EXISTS globalSettings');
+ await queryRunner.query('ALTER TABLE app_version DROP COLUMN IF EXISTS showViewerNavigation');
+ }
+}
diff --git a/server/src/controllers/apps.controller.v2.ts b/server/src/controllers/apps.controller.v2.ts
index 7c972581ef..aa3b791466 100644
--- a/server/src/controllers/apps.controller.v2.ts
+++ b/server/src/controllers/apps.controller.v2.ts
@@ -13,6 +13,7 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { JwtAuthGuard } from '../../src/modules/auth/jwt-auth.guard';
+import { AppAuthGuard } from 'src/modules/auth/app-auth.guard';
import { AppsService } from '../services/apps.service';
import { camelizeKeys, decamelizeKeys } from 'humps';
import { AppsAbilityFactory } from 'src/modules/casl/abilities/apps-ability.factory';
@@ -101,6 +102,8 @@ export class AppsControllerV2 {
return response;
}
+ @UseGuards(AppAuthGuard) // This guard will allow access for unauthenticated user if the app is public
+ @Get('slugs/:slug')
async appFromSlug(@User() user, @AppDecorator() app: App) {
if (user) {
const ability = await this.appsAbilityFactory.appsActions(user, app.id);
diff --git a/server/src/services/app_import_export.service.ts b/server/src/services/app_import_export.service.ts
index d9decca543..259ab9587a 100644
--- a/server/src/services/app_import_export.service.ts
+++ b/server/src/services/app_import_export.service.ts
@@ -355,8 +355,6 @@ export class AppImportExportService {
appResourceMappings.dataQueryMapping
);
- // !-----
-
let updateHomepageId = null;
if (updatedDefinition?.pages) {
@@ -381,10 +379,15 @@ export class AppImportExportService {
handle: page.handle,
appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id],
index: pagePostionIntheList,
+ disabled: page.disabled || false,
+ hidden: page.hidden || false,
});
const pageCreated = await manager.save(newPage);
+ appResourceMappings.pagesMapping[pageId] = pageCreated.id;
+
mappedComponents.forEach((component) => {
+ appResourceMappings.componentsMapping[component.id] = component.id;
component.page = pageCreated;
});
@@ -445,14 +448,54 @@ export class AppImportExportService {
});
});
+ savedComponents.forEach(async (component) => {
+ if (component.type === 'Table') {
+ const tableActions = component.properties?.actions?.value || [];
+ const tableColumns = component.properties?.columns?.value || [];
+
+ const tableActionAndColumnEvents = [];
+
+ tableActions.forEach((action) => {
+ const actionEvents = action.events || [];
+
+ actionEvents.forEach((event, index) => {
+ tableActionAndColumnEvents.push({
+ name: event.eventId,
+ sourceId: component.id,
+ target: Target.tableAction,
+ event: { ...event, ref: action.name },
+ index: event.index ?? index,
+ appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id],
+ });
+ });
+ });
+
+ tableColumns.forEach((column) => {
+ if (column?.columnType !== 'toggle') return;
+ const columnEvents = column.events || [];
+
+ columnEvents.forEach((event, index) => {
+ tableActionAndColumnEvents.push({
+ name: event.eventId,
+ sourceId: component.id,
+ target: Target.tableColumn,
+ event: { ...event, ref: column.name },
+ index: event.index ?? index,
+ appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id],
+ });
+ });
+ });
+
+ await manager.save(EventHandler, tableActionAndColumnEvents);
+ }
+ });
+
if (isHompage) {
updateHomepageId = pageCreated.id;
}
}
}
- //!----
-
await manager.update(
AppVersion,
{ id: appResourceMappings.appVersionMapping[importingAppVersion.id] },
@@ -464,6 +507,19 @@ export class AppImportExportService {
}
}
+ const appVersionIds = Object.values(appResourceMappings.appVersionMapping);
+
+ for (const appVersionId of appVersionIds) {
+ await this.updateEventActionsForNewVersionWithNewMappingIds(
+ manager,
+ appVersionId,
+ appResourceMappings.dataQueryMapping,
+ appResourceMappings.componentsMapping,
+ appResourceMappings.pagesMapping,
+ isNormalizedAppDefinitionSchema
+ );
+ }
+
await this.setEditingVersionAsLatestVersion(manager, appResourceMappings.appVersionMapping, importingAppVersions);
return appResourceMappings;
@@ -576,12 +632,30 @@ export class AppImportExportService {
appResourceMappings.dataQueryMapping = dataQueryMapping;
}
- for (const page of importingPages) {
+ const isChildOfTabsOrCalendar = (component, allComponents = [], componentParentId = undefined) => {
+ if (componentParentId) {
+ const parentId = component?.parent?.split('-').slice(0, -1).join('-');
+
+ const parentComponent = allComponents.find((comp) => comp.id === parentId);
+
+ if (parentComponent) {
+ return parentComponent.type === 'Tabs' || parentComponent.type === 'Calendar';
+ }
+ }
+
+ return false;
+ };
+
+ const pagesOfAppVersion = importingPages.filter((page) => page.appVersionId === importingAppVersion.id);
+
+ for (const page of pagesOfAppVersion) {
const newPage = manager.create(Page, {
name: page.name,
handle: page.handle,
appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id],
index: page.index,
+ disabled: page.disabled || false,
+ hidden: page.hidden || false,
});
const pageCreated = await manager.save(newPage);
@@ -599,12 +673,26 @@ export class AppImportExportService {
for (const component of pageComponents) {
const newComponent = new Component();
+ let parentId = component.parent ? component.parent : null;
+
+ const isParentTabOrCalendar = isChildOfTabsOrCalendar(component, pageComponents, parentId);
+
+ if (isParentTabOrCalendar) {
+ const childTabId = component.parent.split('-')[component.parent.split('-').length - 1];
+ const _parentId = component?.parent?.split('-').slice(0, -1).join('-');
+ const mappedParentId = appResourceMappings.componentsMapping[_parentId];
+
+ parentId = `${mappedParentId}-${childTabId}`;
+ } else {
+ parentId = appResourceMappings.componentsMapping[parentId];
+ }
+
newComponent.name = component.name;
newComponent.type = component.type;
newComponent.properties = component.properties;
newComponent.styles = component.styles;
newComponent.validation = component.validation;
- newComponent.parent = component.parent || null;
+ newComponent.parent = component.parent ? parentId : null;
newComponent.page = pageCreated;
@@ -629,14 +717,14 @@ export class AppImportExportService {
if (componentEvents.length > 0) {
componentEvents.forEach(async (componentEvent) => {
- const newEvent = {
- name: componentEvent.name,
- sourceId: savedComponent.id,
- target: componentEvent.target,
- event: componentEvent.event,
- index: componentEvent.index,
- appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id],
- };
+ const newEvent = new EventHandler();
+ newEvent.name = componentEvent.name;
+ newEvent.sourceId = savedComponent.id;
+ newEvent.target = componentEvent.target;
+ newEvent.event = componentEvent.event;
+ newEvent.index = componentEvent.index;
+ newEvent.appVersionId = appResourceMappings.appVersionMapping[importingAppVersion.id];
+
await manager.save(EventHandler, newEvent);
});
}
@@ -675,18 +763,11 @@ export class AppImportExportService {
if (importingQueryEvents.length > 0) {
importingQueryEvents.forEach(async (dataQueryEvent) => {
- const updatedEventDefinition = this.updateEventActionsForNewVersionWithNewMappingIds(
- dataQueryEvent,
- appResourceMappings.dataQueryMapping,
- appResourceMappings.componentsMapping,
- appResourceMappings.pagesMapping
- );
-
const newEvent = {
name: dataQueryEvent.name,
sourceId: mappedNewDataQuery.id,
target: dataQueryEvent.target,
- event: updatedEventDefinition,
+ event: dataQueryEvent.event,
index: dataQueryEvent.index,
appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id],
};
@@ -698,18 +779,11 @@ export class AppImportExportService {
delete mappedNewDataQuery?.options?.events;
queryEvents.forEach(async (event, index) => {
- const updatedEventDefinition = this.updateEventActionsForNewVersionWithNewMappingIds(
- { event: event },
- appResourceMappings.dataQueryMapping,
- appResourceMappings.componentsMapping,
- appResourceMappings.pagesMapping
- );
-
const newEvent = {
name: event.eventId,
sourceId: mappedNewDataQuery.id,
target: Target.dataQuery,
- event: updatedEventDefinition,
+ event: event,
index: queryEvents.index || index,
appVersionId: mappedNewDataQuery.appVersionId,
};
@@ -1374,29 +1448,39 @@ export class AppImportExportService {
return { ...queryOptions, table_id: tooljetDatabaseMapping[queryOptions.table_id]?.id };
}
- updateEventActionsForNewVersionWithNewMappingIds(
- queryEvent: EventHandler | { event: any },
+ async updateEventActionsForNewVersionWithNewMappingIds(
+ manager: EntityManager,
+ versionId: string,
oldDataQueryToNewMapping: Record,
oldComponentToNewComponentMapping: Record,
- oldPageToNewPageMapping: Record
+ oldPageToNewPageMapping: Record,
+ isNormalizedAppDefinitionSchema: boolean
) {
- const event = JSON.parse(JSON.stringify(queryEvent));
+ if (!isNormalizedAppDefinitionSchema) return;
- const eventDefinition = event.event;
+ const allEvents = await manager.find(EventHandler, {
+ where: { appVersionId: versionId },
+ });
- if (eventDefinition?.actionId === 'run-query') {
- eventDefinition.queryId = oldDataQueryToNewMapping[eventDefinition.queryId];
+ for (const event of allEvents) {
+ const eventDefinition = event.event;
+
+ if (eventDefinition?.actionId === 'run-query') {
+ eventDefinition.queryId = oldDataQueryToNewMapping[eventDefinition.queryId];
+ }
+
+ if (eventDefinition?.actionId === 'control-component') {
+ eventDefinition.componentId = oldComponentToNewComponentMapping[eventDefinition.componentId];
+ }
+
+ if (eventDefinition?.actionId === 'switch-page') {
+ eventDefinition.pageId = oldPageToNewPageMapping[eventDefinition.pageId];
+ }
+
+ event.event = eventDefinition;
+
+ await manager.save(event);
}
-
- if (eventDefinition?.actionId === 'control-component') {
- eventDefinition.componentId = oldComponentToNewComponentMapping[eventDefinition.componentId];
- }
-
- if (eventDefinition?.actionId === 'switch-page') {
- eventDefinition.pageId = oldPageToNewPageMapping[eventDefinition.pageId];
- }
-
- return eventDefinition;
}
}
diff --git a/server/src/services/apps.service.ts b/server/src/services/apps.service.ts
index bca36716e4..92df0026d2 100644
--- a/server/src/services/apps.service.ts
+++ b/server/src/services/apps.service.ts
@@ -459,12 +459,28 @@ export class AppsService {
const oldComponentToNewComponentMapping = {};
const oldPageToNewPageMapping = {};
+ const isChildOfTabsOrCalendar = (component, allComponents = [], componentParentId = undefined) => {
+ if (componentParentId) {
+ const parentId = component?.parent?.split('-').slice(0, -1).join('-');
+
+ const parentComponent = allComponents.find((comp) => comp.id === parentId);
+
+ if (parentComponent) {
+ return parentComponent.type === 'Tabs' || parentComponent.type === 'Calendar';
+ }
+ }
+
+ return false;
+ };
+
for (const page of pages) {
const savedPage = await manager.save(
manager.create(Page, {
name: page.name,
handle: page.handle,
index: page.index,
+ disabled: page.disabled,
+ hidden: page.hidden,
appVersionId: appVersion.id,
})
);
@@ -497,13 +513,27 @@ export class AppsService {
oldComponentToNewComponentMapping[component.id] = newComponent.id;
+ let parentId = component.parent ? component.parent : null;
+
+ const isParentTabOrCalendar = isChildOfTabsOrCalendar(component, page.components, parentId);
+
+ if (isParentTabOrCalendar) {
+ const childTabId = component.parent.split('-')[component.parent.split('-').length - 1];
+ const _parentId = component?.parent?.split('-').slice(0, -1).join('-');
+ const mappedParentId = oldComponentToNewComponentMapping[_parentId];
+
+ parentId = `${mappedParentId}-${childTabId}`;
+ } else {
+ parentId = oldComponentToNewComponentMapping[parentId];
+ }
+
newComponent.name = component.name;
newComponent.type = component.type;
newComponent.pageId = savedPage.id;
newComponent.properties = component.properties;
newComponent.styles = component.styles;
newComponent.validation = component.validation;
- newComponent.parent = component.parent ? oldComponentToNewComponentMapping[component.parent] : null;
+ newComponent.parent = component.parent ? parentId : null;
newComponent.page = savedPage;
newComponents.push(newComponent);