contextMenu.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import {useMemo} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import {openAddToDashboardModal, openModal} from 'sentry/actionCreators/modal';
  4. import {navigateTo} from 'sentry/actionCreators/navigation';
  5. import type {MenuItemProps} from 'sentry/components/dropdownMenu';
  6. import {DropdownMenu} from 'sentry/components/dropdownMenu';
  7. import {
  8. IconClose,
  9. IconCopy,
  10. IconDashboard,
  11. IconEllipsis,
  12. IconSettings,
  13. IconSiren,
  14. } from 'sentry/icons';
  15. import {t} from 'sentry/locale';
  16. import type {Organization} from 'sentry/types';
  17. import {isCustomMeasurement, isCustomMetric} from 'sentry/utils/metrics';
  18. import {
  19. convertToDashboardWidget,
  20. encodeWidgetQuery,
  21. getWidgetAsQueryParams,
  22. getWidgetQuery,
  23. } from 'sentry/utils/metrics/dashboard';
  24. import {hasDDMFeature} from 'sentry/utils/metrics/features';
  25. import type {MetricDisplayType, MetricsQuery} from 'sentry/utils/metrics/types';
  26. import useOrganization from 'sentry/utils/useOrganization';
  27. import useRouter from 'sentry/utils/useRouter';
  28. import {useDDMContext} from 'sentry/views/ddm/context';
  29. import {CreateAlertModal} from 'sentry/views/ddm/createAlertModal';
  30. import {OrganizationContext} from 'sentry/views/organizationContext';
  31. type ContextMenuProps = {
  32. displayType: MetricDisplayType;
  33. metricsQuery: MetricsQuery;
  34. widgetIndex: number;
  35. };
  36. export function MetricQueryContextMenu({
  37. metricsQuery,
  38. displayType,
  39. widgetIndex,
  40. }: ContextMenuProps) {
  41. const organization = useOrganization();
  42. const router = useRouter();
  43. const {removeWidget, duplicateWidget, widgets} = useDDMContext();
  44. const createAlert = useMemo(
  45. () => getCreateAlert(organization, metricsQuery),
  46. [metricsQuery, organization]
  47. );
  48. const createDashboardWidget = useCreateDashboardWidget(
  49. organization,
  50. metricsQuery,
  51. displayType
  52. );
  53. const canDelete = widgets.length > 1;
  54. const items = useMemo<MenuItemProps[]>(
  55. () => [
  56. {
  57. leadingItems: [<IconCopy key="icon" />],
  58. key: 'duplicate',
  59. label: t('Duplicate'),
  60. onAction: () => {
  61. Sentry.metrics.increment('ddm.widget.duplicate');
  62. duplicateWidget(widgetIndex);
  63. },
  64. },
  65. {
  66. leadingItems: [<IconSiren key="icon" />],
  67. key: 'add-alert',
  68. label: t('Create Alert'),
  69. disabled: !createAlert,
  70. onAction: () => {
  71. Sentry.metrics.increment('ddm.widget.alert');
  72. createAlert?.();
  73. },
  74. },
  75. {
  76. leadingItems: [<IconDashboard key="icon" />],
  77. key: 'add-dashoard',
  78. label: t('Add to Dashboard'),
  79. disabled: !createDashboardWidget,
  80. onAction: () => {
  81. Sentry.metrics.increment('ddm.widget.dashboard');
  82. createDashboardWidget?.();
  83. },
  84. },
  85. {
  86. leadingItems: [<IconSettings key="icon" />],
  87. key: 'settings',
  88. label: t('Metric Settings'),
  89. disabled: !isCustomMetric({mri: metricsQuery.mri}),
  90. onAction: () => {
  91. Sentry.metrics.increment('ddm.widget.settings');
  92. navigateTo(
  93. `/settings/projects/:projectId/metrics/${encodeURIComponent(
  94. metricsQuery.mri
  95. )}`,
  96. router
  97. );
  98. },
  99. },
  100. {
  101. leadingItems: [<IconClose key="icon" />],
  102. key: 'delete',
  103. label: t('Remove Query'),
  104. disabled: !canDelete,
  105. onAction: () => {
  106. Sentry.metrics.increment('ddm.widget.delete');
  107. removeWidget(widgetIndex);
  108. },
  109. },
  110. ],
  111. [
  112. createAlert,
  113. createDashboardWidget,
  114. duplicateWidget,
  115. removeWidget,
  116. widgetIndex,
  117. canDelete,
  118. metricsQuery.mri,
  119. router,
  120. ]
  121. );
  122. if (!hasDDMFeature(organization)) {
  123. return null;
  124. }
  125. return (
  126. <DropdownMenu
  127. items={items}
  128. triggerProps={{
  129. 'aria-label': t('Widget actions'),
  130. size: 'md',
  131. showChevron: false,
  132. icon: <IconEllipsis direction="down" size="sm" />,
  133. }}
  134. position="bottom-end"
  135. />
  136. );
  137. }
  138. export function getCreateAlert(organization: Organization, metricsQuery: MetricsQuery) {
  139. if (
  140. !metricsQuery.mri ||
  141. !metricsQuery.op ||
  142. isCustomMeasurement(metricsQuery) ||
  143. !organization.access.includes('alerts:write')
  144. ) {
  145. return undefined;
  146. }
  147. return function () {
  148. return openModal(deps => (
  149. <OrganizationContext.Provider value={organization}>
  150. <CreateAlertModal metricsQuery={metricsQuery} {...deps} />
  151. </OrganizationContext.Provider>
  152. ));
  153. };
  154. }
  155. export function useCreateDashboardWidget(
  156. organization: Organization,
  157. metricsQuery: MetricsQuery,
  158. displayType?: MetricDisplayType
  159. ) {
  160. const router = useRouter();
  161. const {projects, environments, datetime} = metricsQuery;
  162. return useMemo(() => {
  163. if (!metricsQuery.mri || !metricsQuery.op || isCustomMeasurement(metricsQuery)) {
  164. return undefined;
  165. }
  166. const widgetQuery = getWidgetQuery(metricsQuery);
  167. const urlWidgetQuery = encodeWidgetQuery(widgetQuery);
  168. const widgetAsQueryParams = getWidgetAsQueryParams(
  169. metricsQuery,
  170. urlWidgetQuery,
  171. displayType
  172. );
  173. return () =>
  174. openAddToDashboardModal({
  175. organization,
  176. selection: {
  177. projects,
  178. environments,
  179. datetime,
  180. },
  181. widget: convertToDashboardWidget(metricsQuery, displayType),
  182. router,
  183. widgetAsQueryParams,
  184. location: router.location,
  185. });
  186. }, [metricsQuery, datetime, displayType, environments, organization, projects, router]);
  187. }