import {Component, Fragment} from 'react'; import {RouteComponentProps} from 'react-router'; import styled from '@emotion/styled'; import Feature from 'sentry/components/acl/feature'; import FeatureDisabled from 'sentry/components/acl/featureDisabled'; import CreateAlertButton from 'sentry/components/createAlertButton'; import {Hovercard} from 'sentry/components/hovercard'; import * as Layout from 'sentry/components/layouts/thirds'; import ExternalLink from 'sentry/components/links/externalLink'; import List from 'sentry/components/list'; import ListItem from 'sentry/components/list/listItem'; import {Panel, PanelBody, PanelHeader} from 'sentry/components/panels'; import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import {t} from 'sentry/locale'; import space from 'sentry/styles/space'; import {Organization, Project} from 'sentry/types'; import {logExperiment} from 'sentry/utils/analytics'; import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent'; import withProjects from 'sentry/utils/withProjects'; import BuilderBreadCrumbs from 'sentry/views/alerts/builder/builderBreadCrumbs'; import {Dataset} from 'sentry/views/alerts/rules/metric/types'; import {AlertRuleType} from 'sentry/views/alerts/types'; import {PRESET_AGGREGATES} from '../rules/metric/presets'; import { AlertType, AlertWizardAlertNames, AlertWizardRuleTemplates, getAlertWizardCategories, WizardRuleTemplate, } from './options'; import {AlertWizardPanelContent} from './panelContent'; import RadioPanelGroup from './radioPanelGroup'; type RouteParams = { orgId: string; projectId?: string; }; type Props = RouteComponentProps & { organization: Organization; projectId: string; projects: Project[]; }; type State = { alertOption: AlertType; }; const DEFAULT_ALERT_OPTION = 'issues'; class AlertWizard extends Component { state: State = { alertOption: this.props.location.query.alert_option in AlertWizardAlertNames ? this.props.location.query.alert_option : DEFAULT_ALERT_OPTION, }; componentDidMount() { // capture landing on the alert wizard page and viewing the issue alert by default this.trackView(); } trackView(alertType: AlertType = DEFAULT_ALERT_OPTION) { const {organization} = this.props; trackAdvancedAnalyticsEvent('alert_wizard.option_viewed', { organization, alert_type: alertType, }); } handleChangeAlertOption = (alertOption: AlertType) => { this.setState({alertOption}); this.trackView(alertOption); }; renderCreateAlertButton() { const {organization, location, params, projectId: _projectId} = this.props; const {alertOption} = this.state; const projectId = params.projectId ?? _projectId; const project = this.props.projects.find(p => p.slug === projectId); let metricRuleTemplate: Readonly | undefined = AlertWizardRuleTemplates[alertOption]; const isMetricAlert = !!metricRuleTemplate; const isTransactionDataset = metricRuleTemplate?.dataset === Dataset.TRANSACTIONS; if ( organization.features.includes('alert-crash-free-metrics') && metricRuleTemplate?.dataset === Dataset.SESSIONS ) { metricRuleTemplate = {...metricRuleTemplate, dataset: Dataset.METRICS}; } const supportedPreset = PRESET_AGGREGATES.filter( agg => agg.alertType === alertOption )[0]; const to = { pathname: `/organizations/${organization.slug}/alerts/new/${ isMetricAlert ? AlertRuleType.METRIC : AlertRuleType.ISSUE }/`, query: { ...(metricRuleTemplate ? metricRuleTemplate : {}), project: projectId, referrer: location?.query?.referrer, }, }; const renderNoAccess = p => ( } > {p.children(p)} ); let showUseTemplateBtn: boolean = !!project?.firstTransactionEvent && isMetricAlert && metricRuleTemplate?.dataset === Dataset.TRANSACTIONS && !!supportedPreset; if (showUseTemplateBtn) { logExperiment({ key: 'MetricAlertPresetExperiment', organization, }); } showUseTemplateBtn = showUseTemplateBtn && !!organization.experiments.MetricAlertPresetExperiment; return ( {({hasFeature}) => ( trackAdvancedAnalyticsEvent('alert_wizard.option_selected', { organization, alert_type: alertOption, }) } > {showUseTemplateBtn && ( { trackAdvancedAnalyticsEvent('growth.metric_alert_preset_use_template', { organization, preset: supportedPreset.id, }); }} hideIcon > {t('Use Template')} )} {t('Set Conditions')} )} ); } render() { const {organization, params, projectId: _projectId, routes, location} = this.props; const {alertOption} = this.state; const projectId = params.projectId ?? _projectId; const title = t('Alert Creation Wizard'); const panelContent = AlertWizardPanelContent[alertOption]; return ( {t('Select Alert')} {t('Errors')} {getAlertWizardCategories(organization).map( ({categoryHeading, options}, i) => ( {i > 0 && {categoryHeading} } { return [alertType, AlertWizardAlertNames[alertType]]; })} onChange={this.handleChangeAlertOption} value={alertOption} label="alert-option" /> ) )}
{AlertWizardAlertNames[alertOption]} {panelContent.description}{' '} {panelContent.docsLink && ( {t('Learn more')} )} {t('Examples')} {panelContent.examples.map((example, i) => ( {example} ))}
{this.renderCreateAlertButton()}
); } } const StyledHeaderContent = styled(Layout.HeaderContent)` overflow: visible; `; const CategoryTitle = styled('h2')` font-weight: normal; font-size: ${p => p.theme.fontSizeExtraLarge}; margin-bottom: ${space(1)} !important; `; const WizardBody = styled('div')` display: flex; padding-top: ${space(1)}; `; const WizardOptions = styled('div')` flex: 3; margin-right: ${space(3)}; padding-right: ${space(3)}; max-width: 300px; `; const WizardImage = styled('img')` max-height: 300px; `; const WizardPanel = styled(Panel)<{visible?: boolean}>` max-width: 700px; position: sticky; top: 20px; flex: 5; display: flex; ${p => !p.visible && 'visibility: hidden'}; flex-direction: column; align-items: start; align-self: flex-start; ${p => p.visible && 'animation: 0.6s pop ease forwards'}; @keyframes pop { 0% { transform: translateY(30px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } `; const ExampleList = styled(List)` margin-bottom: ${space(2)} !important; `; const WizardPanelBody = styled(PanelBody)` flex: 1; min-width: 100%; `; const PanelDescription = styled('p')` margin-bottom: ${space(2)}; `; const ExampleHeader = styled('div')` margin: 0 0 ${space(1)} 0; font-size: ${p => p.theme.fontSizeLarge}; `; const ExampleItem = styled(ListItem)` font-size: ${p => p.theme.fontSizeMedium}; `; const OptionsWrapper = styled('div')` margin-bottom: ${space(4)}; &:last-child { margin-bottom: 0; } `; const WizardFooter = styled('div')` border-top: 1px solid ${p => p.theme.border}; padding: ${space(1.5)} ${space(1.5)} ${space(1.5)} ${space(1.5)}; `; const WizardButtonContainer = styled('div')` display: flex; justify-content: flex-end; a:not(:last-child) { margin-right: ${space(1)}; } `; export default withProjects(AlertWizard);