monitorIssues.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {Fragment, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import {LinkButton} from 'sentry/components/button';
  4. import EmptyStateWarning from 'sentry/components/emptyStateWarning';
  5. import GroupList from 'sentry/components/issues/groupList';
  6. import Panel from 'sentry/components/panels/panel';
  7. import PanelBody from 'sentry/components/panels/panelBody';
  8. import {SegmentedControl} from 'sentry/components/segmentedControl';
  9. import {t} from 'sentry/locale';
  10. import {space} from 'sentry/styles/space';
  11. import {getUtcDateString} from 'sentry/utils/dates';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. import usePageFilters from 'sentry/utils/usePageFilters';
  14. import type {Monitor, MonitorEnvironment} from '../types';
  15. enum IssuesType {
  16. ALL = 'all',
  17. UNRESOLVED = 'unresolved',
  18. }
  19. const ISSUE_TYPES = [
  20. {value: IssuesType.UNRESOLVED, label: t('Unresolved Issues')},
  21. {value: IssuesType.ALL, label: t('All Issues')},
  22. ];
  23. type Props = {
  24. monitor: Monitor;
  25. monitorEnvs: MonitorEnvironment[];
  26. };
  27. function MonitorIssuesEmptyMessage() {
  28. return (
  29. <Panel>
  30. <PanelBody>
  31. <EmptyStateWarning>
  32. <p>{t('No issues relating to this cron monitor have been found.')}</p>
  33. </EmptyStateWarning>
  34. </PanelBody>
  35. </Panel>
  36. );
  37. }
  38. export function MonitorIssues({monitor, monitorEnvs}: Props) {
  39. const organization = useOrganization();
  40. const {selection} = usePageFilters();
  41. const {start, end, period} = selection.datetime;
  42. const timeProps =
  43. start && end
  44. ? {
  45. start: getUtcDateString(start),
  46. end: getUtcDateString(end),
  47. }
  48. : {
  49. statsPeriod: period,
  50. };
  51. const [issuesType, setIssuesType] = useState<IssuesType>(IssuesType.UNRESOLVED);
  52. const monitorFilter = `monitor.slug:${monitor.slug}`;
  53. const envFilter = `environment:[${monitorEnvs.map(e => e.name).join(',')}]`;
  54. const issueTypeFilter = issuesType === IssuesType.UNRESOLVED ? 'is:unresolved' : '';
  55. const issueQuery = `${monitorFilter} ${envFilter} ${issueTypeFilter}`;
  56. const issueSearchLocation = {
  57. pathname: `/organizations/${organization.slug}/issues/`,
  58. query: {
  59. query: issueQuery,
  60. project: monitor.project.id,
  61. ...timeProps,
  62. },
  63. };
  64. // TODO(epurkhiser): We probably want to filter on envrionemnt
  65. return (
  66. <Fragment>
  67. <ControlsWrapper>
  68. <SegmentedControl
  69. aria-label={t('Issue category')}
  70. value={issuesType}
  71. size="xs"
  72. onChange={setIssuesType}
  73. >
  74. {ISSUE_TYPES.map(({value, label}) => (
  75. <SegmentedControl.Item key={value} textValue={label}>
  76. {label}
  77. </SegmentedControl.Item>
  78. ))}
  79. </SegmentedControl>
  80. <LinkButton size="xs" to={issueSearchLocation}>
  81. {t('Open In Issues')}
  82. </LinkButton>
  83. </ControlsWrapper>
  84. <GroupList
  85. orgSlug={organization.slug}
  86. queryParams={{
  87. query: issueQuery,
  88. project: monitor.project.id,
  89. limit: 20,
  90. ...timeProps,
  91. }}
  92. renderEmptyMessage={MonitorIssuesEmptyMessage}
  93. canSelectGroups={false}
  94. withPagination={false}
  95. withChart={false}
  96. useTintRow={false}
  97. source="monitors"
  98. />
  99. </Fragment>
  100. );
  101. }
  102. const ControlsWrapper = styled('div')`
  103. display: flex;
  104. align-items: flex-end;
  105. justify-content: space-between;
  106. margin-bottom: ${space(1)};
  107. flex-wrap: wrap;
  108. `;