From 0c6f3545f456a90281b1a7c9923d59abd46cf517 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 23 Jan 2023 10:51:01 +0100 Subject: [PATCH 1/2] change(ui) - tooltips for ee features --- frontend/app/components/Assist/Assist.tsx | 16 +- .../AssistActions/AssistActions.tsx | 2 +- .../DashboardOptions/DashboardOptions.tsx | 4 +- .../MetricTypeItem/MetricTypeItem.tsx | 36 ++-- .../MetricTypeList/MetricTypeList.tsx | 35 +++- frontend/app/components/Login/Login.js | 161 +++++++++++------- .../ScreenRecorder/ScreenRecorder.tsx | 7 +- .../app/components/ui/ItemMenu/ItemMenu.tsx | 37 ++-- .../ui/SideMenuitem/SideMenuitem.js | 5 +- frontend/app/constants/card.ts | 36 ++-- frontend/app/mstore/types/widget.ts | 2 + 11 files changed, 203 insertions(+), 138 deletions(-) diff --git a/frontend/app/components/Assist/Assist.tsx b/frontend/app/components/Assist/Assist.tsx index d46dcbdd0..abb1403a9 100644 --- a/frontend/app/components/Assist/Assist.tsx +++ b/frontend/app/components/Assist/Assist.tsx @@ -21,7 +21,7 @@ function Assist(props: Props) { const redirect = (path: string) => { history.push(withSiteId(path, siteId)); }; - if (isEnterprise) { + // if (isEnterprise) { return (
@@ -39,6 +39,8 @@ function Assist(props: Props) { title="Recordings" iconName="record-circle" onClick={() => redirect(recordings())} + disabled={!isEnterprise} + tooltipTitle="This feature requires an enterprise license." />
@@ -47,13 +49,13 @@ function Assist(props: Props) {
); - } + // } - return ( -
- -
- ) + // return ( + //
+ // + //
+ // ) } const Cont = connect((state: any) => ({ diff --git a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx index c26c9427b..18c717cf4 100644 --- a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx +++ b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx @@ -181,7 +181,7 @@ function AssistActions({ )} {/* @ts-ignore wtf? */} - {isEnterprise ? : null} +
{/* @ts-ignore */} diff --git a/frontend/app/components/Dashboard/components/DashboardOptions/DashboardOptions.tsx b/frontend/app/components/Dashboard/components/DashboardOptions/DashboardOptions.tsx index b600efb69..62cfd9404 100644 --- a/frontend/app/components/Dashboard/components/DashboardOptions/DashboardOptions.tsx +++ b/frontend/app/components/Dashboard/components/DashboardOptions/DashboardOptions.tsx @@ -16,10 +16,8 @@ function DashboardOptions(props: Props) { { icon: 'text-paragraph', text: `${!isTitlePresent ? 'Add' : 'Edit'} Description`, onClick: () => editHandler(false) }, { icon: 'users', text: 'Visibility & Access', onClick: editHandler }, { icon: 'trash', text: 'Delete', onClick: deleteHandler }, + { icon: 'pdf-download', text: 'Download Report', onClick: renderReport, disabled: !isEnterprise, tooltipTitle: 'This feature requires an enterprise license.' } ] - if (isEnterprise) { - menuItems.unshift({ icon: 'pdf-download', text: 'Download Report', onClick: renderReport }); - } return ( {}, } = props; return ( -
-
- {/* @ts-ignore */} - + +
+
+ {/* @ts-ignore */} + +
+
+
{title}
+
{description}
+
-
-
{title}
-
{description}
-
-
+ ); } diff --git a/frontend/app/components/Dashboard/components/MetricTypeList/MetricTypeList.tsx b/frontend/app/components/Dashboard/components/MetricTypeList/MetricTypeList.tsx index 46c540d07..e033396c4 100644 --- a/frontend/app/components/Dashboard/components/MetricTypeList/MetricTypeList.tsx +++ b/frontend/app/components/Dashboard/components/MetricTypeList/MetricTypeList.tsx @@ -2,27 +2,44 @@ import { useModal } from 'App/components/Modal'; import React from 'react'; import MetricsLibraryModal from '../MetricsLibraryModal'; import MetricTypeItem, { MetricType } from '../MetricTypeItem/MetricTypeItem'; -import { TYPES, LIBRARY } from 'App/constants/card'; +import { TYPES, LIBRARY, INSIGHTS } from 'App/constants/card'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { dashboardMetricCreate, withSiteId } from 'App/routes'; import { useStore } from 'App/mstore'; +import { connect } from 'react-redux'; interface Props extends RouteComponentProps { dashboardId: number; siteId: string; + isEnterprise: boolean; } function MetricTypeList(props: Props) { - const { dashboardId, siteId, history } = props; + const { dashboardId, siteId, history, isEnterprise } = props; const { metricStore } = useStore(); const { hideModal } = useModal(); + const list = React.useMemo(() => { + return TYPES.map((metric: MetricType) => { + const disabled = metric.slug === INSIGHTS && !isEnterprise; + return { + ...metric, + disabled: metric.slug === INSIGHTS && !isEnterprise, + tooltipTitle: disabled ? 'This feature requires an enterprise license.' : '', + }; + }); + }, []); + const { showModal } = useModal(); const onClick = ({ slug }: MetricType) => { hideModal(); if (slug === LIBRARY) { - return showModal(, { right: true, width: 800, onClose: () => { - metricStore.updateKey('metricsSearch', '') - } }); + return showModal(, { + right: true, + width: 800, + onClose: () => { + metricStore.updateKey('metricsSearch', ''); + }, + }); } // TODO redirect to card builder with metricType query param @@ -30,17 +47,19 @@ function MetricTypeList(props: Props) { const queryString = new URLSearchParams({ type: slug }).toString(); history.push({ pathname: path, - search: `?${queryString}` + search: `?${queryString}`, }); }; return ( <> - {TYPES.map((metric: MetricType) => ( + {list.map((metric: MetricType) => ( onClick(metric)} /> ))} ); } -export default withRouter(MetricTypeList); +export default connect((state: any) => ({ + isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee', +}))(withRouter(MetricTypeList)); diff --git a/frontend/app/components/Login/Login.js b/frontend/app/components/Login/Login.js index 06a2a8dd7..9ce132f28 100644 --- a/frontend/app/components/Login/Login.js +++ b/frontend/app/components/Login/Login.js @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import withPageTitle from 'HOCs/withPageTitle'; -import { Icon, Loader, Button, Link, Input, Form } from 'UI'; +import { Icon, Loader, Button, Link, Input, Form, Popover, Tooltip } from 'UI'; import { login } from 'Duck/user'; import { forgotPassword, signup } from 'App/routes'; import ReCAPTCHA from 'react-google-recaptcha'; @@ -16,12 +16,12 @@ const recaptchaRef = React.createRef(); @connect( (state, props) => ({ - errors: state.getIn([ 'user', 'loginRequest', 'errors' ]), - loading: state.getIn([ 'user', 'loginRequest', 'loading' ]), + errors: state.getIn(['user', 'loginRequest', 'errors']), + loading: state.getIn(['user', 'loginRequest', 'loading']), authDetails: state.getIn(['user', 'authDetails']), - params: new URLSearchParams(props.location.search) + params: new URLSearchParams(props.location.search), }), - { login, setJwt }, + { login, setJwt } ) @withPageTitle('Login - OpenReplay') @withRouter @@ -34,7 +34,7 @@ export default class Login extends React.Component { componentDidMount() { const { params } = this.props; - const jwt = params.get('jwt') + const jwt = params.get('jwt'); if (jwt) { this.props.setJwt(jwt); window.location.href = '/'; @@ -44,110 +44,139 @@ export default class Login extends React.Component { handleSubmit = (token) => { const { email, password } = this.state; this.props.login({ email: email.trim(), password, 'g-recaptcha-response': token }).then(() => { - const { errors } = this.props; - }) - } + const { errors } = this.props; + }); + }; - onSubmit = (e) => { + onSubmit = (e) => { e.preventDefault(); const { CAPTCHA_ENABLED } = this.state; if (CAPTCHA_ENABLED && recaptchaRef.current) { - recaptchaRef.current.execute(); + recaptchaRef.current.execute(); } else if (!CAPTCHA_ENABLED) { this.handleSubmit(); } - } + }; - write = ({ target: { value, name } }) => this.setState({ [ name ]: value }) - - + write = ({ target: { value, name } }) => this.setState({ [name]: value }); render() { const { errors, loading, authDetails } = this.props; const { CAPTCHA_ENABLED } = this.state; return ( -
-
+
+
-
+
Welcome Back!
-
+

Login to OpenReplay

- { !authDetails.tenants &&
Don't have an account? Sign up
} + {!authDetails.tenants && ( +
+ Don't have an account?{' '} + + Sign up + +
+ )}
- - { CAPTCHA_ENABLED && ( + + {CAPTCHA_ENABLED && ( this.handleSubmit(token) } + sitekey={window.env.CAPTCHA_SITE_KEY} + onChange={(token) => this.handleSubmit(token)} /> - )} -
+ )} +
- +
- +
- { errors.length ? - (
- { errors.map(error => ( + {errors.length ? ( +
+ {errors.map((error) => (
- - { error }
+ + + {error} +
+
- )) } -
) : null - } - {/*
*/} - - -
- {'Forgot your password?'} + ))}
+ ) : null} + {/*
*/} + + +
+ {'Forgot your password?'} +
{/*
*/} - { authDetails.sso && ( -
-
or
- - - + +
+
or
+ +
+ This feature requires an enterprise license.
} + placement="top" + // open={true} + > + + + +
- )} +
diff --git a/frontend/app/components/Session_/ScreenRecorder/ScreenRecorder.tsx b/frontend/app/components/Session_/ScreenRecorder/ScreenRecorder.tsx index fc5c66eea..9e9aa0ed3 100644 --- a/frontend/app/components/Session_/ScreenRecorder/ScreenRecorder.tsx +++ b/frontend/app/components/Session_/ScreenRecorder/ScreenRecorder.tsx @@ -35,9 +35,11 @@ const supportedMessage = `Supported Browsers: ${supportedBrowsers.join(', ')}`; function ScreenRecorder({ siteId, sessionId, + isEnterprise, }: { siteId: string; sessionId: string; + isEnterprise: boolean; }) { const { player, store } = React.useContext(PlayerContext) const recordingState = store.get().recordingState @@ -93,11 +95,11 @@ function ScreenRecorder({ player.assistManager.requestRecording() }; - if (!isSupported()) { + if (!isSupported() || !isEnterprise) { return (
{/* @ts-ignore */} - + @@ -119,6 +121,7 @@ function ScreenRecorder({ } export default connect((state: any) => ({ + isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee', siteId: state.getIn(['site', 'siteId']), sessionId: state.getIn(['sessions', 'current']).sessionId, }))(observer(ScreenRecorder)) diff --git a/frontend/app/components/ui/ItemMenu/ItemMenu.tsx b/frontend/app/components/ui/ItemMenu/ItemMenu.tsx index c23dae666..4c5702c08 100644 --- a/frontend/app/components/ui/ItemMenu/ItemMenu.tsx +++ b/frontend/app/components/ui/ItemMenu/ItemMenu.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Icon, Popover } from 'UI'; +import { Icon, Popover, Tooltip } from 'UI'; import styles from './itemMenu.module.css'; import cn from 'classnames'; @@ -9,6 +9,7 @@ interface Item { onClick: (args: any) => void; hidden?: boolean; disabled?: boolean; + tooltipTitle?: string; } interface Props { @@ -66,23 +67,25 @@ export default class ItemMenu extends React.PureComponent { > {items .filter(({ hidden }) => !hidden) - .map(({ onClick, text, icon, disabled = false }) => ( -
{}} - className={disabled ? 'cursor-not-allowed' : ''} - role="menuitem" - > -
- {icon && ( -
- {/* @ts-ignore */} - -
- )} -
{text}
+ .map(({ onClick, text, icon, disabled = false, tooltipTitle = '' }) => ( + +
{}} + className={disabled ? 'cursor-not-allowed' : ''} + role="menuitem" + > +
+ {icon && ( +
+ {/* @ts-ignore */} + +
+ )} +
{text}
+
-
+ ))}
)} diff --git a/frontend/app/components/ui/SideMenuitem/SideMenuitem.js b/frontend/app/components/ui/SideMenuitem/SideMenuitem.js index 8538a9a63..c62b84787 100644 --- a/frontend/app/components/ui/SideMenuitem/SideMenuitem.js +++ b/frontend/app/components/ui/SideMenuitem/SideMenuitem.js @@ -12,6 +12,7 @@ function SideMenuitem({ title, active = false, disabled = false, + tooltipTitle = '', onClick, deleteHandler = null, leading = null, @@ -20,8 +21,8 @@ function SideMenuitem({ return (
Object.keys(i)) From 678724aecb9bbbc34668649f5261ccfa0a5b253a Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 23 Jan 2023 11:31:23 +0100 Subject: [PATCH 2/2] change(ui) - dashboard more options button --- frontend/app/components/ui/ItemMenu/ItemMenu.tsx | 2 +- frontend/app/components/ui/ItemMenu/itemMenu.module.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/ui/ItemMenu/ItemMenu.tsx b/frontend/app/components/ui/ItemMenu/ItemMenu.tsx index 4c5702c08..04e284a95 100644 --- a/frontend/app/components/ui/ItemMenu/ItemMenu.tsx +++ b/frontend/app/components/ui/ItemMenu/ItemMenu.tsx @@ -93,7 +93,7 @@ export default class ItemMenu extends React.PureComponent {