columnEditModal.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import {Fragment, useEffect, useState} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import type {ModalRenderProps} from 'sentry/actionCreators/modal';
  5. import {Button} from 'sentry/components/button';
  6. import ButtonBar from 'sentry/components/buttonBar';
  7. import ExternalLink from 'sentry/components/links/externalLink';
  8. import {DISCOVER2_DOCS_URL} from 'sentry/constants';
  9. import {t, tct} from 'sentry/locale';
  10. import {space} from 'sentry/styles/space';
  11. import type {Organization} from 'sentry/types/organization';
  12. import {trackAnalytics} from 'sentry/utils/analytics';
  13. import type {CustomMeasurementCollection} from 'sentry/utils/customMeasurements/customMeasurements';
  14. import {
  15. type Column,
  16. ERROR_FIELDS,
  17. TRANSACTION_FIELDS,
  18. } from 'sentry/utils/discover/fields';
  19. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  20. import {FieldKey} from 'sentry/utils/fields';
  21. import theme from 'sentry/utils/theme';
  22. import useTags from 'sentry/utils/useTags';
  23. import {generateFieldOptions} from 'sentry/views/discover/utils';
  24. import ColumnEditCollection from './columnEditCollection';
  25. type Props = {
  26. columns: Column[];
  27. measurementKeys: null | string[];
  28. // Fired when column selections have been applied.
  29. onApply: (columns: Column[]) => void;
  30. organization: Organization;
  31. customMeasurements?: CustomMeasurementCollection;
  32. dataset?: DiscoverDatasets;
  33. spanOperationBreakdownKeys?: string[];
  34. } & ModalRenderProps;
  35. function ColumnEditModal(props: Props) {
  36. const {
  37. Header,
  38. Body,
  39. Footer,
  40. measurementKeys,
  41. spanOperationBreakdownKeys,
  42. organization,
  43. onApply,
  44. closeModal,
  45. customMeasurements,
  46. dataset,
  47. } = props;
  48. // Only run once for each organization.id.
  49. useEffect(() => {
  50. trackAnalytics('discover_v2.column_editor.open', {organization});
  51. }, [organization]);
  52. const tags = useTags();
  53. const tagKeys = Object.keys(tags);
  54. const [columns, setColumns] = useState<Column[]>(props.columns);
  55. function handleApply() {
  56. onApply(columns);
  57. closeModal();
  58. }
  59. let fieldOptions: ReturnType<typeof generateFieldOptions>;
  60. if (dataset === DiscoverDatasets.ERRORS) {
  61. fieldOptions = generateFieldOptions({
  62. organization,
  63. tagKeys,
  64. fieldKeys: ERROR_FIELDS,
  65. });
  66. } else if (dataset === DiscoverDatasets.TRANSACTIONS) {
  67. fieldOptions = generateFieldOptions({
  68. organization,
  69. tagKeys,
  70. measurementKeys,
  71. spanOperationBreakdownKeys,
  72. customMeasurements: Object.values(customMeasurements ?? {}).map(
  73. ({key, functions}) => ({
  74. key,
  75. functions,
  76. })
  77. ),
  78. fieldKeys: TRANSACTION_FIELDS,
  79. });
  80. } else {
  81. fieldOptions = generateFieldOptions({
  82. organization,
  83. tagKeys,
  84. measurementKeys,
  85. spanOperationBreakdownKeys,
  86. customMeasurements: Object.values(customMeasurements ?? {}).map(
  87. ({key, functions}) => ({
  88. key,
  89. functions,
  90. })
  91. ),
  92. });
  93. }
  94. return (
  95. <Fragment>
  96. <Header closeButton>
  97. <h4>{t('Edit Columns')}</h4>
  98. </Header>
  99. <Body>
  100. <Instruction>
  101. {tct(
  102. 'To group events, add [functionLink: functions] f(x) that may take in additional parameters. [fieldTagLink: Tag and field] columns will help you view more details about the events (i.e. title).',
  103. {
  104. functionLink: (
  105. <ExternalLink href="https://docs.sentry.io/product/discover-queries/query-builder/#filter-by-table-columns" />
  106. ),
  107. fieldTagLink: (
  108. <ExternalLink href="https://docs.sentry.io/product/sentry-basics/search/searchable-properties/#event-properties" />
  109. ),
  110. }
  111. )}
  112. </Instruction>
  113. <ColumnEditCollection
  114. columns={columns}
  115. fieldOptions={fieldOptions}
  116. filterAggregateParameters={option =>
  117. option.value.meta.name !== FieldKey.TOTAL_COUNT
  118. }
  119. onChange={setColumns}
  120. organization={organization}
  121. />
  122. </Body>
  123. <Footer>
  124. <ButtonBar gap={1}>
  125. <Button priority="default" href={DISCOVER2_DOCS_URL} external>
  126. {t('Read the Docs')}
  127. </Button>
  128. <Button aria-label={t('Apply')} priority="primary" onClick={handleApply}>
  129. {t('Apply')}
  130. </Button>
  131. </ButtonBar>
  132. </Footer>
  133. </Fragment>
  134. );
  135. }
  136. const Instruction = styled('div')`
  137. margin-bottom: ${space(4)};
  138. `;
  139. const modalCss = css`
  140. @media (min-width: ${theme.breakpoints.medium}) {
  141. width: auto;
  142. max-width: 900px;
  143. }
  144. `;
  145. export default ColumnEditModal;
  146. export {modalCss};