@@ -24,6 +24,7 @@ import {SearchInvalidTag} from 'sentry/components/smartSearchBar/searchInvalidTa
2424import { t , tct } from 'sentry/locale' ;
2525import { space } from 'sentry/styles/space' ;
2626import type { Environment , Organization , Project , SelectValue } from 'sentry/types' ;
27+ import { ActivationConditionType , MonitorType } from 'sentry/types/alerts' ;
2728import { getDisplayName } from 'sentry/utils/environment' ;
2829import { hasCustomMetrics } from 'sentry/utils/metrics/features' ;
2930import { getMRI } from 'sentry/utils/metrics/mri' ;
@@ -69,20 +70,28 @@ type Props = {
6970 disabled : boolean ;
7071 onComparisonDeltaChange : ( value : number ) => void ;
7172 onFilterSearch : ( query : string , isQueryValid ) => void ;
73+ onMonitorTypeSelect : ( activatedAlertFields : {
74+ activationCondition ?: ActivationConditionType | undefined ;
75+ monitorType ?: MonitorType ;
76+ monitorWindowSuffix ?: string | undefined ;
77+ monitorWindowValue ?: number | undefined ;
78+ } ) => void ;
7279 onTimeWindowChange : ( value : number ) => void ;
7380 organization : Organization ;
7481 project : Project ;
7582 projects : Project [ ] ;
7683 router : InjectedRouter ;
7784 thresholdChart : React . ReactNode ;
7885 timeWindow : number ;
86+ activationCondition ?: ActivationConditionType ;
7987 allowChangeEventTypes ?: boolean ;
8088 comparisonDelta ?: number ;
8189 disableProjectSelector ?: boolean ;
8290 isErrorMigration ?: boolean ;
8391 isExtrapolatedChartData ?: boolean ;
8492 isTransactionMigration ?: boolean ;
8593 loadingProjects ?: boolean ;
94+ monitorType ?: number ;
8695} ;
8796
8897type State = {
@@ -162,6 +171,20 @@ class RuleConditionsForm extends PureComponent<Props, State> {
162171 }
163172 }
164173
174+ get selectControlStyles ( ) {
175+ return {
176+ control : ( provided : { [ x : string ] : string | number | boolean } ) => ( {
177+ ...provided ,
178+ minWidth : 200 ,
179+ maxWidth : 300 ,
180+ } ) ,
181+ container : ( provided : { [ x : string ] : string | number | boolean } ) => ( {
182+ ...provided ,
183+ margin : `${ space ( 0.5 ) } ` ,
184+ } ) ,
185+ } ;
186+ }
187+
165188 renderEventTypeFilter ( ) {
166189 const { organization, disabled, alertType, isErrorMigration} = this . props ;
167190
@@ -326,8 +349,15 @@ class RuleConditionsForm extends PureComponent<Props, State> {
326349 }
327350
328351 renderInterval ( ) {
329- const { organization, disabled, alertType, timeWindow, onTimeWindowChange, project} =
330- this . props ;
352+ const {
353+ organization,
354+ disabled,
355+ alertType,
356+ timeWindow,
357+ onTimeWindowChange,
358+ project,
359+ monitorType,
360+ } = this . props ;
331361
332362 return (
333363 < Fragment >
@@ -353,27 +383,107 @@ class RuleConditionsForm extends PureComponent<Props, State> {
353383 alertType = { alertType }
354384 required
355385 />
356- < SelectControl
357- name = "timeWindow"
358- styles = { {
359- control : ( provided : { [ x : string ] : string | number | boolean } ) => ( {
360- ...provided ,
361- minWidth : 200 ,
362- maxWidth : 300 ,
363- } ) ,
364- container : ( provided : { [ x : string ] : string | number | boolean } ) => ( {
365- ...provided ,
366- margin : `${ space ( 0.5 ) } ` ,
367- } ) ,
368- } }
369- options = { this . timeWindowOptions }
370- required
371- isDisabled = { disabled }
372- value = { timeWindow }
373- onChange = { ( { value} ) => onTimeWindowChange ( value ) }
374- inline = { false }
375- flexibleControlStateSize
376- />
386+ { monitorType === MonitorType . CONTINUOUS && (
387+ < SelectControl
388+ name = "timeWindow"
389+ styles = { this . selectControlStyles }
390+ options = { this . timeWindowOptions }
391+ required = { monitorType === MonitorType . CONTINUOUS }
392+ isDisabled = { disabled }
393+ value = { timeWindow }
394+ onChange = { ( { value} ) => onTimeWindowChange ( value ) }
395+ inline = { false }
396+ flexibleControlStateSize
397+ />
398+ ) }
399+ </ FormRow >
400+ </ Fragment >
401+ ) ;
402+ }
403+
404+ renderMonitorTypeSelect ( ) {
405+ const {
406+ onMonitorTypeSelect,
407+ monitorType,
408+ activationCondition,
409+ timeWindow,
410+ onTimeWindowChange,
411+ } = this . props ;
412+
413+ return (
414+ < Fragment >
415+ < StyledListItem >
416+ < StyledListTitle >
417+ < div > { t ( 'Select Monitor Type' ) } </ div >
418+ </ StyledListTitle >
419+ </ StyledListItem >
420+ < FormRow >
421+ < MonitorSelect >
422+ < MonitorCard
423+ position = "left"
424+ isSelected = { monitorType === MonitorType . CONTINUOUS }
425+ onClick = { ( ) =>
426+ onMonitorTypeSelect ( {
427+ monitorType : MonitorType . CONTINUOUS ,
428+ activationCondition,
429+ } )
430+ }
431+ >
432+ < strong > { t ( 'Continuous' ) } </ strong >
433+ < div > { t ( 'Continuously monitor trends for the metrics outlined below' ) } </ div >
434+ </ MonitorCard >
435+ < MonitorCard
436+ position = "right"
437+ isSelected = { monitorType === MonitorType . ACTIVATED }
438+ onClick = { ( ) =>
439+ onMonitorTypeSelect ( {
440+ monitorType : MonitorType . ACTIVATED ,
441+ } )
442+ }
443+ >
444+ < strong > Conditional</ strong >
445+ { monitorType === MonitorType . ACTIVATED ? (
446+ < ActivatedAlertFields >
447+ { `${ t ( 'Monitor' ) } ` }
448+ < SelectControl
449+ name = "activationCondition"
450+ styles = { this . selectControlStyles }
451+ options = { [
452+ {
453+ value : ActivationConditionType . RELEASE_CREATION ,
454+ label : t ( 'New Release' ) ,
455+ } ,
456+ {
457+ value : ActivationConditionType . DEPLOY_CREATION ,
458+ label : t ( 'New Deploy' ) ,
459+ } ,
460+ ] }
461+ required
462+ value = { activationCondition }
463+ onChange = { ( { value} ) =>
464+ onMonitorTypeSelect ( { activationCondition : value } )
465+ }
466+ inline = { false }
467+ flexibleControlStateSize
468+ />
469+ { ` ${ t ( 'for' ) } ` }
470+ < SelectControl
471+ name = "timeWindow"
472+ styles = { this . selectControlStyles }
473+ options = { this . timeWindowOptions }
474+ value = { timeWindow }
475+ onChange = { ( { value} ) => onTimeWindowChange ( value ) }
476+ inline = { false }
477+ flexibleControlStateSize
478+ />
479+ </ ActivatedAlertFields >
480+ ) : (
481+ < div >
482+ { t ( 'Temporarily monitor specified query given activation condition' ) }
483+ </ div >
484+ ) }
485+ </ MonitorCard >
486+ </ MonitorSelect >
377487 </ FormRow >
378488 </ Fragment >
379489 ) ;
@@ -395,6 +505,7 @@ class RuleConditionsForm extends PureComponent<Props, State> {
395505 } = this . props ;
396506
397507 const { environments} = this . state ;
508+ const hasActivatedAlerts = organization . features . includes ( 'activated-alert-rules' ) ;
398509
399510 const environmentOptions : SelectValue < string | null > [ ] = [
400511 {
@@ -425,6 +536,7 @@ class RuleConditionsForm extends PureComponent<Props, State> {
425536 ) }
426537 />
427538 ) }
539+ { hasActivatedAlerts && this . renderMonitorTypeSelect ( ) }
428540 { ! isErrorMigration && this . renderInterval ( ) }
429541 < StyledListItem > { t ( 'Filter events' ) } </ StyledListItem >
430542 < FormRow noMargin columns = { 1 + ( allowChangeEventTypes ? 1 : 0 ) + 1 } >
@@ -653,4 +765,46 @@ const FormRow = styled('div')<{columns?: number; noMargin?: boolean}>`
653765 ` }
654766` ;
655767
768+ const MonitorSelect = styled ( 'div' ) `
769+ border-radius: ${ p => p . theme . borderRadius } ;
770+ border: 1px solid ${ p => p . theme . border } ;
771+ width: 100%;
772+ display: grid;
773+ grid-template-columns: 1fr 1fr;
774+ ` ;
775+
776+ type MonitorCardProps = {
777+ isSelected : boolean ;
778+ /**
779+ * Adds hover and focus states to the card
780+ */
781+ position : 'left' | 'right' ;
782+ } ;
783+
784+ const MonitorCard = styled ( 'div' ) < MonitorCardProps > `
785+ padding: ${ space ( 1 ) } ;
786+ display: flex;
787+ flex-grow: 1;
788+ flex-direction: column;
789+ cursor: pointer;
790+
791+ &:focus,
792+ &:hover {
793+ outline: 1px solid ${ p => p . theme . purple200 } ;
794+ background-color: ${ p => p . theme . backgroundSecondary } ;
795+ }
796+
797+ border-top-left-radius: ${ p => ( p . position === 'left' ? p . theme . borderRadius : 0 ) } ;
798+ border-bottom-left-radius: ${ p => ( p . position === 'left' ? p . theme . borderRadius : 0 ) } ;
799+ border-top-right-radius: ${ p => ( p . position !== 'left' ? p . theme . borderRadius : 0 ) } ;
800+ border-bottom-right-radius: ${ p => ( p . position !== 'left' ? p . theme . borderRadius : 0 ) } ;
801+ outline: ${ p => ( p . isSelected ? `1px solid ${ p . theme . purple400 } ` : 'none' ) } ;
802+ ` ;
803+
804+ const ActivatedAlertFields = styled ( 'div' ) `
805+ display: flex;
806+ align-items: center;
807+ justify-content: space-between;
808+ ` ;
809+
656810export default withApi ( withProjects ( RuleConditionsForm ) ) ;
0 commit comments