create.tsx 6.6 KB

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