mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-06 06:48:21 +00:00
* fix deps * Modularisation changes for Build with AI feature * New app loading UI for Build with AI feature & added animation for chat messages * Fix Error using AI feature * add missing services and logic * fix app gen * update submodules * EE frontend submodule updated * update submodules * EE frontend submodule updated post sync * Added Artifact Preview component to ee moddules list * Updated ai slice code * app gen changes * Resolved fix with AI bugs * Resolved AI Copilot bugs * app gen changes and query fixes * fix query generation bugs * update copilot * Resolved ChatMode dropdown & popover bug fix * Resolved PR suggestions & PreviewBox component in CE edition * Synced frontend/ee with main * Synced server/ee with main branch * Enhance submodule checkout process to handle branch existence and fallback to main (#13218) * Enhance submodule checkout process to handle branch existence and fallback to main * Improve submodule checkout process to handle branch validation and fallback to main * chore: Comment out Node.js setup, dependency installation, and build steps in cloud frontend workflow * refactor: Enhance submodule checkout process to include submodule name in logs * Update submodule checkout process to use the correct submodule name extraction method * fix: Update submodule checkout script to use correct submodule path variable * Improve submodule checkout process to correctly handle branch names and fallback to main * chore: Uncomment Node.js setup, dependency installation, and build steps in cloud frontend workflow * fix: Update branch checkout logic to use correct syntax and improve fallback handling * fix: Update git checkout command to use -B flag for branch creation * fix: Improve submodule checkout process to explicitly fetch branch ref before checkout * fix: Enhance submodule checkout process with improved branch validation and error handling * fix: Improve branch checkout logic by enhancing fetch command and validating branch existence * fix: Enhance manual Git checkout process with improved fetch and error handling * fix: Restore Node.js setup, dependency installation, and Netlify deployment steps in workflow * 🔄 chore: update submodules to latest main after auto-merge * Took sync of fix/appbuilder-02 in frontend/ee --------- Co-authored-by: Kartik Gupta <gupta.kartik18kg@gmail.com> Co-authored-by: Adish M <44204658+adishM98@users.noreply.github.com> Co-authored-by: adishM98 Bot <adish.madhu@gmail.com>
278 lines
9.6 KiB
JavaScript
278 lines
9.6 KiB
JavaScript
import React, { useState, useEffect, useRef } from 'react';
|
|
import useStore from '@/AppBuilder/_stores/store';
|
|
import { SidebarItem } from './SidebarItem';
|
|
import cx from 'classnames';
|
|
import { shallow } from 'zustand/shallow';
|
|
import { DarkModeToggle } from '@/_components';
|
|
import Popover from '@/_ui/Popover';
|
|
// import { PageMenu } from './PageMenu';
|
|
import LeftSidebarInspector from './LeftSidebarInspector/LeftSidebarInspector';
|
|
import GlobalSettings from './GlobalSettings';
|
|
import '../../_styles/left-sidebar.scss';
|
|
import Debugger from './Debugger/Debugger';
|
|
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
|
import { withEditionSpecificComponent } from '@/modules/common/helpers/withEditionSpecificComponent';
|
|
import { PageMenu } from '../RightSideBar/PageSettingsTab/PageMenu';
|
|
|
|
// TODO: remove passing refs to LeftSidebarItem and use state
|
|
// TODO: need to add datasources to the sidebar.
|
|
// TODO: add dark/light mode toggle
|
|
// TODO: move popover and component selection to separate component
|
|
// TODO: create usable header component that can accept page specific buttton as props/children
|
|
export const BaseLeftSidebar = ({
|
|
darkMode = false,
|
|
switchDarkMode,
|
|
renderAISideBarTrigger = () => null,
|
|
renderAIChat = () => null,
|
|
isUserInZeroToOneFlow,
|
|
}) => {
|
|
const { moduleId, isModuleEditor, appType } = useModuleContext();
|
|
const [
|
|
pinned,
|
|
selectedSidebarItem,
|
|
setPinned,
|
|
setSelectedSidebarItem,
|
|
currentMode,
|
|
queryPanelHeight,
|
|
unreadErrorCount,
|
|
resetUnreadErrorCount,
|
|
toggleLeftSidebar,
|
|
isSidebarOpen,
|
|
isDraggingQueryPane,
|
|
] = useStore(
|
|
(state) => [
|
|
state.isLeftSideBarPinned,
|
|
state.selectedSidebarItem,
|
|
state.setIsLeftSideBarPinned,
|
|
state.setSelectedSidebarItem,
|
|
state.modeStore.modules[moduleId].currentMode,
|
|
state.queryPanel.queryPanelHeight,
|
|
state.debugger.unreadErrorCount,
|
|
state.debugger.resetUnreadErrorCount,
|
|
state.toggleLeftSidebar,
|
|
state.isSidebarOpen,
|
|
state.queryPanel.isDraggingQueryPane,
|
|
],
|
|
shallow
|
|
);
|
|
|
|
const [popoverContentHeight, setPopoverContentHeight] = useState(queryPanelHeight);
|
|
const sideBarBtnRefs = useRef({});
|
|
|
|
const handleSelectedSidebarItem = (item) => {
|
|
if (item === 'debugger') resetUnreadErrorCount();
|
|
setSelectedSidebarItem(item);
|
|
if (item === selectedSidebarItem && !pinned) {
|
|
return toggleLeftSidebar(false);
|
|
}
|
|
if (!isSidebarOpen) toggleLeftSidebar(true);
|
|
};
|
|
|
|
const setSideBarBtnRefs = (page) => (ref) => {
|
|
sideBarBtnRefs.current[page] = ref;
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (isUserInZeroToOneFlow) {
|
|
setPopoverContentHeight(((window.innerHeight - 48) / window.innerHeight) * 100);
|
|
return;
|
|
}
|
|
|
|
if (!isDraggingQueryPane) {
|
|
setPopoverContentHeight(
|
|
((window.innerHeight - (queryPanelHeight == 0 ? 40 : queryPanelHeight) - 45) / window.innerHeight) * 100
|
|
);
|
|
} else {
|
|
setPopoverContentHeight(100);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [isUserInZeroToOneFlow, queryPanelHeight, isDraggingQueryPane]);
|
|
|
|
const renderPopoverContent = () => {
|
|
if (selectedSidebarItem === null || !isSidebarOpen) return null;
|
|
switch (selectedSidebarItem) {
|
|
case 'page':
|
|
return (
|
|
<PageMenu
|
|
setPinned={setPinned}
|
|
pinned={pinned}
|
|
darkMode={darkMode}
|
|
selectedSidebarItem={selectedSidebarItem}
|
|
/>
|
|
);
|
|
case 'inspect':
|
|
return (
|
|
<LeftSidebarInspector
|
|
darkMode={darkMode}
|
|
// selectedSidebarItem={selectedSidebarItem}
|
|
// appDefinition={appDefinition}
|
|
// setSelectedComponent={setSelectedComponent}
|
|
// removeComponent={removeComponent}
|
|
// runQuery={runQuery}
|
|
// popoverContentHeight={popoverContentHeight}
|
|
setPinned={setPinned}
|
|
pinned={pinned}
|
|
moduleId={moduleId}
|
|
appType={appType}
|
|
/>
|
|
);
|
|
case 'tooljetai':
|
|
return renderAIChat({ darkMode, isUserInZeroToOneFlow });
|
|
// case 'datasource':
|
|
// return (
|
|
// <LeftSidebarDataSources
|
|
// darkMode={darkMode}
|
|
// appId={appId}
|
|
// dataSourcesChanged={dataSourcesChanged}
|
|
// globalDataSourcesChanged={globalDataSourcesChanged}
|
|
// dataQueriesChanged={dataQueriesChanged}
|
|
// toggleDataSourceManagerModal={toggleDataSourceManagerModal}
|
|
// showDataSourceManagerModal={showDataSourceManagerModal}
|
|
// onDeleteofAllDataSources={() => {
|
|
// handleSelectedSidebarItem(null);
|
|
// handlePin(false);
|
|
// delete sideBarBtnRefs.current['datasource'];
|
|
// }}
|
|
// setPinned={handlePin}
|
|
// pinned={pinned}
|
|
// />
|
|
// );
|
|
case 'debugger':
|
|
return <Debugger setPinned={setPinned} pinned={pinned} darkMode={darkMode} />;
|
|
// );
|
|
// case 'settings':
|
|
// return (
|
|
// <GlobalSettings
|
|
// globalSettingsChanged={globalSettingsChanged}
|
|
// globalSettings={appDefinition.globalSettings}
|
|
// darkMode={darkMode}
|
|
// toggleAppMaintenance={toggleAppMaintenance}
|
|
// isMaintenanceOn={isMaintenanceOn}
|
|
// app={app}
|
|
// backgroundFxQuery={backgroundFxQuery}
|
|
// />
|
|
// );
|
|
case 'settings':
|
|
return (
|
|
<GlobalSettings
|
|
// globalSettingsChanged={globalSettingsChanged}
|
|
// globalSettings={appDefinition.globalSettings}
|
|
darkMode={darkMode}
|
|
isModuleEditor={isModuleEditor}
|
|
// toggleAppMaintenance={toggleAppMaintenance}
|
|
// isMaintenanceOn={isMaintenanceOn}
|
|
// app={app}
|
|
// backgroundFxQuery={backgroundFxQuery}
|
|
/>
|
|
);
|
|
}
|
|
};
|
|
|
|
// TODO: Move this logic to a wrapper component and show components based on the mode
|
|
if (currentMode === 'view') {
|
|
return null;
|
|
}
|
|
|
|
const renderCommonItems = () => {
|
|
return (
|
|
<>
|
|
<SidebarItem
|
|
selectedSidebarItem={selectedSidebarItem}
|
|
onClick={() => handleSelectedSidebarItem('inspect')}
|
|
darkMode={darkMode}
|
|
icon="inspect"
|
|
className={`left-sidebar-item left-sidebar-layout left-sidebar-inspector`}
|
|
tip="Inspector"
|
|
ref={setSideBarBtnRefs('inspect')}
|
|
/>
|
|
|
|
<SidebarItem
|
|
icon="debugger"
|
|
selectedSidebarItem={selectedSidebarItem}
|
|
darkMode={darkMode}
|
|
// eslint-disable-next-line no-unused-vars
|
|
onClick={(e) => handleSelectedSidebarItem('debugger')}
|
|
className={`left-sidebar-item left-sidebar-layout`}
|
|
badge={true}
|
|
count={unreadErrorCount}
|
|
tip="Debugger"
|
|
ref={setSideBarBtnRefs('debugger')}
|
|
/>
|
|
</>
|
|
);
|
|
};
|
|
|
|
const renderLeftSidebarItems = () => {
|
|
if (isModuleEditor) {
|
|
return renderCommonItems();
|
|
}
|
|
return (
|
|
<>
|
|
{renderAISideBarTrigger({
|
|
selectedSidebarItem: selectedSidebarItem,
|
|
onClick: () => handleSelectedSidebarItem('tooljetai'),
|
|
darkMode: darkMode,
|
|
icon: 'tooljetai',
|
|
className: `left-sidebar-item left-sidebar-layout left-sidebar-page-selector`,
|
|
tip: 'Build with AI',
|
|
ref: setSideBarBtnRefs('tooljetai'),
|
|
})}
|
|
|
|
{!isUserInZeroToOneFlow && (
|
|
<>
|
|
{renderCommonItems()}
|
|
<SidebarItem
|
|
icon="settings"
|
|
selectedSidebarItem={selectedSidebarItem}
|
|
darkMode={darkMode}
|
|
// eslint-disable-next-line no-unused-vars
|
|
onClick={(e) => handleSelectedSidebarItem('settings')}
|
|
className={`left-sidebar-item left-sidebar-layout`}
|
|
badge={true}
|
|
tip="Settings"
|
|
ref={setSideBarBtnRefs('settings')}
|
|
isModuleEditor={isModuleEditor}
|
|
/>
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className={cx('left-sidebar', { 'dark-theme theme-dark': darkMode })} data-cy="left-sidebar-inspector">
|
|
{renderLeftSidebarItems()}
|
|
<Popover
|
|
onInteractOutside={(e) => {
|
|
// if tooljetai is open don't close
|
|
if (selectedSidebarItem === 'tooljetai') return;
|
|
const isWithinSidebar = e.target.closest('.left-sidebar');
|
|
const isClickOnInspect = e.target.closest('.config-handle-inspect');
|
|
if (pinned || isWithinSidebar || isClickOnInspect) return;
|
|
toggleLeftSidebar(false);
|
|
}}
|
|
open={isSidebarOpen}
|
|
popoverContentClassName={`p-0 left-sidebar-scrollbar sidebar-h-100-popover ${selectedSidebarItem}`}
|
|
side="right"
|
|
popoverContent={renderPopoverContent()}
|
|
popoverContentHeight={popoverContentHeight}
|
|
/>
|
|
<div className="left-sidebar-stack-bottom">
|
|
<div className="">
|
|
{/* <div style={{ maxHeight: '32px', maxWidth: '32px', marginBottom: '16px' }}>
|
|
<LeftSidebarComment
|
|
selectedSidebarItem={showComments ? 'comments' : ''}
|
|
currentPageId={currentPageId}
|
|
isVersionReleased={isVersionReleased}
|
|
isEditorFreezed={isEditorFreezed}
|
|
ref={setSideBarBtnRefs('comments')}
|
|
/>
|
|
</div> */}
|
|
<DarkModeToggle switchDarkMode={switchDarkMode} darkMode={darkMode} tooltipPlacement="right" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const LeftSidebar = withEditionSpecificComponent(BaseLeftSidebar, 'AiBuilder');
|