contextMenu.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {openAddToDashboardModal} from 'sentry/actionCreators/modal';
  4. import {DropdownMenu} from 'sentry/components/dropdownMenu';
  5. import {IconEllipsis} from 'sentry/icons';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {Organization} from 'sentry/types';
  9. import {MetricDisplayType, MetricsQuery, mriToField} from 'sentry/utils/metrics';
  10. import useOrganization from 'sentry/utils/useOrganization';
  11. import usePageFilters from 'sentry/utils/usePageFilters';
  12. import useProjects from 'sentry/utils/useProjects';
  13. import useRouter from 'sentry/utils/useRouter';
  14. import {Dataset, EventTypes} from 'sentry/views/alerts/rules/metric/types';
  15. import {DashboardWidgetSource} from 'sentry/views/dashboards/types';
  16. type ContextMenuProps = {
  17. displayType: MetricDisplayType;
  18. metricsQuery: MetricsQuery;
  19. };
  20. export function MetricWidgetContextMenu({metricsQuery, displayType}: ContextMenuProps) {
  21. const organization = useOrganization();
  22. const createAlertUrl = useCreateAlertUrl(organization, metricsQuery);
  23. const handleAddQueryToDashboard = useHandleAddQueryToDashboard(
  24. organization,
  25. metricsQuery,
  26. displayType
  27. );
  28. if (!organization.features.includes('ddm-experimental')) {
  29. return null;
  30. }
  31. return (
  32. <StyledDropdownMenuControl
  33. items={[
  34. {
  35. key: 'add-alert',
  36. label: t('Create Alert'),
  37. disabled: !createAlertUrl,
  38. to: createAlertUrl,
  39. },
  40. {
  41. key: 'add-dashoard',
  42. label: t('Add to Dashboard'),
  43. disabled: !handleAddQueryToDashboard,
  44. onAction: handleAddQueryToDashboard,
  45. },
  46. ]}
  47. triggerProps={{
  48. 'aria-label': t('Widget actions'),
  49. size: 'xs',
  50. borderless: true,
  51. showChevron: false,
  52. icon: <IconEllipsis direction="down" size="sm" />,
  53. }}
  54. position="bottom-end"
  55. />
  56. );
  57. }
  58. function useHandleAddQueryToDashboard(
  59. organization: Organization,
  60. {projects, environments, datetime, op, mri, groupBy, query}: MetricsQuery,
  61. displayType?: MetricDisplayType
  62. ) {
  63. const router = useRouter();
  64. const {start, end, period} = datetime;
  65. return useMemo(() => {
  66. if (!mri || !op) {
  67. return undefined;
  68. }
  69. const field = mriToField(mri, op);
  70. const limit = !groupBy?.length ? 1 : 10;
  71. const widgetAsQueryParams = {
  72. ...router.location?.query,
  73. source: DashboardWidgetSource.DDM,
  74. start,
  75. end,
  76. statsPeriod: period,
  77. defaultWidgetQuery: field,
  78. defaultTableColumns: [],
  79. defaultTitle: 'DDM Widget',
  80. displayType,
  81. };
  82. return () =>
  83. openAddToDashboardModal({
  84. organization,
  85. selection: {
  86. projects,
  87. environments,
  88. datetime,
  89. },
  90. widget: {
  91. title: 'DDM Widget',
  92. displayType,
  93. widgetType: 'custom-metrics',
  94. limit,
  95. queries: [
  96. {
  97. name: '',
  98. aggregates: [field],
  99. columns: groupBy ?? [],
  100. fields: [field],
  101. conditions: query,
  102. },
  103. ],
  104. },
  105. router,
  106. widgetAsQueryParams,
  107. location: router.location,
  108. });
  109. }, [
  110. datetime,
  111. displayType,
  112. end,
  113. environments,
  114. groupBy,
  115. mri,
  116. op,
  117. organization,
  118. period,
  119. projects,
  120. query,
  121. router,
  122. start,
  123. ]);
  124. }
  125. function useCreateAlertUrl(organization: Organization, metricsQuery: MetricsQuery) {
  126. const projects = useProjects();
  127. const pageFilters = usePageFilters();
  128. const selectedProjects = pageFilters.selection.projects;
  129. const firstProjectSlug =
  130. selectedProjects.length > 0 &&
  131. projects.projects.find(p => p.id === selectedProjects[0].toString())?.slug;
  132. return useMemo(() => {
  133. if (!firstProjectSlug || !metricsQuery.mri || !metricsQuery.op) {
  134. return undefined;
  135. }
  136. return {
  137. pathname: `/organizations/${organization.slug}/alerts/new/metric/`,
  138. query: {
  139. // Needed, so alerts-create also collects environment via event view
  140. createFromDiscover: true,
  141. dataset: Dataset.GENERIC_METRICS,
  142. eventTypes: EventTypes.TRANSACTION,
  143. aggregate: mriToField(metricsQuery.mri, metricsQuery.op as string),
  144. referrer: 'ddm',
  145. // Event type also needs to be added to the query
  146. query: `${metricsQuery.query} event.type:transaction`.trim(),
  147. environment: metricsQuery.environments,
  148. project: firstProjectSlug,
  149. },
  150. };
  151. }, [
  152. firstProjectSlug,
  153. metricsQuery.environments,
  154. metricsQuery.mri,
  155. metricsQuery.op,
  156. metricsQuery.query,
  157. organization.slug,
  158. ]);
  159. }
  160. const StyledDropdownMenuControl = styled(DropdownMenu)`
  161. margin: ${space(1)};
  162. `;