diff --git a/.version b/.version index 3a2b37ff57..bc2e849859 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.50.6 +2.50.7 diff --git a/frontend/.version b/frontend/.version index 3a2b37ff57..bc2e849859 100644 --- a/frontend/.version +++ b/frontend/.version @@ -1 +1 @@ -2.50.6 +2.50.7 diff --git a/frontend/src/Editor/Components/Form/Form.jsx b/frontend/src/Editor/Components/Form/Form.jsx index 1c861bf8e7..3a592dfc9a 100644 --- a/frontend/src/Editor/Components/Form/Form.jsx +++ b/frontend/src/Editor/Components/Form/Form.jsx @@ -3,7 +3,7 @@ import { SubCustomDragLayer } from '@/Editor/SubCustomDragLayer'; import { SubContainer } from '@/Editor/SubContainer'; // eslint-disable-next-line import/no-unresolved import { diff } from 'deep-object-diff'; -import _, { omit } from 'lodash'; +import _, { debounce, omit } from 'lodash'; import { Box } from '@/Editor/Box'; import { generateUIComponents } from './FormUtils'; import { useMounted } from '@/_hooks/use-mount'; @@ -189,7 +189,9 @@ export const Form = function Form(props) { }; const fireSubmissionEvent = () => { if (isValid) { - onEvent('onSubmit', formEvents).then(() => resetComponent()); + onEvent('onSubmit', formEvents).then(() => { + debounce(() => resetComponent(), 100)(); + }); } else { fireEvent('onInvalid'); } diff --git a/frontend/src/Editor/Container.jsx b/frontend/src/Editor/Container.jsx index 000dd67328..212221bf50 100644 --- a/frontend/src/Editor/Container.jsx +++ b/frontend/src/Editor/Container.jsx @@ -258,16 +258,18 @@ export const Container = ({ return; } - if (!appDefinition.pages[currentPageId]?.components) return; + const definition = useEditorStore.getState().appDefinition; + + if (!definition.pages[currentPageId]?.components) return; const newDefinition = { - ...appDefinition, + ...definition, pages: { - ...appDefinition.pages, + ...definition.pages, [currentPageId]: { - ...appDefinition.pages[currentPageId], + ...definition.pages[currentPageId], components: { - ...appDefinition.pages[currentPageId]?.components, + ...definition.pages[currentPageId]?.components, ...boxes, }, }, @@ -276,7 +278,7 @@ export const Container = ({ //need to check if a new component is added or deleted - const oldComponents = appDefinition.pages[currentPageId]?.components ?? {}; + const oldComponents = definition.pages[currentPageId]?.components ?? {}; const newComponents = boxes; const componendAdded = Object.keys(newComponents).length > Object.keys(oldComponents).length; @@ -289,7 +291,8 @@ export const Container = ({ opts.componentAdded = true; } - const shouldUpdate = !_.isEmpty(diff(appDefinition, newDefinition)); + const shouldUpdate = !_.isEmpty(diff(definition, newDefinition)); + if (shouldUpdate) { appDefinitionChanged(newDefinition, opts); } @@ -1045,15 +1048,16 @@ const WidgetWrapper = ({ const isWidgetActive = (isSelected || isDragging) && mode !== 'view'; const { label = { value: null } } = propertiesDefinition ?? {}; + const visibility = propertiesDefinition?.visibility?.value ?? stylesDefinition?.visibility?.value ?? null; + const resolvedVisibility = resolveWidgetFieldValue(visibility); const styles = { width: width + 'px', - height: calculateMoveableBoxHeight() + 'px', + height: resolvedVisibility ? calculateMoveableBoxHeight() + 'px' : '10px', transform: `translate(${layoutData.left * gridWidth}px, ${layoutData.top}px)`, ...(isGhostComponent ? { opacity: 0.5 } : {}), ...(isWidgetActive ? { zIndex: 3 } : {}), }; - return ( <>
{ return ( deepEqualityCheckusingLoDash(prevProps?.id, nextProps?.id) && deepEqualityCheckusingLoDash(prevProps?.component?.definition, nextProps?.component?.definition) && + deepEqualityCheckusingLoDash(prevProps?.customResolvables, nextProps?.customResolvables) && prevProps?.width === nextProps?.width && prevProps?.height === nextProps?.height && prevProps?.darkMode === nextProps?.darkMode && diff --git a/frontend/src/Editor/SubContainer.jsx b/frontend/src/Editor/SubContainer.jsx index bacce01b9e..fd7af6a003 100644 --- a/frontend/src/Editor/SubContainer.jsx +++ b/frontend/src/Editor/SubContainer.jsx @@ -190,22 +190,24 @@ export const SubContainer = ({ }, [containerWidth]); useEffect(() => { - if (appDefinitionChanged) { + const definition = useEditorStore.getState().appDefinition; + + if (definition) { const newDefinition = { - ...appDefinition, + ...definition, pages: { - ...appDefinition.pages, + ...definition.pages, [currentPageId]: { - ...appDefinition.pages[currentPageId], + ...definition.pages[currentPageId], components: { - ...appDefinition.pages[currentPageId].components, + ...definition.pages[currentPageId].components, ...childWidgets, }, }, }, }; - const oldComponents = appDefinition.pages[currentPageId]?.components ?? {}; + const oldComponents = definition.pages[currentPageId]?.components ?? {}; const newComponents = newDefinition.pages[currentPageId]?.components ?? {}; const componendAdded = Object.keys(newComponents).length > Object.keys(oldComponents).length; @@ -216,7 +218,7 @@ export const SubContainer = ({ opts.componentAdded = true; } - const shouldUpdate = !_.isEmpty(diff(appDefinition, newDefinition)); + const shouldUpdate = !_.isEmpty(diff(definition, newDefinition)); if (shouldUpdate) { appDefinitionChanged(newDefinition, opts); @@ -716,11 +718,19 @@ const SubWidgetWrapper = ({ const isDragging = useGridStore((state) => state?.draggingComponentId === id); + const isComponentVisible = () => { + const visibility = + widget.component.definition?.properties?.visibility?.value ?? + widget.component.definition?.styles?.visibility?.value ?? + null; + return resolveWidgetFieldValue(visibility); + }; + let width = (canvasWidth * layoutData.width) / 43; width = width > canvasWidth ? canvasWidth : width; //this handles scenarios where the width is set more than canvas for older components const styles = { width: width + 'px', - height: layoutData.height + 'px', + height: isComponentVisible() ? layoutData.height + 'px' : '10px', transform: `translate(${layoutData.left * gridWidth}px, ${layoutData.top}px)`, ...(isGhostComponent ? { opacity: 0.5 } : {}), }; diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js index 922b1f2e01..12afe5f831 100644 --- a/frontend/src/_helpers/appUtils.js +++ b/frontend/src/_helpers/appUtils.js @@ -105,15 +105,22 @@ export function onComponentOptionsChanged(component, options, id) { componentData = deepClone(componentData) || {}; const shouldUpdateResolvedRefsOfHints = []; - + const isListviewOrKanbaComponent = component.component === 'Listview' || component.component === 'Kanban'; + const isFromComponent = component.component === 'Form'; for (const option of options) { componentData[option[0]] = option[1]; - const isListviewOrKanbaComponent = component.component === 'Listview' || component.component === 'Kanban'; - let path = null; if (isListviewOrKanbaComponent) { path = `components.${componentName}`; + } else if (isFromComponent) { + const basePath = `components.${componentName}.${option[0]}`; + + useResolveStore.getState().actions.addAppSuggestions({ + [basePath]: option[1], + }); + + shouldUpdateResolvedRefsOfHints.push({ hint: basePath, newRef: componentData[option[1]] }); } else { path = `components.${componentName}.${option[0]}`; } @@ -405,7 +412,7 @@ export async function executeActionsForEventId(_ref, eventId, events = [], mode, const filteredEvents = events?.filter((event) => event?.event.eventId === eventId)?.sort((a, b) => a.index - b.index); for (const event of filteredEvents) { - await executeAction(_ref, event.event, mode, customVariables); // skipcq: JS-0032 + await executeActionWithDebounce(_ref, event.event, mode, customVariables); } } @@ -745,9 +752,8 @@ export async function onEvent(_ref, eventName, events, options = {}, mode = 'edi const { customVariables } = options; if (eventName === 'onPageLoad') { - return _.debounce(async () => { - await executeActionsForEventId(_ref, 'onPageLoad', events, mode, customVariables); - }, 10); + // for onPageLoad events, we need to execute the actions after the page is loaded + handleLowPriorityWork(() => executeActionsForEventId(_ref, 'onPageLoad', events, mode, customVariables)); } if (eventName === 'onTrigger') { @@ -1221,6 +1227,16 @@ export function runQuery( }); resolve(data); onEvent(_self, 'onDataQueryFailure', queryEvents); + + const toUpdateRefs = [ + { hint: `queries.${queryName}.isLoading`, newRef: false }, + { + hint: `queries.${queryName}.data`, + newRef: [], + }, + ]; + + useResolveStore.getState().actions.updateResolvedRefsOfHints(toUpdateRefs); if (mode !== 'view') { const err = query.kind == 'tooljetdb' ? data?.error || data : data; toast.error(err?.message ? err?.message : 'Something went wrong'); @@ -1652,7 +1668,7 @@ export const cloneComponents = ( }); }; -const getAllChildComponents = (allComponents, parentId) => { +export const getAllChildComponents = (allComponents, parentId) => { const childComponents = []; Object.keys(allComponents).forEach((componentId) => { @@ -1660,7 +1676,8 @@ const getAllChildComponents = (allComponents, parentId) => { const isParentTabORCalendar = allComponents[parentId]?.component?.component === 'Tabs' || - allComponents[parentId]?.component?.component === 'Calendar'; + allComponents[parentId]?.component?.component === 'Calendar' || + allComponents[parentId]?.component?.component === 'Kanban'; if (componentParentId && isParentTabORCalendar) { const childComponent = allComponents[componentId]; diff --git a/frontend/src/_helpers/utility.js b/frontend/src/_helpers/utility.js index 3906f25460..ea1c6e34ee 100644 --- a/frontend/src/_helpers/utility.js +++ b/frontend/src/_helpers/utility.js @@ -2,7 +2,7 @@ import { useResolveStore } from '@/_stores/resolverStore'; import _ from 'lodash'; export function validateMultilineCode(code) { - const reservedKeyword = ['app', 'window', 'this']; // Case-sensitive reserved keywords + const reservedKeyword = ['app', 'this']; // Case-sensitive reserved keywords except 'window' const keywordRegex = new RegExp(`\\b(${reservedKeyword.join('|')})\\b`, 'i'); let inString = false; let inComment = false; @@ -33,6 +33,18 @@ export function validateMultilineCode(code) { // If we are not within a string or a comment, check for keywords if (!inString && !inComment) { + // Special handling for 'window' + if (code.substring(i, i + 6) === 'window' && (code[i + 6] === undefined || code[i + 6] !== '.')) { + return { + status: 'failed', + data: { + message: `Code contains reserved keyword 'window'`, + description: + 'Cannot resolve code with reserved keyword "window" in it unless it is followed by a dot. Please remove it and try again.', + }, + }; + } + const restOfCode = code.substring(i); const match = restOfCode.match(keywordRegex); diff --git a/server/.version b/server/.version index 3a2b37ff57..bc2e849859 100644 --- a/server/.version +++ b/server/.version @@ -1 +1 @@ -2.50.6 +2.50.7 diff --git a/server/src/services/components.service.ts b/server/src/services/components.service.ts index 35f51a314a..8193b4d9ed 100644 --- a/server/src/services/components.service.ts +++ b/server/src/services/components.service.ts @@ -108,13 +108,12 @@ export class ComponentsService { return acc; }, {}); + // Update the component with merged data await manager.update(Component, componentId, newComponentsData); - return; + } else { + // Update the component directly if definition is not changed + await manager.update(Component, componentId, component); } - - await manager.update(Component, componentId, component); - - return; } }, appVersionId); }