mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 09:28:31 +00:00
406 lines
15 KiB
JavaScript
406 lines
15 KiB
JavaScript
import React, { Suspense } from 'react';
|
|
// eslint-disable-next-line no-unused-vars
|
|
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
|
|
import { authorizeWorkspace } from '@/_helpers/authorizeWorkspace';
|
|
import { authenticationService, tooljetService } from '@/_services';
|
|
import { withRouter } from '@/_hoc/withRouter';
|
|
import { PrivateRoute, AdminRoute, AppsRoute, SwitchWorkspaceRoute, OrganizationInviteRoute } from '@/Routes';
|
|
import { HomePage } from '@/HomePage';
|
|
import { LoginPage } from '@/LoginPage';
|
|
import { SignupPage } from '@/SignupPage';
|
|
import { TooljetDatabase } from '@/TooljetDatabase';
|
|
import { OrganizationInvitationPage } from '@/ConfirmationPage';
|
|
import { Authorize } from '@/Oauth2';
|
|
import { Authorize as Oauth } from '@/Oauth';
|
|
import { Viewer } from '@/Editor';
|
|
import { OrganizationSettings } from '@/OrganizationSettingsPage';
|
|
import { SettingsPage } from '../SettingsPage/SettingsPage';
|
|
import { ForgotPassword } from '@/ForgotPassword';
|
|
import { ResetPassword } from '@/ResetPassword';
|
|
import { MarketplacePage } from '@/MarketplacePage';
|
|
import SwitchWorkspacePage from '@/HomePage/SwitchWorkspacePage';
|
|
import { GlobalDatasources } from '@/GlobalDatasources';
|
|
import { lt } from 'semver';
|
|
import Toast from '@/_ui/Toast';
|
|
import { VerificationSuccessInfoScreen } from '@/SuccessInfoScreen';
|
|
import '@/_styles/theme.scss';
|
|
import { AppLoader } from '@/AppLoader';
|
|
import SetupScreenSelfHost from '../SuccessInfoScreen/SetupScreenSelfHost';
|
|
export const BreadCrumbContext = React.createContext({});
|
|
import 'react-tooltip/dist/react-tooltip.css';
|
|
import { getWorkspaceIdOrSlugFromURL } from '@/_helpers/routes';
|
|
import ErrorPage from '@/_components/ErrorComponents/ErrorPage';
|
|
import WorkspaceConstants from '@/WorkspaceConstants';
|
|
import { AuthRoute } from '@/Routes/AuthRoute';
|
|
import { useAppDataStore } from '@/_stores/appDataStore';
|
|
import cx from 'classnames';
|
|
import useAppDarkMode from '@/_hooks/useAppDarkMode';
|
|
import { ManageOrgUsers } from '@/ManageOrgUsers';
|
|
import { ManageGroupPermissions } from '@/ManageGroupPermissions';
|
|
import OrganizationLogin from '@/_components/OrganizationLogin/OrganizationLogin';
|
|
import { ManageOrgVars } from '@/ManageOrgVars';
|
|
import { ManageGroupPermissionsV2 } from '@/ManageGroupPermissionsV2/ManageGroupPermissionsV2';
|
|
|
|
const AppWrapper = (props) => {
|
|
const { isAppDarkMode } = useAppDarkMode();
|
|
return (
|
|
<Suspense fallback={null}>
|
|
<BrowserRouter basename={window.public_config?.SUB_PATH || '/'}>
|
|
<AppWithRouter props={props} isAppDarkMode={isAppDarkMode} />
|
|
</BrowserRouter>
|
|
</Suspense>
|
|
);
|
|
};
|
|
|
|
class AppComponent extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
currentUser: null,
|
|
fetchedMetadata: false,
|
|
darkMode: localStorage.getItem('darkMode') === 'true',
|
|
isEditorOrViewer: '',
|
|
};
|
|
}
|
|
updateSidebarNAV = (val) => {
|
|
this.setState({ sidebarNav: val });
|
|
};
|
|
fetchMetadata = () => {
|
|
tooljetService.fetchMetaData().then((data) => {
|
|
useAppDataStore.getState().actions.setMetadata(data);
|
|
localStorage.setItem('currentVersion', data.installed_version);
|
|
if (data.latest_version && lt(data.installed_version, data.latest_version) && data.version_ignored === false) {
|
|
this.setState({ updateAvailable: true });
|
|
}
|
|
});
|
|
};
|
|
|
|
componentDidMount() {
|
|
authorizeWorkspace();
|
|
this.fetchMetadata();
|
|
setInterval(this.fetchMetadata, 1000 * 60 * 60 * 1);
|
|
}
|
|
// check if its getting routed from editor
|
|
checkPreviousRoute = (route) => {
|
|
if (route.includes('/apps')) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
componentDidUpdate(prevProps) {
|
|
// Check if the current location is the dashboard (homepage)
|
|
if (
|
|
this.props.location.pathname === `/${getWorkspaceIdOrSlugFromURL()}` &&
|
|
prevProps.location.pathname !== `/${getWorkspaceIdOrSlugFromURL()}` &&
|
|
this.checkPreviousRoute(prevProps.location.pathname) &&
|
|
prevProps.location.pathname !== `/:workspaceId`
|
|
) {
|
|
// Reload the page for clearing already set intervals
|
|
window.location.reload();
|
|
}
|
|
}
|
|
|
|
switchDarkMode = (newMode) => {
|
|
this.setState({ darkMode: newMode });
|
|
useAppDataStore.getState().actions.updateIsTJDarkMode(newMode);
|
|
localStorage.setItem('darkMode', newMode);
|
|
};
|
|
|
|
render() {
|
|
const { updateAvailable, darkMode, isEditorOrViewer } = this.state;
|
|
let toastOptions = {
|
|
style: {
|
|
wordBreak: 'break-all',
|
|
},
|
|
};
|
|
|
|
if (isEditorOrViewer === 'viewer' ? this.props.isAppDarkMode : darkMode) {
|
|
toastOptions = {
|
|
className: 'toast-dark-mode',
|
|
style: {
|
|
borderRadius: '10px',
|
|
background: '#333',
|
|
color: '#fff',
|
|
wordBreak: 'break-all',
|
|
},
|
|
};
|
|
}
|
|
const { sidebarNav } = this.state;
|
|
const { updateSidebarNAV } = this;
|
|
return (
|
|
<>
|
|
<div
|
|
className={cx('main-wrapper', {
|
|
'theme-dark dark-theme': !isEditorOrViewer && darkMode,
|
|
})}
|
|
data-cy="main-wrapper"
|
|
>
|
|
{updateAvailable && (
|
|
<div className="alert alert-info alert-dismissible" role="alert">
|
|
<h3 className="mb-1">Update available</h3>
|
|
<p>A new version of ToolJet has been released.</p>
|
|
<div className="btn-list">
|
|
<a
|
|
href="https://docs.tooljet.io/docs/setup/updating"
|
|
target="_blank"
|
|
className="btn btn-info"
|
|
rel="noreferrer"
|
|
>
|
|
Read release notes & update
|
|
</a>
|
|
<a
|
|
onClick={() => {
|
|
tooljetService.skipVersion();
|
|
this.setState({ updateAvailable: false });
|
|
}}
|
|
className="btn"
|
|
>
|
|
Skip this version
|
|
</a>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<BreadCrumbContext.Provider value={{ sidebarNav, updateSidebarNAV }}>
|
|
<Routes>
|
|
<Route
|
|
path="/login/:organizationId"
|
|
exact
|
|
element={
|
|
<AuthRoute {...this.props}>
|
|
<LoginPage {...this.props} />
|
|
</AuthRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/login"
|
|
exact
|
|
element={
|
|
<AuthRoute {...this.props}>
|
|
<LoginPage {...this.props} />
|
|
</AuthRoute>
|
|
}
|
|
/>
|
|
<Route path="/setup" exact element={<SetupScreenSelfHost {...this.props} darkMode={darkMode} />} />
|
|
<Route path="/sso/:origin/:configId" exact element={<Oauth {...this.props} />} />
|
|
<Route path="/sso/:origin" exact element={<Oauth {...this.props} />} />
|
|
<Route
|
|
path="/signup/:organizationId"
|
|
exact
|
|
element={
|
|
<AuthRoute {...this.props}>
|
|
<SignupPage {...this.props} />
|
|
</AuthRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/signup"
|
|
exact
|
|
element={
|
|
<AuthRoute {...this.props}>
|
|
<SignupPage {...this.props} />
|
|
</AuthRoute>
|
|
}
|
|
/>
|
|
<Route path="/forgot-password" element={<ForgotPassword />} />
|
|
<Route path="/reset-password/:token" element={<ResetPassword />} />
|
|
<Route path="/reset-password" element={<ResetPassword />} />
|
|
<Route path="/invitations/:token" element={<VerificationSuccessInfoScreen />} />
|
|
<Route
|
|
path="/invitations/:token/workspaces/:organizationToken"
|
|
element={
|
|
<OrganizationInviteRoute {...this.props}>
|
|
<VerificationSuccessInfoScreen />
|
|
</OrganizationInviteRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/organization-invitations/:token"
|
|
element={
|
|
<OrganizationInviteRoute {...this.props} isOrgazanizationOnlyInvite={true}>
|
|
<OrganizationInvitationPage {...this.props} darkMode={darkMode} />
|
|
</OrganizationInviteRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
exact
|
|
path="/:workspaceId/apps/:slug/:pageHandle?/*"
|
|
element={
|
|
<AppsRoute componentType="editor">
|
|
<AppLoader
|
|
switchDarkMode={this.switchDarkMode}
|
|
darkMode={darkMode}
|
|
setEditorOrViewer={(value) => this.setState({ isEditorOrViewer: value })}
|
|
/>
|
|
</AppsRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
exact
|
|
path="/:workspaceId/workspace-constants"
|
|
element={
|
|
<PrivateRoute>
|
|
<WorkspaceConstants switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</PrivateRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
exact
|
|
path="/applications/:slug/:pageHandle?"
|
|
element={
|
|
<AppsRoute componentType="viewer">
|
|
<Viewer
|
|
switchDarkMode={this.switchDarkMode}
|
|
darkMode={this.props.isAppDarkMode}
|
|
setEditorOrViewer={(value) => this.setState({ isEditorOrViewer: value })}
|
|
/>
|
|
</AppsRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
exact
|
|
path="/applications/:slug/versions/:versionId/:pageHandle?"
|
|
element={
|
|
<AppsRoute componentType="viewer">
|
|
<Viewer
|
|
switchDarkMode={this.switchDarkMode}
|
|
darkMode={this.props.isAppDarkMode}
|
|
setEditorOrViewer={(value) => this.setState({ isEditorOrViewer: value })}
|
|
/>
|
|
</AppsRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
exact
|
|
path="/oauth2/authorize"
|
|
element={
|
|
<PrivateRoute>
|
|
<Authorize switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</PrivateRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
exact
|
|
path="/:workspaceId/workspace-settings"
|
|
element={
|
|
<AdminRoute {...this.props}>
|
|
<OrganizationSettings switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</AdminRoute>
|
|
}
|
|
>
|
|
<Route
|
|
path="users"
|
|
element={
|
|
<AdminRoute>
|
|
<ManageOrgUsers switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</AdminRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="workspace-login"
|
|
element={
|
|
<AdminRoute>
|
|
<OrganizationLogin switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</AdminRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="groups"
|
|
element={
|
|
<AdminRoute>
|
|
<ManageGroupPermissionsV2 switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</AdminRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="workspace-variables"
|
|
element={<ManageOrgVars switchDarkMode={this.switchDarkMode} darkMode={darkMode} />}
|
|
/>
|
|
</Route>
|
|
<Route
|
|
exact
|
|
path="/:workspaceId/settings"
|
|
element={
|
|
<PrivateRoute>
|
|
<SettingsPage switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</PrivateRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
exact
|
|
path="/:workspaceId/data-sources"
|
|
element={
|
|
<PrivateRoute>
|
|
<GlobalDatasources switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</PrivateRoute>
|
|
}
|
|
/>
|
|
{window.public_config?.ENABLE_TOOLJET_DB == 'true' && (
|
|
<Route
|
|
exact
|
|
path="/:workspaceId/database"
|
|
element={
|
|
<PrivateRoute>
|
|
<TooljetDatabase switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</PrivateRoute>
|
|
}
|
|
/>
|
|
)}
|
|
|
|
{window.public_config?.ENABLE_MARKETPLACE_FEATURE === 'true' && (
|
|
<Route
|
|
exact
|
|
path="/integrations"
|
|
element={
|
|
<AdminRoute {...this.props}>
|
|
<MarketplacePage switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</AdminRoute>
|
|
}
|
|
/>
|
|
)}
|
|
<Route exact path="/" element={<Navigate to="/:workspaceId" />} />
|
|
<Route
|
|
exact
|
|
path="/error/:errorType"
|
|
element={<ErrorPage switchDarkMode={this.switchDarkMode} darkMode={darkMode} />}
|
|
/>
|
|
<Route
|
|
exact
|
|
path="/switch-workspace"
|
|
element={
|
|
<SwitchWorkspaceRoute>
|
|
<SwitchWorkspacePage switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</SwitchWorkspaceRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
exact
|
|
path="/:workspaceId"
|
|
element={
|
|
<PrivateRoute>
|
|
<HomePage switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
|
</PrivateRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="*"
|
|
render={() => {
|
|
if (authenticationService?.currentSessionValue?.current_organization_id) {
|
|
return <Navigate to="/:workspaceId" />;
|
|
}
|
|
return <Navigate to="/login" />;
|
|
}}
|
|
/>
|
|
</Routes>
|
|
</BreadCrumbContext.Provider>
|
|
</div>
|
|
|
|
<Toast toastOptions={toastOptions} />
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
export const App = AppWrapper;
|
|
const AppWithRouter = withRouter(AppComponent);
|