mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 00:48:25 +00:00
Merge pull request #11319 from ToolJet/fixes/release-v3.0/appbuilder
LTS 3.0 Patch release
This commit is contained in:
commit
a16b006e4c
83 changed files with 1226 additions and 575 deletions
2
.version
2
.version
|
|
@ -1 +1 @@
|
|||
3.0.0-ce-lts
|
||||
3.0.1-ce-lts
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.0.0-ce-lts
|
||||
3.0.1-ce-lts
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@ import React, { useState, useEffect, useRef } from 'react';
|
|||
import { Container } from './Container';
|
||||
import Grid from './Grid';
|
||||
import { EditorSelecto } from './Selecto';
|
||||
import { ModuleProvider } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
import { HotkeyProvider } from './HotkeyProvider';
|
||||
import './appCanvas.scss';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { getCanvasWidth } from './appCanvasUtils';
|
||||
import { getCanvasWidth, computeViewerBackgroundColor } from './appCanvasUtils';
|
||||
import { NO_OF_GRIDS } from './appCanvasConstants';
|
||||
import cx from 'classnames';
|
||||
import FreezeVersionInfo from '@/AppBuilder/Header/FreezeVersionInfo';
|
||||
|
|
@ -38,8 +37,9 @@ export const AppCanvas = ({ moduleId, appId, isViewerSidebarPinned }) => {
|
|||
const setIsComponentLayoutReady = useStore((state) => state.setIsComponentLayoutReady, shallow);
|
||||
const canvasMaxWidth = useAppCanvasMaxWidth({ mode: currentMode });
|
||||
const editorMarginLeft = useSidebarMargin(canvasContainerRef);
|
||||
const pageSwitchInProgress = useStore((state) => state.pageSwitchInProgress);
|
||||
const setPageSwitchInProgress = useStore((state) => state.setPageSwitchInProgress);
|
||||
const isSidebarOpen = useStore((state) => state.isSidebarOpen, shallow);
|
||||
const isPagesSidebarHidden = useStore((state) => state.getPagesSidebarVisibility('canvas'), shallow);
|
||||
|
||||
useEffect(() => {
|
||||
// Need to remove this if we shift setExposedVariable Logic outside of components
|
||||
// Currently present to run onLoadQueries after the component is mounted
|
||||
|
|
@ -60,21 +60,27 @@ export const AppCanvas = ({ moduleId, appId, isViewerSidebarPinned }) => {
|
|||
|
||||
return (
|
||||
<div className={cx(`main main-editor-canvas`, {})} id="main-editor-canvas" onMouseUp={handleCanvasContainerMouseUp}>
|
||||
{creationMode === 'GIT' && <FreezeVersionInfo info={'Apps imported from git repository cannot be edited'} />}
|
||||
{creationMode !== 'GIT' && <FreezeVersionInfo hide={currentMode !== 'edit'} />}
|
||||
<div
|
||||
ref={canvasContainerRef}
|
||||
className={cx(
|
||||
'canvas-container align-items-center page-container',
|
||||
{ 'dark-theme theme-dark': isAppDarkMode, close: !isViewerSidebarPinned }
|
||||
// { 'hide-sidebar': !showLeftSidebar }
|
||||
{ 'dark-theme theme-dark': isAppDarkMode, close: !isViewerSidebarPinned },
|
||||
{ 'overflow-x-auto': (currentMode === 'edit' && isSidebarOpen) || currentMode === 'view' }
|
||||
)}
|
||||
style={{
|
||||
// transform: `scale(1)`,
|
||||
borderLeft: editorMarginLeft + 'px solid',
|
||||
borderLeft: currentMode === 'edit' && editorMarginLeft + 'px solid',
|
||||
height: currentMode === 'edit' ? canvasContainerHeight : '100%',
|
||||
backgroundColor: canvasBgColor,
|
||||
// background: !isAppDarkMode ? '#EBEBEF' : '#2E3035',
|
||||
background:
|
||||
currentMode === 'view'
|
||||
? computeViewerBackgroundColor(isAppDarkMode, canvasBgColor)
|
||||
: !isAppDarkMode
|
||||
? '#EBEBEF'
|
||||
: '#2F3C4C',
|
||||
marginLeft:
|
||||
isViewerSidebarPinned && currentLayout !== 'mobile' && currentMode !== 'edit'
|
||||
isViewerSidebarPinned && !isPagesSidebarHidden && currentLayout !== 'mobile' && currentMode !== 'edit'
|
||||
? pageSidebarStyle === 'icon'
|
||||
? '65px'
|
||||
: '210px'
|
||||
|
|
@ -87,9 +93,9 @@ export const AppCanvas = ({ moduleId, appId, isViewerSidebarPinned }) => {
|
|||
}}
|
||||
className={`app-${appId}`}
|
||||
>
|
||||
<AutoComputeMobileLayoutAlert currentLayout={currentLayout} darkMode={isAppDarkMode} />
|
||||
{creationMode === 'GIT' && <FreezeVersionInfo info={'Apps imported from git repository cannot be edited'} />}
|
||||
{creationMode !== 'GIT' && <FreezeVersionInfo hide={currentMode !== 'edit'} />}
|
||||
{currentMode === 'edit' && (
|
||||
<AutoComputeMobileLayoutAlert currentLayout={currentLayout} darkMode={isAppDarkMode} />
|
||||
)}
|
||||
<DeleteWidgetConfirmation darkMode={isAppDarkMode} />
|
||||
<HotkeyProvider mode={currentMode} canvasMaxWidth={canvasMaxWidth} currentLayout={currentLayout}>
|
||||
{environmentLoadingState !== 'loading' && (
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ export default function AutoComputeMobileLayoutAlert({ currentLayout, darkMode }
|
|||
padding: 'var(--7, 16px)',
|
||||
background: 'var(--base)',
|
||||
margin: '10px',
|
||||
zIndex: '1',
|
||||
}}
|
||||
className="d-flex flex-row"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import WidgetWrapper from './WidgetWrapper';
|
|||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { useDrop } from 'react-dnd';
|
||||
import { addChildrenWidgetsToParent, addNewWidgetToTheEditor } from './appCanvasUtils';
|
||||
import { addChildrenWidgetsToParent, addNewWidgetToTheEditor, computeViewerBackgroundColor } from './appCanvasUtils';
|
||||
import { CANVAS_WIDTHS, NO_OF_GRIDS, WIDGETS_WITH_DEFAULT_CHILDREN } from './appCanvasConstants';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import NoComponentCanvasContainer from './NoComponentCanvasContainer';
|
||||
|
|
@ -40,7 +40,6 @@ export const Container = React.memo(
|
|||
const components = useStore((state) => state.getContainerChildrenMapping(id), shallow);
|
||||
const componentType = useStore((state) => state.getComponentTypeFromId(id), shallow);
|
||||
const addComponentToCurrentPage = useStore((state) => state.addComponentToCurrentPage, shallow);
|
||||
const setSelectedComponents = useStore((state) => state.setSelectedComponents, shallow);
|
||||
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab, shallow);
|
||||
const canvasBgColor = useStore(
|
||||
(state) => (id === 'canvas' ? state.getCanvasBackgroundColor('canvas', darkMode) : ''),
|
||||
|
|
@ -124,7 +123,12 @@ export const Container = React.memo(
|
|||
height: id === 'canvas' ? `${canvasHeight}` : '100%',
|
||||
// backgroundSize: '25.3953px 10px',
|
||||
backgroundSize: `${gridWidth}px 10px`,
|
||||
backgroundColor: id === 'canvas' ? canvasBgColor : '#f0f0f0',
|
||||
backgroundColor:
|
||||
currentMode === 'view'
|
||||
? computeViewerBackgroundColor(darkMode, canvasBgColor)
|
||||
: id === 'canvas'
|
||||
? canvasBgColor
|
||||
: '#f0f0f0',
|
||||
width: getCanvasWidth(),
|
||||
maxWidth: (() => {
|
||||
// For Main Canvas
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const isGroupHandleHoverd = useIsGroupHandleHoverd();
|
||||
const openModalWidgetId = useOpenModalWidgetId();
|
||||
const moveableRef = useRef(null);
|
||||
const [triggerCanvasUpdater, setTriggerCanvasUpdater] = useState(false);
|
||||
const triggerCanvasUpdater = useStore((state) => state.triggerCanvasUpdater, shallow);
|
||||
const toggleCanvasUpdater = useStore((state) => state.toggleCanvasUpdater, shallow);
|
||||
const groupResizeDataRef = useRef([]);
|
||||
const isDraggingRef = useRef(false);
|
||||
const canvasWidth = NO_OF_GRIDS * gridWidth;
|
||||
|
|
@ -347,7 +348,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
return layouts;
|
||||
}, {});
|
||||
setComponentLayout(updatedLayouts, newParent, undefined, { updateParent: true });
|
||||
setTriggerCanvasUpdater((prev) => !prev);
|
||||
toggleCanvasUpdater();
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[boxList, currentLayout, gridWidth]
|
||||
|
|
@ -488,7 +489,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
console.error('ResizeEnd error ->', error);
|
||||
}
|
||||
useGridStore.getState().actions.setDragTarget();
|
||||
setTriggerCanvasUpdater((prev) => !prev);
|
||||
toggleCanvasUpdater();
|
||||
}}
|
||||
onResizeStart={(e) => {
|
||||
if (!isComponentVisible(e.target.id)) {
|
||||
|
|
@ -575,7 +576,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
} catch (error) {
|
||||
console.error('Error resizing group', error);
|
||||
}
|
||||
setTriggerCanvasUpdater((prev) => !prev);
|
||||
toggleCanvasUpdater();
|
||||
}}
|
||||
checkInput
|
||||
onDragStart={(e) => {
|
||||
|
|
@ -722,7 +723,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
element.classList.add('hide-grid');
|
||||
});
|
||||
document.getElementById('real-canvas')?.classList.remove('show-grid');
|
||||
setTriggerCanvasUpdater((prev) => !prev);
|
||||
toggleCanvasUpdater();
|
||||
}}
|
||||
onDrag={(e) => {
|
||||
// Since onDrag is called multiple times when dragging, hence we are using isDraggingRef to prevent setting state again and again
|
||||
|
|
@ -857,7 +858,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
} catch (error) {
|
||||
console.error('Error dragging group', error);
|
||||
}
|
||||
setTriggerCanvasUpdater((prev) => !prev);
|
||||
toggleCanvasUpdater();
|
||||
}}
|
||||
// throttleDrag={1}
|
||||
// edgeDraggable={false}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ const RenderWidget = ({
|
|||
...{ validationObject: unResolvedValidation },
|
||||
customResolveObjects: customResolvables,
|
||||
}),
|
||||
[validateWidget, customResolvables, unResolvedValidation]
|
||||
[validateWidget, customResolvables, unResolvedValidation, resolvedValidation]
|
||||
);
|
||||
|
||||
const resetComponent = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -350,7 +350,14 @@ export function pasteComponents(parentId, copiedComponentObj) {
|
|||
const currentPageId = useStore.getState().getCurrentPageId();
|
||||
const { isCut = false, newComponents: pastedComponents = [], pageId, isCloning = false } = copiedComponentObj;
|
||||
// Prevent pasting if the parent subcontainer was deleted during a cut operation
|
||||
if (parentId && !Object.keys(components).find((key) => parentId === key)) {
|
||||
if (
|
||||
parentId &&
|
||||
!Object.keys(components).find(
|
||||
(key) =>
|
||||
parentId === key ||
|
||||
(components?.[key]?.component.component === 'Tabs' && parentId?.split('-')?.slice(0, -1)?.join('-') === key)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (parentId) {
|
||||
|
|
@ -444,10 +451,9 @@ export const getCanvasWidth = (currentLayout) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const computeCanvasBackgroundColor = (isAppDarkMode, canvasBgColor) => {
|
||||
const canvasBackgroundColor = canvasBgColor ? canvasBgColor : '#edeff5';
|
||||
if (['#2f3c4c', '#edeff5'].includes(canvasBackgroundColor)) {
|
||||
return isAppDarkMode ? '#2f3c4c' : '#edeff5';
|
||||
export const computeViewerBackgroundColor = (isAppDarkMode, canvasBgColor) => {
|
||||
if (['#2f3c4c', '#F2F2F5', '#edeff5'].includes(canvasBgColor)) {
|
||||
return isAppDarkMode ? '#2f3c4c' : '#F2F2F5';
|
||||
}
|
||||
return canvasBackgroundColor;
|
||||
return canvasBgColor;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import { LEFT_SIDEBAR_WIDTH } from './appCanvasConstants';
|
|||
|
||||
const useSidebarMargin = (canvasContainerRef) => {
|
||||
const [editorMarginLeft, setEditorMarginLeft] = useState(0);
|
||||
const selectedSidebarItem = useStore((state) => state.selectedSidebarItem, shallow);
|
||||
const isSidebarOpen = useStore((state) => state.isSidebarOpen, shallow);
|
||||
const mode = useStore((state) => state.currentMode, shallow);
|
||||
|
||||
useEffect(() => {
|
||||
setEditorMarginLeft(selectedSidebarItem ? LEFT_SIDEBAR_WIDTH : 0);
|
||||
}, [selectedSidebarItem]);
|
||||
if (mode !== 'view') setEditorMarginLeft(isSidebarOpen ? LEFT_SIDEBAR_WIDTH : 0);
|
||||
else setEditorMarginLeft(0);
|
||||
}, [isSidebarOpen, mode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEmpty(canvasContainerRef?.current)) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@ const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...r
|
|||
const [currentValue, setCurrentValue] = useState('');
|
||||
const [errorStateActive, setErrorStateActive] = useState(false);
|
||||
const [cursorInsidePreview, setCursorInsidePreview] = useState(false);
|
||||
const componentDefinition = useStore((state) => state.getComponentDefinition(componentId), shallow);
|
||||
const parentId = componentDefinition?.component?.parent;
|
||||
const customResolvables = useStore((state) => state.resolvedStore.modules.canvas?.customResolvables, shallow);
|
||||
|
||||
const customVariables = customResolvables?.[parentId]?.[0] || {};
|
||||
|
||||
const isPreviewFocused = useRef(false);
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
|
|
@ -52,8 +58,10 @@ const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...r
|
|||
// ? resolveReferences(newInitialValue, validation, customVariables)
|
||||
// : [true, null];
|
||||
|
||||
// Need to add customVariables while resolving the value like above
|
||||
const [valid, _error] = !isEmpty(validation) ? resolveReferences(newInitialValue, validation) : [true, null];
|
||||
//!TODO use the updated new resolver
|
||||
const [valid, _error] = !isEmpty(validation)
|
||||
? resolveReferences(newInitialValue, validation, customVariables)
|
||||
: [true, null];
|
||||
|
||||
if (!valid) {
|
||||
setErrorStateActive(true);
|
||||
|
|
@ -90,6 +98,7 @@ const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...r
|
|||
style={{ width: '100%', height: restProps?.lang === 'jsx' && '320px' }}
|
||||
>
|
||||
<PreviewBox.Container
|
||||
customVariables={customVariables}
|
||||
enablePreview={enablePreview}
|
||||
currentValue={currentValue}
|
||||
isFocused={isFocused}
|
||||
|
|
@ -103,7 +112,6 @@ const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...r
|
|||
errorStateActive={errorStateActive}
|
||||
previewPlacement={restProps?.cyLabel === 'canvas-bg-colour' ? 'top' : 'left-start'}
|
||||
isPortalOpen={restProps?.portalProps?.isOpen}
|
||||
// customVariables={customVariables}
|
||||
>
|
||||
<div className="code-editor-basic-wrapper d-flex">
|
||||
<div className="codehinter-container w-100">
|
||||
|
|
|
|||
|
|
@ -125,10 +125,7 @@ export const AppVersionsManager = function ({ darkMode }) {
|
|||
deleteVersionAction(
|
||||
appId,
|
||||
versionId,
|
||||
(newVersionDef) => {
|
||||
if (newVersionDef) {
|
||||
setCurrentVersionId(newVersionDef.id);
|
||||
}
|
||||
() => {
|
||||
toast.dismiss(deleteingToastId);
|
||||
toast.success(`Version - ${decodeEntities(versionName)} Deleted`);
|
||||
resetDeleteModal();
|
||||
|
|
|
|||
|
|
@ -18,13 +18,6 @@ export const EditorHeader = ({ darkMode }) => {
|
|||
isSaving: state.app.isSaving,
|
||||
saveError: state.app.saveError,
|
||||
isVersionReleased: state.isVersionReleased,
|
||||
user: state.user,
|
||||
app: state.app,
|
||||
appId: state.app.appId,
|
||||
editingVersion: state.editingVersion,
|
||||
updateReleasedVersionId: state.updateReleasedVersionId,
|
||||
updateEditingVersion: state.updateEditingVersion,
|
||||
featureAccess: state.featureAccess,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
|
|
|
|||
|
|
@ -62,12 +62,13 @@ export const LeftSidebar = ({ darkMode = false, switchDarkMode }) => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPopoverContentHeight(((window.innerHeight - queryPanelHeight - 45) / window.innerHeight) * 100);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
setPopoverContentHeight(
|
||||
((window.innerHeight - (queryPanelHeight == 0 ? 40 : queryPanelHeight) - 45) / window.innerHeight) * 100
|
||||
); // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [queryPanelHeight]);
|
||||
|
||||
const renderPopoverContent = () => {
|
||||
if (selectedSidebarItem === null) return null;
|
||||
if (selectedSidebarItem === null || !isSidebarOpen) return null;
|
||||
switch (selectedSidebarItem) {
|
||||
case 'page':
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ const LeftSidebarInspector = ({ darkMode, pinned, setPinned }) => {
|
|||
|
||||
return jsontreeData;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sortedComponents, sortedQueries, sortedVariables, sortedConstants, sortedPageVariables]);
|
||||
}, [sortedComponents, sortedQueries, sortedVariables, sortedConstants, sortedPageVariables, sortedGlobalVariables]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export default function IconSelector({ iconName, iconColor, pageId }) {
|
|||
};
|
||||
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons?.[iconName] ?? Icons?.['IconHome2'];
|
||||
const IconElement = Icons?.[iconName] ?? Icons?.['IconFileDescription'];
|
||||
|
||||
return (
|
||||
<OverlayTrigger
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useContext, useEffect, useRef } from 'react';
|
||||
import React from 'react';
|
||||
import { Overlay, Popover } from 'react-bootstrap';
|
||||
import { Button } from '@/_ui/LeftSidebar';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
|
||||
export const PageHandlerMenu = ({ darkMode }) => {
|
||||
|
|
@ -57,7 +56,7 @@ export const PageHandlerMenu = ({ darkMode }) => {
|
|||
|
||||
return (
|
||||
<Overlay
|
||||
placement="auto"
|
||||
placement="right"
|
||||
target={targetContainer}
|
||||
show={showMenu}
|
||||
rootClose
|
||||
|
|
@ -228,8 +227,8 @@ const Field = ({ id, text, iconSrc, customClass = '', closeMenu, disabled = fals
|
|||
const handleOnClick = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeMenu();
|
||||
callback(id);
|
||||
closeMenu();
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -47,8 +47,9 @@ export const PageMenuItem = withRouter(
|
|||
|
||||
const isEditingPage = editingPage?.id === page?.id;
|
||||
const icon = () => {
|
||||
const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon;
|
||||
if (!isDisabled && !isHidden) {
|
||||
return <IconSelector iconColor={computedStyles?.icon?.color} iconName={page.icon} pageId={page.id} />;
|
||||
return <IconSelector iconColor={computedStyles?.icon?.color} iconName={iconName} pageId={page.id} />;
|
||||
}
|
||||
if (isDisabled || (isDisabled && isHidden)) {
|
||||
return (
|
||||
|
|
@ -136,8 +137,21 @@ export const PageMenuItem = withRouter(
|
|||
setCurrentPageHandle(page.handle);
|
||||
}, [currentPageId, page.id, page.handle, switchPage, setCurrentPageHandle]);
|
||||
|
||||
const handlePageMenuSettings = useCallback(
|
||||
(event) => {
|
||||
event.stopPropagation();
|
||||
openPageEditPopover(page, popoverRef);
|
||||
},
|
||||
[popoverRef.current, page]
|
||||
);
|
||||
|
||||
return (
|
||||
<div onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
|
||||
<div
|
||||
ref={popoverRef}
|
||||
id={`edit-popover-${page.id}`}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
{editingPageName && editingPage?.id === page?.id ? (
|
||||
<>
|
||||
<RenameInput
|
||||
|
|
@ -160,9 +174,9 @@ export const PageMenuItem = withRouter(
|
|||
>
|
||||
<div className="left">
|
||||
{icon()}
|
||||
<div style={{ ...computedStyles?.text }} className="page-name">
|
||||
<OverflowTooltip>{page.name}</OverflowTooltip>
|
||||
</div>
|
||||
<OverflowTooltip childrenClassName="page-name" style={{ ...computedStyles?.text }}>
|
||||
{page.name}
|
||||
</OverflowTooltip>
|
||||
<span
|
||||
style={{
|
||||
marginLeft: '8px',
|
||||
|
|
@ -188,10 +202,7 @@ export const PageMenuItem = withRouter(
|
|||
}),
|
||||
}}
|
||||
className="edit-page-overlay-toggle"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
openPageEditPopover(page, popoverRef);
|
||||
}}
|
||||
onClick={handlePageMenuSettings}
|
||||
>
|
||||
<SolidIcon width="20" dataCy={`page-menu`} name="morevertical" />
|
||||
</button>
|
||||
|
|
@ -200,15 +211,6 @@ export const PageMenuItem = withRouter(
|
|||
</div>
|
||||
</>
|
||||
)}
|
||||
<div
|
||||
id={`edit-popover-${page.id}`}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
}}
|
||||
ref={popoverRef}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -311,6 +311,7 @@ export const QueryManagerBody = ({ darkMode, options, setOptions, activeTab }) =
|
|||
);
|
||||
};
|
||||
|
||||
// if (selectedQueryId !== selectedQuery?.id) return;
|
||||
const hasPermissions =
|
||||
selectedDataSource?.scope === 'global' && selectedDataSource?.type !== DATA_SOURCE_TYPE.SAMPLE
|
||||
? canUpdateDataSource(selectedQuery?.data_source_id) ||
|
||||
|
|
|
|||
|
|
@ -21,20 +21,22 @@ export const QueryDataPane = ({ darkMode }) => {
|
|||
const { t } = useTranslation();
|
||||
|
||||
const loadingDataQueries = useStore((state) => state.queryPanel.loadingDataQueries);
|
||||
const setQueryPanelSearchTerm = useStore((state) => state.queryPanel.setQueryPanelSearchTerm);
|
||||
const storedSearchTerm = useStore((state) => state.queryPanel.queryPanelSearchTem);
|
||||
|
||||
const dataQueries = useStore((state) => state.dataQuery.queries.modules.canvas);
|
||||
const dataSources = useStore((state) => state.dataSources);
|
||||
const [filteredQueries, setFilteredQueries] = useState(dataQueries);
|
||||
const [showSearchBox, setShowSearchBox] = useState(false);
|
||||
const [showSearchBox, setShowSearchBox] = useState(!!storedSearchTerm);
|
||||
const searchBoxRef = useRef(null);
|
||||
const [dataSourcesForFilters, setDataSourcesForFilters] = useState([]);
|
||||
const [searchTermForFilters, setSearchTermForFilters] = useState();
|
||||
|
||||
const [searchTermForFilters, setSearchTermForFilters] = useState(storedSearchTerm ?? '');
|
||||
function isDataSourceLocal(dataQuery) {
|
||||
return dataSources.some((dataSource) => dataSource.id === dataQuery.data_source_id);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setQueryPanelSearchTerm(searchTermForFilters);
|
||||
// Create a copy of the dataQueries array to perform filtering without modifying the original data.
|
||||
let filteredDataQueries = [...dataQueries];
|
||||
|
||||
|
|
@ -84,7 +86,7 @@ export const QueryDataPane = ({ darkMode }) => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
showSearchBox && searchBoxRef.current.focus();
|
||||
showSearchBox && !storedSearchTerm && searchBoxRef.current.focus();
|
||||
}, [showSearchBox]);
|
||||
|
||||
return (
|
||||
|
|
@ -135,6 +137,7 @@ export const QueryDataPane = ({ darkMode }) => {
|
|||
placeholder={t('globals.search', 'Search')}
|
||||
customClass="query-manager-search-box-wrapper flex-grow-1"
|
||||
showClearButton
|
||||
clearTextOnBlur={false}
|
||||
/>
|
||||
<ButtonSolid
|
||||
size="sm"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const DesktopHeader = ({
|
|||
}) => {
|
||||
const { showDarkModeToggle, isReleasedVersionId } = useStore(
|
||||
(state) => ({
|
||||
isReleasedVersionId: state?.releasedVersionId == state.selectedVersion?.id || state.isVersionReleased,
|
||||
isReleasedVersionId: state?.releasedVersionId == state.currentVersionId || state.isVersionReleased,
|
||||
showDarkModeToggle: state.globalSettings.appMode === 'auto' || !state.globalSettings.appMode,
|
||||
}),
|
||||
shallow
|
||||
|
|
|
|||
|
|
@ -22,7 +22,12 @@ const MobileHeader = ({
|
|||
setAppDefinitionFromVersion,
|
||||
showViewerNavigation,
|
||||
}) => {
|
||||
const isVersionReleased = useStore((state) => state.isVersionReleased);
|
||||
const { isReleasedVersionId } = useStore(
|
||||
(state) => ({
|
||||
isReleasedVersionId: state?.releasedVersionId == state.currentVersionId || state.isVersionReleased,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
const editingVersion = useStore((state) => state.editingVersion);
|
||||
const showDarkModeToggle = useStore((state) => state.globalSettings.appMode === 'auto');
|
||||
|
||||
|
|
@ -33,7 +38,7 @@ const MobileHeader = ({
|
|||
const _renderAppNameAndLogo = () => (
|
||||
<div
|
||||
className={classNames('d-flex', 'align-items-center')}
|
||||
style={{ visibility: showHeader || isVersionReleased ? 'visible' : 'hidden' }}
|
||||
style={{ visibility: showHeader || isReleasedVersionId ? 'visible' : 'hidden' }}
|
||||
>
|
||||
<h1 className="navbar-brand d-none-navbar-horizontal pe-0">
|
||||
<Link
|
||||
|
|
@ -66,14 +71,15 @@ const MobileHeader = ({
|
|||
/>
|
||||
);
|
||||
|
||||
const _renderPreviewSettings = () => (
|
||||
<PreviewSettings
|
||||
isMobileLayout
|
||||
showHeader={showHeader}
|
||||
setAppDefinitionFromVersion={setAppDefinitionFromVersion}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
);
|
||||
const _renderPreviewSettings = () =>
|
||||
!isReleasedVersionId && (
|
||||
<PreviewSettings
|
||||
isMobileLayout
|
||||
showHeader={showHeader}
|
||||
setAppDefinitionFromVersion={setAppDefinitionFromVersion}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
);
|
||||
|
||||
const _renderDarkModeBtn = (args) => {
|
||||
if (!showDarkModeToggle) return null;
|
||||
|
|
@ -88,11 +94,11 @@ const MobileHeader = ({
|
|||
);
|
||||
};
|
||||
|
||||
if (!showHeader) {
|
||||
if (!showHeader && isReleasedVersionId) {
|
||||
return <>{showViewerNavigation ? _renderMobileNavigationMenu() : _renderDarkModeBtn()}</>;
|
||||
}
|
||||
|
||||
if (!showHeader && !isVersionReleased) {
|
||||
if (!showHeader && !isReleasedVersionId) {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
|
|
@ -122,7 +128,7 @@ const MobileHeader = ({
|
|||
<span style={{}}>{_renderAppNameAndLogo()}</span>
|
||||
{_renderMobileNavigationMenu()}
|
||||
</div>
|
||||
{!isVersionReleased && !isEmpty(editingVersion) && _renderPreviewSettings()}
|
||||
{!isReleasedVersionId && !isEmpty(editingVersion) && _renderPreviewSettings()}
|
||||
{!showViewerNavigation && _renderDarkModeBtn({ styles: { top: '2px' } })}
|
||||
</Header>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ import DesktopHeader from './DesktopHeader';
|
|||
import MobileHeader from './MobileHeader';
|
||||
import ViewerSidebarNavigation from './ViewerSidebarNavigation';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { computeCanvasBackgroundColor } from '@/AppBuilder/AppCanvas/appCanvasUtils';
|
||||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import Popups from '../Popups';
|
||||
import TooljetBanner from './TooljetBanner';
|
||||
import { ModuleProvider } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
|
|
@ -41,6 +39,7 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
|
|||
homePageId,
|
||||
isMaintenanceOn,
|
||||
setIsViewer,
|
||||
toggleCurrentLayout,
|
||||
} = useStore(
|
||||
(state) => ({
|
||||
isEditorLoading: state.isEditorLoading,
|
||||
|
|
@ -60,14 +59,15 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
|
|||
updateCanvasHeight: state.updateCanvasBottomHeight,
|
||||
isMaintenanceOn: state.app.isMaintenanceOn,
|
||||
setIsViewer: state.setIsViewer,
|
||||
toggleCurrentLayout: state.toggleCurrentLayout,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
const getCurrentPageComponents = useStore((state) => state.getCurrentPageComponents, shallow);
|
||||
const currentPageComponents = useMemo(() => getCurrentPageComponents(), [getCurrentPageComponents]);
|
||||
const changeDarkMode = useStore((state) => state.changeDarkMode);
|
||||
const getCurrentPageComponents = useStore((state) => state.getCurrentPageComponents(), shallow);
|
||||
const currentPageComponents = useMemo(() => getCurrentPageComponents, [getCurrentPageComponents]);
|
||||
const isPagesSidebarHidden = useStore((state) => state.getPagesSidebarVisibility('canvas'), shallow);
|
||||
const canvasBgColor = useStore((state) => state.getCanvasBackgroundColor('canvas', darkMode), shallow);
|
||||
const deviceWindowWidth = window.screen.width - 5;
|
||||
|
||||
const computeCanvasMaxWidth = useCallback(() => {
|
||||
if (globalSettings?.maxCanvasWidth) {
|
||||
|
|
@ -92,7 +92,6 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
|
|||
const isLoading = false;
|
||||
const isMobilePreviewMode = selectedVersion?.id && currentLayout === 'mobile';
|
||||
const isAppLoaded = !!editingVersion;
|
||||
const deviceWindowWidth = window.screen.width - 5;
|
||||
const isMobileDevice = deviceWindowWidth < 600;
|
||||
const switchPage = useStore((state) => state.switchPage);
|
||||
|
||||
|
|
@ -106,6 +105,8 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
|
|||
switchDarkMode(newMode);
|
||||
};
|
||||
useEffect(() => {
|
||||
const isMobileDevice = deviceWindowWidth < 600;
|
||||
toggleCurrentLayout(isMobileDevice ? 'mobile' : 'desktop');
|
||||
setIsViewer(true);
|
||||
return () => {
|
||||
setIsViewer(false);
|
||||
|
|
@ -170,7 +171,7 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
|
|||
}}
|
||||
>
|
||||
<div className={`areas d-flex flex-rows app-${appId}`}>
|
||||
{!isPagesSidebarHidden && (
|
||||
{currentLayout !== 'mobile' && !isPagesSidebarHidden && (
|
||||
<ViewerSidebarNavigation
|
||||
showHeader={showHeader}
|
||||
isMobileDevice={currentLayout === 'mobile'}
|
||||
|
|
@ -200,6 +201,7 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
|
|||
maxWidth: isMobilePreviewMode ? '450px' : computeCanvasMaxWidth(),
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{currentLayout === 'mobile' && isMobilePreviewMode && (
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import FolderList from '@/_ui/FolderList/FolderList';
|
|||
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { APP_HEADER_HEIGHT } from '../AppCanvas/appCanvasConstants';
|
||||
import OverflowTooltip from '@/_components/OverflowTooltip';
|
||||
|
||||
export const ViewerSidebarNavigation = ({
|
||||
isMobileDevice,
|
||||
|
|
@ -21,6 +22,7 @@ export const ViewerSidebarNavigation = ({
|
|||
const { definition: { styles = {}, properties = {} } = {} } = useStore((state) => state.pageSettings) || {};
|
||||
const selectedVersionName = useStore((state) => state.selectedVersion?.name);
|
||||
const selectedEnvironmentName = useStore((state) => state.selectedEnvironment?.name);
|
||||
const homePageId = useStore((state) => state.app.homePageId);
|
||||
|
||||
if (isMobileDevice) {
|
||||
return null;
|
||||
|
|
@ -127,8 +129,10 @@ export const ViewerSidebarNavigation = ({
|
|||
></ButtonSolid>
|
||||
<div className={cx('page-handler-wrapper', { 'dark-theme': darkMode })}>
|
||||
{pages.map((page) => {
|
||||
const isHomePage = page.id === homePageId;
|
||||
const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon;
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons?.[page.icon] ?? Icons?.['IconHome2'];
|
||||
const IconElement = Icons?.[iconName] ?? Icons?.['IconFileDescription'];
|
||||
return page.hidden || page.disabled ? null : (
|
||||
<FolderList
|
||||
key={page.handle}
|
||||
|
|
@ -139,8 +143,10 @@ export const ViewerSidebarNavigation = ({
|
|||
darkMode={darkMode}
|
||||
>
|
||||
{!labelStyle?.label?.hidden && (
|
||||
<span data-cy={`pages-name-${String(page?.name).toLowerCase()}`} className="mx-2 text-wrap page-name">
|
||||
{_.truncate(page?.name, { length: 18 })}
|
||||
<span data-cy={`pages-name-${String(page?.name).toLowerCase()}`}>
|
||||
<OverflowTooltip style={{ width: '110px' }} childrenClassName={'mx-2 page-name'}>
|
||||
{page.name}
|
||||
</OverflowTooltip>
|
||||
</span>
|
||||
)}
|
||||
</FolderList>
|
||||
|
|
|
|||
|
|
@ -146,8 +146,8 @@ export const buttonGroupConfig = {
|
|||
visibility: { value: '{{true}}' },
|
||||
borderRadius: { value: '{{4}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
selectedTextColor: { value: '' },
|
||||
selectedBackgroundColor: { value: '' },
|
||||
selectedTextColor: { value: '#FFFFFF' },
|
||||
selectedBackgroundColor: { value: '#4368E3' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -307,12 +307,16 @@ export const Form = function Form(props) {
|
|||
}
|
||||
key={index}
|
||||
>
|
||||
<RenderSchema
|
||||
component={item}
|
||||
id={index}
|
||||
onOptionChange={onComponentOptionChangedForSubcontainer}
|
||||
onOptionsChange={onComponentOptionsChangedForSubcontainer}
|
||||
/>
|
||||
<div style={{ position: 'relative' }} className={`form-ele form-${id}-${index}`}>
|
||||
<RenderSchema
|
||||
component={item}
|
||||
parent={id}
|
||||
id={index}
|
||||
darkMode={darkMode}
|
||||
onOptionChange={onComponentOptionChangedForSubcontainer}
|
||||
onOptionsChange={onComponentOptionsChangedForSubcontainer}
|
||||
/>
|
||||
</div>
|
||||
{/* <Box
|
||||
{...props}
|
||||
component={item}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ export function generateUIComponents(JSONSchema, advanced, componentName = '') {
|
|||
const itemType = typeResolver(value?.type);
|
||||
if (itemType) {
|
||||
uiComponentsDraft.push(getComponentDefinition('Text'));
|
||||
uiComponentsDraft.push(getComponentDefinition(itemType));
|
||||
//only add if there is a valid item type
|
||||
} else {
|
||||
// useCurrentStateStore.getState().actions.setErrors({
|
||||
|
|
@ -89,8 +90,8 @@ export function generateUIComponents(JSONSchema, advanced, componentName = '') {
|
|||
// },
|
||||
// });
|
||||
uiComponentsDraft.push(undefined);
|
||||
uiComponentsDraft.push(undefined);
|
||||
}
|
||||
uiComponentsDraft.push(getComponentDefinition(itemType));
|
||||
});
|
||||
Object.entries(JSONSchema?.properties).forEach(([key, value], index) => {
|
||||
if (uiComponentsDraft?.length > 0 && uiComponentsDraft[index * 2 + 1]) {
|
||||
|
|
@ -148,7 +149,7 @@ export function generateUIComponents(JSONSchema, advanced, componentName = '') {
|
|||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['display_values'] = value?.displayValues;
|
||||
if (value?.label) uiComponentsDraft[index * 2 + 1]['definition']['properties']['label'] = value?.label;
|
||||
if (value?.value) uiComponentsDraft[index * 2 + 1]['definition']['properties']['value'] = value?.value;
|
||||
if (value?.values) uiComponentsDraft[index * 2 + 1]['definition']['properties']['value'] = value?.values;
|
||||
if (value?.values) uiComponentsDraft[index * 2 + 1]['definition']['properties']['values'] = value?.values;
|
||||
if (value?.loading)
|
||||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['loadingState'] = value?.loading;
|
||||
break;
|
||||
|
|
@ -362,7 +363,7 @@ export function generateUIComponents(JSONSchema, advanced, componentName = '') {
|
|||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['display_values'] = value?.displayValues;
|
||||
if (value?.label) uiComponentsDraft[index * 2 + 1]['definition']['properties']['label'] = value?.label;
|
||||
if (value?.value) uiComponentsDraft[index * 2 + 1]['definition']['properties']['value'] = value?.value;
|
||||
if (value?.values) uiComponentsDraft[index * 2 + 1]['definition']['properties']['value'] = value?.values;
|
||||
if (value?.values) uiComponentsDraft[index * 2 + 1]['definition']['properties']['values'] = value?.values;
|
||||
if (value?.showAllOption)
|
||||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['showAllOption'] = value?.showAllOption;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { getComponentToRender } from '@/AppBuilder/_helpers/editorHelpers';
|
|||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
const RenderSchema = ({ component, id, onOptionChange, onOptionsChange }) => {
|
||||
const RenderSchema = ({ component, parent, id, onOptionChange, onOptionsChange, darkMode }) => {
|
||||
const ComponentToRender = useMemo(() => getComponentToRender(component?.component), [component?.component]);
|
||||
const validateWidget = useStore((state) => state.validateWidget, shallow);
|
||||
|
||||
|
|
@ -21,13 +21,21 @@ const RenderSchema = ({ component, id, onOptionChange, onOptionsChange }) => {
|
|||
[id, onOptionsChange]
|
||||
);
|
||||
|
||||
const validate = (value) => {
|
||||
return validateWidget({
|
||||
...{ widgetValue: value },
|
||||
...{ validationObject: component.definition.validation },
|
||||
});
|
||||
};
|
||||
const validate = useCallback(
|
||||
(value) => {
|
||||
return validateWidget({
|
||||
...{ widgetValue: value },
|
||||
...{ validationObject: component.definition.validation },
|
||||
});
|
||||
},
|
||||
[component.definition.validation]
|
||||
);
|
||||
|
||||
const fireEvent = useCallback(() => {
|
||||
return Promise.resolve();
|
||||
}, []);
|
||||
|
||||
const formId = `${parent}-${id}`;
|
||||
return (
|
||||
<ComponentToRender
|
||||
properties={component?.definition?.properties}
|
||||
|
|
@ -39,7 +47,9 @@ const RenderSchema = ({ component, id, onOptionChange, onOptionsChange }) => {
|
|||
setExposedVariable={setExposedVariable}
|
||||
setExposedVariables={setExposedVariables}
|
||||
validate={validate}
|
||||
fireEvent={() => {}}
|
||||
darkMode={darkMode}
|
||||
fireEvent={fireEvent}
|
||||
formId={formId}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -72,34 +72,22 @@ export const Modal = function Modal({
|
|||
setShowModal(true);
|
||||
},
|
||||
close: async function () {
|
||||
setShowModal(false);
|
||||
setExposedVariable('show', false);
|
||||
setShowModal(false);
|
||||
},
|
||||
};
|
||||
setExposedVariables(exposedVariables);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) {
|
||||
isInitialRender.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
fireEvent(!showModal ? 'onClose' : 'onOpen');
|
||||
const inputRef = document?.getElementsByClassName('tj-text-input-widget')?.[0];
|
||||
inputRef?.blur();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [showModal]);
|
||||
|
||||
function hideModal() {
|
||||
setShowModal(false);
|
||||
setExposedVariable('show', false);
|
||||
setShowModal(false);
|
||||
}
|
||||
|
||||
function openModal() {
|
||||
setShowModal(true);
|
||||
setExposedVariable('show', true);
|
||||
setShowModal(true);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -149,6 +137,19 @@ export const Modal = function Modal({
|
|||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [showModal, modalHeight]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) {
|
||||
isInitialRender.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
fireEvent(!showModal ? 'onClose' : 'onOpen');
|
||||
const inputRef = document?.getElementsByClassName('tj-text-input-widget')?.[0];
|
||||
inputRef?.blur();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [showModal]);
|
||||
|
||||
const backwardCompatibilityCheck = height == '34' || modalHeight != undefined ? true : false;
|
||||
|
||||
const customStyles = {
|
||||
|
|
|
|||
|
|
@ -85,16 +85,20 @@ export const TableRow = React.memo(
|
|||
: ''
|
||||
}`}
|
||||
{...rowProps}
|
||||
onClickCapture={async () => {
|
||||
onClickCapture={() => {
|
||||
// toggleRowSelected will triggered useRededcuer function in useTable and in result will get the selectedFlatRows consisting row which are selected
|
||||
const selectedRow = row.original;
|
||||
const selectedRowId = row.id;
|
||||
setExposedVariables({ selectedRow, selectedRowId });
|
||||
fireEvent('onRowClicked');
|
||||
}}
|
||||
onClick={async () => {
|
||||
if (allowSelection) {
|
||||
await toggleRowSelected(row.id);
|
||||
}
|
||||
const selectedRow = row.original;
|
||||
const selectedRowId = row.id;
|
||||
setExposedVariables({ selectedRow, selectedRowId });
|
||||
mergeToTableDetails({ selectedRow, selectedRowId });
|
||||
fireEvent('onRowClicked');
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
if (hoverAdded) {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export const Pagination = function Pagination({
|
|||
{!serverSide && tableWidth > 460 && (
|
||||
<ButtonSolid
|
||||
variant="ghostBlack"
|
||||
className="tj-text-xsm"
|
||||
className="tj-text-xsm table-pagination-btn"
|
||||
style={{
|
||||
minWidth: '28px',
|
||||
width: '28px',
|
||||
|
|
@ -92,7 +92,7 @@ export const Pagination = function Pagination({
|
|||
)}
|
||||
<ButtonSolid
|
||||
variant="ghostBlack"
|
||||
className="tj-text-xsm"
|
||||
className="tj-text-xsm table-pagination-btn"
|
||||
style={{
|
||||
minWidth: '28px',
|
||||
width: '28px',
|
||||
|
|
@ -144,7 +144,7 @@ export const Pagination = function Pagination({
|
|||
<div className="d-flex">
|
||||
<ButtonSolid
|
||||
variant="ghostBlack"
|
||||
className="tj-text-xsm"
|
||||
className="tj-text-xsm table-pagination-btn"
|
||||
style={{
|
||||
minWidth: '28px',
|
||||
width: '28px',
|
||||
|
|
@ -169,7 +169,7 @@ export const Pagination = function Pagination({
|
|||
{!serverSide && tableWidth > 460 && (
|
||||
<ButtonSolid
|
||||
variant="ghostBlack"
|
||||
className="tj-text-xsm"
|
||||
className="tj-text-xsm table-pagination-btn"
|
||||
style={{
|
||||
minWidth: '28px',
|
||||
width: '28px',
|
||||
|
|
|
|||
|
|
@ -236,8 +236,9 @@ export const Table = React.memo(
|
|||
});
|
||||
const changesToBeSavedAndExposed = { dataUpdates: newDataUpdates, changeSet: newChangeset };
|
||||
mergeToTableDetails(changesToBeSavedAndExposed);
|
||||
setExposedVariables({ ...changesToBeSavedAndExposed, updatedData: clonedTableData });
|
||||
fireEvent('onCellValueChanged');
|
||||
return setExposedVariables({ ...changesToBeSavedAndExposed, updatedData: clonedTableData });
|
||||
return;
|
||||
}
|
||||
|
||||
const copyOfTableDetails = useRef(tableDetails);
|
||||
|
|
@ -565,12 +566,12 @@ export const Table = React.memo(
|
|||
|
||||
useEffect(() => {
|
||||
if (
|
||||
tableData.length != 0 &&
|
||||
properties.data.length != 0 &&
|
||||
properties.autogenerateColumns &&
|
||||
(useDynamicColumn || mode === 'edit' || mode === 'view')
|
||||
) {
|
||||
const generatedColumnFromData = autogenerateColumns(
|
||||
tableData,
|
||||
properties.data,
|
||||
properties.columns,
|
||||
properties?.columnDeletionHistory ?? [],
|
||||
useDynamicColumn,
|
||||
|
|
@ -579,9 +580,25 @@ export const Table = React.memo(
|
|||
properties.autogenerateColumns ?? false,
|
||||
id
|
||||
);
|
||||
useDynamicColumn && setGeneratedColumn(generatedColumnFromData);
|
||||
if (useDynamicColumn) {
|
||||
const dynamicColumnHasId = dynamicColumn && dynamicColumn.every((column) => 'id' in column);
|
||||
if (!dynamicColumnHasId) {
|
||||
// if dynamic columns do not have an id then we need to manually compare the generated columns with the columns in the state because the id that we generate for columns without id is a uuid and it will be different every time
|
||||
const generatedColumnsWithoutIds = generatedColumnFromData.map(({ id, ...rest }) => ({
|
||||
...rest,
|
||||
}));
|
||||
const columnsFromStateWithoutIds = generatedColumn.map(({ id, ...rest }) => ({
|
||||
...rest,
|
||||
}));
|
||||
!isEqual(generatedColumnsWithoutIds, columnsFromStateWithoutIds) &&
|
||||
setGeneratedColumn(generatedColumnFromData);
|
||||
return;
|
||||
}
|
||||
setGeneratedColumn(generatedColumnFromData);
|
||||
}
|
||||
}
|
||||
}, [tableData, JSON.stringify(dynamicColumn)]);
|
||||
// }, [tableData, JSON.stringify(dynamicColumn)]);
|
||||
}, [JSON.stringify(properties.data), JSON.stringify(dynamicColumn)]);
|
||||
|
||||
const computedStyles = {
|
||||
// width: `${width}px`,
|
||||
|
|
@ -865,7 +882,6 @@ export const Table = React.memo(
|
|||
currentData: data,
|
||||
selectedRow: [],
|
||||
selectedRowId: null,
|
||||
pageIndex: pageIndex + 1,
|
||||
});
|
||||
if (tableDetails.selectedRowId || !isEmpty(tableDetails.selectedRowDetails)) {
|
||||
toggleAllRowsSelected(false);
|
||||
|
|
|
|||
|
|
@ -107,7 +107,14 @@ export default function autogenerateColumns(
|
|||
finalKeys.includes(column?.key || column?.name)
|
||||
);
|
||||
|
||||
setTimeout(() => setProperty(id, 'columns', finalColumns, 'properties'), 10);
|
||||
setTimeout(
|
||||
() =>
|
||||
setProperty(id, 'columns', finalColumns, 'properties', 'value', false, 'canvas', {
|
||||
skipUndoRedo: true,
|
||||
saveAfterAction: true,
|
||||
}),
|
||||
10
|
||||
);
|
||||
}
|
||||
|
||||
const dataTypeToColumnTypeMapping = {
|
||||
|
|
|
|||
|
|
@ -69,14 +69,14 @@ export const Tabs = function Tabs({
|
|||
}, [parsedDefaultTab]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentTabData = parsedTabs.filter((tab) => tab.id === currentTab);
|
||||
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
|
||||
}, [currentTab, darkMode]);
|
||||
|
||||
function computeTabDisplay(componentId, id) {
|
||||
let tabVisibility = 'none';
|
||||
if (id !== currentTab) {
|
||||
if (id != currentTab) {
|
||||
return tabVisibility;
|
||||
}
|
||||
|
||||
|
|
@ -87,14 +87,13 @@ export const Tabs = function Tabs({
|
|||
}
|
||||
}
|
||||
|
||||
return id === currentTab ? 'block' : 'none';
|
||||
return id == currentTab ? 'block' : 'none';
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const exposedVariables = {
|
||||
setTab: async function (id) {
|
||||
id = typeof id === 'number' ? String(id) : id;
|
||||
if (id && currentTab !== id) {
|
||||
if (currentTab != id) {
|
||||
setCurrentTab(id);
|
||||
setExposedVariable('currentTab', id);
|
||||
fireEvent('onTabSwitch');
|
||||
|
|
@ -134,7 +133,7 @@ export const Tabs = function Tabs({
|
|||
|
||||
function shouldRenderTabContent(tab) {
|
||||
if (parsedRenderOnlyActiveTab) {
|
||||
return tab.id === currentTab;
|
||||
return tab.id == currentTab;
|
||||
}
|
||||
return true; // Render by default if no specific conditions are met
|
||||
}
|
||||
|
|
@ -162,7 +161,7 @@ export const Tabs = function Tabs({
|
|||
className="nav-item"
|
||||
style={{ opacity: tab?.disabled && '0.5', width: tabWidth == 'split' && equalSplitWidth + '%' }}
|
||||
onClick={() => {
|
||||
if (currentTab === tab.id) return;
|
||||
if (currentTab == tab.id) return;
|
||||
|
||||
!tab?.disabled && setCurrentTab(tab.id);
|
||||
!tab?.disabled && setExposedVariable('currentTab', tab.id);
|
||||
|
|
@ -193,7 +192,7 @@ export const Tabs = function Tabs({
|
|||
<div
|
||||
className="tab-content"
|
||||
ref={(newCurrent) => {
|
||||
if (currentTab === tab.id) {
|
||||
if (currentTab == tab.id) {
|
||||
parentRef.current = newCurrent;
|
||||
}
|
||||
}}
|
||||
|
|
@ -202,7 +201,7 @@ export const Tabs = function Tabs({
|
|||
>
|
||||
{shouldRenderTabContent(tab) && renderTabContent(id, tab)}
|
||||
|
||||
{/* {tab.id === currentTab && <SubContainer id={`${id}-${tab.id}`} canvasHeight={'200'} canvasWidth={width} />} */}
|
||||
{/* {tab.id == currentTab && <SubContainer id={`${id}-${tab.id}`} canvasHeight={'200'} canvasWidth={width} />} */}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { usePrevious } from '@dnd-kit/utilities';
|
|||
import { deepCamelCase } from '@/_helpers/appUtils';
|
||||
import { useEventActions } from '../_stores/slices/eventsSlice';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
import { navigate } from '../_utils/misc';
|
||||
import { extractEnvironmentConstantsFromConstantsList, navigate } from '../_utils/misc';
|
||||
import { getWorkspaceId } from '@/_helpers/utils';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { fetchAndSetWindowTitle, pageTitles, defaultWhiteLabellingSettings } from '@white-label/whiteLabelling';
|
||||
|
|
@ -93,7 +93,9 @@ const useAppData = (appId, moduleId, mode = 'edit', { environmentId, versionId }
|
|||
if (pageSwitchInProgress) {
|
||||
const currentPageEvents = events.filter((event) => event.target === 'page' && event.sourceId === currentPageId);
|
||||
setPageSwitchInProgress(false);
|
||||
handleEvent('onPageLoad', currentPageEvents, {});
|
||||
setTimeout(() => {
|
||||
handleEvent('onPageLoad', currentPageEvents, {});
|
||||
}, 0);
|
||||
}
|
||||
}, [pageSwitchInProgress, currentPageId]);
|
||||
|
||||
|
|
@ -171,10 +173,25 @@ const useAppData = (appId, moduleId, mode = 'edit', { environmentId, versionId }
|
|||
};
|
||||
}
|
||||
|
||||
const constantsResp =
|
||||
isPublicAccess && appData.is_public
|
||||
? await orgEnvironmentConstantService.getConstantsFromPublicApp(slug)
|
||||
: await orgEnvironmentConstantService.getConstantsFromApp();
|
||||
let constantsResp;
|
||||
if (mode === 'edit') {
|
||||
let defaultEnvId = null;
|
||||
if (editorEnvironment?.id == null) {
|
||||
const envs = await appEnvironmentService.getAllEnvironments(appId);
|
||||
const defaultEnv = envs.environments.find((env) => env?.is_default === true);
|
||||
defaultEnvId = defaultEnv ? defaultEnv.id : null;
|
||||
}
|
||||
constantsResp = await orgEnvironmentConstantService.getConstantsFromEnvironment(
|
||||
editorEnvironment?.id || defaultEnvId
|
||||
);
|
||||
} else {
|
||||
constantsResp =
|
||||
isPublicAccess && appData.is_public
|
||||
? await orgEnvironmentConstantService.getConstantsFromPublicApp(slug)
|
||||
: await orgEnvironmentConstantService.getConstantsFromApp(slug);
|
||||
}
|
||||
|
||||
constantsResp.constants = extractEnvironmentConstantsFromConstantsList(constantsResp?.constants, 'production');
|
||||
|
||||
const pages = appData.pages.map((page) => {
|
||||
return page;
|
||||
|
|
@ -189,7 +206,12 @@ const useAppData = (appId, moduleId, mode = 'edit', { environmentId, versionId }
|
|||
appId: appData.id,
|
||||
slug: appData.slug,
|
||||
currentAppEnvironmentId: editorEnvironmentId,
|
||||
isMaintenanceOn: result.is_maintenance_on,
|
||||
isMaintenanceOn:
|
||||
'is_maintenance_on' in result
|
||||
? result.is_maintenance_on
|
||||
: 'isMaintenanceOn' in result
|
||||
? result.isMaintenanceOn
|
||||
: false,
|
||||
organizationId: appData.organizationId || appData.organization_id,
|
||||
homePageId: homePageId,
|
||||
isPublic: appData.is_public,
|
||||
|
|
@ -201,8 +223,7 @@ const useAppData = (appId, moduleId, mode = 'edit', { environmentId, versionId }
|
|||
);
|
||||
|
||||
setPages(pages, moduleId);
|
||||
setPageSettings(deepCamelCase(appData?.editing_version?.page_settings));
|
||||
|
||||
setPageSettings(deepCamelCase(appData?.editing_version?.page_settings ?? appData?.page_settings));
|
||||
// set starting page as homepage initially
|
||||
let startingPage = appData.pages.find((page) => page.id === homePageId);
|
||||
|
||||
|
|
@ -347,7 +368,12 @@ const useAppData = (appId, moduleId, mode = 'edit', { environmentId, versionId }
|
|||
appId: appData.id,
|
||||
slug: appData.slug,
|
||||
creationMode: appData.creationMode,
|
||||
isMaintenanceOn: appData.is_maintenance_on,
|
||||
isMaintenanceOn:
|
||||
'is_maintenance_on' in appData
|
||||
? appData.is_maintenance_on
|
||||
: 'isMaintenanceOn' in appData
|
||||
? appData.isMaintenanceOn
|
||||
: false,
|
||||
organizationId: appData.organizationId || appData.organization_id,
|
||||
homePageId: appData.editing_version.homePageId,
|
||||
isPublic: appData.isPublic,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,38 @@
|
|||
const acorn = require('acorn');
|
||||
const walk = require('acorn-walk');
|
||||
|
||||
function findExpression(input) {
|
||||
const matches = [];
|
||||
let startIdx = -1;
|
||||
let braceCount = 0;
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
if (input[i] === '{' && input[i + 1] === '{' && braceCount === 0) {
|
||||
startIdx = i;
|
||||
braceCount = 2;
|
||||
i++; // Skip the second '{'
|
||||
} else if (input[i] === '{' && braceCount > 0) {
|
||||
braceCount++;
|
||||
} else if (input[i] === '}' && braceCount > 0) {
|
||||
braceCount--;
|
||||
if (braceCount === 0 && startIdx !== -1) {
|
||||
matches.push({
|
||||
fullMatch: input.slice(startIdx, i + 1),
|
||||
expression: input.slice(startIdx + 2, i - 1).trim(),
|
||||
index: startIdx,
|
||||
});
|
||||
startIdx = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
export function extractAndReplaceReferencesFromString(input, componentIdNameMapping = {}, queryIdNameMapping = {}) {
|
||||
// Quick check for relevant keywords
|
||||
const regexForQuickCheck = /\b(components|queries|globals|variables|page|parameters|secrets)(?:\[\S*|\.\S*|\?\.\S*)/;
|
||||
const regexForQuickCheck =
|
||||
/\b(components|queries|globals|variables|page|parameters|secrets|constants)(?:\[\S*|\.\S*|\?\.\S*)/;
|
||||
if (!regexForQuickCheck.test(input)) {
|
||||
return {
|
||||
allRefs: [],
|
||||
|
|
@ -12,7 +41,7 @@ export function extractAndReplaceReferencesFromString(input, componentIdNameMapp
|
|||
};
|
||||
}
|
||||
|
||||
const relevantKeywords = /\b(components|queries|globals|variables|page|parameters|secrets)\b/;
|
||||
const relevantKeywords = /\b(components|queries|globals|variables|page|parameters|secrets|constants)\b/;
|
||||
const expressionRegex = /{{(.*?)}}/gs;
|
||||
const results = [];
|
||||
let lastIndex = 0;
|
||||
|
|
@ -24,12 +53,93 @@ export function extractAndReplaceReferencesFromString(input, componentIdNameMapp
|
|||
/\b(components|queries)(\??\.|\??\.?\[['"]?)([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(['"]?\])?/g;
|
||||
|
||||
let match;
|
||||
if (input.startsWith('{{{') && input.endsWith('}}}')) {
|
||||
input = input.replace(/\{\{(.*)\}\}/, '{{($1)}}');
|
||||
const matches = findExpression(input);
|
||||
for (const match of matches) {
|
||||
const { fullMatch, expression, index } = match;
|
||||
|
||||
// Check if the expression contains relevant keywords
|
||||
if (!relevantKeywords.test(expression)) {
|
||||
replacedString += input.slice(lastIndex, index);
|
||||
bracketNotationString += input.slice(lastIndex, index);
|
||||
replacedString += fullMatch;
|
||||
bracketNotationString += fullMatch;
|
||||
lastIndex = index + fullMatch.length;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const { processedExpression, uuidMappings } = preprocessExpression(
|
||||
expression,
|
||||
uuidRegex,
|
||||
componentIdNameMapping,
|
||||
queryIdNameMapping
|
||||
);
|
||||
const parsedResult = parseExpression(
|
||||
processedExpression,
|
||||
componentIdNameMapping,
|
||||
queryIdNameMapping,
|
||||
uuidMappings
|
||||
);
|
||||
|
||||
replacedString += input.slice(lastIndex, index);
|
||||
bracketNotationString += input.slice(lastIndex, index);
|
||||
|
||||
const replacedExpression = replaceIdsInExpression(
|
||||
processedExpression,
|
||||
componentIdNameMapping,
|
||||
queryIdNameMapping,
|
||||
false,
|
||||
uuidMappings
|
||||
);
|
||||
const bracketNotationExpression = replaceIdsInExpression(
|
||||
processedExpression,
|
||||
componentIdNameMapping,
|
||||
queryIdNameMapping,
|
||||
true,
|
||||
uuidMappings
|
||||
);
|
||||
|
||||
replacedString += `{{${replacedExpression}}}`;
|
||||
bracketNotationString += `{{${bracketNotationExpression}}}`;
|
||||
|
||||
results.push({
|
||||
allRefs: parsedResult.references,
|
||||
valueWithId: `{{${replacedExpression}}}`,
|
||||
valueWithBrackets: `{{${bracketNotationExpression}}}`,
|
||||
});
|
||||
} catch (error) {
|
||||
replacedString += fullMatch;
|
||||
bracketNotationString += fullMatch;
|
||||
results.push({
|
||||
allRefs: [],
|
||||
valueWithId: fullMatch,
|
||||
valueWithBrackets: fullMatch,
|
||||
});
|
||||
}
|
||||
|
||||
lastIndex = index + fullMatch.length;
|
||||
}
|
||||
|
||||
replacedString += input.slice(lastIndex);
|
||||
bracketNotationString += input.slice(lastIndex);
|
||||
// remove the parentheses that were added
|
||||
|
||||
return {
|
||||
valueWithId: `{{${replacedString.slice(3, -3)}}}`,
|
||||
valueWithBrackets: `{{${bracketNotationString.slice(3, -3)}}}`,
|
||||
allRefs: results.flatMap((r) => r.allRefs),
|
||||
};
|
||||
}
|
||||
while ((match = expressionRegex.exec(input)) !== null) {
|
||||
const fullMatch = match[0];
|
||||
const expression = match[1].trim();
|
||||
|
||||
// Check if the expression contains relevant keywords
|
||||
if (!relevantKeywords.test(expression)) {
|
||||
replacedString += input.slice(lastIndex, match.index);
|
||||
bracketNotationString += input.slice(lastIndex, match.index);
|
||||
replacedString += fullMatch;
|
||||
bracketNotationString += fullMatch;
|
||||
lastIndex = match.index + fullMatch.length;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import DependencyGraph from './DependencyClass';
|
|||
import { getWorkspaceId } from '@/_helpers/utils';
|
||||
import { navigate } from '@/AppBuilder/_utils/misc';
|
||||
import queryString from 'query-string';
|
||||
import { replaceEntityReferencesWithIds } from '../utils';
|
||||
|
||||
const initialState = {
|
||||
app: {},
|
||||
|
|
@ -63,15 +64,19 @@ export const createAppSlice = (set, get) => ({
|
|||
}
|
||||
});
|
||||
},
|
||||
globalSettingsChanged: async (newOptions) => {
|
||||
for (const [key, value] of Object.entries(newOptions)) {
|
||||
globalSettingsChanged: async (options) => {
|
||||
const componentNameIdMapping = get().modules.canvas.componentNameIdMapping;
|
||||
const queryNameIdMapping = get().modules.canvas.queryNameIdMapping;
|
||||
for (const [key, value] of Object.entries(options)) {
|
||||
if (value?.[1]?.a == undefined) {
|
||||
newOptions[key] = value;
|
||||
options[key] = value;
|
||||
} else {
|
||||
const hexCode = `${value?.[0]}${decimalToHex(value?.[1]?.a)}`;
|
||||
newOptions[key] = hexCode;
|
||||
options[key] = hexCode;
|
||||
}
|
||||
}
|
||||
// Replace entity references with ids if present
|
||||
const newOptions = replaceEntityReferencesWithIds(options, componentNameIdMapping, queryNameIdMapping);
|
||||
const { app, currentVersionId, currentPageId } = get();
|
||||
try {
|
||||
const res = await appVersionService.autoSaveApp(
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import {
|
|||
resolveDynamicValues,
|
||||
// extractAndReplaceReferencesFromString,
|
||||
checkSubstringRegex,
|
||||
hasArrayNotation,
|
||||
parsePropertyPath,
|
||||
} from '@/AppBuilder/_stores/utils';
|
||||
import { extractAndReplaceReferencesFromString } from '@/AppBuilder/_stores/ast';
|
||||
import { componentTypeDefinitionMap } from '@/AppBuilder/WidgetManager';
|
||||
import { deepClone } from '@/_helpers/utilities/utils.helpers';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import _, { cloneDeep, merge } from 'lodash';
|
||||
import { cloneDeep, merge, set as lodashSet } from 'lodash';
|
||||
import { computeComponentName, getAllChildComponents } from '@/AppBuilder/AppCanvas/appCanvasUtils';
|
||||
import { pageConfig } from '@/AppBuilder/RightSideBar/PageSettingsTab/pageConfig';
|
||||
import { RIGHT_SIDE_BAR_TAB } from '@/AppBuilder/RightSideBar/rightSidebarConstants';
|
||||
|
|
@ -243,7 +243,8 @@ export const createComponentsSlice = (set, get) => ({
|
|||
property,
|
||||
value,
|
||||
component,
|
||||
componentResolvedValues = {}, // If componentResolvedValues is null, then it is from a setComponentProperty call
|
||||
componentResolvedValues = {},
|
||||
updatePassedValue = true,
|
||||
moduleId
|
||||
) => {
|
||||
const {
|
||||
|
|
@ -281,7 +282,7 @@ export const createComponentsSlice = (set, get) => ({
|
|||
allRefs.push(customResolvablePath);
|
||||
}
|
||||
}
|
||||
if (componentResolvedValues !== null)
|
||||
if (updatePassedValue)
|
||||
setAllValueToComponent(
|
||||
componentDetails,
|
||||
valueWithBrackets,
|
||||
|
|
@ -294,7 +295,7 @@ export const createComponentsSlice = (set, get) => ({
|
|||
|
||||
return { updatedValue: valueWithId, allRefs, unResolvedValue: valueWithBrackets, componentResolvedValues };
|
||||
} else {
|
||||
if (componentResolvedValues !== null)
|
||||
if (updatePassedValue)
|
||||
setAllValueToComponent(
|
||||
componentDetails,
|
||||
value,
|
||||
|
|
@ -317,7 +318,7 @@ export const createComponentsSlice = (set, get) => ({
|
|||
componentResolvedValues = {},
|
||||
moduleId
|
||||
) => {
|
||||
const { getAllExposedValues } = get();
|
||||
const { getAllExposedValues, getComponentTypeFromId } = get();
|
||||
const { componentId, paramType, property } = componentDetails;
|
||||
const length = Object.keys(customResolvables).length;
|
||||
if (length === 0) {
|
||||
|
|
@ -334,12 +335,31 @@ export const createComponentsSlice = (set, get) => ({
|
|||
if (!componentResolvedValues[componentId][index][paramType]) {
|
||||
componentResolvedValues[componentId][index][paramType] = {};
|
||||
}
|
||||
componentResolvedValues[componentId][index][paramType][property] = resolvedValue;
|
||||
if (hasArrayNotation(property)) {
|
||||
const keys = parsePropertyPath(property);
|
||||
lodashSet(
|
||||
componentResolvedValues,
|
||||
[componentId, index, paramType, ...keys],
|
||||
getComponentTypeFromId(componentId) === 'Table' ? value : resolvedValue
|
||||
);
|
||||
} else {
|
||||
componentResolvedValues[componentId][index][paramType][property] = resolvedValue;
|
||||
}
|
||||
} else {
|
||||
if (!componentResolvedValues[componentId][paramType]) {
|
||||
componentResolvedValues[componentId][paramType] = {};
|
||||
}
|
||||
componentResolvedValues[componentId][paramType][property] = resolvedValue;
|
||||
|
||||
if (hasArrayNotation(property)) {
|
||||
const keys = parsePropertyPath(property);
|
||||
lodashSet(
|
||||
componentResolvedValues,
|
||||
[componentId, paramType, ...keys],
|
||||
getComponentTypeFromId(componentId) === 'Table' ? value : resolvedValue
|
||||
);
|
||||
} else {
|
||||
componentResolvedValues[componentId][paramType][property] = resolvedValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Loop all the index and set the resolved value
|
||||
|
|
@ -564,6 +584,83 @@ export const createComponentsSlice = (set, get) => ({
|
|||
};
|
||||
},
|
||||
|
||||
// This function checks whether the property value is an array or not and then resolves the value accordingly
|
||||
// Cases like Table column, Dropdown options, etc.
|
||||
checkValueAndResolve: (
|
||||
componentId,
|
||||
paramType,
|
||||
property,
|
||||
value,
|
||||
component,
|
||||
resolvedComponentValues,
|
||||
updatePassedValue = true,
|
||||
moduleId
|
||||
) => {
|
||||
const { updateResolvedValues, generateDependencyGraphForRefs } = get();
|
||||
const updatedPropertyValue = cloneDeep(value);
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((val, index) => {
|
||||
//This code assumes that the array always consists of objects the else condition is to handle the case when the value is an array of strings/numbers
|
||||
if (val && typeof val === 'object') {
|
||||
Object.entries(val).forEach(([key, keyValue]) => {
|
||||
const propertyWithArrayValue = `${property}[${index}].${key}`;
|
||||
const keys = [key];
|
||||
if (keyValue?.value) {
|
||||
keys.push('value');
|
||||
}
|
||||
const { allRefs, unResolvedValue, updatedValue } = updateResolvedValues(
|
||||
componentId,
|
||||
paramType,
|
||||
propertyWithArrayValue,
|
||||
keyValue?.value ?? keyValue,
|
||||
component,
|
||||
resolvedComponentValues,
|
||||
updatePassedValue,
|
||||
moduleId
|
||||
);
|
||||
lodashSet(updatedPropertyValue, [index, ...keys], updatedValue);
|
||||
if (allRefs.length) {
|
||||
generateDependencyGraphForRefs(allRefs, componentId, paramType, propertyWithArrayValue, unResolvedValue);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const propertyWithArrayValue = `${property}[${index}]`;
|
||||
const { allRefs, unResolvedValue, updatedValue } = updateResolvedValues(
|
||||
componentId,
|
||||
paramType,
|
||||
propertyWithArrayValue,
|
||||
val,
|
||||
component,
|
||||
resolvedComponentValues,
|
||||
updatePassedValue,
|
||||
moduleId
|
||||
);
|
||||
updatedPropertyValue[index] = updatedValue;
|
||||
console.log('updatedPropertyValue', updatedPropertyValue);
|
||||
if (allRefs.length) {
|
||||
generateDependencyGraphForRefs(allRefs, componentId, paramType, propertyWithArrayValue, unResolvedValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
return { updatedValue: updatedPropertyValue };
|
||||
} else {
|
||||
const { allRefs, unResolvedValue, updatedValue } = updateResolvedValues(
|
||||
componentId,
|
||||
paramType,
|
||||
property,
|
||||
value,
|
||||
component,
|
||||
resolvedComponentValues,
|
||||
updatePassedValue,
|
||||
moduleId
|
||||
);
|
||||
if (allRefs.length) {
|
||||
generateDependencyGraphForRefs(allRefs, componentId, paramType, property, unResolvedValue);
|
||||
}
|
||||
return { allRefs, unResolvedValue, updatedValue };
|
||||
}
|
||||
},
|
||||
|
||||
updateDependencyGraphAndResolvedValues: (
|
||||
moduleId,
|
||||
componentId,
|
||||
|
|
@ -572,22 +669,20 @@ export const createComponentsSlice = (set, get) => ({
|
|||
resolvedComponentValues = {},
|
||||
paramType
|
||||
) => {
|
||||
const { updateResolvedValues, generateDependencyGraphForRefs, setAllValueToComponent } = get();
|
||||
const { checkValueAndResolve, setAllValueToComponent } = get();
|
||||
if (component.definition[paramType] === undefined) return;
|
||||
Object.entries(component.definition[paramType]).forEach(([property, value]) => {
|
||||
if (!value?.skipResolve) {
|
||||
const { allRefs, unResolvedValue } = updateResolvedValues(
|
||||
checkValueAndResolve(
|
||||
componentId,
|
||||
paramType,
|
||||
property,
|
||||
value?.value,
|
||||
component,
|
||||
resolvedComponentValues,
|
||||
true,
|
||||
moduleId
|
||||
);
|
||||
if (allRefs.length) {
|
||||
generateDependencyGraphForRefs(allRefs, componentId, paramType, property, unResolvedValue);
|
||||
}
|
||||
} else {
|
||||
const componentDetails = { componentId, paramType, property };
|
||||
setAllValueToComponent(componentDetails, value?.value, false, null, {}, resolvedComponentValues, moduleId);
|
||||
|
|
@ -925,11 +1020,10 @@ export const createComponentsSlice = (set, get) => ({
|
|||
saveComponentChanges,
|
||||
withUndoRedo,
|
||||
getComponentTypeFromId,
|
||||
updateResolvedValues,
|
||||
generateDependencyGraphForRefs,
|
||||
setResolvedComponent,
|
||||
getComponentDefinition,
|
||||
currentLayout,
|
||||
checkValueAndResolve,
|
||||
} = get();
|
||||
let hasParentChanged = false;
|
||||
let oldParentId;
|
||||
|
|
@ -1003,19 +1097,16 @@ export const createComponentsSlice = (set, get) => ({
|
|||
objectsToUpdate.forEach((paramType) => {
|
||||
if (component.definition[paramType]) {
|
||||
Object.entries(component.definition[paramType]).forEach(([property, value]) => {
|
||||
const { allRefs, unResolvedValue } = updateResolvedValues(
|
||||
checkValueAndResolve(
|
||||
componentId,
|
||||
paramType,
|
||||
property,
|
||||
value.value,
|
||||
component,
|
||||
resolvedComponentValues,
|
||||
true,
|
||||
moduleId
|
||||
);
|
||||
|
||||
if (allRefs.length) {
|
||||
generateDependencyGraphForRefs(allRefs, componentId, paramType, property, unResolvedValue, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -1066,22 +1157,67 @@ export const createComponentsSlice = (set, get) => ({
|
|||
removeDependency,
|
||||
getComponentDefinition,
|
||||
setValueToComponent,
|
||||
checkValueAndResolve,
|
||||
getResolvedComponent,
|
||||
setResolvedComponent,
|
||||
} = get();
|
||||
const { component } = getComponentDefinition(componentId, moduleId);
|
||||
const oldValue = component.definition[paramType][property];
|
||||
|
||||
if (Array.isArray(oldValue?.value)) {
|
||||
const resolvedComponent = { [componentId]: deepClone(getResolvedComponent(componentId) ?? {}) };
|
||||
resolvedComponent[componentId][paramType][property] = [];
|
||||
|
||||
const { updatedValue } = checkValueAndResolve(
|
||||
componentId,
|
||||
paramType,
|
||||
property,
|
||||
value,
|
||||
component,
|
||||
resolvedComponent,
|
||||
true,
|
||||
moduleId
|
||||
);
|
||||
setResolvedComponent(componentId, resolvedComponent[componentId], moduleId);
|
||||
|
||||
// If the value is not changed, return
|
||||
if (oldValue?.[attr] === updatedValue || oldValue === updatedValue) return;
|
||||
|
||||
set(
|
||||
withUndoRedo((state) => {
|
||||
const pageComponent = state.modules[moduleId].pages[currentPageIndex].components[componentId].component;
|
||||
lodashSet(pageComponent, ['definition', paramType, property, attr], updatedValue);
|
||||
}, skipUndoRedo),
|
||||
false,
|
||||
'setComponentProperty'
|
||||
);
|
||||
|
||||
const diff = {
|
||||
[componentId]: { component: get().modules[moduleId].pages[currentPageIndex].components[componentId].component },
|
||||
};
|
||||
|
||||
if (saveAfterAction) {
|
||||
const currentMode = get().currentMode;
|
||||
if (currentMode !== 'view') saveComponentChanges(diff, 'components', 'update');
|
||||
|
||||
get().multiplayer.broadcastUpdates({ componentId, property, value, paramType, attr }, 'components', 'update');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the value and get new dependencies
|
||||
const { updatedValue, allRefs, unResolvedValue } =
|
||||
attr === 'value' && !skipResolve
|
||||
? updateResolvedValues(componentId, paramType, property, value, component, null, moduleId)
|
||||
? updateResolvedValues(componentId, paramType, property, value, component, null, false, moduleId)
|
||||
: { updatedValue: value, allRefs: [], unResolvedValue: value };
|
||||
|
||||
// If the value is not changed, return
|
||||
const oldValue = component.definition[paramType][property];
|
||||
if (oldValue?.[attr] === updatedValue || oldValue === updatedValue) return;
|
||||
|
||||
set(
|
||||
withUndoRedo((state) => {
|
||||
const pageComponent = state.modules[moduleId].pages[currentPageIndex].components[componentId].component;
|
||||
_.set(pageComponent, ['definition', paramType, property, attr], updatedValue);
|
||||
lodashSet(pageComponent, ['definition', paramType, property, attr], updatedValue);
|
||||
}, skipUndoRedo),
|
||||
false,
|
||||
'setComponentProperty'
|
||||
|
|
@ -1130,9 +1266,8 @@ export const createComponentsSlice = (set, get) => ({
|
|||
const {
|
||||
currentPageIndex,
|
||||
saveComponentChanges,
|
||||
updateResolvedValues,
|
||||
checkValueAndResolve,
|
||||
getComponentDefinition,
|
||||
generateDependencyGraphForRefs,
|
||||
getComponentTypeFromId,
|
||||
setResolvedComponent,
|
||||
withUndoRedo,
|
||||
|
|
@ -1189,19 +1324,16 @@ export const createComponentsSlice = (set, get) => ({
|
|||
objectsToUpdate.forEach((paramType) => {
|
||||
if (component.definition[paramType]) {
|
||||
Object.entries(component.definition[paramType]).forEach(([property, value]) => {
|
||||
const { allRefs, unResolvedValue } = updateResolvedValues(
|
||||
checkValueAndResolve(
|
||||
componentId,
|
||||
paramType,
|
||||
property,
|
||||
value.value,
|
||||
component,
|
||||
resolvedComponentValues,
|
||||
true,
|
||||
moduleId
|
||||
);
|
||||
|
||||
if (allRefs.length) {
|
||||
generateDependencyGraphForRefs(allRefs, componentId, paramType, property, unResolvedValue, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -1427,6 +1559,8 @@ export const createComponentsSlice = (set, get) => ({
|
|||
getNodeData,
|
||||
getEntityResolvedValueLength,
|
||||
updateChildComponentResolvedValues,
|
||||
getComponentTypeFromId,
|
||||
getResolvedComponent,
|
||||
} = get();
|
||||
const dependecies = getDependencies(path, moduleId);
|
||||
if (dependecies?.length) {
|
||||
|
|
@ -1436,7 +1570,8 @@ export const createComponentsSlice = (set, get) => ({
|
|||
if (itemsLength) {
|
||||
updateChildComponentResolvedValues(dependency, path, itemsLength, moduleId);
|
||||
} else {
|
||||
const [entityType, entityId, type, key] = dependency.split('.');
|
||||
const [entityType, entityId, type, ...keys] = dependency.split('.');
|
||||
const key = keys.join('.');
|
||||
const unResolvedValue = getNodeData(dependency);
|
||||
const resolvedValue = resolveDynamicValues(unResolvedValue, getAllExposedValues(), {}, false, []);
|
||||
|
||||
|
|
@ -1455,13 +1590,46 @@ export const createComponentsSlice = (set, get) => ({
|
|||
? get().debugger.validateProperty(entityId, type, key, resolvedValue)
|
||||
: resolvedValue;
|
||||
|
||||
set(
|
||||
(state) => {
|
||||
state.resolvedStore.modules[moduleId][entityType][entityId][type][key] = validatedValue;
|
||||
},
|
||||
false,
|
||||
'updateDependencyValues'
|
||||
);
|
||||
// logic to handle the key like options[0].visible. It will resolve the visible directly and update the resolved store
|
||||
if (hasArrayNotation(key)) {
|
||||
const keys = parsePropertyPath(key);
|
||||
// Triggering a re-render of the table component if any of the dependent component is updated
|
||||
// This is done to calculate the callValues in the table component
|
||||
// Need to find a better way to handle this
|
||||
if (getComponentTypeFromId(entityId, moduleId) === 'Table') {
|
||||
set(
|
||||
(state) => {
|
||||
lodashSet(
|
||||
state.resolvedStore.modules[moduleId][entityType][entityId],
|
||||
['properties', 'shouldRender'],
|
||||
(getResolvedComponent(entityId)?.['properties']?.['shouldRender'] ?? 0) + 1
|
||||
);
|
||||
},
|
||||
false,
|
||||
'updateDependencyValues'
|
||||
);
|
||||
} else {
|
||||
set(
|
||||
(state) => {
|
||||
lodashSet(
|
||||
state.resolvedStore.modules[moduleId][entityType][entityId],
|
||||
[type, ...keys],
|
||||
getComponentTypeFromId(entityId, moduleId) === 'Table' ? unResolvedValue + ' ' : validatedValue
|
||||
);
|
||||
},
|
||||
false,
|
||||
'updateDependencyValues'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
set(
|
||||
(state) => {
|
||||
state.resolvedStore.modules[moduleId][entityType][entityId][type][key] = validatedValue;
|
||||
},
|
||||
false,
|
||||
'updateDependencyValues'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1588,7 +1756,7 @@ export const createComponentsSlice = (set, get) => ({
|
|||
};
|
||||
|
||||
const regex =
|
||||
/(components|queries)(\??\.|\??\.?\[['"]?)([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})(['"]?\])?(\??\.?)([^\s|},[\])?]+)?/g;
|
||||
/(components|queries)(\??\.|\??\.?\[['"]?)([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})(['"]?\])?(\??\.|\[['"]?)([^\s:?[\]'"+\-&|]+)/g;
|
||||
|
||||
return input.replace(regex, (match, category, prefix, id, suffix, optionalChaining, property) => {
|
||||
if (mappings[category] && mappings[category][id]) {
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ export const createEventsSlice = (set, get) => ({
|
|||
state.eventsSlice.module[moduleId].events = newEvents;
|
||||
});
|
||||
},
|
||||
setTablePageIndex: (tableId, index) => {
|
||||
setTablePageIndex: (tableId, index = 1) => {
|
||||
const { getExposedValueOfComponent } = get();
|
||||
if (_.isEmpty(tableId)) {
|
||||
console.log('No table is associated with this event.');
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { NO_OF_GRIDS } from '@/AppBuilder/AppCanvas/appCanvasConstants';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const initialState = {
|
||||
hoveredComponentForGrid: '',
|
||||
triggerCanvasUpdater: false,
|
||||
};
|
||||
|
||||
export const createGridSlice = (set, get) => ({
|
||||
|
|
@ -9,8 +11,13 @@ export const createGridSlice = (set, get) => ({
|
|||
setHoveredComponentForGrid: (id) =>
|
||||
set(() => ({ hoveredComponentForGrid: id }), false, { type: 'setHoveredComponentForGrid', id }),
|
||||
getHoveredComponentForGrid: () => get().hoveredComponentForGrid,
|
||||
toggleCanvasUpdater: () =>
|
||||
set((state) => ({ triggerCanvasUpdater: !state.triggerCanvasUpdater }), false, { type: 'toggleCanvasUpdater' }),
|
||||
debouncedToggleCanvasUpdater: debounce(() => {
|
||||
get().toggleCanvasUpdater();
|
||||
}, 200),
|
||||
moveComponentPosition: (direction) => {
|
||||
const { setComponentLayout, currentLayout, getSelectedComponentsDefinition } = get();
|
||||
const { setComponentLayout, currentLayout, getSelectedComponentsDefinition, debouncedToggleCanvasUpdater } = get();
|
||||
let layouts = {};
|
||||
const selectedComponents = getSelectedComponentsDefinition();
|
||||
selectedComponents.forEach((selectedComponent) => {
|
||||
|
|
@ -53,5 +60,6 @@ export const createGridSlice = (set, get) => ({
|
|||
};
|
||||
});
|
||||
setComponentLayout(layouts);
|
||||
debouncedToggleCanvasUpdater();
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,21 +1,23 @@
|
|||
const selectedItem = localStorage.getItem('selectedSidebarItem');
|
||||
const isLeftSideBarPinned = !!selectedItem;
|
||||
const selectedSidebarItem = selectedItem;
|
||||
const validSidebarItems = ['page', 'inspect', 'debugger', 'settings'];
|
||||
const storedIsSidebarPinned = localStorage.getItem('isLeftSideBarPinned') === 'true' ? true : false;
|
||||
const storedSelectedSidebarItem = !storedIsSidebarPinned
|
||||
? null
|
||||
: localStorage.getItem('selectedSidebarItem') &&
|
||||
validSidebarItems.includes(localStorage.getItem('selectedSidebarItem'))
|
||||
? localStorage.getItem('selectedSidebarItem')
|
||||
: 'page';
|
||||
|
||||
const initialState = {
|
||||
isSidebarOpen: isLeftSideBarPinned || !!selectedSidebarItem,
|
||||
isLeftSideBarPinned,
|
||||
selectedSidebarItem,
|
||||
isLeftSideBarPinned: storedIsSidebarPinned,
|
||||
selectedSidebarItem: storedIsSidebarPinned ? storedSelectedSidebarItem : null,
|
||||
isSidebarOpen: storedIsSidebarPinned,
|
||||
};
|
||||
|
||||
export const createLeftSideBarSlice = (set, get) => ({
|
||||
export const createLeftSideBarSlice = (set) => ({
|
||||
...initialState,
|
||||
setIsLeftSideBarPinned: (status) => {
|
||||
status
|
||||
? localStorage.setItem('selectedSidebarItem', get().selectedSidebarItem)
|
||||
: localStorage.removeItem('selectedSidebarItem');
|
||||
|
||||
set(() => ({ isLeftSideBarPinned: status }));
|
||||
localStorage.setItem('isLeftSideBarPinned', status === true ? 'true' : 'false');
|
||||
set(() => ({ isLeftSideBarPinned: status }), false, 'setIsLeftSideBarPinned');
|
||||
},
|
||||
setSelectedSidebarItem: (selectedSidebarItem) =>
|
||||
set(() => ({ selectedSidebarItem }), false, 'setSelectedSidebarItem'),
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ export const createPageMenuSlice = (set, get) => {
|
|||
set((state) => {
|
||||
state.editingPage = page;
|
||||
if (ref) {
|
||||
state.showEditingPopover = true;
|
||||
state.popoverTargetId = ref?.current?.id;
|
||||
state.showEditingPopover = true;
|
||||
}
|
||||
}),
|
||||
|
||||
|
|
|
|||
|
|
@ -26,11 +26,20 @@ const initialState = {
|
|||
previewPanelExpanded: false,
|
||||
loadingDataQueries: false,
|
||||
isPreviewQueryLoading: false,
|
||||
queryPanelSearchTem: '',
|
||||
};
|
||||
|
||||
export const createQueryPanelSlice = (set, get) => ({
|
||||
queryPanel: {
|
||||
...initialState,
|
||||
setQueryPanelSearchTerm: (searchTerm) =>
|
||||
set(
|
||||
(state) => {
|
||||
state.queryPanel.queryPanelSearchTem = searchTerm;
|
||||
},
|
||||
false,
|
||||
'setQueryPanelSearchTerm'
|
||||
),
|
||||
setIsDraggingQueryPane: (isDraggingQueryPane) =>
|
||||
set(
|
||||
(state) => {
|
||||
|
|
|
|||
|
|
@ -394,7 +394,22 @@ export const createResolvedSlice = (set, get) => ({
|
|||
return get().resolvedStore?.modules?.[moduleId]?.components?.[componentId];
|
||||
},
|
||||
getExposedValueOfComponent: (componentId, moduleId = 'canvas') => {
|
||||
return get().resolvedStore.modules[moduleId].exposedValues.components[componentId] || {};
|
||||
try {
|
||||
const components = get().getCurrentPageComponents();
|
||||
const {
|
||||
component: { parent: parentId, name: componentName },
|
||||
} = components[componentId];
|
||||
if (parentId) {
|
||||
// if parent is form get exposed values from children
|
||||
const { component: parentComopnent } = components?.[parentId] || {};
|
||||
if (parentComopnent?.component === 'Form') {
|
||||
return get().resolvedStore.modules[moduleId].exposedValues.components[parentId].children[componentName] || {};
|
||||
}
|
||||
}
|
||||
return get().resolvedStore.modules[moduleId].exposedValues.components[componentId] || {};
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
getAllExposedValues: (moduleId = 'canvas') => {
|
||||
return get().resolvedStore.modules[moduleId].exposedValues;
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ export const createUndoRedoSlice = (set, get) => {
|
|||
componenetPropertiesToUpdate.paramType,
|
||||
componenetPropertiesToUpdate.attr,
|
||||
undefined,
|
||||
undefined,
|
||||
{ skipUndoRedo: true }
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -672,3 +672,34 @@ export function convertAllKeysToSnakeCase(o) {
|
|||
|
||||
// return { suggestionList, hintsMap, resolvedRefs };
|
||||
// }
|
||||
|
||||
export const hasArrayNotation = (property) => {
|
||||
// Regular expression to match array notation pattern
|
||||
const arrayPattern = /\[\d+\]/;
|
||||
return arrayPattern.test(property);
|
||||
};
|
||||
|
||||
export const parsePropertyPath = (property) => {
|
||||
// Split the property path into segments
|
||||
const segments = property.split('.');
|
||||
const result = [];
|
||||
|
||||
for (const segment of segments) {
|
||||
// Check if segment contains array notation
|
||||
if (hasArrayNotation(segment)) {
|
||||
// Extract the property name and array index
|
||||
const [name, ...rest] = segment.split('[');
|
||||
if (name) result.push(name);
|
||||
|
||||
// Extract and clean up array indices
|
||||
for (const item of rest) {
|
||||
const index = parseInt(item.replace(']', ''));
|
||||
result.push(index);
|
||||
}
|
||||
} else {
|
||||
result.push(segment);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,6 +24,26 @@ export async function copyToClipboard(text) {
|
|||
}
|
||||
}
|
||||
|
||||
export const extractEnvironmentConstantsFromConstantsList = (constantsList = [], environmentName = 'development') => {
|
||||
try {
|
||||
return constantsList.map((constant) => {
|
||||
if (constant.values && Array.isArray(constant.values)) {
|
||||
const { value } = constant.values.find((value) => value.environmentName === environmentName);
|
||||
return {
|
||||
id: constant.id,
|
||||
name: constant.name,
|
||||
value,
|
||||
type: constant.type,
|
||||
};
|
||||
} else {
|
||||
return constant;
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export function setTablePageIndex(tableId, index) {
|
||||
if (_.isEmpty(tableId)) {
|
||||
console.log('No table is associated with this event.');
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ export const ButtonGroup = function Button({
|
|||
display: visibility ? '' : 'none',
|
||||
};
|
||||
|
||||
const disabledStyles = {
|
||||
opacity: 0.5,
|
||||
pointerEvents: 'none',
|
||||
cursor: 'not-allowed',
|
||||
};
|
||||
|
||||
const [defaultActive, setDefaultActive] = useState(defaultSelected);
|
||||
const [data, setData] = useState(values);
|
||||
|
||||
|
|
@ -62,7 +68,7 @@ export const ButtonGroup = function Button({
|
|||
|
||||
const buttonClick = (index) => {
|
||||
if (defaultActive?.includes(values[index]) && multiSelection) {
|
||||
const copyDefaultActive = defaultActive;
|
||||
const copyDefaultActive = [...defaultActive];
|
||||
copyDefaultActive?.splice(copyDefaultActive?.indexOf(values[index]), 1);
|
||||
setDefaultActive(copyDefaultActive);
|
||||
setExposedVariable('selected', copyDefaultActive.join(','));
|
||||
|
|
@ -100,6 +106,7 @@ export const ButtonGroup = function Button({
|
|||
color: defaultActive?.includes(values[index]) ? selectedTextColor : textColor,
|
||||
transition: 'all .1s ease',
|
||||
boxShadow,
|
||||
...(disabledState && disabledStyles),
|
||||
}}
|
||||
key={index}
|
||||
disabled={disabledState}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ export const Checkbox = ({
|
|||
const isMandatory = validation?.mandatory ?? false;
|
||||
const [defaultValue, setDefaultValue] = useState(defaultValueFromProperties);
|
||||
const [checked, setChecked] = useState(defaultValueFromProperties);
|
||||
const [value, setValue] = React.useState(defaultValueFromProperties);
|
||||
const [userInteracted, setUserInteracted] = useState(false);
|
||||
|
||||
const { label } = properties;
|
||||
|
|
@ -33,14 +32,12 @@ export const Checkbox = ({
|
|||
const [loading, setLoading] = useState(properties?.loadingState);
|
||||
const [disable, setDisable] = useState(disabledState || loadingState);
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
const { isValid, validationError } = validate(checked);
|
||||
const [validationStatus, setValidationStatus] = useState(validate(checked));
|
||||
const { isValid, validationError } = validationStatus;
|
||||
|
||||
const toggleValue = (e) => {
|
||||
const isChecked = e.target.checked;
|
||||
setChecked(isChecked);
|
||||
setValue(isChecked);
|
||||
|
||||
setExposedVariable('value', isChecked);
|
||||
setInputValue(isChecked);
|
||||
if (isChecked) {
|
||||
fireEvent('onCheck');
|
||||
} else {
|
||||
|
|
@ -52,9 +49,8 @@ export const Checkbox = ({
|
|||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setDefaultValue(defaultValueFromProperties);
|
||||
setChecked(defaultValueFromProperties);
|
||||
setValue(defaultValueFromProperties);
|
||||
setExposedVariable('value', defaultValueFromProperties);
|
||||
setInputValue(defaultValueFromProperties);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [defaultValueFromProperties]);
|
||||
|
||||
|
|
@ -104,20 +100,29 @@ export const Checkbox = ({
|
|||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isValid', isValid);
|
||||
const validationStatus = validate(checked);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('toggle', async function () {
|
||||
setInputValue(!checked);
|
||||
fireEvent('onChange');
|
||||
setUserInteracted(true);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isValid]);
|
||||
}, [checked]);
|
||||
|
||||
useEffect(() => {
|
||||
const setCheckedAndNotify = async (status) => {
|
||||
await setExposedVariable('value', status);
|
||||
setInputValue(status);
|
||||
if (status) {
|
||||
fireEvent('onCheck');
|
||||
} else {
|
||||
fireEvent('onUnCheck');
|
||||
}
|
||||
setChecked(status);
|
||||
setValue(status);
|
||||
};
|
||||
|
||||
const exposedVariables = {
|
||||
|
|
@ -137,14 +142,9 @@ export const Checkbox = ({
|
|||
setExposedVariable('isDisabled', disable);
|
||||
},
|
||||
toggle: () => {
|
||||
setExposedVariable('toggle', async function () {
|
||||
setExposedVariable('value', !checked);
|
||||
fireEvent('onChange');
|
||||
setChecked(!checked);
|
||||
setValue(!checked);
|
||||
setUserInteracted(true);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
setInputValue(!checked);
|
||||
fireEvent('onChange');
|
||||
setUserInteracted(true);
|
||||
},
|
||||
label: label,
|
||||
isMandatory: isMandatory,
|
||||
|
|
@ -154,10 +154,6 @@ export const Checkbox = ({
|
|||
isValid: isValid,
|
||||
};
|
||||
|
||||
setDefaultValue(defaultValueFromProperties);
|
||||
setChecked(defaultValueFromProperties);
|
||||
setValue(defaultValueFromProperties);
|
||||
|
||||
setExposedVariables(exposedVariables);
|
||||
|
||||
isInitialRender.current = false;
|
||||
|
|
@ -167,9 +163,7 @@ export const Checkbox = ({
|
|||
|
||||
const handleToggleChange = () => {
|
||||
const newCheckedState = !checked;
|
||||
setChecked(newCheckedState);
|
||||
setValue(newCheckedState);
|
||||
setExposedVariable('value', newCheckedState);
|
||||
setInputValue(newCheckedState);
|
||||
fireEvent('onChange');
|
||||
if (newCheckedState) {
|
||||
fireEvent('onCheck');
|
||||
|
|
@ -179,6 +173,14 @@ export const Checkbox = ({
|
|||
setUserInteracted(true);
|
||||
};
|
||||
|
||||
const setInputValue = (value) => {
|
||||
setChecked(value);
|
||||
setExposedVariable('value', value);
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
const renderCheckBox = () => (
|
||||
<>
|
||||
<div
|
||||
|
|
@ -251,7 +253,7 @@ export const Checkbox = ({
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
{validationError && visibility && !checked && userInteracted && (
|
||||
{!isValid && visibility && userInteracted && (
|
||||
<div
|
||||
data-cy={`${String(componentName).toLowerCase()}-invalid-feedback`}
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -39,11 +39,12 @@ export const Datepicker = function Datepicker({
|
|||
}
|
||||
};
|
||||
|
||||
const [validationStatus, setValidationStatus] = useState(validate(computeDateString(date)));
|
||||
const { isValid, validationError } = validationStatus;
|
||||
|
||||
const onDateChange = (date) => {
|
||||
setShowValidationError(true);
|
||||
setDate(date);
|
||||
const dateString = computeDateString(date);
|
||||
setExposedVariable('value', dateString);
|
||||
setInputValue(date);
|
||||
fireEvent('onSelect');
|
||||
};
|
||||
|
||||
|
|
@ -51,11 +52,9 @@ export const Datepicker = function Datepicker({
|
|||
if (isInitialRender.current) return;
|
||||
const dateMomentInstance = defaultValue && moment(defaultValue, selectedDateFormat);
|
||||
if (dateMomentInstance && dateMomentInstance.isValid()) {
|
||||
setDate(dateMomentInstance.toDate());
|
||||
setExposedVariable('value', defaultValue);
|
||||
setInputValue(dateMomentInstance.toDate());
|
||||
} else {
|
||||
setDate(null);
|
||||
setExposedVariable('value', undefined);
|
||||
setInputValue(null);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [defaultValue]);
|
||||
|
|
@ -73,32 +72,28 @@ export const Datepicker = function Datepicker({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disabledDates, format]);
|
||||
|
||||
const validationData = validate(computeDateString(date));
|
||||
const { isValid, validationError } = validationData;
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
const validationStatus = validate(computeDateString(date));
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
isInitialRender.current = false;
|
||||
setExposedVariable('isValid', isValid);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isValid]);
|
||||
|
||||
useEffect(() => {
|
||||
const exposedVariables = {
|
||||
isValid,
|
||||
};
|
||||
const dateMomentInstance = defaultValue && moment(defaultValue, selectedDateFormat);
|
||||
if (dateMomentInstance && dateMomentInstance.isValid()) {
|
||||
setDate(dateMomentInstance.toDate());
|
||||
exposedVariables.value = defaultValue;
|
||||
} else {
|
||||
setDate(null);
|
||||
exposedVariables.value = undefined;
|
||||
}
|
||||
setExposedVariables(exposedVariables);
|
||||
setInputValue(dateMomentInstance && dateMomentInstance.isValid() ? dateMomentInstance.toDate() : null);
|
||||
isInitialRender.current = false;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const setInputValue = (value) => {
|
||||
setDate(value);
|
||||
setExposedVariable('value', value ? computeDateString(value) : undefined);
|
||||
const validationStatus = validate(computeDateString(value));
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
data-disabled={disabledState}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export const DaterangePicker = function DaterangePicker({
|
|||
fireEvent,
|
||||
dataCy,
|
||||
id,
|
||||
formId,
|
||||
}) {
|
||||
const isInitialRender = useRef(true);
|
||||
const { borderRadius, visibility, disabledState, boxShadow } = styles;
|
||||
|
|
@ -82,10 +83,14 @@ export const DaterangePicker = function DaterangePicker({
|
|||
|
||||
function focusChanged(focus) {
|
||||
setFocusedInput(focus);
|
||||
if (focus) {
|
||||
document.querySelector(`.ele-${id}`).style.zIndex = 3;
|
||||
} else {
|
||||
document.querySelector(`.ele-${id}`).style.zIndex = '';
|
||||
const currentComponent = document.querySelector(`.ele-${id}`);
|
||||
if (currentComponent) {
|
||||
currentComponent.style.zIndex = focus ? 3 : '';
|
||||
}
|
||||
|
||||
const formComponent = formId && document.querySelector(`.form-${formId}`);
|
||||
if (formComponent) {
|
||||
formComponent.style.zIndex = focus ? 4 : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ export const DropDown = function DropDown({
|
|||
const { selectedTextColor, borderRadius, visibility, disabledState, justifyContent, boxShadow } = styles;
|
||||
const [currentValue, setCurrentValue] = useState(() => (advanced ? findDefaultItem(schema) : value));
|
||||
const [showValidationError, setShowValidationError] = useState(false);
|
||||
const validationData = validate(value);
|
||||
const { isValid, validationError } = validationData;
|
||||
const [validationStatus, setValidationStatus] = useState(validate(value));
|
||||
const { isValid, validationError } = validationStatus;
|
||||
function findDefaultItem(schema) {
|
||||
const foundItem = schema?.find((item) => item?.default === true);
|
||||
return !hasVisibleFalse(foundItem?.value) ? foundItem?.value : undefined;
|
||||
|
|
@ -59,15 +59,11 @@ export const DropDown = function DropDown({
|
|||
}
|
||||
|
||||
const setExposedItem = (value, index, onSelectFired = false) => {
|
||||
setCurrentValue(value);
|
||||
const selectedOptionLabel = index === undefined ? undefined : display_values?.[index];
|
||||
setInputValue(value, selectedOptionLabel);
|
||||
if (onSelectFired) {
|
||||
fireEvent('onSelect');
|
||||
}
|
||||
const exposedVariables = {
|
||||
value,
|
||||
selectedOptionLabel: index === undefined ? undefined : display_values?.[index],
|
||||
};
|
||||
setExposedVariables(exposedVariables);
|
||||
};
|
||||
|
||||
function selectOption(value) {
|
||||
|
|
@ -80,17 +76,6 @@ export const DropDown = function DropDown({
|
|||
setExposedItem(undefined, undefined, true);
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isValid', isValid);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isValid]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('value', currentValue);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
|
|
@ -105,6 +90,13 @@ export const DropDown = function DropDown({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [label]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
const validationStatus = validate(currentValue);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
if (advanced) {
|
||||
|
|
@ -113,7 +105,7 @@ export const DropDown = function DropDown({
|
|||
schema?.filter((item) => item?.visible)?.map((item) => item.label)
|
||||
);
|
||||
if (hasVisibleFalse(currentValue)) {
|
||||
setCurrentValue(findDefaultItem(schema));
|
||||
setInputValue(findDefaultItem(schema));
|
||||
}
|
||||
} else setExposedVariable('optionLabels', display_values);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -137,14 +129,21 @@ export const DropDown = function DropDown({
|
|||
};
|
||||
|
||||
setExposedVariables(exposedVariables);
|
||||
if (hasVisibleFalse(currentValue)) {
|
||||
setCurrentValue(findDefaultItem(schema));
|
||||
}
|
||||
isInitialRender.current = false;
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const exposedVariables = {
|
||||
selectOption: async function (value) {
|
||||
selectOption(value);
|
||||
},
|
||||
};
|
||||
|
||||
setExposedVariables(exposedVariables);
|
||||
}, [JSON.stringify(properties.values)]);
|
||||
|
||||
useEffect(() => {
|
||||
let newValue = undefined;
|
||||
let index = null;
|
||||
|
|
@ -184,6 +183,14 @@ export const DropDown = function DropDown({
|
|||
}
|
||||
};
|
||||
|
||||
const setInputValue = (value, label) => {
|
||||
setCurrentValue(value);
|
||||
setExposedVariables({ value, selectedOptionLabel: label });
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
const customStyles = {
|
||||
control: (provided, state) => ({
|
||||
...provided,
|
||||
|
|
@ -276,10 +283,8 @@ export const DropDown = function DropDown({
|
|||
onChange={(selectedOption, actionProps) => {
|
||||
setShowValidationError(true);
|
||||
if (actionProps.action === 'select-option') {
|
||||
setCurrentValue(selectedOption.value);
|
||||
setExposedVariable('value', selectedOption.value);
|
||||
setInputValue(selectedOption.value, selectedOption.label);
|
||||
fireEvent('onSelect');
|
||||
setExposedVariable('selectedOptionLabel', selectedOption.label);
|
||||
}
|
||||
}}
|
||||
options={selectOptions}
|
||||
|
|
|
|||
|
|
@ -90,17 +90,19 @@ export const DropdownV2 = ({
|
|||
} = styles;
|
||||
const isInitialRender = useRef(true);
|
||||
const [currentValue, setCurrentValue] = useState(() => (advanced ? findDefaultItem(schema) : value));
|
||||
const getResolvedValue = useStore((state) => state.getResolvedValue, shallow);
|
||||
const isMandatory = validation?.mandatory ?? false;
|
||||
const options = properties?.options;
|
||||
const validationData = validate(currentValue);
|
||||
const { isValid, validationError } = validationData;
|
||||
const [validationStatus, setValidationStatus] = useState(validate(currentValue));
|
||||
const { isValid, validationError } = validationStatus;
|
||||
const ref = React.useRef(null);
|
||||
const dropdownRef = React.useRef(null);
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
const [isDropdownLoading, setIsDropdownLoading] = useState(dropdownLoadingState);
|
||||
const [isDropdownDisabled, setIsDropdownDisabled] = useState(disabledState);
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const [searchInputValue, setSearchInputValue] = useState('');
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const [userInteracted, setUserInteracted] = useState(false);
|
||||
|
||||
const _height = padding === 'default' ? `${height}px` : `${height + 4}px`;
|
||||
const labelRef = useRef();
|
||||
function findDefaultItem(schema) {
|
||||
|
|
@ -115,12 +117,12 @@ export const DropdownV2 = ({
|
|||
let _options = advanced ? schema : options;
|
||||
if (Array.isArray(_options)) {
|
||||
let _selectOptions = _options
|
||||
.filter((data) => getResolvedValue(advanced ? data?.visible : data?.visible?.value) ?? true)
|
||||
.filter((data) => data?.visible ?? true)
|
||||
.map((data) => ({
|
||||
...data,
|
||||
label: String(getResolvedValue(data?.label)),
|
||||
value: getResolvedValue(data?.value),
|
||||
isDisabled: getResolvedValue(advanced ? data?.disable : data?.disable?.value) ?? false,
|
||||
label: data?.label,
|
||||
value: data?.value,
|
||||
isDisabled: data?.disable ?? false,
|
||||
}));
|
||||
|
||||
return _selectOptions;
|
||||
|
|
@ -132,7 +134,7 @@ export const DropdownV2 = ({
|
|||
function selectOption(value) {
|
||||
const val = selectOptions.filter((option) => !option.isDisabled)?.find((option) => option.value === value);
|
||||
if (val) {
|
||||
setCurrentValue(value);
|
||||
setInputValue(value);
|
||||
fireEvent('onSelect');
|
||||
}
|
||||
}
|
||||
|
|
@ -157,24 +159,48 @@ export const DropdownV2 = ({
|
|||
const handleOutsideClick = (e) => {
|
||||
let menu = ref.current.querySelector('.select__menu');
|
||||
if (!ref.current.contains(e.target) || !menu || !menu.contains(e.target)) {
|
||||
setIsFocused(false);
|
||||
setSearchInputValue('');
|
||||
}
|
||||
if (dropdownRef.current && !dropdownRef.current?.contains(e.target) && !menu && !menu?.contains(e.target)) {
|
||||
if (isDropdownOpen) {
|
||||
fireEvent('onBlur');
|
||||
}
|
||||
setIsDropdownOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const setInputValue = (value) => {
|
||||
setCurrentValue(value);
|
||||
const _selectedOption = selectOptions.find((option) => option.value === value);
|
||||
setExposedVariables({
|
||||
value,
|
||||
selectedOption: pick(_selectedOption, ['label', 'value']),
|
||||
});
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (advanced) {
|
||||
setCurrentValue(findDefaultItem(schema));
|
||||
} else setCurrentValue(value);
|
||||
setInputValue(findDefaultItem(schema));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [advanced, value, JSON.stringify(schema)]);
|
||||
}, [advanced, JSON.stringify(schema)]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!advanced) {
|
||||
setInputValue(value);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [advanced, value]);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('mousedown', handleOutsideClick);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleOutsideClick);
|
||||
};
|
||||
}, []);
|
||||
}, [isDropdownOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (visibility !== properties.visibility) setVisibility(properties.visibility);
|
||||
|
|
@ -186,18 +212,6 @@ export const DropdownV2 = ({
|
|||
|
||||
// Exposed variables
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('value', currentValue);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentValue]);
|
||||
|
||||
useEffect(() => {
|
||||
const _selectedOption = selectOptions.find((option) => option.value === currentValue);
|
||||
setExposedVariable('selectedOption', pick(_selectedOption, ['label', 'value']));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentValue, JSON.stringify(selectOptions)]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
const _options = selectOptions?.map(({ label, value }) => ({ label, value }));
|
||||
|
|
@ -221,11 +235,6 @@ export const DropdownV2 = ({
|
|||
setExposedVariable('searchText', searchInputValue);
|
||||
}, [searchInputValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isValid', isValid);
|
||||
}, [isValid]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isVisible', properties.visibility);
|
||||
|
|
@ -246,11 +255,18 @@ export const DropdownV2 = ({
|
|||
setExposedVariable('isMandatory', isMandatory);
|
||||
}, [isMandatory]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
const validationStatus = validate(currentValue);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
const _options = selectOptions?.map(({ label, value }) => ({ label, value }));
|
||||
const exposedVariables = {
|
||||
clear: async function () {
|
||||
setCurrentValue(null);
|
||||
setInputValue(null);
|
||||
},
|
||||
setVisibility: async function (value) {
|
||||
setVisibility(value);
|
||||
|
|
@ -303,6 +319,7 @@ export const DropdownV2 = ({
|
|||
accentColor,
|
||||
isLoading: isDropdownLoading,
|
||||
isDisabled: isDropdownDisabled,
|
||||
userInteracted,
|
||||
}),
|
||||
backgroundColor: getInputBackgroundColor({
|
||||
fieldBackgroundColor,
|
||||
|
|
@ -406,6 +423,7 @@ export const DropdownV2 = ({
|
|||
return (
|
||||
<>
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
data-cy={`label-${String(componentName).toLowerCase()} `}
|
||||
className={cx('dropdown-widget', 'd-flex', {
|
||||
[alignment === 'top' &&
|
||||
|
|
@ -442,32 +460,36 @@ export const DropdownV2 = ({
|
|||
_width={_width}
|
||||
top={'1px'}
|
||||
/>
|
||||
<div className="w-100 px-0 h-100" ref={ref}>
|
||||
<div
|
||||
className="w-100 px-0 h-100"
|
||||
onClick={() => {
|
||||
if (!isDropdownDisabled) {
|
||||
fireEvent('onFocus');
|
||||
setIsDropdownOpen((prev) => !prev);
|
||||
}
|
||||
}}
|
||||
ref={ref}
|
||||
>
|
||||
<Select
|
||||
isDisabled={isDropdownDisabled}
|
||||
value={selectOptions.filter((option) => option.value === currentValue)[0] ?? null}
|
||||
onChange={(selectedOption, actionProps) => {
|
||||
if (actionProps.action === 'clear') {
|
||||
setCurrentValue(null);
|
||||
setInputValue(null);
|
||||
}
|
||||
if (actionProps.action === 'select-option') {
|
||||
setCurrentValue(selectedOption.value);
|
||||
setInputValue(selectedOption.value);
|
||||
fireEvent('onSelect');
|
||||
}
|
||||
setIsFocused(false);
|
||||
setIsDropdownOpen(false);
|
||||
setUserInteracted(true);
|
||||
}}
|
||||
options={selectOptions}
|
||||
styles={customStyles}
|
||||
isLoading={isDropdownLoading}
|
||||
menuIsOpen={isDropdownOpen}
|
||||
onInputChange={onSearchTextChange}
|
||||
inputValue={searchInputValue}
|
||||
onMenuOpen={() => {
|
||||
fireEvent('onFocus');
|
||||
}}
|
||||
onMenuInputFocus={() => setIsFocused(true)}
|
||||
onBlur={() => {
|
||||
fireEvent('onBlur');
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
menuPortalTarget={document.body}
|
||||
components={{
|
||||
|
|
@ -479,11 +501,6 @@ export const DropdownV2 = ({
|
|||
ClearIndicator: CustomClearIndicator,
|
||||
}}
|
||||
isClearable
|
||||
{...{
|
||||
menuIsOpen: isFocused || undefined,
|
||||
isFocused: isFocused || undefined,
|
||||
}}
|
||||
// select props
|
||||
icon={icon}
|
||||
doShowIcon={iconVisibility}
|
||||
iconColor={iconColor}
|
||||
|
|
@ -494,19 +511,20 @@ export const DropdownV2 = ({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`${isValid ? '' : visibility ? 'd-flex' : 'none'}`}
|
||||
style={{
|
||||
color: errTextColor,
|
||||
justifyContent: direction === 'right' ? 'flex-start' : 'flex-end',
|
||||
fontSize: '11px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '16px',
|
||||
display: visibility ? 'block' : 'none',
|
||||
}}
|
||||
>
|
||||
{!isValid && validationError}
|
||||
</div>
|
||||
{userInteracted && visibility && !isValid && (
|
||||
<div
|
||||
className={'d-flex'}
|
||||
style={{
|
||||
color: errTextColor,
|
||||
justifyContent: direction === 'right' ? 'flex-start' : 'flex-end',
|
||||
fontSize: '11px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '16px',
|
||||
}}
|
||||
>
|
||||
{validationError}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,8 +7,16 @@ export const getInputFocusedColor = ({ accentColor }) => {
|
|||
return 'var(--primary-accent-strong)';
|
||||
};
|
||||
|
||||
export const getInputBorderColor = ({ isValid, isFocused, fieldBorderColor, accentColor, isLoading, isDisabled }) => {
|
||||
if (!isValid) {
|
||||
export const getInputBorderColor = ({
|
||||
isValid,
|
||||
isFocused,
|
||||
fieldBorderColor,
|
||||
accentColor,
|
||||
isLoading,
|
||||
isDisabled,
|
||||
userInteracted,
|
||||
}) => {
|
||||
if (userInteracted && !isValid) {
|
||||
return 'var(--status-error-strong)';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export const Multiselect = function Multiselect({
|
|||
fireEvent,
|
||||
componentName,
|
||||
dataCy,
|
||||
formId,
|
||||
}) {
|
||||
const { label, value, values, display_values, showAllOption } = properties;
|
||||
const { borderRadius, visibility, disabledState, boxShadow } = styles;
|
||||
|
|
@ -210,6 +211,11 @@ export const Multiselect = function Multiselect({
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
const formComponent = formId && document.querySelector(`.form-${formId}`);
|
||||
if (formComponent) {
|
||||
formComponent.style.zIndex = isOpen ? 4 : '';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ import Label from '@/_ui/Label';
|
|||
const tinycolor = require('tinycolor2');
|
||||
import { CustomDropdownIndicator, CustomClearIndicator } from '../DropdownV2/DropdownV2';
|
||||
import { getInputBackgroundColor, getInputBorderColor, getInputFocusedColor } from '../DropdownV2/utils';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
export const MultiselectV2 = ({
|
||||
id,
|
||||
|
|
@ -28,7 +26,6 @@ export const MultiselectV2 = ({
|
|||
validate,
|
||||
validation,
|
||||
componentName,
|
||||
width,
|
||||
}) => {
|
||||
let {
|
||||
label,
|
||||
|
|
@ -65,16 +62,18 @@ export const MultiselectV2 = ({
|
|||
const isMandatory = validation?.mandatory ?? false;
|
||||
const multiselectRef = React.useRef(null);
|
||||
const labelRef = React.useRef(null);
|
||||
const validationData = validate(selected?.length ? selected?.map((option) => option.value) : null);
|
||||
const { isValid, validationError } = validationData;
|
||||
const [validationStatus, setValidationStatus] = useState(
|
||||
validate(selected?.length ? selected?.map((option) => option.value) : null)
|
||||
);
|
||||
const { isValid, validationError } = validationStatus;
|
||||
const valueContainerRef = React.useRef(null);
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
const [isMultiSelectLoading, setIsMultiSelectLoading] = useState(multiSelectLoadingState);
|
||||
const getResolvedValue = useStore((state) => state.getResolvedValue, shallow);
|
||||
const [isMultiSelectDisabled, setIsMultiSelectDisabled] = useState(disabledState);
|
||||
const [isSelectAllSelected, setIsSelectAllSelected] = useState(false);
|
||||
const [searchInputValue, setSearchInputValue] = useState('');
|
||||
const _height = padding === 'default' ? `${height}px` : `${height + 4}px`;
|
||||
const [userInteracted, setUserInteracted] = useState(false);
|
||||
|
||||
const [isMultiselectOpen, setIsMultiselectOpen] = useState(false);
|
||||
useEffect(() => {
|
||||
|
|
@ -89,12 +88,12 @@ export const MultiselectV2 = ({
|
|||
const _options = advanced ? schema : options;
|
||||
let _selectOptions = Array.isArray(_options)
|
||||
? _options
|
||||
.filter((data) => getResolvedValue(advanced ? data?.visible : data?.visible?.value) ?? true)
|
||||
.filter((data) => data?.visible ?? true)
|
||||
.map((data) => ({
|
||||
...data,
|
||||
label: getResolvedValue(data?.label),
|
||||
value: getResolvedValue(data?.value),
|
||||
isDisabled: getResolvedValue(advanced ? data?.disable : data?.disable?.value) ?? false,
|
||||
label: data?.label,
|
||||
value: data?.value,
|
||||
isDisabled: data?.disable ?? false,
|
||||
}))
|
||||
: [];
|
||||
return _selectOptions;
|
||||
|
|
@ -126,39 +125,41 @@ export const MultiselectV2 = ({
|
|||
return false;
|
||||
}
|
||||
const onChangeHandler = (items, action) => {
|
||||
setSelected(items);
|
||||
if (action.action === 'select-option') {
|
||||
setExposedVariable(
|
||||
'values',
|
||||
items.map((item) => item.value)
|
||||
);
|
||||
fireEvent('onSelect');
|
||||
}
|
||||
setInputValue(items);
|
||||
fireEvent('onSelect');
|
||||
setUserInteracted(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let foundItem = findDefaultItem(values, advanced);
|
||||
setSelected(foundItem);
|
||||
setInputValue(foundItem);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
let foundItem = findDefaultItem(values, advanced, true);
|
||||
setSelected(foundItem);
|
||||
if (advanced) {
|
||||
let foundItem = findDefaultItem(values, advanced, true);
|
||||
setInputValue(foundItem);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [advanced, JSON.stringify(schema), JSON.stringify(values)]);
|
||||
}, [advanced, JSON.stringify(values), JSON.stringify(schema)]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!advanced) {
|
||||
let foundItem = findDefaultItem(values, advanced, true);
|
||||
setInputValue(foundItem);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [advanced, JSON.stringify(values)]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable(
|
||||
'selectedOptions',
|
||||
Array.isArray(selected) && selected?.map(({ label, value }) => ({ label, value }))
|
||||
);
|
||||
setExposedVariable(
|
||||
'options',
|
||||
Array.isArray(selectOptions) && selectOptions?.map(({ label, value }) => ({ label, value }))
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify(selected), selectOptions]);
|
||||
}, [selectOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
|
|
@ -187,13 +188,17 @@ export const MultiselectV2 = ({
|
|||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isValid', isValid);
|
||||
}, [isValid]);
|
||||
const validationStatus = validate(selected?.length ? selected?.map((option) => option.value) : null);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
const defaultItems = findDefaultItem(values, advanced, true);
|
||||
|
||||
const exposedVariables = {
|
||||
clear: async function () {
|
||||
setSelected([]);
|
||||
setInputValue([]);
|
||||
},
|
||||
setVisibility: async function (value) {
|
||||
setVisibility(value);
|
||||
|
|
@ -210,7 +215,7 @@ export const MultiselectV2 = ({
|
|||
isDisabled: disabledState,
|
||||
isMandatory: isMandatory,
|
||||
isValid: isValid,
|
||||
selectedOptions: Array.isArray(selected) && selected?.map(({ label, value }) => ({ label, value })),
|
||||
selectedOptions: Array.isArray(defaultItems) && defaultItems?.map(({ label, value }) => ({ label, value })),
|
||||
options: Array.isArray(selectOptions) && selectOptions?.map(({ label, value }) => ({ label, value })),
|
||||
};
|
||||
setExposedVariables(exposedVariables);
|
||||
|
|
@ -237,7 +242,7 @@ export const MultiselectV2 = ({
|
|||
newSelected.push(...optionsToAdd);
|
||||
}
|
||||
});
|
||||
setSelected(newSelected);
|
||||
setInputValue(newSelected);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -247,7 +252,7 @@ export const MultiselectV2 = ({
|
|||
// Check if array provided is a list of objects with value key
|
||||
const _value = value.map((val) => (isObject(val) && has(val, 'value') ? val.value : val));
|
||||
const newSelected = selected.filter((option) => !_value.includes(option.value));
|
||||
setSelected(newSelected);
|
||||
setInputValue(newSelected);
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -276,6 +281,17 @@ export const MultiselectV2 = ({
|
|||
}
|
||||
};
|
||||
|
||||
const setInputValue = (values) => {
|
||||
setSelected(values);
|
||||
setExposedVariables({
|
||||
values: values.map((item) => item.value),
|
||||
selectedOptions: Array.isArray(values) && values?.map(({ label, value }) => ({ label, value })),
|
||||
});
|
||||
const validationStatus = validate(values?.length ? values?.map((option) => option.value) : null);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('mousedown', handleClickOutside, { capture: true });
|
||||
return () => {
|
||||
|
|
@ -312,6 +328,7 @@ export const MultiselectV2 = ({
|
|||
accentColor,
|
||||
isLoading: isMultiSelectLoading,
|
||||
isDisabled: isMultiSelectDisabled,
|
||||
userInteracted,
|
||||
}),
|
||||
backgroundColor: getInputBackgroundColor({
|
||||
fieldBackgroundColor,
|
||||
|
|
@ -400,7 +417,6 @@ export const MultiselectV2 = ({
|
|||
}),
|
||||
};
|
||||
const _width = (labelWidth / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
|
@ -484,8 +500,13 @@ export const MultiselectV2 = ({
|
|||
containerRef={valueContainerRef}
|
||||
showAllOption={showAllOption}
|
||||
isSelectAllSelected={isSelectAllSelected}
|
||||
setIsSelectAllSelected={setIsSelectAllSelected}
|
||||
setSelected={setSelected}
|
||||
setIsSelectAllSelected={(value) => {
|
||||
setIsSelectAllSelected(value);
|
||||
if (!value) {
|
||||
fireEvent('onSelect');
|
||||
}
|
||||
}}
|
||||
setSelected={setInputValue}
|
||||
iconColor={iconColor}
|
||||
optionsLoadingState={optionsLoadingState && advanced}
|
||||
darkMode={darkMode}
|
||||
|
|
@ -495,19 +516,20 @@ export const MultiselectV2 = ({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`${isValid ? '' : visibility ? 'd-flex' : 'none'}`}
|
||||
style={{
|
||||
color: errTextColor,
|
||||
justifyContent: direction === 'right' ? 'flex-start' : 'flex-end',
|
||||
fontSize: '11px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '16px',
|
||||
display: visibility ? 'block' : 'none',
|
||||
}}
|
||||
>
|
||||
{!isValid && validationError}
|
||||
</div>
|
||||
{userInteracted && visibility && !isValid && (
|
||||
<div
|
||||
className={'d-flex'}
|
||||
style={{
|
||||
color: errTextColor,
|
||||
justifyContent: direction === 'right' ? 'flex-start' : 'flex-end',
|
||||
fontSize: '11px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '16px',
|
||||
}}
|
||||
>
|
||||
{validationError}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ export const NumberInput = function NumberInput({
|
|||
const [loading, setLoading] = useState(loadingState);
|
||||
const [showValidationError, setShowValidationError] = useState(false);
|
||||
const [value, setValue] = React.useState(Number(parseFloat(properties.value).toFixed(properties.decimalPlaces)));
|
||||
const { isValid, validationError } = validate(value);
|
||||
const [validationStatus, setValidationStatus] = useState(validate(value));
|
||||
const { isValid, validationError } = validationStatus;
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
|
||||
const inputRef = useRef(null);
|
||||
|
|
@ -65,31 +66,22 @@ export const NumberInput = function NumberInput({
|
|||
}, [label]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(Number(parseFloat(value).toFixed(properties.decimalPlaces)));
|
||||
setInputValue(Number(parseFloat(value).toFixed(properties.decimalPlaces)));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.decimalPlaces]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(Number(parseFloat(properties.value).toFixed(properties.decimalPlaces)));
|
||||
setInputValue(Number(parseFloat(properties.value).toFixed(properties.decimalPlaces)));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.value]);
|
||||
|
||||
const handleBlur = (e) => {
|
||||
setValue(Number(parseFloat(e.target.value).toFixed(properties.decimalPlaces)));
|
||||
setInputValue(Number(parseFloat(e.target.value).toFixed(properties.decimalPlaces)));
|
||||
setShowValidationError(true);
|
||||
e.stopPropagation();
|
||||
fireEvent('onBlur');
|
||||
setIsFocused(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
if (!isNaN(value)) {
|
||||
setExposedVariable('value', value);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isMandatory', isMandatory);
|
||||
|
|
@ -114,12 +106,6 @@ export const NumberInput = function NumberInput({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disable]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isValid', isValid);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isValid]);
|
||||
|
||||
useEffect(() => {
|
||||
disable !== disabledState && setDisable(disabledState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -128,11 +114,19 @@ export const NumberInput = function NumberInput({
|
|||
visibility !== properties.visibility && setVisibility(properties.visibility);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
loading !== loadingState && setLoading(loadingState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loadingState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
const exposedVariables = {
|
||||
setFocus: async function () {
|
||||
|
|
@ -144,14 +138,12 @@ export const NumberInput = function NumberInput({
|
|||
setText: async function (text) {
|
||||
if (text) {
|
||||
const newValue = Number(parseFloat(text));
|
||||
setValue(newValue);
|
||||
setExposedVariable('value', text);
|
||||
setInputValue(newValue);
|
||||
fireEvent('onChange');
|
||||
}
|
||||
},
|
||||
clear: async function () {
|
||||
setValue('');
|
||||
setExposedVariable('value', '');
|
||||
setInputValue('');
|
||||
fireEvent('onChange');
|
||||
},
|
||||
setLoading: async function (loading) {
|
||||
|
|
@ -244,14 +236,13 @@ export const NumberInput = function NumberInput({
|
|||
// eslint-disable-next-line import/namespace
|
||||
|
||||
const handleChange = (e) => {
|
||||
setValue(Number(parseFloat(e.target.value)));
|
||||
if (e.target.value == '') {
|
||||
setValue(null);
|
||||
setExposedVariable('value', null);
|
||||
setInputValue(null);
|
||||
fireEvent('onChange');
|
||||
} else {
|
||||
setInputValue(Number(parseFloat(e.target.value)));
|
||||
}
|
||||
if (!isNaN(Number(parseFloat(e.target.value)))) {
|
||||
setExposedVariable('value', Number(parseFloat(e.target.value)));
|
||||
fireEvent('onChange');
|
||||
}
|
||||
};
|
||||
|
|
@ -260,22 +251,30 @@ export const NumberInput = function NumberInput({
|
|||
e.preventDefault(); // Prevent the default button behavior (form submission, page reload)
|
||||
|
||||
const newValue = (value || 0) + 1;
|
||||
setValue(newValue);
|
||||
setInputValue(newValue);
|
||||
if (!isNaN(newValue)) {
|
||||
setExposedVariable('value', newValue);
|
||||
fireEvent('onChange');
|
||||
}
|
||||
};
|
||||
const handleDecrement = (e) => {
|
||||
e.preventDefault();
|
||||
const newValue = (value || 0) - 1;
|
||||
setValue(newValue);
|
||||
setInputValue(newValue);
|
||||
if (!isNaN(newValue)) {
|
||||
setExposedVariable('value', newValue);
|
||||
fireEvent('onChange');
|
||||
}
|
||||
};
|
||||
|
||||
const setInputValue = (value) => {
|
||||
setValue(value);
|
||||
if (!isNaN(value)) {
|
||||
setExposedVariable('value', value);
|
||||
}
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
const loaderStyle = {
|
||||
right:
|
||||
direction === 'right' &&
|
||||
|
|
@ -375,8 +374,7 @@ export const NumberInput = function NumberInput({
|
|||
autoComplete="off"
|
||||
onKeyUp={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
setInputValue(e.target.value);
|
||||
fireEvent('onEnterPressed');
|
||||
}
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ export const PasswordInput = function PasswordInput({
|
|||
const [disable, setDisable] = useState(disabledState || loadingState);
|
||||
const [passwordValue, setPasswordValue] = useState(properties.value);
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
const { isValid, validationError } = validate(passwordValue);
|
||||
const [validationStatus, setValidationStatus] = useState(validate(passwordValue));
|
||||
const { isValid, validationError } = validationStatus;
|
||||
const [showValidationError, setShowValidationError] = useState(false);
|
||||
const [labelWidth, setLabelWidth] = useState(0);
|
||||
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
|
||||
|
|
@ -152,12 +153,6 @@ export const PasswordInput = function PasswordInput({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loadingState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isValid', isValid);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isValid]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isMandatory', isMandatory);
|
||||
|
|
@ -184,11 +179,17 @@ export const PasswordInput = function PasswordInput({
|
|||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setPasswordValue(properties.value);
|
||||
setExposedVariable('value', properties?.value ?? '');
|
||||
setInputValue(properties?.value || '');
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
const validationStatus = validate(passwordValue);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
const exposedVariables = {
|
||||
setFocus: async function () {
|
||||
|
|
@ -198,13 +199,11 @@ export const PasswordInput = function PasswordInput({
|
|||
textInputRef.current.blur();
|
||||
},
|
||||
setText: async function (text) {
|
||||
setPasswordValue(text);
|
||||
setExposedVariable('value', text);
|
||||
setInputValue(text);
|
||||
fireEvent('onChange');
|
||||
},
|
||||
clear: async function () {
|
||||
setPasswordValue('');
|
||||
setExposedVariable('value', '');
|
||||
setInputValue('');
|
||||
fireEvent('onChange');
|
||||
},
|
||||
setLoading: async function (loading) {
|
||||
|
|
@ -228,7 +227,6 @@ export const PasswordInput = function PasswordInput({
|
|||
value: properties?.value ?? '',
|
||||
};
|
||||
|
||||
setPasswordValue(properties.value ?? '');
|
||||
setExposedVariables(exposedVariables);
|
||||
isInitialRender.current = false;
|
||||
|
||||
|
|
@ -254,6 +252,15 @@ export const PasswordInput = function PasswordInput({
|
|||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const setInputValue = (value) => {
|
||||
setPasswordValue(value);
|
||||
setExposedVariable('value', value);
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
const renderInput = () => (
|
||||
<>
|
||||
<div
|
||||
|
|
@ -361,14 +368,12 @@ export const PasswordInput = function PasswordInput({
|
|||
autoComplete="new-password"
|
||||
onKeyUp={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setPasswordValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
setInputValue(e.target.value);
|
||||
fireEvent('onEnterPressed');
|
||||
}
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setPasswordValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
setInputValue(e.target.value);
|
||||
fireEvent('onChange');
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ export const TextInput = function TextInput({
|
|||
const [disable, setDisable] = useState(disabledState || loadingState);
|
||||
const [value, setValue] = useState(properties.value);
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
// const isValid = true; // TODO: remove this and uncomment the below line
|
||||
const { isValid, validationError } = validate(value);
|
||||
const [validationStatus, setValidationStatus] = useState(validate(value));
|
||||
const { isValid, validationError } = validationStatus;
|
||||
const [showValidationError, setShowValidationError] = useState(false);
|
||||
|
||||
const [labelWidth, setLabelWidth] = useState(0);
|
||||
|
|
@ -156,18 +156,18 @@ export const TextInput = function TextInput({
|
|||
}, [loadingState]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isValid', isValid);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isValid]);
|
||||
if (isInitialRender.current) return;
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
if (properties.value === undefined) {
|
||||
setValue('');
|
||||
setExposedVariable('value', '');
|
||||
setInputValue('');
|
||||
} else {
|
||||
setValue(properties.value);
|
||||
setExposedVariable('value', properties.value);
|
||||
setInputValue(properties.value);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -200,13 +200,11 @@ export const TextInput = function TextInput({
|
|||
useEffect(() => {
|
||||
const exposedVariables = {
|
||||
setText: async function (text) {
|
||||
setValue(text);
|
||||
setExposedVariable('value', text);
|
||||
setInputValue(text);
|
||||
fireEvent('onChange');
|
||||
},
|
||||
clear: async function () {
|
||||
setValue('');
|
||||
setExposedVariable('value', '');
|
||||
setInputValue('');
|
||||
fireEvent('onChange');
|
||||
},
|
||||
setFocus: async function () {
|
||||
|
|
@ -241,13 +239,20 @@ export const TextInput = function TextInput({
|
|||
isVisible: visibility,
|
||||
isDisabled: disable,
|
||||
};
|
||||
setValue(properties.value);
|
||||
|
||||
setExposedVariables(exposedVariables);
|
||||
isInitialRender.current = false;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const setInputValue = (value) => {
|
||||
setValue(value);
|
||||
setExposedVariable('value', value);
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
const iconName = styles.icon; // Replace with the name of the icon you want
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons[iconName] == undefined ? Icons['IconHome2'] : Icons[iconName];
|
||||
|
|
@ -320,14 +325,12 @@ export const TextInput = function TextInput({
|
|||
} validation-without-icon`}
|
||||
onKeyUp={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
setInputValue(e.target.value);
|
||||
fireEvent('onEnterPressed');
|
||||
}
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
setInputValue(e.target.value);
|
||||
fireEvent('onChange');
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
|
|
|
|||
|
|
@ -12,13 +12,11 @@ const Switch = ({
|
|||
borderColor,
|
||||
setOn,
|
||||
styles,
|
||||
setExposedVariable,
|
||||
fireEvent,
|
||||
setUserInteracted,
|
||||
}) => {
|
||||
const handleToggleChange = () => {
|
||||
setOn(!on);
|
||||
setExposedVariable('value', !on);
|
||||
fireEvent('onChange');
|
||||
setUserInteracted(true);
|
||||
};
|
||||
|
|
@ -105,8 +103,8 @@ export const ToggleSwitchV2 = ({
|
|||
const [on, setOn] = useState(Boolean(defaultValue));
|
||||
const label = properties.label;
|
||||
const isMandatory = validation?.mandatory ?? false;
|
||||
const { isValid, validationError } = validate(on);
|
||||
const [showValidationError, setShowValidationError] = useState(true);
|
||||
const [validationStatus, setValidationStatus] = useState(validate(on));
|
||||
const { isValid, validationError } = validationStatus;
|
||||
const [loading, setLoading] = useState(properties?.loadingState);
|
||||
const [disable, setDisable] = useState(properties.disabledState || properties.loadingState);
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
|
|
@ -123,17 +121,24 @@ export const ToggleSwitchV2 = ({
|
|||
};
|
||||
// Exposing the initially set false value once on load
|
||||
|
||||
const setInputValue = (value) => {
|
||||
setOn(value);
|
||||
setExposedVariable('value', value);
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('value', defaultValue);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
setOn(defaultValue);
|
||||
setInputValue(defaultValue);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [defaultValue]);
|
||||
|
||||
const toggle = () => {
|
||||
setOn(!on);
|
||||
setInputValue(!on);
|
||||
setUserInteracted(true);
|
||||
};
|
||||
|
||||
|
|
@ -164,6 +169,13 @@ export const ToggleSwitchV2 = ({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isMandatory]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
const validationStatus = validate(on);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isLoading', loading);
|
||||
|
|
@ -180,17 +192,11 @@ export const ToggleSwitchV2 = ({
|
|||
setExposedVariable('isDisabled', disable);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disable]);
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('isValid', isValid);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isValid]);
|
||||
|
||||
useEffect(() => {
|
||||
const exposedVariables = {
|
||||
setValue: async function (value) {
|
||||
setOn(value);
|
||||
setExposedVariable('value', value);
|
||||
setInputValue(value);
|
||||
setUserInteracted(true);
|
||||
},
|
||||
setVisibility: async function (state) {
|
||||
|
|
@ -214,15 +220,13 @@ export const ToggleSwitchV2 = ({
|
|||
value: defaultValue,
|
||||
};
|
||||
setExposedVariables(exposedVariables);
|
||||
setOn(defaultValue);
|
||||
isInitialRender.current = false;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('toggle', async function () {
|
||||
setExposedVariable('value', !on);
|
||||
setInputValue(!on);
|
||||
fireEvent('onChange');
|
||||
setOn(!on);
|
||||
setUserInteracted(true);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -270,15 +274,11 @@ export const ToggleSwitchV2 = ({
|
|||
onChange={toggleValue}
|
||||
color={toggleSwitchColor}
|
||||
alignment={alignment}
|
||||
validationError={validationError}
|
||||
isValid={isValid}
|
||||
showValidationError={showValidationError}
|
||||
properties={properties}
|
||||
setShowValidationError={setShowValidationError}
|
||||
borderColor={borderColor}
|
||||
setOn={setOn}
|
||||
setOn={setInputValue}
|
||||
styles={styles}
|
||||
setExposedVariable={setExposedVariable}
|
||||
fireEvent={fireEvent}
|
||||
setUserInteracted={setUserInteracted}
|
||||
/>
|
||||
|
|
@ -294,7 +294,7 @@ export const ToggleSwitchV2 = ({
|
|||
}}
|
||||
>
|
||||
{renderInput()}
|
||||
{showValidationError && isMandatory && userInteracted && !on && visibility && (
|
||||
{userInteracted && visibility && !isValid && (
|
||||
<div
|
||||
data-cy={`${String(componentName).toLowerCase()}-invalid-feedback`}
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -742,9 +742,9 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
|
|||
<p className="tj-text-sm d-flex align-items-center">
|
||||
<Avatar
|
||||
className="name-avatar"
|
||||
avatarId={user.avatarId}
|
||||
text={`${user.first_name ? user.first_name[0] : ''}${
|
||||
user.last_name ? user.last_name[0] : ''
|
||||
avatarId={user?.avatarId}
|
||||
text={`${user.firstName ? user.firstName[0] : ''}${
|
||||
user.lastName ? user.lastName[0] : ''
|
||||
}`}
|
||||
/>
|
||||
<span>{`${user?.firstName ?? ''} ${user?.lastName ?? ''}`}</span>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
|||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import classnames from 'classnames';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
export const DarkModeToggle = function DarkModeToggle({
|
||||
darkMode = false,
|
||||
|
|
@ -11,8 +13,14 @@ export const DarkModeToggle = function DarkModeToggle({
|
|||
tooltipPlacement = 'bottom',
|
||||
showText = false,
|
||||
}) {
|
||||
const setResolvedGlobals = useStore((state) => state.setResolvedGlobals, shallow);
|
||||
const appMode = useStore((state) => state.globalSettings.appMode, shallow);
|
||||
|
||||
const toggleDarkMode = () => {
|
||||
switchDarkMode(!darkMode);
|
||||
if (appMode === 'auto') {
|
||||
setResolvedGlobals('theme', { name: !darkMode ? 'dark' : 'light' });
|
||||
}
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export const SearchBox = forwardRef(
|
|||
autoFocus = false,
|
||||
showClearButton,
|
||||
initialValue = '',
|
||||
clearTextOnBlur = true,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
|
|
@ -39,7 +40,7 @@ export const SearchBox = forwardRef(
|
|||
};
|
||||
|
||||
const handleClickOutside = (event) => {
|
||||
if (ref?.current && !ref.current.contains(event.target)) {
|
||||
if (ref?.current && !ref.current.contains(event.target) && clearTextOnBlur) {
|
||||
clearSearchText();
|
||||
// Your function to be triggered
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ export function SortableList({ items, onChange, renderItem }) {
|
|||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: { delay: 150 },
|
||||
}),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates,
|
||||
})
|
||||
// useSensor(KeyboardSensor, {
|
||||
// coordinateGetter: sortableKeyboardCoordinates,
|
||||
// })
|
||||
);
|
||||
|
||||
const shouldFreeze = useStore((state) => state.isVersionReleased || state.isEditorFreezed);
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@ $btn-dark-color: #FFFFFF;
|
|||
|
||||
|
||||
.page-selector-panel-body {
|
||||
height: 100%;
|
||||
padding: 12px 16px;
|
||||
border-right: 1px solid #DFE3E6;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
}
|
||||
|
||||
.page-name {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.navigation-area {
|
||||
|
|
|
|||
|
|
@ -766,7 +766,7 @@ button {
|
|||
right: 300px;
|
||||
left: 48px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
|
|
@ -3875,6 +3875,18 @@ input[type="text"] {
|
|||
}
|
||||
}
|
||||
|
||||
.form-ele{
|
||||
.DateRangePicker_picker{
|
||||
top: 40px !important;
|
||||
}
|
||||
|
||||
.daterange-picker-widget{
|
||||
.DateInput_fang {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fw-400 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
|
@ -9195,6 +9207,17 @@ tbody {
|
|||
.spin-loader {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
|
||||
&.theme-dark {
|
||||
background-color: #1f2936;
|
||||
|
||||
.load {
|
||||
background-color: #1f2936;
|
||||
}
|
||||
}
|
||||
|
||||
.load {
|
||||
display: flex;
|
||||
|
|
@ -12701,7 +12724,7 @@ color: var(--text-default);
|
|||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
top: 55px;
|
||||
top: 65px;
|
||||
|
||||
.released-version-popup-cover {
|
||||
width: 250px;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,16 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
&.table-pagination-btn{
|
||||
&:disabled {
|
||||
svg {
|
||||
path {
|
||||
fill: var(--slate8) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: var(--slate6) !important;
|
||||
color: var(--slate9) !important;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.load.dark-loader {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 365 KiB After Width: | Height: | Size: 364 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 342 KiB After Width: | Height: | Size: 342 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 20 KiB |
|
|
@ -47,7 +47,12 @@ const WorkspaceNameFormCE = () => {
|
|||
shallow
|
||||
);
|
||||
useEnterKeyPress(() => handleSubmit());
|
||||
|
||||
const { setOnboardingStepsCompleted } = useOnboardingStore(
|
||||
(state) => ({
|
||||
setOnboardingStepsCompleted: state.setOnboardingStepsCompleted,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
const [formData, setFormData] = useState({ workspaceName: workspaceName });
|
||||
const [error, setError] = useState('');
|
||||
const [isFormValid, setIsFormValid] = useState(true);
|
||||
|
|
@ -121,6 +126,7 @@ const WorkspaceNameFormCE = () => {
|
|||
try {
|
||||
await setWorkspaceName(formData.workspaceName);
|
||||
await onboardUserOrCreateAdmin();
|
||||
setOnboardingStepsCompleted();
|
||||
} catch (error) {
|
||||
const errorMessage = error?.error || 'Something went wrong. Please try again.';
|
||||
toast.error(errorMessage);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,12 @@ import OnboardingFormWrapper from '../OnboardingFormWrapper/OnboardingFormWrappe
|
|||
import './resources/styles/onboarding-ui-wrapper.styles.scss';
|
||||
|
||||
const OnboardingUIWrapper = ({ children: components }) => {
|
||||
const pageLocation = window.location.pathname;
|
||||
if (pageLocation == '/setup') {
|
||||
const isEmptyPath = window.location.pathname == '/';
|
||||
const isSetupRoute = window.location.pathname.split('/').pop().toLowerCase() === 'setup';
|
||||
const pathEndSegments = window.location.pathname.split('/').filter(Boolean).slice(-2);
|
||||
const isInvitationRoute =
|
||||
pathEndSegments.length === 2 && pathEndSegments[0] === 'invitations' && pathEndSegments[1]?.length > 0;
|
||||
if (isSetupRoute || isInvitationRoute || isEmptyPath) {
|
||||
return (
|
||||
<div className="onboarding-setup-wrapper">
|
||||
<OnboardingFormWrapper>{components}</OnboardingFormWrapper>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ import invitationsStore from '@/modules/onboarding/stores/invitationsStore';
|
|||
import { LinkExpiredPage } from '@/ConfirmationPage/LinkExpiredPage';
|
||||
import { utils } from '@/modules/common/helpers';
|
||||
import { getSubpath } from '@/_helpers/routes';
|
||||
|
||||
const PostOnboardingComponent = () => null;
|
||||
import { TJLoader } from '@/_ui/TJLoader/TJLoader';
|
||||
import useOnboardingStore from '@/modules/onboarding/stores/onboardingStore';
|
||||
const PostOnboardingComponent = () => <TJLoader />;
|
||||
|
||||
export const InvitationPage = (darkMode = false) => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
|
@ -23,8 +24,8 @@ export const InvitationPage = (darkMode = false) => {
|
|||
const source = searchParams.get('source');
|
||||
const redirectTo = searchParams.get('redirectTo');
|
||||
|
||||
const { initiateInvitedUserOnboarding, isOnboardingStepsCompleted } = invitationsStore();
|
||||
|
||||
const { initiateInvitedUserOnboarding } = invitationsStore();
|
||||
const { isOnboardingStepsCompleted } = useOnboardingStore();
|
||||
useEffect(() => {
|
||||
getUserDetails();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { OnboardingBackgroundWrapper, OnboardingQuestions } from '@/modules/onboarding/components';
|
||||
import { SetupAdminForm } from './components';
|
||||
import { GeneralFeatureImage } from '@/modules/common/components';
|
||||
import useOnboardingStore from '@/modules/onboarding/stores/onboardingStore';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { TJLoader } from '@/_ui/TJLoader/TJLoader';
|
||||
const PostOnboardingComponent = () => <TJLoader />;
|
||||
|
||||
const SetupAdminPageCE = () => {
|
||||
const { currentStep } = useOnboardingStore(
|
||||
const { currentStep, isOnboardingStepsCompleted } = useOnboardingStore(
|
||||
(state) => ({
|
||||
currentStep: state.currentStep,
|
||||
isOnboardingStepsCompleted: state.isOnboardingStepsCompleted,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
|
||||
if (currentStep > 0) {
|
||||
if (isOnboardingStepsCompleted && PostOnboardingComponent) {
|
||||
return <PostOnboardingComponent />;
|
||||
} else if (currentStep > 0) {
|
||||
return <OnboardingQuestions />;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ const useCEOnboardingStore = create(
|
|||
currentStep: 0,
|
||||
totalSteps: 1,
|
||||
accountCreated: false,
|
||||
isOnboardingStepsCompleted: false,
|
||||
|
||||
// Action to update admin details
|
||||
setAdminDetails: (details) =>
|
||||
|
|
@ -40,6 +41,7 @@ const useCEOnboardingStore = create(
|
|||
// action to set current step
|
||||
setCurrentStep: (step) => set({ currentStep: step }),
|
||||
|
||||
setOnboardingStepsCompleted: () => set({ isOnboardingStepsCompleted: true }),
|
||||
// Action to reset the store
|
||||
resetStore: () =>
|
||||
set({
|
||||
|
|
@ -75,7 +77,8 @@ const useCEOnboardingStore = create(
|
|||
const path = getSubpath()
|
||||
? `${getSubpath()}/${session?.current_organization_slug}/apps/${appId}`
|
||||
: `/${session?.current_organization_slug}/apps/${appId}`;
|
||||
window.location.href = path;
|
||||
history.pushState(null, null, path);
|
||||
window.location.reload();
|
||||
},
|
||||
|
||||
setAccountCreated: (value) => set({ accountCreated: value }),
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.0.0-ce-lts
|
||||
3.0.1-ce-lts
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export class OrganizationConstantController {
|
|||
@Get('public/:app_slug')
|
||||
async getConstantsFromPublicApp(@App() app) {
|
||||
const result = await this.organizationConstantsService.allEnvironmentConstants(
|
||||
app.OrganizationId,
|
||||
app.organizationId,
|
||||
false,
|
||||
OrganizationConstantType.GLOBAL
|
||||
);
|
||||
|
|
|
|||
|
|
@ -36,11 +36,13 @@ export const buildComponentMetaDefinition = (components = {}) => {
|
|||
componentMeta.definition.properties,
|
||||
currentComponentData?.component?.definition?.properties,
|
||||
(objValue, srcValue) => {
|
||||
if (
|
||||
['Table', 'DropdownV2', 'MultiselectV2'].includes(currentComponentData?.component?.component) &&
|
||||
if (['Table'].includes(currentComponentData?.component?.component) && isArray(objValue)) {
|
||||
return srcValue;
|
||||
} else if (
|
||||
['DropdownV2', 'MultiselectV2'].includes(currentComponentData?.component?.component) &&
|
||||
isArray(objValue)
|
||||
) {
|
||||
return srcValue;
|
||||
return isArray(srcValue) ? srcValue : Object.values(srcValue);
|
||||
}
|
||||
}
|
||||
),
|
||||
|
|
|
|||
|
|
@ -146,8 +146,8 @@ export const buttonGroupConfig = {
|
|||
visibility: { value: '{{true}}' },
|
||||
borderRadius: { value: '{{4}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
selectedTextColor: { value: '' },
|
||||
selectedBackgroundColor: { value: '' },
|
||||
selectedTextColor: { value: '#FFFFFF' },
|
||||
selectedBackgroundColor: { value: '#4368E3' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -325,6 +325,20 @@ export class AppImportExportService {
|
|||
await manager.save(toUpdateDataQueries);
|
||||
}
|
||||
}
|
||||
// update Global settings of created versions
|
||||
const appVersionIds = Object.values(resourceMapping.appVersionMapping);
|
||||
const newAppVersions = await manager.find(AppVersion, {
|
||||
where: {
|
||||
id: In(appVersionIds),
|
||||
},
|
||||
select: ['id', 'globalSettings'],
|
||||
});
|
||||
for (const appVersion of newAppVersions) {
|
||||
if (appVersion.globalSettings) {
|
||||
const updatedGlobalSettings = updateEntityReferences(appVersion.globalSettings, mappings);
|
||||
await manager.update(AppVersion, { id: appVersion.id }, { globalSettings: updatedGlobalSettings });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async createImportedAppForUser(manager: EntityManager, appParams: any, user: User, isGitApp = false): Promise<App> {
|
||||
|
|
@ -1260,6 +1274,23 @@ export class AppImportExportService {
|
|||
return appResourceMappings;
|
||||
}
|
||||
|
||||
createViewerNavigationVisibilityForImportedApp(importedVersion: AppVersion) {
|
||||
let pageSettings = {};
|
||||
if (importedVersion.pageSettings) {
|
||||
pageSettings = { ...importedVersion.pageSettings };
|
||||
} else {
|
||||
pageSettings = {
|
||||
properties: {
|
||||
disableMenu: {
|
||||
value: `{{${!importedVersion.showViewerNavigation}}}`,
|
||||
fxActive: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return pageSettings;
|
||||
}
|
||||
|
||||
async createAppVersionsForImportedApp(
|
||||
manager: EntityManager,
|
||||
user: User,
|
||||
|
|
@ -1300,7 +1331,7 @@ export class AppImportExportService {
|
|||
version.showViewerNavigation = appVersion.showViewerNavigation;
|
||||
version.homePageId = appVersion.homePageId;
|
||||
version.globalSettings = appVersion.globalSettings;
|
||||
version.pageSettings = appVersion.pageSettings;
|
||||
version.pageSettings = this.createViewerNavigationVisibilityForImportedApp(appVersion);
|
||||
} else {
|
||||
version.showViewerNavigation = appVersion.definition?.showViewerNavigation || true;
|
||||
version.homePageId = appVersion.definition?.homePageId;
|
||||
|
|
@ -1318,7 +1349,7 @@ export class AppImportExportService {
|
|||
};
|
||||
} else {
|
||||
version.globalSettings = appVersion.definition?.globalSettings;
|
||||
version.pageSettings = appVersion.definition?.pageSettings;
|
||||
version.pageSettings = this.createViewerNavigationVisibilityForImportedApp(appVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -392,6 +392,15 @@ export class AppsService {
|
|||
dataQueryMapping: oldDataQueryToNewMapping,
|
||||
});
|
||||
|
||||
if (appVersion.globalSettings) {
|
||||
const globalSettings = appVersion.globalSettings;
|
||||
const updatedGlobalSettings = updateEntityReferences(globalSettings, {
|
||||
...oldDataQueryToNewMapping,
|
||||
...oldComponentToNewComponentMapping,
|
||||
});
|
||||
await manager.update(AppVersion, { id: appVersion.id }, { globalSettings: updatedGlobalSettings });
|
||||
}
|
||||
|
||||
await this.updateEventActionsForNewVersionWithNewMappingIds(
|
||||
manager,
|
||||
appVersion.id,
|
||||
|
|
|
|||
|
|
@ -97,13 +97,13 @@ export class ComponentsService {
|
|||
componentData[column === 'others' ? 'displayPreferences' : column],
|
||||
updatedDefinition[column],
|
||||
(objValue, srcValue) => {
|
||||
if (
|
||||
(componentData.type === 'Table' ||
|
||||
componentData.type === 'DropdownV2' ||
|
||||
componentData.type === 'MultiselectV2') &&
|
||||
if (componentData.type === 'Table' && _.isArray(objValue)) {
|
||||
return srcValue;
|
||||
} else if (
|
||||
(componentData.type === 'DropdownV2' || componentData.type === 'MultiselectV2') &&
|
||||
_.isArray(objValue)
|
||||
) {
|
||||
return srcValue;
|
||||
return _.isArray(srcValue) ? srcValue : Object.values(srcValue);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue