create.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import {Fragment, useEffect, useRef} from 'react';
  2. import * as Layout from 'sentry/components/layouts/thirds';
  3. import LoadingIndicator from 'sentry/components/loadingIndicator';
  4. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  5. import {t} from 'sentry/locale';
  6. import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';
  7. import type {Member, Organization} from 'sentry/types/organization';
  8. import type {Project} from 'sentry/types/project';
  9. import EventView from 'sentry/utils/discover/eventView';
  10. import {uniqueId} from 'sentry/utils/guid';
  11. import useRouteAnalyticsEventNames from 'sentry/utils/routeAnalytics/useRouteAnalyticsEventNames';
  12. import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams';
  13. import normalizeUrl from 'sentry/utils/url/normalizeUrl';
  14. import {useNavigate} from 'sentry/utils/useNavigate';
  15. import {useUserTeams} from 'sentry/utils/useUserTeams';
  16. import BuilderBreadCrumbs from 'sentry/views/alerts/builder/builderBreadCrumbs';
  17. import {makeAlertsPathname} from 'sentry/views/alerts/pathnames';
  18. import IssueRuleEditor from 'sentry/views/alerts/rules/issue';
  19. import MetricRulesCreate from 'sentry/views/alerts/rules/metric/create';
  20. import MetricRuleDuplicate from 'sentry/views/alerts/rules/metric/duplicate';
  21. import {UptimeAlertForm} from 'sentry/views/alerts/rules/uptime/uptimeAlertForm';
  22. import {AlertRuleType} from 'sentry/views/alerts/types';
  23. import type {
  24. AlertType as WizardAlertType,
  25. WizardRuleTemplate,
  26. } from 'sentry/views/alerts/wizard/options';
  27. import {
  28. AlertWizardAlertNames,
  29. DEFAULT_WIZARD_TEMPLATE,
  30. } from 'sentry/views/alerts/wizard/options';
  31. import {getAlertTypeFromAggregateDataset} from 'sentry/views/alerts/wizard/utils';
  32. import MonitorForm from 'sentry/views/monitors/components/monitorForm';
  33. import type {Monitor} from 'sentry/views/monitors/types';
  34. type RouteParams = {
  35. alertType?: AlertRuleType;
  36. projectId?: string;
  37. };
  38. type Props = RouteComponentProps<RouteParams> & {
  39. hasMetricAlerts: boolean;
  40. members: Member[] | undefined;
  41. organization: Organization;
  42. project: Project;
  43. };
  44. function Create(props: Props) {
  45. const {hasMetricAlerts, organization, project, location, members, params, router} =
  46. props;
  47. const {
  48. aggregate,
  49. dataset,
  50. eventTypes,
  51. createFromDuplicate,
  52. duplicateRuleId,
  53. createFromDiscover,
  54. query,
  55. createFromWizard,
  56. } = location?.query ?? {};
  57. const alertType = params.alertType || AlertRuleType.METRIC;
  58. const sessionId = useRef(uniqueId());
  59. const navigate = useNavigate();
  60. const isDuplicateRule = createFromDuplicate === 'true' && duplicateRuleId;
  61. useEffect(() => {
  62. // TODO(taylangocmen): Remove redirect with aggregate && dataset && eventTypes, init from template
  63. if (
  64. alertType === AlertRuleType.METRIC &&
  65. !(aggregate && dataset && eventTypes) &&
  66. !createFromDuplicate
  67. ) {
  68. router.replace(
  69. normalizeUrl({
  70. ...location,
  71. pathname: makeAlertsPathname({
  72. path: `/new/${alertType}/`,
  73. organization,
  74. }),
  75. query: {
  76. ...location.query,
  77. ...DEFAULT_WIZARD_TEMPLATE,
  78. project: project.slug,
  79. },
  80. })
  81. );
  82. }
  83. }, [
  84. alertType,
  85. aggregate,
  86. dataset,
  87. eventTypes,
  88. createFromDuplicate,
  89. router,
  90. location,
  91. organization.slug,
  92. project.slug,
  93. organization,
  94. ]);
  95. const {teams, isLoading} = useUserTeams();
  96. useRouteAnalyticsParams({
  97. project_id: project.id,
  98. session_id: sessionId.current,
  99. alert_type: alertType,
  100. duplicate_rule: isDuplicateRule ? 'true' : 'false',
  101. wizard_v3: 'true',
  102. });
  103. useRouteAnalyticsEventNames('new_alert_rule.viewed', 'New Alert Rule: Viewed');
  104. const wizardTemplate: WizardRuleTemplate = {
  105. aggregate: aggregate ?? DEFAULT_WIZARD_TEMPLATE.aggregate,
  106. dataset: dataset ?? DEFAULT_WIZARD_TEMPLATE.dataset,
  107. eventTypes: eventTypes ?? DEFAULT_WIZARD_TEMPLATE.eventTypes,
  108. query: query ?? DEFAULT_WIZARD_TEMPLATE.query,
  109. };
  110. const eventView = createFromDiscover ? EventView.fromLocation(location) : undefined;
  111. let wizardAlertType: undefined | WizardAlertType;
  112. if (createFromWizard && alertType === AlertRuleType.METRIC) {
  113. wizardAlertType = wizardTemplate
  114. ? getAlertTypeFromAggregateDataset(wizardTemplate)
  115. : 'issues';
  116. }
  117. const title = t('New Alert Rule');
  118. return (
  119. <Fragment>
  120. <SentryDocumentTitle title={title} projectSlug={project.slug} />
  121. <Layout.Header>
  122. <Layout.HeaderContent>
  123. <BuilderBreadCrumbs
  124. organization={organization}
  125. alertName={t('Set Conditions')}
  126. title={wizardAlertType ? t('Select Alert') : title}
  127. projectSlug={project.slug}
  128. />
  129. <Layout.Title>
  130. {wizardAlertType
  131. ? `${t('Set Conditions for')} ${AlertWizardAlertNames[wizardAlertType]}`
  132. : title}
  133. </Layout.Title>
  134. </Layout.HeaderContent>
  135. </Layout.Header>
  136. <Layout.Body>
  137. {isLoading ? (
  138. <LoadingIndicator />
  139. ) : (
  140. <Fragment>
  141. {alertType === AlertRuleType.UPTIME ? (
  142. <UptimeAlertForm {...props} />
  143. ) : alertType === AlertRuleType.CRONS ? (
  144. <MonitorForm
  145. apiMethod="POST"
  146. apiEndpoint={`/organizations/${organization.slug}/monitors/`}
  147. onSubmitSuccess={(data: Monitor) =>
  148. navigate(
  149. makeAlertsPathname({
  150. path: `/rules/crons/${data.project.slug}/${data.slug}/details/`,
  151. organization,
  152. })
  153. )
  154. }
  155. submitLabel={t('Create')}
  156. />
  157. ) : !hasMetricAlerts || alertType === AlertRuleType.ISSUE ? (
  158. <IssueRuleEditor
  159. {...props}
  160. userTeamIds={teams.map(({id}) => id)}
  161. members={members}
  162. />
  163. ) : (
  164. hasMetricAlerts &&
  165. alertType === AlertRuleType.METRIC &&
  166. (isDuplicateRule ? (
  167. <MetricRuleDuplicate
  168. {...props}
  169. eventView={eventView}
  170. wizardTemplate={wizardTemplate}
  171. sessionId={sessionId.current}
  172. userTeamIds={teams.map(({id}) => id)}
  173. />
  174. ) : (
  175. <MetricRulesCreate
  176. {...props}
  177. eventView={eventView}
  178. wizardTemplate={wizardTemplate}
  179. sessionId={sessionId.current}
  180. userTeamIds={teams.map(({id}) => id)}
  181. />
  182. ))
  183. )}
  184. </Fragment>
  185. )}
  186. </Layout.Body>
  187. </Fragment>
  188. );
  189. }
  190. export default Create;