Superstore based state management architecture (#8678)

* Add a new type of component called "Module"

* Introduce superstore and load editorStore from it

* Load queryPanelStore from superstore

* Load useDataQueriesStore from superstore

* Load datasourcesStore from superstore

* Load currentStateStore from superstore

* Load appversionStore from superstore

* Load appDataStore from superstore

* Refactor superstore

* Module component and module mode in Viewer

* Create module in dashboard

* Remove unnecessary code
This commit is contained in:
Sherfin Shamsudeen 2024-02-05 19:14:14 +05:30 committed by GitHub
parent 291a955fec
commit 653078feb0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 1672 additions and 1107 deletions

View file

@ -69,6 +69,7 @@ import { useTranslation } from 'react-i18next';
import { useCurrentState } from '@/_stores/currentStateStore';
import { useAppInfo } from '@/_stores/appDataStore';
import WidgetIcon from '@/../assets/images/icons/widgets';
import { useModuleName } from '../_contexts/ModuleContext';
const AllComponents = {
Button,
@ -153,6 +154,7 @@ export const Box = memo(
const { t } = useTranslation();
const backgroundColor = yellow ? 'yellow' : '';
const currentState = useCurrentState();
const moduleName = useModuleName();
let styles = {
height: '100%',

View file

@ -1,5 +1,5 @@
/* eslint-disable import/no-named-as-default */
import React, { useCallback, useState, useEffect, useRef, useMemo } from 'react';
import React, { useCallback, useState, useEffect, useRef, useMemo, useContext } from 'react';
import cx from 'classnames';
import { useDrop, useDragLayer } from 'react-dnd';
import { ItemTypes } from './ItemTypes';
@ -17,8 +17,10 @@ import { addComponents, addNewWidgetToTheEditor } from '@/_helpers/appUtils';
import { useCurrentState } from '@/_stores/currentStateStore';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { useEditorStore } from '@/_stores/editorStore';
import { useSuperStore } from '@/_stores/superStore';
import { useAppInfo } from '@/_stores/appDataStore';
import { shallow } from 'zustand/shallow';
import { ModuleContext } from '../_contexts/ModuleContext';
import _ from 'lodash';
// eslint-disable-next-line import/no-unresolved
import { diff } from 'deep-object-diff';
@ -51,6 +53,8 @@ export const Container = ({
// redundant save on app definition load
const firstUpdate = useRef(true);
const moduleName = useContext(ModuleContext);
const { showComments, currentLayout } = useEditorStore(
(state) => ({
showComments: state?.showComments,
@ -334,7 +338,8 @@ export const Container = ({
let newBoxes = { ...boxes };
for (const selectedComponent of useEditorStore.getState().selectedComponents) {
for (const selectedComponent of useSuperStore.getState().modules[moduleName].useEditorStore.getState()
.selectedComponents) {
newBoxes = produce(newBoxes, (draft) => {
if (draft[selectedComponent.id]) {
const topOffset = draft[selectedComponent.id].layouts[currentLayout].top;

View file

@ -1,4 +1,4 @@
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import React, { useEffect, useLayoutEffect, useRef, useState, useContext } from 'react';
import {
appService,
authenticationService,
@ -32,6 +32,7 @@ import {
buildComponentMetaDefinition,
} from '@/_helpers/appUtils';
import { Confirm } from './Viewer/Confirm';
// eslint-disable-next-line import/no-unresolved
import { Tooltip as ReactTooltip } from 'react-tooltip';
import CommentNotifications from './CommentNotifications';
import { WidgetManager } from './WidgetManager';
@ -54,7 +55,6 @@ import { ReleasedVersionError } from './AppVersionsManager/ReleasedVersionError'
import { useDataSourcesStore } from '@/_stores/dataSourcesStore';
import { useDataQueriesStore } from '@/_stores/dataQueriesStore';
import { useAppVersionStore, useAppVersionActions, useAppVersionState } from '@/_stores/appVersionStore';
import { useQueryPanelStore } from '@/_stores/queryPanelStore';
import { useCurrentStateStore, useCurrentState, getCurrentState } from '@/_stores/currentStateStore';
import { computeAppDiff, computeComponentPropertyDiff, isParamFromTableColumn, resetAllStores } from '@/_stores/utils';
import { setCookie } from '@/_helpers/cookie';
@ -70,6 +70,8 @@ import useDebouncedArrowKeyPress from '@/_hooks/useDebouncedArrowKeyPress';
import { getQueryParams } from '@/_helpers/routes';
import RightSidebarTabManager from './RightSidebarTabManager';
import { shallow } from 'zustand/shallow';
import { ModuleContext } from '../_contexts/ModuleContext';
import { useSuperStore } from '../_stores/superStore';
setAutoFreeze(false);
enablePatches();
@ -81,6 +83,7 @@ function setWindowTitle(name) {
const decimalToHex = (alpha) => (alpha === 0 ? '00' : Math.round(255 * alpha).toString(16));
const EditorComponent = (props) => {
const moduleName = useContext(ModuleContext);
const { socket } = createWebsocketConnection(props?.params?.id);
const isSocketOpen = useSocketOpen(socket);
const mounted = useMounted();
@ -220,18 +223,21 @@ const EditorComponent = (props) => {
updateState({
currentUser: appUserDetails,
});
useCurrentStateStore.getState().actions.setCurrentState({
globals: {
...currentState.globals,
theme: { name: props?.darkMode ? 'dark' : 'light' },
urlparams: JSON.parse(JSON.stringify(queryString.parse(props.location.search))),
currentUser: userVars,
/* Constant value.it will only change for viewer */
mode: {
value: 'edit',
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
globals: {
...currentState.globals,
theme: { name: props?.darkMode ? 'dark' : 'light' },
urlparams: JSON.parse(JSON.stringify(queryString.parse(props.location.search))),
currentUser: userVars,
/* Constant value.it will only change for viewer */
mode: {
value: 'edit',
},
},
},
});
});
}
});
@ -243,7 +249,7 @@ const EditorComponent = (props) => {
socket && socket?.close();
subscription.unsubscribe();
if (config.ENABLE_MULTIPLAYER_EDITING) props?.provider?.disconnect();
useEditorStore.getState().actions.setIsEditorActive(false);
useSuperStore.getState().modules[moduleName].useEditorStore.getState().actions.setIsEditorActive(false);
prevAppDefinition.current = null;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -261,9 +267,9 @@ const EditorComponent = (props) => {
if (mounted && didAppDefinitionChanged && currentPageId) {
const components = appDefinition?.pages[currentPageId]?.components || {};
computeComponentState(components);
computeComponentState(components, moduleName);
if (useEditorStore.getState().isUpdatingEditorStateInProcess) {
if (useSuperStore.getState().modules[moduleName].useEditorStore.getState().isUpdatingEditorStateInProcess) {
autoSave();
}
}
@ -273,7 +279,7 @@ const EditorComponent = (props) => {
useEffect(
() => {
const components = appDefinition?.pages?.[currentPageId]?.components || {};
computeComponentState(components);
computeComponentState(components, moduleName);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentPageId]
@ -299,7 +305,7 @@ const EditorComponent = (props) => {
useEffect(() => {
if (mounted) {
useCurrentStateStore.getState().actions.setCurrentState({
useSuperStore.getState().modules[moduleName].useCurrentStateStore.getState().actions.setCurrentState({
layout: currentLayout,
});
}
@ -328,12 +334,14 @@ const EditorComponent = (props) => {
const getEditorRef = () => {
const editorRef = {
appDefinition: useEditorStore.getState().appDefinition,
queryConfirmationList: useEditorStore.getState().queryConfirmationList,
appDefinition: useSuperStore.getState().modules[moduleName].useEditorStore.getState().appDefinition,
queryConfirmationList: useSuperStore.getState().modules[moduleName].useEditorStore.getState()
.queryConfirmationList,
updateQueryConfirmationList: updateQueryConfirmationList,
navigate: props.navigate,
switchPage: switchPage,
currentPageId: useEditorStore.getState().currentPageId,
currentPageId: useSuperStore.getState().modules[moduleName].useEditorStore.getState().currentPageId,
moduleName,
};
return editorRef;
};
@ -363,7 +371,7 @@ const EditorComponent = (props) => {
}
});
useCurrentStateStore.getState().actions.setCurrentState({
useSuperStore.getState().modules[moduleName].useCurrentStateStore.getState().actions.setCurrentState({
server: server_variables,
client: client_variables,
});
@ -379,7 +387,7 @@ const EditorComponent = (props) => {
orgConstants[constant.name] = constantValue;
});
useCurrentStateStore.getState().actions.setCurrentState({
useSuperStore.getState().modules[moduleName].useCurrentStateStore.getState().actions.setCurrentState({
constants: orgConstants,
});
});
@ -480,18 +488,22 @@ const EditorComponent = (props) => {
};
const fetchDataQueries = async (id, selectFirstQuery = false, runQueriesOnAppLoad = false) => {
await useDataQueriesStore
await useSuperStore
.getState()
.actions.fetchDataQueries(id, selectFirstQuery, runQueriesOnAppLoad, getEditorRef());
.modules[moduleName].useDataQueriesStore.getState()
.actions.fetchDataQueries(id, selectFirstQuery, runQueriesOnAppLoad, getEditorRef(), moduleName);
};
const fetchDataSources = (id) => {
useDataSourcesStore.getState().actions.fetchDataSources(id);
useSuperStore.getState().modules[moduleName].useDataSourcesStore.getState().actions.fetchDataSources(id);
};
const fetchGlobalDataSources = () => {
const { current_organization_id: organizationId } = currentUser;
useDataSourcesStore.getState().actions.fetchGlobalDataSources(organizationId);
useSuperStore
.getState()
.modules[moduleName].useDataSourcesStore.getState()
.actions.fetchGlobalDataSources(organizationId);
};
const onVersionDelete = () => {
@ -562,18 +574,21 @@ const EditorComponent = (props) => {
const computeCanvasContainerHeight = () => {
// 45 = (height of header)
// 85 = (the height of the query panel header when minimised) + (height of header)
return `calc(${100}% - ${Math.max(useQueryPanelStore.getState().queryPanelHeight + 45, 85)}px)`;
return `calc(${100}% - ${Math.max(
useSuperStore.getState().modules[moduleName].useQueryPanelStore.getState().queryPanelHeight + 45,
85
)}px)`;
};
const handleQueryPaneDragging = (bool) => setIsQueryPaneDragging(bool);
const handleQueryPaneExpanding = (bool) => setIsQueryPaneExpanded(bool);
const handleOnComponentOptionChanged = (component, optionName, value) => {
return onComponentOptionChanged(component, optionName, value);
return onComponentOptionChanged(moduleName, component, optionName, value);
};
const handleOnComponentOptionsChanged = (component, options) => {
return onComponentOptionsChanged(component, options);
return onComponentOptionsChanged(moduleName, component, options);
};
const handleComponentClick = (id, component) => {
@ -584,21 +599,24 @@ const EditorComponent = (props) => {
const sideBarDebugger = {
error: (data) => {
debuggerActions.error(data);
debuggerActions.error(data, moduleName);
},
flush: () => {
debuggerActions.flush();
debuggerActions.flush(moduleName);
},
generateErrorLogs: (errors) => debuggerActions.generateErrorLogs(errors),
generateErrorLogs: (errors) => debuggerActions.generateErrorLogs(errors, moduleName),
};
const changeDarkMode = (newMode) => {
useCurrentStateStore.getState().actions.setCurrentState({
globals: {
...currentState.globals,
theme: { name: newMode ? 'dark' : 'light' },
},
});
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
globals: {
...currentState.globals,
theme: { name: newMode ? 'dark' : 'light' },
},
});
props.switchDarkMode(newMode);
};
@ -613,7 +631,10 @@ const EditorComponent = (props) => {
};
const setSelectedComponent = (id, component, multiSelect = false) => {
const isAlreadySelected = useEditorStore.getState()?.selectedComponents.find((component) => component.id === id);
const isAlreadySelected = useSuperStore
.getState()
.modules[moduleName].useEditorStore.getState()
?.selectedComponents.find((component) => component.id === id);
if (!isAlreadySelected) {
setSelectedComponents([{ id, component }], multiSelect);
@ -621,7 +642,10 @@ const EditorComponent = (props) => {
};
const onVersionRelease = (versionId) => {
useAppVersionStore.getState().actions.updateReleasedVersionId(versionId);
useSuperStore
.getState()
.modules[moduleName].useAppVersionStore.getState()
.actions.updateReleasedVersionId(versionId);
if (socket instanceof WebSocket && socket?.readyState === WebSocket.OPEN) {
socket.send(
@ -673,9 +697,15 @@ const EditorComponent = (props) => {
const callBack = async (data, startingPageHandle, versionSwitched = false) => {
setWindowTitle(data.name);
useAppVersionStore.getState().actions.updateEditingVersion(data.editing_version);
useSuperStore
.getState()
.modules[moduleName].useAppVersionStore.getState()
.actions.updateEditingVersion(data.editing_version);
if (!releasedVersionId || !versionSwitched) {
useAppVersionStore.getState().actions.updateReleasedVersionId(data.current_version_id);
useSuperStore
.getState()
.modules[moduleName].useAppVersionStore.getState()
.actions.updateReleasedVersionId(data.current_version_id);
}
const appVersions = await appEnvironmentService.getVersionsByEnvironment(data?.id);
@ -712,7 +742,7 @@ const EditorComponent = (props) => {
setCurrentPageId(homePageId);
useCurrentStateStore.getState().actions.setCurrentState({
useSuperStore.getState().modules[moduleName].useCurrentStateStore.getState().actions.setCurrentState({
page: currentpageData,
});
@ -730,7 +760,10 @@ const EditorComponent = (props) => {
});
}
await useDataSourcesStore.getState().actions.fetchGlobalDataSources(data?.organization_id);
await useSuperStore
.getState()
.modules[moduleName].useDataSourcesStore.getState()
.actions.fetchGlobalDataSources(data?.organization_id);
await fetchDataSources(data.editing_version?.id);
await fetchDataQueries(data.editing_version?.id, true, true);
const currentPageEvents = data.events.filter((event) => event.target === 'page' && event.sourceId === homePageId);
@ -786,11 +819,13 @@ const EditorComponent = (props) => {
});
}
let updatedAppDefinition;
const copyOfAppDefinition = JSON.parse(JSON.stringify(useEditorStore.getState().appDefinition));
const copyOfAppDefinition = JSON.parse(
JSON.stringify(useSuperStore.getState().modules[moduleName].useEditorStore.getState().appDefinition)
);
if (opts?.skipYmapUpdate && opts?.currentSessionId !== currentSessionId) {
updatedAppDefinition = produce(copyOfAppDefinition, (draft) => {
const _currentPageId = useEditorStore.getState().currentPageId;
const _currentPageId = useSuperStore.getState().modules[moduleName].useEditorStore.getState().currentPageId;
if (opts?.componentDeleting) {
const currentPageComponentIds = Object.keys(copyOfAppDefinition.pages[_currentPageId]?.components);
const newComponentIds = Object.keys(newDefinition.pages[_currentPageId]?.components);
@ -928,7 +963,7 @@ const EditorComponent = (props) => {
};
const saveEditingVersion = (isUserSwitchedVersion = false) => {
const editingVersion = useAppVersionStore.getState().editingVersion;
const editingVersion = useSuperStore.getState().modules[moduleName].useAppVersionStore.getState().editingVersion;
if (isVersionReleased && !isUserSwitchedVersion) {
updateEditorState({
isUpdatingEditorStateInProcess: false,
@ -958,7 +993,10 @@ const EditorComponent = (props) => {
...editingVersion,
...{ definition: appDefinition },
};
useAppVersionStore.getState().actions.updateEditingVersion(_editingVersion);
useSuperStore
.getState()
.modules[moduleName].useAppVersionStore.getState()
.actions.updateEditingVersion(_editingVersion);
if (config.ENABLE_MULTIPLAYER_EDITING) {
props.ymap?.set('appDef', {
@ -1131,7 +1169,10 @@ const EditorComponent = (props) => {
const componentDefinitionChanged = (componentDefinition, props) => {
if (isVersionReleased) {
useAppVersionStore.getState().actions.enableReleasedVersionPopupState();
useSuperStore
.getState()
.modules[moduleName].useAppVersionStore.getState()
.actions.enableReleasedVersionPopupState();
return;
}
@ -1189,7 +1230,10 @@ const EditorComponent = (props) => {
componentDeleted: true,
});
} else {
useAppVersionStore.getState().actions.enableReleasedVersionPopupState();
useSuperStore
.getState()
.modules[moduleName].useAppVersionStore.getState()
.actions.enableReleasedVersionPopupState();
}
};
@ -1197,7 +1241,9 @@ const EditorComponent = (props) => {
const gridWidth = (1 * 100) / 43; // width of the canvas grid in percentage
const _appDefinition = _.cloneDeep(appDefinition);
let newComponents = _appDefinition?.pages[currentPageId].components;
const selectedComponents = useEditorStore.getState()?.selectedComponents;
const selectedComponents = useSuperStore
.getState()
.modules[moduleName].useEditorStore.getState()?.selectedComponents;
for (const selectedComponent of selectedComponents) {
let top = newComponents[selectedComponent.id].layouts[currentLayout].top;
@ -1229,7 +1275,7 @@ const EditorComponent = (props) => {
const copyComponents = () =>
cloneComponents(
useEditorStore.getState()?.selectedComponents,
useSuperStore.getState().modules[moduleName].useEditorStore.getState()?.selectedComponents,
appDefinition,
currentPageId,
appDefinitionChanged,
@ -1238,13 +1284,16 @@ const EditorComponent = (props) => {
const cutComponents = () => {
if (isVersionReleased) {
useAppVersionStore.getState().actions.enableReleasedVersionPopupState();
useSuperStore
.getState()
.modules[moduleName].useAppVersionStore.getState()
.actions.enableReleasedVersionPopupState();
return;
}
cloneComponents(
useEditorStore.getState()?.selectedComponents,
useSuperStore.getState().modules[moduleName].useEditorStore.getState()?.selectedComponents,
appDefinition,
currentPageId,
appDefinitionChanged,
@ -1255,11 +1304,14 @@ const EditorComponent = (props) => {
const cloningComponents = () => {
if (isVersionReleased) {
useAppVersionStore.getState().actions.enableReleasedVersionPopupState();
useSuperStore
.getState()
.modules[moduleName].useAppVersionStore.getState()
.actions.enableReleasedVersionPopupState();
return;
}
cloneComponents(
useEditorStore.getState()?.selectedComponents,
useSuperStore.getState().modules[moduleName].useEditorStore.getState()?.selectedComponents,
appDefinition,
currentPageId,
appDefinitionChanged,
@ -1269,7 +1321,7 @@ const EditorComponent = (props) => {
};
const handleEditorEscapeKeyPress = () => {
if (useEditorStore.getState()?.selectedComponents?.length > 0) {
if (useSuperStore.getState().modules[moduleName].useEditorStore.getState()?.selectedComponents?.length > 0) {
updateEditorState({
selectedComponents: [],
});
@ -1277,7 +1329,9 @@ const EditorComponent = (props) => {
};
const removeComponents = () => {
const selectedComponents = useEditorStore.getState()?.selectedComponents;
const selectedComponents = useSuperStore
.getState()
.modules[moduleName].useEditorStore.getState()?.selectedComponents;
if (!isVersionReleased && selectedComponents?.length > 1) {
let newDefinition = cloneDeep(appDefinition);
@ -1293,7 +1347,10 @@ const EditorComponent = (props) => {
});
}
} else if (isVersionReleased) {
useAppVersionStore.getState().actions.enableReleasedVersionPopupState();
useSuperStore
.getState()
.modules[moduleName].useAppVersionStore.getState()
.actions.enableReleasedVersionPopupState();
}
};
@ -1376,11 +1433,14 @@ const EditorComponent = (props) => {
const globals = {
...currentState.globals,
};
useCurrentStateStore.getState().actions.setCurrentState({ globals, page });
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({ globals, page });
};
const navigateToPage = (queryParams = [], handle) => {
const appId = useAppDataStore.getState()?.appId;
const appId = useSuperStore.getState().modules[moduleName].useAppDataStore.getState()?.appId;
const queryParamsString = queryParams.map(([key, value]) => `${key}=${value}`).join('&');
props?.navigate(`/${getWorkspaceId()}/apps/${slug ?? appId}/${handle}?${queryParamsString}`, {
@ -1392,9 +1452,9 @@ const EditorComponent = (props) => {
const switchPage = (pageId, queryParams = []) => {
// This are fetched from store to handle runQueriesOnAppLoad
const currentPageId = useEditorStore.getState().currentPageId;
const appDefinition = useEditorStore.getState().appDefinition;
const pageHandle = getCurrentState().pageHandle;
const currentPageId = useSuperStore.getState().modules[moduleName].useEditorStore.getState().currentPageId;
const appDefinition = useSuperStore.getState().modules[moduleName].useEditorStore.getState().appDefinition;
const pageHandle = getCurrentState(moduleName).pageHandle;
if (currentPageId === pageId && pageHandle === appDefinition?.pages[pageId]?.handle) {
return;
@ -1417,7 +1477,10 @@ const EditorComponent = (props) => {
...currentState.globals,
urlparams: JSON.parse(JSON.stringify(queryString.parse(queryParamsString))),
};
useCurrentStateStore.getState().actions.setCurrentState({ globals, page });
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({ globals, page });
setCurrentPageId(pageId);
@ -1646,7 +1709,7 @@ const EditorComponent = (props) => {
const handleCanvasContainerMouseUp = (e) => {
if (
['real-canvas', 'modal'].includes(e.target.className) &&
useEditorStore.getState()?.selectedComponents?.length
useSuperStore.getState().modules[moduleName].useEditorStore.getState()?.selectedComponents?.length
) {
setSelectedComponents(EMPTY_ARRAY);
}

View file

@ -1,7 +1,9 @@
import React, { useCallback, memo } from 'react';
import React, { useCallback, memo, useContext } from 'react';
import Selecto from 'react-selecto';
import { useEditorStore, EMPTY_ARRAY } from '@/_stores/editorStore';
import { shallow } from 'zustand/shallow';
import { useSuperStore } from '../_stores/superStore';
import { ModuleContext } from '../_contexts/ModuleContext';
const EditorSelecto = ({
selectionRef,
@ -20,11 +22,19 @@ const EditorSelecto = ({
shallow
);
const moduleName = useContext(ModuleContext);
const onAreaSelectionStart = useCallback(
(e) => {
const isMultiSelect = e.inputEvent.shiftKey || useEditorStore.getState().selectedComponents.length > 0;
const isMultiSelect =
e.inputEvent.shiftKey ||
useSuperStore.getState().modules[moduleName].useEditorStore.getState().selectedComponents.length > 0;
setSelectionInProgress(true);
setSelectedComponents([...(isMultiSelect ? useEditorStore.getState().selectedComponents : EMPTY_ARRAY)]);
setSelectedComponents([
...(isMultiSelect
? useSuperStore.getState().modules[moduleName].useEditorStore.getState().selectedComponents
: EMPTY_ARRAY),
]);
},
[setSelectionInProgress, setSelectedComponents]
);
@ -33,7 +43,7 @@ const EditorSelecto = ({
e.added.forEach((el) => {
el.classList.add('resizer-select');
});
if (useEditorStore.getState().selectionInProgress) {
if (useSuperStore.getState().modules[moduleName].useEditorStore.getState().selectionInProgress) {
e.removed.forEach((el) => {
el.classList.remove('resizer-select');
});
@ -68,7 +78,8 @@ const EditorSelecto = ({
(e) => {
if (selectionDragRef.current) {
e.stop();
useEditorStore.getState().selectionInProgress && setSelectionInProgress(false);
useSuperStore.getState().modules[moduleName].useEditorStore.getState().selectionInProgress &&
setSelectionInProgress(false);
}
},
[setSelectionInProgress, selectionDragRef]
@ -76,7 +87,8 @@ const EditorSelecto = ({
const onAreaSelectionDragEnd = () => {
selectionDragRef.current = false;
useEditorStore.getState().selectionInProgress && setSelectionInProgress(false);
useSuperStore.getState().modules[moduleName].useEditorStore.getState().selectionInProgress &&
setSelectionInProgress(false);
};
return (

View file

@ -17,8 +17,11 @@ import List from '@/ToolJetUI/List/List';
import { capitalize, has } from 'lodash';
import NoListItem from './NoListItem';
import { ProgramaticallyHandleProperties } from './ProgramaticallyHandleProperties';
import { useAppDataStore } from '@/_stores/appDataStore';
import { ModuleContext } from '../../../../_contexts/ModuleContext';
import { useSuperStore } from '@/_stores/superStore';
class TableComponent extends React.Component {
static contextType = ModuleContext;
constructor(props) {
super(props);
@ -749,13 +752,19 @@ class TableComponent extends React.Component {
};
deleteEvents = (ref, eventTarget) => {
const events = useAppDataStore.getState().events.filter((event) => event.target === eventTarget);
const events = useSuperStore
.getState()
.modules[this.context].useAppDataStore.getState()
.events.filter((event) => event.target === eventTarget);
const toDelete = events?.filter((e) => e.event?.ref === ref.ref);
return new Promise.all(
toDelete?.forEach((e) => {
return useAppDataStore.getState().actions.deleteAppVersionEventHandler(e.id);
return useSuperStore
.getState()
.modules[this.context].useAppDataStore.getState()
.actions.deleteAppVersionEventHandler(e.id);
})
);
};

View file

@ -1,5 +1,6 @@
import { useState, useEffect } from 'react';
import { useCurrentStateStore } from '@/_stores/currentStateStore';
import { useModuleName } from '@/_contexts/ModuleContext';
import { shallow } from 'zustand/shallow';
import { debuggerActions } from '@/_helpers/appUtils';
import { flow } from 'lodash';
@ -11,6 +12,8 @@ const useDebugger = ({ currentPageId, isDebuggerOpen }) => {
const [unReadErrorCount, setUnReadErrorCount] = useState({ read: 0, unread: 0 });
const [allLog, setAllLog] = useState([]);
const moduleName = useModuleName();
const { errors, succededQuery } = useCurrentStateStore(
(state) => ({
errors: state.errors,
@ -43,7 +46,7 @@ const useDebugger = ({ currentPageId, isDebuggerOpen }) => {
(arr) => arr.filter(([key, value]) => value.data?.status),
Object.fromEntries,
])(errors);
const newErrorLogs = debuggerActions.generateErrorLogs(newError);
const newErrorLogs = debuggerActions.generateErrorLogs(newError, moduleName);
const newPageLevelErrorLogs = newErrorLogs.filter((error) => error.strace === 'page_level');
const newAppLevelErrorLogs = newErrorLogs.filter((error) => error.strace === 'app_level');
if (newErrorLogs) {
@ -64,19 +67,19 @@ const useDebugger = ({ currentPageId, isDebuggerOpen }) => {
};
});
}
debuggerActions.flush();
debuggerActions.flush(moduleName);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify({ errors })]);
useEffect(() => {
const successQueryLogs = debuggerActions.generateQuerySuccessLogs(succededQuery);
const successQueryLogs = debuggerActions.generateQuerySuccessLogs(succededQuery, moduleName);
if (successQueryLogs?.length) {
setAllLog((prevLogs) => {
const temp = [...successQueryLogs, ...prevLogs];
const sortedDatesDesc = temp.sort((a, b) => moment(b.timestamp).diff(moment(a.timestamp)));
return sortedDatesDesc;
});
debuggerActions.flushAllLog();
debuggerActions.flushAllLog(moduleName);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify({ succededQuery })]);

View file

@ -13,9 +13,12 @@ import SolidIcon from '@/_ui/Icon/SolidIcons';
import cx from 'classnames';
import { ToolTip } from '@/_components/ToolTip';
import { TOOLTIP_MESSAGES } from '@/_helpers/constants';
import { useAppDataStore } from '@/_stores/appDataStore';
import { useSuperStore } from '../_stores/superStore';
import { ModuleContext } from '../_contexts/ModuleContext';
class ManageAppUsersComponent extends React.Component {
static contextType = ModuleContext;
constructor(props) {
super(props);
this.isUserAdmin = authenticationService.currentSessionValue?.admin;
@ -109,7 +112,10 @@ class ManageAppUsersComponent extends React.Component {
ischangingVisibility: true,
});
useAppDataStore.getState().actions.updateState({ isPublic: newState });
useSuperStore
.getState()
.modules[this.context].useAppDataStore.getState()
.actions.updateState({ isPublic: newState });
// eslint-disable-next-line no-unused-vars
appsService
@ -165,7 +171,10 @@ class ManageAppUsersComponent extends React.Component {
});
replaceEditorURL(value, this.props.pageHandle);
useAppDataStore.getState().actions.updateState({ slug: value });
useSuperStore
.getState()
.modules[this.context].useAppDataStore.getState()
.actions.updateState({ slug: value });
})
.catch(({ error }) => {
this.setState({

View file

@ -20,8 +20,10 @@ import { useAppVersionStore } from '@/_stores/appVersionStore';
import { shallow } from 'zustand/shallow';
import { Tooltip } from 'react-tooltip';
import { Button } from 'react-bootstrap';
import { useModuleName } from '@/_contexts/ModuleContext';
export const QueryManagerHeader = forwardRef(({ darkMode, options, editorRef }, ref) => {
const moduleName = useModuleName();
const { renameQuery } = useDataQueriesActions();
const selectedQuery = useSelectedQuery();
const selectedDataSource = useSelectedDataSource();
@ -51,7 +53,7 @@ export const QueryManagerHeader = forwardRef(({ darkMode, options, editorRef },
return false;
}
const isNewQueryNameAlreadyExists = checkExistingQueryName(newName);
const isNewQueryNameAlreadyExists = checkExistingQueryName(newName, moduleName);
if (isNewQueryNameAlreadyExists) {
toast.error('Query name already exists');
return false;

View file

@ -10,8 +10,10 @@ import { shallow } from 'zustand/shallow';
import Copy from '@/_ui/Icon/solidIcons/Copy';
import DataSourceIcon from '../QueryManager/Components/DataSourceIcon';
import { isQueryRunnable } from '@/_helpers/utils';
import { useModuleName } from '@/_contexts/ModuleContext';
export const QueryCard = ({ dataQuery, darkMode = false, editorRef, appId }) => {
const moduleName = useModuleName();
const selectedQuery = useSelectedQuery();
const { isDeletingQueryInProcess } = useDataQueriesStore();
const { deleteDataQueries, renameQuery, duplicateQuery } = useDataQueriesActions();
@ -42,7 +44,7 @@ export const QueryCard = ({ dataQuery, darkMode = false, editorRef, appId }) =>
if (name === newName) {
return setRenamingQuery(false);
}
const isNewQueryNameAlreadyExists = checkExistingQueryName(newName);
const isNewQueryNameAlreadyExists = checkExistingQueryName(newName, moduleName);
if (newName && !isNewQueryNameAlreadyExists) {
renameQuery(dataQuery?.id, newName, editorRef);
setRenamingQuery(false);

View file

@ -1,15 +1,17 @@
import React, { useState, useRef, useCallback, useEffect } from 'react';
import React, { useState, useRef, useCallback, useEffect, useContext } from 'react';
import { useEventListener } from '@/_hooks/use-event-listener';
import { Tooltip } from 'react-tooltip';
import { QueryDataPane } from './QueryDataPane';
import QueryManager from '../QueryManager/QueryManager';
import useWindowResize from '@/_hooks/useWindowResize';
import { useQueryPanelStore, useQueryPanelActions } from '@/_stores/queryPanelStore';
import { useQueryPanelActions } from '@/_stores/queryPanelStore';
import { useDataQueriesStore, useDataQueries } from '@/_stores/dataQueriesStore';
import Maximize from '@/_ui/Icon/solidIcons/Maximize';
import { cloneDeep, isEmpty, isEqual } from 'lodash';
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
import { ModuleContext } from '../../_contexts/ModuleContext';
import { useSuperStore } from '../../_stores/superStore';
const QueryPanel = ({
dataQueriesChanged,
@ -22,6 +24,7 @@ const QueryPanel = ({
onQueryPaneDragging,
handleQueryPaneExpanding,
}) => {
const moduleName = useContext(ModuleContext);
const { updateQueryPanelHeight } = useQueryPanelActions();
const dataQueries = useDataQueries();
const queryManagerPreferences = useRef(JSON.parse(localStorage.getItem('queryManagerPreferences')) ?? {});
@ -37,26 +40,28 @@ const QueryPanel = ({
const [windowSize, isWindowResizing] = useWindowResize();
useEffect(() => {
const queryPanelStoreListner = useQueryPanelStore.subscribe(({ selectedQuery }, prevState) => {
if (isEmpty(prevState?.selectedQuery) || isEmpty(selectedQuery)) {
return;
}
const queryPanelStoreListner = useSuperStore
.getState()
.modules[moduleName].useQueryPanelStore.subscribe(({ selectedQuery }, prevState) => {
if (isEmpty(prevState?.selectedQuery) || isEmpty(selectedQuery)) {
return;
}
if (prevState?.selectedQuery?.id !== selectedQuery.id) {
return;
}
if (prevState?.selectedQuery?.id !== selectedQuery.id) {
return;
}
//removing updated_at since this value changes whenever the data is updated in the BE
const formattedQuery = cloneDeep(selectedQuery);
delete formattedQuery.updated_at;
//removing updated_at since this value changes whenever the data is updated in the BE
const formattedQuery = cloneDeep(selectedQuery);
delete formattedQuery.updated_at;
const formattedPrevQuery = cloneDeep(prevState?.selectedQuery || {});
delete formattedPrevQuery.updated_at;
const formattedPrevQuery = cloneDeep(prevState?.selectedQuery || {});
delete formattedPrevQuery.updated_at;
if (!isEqual(formattedQuery, formattedPrevQuery)) {
useDataQueriesStore.getState().actions.saveData(selectedQuery);
}
});
if (!isEqual(formattedQuery, formattedPrevQuery)) {
useSuperStore.getState().modules[moduleName].useDataQueriesStore.getState().actions.saveData(selectedQuery);
}
});
return queryPanelStoreListner;
}, []);

View file

@ -1,5 +1,5 @@
/* eslint-disable import/no-named-as-default */
import React, { useCallback, useState, useEffect, useRef } from 'react';
import React, { useCallback, useState, useEffect, useRef, useContext } from 'react';
import { useDrop, useDragLayer } from 'react-dnd';
import { ItemTypes } from './ItemTypes';
import { DraggableBox } from './DraggableBox';
@ -15,9 +15,10 @@ import { useCurrentState } from '@/_stores/currentStateStore';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { shallow } from 'zustand/shallow';
import { useMounted } from '@/_hooks/use-mount';
import { useEditorStore } from '@/_stores/editorStore';
import { useSuperStore } from '@/_stores/superStore';
// eslint-disable-next-line import/no-unresolved
import { diff } from 'deep-object-diff';
import { ModuleContext } from '../_contexts/ModuleContext';
const NO_OF_GRIDS = 43;
@ -62,6 +63,8 @@ export const SubContainer = ({
Listview: 'listItem',
});
const moduleName = useContext(ModuleContext);
const customResolverVariable = widgetResolvables[parentComponent?.component];
const currentState = useCurrentState();
const { enableReleasedVersionPopupState, isVersionReleased } = useAppVersionStore(
@ -402,7 +405,9 @@ export const SubContainer = ({
let newBoxes = { ...boxes };
const subContainerHeight = canvasBounds.height - 30;
const selectedComponents = useEditorStore.getState().selectedComponents;
const selectedComponents = useSuperStore
.getState()
.modules[moduleName].useEditorStore.getState().selectedComponents;
if (selectedComponents) {
for (const selectedComponent of selectedComponents) {

View file

@ -38,8 +38,12 @@ import { shallow } from 'zustand/shallow';
import { useAppDataActions, useAppDataStore } from '@/_stores/appDataStore';
import { getPreviewQueryParams, redirectToErrorPage } from '@/_helpers/routes';
import { ERROR_TYPES } from '@/_helpers/constants';
import { useSuperStore } from '../_stores/superStore';
import { ModuleContext } from '../_contexts/ModuleContext';
class ViewerComponent extends React.Component {
static contextType = ModuleContext;
constructor(props) {
super(props);
@ -69,6 +73,7 @@ class ViewerComponent extends React.Component {
navigate: this.props.navigate,
switchPage: this.switchPage,
currentPageId: this.state.currentPageId,
moduleName: this.context,
};
}
@ -158,7 +163,7 @@ class ViewerComponent extends React.Component {
const currentPageId = pages.filter((page) => page.handle === startingPageHandle)[0]?.id ?? homePageId;
const currentPage = pages.find((page) => page.id === currentPageId);
useDataQueriesStore.getState().actions.setDataQueries(dataQueries);
useSuperStore.getState().modules[this.context].useDataQueriesStore.getState().actions.setDataQueries(dataQueries);
this.props.setCurrentState({
queries: queryState,
components: {},
@ -180,7 +185,10 @@ class ViewerComponent extends React.Component {
...variables,
...constants,
});
useEditorStore.getState().actions.toggleCurrentLayout(mobileLayoutHasWidgets ? 'mobile' : 'desktop');
useSuperStore
.getState()
.modules[this.context].useEditorStore.getState()
.actions.toggleCurrentLayout(mobileLayoutHasWidgets ? 'mobile' : 'desktop');
this.props.updateState({ events: data.events ?? [] });
this.setState(
{
@ -201,9 +209,8 @@ class ViewerComponent extends React.Component {
() => {
const components = appDefData?.pages[currentPageId]?.components || {};
computeComponentState(components).then(async () => {
computeComponentState(components, this.context).then(async () => {
this.setState({ initialComputationOfStateDone: true, defaultComponentStateComputed: true });
console.log('Default component state computed and set');
this.runQueries(dataQueries);
const currentPageEvents = this.state.events.filter(
@ -237,8 +244,6 @@ class ViewerComponent extends React.Component {
variablesResult = constants;
}
console.log('--org constant 2.0', { variablesResult });
if (variablesResult && Array.isArray(variablesResult)) {
variablesResult.map((constant) => {
const constantValue = constant.values.find((value) => value.environmentName === 'production')['value'];
@ -317,7 +322,10 @@ class ViewerComponent extends React.Component {
};
updateQueryConfirmationList = (queryConfirmationList) =>
useEditorStore.getState().actions.updateQueryConfirmationList(queryConfirmationList);
useSuperStore
.getState()
.modules[this.context].useEditorStore.getState()
.actions.updateQueryConfirmationList(queryConfirmationList);
setupViewer() {
this.subscription = authenticationService.currentSession.subscribe((currentSession) => {
@ -325,9 +333,11 @@ class ViewerComponent extends React.Component {
const appId = this.props.id;
const versionId = this.props.versionId;
console.log({ slug, appId, versionId });
if (currentSession?.load_app && slug) {
if (currentSession?.group_permissions) {
useAppDataStore.getState().actions.setAppId(appId);
useSuperStore.getState().modules[this.context].useAppDataStore.getState().actions.setAppId(appId);
const currentUser = currentSession.current_user;
const userVars = {
@ -376,7 +386,10 @@ class ViewerComponent extends React.Component {
componentDidMount() {
this.setupViewer();
const isMobileDevice = this.state.deviceWindowWidth < 600;
useEditorStore.getState().actions.toggleCurrentLayout(isMobileDevice ? 'mobile' : 'desktop');
useSuperStore
.getState()
.modules[this.context].useEditorStore.getState()
.actions.toggleCurrentLayout(isMobileDevice ? 'mobile' : 'desktop');
window.addEventListener('message', this.handleMessage);
}
@ -434,7 +447,10 @@ class ViewerComponent extends React.Component {
name: targetPage.name,
},
async () => {
computeComponentState(this.state.appDefinition?.pages[this.state.currentPageId].components).then(async () => {
computeComponentState(
this.state.appDefinition?.pages[this.state.currentPageId].components,
this.context
).then(async () => {
const currentPageEvents = this.state.events.filter(
(event) => event.target === 'page' && event.sourceId === this.state.currentPageId
);
@ -547,6 +563,8 @@ class ViewerComponent extends React.Component {
canvasWidth,
} = this.state;
const moduleName = this.context;
const currentCanvasWidth = canvasWidth;
const queryConfirmationList = this.props?.queryConfirmationList ?? [];
@ -589,89 +607,184 @@ class ViewerComponent extends React.Component {
queryConfirmationData={queryConfirmationList[0]}
key={queryConfirmationList[0]?.queryName}
/>
<DndProvider backend={HTML5Backend}>
<ViewerNavigation.Header
showHeader={!appDefinition.globalSettings?.hideHeader && isAppLoaded}
appName={this.state.app?.name ?? null}
changeDarkMode={this.changeDarkMode}
darkMode={this.props.darkMode}
pages={Object.entries(this.state.appDefinition?.pages) ?? []}
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
switchPage={this.switchPage}
/>
<div className="sub-section">
<div className="main">
<div
className="canvas-container align-items-center"
style={{
background: this.computeCanvasBackgroundColor() || (!this.props.darkMode ? '#EBEBEF' : '#2E3035'),
}}
>
<div className="areas d-flex flex-rows">
{appDefinition?.showViewerNavigation && (
<ViewerNavigation
isMobileDevice={this.props.currentLayout === 'mobile'}
canvasBackgroundColor={this.computeCanvasBackgroundColor()}
pages={Object.entries(this.state.appDefinition?.pages) ?? []}
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
switchPage={this.switchPage}
darkMode={this.props.darkMode}
/>
)}
<div className="flex-grow-1 d-flex justify-content-center">
<div
className="canvas-area"
style={{
width: currentCanvasWidth,
maxWidth: canvasMaxWidth,
backgroundColor: this.computeCanvasBackgroundColor(),
margin: 0,
padding: 0,
}}
>
{defaultComponentStateComputed && (
<>
{isLoading ? (
<div className="mx-auto mt-5 w-50 p-5">
<center>
<div className="spinner-border text-azure" role="status"></div>
</center>
</div>
) : (
<Container
appDefinition={appDefinition}
appDefinitionChanged={() => false} // function not relevant in viewer
snapToGrid={true}
appLoading={isLoading}
darkMode={this.props.darkMode}
onEvent={this.handleEvent}
mode="view"
deviceWindowWidth={deviceWindowWidth}
selectedComponent={this.state.selectedComponent}
onComponentClick={(id, component) => {
this.setState({
selectedComponent: { id, component },
});
onComponentClick(this, id, component, 'view');
}}
onComponentOptionChanged={(component, optionName, value) => {
return onComponentOptionChanged(component, optionName, value);
}}
onComponentOptionsChanged={onComponentOptionsChanged}
canvasWidth={this.getCanvasWidth()}
dataQueries={dataQueries}
currentPageId={this.state.currentPageId}
/>
)}
</>
)}
{!this.props.moduleMode && (
<DndProvider backend={HTML5Backend}>
<ViewerNavigation.Header
showHeader={!appDefinition.globalSettings?.hideHeader && isAppLoaded && !this.props.moduleMode}
appName={this.state.app?.name ?? null}
changeDarkMode={this.changeDarkMode}
darkMode={this.props.darkMode}
pages={Object.entries(this.state.appDefinition?.pages) ?? []}
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
switchPage={this.switchPage}
/>
<div className="sub-section">
<div className="main">
<div
className="canvas-container align-items-center"
style={{
background:
this.computeCanvasBackgroundColor() || (!this.props.darkMode ? '#EBEBEF' : '#2E3035'),
...(this.props.moduleMode ? { top: 0 } : {}),
}}
>
<div className="areas d-flex flex-rows">
{appDefinition?.showViewerNavigation && !this.props.moduleMode && (
<ViewerNavigation
isMobileDevice={this.props.currentLayout === 'mobile'}
canvasBackgroundColor={this.computeCanvasBackgroundColor()}
pages={Object.entries(this.state.appDefinition?.pages) ?? []}
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
switchPage={this.switchPage}
darkMode={this.props.darkMode}
/>
)}
<div className="flex-grow-1 d-flex justify-content-center">
<div
className="canvas-area"
style={{
width: currentCanvasWidth,
maxWidth: canvasMaxWidth,
backgroundColor: this.computeCanvasBackgroundColor(),
margin: 0,
padding: 0,
}}
>
{defaultComponentStateComputed && (
<>
{isLoading ? (
<div className="mx-auto mt-5 w-50 p-5">
<center>
<div className="spinner-border text-azure" role="status"></div>
</center>
</div>
) : (
<Container
appDefinition={appDefinition}
appDefinitionChanged={() => false} // function not relevant in viewer
snapToGrid={true}
appLoading={isLoading}
darkMode={this.props.darkMode}
onEvent={this.handleEvent}
mode="view"
deviceWindowWidth={deviceWindowWidth}
selectedComponent={this.state.selectedComponent}
onComponentClick={(id, component) => {
this.setState({
selectedComponent: { id, component },
});
onComponentClick(this, id, component, 'view');
}}
onComponentOptionChanged={(component, optionName, value) => {
return onComponentOptionChanged(this.context, component, optionName, value);
}}
onComponentOptionsChanged={(...props) =>
onComponentOptionsChanged(this.context, ...props)
}
canvasWidth={this.getCanvasWidth()}
dataQueries={dataQueries}
currentPageId={this.state.currentPageId}
/>
)}
</>
)}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</DndProvider>
</DndProvider>
)}
{this.props.moduleMode && (
<>
<ViewerNavigation.Header
showHeader={!appDefinition.globalSettings?.hideHeader && isAppLoaded && !this.props.moduleMode}
appName={this.state.app?.name ?? null}
changeDarkMode={this.changeDarkMode}
darkMode={this.props.darkMode}
pages={Object.entries(this.state.appDefinition?.pages) ?? []}
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
switchPage={this.switchPage}
/>
<div className="sub-section">
<div className="main">
<div
className="canvas-container align-items-center"
style={{
background:
this.computeCanvasBackgroundColor() || (!this.props.darkMode ? '#EBEBEF' : '#2E3035'),
...(this.props.moduleMode ? { top: 0 } : {}),
}}
>
<div className="areas d-flex flex-rows">
{appDefinition?.showViewerNavigation && !this.props.moduleMode && (
<ViewerNavigation
isMobileDevice={this.props.currentLayout === 'mobile'}
canvasBackgroundColor={this.computeCanvasBackgroundColor()}
pages={Object.entries(this.state.appDefinition?.pages) ?? []}
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
switchPage={this.switchPage}
darkMode={this.props.darkMode}
/>
)}
<div className="flex-grow-1 d-flex justify-content-center">
<div
className="canvas-area"
style={{
width: currentCanvasWidth,
maxWidth: canvasMaxWidth,
backgroundColor: this.computeCanvasBackgroundColor(),
margin: 0,
padding: 0,
}}
>
{defaultComponentStateComputed && (
<>
{isLoading ? (
<div className="mx-auto mt-5 w-50 p-5">
<center>
<div className="spinner-border text-azure" role="status"></div>
</center>
</div>
) : (
<Container
appDefinition={appDefinition}
appDefinitionChanged={() => false} // function not relevant in viewer
snapToGrid={true}
appLoading={isLoading}
darkMode={this.props.darkMode}
onEvent={this.handleEvent}
mode="view"
deviceWindowWidth={deviceWindowWidth}
selectedComponent={this.state.selectedComponent}
onComponentClick={(id, component) => {
this.setState({
selectedComponent: { id, component },
});
onComponentClick(this, id, component, 'view');
}}
onComponentOptionChanged={(component, optionName, value) => {
return onComponentOptionChanged(this.context, component, optionName, value);
}}
onComponentOptionsChanged={(...props) =>
onComponentOptionsChanged(this.context, ...props)
}
canvasWidth={this.getCanvasWidth()}
dataQueries={dataQueries}
currentPageId={this.state.currentPageId}
/>
)}
</>
)}
</div>
</div>
</div>
</div>
</div>
</div>
</>
)}
</div>
);
}

View file

@ -63,6 +63,7 @@ class HomePageComponent extends React.Component {
showTemplateLibraryModal: false,
app: {},
showCreateAppModal: false,
showCreateModuleModal: false,
showCreateAppFromTemplateModal: false,
showImportAppModal: false,
showCloneAppModal: false,
@ -131,11 +132,11 @@ class HomePageComponent extends React.Component {
this.fetchFolders();
};
createApp = async (appName) => {
createApp = async (appName, type) => {
let _self = this;
_self.setState({ creatingApp: true });
try {
const data = await appsService.createApp({ icon: sample(iconList), name: appName });
const data = await appsService.createApp({ icon: sample(iconList), name: appName, type });
const workspaceId = getWorkspaceId();
_self.props.navigate(`/${workspaceId}/apps/${data.id}`);
toast.success('App created successfully!');
@ -540,11 +541,11 @@ class HomePageComponent extends React.Component {
};
openCreateAppModal = () => {
this.setState({ showCreateAppModal: true });
this.setState({ showCreateAppModal: true, showCreateModuleModal: true });
};
closeCreateAppModal = () => {
this.setState({ showCreateAppModal: false });
this.setState({ showCreateAppModal: false, showCreateModuleModal: false });
};
render() {
@ -568,6 +569,7 @@ class HomePageComponent extends React.Component {
appToBeDeleted,
app,
showCreateAppModal,
showCreateModuleModal,
showImportAppModal,
fileContent,
fileName,
@ -577,13 +579,13 @@ class HomePageComponent extends React.Component {
return (
<Layout switchDarkMode={this.props.switchDarkMode} darkMode={this.props.darkMode}>
<div className="wrapper home-page">
{showCreateAppModal && (
{(showCreateAppModal || showCreateModuleModal) && (
<AppModal
closeModal={this.closeCreateAppModal}
processApp={this.createApp}
processApp={(name) => this.createApp(name, showCreateAppModal ? 'front-end' : 'module')}
show={this.openCreateAppModal}
title={'Create app'}
actionButton={'+ Create app'}
title={showCreateAppModal ? 'Create app' : 'Create module'}
actionButton={showCreateAppModal ? '+ Create app' : '+ Create module'}
actionLoadingButton={'Creating'}
/>
)}
@ -795,6 +797,13 @@ class HomePageComponent extends React.Component {
data-cy="import-option-input"
/>
</label>
<Dropdown.Item
className="homepage-dropdown-style tj-text tj-text-xsm"
onClick={() => this.setState({ showCreateModuleModal: true })}
data-cy="create-module-button"
>
{this.props.t('homePage.header.createModule', 'Create module')}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</div>

View file

@ -0,0 +1,11 @@
import { createContext, useContext } from 'react';
export const ModuleContext = createContext(null);
export const useModuleName = () => {
const moduleName = useContext(ModuleContext);
if (!moduleName) throw Error('useModuleName can only be used inside a ModuleContext');
return moduleName;
};

View file

@ -35,6 +35,7 @@ import { useAppVersionStore } from '@/_stores/appVersionStore';
import { camelizeKeys } from 'humps';
import { useAppDataStore } from '@/_stores/appDataStore';
import { useEditorStore } from '@/_stores/editorStore';
import { useSuperStore } from '@/_stores/superStore';
const ERROR_TYPES = Object.freeze({
ReferenceError: 'ReferenceError',
@ -62,9 +63,10 @@ export function setCurrentStateAsync(_ref, changes) {
});
}
export function onComponentOptionsChanged(component, options) {
export function onComponentOptionsChanged(moduleName, component, options) {
const componentName = component.name;
const components = getCurrentState().components;
console.log({ moduleName });
const components = getCurrentState(moduleName).components;
let componentData = components[componentName];
componentData = componentData || {};
@ -72,27 +74,36 @@ export function onComponentOptionsChanged(component, options) {
componentData[option[0]] = option[1];
}
useCurrentStateStore.getState().actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
return Promise.resolve();
}
export function onComponentOptionChanged(component, option_name, value) {
export function onComponentOptionChanged(moduleName, component, option_name, value) {
const componentName = component.name;
const components = getCurrentState().components;
const components = getCurrentState(moduleName).components;
let componentData = components[componentName];
componentData = componentData || {};
componentData[option_name] = value;
if (option_name !== 'id') {
useCurrentStateStore.getState().actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
} else if (!componentData?.id) {
useCurrentStateStore.getState().actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
}
return Promise.resolve();
@ -133,7 +144,7 @@ async function executeRunPycode(_ref, code, query, isPreview, mode) {
const evaluatePythonCode = async (pyodide) => {
let result = {};
const currentState = getCurrentState();
const currentState = getCurrentState(_ref.moduleName);
try {
const appStateVars = currentState['variables'] ?? {};
@ -284,7 +295,7 @@ export async function runTransformation(
let result = [];
const currentState = getCurrentState() || {};
const currentState = getCurrentState(_ref.moduleName) || {};
if (transformationLanguage === 'python') {
result = await runPythonTransformation(currentState, data, transformation, query, mode);
@ -364,13 +375,13 @@ function showModal(_ref, modal, show) {
const modalMeta = _ref.appDefinition.pages[_ref.currentPageId].components[modalId]; //! NeedToFix
const _components = {
...getCurrentState().components,
...getCurrentState(_ref.moduleName).components,
[modalMeta.component.name]: {
...getCurrentState().components[modalMeta.component.name],
...getCurrentState(_ref.moduleName).components[modalMeta.component.name],
show: show,
},
};
useCurrentStateStore.getState().actions.setCurrentState({
useSuperStore.getState().modules[_ref.moduleName].useCurrentStateStore.getState().actions.setCurrentState({
components: _components,
});
return Promise.resolve();
@ -402,14 +413,19 @@ export const executeAction = debounce(executeActionWithDebounce);
function executeActionWithDebounce(_ref, event, mode, customVariables) {
if (event) {
if (event.runOnlyIf) {
const shouldRun = resolveReferences(event.runOnlyIf, getCurrentState(), undefined, customVariables);
const shouldRun = resolveReferences(
event.runOnlyIf,
getCurrentState(_ref.moduleName),
undefined,
customVariables
);
if (!shouldRun) {
return false;
}
}
switch (event.actionId) {
case 'show-alert': {
const message = resolveReferences(event.message, getCurrentState(), undefined, customVariables);
const message = resolveReferences(event.message, getCurrentState(_ref.moduleName), undefined, customVariables);
switch (event.alertType) {
case 'success':
case 'error':
@ -433,11 +449,15 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) {
const resolvedParams = {};
if (params) {
Object.keys(params).map(
(param) => (resolvedParams[param] = resolveReferences(params[param], getCurrentState(), undefined))
(param) =>
(resolvedParams[param] = resolveReferences(params[param], getCurrentState(_ref.moduleName), undefined))
);
}
const name =
useDataQueriesStore.getState().dataQueries.find((query) => query.id === queryId)?.name ?? queryName;
useSuperStore
.getState()
.modules[_ref.moduleName].useDataQueriesStore.getState()
.dataQueries.find((query) => query.id === queryId)?.name ?? queryName;
return runQuery(_ref, queryId, name, undefined, mode, resolvedParams);
}
case 'logout': {
@ -445,20 +465,20 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) {
}
case 'open-webpage': {
const url = resolveReferences(event.url, getCurrentState(), undefined, customVariables);
const url = resolveReferences(event.url, getCurrentState(_ref.moduleName), undefined, customVariables);
window.open(url, '_blank');
return Promise.resolve();
}
case 'go-to-app': {
const slug = resolveReferences(event.slug, getCurrentState(), undefined, customVariables);
const slug = resolveReferences(event.slug, getCurrentState(_ref.moduleName), undefined, customVariables);
const queryParams = event.queryParams?.reduce(
(result, queryParam) => ({
...result,
...{
[resolveReferences(queryParam[0], getCurrentState())]: resolveReferences(
[resolveReferences(queryParam[0], getCurrentState(_ref.moduleName))]: resolveReferences(
queryParam[1],
getCurrentState(),
getCurrentState(_ref.moduleName),
undefined,
customVariables
),
@ -492,15 +512,20 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) {
return showModal(_ref, event.modal, false);
case 'copy-to-clipboard': {
const contentToCopy = resolveReferences(event.contentToCopy, getCurrentState(), undefined, customVariables);
const contentToCopy = resolveReferences(
event.contentToCopy,
getCurrentState(_ref.moduleName),
undefined,
customVariables
);
copyToClipboard(contentToCopy);
return Promise.resolve();
}
case 'set-localstorage-value': {
const key = resolveReferences(event.key, getCurrentState(), undefined, customVariables);
const value = resolveReferences(event.value, getCurrentState(), undefined, customVariables);
const key = resolveReferences(event.key, getCurrentState(_ref.moduleName), undefined, customVariables);
const value = resolveReferences(event.value, getCurrentState(_ref.moduleName), undefined, customVariables);
localStorage.setItem(key, value);
return Promise.resolve();
@ -508,9 +533,11 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) {
case 'generate-file': {
// const fileType = event.fileType;
const data = resolveReferences(event.data, getCurrentState(), undefined, customVariables) ?? [];
const fileName = resolveReferences(event.fileName, getCurrentState(), undefined, customVariables) ?? 'data.txt';
const fileType = resolveReferences(event.fileType, getCurrentState(), undefined, customVariables) ?? 'csv';
const data = resolveReferences(event.data, getCurrentState(_ref.moduleName), undefined, customVariables) ?? [];
const fileName =
resolveReferences(event.fileName, getCurrentState(_ref.moduleName), undefined, customVariables) ?? 'data.txt';
const fileType =
resolveReferences(event.fileType, getCurrentState(_ref.moduleName), undefined, customVariables) ?? 'csv';
const fileData = {
csv: generateCSV,
plaintext: (plaintext) => plaintext,
@ -521,57 +548,69 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) {
}
case 'set-table-page': {
setTablePageIndex(event.table, event.pageIndex);
setTablePageIndex(event.table, event.pageIndex, _ref);
break;
}
case 'set-custom-variable': {
const key = resolveReferences(event.key, getCurrentState(), undefined, customVariables);
const value = resolveReferences(event.value, getCurrentState(), undefined, customVariables);
const customAppVariables = { ...getCurrentState().variables };
const key = resolveReferences(event.key, getCurrentState(_ref.moduleName), undefined, customVariables);
const value = resolveReferences(event.value, getCurrentState(_ref.moduleName), undefined, customVariables);
const customAppVariables = { ...getCurrentState(_ref.moduleName).variables };
customAppVariables[key] = value;
return useCurrentStateStore.getState().actions.setCurrentState({
variables: customAppVariables,
});
return useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
variables: customAppVariables,
});
}
case 'unset-custom-variable': {
const key = resolveReferences(event.key, getCurrentState(), undefined, customVariables);
const customAppVariables = { ...getCurrentState().variables };
const key = resolveReferences(event.key, getCurrentState(_ref.moduleName), undefined, customVariables);
const customAppVariables = { ...getCurrentState(_ref.moduleName).variables };
delete customAppVariables[key];
return useCurrentStateStore.getState().actions.setCurrentState({
variables: customAppVariables,
});
return useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
variables: customAppVariables,
});
}
case 'set-page-variable': {
const key = resolveReferences(event.key, getCurrentState(), undefined, customVariables);
const value = resolveReferences(event.value, getCurrentState(), undefined, customVariables);
const key = resolveReferences(event.key, getCurrentState(_ref.moduleName), undefined, customVariables);
const value = resolveReferences(event.value, getCurrentState(_ref.moduleName), undefined, customVariables);
const customPageVariables = {
...getCurrentState().page.variables,
...getCurrentState(_ref.moduleName).page.variables,
[key]: value,
};
return useCurrentStateStore.getState().actions.setCurrentState({
page: {
...getCurrentState().page,
variables: customPageVariables,
},
});
return useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
page: {
...getCurrentState(_ref.moduleName).page,
variables: customPageVariables,
},
});
}
case 'unset-page-variable': {
const key = resolveReferences(event.key, getCurrentState(), undefined, customVariables);
const customPageVariables = _.omit(getCurrentState().page.variables, key);
return useCurrentStateStore.getState().actions.setCurrentState({
page: {
...getCurrentState().page,
variables: customPageVariables,
},
});
const key = resolveReferences(event.key, getCurrentState(_ref.moduleName), undefined, customVariables);
const customPageVariables = _.omit(getCurrentState(_ref.moduleName).page.variables, key);
return useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
page: {
...getCurrentState(_ref.moduleName).page,
variables: customPageVariables,
},
});
}
case 'control-component': {
let component = Object.values(getCurrentState()?.components ?? {}).filter(
let component = Object.values(getCurrentState(_ref.moduleName)?.components ?? {}).filter(
(component) => component.id === event.componentId
)[0];
let action = '';
@ -579,8 +618,10 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) {
// check if component id not found then try to find if its available as child widget else continue
// with normal flow finding action
if (component == undefined) {
component = _ref.appDefinition.pages[getCurrentState()?.page?.id].components[event.componentId].component;
const parent = Object.values(getCurrentState()?.components ?? {}).find(
component =
_ref.appDefinition.pages[getCurrentState(_ref.moduleName)?.page?.id].components[event.componentId]
.component;
const parent = Object.values(getCurrentState(_ref.moduleName)?.components ?? {}).find(
(item) => item.id === component.parent
);
const child = Object.values(parent?.children).find((item) => item.id === event.componentId);
@ -593,7 +634,7 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) {
}
actionArguments = _.map(event.componentSpecificActionParams, (param) => ({
...param,
value: resolveReferences(param.value, getCurrentState(), undefined, customVariables),
value: resolveReferences(param.value, getCurrentState(_ref.moduleName), undefined, customVariables),
}));
const actionPromise = action && action(...actionArguments.map((argument) => argument.value));
return actionPromise ?? Promise.resolve();
@ -604,7 +645,10 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) {
// Don't allow switching to disabled page in editor as well as viewer
if (!disabled) {
_ref.switchPage(event.pageId, resolveReferences(event.queryParams, getCurrentState(), [], customVariables));
_ref.switchPage(
event.pageId,
resolveReferences(event.queryParams, getCurrentState(_ref.moduleName), [], customVariables)
);
}
if (_ref.appDefinition.pages[event.pageId]) {
if (disabled) {
@ -618,7 +662,10 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) {
},
},
};
useCurrentStateStore.getState().actions.setErrors(generalProps);
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setErrors(generalProps);
}
}
@ -641,44 +688,53 @@ export async function onEvent(_ref, eventName, events, options = {}, mode = 'edi
if (eventName === 'onTrigger') {
const { component, queryId, queryName, parameters } = options;
useCurrentStateStore.getState().actions.setCurrentState({
components: {
...getCurrentState().components,
[component.name]: {
...getCurrentState().components[component.name],
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
components: {
...getCurrentState(_ref.moduleName).components,
[component.name]: {
...getCurrentState(_ref.moduleName).components[component.name],
},
},
},
});
});
runQuery(_ref, queryId, queryName, true, mode, parameters);
}
if (eventName === 'onCalendarEventSelect') {
const { component, calendarEvent } = options;
useCurrentStateStore.getState().actions.setCurrentState({
components: {
...getCurrentState().components,
[component.name]: {
...getCurrentState().components[component.name],
selectedEvent: { ...calendarEvent },
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
components: {
...getCurrentState(_ref.moduleName).components,
[component.name]: {
...getCurrentState(_ref.moduleName).components[component.name],
selectedEvent: { ...calendarEvent },
},
},
},
});
});
executeActionsForEventId(_ref, 'onCalendarEventSelect', events, mode, customVariables);
}
if (eventName === 'onCalendarSlotSelect') {
const { component, selectedSlots } = options;
useCurrentStateStore.getState().actions.setCurrentState({
components: {
...getCurrentState().components,
[component.name]: {
...getCurrentState().components[component.name],
selectedSlots,
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
components: {
...getCurrentState(_ref.moduleName).components,
[component.name]: {
...getCurrentState(_ref.moduleName).components[component.name],
selectedSlots,
},
},
},
});
});
executeActionsForEventId(_ref, 'onCalendarSlotSelect', events, mode, customVariables);
}
@ -828,9 +884,9 @@ export function getQueryVariables(options, state) {
}
export function previewQuery(_ref, query, calledFromQuery = false, parameters = {}, hasParamSupport = false) {
const options = getQueryVariables(query.options, getCurrentState());
const options = getQueryVariables(query.options, getCurrentState(_ref.moduleName));
const queryPanelState = useQueryPanelStore.getState();
const queryPanelState = useSuperStore.getState().modules[_ref.moduleName].useQueryPanelStore.getState();
const { queryPreviewData } = queryPanelState;
const { setPreviewLoading, setPreviewData } = queryPanelState.actions;
@ -862,14 +918,14 @@ export function previewQuery(_ref, query, calledFromQuery = false, parameters =
hasParamSupport
);
} else if (query.kind === 'tooljetdb') {
queryExecutionPromise = tooljetDbOperations.perform(query, getCurrentState());
queryExecutionPromise = tooljetDbOperations.perform(query, getCurrentState(_ref.moduleName));
} else if (query.kind === 'runpy') {
queryExecutionPromise = executeRunPycode(_ref, query.options.code, query, true, 'edit');
} else {
queryExecutionPromise = dataqueryService.preview(
query,
options,
useAppVersionStore.getState().editingVersion?.id
useSuperStore.getState().modules[_ref.moduleName].useAppVersionStore.getState().editingVersion?.id
);
}
@ -944,15 +1000,19 @@ export function previewQuery(_ref, query, calledFromQuery = false, parameters =
}
export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode = 'edit', parameters = {}) {
const query = useDataQueriesStore.getState().dataQueries.find((query) => query.id === queryId);
const queryEvents = useAppDataStore
const query = useSuperStore
.getState()
.modules[_ref.moduleName].useDataQueriesStore.getState()
.dataQueries.find((query) => query.id === queryId);
const queryEvents = useSuperStore
.getState()
.modules[_ref.moduleName].useAppDataStore.getState()
.events.filter((event) => event.target === 'data_query' && event.sourceId === queryId);
let dataQuery = {};
// const { setPreviewLoading, setPreviewData } = useQueryPanelStore.getState().actions;
const queryPanelState = useQueryPanelStore.getState();
// const { setPreviewLoading, setPreviewData } = useSuperStore.getState().modules[_ref.moduleName].useQueryPanelStore.getState().actions;
const queryPanelState = useSuperStore.getState().modules[_ref.moduleName].useQueryPanelStore.getState();
const { queryPreviewData } = queryPanelState;
const { setPreviewLoading, setPreviewData } = queryPanelState.actions;
if (parameters?.shouldSetPreviewData) {
@ -967,11 +1027,12 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode =
return;
}
const options = getQueryVariables(dataQuery.options, getCurrentState());
const options = getQueryVariables(dataQuery.options, getCurrentState(_ref.moduleName));
if (dataQuery.options?.requestConfirmation) {
const queryConfirmationList = useEditorStore.getState().queryConfirmationList
? [...useEditorStore.getState().queryConfirmationList]
const queryConfirmationList = useSuperStore.getState().modules[_ref.moduleName].useEditorStore.getState()
.queryConfirmationList
? [...useSuperStore.getState().modules[_ref.moduleName].useEditorStore.getState().queryConfirmationList]
: [];
const queryConfirmation = {
@ -993,25 +1054,28 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode =
// eslint-disable-next-line no-unused-vars
return new Promise(function (resolve, reject) {
useCurrentStateStore.getState().actions.setCurrentState({
queries: {
...getCurrentState().queries,
[queryName]: {
...getCurrentState().queries[queryName],
isLoading: true,
data: [],
rawData: [],
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
queries: {
...getCurrentState(_ref.moduleName).queries,
[queryName]: {
...getCurrentState(_ref.moduleName).queries[queryName],
isLoading: true,
data: [],
rawData: [],
},
},
},
errors: {},
});
errors: {},
});
let queryExecutionPromise = null;
if (query.kind === 'runjs') {
queryExecutionPromise = executeMultilineJS(_self, query.options.code, query?.id, false, mode, parameters);
} else if (query.kind === 'runpy') {
queryExecutionPromise = executeRunPycode(_self, query.options.code, query, false, mode);
} else if (query.kind === 'tooljetdb') {
queryExecutionPromise = tooljetDbOperations.perform(query, getCurrentState());
queryExecutionPromise = tooljetDbOperations.perform(query, getCurrentState(_ref.moduleName));
} else {
queryExecutionPromise = dataqueryService.run(queryId, options, query?.options);
}
@ -1066,33 +1130,39 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode =
setPreviewData(errorData);
}
// errorData = query.kind === 'runpy' ? data.data : data;
useCurrentStateStore.getState().actions.setErrors({
[queryName]: {
type: 'query',
kind: query.kind,
data: errorData,
options: options,
},
});
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setErrors({
[queryName]: {
type: 'query',
kind: query.kind,
data: errorData,
options: options,
},
});
useCurrentStateStore.getState().actions.setCurrentState({
queries: {
...getCurrentState().queries,
[queryName]: _.assign(
{
...getCurrentState().queries[queryName],
isLoading: false,
},
query.kind === 'restapi'
? {
request: data.data.requestObject,
response: data.data.responseObject,
responseHeaders: data.data.responseHeaders,
}
: {}
),
},
});
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
queries: {
...getCurrentState(_ref.moduleName).queries,
[queryName]: _.assign(
{
...getCurrentState(_ref.moduleName).queries[queryName],
isLoading: false,
},
query.kind === 'restapi'
? {
request: data.data.requestObject,
response: data.data.responseObject,
responseHeaders: data.data.responseHeaders,
}
: {}
),
},
});
resolve(data);
onEvent(_self, 'onDataQueryFailure', queryEvents);
if (mode !== 'view') {
@ -1119,23 +1189,29 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode =
'edit'
);
if (finalData.status === 'failed') {
useCurrentStateStore.getState().actions.setCurrentState({
queries: {
...getCurrentState().queries,
[queryName]: {
...getCurrentState().queries[queryName],
isLoading: false,
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
queries: {
...getCurrentState(_ref.moduleName).queries,
[queryName]: {
...getCurrentState(_ref.moduleName).queries[queryName],
isLoading: false,
},
},
},
});
});
useCurrentStateStore.getState().actions.setErrors({
[queryName]: {
type: 'transformations',
data: finalData,
options: options,
},
});
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setErrors({
[queryName]: {
type: 'transformations',
data: finalData,
options: options,
},
});
resolve(finalData);
onEvent(_self, 'onDataQueryFailure', queryEvents);
return;
@ -1148,61 +1224,69 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode =
duration: notificationDuration,
});
}
useCurrentStateStore.getState().actions.setCurrentState({
queries: {
...getCurrentState().queries,
[queryName]: _.assign(
{
...getCurrentState().queries[queryName],
isLoading: false,
data: finalData,
rawData,
},
query.kind === 'restapi'
? {
request: data.request,
response: data.response,
responseHeaders: data.responseHeaders,
}
: {}
),
},
// Used to generate logs
succededQuery: {
[queryName]: {
type: 'query',
kind: query.kind,
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
queries: {
...getCurrentState(_ref.moduleName).queries,
[queryName]: _.assign(
{
...getCurrentState(_ref.moduleName).queries[queryName],
isLoading: false,
data: finalData,
rawData,
},
query.kind === 'restapi'
? {
request: data.request,
response: data.response,
responseHeaders: data.responseHeaders,
}
: {}
),
},
},
});
// Used to generate logs
succededQuery: {
[queryName]: {
type: 'query',
kind: query.kind,
},
},
});
resolve({ status: 'ok', data: finalData });
onEvent(_self, 'onDataQuerySuccess', queryEvents, mode);
}
})
.catch(({ error }) => {
if (mode !== 'view') toast.error(error ?? 'Unknown error');
useCurrentStateStore.getState().actions.setCurrentState({
queries: {
...getCurrentState().queries,
[queryName]: {
isLoading: false,
useSuperStore
.getState()
.modules[_ref.moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
queries: {
...getCurrentState(_ref.moduleName).queries,
[queryName]: {
isLoading: false,
},
},
},
});
});
resolve({ status: 'failed', message: error });
});
});
}
export function setTablePageIndex(tableId, index) {
export function setTablePageIndex(tableId, index, _ref) {
if (_.isEmpty(tableId)) {
console.log('No table is associated with this event.');
return Promise.resolve();
}
const table = Object.entries(getCurrentState().components).filter((entry) => entry[1].id === tableId)[0][1];
const newPageIndex = resolveReferences(index, getCurrentState());
const table = Object.entries(getCurrentState(_ref.moduleName).components).filter(
(entry) => entry[1].id === tableId
)[0][1];
const newPageIndex = resolveReferences(index, getCurrentState(_ref.moduleName));
table.setPage(newPageIndex ?? 1);
return Promise.resolve();
}
@ -1223,10 +1307,10 @@ for computing component state. It replaces the previous try-catch block with
a more efficient approach, precomputing the parent component types and using
conditional checks for better performance and error handling.*/
export function computeComponentState(components = {}) {
export function computeComponentState(components = {}, moduleName) {
try {
let componentState = {};
const currentComponents = getCurrentState().components;
const currentComponents = getCurrentState(moduleName).components;
// Precompute parent component types
const parentComponentTypes = {};
@ -1263,14 +1347,17 @@ export function computeComponentState(components = {}) {
}
});
useCurrentStateStore.getState().actions.setCurrentState({
components: {
...componentState,
},
});
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
components: {
...componentState,
},
});
return new Promise((resolve) => {
useEditorStore.getState().actions.updateEditorState({
useSuperStore.getState().modules[moduleName].useEditorStore.getState().actions.updateEditorState({
defaultComponentStateComputed: true,
});
resolve();
@ -1294,14 +1381,17 @@ export const getSvgIcon = (key, height = 50, width = 50, iconFile = undefined, s
};
export const debuggerActions = {
error: (errors) => {
useCurrentStateStore.getState().actions.setErrors({
...errors,
});
error: (errors, moduleName) => {
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setErrors({
...errors,
});
},
flush: () => {
useCurrentStateStore.getState().actions.setCurrentState({
flush: (moduleName) => {
useSuperStore.getState().modules[moduleName].useCurrentStateStore.getState().actions.setCurrentState({
errors: {},
});
},
@ -1395,8 +1485,8 @@ export const debuggerActions = {
});
return querySuccesslogs;
},
flushAllLog: () => {
useCurrentStateStore.getState().actions.setCurrentState({
flushAllLog: (moduleName) => {
useSuperStore.getState().modules[moduleName].useCurrentStateStore.getState().actions.setCurrentState({
succededQuery: {},
});
},
@ -1437,7 +1527,8 @@ export const cloneComponents = (
currentPageId,
updateAppDefinition,
isCloning = true,
isCut = false
isCut = false,
moduleName
) => {
if (selectedComponents.length < 1) return getSelectedText();
@ -1504,7 +1595,7 @@ export const cloneComponents = (
}
return new Promise((resolve) => {
useEditorStore.getState().actions.updateEditorState({
useSuperStore.getState().modules[moduleName].useEditorStore.getState().actions.updateEditorState({
currentSidebarTab: 2,
});
resolve();
@ -1814,8 +1905,11 @@ function convertMapSet(obj) {
}
}
export const checkExistingQueryName = (newName) =>
useDataQueriesStore.getState().dataQueries.some((query) => query.name === newName);
export const checkExistingQueryName = (newName, moduleName) =>
useSuperStore
.getState()
.modules[moduleName].useDataQueriesStore.getState()
.dataQueries.some((query) => query.name === newName);
export const runQueries = (queries, _ref) => {
queries.forEach((query) => {
@ -1825,30 +1919,33 @@ export const runQueries = (queries, _ref) => {
});
};
export const computeQueryState = (queries) => {
export const computeQueryState = (queries, moduleName) => {
let queryState = {};
queries.forEach((query) => {
if (query.plugin?.plugin_id) {
queryState[query.name] = {
...query.plugin.manifest_file.data?.source?.exposedVariables,
kind: query.plugin.manifest_file.data.source.kind,
...getCurrentState().queries[query.name],
...getCurrentState(moduleName).queries[query.name],
};
} else {
queryState[query.name] = {
...DataSourceTypes.find((source) => source.kind === query.kind)?.exposedVariables,
kind: DataSourceTypes.find((source) => source.kind === query.kind)?.kind,
...getCurrentState()?.queries[query.name],
...getCurrentState(moduleName)?.queries[query.name],
};
}
});
const hasDiffQueryState = !_.isEqual(getCurrentState()?.queries, queryState);
const hasDiffQueryState = !_.isEqual(getCurrentState(moduleName)?.queries, queryState);
if (hasDiffQueryState) {
useCurrentStateStore.getState().actions.setCurrentState({
queries: {
...queryState,
},
});
useSuperStore
.getState()
.modules[moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
queries: {
...queryState,
},
});
}
};

View file

@ -6,7 +6,7 @@ import JSON5 from 'json5';
import { previewQuery, executeAction } from '@/_helpers/appUtils';
import { toast } from 'react-hot-toast';
import { authenticationService } from '@/_services/authentication.service';
import { useSuperStore } from '../_stores/superStore';
import { useDataQueriesStore } from '@/_stores/dataQueriesStore';
import { getCurrentState } from '@/_stores/currentStateStore';
import { getWorkspaceIdOrSlugFromURL, getSubpath, returnWorkspaceIdIfNeed } from './routes';
@ -418,7 +418,7 @@ export async function executeMultilineJS(
parameters = {},
hasParamSupport = false
) {
const currentState = getCurrentState();
const currentState = getCurrentState(_ref.moduleName);
let result = {},
error = null;
@ -429,7 +429,10 @@ export async function executeMultilineJS(
const actions = generateAppActions(_ref, queryId, mode, isPreview);
const queryDetails = useDataQueriesStore.getState().dataQueries.find((q) => q.id === queryId);
const queryDetails = useSuperStore
.getState()
.modules[_ref.moduleName].useDataQueriesStore.getState()
.dataQueries.find((q) => q.id === queryId);
hasParamSupport = !hasParamSupport ? queryDetails?.options?.hasParamSupport : hasParamSupport;
const defaultParams =
@ -459,7 +462,10 @@ export async function executeMultilineJS(
params = {};
}
const processedParams = {};
const query = useDataQueriesStore.getState().dataQueries.find((q) => q.name === key);
const query = useSuperStore
.getState()
.modules.modules[_ref.moduleName].useDataQueriesStore.getState()
.dataQueries.find((q) => q.name === key);
query.options.parameters?.forEach((arg) => (processedParams[arg.name] = params[arg.name]));
return actions.runQuery(key, processedParams);
},
@ -576,14 +582,17 @@ export const generateAppActions = (_ref, queryId, mode, isPreview = false) => {
: {};
const runQuery = (queryName = '', parameters) => {
const query = useDataQueriesStore.getState().dataQueries.find((query) => {
const isFound = query.name === queryName;
if (isPreview) {
return isFound;
} else {
return isFound && isQueryRunnable(query);
}
});
const query = useSuperStore
.getState()
.modules[_ref.moduleName].useDataQueriesStore.getState()
.dataQueries.find((query) => {
const isFound = query.name === queryName;
if (isPreview) {
return isFound;
} else {
return isFound && isQueryRunnable(query);
}
});
const processedParams = {};
if (_.isEmpty(query) || queryId === query?.id) {

View file

@ -1,132 +1,147 @@
import { appVersionService } from '@/_services';
import { create, zustandDevTools } from './utils';
import { shallow } from 'zustand/shallow';
import { useContext } from 'react';
import { useSuperStore } from './superStore';
import { ModuleContext } from '../_contexts/ModuleContext';
const initialState = {
editingVersion: null,
currentUser: null,
apps: [],
appName: null,
slug: null,
isPublic: null,
isMaintenanceOn: null,
organizationId: null,
currentVersionId: null,
userId: null,
app: {},
components: [],
pages: [],
layouts: [],
events: [],
eventHandlers: [],
appDefinitionDiff: null,
appDiffOptions: {},
isSaving: false,
appId: null,
areOthersOnSameVersionAndPage: false,
appVersionPreviewLink: null,
};
export function createAppDataStore(moduleName) {
const initialState = {
editingVersion: null,
currentUser: null,
apps: [],
appName: null,
slug: null,
isPublic: null,
isMaintenanceOn: null,
organizationId: null,
currentVersionId: null,
userId: null,
app: {},
components: [],
pages: [],
layouts: [],
events: [],
eventHandlers: [],
appDefinitionDiff: null,
appDiffOptions: {},
isSaving: false,
appId: null,
areOthersOnSameVersionAndPage: false,
appVersionPreviewLink: null,
moduleName,
};
return create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
updateEditingVersion: (version) => set(() => ({ editingVersion: version })),
updateApps: (apps) => set(() => ({ apps: apps })),
updateState: (state) => set((prev) => ({ ...prev, ...state })),
updateAppDefinitionDiff: (appDefinitionDiff) => set(() => ({ appDefinitionDiff: appDefinitionDiff })),
updateAppVersion: (appId, versionId, pageId, appDefinitionDiff, isUserSwitchedVersion = false) => {
return new Promise((resolve, reject) => {
get().actions.setIsSaving(true);
const isComponentCutProcess = get().appDiffOptions?.componentCut === true;
export const useAppDataStore = create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
updateEditingVersion: (version) => set(() => ({ editingVersion: version })),
updateApps: (apps) => set(() => ({ apps: apps })),
updateState: (state) => set((prev) => ({ ...prev, ...state })),
updateAppDefinitionDiff: (appDefinitionDiff) => set(() => ({ appDefinitionDiff: appDefinitionDiff })),
updateAppVersion: (appId, versionId, pageId, appDefinitionDiff, isUserSwitchedVersion = false) => {
return new Promise((resolve, reject) => {
useAppDataStore.getState().actions.setIsSaving(true);
const isComponentCutProcess = get().appDiffOptions?.componentCut === true;
appVersionService
.autoSaveApp(
appId,
versionId,
appDefinitionDiff.updateDiff,
appDefinitionDiff.type,
pageId,
appDefinitionDiff.operation,
isUserSwitchedVersion,
isComponentCutProcess
)
.then(() => {
get().actions.setIsSaving(false);
})
.catch((error) => {
get().actions.setIsSaving(false);
reject(error);
})
.finally(() => resolve());
});
},
updateAppVersionEventHandlers: async (events, updateType = 'update') => {
get().actions.setIsSaving(true);
const appId = get().appId;
const versionId = get().currentVersionId;
appVersionService
.autoSaveApp(
appId,
versionId,
appDefinitionDiff.updateDiff,
appDefinitionDiff.type,
pageId,
appDefinitionDiff.operation,
isUserSwitchedVersion,
isComponentCutProcess
)
.then(() => {
useAppDataStore.getState().actions.setIsSaving(false);
})
.catch((error) => {
useAppDataStore.getState().actions.setIsSaving(false);
reject(error);
})
.finally(() => resolve());
});
},
updateAppVersionEventHandlers: async (events, updateType = 'update') => {
useAppDataStore.getState().actions.setIsSaving(true);
const appId = get().appId;
const versionId = get().currentVersionId;
const response = await appVersionService.saveAppVersionEventHandlers(appId, versionId, events, updateType);
const response = await appVersionService.saveAppVersionEventHandlers(appId, versionId, events, updateType);
get().actions.setIsSaving(false);
const updatedEvents = get().events;
useAppDataStore.getState().actions.setIsSaving(false);
const updatedEvents = get().events;
updatedEvents.forEach((e, index) => {
const toUpdate = response.find((r) => r.id === e.id);
if (toUpdate) {
updatedEvents[index] = toUpdate;
}
});
set(() => ({ events: updatedEvents }));
},
createAppVersionEventHandlers: async (event) => {
useAppDataStore.getState().actions.setIsSaving(true);
const appId = get().appId;
const versionId = get().currentVersionId;
const updatedEvents = get().events;
const response = await appVersionService.createAppVersionEventHandler(appId, versionId, event);
useAppDataStore.getState().actions.setIsSaving(false);
updatedEvents.push(response);
set(() => ({ events: updatedEvents }));
},
deleteAppVersionEventHandler: async (eventId) => {
useAppDataStore.getState().actions.setIsSaving(true);
const appId = get().appId;
const versionId = get().currentVersionId;
const updatedEvents = get().events;
const response = await appVersionService.deleteAppVersionEventHandler(appId, versionId, eventId);
useAppDataStore.getState().actions.setIsSaving(false);
if (response?.affected === 1) {
updatedEvents.splice(
updatedEvents.findIndex((e) => e.id === eventId),
1
);
updatedEvents.forEach((e, index) => {
const toUpdate = response.find((r) => r.id === e.id);
if (toUpdate) {
updatedEvents[index] = toUpdate;
}
});
set(() => ({ events: updatedEvents }));
}
},
autoUpdateEventStore: async (versionId) => {
const appId = get().appId;
const response = await appVersionService.findAllEventsWithSourceId(appId, versionId);
},
set(() => ({ events: response }));
createAppVersionEventHandlers: async (event) => {
get().actions.setIsSaving(true);
const appId = get().appId;
const versionId = get().currentVersionId;
const updatedEvents = get().events;
const response = await appVersionService.createAppVersionEventHandler(appId, versionId, event);
get().actions.setIsSaving(false);
updatedEvents.push(response);
set(() => ({ events: updatedEvents }));
},
deleteAppVersionEventHandler: async (eventId) => {
get().actions.setIsSaving(true);
const appId = get().appId;
const versionId = get().currentVersionId;
const updatedEvents = get().events;
const response = await appVersionService.deleteAppVersionEventHandler(appId, versionId, eventId);
get().actions.setIsSaving(false);
if (response?.affected === 1) {
updatedEvents.splice(
updatedEvents.findIndex((e) => e.id === eventId),
1
);
set(() => ({ events: updatedEvents }));
}
},
autoUpdateEventStore: async (versionId) => {
const appId = get().appId;
const response = await appVersionService.findAllEventsWithSourceId(appId, versionId);
set(() => ({ events: response }));
},
setIsSaving: (isSaving) => set(() => ({ isSaving })),
setAppId: (appId) => set(() => ({ appId })),
setAppPreviewLink: (appVersionPreviewLink) => set(() => ({ appVersionPreviewLink })),
},
setIsSaving: (isSaving) => set(() => ({ isSaving })),
setAppId: (appId) => set(() => ({ appId })),
setAppPreviewLink: (appVersionPreviewLink) => set(() => ({ appVersionPreviewLink })),
},
}),
{ name: 'App Data Store' }
)
);
}),
{ name: 'App Data Store' }
)
);
}
export const useAppDataStore = (callback, shallow) => {
const moduleName = useContext(ModuleContext);
if (!moduleName) throw Error('module context not available');
const _useAppDataStore = useSuperStore((state) => state.modules[moduleName].useAppDataStore);
return _useAppDataStore(callback, shallow);
};
export const useEditingVersion = () => useAppDataStore((state) => state.editingVersion, shallow);
export const useIsSaving = () => useAppDataStore((state) => state.isSaving, shallow);

View file

@ -1,33 +1,49 @@
import { create, zustandDevTools } from './utils';
import { useContext } from 'react';
import { useSuperStore } from './superStore';
import { ModuleContext } from '../_contexts/ModuleContext';
const initialState = {
editingVersion: null,
isUserEditingTheVersion: false,
releasedVersionId: null,
isVersionReleased: false,
appVersions: [],
export function createAppVersionStore(moduleName) {
const initialState = {
editingVersion: null,
isUserEditingTheVersion: false,
releasedVersionId: null,
isVersionReleased: false,
appVersions: [],
moduleName,
};
return create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
updateEditingVersion: (version) =>
set({ editingVersion: version, isVersionReleased: get().releasedVersionId === version?.id }),
enableReleasedVersionPopupState: () => set({ isUserEditingTheVersion: true }),
disableReleasedVersionPopupState: () => set({ isUserEditingTheVersion: false }),
updateReleasedVersionId: (versionId) =>
set({
releasedVersionId: versionId,
isVersionReleased: get().editingVersion?.id ? get().editingVersion?.id === versionId : false,
}),
setAppVersions: (versions) => set({ appVersions: versions }),
},
}),
{ name: 'App Version Manager Store' }
)
);
}
export const useAppVersionStore = (callback, shallow) => {
const moduleName = useContext(ModuleContext);
if (!moduleName) throw Error('module context not available');
const _useAppVersionStore = useSuperStore((state) => state.modules[moduleName].useAppVersionStore);
return _useAppVersionStore(callback, shallow);
};
export const useAppVersionStore = create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
updateEditingVersion: (version) =>
set({ editingVersion: version, isVersionReleased: get().releasedVersionId === version?.id }),
enableReleasedVersionPopupState: () => set({ isUserEditingTheVersion: true }),
disableReleasedVersionPopupState: () => set({ isUserEditingTheVersion: false }),
updateReleasedVersionId: (versionId) =>
set({
releasedVersionId: versionId,
isVersionReleased: get().editingVersion?.id ? get().editingVersion?.id === versionId : false,
}),
setAppVersions: (versions) => set({ appVersions: versions }),
},
}),
{ name: 'App Version Manager Store' }
)
);
export const useAppVersionActions = () => useAppVersionStore((state) => state.actions);
export const useAppVersionState = () => useAppVersionStore((state) => state);

View file

@ -1,41 +1,57 @@
import { shallow } from 'zustand/shallow';
import { create, zustandDevTools } from './utils';
import { omit } from 'lodash';
import { useContext } from 'react';
import { useSuperStore } from './superStore';
import { ModuleContext } from '../_contexts/ModuleContext';
const initialState = {
queries: {},
components: {},
globals: {
theme: { name: 'light' },
urlparams: null,
},
errors: {},
variables: {},
client: {},
server: {},
page: {
handle: '',
export function createCurrentStateStore(moduleName) {
const initialState = {
queries: {},
components: {},
globals: {
theme: { name: 'light' },
urlparams: null,
},
errors: {},
variables: {},
},
succededQuery: {},
};
client: {},
server: {},
page: {
handle: '',
variables: {},
},
succededQuery: {},
moduleName,
};
export const useCurrentStateStore = create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
setCurrentState: (currentState) => {
set({ ...currentState }, false, { type: 'SET_CURRENT_STATE', currentState });
return create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
setCurrentState: (currentState) => {
set({ ...currentState }, false, { type: 'SET_CURRENT_STATE', currentState });
},
setErrors: (error) => {
set({ errors: { ...get().errors, ...error } }, false, { type: 'SET_ERRORS', error });
},
},
setErrors: (error) => {
set({ errors: { ...get().errors, ...error } }, false, { type: 'SET_ERRORS', error });
},
},
}),
{ name: 'Current State' }
)
);
}),
{ name: 'Current State' }
)
);
}
export const useCurrentStateStore = (callback, shallow) => {
const moduleName = useContext(ModuleContext);
if (!moduleName) throw Error('module context not available');
const _useCurrentStateStore = useSuperStore((state) => state.modules[moduleName].useCurrentStateStore);
return _useCurrentStateStore(callback, shallow);
};
export const useCurrentState = () =>
// Omitting 'actions' here because we don't want to expose it to user
@ -55,6 +71,6 @@ export const useCurrentState = () =>
};
}, shallow);
export const getCurrentState = () => {
return omit(useCurrentStateStore.getState(), 'actions');
export const getCurrentState = (moduleName) => {
return omit(useSuperStore.getState().modules[moduleName].useCurrentStateStore.getState(), 'actions');
};

View file

@ -3,353 +3,394 @@ import { getDefaultOptions } from './storeHelper';
import { dataqueryService } from '@/_services';
// import debounce from 'lodash/debounce';
import { useAppDataStore } from '@/_stores/appDataStore';
import { useQueryPanelStore } from '@/_stores/queryPanelStore';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { runQueries } from '@/_helpers/appUtils';
import { v4 as uuidv4 } from 'uuid';
import { toast } from 'react-hot-toast';
import _, { isEmpty, throttle } from 'lodash';
import { useEditorStore } from './editorStore';
import { useSuperStore } from './superStore';
import { shallow } from 'zustand/shallow';
import { useCurrentStateStore } from './currentStateStore';
import { useContext } from 'react';
import { ModuleContext } from '../_contexts/ModuleContext';
const initialState = {
dataQueries: [],
sortBy: 'updated_at',
sortOrder: 'desc',
loadingDataQueries: true,
isDeletingQueryInProcess: false,
/** TODO: Below two params are primarily used only for websocket invocation post update. Can be removed onece websocket logic is revamped */
// isCreatingQueryInProcess: false,
creatingQueryInProcessId: null,
isUpdatingQueryInProcess: false,
/** When a 'Create Data Query' operation is in progress, rename/update API calls are cached in the variable. */
queuedActions: {},
};
export function createDataQueriesStore(moduleName) {
const initialState = {
dataQueries: [],
sortBy: 'updated_at',
sortOrder: 'desc',
loadingDataQueries: true,
isDeletingQueryInProcess: false,
/** TODO: Below two params are primarily used only for websocket invocation post update. Can be removed onece websocket logic is revamped */
// isCreatingQueryInProcess: false,
creatingQueryInProcessId: null,
isUpdatingQueryInProcess: false,
/** When a 'Create Data Query' operation is in progress, rename/update API calls are cached in the variable. */
queuedActions: {},
moduleName, // TODOS: change this
};
export const useDataQueriesStore = create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
// TODO: Remove editor state while changing currentState
fetchDataQueries: async (appVersionId, selectFirstQuery = false, runQueriesOnAppLoad = false, ref) => {
set({ loadingDataQueries: true });
const data = await dataqueryService.getAll(appVersionId);
set((state) => ({
dataQueries: sortByAttribute(data.data_queries, state.sortBy, state.sortOrder),
loadingDataQueries: false,
}));
return create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
// TODO: Remove editor state while changing currentState
fetchDataQueries: async (appVersionId, selectFirstQuery = false, runQueriesOnAppLoad = false, ref) => {
set({ loadingDataQueries: true });
const data = await dataqueryService.getAll(appVersionId);
set((state) => ({
dataQueries: sortByAttribute(data.data_queries, state.sortBy, state.sortOrder),
loadingDataQueries: false,
}));
if (data.data_queries.length !== 0) {
const queryConfirmationList = [];
const updatedQueries = {};
const currentQueries = useCurrentStateStore.getState().queries;
if (data.data_queries.length !== 0) {
const queryConfirmationList = [];
const updatedQueries = {};
const currentQueries = useSuperStore
.getState()
.modules[get().moduleName].useCurrentStateStore.getState().queries;
data.data_queries.forEach(({ id, name, options }) => {
updatedQueries[name] = _.merge(currentQueries[name], { id: id });
if (options && options?.requestConfirmation && options?.runOnPageLoad) {
queryConfirmationList.push({ queryId: id, queryName: name });
data.data_queries.forEach(({ id, name, options }) => {
updatedQueries[name] = _.merge(currentQueries[name], { id: id });
if (options && options?.requestConfirmation && options?.runOnPageLoad) {
queryConfirmationList.push({ queryId: id, queryName: name });
}
});
if (queryConfirmationList.length !== 0) {
useSuperStore
.getState()
.modules[get().moduleName].useEditorStore.getState()
.actions.updateQueryConfirmationList(queryConfirmationList);
}
});
if (queryConfirmationList.length !== 0) {
useEditorStore.getState().actions.updateQueryConfirmationList(queryConfirmationList);
useSuperStore
.getState()
.modules[get().moduleName].useCurrentStateStore.getState()
.actions.setCurrentState({
...useSuperStore.getState().modules[get().moduleName].useCurrentStateStore.getState(),
queries: updatedQueries,
});
}
useCurrentStateStore.getState().actions.setCurrentState({
...useCurrentStateStore.getState(),
queries: updatedQueries,
});
}
// Compute query state to be added in the current state
const { actions, selectedQuery } = useSuperStore
.getState()
.modules[get().moduleName].useQueryPanelStore.getState();
if (selectFirstQuery) {
actions.setSelectedQuery(data.data_queries[0]?.id, data.data_queries[0]);
} else if (selectedQuery?.id) {
const query = data.data_queries.find((query) => query.id === selectedQuery?.id);
actions.setSelectedQuery(query?.id);
}
// Compute query state to be added in the current state
const { actions, selectedQuery } = useQueryPanelStore.getState();
if (selectFirstQuery) {
actions.setSelectedQuery(data.data_queries[0]?.id, data.data_queries[0]);
} else if (selectedQuery?.id) {
const query = data.data_queries.find((query) => query.id === selectedQuery?.id);
actions.setSelectedQuery(query?.id);
}
// Runs query on loading application
if (runQueriesOnAppLoad) runQueries(data.data_queries, ref);
},
setDataQueries: (dataQueries) => set({ dataQueries }),
deleteDataQueries: (queryId) => {
set({ isDeletingQueryInProcess: true });
useAppDataStore.getState().actions.setIsSaving(true);
dataqueryService
.del(queryId)
.then(() => {
const { actions } = useQueryPanelStore.getState();
const { dataQueries } = useDataQueriesStore.getState();
const newSelectedQuery = dataQueries.find((query) => query.id !== queryId);
actions.setSelectedQuery(newSelectedQuery?.id || null);
if (!newSelectedQuery?.id) {
actions.setSelectedDataSource(null);
}
set((state) => ({
isDeletingQueryInProcess: false,
dataQueries: state.dataQueries.filter((query) => query.id !== queryId),
}));
})
.catch(() => {
set({
isDeletingQueryInProcess: false,
});
})
.finally(() => useAppDataStore.getState().actions.setIsSaving(false));
},
updateDataQuery: (options) => {
set({ isUpdatingQueryInProcess: true });
const { actions, selectedQuery } = useQueryPanelStore.getState();
set((state) => ({
isUpdatingQueryInProcess: false,
dataQueries: state.dataQueries.map((query) => {
if (query.id === selectedQuery.id) {
return {
...query,
options: { ...options },
};
}
return query;
}),
}));
actions.setSelectedQuery(selectedQuery.id);
},
// createDataQuery: (appId, appVersionId, options, kind, name, selectedDataSource, shouldRunQuery) => {
createDataQuery: (selectedDataSource, shouldRunQuery) => {
const appVersionId = useAppVersionStore.getState().editingVersion?.id;
const appId = useAppDataStore.getState().appId;
const { options, name } = getDefaultOptions(selectedDataSource);
const kind = selectedDataSource.kind;
const tempId = uuidv4();
set({ creatingQueryInProcessId: tempId });
const { actions, selectedQuery } = useQueryPanelStore.getState();
const dataSourceId = selectedDataSource?.id !== 'null' ? selectedDataSource?.id : null;
const pluginId = selectedDataSource.pluginId || selectedDataSource.plugin_id;
useAppDataStore.getState().actions.setIsSaving(true);
const { dataQueries } = useDataQueriesStore.getState();
const currDataQueries = [...dataQueries];
set(() => ({
dataQueries: [
{
...selectedQuery,
data_source_id: dataSourceId,
app_version_id: appVersionId,
options,
name,
kind,
id: tempId,
plugin: selectedDataSource.plugin,
},
...currDataQueries,
],
}));
actions.setSelectedQuery(tempId);
actions.setNameInputFocussed(true);
dataqueryService
.create(appId, appVersionId, name, kind, options, dataSourceId, pluginId)
.then((data) => {
set((state) => ({
creatingQueryInProcessId: null,
dataQueries: state.dataQueries.map((query) => {
if (query.id === tempId) {
return {
...query,
id: data.id,
data_source_id: dataSourceId,
};
}
return query;
}),
}));
actions.setSelectedQuery(data.id, data);
if (shouldRunQuery) actions.setQueryToBeRun(data);
/** Checks if there is an API call cached. If yes execute it */
if (!isEmpty(get()?.queuedActions?.renameQuery)) {
get().actions.renameQuery(data.id, get().queuedActions.renameQuery);
set({ queuedActions: { ...get().queuedActions, renameQuery: undefined } });
}
if (!isEmpty(get()?.queuedActions?.saveData)) {
get().actions.saveData({ ...get().queuedActions.saveData, id: data.id });
set({ queuedActions: { ...get().queuedActions, saveData: undefined } });
}
})
.catch((error) => {
set((state) => ({
creatingQueryInProcessId: null,
dataQueries: state.dataQueries.filter((query) => query.id !== tempId),
}));
actions.setSelectedQuery(null);
toast.error(`Failed to create query: ${error.message}`);
})
.finally(() => useAppDataStore.getState().actions.setIsSaving(false));
},
renameQuery: (id, newName) => {
/** If query creation in progress, skips call and pushes the update to queue */
if (get().creatingQueryInProcessId === id) {
set({ queuedActions: { ...get().queuedActions, renameQuery: newName } });
return;
}
useAppDataStore.getState().actions.setIsSaving(true);
/**
* Seting name to store before api call for instant UI update and better UX.
* Name is again set to state post api call to handle if renaming fails in backend.
* */
set((state) => ({
dataQueries: state.dataQueries.map((query) => (query.id === id ? { ...query, name: newName } : query)),
}));
dataqueryService
.update(id, newName)
.then((data) => {
set((state) => ({
dataQueries: state.dataQueries.map((query) => {
if (query.id === id) {
return { ...query, name: newName, updated_at: data.updated_at };
}
return query;
}),
}));
useQueryPanelStore.getState().actions.setSelectedQuery(id);
})
.finally(() => useAppDataStore.getState().actions.setIsSaving(false));
},
changeDataQuery: (newDataSource) => {
const { selectedQuery } = useQueryPanelStore.getState();
set({
isUpdatingQueryInProcess: true,
});
useAppDataStore.getState().actions.setIsSaving(true);
dataqueryService
.changeQueryDataSource(selectedQuery?.id, newDataSource.id)
.then(() => {
set((state) => ({
isUpdatingQueryInProcess: false,
dataQueries: state.dataQueries.map((query) => {
if (query?.id === selectedQuery?.id) {
return { ...query, dataSourceId: newDataSource?.id, data_source_id: newDataSource?.id };
}
return query;
}),
}));
useQueryPanelStore.getState().actions.setSelectedQuery(selectedQuery.id);
useQueryPanelStore.getState().actions.setSelectedDataSource(newDataSource);
})
.catch(() => {
set({
isUpdatingQueryInProcess: false,
});
})
.finally(() => useAppDataStore.getState().actions.setIsSaving(false));
},
duplicateQuery: (id, appId) => {
set({ creatingQueryInProcessId: uuidv4() });
const { actions } = useQueryPanelStore.getState();
const { dataQueries } = useDataQueriesStore.getState();
const queryToClone = { ...dataQueries.find((query) => query.id === id) };
let newName = queryToClone.name + '_copy';
const names = dataQueries.map(({ name }) => name);
let count = 0;
while (names.includes(newName)) {
count++;
newName = queryToClone.name + '_copy' + count.toString();
}
queryToClone.name = newName;
useAppDataStore.getState().actions.setIsSaving(true);
dataqueryService
.create(
appId,
queryToClone.app_version_id,
queryToClone.name,
queryToClone.kind,
queryToClone.options,
queryToClone.data_source_id,
queryToClone.pluginId
)
.then((data) => {
set((state) => ({
creatingQueryInProcessId: null,
dataQueries: [{ ...data, data_source_id: queryToClone.data_source_id }, ...state.dataQueries],
}));
actions.setSelectedQuery(data.id, { ...data, data_source_id: queryToClone.data_source_id });
const dataQueryEvents = useAppDataStore
.getState()
.events?.filter((event) => event.target === 'data_query' && event.sourceId === queryToClone.id);
if (dataQueryEvents?.length === 0) return;
return Promise.all(
dataQueryEvents.map((event) => {
const newEvent = {
event: {
...event?.event,
},
eventType: event?.target,
attachedTo: data.id,
index: event?.index,
};
useAppDataStore.getState().actions?.createAppVersionEventHandlers(newEvent);
})
// Runs query on loading application
if (runQueriesOnAppLoad) runQueries(data.data_queries, ref);
},
setDataQueries: (dataQueries) => set({ dataQueries }),
deleteDataQueries: (queryId) => {
set({ isDeletingQueryInProcess: true });
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(true);
dataqueryService
.del(queryId)
.then(() => {
const { actions } = useSuperStore.getState().modules[get().moduleName].useQueryPanelStore.getState();
const { dataQueries } = get();
const newSelectedQuery = dataQueries.find((query) => query.id !== queryId);
actions.setSelectedQuery(newSelectedQuery?.id || null);
if (!newSelectedQuery?.id) {
actions.setSelectedDataSource(null);
}
set((state) => ({
isDeletingQueryInProcess: false,
dataQueries: state.dataQueries.filter((query) => query.id !== queryId),
}));
})
.catch(() => {
set({
isDeletingQueryInProcess: false,
});
})
.finally(() =>
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(false)
);
})
.catch((error) => {
console.error('error', error);
set({
creatingQueryInProcessId: null,
});
})
.finally(() => useAppDataStore.getState().actions.setIsSaving(false));
},
updateDataQuery: (options) => {
set({ isUpdatingQueryInProcess: true });
const { actions, selectedQuery } = useSuperStore
.getState()
.modules[get().moduleName].useQueryPanelStore.getState();
set((state) => ({
isUpdatingQueryInProcess: false,
dataQueries: state.dataQueries.map((query) => {
if (query.id === selectedQuery.id) {
return {
...query,
options: { ...options },
};
}
return query;
}),
}));
actions.setSelectedQuery(selectedQuery.id);
},
// createDataQuery: (appId, appVersionId, options, kind, name, selectedDataSource, shouldRunQuery) => {
createDataQuery: (selectedDataSource, shouldRunQuery) => {
const appVersionId = useSuperStore.getState().modules[get().moduleName].useAppVersionStore.getState()
.editingVersion?.id;
const appId = useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().appId;
const { options, name } = getDefaultOptions(selectedDataSource, get().moduleName);
const kind = selectedDataSource.kind;
const tempId = uuidv4();
set({ creatingQueryInProcessId: tempId });
const { actions, selectedQuery } = useSuperStore
.getState()
.modules[get().moduleName].useQueryPanelStore.getState();
const dataSourceId = selectedDataSource?.id !== 'null' ? selectedDataSource?.id : null;
const pluginId = selectedDataSource.pluginId || selectedDataSource.plugin_id;
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(true);
const { dataQueries } = get();
const currDataQueries = [...dataQueries];
set(() => ({
dataQueries: [
{
...selectedQuery,
data_source_id: dataSourceId,
app_version_id: appVersionId,
options,
name,
kind,
id: tempId,
plugin: selectedDataSource.plugin,
},
...currDataQueries,
],
}));
actions.setSelectedQuery(tempId);
actions.setNameInputFocussed(true);
dataqueryService
.create(appId, appVersionId, name, kind, options, dataSourceId, pluginId)
.then((data) => {
set((state) => ({
creatingQueryInProcessId: null,
dataQueries: state.dataQueries.map((query) => {
if (query.id === tempId) {
return {
...query,
id: data.id,
data_source_id: dataSourceId,
};
}
return query;
}),
}));
actions.setSelectedQuery(data.id, data);
if (shouldRunQuery) actions.setQueryToBeRun(data);
/** Checks if there is an API call cached. If yes execute it */
if (!isEmpty(get()?.queuedActions?.renameQuery)) {
get().actions.renameQuery(data.id, get().queuedActions.renameQuery);
set({ queuedActions: { ...get().queuedActions, renameQuery: undefined } });
}
if (!isEmpty(get()?.queuedActions?.saveData)) {
get().actions.saveData({ ...get().queuedActions.saveData, id: data.id });
set({ queuedActions: { ...get().queuedActions, saveData: undefined } });
}
})
.catch((error) => {
set((state) => ({
creatingQueryInProcessId: null,
dataQueries: state.dataQueries.filter((query) => query.id !== tempId),
}));
actions.setSelectedQuery(null);
toast.error(`Failed to create query: ${error.message}`);
})
.finally(() =>
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(false)
);
},
renameQuery: (id, newName) => {
/** If query creation in progress, skips call and pushes the update to queue */
if (get().creatingQueryInProcessId === id) {
set({ queuedActions: { ...get().queuedActions, renameQuery: newName } });
return;
}
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(true);
/**
* Seting name to store before api call for instant UI update and better UX.
* Name is again set to state post api call to handle if renaming fails in backend.
* */
set((state) => ({
dataQueries: state.dataQueries.map((query) => (query.id === id ? { ...query, name: newName } : query)),
}));
dataqueryService
.update(id, newName)
.then((data) => {
set((state) => ({
dataQueries: state.dataQueries.map((query) => {
if (query.id === id) {
return { ...query, name: newName, updated_at: data.updated_at };
}
return query;
}),
}));
useSuperStore
.getState()
.modules[get().moduleName].useQueryPanelStore.getState()
.actions.setSelectedQuery(id);
})
.finally(() =>
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(false)
);
},
changeDataQuery: (newDataSource) => {
const { selectedQuery } = useSuperStore.getState().modules[get().moduleName].useQueryPanelStore.getState();
set({
isUpdatingQueryInProcess: true,
});
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(true);
dataqueryService
.changeQueryDataSource(selectedQuery?.id, newDataSource.id)
.then(() => {
set((state) => ({
isUpdatingQueryInProcess: false,
dataQueries: state.dataQueries.map((query) => {
if (query?.id === selectedQuery?.id) {
return { ...query, dataSourceId: newDataSource?.id, data_source_id: newDataSource?.id };
}
return query;
}),
}));
useSuperStore
.getState()
.modules[get().moduleName].useQueryPanelStore.getState()
.actions.setSelectedQuery(selectedQuery.id);
useSuperStore
.getState()
.modules[get().moduleName].useQueryPanelStore.getState()
.actions.setSelectedDataSource(newDataSource);
})
.catch(() => {
set({
isUpdatingQueryInProcess: false,
});
})
.finally(() =>
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(false)
);
},
duplicateQuery: (id, appId) => {
set({ creatingQueryInProcessId: uuidv4() });
const { actions } = useSuperStore.getState().modules[get().moduleName].useQueryPanelStore.getState();
const { dataQueries } = get();
const queryToClone = { ...dataQueries.find((query) => query.id === id) };
let newName = queryToClone.name + '_copy';
const names = dataQueries.map(({ name }) => name);
let count = 0;
while (names.includes(newName)) {
count++;
newName = queryToClone.name + '_copy' + count.toString();
}
queryToClone.name = newName;
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(true);
dataqueryService
.create(
appId,
queryToClone.app_version_id,
queryToClone.name,
queryToClone.kind,
queryToClone.options,
queryToClone.data_source_id,
queryToClone.pluginId
)
.then((data) => {
set((state) => ({
creatingQueryInProcessId: null,
dataQueries: [{ ...data, data_source_id: queryToClone.data_source_id }, ...state.dataQueries],
}));
actions.setSelectedQuery(data.id, { ...data, data_source_id: queryToClone.data_source_id });
const dataQueryEvents = useAppDataStore
.getState()
.events?.filter((event) => event.target === 'data_query' && event.sourceId === queryToClone.id);
if (dataQueryEvents?.length === 0) return;
return Promise.all(
dataQueryEvents.map((event) => {
const newEvent = {
event: {
...event?.event,
},
eventType: event?.target,
attachedTo: data.id,
index: event?.index,
};
useSuperStore
.getState()
.modules[get().moduleName].useAppDataStore.getState()
.actions?.createAppVersionEventHandlers(newEvent);
})
);
})
.catch((error) => {
console.error('error', error);
set({
creatingQueryInProcessId: null,
});
})
.finally(() =>
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(false)
);
},
saveData: throttle((newValues) => {
/** If query creation in progress, skips call and pushes the update to queue */
if (get().creatingQueryInProcessId && get().creatingQueryInProcessId === newValues.id) {
set({ queuedActions: { ...get().queuedActions, saveData: newValues } });
return;
}
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(true);
set({ isUpdatingQueryInProcess: true });
dataqueryService
.update(newValues?.id, newValues?.name, newValues?.options)
.then((data) => {
localStorage.removeItem('transformation');
set((state) => ({
dataQueries: state.dataQueries.map((query) => {
if (query.id === newValues?.id) {
return { ...query, updated_at: data.updated_at };
}
return query;
}),
isUpdatingQueryInProcess: false,
}));
})
.catch(() => {
set({
isUpdatingQueryInProcess: false,
});
})
.finally(() =>
useSuperStore.getState().modules[get().moduleName].useAppDataStore.getState().actions.setIsSaving(false)
);
}, 500),
sortDataQueries: (sortBy, sortOrder) => {
set(({ dataQueries, sortOrder: currSortOrder }) => {
const newSortOrder = sortOrder ? sortOrder : currSortOrder === 'asc' ? 'desc' : 'asc';
return {
sortBy,
sortOrder: newSortOrder,
dataQueries: sortByAttribute(dataQueries, sortBy, newSortOrder),
};
});
},
},
saveData: throttle((newValues) => {
/** If query creation in progress, skips call and pushes the update to queue */
if (get().creatingQueryInProcessId && get().creatingQueryInProcessId === newValues.id) {
set({ queuedActions: { ...get().queuedActions, saveData: newValues } });
return;
}
useAppDataStore.getState().actions.setIsSaving(true);
set({ isUpdatingQueryInProcess: true });
dataqueryService
.update(newValues?.id, newValues?.name, newValues?.options)
.then((data) => {
localStorage.removeItem('transformation');
set((state) => ({
dataQueries: state.dataQueries.map((query) => {
if (query.id === newValues?.id) {
return { ...query, updated_at: data.updated_at };
}
return query;
}),
isUpdatingQueryInProcess: false,
}));
})
.catch(() => {
set({
isUpdatingQueryInProcess: false,
});
})
.finally(() => useAppDataStore.getState().actions.setIsSaving(false));
}, 500),
sortDataQueries: (sortBy, sortOrder) => {
set(({ dataQueries, sortOrder: currSortOrder }) => {
const newSortOrder = sortOrder ? sortOrder : currSortOrder === 'asc' ? 'desc' : 'asc';
return {
sortBy,
sortOrder: newSortOrder,
dataQueries: sortByAttribute(dataQueries, sortBy, newSortOrder),
};
});
},
},
}),
{ name: 'Data Queries Store' }
)
);
}),
{ name: 'Data Queries Store' }
)
);
}
const sortByAttribute = (data, sortBy, order) => {
if (order === 'asc') {
@ -360,6 +401,19 @@ const sortByAttribute = (data, sortBy, order) => {
}
};
export const useDataQueriesStore = (callback, shallow) => {
const moduleName = useContext(ModuleContext);
if (!moduleName)
throw Error(
'useDataQueriesStore can only be called inside Module context. (hint: Wrap with ModuleContext.Provider)'
);
const _useDataQueriesStore = useSuperStore((state) => state.modules[moduleName].useDataQueriesStore);
return _useDataQueriesStore(callback, shallow);
};
export const useDataQueries = () => useDataQueriesStore((state) => state.dataQueries, shallow);
export const useDataQueriesActions = () => useDataQueriesStore((state) => state.actions);
export const useQueryCreationLoading = () => useDataQueriesStore((state) => !!state.creatingQueryInProcessId, shallow);

View file

@ -1,53 +1,69 @@
import { create, zustandDevTools } from './utils';
import { datasourceService, globalDatasourceService } from '@/_services';
import { useContext } from 'react';
import { useSuperStore } from './superStore';
import { ModuleContext } from '../_contexts/ModuleContext';
const initialState = {
dataSources: [],
loadingDataSources: true,
globalDataSources: [],
globalDataSourceStatus: {
isSaving: false,
isEditing: false,
unSavedModalVisible: false,
action: null,
saveAction: null,
},
export function createDataSourcesStore(moduleName) {
const initialState = {
dataSources: [],
loadingDataSources: true,
globalDataSources: [],
globalDataSourceStatus: {
isSaving: false,
isEditing: false,
unSavedModalVisible: false,
action: null,
saveAction: null,
},
moduleName,
};
return create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
fetchDataSources: (appId) => {
set({ loadingDataSources: true });
datasourceService.getAll(appId).then((data) => {
set({
dataSources: data.data_sources,
loadingDataSources: false,
});
});
},
fetchGlobalDataSources: (organizationId) => {
globalDatasourceService.getAll(organizationId).then((data) => {
set({
globalDataSources: data.data_sources,
});
});
},
setGlobalDataSourceStatus: (status) =>
set({
globalDataSourceStatus: {
...get().globalDataSourceStatus,
...status,
},
}),
},
}),
{ name: 'Data Source Store' }
)
);
}
export const useDataSourcesStore = (callback, shallow) => {
const moduleName = useContext(ModuleContext);
if (!moduleName) throw Error('module context not available');
const _useDataSourcesStore = useSuperStore((state) => state.modules[moduleName].useDataSourcesStore);
return _useDataSourcesStore(callback, shallow);
};
export const useDataSourcesStore = create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
fetchDataSources: (appId) => {
set({ loadingDataSources: true });
datasourceService.getAll(appId).then((data) => {
set({
dataSources: data.data_sources,
loadingDataSources: false,
});
});
},
fetchGlobalDataSources: (organizationId) => {
globalDatasourceService.getAll(organizationId).then((data) => {
set({
globalDataSources: data.data_sources,
});
});
},
setGlobalDataSourceStatus: (status) =>
set({
globalDataSourceStatus: {
...get().globalDataSourceStatus,
...status,
},
}),
},
}),
{ name: 'Data Source Store' }
)
);
export const useDataSources = () => useDataSourcesStore((state) => state.dataSources);
export const useGlobalDataSources = () => useDataSourcesStore((state) => state.globalDataSources);
export const useLoadingDataSources = () => useDataSourcesStore((state) => state.loadingDataSources);

View file

@ -1,5 +1,8 @@
import { create } from './utils';
import { v4 as uuid } from 'uuid';
import { useContext } from 'react';
import { useSuperStore } from './superStore';
import { ModuleContext } from '../_contexts/ModuleContext';
const STORE_NAME = 'Editor';
export const EMPTY_ARRAY = [];
@ -14,84 +17,97 @@ const ACTIONS = {
SET_IS_EDITOR_ACTIVE: 'SET_IS_EDITOR_ACTIVE',
};
const initialState = {
currentLayout: 'desktop',
showComments: false,
hoveredComponent: '',
selectionInProgress: false,
selectedComponents: EMPTY_ARRAY,
isEditorActive: false,
selectedComponent: null,
scrollOptions: {
container: null,
throttleTime: 0,
threshold: 0,
},
canUndo: false,
canRedo: false,
currentVersion: {},
noOfVersionsSupported: 100,
appDefinition: {},
isUpdatingEditorStateInProcess: false,
saveError: false,
isLoading: true,
defaultComponentStateComputed: false,
showLeftSidebar: true,
queryConfirmationList: [],
currentPageId: null,
currentSessionId: uuid(),
};
export const useEditorStore = create(
//Redux Dev tools for this store are disabled 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 }
);
},
setSelectedComponents: (selectedComponents, isMulti = false) => {
const newSelectedComponents = isMulti
? [...get().selectedComponents, ...selectedComponents]
: selectedComponents;
set({
selectedComponents: newSelectedComponents,
});
},
setCurrentPageId: (currentPageId) => set({ currentPageId }),
export function createEditorStore(moduleName) {
const initialState = {
currentLayout: 'desktop',
showComments: false,
hoveredComponent: '',
selectionInProgress: false,
selectedComponents: [],
isEditorActive: false,
selectedComponent: null,
scrollOptions: {
container: null,
throttleTime: 0,
threshold: 0,
},
}),
{ name: STORE_NAME }
);
canUndo: false,
canRedo: false,
currentVersion: {},
noOfVersionsSupported: 100,
appDefinition: {},
isUpdatingEditorStateInProcess: false,
saveError: false,
isLoading: true,
defaultComponentStateComputed: false,
showLeftSidebar: true,
queryConfirmationList: [],
currentPageId: null,
currentSessionId: uuid(),
moduleName,
};
return create(
//Redux Dev tools for this store are disabled 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 }
);
},
setSelectedComponents: (selectedComponents, isMulti = false) => {
const newSelectedComponents = isMulti
? [...get().selectedComponents, ...selectedComponents]
: selectedComponents;
set({
selectedComponents: newSelectedComponents,
});
},
setCurrentPageId: (currentPageId) => set({ currentPageId }),
},
}),
{ name: STORE_NAME }
);
}
export const useEditorStore = (callback, shallow) => {
const moduleName = useContext(ModuleContext);
if (!moduleName) throw Error('module context not available');
const _useEditorStore = useSuperStore((state) => state.modules[moduleName].useEditorStore);
return _useEditorStore(callback, shallow);
};
export const useEditorActions = () => useEditorStore((state) => state.actions);
export const useEditorState = () => useEditorStore((state) => state);

View file

@ -1,45 +1,67 @@
import { create, zustandDevTools } from './utils';
import { useContext } from 'react';
import { ModuleContext } from '../_contexts/ModuleContext';
import { useDataQueriesStore } from '@/_stores/dataQueriesStore';
import { useSuperStore } from '@/_stores/superStore';
const queryManagerPreferences = JSON.parse(localStorage.getItem('queryManagerPreferences')) ?? {};
const initialState = {
queryPanelHeight: queryManagerPreferences?.isExpanded ? queryManagerPreferences?.queryPanelHeight : 95 ?? 70,
selectedQuery: null,
selectedDataSource: null,
queryToBeRun: null,
previewLoading: false,
queryPreviewData: '',
showCreateQuery: false,
nameInputFocussed: false,
};
export function createQueryPanelStore(moduleName) {
const queryManagerPreferences = JSON.parse(localStorage.getItem('queryManagerPreferences')) ?? {};
const initialState = {
queryPanelHeight: queryManagerPreferences?.isExpanded ? queryManagerPreferences?.queryPanelHeight : 95 ?? 70,
selectedQuery: null,
selectedDataSource: null,
queryToBeRun: null,
previewLoading: false,
queryPreviewData: '',
showCreateQuery: false,
nameInputFocussed: false,
moduleName,
};
export const useQueryPanelStore = create(
zustandDevTools(
(set) => ({
...initialState,
actions: {
updateQueryPanelHeight: (newHeight) => set(() => ({ queryPanelHeight: newHeight })),
setSelectedQuery: (queryId) => {
set(() => {
if (queryId === null) {
return { selectedQuery: null };
}
const query = useDataQueriesStore.getState().dataQueries.find((query) => query.id === queryId);
return { selectedQuery: query };
});
return create(
zustandDevTools(
(set, get) => ({
...initialState,
actions: {
updateQueryPanelHeight: (newHeight) => set(() => ({ queryPanelHeight: newHeight })),
setSelectedQuery: (queryId) => {
set(() => {
if (queryId === null) {
return { selectedQuery: null };
}
const query = useSuperStore
.getState()
.modules[get().moduleName].useDataQueriesStore.getState()
.dataQueries.find((query) => query.id === queryId);
return { selectedQuery: query };
});
},
setSelectedDataSource: (dataSource = null) => set({ selectedDataSource: dataSource }),
setQueryToBeRun: (query) => set({ queryToBeRun: query }),
setPreviewLoading: (status) => set({ previewLoading: status }),
setPreviewData: (data) => set({ queryPreviewData: data }),
setShowCreateQuery: (showCreateQuery) => set({ showCreateQuery }),
setNameInputFocussed: (nameInputFocussed) => set({ nameInputFocussed }),
},
setSelectedDataSource: (dataSource = null) => set({ selectedDataSource: dataSource }),
setQueryToBeRun: (query) => set({ queryToBeRun: query }),
setPreviewLoading: (status) => set({ previewLoading: status }),
setPreviewData: (data) => set({ queryPreviewData: data }),
setShowCreateQuery: (showCreateQuery) => set({ showCreateQuery }),
setNameInputFocussed: (nameInputFocussed) => set({ nameInputFocussed }),
},
}),
{ name: 'Query Panel Store' }
)
);
}),
{ name: 'Query Panel Store' }
)
);
}
export const useQueryPanelStore = (callback, shallow) => {
const moduleName = useContext(ModuleContext);
if (!moduleName)
throw Error(
'useQueryPanelStore can only be called inside Module context. (hint: Wrap with ModuleContext.Provider)'
);
const _useQueryPanelStore = useSuperStore((state) => state.modules[moduleName].useQueryPanelStore);
return _useQueryPanelStore(callback, shallow);
};
export const usePanelHeight = () => useQueryPanelStore((state) => state.queryPanelHeight);
export const useSelectedQuery = () => useQueryPanelStore((state) => state.selectedQuery);

View file

@ -1,9 +1,9 @@
import { schemaUnavailableOptions } from '@/Editor/QueryManager/constants';
import { allOperations } from '@tooljet/plugins/client';
import { capitalize } from 'lodash';
import { useDataQueriesStore } from '@/_stores/dataQueriesStore';
import { useSuperStore } from './superStore';
export const getDefaultOptions = (source) => {
export const getDefaultOptions = (source, moduleName) => {
const isSchemaUnavailable = Object.keys(schemaUnavailableOptions).includes(source.kind);
let options = {};
@ -36,11 +36,11 @@ export const getDefaultOptions = (source) => {
}
}
return { options, name: computeQueryName(source.kind) };
return { options, name: computeQueryName(source.kind, moduleName) };
};
const computeQueryName = (kind) => {
const dataQueries = useDataQueriesStore.getState().dataQueries;
const computeQueryName = (kind, moduleName) => {
const dataQueries = useSuperStore.getState().modules[moduleName].useDataQueriesStore.getState().dataQueries;
const currentQueriesForKind = dataQueries.filter((query) => query.kind === kind);
let currentNumber = currentQueriesForKind.length + 1;

View file

@ -0,0 +1,44 @@
import { createDataQueriesStore } from './dataQueriesStore';
import { createEditorStore } from './editorStore';
import { createQueryPanelStore } from './queryPanelStore';
import { createDataSourcesStore } from './dataSourcesStore';
import { createCurrentStateStore } from './currentStateStore';
import { create } from './utils';
import { createAppVersionStore } from './appVersionStore';
import { createAppDataStore } from './appDataStore';
import { omit } from 'lodash';
const generateModule = (moduleName) => ({
useEditorStore: createEditorStore(moduleName),
useQueryPanelStore: createQueryPanelStore(moduleName),
useDataQueriesStore: createDataQueriesStore(moduleName),
useDataSourcesStore: createDataSourcesStore(moduleName),
useCurrentStateStore: createCurrentStateStore(moduleName),
useAppVersionStore: createAppVersionStore(moduleName),
useAppDataStore: createAppDataStore(moduleName),
});
const mainModule = generateModule('#main');
export const useSuperStore = create((set, get) => ({
modules: {
'#main': mainModule,
},
createModule: (moduleName) => {
const modules = get().modules;
const newModules = {
...modules,
[moduleName]: generateModule(moduleName),
};
set(() => ({ modules: newModules }));
return true;
},
destroyModule: (moduleName) => {
const modules = get().modules;
const newModules = omit(modules, moduleName);
set(() => ({ modules: newModules }));
},
}));

View file

@ -10,6 +10,7 @@ import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
// import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
import { ModuleContext } from './_contexts/ModuleContext';
const AppWithProfiler = Sentry.withProfiler(App);
@ -62,5 +63,12 @@ appService
});
}
})
.then(() => render(<AppWithProfiler />, document.getElementById('app')));
.then(() =>
render(
<ModuleContext.Provider value={'#main'}>
<AppWithProfiler />
</ModuleContext.Provider>,
document.getElementById('app')
)
);
// .then(() => createRoot(document.getElementById('app')).render(<AppWithProfiler />));

View file

@ -92,6 +92,8 @@ export class AppsControllerV2 {
response['pages'] = pagesForVersion;
response['events'] = eventsForVersion;
console.log({ app });
//! if editing version exists, camelize the definition
if (app.editingVersion && app.editingVersion.definition) {
response['editing_version'] = {