Skip to content

Commit f1b8404

Browse files
scttcperandrewshie-sentry
authored andcommitted
feat(aci): Edit detector form (#93579)
1 parent d5e208e commit f1b8404

File tree

12 files changed

+483
-134
lines changed

12 files changed

+483
-134
lines changed

static/app/components/workflowEngine/form/control/index.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Form from 'sentry/components/forms/form';
55
import PriorityControl from 'sentry/components/workflowEngine/form/control/priorityControl';
66
import * as Storybook from 'sentry/stories';
77
import {space} from 'sentry/styles/space';
8+
import {DetectorPriorityLevel} from 'sentry/types/workflowEngine/dataConditions';
89

910
export default Storybook.story('Form Controls', story => {
1011
story('PriorityControl', () => (
@@ -16,7 +17,7 @@ export default Storybook.story('Form Controls', story => {
1617

1718
<Form hideFooter>
1819
<Flex column gap={space(2)}>
19-
<PriorityControl />
20+
<PriorityControl minimumPriority={DetectorPriorityLevel.LOW} />
2021
</Flex>
2122
</Form>
2223
</Fragment>

static/app/components/workflowEngine/form/control/priorityControl.spec.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('PriorityControl', function () {
1919
});
2020
render(
2121
<Form model={formModel} hideFooter>
22-
<PriorityControl />
22+
<PriorityControl minimumPriority={DetectorPriorityLevel.LOW} />
2323
</Form>
2424
);
2525

@@ -37,7 +37,7 @@ describe('PriorityControl', function () {
3737
});
3838
render(
3939
<Form model={formModel} hideFooter>
40-
<PriorityControl />
40+
<PriorityControl minimumPriority={DetectorPriorityLevel.LOW} />
4141
</Form>
4242
);
4343
expect(await screen.findByRole('button', {name: 'Low'})).toBeInTheDocument();
@@ -62,7 +62,7 @@ describe('PriorityControl', function () {
6262
});
6363
render(
6464
<Form model={formModel} hideFooter>
65-
<PriorityControl />
65+
<PriorityControl minimumPriority={DetectorPriorityLevel.LOW} />
6666
</Form>
6767
);
6868
const medium = await screen.findByTestId('priority-control-medium');

static/app/components/workflowEngine/form/control/priorityControl.tsx

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import styled from '@emotion/styled';
44
import {GroupPriorityBadge} from 'sentry/components/badge/groupPriority';
55
import {Flex} from 'sentry/components/container/flex';
66
import {CompactSelect} from 'sentry/components/core/compactSelect';
7-
import {FieldWrapper} from 'sentry/components/forms/fieldGroup/fieldWrapper';
87
import NumberField from 'sentry/components/forms/fields/numberField';
98
import FormContext from 'sentry/components/forms/formContext';
109
import InteractionStateLayer from 'sentry/components/interactionStateLayer';
@@ -67,12 +66,10 @@ function ChangePriority() {
6766
}
6867

6968
interface PriorityControlProps {
70-
minimumPriority?: DetectorPriorityLevel;
69+
minimumPriority: DetectorPriorityLevel;
7170
}
7271

73-
export default function PriorityControl({
74-
minimumPriority = DetectorPriorityLevel.LOW,
75-
}: PriorityControlProps) {
72+
export default function PriorityControl({minimumPriority}: PriorityControlProps) {
7673
// TODO: kind type not yet available from detector types
7774
const detectorKind = useMetricDetectorFormField(METRIC_DETECTOR_FORM_FIELDS.kind);
7875
const initialPriorityLevel = useMetricDetectorFormField(
@@ -84,7 +81,7 @@ export default function PriorityControl({
8481
<PrioritizeRow
8582
left={
8683
<Flex align="center" column>
87-
{!detectorKind || detectorKind === 'threshold' ? (
84+
{!detectorKind || detectorKind === 'static' ? (
8885
<ThresholdPriority />
8986
) : (
9087
<ChangePriority />
@@ -97,7 +94,7 @@ export default function PriorityControl({
9794
{priorityIsConfigurable(initialPriorityLevel, DetectorPriorityLevel.MEDIUM) && (
9895
<PrioritizeRow
9996
left={
100-
<NumberField
97+
<SmallNumberField
10198
alignRight
10299
inline
103100
hideLabel
@@ -116,7 +113,7 @@ export default function PriorityControl({
116113
{priorityIsConfigurable(initialPriorityLevel, DetectorPriorityLevel.HIGH) && (
117114
<PrioritizeRow
118115
left={
119-
<NumberField
116+
<SmallNumberField
120117
alignRight
121118
inline
122119
hideLabel
@@ -146,13 +143,9 @@ function priorityIsConfigurable(
146143
function PrioritizeRow({left, right}: {left: React.ReactNode; right: React.ReactNode}) {
147144
return (
148145
<Row>
149-
<Cell align="center" justify="flex-end">
150-
{left}
151-
</Cell>
146+
<Cell>{left}</Cell>
152147
<IconArrow color="gray300" direction="right" />
153-
<Cell align="center" justify="flex-start">
154-
{right}
155-
</Cell>
148+
<Cell>{right}</Cell>
156149
</Row>
157150
);
158151
}
@@ -227,13 +220,16 @@ const Row = styled('div')`
227220
display: contents;
228221
`;
229222

230-
const Cell = styled(Flex)`
223+
const Cell = styled('div')`
224+
display: flex;
225+
align-items: center;
226+
justify-content: center;
231227
padding: ${space(1)};
228+
`;
232229

233-
${FieldWrapper} {
234-
padding: 0;
235-
width: 5rem;
236-
}
230+
const SmallNumberField = styled(NumberField)`
231+
width: 5rem;
232+
padding: 0;
237233
`;
238234

239235
const SecondaryLabel = styled('div')`

static/app/types/workflowEngine/detectors.tsx

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type {DataConditionGroup} from 'sentry/types/workflowEngine/dataConditions';
2+
import type {AlertRuleSensitivity} from 'sentry/views/alerts/rules/metric/types';
23

34
/**
45
* See SnubaQuerySerializer
@@ -60,9 +61,42 @@ type DataSource = SnubaQueryDataSource | UptimeSubscriptionDataSource;
6061

6162
export type DetectorType = 'error' | 'metric_issue' | 'uptime_domain_failure';
6263

64+
interface BaseDetectorConfig {
65+
threshold_period: number;
66+
}
67+
68+
/**
69+
* Configuration for static/threshold-based detection
70+
*/
71+
interface StaticDetectorConfig extends BaseDetectorConfig {
72+
detection_type: 'static';
73+
}
74+
75+
/**
76+
* Configuration for percentage-based change detection
77+
*/
78+
interface PercentDetectorConfig extends BaseDetectorConfig {
79+
comparison_delta: number;
80+
detection_type: 'percent';
81+
}
82+
83+
/**
84+
* Configuration for dynamic/anomaly detection
85+
*/
86+
interface DynamicDetectorConfig extends BaseDetectorConfig {
87+
detection_type: 'dynamic';
88+
seasonality?: 'auto' | 'daily' | 'weekly' | 'monthly';
89+
sensitivity?: AlertRuleSensitivity;
90+
}
91+
92+
export type DetectorConfig =
93+
| StaticDetectorConfig
94+
| PercentDetectorConfig
95+
| DynamicDetectorConfig;
96+
6397
interface NewDetector {
6498
conditionGroup: DataConditionGroup | null;
65-
config: Record<string, unknown>;
99+
config: DetectorConfig;
66100
dataSources: DataSource[] | null;
67101
disabled: boolean;
68102
name: string;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {Flex} from 'sentry/components/container/flex';
2+
import {ProjectAvatar} from 'sentry/components/core/avatar/projectAvatar';
3+
import {t} from 'sentry/locale';
4+
import {space} from 'sentry/styles/space';
5+
import useProjects from 'sentry/utils/useProjects';
6+
7+
interface DetectorSubtitleProps {
8+
environment: string;
9+
projectId: string;
10+
}
11+
12+
export function DetectorSubtitle({projectId, environment}: DetectorSubtitleProps) {
13+
const {projects} = useProjects();
14+
const project = projects.find(p => p.id === projectId);
15+
return (
16+
<Flex gap={space(1)} align="center">
17+
{project && (
18+
<Flex gap={space(1)} align="center">
19+
<ProjectAvatar project={project} title={project.slug} />
20+
<span>{project.slug}</span>
21+
</Flex>
22+
)}
23+
<div aria-hidden>|</div>
24+
<div>{environment || t('All Environments')}</div>
25+
</Flex>
26+
);
27+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import styled from '@emotion/styled';
2+
3+
import Form from 'sentry/components/forms/form';
4+
5+
/**
6+
* Extends the Form component to be full height and have a sticky footer.
7+
*/
8+
export const FullHeightForm = styled(Form)`
9+
display: flex;
10+
flex-direction: column;
11+
flex: 1 1 0%;
12+
13+
& > div:first-child {
14+
display: flex;
15+
flex-direction: column;
16+
flex: 1;
17+
}
18+
`;

static/app/views/detectors/components/forms/metric.tsx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ import {IconAdd} from 'sentry/icons';
1616
import {t} from 'sentry/locale';
1717
import {space} from 'sentry/styles/space';
1818
import type {TagCollection} from 'sentry/types/group';
19-
import {DataConditionType} from 'sentry/types/workflowEngine/dataConditions';
19+
import {
20+
DataConditionType,
21+
DetectorPriorityLevel,
22+
} from 'sentry/types/workflowEngine/dataConditions';
2023
import {
2124
ALLOWED_EXPLORE_VISUALIZE_AGGREGATES,
2225
FieldKey,
@@ -48,12 +51,8 @@ export function MetricDetectorForm() {
4851

4952
function MonitorKind() {
5053
const options: Array<[MetricDetectorFormData['kind'], string, string]> = [
51-
[
52-
'threshold',
53-
t('Threshold'),
54-
t('Absolute-valued thresholds, for non-seasonal data.'),
55-
],
56-
['change', t('Change'), t('Percentage changes over defined time windows.')],
54+
['static', t('Threshold'), t('Absolute-valued thresholds, for non-seasonal data.')],
55+
['percent', t('Change'), t('Percentage changes over defined time windows.')],
5756
[
5857
'dynamic',
5958
t('Dynamic'),
@@ -63,7 +62,7 @@ function MonitorKind() {
6362

6463
return (
6564
<MonitorKindField
66-
label={t('...and monitor for changes in the following way:')}
65+
label={t('\u2026and monitor for changes in the following way:')}
6766
flexibleControlStateSize
6867
inline={false}
6968
name={METRIC_DETECTOR_FORM_FIELDS.kind}
@@ -149,7 +148,9 @@ function PrioritizeSection() {
149148
: t('Update issue priority when the following thresholds are met:')
150149
}
151150
>
152-
{kind !== 'dynamic' && <PriorityControl />}
151+
{kind !== 'dynamic' && (
152+
<PriorityControl minimumPriority={DetectorPriorityLevel.MEDIUM} />
153+
)}
153154
</Section>
154155
</Container>
155156
);
@@ -193,7 +194,7 @@ function DetectSection() {
193194
</FirstRow>
194195
<MonitorKind />
195196
<Flex column>
196-
{(!kind || kind === 'threshold') && (
197+
{(!kind || kind === 'static') && (
197198
<Flex column>
198199
<MutedText>{t('An issue will be created when query value is:')}</MutedText>
199200
<Flex align="center" gap={space(1)}>
@@ -224,7 +225,7 @@ function DetectSection() {
224225
</Flex>
225226
</Flex>
226227
)}
227-
{kind === 'change' && (
228+
{kind === 'percent' && (
228229
<Flex column>
229230
<MutedText>{t('An issue will be created when query value is:')}</MutedText>
230231
<Flex align="center" gap={space(1)}>

0 commit comments

Comments
 (0)