|
@@ -1,6 +1,7 @@
|
|
|
import React from 'react';
|
|
|
import PropTypes from 'prop-types';
|
|
|
import styled from 'react-emotion';
|
|
|
+import Modal from 'react-bootstrap/lib/Modal';
|
|
|
|
|
|
import {t} from 'app/locale';
|
|
|
import Access from 'app/components/acl/access';
|
|
@@ -11,7 +12,9 @@ import DropdownLink from 'app/components/dropdownLink';
|
|
|
import QueryCount from 'app/components/queryCount';
|
|
|
import InlineSvg from 'app/components/inlineSvg';
|
|
|
import SentryTypes from 'app/sentryTypes';
|
|
|
+import {TextField} from 'app/components/forms';
|
|
|
import space from 'app/styles/space';
|
|
|
+import withApi from 'app/utils/withApi';
|
|
|
|
|
|
export default class OrganizationSavedSearchSelector extends React.Component {
|
|
|
static propTypes = {
|
|
@@ -19,7 +22,7 @@ export default class OrganizationSavedSearchSelector extends React.Component {
|
|
|
savedSearchList: PropTypes.array.isRequired,
|
|
|
onSavedSearchSelect: PropTypes.func.isRequired,
|
|
|
onSavedSearchDelete: PropTypes.func.isRequired,
|
|
|
- query: PropTypes.string,
|
|
|
+ query: PropTypes.string.isRequired,
|
|
|
queryCount: PropTypes.number,
|
|
|
queryMaxCount: PropTypes.number,
|
|
|
searchId: PropTypes.string,
|
|
@@ -79,7 +82,7 @@ export default class OrganizationSavedSearchSelector extends React.Component {
|
|
|
}
|
|
|
|
|
|
render() {
|
|
|
- const {queryCount, queryMaxCount} = this.props;
|
|
|
+ const {organization, query, queryCount, queryMaxCount} = this.props;
|
|
|
|
|
|
return (
|
|
|
<Container>
|
|
@@ -92,12 +95,115 @@ export default class OrganizationSavedSearchSelector extends React.Component {
|
|
|
}
|
|
|
>
|
|
|
{this.renderList()}
|
|
|
+ <Access
|
|
|
+ organization={organization}
|
|
|
+ access={['org:write']}
|
|
|
+ renderNoAccessMessage={false}
|
|
|
+ >
|
|
|
+ <StyledMenuItem divider={true} />
|
|
|
+ <ButtonBar>
|
|
|
+ <SaveSearchButton query={query} organization={organization} />
|
|
|
+ </ButtonBar>
|
|
|
+ </Access>
|
|
|
</StyledDropdownLink>
|
|
|
</Container>
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+const SaveSearchButton = withApi(
|
|
|
+ class SaveSearchButton extends React.Component {
|
|
|
+ static propTypes = {
|
|
|
+ // api: PropTypes.object.isRequired,
|
|
|
+ query: PropTypes.string.isRequired,
|
|
|
+ // organization: SentryTypes.Organization.isRequired,
|
|
|
+ };
|
|
|
+
|
|
|
+ constructor(props) {
|
|
|
+ super(props);
|
|
|
+ this.state = {
|
|
|
+ isModalOpen: false,
|
|
|
+ isSaving: false,
|
|
|
+ query: props.query,
|
|
|
+ name: '',
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ onSubmit = e => {
|
|
|
+ e.preventDefault();
|
|
|
+
|
|
|
+ // TODO: implement saving
|
|
|
+ };
|
|
|
+
|
|
|
+ onToggle = () => {
|
|
|
+ this.setState({
|
|
|
+ isModalOpen: !this.state.isModalOpen,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ handleChangeName = val => {
|
|
|
+ this.setState({name: val});
|
|
|
+ };
|
|
|
+
|
|
|
+ handleChangeQuery = val => {
|
|
|
+ this.setState({query: val});
|
|
|
+ };
|
|
|
+
|
|
|
+ render() {
|
|
|
+ const {isSaving, isModalOpen} = this.state;
|
|
|
+
|
|
|
+ return (
|
|
|
+ <React.Fragment>
|
|
|
+ <Button size="xsmall" onClick={this.onToggle}>
|
|
|
+ {t('Save Current Search')}
|
|
|
+ </Button>
|
|
|
+ <Modal show={isModalOpen} animation={false} onHide={this.onToggle}>
|
|
|
+ <form onSubmit={this.onSubmit}>
|
|
|
+ <div className="modal-header">
|
|
|
+ <h4>{t('Save Current Search')}</h4>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="modal-body">
|
|
|
+ <p>{t('All team members will now have access to this search.')}</p>
|
|
|
+ <TextField
|
|
|
+ key="name"
|
|
|
+ name="name"
|
|
|
+ label={t('Name')}
|
|
|
+ placeholder="e.g. My Search Results"
|
|
|
+ required={true}
|
|
|
+ onChange={this.handleChangeName}
|
|
|
+ />
|
|
|
+ <TextField
|
|
|
+ key="query"
|
|
|
+ name="query"
|
|
|
+ label={t('Query')}
|
|
|
+ value={this.props.query}
|
|
|
+ required={true}
|
|
|
+ onChange={this.handleChangeQuery}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div className="modal-footer">
|
|
|
+ <Button
|
|
|
+ priority="default"
|
|
|
+ size="small"
|
|
|
+ disabled={isSaving}
|
|
|
+ onClick={this.onToggle}
|
|
|
+ style={{marginRight: space(1)}}
|
|
|
+ >
|
|
|
+ {t('Cancel')}
|
|
|
+ </Button>
|
|
|
+ <Button priority="primary" size="small" disabled={isSaving}>
|
|
|
+ {t('Save')}
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </form>
|
|
|
+ </Modal>
|
|
|
+ </React.Fragment>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
const Container = styled.div`
|
|
|
& .dropdown-menu {
|
|
|
max-width: 350px;
|
|
@@ -176,3 +282,15 @@ const EmptyItem = styled.li`
|
|
|
padding: 8px 10px 5px;
|
|
|
font-style: italic;
|
|
|
`;
|
|
|
+
|
|
|
+const ButtonBar = styled.li`
|
|
|
+ padding: ${space(0.5)} ${space(1)};
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+
|
|
|
+ & a {
|
|
|
+ /* need to override .dropdown-menu li a in shared-components.less */
|
|
|
+ padding: 0 !important;
|
|
|
+ line-height: 1 !important;
|
|
|
+ }
|
|
|
+`;
|