contextMenu.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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 {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: !handleAddQueryToDashboard,
  46. onAction: handleAddQueryToDashboard,
  47. },
  48. ]}
  49. triggerProps={{
  50. 'aria-label': t('Widget actions'),
  51. size: 'xs',
  52. borderless: true,
  53. showChevron: false,
  54. icon: <IconEllipsis direction="down" size="sm" />,
  55. }}
  56. position="bottom-end"
  57. />
  58. );
  59. }
  60. function useHandleAddQueryToDashboard(
  61. organization: Organization,
  62. {projects, environments, datetime, op, mri, groupBy, query}: MetricsQuery,
  63. displayType?: MetricDisplayType
  64. ) {
  65. const router = useRouter();
  66. const {start, end, period} = datetime;
  67. return useMemo(() => {
  68. if (!mri || !op) {
  69. return undefined;
  70. }
  71. const field = MRIToField(mri, op);
  72. const limit = !groupBy?.length ? 1 : 10;
  73. const widgetQuery = {
  74. name: '',
  75. aggregates: [field],
  76. columns: groupBy ?? [],
  77. fields: [field],
  78. conditions: query ?? '',
  79. orderby: '',
  80. };
  81. const urlWidgetQuery = urlEncode({
  82. ...widgetQuery,
  83. aggregates: field,
  84. fields: field,
  85. columns: groupBy?.join(',') ?? '',
  86. });
  87. const widgetAsQueryParams = {
  88. source: DashboardWidgetSource.DDM,
  89. start,
  90. end,
  91. statsPeriod: period,
  92. defaultWidgetQuery: urlWidgetQuery,
  93. defaultTableColumns: [],
  94. defaultTitle: 'DDM Widget',
  95. environment: environments,
  96. displayType,
  97. project: projects,
  98. };
  99. return () =>
  100. openAddToDashboardModal({
  101. organization,
  102. selection: {
  103. projects,
  104. environments,
  105. datetime,
  106. },
  107. widget: {
  108. title: 'DDM Widget',
  109. displayType,
  110. widgetType: WidgetType.METRICS,
  111. limit,
  112. queries: [widgetQuery],
  113. },
  114. router,
  115. widgetAsQueryParams,
  116. location: router.location,
  117. });
  118. }, [
  119. datetime,
  120. displayType,
  121. end,
  122. environments,
  123. groupBy,
  124. mri,
  125. op,
  126. organization,
  127. period,
  128. projects,
  129. query,
  130. router,
  131. start,
  132. ]);
  133. }
  134. function useCreateAlert(organization: Organization, metricsQuery: MetricsQuery) {
  135. return useMemo(() => {
  136. if (
  137. !metricsQuery.mri ||
  138. !metricsQuery.op ||
  139. parseMRI(metricsQuery.mri)?.useCase !== 'custom' ||
  140. !organization.access.includes('alerts:write')
  141. ) {
  142. return undefined;
  143. }
  144. return () =>
  145. openModal(deps => (
  146. <OrganizationContext.Provider value={organization}>
  147. <CreateAlertModal metricsQuery={metricsQuery} {...deps} />
  148. </OrganizationContext.Provider>
  149. ));
  150. }, [metricsQuery, organization]);
  151. }
  152. const StyledDropdownMenuControl = styled(DropdownMenu)`
  153. margin: ${space(1)};
  154. `;