contextMenu.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {urlEncode} from '@sentry/utils';
  4. import {openAddToDashboardModal, openModal} from 'sentry/actionCreators/modal';
  5. import {DropdownMenu} from 'sentry/components/dropdownMenu';
  6. import {IconEllipsis} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import {space} from 'sentry/styles/space';
  9. import {Organization} from 'sentry/types';
  10. import {MetricDisplayType, MetricsQuery} from 'sentry/utils/metrics';
  11. import {hasDDMExperimentalFeature, hasDDMFeature} from 'sentry/utils/metrics/features';
  12. import {MRIToField, parseMRI} from 'sentry/utils/metrics/mri';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import useRouter from 'sentry/utils/useRouter';
  15. import {DashboardWidgetSource, WidgetType} from 'sentry/views/dashboards/types';
  16. import {CreateAlertModal} from 'sentry/views/ddm/createAlertModal';
  17. import {OrganizationContext} from 'sentry/views/organizationContext';
  18. type ContextMenuProps = {
  19. displayType: MetricDisplayType;
  20. metricsQuery: MetricsQuery;
  21. };
  22. export function MetricWidgetContextMenu({metricsQuery, displayType}: ContextMenuProps) {
  23. const organization = useOrganization();
  24. const createAlert = useCreateAlert(organization, metricsQuery);
  25. const handleAddQueryToDashboard = useHandleAddQueryToDashboard(
  26. organization,
  27. metricsQuery,
  28. displayType
  29. );
  30. if (!hasDDMFeature(organization)) {
  31. return null;
  32. }
  33. return (
  34. <StyledDropdownMenuControl
  35. items={[
  36. {
  37. key: 'add-alert',
  38. label: t('Create Alert'),
  39. disabled: !createAlert,
  40. onAction: createAlert,
  41. },
  42. {
  43. key: 'add-dashoard',
  44. label: t('Add to Dashboard'),
  45. disabled:
  46. !hasDDMExperimentalFeature(organization) || !handleAddQueryToDashboard,
  47. onAction: handleAddQueryToDashboard,
  48. },
  49. ]}
  50. triggerProps={{
  51. 'aria-label': t('Widget actions'),
  52. size: 'xs',
  53. borderless: true,
  54. showChevron: false,
  55. icon: <IconEllipsis direction="down" size="sm" />,
  56. }}
  57. position="bottom-end"
  58. />
  59. );
  60. }
  61. function useHandleAddQueryToDashboard(
  62. organization: Organization,
  63. {projects, environments, datetime, op, mri, groupBy, query}: MetricsQuery,
  64. displayType?: MetricDisplayType
  65. ) {
  66. const router = useRouter();
  67. const {start, end, period} = datetime;
  68. return useMemo(() => {
  69. if (!mri || !op) {
  70. return undefined;
  71. }
  72. const field = MRIToField(mri, op);
  73. const limit = !groupBy?.length ? 1 : 10;
  74. const widgetQuery = {
  75. name: '',
  76. aggregates: [field],
  77. columns: groupBy ?? [],
  78. fields: [field],
  79. conditions: query ?? '',
  80. orderby: '',
  81. };
  82. const urlWidgetQuery = urlEncode({
  83. ...widgetQuery,
  84. aggregates: field,
  85. fields: field,
  86. columns: groupBy?.join(',') ?? '',
  87. });
  88. const widgetAsQueryParams = {
  89. source: DashboardWidgetSource.DDM,
  90. start,
  91. end,
  92. statsPeriod: period,
  93. defaultWidgetQuery: urlWidgetQuery,
  94. defaultTableColumns: [],
  95. defaultTitle: 'DDM Widget',
  96. environment: environments,
  97. displayType,
  98. project: projects,
  99. };
  100. return () =>
  101. openAddToDashboardModal({
  102. organization,
  103. selection: {
  104. projects,
  105. environments,
  106. datetime,
  107. },
  108. widget: {
  109. title: 'DDM Widget',
  110. displayType,
  111. widgetType: WidgetType.METRICS,
  112. limit,
  113. queries: [widgetQuery],
  114. },
  115. router,
  116. widgetAsQueryParams,
  117. location: router.location,
  118. });
  119. }, [
  120. datetime,
  121. displayType,
  122. end,
  123. environments,
  124. groupBy,
  125. mri,
  126. op,
  127. organization,
  128. period,
  129. projects,
  130. query,
  131. router,
  132. start,
  133. ]);
  134. }
  135. function useCreateAlert(organization: Organization, metricsQuery: MetricsQuery) {
  136. return useMemo(() => {
  137. if (
  138. !metricsQuery.mri ||
  139. !metricsQuery.op ||
  140. parseMRI(metricsQuery.mri)?.useCase !== 'custom' ||
  141. !organization.access.includes('alerts:write')
  142. ) {
  143. return undefined;
  144. }
  145. return () =>
  146. openModal(deps => (
  147. <OrganizationContext.Provider value={organization}>
  148. <CreateAlertModal metricsQuery={metricsQuery} {...deps} />
  149. </OrganizationContext.Provider>
  150. ));
  151. }, [metricsQuery, organization]);
  152. }
  153. const StyledDropdownMenuControl = styled(DropdownMenu)`
  154. margin: ${space(1)};
  155. `;