diff --git a/frontend/src/Editor/AppVersionsManager/AppVersionsManager.jsx b/frontend/src/Editor/AppVersionsManager/AppVersionsManager.jsx
index f43445cec5..5db49eed9d 100644
--- a/frontend/src/Editor/AppVersionsManager/AppVersionsManager.jsx
+++ b/frontend/src/Editor/AppVersionsManager/AppVersionsManager.jsx
@@ -7,6 +7,7 @@ import { shallow } from 'zustand/shallow';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { useEditorStore } from '@/_stores/editorStore';
import { useEnvironmentsAndVersionsStore } from '@/_stores/environmentsAndVersionsStore';
+import { useAppDataStore } from '@/_stores/appDataStore';
const appVersionLoadingStatus = Object.freeze({
loading: 'loading',
@@ -85,7 +86,17 @@ const RenderComponent = ({
const darkMode = localStorage.getItem('darkMode') === 'true';
const selectVersion = (id) => {
- appVersionService
+ const currentVersionId = useAppDataStore.getState().currentVersionId;
+
+ const isSameVersionSelected = currentVersionId === id;
+
+ if (isSameVersionSelected) {
+ return toast('You are already editing this version', {
+ icon: '⚠️',
+ });
+ }
+
+ return appVersionService
.getAppVersionData(appId, id)
.then((data) => {
const isCurrentVersionReleased = data.currentVersionId ? true : false;
diff --git a/frontend/src/Editor/CodeEditor/CodeHinter.jsx b/frontend/src/Editor/CodeEditor/CodeHinter.jsx
index 70453a4895..39d615384c 100644
--- a/frontend/src/Editor/CodeEditor/CodeHinter.jsx
+++ b/frontend/src/Editor/CodeEditor/CodeHinter.jsx
@@ -88,8 +88,7 @@ const Portal = ({ children, ...restProps }) => {
const PopupIcon = ({ callback, icon, tip, position, isMultiEditor = false }) => {
const size = 16;
const topRef = isNumber(position?.height) ? Math.floor(position?.height) - 30 : 32;
- let top = isMultiEditor ? 370 : topRef > 32 ? topRef : 0;
-
+ let top = isMultiEditor ? 270 : topRef > 32 ? topRef : 0;
return (
{
+export const PreviewBox = ({ currentValue, validationSchema, setErrorStateActive, componentId, setErrorMessage }) => {
const { variablesExposedForPreview } = useContext(EditorContext);
const customVariables = variablesExposedForPreview?.[componentId] ?? {};
@@ -27,10 +20,8 @@ export const PreviewBox = ({
const [resolvedValue, setResolvedValue] = useState('');
const [error, setError] = useState(null);
const [coersionData, setCoersionData] = useState(null);
-
const getPreviewContent = (content, type) => {
if (!content) return currentValue;
-
try {
switch (type) {
case 'Object':
@@ -75,12 +66,7 @@ export const PreviewBox = ({
}, [error]);
useEffect(() => {
- const [valid, _error, newValue, resolvedValue] = resolveReferences(
- currentValue,
- validationSchema,
- customVariables,
- fxActive
- );
+ const [valid, _error, newValue, resolvedValue] = resolveReferences(currentValue, validationSchema, customVariables);
if (!validationSchema || isEmpty(validationSchema)) {
return setResolvedValue(newValue);
@@ -345,7 +331,6 @@ const PreviewCodeBlock = ({ code, isExpectValue = false }) => {
);
}
-
return (
{
padding: '0',
}}
>
- {prettyPrintedJson}
+ {prettyPrintedJson?.startsWith('{{')
+ ? prettyPrintedJson?.replace(/{{/g, '').replace(/}}/g, '')
+ : prettyPrintedJson}
);
diff --git a/frontend/src/Editor/CodeEditor/SingleLineCodeEditor.jsx b/frontend/src/Editor/CodeEditor/SingleLineCodeEditor.jsx
index 2c3c9403ef..0b620851be 100644
--- a/frontend/src/Editor/CodeEditor/SingleLineCodeEditor.jsx
+++ b/frontend/src/Editor/CodeEditor/SingleLineCodeEditor.jsx
@@ -34,15 +34,9 @@ const SingleLineCodeEditor = ({ suggestions, componentName, fieldMeta = {}, fxAc
useEffect(() => {
if (typeof initialValue !== 'string') return;
-
- if (fxActive && initialValue?.startsWith('{{')) {
- const _value = initialValue?.replace(/{{/g, '').replace(/}}/g, '');
- return setCurrentValue(_value);
- }
-
setCurrentValue(initialValue);
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [componentName, initialValue, fxActive]);
+ }, [componentName, initialValue]);
useEffect(() => {
const handleClickOutside = (event) => {
@@ -80,7 +74,7 @@ const SingleLineCodeEditor = ({ suggestions, componentName, fieldMeta = {}, fxAc
setErrorStateActive={setErrorStateActive}
ignoreValidation={restProps?.ignoreValidation || isEmpty(validation)}
componentId={restProps?.componentId ?? null}
- fxActive={fxActive}
+ // fxActive={fxActive}
isWorkspaceVariable={isWorkspaceVariable}
errorStateActive={errorStateActive}
previewPlacement={restProps?.cyLabel === 'canvas-bg-colour' ? 'top' : 'left-start'}
@@ -99,7 +93,6 @@ const SingleLineCodeEditor = ({ suggestions, componentName, fieldMeta = {}, fxAc
cyLabel={restProps.cyLabel}
portalProps={portalProps}
componentName={componentName}
- fxActive={fxActive}
{...restProps}
/>
@@ -124,7 +117,6 @@ const EditorInput = ({
renderPreview,
portalProps,
ignoreValidation,
- fxActive,
lang,
isFocused,
componentId,
@@ -138,15 +130,15 @@ const EditorInput = ({
if (totalReferences > 1) {
const currentWord = queryInput.split('{{').pop().split('}}')[0];
- queryInput = fxActive ? currentWord : `{{${currentWord}}}`;
+ queryInput = currentWord;
}
- let completions = getAutocompletion(queryInput, validationType, hints, fxActive, totalReferences);
+ let completions = getAutocompletion(queryInput, validationType, hints, totalReferences);
return {
from: word.from,
options: completions,
- validFor: !fxActive ? /^\{\{.*\}\}$/ : '',
+ validFor: /^\{\{.*\}\}$/,
};
}
@@ -168,18 +160,17 @@ const EditorInput = ({
setCurrentValue(val);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
-
const handleOnBlur = React.useCallback(() => {
setFirstTimeFocus(false);
if (ignoreValidation) {
return onBlurUpdate(currentValue);
}
-
- if (!error) {
- const _value = fxActive ? `{{${currentValue}}}` : currentValue;
-
- onBlurUpdate(_value);
- }
+ setTimeout(() => {
+ if (!error || currentValue == '') {
+ const _value = currentValue;
+ onBlurUpdate(_value);
+ }
+ }, 0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentValue, error]);
diff --git a/frontend/src/Editor/CodeEditor/autocompleteExtensionConfig.js b/frontend/src/Editor/CodeEditor/autocompleteExtensionConfig.js
index d06c49a1bf..ab08f79e82 100644
--- a/frontend/src/Editor/CodeEditor/autocompleteExtensionConfig.js
+++ b/frontend/src/Editor/CodeEditor/autocompleteExtensionConfig.js
@@ -1,8 +1,7 @@
-export const getAutocompletion = (input, fieldType, hints, fxActive = false, totalReferences = 1) => {
- if (!fxActive && (!input.startsWith('{{') || !input.endsWith('}}'))) return [];
-
- const actualInput = !fxActive ? input.replace(/{{|}}/g, '') : input;
+export const getAutocompletion = (input, fieldType, hints, totalReferences = 1) => {
+ if (!input.startsWith('{{') || !input.endsWith('}}')) return [];
+ const actualInput = input.replace(/{{|}}/g, '');
let JSLangHints = [];
if (fieldType) {
@@ -50,7 +49,7 @@ export const getAutocompletion = (input, fieldType, hints, fxActive = false, tot
if (autoSuggestionList.length === 0 && !cm.hint.includes(actualInput)) return true;
});
- const suggestions = generateHints([...jsHints, ...autoSuggestionList], fxActive, totalReferences);
+ const suggestions = generateHints([...jsHints, ...autoSuggestionList], totalReferences);
return orderSuggestions(suggestions, fieldType).map((cm, index) => ({ ...cm, boost: 100 - index }));
};
@@ -64,7 +63,7 @@ function orderSuggestions(suggestions, validationType) {
return [...matchingSuggestions, ...otherSuggestions];
}
-export const generateHints = (hints, isFxHinter = false, totalReferences = 1) => {
+export const generateHints = (hints, totalReferences = 1) => {
if (!hints) return [];
const suggestions = hints.map(({ hint, type }) => {
@@ -91,9 +90,7 @@ export const generateHints = (hints, isFxHinter = false, totalReferences = 1) =>
insert: completion.label,
};
- let anchorSelection = isFxHinter
- ? pickedCompletionConfig.insert.length
- : pickedCompletionConfig.insert.length + 2;
+ let anchorSelection = pickedCompletionConfig.insert.length + 2;
if (completion.type === 'js_methods') {
pickedCompletionConfig.from = from;
diff --git a/frontend/src/Editor/CodeEditor/styles.scss b/frontend/src/Editor/CodeEditor/styles.scss
index 3acc3a490c..f032ee08b0 100644
--- a/frontend/src/Editor/CodeEditor/styles.scss
+++ b/frontend/src/Editor/CodeEditor/styles.scss
@@ -24,6 +24,10 @@
}
}
+.cm-widgetBuffer {
+ display: none !important;
+}
+
.cm-base-autocomplete {
@@ -153,18 +157,35 @@
}
}
+.query-manager-sort-filter-popup {
+ .cm-base-autocomplete {
+ position: fixed !important;
+ top: 130px !important;
+ }
+}
+.canvas-codehinter-container {
+ .cm-base-autocomplete {
+ position: fixed !important;
+ top: 500px !important;
+ left: 38px !important;
+ }
+}
.widget-code-editor {
height: 100%;
.cm-content {
- max-width: 220px !important;
+ max-width: 100% !important;
white-space: pre-wrap;
word-wrap: break-word;
}
}
+.cm-placeholder {
+ width: 220px;
+}
+
.code-hinter-wrapper {
.cm-editor {
min-height: 32px;
@@ -224,6 +245,7 @@
.cm-tooltip-autocomplete {
@extend .cm-base-autocomplete;
+ top: 0px !important;
}
}
@@ -277,6 +299,7 @@
overflow-y: auto !important;
overflow-x: hidden !important;
padding: 2px !important;
+ overscroll-behavior: contain;
}
.cm-focused {
@@ -315,8 +338,6 @@
.code-hinter-preview-card-body::-webkit-scrollbar {
width: 4px !important;
- /* for vertical scrollbars */
- ;
}
.code-hinter-preview-card-body::-webkit-scrollbar-track {
diff --git a/frontend/src/Editor/CodeEditor/utils.js b/frontend/src/Editor/CodeEditor/utils.js
index 960e735edf..b068ddb487 100644
--- a/frontend/src/Editor/CodeEditor/utils.js
+++ b/frontend/src/Editor/CodeEditor/utils.js
@@ -112,7 +112,7 @@ const resolveWorkspaceVariables = (query, state) => {
resolvedStr = resolvedStr.replace(serverMatch, 'HiddenEnvironmentVariable');
error = 'Server variables cannot be resolved in the client.';
} else {
- const [resolvedCode, err] = resolveCode(code, state);
+ const [resolvedCode, err] = resolveCode(code);
if (!resolvedCode) {
error = err ? err : `Cannot resolve ${query}`;
@@ -126,7 +126,7 @@ const resolveWorkspaceVariables = (query, state) => {
return [valid, error, resolvedStr];
};
-function resolveCode(code, state, customObjects = {}, withError = true, reservedKeyword, isJsCode) {
+function resolveCode(code, customObjects = {}, withError = true, reservedKeyword, isJsCode) {
let result = '';
let error;
@@ -135,6 +135,7 @@ function resolveCode(code, state, customObjects = {}, withError = true, reserved
error = `Cannot resolve function call ${code}`;
} else {
try {
+ const state = useCurrentStateStore.getState();
const evalFunction = Function(
[
'variables',
@@ -199,8 +200,7 @@ const resolveMultiDynamicReferences = (code, lookupTable) => {
resolvedValue = resolvedValue.replace(variable, res);
} else {
- const currentState = useCurrentStateStore.getState();
- const [resolvedCode] = resolveCode(variableToResolve, currentState, {}, true, [], true);
+ const [resolvedCode] = resolveCode(variableToResolve, {}, true, [], true);
resolvedValue = resolvedCode;
}
@@ -210,40 +210,40 @@ const resolveMultiDynamicReferences = (code, lookupTable) => {
return resolvedValue;
};
-export const resolveReferences = (query, validationSchema, customResolvers = {}, fxActive = false) => {
+export const resolveReferences = (query, validationSchema, customResolvers = {}) => {
if (!query || typeof query !== 'string') return [false, null, null];
-
let resolvedValue = query;
let error = null;
- const currentState = useCurrentStateStore.getState();
-
//Todo : remove resolveWorkspaceVariables when workspace variables are removed
if (query?.startsWith('%%') && query?.endsWith('%%')) {
- return resolveWorkspaceVariables(query, currentState);
+ return resolveWorkspaceVariables(query);
}
if ((!validationSchema || isEmpty(validationSchema)) && (!query?.includes('{{') || !query?.includes('}}'))) {
return [true, error, resolvedValue];
}
- if (validationSchema && !fxActive && !query?.includes('{{') && !query?.includes('}}')) {
+ if (validationSchema && !query?.includes('{{') && !query?.includes('}}')) {
const [valid, errors, newValue] = validateComponentProperty(query, validationSchema);
return [valid, errors, newValue, resolvedValue];
}
- const hasMultiDynamicVariables = getDynamicVariables(query);
+ const hasMultiDynamicVariables = getDynamicVariables(query)?.length > 1;
const { lookupTable } = useResolveStore.getState();
- if (isEmpty(validationSchema) && hasMultiDynamicVariables) {
+ if (hasMultiDynamicVariables) {
resolvedValue = resolveMultiDynamicReferences(query, lookupTable);
} else {
- let value = !fxActive ? query?.replace(/{{|}}/g, '').trim() : query;
+ let value = query?.replace(/{{|}}/g, '').trim();
- if (fxActive && (value.startsWith('#') || value.includes('table-'))) {
+ if (value.startsWith('#') || value.includes('table-')) {
value = JSON.stringify(value);
}
- const { toResolveReference, jsExpression, jsExpMatch } = inferJSExpAndReferences(value, lookupTable.hints);
+ const { toResolveReference, jsExpression, jsExpMatch } =
+ lookupTable.hints || lookupTable.hints.has
+ ? inferJSExpAndReferences(value, lookupTable.hints)
+ : { toResolveReference: null, jsExpression: null, jsExpMatch: null };
if (!jsExpMatch && toResolveReference && lookupTable.hints.has(toResolveReference)) {
const idToLookUp = lookupTable.hints.get(toResolveReference);
@@ -253,10 +253,10 @@ export const resolveReferences = (query, validationSchema, customResolvers = {},
let jscode = value.replace(toResolveReference, resolvedValue);
jscode = value.replace(toResolveReference, `'${resolvedValue}'`);
- resolvedValue = resolveCode(jscode, currentState, customResolvers);
+ resolvedValue = resolveCode(jscode, customResolvers);
}
} else {
- const [resolvedCode, errorRef] = resolveCode(value, currentState, customResolvers, true, [], true);
+ const [resolvedCode, errorRef] = resolveCode(value, customResolvers, true, [], true);
resolvedValue = resolvedCode;
error = errorRef || null;
@@ -309,7 +309,7 @@ const inferJSExpAndReferences = (code, hintsMap) => {
const potentialReference = referenceChain ? referenceChain + '.' + segment : segment;
// Check if the potential reference exists in hintsMap
- if (hintsMap.has(potentialReference)) {
+ if (hintsMap.has && hintsMap.has(potentialReference)) {
// If it does, update the referenceChain
referenceChain = potentialReference;
} else {
diff --git a/frontend/src/Editor/Components/FilePicker.jsx b/frontend/src/Editor/Components/FilePicker.jsx
index 0a144162cd..1be05177c7 100644
--- a/frontend/src/Editor/Components/FilePicker.jsx
+++ b/frontend/src/Editor/Components/FilePicker.jsx
@@ -4,7 +4,7 @@ import { resolveWidgetFieldValue } from '@/_helpers/utils';
import { toast } from 'react-hot-toast';
// eslint-disable-next-line import/no-unresolved
import * as XLSX from 'xlsx/xlsx.mjs';
-import { useCurrentState } from '@/_stores/currentStateStore';
+
import { useAppInfo } from '@/_stores/appDataStore';
export const FilePicker = ({
@@ -19,7 +19,6 @@ export const FilePicker = ({
setExposedVariable,
dataCy,
}) => {
- const currentState = useCurrentState();
//* properties definitions
const instructionText =
component.definition.properties.instructionText?.value ?? 'Drag and drop files here or click to select files';
@@ -30,31 +29,25 @@ export const FilePicker = ({
const fileType = component.definition.properties.fileType?.value ?? 'image/*';
const maxSize = component.definition.properties.maxSize?.value ?? 1048576;
const minSize = component.definition.properties.minSize?.value ?? 0;
- const parseContent = resolveWidgetFieldValue(
- component.definition.properties.parseContent?.value ?? false,
- currentState
- );
+ const parseContent = resolveWidgetFieldValue(component.definition.properties.parseContent?.value);
const fileTypeFromExtension = component.definition.properties.parseFileType?.value ?? 'auto-detect';
- const parsedEnableDropzone =
- typeof enableDropzone !== 'boolean' ? resolveWidgetFieldValue(enableDropzone, currentState) : true;
- const parsedEnablePicker =
- typeof enablePicker !== 'boolean' ? resolveWidgetFieldValue(enablePicker, currentState) : true;
+ const parsedEnableDropzone = typeof enableDropzone !== 'boolean' ? resolveWidgetFieldValue(enableDropzone) : true;
+ const parsedEnablePicker = typeof enablePicker !== 'boolean' ? resolveWidgetFieldValue(enablePicker) : true;
- const parsedMaxFileCount =
- typeof maxFileCount !== 'number' ? resolveWidgetFieldValue(maxFileCount, currentState) : maxFileCount;
+ const parsedMaxFileCount = typeof maxFileCount !== 'number' ? resolveWidgetFieldValue(maxFileCount) : maxFileCount;
const parsedEnableMultiple =
- typeof enableMultiple !== 'boolean' ? resolveWidgetFieldValue(enableMultiple, currentState) : enableMultiple;
- const parsedFileType = resolveWidgetFieldValue(fileType, currentState);
- const parsedMinSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(minSize, currentState) : minSize;
- const parsedMaxSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(maxSize, currentState) : maxSize;
+ typeof enableMultiple !== 'boolean' ? resolveWidgetFieldValue(enableMultiple) : enableMultiple;
+ const parsedFileType = resolveWidgetFieldValue(fileType);
+ const parsedMinSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(minSize) : minSize;
+ const parsedMaxSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(maxSize) : maxSize;
//* styles definitions
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
const disabledState = component.definition.styles?.disabledState?.value ?? false;
const parsedDisabledState =
- typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
+ typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState;
const parsedWidgetVisibility =
- typeof widgetVisibility !== 'boolean' ? resolveWidgetFieldValue(widgetVisibility, currentState) : widgetVisibility;
+ typeof widgetVisibility !== 'boolean' ? resolveWidgetFieldValue(widgetVisibility) : widgetVisibility;
const { events: allAppEvents } = useAppInfo();
diff --git a/frontend/src/Editor/Components/Map/Map.jsx b/frontend/src/Editor/Components/Map/Map.jsx
index 1867bcf1e5..19748d5003 100644
--- a/frontend/src/Editor/Components/Map/Map.jsx
+++ b/frontend/src/Editor/Components/Map/Map.jsx
@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useCallback, useEffect } from 'react';
import { GoogleMap, LoadScript, Marker, Autocomplete, Polygon } from '@react-google-maps/api';
-import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
+import { resolveWidgetFieldValue } from '@/_helpers/utils';
import { darkModeStyles } from './styles';
import { useTranslation } from 'react-i18next';
@@ -12,7 +12,6 @@ export const Map = function Map({
component,
darkMode,
onComponentClick,
- currentState,
onComponentOptionChanged,
onComponentOptionsChanged,
onEvent,
@@ -27,27 +26,27 @@ export const Map = function Map({
const { t } = useTranslation();
const addNewMarkersProp = component.definition.properties.addNewMarkers;
- const canAddNewMarkers = addNewMarkersProp ? resolveReferences(addNewMarkersProp.value, currentState) : false;
+ const canAddNewMarkers = addNewMarkersProp ? resolveWidgetFieldValue(addNewMarkersProp.value) : false;
const canSearchProp = component.definition.properties.canSearch;
- const canSearch = canSearchProp ? resolveReferences(canSearchProp.value, currentState) : false;
+ const canSearch = canSearchProp ? resolveWidgetFieldValue(canSearchProp.value) : false;
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
const disabledState = component.definition.styles?.disabledState?.value ?? false;
const parsedDisabledState =
- typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
+ typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState;
let parsedWidgetVisibility = widgetVisibility;
try {
- parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
+ parsedWidgetVisibility = resolveWidgetFieldValue(parsedWidgetVisibility);
} catch (err) {
console.log(err);
}
const [gmap, setGmap] = useState(null);
const [autoComplete, setAutoComplete] = useState(null);
- const [mapCenter, setMapCenter] = useState(resolveReferences(center, currentState));
+ const [mapCenter, setMapCenter] = useState(() => resolveWidgetFieldValue(center));
const [markers, setMarkers] = useState(defaultMarkers);
const containerStyle = {
@@ -97,7 +96,7 @@ export const Map = function Map({
}
useEffect(() => {
- const resolvedCenter = resolveReferences(center, currentState);
+ const resolvedCenter = resolveWidgetFieldValue(center);
setMapCenter(resolvedCenter);
onComponentOptionsChanged(component, [['center', addMapUrlToJson(resolvedCenter)]]);
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -128,7 +127,7 @@ export const Map = function Map({
useEffect(() => {
setExposedVariable('setLocation', async function (lat, lng) {
- if (lat && lng) setMapCenter(resolveReferences({ lat, lng }, currentState));
+ if (lat && lng) setMapCenter(resolveWidgetFieldValue({ lat, lng }));
});
}, [setMapCenter]);
diff --git a/frontend/src/Editor/Components/NumberInput.jsx b/frontend/src/Editor/Components/NumberInput.jsx
index 542ff28901..151440290e 100644
--- a/frontend/src/Editor/Components/NumberInput.jsx
+++ b/frontend/src/Editor/Components/NumberInput.jsx
@@ -3,8 +3,8 @@ import './numberinput.scss';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import * as Icons from '@tabler/icons-react';
import Loader from '@/ToolJetUI/Loader/Loader';
-import { resolveReferences } from '@/_helpers/utils';
-import { useCurrentState } from '@/_stores/currentStateStore';
+import { resolveWidgetFieldValue } from '@/_helpers/utils';
+
const tinycolor = require('tinycolor2');
import Label from '@/_ui/Label';
@@ -38,9 +38,9 @@ export const NumberInput = function NumberInput({
} = styles;
const textColor = darkMode && ['#232e3c', '#000000ff'].includes(styles.textColor) ? '#fff' : styles.textColor;
- const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState) ?? false;
- const minValue = resolveReferences(component?.definition?.validation?.minValue?.value, currentState) ?? null;
- const maxValue = resolveReferences(component?.definition?.validation?.maxValue?.value, currentState) ?? null;
+ const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value) ?? false;
+ const minValue = resolveWidgetFieldValue(component?.definition?.validation?.minValue?.value) ?? null;
+ const maxValue = resolveWidgetFieldValue(component?.definition?.validation?.maxValue?.value) ?? null;
const [visibility, setVisibility] = useState(properties.visibility);
const [loading, setLoading] = useState(loadingState);
@@ -50,7 +50,7 @@ export const NumberInput = function NumberInput({
const [isFocused, setIsFocused] = useState(false);
const inputRef = useRef(null);
- const currentState = useCurrentState();
+
const [disable, setDisable] = useState(disabledState || loadingState);
const labelRef = useRef();
const _width = (width / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value
diff --git a/frontend/src/Editor/Components/PasswordInput.jsx b/frontend/src/Editor/Components/PasswordInput.jsx
index 8b0b8e5b50..71b9f99c4d 100644
--- a/frontend/src/Editor/Components/PasswordInput.jsx
+++ b/frontend/src/Editor/Components/PasswordInput.jsx
@@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from 'react';
-import { resolveReferences } from '@/_helpers/utils';
-import { useCurrentState } from '@/_stores/currentStateStore';
+import { resolveWidgetFieldValue } from '@/_helpers/utils';
+
import * as Icons from '@tabler/icons-react';
import Loader from '@/ToolJetUI/Loader/Loader';
import SolidIcon from '@/_ui/Icon/SolidIcons';
@@ -44,8 +44,8 @@ export const PasswordInput = function PasswordInput({
const [visibility, setVisibility] = useState(properties.visibility);
const { isValid, validationError } = validate(passwordValue);
const [showValidationError, setShowValidationError] = useState(false);
- const currentState = useCurrentState();
- const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState);
+
+ const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value);
const [labelWidth, setLabelWidth] = useState(0);
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
const [iconVisibility, setIconVisibility] = useState(false);
diff --git a/frontend/src/Editor/Components/Table/Table.jsx b/frontend/src/Editor/Components/Table/Table.jsx
index 19cb66c7ef..42604a0926 100644
--- a/frontend/src/Editor/Components/Table/Table.jsx
+++ b/frontend/src/Editor/Components/Table/Table.jsx
@@ -14,7 +14,12 @@ import {
useColumnOrder,
} from 'react-table';
import cx from 'classnames';
-import { resolveReferences, validateWidget, determineJustifyContentValue } from '@/_helpers/utils';
+import {
+ resolveReferences,
+ validateWidget,
+ determineJustifyContentValue,
+ resolveWidgetFieldValue,
+} from '@/_helpers/utils';
import { useExportData } from 'react-table-plugins';
import Papa from 'papaparse';
import { Pagination } from './Pagination';
@@ -388,11 +393,11 @@ export function Table({
let tableData = [],
dynamicColumn = [];
- const useDynamicColumn = resolveReferences(component.definition.properties?.useDynamicColumn?.value, currentState);
+ const useDynamicColumn = resolveWidgetFieldValue(component.definition.properties?.useDynamicColumn?.value);
if (currentState) {
- tableData = resolveReferences(component.definition.properties.data.value, currentState, []);
+ tableData = resolveWidgetFieldValue(component.definition.properties.data.value);
dynamicColumn = useDynamicColumn
- ? resolveReferences(component.definition.properties?.columnData?.value, currentState, []) ?? []
+ ? resolveWidgetFieldValue(component.definition.properties?.columnData?.value) ?? []
: [];
if (!Array.isArray(tableData)) {
tableData = [];
diff --git a/frontend/src/Editor/Components/Tabs.jsx b/frontend/src/Editor/Components/Tabs.jsx
index 74c59ec1ee..5407d5a99f 100644
--- a/frontend/src/Editor/Components/Tabs.jsx
+++ b/frontend/src/Editor/Components/Tabs.jsx
@@ -1,7 +1,7 @@
import React, { useRef, useState, useEffect } from 'react';
import { SubCustomDragLayer } from '../SubCustomDragLayer';
import { SubContainer } from '../SubContainer';
-import { resolveReferences, resolveWidgetFieldValue, isExpectedDataType } from '@/_helpers/utils';
+import { resolveWidgetFieldValue, isExpectedDataType } from '@/_helpers/utils';
import { handleLowPriorityWork } from '@/_helpers/editorHelpers';
export const Tabs = function Tabs({
@@ -10,7 +10,6 @@ export const Tabs = function Tabs({
width,
height,
containerProps,
- currentState,
removeComponent,
setExposedVariable,
setExposedVariables,
@@ -25,9 +24,9 @@ export const Tabs = function Tabs({
const disabledState = component.definition.styles?.disabledState?.value ?? false;
const defaultTab = component.definition.properties.defaultTab.value;
// config for tabs. Includes title
- const tabs = isExpectedDataType(resolveReferences(component.definition.properties.tabs.value, currentState), 'array');
+ const tabs = isExpectedDataType(resolveWidgetFieldValue(component.definition.properties?.tabs?.value), 'array');
let parsedTabs = tabs;
- parsedTabs = resolveWidgetFieldValue(parsedTabs, currentState);
+ parsedTabs = resolveWidgetFieldValue(parsedTabs);
const hideTabs = component.definition.properties?.hideTabs?.value ?? false;
//* renderOnlyActiveTab - TRUE (renders only the content of the active tab)
@@ -40,25 +39,23 @@ export const Tabs = function Tabs({
// Highlight color - for active tab text and border
const highlightColor = component.definition.styles?.highlightColor?.value ?? '#f44336';
let parsedHighlightColor = highlightColor;
- parsedHighlightColor = resolveWidgetFieldValue(highlightColor, currentState);
+ parsedHighlightColor = resolveWidgetFieldValue(highlightColor);
// Default tab
let parsedDefaultTab = defaultTab;
- parsedDefaultTab = resolveWidgetFieldValue(parsedDefaultTab, currentState, 1);
+ parsedDefaultTab = resolveWidgetFieldValue(parsedDefaultTab, 1);
const parsedDisabledState =
- typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
+ typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState;
- const parsedHideTabs = typeof hideTabs !== 'boolean' ? resolveWidgetFieldValue(hideTabs, currentState) : hideTabs;
+ const parsedHideTabs = typeof hideTabs !== 'boolean' ? resolveWidgetFieldValue(hideTabs) : hideTabs;
const parsedRenderOnlyActiveTab =
- typeof renderOnlyActiveTab !== 'boolean'
- ? resolveWidgetFieldValue(renderOnlyActiveTab, currentState)
- : renderOnlyActiveTab;
+ typeof renderOnlyActiveTab !== 'boolean' ? resolveWidgetFieldValue(renderOnlyActiveTab) : renderOnlyActiveTab;
let parsedWidgetVisibility = widgetVisibility;
try {
- parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
+ parsedWidgetVisibility = resolveWidgetFieldValue(parsedWidgetVisibility);
} catch (err) {
console.log(err);
}
@@ -77,7 +74,7 @@ export const Tabs = function Tabs({
const currentTabData = parsedTabs.filter((tab) => tab.id === currentTab);
setBgColor(currentTabData[0]?.backgroundColor ? currentTabData[0]?.backgroundColor : darkMode ? '#324156' : '#fff');
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [currentState, currentTab]);
+ }, [currentTab]);
function computeTabVisibility(componentId, id) {
let tabVisibility = 'hidden';
diff --git a/frontend/src/Editor/Components/TextInput.jsx b/frontend/src/Editor/Components/TextInput.jsx
index d0db47de78..6084aa16d9 100644
--- a/frontend/src/Editor/Components/TextInput.jsx
+++ b/frontend/src/Editor/Components/TextInput.jsx
@@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from 'react';
-import { resolveReferences } from '@/_helpers/utils';
-import { useCurrentState } from '@/_stores/currentStateStore';
+import { resolveWidgetFieldValue } from '@/_helpers/utils';
+
import * as Icons from '@tabler/icons-react';
import Loader from '@/ToolJetUI/Loader/Loader';
const tinycolor = require('tinycolor2');
@@ -45,8 +45,8 @@ export const TextInput = function TextInput({
const [visibility, setVisibility] = useState(properties.visibility);
const { isValid, validationError } = validate(value);
const [showValidationError, setShowValidationError] = useState(false);
- const currentState = useCurrentState();
- const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState);
+
+ const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value);
const [labelWidth, setLabelWidth] = useState(0);
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
const [loading, setLoading] = useState(loadingState);
diff --git a/frontend/src/Editor/Container.jsx b/frontend/src/Editor/Container.jsx
index 0322489fc6..9b62b777ec 100644
--- a/frontend/src/Editor/Container.jsx
+++ b/frontend/src/Editor/Container.jsx
@@ -6,7 +6,7 @@ import { ItemTypes, EditorConstants } from './editorConstants';
import { DraggableBox } from './DraggableBox';
import update from 'immutability-helper';
import { componentTypes } from './WidgetManager/components';
-import { resolveReferences } from '@/_helpers/utils';
+import { resolveWidgetFieldValue } from '@/_helpers/utils';
import Comments from './Comments';
import { commentsService } from '@/_services';
import config from 'config';
@@ -25,7 +25,7 @@ import DragContainer from './DragContainer';
import { compact, correctBounds } from './gridUtils';
import { isPDFSupported } from '@/_stores/utils';
import toast from 'react-hot-toast';
-import { isOnlyLayoutUpdate } from '@/_helpers/editorHelpers';
+import { isOnlyLayoutUpdate, handleLowPriorityWork } from '@/_helpers/editorHelpers';
import GhostWidget from './GhostWidget';
import { useDraggedSubContainer, useGridStore } from '@/_stores/gridStore';
@@ -46,8 +46,8 @@ export const Container = ({
socket,
handleUndo,
handleRedo,
- currentPageId,
}) => {
+ const currentPageId = useEditorStore.getState().currentPageId;
const appDefinition = useEditorStore.getState().appDefinition;
// Dont update first time to skip
// redundant save on app definition load
@@ -279,6 +279,10 @@ export const Container = ({
appDefinitionChanged(newDefinition, opts);
}
+ return () => {
+ firstUpdate.current = true;
+ };
+
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [boxes]);
@@ -594,22 +598,26 @@ export const Container = ({
...copyOfBoxes,
...updatedBoxes,
};
- const diffState = diff(boxes, newBoxes);
- setBoxes((prev) => {
- const updatedComponentsAsperDiff = Object.keys(diffState).reduce((acc, key) => {
- const component = newBoxes[key];
- if (component) {
- acc[key] = component;
- }
- return acc;
- }, {});
+ handleLowPriorityWork(() => {
+ const diffState = diff(boxes, newBoxes);
- return {
- ...prev,
- ...updatedComponentsAsperDiff,
- };
+ setBoxes((prev) => {
+ const updatedComponentsAsperDiff = Object.keys(diffState).reduce((acc, key) => {
+ const component = newBoxes[key];
+ if (component) {
+ acc[key] = component;
+ }
+ return acc;
+ }, {});
+
+ return {
+ ...prev,
+ ...updatedComponentsAsperDiff,
+ };
+ });
});
+
updateCanvasHeight(newBoxes);
}
@@ -819,7 +827,8 @@ export const Container = ({
.map(([id, box]) => {
const canShowInCurrentLayout =
box.component.definition.others[currentLayout === 'mobile' ? 'showOnMobile' : 'showOnDesktop'].value;
- if (box.parent || !resolveReferences(canShowInCurrentLayout, useCurrentStateStore.getState())) {
+
+ if (box.parent || !resolveWidgetFieldValue(canShowInCurrentLayout)) {
return '';
}
return (
diff --git a/frontend/src/Editor/DragContainer.jsx b/frontend/src/Editor/DragContainer.jsx
index 7c719cb851..8355f0a678 100644
--- a/frontend/src/Editor/DragContainer.jsx
+++ b/frontend/src/Editor/DragContainer.jsx
@@ -40,15 +40,14 @@ export default function DragContainer({
const preant = boxes.find((box) => box.id == lastDraggedEventsRef.current.events[0].target.id)?.component
?.parent;
// Adding the new updates to the macro task queue to unblock UI
- setTimeout(() =>
- onDrag(
- lastDraggedEventsRef.current.events.map((ev) => ({
- id: ev.target.id,
- x: ev.translate[0],
- y: ev.translate[1],
- parent: preant,
- }))
- )
+
+ onDrag(
+ lastDraggedEventsRef.current.events.map((ev) => ({
+ id: ev.target.id,
+ x: ev.translate[0],
+ y: ev.translate[1],
+ parent: preant,
+ }))
);
}
if (useGridStore.getState().isGroupHandleHoverd) {
@@ -363,9 +362,9 @@ export default function DragContainer({
resizeData.gw = _gridWidth;
}
// Adding the new updates to the macro task queue to unblock UI
- setTimeout(() => {
- onResizeStop([resizeData]);
- });
+ // setTimeout(() => {
+ // });
+ onResizeStop([resizeData]);
} catch (error) {
console.error('ResizeEnd error ->', error);
}
@@ -432,9 +431,9 @@ export default function DragContainer({
if (groupResizeDataRef.current.length) {
// Adding the new updates to the macro task queue to unblock UI
- setTimeout(() => {
- onResizeStop([newBoxs]);
- });
+ // setTimeout(() => {
+ // });
+ onResizeStop([newBoxs]);
} else {
events.forEach((ev) => {
const currentWidget = boxes.find(({ id }) => {
@@ -562,24 +561,24 @@ export default function DragContainer({
toast.error(`${currentWidget} is not compatible as a child component of ${parentWidget}`);
e.target.style.transform = `translate(${left}px, ${top}px)`;
}
- } else {
- e.target.style.transform = `translate(${Math.round(left / _gridWidth) * _gridWidth}px, ${
- Math.round(top / 10) * 10
- }px)`;
}
+ e.target.style.transform = `translate(${Math.round(left / _gridWidth) * _gridWidth}px, ${
+ Math.round(top / 10) * 10
+ }px)`;
+
if (draggedOverElemId === currentParentId || isParentChangeAllowed) {
// Adding the new updates to the macro task queue to unblock UI
- setTimeout(() =>
- onDrag([
- {
- id: e.target.id,
- x: left,
- y: Math.round(top / 10) * 10,
- parent: isParentChangeAllowed ? draggedOverElemId : undefined,
- },
- ])
- );
+ // setTimeout(() =>
+ // );
+ onDrag([
+ {
+ id: e.target.id,
+ x: left,
+ y: Math.round(top / 10) * 10,
+ parent: isParentChangeAllowed ? draggedOverElemId : undefined,
+ },
+ ]);
}
const box = boxes.find((box) => box.id === e.target.id);
setTimeout(() => useEditorStore.getState().actions.setSelectedComponents([{ ...box }]));
@@ -696,31 +695,29 @@ export default function DragContainer({
const { posRight, posLeft, posTop, posBottom } = getPositionForGroupDrag(events, parentWidth, parentHeight);
// Adding the new updates to the macro task queue to unblock UI
- setTimeout(() =>
- onDrag(
- events.map((ev) => {
- let posX = ev.lastEvent.translate[0];
- let posY = ev.lastEvent.translate[1];
- if (posLeft < 0) {
- posX = ev.lastEvent.translate[0] - posLeft;
- }
- if (posTop < 0) {
- posY = ev.lastEvent.translate[1] - posTop;
- }
- if (posRight < 0) {
- posX = ev.lastEvent.translate[0] + posRight;
- }
- if (posBottom < 0) {
- posY = ev.lastEvent.translate[1] + posBottom;
- }
- return {
- id: ev.target.id,
- x: posX,
- y: posY,
- parent: parentId,
- };
- })
- )
+ onDrag(
+ events.map((ev) => {
+ let posX = ev.lastEvent.translate[0];
+ let posY = ev.lastEvent.translate[1];
+ if (posLeft < 0) {
+ posX = ev.lastEvent.translate[0] - posLeft;
+ }
+ if (posTop < 0) {
+ posY = ev.lastEvent.translate[1] - posTop;
+ }
+ if (posRight < 0) {
+ posX = ev.lastEvent.translate[0] + posRight;
+ }
+ if (posBottom < 0) {
+ posY = ev.lastEvent.translate[1] + posBottom;
+ }
+ return {
+ id: ev.target.id,
+ x: posX,
+ y: posY,
+ parent: parentId,
+ };
+ })
);
} catch (error) {
console.error('Error dragging group', error);
diff --git a/frontend/src/Editor/DraggableBox.jsx b/frontend/src/Editor/DraggableBox.jsx
index 653a5186ff..d51387fa8c 100644
--- a/frontend/src/Editor/DraggableBox.jsx
+++ b/frontend/src/Editor/DraggableBox.jsx
@@ -131,7 +131,7 @@ const DraggableBox = React.memo(
const configWidgetHandlerForModalComponent =
!isSelectedComponent &&
component.component === 'Modal' &&
- resolveWidgetFieldValue(component.definition.properties.useDefaultButton, currentState)?.value === false;
+ resolveWidgetFieldValue(component.definition.properties.useDefaultButton?.value) === false;
const onComponentHover = useCallback(
(id) => {
diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx
index 38ab16ec5e..1f3af82f68 100644
--- a/frontend/src/Editor/Editor.jsx
+++ b/frontend/src/Editor/Editor.jsx
@@ -89,7 +89,7 @@ import { HotkeysProvider } from 'react-hotkeys-hook';
import { useResolveStore } from '@/_stores/resolverStore';
import { dfs } from '@/_stores/handleReferenceTransactions';
import { decimalToHex } from './editorConstants';
-import { findComponentsWithReferences, generatePath, handleLowPriorityWork } from '@/_helpers/editorHelpers';
+import { findComponentsWithReferences, handleLowPriorityWork } from '@/_helpers/editorHelpers';
setAutoFreeze(false);
enablePatches();
@@ -282,9 +282,9 @@ const EditorComponent = (props) => {
}
if (mounted && didAppDefinitionChanged && currentPageId) {
- const components = appDefinition?.pages[currentPageId]?.components || {};
+ // const components = appDefinition?.pages[currentPageId]?.components || {};
- computeComponentState(components);
+ // computeComponentState(components);
if (appDiffOptions?.skipAutoSave === true || appDiffOptions?.entityReferenceUpdated === true) return;
@@ -293,8 +293,6 @@ const EditorComponent = (props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify({ appDefinition, currentPageId, dataQueries })]);
- const prevCurrentStateRef = useRef(currentState);
-
/**
** Async updates components in batches to optimize and processing efficiency.
* This function iterates over an array of component IDs, updating them in fixed-size batches,
@@ -634,8 +632,18 @@ const EditorComponent = (props) => {
props.switchDarkMode(newMode);
};
- const handleEvent = React.useCallback((eventName, event, options) => {
- return onEvent(getEditorRef(), eventName, event, options, 'edit');
+ const handleEvent = React.useCallback((eventName, events, options) => {
+ const latestEvents = useAppDataStore.getState().events;
+ const filteredEvents = latestEvents.filter((event) => {
+ const foundEvent = events.find((e) => e.id === event.id);
+ return foundEvent && foundEvent.name === eventName;
+ });
+
+ try {
+ return onEvent(getEditorRef(), eventName, filteredEvents, options, 'edit');
+ } catch (error) {
+ console.error(error);
+ }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -743,12 +751,12 @@ const EditorComponent = (props) => {
useCurrentStateStore.getState().actions.setCurrentState({
page: currentpageData,
- }),
- updateEditorState({
- isLoading: false,
- appDefinition: appJson,
- isUpdatingEditorStateInProcess: false,
- });
+ });
+ updateEditorState({
+ isLoading: false,
+ appDefinition: appJson,
+ isUpdatingEditorStateInProcess: false,
+ });
updateState({ components: appJson.pages[homePageId]?.components });
@@ -765,129 +773,9 @@ const EditorComponent = (props) => {
await fetchDataSources(data.editing_version?.id),
await fetchDataQueries(data.editing_version?.id, true, true),
])
- .then(() => {
- useCurrentStateStore.getState().actions.setEditorReady(true);
-
- const currentPageId = useEditorStore.getState().currentPageId;
- const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components;
-
- const referenceManager = useResolveStore.getState().referenceMapper;
-
- const newComponents = Object.keys(currentComponents).map((componentId) => {
- const component = currentComponents[componentId];
-
- if (!referenceManager.get(componentId)) {
- return {
- id: componentId,
- name: component.component.name,
- };
- }
- });
-
- useResolveStore.getState().actions.addEntitiesToMap(newComponents);
- })
- .then(() => {
- const currentPageId = useEditorStore.getState().currentPageId;
- const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components;
- let dataQueries = JSON.parse(JSON.stringify(useDataQueriesStore.getState().dataQueries));
- let allEvents = JSON.parse(JSON.stringify(useAppDataStore.getState().events));
-
- const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, [])
- ?.map((entity) => {
- if (entity && isValidUUID(entity)) {
- return entity;
- }
- })
- ?.filter((e) => e !== undefined);
-
- const entityReferencesInQueryoOptions = findAllEntityReferences(dataQueries, [])
- ?.map((entity) => {
- if (entity && isValidUUID(entity)) {
- return entity;
- }
- })
- ?.filter((e) => e !== undefined);
-
- const entityReferencesInEvents = findAllEntityReferences(allEvents, [])
- ?.map((entity) => {
- if (entity && isValidUUID(entity)) {
- return entity;
- }
- })
- ?.filter((e) => e !== undefined);
-
- const manager = useResolveStore.getState().referenceMapper;
-
- if (
- Array.isArray(entityReferencesInComponentDefinitions) &&
- entityReferencesInComponentDefinitions?.length > 0
- ) {
- let newComponentDefinition = JSON.parse(JSON.stringify(currentComponents));
-
- entityReferencesInComponentDefinitions.forEach((entity) => {
- const entityrefExists = manager.has(entity);
-
- if (entityrefExists) {
- const value = manager.get(entity);
- newComponentDefinition = dfs(newComponentDefinition, entity, value);
- }
- });
-
- const newAppDefinition = produce(appJson, (draft) => {
- draft.pages[homePageId].components = newComponentDefinition;
- });
-
- updateEditorState({
- isUpdatingEditorStateInProcess: false,
- appDefinition: newAppDefinition,
- });
- }
-
- if (Array.isArray(entityReferencesInQueryoOptions) && entityReferencesInQueryoOptions?.length > 0) {
- let newQueryOptions = {};
- dataQueries?.forEach((query) => {
- newQueryOptions[query.id] = query.options;
- ``;
- });
-
- entityReferencesInQueryoOptions.forEach((entity) => {
- const entityrefExists = manager.has(entity);
-
- if (entityrefExists) {
- const value = manager.get(entity);
- newQueryOptions = dfs(newQueryOptions, entity, value);
- }
- });
-
- dataQueries = dataQueries.map((query) => {
- const queryId = query.id;
- const dqOptions = newQueryOptions[queryId];
-
- return {
- ...query,
- options: dqOptions,
- };
- });
-
- useDataQueriesStore.getState().actions.setDataQueries(dataQueries, 'mappingUpdate');
- }
-
- if (Array.isArray(entityReferencesInEvents) && entityReferencesInEvents?.length > 0) {
- let newEvents = JSON.parse(JSON.stringify(allEvents));
-
- entityReferencesInEvents.forEach((entity) => {
- const entityrefExists = manager.has(entity);
-
- if (entityrefExists) {
- const value = manager.get(entity);
- newEvents = dfs(newEvents, entity, value);
- }
- });
-
- updateState({
- events: newEvents,
- });
- }
+ .then(async () => {
+ await onEditorLoad(appJson, homePageId, versionSwitched);
+ updateEntityReferences(appJson, homePageId);
})
.finally(async () => {
const currentPageEvents = data.events.filter(
@@ -927,6 +815,9 @@ const EditorComponent = (props) => {
updateEditorState({
isLoading: true,
});
+ useCurrentStateStore.getState().actions.setCurrentState({});
+ useCurrentStateStore.getState().actions.setEditorReady(false);
+ useResolveStore.getState().actions.resetStore();
callBack(appData, null, true);
initComponentVersioning();
@@ -1093,7 +984,7 @@ const EditorComponent = (props) => {
updateEditorState({
isUpdatingEditorStateInProcess: false,
});
- } else if (!isEmpty(editingVersion)) {
+ } else if (!isEmpty(editingVersion) && !isEmpty(appDiffOptions) && appDefinition) {
//! The computeComponentPropertyDiff function manages the calculation of differences in table columns by requiring complete column data. Without this complete data, the resulting JSON structure may be incorrect.
const paramDiff = computeComponentPropertyDiff(appDefinitionDiff, appDefinition, appDiffOptions);
const updateDiff = computeAppDiff(paramDiff, currentPageId, appDiffOptions, currentLayout);
@@ -1131,24 +1022,26 @@ const EditorComponent = (props) => {
}
//Todo: Move this to a separate function or as a middleware of the api to createing a component
- if (updateDiff?.type === 'components' && updateDiff?.operation === 'create') {
- const componentsFromCurrentState = getCurrentState().components;
- const newComponentIds = Object.keys(updateDiff?.updateDiff);
- const newComponentsExposedData = {};
- const componentEntityArray = [];
- Object.values(componentsFromCurrentState).filter((component) => {
- if (newComponentIds.includes(component.id)) {
- const componentName = updateDiff?.updateDiff[component.id]?.name;
- newComponentsExposedData[componentName] = component;
- componentEntityArray.push({ id: component.id, name: componentName });
- }
- });
+ handleLowPriorityWork(() => {
+ if (updateDiff?.type === 'components' && updateDiff?.operation === 'create') {
+ const componentsFromCurrentState = getCurrentState().components;
+ const newComponentIds = Object.keys(updateDiff?.updateDiff);
+ const newComponentsExposedData = {};
+ const componentEntityArray = [];
+ Object.values(componentsFromCurrentState).filter((component) => {
+ if (newComponentIds.includes(component.id)) {
+ const componentName = updateDiff?.updateDiff[component.id]?.name;
+ newComponentsExposedData[componentName] = component;
+ componentEntityArray.push({ id: component.id, name: componentName });
+ }
+ });
- useResolveStore.getState().actions.addEntitiesToMap(componentEntityArray);
- useResolveStore.getState().actions.addAppSuggestions({
- components: newComponentsExposedData,
- });
- }
+ useResolveStore.getState().actions.addEntitiesToMap(componentEntityArray);
+ useResolveStore.getState().actions.addAppSuggestions({
+ components: newComponentsExposedData,
+ });
+ }
+ });
if (
updateDiff?.type === 'components' &&
@@ -1199,7 +1092,7 @@ const EditorComponent = (props) => {
};
const realtimeSave = debounce(appDefinitionChanged, 100);
- const autoSave = debounce(saveEditingVersion, 150);
+ const autoSave = saveEditingVersion;
function handlePaths(prevPatch, path = [], appJSON) {
const paths = [...path];
@@ -1488,6 +1381,117 @@ const EditorComponent = (props) => {
}
};
+ const onEditorLoad = (appJson, pageId, isPageSwitchOrVersionSwitch = false) => {
+ useCurrentStateStore.getState().actions.setEditorReady(true);
+ const currentComponents = appJson?.pages?.[pageId]?.components;
+
+ const referenceManager = useResolveStore.getState().referenceMapper;
+
+ const newComponents = Object.keys(currentComponents).map((componentId) => {
+ const component = currentComponents[componentId];
+
+ if (isPageSwitchOrVersionSwitch || !referenceManager.get(componentId)) {
+ return {
+ id: componentId,
+ name: component.component.name,
+ };
+ }
+ });
+
+ useResolveStore.getState().actions.addEntitiesToMap(newComponents);
+ };
+
+ const updateEntityReferences = (appJson, pageId) => {
+ const currentComponents = appJson?.pages?.[pageId]?.components;
+
+ let dataQueries = JSON.parse(JSON.stringify(useDataQueriesStore.getState().dataQueries));
+ let allEvents = JSON.parse(JSON.stringify(useAppDataStore.getState().events));
+
+ const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, [])?.filter(
+ (entity) => entity && isValidUUID(entity)
+ );
+
+ const entityReferencesInQueryOptions = findAllEntityReferences(dataQueries, [])?.filter(
+ (entity) => entity && isValidUUID(entity)
+ );
+
+ const entityReferencesInEvents = findAllEntityReferences(allEvents, [])?.filter(
+ (entity) => entity && isValidUUID(entity)
+ );
+
+ const manager = useResolveStore.getState().referenceMapper;
+
+ if (Array.isArray(entityReferencesInComponentDefinitions) && entityReferencesInComponentDefinitions?.length > 0) {
+ let newComponentDefinition = JSON.parse(JSON.stringify(currentComponents));
+
+ entityReferencesInComponentDefinitions.forEach((entity) => {
+ const entityrefExists = manager.has(entity);
+
+ if (entityrefExists) {
+ const value = manager.get(entity);
+ newComponentDefinition = dfs(newComponentDefinition, entity, value);
+ }
+ });
+
+ const newAppDefinition = produce(appJson, (draft) => {
+ draft.pages[pageId].components = newComponentDefinition;
+ });
+
+ handleLowPriorityWork(() => {
+ updateEditorState({
+ isUpdatingEditorStateInProcess: false,
+ appDefinition: newAppDefinition,
+ });
+ });
+ }
+
+ if (Array.isArray(entityReferencesInQueryOptions) && entityReferencesInQueryOptions?.length > 0) {
+ let newQueryOptions = {};
+ dataQueries?.forEach((query) => {
+ newQueryOptions[query.id] = query.options;
+ ``;
+ });
+
+ entityReferencesInQueryOptions.forEach((entity) => {
+ const entityrefExists = manager.has(entity);
+
+ if (entityrefExists) {
+ const value = manager.get(entity);
+ newQueryOptions = dfs(newQueryOptions, entity, value);
+ }
+ });
+
+ dataQueries = dataQueries.map((query) => {
+ const queryId = query.id;
+ const dqOptions = newQueryOptions[queryId];
+
+ return {
+ ...query,
+ options: dqOptions,
+ };
+ });
+
+ useDataQueriesStore.getState().actions.setDataQueries(dataQueries, 'mappingUpdate');
+ }
+
+ if (Array.isArray(entityReferencesInEvents) && entityReferencesInEvents?.length > 0) {
+ let newEvents = JSON.parse(JSON.stringify(allEvents));
+
+ entityReferencesInEvents.forEach((entity) => {
+ const entityrefExists = manager.has(entity);
+
+ if (entityrefExists) {
+ const value = manager.get(entity);
+ newEvents = dfs(newEvents, entity, value);
+ }
+ });
+
+ updateState({
+ events: newEvents,
+ });
+ }
+ };
+
const removeComponents = () => {
const selectedComponents = useEditorStore.getState()?.selectedComponents;
if (!isVersionReleased && selectedComponents?.length > 1) {
@@ -1602,7 +1606,9 @@ const EditorComponent = (props) => {
});
};
- const switchPage = (pageId, queryParams = []) => {
+ const switchPage = async (pageId, queryParams = []) => {
+ useCurrentStateStore.getState().actions.setEditorReady(false);
+ useResolveStore.getState().actions.resetStore();
// This are fetched from store to handle runQueriesOnAppLoad
const currentPageId = useEditorStore.getState().currentPageId;
const appDefinition = useEditorStore.getState().appDefinition;
@@ -1629,7 +1635,13 @@ const EditorComponent = (props) => {
...currentState.globals,
urlparams: JSON.parse(JSON.stringify(queryString.parse(queryParamsString))),
};
+
useCurrentStateStore.getState().actions.setCurrentState({ globals, page });
+ useResolveStore.getState().actions.pageSwitched(true);
+
+ await onEditorLoad(appDefinition, pageId, true);
+ updateEntityReferences(appDefinition, pageId);
+ useResolveStore.getState().actions.updateJSHints();
setCurrentPageId(pageId);
diff --git a/frontend/src/Editor/EditorSelecto.jsx b/frontend/src/Editor/EditorSelecto.jsx
index db85ebda18..d99855439b 100644
--- a/frontend/src/Editor/EditorSelecto.jsx
+++ b/frontend/src/Editor/EditorSelecto.jsx
@@ -3,18 +3,13 @@ import Selecto from 'react-selecto';
import { useEditorStore, EMPTY_ARRAY } from '@/_stores/editorStore';
import { shallow } from 'zustand/shallow';
-const EditorSelecto = ({
- selectionRef,
- canvasContainerRef,
- currentPageId,
- setSelectedComponent,
- appDefinition,
- selectionDragRef,
-}) => {
- const { setSelectionInProgress, setSelectedComponents } = useEditorStore(
+const EditorSelecto = ({ selectionRef, canvasContainerRef, setSelectedComponent, selectionDragRef }) => {
+ const { setSelectionInProgress, setSelectedComponents, currentPageId, appDefinition } = useEditorStore(
(state) => ({
setSelectionInProgress: state?.actions?.setSelectionInProgress,
setSelectedComponents: state?.actions?.setSelectedComponents,
+ currentPageId: state?.currentPageId,
+ appDefinition: state?.appDefinition,
}),
shallow
);
diff --git a/frontend/src/Editor/Inspector/Components/Chart.jsx b/frontend/src/Editor/Inspector/Components/Chart.jsx
index 9f34c632e8..fd657bf17c 100644
--- a/frontend/src/Editor/Inspector/Components/Chart.jsx
+++ b/frontend/src/Editor/Inspector/Components/Chart.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import { renderElement } from '../Utils';
import { EventManager } from '@/Editor/Inspector/EventManager';
import Accordion from '@/_ui/Accordion';
-import { resolveReferences } from '@/_helpers/utils';
+import { resolveWidgetFieldValue } from '@/_helpers/utils';
import CodeHinter from '@/Editor/CodeEditor';
class Chart extends React.Component {
@@ -74,9 +74,8 @@ class Chart extends React.Component {
const jsonDescription = this.props.component.component.definition.properties.jsonDescription;
- const plotFromJson = resolveReferences(
- this.props.component.component.definition.properties.plotFromJson?.value,
- currentState
+ const plotFromJson = resolveWidgetFieldValue(
+ this.props.component.component.definition.properties.plotFromJson?.value
);
const chartType = this.props.component.component.definition.properties.type.value;
diff --git a/frontend/src/Editor/Inspector/EventManager.jsx b/frontend/src/Editor/Inspector/EventManager.jsx
index ae2bd7ac77..b723f17180 100644
--- a/frontend/src/Editor/Inspector/EventManager.jsx
+++ b/frontend/src/Editor/Inspector/EventManager.jsx
@@ -108,10 +108,10 @@ export const EventManager = ({
return { name: action.name, value: action.id };
});
- let checkIfClicksAreInsideOf = document.querySelector('#cm-complete-0');
+ let checkIfClicksAreInsideOf = document.querySelector('.cm-completionListIncompleteBottom');
// Listen for click events on body
if (checkIfClicksAreInsideOf) {
- document.body.addEventListener('click', function (event) {
+ document.body.addEventListener('mousedown', function (event) {
if (checkIfClicksAreInsideOf.contains(event.target)) {
event.stopPropagation();
}
diff --git a/frontend/src/Editor/QueryManager/Components/ParameterDetails.jsx b/frontend/src/Editor/QueryManager/Components/ParameterDetails.jsx
index 528cc480f9..a92919eaca 100644
--- a/frontend/src/Editor/QueryManager/Components/ParameterDetails.jsx
+++ b/frontend/src/Editor/QueryManager/Components/ParameterDetails.jsx
@@ -21,19 +21,19 @@ const ParameterDetails = ({ darkMode, onSubmit, isEdit, name, defaultValue, onRe
if (
showModal &&
event.target.closest('#parameter-form-popover') === null &&
- event.target.closest('#cm-complete-0') === null
+ event.target.closest('.cm-completionListIncompleteBottom') === null
) {
closeMenu();
}
};
if (showModal) {
- document.addEventListener('mouseup', handleClickOutside);
+ document.addEventListener('click', handleClickOutside);
} else {
- document.removeEventListener('mouseup', handleClickOutside);
+ document.removeEventListener('click', handleClickOutside);
}
return () => {
- document.removeEventListener('mouseup', handleClickOutside);
+ document.removeEventListener('click', handleClickOutside);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [showModal]);
diff --git a/frontend/src/Editor/SubContainer.jsx b/frontend/src/Editor/SubContainer.jsx
index 1fd04a9fa0..c664c87fea 100644
--- a/frontend/src/Editor/SubContainer.jsx
+++ b/frontend/src/Editor/SubContainer.jsx
@@ -7,12 +7,12 @@ import update from 'immutability-helper';
import _, { isEmpty } from 'lodash';
import { componentTypes } from './WidgetManager/components';
import { addNewWidgetToTheEditor, onComponentOptionChanged, onComponentOptionsChanged } from '@/_helpers/appUtils';
-import { resolveReferences } from '@/_helpers/utils';
+import { resolveWidgetFieldValue } from '@/_helpers/utils';
import { toast } from 'react-hot-toast';
import { restrictedWidgetsObj } from '@/Editor/WidgetManager/restrictedWidgetsConfig';
import { useCurrentState } from '@/_stores/currentStateStore';
import { shallow } from 'zustand/shallow';
-import { useMounted } from '@/_hooks/use-mount';
+
import { useEditorStore } from '@/_stores/editorStore';
// eslint-disable-next-line import/no-unresolved
import { diff } from 'deep-object-diff';
@@ -450,7 +450,7 @@ export const SubContainer = ({
const canShowInCurrentLayout =
box.component.definition.others[currentLayout === 'mobile' ? 'showOnMobile' : 'showOnDesktop'].value;
- if (box.component.parent && resolveReferences(canShowInCurrentLayout, currentState)) {
+ if (box.component.parent && resolveWidgetFieldValue(canShowInCurrentLayout)) {
return (
page.id === currentPageId);
useDataQueriesStore.getState().actions.setDataQueries(dataQueries);
+ useEditorStore.getState().actions.updateEditorState({
+ currentPageId: currentPageId,
+ });
this.props.setCurrentState({
queries: queryState,
components: {},
@@ -189,6 +198,25 @@ class ViewerComponent extends React.Component {
});
useEditorStore.getState().actions.toggleCurrentLayout(this.props?.currentLayout == 'mobile' ? 'mobile' : 'desktop');
this.props.updateState({ events: data.events ?? [] });
+ const currentPageComponents = appDefData?.pages[currentPageId]?.components;
+
+ if (currentPageComponents && !_.isEmpty(currentPageComponents)) {
+ const referenceManager = useResolveStore.getState().referenceMapper;
+
+ const newComponents = Object.keys(currentPageComponents).map((componentId) => {
+ const component = currentPageComponents[componentId];
+
+ if (!referenceManager.get(componentId)) {
+ return {
+ id: componentId,
+ name: component.component.name,
+ };
+ }
+ });
+
+ useResolveStore.getState().actions.addEntitiesToMap(newComponents);
+ }
+
this.setState(
{
currentUser,
@@ -206,10 +234,102 @@ class ViewerComponent extends React.Component {
events: data.events ?? [],
},
() => {
- const components = appDefData?.pages[currentPageId]?.components || {};
+ // const components = appDefData?.pages[currentPageId]?.components || {};
- computeComponentState(components).then(async () => {
+ const appJson = JSON.parse(JSON.stringify(appDefData));
+ const currentPageId = useEditorStore.getState().currentPageId;
+ const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components;
+ let dataQueries = JSON.parse(JSON.stringify(useDataQueriesStore.getState().dataQueries));
+ let allEvents = JSON.parse(JSON.stringify(useAppDataStore.getState().events));
+
+ const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, [])?.filter(
+ (entity) => entity && isValidUUID(entity)
+ );
+
+ const entityReferencesInQueryOptions = findAllEntityReferences(dataQueries, [])?.filter(
+ (entity) => entity && isValidUUID(entity)
+ );
+
+ const entityReferencesInEvents = findAllEntityReferences(allEvents, [])?.filter(
+ (entity) => entity && isValidUUID(entity)
+ );
+
+ const manager = useResolveStore.getState().referenceMapper;
+
+ if (
+ Array.isArray(entityReferencesInComponentDefinitions) &&
+ entityReferencesInComponentDefinitions?.length > 0
+ ) {
+ let newComponentDefinition = JSON.parse(JSON.stringify(currentComponents));
+
+ entityReferencesInComponentDefinitions.forEach((entity) => {
+ const entityrefExists = manager.has(entity);
+
+ if (entityrefExists) {
+ const value = manager.get(entity);
+ newComponentDefinition = dfs(newComponentDefinition, entity, value);
+ }
+ });
+
+ const newAppDefinition = produce(appJson, (draft) => {
+ draft.pages[homePageId].components = newComponentDefinition;
+ });
+
+ useEditorStore.getState().actions.updateEditorState({
+ isUpdatingEditorStateInProcess: false,
+ appDefinition: newAppDefinition,
+ });
+ }
+
+ if (Array.isArray(entityReferencesInQueryOptions) && entityReferencesInQueryOptions?.length > 0) {
+ let newQueryOptions = {};
+ dataQueries?.forEach((query) => {
+ newQueryOptions[query.id] = query.options;
+ ``;
+ });
+
+ entityReferencesInQueryOptions.forEach((entity) => {
+ const entityrefExists = manager.has(entity);
+
+ if (entityrefExists) {
+ const value = manager.get(entity);
+ newQueryOptions = dfs(newQueryOptions, entity, value);
+ }
+ });
+
+ dataQueries = dataQueries.map((query) => {
+ const queryId = query.id;
+ const dqOptions = newQueryOptions[queryId];
+
+ return {
+ ...query,
+ options: dqOptions,
+ };
+ });
+
+ useDataQueriesStore.getState().actions.setDataQueries(dataQueries, 'mappingUpdate');
+ }
+
+ if (Array.isArray(entityReferencesInEvents) && entityReferencesInEvents?.length > 0) {
+ let newEvents = JSON.parse(JSON.stringify(allEvents));
+
+ entityReferencesInEvents.forEach((entity) => {
+ const entityrefExists = manager.has(entity);
+
+ if (entityrefExists) {
+ const value = manager.get(entity);
+ newEvents = dfs(newEvents, entity, value);
+ }
+ });
+
+ this.props.updateState({
+ events: newEvents,
+ });
+ }
+
+ computeComponentState(currentComponents).then(async () => {
this.setState({ initialComputationOfStateDone: true, defaultComponentStateComputed: true });
+ useCurrentStateStore.getState().actions.setEditorReady(true);
this.runQueries(dataQueries);
const currentPageEvents = this.state.events.filter(
@@ -770,14 +890,52 @@ class ViewerComponent extends React.Component {
}
const withStore = (Component) => (props) => {
const currentState = useCurrentStateStore();
- const { currentLayout, queryConfirmationList } = useEditorStore(
+ const { currentLayout, queryConfirmationList, currentPageId } = useEditorStore(
(state) => ({
currentLayout: state?.currentLayout,
queryConfirmationList: state?.queryConfirmationList,
+ currentPageId: state?.currentPageId,
}),
shallow
);
+
+ const { updateComponentsNeedsUpdateOnNextRender } = useEditorActions();
const { updateState } = useAppDataActions();
+
+ const lastUpdatedRef = useResolveStore((state) => state.lastUpdatedRefs, shallow);
+
+ async function batchUpdateComponents(componentIds) {
+ if (componentIds.length === 0) return;
+
+ let updatedComponentIds = [];
+
+ for (let i = 0; i < componentIds.length; i += 10) {
+ const batch = componentIds.slice(i, i + 10);
+ batch.forEach((id) => {
+ updatedComponentIds.push(id);
+ });
+
+ updateComponentsNeedsUpdateOnNextRender(batch);
+ // Delay to allow UI to process
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ }
+
+ // Flush only updated components
+ flushComponentsToRender(updatedComponentIds);
+ }
+
+ React.useEffect(() => {
+ if (lastUpdatedRef.length > 0) {
+ const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components || {};
+ const componentIdsWithReferences = findComponentsWithReferences(currentComponents, lastUpdatedRef);
+
+ if (componentIdsWithReferences.length > 0) {
+ batchUpdateComponents(componentIdsWithReferences);
+ }
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [lastUpdatedRef]);
+
return (
{
_.unset(editingVersion, 'id');
const pages = data.pages.reduce((acc, page) => {
- const currentComponents = buildComponentMetaDefinition(_.cloneDeep(page?.components));
+ const currentComponents = buildComponentMetaDefinition(page?.components);
page.components = currentComponents;
diff --git a/frontend/src/_helpers/editorHelpers.js b/frontend/src/_helpers/editorHelpers.js
index 2971cd3bc0..00649a0750 100644
--- a/frontend/src/_helpers/editorHelpers.js
+++ b/frontend/src/_helpers/editorHelpers.js
@@ -174,7 +174,11 @@ export function findComponentsWithReferences(components, changedCurrentState) {
return componentIdsWithReferences;
}
-export function handleLowPriorityWork(callback, timeout = null) {
+export function handleLowPriorityWork(callback, timeout = null, immediate = false) {
+ if (immediate) {
+ callback();
+ }
+
const options = timeout ? { timeout } : {};
window.requestIdleCallback(callback, options);
}
diff --git a/frontend/src/_helpers/utils.js b/frontend/src/_helpers/utils.js
index 4d8fc06783..9ef51e0e57 100644
--- a/frontend/src/_helpers/utils.js
+++ b/frontend/src/_helpers/utils.js
@@ -8,10 +8,12 @@ import { toast } from 'react-hot-toast';
import { authenticationService } from '@/_services/authentication.service';
import { useDataQueriesStore } from '@/_stores/dataQueriesStore';
-import { getCurrentState } from '@/_stores/currentStateStore';
+import { getCurrentState, useCurrentState } from '@/_stores/currentStateStore';
import { getWorkspaceIdOrSlugFromURL, getSubpath, returnWorkspaceIdIfNeed } from './routes';
import { getCookie, eraseCookie } from '@/_helpers/cookie';
import { staticDataSources } from '@/Editor/QueryManager/constants';
+import { resolveReferences as newResolver } from '@/Editor/CodeEditor/utils';
+import { useResolveStore } from '@/_stores/resolverStore';
export function findProp(obj, prop, defval) {
if (typeof defval === 'undefined') defval = null;
@@ -324,10 +326,22 @@ export const serializeNestedObjectToQueryParams = function (obj, prefix) {
return str.join('&');
};
-export function resolveWidgetFieldValue(prop, state, _default = [], customResolveObjects = {}) {
+export function resolveWidgetFieldValue(prop, _default = [], customResolveObjects = {}) {
const widgetFieldValue = prop;
+ const isStoreAndEditorReady = useResolveStore.getState().updateStoreState && useCurrentState.getState().isEditorReady;
+
try {
+ if (isStoreAndEditorReady) {
+ const [_, _error, resolveValue] = newResolver(widgetFieldValue?.value);
+
+ if (_error) {
+ return _default;
+ }
+
+ return resolveValue;
+ }
+ const state = getCurrentState();
return resolveReferences(widgetFieldValue, state, _default, customResolveObjects);
} catch (err) {
console.log(err);
@@ -347,7 +361,7 @@ export function validateWidget({ validationObject, widgetValue, currentState, cu
const maxValue = validationObject?.maxValue?.value;
const customRule = validationObject?.customRule?.value;
const mandatory = validationObject?.mandatory?.value;
- const validationRegex = resolveWidgetFieldValue(regex, currentState, '', customResolveObjects);
+ const validationRegex = resolveWidgetFieldValue(regex, '', customResolveObjects);
const re = new RegExp(validationRegex, 'g');
if (!re.test(widgetValue)) {
@@ -357,7 +371,7 @@ export function validateWidget({ validationObject, widgetValue, currentState, cu
};
}
- const resolvedMinLength = resolveWidgetFieldValue(minLength, currentState, 0, customResolveObjects);
+ const resolvedMinLength = resolveWidgetFieldValue(minLength, 0, customResolveObjects);
if ((widgetValue || '').length < parseInt(resolvedMinLength)) {
return {
isValid: false,
@@ -365,7 +379,7 @@ export function validateWidget({ validationObject, widgetValue, currentState, cu
};
}
- const resolvedMaxLength = resolveWidgetFieldValue(maxLength, currentState, undefined, customResolveObjects);
+ const resolvedMaxLength = resolveWidgetFieldValue(maxLength, undefined, customResolveObjects);
if (resolvedMaxLength !== undefined) {
if ((widgetValue || '').length > parseInt(resolvedMaxLength)) {
return {
@@ -375,7 +389,7 @@ export function validateWidget({ validationObject, widgetValue, currentState, cu
}
}
- const resolvedMinValue = resolveWidgetFieldValue(minValue, currentState, undefined, customResolveObjects);
+ const resolvedMinValue = resolveWidgetFieldValue(minValue, undefined, customResolveObjects);
if (resolvedMinValue !== undefined) {
if (widgetValue === undefined || widgetValue < parseFloat(resolvedMinValue)) {
return {
@@ -395,12 +409,12 @@ export function validateWidget({ validationObject, widgetValue, currentState, cu
}
}
- const resolvedCustomRule = resolveWidgetFieldValue(customRule, currentState, false, customResolveObjects);
+ const resolvedCustomRule = resolveWidgetFieldValue(customRule, false, customResolveObjects);
if (typeof resolvedCustomRule === 'string' && resolvedCustomRule !== '') {
return { isValid: false, validationError: resolvedCustomRule };
}
- const resolvedMandatory = resolveWidgetFieldValue(mandatory, currentState, false, customResolveObjects);
+ const resolvedMandatory = resolveWidgetFieldValue(mandatory, false, customResolveObjects);
if (resolvedMandatory == true) {
if (!widgetValue) {
diff --git a/frontend/src/_hooks/useRenderCount.js b/frontend/src/_hooks/useRenderCount.js
index 3ad64f6b9e..c07b5734e3 100644
--- a/frontend/src/_hooks/useRenderCount.js
+++ b/frontend/src/_hooks/useRenderCount.js
@@ -3,15 +3,8 @@ import { useRef, useEffect } from 'react';
function useRenderCount(componentName) {
const renderCountRef = useRef(0);
- useEffect(() => {
- return () => {
- console.log(`--Component ${componentName} rendered unmounting ${renderCountRef.current} times.`);
- };
- }, []);
-
renderCountRef.current++;
- console.log(`CountingRender- Component ${componentName} rendered ${renderCountRef.current} times.`);
return renderCountRef.current;
}
diff --git a/frontend/src/_stores/appDataStore.js b/frontend/src/_stores/appDataStore.js
index f8227587c1..182fbee1b2 100644
--- a/frontend/src/_stores/appDataStore.js
+++ b/frontend/src/_stores/appDataStore.js
@@ -46,7 +46,6 @@ export const useAppDataStore = create(
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) => {
@@ -111,13 +110,10 @@ export const useAppDataStore = create(
}
});
- const entityReferencesInEvents = findAllEntityReferences(updatedEvents, [])
- ?.map((entity) => {
- if (entity && isValidUUID(entity)) {
- return entity;
- }
- })
- ?.filter((e) => e !== undefined);
+ const entityReferencesInEvents = findAllEntityReferences(updatedEvents, [])?.filter(
+ (entity) => entity && isValidUUID(entity)
+ );
+
const manager = useResolveStore.getState().referenceMapper;
let newEvents = JSON.parse(JSON.stringify(updatedEvents));
diff --git a/frontend/src/_stores/currentStateStore.js b/frontend/src/_stores/currentStateStore.js
index bb7af52df0..8d319124b9 100644
--- a/frontend/src/_stores/currentStateStore.js
+++ b/frontend/src/_stores/currentStateStore.js
@@ -2,9 +2,6 @@ import { shallow } from 'zustand/shallow';
import { create, zustandDevTools } from './utils';
import _, { debounce, merge, omit } from 'lodash';
import { useResolveStore } from './resolverStore';
-// eslint-disable-next-line import/no-unresolved
-import { diff } from 'deep-object-diff';
-import { useEditorStore } from './editorStore';
import { handleLowPriorityWork } from '@/_helpers/editorHelpers';
const initialState = {
@@ -88,19 +85,26 @@ useCurrentStateStore.subscribe((state) => {
const isStoreIntialized = useResolveStore.getState().storeReady;
if (!isStoreIntialized) {
- handleLowPriorityWork(() => {
- useResolveStore.getState().actions.updateAppSuggestions({
- queries: state.queries,
- components: state.components,
- globals: state.globals,
- page: state.page,
- variables: state.variables,
- client: state.client,
- server: state.server,
- constants: state.constants,
- });
- });
- console.log('Resolver store initialized with current state.');
+ const isPageSwitched = useResolveStore.getState().isPageSwitched;
+
+ handleLowPriorityWork(
+ () => {
+ useResolveStore.getState().actions.updateAppSuggestions({
+ queries: state.queries,
+ components: state.components,
+ globals: state.globals,
+ page: state.page,
+ variables: state.variables,
+ client: state.client,
+ server: state.server,
+ constants: state.constants,
+ });
+ useResolveStore.getState().actions.pageSwitched(false);
+ },
+ null,
+ isPageSwitched
+ );
+
return useResolveStore.getState().actions.updateStoreState({ storeReady: true });
}
}, shallow);
diff --git a/frontend/src/_stores/resolverStore.js b/frontend/src/_stores/resolverStore.js
index 48c6b94fcd..8483d4b2ce 100644
--- a/frontend/src/_stores/resolverStore.js
+++ b/frontend/src/_stores/resolverStore.js
@@ -60,6 +60,7 @@ const initialState = {
},
lastUpdatedRefs: [],
referenceMapper: new ReferencesBiMap(),
+ isPageSwitched: false,
};
export const useResolveStore = create(
@@ -69,6 +70,10 @@ export const useResolveStore = create(
updateStoreState: (state) => {
set(() => ({ ...state, storeReady: true }));
},
+ resetStore: () => {
+ set(() => initialState);
+ },
+ pageSwitched: (bool) => set(() => ({ isPageSwitched: bool })),
updateAppSuggestions: (refState) => {
const { suggestionList, hintsMap, resolvedRefs } = createReferencesLookup(refState, false, true);
@@ -136,13 +141,13 @@ export const useResolveStore = create(
},
removeAppSuggestions: (suggestionsArray) => {
- if (suggestionsArray.length === 0) return new Promise((resolve) => resolve({ status: '' }));
+ if (suggestionsArray?.length === 0) return new Promise((resolve) => resolve({ status: '' }));
const lookupHintsMap = new Map([...get().lookupTable.hints]);
const lookupResolvedRefs = new Map([...get().lookupTable.resolvedRefs]);
const currentSuggestions = get().suggestions.appHints;
- suggestionsArray.forEach((suggestion) => {
+ suggestionsArray?.forEach((suggestion) => {
const index = currentSuggestions.findIndex((s) => s.hint === suggestion);
if (index === -1) return;
diff --git a/frontend/src/_stores/utils.js b/frontend/src/_stores/utils.js
index e7d2df97e4..07899b3341 100644
--- a/frontend/src/_stores/utils.js
+++ b/frontend/src/_stores/utils.js
@@ -505,7 +505,12 @@ export function findAllEntityReferences(node, allRefs) {
if (typeof node === 'object') {
for (let key in node) {
const value = node[key];
- if (typeof value === 'string' && value.includes('{{') && value.includes('}}')) {
+ if (
+ typeof value === 'string' &&
+ value.includes('{{') &&
+ value.includes('}}') &&
+ (value.startsWith('{{components') || value.startsWith('queries'))
+ ) {
const referenceExists = value;
if (referenceExists) {
diff --git a/server/src/services/app_import_export.service.ts b/server/src/services/app_import_export.service.ts
index d30e0f936e..147c024887 100644
--- a/server/src/services/app_import_export.service.ts
+++ b/server/src/services/app_import_export.service.ts
@@ -32,7 +32,6 @@ import { Component } from 'src/entities/component.entity';
import { Layout } from 'src/entities/layout.entity';
import { EventHandler, Target } from 'src/entities/event_handler.entity';
import { v4 as uuid } from 'uuid';
-
interface AppResourceMappings {
defaultDataSourceIdMapping: Record;
dataQueryMapping: Record;
diff --git a/server/src/services/apps.service.ts b/server/src/services/apps.service.ts
index fa28f609bd..f1f54bb27a 100644
--- a/server/src/services/apps.service.ts
+++ b/server/src/services/apps.service.ts
@@ -25,12 +25,19 @@ import { DataBaseConstraints } from 'src/helpers/db_constraints.constants';
import { Page } from 'src/entities/page.entity';
import { AppVersionUpdateDto } from '@dto/app-version-update.dto';
import { Layout } from 'src/entities/layout.entity';
-
import { Component } from 'src/entities/component.entity';
import { EventHandler } from 'src/entities/event_handler.entity';
import { VersionReleaseDto } from '@dto/version-release.dto';
+import { findAllEntityReferences, isValidUUID, updateEntityReferences } from 'src/helpers/import_export.helpers';
+import { isEmpty } from 'lodash';
const uuid = require('uuid');
+
+interface AppResourceMappings {
+ dataQueryMapping: Record;
+ componentsMapping: Record;
+}
+
@Injectable()
export class AppsService {
constructor(
@@ -392,6 +399,11 @@ export class AppsService {
const { oldComponentToNewComponentMapping, oldPageToNewPageMapping } =
await this.createNewPagesAndComponentsForVersion(manager, appVersion, versionFrom.id, versionFrom.homePageId);
+ await this.updateEntityReferencesForNewVersion(manager, {
+ componentsMapping: oldComponentToNewComponentMapping,
+ dataQueryMapping: oldDataQueryToNewMapping,
+ });
+
await this.updateEventActionsForNewVersionWithNewMappingIds(
manager,
appVersion.id,
@@ -405,6 +417,64 @@ export class AppsService {
}, manager);
}
+ async updateEntityReferencesForNewVersion(manager: EntityManager, resourceMapping: AppResourceMappings) {
+ const mappings = { ...resourceMapping.componentsMapping, ...resourceMapping.dataQueryMapping };
+ const newComponentIds = Object.values(resourceMapping.componentsMapping);
+ const newQueriesIds = Object.values(resourceMapping.dataQueryMapping);
+
+ if (newComponentIds.length > 0) {
+ const components = await manager
+ .createQueryBuilder(Component, 'components')
+ .where('components.id IN(:...componentIds)', { componentIds: newComponentIds })
+ .select([
+ 'components.id',
+ 'components.properties',
+ 'components.styles',
+ 'components.general',
+ 'components.validation',
+ 'components.generalStyles',
+ 'components.displayPreferences',
+ ])
+ .getMany();
+
+ const toUpdateComponents = components.filter((component) => {
+ const entityReferencesInComponentDefinitions = findAllEntityReferences(component, []).filter(
+ (entity) => entity && isValidUUID(entity)
+ );
+
+ if (entityReferencesInComponentDefinitions.length > 0) {
+ return updateEntityReferences(component, mappings);
+ }
+ });
+
+ if (!isEmpty(toUpdateComponents)) {
+ await manager.save(toUpdateComponents);
+ }
+ }
+
+ if (newQueriesIds.length > 0) {
+ const dataQueries = await manager
+ .createQueryBuilder(DataQuery, 'dataQueries')
+ .where('dataQueries.id IN(:...dataQueryIds)', { dataQueryIds: newQueriesIds })
+ .select(['dataQueries.id', 'dataQueries.options'])
+ .getMany();
+
+ const toUpdateDataQueries = dataQueries.filter((dataQuery) => {
+ const entityReferencesInQueryOptions = findAllEntityReferences(dataQuery, []).filter(
+ (entity) => entity && isValidUUID(entity)
+ );
+
+ if (entityReferencesInQueryOptions.length > 0) {
+ return updateEntityReferences(dataQuery, mappings);
+ }
+ });
+
+ if (!isEmpty(toUpdateDataQueries)) {
+ await manager.save(toUpdateDataQueries);
+ }
+ }
+ }
+
async updateEventActionsForNewVersionWithNewMappingIds(
manager: EntityManager,
versionId: string,
@@ -416,8 +486,10 @@ export class AppsService {
where: { appVersionId: versionId },
});
+ const mappings = { ...oldDataQueryToNewMapping, ...oldComponentToNewComponentMapping } as Record;
+
for (const event of allEvents) {
- const eventDefinition = event.event;
+ const eventDefinition = updateEntityReferences(event.event, mappings);
if (eventDefinition?.actionId === 'run-query') {
eventDefinition.queryId = oldDataQueryToNewMapping[eventDefinition.queryId];