From 5a1427e2e426d05d1c958c5de1e0554b429c608a Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Fri, 28 Feb 2025 01:48:53 +0530 Subject: [PATCH 01/18] Added auto scroll functionality if the component list is long in inspector. --- .../LeftSidebarInspector/LeftSidebarInspector.jsx | 14 ++++++++++++-- frontend/src/_ui/JSONTreeViewer/JSONNode.jsx | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/LeftSidebarInspector.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/LeftSidebarInspector.jsx index dc4b2cf7af..3adca4be98 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/LeftSidebarInspector.jsx +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/LeftSidebarInspector.jsx @@ -88,9 +88,19 @@ const LeftSidebarInspector = ({ darkMode, pinned, setPinned }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [sortedComponents, sortedQueries, sortedVariables, sortedConstants, sortedPageVariables, sortedGlobalVariables]); - const handleNodeExpansion = (path) => { + const handleNodeExpansion = (path, data, currentNode) => { if (pathToBeInspected && path?.length > 0) { - return pathToBeInspected.includes(path[path.length - 1]); + const shouldExpand = pathToBeInspected.includes(path[path.length - 1]); + + // Scroll to the component in the inspector + if (path?.length === 2 && path?.[0] === 'components' && shouldExpand) { + const target = document.getElementById(`inspector-node-${String(currentNode).toLowerCase()}`); + if (target) { + target.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + } + + return shouldExpand; } else return false; }; diff --git a/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx b/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx index 65a4ece91d..8913dbb277 100644 --- a/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx +++ b/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx @@ -53,7 +53,7 @@ export const JSONNode = ({ data, ...restProps }) => { React.useEffect(() => { if (typeof shouldExpandNode === 'function') { - set(shouldExpandNode(path, data)); + set(shouldExpandNode(path, data, currentNode)); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [pathToBeInspected]); @@ -337,6 +337,7 @@ export const JSONNode = ({ data, ...restProps }) => { 'group-object-container': shouldDisplayIntendedBlock, 'mx-2': typeofCurrentNode !== 'Object' && typeofCurrentNode !== 'Array', })} + id={`inspector-node-${String(currentNode).toLowerCase()}`} data-cy={`inspector-node-${String(currentNode).toLowerCase()}`} > {$NODEIcon &&
{$NODEIcon}
} From 1ad1d54c82b86358f5185aced3723f055fe07cbe Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Fri, 28 Feb 2025 16:26:07 +0530 Subject: [PATCH 02/18] Refactored the code and added conditions for tabs, modal and kanban components as parent component. --- .../useCallbackActions.js | 27 ++---------- .../_stores/slices/leftSideBarSlice.js | 43 +++++++++++++++++++ 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js index 7067cd540d..6fea25c151 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js @@ -30,30 +30,11 @@ const useCallbackActions = () => { return toast.success('Copied to the clipboard', { position: 'top-center' }); }; - const autoScrollTo = (id) => { - setSelectedComponents([id]); - const target = document.getElementById(id); - target.scrollIntoView({ behavior: 'smooth', block: 'center' }); - }; - const handleAutoScrollToComponent = (data) => { - const currentPageComponents = useStore.getState().getCurrentPageComponents(); - const component = currentPageComponents?.[data.id]; - - let parentId = component?.component?.parent; - if (parentId) { - const regex = /-\d+$/; - if (regex.test(parentId)) { - parentId = parentId.replace(regex, ''); // To get parentId without tab index if parent type is Tab - } - const parentType = currentPageComponents?.[parentId]?.component?.component; - if (parentType && (parentType === 'Modal' || parentType === 'Tabs')) { - autoScrollTo(parentId); // To scroll to parent component if parent type is Modal or Tabs - return; - } - } - - autoScrollTo(data.id); + const computedComponentId = useStore.getState().getComponentIdToAutoScroll(data.id); + setSelectedComponents([computedComponentId]); + const target = document.getElementById(computedComponentId); + target.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }; const callbackActions = [ diff --git a/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js b/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js index 98decac629..77e8b5f284 100644 --- a/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js @@ -37,4 +37,47 @@ export const createLeftSideBarSlice = (set, get) => ({ toggleLeftSidebar(true); } }, + getComponentIdToAutoScroll: (componentId) => { + const { getCurrentPageComponents, getAllExposedValues } = get(); + const currentPageComponents = getCurrentPageComponents(); + const component = currentPageComponents?.[componentId]; + let parentId = component?.component?.parent; + + if (!parentId) { + return componentId; + } + + // If the component exists inside a tab component + const regForTabs = /-(?!\d{12}$)\d+$/; // Parent id for tabs follow the format 'id-index' and index is not UUIDv4 id segment + if (regForTabs.test(parentId)) { + const reg = /-(\d+)$/; + const tabIndex = Number(parentId.match(reg)[1]); // Tab index inside which the component exists + + parentId = parentId.replace(regForTabs, ''); // Extract tab id from parent id + const { currentTab } = getAllExposedValues().components[parentId]; + const activeTabIndex = Number(currentTab); + + if (tabIndex !== activeTabIndex) { + return parentId; + } else return componentId; + } + + const parentExposedValues = getAllExposedValues().components[parentId]; + const parentComponent = currentPageComponents?.[parentId]; + + // If the component exists inside a modal component + if (parentComponent?.component?.component === 'Modal') { + if (parentExposedValues?.show) { + return componentId; + } else return parentId; + } + + // If the component exists inside the kanban component's modal + if (parentId.endsWith('-modal')) { + return parentId.replace(/-modal$/, ''); // Extract kanban id from parent id + } + + // If the component exists inside any other component + return componentId; + }, }); From a6e66889c27eb53de62935c6f6bcd76aca2d6182 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Mon, 3 Mar 2025 14:07:47 +0530 Subject: [PATCH 03/18] Added state variable to keep track of open modals on canvas. --- .../src/AppBuilder/Widgets/Kanban/KanbanBoard.jsx | 2 ++ frontend/src/AppBuilder/Widgets/Modal.jsx | 2 ++ .../AppBuilder/_stores/slices/componentsSlice.js | 14 ++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/frontend/src/AppBuilder/Widgets/Kanban/KanbanBoard.jsx b/frontend/src/AppBuilder/Widgets/Kanban/KanbanBoard.jsx index 34efc57221..2621708532 100644 --- a/frontend/src/AppBuilder/Widgets/Kanban/KanbanBoard.jsx +++ b/frontend/src/AppBuilder/Widgets/Kanban/KanbanBoard.jsx @@ -56,6 +56,7 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef, id }) { const [containers, setContainers] = useState([]); const [showModal, setShowModal] = useState(false); + const setModalOpenOnCanvas = useStore((state) => state.setModalOpenOnCanvas); const [activeId, setActiveId] = useState(null); const cardMovementRef = useRef(null); const shouldUpdateData = useRef(false); @@ -117,6 +118,7 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef, id }) { } /**** End - Logic to reduce the zIndex of modal control box ****/ } + setModalOpenOnCanvas(`${id}-modal`, showModal); }, [showModal]); useEffect(() => { diff --git a/frontend/src/AppBuilder/Widgets/Modal.jsx b/frontend/src/AppBuilder/Widgets/Modal.jsx index 5543ce4ee0..e0f099205f 100644 --- a/frontend/src/AppBuilder/Widgets/Modal.jsx +++ b/frontend/src/AppBuilder/Widgets/Modal.jsx @@ -49,6 +49,7 @@ export const Modal = function Modal({ const size = properties.size ?? 'lg'; const [modalWidth, setModalWidth] = useState(); const mode = useStore((state) => state.currentMode, shallow); + const setModalOpenOnCanvas = useStore((state) => state.setModalOpenOnCanvas); /**** Start - Logic to reset the zIndex of modal control box ****/ useEffect(() => { @@ -63,6 +64,7 @@ export const Modal = function Modal({ useGridStore.getState().actions.setOpenModalWidgetId(null); } } + setModalOpenOnCanvas(id, showModal); }, [showModal, id, mode]); /**** End - Logic to reset the zIndex of modal control box ****/ diff --git a/frontend/src/AppBuilder/_stores/slices/componentsSlice.js b/frontend/src/AppBuilder/_stores/slices/componentsSlice.js index 1f6ce18405..8690c428f9 100644 --- a/frontend/src/AppBuilder/_stores/slices/componentsSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/componentsSlice.js @@ -40,6 +40,7 @@ const initialState = { currentPageHandle: null, showWidgetDeleteConfirmation: false, focusedParentId: null, + modalsOpenOnCanvas: [], }; export const createComponentsSlice = (set, get) => ({ @@ -1860,4 +1861,17 @@ export const createComponentsSlice = (set, get) => ({ const currentPage = getCurrentPage(moduleId); return currentPage?.autoComputeLayout; }, + setModalOpenOnCanvas: (modalId, isOpen) => { + const { modalsOpenOnCanvas } = get(); + let newModalOpenOnCanvas = []; + + if (isOpen) { + newModalOpenOnCanvas = [...modalsOpenOnCanvas, modalId]; + } else { + newModalOpenOnCanvas = modalsOpenOnCanvas.filter((id) => id !== modalId); + } + set((state) => { + state.modalsOpenOnCanvas = newModalOpenOnCanvas; + }); + }, }); From 6dd933fa63c77df1047cbccc1eec1459fdd65cb1 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Mon, 3 Mar 2025 14:18:22 +0530 Subject: [PATCH 04/18] Implemented logic for handling nested components. --- .../_stores/slices/leftSideBarSlice.js | 108 ++++++++++++------ 1 file changed, 73 insertions(+), 35 deletions(-) diff --git a/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js b/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js index 77e8b5f284..3cc424f790 100644 --- a/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js @@ -38,46 +38,84 @@ export const createLeftSideBarSlice = (set, get) => ({ } }, getComponentIdToAutoScroll: (componentId) => { - const { getCurrentPageComponents, getAllExposedValues } = get(); + const { getCurrentPageComponents, getAllExposedValues, modalsOpenOnCanvas } = get(); const currentPageComponents = getCurrentPageComponents(); - const component = currentPageComponents?.[componentId]; - let parentId = component?.component?.parent; - if (!parentId) { - return componentId; + let candidate = componentId; + let current = componentId; + const visited = new Set(); + let closedUnusedModals = false; // Flag to check if we have closed all unused modals on the canvas + + // eslint-disable-next-line no-constant-condition + while (true) { + if (visited.has(current)) break; + visited.add(current); + + const component = currentPageComponents?.[current]; + if (!component) break; + + const parentId = component?.component?.parent; + if (!parentId) break; + + let isParentInactive = false; + let newCandidate = candidate; + + // If the component exists inside a tab component + const regForTabs = /-(?!\d{12}$)\d+$/; // Parent id for tabs follow the format 'id-index' and index is not UUIDv4 id segment + if (regForTabs.test(parentId)) { + const reg = /-(\d+)$/; + const tabIndex = Number(parentId.match(reg)[1]); // Tab index inside which the component exists + + const tabId = parentId.replace(regForTabs, ''); // Extract tab id from parent id + const { currentTab } = getAllExposedValues().components[tabId]; + const activeTabIndex = Number(currentTab); + + if (tabIndex !== activeTabIndex) { + isParentInactive = true; + newCandidate = tabId; + } + } + + const parentExposedValues = getAllExposedValues().components[parentId]; + + // If the component exists inside a modal component + if (currentPageComponents?.[parentId]?.component?.component === 'Modal') { + // Close all modals that are open on the canvas until we get to the parent modal + if (modalsOpenOnCanvas.length > 0 && !closedUnusedModals) { + if (!modalsOpenOnCanvas.includes(parentId)) { + modalsOpenOnCanvas.map((modalId) => getAllExposedValues().components[modalId]?.close()); + } else { + const idx = modalsOpenOnCanvas.indexOf(parentId); + modalsOpenOnCanvas.slice(idx + 1).map((modalId) => getAllExposedValues().components[modalId]?.close()); + } + closedUnusedModals = true; + } + + if (!parentExposedValues?.show) { + isParentInactive = true; + newCandidate = parentId; + } + } + + // If the component exists inside the kanban component's modal + if (parentId.endsWith('-modal')) { + isParentInactive = true; + newCandidate = parentId.replace(/-modal$/, ''); // Extract kanban id from parent id + } + + if (isParentInactive) { + candidate = newCandidate; + current = newCandidate; + } else { + current = parentId; + } } - // If the component exists inside a tab component - const regForTabs = /-(?!\d{12}$)\d+$/; // Parent id for tabs follow the format 'id-index' and index is not UUIDv4 id segment - if (regForTabs.test(parentId)) { - const reg = /-(\d+)$/; - const tabIndex = Number(parentId.match(reg)[1]); // Tab index inside which the component exists - - parentId = parentId.replace(regForTabs, ''); // Extract tab id from parent id - const { currentTab } = getAllExposedValues().components[parentId]; - const activeTabIndex = Number(currentTab); - - if (tabIndex !== activeTabIndex) { - return parentId; - } else return componentId; + // Close all modals that are open on the canvas if the component is not inside any of the modals + if (modalsOpenOnCanvas.length > 0 && !closedUnusedModals) { + modalsOpenOnCanvas.map((modalId) => getAllExposedValues().components[modalId]?.close()); } - const parentExposedValues = getAllExposedValues().components[parentId]; - const parentComponent = currentPageComponents?.[parentId]; - - // If the component exists inside a modal component - if (parentComponent?.component?.component === 'Modal') { - if (parentExposedValues?.show) { - return componentId; - } else return parentId; - } - - // If the component exists inside the kanban component's modal - if (parentId.endsWith('-modal')) { - return parentId.replace(/-modal$/, ''); // Extract kanban id from parent id - } - - // If the component exists inside any other component - return componentId; + return candidate; }, }); From 46ab01fead51186eba26fb88cc5243d296090e14 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Tue, 4 Mar 2025 03:09:50 +0530 Subject: [PATCH 05/18] Removed logic for closing modals and refactored the code for better readability. --- .../useCallbackActions.js | 4 +- .../_stores/slices/leftSideBarSlice.js | 67 ++++++++----------- 2 files changed, 31 insertions(+), 40 deletions(-) diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js index 6fea25c151..bf661ea20d 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js @@ -9,6 +9,7 @@ const useCallbackActions = () => { const currentPageComponents = useStore((state) => state?.getCurrentPageComponents(), shallow); const shouldFreeze = useStore((state) => state.getShouldFreeze()); const runQuery = useStore((state) => state.queryPanel.runQuery); + const getComponentIdToAutoScroll = useStore((state) => state.getComponentIdToAutoScroll); const handleRemoveComponent = (component) => { deleteComponents([component.id]); @@ -31,7 +32,8 @@ const useCallbackActions = () => { }; const handleAutoScrollToComponent = (data) => { - const computedComponentId = useStore.getState().getComponentIdToAutoScroll(data.id); + const computedComponentId = getComponentIdToAutoScroll(data.id); + if (!computedComponentId) return; setSelectedComponents([computedComponentId]); const target = document.getElementById(computedComponentId); target.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); diff --git a/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js b/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js index 3cc424f790..da520544b2 100644 --- a/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js @@ -41,24 +41,22 @@ export const createLeftSideBarSlice = (set, get) => ({ const { getCurrentPageComponents, getAllExposedValues, modalsOpenOnCanvas } = get(); const currentPageComponents = getCurrentPageComponents(); - let candidate = componentId; + let targetComponentId = componentId; let current = componentId; const visited = new Set(); - let closedUnusedModals = false; // Flag to check if we have closed all unused modals on the canvas + let isInsideOpenModal = false; + // Bubble up to the outermost parent to find the target component // eslint-disable-next-line no-constant-condition while (true) { if (visited.has(current)) break; visited.add(current); - const component = currentPageComponents?.[current]; - if (!component) break; - - const parentId = component?.component?.parent; + const parentId = currentPageComponents?.[current]?.component?.parent; if (!parentId) break; - let isParentInactive = false; - let newCandidate = candidate; + let isComponentVisibleInParent = true; + let nextPossibleCandidate = parentId; // If the component exists inside a tab component const regForTabs = /-(?!\d{12}$)\d+$/; // Parent id for tabs follow the format 'id-index' and index is not UUIDv4 id segment @@ -67,55 +65,46 @@ export const createLeftSideBarSlice = (set, get) => ({ const tabIndex = Number(parentId.match(reg)[1]); // Tab index inside which the component exists const tabId = parentId.replace(regForTabs, ''); // Extract tab id from parent id - const { currentTab } = getAllExposedValues().components[tabId]; + + const { currentTab } = getAllExposedValues().components?.[tabId] || {}; const activeTabIndex = Number(currentTab); + nextPossibleCandidate = tabId; if (tabIndex !== activeTabIndex) { - isParentInactive = true; - newCandidate = tabId; + isComponentVisibleInParent = false; } } - const parentExposedValues = getAllExposedValues().components[parentId]; - // If the component exists inside a modal component if (currentPageComponents?.[parentId]?.component?.component === 'Modal') { - // Close all modals that are open on the canvas until we get to the parent modal - if (modalsOpenOnCanvas.length > 0 && !closedUnusedModals) { - if (!modalsOpenOnCanvas.includes(parentId)) { - modalsOpenOnCanvas.map((modalId) => getAllExposedValues().components[modalId]?.close()); - } else { - const idx = modalsOpenOnCanvas.indexOf(parentId); - modalsOpenOnCanvas.slice(idx + 1).map((modalId) => getAllExposedValues().components[modalId]?.close()); - } - closedUnusedModals = true; - } - - if (!parentExposedValues?.show) { - isParentInactive = true; - newCandidate = parentId; + nextPossibleCandidate = parentId; + if (!modalsOpenOnCanvas.includes(parentId)) { + isComponentVisibleInParent = false; } } // If the component exists inside the kanban component's modal if (parentId.endsWith('-modal')) { - isParentInactive = true; - newCandidate = parentId.replace(/-modal$/, ''); // Extract kanban id from parent id + nextPossibleCandidate = parentId.replace(/-modal$/, ''); // Extract kanban id from parent id + if (!modalsOpenOnCanvas.includes(parentId)) { + isComponentVisibleInParent = false; + } } - if (isParentInactive) { - candidate = newCandidate; - current = newCandidate; - } else { - current = parentId; + // If the open modal contains the component + if (modalsOpenOnCanvas[modalsOpenOnCanvas.length - 1] === parentId) { + isInsideOpenModal = true; } + + if (!isComponentVisibleInParent) { + targetComponentId = nextPossibleCandidate; + } + current = nextPossibleCandidate; } - // Close all modals that are open on the canvas if the component is not inside any of the modals - if (modalsOpenOnCanvas.length > 0 && !closedUnusedModals) { - modalsOpenOnCanvas.map((modalId) => getAllExposedValues().components[modalId]?.close()); + if (modalsOpenOnCanvas.length > 0 && !isInsideOpenModal) { + return undefined; } - - return candidate; + return targetComponentId; }, }); From a80ef5ac6abe69ac21000943e8f2013519239b5a Mon Sep 17 00:00:00 2001 From: Rudra deep Biswas Date: Fri, 7 Mar 2025 17:16:25 +0530 Subject: [PATCH 06/18] init --- server/src/modules/organization-themes/constants/feature.ts | 2 +- server/src/modules/users/repository.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/modules/organization-themes/constants/feature.ts b/server/src/modules/organization-themes/constants/feature.ts index 0fa5e3dfd3..25b8274fa4 100644 --- a/server/src/modules/organization-themes/constants/feature.ts +++ b/server/src/modules/organization-themes/constants/feature.ts @@ -7,7 +7,7 @@ export const FEATURES: FeaturesConfig = { [MODULES.ORGANIZATION_THEMES]: { [FEATURE_KEY.THEMES_CREATE]: { license: LICENSE_FIELD.CUSTOM_THEMES }, [FEATURE_KEY.THEMES_DELETE]: { license: LICENSE_FIELD.CUSTOM_THEMES }, - [FEATURE_KEY.THEMES_GET_ALL]: { license: LICENSE_FIELD.CUSTOM_THEMES }, + [FEATURE_KEY.THEMES_GET_ALL]: {}, [FEATURE_KEY.THEMES_UPDATE_DEFAULT]: { license: LICENSE_FIELD.CUSTOM_THEMES }, [FEATURE_KEY.THEMES_UPDATE_DEFINITION]: { license: LICENSE_FIELD.CUSTOM_THEMES }, [FEATURE_KEY.THEMES_UPDATE_NAME]: { license: LICENSE_FIELD.CUSTOM_THEMES }, diff --git a/server/src/modules/users/repository.ts b/server/src/modules/users/repository.ts index 2771eb5d9b..0a4f77902a 100644 --- a/server/src/modules/users/repository.ts +++ b/server/src/modules/users/repository.ts @@ -65,6 +65,7 @@ export class UserRepository extends Repository { organizationId: true, organization: { name: true, + status: true, }, }, }, From 4dbd36162591566e7af79a762f7eebc65acb3791 Mon Sep 17 00:00:00 2001 From: rohanlahori Date: Fri, 7 Mar 2025 17:24:09 +0530 Subject: [PATCH 07/18] fixes --- .../src/_services/custom_styles.service.js | 4 +-- server/src/modules/app/module.ts | 2 ++ server/src/modules/custom-styles/module.ts | 30 ++++++++++++------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/frontend/src/_services/custom_styles.service.js b/frontend/src/_services/custom_styles.service.js index d3c98d6382..73de321466 100644 --- a/frontend/src/_services/custom_styles.service.js +++ b/frontend/src/_services/custom_styles.service.js @@ -3,13 +3,13 @@ import { authHeader, handleResponse, handleResponseWithoutValidation } from '@/_ function save(body) { const requestOptions = { method: 'POST', headers: authHeader(), credentials: 'include', body: JSON.stringify(body) }; - return fetch(`${config.apiUrl}/custom-styles/`, requestOptions).then(handleResponse); + return fetch(`${config.apiUrl}/custom-styles`, requestOptions).then(handleResponse); } function get(validateResponse = true) { const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; const handleOutput = validateResponse ? handleResponse : handleResponseWithoutValidation; - return fetch(`${config.apiUrl}/custom-styles/`, requestOptions).then(handleOutput); + return fetch(`${config.apiUrl}/custom-styles`, requestOptions).then(handleOutput); } function getForAppViewerEditor(validateResponse = true) { diff --git a/server/src/modules/app/module.ts b/server/src/modules/app/module.ts index 1f3939cd32..c0ca97e4be 100644 --- a/server/src/modules/app/module.ts +++ b/server/src/modules/app/module.ts @@ -40,6 +40,7 @@ import { ImportExportResourcesModule } from '@modules/import-export-resources/mo import { TooljetDbModule } from '@modules/tooljet-db/module'; import { WorkflowsModule } from '@modules/workflows/module'; import { AiModule } from '@modules/ai/module'; +import { CustomStylesModule } from '@modules/custom-styles/module'; export class AppModule implements OnModuleInit { static async register(configs: { IS_GET_CONTEXT: boolean }): Promise { @@ -92,6 +93,7 @@ export class AppModule implements OnModuleInit { await TooljetDbModule.register(configs), await WorkflowsModule.register(configs), await AiModule.register(configs), + await CustomStylesModule.register(configs), ]; return { diff --git a/server/src/modules/custom-styles/module.ts b/server/src/modules/custom-styles/module.ts index 9ff9dc290b..20608341f2 100644 --- a/server/src/modules/custom-styles/module.ts +++ b/server/src/modules/custom-styles/module.ts @@ -1,11 +1,21 @@ -import { Module } from '@nestjs/common'; -import { CustomStylesController } from '@modules/custom-styles/controller'; -import { CustomStylesService } from '@modules/custom-styles/service'; +import { DynamicModule } from '@nestjs/common'; +import { getImportPath } from '@modules/app/constants'; +import { OrganizationsModule } from '@modules/organizations/module'; +import { FeatureAbilityFactory } from './ability'; +import { OrganizationRepository } from '@modules/organizations/repository'; +import { AppsRepository } from '@modules/apps/repository'; -@Module({ - imports: [], - providers: [CustomStylesService], - controllers: [CustomStylesController], - exports: [], -}) -export class CustomStylesModule {} +export class CustomStylesModule { + static async register(configs?: { IS_GET_CONTEXT: boolean }): Promise { + const importPath = await getImportPath(configs?.IS_GET_CONTEXT); + const { CustomStylesController } = await import(`${importPath}/custom-styles/controller`); + const { CustomStylesService } = await import(`${importPath}/custom-styles/service`); + return { + module: CustomStylesModule, + imports: [await OrganizationsModule.register(configs)], + providers: [CustomStylesService, FeatureAbilityFactory, OrganizationRepository, AppsRepository], + controllers: [CustomStylesController], + exports: [], + }; + } +} From 5f416a31bf297abdec931e9a1e26a97cbcee18a1 Mon Sep 17 00:00:00 2001 From: Manish Kushare <37823141+manishkushare@users.noreply.github.com> Date: Fri, 7 Mar 2025 18:11:18 +0530 Subject: [PATCH 08/18] [Fix] - TJDB bug fixes in modularisation (#12112) * The space is required in between the primary column's name * Added change for Made content width to 100% for request field in rest api query manager * Filter label update with value 2 without apply 2 conditions * The SQL editor size is not correct for the TJDB query * There is a blank space in the json code hinter * The blank space added after vertical scroll in the edit row tab * Esc button size is not correct in the calendar for date and time data-type * Unable to see the time in calendar for the date time column * Unable to see the time in calendar for the date time column- added more specificity of class * Fix bulk update primary key CodeHinter initialization --- .../src/AppBuilder/CodeEditor/TJDBHinter.jsx | 6 +- .../src/AppBuilder/CodeEditor/styles.scss | 6 ++ .../QueryEditors/Restapi/BaseUrl.jsx | 20 +++- .../QueryEditors/Restapi/index.jsx | 102 +++++++++++++++++- .../TooljetDatabase/BulkUploadPrimaryKey.jsx | 4 +- .../DateTimePicker/styles.scss | 5 + .../TooljetDatabase/ToolJetDbOperations.jsx | 1 + .../DateTimePicker/DateTimePicker.jsx | 2 +- .../DateTimePicker/styles.scss | 20 ++++ frontend/src/TooljetDatabase/Filter/index.jsx | 6 +- frontend/src/_components/OverflowTooltip.jsx | 4 +- frontend/src/_styles/queryManager.scss | 5 + 12 files changed, 159 insertions(+), 22 deletions(-) diff --git a/frontend/src/AppBuilder/CodeEditor/TJDBHinter.jsx b/frontend/src/AppBuilder/CodeEditor/TJDBHinter.jsx index 4edd8a0fda..625b11dedd 100644 --- a/frontend/src/AppBuilder/CodeEditor/TJDBHinter.jsx +++ b/frontend/src/AppBuilder/CodeEditor/TJDBHinter.jsx @@ -150,7 +150,7 @@ const TJDBCodeEditor = (props) => { className="cm-codehinter position-relative" style={{ width: '100%', - height: isOpen ? '350p' : 'auto', + height: isOpen ? '350px' : 'auto', }} >
@@ -167,14 +167,14 @@ const TJDBCodeEditor = (props) => { componentName={componentName} key={componentName} forceUpdate={forceUpdate} - optionalProps={{ styles: { height: 300 }, cls: '' }} + optionalProps={{ styles: { height: 300 }, cls: 'tjdb-hinter-portal' }} darkMode={darkMode} selectors={{ className: 'preview-block-portal tjdb-portal-codehinter' }} dragResizePortal={true} callgpt={null} > -
+
{ +export const BaseUrl = ({ dataSourceURL, theme, className = 'col-auto', style = {} }) => { return ( - {dataSourceURL} + + {dataSourceURL} + ); }; diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/Restapi/index.jsx b/frontend/src/AppBuilder/QueryManager/QueryEditors/Restapi/index.jsx index feaeadcb23..4c89ad849e 100644 --- a/frontend/src/AppBuilder/QueryManager/QueryEditors/Restapi/index.jsx +++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/Restapi/index.jsx @@ -28,7 +28,10 @@ class Restapi extends React.Component { this.state = { options, + codeHinterHeight: 32, // Default height }; + this.codeHinterRef = React.createRef(); + this.resizeObserver = null; } componentDidUpdate(prevProps) { @@ -40,21 +43,95 @@ class Restapi extends React.Component { }, }); } + // Setup resize observer if it's not already set up + if (this.codeHinterRef.current && !this.resizeObserver) { + this.setupResizeObserver(); + } } componentDidMount() { try { + if (isEmpty(this.state.options['headers'])) { + this.addNewKeyValuePair('headers'); + } + if (isEmpty(this.state.options['cookies'])) { + this.addNewKeyValuePair('cookies'); + } if (isEmpty(this.state.options['method'])) { changeOption(this, 'method', 'get'); } + setTimeout(() => { + if (isEmpty(this.state.options['url_params'])) { + this.addNewKeyValuePair('url_params'); + } + }, 1000); + setTimeout(() => { + if (isEmpty(this.state.options['body'])) { + this.addNewKeyValuePair('body'); + } + }, 1000); setTimeout(() => { this.initizalizeRetryNetworkErrorsToggle(); }, 1000); + + this.setupResizeObserver(); } catch (error) { console.log(error); } } + componentWillUnmount() { + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + } + } + + setupResizeObserver() { + if (!this.codeHinterRef.current) return; + + // Try to find the editor element, checking multiple possible selectors + const findEditorElement = () => { + const element = + this.codeHinterRef.current.querySelector('.cm-editor') || + this.codeHinterRef.current.querySelector('.codehinter-input') || + this.codeHinterRef.current.querySelector('.code-hinter-wrapper'); + return element; + }; + + // Initial attempt to find editor + let editorElement = findEditorElement(); + + // If not found immediately, try again after a short delay + if (!editorElement) { + setTimeout(() => { + editorElement = findEditorElement(); + if (editorElement) { + this.setupObserverForElement(editorElement); + } + }, 100); + return; + } + + this.setupObserverForElement(editorElement); + } + + setupObserverForElement(element) { + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + } + + this.resizeObserver = new ResizeObserver((entries) => { + for (let entry of entries) { + const height = Math.max(32, Math.min(entry.contentRect.height, 220)); + if (height !== this.state.codeHinterHeight) { + this.setState({ codeHinterHeight: height }); + } + } + }); + + this.resizeObserver.observe(element); + } + initizalizeRetryNetworkErrorsToggle = () => { const isRetryNetworkErrorToggleUnused = this.props.options.retry_network_errors === null; if (isRetryNetworkErrorToggleUnused) { @@ -212,13 +289,30 @@ class Restapi extends React.Component { useCustomStyles={true} />
-
+
URL
-
+
{dataSourceURL && ( - + )} -
+
{ > {
{ diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss index 95d2ab56e0..23d1c7f7cf 100644 --- a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss +++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss @@ -214,4 +214,9 @@ .input-value-padding { box-sizing: border-box; padding-right: 30px !important; +} + +.react-datepicker__navigation{ + overflow: visible !important; + height: inherit !important; } \ No newline at end of file diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx index e3f31c974d..e873228888 100644 --- a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx +++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx @@ -677,6 +677,7 @@ const ToolJetDbOperations = ({ optionchanged, options, darkMode, isHorizontalLay }} componentName="TooljetDatabase" delayOnChange={false} + className="w-100" />
)} diff --git a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/DateTimePicker.jsx b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/DateTimePicker.jsx index 40818a3bb9..7a4a0b0bce 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/DateTimePicker.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/DateTimePicker.jsx @@ -163,7 +163,7 @@ export const DateTimePicker = ({
Save Changes
-
+
Esc
Discard Changes
diff --git a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss index 95d2ab56e0..55d0e7f3ed 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss +++ b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss @@ -214,4 +214,24 @@ .input-value-padding { box-sizing: border-box; padding-right: 30px !important; +} + +.datepicker-widget.theme-tjdb{ + .react-datepicker__navigation{ + overflow: visible !important; + height: inherit !important; + } +} + +.esc-btn-datepicker{ + height: 18px ; + align-items: center; +} + +.tjdb-td-wrapper{ + .react-datepicker-time__input{ + input{ + line-height: normal !important; + } + } } \ No newline at end of file diff --git a/frontend/src/TooljetDatabase/Filter/index.jsx b/frontend/src/TooljetDatabase/Filter/index.jsx index 84b0110fe4..6c8f7c811d 100644 --- a/frontend/src/TooljetDatabase/Filter/index.jsx +++ b/frontend/src/TooljetDatabase/Filter/index.jsx @@ -251,11 +251,7 @@ const Filter = ({ } />
- {filterCount > 0 ? ( - {pluralize(validFilterCountRef.current, 'filter')} - ) : ( -
  Filter
- )} + {filterCount > 0 ? {pluralize(filterCount, 'filter')} :
  Filter
}
{/* {areFiltersApplied && ( ed by {pluralize(Object.values(filters).filter(checkIsFilterObjectEmpty).length, 'column')} diff --git a/frontend/src/_components/OverflowTooltip.jsx b/frontend/src/_components/OverflowTooltip.jsx index 297f17d236..1523ae449a 100644 --- a/frontend/src/_components/OverflowTooltip.jsx +++ b/frontend/src/_components/OverflowTooltip.jsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { ToolTip } from '@/_components'; -export default function OverflowTooltip({ children, className, whiteSpace = 'nowrap', ...rest }) { +export default function OverflowTooltip({ children, className, whiteSpace = 'nowrap', placement = 'bottom', ...rest }) { const [isOverflowed, setIsOverflow] = useState(false); const textElementRef = useRef(); @@ -17,7 +17,7 @@ export default function OverflowTooltip({ children, className, whiteSpace = 'now className={className} delay={{ show: '0', hide: '0' }} tooltipClassName="overflow-tooltip" - placement="bottom" + placement={placement} message={children} show={isOverflowed} width={rest?.width} diff --git a/frontend/src/_styles/queryManager.scss b/frontend/src/_styles/queryManager.scss index 8a9eaf972a..7b256c3812 100644 --- a/frontend/src/_styles/queryManager.scss +++ b/frontend/src/_styles/queryManager.scss @@ -1250,6 +1250,11 @@ $border-radius: 4px; color: var(--slate12) !important; } } + &.data-source-exists { + .cm-editor { + border-radius: 0 4px 4px 0 !important; + } + } } .rest-api-methods-select-element-container { From 1ef408f11394a1dd1af09d0b3fd828b400eeaae3 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Fri, 7 Mar 2025 18:11:29 +0530 Subject: [PATCH 09/18] Changed function response to handle non accessible components. --- .../LeftSidebarInspector/useCallbackActions.js | 14 ++++++++++++-- .../AppBuilder/_stores/slices/leftSideBarSlice.js | 15 +++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js index bf661ea20d..937fe45b2c 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js @@ -32,8 +32,18 @@ const useCallbackActions = () => { }; const handleAutoScrollToComponent = (data) => { - const computedComponentId = getComponentIdToAutoScroll(data.id); - if (!computedComponentId) return; + const { isAccessible, computedComponentId, isOnCanvas } = getComponentIdToAutoScroll(data.id); + if (!isAccessible) { + if (isOnCanvas) { + toast.success( + `This component can't be opened because it's on the main canvas. Close ${computedComponentId} and click "Go to component" to view it there` + ); + } else + toast.success( + `This component can't be opened because it's inside ${computedComponentId}. Open ${computedComponentId} and click "Go to component"to view it.` + ); + return; + } setSelectedComponents([computedComponentId]); const target = document.getElementById(computedComponentId); target.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); diff --git a/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js b/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js index da520544b2..367ca4cf0c 100644 --- a/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/leftSideBarSlice.js @@ -103,8 +103,19 @@ export const createLeftSideBarSlice = (set, get) => ({ } if (modalsOpenOnCanvas.length > 0 && !isInsideOpenModal) { - return undefined; + const targetId = visited.size === 1 ? modalsOpenOnCanvas[modalsOpenOnCanvas.length - 1] : current; + const componentName = currentPageComponents?.[targetId]?.component?.name; + + return { + isAccessible: false, + computedComponentId: componentName, + isOnCanvas: visited.size === 1, + }; } - return targetComponentId; + + return { + isAccessible: true, + computedComponentId: targetComponentId, + }; }, }); From 6b50f13795c278a7aecbc4ba7013adf9dfa2e5de Mon Sep 17 00:00:00 2001 From: Anantshree Chandola Date: Fri, 7 Mar 2025 18:52:23 +0530 Subject: [PATCH 10/18] fixes --- server/src/modules/organization-themes/constants/feature.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/modules/organization-themes/constants/feature.ts b/server/src/modules/organization-themes/constants/feature.ts index 0fa5e3dfd3..25b8274fa4 100644 --- a/server/src/modules/organization-themes/constants/feature.ts +++ b/server/src/modules/organization-themes/constants/feature.ts @@ -7,7 +7,7 @@ export const FEATURES: FeaturesConfig = { [MODULES.ORGANIZATION_THEMES]: { [FEATURE_KEY.THEMES_CREATE]: { license: LICENSE_FIELD.CUSTOM_THEMES }, [FEATURE_KEY.THEMES_DELETE]: { license: LICENSE_FIELD.CUSTOM_THEMES }, - [FEATURE_KEY.THEMES_GET_ALL]: { license: LICENSE_FIELD.CUSTOM_THEMES }, + [FEATURE_KEY.THEMES_GET_ALL]: {}, [FEATURE_KEY.THEMES_UPDATE_DEFAULT]: { license: LICENSE_FIELD.CUSTOM_THEMES }, [FEATURE_KEY.THEMES_UPDATE_DEFINITION]: { license: LICENSE_FIELD.CUSTOM_THEMES }, [FEATURE_KEY.THEMES_UPDATE_NAME]: { license: LICENSE_FIELD.CUSTOM_THEMES }, From 679811ad760b7b74a1fbc1f6882c0f1bf0a34478 Mon Sep 17 00:00:00 2001 From: Muhsin Shah C P Date: Fri, 7 Mar 2025 19:47:46 +0530 Subject: [PATCH 11/18] Fixed onboarding questions bug (#12160) --- .../modules/onboarding/pages/InvitationPage/InvitationPage.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/modules/onboarding/pages/InvitationPage/InvitationPage.jsx b/frontend/src/modules/onboarding/pages/InvitationPage/InvitationPage.jsx index f157f4744b..e2a4360943 100644 --- a/frontend/src/modules/onboarding/pages/InvitationPage/InvitationPage.jsx +++ b/frontend/src/modules/onboarding/pages/InvitationPage/InvitationPage.jsx @@ -9,6 +9,7 @@ import { utils } from '@/modules/common/helpers'; import { getSubpath } from '@/_helpers/routes'; import { TJLoader } from '@/_ui/TJLoader/TJLoader'; import useOnboardingStore from '@/modules/common/helpers/onboardingStoreHelper'; +import useInvitationsStore from '@/modules/common/helpers/invitationStoreHelper'; const PostOnboardingComponent = () => ; export const InvitationPage = (darkMode = false) => { @@ -24,7 +25,7 @@ export const InvitationPage = (darkMode = false) => { const source = searchParams.get('source'); const redirectTo = searchParams.get('redirectTo'); - const { initiateInvitedUserOnboarding } = invitationsStore(); + const { initiateInvitedUserOnboarding } = useInvitationsStore(); const { resumeSignupOnboarding, isOnboardingStepsCompleted } = useOnboardingStore(); useEffect(() => { // getUserDetails(); From 1b1a8061e4553007c6ca177be29b364601ae711f Mon Sep 17 00:00:00 2001 From: Rudra deep Biswas Date: Mon, 10 Mar 2025 01:34:32 +0530 Subject: [PATCH 12/18] pass appid in ability --- server/src/modules/apps/ability/index.ts | 44 +++++++++--------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/server/src/modules/apps/ability/index.ts b/server/src/modules/apps/ability/index.ts index 4200a1c463..d53309202f 100644 --- a/server/src/modules/apps/ability/index.ts +++ b/server/src/modules/apps/ability/index.ts @@ -15,7 +15,13 @@ export class FeatureAbilityFactory extends AbilityFactory return App; } - protected defineAbilityFor(can: AbilityBuilder['can'], UserAllPermissions: UserAllPermissions): void { + protected defineAbilityFor( + can: AbilityBuilder['can'], + UserAllPermissions: UserAllPermissions, + extractedMetadata: { moduleName: string; features: string[] }, + request?: any + ): void { + const appId = request?.tj_resource_id; const { superAdmin, isAdmin, userPermission } = UserAllPermissions; const userAppPermissions = userPermission?.[MODULES.APP]; @@ -51,7 +57,10 @@ export class FeatureAbilityFactory extends AbilityFactory can(FEATURE_KEY.CREATE, App); } - if (isAllAppsEditable) { + if ( + isAllAppsEditable || + (userAppPermissions?.editableAppsId?.length && appId && userAppPermissions.editableAppsId.includes(appId)) + ) { can( [ FEATURE_KEY.UPDATE, @@ -70,35 +79,14 @@ export class FeatureAbilityFactory extends AbilityFactory can(FEATURE_KEY.DELETE, App); } return; - } else if (userAppPermissions?.editableAppsId?.length) { - can( - [ - FEATURE_KEY.DELETE, - FEATURE_KEY.UPDATE_ICON, - FEATURE_KEY.GET_ONE, - FEATURE_KEY.GET_BY_SLUG, - FEATURE_KEY.RELEASE, - FEATURE_KEY.VALIDATE_PRIVATE_APP_ACCESS, - FEATURE_KEY.VALIDATE_RELEASED_APP_ACCESS, - FEATURE_KEY.UPDATE, - FEATURE_KEY.GET_ASSOCIATED_TABLES, - ], - App, - { id: { $in: userAppPermissions.editableAppsId } } - ); - if (isAllAppsDeletable) { - // Gives delete permission only for editable apps - can(FEATURE_KEY.DELETE, App, { id: { $in: userAppPermissions.editableAppsId } }); - } } - if (isAllAppsViewable) { - // add view permissions for all apps + if ( + isAllAppsViewable || + (userAppPermissions?.viewableAppsId?.length && appId && userAppPermissions.viewableAppsId.includes(appId)) + ) { + // add view permissions for all apps or specific app can([FEATURE_KEY.GET_ONE, FEATURE_KEY.GET_BY_SLUG, FEATURE_KEY.VALIDATE_RELEASED_APP_ACCESS], App); - } else if (userAppPermissions?.viewableAppsId?.length) { - can([FEATURE_KEY.GET_ONE, FEATURE_KEY.GET_BY_SLUG, FEATURE_KEY.VALIDATE_RELEASED_APP_ACCESS], App, { - id: { $in: userAppPermissions.viewableAppsId }, - }); } } } From cd8e330208dd4396ac0cd88bee448740e7ea190a Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Fri, 17 Jan 2025 15:06:47 +0530 Subject: [PATCH 13/18] Fix: Not able to copy value in debugger --- .../src/Editor/LeftSidebar/SidebarDebugger/Logs.jsx | 12 +++++++++++- frontend/src/_ui/JSONTreeViewer/JSONNode.jsx | 8 +++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/frontend/src/Editor/LeftSidebar/SidebarDebugger/Logs.jsx b/frontend/src/Editor/LeftSidebar/SidebarDebugger/Logs.jsx index 66548cbef8..aba2ca6af1 100644 --- a/frontend/src/Editor/LeftSidebar/SidebarDebugger/Logs.jsx +++ b/frontend/src/Editor/LeftSidebar/SidebarDebugger/Logs.jsx @@ -5,6 +5,7 @@ import JSONTreeViewer from '@/_ui/JSONTreeViewer'; import cx from 'classnames'; import SolidIcon from '@/_ui/Icon/SolidIcons'; import useStore from '@/AppBuilder/_stores/store'; +import { toast } from 'react-hot-toast'; function Logs({ logProps, idx }) { const [open, setOpen] = React.useState(false); @@ -52,10 +53,19 @@ function Logs({ logProps, idx }) { } }; + const copyToClipboard = (data) => { + const stringified = JSON.stringify(data, null, 2).replace(/\\/g, ''); + navigator.clipboard.writeText(stringified); + return toast.success('Value copied to clipboard', { position: 'top-center' }); + }; + const callbackActions = [ { for: 'all', - actions: [{ name: 'Select Widget', dispatchAction: handleSelectComponentOnEditor, icon: false, onSelect: true }], + actions: [ + { name: 'Copy value', dispatchAction: copyToClipboard, icon: false }, + { name: 'Select Widget', dispatchAction: handleSelectComponentOnEditor, icon: false, onSelect: true }, + ], enableForAllChildren: true, enableFor1stLevelChildren: true, }, diff --git a/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx b/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx index 65a4ece91d..c6b2a504fa 100644 --- a/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx +++ b/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx @@ -269,7 +269,7 @@ export const JSONNode = ({ data, ...restProps }) => { return (
- {enableCopyToClipboard && ( + {enableCopyToClipboard ? ( { @@ -281,6 +281,12 @@ export const JSONNode = ({ data, ...restProps }) => { + ) : ( + // Temporary fix for hover issue for copy value button. Need to remove this once inspector gets revamped. + )} Date: Fri, 17 Jan 2025 15:40:53 +0530 Subject: [PATCH 14/18] Revamped fix for copy button on hover in debugger. --- frontend/src/_ui/JSONTreeViewer/JSONNode.jsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx b/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx index c6b2a504fa..d1ce9518d5 100644 --- a/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx +++ b/frontend/src/_ui/JSONTreeViewer/JSONNode.jsx @@ -268,8 +268,16 @@ export const JSONNode = ({ data, ...restProps }) => { }; return ( -
- {enableCopyToClipboard ? ( +
+ {enableCopyToClipboard && ( { @@ -281,12 +289,6 @@ export const JSONNode = ({ data, ...restProps }) => { - ) : ( - // Temporary fix for hover issue for copy value button. Need to remove this once inspector gets revamped. - )} Date: Thu, 16 Jan 2025 12:27:07 +0530 Subject: [PATCH 15/18] Fix: Date picker gets hidden inside table and user needs to scroll to see it. --- frontend/src/AppBuilder/Widgets/Table/Datepicker.jsx | 3 +++ frontend/src/AppBuilder/Widgets/Table/Table.jsx | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/AppBuilder/Widgets/Table/Datepicker.jsx b/frontend/src/AppBuilder/Widgets/Table/Datepicker.jsx index 4e5a481dd2..930021c994 100644 --- a/frontend/src/AppBuilder/Widgets/Table/Datepicker.jsx +++ b/frontend/src/AppBuilder/Widgets/Table/Datepicker.jsx @@ -263,6 +263,9 @@ export const Datepicker = function Datepicker({ } setIsDateInputFocussed(false); }} + closeOnScroll={(e) => { + return e.target.className === 'table-responsive jet-data-table false false'; + }} />
); diff --git a/frontend/src/AppBuilder/Widgets/Table/Table.jsx b/frontend/src/AppBuilder/Widgets/Table/Table.jsx index 68b1609748..7d378e7d70 100644 --- a/frontend/src/AppBuilder/Widgets/Table/Table.jsx +++ b/frontend/src/AppBuilder/Widgets/Table/Table.jsx @@ -1114,10 +1114,9 @@ export const Table = React.memo(
{items.map((virtualRow) => { From ac36acd9c1d41186fd8c051918595ab7d13705e3 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Thu, 9 Jan 2025 12:17:52 +0530 Subject: [PATCH 16/18] onSelect event gets triggered when dropdown value is cleared using clear button. --- frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx b/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx index 72e2b39664..a01ed895e0 100644 --- a/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx +++ b/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx @@ -436,6 +436,7 @@ export const DropdownV2 = ({ onChange={(selectedOption, actionProps) => { if (actionProps.action === 'clear') { setInputValue(null); + fireEvent('onSelect'); } if (actionProps.action === 'select-option') { setInputValue(selectedOption.value); From f2bd5a54b8b32516e9bd71f57690389da99a195a Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Thu, 16 Jan 2025 23:16:01 +0530 Subject: [PATCH 17/18] Fix: Table component's max row height not getting updated. --- .../CodeBuilder/Elements/TableRowHeightInput.jsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/frontend/src/AppBuilder/CodeBuilder/Elements/TableRowHeightInput.jsx b/frontend/src/AppBuilder/CodeBuilder/Elements/TableRowHeightInput.jsx index b9fb85fcae..099cd3763a 100644 --- a/frontend/src/AppBuilder/CodeBuilder/Elements/TableRowHeightInput.jsx +++ b/frontend/src/AppBuilder/CodeBuilder/Elements/TableRowHeightInput.jsx @@ -5,19 +5,13 @@ const MIN_TABLE_ROW_HEIGHT_DEFAULT = 45; const TableRowHeightInput = ({ value, onChange, cyLabel, staticText, styleDefinition }) => { const [inputValue, setInputValue] = useState(value); + const minValue = + styleDefinition.cellSize?.value === 'condensed' ? MIN_TABLE_ROW_HEIGHT_CONDENSED : MIN_TABLE_ROW_HEIGHT_DEFAULT; + useEffect(() => { setInputValue(value < minValue ? minValue : value); // eslint-disable-next-line react-hooks/exhaustive-deps }, [value, styleDefinition.cellSize?.value]); - useEffect(() => { - onChange( - styleDefinition.cellSize?.value === 'condensed' ? MIN_TABLE_ROW_HEIGHT_CONDENSED : MIN_TABLE_ROW_HEIGHT_DEFAULT - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const minValue = - styleDefinition.cellSize?.value === 'condensed' ? MIN_TABLE_ROW_HEIGHT_CONDENSED : MIN_TABLE_ROW_HEIGHT_DEFAULT; const handleBlur = () => { const newValue = Math.max(inputValue, minValue); From 369afb1c21deb336bde11243f926b8e890436c78 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Wed, 8 Jan 2025 11:47:11 +0530 Subject: [PATCH 18/18] Fix: Not able to pass parameters to run query in custom component. --- .../src/Editor/Components/CustomComponent/CustomComponent.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Editor/Components/CustomComponent/CustomComponent.jsx b/frontend/src/Editor/Components/CustomComponent/CustomComponent.jsx index 5bfe9818e5..4d4b7a2e48 100644 --- a/frontend/src/Editor/Components/CustomComponent/CustomComponent.jsx +++ b/frontend/src/Editor/Components/CustomComponent/CustomComponent.jsx @@ -42,7 +42,7 @@ export const CustomComponent = (props) => { setCustomProps({ ...customPropRef.current, ...e.data.updatedObj }); } else if (e.data.message === 'RUN_QUERY') { const options = { - parameters: e.data.parameters, + parameters: JSON.parse(e.data.parameters), queryName: e.data.queryName, }; onEvent('onTrigger', [], options);