pageHeaderActions.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import {useMemo} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import {navigateTo} from 'sentry/actionCreators/navigation';
  4. import {Button} from 'sentry/components/button';
  5. import ButtonBar from 'sentry/components/buttonBar';
  6. import {DropdownMenu} from 'sentry/components/dropdownMenu';
  7. import {
  8. IconAdd,
  9. IconBookmark,
  10. IconDashboard,
  11. IconEllipsis,
  12. IconSettings,
  13. IconSiren,
  14. } from 'sentry/icons';
  15. import {t} from 'sentry/locale';
  16. import {trackAnalytics} from 'sentry/utils/analytics';
  17. import {isCustomMeasurement} from 'sentry/utils/metrics';
  18. import {MRIToField} from 'sentry/utils/metrics/mri';
  19. import {middleEllipsis} from 'sentry/utils/middleEllipsis';
  20. import useOrganization from 'sentry/utils/useOrganization';
  21. import usePageFilters from 'sentry/utils/usePageFilters';
  22. import useRouter from 'sentry/utils/useRouter';
  23. import {useDDMContext} from 'sentry/views/ddm/context';
  24. import {getCreateAlert} from 'sentry/views/ddm/contextMenu';
  25. import {QuerySymbol} from 'sentry/views/ddm/querySymbol';
  26. import {useCreateDashboard} from 'sentry/views/ddm/useCreateDashboard';
  27. interface Props {
  28. addCustomMetric: (referrer: string) => void;
  29. showCustomMetricButton: boolean;
  30. }
  31. export function PageHeaderActions({showCustomMetricButton, addCustomMetric}: Props) {
  32. const router = useRouter();
  33. const organization = useOrganization();
  34. const {selection} = usePageFilters();
  35. const createDashboard = useCreateDashboard();
  36. const {
  37. addWidget,
  38. isDefaultQuery,
  39. setDefaultQuery,
  40. widgets,
  41. showQuerySymbols,
  42. selectedWidgetIndex,
  43. } = useDDMContext();
  44. const hasEmptyWidget = widgets.length === 0 || widgets.some(widget => !widget.mri);
  45. const items = useMemo(
  46. () => [
  47. {
  48. leadingItems: [<IconAdd isCircled key="icon" />],
  49. key: 'add-query',
  50. label: t('Add Query'),
  51. disabled: hasEmptyWidget,
  52. onAction: () => {
  53. trackAnalytics('ddm.widget.add', {
  54. organization,
  55. });
  56. Sentry.metrics.increment('ddm.widget.add');
  57. addWidget();
  58. },
  59. },
  60. {
  61. leadingItems: [<IconDashboard key="icon" />],
  62. key: 'add-dashboard',
  63. label: t('Add to Dashboard'),
  64. onAction: () => {
  65. trackAnalytics('ddm.add-to-dashboard', {
  66. organization,
  67. source: 'global',
  68. });
  69. createDashboard();
  70. },
  71. },
  72. {
  73. leadingItems: [<IconSettings key="icon" />],
  74. key: 'metrics-settings',
  75. label: t('Metrics Settings'),
  76. onAction: () => navigateTo(`/settings/projects/:projectId/metrics/`, router),
  77. },
  78. ],
  79. [addWidget, createDashboard, hasEmptyWidget, organization, router]
  80. );
  81. const alertItems = useMemo(
  82. () =>
  83. widgets.map((widget, index) => {
  84. const createAlert = getCreateAlert(organization, {
  85. datetime: selection.datetime,
  86. projects: selection.projects,
  87. environments: selection.environments,
  88. query: widget.query,
  89. mri: widget.mri,
  90. groupBy: widget.groupBy,
  91. op: widget.op,
  92. });
  93. return {
  94. leadingItems: showQuerySymbols
  95. ? [
  96. <QuerySymbol
  97. key="icon"
  98. index={index}
  99. isSelected={index === selectedWidgetIndex}
  100. />,
  101. ]
  102. : [],
  103. key: `add-alert-${index}`,
  104. label: widget.mri
  105. ? middleEllipsis(MRIToField(widget.mri, widget.op!), 60, /\.|-|_/)
  106. : t('Select a metric to create an alert'),
  107. tooltip: isCustomMeasurement({mri: widget.mri})
  108. ? t('Custom measurements cannot be used to create alerts')
  109. : undefined,
  110. disabled: !createAlert,
  111. onAction: () => {
  112. trackAnalytics('ddm.create-alert', {
  113. organization,
  114. source: 'global',
  115. });
  116. createAlert?.();
  117. },
  118. };
  119. }),
  120. [
  121. organization,
  122. selectedWidgetIndex,
  123. selection.datetime,
  124. selection.environments,
  125. selection.projects,
  126. showQuerySymbols,
  127. widgets,
  128. ]
  129. );
  130. return (
  131. <ButtonBar gap={1}>
  132. {showCustomMetricButton && (
  133. <Button priority="primary" onClick={() => addCustomMetric('header')} size="sm">
  134. {t('Add Custom Metric')}
  135. </Button>
  136. )}
  137. <Button
  138. size="sm"
  139. icon={<IconBookmark isSolid={isDefaultQuery} />}
  140. onClick={() => setDefaultQuery(isDefaultQuery ? null : router.location.query)}
  141. >
  142. {isDefaultQuery ? t('Remove Default') : t('Save as default')}
  143. </Button>
  144. {alertItems.length === 1 ? (
  145. <Button
  146. size="sm"
  147. icon={<IconSiren />}
  148. disabled={!alertItems[0].onAction}
  149. onClick={alertItems[0].onAction}
  150. >
  151. {t('Create Alert')}
  152. </Button>
  153. ) : (
  154. <DropdownMenu
  155. items={alertItems}
  156. triggerLabel={t('Create Alert')}
  157. triggerProps={{
  158. size: 'sm',
  159. showChevron: false,
  160. icon: <IconSiren direction="down" size="sm" />,
  161. }}
  162. position="bottom-end"
  163. />
  164. )}
  165. <DropdownMenu
  166. items={items}
  167. triggerProps={{
  168. 'aria-label': t('Page actions'),
  169. size: 'sm',
  170. showChevron: false,
  171. icon: <IconEllipsis direction="down" size="xs" />,
  172. }}
  173. position="bottom-end"
  174. />
  175. </ButtonBar>
  176. );
  177. }