toolbarSaveAs.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import styled from '@emotion/styled';
  2. import Feature from 'sentry/components/acl/feature';
  3. import ButtonBar from 'sentry/components/buttonBar';
  4. import {Button, LinkButton} from 'sentry/components/core/button';
  5. import {DropdownMenu, type MenuItemProps} from 'sentry/components/dropdownMenu';
  6. import {t} from 'sentry/locale';
  7. import {trackAnalytics} from 'sentry/utils/analytics';
  8. import {dedupeArray} from 'sentry/utils/dedupeArray';
  9. import {parseFunction, prettifyParsedFunction} from 'sentry/utils/discover/fields';
  10. import {useLocation} from 'sentry/utils/useLocation';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import usePageFilters from 'sentry/utils/usePageFilters';
  13. import useProjects from 'sentry/utils/useProjects';
  14. import {Dataset} from 'sentry/views/alerts/rules/metric/types';
  15. import {
  16. useExploreFields,
  17. useExploreGroupBys,
  18. useExploreMode,
  19. useExploreQuery,
  20. useExploreSortBys,
  21. useExploreVisualizes,
  22. } from 'sentry/views/explore/contexts/pageParamsContext';
  23. import {useAddToDashboard} from 'sentry/views/explore/hooks/useAddToDashboard';
  24. import {useChartInterval} from 'sentry/views/explore/hooks/useChartInterval';
  25. import {generateExploreCompareRoute} from 'sentry/views/explore/multiQueryMode/locationUtils';
  26. import {ToolbarSection} from 'sentry/views/explore/toolbar/styles';
  27. import {getAlertsUrl} from 'sentry/views/insights/common/utils/getAlertsUrl';
  28. export function ToolbarSaveAs() {
  29. const {addToDashboard} = useAddToDashboard();
  30. const location = useLocation();
  31. const organization = useOrganization();
  32. const {projects} = useProjects();
  33. const pageFilters = usePageFilters();
  34. const query = useExploreQuery();
  35. const groupBys = useExploreGroupBys();
  36. const visualizes = useExploreVisualizes();
  37. const fields = useExploreFields();
  38. const sortBys = useExploreSortBys();
  39. const mode = useExploreMode();
  40. const visualizeYAxes = visualizes.flatMap(v => v.yAxes);
  41. const [interval] = useChartInterval();
  42. const project =
  43. projects.length === 1
  44. ? projects[0]
  45. : projects.find(p => p.id === `${pageFilters.selection.projects[0]}`);
  46. const alertsUrls = visualizeYAxes.map((yAxis, index) => ({
  47. key: `${yAxis}-${index}`,
  48. label: yAxis,
  49. to: getAlertsUrl({
  50. project,
  51. query,
  52. pageFilters: pageFilters.selection,
  53. aggregate: yAxis,
  54. organization,
  55. dataset: Dataset.EVENTS_ANALYTICS_PLATFORM,
  56. interval,
  57. }),
  58. onAction: () => {
  59. trackAnalytics('trace_explorer.save_as', {
  60. save_type: 'alert',
  61. ui_source: 'toolbar',
  62. organization,
  63. });
  64. },
  65. }));
  66. const items: MenuItemProps[] = [];
  67. if (organization.features.includes('alerts-eap')) {
  68. items.push({
  69. key: 'create-alert',
  70. label: t('An Alert for'),
  71. children: alertsUrls ?? [],
  72. disabled: !alertsUrls || alertsUrls.length === 0,
  73. isSubmenu: true,
  74. });
  75. }
  76. if (organization.features.includes('dashboards-eap')) {
  77. const disableAddToDashboard = !organization.features.includes('dashboards-edit');
  78. const chartOptions = visualizes.map((chart, index) => {
  79. const dedupedYAxes = dedupeArray(chart.yAxes);
  80. const formattedYAxes = dedupedYAxes.map(yaxis => {
  81. const func = parseFunction(yaxis);
  82. return func ? prettifyParsedFunction(func) : undefined;
  83. });
  84. return {
  85. key: chart.label,
  86. label: t('%s - %s', chart.label, formattedYAxes.filter(Boolean).join(', ')),
  87. onAction: () => {
  88. if (disableAddToDashboard) {
  89. return undefined;
  90. }
  91. trackAnalytics('trace_explorer.save_as', {
  92. save_type: 'dashboard',
  93. ui_source: 'toolbar',
  94. organization,
  95. });
  96. return addToDashboard(index);
  97. },
  98. };
  99. });
  100. items.push({
  101. key: 'add-to-dashboard',
  102. textValue: t('A Dashboard widget'),
  103. isSubmenu: chartOptions.length > 1 ? true : false,
  104. label: (
  105. <Feature
  106. hookName="feature-disabled:dashboards-edit"
  107. features="organizations:dashboards-edit"
  108. renderDisabled={() => <DisabledText>{t('A Dashboard widget')}</DisabledText>}
  109. >
  110. {t('A Dashboard widget')}
  111. </Feature>
  112. ),
  113. disabled: disableAddToDashboard,
  114. children: chartOptions.length > 1 ? chartOptions : undefined,
  115. onAction: () => {
  116. if (disableAddToDashboard || chartOptions.length > 1) {
  117. return undefined;
  118. }
  119. trackAnalytics('trace_explorer.save_as', {
  120. save_type: 'dashboard',
  121. ui_source: 'toolbar',
  122. organization,
  123. });
  124. return addToDashboard(0);
  125. },
  126. });
  127. }
  128. if (items.length === 0) {
  129. return null;
  130. }
  131. return (
  132. <ToolbarSection data-test-id="section-save-as">
  133. <ButtonBar gap={1}>
  134. <DropdownMenu
  135. items={items}
  136. trigger={triggerProps => (
  137. <SaveAsButton
  138. {...triggerProps}
  139. aria-label={t('Save as')}
  140. onClick={e => {
  141. e.stopPropagation();
  142. e.preventDefault();
  143. triggerProps.onClick?.(e);
  144. }}
  145. >
  146. {`${t('Save as')}\u2026`}
  147. </SaveAsButton>
  148. )}
  149. />
  150. {organization.features.includes('explore-multi-query') && (
  151. <LinkButton
  152. aria-label={t('Compare')}
  153. onClick={() =>
  154. trackAnalytics('trace_explorer.compare', {
  155. organization,
  156. })
  157. }
  158. to={generateExploreCompareRoute({
  159. organization,
  160. mode,
  161. location,
  162. query,
  163. yAxes: [visualizeYAxes[0]!],
  164. groupBys,
  165. fields,
  166. sortBys,
  167. })}
  168. >{`${t('Compare Queries')}`}</LinkButton>
  169. )}
  170. </ButtonBar>
  171. </ToolbarSection>
  172. );
  173. }
  174. const DisabledText = styled('span')`
  175. color: ${p => p.theme.disabled};
  176. `;
  177. const SaveAsButton = styled(Button)`
  178. width: 100%;
  179. `;