mirror of
https://github.com/ToolJet/ToolJet
synced 2026-04-21 13:37:28 +00:00
Updated React to 18.2.0 (#5555)
* updated react to 18.2.0 * Updated Frontend Packages (#5569) * Updated tabler icon and fixed react hot keys issue * Fixed vulnerabilities * Updated dom purify and yjs * Reverted Eslint update * React-big-calendar update * Updated and fixed changes related to react-bootstrap * Updated eslint package * Fixes react-select-search ui * Updated packages in root * Updated & Fixed React-tooltip changes * Updated and fixed changes related to react-router-dom * Fixed copyToClipboard bug on usersTable * Fixed folder popover issue and comment issue * Fixed flickering issue on Editor * Fixed routing and dark mode bugs * Fixed app crash on page options click * Fixed SVG issues in data sources * Fixed calendar widget crash * Fixed popover issue in table * Fixed dark mode issue on react-select-search * Fixed popover issue in tooljetdb table * Fixed popover issue in pages * Fixed search bar crash * Fixes dark mode issue on react-select-search * Resolved conflicts
This commit is contained in:
parent
e6bae54130
commit
4c94de899d
88 changed files with 45477 additions and 19257 deletions
|
|
@ -55,7 +55,7 @@ module.exports = {
|
|||
'jest/no-identical-title': 'error',
|
||||
'jest/prefer-to-have-length': 'warn',
|
||||
'jest/valid-expect': 'error',
|
||||
'import/no-unresolved': ['error', { ignore: ['^@/', 'react-hot-toast'] }],
|
||||
'import/no-unresolved': ['error', { ignore: ['^@/', 'react-hot-toast', 'react-i18next'] }],
|
||||
'react/no-unknown-property': 'off',
|
||||
},
|
||||
settings: {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Avatar from '../../../src/_ui/Avatar';
|
|||
import Skeleton from 'react-loading-skeleton';
|
||||
import cx from 'classnames';
|
||||
import { Pagination } from '@/_components';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
|
||||
const UsersTable = ({
|
||||
isLoading,
|
||||
|
|
@ -99,16 +100,20 @@ const UsersTable = ({
|
|||
{user.status}
|
||||
</small>
|
||||
{user.status === 'invited' && 'invitation_token' in user ? (
|
||||
<CopyToClipboard text={generateInvitationURL(user)} onCopy={invitationLinkCopyHandler}>
|
||||
<img
|
||||
data-tip="Copy invitation link"
|
||||
className="svg-icon cursor-pointer"
|
||||
src="assets/images/icons/copy.svg"
|
||||
width="15"
|
||||
height="15"
|
||||
data-cy="copy-invitation-link"
|
||||
></img>
|
||||
</CopyToClipboard>
|
||||
<>
|
||||
<CopyToClipboard text={generateInvitationURL(user)} onCopy={invitationLinkCopyHandler}>
|
||||
<img
|
||||
data-tooltip-id="tooltip-for-copy-invitation-link"
|
||||
data-tooltip-content="Copy invitation link"
|
||||
className="svg-icon cursor-pointer"
|
||||
src="assets/images/icons/copy.svg"
|
||||
width="15"
|
||||
height="15"
|
||||
data-cy="copy-invitation-link"
|
||||
></img>
|
||||
</CopyToClipboard>
|
||||
<Tooltip id="tooltip-for-copy-invitation-link" className="tooltip" />
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
|
|
|||
60210
frontend/package-lock.json
generated
60210
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,133 +3,134 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.0.5",
|
||||
"@dnd-kit/sortable": "^7.0.1",
|
||||
"@dnd-kit/utilities": "^3.2.0",
|
||||
"@radix-ui/react-popover": "^1.0.2",
|
||||
"@react-google-maps/api": "^2.1.1",
|
||||
"@sentry/react": "^7.12.0",
|
||||
"@sentry/tracing": "^7.12.0",
|
||||
"@tabler/icons": "^1.91.1",
|
||||
"@dnd-kit/core": "^6.0.7",
|
||||
"@dnd-kit/sortable": "^7.0.2",
|
||||
"@dnd-kit/utilities": "^3.2.1",
|
||||
"@emoji-mart/data": "^1.1.2",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@radix-ui/react-popover": "^1.0.3",
|
||||
"@react-google-maps/api": "^2.18.1",
|
||||
"@sentry/react": "^7.37.2",
|
||||
"@sentry/tracing": "^7.37.2",
|
||||
"@tabler/icons-react": "^2.4.0",
|
||||
"@tooljet/plugins": "../plugins",
|
||||
"@uiw/react-codemirror": "^3.0.6",
|
||||
"@y-presence/react": "^2.0.0",
|
||||
"array-move": "^3.0.1",
|
||||
"axios": "^0.24.0",
|
||||
"bootstrap": "^4.6.0",
|
||||
"classnames": "^2.3.1",
|
||||
"date-fns": "^2.28.0",
|
||||
"deep-object-diff": "^1.1.7",
|
||||
"dompurify": "^2.2.7",
|
||||
"@y-presence/react": "^2.0.1",
|
||||
"array-move": "^4.0.0",
|
||||
"axios": "^1.3.3",
|
||||
"bootstrap": "^5.2.3",
|
||||
"classnames": "^2.3.2",
|
||||
"date-fns": "^2.29.3",
|
||||
"deep-object-diff": "^1.1.9",
|
||||
"dompurify": "^3.0.0",
|
||||
"draft-js": "^0.11.7",
|
||||
"draft-js-export-html": "^1.4.1",
|
||||
"driver.js": "^0.9.8",
|
||||
"emoji-mart": "^3.0.1",
|
||||
"focus-trap-react": "^10.0.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
"history": "^4.9.0",
|
||||
"html-loader": "^3.1.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"emoji-mart": "^5.5.2",
|
||||
"focus-trap-react": "^10.0.2",
|
||||
"fuse.js": "^6.6.2",
|
||||
"html-loader": "^4.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"humps": "^2.0.1",
|
||||
"i18next": "^21.8.14",
|
||||
"i18next-browser-languagedetector": "^6.1.4",
|
||||
"i18next-http-backend": "^1.4.1",
|
||||
"immer": "^9.0.6",
|
||||
"i18next": "^22.4.9",
|
||||
"i18next-browser-languagedetector": "^7.0.1",
|
||||
"i18next-http-backend": "^2.1.1",
|
||||
"immer": "^9.0.19",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"jspdf-autotable": "^3.5.25",
|
||||
"jspdf-autotable": "^3.5.28",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.1",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"papaparse": "^5.3.0",
|
||||
"plotly.js-basic-dist-min": "^1.58.4",
|
||||
"psl": "^1.8.0",
|
||||
"query-string": "^6.13.6",
|
||||
"rc-slider": "^9.7.5",
|
||||
"react": "^16.14.0",
|
||||
"react-beautiful-dnd": "^13.1.0",
|
||||
"react-big-calendar": "^0.38.0",
|
||||
"react-bootstrap": "^1.5.2",
|
||||
"react-burger-menu": "^3.0.8",
|
||||
"react-checkbox-tree": "^1.7.3",
|
||||
"react-circular-progressbar": "^2.0.4",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.40",
|
||||
"papaparse": "^5.3.2",
|
||||
"plotly.js-basic-dist-min": "^2.18.1",
|
||||
"psl": "^1.9.0",
|
||||
"query-string": "^8.1.0",
|
||||
"rc-slider": "^10.1.1",
|
||||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-big-calendar": "^1.6.5",
|
||||
"react-bootstrap": "^2.7.2",
|
||||
"react-burger-menu": "^3.0.9",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
"react-circular-progressbar": "^2.1.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-copy-to-clipboard": "^5.0.3",
|
||||
"react-datepicker": "^4.7.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-datepicker": "^4.10.0",
|
||||
"react-dates": "^21.8.0",
|
||||
"react-datetime": "^3.0.4",
|
||||
"react-dnd": "^14.0.2",
|
||||
"react-dnd-html5-backend": "^14.0.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-dropzone": "^11.4.2",
|
||||
"react-easy-sort": "^1.5.0",
|
||||
"react-hot-toast": "^2.1.1",
|
||||
"react-hotkeys-hook": "^3.4.4",
|
||||
"react-i18next": "^11.18.3",
|
||||
"react-datetime": "^3.2.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-easy-sort": "^1.5.1",
|
||||
"react-google-login": "^5.2.2",
|
||||
"react-hot-toast": "^2.4.0",
|
||||
"react-hotkeys-hook": "^4.3.5",
|
||||
"react-i18next": "^12.1.5",
|
||||
"react-image-annotation": "^0.9.10",
|
||||
"react-json-tree": "^0.16.1",
|
||||
"react-json-tree": "^0.18.0",
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-lazy-load-image-component": "^1.5.1",
|
||||
"react-lazy-load-image-component": "^1.5.6",
|
||||
"react-lazyload": "^3.2.0",
|
||||
"react-loading-skeleton": "^2.2.0",
|
||||
"react-mentions": "^4.3.0",
|
||||
"react-multi-select-component": "^4.2.3",
|
||||
"react-pdf": "^5.7.2",
|
||||
"react-plotly.js": "^2.5.1",
|
||||
"react-loading-skeleton": "^3.1.1",
|
||||
"react-mentions": "^4.4.7",
|
||||
"react-multi-select-component": "^4.3.4",
|
||||
"react-pdf": "^6.2.2",
|
||||
"react-plotly.js": "^2.6.0",
|
||||
"react-qr-reader": "^2.2.1",
|
||||
"react-rnd": "^10.3.0",
|
||||
"react-router-breadcrumbs-hoc": "^4.1.0",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-select": "^4.3.1",
|
||||
"react-select-search": "^3.0.5",
|
||||
"react-selecto": "^1.17.0",
|
||||
"react-spring": "^9.2.4",
|
||||
"react-table": "^7.6.3",
|
||||
"react-table-plugins": "^1.3.1",
|
||||
"react-tooltip": "^4.2.18",
|
||||
"react-virtuoso": "^2.17.2",
|
||||
"react-zoom-pan-pinch": "^2.1.3",
|
||||
"rxjs": "^6.3.3",
|
||||
"semver": "^5.7.1",
|
||||
"superstruct": "^0.15.4",
|
||||
"tinycolor2": "^1.4.2",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-router-dom": "^6.8.1",
|
||||
"react-select": "^5.7.0",
|
||||
"react-select-search": "^4.1.6",
|
||||
"react-selecto": "^1.22.0",
|
||||
"react-spring": "^9.6.1",
|
||||
"react-table": "^7.8.0",
|
||||
"react-table-plugins": "^1.3.2",
|
||||
"react-tooltip": "^5.8.1",
|
||||
"react-virtuoso": "^4.1.0",
|
||||
"react-zoom-pan-pinch": "^2.6.1",
|
||||
"rxjs": "^7.8.0",
|
||||
"semver": "^7.3.8",
|
||||
"superstruct": "^1.0.3",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"url-join": "^5.0.0",
|
||||
"uuid": "8.3.2",
|
||||
"use-react-router-breadcrumbs": "^4.0.1",
|
||||
"uuid": "9.0.0",
|
||||
"xlsx": "^0.18.5",
|
||||
"y-websocket": "^1.4.0",
|
||||
"yjs": "^13.5.28",
|
||||
"yup": "^0.27.0"
|
||||
"y-websocket": "^1.4.5",
|
||||
"yjs": "^13.5.46"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.3",
|
||||
"@babel/eslint-parser": "^7.18.9",
|
||||
"@babel/plugin-transform-runtime": "^7.16.10",
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/core": "^7.20.12",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@babel/plugin-transform-runtime": "^7.19.6",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
"babel-loader": "^8.0.5",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"babel-loader": "^9.1.2",
|
||||
"babel-plugin-console-source": "^2.0.5",
|
||||
"babel-plugin-import": "^1.13.3",
|
||||
"babel-plugin-import": "^1.13.6",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"esbuild": "^0.15.3",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-import-resolver-webpack": "^0.13.1",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-jest": "^26.1.1",
|
||||
"eslint-plugin-prettier": "^3.4.1",
|
||||
"eslint-plugin-react": "^7.25.2",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"html-loader": "^3.1.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"jest": "^27.5.1",
|
||||
"css-loader": "^6.7.3",
|
||||
"esbuild": "^0.17.8",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-import-resolver-webpack": "^0.13.2",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-jest": "^27.2.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"html-loader": "^4.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"jest": "^29.4.2",
|
||||
"node-sass": "^8.0.0",
|
||||
"path": "^0.12.7",
|
||||
"prettier": "^2.3.2",
|
||||
"prettier": "^2.8.4",
|
||||
"sass-loader": "^13.2.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"terser-webpack-plugin": "^5.3.6",
|
||||
|
|
@ -138,9 +139,33 @@
|
|||
"webpack-dev-server": "^4.11.1"
|
||||
},
|
||||
"overrides": {
|
||||
"@y-presence/react": {
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0"
|
||||
"react-dates": {
|
||||
"react": "$react",
|
||||
"react-dom": "$react-dom"
|
||||
},
|
||||
"react-google-login": {
|
||||
"react": "$react",
|
||||
"react-dom": "$react-dom"
|
||||
},
|
||||
"react-json-view": {
|
||||
"react": "$react",
|
||||
"react-dom": "$react-dom"
|
||||
},
|
||||
"react-lazyload": {
|
||||
"react": "$react",
|
||||
"react-dom": "$react-dom"
|
||||
},
|
||||
"react-qr-reader": {
|
||||
"react": "$react",
|
||||
"react-dom": "$react-dom"
|
||||
},
|
||||
"react-table-plugins": {
|
||||
"react": "$react",
|
||||
"react-dom": "$react-dom"
|
||||
},
|
||||
"react-image-annotation": {
|
||||
"react": "$react",
|
||||
"react-dom": "$react-dom"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { Suspense } from 'react';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import config from 'config';
|
||||
import { BrowserRouter, Route, Redirect } from 'react-router-dom';
|
||||
import { history } from '@/_helpers';
|
||||
import { Route, Routes, BrowserRouter } from 'react-router-dom';
|
||||
import { withRouter } from '@/_hoc/withRouter';
|
||||
import { authenticationService, tooljetService } from '@/_services';
|
||||
import { PrivateRoute, AdminRoute } from '@/_components';
|
||||
import { HomePage } from '@/HomePage';
|
||||
|
|
@ -22,11 +22,21 @@ import { lt } from 'semver';
|
|||
import Toast from '@/_ui/Toast';
|
||||
import { VerificationSuccessInfoScreen } from '@/SuccessInfoScreen';
|
||||
import '@/_styles/theme.scss';
|
||||
import 'emoji-mart/css/emoji-mart.css';
|
||||
import { AppLoader } from '@/AppLoader';
|
||||
import SetupScreenSelfHost from '../SuccessInfoScreen/SetupScreenSelfHost';
|
||||
import 'react-tooltip/dist/react-tooltip.css';
|
||||
|
||||
class App extends React.Component {
|
||||
const AppWrapper = (props) => {
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<BrowserRouter basename={window.public_config?.SUB_PATH || '/'}>
|
||||
<AppWithRouter props={props} />
|
||||
</BrowserRouter>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
class AppComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -55,11 +65,6 @@ class App extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
logout = () => {
|
||||
authenticationService.logout();
|
||||
history.push('/login');
|
||||
};
|
||||
|
||||
switchDarkMode = (newMode) => {
|
||||
this.setState({ darkMode: newMode });
|
||||
localStorage.setItem('darkMode', newMode);
|
||||
|
|
@ -86,177 +91,149 @@ class App extends React.Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<BrowserRouter history={history} basename={window.public_config?.SUB_PATH || '/'}>
|
||||
<div className={`main-wrapper ${darkMode ? 'theme-dark' : ''}`} 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 className={`main-wrapper ${darkMode ? 'theme-dark' : ''}`} 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>
|
||||
)}
|
||||
|
||||
<PrivateRoute
|
||||
</div>
|
||||
)}
|
||||
<Routes>
|
||||
<Route
|
||||
exact
|
||||
path="/"
|
||||
component={HomePage}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<Route path="/login/:organizationId" exact component={LoginPage} />
|
||||
<Route path="/login" exact component={LoginPage} />
|
||||
<Route path="/setup" exact component={(props) => <SetupScreenSelfHost {...props} darkMode={darkMode} />} />
|
||||
<Route path="/sso/:origin/:configId" exact component={Oauth} />
|
||||
<Route path="/sso/:origin" exact component={Oauth} />
|
||||
<Route path="/signup" component={SignupPage} />
|
||||
<Route path="/forgot-password" component={ForgotPassword} />
|
||||
<Route
|
||||
path="/reset-password/:token"
|
||||
render={(props) => (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/reset-password',
|
||||
state: {
|
||||
token: props.match.params.token,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route path="/reset-password" component={ResetPassword} />
|
||||
<Route
|
||||
path="/invitations/:token"
|
||||
render={(props) => (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/confirm',
|
||||
state: {
|
||||
token: props.match.params.token,
|
||||
search: props.location.search,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
path="*"
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<HomePage switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route path="/login/:organizationId" exact element={<LoginPage />} />
|
||||
<Route path="/login" exact element={<LoginPage />} />
|
||||
<Route path="/setup" exact element={<SetupScreenSelfHost {...this.props} darkMode={darkMode} />} />
|
||||
<Route path="/sso/:origin/:configId" exact element={<Oauth />} />
|
||||
<Route path="/sso/:origin" exact element={<Oauth />} />
|
||||
<Route path="/signup" element={<SignupPage />} />
|
||||
<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"
|
||||
render={(props) => (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/confirm',
|
||||
state: {
|
||||
token: props.match.params.token,
|
||||
organizationToken: props.match.params.organizationToken,
|
||||
search: props.location.search,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
element={<VerificationSuccessInfoScreen />}
|
||||
/>
|
||||
<Route path="/confirm" component={VerificationSuccessInfoScreen} />
|
||||
<Route path="/confirm" element={<VerificationSuccessInfoScreen />} />
|
||||
<Route
|
||||
path="/organization-invitations/:token"
|
||||
render={(props) => (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/confirm-invite',
|
||||
state: {
|
||||
token: props.match.params.token,
|
||||
search: props.location.search,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
element={<OrganizationInvitationPage {...this.props} darkMode={darkMode} />}
|
||||
/>
|
||||
<Route
|
||||
path="/confirm-invite"
|
||||
component={(props) => <OrganizationInvitationPage {...props} darkMode={darkMode} />}
|
||||
element={<OrganizationInvitationPage {...this.props} darkMode={darkMode} />}
|
||||
/>
|
||||
<PrivateRoute
|
||||
<Route
|
||||
exact
|
||||
path="/apps/:id/:pageHandle?"
|
||||
component={AppLoader}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
path="/apps/:id/:pageHandle?/*"
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<AppLoader switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<PrivateRoute
|
||||
<Route
|
||||
exact
|
||||
path="/applications/:id/versions/:versionId/:pageHandle?"
|
||||
component={Viewer}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Viewer switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<PrivateRoute
|
||||
<Route
|
||||
exact
|
||||
path="/applications/:slug/:pageHandle?"
|
||||
component={Viewer}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Viewer switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<PrivateRoute
|
||||
<Route
|
||||
exact
|
||||
path="/oauth2/authorize"
|
||||
component={Authorize}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Authorize switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<PrivateRoute
|
||||
<Route
|
||||
exact
|
||||
path="/workspace-settings"
|
||||
component={OrganizationSettings}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<OrganizationSettings switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<PrivateRoute
|
||||
<Route
|
||||
exact
|
||||
path="/settings"
|
||||
component={SettingsPage}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<SettingsPage switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
{window.public_config?.ENABLE_TOOLJET_DB == 'true' && (
|
||||
<PrivateRoute
|
||||
<Route
|
||||
exact
|
||||
path="/database"
|
||||
component={TooljetDatabase}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<TooljetDatabase switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{window.public_config?.ENABLE_MARKETPLACE_FEATURE === 'true' && (
|
||||
<AdminRoute
|
||||
<Route
|
||||
exact
|
||||
path="/integrations"
|
||||
component={MarketplacePage}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
element={
|
||||
<AdminRoute>
|
||||
<MarketplacePage switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
</AdminRoute>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</Routes>
|
||||
</div>
|
||||
<Toast toastOptions={toastOptions} />
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { App };
|
||||
export const App = AppWrapper;
|
||||
const AppWithRouter = withRouter(AppComponent);
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ import config from 'config';
|
|||
import { safelyParseJSON, stripTrailingSlash } from '@/_helpers/utils';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
const AppLoaderComponent = (props) => {
|
||||
const router = useRouter();
|
||||
const appId = props.match.params.id;
|
||||
const params = useParams();
|
||||
const appId = params.id;
|
||||
const currentUser = authenticationService.currentUserValue;
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import EyeHide from '../../assets/images/onboardingassets/Icons/EyeHide';
|
|||
import EyeShow from '../../assets/images/onboardingassets/Icons/EyeShow';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
import { LinkExpiredInfoScreen } from '../SuccessInfoScreen/LinkExpiredInfoScreen';
|
||||
import { withRouter } from '@/_hoc/withRouter';
|
||||
class OrganizationInvitationPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -27,8 +28,8 @@ class OrganizationInvitationPageComponent extends React.Component {
|
|||
};
|
||||
this.formRef = React.createRef(null);
|
||||
this.single_organization = window.public_config?.DISABLE_MULTI_WORKSPACE === 'true';
|
||||
this.organizationId = new URLSearchParams(props?.location?.state?.search).get('oid');
|
||||
this.source = new URLSearchParams(props?.location?.state?.search).get('source');
|
||||
this.organizationId = new URLSearchParams(props?.location?.search).get('oid');
|
||||
this.source = new URLSearchParams(props?.location?.search).get('source');
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
|
@ -61,7 +62,7 @@ class OrganizationInvitationPageComponent extends React.Component {
|
|||
}
|
||||
|
||||
authenticationService
|
||||
.verifyOrganizationToken(this.props?.location?.state?.token)
|
||||
.verifyOrganizationToken(this.props?.params?.token)
|
||||
.then((data) => {
|
||||
this.setState({ userDetails: data });
|
||||
if (data?.email !== '') {
|
||||
|
|
@ -85,7 +86,7 @@ class OrganizationInvitationPageComponent extends React.Component {
|
|||
e.preventDefault();
|
||||
|
||||
const isSetPassword = !!this.state?.userDetails?.onboarding_details?.password;
|
||||
const token = this.props?.location?.state?.token;
|
||||
const token = this.props?.params?.token;
|
||||
const { password } = this.state;
|
||||
this.setState({ isLoading: true });
|
||||
|
||||
|
|
@ -116,9 +117,9 @@ class OrganizationInvitationPageComponent extends React.Component {
|
|||
json.then((res) => {
|
||||
authenticationService.updateUser(res?.user);
|
||||
authenticationService.deleteLoginOrganizationId();
|
||||
this.props.history.push('/login');
|
||||
this.props.navigate('/login');
|
||||
});
|
||||
} else this.props.history.push('/login');
|
||||
} else this.props.navigate('/login');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
|
|
@ -402,4 +403,4 @@ class OrganizationInvitationPageComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export const OrganizationInvitationPage = withTranslation()(OrganizationInvitationPageComponent);
|
||||
export const OrganizationInvitationPage = withTranslation()(withRouter(OrganizationInvitationPageComponent));
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export const BoxShadow = ({ value, onChange, forceCodeBox, cyLabel }) => {
|
|||
style={{ width: '350px', maxWidth: '350px' }}
|
||||
className={`${darkMode && 'popover-dark-themed theme-dark'} shadow`}
|
||||
>
|
||||
<Popover.Content>
|
||||
<Popover.Body>
|
||||
<>
|
||||
{input.map((item) => (
|
||||
<div className="row" key={item}>
|
||||
|
|
@ -142,7 +142,7 @@ export const BoxShadow = ({ value, onChange, forceCodeBox, cyLabel }) => {
|
|||
Clear
|
||||
</button>
|
||||
</>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { Picker } from 'emoji-mart';
|
||||
import data from '@emoji-mart/data/sets/14/apple.json';
|
||||
import Picker from '@emoji-mart/react';
|
||||
|
||||
import TextareaMentions from '@/_ui/Mentions';
|
||||
import Button from '@/_ui/Button';
|
||||
import usePopover from '@/_hooks/use-popover';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function CommentFooter({
|
||||
|
|
@ -38,12 +40,18 @@ function CommentFooter({
|
|||
setOpen(false);
|
||||
};
|
||||
|
||||
useHotkeys('⌘+enter, control+enter', () => handleClick());
|
||||
useHotkeys('meta+enter, control+enter', () => handleClick());
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
return (
|
||||
<>
|
||||
<div {...content} className={open ? 'show' : 'hide'}>
|
||||
<Picker theme={darkMode ? 'dark' : 'light'} style={{ width: 320 }} set="apple" onSelect={addEmoji} />
|
||||
<Picker
|
||||
data={data}
|
||||
theme={darkMode ? 'dark' : 'light'}
|
||||
style={{ width: 320 }}
|
||||
set="apple"
|
||||
onEmojiSelect={addEmoji}
|
||||
/>
|
||||
</div>
|
||||
<div className="card-footer">
|
||||
<div className="row align-items-center">
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ const Comment = ({ socket, x, y, threadId, user = {}, isResolved, fetchThreads,
|
|||
}, []);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
const { left } = trigger?.ref?.current?.getBoundingClientRect();
|
||||
|
||||
if (left < 460) setPlacement('right');
|
||||
|
|
@ -54,7 +55,7 @@ const Comment = ({ socket, x, y, threadId, user = {}, isResolved, fetchThreads,
|
|||
} else {
|
||||
// resetting the query param
|
||||
// react router updates the url with the set basename resulting invalid url unless replaced
|
||||
router.push(window.location.pathname.replace(window.public_config?.SUB_PATH, '/'));
|
||||
router.history(window.location.pathname.replace(window.public_config?.SUB_PATH, '/'));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [open]);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export const ButtonGroup = function Button({
|
|||
|
||||
const [defaultActive, setDefaultActive] = useState(defaultSelected);
|
||||
const [data, setData] = useState(
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
values?.length <= labels?.length ? [...labels, ...values?.slice(labels?.length)] : labels
|
||||
);
|
||||
// data is used as state to show what to display , club of label+values / values
|
||||
|
|
@ -39,6 +40,7 @@ export const ButtonGroup = function Button({
|
|||
|
||||
useEffect(() => {
|
||||
if (labels?.length < values?.length) {
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
setData([...labels, ...values?.slice(labels?.length)]);
|
||||
} else {
|
||||
setData(labels);
|
||||
|
|
|
|||
|
|
@ -119,11 +119,6 @@ export const Calendar = function ({
|
|||
},
|
||||
};
|
||||
|
||||
//! hack
|
||||
if (exposedVariables.currentDate === undefined) {
|
||||
setExposedVariable('currentDate', moment(defaultDate).format(properties.dateFormat));
|
||||
}
|
||||
|
||||
return (
|
||||
<div id={id} style={{ display: styles.visibility ? 'block' : 'none' }} data-cy={dataCy}>
|
||||
<ReactCalendar
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import * as Icons from '@tabler/icons';
|
||||
import * as Icons from '@tabler/icons-react';
|
||||
import cx from 'classnames';
|
||||
|
||||
export const Icon = ({ properties, styles, fireEvent, width, height, registerAction, darkMode, dataCy }) => {
|
||||
const { icon } = properties;
|
||||
const { iconColor, visibility } = styles;
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons[icon];
|
||||
|
||||
const color = iconColor === '#000' ? (darkMode ? '#fff' : '#000') : iconColor;
|
||||
|
|
@ -42,6 +43,7 @@ export const Icon = ({ properties, styles, fireEvent, width, height, registerAct
|
|||
event.stopPropagation();
|
||||
fireEvent('onHover');
|
||||
}}
|
||||
stroke={1.5}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import Slider, { Range } from 'rc-slider';
|
||||
import Slider from 'rc-slider';
|
||||
import 'rc-slider/assets/index.css';
|
||||
|
||||
export const RangeSlider = function RangeSlider({ height, properties, styles, setExposedVariable, fireEvent, dataCy }) {
|
||||
|
|
@ -64,7 +64,8 @@ export const RangeSlider = function RangeSlider({ height, properties, styles, se
|
|||
return (
|
||||
<div style={computedStyles} className="range-slider" data-cy={dataCy}>
|
||||
{enableTwoHandle ? (
|
||||
<Range
|
||||
<Slider
|
||||
range
|
||||
min={min}
|
||||
max={max}
|
||||
defaultValue={toArray(rangeValue)}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export const CustomSelect = ({ options, value, multiple, onChange, darkMode, isE
|
|||
onChange={onChange}
|
||||
multiple={multiple}
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
className={'select-search'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import generateColumnsData from './columns';
|
|||
import generateActionsData from './columns/actions';
|
||||
import autogenerateColumns from './columns/autogenerateColumns';
|
||||
import IndeterminateCheckbox from './IndeterminateCheckbox';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { useTranslation } from 'react-i18next';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import JsPDF from 'jspdf';
|
||||
|
|
@ -35,7 +36,7 @@ import JsPDF from 'jspdf';
|
|||
import 'jspdf-autotable';
|
||||
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { IconEyeOff } from '@tabler/icons';
|
||||
import { IconEyeOff } from '@tabler/icons-react';
|
||||
import * as XLSX from 'xlsx/xlsx.mjs';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Popover from 'react-bootstrap/Popover';
|
||||
|
|
@ -43,6 +44,7 @@ import { useMounted } from '@/_hooks/use-mount';
|
|||
import GenerateEachCellValue from './GenerateEachCellValue';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
|
||||
export function Table({
|
||||
id,
|
||||
|
|
@ -348,6 +350,9 @@ export function Table({
|
|||
darkMode,
|
||||
] // Hack: need to fix
|
||||
);
|
||||
|
||||
console.log('columns--- ', columns);
|
||||
|
||||
const data = useMemo(
|
||||
() => tableData,
|
||||
[
|
||||
|
|
@ -598,7 +603,7 @@ export function Table({
|
|||
className={`${darkMode && 'popover-dark-themed theme-dark'} shadow table-widget-download-popup`}
|
||||
placement="bottom"
|
||||
>
|
||||
<Popover.Content>
|
||||
<Popover.Body>
|
||||
<div className="d-flex flex-column">
|
||||
<span data-cy={`option-download-CSV`} className="cursor-pointer" onClick={() => exportData('csv', true)}>
|
||||
Download as CSV
|
||||
|
|
@ -618,7 +623,7 @@ export function Table({
|
|||
Download as PDF
|
||||
</span>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
|
@ -635,7 +640,6 @@ export function Table({
|
|||
borderRadius: Number.parseFloat(borderRadius),
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
ref={tableRef}
|
||||
|
|
@ -661,19 +665,34 @@ export function Table({
|
|||
)}
|
||||
<div>
|
||||
{showFilterButton && (
|
||||
<span data-tip="Filter data" className="btn btn-light btn-sm p-1 mx-1" onClick={() => showFilters()}>
|
||||
<img src="assets/images/icons/filter.svg" width="15" height="15" />
|
||||
{tableDetails.filterDetails.filters.length > 0 && (
|
||||
<a className="badge bg-azure" style={{ width: '4px', height: '4px', marginTop: '5px' }}></a>
|
||||
)}
|
||||
</span>
|
||||
<>
|
||||
<span
|
||||
className="btn btn-light btn-sm p-1 mx-1"
|
||||
onClick={() => showFilters()}
|
||||
data-tooltip-id="tooltip-for-filter-data"
|
||||
data-tooltip-content="Filter data"
|
||||
>
|
||||
<img src="assets/images/icons/filter.svg" width="15" height="15" />
|
||||
{tableDetails.filterDetails.filters.length > 0 && (
|
||||
<a className="badge bg-azure" style={{ width: '4px', height: '4px', marginTop: '5px' }}></a>
|
||||
)}
|
||||
</span>
|
||||
<Tooltip id="tooltip-for-filter-data" className="tooltip" />
|
||||
</>
|
||||
)}
|
||||
{showDownloadButton && (
|
||||
<OverlayTrigger trigger="click" overlay={downlaodPopover()} rootClose={true} placement={'bottom-end'}>
|
||||
<span data-tip="Download" className="btn btn-light btn-sm p-1">
|
||||
<img src="assets/images/icons/download.svg" width="15" height="15" />
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
<>
|
||||
<OverlayTrigger trigger="click" overlay={downlaodPopover()} rootClose={true} placement={'bottom-end'}>
|
||||
<span
|
||||
className="btn btn-light btn-sm p-1"
|
||||
data-tooltip-id="tooltip-for-download"
|
||||
data-tooltip-content="Download"
|
||||
>
|
||||
<img src="assets/images/icons/download.svg" width="15" height="15" />
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
<Tooltip id="tooltip-for-download" className="tooltip" />
|
||||
</>
|
||||
)}
|
||||
{!hideColumnSelectorButton && (
|
||||
<OverlayTrigger
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import SelectSearch from 'react-select-search';
|
||||
import { resolveReferences, validateWidget } from '@/_helpers/utils';
|
||||
import { CustomSelect } from '../CustomSelect';
|
||||
import { Tags } from '../Tags';
|
||||
|
|
@ -280,10 +280,10 @@ export default function generateColumnsData({
|
|||
onChange={(value) => {
|
||||
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
disabled={!column.isEditable}
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
className={'select-search'}
|
||||
/>
|
||||
<div className={`invalid-feedback ${isValid ? '' : 'd-flex'}`}>{validationError}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -68,11 +68,11 @@ export const Container = ({
|
|||
const canvasRef = useRef(null);
|
||||
const focusedParentIdRef = useRef(undefined);
|
||||
|
||||
useHotkeys('⌘+z, control+z', () => handleUndo());
|
||||
useHotkeys('⌘+shift+z, control+shift+z', () => handleRedo());
|
||||
useHotkeys('meta+z, control+z', () => handleUndo());
|
||||
useHotkeys('meta+shift+z, control+shift+z', () => handleRedo());
|
||||
|
||||
useHotkeys(
|
||||
'⌘+v, control+v',
|
||||
'meta+v, control+v',
|
||||
() => {
|
||||
if (isContainerFocused) {
|
||||
navigator.clipboard.readText().then((cliptext) => {
|
||||
|
|
|
|||
|
|
@ -176,15 +176,9 @@ export const DraggableBox = function DraggableBox({
|
|||
};
|
||||
|
||||
const layoutData = inCanvas ? layouts[currentLayout] || defaultData : defaultData;
|
||||
const [currentLayoutOptions, setCurrentLayoutOptions] = useState(layoutData);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(layoutData);
|
||||
setCurrentLayoutOptions(layoutData);
|
||||
}, [layoutData.height, layoutData.width, layoutData.left, layoutData.top, currentLayout]);
|
||||
|
||||
const gridWidth = canvasWidth / 43;
|
||||
const width = (canvasWidth * currentLayoutOptions.width) / 43;
|
||||
const width = (canvasWidth * layoutData.width) / 43;
|
||||
|
||||
const configWidgetHandlerForModalComponent =
|
||||
!isSelectedComponent &&
|
||||
|
|
@ -217,11 +211,11 @@ export const DraggableBox = function DraggableBox({
|
|||
dragGrid={[gridWidth, 10]}
|
||||
size={{
|
||||
width: width,
|
||||
height: currentLayoutOptions.height,
|
||||
height: layoutData.height,
|
||||
}}
|
||||
position={{
|
||||
x: currentLayoutOptions ? (currentLayoutOptions.left * canvasWidth) / 100 : 0,
|
||||
y: currentLayoutOptions ? currentLayoutOptions.top : 0,
|
||||
x: layoutData ? (layoutData.left * canvasWidth) / 100 : 0,
|
||||
y: layoutData ? layoutData.top : 0,
|
||||
}}
|
||||
defaultSize={{}}
|
||||
className={`resizer ${
|
||||
|
|
@ -241,7 +235,7 @@ export const DraggableBox = function DraggableBox({
|
|||
disableDragging={mode !== 'edit' || readOnly}
|
||||
onDragStop={(e, direction) => {
|
||||
setDragging(false);
|
||||
onDragStop(e, id, direction, currentLayout, currentLayoutOptions);
|
||||
onDragStop(e, id, direction, currentLayout, layoutData);
|
||||
}}
|
||||
cancel={`div.table-responsive.jet-data-table, div.calendar-widget, div.text-input, .textarea, .map-widget, .range-slider, .kanban-container`}
|
||||
onDragStart={(e) => e.stopPropagation()}
|
||||
|
|
@ -261,9 +255,9 @@ export const DraggableBox = function DraggableBox({
|
|||
id={id}
|
||||
removeComponent={removeComponent}
|
||||
component={component}
|
||||
position={currentLayoutOptions.top < 15 ? 'bottom' : 'top'}
|
||||
widgetTop={currentLayoutOptions.top}
|
||||
widgetHeight={currentLayoutOptions.height}
|
||||
position={layoutData.top < 15 ? 'bottom' : 'top'}
|
||||
widgetTop={layoutData.top}
|
||||
widgetHeight={layoutData.height}
|
||||
isMultipleComponentsSelected={isMultipleComponentsSelected}
|
||||
configWidgetHandlerForModalComponent={configWidgetHandlerForModalComponent}
|
||||
/>
|
||||
|
|
@ -273,7 +267,7 @@ export const DraggableBox = function DraggableBox({
|
|||
component={component}
|
||||
id={id}
|
||||
width={width}
|
||||
height={currentLayoutOptions.height - 4}
|
||||
height={layoutData.height - 4}
|
||||
mode={mode}
|
||||
changeCanDrag={changeCanDrag}
|
||||
inCanvas={inCanvas}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import {
|
|||
removeSelectedComponent,
|
||||
} from '@/_helpers/appUtils';
|
||||
import { Confirm } from './Viewer/Confirm';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { Tooltip as ReactTooltip } from 'react-tooltip';
|
||||
import CommentNotifications from './CommentNotifications';
|
||||
import { WidgetManager } from './WidgetManager';
|
||||
import Fuse from 'fuse.js';
|
||||
|
|
@ -53,6 +53,8 @@ import { v4 as uuid } from 'uuid';
|
|||
import Skeleton from 'react-loading-skeleton';
|
||||
import EmptyQueriesIllustration from '@assets/images/icons/no-queries-added.svg';
|
||||
import EditorHeader from './Header';
|
||||
import '@/_styles/editor/react-select-search.scss';
|
||||
import { withRouter } from '@/_hoc/withRouter';
|
||||
|
||||
setAutoFreeze(false);
|
||||
enablePatches();
|
||||
|
|
@ -61,9 +63,9 @@ class EditorComponent extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const appId = this.props.match.params.id;
|
||||
const appId = this.props.params.id;
|
||||
|
||||
const pageHandle = this.props.match.params.pageHandle;
|
||||
const pageHandle = this.props.params.pageHandle;
|
||||
|
||||
const currentUser = authenticationService.currentUserValue;
|
||||
|
||||
|
|
@ -174,7 +176,7 @@ class EditorComponent extends React.Component {
|
|||
componentDidMount() {
|
||||
this.autoSave();
|
||||
this.fetchApps(0);
|
||||
this.fetchApp(this.props.match.params.pageHandle);
|
||||
this.fetchApp(this.props.params.pageHandle);
|
||||
this.fetchOrgEnvironmentVariables();
|
||||
this.initComponentVersioning();
|
||||
this.initRealtimeSave();
|
||||
|
|
@ -402,7 +404,7 @@ class EditorComponent extends React.Component {
|
|||
};
|
||||
|
||||
fetchApp = (startingPageHandle) => {
|
||||
const appId = this.props.match.params.id;
|
||||
const appId = this.props.params.id;
|
||||
|
||||
const callBack = async (data) => {
|
||||
let dataDefinition = defaults(data.definition, this.defaultDefinition);
|
||||
|
|
@ -1627,7 +1629,7 @@ class EditorComponent extends React.Component {
|
|||
|
||||
const queryParamsString = queryParams.map(([key, value]) => `${key}=${value}`).join('&');
|
||||
|
||||
this.props.history.push(`/apps/${this.state.appId}/${handle}?${queryParamsString}`);
|
||||
this.props.navigate(`/apps/${this.state.appId}/${handle}?${queryParamsString}`);
|
||||
|
||||
const { globals: existingGlobals } = this.state.currentState;
|
||||
|
||||
|
|
@ -1765,7 +1767,6 @@ class EditorComponent extends React.Component {
|
|||
|
||||
return (
|
||||
<div className="editor wrapper">
|
||||
<ReactTooltip type="dark" effect="solid" eventOff="click" delayShow={250} />
|
||||
<Confirm
|
||||
show={queryConfirmationList.length > 0}
|
||||
message={`Do you want to run this query - ${queryConfirmationList[0]?.queryName}?`}
|
||||
|
|
@ -2037,11 +2038,11 @@ class EditorComponent extends React.Component {
|
|||
onClick={() => {
|
||||
this.handleAddNewQuery(setSaveConfirmation, setCancelData);
|
||||
}}
|
||||
data-tooltip-id="tooltip-for-add-query"
|
||||
data-tooltip-content="Add new query"
|
||||
>
|
||||
<span
|
||||
className={` d-flex query-manager-btn-svg-wrapper align-items-center query-icon-wrapper`}
|
||||
data-tip="Add new query"
|
||||
data-class=""
|
||||
>
|
||||
<svg
|
||||
width="auto"
|
||||
|
|
@ -2133,6 +2134,7 @@ class EditorComponent extends React.Component {
|
|||
</>
|
||||
)}
|
||||
</QueryPanel>
|
||||
<ReactTooltip id="tooltip-for-add-query" className="tooltip" />
|
||||
</div>
|
||||
<div className="editor-sidebar">
|
||||
<EditorKeyHooks
|
||||
|
|
@ -2197,4 +2199,4 @@ class EditorComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export const Editor = withTranslation()(EditorComponent);
|
||||
export const Editor = withTranslation()(withRouter(EditorComponent));
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export const EditorKeyHooks = ({
|
|||
cloneComponents();
|
||||
break;
|
||||
case 'KeyC':
|
||||
console.log('copyComponent');
|
||||
copyComponents();
|
||||
break;
|
||||
case 'KeyX':
|
||||
|
|
@ -32,7 +33,7 @@ export const EditorKeyHooks = ({
|
|||
};
|
||||
|
||||
useKeyHooks(
|
||||
['up, down, left, right', 'esc', 'backspace', 'cmd+d, ctrl+d, cmd+c, ctrl+c, cmd+x, ctrl+x'],
|
||||
['up, down, left, right', 'esc', 'backspace', 'meta+d, ctrl+d, meta+c, ctrl+c, meta+x, ctrl+x'],
|
||||
handleHotKeysCallback
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Popover from 'react-bootstrap/Popover';
|
||||
import { SketchPicker } from 'react-color';
|
||||
import { Confirm } from '../Viewer/Confirm';
|
||||
import { HeaderSection } from '@/_ui/LeftSidebar';
|
||||
|
|
@ -11,6 +9,7 @@ import { CodeHinter } from '../CodeBuilder/CodeHinter';
|
|||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import _ from 'lodash';
|
||||
import Popover from '@/_ui/Popover';
|
||||
|
||||
export const GlobalSettings = ({
|
||||
globalSettings,
|
||||
|
|
@ -49,8 +48,8 @@ export const GlobalSettings = ({
|
|||
}, [JSON.stringify(resolveReferences(backgroundFxQuery, realState))]);
|
||||
|
||||
const popoverContent = (
|
||||
<Popover id="global-settings-popover" className={cx({ 'theme-dark': darkMode })}>
|
||||
<Popover.Content bsPrefix="global-settings-popover">
|
||||
<div id="global-settings-popover" className={cx({ 'theme-dark': darkMode })}>
|
||||
<div bsPrefix="global-settings-popover">
|
||||
<HeaderSection darkMode={darkMode}>
|
||||
<HeaderSection.PanelHeader title="Global settings" />
|
||||
</HeaderSection>
|
||||
|
|
@ -207,8 +206,8 @@ export const GlobalSettings = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
@ -224,16 +223,15 @@ export const GlobalSettings = ({
|
|||
onCancel={() => setConfirmationShow(false)}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<OverlayTrigger
|
||||
onToggle={(show) => {
|
||||
<Popover
|
||||
handleToggle={(show) => {
|
||||
if (show) setShow('settings');
|
||||
else setShow('');
|
||||
}}
|
||||
rootClose
|
||||
trigger="click"
|
||||
placement="bottom"
|
||||
overlay={popoverContent}
|
||||
containerPadding={50}
|
||||
popoverContentClassName="p-0 sidebar-h-100-popover"
|
||||
side="bottom"
|
||||
popoverContent={popoverContent}
|
||||
popoverContentHeight="auto"
|
||||
>
|
||||
<LeftSidebarItem
|
||||
selectedSidebarItem={show}
|
||||
|
|
@ -241,7 +239,7 @@ export const GlobalSettings = ({
|
|||
className={cx(`cursor-pointer sidebar-global-settings`)}
|
||||
tip="Settings"
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
|
||||
function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo, currentLayout, toggleCurrentLayout }) {
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
|
@ -68,7 +69,6 @@ function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo, currentLayout
|
|||
disabled: !canUndo,
|
||||
})}
|
||||
width="44"
|
||||
data-tip="undo"
|
||||
height="44"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
|
|
@ -76,6 +76,8 @@ function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo, currentLayout
|
|||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
data-tooltip-id="tooltip-for-undo"
|
||||
data-tooltip-content="Undo"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none">
|
||||
<title>undo</title>
|
||||
|
|
@ -86,7 +88,6 @@ function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo, currentLayout
|
|||
</svg>
|
||||
<svg
|
||||
title="redo"
|
||||
data-tip="redo"
|
||||
onClick={handleRedo}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={cx('redo-button cursor-pointer icon icon-tabler icon-tabler-arrow-forward-up', {
|
||||
|
|
@ -100,12 +101,16 @@ function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo, currentLayout
|
|||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
data-tooltip-id="tooltip-for-redo"
|
||||
data-tooltip-content="Redo"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none">
|
||||
<title>redo</title>
|
||||
</path>
|
||||
<path d="M15 13l4 -4l-4 -4m4 4h-11a4 4 0 0 0 0 8h1" />
|
||||
</svg>
|
||||
<Tooltip id="tooltip-for-undo" className="tooltip" />
|
||||
<Tooltip id="tooltip-for-redo" className="tooltip" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
|||
import Popover from 'react-bootstrap/Popover';
|
||||
import { SearchBox } from '@/_components/SearchBox';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import * as Icons from '@tabler/icons';
|
||||
import * as Icons from '@tabler/icons-react';
|
||||
import { VirtuosoGrid } from 'react-virtuoso';
|
||||
|
||||
export function Icon({ componentMeta, darkMode, ...restProps }) {
|
||||
|
|
@ -47,10 +47,10 @@ export function Icon({ componentMeta, darkMode, ...restProps }) {
|
|||
style={{ width: '460px', maxWidth: '460px' }}
|
||||
className={`${darkMode && 'popover-dark-themed theme-dark'} shadow icon-widget-popover`}
|
||||
>
|
||||
<Popover.Title>
|
||||
<Popover.Header>
|
||||
<SearchBox onSubmit={searchIcon} width="100%" />
|
||||
</Popover.Title>
|
||||
<Popover.Content>
|
||||
</Popover.Header>
|
||||
<Popover.Body>
|
||||
<div className="row">
|
||||
{
|
||||
<VirtuosoGrid
|
||||
|
|
@ -59,7 +59,9 @@ export function Icon({ componentMeta, darkMode, ...restProps }) {
|
|||
listClassName="icon-list-wrapper"
|
||||
itemClassName="icon-list"
|
||||
itemContent={(index) => {
|
||||
if (filteredIcons[index] === undefined) return null;
|
||||
if (filteredIcons[index] === undefined || filteredIcons[index] === 'createReactComponent')
|
||||
return null;
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons[filteredIcons[index]];
|
||||
return (
|
||||
<div
|
||||
|
|
@ -71,7 +73,7 @@ export function Icon({ componentMeta, darkMode, ...restProps }) {
|
|||
>
|
||||
<IconElement
|
||||
color={`${darkMode ? '#fff' : '#000'}`}
|
||||
stroke={3}
|
||||
stroke={1.5}
|
||||
strokeLinejoin="miter"
|
||||
style={{ width: '24px', height: '24px' }}
|
||||
/>
|
||||
|
|
@ -81,13 +83,14 @@ export function Icon({ componentMeta, darkMode, ...restProps }) {
|
|||
/>
|
||||
}
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
function renderIconPicker() {
|
||||
const icon = component.component.definition.properties.icon;
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons[icon.value];
|
||||
return (
|
||||
<>
|
||||
|
|
@ -109,7 +112,7 @@ export function Icon({ componentMeta, darkMode, ...restProps }) {
|
|||
<div className="col-auto">
|
||||
<IconElement
|
||||
color={`${darkMode ? '#fff' : '#000'}`}
|
||||
stroke={2}
|
||||
stroke={1.5}
|
||||
strokeLinejoin="miter"
|
||||
style={{ width: '20px', height: '20px' }}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
|
|||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Popover from 'react-bootstrap/Popover';
|
||||
import { Color } from '../Elements/Color';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import SelectSearch from 'react-select-search';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { EventManager } from '../EventManager';
|
||||
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
|
||||
|
|
@ -167,13 +167,13 @@ class TableComponent extends React.Component {
|
|||
];
|
||||
return (
|
||||
<Popover id="popover-basic-2" className={`${this.props.darkMode && 'popover-dark-themed theme-dark'} shadow`}>
|
||||
<Popover.Content>
|
||||
<Popover.Body>
|
||||
<div className="field mb-2" data-cy={`dropdown-column-type`}>
|
||||
<label data-cy={`label-column-type`} className="form-label">
|
||||
{this.props.t('widget.Table.columnType', 'Column type')}
|
||||
</label>
|
||||
<SelectSearch
|
||||
className={`${this.props.darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
className={`${this.props.darkMode ? 'select-search' : 'select-search'}`}
|
||||
options={[
|
||||
{ name: 'Default', value: 'default' },
|
||||
{ name: 'String', value: 'string' },
|
||||
|
|
@ -195,7 +195,7 @@ class TableComponent extends React.Component {
|
|||
onChange={(value) => {
|
||||
this.onColumnItemChange(index, 'columnType', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
placeholder={this.props.t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -220,7 +220,7 @@ class TableComponent extends React.Component {
|
|||
{this.props.t('widget.Table.overflow', 'Overflow')}
|
||||
</label>
|
||||
<SelectSearch
|
||||
className={`${this.props.darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
className={'select-search'}
|
||||
options={[
|
||||
{ name: 'Wrap', value: 'wrap' },
|
||||
{ name: 'Scroll', value: 'scroll' },
|
||||
|
|
@ -232,7 +232,7 @@ class TableComponent extends React.Component {
|
|||
onChange={(value) => {
|
||||
this.onColumnItemChange(index, 'textWrap', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
placeholder={this.props.t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -550,7 +550,7 @@ class TableComponent extends React.Component {
|
|||
</label>
|
||||
<div data-cy={`input-parse-timezone`} className="field mb-2">
|
||||
<SelectSearch
|
||||
className={`${this.props.darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
className={'select-search'}
|
||||
options={timeZoneOptions}
|
||||
value={column.timeZoneValue}
|
||||
search={true}
|
||||
|
|
@ -558,7 +558,7 @@ class TableComponent extends React.Component {
|
|||
onChange={(value) => {
|
||||
this.onColumnItemChange(index, 'timeZoneValue', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -567,7 +567,7 @@ class TableComponent extends React.Component {
|
|||
</label>
|
||||
<div ata-cy={`input-display-time-zone`} className="field mb-2">
|
||||
<SelectSearch
|
||||
className={`${this.props.darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
className={'select-search'}
|
||||
options={timeZoneOptions}
|
||||
value={column.timeZoneDisplay}
|
||||
search={true}
|
||||
|
|
@ -575,7 +575,7 @@ class TableComponent extends React.Component {
|
|||
onChange={(value) => {
|
||||
this.onColumnItemChange(index, 'timeZoneDisplay', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -641,7 +641,7 @@ class TableComponent extends React.Component {
|
|||
<div data-cy={`input-and-label-object-fit`} className="field mb-2">
|
||||
<label className="form-label">{this.props.t('widget.Table.objectFit', 'Object fit')}</label>
|
||||
<SelectSearch
|
||||
className={`${this.props.darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
className={'select-search'}
|
||||
options={[
|
||||
{ name: 'Cover', value: 'cover' },
|
||||
{ name: 'Contain', value: 'contain' },
|
||||
|
|
@ -653,7 +653,7 @@ class TableComponent extends React.Component {
|
|||
onChange={(value) => {
|
||||
this.onColumnItemChange(index, 'objectFit', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
placeholder={this.props.t('Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -674,7 +674,7 @@ class TableComponent extends React.Component {
|
|||
</span>
|
||||
</div>
|
||||
)}
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
|
@ -690,7 +690,7 @@ class TableComponent extends React.Component {
|
|||
|
||||
return (
|
||||
<Popover id="popover-basic" className={`${this.props.darkMode && 'popover-dark-themed theme-dark'} shadow`}>
|
||||
<Popover.Content>
|
||||
<Popover.Body>
|
||||
<div className="field mb-2">
|
||||
<label data-cy={`label-action-button-text`} className="form-label">
|
||||
{this.props.t('widget.Table.buttonText', 'Button Text')}
|
||||
|
|
@ -711,7 +711,7 @@ class TableComponent extends React.Component {
|
|||
{this.props.t('widget.Table.buttonPosition', 'Button Position')}
|
||||
</label>
|
||||
<SelectSearch
|
||||
className={`${this.props.darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
className={'select-search'}
|
||||
options={[
|
||||
{ name: 'Left', value: 'left' },
|
||||
{ name: 'Right', value: 'right' },
|
||||
|
|
@ -722,7 +722,7 @@ class TableComponent extends React.Component {
|
|||
onChange={(value) => {
|
||||
this.onActionButtonPropertyChanged(index, 'position', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
placeholder="Select position"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -760,7 +760,7 @@ class TableComponent extends React.Component {
|
|||
<button className="btn btn-sm btn-outline-danger mt-2 col" onClick={() => this.removeAction(index)}>
|
||||
{this.props.t('widget.Table.remove', 'Remove')}
|
||||
</button>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { ToolTip } from './Components/ToolTip';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import SelectSearch from 'react-select-search';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Select = ({ param, definition, onChange, paramType, componentMeta }) => {
|
||||
|
|
@ -18,7 +19,7 @@ export const Select = ({ param, definition, onChange, paramType, componentMeta }
|
|||
value={value}
|
||||
search={true}
|
||||
onChange={(newVal) => onChange(param, 'value', newVal, paramType)}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ export const EventManager = ({
|
|||
className={`${darkMode && 'popover-dark-themed theme-dark'} shadow`}
|
||||
data-cy="popover-card"
|
||||
>
|
||||
<Popover.Content>
|
||||
<Popover.Body>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">
|
||||
<span data-cy="event-label">{t('editor.inspector.eventManager.event', 'Event')}</span>
|
||||
|
|
@ -692,7 +692,7 @@ export const EventManager = ({
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useState } from 'react';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import SelectSearch from 'react-select-search';
|
||||
import Collapse from 'react-bootstrap/Collapse';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const QuerySelector = ({ param, definition, eventOptionUpdated, dataQueries, extraData, eventMeta }) => {
|
||||
|
|
@ -43,7 +44,7 @@ export const QuerySelector = ({ param, definition, eventOptionUpdated, dataQueri
|
|||
onChange={(value) => {
|
||||
onChange(value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export const GlobalSettings = ({ darkMode, showHideViewerNavigationControls, sho
|
|||
rootClose={true}
|
||||
overlay={
|
||||
<Popover id="page-handler-menu" className={`global-settings ${darkMode && 'popover-dark-themed'}`}>
|
||||
<Popover.Content bsPrefix="popover-body">
|
||||
<Popover.Body bsPrefix="popover-body">
|
||||
<div className="card-body">
|
||||
<label htmlFor="pin" className="form-label" data-cy={`page-settings-header`}>
|
||||
Settings
|
||||
|
|
@ -24,11 +24,13 @@ export const GlobalSettings = ({ darkMode, showHideViewerNavigationControls, sho
|
|||
<Toggle onChange={onChange} value={!showPageViwerPageNavitation} />
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
}
|
||||
>
|
||||
<MenuIcon width="10" height="16" data-cy={'menu-icon'} />
|
||||
<span>
|
||||
<MenuIcon width="10" height="16" data-cy={'menu-icon'} />
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export const PagehandlerMenu = ({ page, darkMode, handlePageCallback, showMenu,
|
|||
show={showMenu}
|
||||
overlay={
|
||||
<Popover key={page.id} id="page-handler-menu" className={darkMode && 'popover-dark-themed'}>
|
||||
<Popover.Content key={page.id} bsPrefix="popover-body">
|
||||
<Popover.Body key={page.id} bsPrefix="popover-body">
|
||||
<div className="card-body">
|
||||
<PageHandleField page={page} updatePageHandle={handlePageCallback} />
|
||||
<hr style={{ margin: '0.75rem 0' }} />
|
||||
|
|
@ -84,19 +84,21 @@ export const PagehandlerMenu = ({ page, darkMode, handlePageCallback, showMenu,
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
}
|
||||
>
|
||||
<Button.UnstyledButton
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setShowMenu(true);
|
||||
}}
|
||||
styles={{ height: '20px', marginTop: '2px' }}
|
||||
>
|
||||
<Button.Content dataCy={`page-menu`} iconSrc={'assets/images/icons/3dots-menu.svg'} />
|
||||
</Button.UnstyledButton>
|
||||
<span>
|
||||
<Button.UnstyledButton
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setShowMenu(true);
|
||||
}}
|
||||
styles={{ height: '20px', marginTop: '2px' }}
|
||||
>
|
||||
<Button.Content dataCy={`page-menu`} iconSrc={'assets/images/icons/3dots-menu.svg'} />
|
||||
</Button.UnstyledButton>
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ const LeftSidebarPageSelector = ({
|
|||
|
||||
const popoverContent = (
|
||||
<div>
|
||||
<div className="card-body p-0 pb-5" onClick={(event) => event.stopPropagation()}>
|
||||
<div className="card-body p-0 pb-5">
|
||||
<HeaderSection darkMode={darkMode}>
|
||||
<HeaderSection.PanelHeader
|
||||
title="Pages"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { dataqueryService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import { allSources, source } from './QueryEditors';
|
||||
import { Transformation } from './Transformation';
|
||||
import { previewQuery } from '@/_helpers/appUtils';
|
||||
|
|
@ -12,6 +12,7 @@ import Preview from './Preview';
|
|||
import DataSourceLister from './DataSourceLister';
|
||||
import _, { isEmpty, isEqual, capitalize } from 'lodash';
|
||||
import { allOperations } from '@tooljet/plugins/client';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import cx from 'classnames';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
|
|
@ -54,7 +55,6 @@ class QueryManagerComponent extends React.Component {
|
|||
}
|
||||
|
||||
setStateFromProps = (props) => {
|
||||
console.log('setStateFromProps--- ', props.isUnsavedQueriesAvailable);
|
||||
const selectedQuery = props.selectedQuery;
|
||||
|
||||
const dataSourceId = selectedQuery?.data_source_id;
|
||||
|
|
@ -522,8 +522,6 @@ class QueryManagerComponent extends React.Component {
|
|||
})}
|
||||
key={selectedQuery ? selectedQuery.id : ''}
|
||||
>
|
||||
<ReactTooltip type="dark" effect="solid" delayShow={250} />
|
||||
|
||||
<div className="row header" style={{ padding: '8px 0' }}>
|
||||
<div className="col d-flex align-items-center px-3 h-100 font-weight-500 py-1" style={{ gap: '10px' }}>
|
||||
{(addingQuery || editingQuery) && selectedDataSource && (
|
||||
|
|
@ -700,7 +698,8 @@ class QueryManagerComponent extends React.Component {
|
|||
<span
|
||||
onClick={this.props.toggleQueryEditor}
|
||||
className={`cursor-pointer m-3 toggle-query-editor-svg d-flex`}
|
||||
data-tip="Hide query editor"
|
||||
data-tooltip-id="tooltip-for-hide-query-editor"
|
||||
data-tooltip-content="Hide query editor"
|
||||
>
|
||||
<svg width="5.58" height="10.25" viewBox="0 0 6 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
|
|
@ -709,6 +708,7 @@ class QueryManagerComponent extends React.Component {
|
|||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<Tooltip id="tooltip-for-hide-query-editor" className="tooltip" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
||||
import { useEventListener } from '@/_hooks/use-event-listener';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
|
||||
const QueryPanel = ({ children, computeCurrentQueryPanelHeight }) => {
|
||||
const queryManagerPreferences = useRef(JSON.parse(localStorage.getItem('queryManagerPreferences')) ?? {});
|
||||
|
|
@ -93,7 +94,12 @@ const QueryPanel = ({ children, computeCurrentQueryPanelHeight }) => {
|
|||
<h5 className="mb-0 font-weight-500 cursor-pointer" onClick={toggleQueryEditor}>
|
||||
QUERIES
|
||||
</h5>
|
||||
<span onClick={toggleQueryEditor} className="cursor-pointer m-1 d-flex" data-tip="Show query editor">
|
||||
<span
|
||||
onClick={toggleQueryEditor}
|
||||
className="cursor-pointer m-1 d-flex"
|
||||
data-tooltip-id="tooltip-for-show-query-editor"
|
||||
data-tooltip-content="Show query editor"
|
||||
>
|
||||
{isExpanded ? (
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
|
|
@ -128,6 +134,7 @@ const QueryPanel = ({ children, computeCurrentQueryPanelHeight }) => {
|
|||
setCancelData,
|
||||
})}
|
||||
</div>
|
||||
<Tooltip id="tooltip-for-show-query-editor" className="tooltip" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import Popover from '@/_ui/Popover';
|
||||
import Avatar from '@/_ui/Avatar';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
|
|
@ -18,13 +17,6 @@ const RealtimeAvatars = ({ darkMode }) => {
|
|||
const getAvatarText = (presence) => presence.firstName?.charAt(0) + presence.lastName?.charAt(0);
|
||||
const getAvatarTitle = (presence) => `${presence.firstName} ${presence.lastName}`;
|
||||
|
||||
// This is required for the tooltip not binding to dynamic content
|
||||
// i.e. when others on the same version changes, tooltip
|
||||
// ref: https://github.com/wwayne/react-tooltip#3-tooltip-not-binding-to-dynamic-content
|
||||
React.useEffect(() => {
|
||||
ReactTooltip.rebuild();
|
||||
}, [othersOnSameVersionAndPage?.length]);
|
||||
|
||||
const popoverContent = () => {
|
||||
return othersOnSameVersionAndPage
|
||||
.slice(MAX_DISPLAY_USERS, othersOnSameVersionAndPage.length)
|
||||
|
|
@ -40,6 +32,7 @@ const RealtimeAvatars = ({ darkMode }) => {
|
|||
text={getAvatarText(presence)}
|
||||
image={presence?.image}
|
||||
borderShape="rounded"
|
||||
indexId={id}
|
||||
/>
|
||||
</div>
|
||||
<div className={`col text-truncate ${darkMode && 'text-white'}`}>
|
||||
|
|
@ -72,6 +65,7 @@ const RealtimeAvatars = ({ darkMode }) => {
|
|||
text={getAvatarText(self?.presence)}
|
||||
image={self?.presence?.image}
|
||||
borderShape="rounded"
|
||||
indexId={self?.presence?.id}
|
||||
/>
|
||||
)}
|
||||
{othersOnSameVersionAndPage.slice(0, MAX_DISPLAY_USERS).map(({ id, presence }) => {
|
||||
|
|
@ -83,6 +77,7 @@ const RealtimeAvatars = ({ darkMode }) => {
|
|||
text={getAvatarText(presence)}
|
||||
image={presence?.image}
|
||||
borderShape="rounded"
|
||||
indexId={id}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { RoomProvider } from '@y-presence/react';
|
|||
import Spinner from '@/_ui/Spinner';
|
||||
import { Editor } from '@/Editor';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
import { useParams } from 'react-router-dom';
|
||||
const Y = require('yjs');
|
||||
const psl = require('psl');
|
||||
const { WebsocketProvider } = require('y-websocket');
|
||||
|
|
@ -27,7 +28,8 @@ const getWebsocketUrl = () => {
|
|||
};
|
||||
|
||||
export const RealtimeEditor = (props) => {
|
||||
const appId = props.match.params.id;
|
||||
const params = useParams();
|
||||
const appId = params.id;
|
||||
const [provider, setProvider] = React.useState();
|
||||
const router = useRouter();
|
||||
|
||||
|
|
|
|||
|
|
@ -74,15 +74,18 @@ export const SubContainer = ({
|
|||
allComponents[parent]?.component?.component === 'Form') ??
|
||||
false;
|
||||
|
||||
let childWidgets = [];
|
||||
|
||||
Object.keys(allComponents).forEach((key) => {
|
||||
if (allComponents[key].parent === parent) {
|
||||
childWidgets[key] = { ...allComponents[key], component: { ...allComponents[key]['component'], parent } };
|
||||
}
|
||||
});
|
||||
const getChildWidgets = (components) => {
|
||||
let childWidgets = [];
|
||||
Object.keys(components).forEach((key) => {
|
||||
if (components[key].parent === parent) {
|
||||
childWidgets[key] = { ...components[key], component: { ...components[key]['component'], parent } };
|
||||
}
|
||||
});
|
||||
return childWidgets;
|
||||
};
|
||||
|
||||
const [boxes, setBoxes] = useState(allComponents);
|
||||
const [childWidgets, setChildWidgets] = useState(() => getChildWidgets(allComponents));
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
// const [subContainerHeight, setSubContainerHeight] = useState('100%'); //used to determine the height of the sub container for modal
|
||||
|
|
@ -90,7 +93,9 @@ export const SubContainer = ({
|
|||
|
||||
useEffect(() => {
|
||||
setBoxes(allComponents);
|
||||
}, [allComponents]);
|
||||
setChildWidgets(() => getChildWidgets(allComponents));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [allComponents, parent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (mounted) {
|
||||
|
|
@ -348,6 +353,7 @@ export const SubContainer = ({
|
|||
}
|
||||
}
|
||||
|
||||
setChildWidgets(() => getChildWidgets(newBoxes));
|
||||
setBoxes(newBoxes);
|
||||
}
|
||||
|
||||
|
|
@ -534,18 +540,6 @@ export const SubContainer = ({
|
|||
);
|
||||
}
|
||||
})}
|
||||
|
||||
{Object.keys(boxes).length === 0 && !appLoading && !isDragging && (
|
||||
<div className="mx-auto mt-5 w-50 p-5 bg-light no-components-box" data-cy="----Test----">
|
||||
<center className="text-muted">
|
||||
Drag components from the right sidebar and drop here. Check out our{' '}
|
||||
<a href="https://docs.tooljet.io/docs/tutorial/adding-widget" target="_blank" rel="noreferrer">
|
||||
guide
|
||||
</a>{' '}
|
||||
on adding widgets.
|
||||
</center>
|
||||
</div>
|
||||
)}
|
||||
{appLoading && (
|
||||
<div className="mx-auto mt-5 w-50 p-5">
|
||||
<center>
|
||||
|
|
|
|||
|
|
@ -20,9 +20,10 @@ import { DataSourceTypes } from './DataSourceManager/SourceComponents';
|
|||
import { resolveReferences, safelyParseJSON, stripTrailingSlash } from '@/_helpers/utils';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import _ from 'lodash';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { withRouter } from '@/_hoc/withRouter';
|
||||
|
||||
class ViewerComponent extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -31,11 +32,11 @@ class ViewerComponent extends React.Component {
|
|||
const deviceWindowWidth = window.screen.width - 5;
|
||||
const isMobileDevice = deviceWindowWidth < 600;
|
||||
|
||||
const pageHandle = this.props.match?.params?.pageHandle;
|
||||
const pageHandle = this.props?.params?.pageHandle;
|
||||
|
||||
const slug = this.props.match.params.slug;
|
||||
const appId = this.props.match.params.id;
|
||||
const versionId = this.props.match.params.versionId;
|
||||
const slug = this.props.params.slug;
|
||||
const appId = this.props.params.id;
|
||||
const versionId = this.props.params.versionId;
|
||||
|
||||
this.state = {
|
||||
slug,
|
||||
|
|
@ -128,7 +129,7 @@ class ViewerComponent extends React.Component {
|
|||
|
||||
const pages = Object.entries(data.definition.pages).map(([pageId, page]) => ({ id: pageId, ...page }));
|
||||
const homePageId = data.definition.homePageId;
|
||||
const startingPageHandle = this.props.match?.params?.pageHandle;
|
||||
const startingPageHandle = this.props?.params?.pageHandle;
|
||||
const currentPageId = pages.filter((page) => page.handle === startingPageHandle)[0]?.id ?? homePageId;
|
||||
const currentPage = pages.find((page) => page.id === currentPageId);
|
||||
|
||||
|
|
@ -169,6 +170,7 @@ class ViewerComponent extends React.Component {
|
|||
this.setState({ initialComputationOfStateDone: true });
|
||||
console.log('Default component state computed and set');
|
||||
this.runQueries(data.data_queries);
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
const { events } = this.state.appDefinition?.pages[this.state.currentPageId];
|
||||
for (const event of events ?? []) {
|
||||
await this.handleEvent(event.eventId, event);
|
||||
|
|
@ -270,41 +272,41 @@ class ViewerComponent extends React.Component {
|
|||
this.switchOrganization(errorObj?.organizationId, appId, versionId);
|
||||
return;
|
||||
}
|
||||
return <Redirect to={'/'} />;
|
||||
return <Navigate replace to={'/'} />;
|
||||
} else if (statusCode === 401) {
|
||||
return <Redirect to={`/login?redirectTo=${this.props.location.pathname}`} />;
|
||||
return <Navigate replace to={`/login?redirectTo=${this.props.location.pathname}`} />;
|
||||
} else if (statusCode === 404) {
|
||||
toast.error(errorDetails?.error ?? 'App not found', {
|
||||
position: 'top-center',
|
||||
});
|
||||
}
|
||||
return <Redirect to={'/'} />;
|
||||
return <Navigate replace to={'/'} />;
|
||||
}
|
||||
} catch (err) {
|
||||
return <Redirect to={'/'} />;
|
||||
return <Navigate replace to={'/'} />;
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const slug = this.props.match.params.slug;
|
||||
const appId = this.props.match.params.id;
|
||||
const versionId = this.props.match.params.versionId;
|
||||
const slug = this.props.params.slug;
|
||||
const appId = this.props.params.id;
|
||||
const versionId = this.props.params.versionId;
|
||||
|
||||
this.setState({ isLoading: false });
|
||||
slug ? this.loadApplicationBySlug(slug) : this.loadApplicationByVersion(appId, versionId);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.match.params.slug && this.props.match.params.slug !== prevProps.match.params.slug) {
|
||||
if (this.props.params.slug && this.props.params.slug !== prevProps.params.slug) {
|
||||
this.setState({ isLoading: true });
|
||||
this.loadApplicationBySlug(this.props.match.params.slug);
|
||||
this.loadApplicationBySlug(this.props.params.slug);
|
||||
}
|
||||
|
||||
if (this.state.initialComputationOfStateDone) this.handlePageSwitchingBasedOnURLparam();
|
||||
}
|
||||
|
||||
handlePageSwitchingBasedOnURLparam() {
|
||||
const handleOnURL = this.props.match.params.pageHandle;
|
||||
const handleOnURL = this.props.params.pageHandle;
|
||||
const pageIdCorrespondingToHandleOnURL = handleOnURL
|
||||
? this.findPageIdFromHandle(handleOnURL)
|
||||
: this.state.appDefinition.homePageId;
|
||||
|
|
@ -344,6 +346,7 @@ class ViewerComponent extends React.Component {
|
|||
async () => {
|
||||
computeComponentState(this, this.state.appDefinition?.pages[this.state.currentPageId].components).then(
|
||||
async () => {
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
const { events } = this.state.appDefinition?.pages[this.state.currentPageId];
|
||||
for (const event of events ?? []) {
|
||||
await this.handleEvent(event.eventId, event);
|
||||
|
|
@ -404,9 +407,9 @@ class ViewerComponent extends React.Component {
|
|||
|
||||
const queryParamsString = queryParams.map(([key, value]) => `${key}=${value}`).join('&');
|
||||
|
||||
if (this.state.slug) this.props.history.push(`/applications/${this.state.slug}/${handle}?${queryParamsString}`);
|
||||
if (this.state.slug) this.props.navigate(`/applications/${this.state.slug}/${handle}?${queryParamsString}`);
|
||||
else
|
||||
this.props.history.push(
|
||||
this.props.navigate(
|
||||
`/applications/${this.state.appId}/versions/${this.state.versionId}/${handle}?${queryParamsString}`
|
||||
);
|
||||
};
|
||||
|
|
@ -574,4 +577,4 @@ class ViewerComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export const Viewer = withTranslation()(ViewerComponent);
|
||||
export const Viewer = withTranslation()(withRouter(ViewerComponent));
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import cx from 'classnames';
|
||||
import { AppMenu } from './AppMenu';
|
||||
import { history } from '@/_helpers';
|
||||
import moment from 'moment';
|
||||
import { ToolTip } from '@/_components';
|
||||
import { Fade } from '@/_ui/Fade';
|
||||
import useHover from '@/_hooks/useHover';
|
||||
import configs from './Configs/AppIcon.json';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import urlJoin from 'url-join';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const { defaultIcon } = configs;
|
||||
|
|
@ -28,6 +27,7 @@ export default function AppCard({
|
|||
const [focused, setFocused] = useState(false);
|
||||
const [isMenuOpen, setMenuOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onMenuToggle = useCallback(
|
||||
(status) => {
|
||||
|
|
@ -159,7 +159,7 @@ export default function AppCard({
|
|||
if (app?.current_version_id) {
|
||||
window.open(urlJoin(window.public_config?.TOOLJET_HOST, `/applications/${app.slug}`));
|
||||
} else {
|
||||
history.push(app?.current_version_id ? `/applications/${app.slug}` : '');
|
||||
navigate(app?.current_version_id ? `/applications/${app.slug}` : '');
|
||||
}
|
||||
}}
|
||||
data-cy="launch-button"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export const AppMenu = function AppMenu({
|
|||
onToggle={onMenuOpen}
|
||||
overlay={
|
||||
<Popover id="popover-app-menu" className={darkMode && 'popover-dark-themed'}>
|
||||
<Popover.Content bsPrefix="popover-body">
|
||||
<Popover.Body bsPrefix="popover-body">
|
||||
<div data-cy="card-options">
|
||||
{canUpdateApp && (
|
||||
<Field
|
||||
|
|
@ -78,7 +78,7 @@ export const AppMenu = function AppMenu({
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import TemplateLibraryModal from './TemplateLibraryModal/';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { libraryAppService } from '@/_services';
|
||||
import EmptyIllustration from '@assets/images/no-apps.svg';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export const BlankPage = function BlankPage({
|
||||
createApp,
|
||||
|
|
@ -19,7 +19,7 @@ export const BlankPage = function BlankPage({
|
|||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [deploying, setDeploying] = useState(false);
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const staticTemplates = [
|
||||
{ id: 's3-file-explorer', name: 'S3 File Explorer' },
|
||||
|
|
@ -37,7 +37,7 @@ export const BlankPage = function BlankPage({
|
|||
setDeploying(false);
|
||||
toast.dismiss(loadingToastId);
|
||||
toast.success('App created.');
|
||||
history.push(`/apps/${data.id}`);
|
||||
navigate(`/apps/${data.id}`);
|
||||
})
|
||||
.catch((e) => {
|
||||
toast.dismiss(loadingToastId);
|
||||
|
|
|
|||
|
|
@ -45,8 +45,13 @@ export const FolderMenu = function FolderMenu({
|
|||
setOpen(isOpen);
|
||||
}}
|
||||
overlay={
|
||||
<Popover id="popover-app-menu" className={darkMode && 'popover-dark-themed'} data-cy="folder-card">
|
||||
<Popover.Content bsPrefix="popover-body">
|
||||
<Popover
|
||||
id="popover-app-menu"
|
||||
className={darkMode && 'popover-dark-themed'}
|
||||
data-cy="folder-card"
|
||||
style={{ transition: 'none' }}
|
||||
>
|
||||
<Popover.Body bsPrefix="popover-body">
|
||||
<div>
|
||||
{canUpdateFolder && (
|
||||
<Field text={t('homePage.foldersSection.editFolder', 'Edit folder')} onClick={editFolder} />
|
||||
|
|
@ -59,7 +64,7 @@ export const FolderMenu = function FolderMenu({
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { withTranslation } from 'react-i18next';
|
|||
import { sample } from 'lodash';
|
||||
import ExportAppModal from './ExportAppModal';
|
||||
import Footer from './Footer';
|
||||
import { withRouter } from '@/_hoc/withRouter';
|
||||
|
||||
const { iconList, defaultIcon } = configs;
|
||||
|
||||
|
|
@ -116,7 +117,7 @@ class HomePageComponent extends React.Component {
|
|||
appService
|
||||
.createApp({ icon: sample(iconList) })
|
||||
.then((data) => {
|
||||
_self.props.history.push(`/apps/${data.id}`);
|
||||
_self.props.navigate(`/apps/${data.id}`);
|
||||
})
|
||||
.catch(({ error }) => {
|
||||
toast.error(error);
|
||||
|
|
@ -135,7 +136,7 @@ class HomePageComponent extends React.Component {
|
|||
.then((data) => {
|
||||
toast.success('App cloned successfully.');
|
||||
this.setState({ isCloningApp: false });
|
||||
this.props.history.push(`/apps/${data.id}`);
|
||||
this.props.navigate(`/apps/${data.id}`);
|
||||
})
|
||||
.catch(({ _error }) => {
|
||||
toast.error('Could not clone the app.');
|
||||
|
|
@ -163,7 +164,7 @@ class HomePageComponent extends React.Component {
|
|||
this.setState({
|
||||
isImportingApp: false,
|
||||
});
|
||||
this.props.history.push(`/apps/${data.id}`);
|
||||
this.props.navigate(`/apps/${data.id}`);
|
||||
})
|
||||
.catch(({ error }) => {
|
||||
toast.error(`Could not import the app: ${error}`);
|
||||
|
|
@ -687,4 +688,4 @@ class HomePageComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export const HomePage = withTranslation()(HomePageComponent);
|
||||
export const HomePage = withTranslation()(withRouter(HomePageComponent));
|
||||
|
|
|
|||
|
|
@ -57,8 +57,10 @@ const SearchBoxContainer = ({ onChange, queryString }) => {
|
|||
}
|
||||
|
||||
return () => {
|
||||
document.querySelector('.template-search-box .input-icon .form-control:not(:first-child)').style.paddingLeft =
|
||||
'2.5rem';
|
||||
if (document.querySelector('.template-search-box .input-icon .form-control:not(:first-child)')) {
|
||||
document.querySelector('.template-search-box .input-icon .form-control:not(:first-child)').style.paddingLeft =
|
||||
'2.5rem';
|
||||
}
|
||||
};
|
||||
}, [searchText]);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ export default function TemplateDisplay(props) {
|
|||
{sources?.map((source) => (
|
||||
<Badge
|
||||
className="me-2"
|
||||
variant="primary"
|
||||
key={source.id}
|
||||
bg={props.darkMode ? 'dark' : 'light'}
|
||||
style={{
|
||||
backgroundColor: '#D2DDEC',
|
||||
color: 'black',
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { libraryAppService } from '@/_services';
|
|||
import { toast } from 'react-hot-toast';
|
||||
import _ from 'lodash';
|
||||
import TemplateDisplay from './TemplateDisplay';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const identifyUniqueCategories = (templates) =>
|
||||
|
|
@ -16,7 +16,7 @@ const identifyUniqueCategories = (templates) =>
|
|||
}));
|
||||
|
||||
export default function TemplateLibraryModal(props) {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const [libraryApps, setLibraryApps] = useState([]);
|
||||
const [selectedCategory, selectCategory] = useState({ id: 'all', count: 0 });
|
||||
const filteredApps = libraryApps.filter(
|
||||
|
|
@ -60,7 +60,7 @@ export default function TemplateLibraryModal(props) {
|
|||
toast.success('App created.', {
|
||||
position: 'top-center',
|
||||
});
|
||||
history.push(`/apps/${data.id}`);
|
||||
navigate(`/apps/${data.id}`);
|
||||
})
|
||||
.catch((e) => {
|
||||
toast.error(e.error, {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { authenticationService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, Navigate } from 'react-router-dom';
|
||||
import queryString from 'query-string';
|
||||
import GoogleSSOLoginButton from '@ee/components/LoginPage/GoogleSSOLoginButton';
|
||||
import GitSSOLoginButton from '@ee/components/LoginPage/GitSSOLoginButton';
|
||||
|
|
@ -15,6 +15,7 @@ import EyeHide from '../../assets/images/onboardingassets/Icons/EyeHide';
|
|||
import EyeShow from '../../assets/images/onboardingassets/Icons/EyeShow';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
import { getCookie, eraseCookie, setCookie } from '@/_helpers/cookie';
|
||||
import { withRouter } from '@/_hoc/withRouter';
|
||||
class LoginPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -24,9 +25,10 @@ class LoginPageComponent extends React.Component {
|
|||
isGettingConfigs: true,
|
||||
configs: undefined,
|
||||
emailError: false,
|
||||
navigateToLogin: false,
|
||||
};
|
||||
this.single_organization = window.public_config?.DISABLE_MULTI_WORKSPACE === 'true';
|
||||
this.organizationId = props.match.params.organizationId;
|
||||
this.organizationId = props.params.organizationId;
|
||||
}
|
||||
darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
|
|
@ -37,10 +39,7 @@ class LoginPageComponent extends React.Component {
|
|||
(!this.organizationId && authenticationService.currentUserValue) ||
|
||||
(this.organizationId && authenticationService?.currentUserValue?.organization_id === this.organizationId)
|
||||
) {
|
||||
// redirect to home if already logged in
|
||||
// set redirect path for sso login
|
||||
const redirectPath = this.eraseRedirectUrl();
|
||||
return this.props.history.push(redirectPath ? redirectPath : '/');
|
||||
return this.setState({ navigateToLogin: true });
|
||||
}
|
||||
if (this.organizationId || this.single_organization)
|
||||
authenticationService.saveLoginOrganizationId(this.organizationId);
|
||||
|
|
@ -51,7 +50,7 @@ class LoginPageComponent extends React.Component {
|
|||
},
|
||||
(response) => {
|
||||
if (response.data.statusCode !== 404) {
|
||||
return this.props.history.push({
|
||||
return this.props.navigate({
|
||||
pathname: '/',
|
||||
state: { errorMessage: 'Error while login, please try again' },
|
||||
});
|
||||
|
|
@ -59,7 +58,7 @@ class LoginPageComponent extends React.Component {
|
|||
// If there is no organization found for single organization setup
|
||||
// show form to sign up
|
||||
// redirected here for self hosted version
|
||||
this.props.history.push('/setup');
|
||||
this.props.navigate('/setup');
|
||||
|
||||
this.setState({
|
||||
isGettingConfigs: false,
|
||||
|
|
@ -131,7 +130,7 @@ class LoginPageComponent extends React.Component {
|
|||
const params = queryString.parse(this.props.location.search);
|
||||
const { from } = params.redirectTo ? { from: { pathname: params.redirectTo } } : { from: { pathname: '/' } };
|
||||
const redirectPath = from.pathname === '/confirm' ? '/' : from;
|
||||
this.props.history.push(redirectPath);
|
||||
this.props.navigate(redirectPath);
|
||||
this.setState({ isLoading: false });
|
||||
this.eraseRedirectUrl();
|
||||
};
|
||||
|
|
@ -144,227 +143,238 @@ class LoginPageComponent extends React.Component {
|
|||
this.setState({ isLoading: false });
|
||||
};
|
||||
|
||||
redirectToUrl = () => {
|
||||
const redirectPath = this.eraseRedirectUrl();
|
||||
return redirectPath ? redirectPath : '/';
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isLoading, configs, isGettingConfigs } = this.state;
|
||||
const { isLoading, configs, isGettingConfigs, navigateToLogin } = this.state;
|
||||
return (
|
||||
<>
|
||||
<div className="common-auth-section-whole-wrapper page">
|
||||
<div className="common-auth-section-left-wrapper">
|
||||
<OnboardingNavbar darkMode={this.darkMode} />
|
||||
<div className="common-auth-section-left-wrapper-grid">
|
||||
{this.state.isGettingConfigs && (
|
||||
<div className="loader-wrapper">
|
||||
<ShowLoading />
|
||||
</div>
|
||||
)}
|
||||
<form action="." method="get" autoComplete="off">
|
||||
{isGettingConfigs ? (
|
||||
{navigateToLogin ? (
|
||||
<Navigate to={this.redirectToUrl()} />
|
||||
) : (
|
||||
<div className="common-auth-section-whole-wrapper page">
|
||||
<div className="common-auth-section-left-wrapper">
|
||||
<OnboardingNavbar darkMode={this.darkMode} />
|
||||
<div className="common-auth-section-left-wrapper-grid">
|
||||
{this.state.isGettingConfigs && (
|
||||
<div className="loader-wrapper">
|
||||
<ShowLoading />
|
||||
</div>
|
||||
) : (
|
||||
<div className="common-auth-container-wrapper ">
|
||||
{!configs?.form && !configs?.git && !configs?.google && (
|
||||
<div className="text-center-onboard">
|
||||
<h2 data-cy="no-login-methods-warning">
|
||||
{this.props.t(
|
||||
'loginSignupPage.noLoginMethodsEnabled',
|
||||
'No login methods enabled for this workspace'
|
||||
)}
|
||||
</h2>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
{(this.state?.configs?.google?.enabled ||
|
||||
this.state?.configs?.git?.enabled ||
|
||||
configs?.form?.enabled) && (
|
||||
<>
|
||||
<h2 className="common-auth-section-header sign-in-header" data-cy="sign-in-header">
|
||||
{this.props.t('loginSignupPage.signIn', `Sign in`)}
|
||||
</h2>
|
||||
{this.organizationId && (
|
||||
<p
|
||||
className="text-center-onboard workspace-login-description"
|
||||
data-cy="workspace-sign-in-sub-header"
|
||||
>
|
||||
Sign in to your workspace - {configs?.name}
|
||||
</p>
|
||||
)}
|
||||
<div className="tj-text-input-label">
|
||||
{!this.organizationId && (configs?.form?.enable_sign_up || configs?.enable_sign_up) && (
|
||||
<div className="common-auth-sub-header sign-in-sub-header" data-cy="sign-in-sub-header">
|
||||
{this.props.t('newToTooljet', 'New to ToolJet?')}
|
||||
<Link
|
||||
to={'/signup'}
|
||||
tabIndex="-1"
|
||||
style={{ marginLeft: '4px' }}
|
||||
data-cy="create-an-account-link"
|
||||
>
|
||||
{this.props.t('loginSignupPage.createToolJetAccount', `Create an account`)}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<form action="." method="get" autoComplete="off">
|
||||
{isGettingConfigs ? (
|
||||
<div className="loader-wrapper">
|
||||
<ShowLoading />
|
||||
</div>
|
||||
) : (
|
||||
<div className="common-auth-container-wrapper ">
|
||||
{!configs?.form && !configs?.git && !configs?.google && (
|
||||
<div className="text-center-onboard">
|
||||
<h2 data-cy="no-login-methods-warning">
|
||||
{this.props.t(
|
||||
'loginSignupPage.noLoginMethodsEnabled',
|
||||
'No login methods enabled for this workspace'
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{this.state?.configs?.git?.enabled && (
|
||||
<div className="login-sso-wrapper">
|
||||
<GitSSOLoginButton configs={this.state?.configs?.git?.configs} />
|
||||
</h2>
|
||||
</div>
|
||||
)}
|
||||
{this.state?.configs?.google?.enabled && (
|
||||
<div className="login-sso-wrapper">
|
||||
<GoogleSSOLoginButton
|
||||
configs={this.state?.configs?.google?.configs}
|
||||
configId={this.state?.configs?.google?.config_id}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{(this.state?.configs?.google?.enabled || this.state?.configs?.git?.enabled) &&
|
||||
configs?.form?.enabled && (
|
||||
<div className="separator-onboarding ">
|
||||
<div className="mt-2 separator" data-cy="onboarding-separator">
|
||||
<h2>
|
||||
<span>OR</span>
|
||||
</h2>
|
||||
<div>
|
||||
{(this.state?.configs?.google?.enabled ||
|
||||
this.state?.configs?.git?.enabled ||
|
||||
configs?.form?.enabled) && (
|
||||
<>
|
||||
<h2 className="common-auth-section-header sign-in-header" data-cy="sign-in-header">
|
||||
{this.props.t('loginSignupPage.signIn', `Sign in`)}
|
||||
</h2>
|
||||
{this.organizationId && (
|
||||
<p
|
||||
className="text-center-onboard workspace-login-description"
|
||||
data-cy="workspace-sign-in-sub-header"
|
||||
>
|
||||
Sign in to your workspace - {configs?.name}
|
||||
</p>
|
||||
)}
|
||||
<div className="tj-text-input-label">
|
||||
{!this.organizationId && (configs?.form?.enable_sign_up || configs?.enable_sign_up) && (
|
||||
<div className="common-auth-sub-header sign-in-sub-header" data-cy="sign-in-sub-header">
|
||||
{this.props.t('newToTooljet', 'New to ToolJet?')}
|
||||
<Link
|
||||
to={'/signup'}
|
||||
tabIndex="-1"
|
||||
style={{ marginLeft: '4px' }}
|
||||
data-cy="create-an-account-link"
|
||||
>
|
||||
{this.props.t('loginSignupPage.createToolJetAccount', `Create an account`)}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{this.state?.configs?.git?.enabled && (
|
||||
<div className="login-sso-wrapper">
|
||||
<GitSSOLoginButton configs={this.state?.configs?.git?.configs} />
|
||||
</div>
|
||||
)}
|
||||
{configs?.form?.enabled && (
|
||||
<>
|
||||
<div className="signin-email-wrap">
|
||||
<label className="tj-text-input-label" data-cy="work-email-label">
|
||||
{this.props.t('loginSignupPage.workEmail', 'Email?')}
|
||||
</label>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name="email"
|
||||
type="email"
|
||||
className="tj-text-input"
|
||||
placeholder={this.props.t('loginSignupPage.enterWorkEmail', 'Enter your email')}
|
||||
style={{ marginBottom: '0px' }}
|
||||
data-cy="work-email-input"
|
||||
autoFocus
|
||||
autoComplete="off"
|
||||
{this.state?.configs?.google?.enabled && (
|
||||
<div className="login-sso-wrapper">
|
||||
<GoogleSSOLoginButton
|
||||
configs={this.state?.configs?.google?.configs}
|
||||
configId={this.state?.configs?.google?.config_id}
|
||||
/>
|
||||
{this.state?.emailError && (
|
||||
<span className="tj-text-input-error-state" data-cy="email-error-message">
|
||||
{this.state?.emailError}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label className="tj-text-input-label" data-cy="password-label">
|
||||
{this.props.t('loginSignupPage.password', 'Password')}
|
||||
<span style={{ marginLeft: '4px' }}>
|
||||
<Link
|
||||
to={'/forgot-password'}
|
||||
tabIndex="-1"
|
||||
className="login-forgot-password"
|
||||
style={{ color: this.darkMode && '#3E63DD' }}
|
||||
data-cy="forgot-password-link"
|
||||
>
|
||||
{this.props.t('loginSignupPage.forgot', 'Forgot?')}
|
||||
</Link>
|
||||
</span>
|
||||
</label>
|
||||
<div className="login-password">
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name="password"
|
||||
type={this.state?.showPassword ? 'text' : 'password'}
|
||||
className="tj-text-input"
|
||||
placeholder={this.props.t('loginSignupPage.EnterPassword', 'Enter password')}
|
||||
data-cy="password-input-field"
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="login-password-hide-img"
|
||||
onClick={this.handleOnCheck}
|
||||
data-cy="show-password-icon"
|
||||
>
|
||||
{this.state?.showPassword ? (
|
||||
<EyeHide
|
||||
fill={
|
||||
this.darkMode
|
||||
? this.state?.password?.length
|
||||
? '#D1D5DB'
|
||||
: '#656565'
|
||||
: this.state?.password?.length
|
||||
? '#384151'
|
||||
: '#D1D5DB'
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<EyeShow
|
||||
fill={
|
||||
this.darkMode
|
||||
? this.state?.password?.length
|
||||
? '#D1D5DB'
|
||||
: '#656565'
|
||||
: this.state?.password?.length
|
||||
? '#384151'
|
||||
: '#D1D5DB'
|
||||
}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
{(this.state?.configs?.google?.enabled || this.state?.configs?.git?.enabled) &&
|
||||
configs?.form?.enabled && (
|
||||
<div className="separator-onboarding ">
|
||||
<div className="mt-2 separator" data-cy="onboarding-separator">
|
||||
<h2>
|
||||
<span>OR</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={` d-flex flex-column align-items-center ${!configs?.form?.enabled ? 'mt-0' : ''}`}>
|
||||
{configs?.form?.enabled && (
|
||||
<ButtonSolid
|
||||
className="login-btn"
|
||||
onClick={this.authUser}
|
||||
disabled={isLoading}
|
||||
data-cy="login-button"
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="spinner-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span> {this.props.t('loginSignupPage.loginTo', 'Login')}</span>
|
||||
<EnterIcon
|
||||
className="enter-icon-onboard"
|
||||
fill={
|
||||
isLoading || !this.state?.email || !this.state?.password
|
||||
? this.darkMode
|
||||
? '#656565'
|
||||
: ' #D1D5DB'
|
||||
: '#fff'
|
||||
}
|
||||
></EnterIcon>
|
||||
</>
|
||||
)}
|
||||
</ButtonSolid>
|
||||
)}
|
||||
{authenticationService?.currentUserValue?.organization && this.organizationId && (
|
||||
<div
|
||||
className="text-center-onboard mt-3"
|
||||
data-cy={`back-to-${String(authenticationService?.currentUserValue?.organization)
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '-')}`}
|
||||
>
|
||||
back to <Link to="/">{authenticationService?.currentUserValue?.organization}</Link>
|
||||
</div>
|
||||
)}
|
||||
{configs?.form?.enabled && (
|
||||
<>
|
||||
<div className="signin-email-wrap">
|
||||
<label className="tj-text-input-label" data-cy="work-email-label">
|
||||
{this.props.t('loginSignupPage.workEmail', 'Email?')}
|
||||
</label>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name="email"
|
||||
type="email"
|
||||
className="tj-text-input"
|
||||
placeholder={this.props.t('loginSignupPage.enterWorkEmail', 'Enter your email')}
|
||||
style={{ marginBottom: '0px' }}
|
||||
data-cy="work-email-input"
|
||||
autoFocus
|
||||
autoComplete="off"
|
||||
/>
|
||||
{this.state?.emailError && (
|
||||
<span className="tj-text-input-error-state" data-cy="email-error-message">
|
||||
{this.state?.emailError}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label className="tj-text-input-label" data-cy="password-label">
|
||||
{this.props.t('loginSignupPage.password', 'Password')}
|
||||
<span style={{ marginLeft: '4px' }}>
|
||||
<Link
|
||||
to={'/forgot-password'}
|
||||
tabIndex="-1"
|
||||
className="login-forgot-password"
|
||||
style={{ color: this.darkMode && '#3E63DD' }}
|
||||
data-cy="forgot-password-link"
|
||||
>
|
||||
{this.props.t('loginSignupPage.forgot', 'Forgot?')}
|
||||
</Link>
|
||||
</span>
|
||||
</label>
|
||||
<div className="login-password">
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name="password"
|
||||
type={this.state?.showPassword ? 'text' : 'password'}
|
||||
className="tj-text-input"
|
||||
placeholder={this.props.t('loginSignupPage.EnterPassword', 'Enter password')}
|
||||
data-cy="password-input-field"
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="login-password-hide-img"
|
||||
onClick={this.handleOnCheck}
|
||||
data-cy="show-password-icon"
|
||||
>
|
||||
{this.state?.showPassword ? (
|
||||
<EyeHide
|
||||
fill={
|
||||
this.darkMode
|
||||
? this.state?.password?.length
|
||||
? '#D1D5DB'
|
||||
: '#656565'
|
||||
: this.state?.password?.length
|
||||
? '#384151'
|
||||
: '#D1D5DB'
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<EyeShow
|
||||
fill={
|
||||
this.darkMode
|
||||
? this.state?.password?.length
|
||||
? '#D1D5DB'
|
||||
: '#656565'
|
||||
: this.state?.password?.length
|
||||
? '#384151'
|
||||
: '#D1D5DB'
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={` d-flex flex-column align-items-center ${!configs?.form?.enabled ? 'mt-0' : ''}`}
|
||||
>
|
||||
{configs?.form?.enabled && (
|
||||
<ButtonSolid
|
||||
className="login-btn"
|
||||
onClick={this.authUser}
|
||||
disabled={isLoading}
|
||||
data-cy="login-button"
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="spinner-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span> {this.props.t('loginSignupPage.loginTo', 'Login')}</span>
|
||||
<EnterIcon
|
||||
className="enter-icon-onboard"
|
||||
fill={
|
||||
isLoading || !this.state?.email || !this.state?.password
|
||||
? this.darkMode
|
||||
? '#656565'
|
||||
: ' #D1D5DB'
|
||||
: '#fff'
|
||||
}
|
||||
></EnterIcon>
|
||||
</>
|
||||
)}
|
||||
</ButtonSolid>
|
||||
)}
|
||||
{authenticationService?.currentUserValue?.organization && this.organizationId && (
|
||||
<div
|
||||
className="text-center-onboard mt-3"
|
||||
data-cy={`back-to-${String(authenticationService?.currentUserValue?.organization)
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '-')}`}
|
||||
>
|
||||
back to <Link to="/">{authenticationService?.currentUserValue?.organization}</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const LoginPage = withTranslation()(LoginPageComponent);
|
||||
export const LoginPage = withTranslation()(withRouter(LoginPageComponent));
|
||||
|
|
|
|||
|
|
@ -302,6 +302,8 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
|
|||
selectedUsers,
|
||||
} = this.state;
|
||||
|
||||
const searchSelectClass = this.props.darkMode ? 'select-search-dark' : 'select-search';
|
||||
|
||||
const folder_permission = groupPermission
|
||||
? groupPermission.folder_create && groupPermission.folder_delete && groupPermission.folder_update
|
||||
: false;
|
||||
|
|
@ -507,7 +509,23 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
|
|||
<div className="row">
|
||||
<div className="col-6" data-cy="multi-select-search">
|
||||
<MultiSelect
|
||||
className={`${this.props.darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
className={{
|
||||
container: searchSelectClass,
|
||||
value: `${searchSelectClass}__value`,
|
||||
input: `${searchSelectClass}__input`,
|
||||
select: `${searchSelectClass}__select`,
|
||||
options: `${searchSelectClass}__options`,
|
||||
row: `${searchSelectClass}__row`,
|
||||
option: `${searchSelectClass}__option`,
|
||||
group: `${searchSelectClass}__group`,
|
||||
'group-header': `${searchSelectClass}__group-header`,
|
||||
'is-selected': 'is-selected',
|
||||
'is-highlighted': 'is-highlighted',
|
||||
'is-loading': 'is-loading',
|
||||
'is-multiple': 'is-multiple',
|
||||
'has-focus': 'has-focus',
|
||||
'not-found': `${searchSelectClass}__not-found`,
|
||||
}}
|
||||
onSelect={this.setSelectedUsers}
|
||||
onSearch={(query) => this.searchUsersNotInGroup(query, groupPermission.id)}
|
||||
selectedValues={selectedUsers}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { authenticationService, organizationService, organizationUserService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import urlJoin from 'url-join';
|
||||
import ErrorBoundary from '@/Editor/ErrorBoundary';
|
||||
|
|
@ -246,8 +246,6 @@ class ManageOrgUsersComponent extends React.Component {
|
|||
return (
|
||||
<ErrorBoundary showFallback={true}>
|
||||
<div className="wrapper org-users-page animation-fade">
|
||||
<ReactTooltip type="dark" effect="solid" delayShow={250} />
|
||||
|
||||
<div className="page-wrapper">
|
||||
<div className="container-xl">
|
||||
<div className="page-header d-print-none">
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import React from 'react';
|
|||
import { authenticationService, orgEnvironmentVariableService } from '@/_services';
|
||||
import { ConfirmDialog } from '@/_components';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import VariableForm from './VariableForm';
|
||||
import VariablesTable from './VariablesTable';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class ManageOrgVarsComponent extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -236,8 +236,6 @@ class ManageOrgVarsComponent extends React.Component {
|
|||
const { isLoading, showVariableForm, addingVar, variables } = this.state;
|
||||
return (
|
||||
<div className="wrapper org-variables-page animation-fade">
|
||||
<ReactTooltip type="dark" effect="solid" delayShow={250} />
|
||||
|
||||
<ConfirmDialog
|
||||
show={this.state.showVariableDeleteConfirmation}
|
||||
message={this.props.t(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
|
||||
class VariablesTable extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -122,48 +123,56 @@ class VariablesTable extends React.Component {
|
|||
.replace(/\s+/g, '-')}-workspace-variable-update`}
|
||||
>
|
||||
{this.props.canUpdateVariable && (
|
||||
<button
|
||||
className="btn btn-sm btn-org-env"
|
||||
onClick={() => this.props.onEditBtnClicked(variable)}
|
||||
data-cy={`${variable.variable_name
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '-')}-workspace-variable-edit-button`}
|
||||
>
|
||||
<div>
|
||||
<img
|
||||
data-tip="Update"
|
||||
className="svg-icon"
|
||||
src="assets/images/icons/edit.svg"
|
||||
width="15"
|
||||
height="15"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
></img>
|
||||
</div>
|
||||
</button>
|
||||
<>
|
||||
<button
|
||||
className="btn btn-sm btn-org-env"
|
||||
onClick={() => this.props.onEditBtnClicked(variable)}
|
||||
data-cy={`${variable.variable_name
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '-')}-workspace-variable-edit-button`}
|
||||
data-tooltip-id="tooltip-for-update"
|
||||
data-tooltip-content="Update"
|
||||
>
|
||||
<div>
|
||||
<img
|
||||
className="svg-icon"
|
||||
src="assets/images/icons/edit.svg"
|
||||
width="15"
|
||||
height="15"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
></img>
|
||||
</div>
|
||||
</button>
|
||||
<Tooltip id="tooltip-for-update" className="tooltip" />
|
||||
</>
|
||||
)}
|
||||
{this.props.canDeleteVariable && (
|
||||
<button
|
||||
className="btn btn-sm btn-org-env"
|
||||
onClick={() => this.props.onDeleteBtnClicked(variable)}
|
||||
data-cy={`${variable.variable_name
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '-')}-workspace-variable-delete-button`}
|
||||
>
|
||||
<div>
|
||||
<img
|
||||
data-tip="Delete"
|
||||
className="svg-icon"
|
||||
src="assets/images/icons/query-trash-icon.svg"
|
||||
width="15"
|
||||
height="15"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
<>
|
||||
<button
|
||||
className="btn btn-sm btn-org-env"
|
||||
onClick={() => this.props.onDeleteBtnClicked(variable)}
|
||||
data-cy={`${variable.variable_name
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '-')}-workspace-variable-delete-button`}
|
||||
data-tooltip-id="tooltip-for-delete"
|
||||
data-tooltip-content="Delete"
|
||||
>
|
||||
<div>
|
||||
<img
|
||||
className="svg-icon"
|
||||
src="assets/images/icons/query-trash-icon.svg"
|
||||
width="15"
|
||||
height="15"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
<Tooltip id="tooltip-for-delete" className="tooltip" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import { organizationService } from '@/_services';
|
||||
import { Menu } from '@/_components';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { GeneralSettings } from './GeneralSettings';
|
||||
import { Google } from './Google';
|
||||
import { Loader } from './Loader';
|
||||
import { Git } from './Git';
|
||||
import { Form } from './Form';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ErrorBoundary from '@/Editor/ErrorBoundary';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
|
@ -99,7 +99,6 @@ export function ManageSSO({ darkMode }) {
|
|||
return (
|
||||
<ErrorBoundary showFallback={true}>
|
||||
<div className="wrapper manage-sso animation-fade">
|
||||
<ReactTooltip type="dark" effect="solid" delayShow={250} />
|
||||
<div className="page-wrapper">
|
||||
<div className="container-xl">
|
||||
<div className="page-header d-print-none">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
import { authenticationService } from '@/_services';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import Configs from './Configs/Config.json';
|
||||
import { RedirectLoader } from '../_components';
|
||||
|
||||
|
|
@ -59,7 +59,8 @@ export function Authorize() {
|
|||
<div>
|
||||
<RedirectLoader origin={Configs[router.query.origin] ? router.query.origin : 'unknown'} />
|
||||
{(success || error) && (
|
||||
<Redirect
|
||||
<Navigate
|
||||
replace
|
||||
to={{
|
||||
pathname: `/login${error && organizationId ? `/${organizationId}` : ''}`,
|
||||
state: { errorMessage: error && error },
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import queryString from 'query-string';
|
|||
import { datasourceService } from '@/_services';
|
||||
import { RedirectLoader } from '@/_components';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { withRouter } from '@/_hoc/withRouter';
|
||||
class AuthorizeComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -113,4 +114,4 @@ class AuthorizeComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export const Authorize = withTranslation()(AuthorizeComponent);
|
||||
export const Authorize = withTranslation()(withRouter(AuthorizeComponent));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { authenticationService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import OnBoardingInput from './OnBoardingInput';
|
||||
import OnBoardingRadioInput from './OnBoardingRadioInput';
|
||||
import ContinueButton from './ContinueButton';
|
||||
|
|
@ -13,7 +13,7 @@ import LogoDarkMode from '@assets/images/Logomark-dark-mode.svg';
|
|||
|
||||
function OnBoardingForm({ userDetails = {}, token = '', organizationToken = '', password, darkMode }) {
|
||||
const Logo = darkMode ? LogoDarkMode : LogoLightMode;
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const [page, setPage] = useState(0);
|
||||
const [completed, setCompleted] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
|
@ -48,7 +48,7 @@ function OnBoardingForm({ userDetails = {}, token = '', organizationToken = '',
|
|||
authenticationService.updateUser(user);
|
||||
authenticationService.deleteLoginOrganizationId();
|
||||
setIsLoading(false);
|
||||
history.push('/');
|
||||
navigate('/');
|
||||
})
|
||||
.catch((res) => {
|
||||
setIsLoading(false);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { authenticationService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import OnBoardingInput from './OnBoardingInput';
|
||||
import OnBoardingRadioInput from './OnBoardingRadioInput';
|
||||
import AdminSetup from './AdminSetup';
|
||||
|
|
@ -14,7 +14,7 @@ import LogoDarkMode from '@assets/images/Logomark-dark-mode.svg';
|
|||
|
||||
function OnbboardingFromSH({ darkMode }) {
|
||||
const Logo = darkMode ? LogoDarkMode : LogoLightMode;
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const [page, setPage] = useState(0);
|
||||
const [completed, setCompleted] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
|
@ -55,7 +55,7 @@ function OnbboardingFromSH({ darkMode }) {
|
|||
authenticationService.updateUser(user);
|
||||
authenticationService.deleteLoginOrganizationId();
|
||||
setIsLoading(false);
|
||||
history.push('/');
|
||||
navigate('/');
|
||||
})
|
||||
.catch((res) => {
|
||||
setIsLoading(false);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import EyeHide from '../../assets/images/onboardingassets/Icons/EyeHide';
|
|||
import EyeShow from '../../assets/images/onboardingassets/Icons/EyeShow';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
import { withRouter } from '@/_hoc/withRouter';
|
||||
|
||||
class ResetPasswordComponent extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -39,7 +40,7 @@ class ResetPasswordComponent extends React.Component {
|
|||
|
||||
handleClick = (event) => {
|
||||
event.preventDefault();
|
||||
const { token } = this.props.location.state;
|
||||
const { token } = this.props.params;
|
||||
const { password, password_confirmation } = this.state;
|
||||
|
||||
if (password !== password_confirmation) {
|
||||
|
|
@ -232,4 +233,4 @@ class ResetPasswordComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export const ResetPassword = withTranslation()(ResetPasswordComponent);
|
||||
export const ResetPassword = withTranslation()(withRouter(ResetPasswordComponent));
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { withTranslation } from 'react-i18next';
|
|||
import { ShowLoading } from '@/_components';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
import SignupStatusCard from '../OnBoardingForm/SignupStatusCard';
|
||||
import { withRouter } from '@/_hoc/withRouter';
|
||||
class SignupPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -46,7 +47,7 @@ class SignupPageComponent extends React.Component {
|
|||
if (response.data.statusCode !== 404) {
|
||||
this.setState({ isGettingConfigs: false });
|
||||
} else {
|
||||
return this.props.history.push('/setup');
|
||||
return this.props.navigate('/setup');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -330,4 +331,4 @@ class SignupPageComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export const SignupPage = withTranslation()(SignupPageComponent);
|
||||
export const SignupPage = withTranslation()(withRouter(SignupPageComponent));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import { ButtonSolid } from '@/_components/AppButton';
|
||||
export const ForgotPasswordInfoScreen = function ForgotPasswordInfoScreen({ props, email, darkMode }) {
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
export const ForgotPasswordInfoScreen = function ForgotPasswordInfoScreen({ email, darkMode }) {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<div className="info-screen-wrapper">
|
||||
<div className="forget-password-info-card">
|
||||
|
|
@ -34,7 +36,7 @@ export const ForgotPasswordInfoScreen = function ForgotPasswordInfoScreen({ prop
|
|||
<ButtonSolid
|
||||
variant="secondary"
|
||||
className="forgot-password-info-btn"
|
||||
onClick={() => props.history.push('/login')}
|
||||
onClick={() => navigate('/login')}
|
||||
data-cy="back-to-login-button"
|
||||
>
|
||||
Back to log in
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import { ButtonSolid } from '@/_components/AppButton';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export const LinkExpiredInfoScreen = function LinkExpiredInfoScreen({ show = true }) {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
return (
|
||||
|
|
@ -30,7 +30,7 @@ export const LinkExpiredInfoScreen = function LinkExpiredInfoScreen({ show = tru
|
|||
<ButtonSolid
|
||||
variant="secondary"
|
||||
className="link-expired-info-btn"
|
||||
onClick={() => history.push('/signup')}
|
||||
onClick={() => navigate('/signup')}
|
||||
data-cy="back-to-signup-button"
|
||||
>
|
||||
Back to signup
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import { ButtonSolid } from '@/_components/AppButton';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export const PasswordResetinfoScreen = function PasswordResetinfoScreen({ props, darkMode }) {
|
||||
export const PasswordResetinfoScreen = function PasswordResetinfoScreen({ darkMode }) {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<div className="info-screen-wrapper">
|
||||
<div className="password-reset-card">
|
||||
|
|
@ -24,7 +26,7 @@ export const PasswordResetinfoScreen = function PasswordResetinfoScreen({ props,
|
|||
</p>
|
||||
<ButtonSolid
|
||||
variant="secondary"
|
||||
onClick={() => props.history.push('/login')}
|
||||
onClick={() => navigate('/login')}
|
||||
className="reset-password-info-btn"
|
||||
data-cy="back-to-login-button"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import GoogleSSOLoginButton from '@ee/components/LoginPage/GoogleSSOLoginButton'
|
|||
import GitSSOLoginButton from '@ee/components/LoginPage/GitSSOLoginButton';
|
||||
import OnBoardingForm from '../OnBoardingForm/OnBoardingForm';
|
||||
import { authenticationService } from '@/_services';
|
||||
import { useLocation, useHistory } from 'react-router-dom';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { LinkExpiredInfoScreen } from '@/SuccessInfoScreen';
|
||||
import { ShowLoading } from '@/_components';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
|
@ -30,17 +30,18 @@ export const VerificationSuccessInfoScreen = function VerificationSuccessInfoScr
|
|||
const { t } = useTranslation();
|
||||
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const params = useParams();
|
||||
|
||||
const organizationId = new URLSearchParams(location?.state?.search).get('oid');
|
||||
const organizationId = new URLSearchParams(location?.search).get('oid');
|
||||
const single_organization = window.public_config?.DISABLE_MULTI_WORKSPACE === 'true';
|
||||
const source = new URLSearchParams(location?.state?.search).get('source');
|
||||
const source = new URLSearchParams(location?.search).get('source');
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
const getUserDetails = () => {
|
||||
setIsLoading(true);
|
||||
authenticationService
|
||||
.verifyToken(location?.state?.token, location?.state?.organizationToken)
|
||||
.verifyToken(params?.token, params?.organizationToken)
|
||||
.then((data) => {
|
||||
if (data?.redirect_url) {
|
||||
window.location.href = buildURLWithQuery(data.redirect_url, {
|
||||
|
|
@ -52,7 +53,7 @@ export const VerificationSuccessInfoScreen = function VerificationSuccessInfoScr
|
|||
setUserDetails(data);
|
||||
setIsLoading(false);
|
||||
if (data?.email !== '') {
|
||||
if (location?.state?.organizationToken) {
|
||||
if (params?.organizationToken) {
|
||||
setShowJoinWorkspace(true);
|
||||
return;
|
||||
}
|
||||
|
|
@ -122,8 +123,8 @@ export const VerificationSuccessInfoScreen = function VerificationSuccessInfoScr
|
|||
companyName: '',
|
||||
companySize: '',
|
||||
role: '',
|
||||
token: location?.state?.token,
|
||||
organizationToken: location?.state?.organizationToken ?? '',
|
||||
token: params?.token,
|
||||
organizationToken: params?.organizationToken ?? '',
|
||||
source: source,
|
||||
password: password,
|
||||
})
|
||||
|
|
@ -131,7 +132,7 @@ export const VerificationSuccessInfoScreen = function VerificationSuccessInfoScr
|
|||
authenticationService.updateUser(user);
|
||||
authenticationService.deleteLoginOrganizationId();
|
||||
setIsLoading(false);
|
||||
history.push('/');
|
||||
navigate('/');
|
||||
})
|
||||
.catch((res) => {
|
||||
setIsLoading(false);
|
||||
|
|
@ -381,8 +382,8 @@ export const VerificationSuccessInfoScreen = function VerificationSuccessInfoScr
|
|||
{verifiedToken && showOnboarding && (
|
||||
<OnBoardingForm
|
||||
userDetails={userDetails}
|
||||
token={location?.state?.token}
|
||||
organizationToken={location?.state?.organizationToken ?? ''}
|
||||
token={params?.token}
|
||||
organizationToken={params?.organizationToken ?? ''}
|
||||
password={password}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const Filter = ({ filters, setFilters, handleBuildFilterQuery, resetFilterQuery
|
|||
|
||||
const popover = (
|
||||
<Popover id="storage-filter-popover" className={cx({ 'theme-dark': darkMode })} data-cy="filter-section">
|
||||
<Popover.Content bsPrefix="storage-filter-popover">
|
||||
<Popover.Body bsPrefix="storage-filter-popover">
|
||||
<div className="card-body" data-cy="filter-card-body">
|
||||
{Object.values(filters).map((filter, index) => {
|
||||
return (
|
||||
|
|
@ -47,7 +47,7 @@ const Filter = ({ filters, setFilters, handleBuildFilterQuery, resetFilterQuery
|
|||
</svg>
|
||||
Add Condition
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ const Sort = ({ filters, setFilters, handleBuildSortQuery, resetSortQuery }) =>
|
|||
|
||||
const popover = (
|
||||
<Popover id="storage-filter-popover" className={cx({ 'theme-dark': darkMode })} data-cy="sort-section">
|
||||
<Popover.Content bsPrefix="storage-filter-popover">
|
||||
<Popover.Body bsPrefix="storage-filter-popover">
|
||||
<div className="card-body" data-cy="sort-card-body">
|
||||
{Object.values(filters).map((filter, index) => {
|
||||
return (
|
||||
|
|
@ -68,7 +68,7 @@ const Sort = ({ filters, setFilters, handleBuildSortQuery, resetSortQuery }) =>
|
|||
</svg>
|
||||
Add another
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export const TablePopover = ({ disabled, children, onEdit, onDelete }) => {
|
|||
if (disabled) return children;
|
||||
const popover = (
|
||||
<Popover>
|
||||
<Popover.Content>
|
||||
<Popover.Body>
|
||||
{/* <div className="w-min-100 row list-group-item-action cursor-pointer">
|
||||
<div className="col-auto">
|
||||
<EditIcon />
|
||||
|
|
@ -26,7 +26,7 @@ export const TablePopover = ({ disabled, children, onEdit, onDelete }) => {
|
|||
Delete
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const ListItemPopover = ({ onEdit, onDelete, darkMode }) => {
|
|||
|
||||
const popover = (
|
||||
<Popover id="popover-contained" className="table-list-items">
|
||||
<Popover.Content className={`${darkMode && 'theme-dark'}`}>
|
||||
<Popover.Body className={`${darkMode && 'theme-dark'}`}>
|
||||
<div className={`row cursor-pointer`}>
|
||||
<div className="col-auto" data-cy="edit-option-icon">
|
||||
<EditIcon />
|
||||
|
|
@ -42,7 +42,7 @@ export const ListItemPopover = ({ onEdit, onDelete, darkMode }) => {
|
|||
Delete
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
|
|
@ -62,8 +62,11 @@ export const ListItemPopover = ({ onEdit, onDelete, darkMode }) => {
|
|||
trigger="click"
|
||||
placement="bottom"
|
||||
overlay={popover}
|
||||
transition={false}
|
||||
>
|
||||
<EllipsisIcon />
|
||||
<span>
|
||||
<EllipsisIcon />
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { FilterPreview } from '@/_components';
|
||||
import PropTypes from 'prop-types';
|
||||
import Select, { fuzzySearch } from 'react-select-search';
|
||||
import Select from 'react-select-search';
|
||||
import '@/_styles/widgets/multi-select.scss';
|
||||
|
||||
function MultiSelect({
|
||||
|
|
@ -10,18 +10,18 @@ function MultiSelect({
|
|||
selectedValues = [],
|
||||
onReset,
|
||||
placeholder = 'Select',
|
||||
options,
|
||||
isLoading,
|
||||
className,
|
||||
searchLabel,
|
||||
}) {
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [filteredOptions, setOptions] = useState([]);
|
||||
const listOfOptions = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
options && setOptions(filterOptions(options));
|
||||
setOptions(filterOptions(listOfOptions.current));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [options, selectedValues]);
|
||||
}, [selectedValues, listOfOptions.current]);
|
||||
|
||||
const searchFunction = useCallback(
|
||||
async (query) => {
|
||||
|
|
@ -30,7 +30,8 @@ function MultiSelect({
|
|||
return [];
|
||||
}
|
||||
const options = await onSearch(query);
|
||||
return filterOptions(options);
|
||||
listOfOptions.current = filterOptions(options);
|
||||
return listOfOptions.current;
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[setSearchText, onSearch, selectedValues]
|
||||
|
|
@ -49,7 +50,7 @@ function MultiSelect({
|
|||
<Select
|
||||
className={className}
|
||||
getOptions={onSearch ? searchFunction : undefined}
|
||||
options={onSearch ? [] : filteredOptions}
|
||||
options={filteredOptions}
|
||||
closeOnSelect={false}
|
||||
search={true}
|
||||
multiple
|
||||
|
|
@ -59,7 +60,7 @@ function MultiSelect({
|
|||
debounce={onSearch ? 300 : undefined}
|
||||
printOptions="on-focus"
|
||||
emptyMessage={
|
||||
options?.length > 0
|
||||
filteredOptions?.length > 0
|
||||
? 'Not Found'
|
||||
: searchText
|
||||
? 'Not found'
|
||||
|
|
@ -68,7 +69,7 @@ function MultiSelect({
|
|||
: 'Please enter some text'
|
||||
}
|
||||
disabled={isLoading}
|
||||
filterOptions={fuzzySearch}
|
||||
fuzzySearch
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ const Modal = ({ children, handleClose, portalStyles, styles, componentName, dar
|
|||
type="button"
|
||||
className="btn mx-2 btn-light"
|
||||
onClick={handleClose}
|
||||
data-tip="Hide code editor modal"
|
||||
style={{ backgroundColor: darkMode && '#42546a' }}
|
||||
>
|
||||
<img
|
||||
|
|
|
|||
|
|
@ -1,61 +1,46 @@
|
|||
import React from 'react';
|
||||
import { Route, Redirect } from 'react-router-dom';
|
||||
import { Navigate, useLocation } from 'react-router-dom';
|
||||
import { authenticationService } from '@/_services';
|
||||
|
||||
export const PrivateRoute = ({ component: Component, switchDarkMode, darkMode, ...rest }) => (
|
||||
<Route
|
||||
{...rest}
|
||||
render={(props) => {
|
||||
const currentUser = authenticationService.currentUserValue;
|
||||
if (!currentUser && !props.location.pathname.startsWith('/applications/')) {
|
||||
// not logged in so redirect to login page with the return url
|
||||
return (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/login',
|
||||
search: `?redirectTo=${props.location.pathname}`,
|
||||
state: { from: props.location },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export const PrivateRoute = ({ children }) => {
|
||||
const location = useLocation();
|
||||
const currentUser = authenticationService.currentUserValue;
|
||||
return !currentUser && !location.pathname.startsWith('/applications/') ? (
|
||||
<Navigate
|
||||
to={{
|
||||
pathname: '/login',
|
||||
search: `?redirectTo=${location.pathname}`,
|
||||
state: { from: location },
|
||||
}}
|
||||
replace
|
||||
/>
|
||||
) : (
|
||||
children
|
||||
);
|
||||
};
|
||||
|
||||
// authorised so return component
|
||||
return <Component {...props} switchDarkMode={switchDarkMode} darkMode={darkMode} />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export const AdminRoute = ({ component: Component, switchDarkMode, darkMode, ...rest }) => (
|
||||
<Route
|
||||
{...rest}
|
||||
render={(props) => {
|
||||
const currentUser = authenticationService.currentUserValue;
|
||||
if (!currentUser && !props.location.pathname.startsWith('/applications/')) {
|
||||
return (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/login',
|
||||
search: `?redirectTo=${props.location.pathname}`,
|
||||
state: { from: props.location },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!currentUser?.admin) {
|
||||
return (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/',
|
||||
search: `?redirectTo=${props.location.pathname}`,
|
||||
state: { from: props.location },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <Component {...props} switchDarkMode={switchDarkMode} darkMode={darkMode} />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
export const AdminRoute = ({ children }) => {
|
||||
const location = useLocation();
|
||||
const currentUser = authenticationService.currentUserValue;
|
||||
return !currentUser && !location.pathname.startsWith('/applications/') ? (
|
||||
<Navigate
|
||||
to={{
|
||||
pathname: '/login',
|
||||
search: `?redirectTo=${location.pathname}`,
|
||||
state: { from: location },
|
||||
}}
|
||||
replace
|
||||
/>
|
||||
) : !currentUser?.admin ? (
|
||||
<Navigate
|
||||
to={{
|
||||
pathname: '/',
|
||||
search: `?redirectTo=${location.pathname}`,
|
||||
state: { from: location },
|
||||
}}
|
||||
replace
|
||||
/>
|
||||
) : (
|
||||
children
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { authenticationService } from '@/_services';
|
||||
import { history } from '@/_helpers';
|
||||
import Avatar from '@/_ui/Avatar';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
|
@ -10,10 +9,11 @@ import { ToolTip } from '@/_components/ToolTip';
|
|||
export const Profile = function Header({ switchDarkMode, darkMode }) {
|
||||
const { first_name, last_name, avatar_id } = authenticationService.currentUserValue;
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
function logout() {
|
||||
authenticationService.logout();
|
||||
history.push('/login');
|
||||
navigate('/login');
|
||||
}
|
||||
|
||||
const getOverlay = () => {
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ function showModal(_ref, modal, show) {
|
|||
|
||||
function logoutAction(_ref) {
|
||||
localStorage.clear();
|
||||
_ref.props.history.push('/login');
|
||||
_ref.props.navigate('/login');
|
||||
window.location.href = '/login';
|
||||
|
||||
return Promise.resolve();
|
||||
|
|
@ -407,8 +407,7 @@ export const executeAction = (_ref, event, mode, customVariables) => {
|
|||
}
|
||||
|
||||
if (mode === 'view') {
|
||||
_ref.props.history.push(url);
|
||||
_ref.props.history.go();
|
||||
_ref.props.navigate(url);
|
||||
} else {
|
||||
if (confirm('The app will be opened in a new tab as the action is triggered from the editor.')) {
|
||||
window.open(urlJoin(window.public_config?.TOOLJET_HOST, url));
|
||||
|
|
@ -870,6 +869,7 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode =
|
|||
const options = getQueryVariables(dataQuery.options, _ref.state.currentState);
|
||||
|
||||
if (dataQuery.options.requestConfirmation) {
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
const queryConfirmationList = _ref.state?.queryConfirmationList ? [..._ref.state?.queryConfirmationList] : [];
|
||||
const queryConfirmation = {
|
||||
queryId,
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
import { createBrowserHistory } from 'history';
|
||||
|
||||
export const history = createBrowserHistory({
|
||||
basename: window.public_config?.SUB_PATH || '/',
|
||||
});
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
export * from './auth-header';
|
||||
export * from './handle-response';
|
||||
export * from './history';
|
||||
export * from './cookie';
|
||||
|
|
|
|||
10
frontend/src/_hoc/withRouter.jsx
Normal file
10
frontend/src/_hoc/withRouter.jsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
|
||||
export const withRouter = (WrappedComponent) => (props) => {
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return <WrappedComponent {...props} params={params} location={location} navigate={navigate} />;
|
||||
};
|
||||
|
|
@ -1,19 +1,16 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useParams, useLocation, useHistory, useRouteMatch } from 'react-router-dom';
|
||||
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
import queryString from 'query-string';
|
||||
|
||||
export default function useRouter() {
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
const match = useRouteMatch();
|
||||
const history = useNavigate();
|
||||
// Return our custom router object
|
||||
// Memoize so that a new object is only returned if something changes
|
||||
return useMemo(() => {
|
||||
return {
|
||||
// For convenience add push(), replace(), pathname at top level
|
||||
push: history.push,
|
||||
replace: history.replace,
|
||||
pathname: location.pathname,
|
||||
// Merge params and parsed query string into single 'query' object
|
||||
// so that they can be used interchangeably.
|
||||
|
|
@ -24,9 +21,8 @@ export default function useRouter() {
|
|||
},
|
||||
// Include match, location, history objects so we have
|
||||
// access to extra React Router functionality if needed.
|
||||
match,
|
||||
location,
|
||||
history,
|
||||
};
|
||||
}, [params, match, location, history]);
|
||||
}, [params, location, history]);
|
||||
}
|
||||
|
|
|
|||
180
frontend/src/_styles/editor/react-select-search.scss
Normal file
180
frontend/src/_styles/editor/react-select-search.scss
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
|
||||
/**
|
||||
* Main wrapper
|
||||
*/
|
||||
@import "../colors.scss";
|
||||
.select-search-container {
|
||||
--select-search-background: #fff;
|
||||
--select-search-border: #dadcde;
|
||||
--select-search-selected: #dadcde;
|
||||
--select-search-text: 232e3c;
|
||||
--select-search-subtle-text: #6c6f85;
|
||||
--select-search-inverted-text: var(--select-search-background);
|
||||
--select-search-highlight: #F8FAFF;
|
||||
--select-search-font: "Roboto", sans-serif;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
font-family: var(--select-search-font);
|
||||
color: var(--select-search-text);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.main-wrapper .select-search-container {
|
||||
--select-search-background: #fff;
|
||||
--select-search-border: #dce0e8;
|
||||
--select-search-selected: #1e66f5;
|
||||
--select-search-text: #000;
|
||||
--select-search-subtle-text: #6c6f85;
|
||||
--select-search-highlight: #eff1f5;
|
||||
}
|
||||
|
||||
.popover.popover-dark-themed .select-search-container,
|
||||
.main-wrapper.theme-dark .select-search-container {
|
||||
--select-search-background: #272822;
|
||||
--select-search-border: #333c48;
|
||||
--select-search-selected: #89b4fa;
|
||||
--select-search-text: #fff;
|
||||
--select-search-subtle-text: #a6adc8;
|
||||
--select-search-highlight: #1e1e2e;
|
||||
}
|
||||
|
||||
.select-search-container *,
|
||||
.select-search-container *::after,
|
||||
.select-search-container *::before {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
.select-search-input {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background: var(--select-search-background);
|
||||
border: 1px solid var(--select-search-border);
|
||||
color: var(--select-search-text);
|
||||
border-radius: 4px !important;
|
||||
outline: none;
|
||||
padding: 0.4375rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.4285714;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
letter-spacing: 0.01rem;
|
||||
-webkit-appearance: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.select-search-is-multiple .select-search-input {
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.select-search-is-multiple .select-search-input {
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.select-search-input::-webkit-search-decoration,
|
||||
.select-search-input::-webkit-search-cancel-button,
|
||||
.select-search-input::-webkit-search-results-button,
|
||||
.select-search-input::-webkit-search-results-decoration {
|
||||
-webkit-appearance:none;
|
||||
}
|
||||
|
||||
.select-search-input[readonly] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.select-search-is-disabled .select-search-input {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.select-search-container:not(.select-search-is-disabled).select-search-has-focus .select-search-input,
|
||||
.select-search-container:not(.select-search-is-disabled) .select-search-input:hover {
|
||||
border-color: var(--select-search-selected);
|
||||
}
|
||||
|
||||
.select-search-select {
|
||||
background: var(--select-search-background);
|
||||
border: 1px solid $color-dark-indigo-09;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 0 1px #0000001a, 0 4px 11px #0000001a;
|
||||
overflow: auto;
|
||||
max-height: 360px;
|
||||
}
|
||||
|
||||
.select-search-container:not(.select-search-is-multiple) .select-search-select {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 44px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
border-radius: 4px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.select-search-container:not(.select-search-is-multiple).select-search-has-focus .select-search-select {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.select-search-has-focus .select-search-select {
|
||||
border-color: var(--select-search-selected);
|
||||
}
|
||||
|
||||
.select-search-options {
|
||||
list-style: none;
|
||||
padding-left: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.select-search-option,
|
||||
.select-search-not-found {
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
width: 100%;
|
||||
background: var(--select-search-background);
|
||||
border: none;
|
||||
outline: none;
|
||||
font-family: var(--select-search-font);
|
||||
color: var(--select-search-text);
|
||||
font-size: 0.875rem;
|
||||
text-align: left;
|
||||
letter-spacing: 0.01rem;
|
||||
cursor: pointer;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.select-search-option:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.select-search-is-highlighted,
|
||||
.select-search-option:not(.select-search-is-selected):hover {
|
||||
background: var(--select-search-highlight);
|
||||
}
|
||||
|
||||
.select-search-is-selected {
|
||||
font-weight: bold;
|
||||
color: var(--select-search-selected);
|
||||
}
|
||||
|
||||
.select-search-group-header {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
background: var(--select-search-border);
|
||||
color: var(--select-search-subtle-text);
|
||||
letter-spacing: 0.1rem;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.select-search-row:not(:first-child) .select-search-group-header {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.select-search-row:not(:last-child) .select-search-group-header {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.select-search-row button{
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
|
@ -6412,15 +6412,11 @@ tbody {
|
|||
}
|
||||
|
||||
.dropdown-table-column-hide-common {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
right: 0;
|
||||
border-radius: 3px;
|
||||
height: auto;
|
||||
overflow-y: scroll;
|
||||
padding: 8px 16px;
|
||||
width: 20rem;
|
||||
top: 20px;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
|
|
@ -7457,6 +7453,9 @@ tbody {
|
|||
}
|
||||
}
|
||||
|
||||
.react-tooltip {
|
||||
font-size: .765625rem !important;
|
||||
}
|
||||
.tj-db-table {
|
||||
overflow-y: auto;
|
||||
height: 110px;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,15 @@
|
|||
border: 1px solid #dadcde;
|
||||
border-radius: 4px;
|
||||
|
||||
.select-search__select, .select-search-dark__select{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.has-focus>.select-search__select,
|
||||
.has-focus>.select-search-dark__select {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.select-search-dark.is-loading .select-search-dark__value::after {
|
||||
background-size: 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import React from 'react';
|
||||
import { userService } from '@/_services';
|
||||
import cx from 'classnames';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const Avatar = ({ text, image, avatarId, title = '', borderColor = '', borderShape }) => {
|
||||
const Avatar = ({ text, image, avatarId, title = '', borderColor = '', borderShape, indexId = 0 }) => {
|
||||
const formattedTitle = String(title).toLowerCase().replace(/\s+/g, '-');
|
||||
const [avatar, setAvatar] = React.useState();
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
@ -19,7 +21,8 @@ const Avatar = ({ text, image, avatarId, title = '', borderColor = '', borderSha
|
|||
|
||||
return (
|
||||
<span
|
||||
data-tip={title}
|
||||
data-tooltip-id={`tooltip-for-avatar-${formattedTitle}-${indexId}`}
|
||||
data-tooltip-content={title}
|
||||
style={{
|
||||
// border: borderColor ? `1.5px solid ${borderColor}` : 'none',
|
||||
...(image || avatar ? { backgroundImage: `url(${avatar ?? image})` } : {}),
|
||||
|
|
@ -31,6 +34,7 @@ const Avatar = ({ text, image, avatarId, title = '', borderColor = '', borderSha
|
|||
data-cy="avatar-image"
|
||||
>
|
||||
{!image && !avatarId && text}
|
||||
<Tooltip id={`tooltip-for-avatar-${formattedTitle}-${indexId}`} className="tooltip" />
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
// todo: legacy package, remove this and upgrade to react-router-dom v6 (https://reactrouter.com/en/main/upgrading/v5)
|
||||
// v6 has an official way to support breadcrumbs https://reactrouter.com/en/main/hooks/use-matches#breadcrumbs
|
||||
import withBreadcrumbs from 'react-router-breadcrumbs-hoc';
|
||||
import useBreadcrumbs from 'use-react-router-breadcrumbs';
|
||||
|
||||
const Breadcrumbs = ({ breadcrumbs }) => {
|
||||
export const Breadcrumbs = () => {
|
||||
const breadcrumbs = useBreadcrumbs(routes, { excludePaths: ['/'] });
|
||||
return (
|
||||
<ol className="breadcrumb breadcrumb-arrows">
|
||||
{breadcrumbs.length === 0 && (
|
||||
|
|
@ -29,5 +28,3 @@ const routes = [
|
|||
{ path: '/database', breadcrumb: 'Tables', dataCy: 'tables-page-header' },
|
||||
{ path: '/workspace-settings', breadcrumb: 'Workspace settings' },
|
||||
];
|
||||
|
||||
export default withBreadcrumbs(routes, { excludePaths: ['/'] })(Breadcrumbs);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import Breadcrumbs from '../Breadcrumbs';
|
||||
import { Breadcrumbs } from '../Breadcrumbs';
|
||||
import { OrganizationList } from '@/_components/OrganizationManager/List';
|
||||
|
||||
function Header() {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const PopoverComponent = ({
|
|||
const computeStyle = () => {
|
||||
if (popoverContentHeight) {
|
||||
return {
|
||||
height: `${popoverContentHeight}vh`,
|
||||
height: popoverContentHeight === 'auto' ? 'auto' : `${popoverContentHeight}vh`,
|
||||
overflow: 'auto',
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ export const SearchBox = ({ onChange, ...restProps }) => {
|
|||
}
|
||||
|
||||
return () => {
|
||||
document.querySelector('.searchbox-wrapper .input-icon .form-control:not(:first-child)').style.paddingLeft =
|
||||
'2.5rem';
|
||||
if (document.querySelector('.searchbox-wrapper .input-icon .form-control:not(:first-child)')) {
|
||||
document.querySelector('.searchbox-wrapper .input-icon .form-control:not(:first-child)').style.paddingLeft =
|
||||
'2.5rem';
|
||||
}
|
||||
};
|
||||
}, [searchText]);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export default function styles(darkMode, width = 224, height = 32, styles = {})
|
|||
}),
|
||||
valueContainer: (provided, state) => ({
|
||||
...provided,
|
||||
display: 'flex',
|
||||
height: height,
|
||||
marginBottom: '4px',
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Integrations } from '@sentry/tracing';
|
||||
import { createBrowserHistory } from 'history';
|
||||
import { useLocation, useNavigationType, createRoutesFromChildren, matchRoutes } from 'react-router-dom';
|
||||
import { BrowserTracing } from '@sentry/tracing';
|
||||
import { appService } from '@/_services';
|
||||
import { App } from './App';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
|
|
@ -33,7 +33,6 @@ appService
|
|||
});
|
||||
|
||||
if (window.public_config.APM_VENDOR === 'sentry') {
|
||||
const history = createBrowserHistory();
|
||||
const tooljetServerUrl = window.public_config.TOOLJET_SERVER_URL;
|
||||
const tracingOrigins = ['localhost', /^\//];
|
||||
const releaseVersion = window.public_config.RELEASE_VERSION
|
||||
|
|
@ -47,8 +46,14 @@ appService
|
|||
debug: !!window.public_config.SENTRY_DEBUG,
|
||||
release: releaseVersion,
|
||||
integrations: [
|
||||
new Integrations.BrowserTracing({
|
||||
routingInstrumentation: Sentry.reactRouterV5Instrumentation(history),
|
||||
new BrowserTracing({
|
||||
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
|
||||
React.useEffect,
|
||||
useLocation,
|
||||
useNavigationType,
|
||||
createRoutesFromChildren,
|
||||
matchRoutes
|
||||
),
|
||||
tracingOrigins: tracingOrigins,
|
||||
}),
|
||||
],
|
||||
|
|
@ -56,4 +61,4 @@ appService
|
|||
});
|
||||
}
|
||||
})
|
||||
.then(() => render(<AppWithProfiler />, document.getElementById('app')));
|
||||
.then(() => createRoot(document.getElementById('app')).render(<AppWithProfiler />));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const webpack = require('webpack');
|
|||
const path = require('path');
|
||||
const CompressionPlugin = require('compression-webpack-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const hash = require('string-hash');
|
||||
|
||||
const environment = process.env.NODE_ENV === 'production' ? 'production' : 'development';
|
||||
|
||||
|
|
@ -72,14 +73,21 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
use: [
|
||||
{
|
||||
loader: '@svgr/webpack',
|
||||
options: {
|
||||
limit: 10000,
|
||||
use: ({ resource }) => ({
|
||||
loader: '@svgr/webpack',
|
||||
options: {
|
||||
svgoConfig: {
|
||||
plugins: [
|
||||
{
|
||||
name: 'prefixIds',
|
||||
cleanupIDs: {
|
||||
prefix: `svg-${hash(resource)}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
|
|
|
|||
2477
package-lock.json
generated
2477
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -19,9 +19,9 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@tooljet/cli": "^0.0.13",
|
||||
"eslint": "^7.25.0",
|
||||
"husky": "^7.0.2",
|
||||
"lint-staged": "^11.2.3"
|
||||
"eslint": "^8.34.0",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild:plugins": "npm run install:plugins",
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ export class AppsService {
|
|||
});
|
||||
|
||||
if (appVersion?.dataQueries) {
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
for (const query of appVersion?.dataQueries) {
|
||||
if (query?.plugin) {
|
||||
query.plugin.manifestFile.data = JSON.parse(decode(query.plugin.manifestFile.data.toString('utf8')));
|
||||
|
|
|
|||
Loading…
Reference in a new issue