contextMenu.tsx 5.4 KB

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