toolbarSaveAs.tsx 4.8 KB

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