diff --git a/frontend/app/components/Alerts/Notifications/Notifications.tsx b/frontend/app/components/Alerts/Notifications/Notifications.tsx index b5421fd29..05bcea06b 100644 --- a/frontend/app/components/Alerts/Notifications/Notifications.tsx +++ b/frontend/app/components/Alerts/Notifications/Notifications.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import stl from './notifications.module.css'; import { connect } from 'react-redux'; -import { Icon, Popup } from 'UI'; +import { Icon, Tooltip } from 'UI'; import { fetchList, setViewed, clearAll } from 'Duck/notifications'; import { setLastRead } from 'Duck/announcements'; import { useModal } from 'App/components/Modal'; @@ -29,7 +29,7 @@ function Notifications(props: Props) { }, []); return useObserver(() => ( - +
showModal(, { right: true })} @@ -39,7 +39,7 @@ function Notifications(props: Props) {
-
+ )); } diff --git a/frontend/app/components/Announcements/Announcements.js b/frontend/app/components/Announcements/Announcements.js index b17e44982..55306e643 100644 --- a/frontend/app/components/Announcements/Announcements.js +++ b/frontend/app/components/Announcements/Announcements.js @@ -2,7 +2,7 @@ import React from 'react'; import stl from './announcements.module.css'; import ListItem from './ListItem'; import { connect } from 'react-redux'; -import { SlideModal, Icon, NoContent, Popup } from 'UI'; +import { SlideModal, Icon, NoContent, Tooltip } from 'UI'; import { fetchList, setLastRead } from 'Duck/announcements'; import withToggle from 'Components/hocs/withToggle'; import { withRouter } from 'react-router-dom'; @@ -45,14 +45,14 @@ class Announcements extends React.Component { return (
- +
{ unReadNotificationsCount }
-
+
-
- +
{onCall && callObject && ( diff --git a/frontend/app/components/BugFinder/SessionList/Tooltip.js b/frontend/app/components/BugFinder/SessionList/Tooltip.js deleted file mode 100644 index a054b0beb..000000000 --- a/frontend/app/components/BugFinder/SessionList/Tooltip.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import { Popup } from 'UI'; - -export default class Tooltip extends React.PureComponent { - state = { - open: false, - } - mouseOver = false - onMouseEnter = () => { - this.mouseOver = true; - setTimeout(() => { - if (this.mouseOver) this.setState({ open: true }); - }, 1000) - } - onMouseLeave = () => { - this.mouseOver = false; - this.setState({ - open: false, - }); - } - - render() { - const { trigger, tooltip } = this.props; - const { open } = this.state; - return ( - - - { trigger } - - - ); - } -} \ No newline at end of file diff --git a/frontend/app/components/BugFinder/SessionsMenu/SessionsMenu.js b/frontend/app/components/BugFinder/SessionsMenu/SessionsMenu.js index 400c10435..4dde1a130 100644 --- a/frontend/app/components/BugFinder/SessionsMenu/SessionsMenu.js +++ b/frontend/app/components/BugFinder/SessionsMenu/SessionsMenu.js @@ -1,7 +1,7 @@ import React from 'react' import { connect } from 'react-redux'; import cn from 'classnames'; -import { SideMenuitem, Popup } from 'UI' +import { SideMenuitem, Tooltip } from 'UI' import stl from './sessionMenu.module.css'; import { clearEvents } from 'Duck/filters'; import { issues_types } from 'Types/session/issue' @@ -24,12 +24,11 @@ function SessionsMenu(props) { Sessions
showModal(, { right: true })}> - Configure the percentage of sessions
to be captured, timezone and more.
} + Configure the percentage of sessions
to be captured, timezone and more.} > Settings - +
diff --git a/frontend/app/components/Client/Integrations/IntegrationItem.tsx b/frontend/app/components/Client/Integrations/IntegrationItem.tsx index ec730e7c1..d04ac3b7e 100644 --- a/frontend/app/components/Client/Integrations/IntegrationItem.tsx +++ b/frontend/app/components/Client/Integrations/IntegrationItem.tsx @@ -1,6 +1,6 @@ import React from 'react'; import cn from 'classnames'; -import { Icon, Popup } from 'UI'; +import { Icon, Tooltip } from 'UI'; import stl from './integrationItem.module.css'; import { connect } from 'react-redux'; @@ -17,9 +17,9 @@ const IntegrationItem = (props: Props) => {
props.onClick(e)}> {integrated && (
- + - +
)} {integration.icon.length ? integration : ( @@ -33,9 +33,4 @@ const IntegrationItem = (props: Props) => { ); }; -export default connect((state: any, props: Props) => { - const list = state.getIn([props.integration.slug, 'list']) || []; - return { - // integrated: props.integration.slug === 'issues' ? !!(list.first() && list.first().token) : list.size > 0, - }; -})(IntegrationItem); +export default IntegrationItem; diff --git a/frontend/app/components/Client/Integrations/Integrations.js_ b/frontend/app/components/Client/Integrations/Integrations.js_ deleted file mode 100644 index b4a421980..000000000 --- a/frontend/app/components/Client/Integrations/Integrations.js_ +++ /dev/null @@ -1,633 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import withPageTitle from "HOCs/withPageTitle"; -import { Loader, IconButton, SlideModal } from "UI"; -import { fetchList as fetchListSlack } from "Duck/integrations/slack"; -import { remove as removeIntegrationConfig } from "Duck/integrations/actions"; -import { fetchList, init } from "Duck/integrations/actions"; -import cn from "classnames"; - -import IntegrationItem from "./IntegrationItem"; -import SentryForm from "./SentryForm"; -import GithubForm from "./GithubForm"; -import SlackForm from "./SlackForm"; -import DatadogForm from "./DatadogForm"; -import StackdriverForm from "./StackdriverForm"; -import RollbarForm from "./RollbarForm"; -import NewrelicForm from "./NewrelicForm"; -import BugsnagForm from "./BugsnagForm"; -import CloudwatchForm from "./CloudwatchForm"; -import ElasticsearchForm from "./ElasticsearchForm"; -import SumoLogicForm from "./SumoLogicForm"; -import JiraForm from "./JiraForm"; -import styles from "./integrations.module.css"; -import ReduxDoc from "./ReduxDoc"; -import VueDoc from "./VueDoc"; -import GraphQLDoc from "./GraphQLDoc"; -import NgRxDoc from "./NgRxDoc/NgRxDoc"; -import SlackAddForm from "./SlackAddForm"; -import FetchDoc from "./FetchDoc"; -import MobxDoc from "./MobxDoc"; -import ProfilerDoc from "./ProfilerDoc"; -import AssistDoc from "./AssistDoc"; -import AxiosDoc from "./AxiosDoc/AxiosDoc"; - -const NONE = -1; -const SENTRY = 0; -const DATADOG = 1; -const STACKDRIVER = 2; -const ROLLBAR = 3; -const NEWRELIC = 4; -const BUGSNAG = 5; -const CLOUDWATCH = 6; -const ELASTICSEARCH = 7; -const SUMOLOGIC = 8; -const JIRA = 9; -const GITHUB = 10; -const REDUX = 11; -const VUE = 12; -const GRAPHQL = 13; -const NGRX = 14; -const SLACK = 15; -const FETCH = 16; -const MOBX = 17; -const PROFILER = 18; -const ASSIST = 19; -const AXIOS = 20; - -const TITLE = { - [SENTRY]: "Sentry", - [SLACK]: "Slack", - [DATADOG]: "Datadog", - [STACKDRIVER]: "Stackdriver", - [ROLLBAR]: "Rollbar", - [NEWRELIC]: "New Relic", - [BUGSNAG]: "Bugsnag", - [CLOUDWATCH]: "CloudWatch", - [ELASTICSEARCH]: "Elastic Search", - [SUMOLOGIC]: "Sumo Logic", - [JIRA]: "Jira", - [GITHUB]: "Github", - [REDUX]: "Redux", - [VUE]: "VueX", - [GRAPHQL]: "GraphQL", - [NGRX]: "NgRx", - [FETCH]: "Fetch", - [MOBX]: "MobX", - [PROFILER]: "Profiler", - [ASSIST]: "Assist", - [AXIOS]: "Axios", -}; - -const DOCS = [REDUX, VUE, GRAPHQL, NGRX, FETCH, MOBX, PROFILER, ASSIST]; - -const integrations = [ - "sentry", - "datadog", - "stackdriver", - "rollbar", - "newrelic", - "bugsnag", - "cloudwatch", - "elasticsearch", - "sumologic", - "issues", -]; - -@connect( - (state) => { - const props = {}; - integrations.forEach((name) => { - props[`${name}Integrated`] = - name === "issues" - ? !!( - state.getIn([name, "list"]).first() && - state.getIn([name, "list"]).first().token - ) - : state.getIn([name, "list"]).size > 0; - props.loading = - props.loading || state.getIn([name, "fetchRequest", "loading"]); - }); - const site = state.getIn(["site", "instance"]); - return { - ...props, - issues: state.getIn(["issues", "list"]).first() || {}, - slackChannelListExists: state.getIn(["slack", "list"]).size > 0, - tenantId: state.getIn(["user", "account", "tenantId"]), - jwt: state.get("jwt"), - projectKey: site ? site.projectKey : "", - }; - }, - { - fetchList, - init, - fetchListSlack, - removeIntegrationConfig, - } -) -@withPageTitle("Integrations - OpenReplay Preferences") -export default class Integrations extends React.PureComponent { - state = { - modalContent: NONE, - showDetailContent: false, - }; - - componentWillMount() { - integrations.forEach((name) => this.props.fetchList(name)); - this.props.fetchListSlack(); - } - - onClickIntegrationItem = (e, url) => { - e.preventDefault(); - window.open(url); - }; - - closeModal = () => - this.setState({ modalContent: NONE, showDetailContent: false }); - - onOauthClick = (source) => { - if (source === GITHUB) { - const githubUrl = `https://auth.openreplay.com/oauth/login?provider=github&back_url=${document.location.href}`; - const options = { - method: "GET", - credentials: "include", - headers: new Headers({ - Authorization: "Bearer " + this.props.jwt.toString(), - }), - }; - fetch(githubUrl, options).then((resp) => - resp.text().then((txt) => window.open(txt, "_self")) - ); - } - }; - - renderDetailContent() { - switch (this.state.modalContent) { - case SLACK: - return ( - - this.setState({ showDetailContent: false }) - } - /> - ); - } - } - - renderModalContent() { - const { projectKey } = this.props; - - switch (this.state.modalContent) { - case SENTRY: - return ; - case GITHUB: - return ; - case SLACK: - return ( - - this.setState({ showDetailContent: true }) - } - /> - ); - case DATADOG: - return ; - case STACKDRIVER: - return ; - case ROLLBAR: - return ; - case NEWRELIC: - return ; - case BUGSNAG: - return ; - case CLOUDWATCH: - return ; - case ELASTICSEARCH: - return ; - case SUMOLOGIC: - return ; - case JIRA: - return ; - case REDUX: - return ( - - ); - case VUE: - return ( - - ); - case GRAPHQL: - return ( - - ); - case NGRX: - return ( - - ); - case FETCH: - return ( - - ); - case MOBX: - return ( - - ); - case PROFILER: - return ( - - ); - case ASSIST: - return ( - - ); - case AXIOS: - return ( - - ); - default: - return null; - } - } - - deleteHandler = (name) => { - this.props.removeIntegrationConfig(name, null).then( - function () { - this.props.fetchList(name); - }.bind(this) - ); - }; - - showIntegrationConfig = (type) => { - this.setState({ modalContent: type }); - }; - - render() { - const { - loading, - sentryIntegrated, - stackdriverIntegrated, - datadogIntegrated, - rollbarIntegrated, - newrelicIntegrated, - bugsnagIntegrated, - cloudwatchIntegrated, - elasticsearchIntegrated, - sumologicIntegrated, - hideHeader = false, - plugins = false, - jiraIntegrated, - issuesIntegrated, - tenantId, - slackChannelListExists, - issues, - } = this.props; - const { modalContent, showDetailContent } = this.state; - return ( -
- -
{TITLE[modalContent]}
- {modalContent === SLACK && ( - - this.setState({ - showDetailContent: true, - }) - } - /> - )} -
- } - isDisplayed={modalContent !== NONE} - onClose={this.closeModal} - size={ - DOCS.includes(this.state.modalContent) - ? "middle" - : "small" - } - content={this.renderModalContent()} - detailContent={ - showDetailContent && this.renderDetailContent() - } - /> - - {!hideHeader && ( -
-

- {"Integrations"} -

-

- Power your workflow with your favourite tools. -

-
-
- )} - - {plugins && ( -
-
- Use plugins to better debug your application's - store, monitor queries and track performance issues. -
-
- - this.showIntegrationConfig(REDUX) - } - // integrated={ sentryIntegrated } - /> - this.showIntegrationConfig(VUE)} - // integrated={ sentryIntegrated } - /> - - this.showIntegrationConfig(GRAPHQL) - } - // integrated={ sentryIntegrated } - /> - this.showIntegrationConfig(NGRX)} - // integrated={ sentryIntegrated } - /> - this.showIntegrationConfig(MOBX)} - // integrated={ sentryIntegrated } - /> - - this.showIntegrationConfig(FETCH) - } - // integrated={ sentryIntegrated } - /> - - this.showIntegrationConfig(PROFILER) - } - // integrated={ sentryIntegrated } - /> - - this.showIntegrationConfig(AXIOS) - } - // integrated={ sentryIntegrated } - /> - - this.showIntegrationConfig(ASSIST) - } - // integrated={ sentryIntegrated } - /> -
-
- )} - - {!plugins && ( - -
-
-
- How are you monitoring errors and crash - reporting? -
-
- {(!issues.token || - issues.provider !== "github") && ( - - this.showIntegrationConfig(JIRA) - } - integrated={issuesIntegrated} - /> - )} - {(!issues.token || - issues.provider !== "jira") && ( - - this.showIntegrationConfig( - GITHUB - ) - } - integrated={issuesIntegrated} - deleteHandler={ - issuesIntegrated - ? () => - this.deleteHandler( - "issues" - ) - : null - } - /> - )} - - - this.showIntegrationConfig(SLACK) - } - integrated={sentryIntegrated} - /> - - this.showIntegrationConfig(SENTRY) - } - integrated={sentryIntegrated} - /> - - - this.showIntegrationConfig(BUGSNAG) - } - integrated={bugsnagIntegrated} - /> - - - this.showIntegrationConfig(ROLLBAR) - } - integrated={rollbarIntegrated} - /> - - - this.showIntegrationConfig( - ELASTICSEARCH - ) - } - integrated={elasticsearchIntegrated} - /> - - - this.showIntegrationConfig(DATADOG) - } - integrated={datadogIntegrated} - /> - - this.showIntegrationConfig( - SUMOLOGIC - ) - } - integrated={sumologicIntegrated} - /> - - this.showIntegrationConfig( - STACKDRIVER - ) - } - integrated={stackdriverIntegrated} - /> - - - this.showIntegrationConfig( - CLOUDWATCH - ) - } - integrated={cloudwatchIntegrated} - /> - - - this.showIntegrationConfig(NEWRELIC) - } - integrated={newrelicIntegrated} - /> -
-
-
-
- )} -
- ); - } -} diff --git a/frontend/app/components/Client/ManageUsers/ManageUsers.js b/frontend/app/components/Client/ManageUsers/ManageUsers.js deleted file mode 100644 index dd703813e..000000000 --- a/frontend/app/components/Client/ManageUsers/ManageUsers.js +++ /dev/null @@ -1,273 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import cn from 'classnames'; -import withPageTitle from 'HOCs/withPageTitle'; -import { - Form, IconButton, SlideModal, Input, Button, Loader, - NoContent, Popup, CopyButton } from 'UI'; -import Select from 'Shared/Select'; -import { init, save, edit, remove as deleteMember, fetchList, generateInviteLink } from 'Duck/member'; -import { fetchList as fetchRoles } from 'Duck/roles'; -import styles from './manageUsers.module.css'; -import UserItem from './UserItem'; -import { confirm } from 'UI'; -import { toast } from 'react-toastify'; -import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; - -const PERMISSION_WARNING = 'You don’t have the permissions to perform this action.'; -const LIMIT_WARNING = 'You have reached users limit.'; - -@connect(state => ({ - account: state.getIn([ 'user', 'account' ]), - members: state.getIn([ 'members', 'list' ]).filter(u => u.id), - member: state.getIn([ 'members', 'instance' ]), - errors: state.getIn([ 'members', 'saveRequest', 'errors' ]), - loading: state.getIn([ 'members', 'loading' ]), - saving: state.getIn([ 'members', 'saveRequest', 'loading' ]), - roles: state.getIn(['roles', 'list']).filter(r => !r.protected).map(r => ({ label: r.name, value: r.roleId })).toJS(), - isEnterprise: state.getIn([ 'user', 'account', 'edition' ]) === 'ee', -}), { - init, - save, - edit, - deleteMember, - fetchList, - generateInviteLink, - fetchRoles -}) -@withPageTitle('Team - OpenReplay Preferences') -class ManageUsers extends React.PureComponent { - state = { showModal: false, remaining: this.props.account.limits.teamMember.remaining, invited: false } - - // writeOption = (e, { name, value }) => this.props.edit({ [ name ]: value }); - onChange = ({ name, value }) => this.props.edit({ [ name ]: value.value }); - onChangeCheckbox = ({ target: { checked, name } }) => this.props.edit({ [ name ]: checked }); - setFocus = () => this.focusElement && this.focusElement.focus(); - closeModal = () => this.setState({ showModal: false }); - componentWillMount = () => { - this.props.fetchList(); - if (this.props.isEnterprise) { - this.props.fetchRoles(); - } - } - - adminLabel = (user) => { - if (user.superAdmin) return null; - return user.admin ? 'Admin' : ''; - }; - - editHandler = user => { - this.init(user) - } - - deleteHandler = async (user) => { - if (await confirm({ - header: 'Users', - confirmation: `Are you sure you want to remove this user?` - })) { - this.props.deleteMember(user.id).then(() => { - const { remaining } = this.state; - if (remaining <= 0) return; - this.setState({ remaining: remaining - 1 }) - }); - } - } - - save = (e) => { - e.preventDefault(); - this.props.save(this.props.member) - .then(() => { - const { errors } = this.props; - if (errors && errors.size > 0) { - errors.forEach(e => { - toast.error(e); - }) - } - this.setState({ invited: true }) - // this.closeModal() - }); - } - - formContent = () => { - const { member, account, isEnterprise, roles } = this.props; - - return ( -
-
-
- - { this.focusElement = ref; } } - name="name" - value={ member.name } - onChange={ this.onChange } - className={ styles.input } - id="name-field" - /> -
- - - - - - { !account.smtp && -
- SMTP is not configured (see here how to set it up). You can still add new users, but you’d have to manually copy then send them the invitation link. -
- } - - -
{ 'Can manage Projects and team members.' }
-
- - { isEnterprise && ( - - -
- + of the sessions