createSavedSearchModal.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import {Component} from 'react';
  2. import {addLoadingMessage, clearIndicators} from 'sentry/actionCreators/indicator';
  3. import {ModalRenderProps} from 'sentry/actionCreators/modal';
  4. import {createSavedSearch} from 'sentry/actionCreators/savedSearches';
  5. import {Client} from 'sentry/api';
  6. import Alert from 'sentry/components/alert';
  7. import {Form, SelectField, TextField} from 'sentry/components/forms';
  8. import {OnSubmitCallback} from 'sentry/components/forms/type';
  9. import {t} from 'sentry/locale';
  10. import {Organization} from 'sentry/types';
  11. import withApi from 'sentry/utils/withApi';
  12. import {getSortLabel, IssueSortOptions} from './utils';
  13. type Props = ModalRenderProps & {
  14. api: Client;
  15. organization: Organization;
  16. query: string;
  17. sort?: string;
  18. };
  19. type State = {
  20. error: string | null;
  21. isSaving: boolean;
  22. };
  23. const DEFAULT_SORT_OPTIONS = [
  24. IssueSortOptions.DATE,
  25. IssueSortOptions.NEW,
  26. IssueSortOptions.FREQ,
  27. IssueSortOptions.PRIORITY,
  28. IssueSortOptions.USER,
  29. ];
  30. class CreateSavedSearchModal extends Component<Props, State> {
  31. state: State = {
  32. isSaving: false,
  33. error: null,
  34. };
  35. /** Handle "date added" sort not being available for saved searches */
  36. validateSortOption(sort?: string | null): string {
  37. if (this.sortOptions().find(option => option === sort)) {
  38. return sort as string;
  39. }
  40. return IssueSortOptions.DATE;
  41. }
  42. handleSubmit: OnSubmitCallback = (data, onSubmitSuccess, onSubmitError, event) => {
  43. const {api, organization} = this.props;
  44. const sort = this.validateSortOption(data.sort);
  45. event.preventDefault();
  46. this.setState({isSaving: true});
  47. addLoadingMessage(t('Saving Changes'));
  48. createSavedSearch(api, organization.slug, data.name, data.query, sort)
  49. .then(_data => {
  50. this.props.closeModal();
  51. this.setState({
  52. error: null,
  53. isSaving: false,
  54. });
  55. clearIndicators();
  56. onSubmitSuccess(data);
  57. })
  58. .catch(err => {
  59. let error = t('Unable to save your changes.');
  60. if (err.responseJSON && err.responseJSON.detail) {
  61. error = err.responseJSON.detail;
  62. }
  63. this.setState({
  64. error,
  65. isSaving: false,
  66. });
  67. clearIndicators();
  68. onSubmitError(error);
  69. });
  70. };
  71. sortOptions() {
  72. const {organization} = this.props;
  73. const options = [...DEFAULT_SORT_OPTIONS];
  74. if (organization?.features?.includes('issue-list-trend-sort')) {
  75. options.push(IssueSortOptions.TREND);
  76. }
  77. return options;
  78. }
  79. render() {
  80. const {error} = this.state;
  81. const {Header, Body, closeModal, query, sort} = this.props;
  82. const sortOptions = this.sortOptions().map(sortOption => ({
  83. value: sortOption,
  84. label: getSortLabel(sortOption),
  85. }));
  86. const initialData = {
  87. name: '',
  88. query,
  89. sort: this.validateSortOption(sort),
  90. };
  91. return (
  92. <Form
  93. onSubmit={this.handleSubmit}
  94. onCancel={closeModal}
  95. saveOnBlur={false}
  96. initialData={initialData}
  97. submitLabel={t('Save')}
  98. >
  99. <Header>
  100. <h4>{t('Save Current Search')}</h4>
  101. </Header>
  102. <Body>
  103. {this.state.error && <Alert type="error">{error}</Alert>}
  104. <p>{t('All team members will now have access to this search.')}</p>
  105. <TextField
  106. key="name"
  107. name="name"
  108. label={t('Name')}
  109. placeholder="e.g. My Search Results"
  110. inline={false}
  111. stacked
  112. flexibleControlStateSize
  113. required
  114. />
  115. <TextField
  116. key="query"
  117. name="query"
  118. label={t('Query')}
  119. inline={false}
  120. stacked
  121. flexibleControlStateSize
  122. required
  123. />
  124. <SelectField
  125. key="sort"
  126. name="sort"
  127. label={t('Sort By')}
  128. options={sortOptions}
  129. required
  130. clearable={false}
  131. inline={false}
  132. stacked
  133. flexibleControlStateSize
  134. />
  135. </Body>
  136. </Form>
  137. );
  138. }
  139. }
  140. export default withApi(CreateSavedSearchModal);