Skip to content

Commit e9dfe8a

Browse files
authored
ref(aci): Create separate components for each detector type (#95362)
1 parent c6f1979 commit e9dfe8a

File tree

13 files changed

+353
-250
lines changed

13 files changed

+353
-250
lines changed

static/app/components/assigneeSelectorDropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ export default function AssigneeSelectorDropdown({
587587
);
588588
}
589589

590-
export const AssigneeWrapper = styled('div')`
590+
const AssigneeWrapper = styled('div')`
591591
display: flex;
592592
justify-content: flex-end;
593593
text-align: left;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {Button} from 'sentry/components/core/button';
2+
import {LinkButton} from 'sentry/components/core/button/linkButton';
3+
import {IconEdit} from 'sentry/icons';
4+
import {t} from 'sentry/locale';
5+
import type {Detector} from 'sentry/types/workflowEngine/detectors';
6+
import useOrganization from 'sentry/utils/useOrganization';
7+
import {makeMonitorDetailsPathname} from 'sentry/views/detectors/pathnames';
8+
9+
export function DisableDetectorAction({detector}: {detector: Detector}) {
10+
/**
11+
* TODO: Implement disable detector
12+
*/
13+
return (
14+
<Button size="sm" onClick={() => {}}>
15+
{detector.disabled ? t('Enable') : t('Disable')}
16+
</Button>
17+
);
18+
}
19+
20+
export function EditDetectorAction({detector}: {detector: Detector}) {
21+
const organization = useOrganization();
22+
23+
return (
24+
<LinkButton
25+
to={`${makeMonitorDetailsPathname(organization.slug, detector.id)}edit/`}
26+
priority="primary"
27+
icon={<IconEdit />}
28+
size="sm"
29+
>
30+
{t('Edit')}
31+
</LinkButton>
32+
);
33+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import ErrorBoundary from 'sentry/components/errorBoundary';
2+
import Section from 'sentry/components/workflowEngine/ui/section';
3+
import {t} from 'sentry/locale';
4+
import type {Detector} from 'sentry/types/workflowEngine/detectors';
5+
import {ConnectedAutomationsList} from 'sentry/views/detectors/components/connectedAutomationList';
6+
7+
type Props = {
8+
detector: Detector;
9+
};
10+
11+
export function DetectorDetailsAutomations({detector}: Props) {
12+
return (
13+
<Section title={t('Connected Automations')}>
14+
<ErrorBoundary mini>
15+
<ConnectedAutomationsList automationIds={detector.workflowIds} />
16+
</ErrorBoundary>
17+
</Section>
18+
);
19+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import styled from '@emotion/styled';
2+
3+
import {Tooltip} from 'sentry/components/core/tooltip';
4+
import {DateTime} from 'sentry/components/dateTime';
5+
import {KeyValueTable, KeyValueTableRow} from 'sentry/components/keyValueTable';
6+
import Placeholder from 'sentry/components/placeholder';
7+
import TextOverflow from 'sentry/components/textOverflow';
8+
import TimeSince from 'sentry/components/timeSince';
9+
import Section from 'sentry/components/workflowEngine/ui/section';
10+
import {t} from 'sentry/locale';
11+
import type {Detector} from 'sentry/types/workflowEngine/detectors';
12+
import useUserFromId from 'sentry/utils/useUserFromId';
13+
14+
type Props = {
15+
children: React.ReactNode;
16+
};
17+
18+
export function DetectorExtraDetails({children}: Props) {
19+
return (
20+
<Section title={t('Details')}>
21+
<StyledKeyValueTable>{children}</StyledKeyValueTable>
22+
</Section>
23+
);
24+
}
25+
26+
const StyledKeyValueTable = styled(KeyValueTable)`
27+
grid-template-columns: min-content auto;
28+
`;
29+
30+
DetectorExtraDetails.DateCreated = function DetectorExtraDetailsDateCreated({
31+
detector,
32+
}: {
33+
detector: Detector;
34+
}) {
35+
return (
36+
<KeyValueTableRow
37+
keyName={t('Date created')}
38+
value={<DateTime date={detector.dateCreated} dateOnly year />}
39+
/>
40+
);
41+
};
42+
43+
DetectorExtraDetails.CreatedBy = function DetectorExtraDetailsCreatedBy({
44+
detector,
45+
}: {
46+
detector: Detector;
47+
}) {
48+
const createdBy = detector.createdBy ?? null;
49+
50+
const {isPending, data: user} = useUserFromId({
51+
id: createdBy ? parseInt(createdBy, 10) : undefined,
52+
});
53+
54+
const keyName = t('Created by');
55+
56+
if (!createdBy) {
57+
return <KeyValueTableRow keyName={keyName} value={t('Sentry')} />;
58+
}
59+
60+
if (isPending) {
61+
return (
62+
<KeyValueTableRow
63+
keyName={keyName}
64+
value={<Placeholder width="80px" height="16px" />}
65+
/>
66+
);
67+
}
68+
69+
const title = user?.name ?? user?.email ?? t('Unknown');
70+
return (
71+
<KeyValueTableRow
72+
keyName={keyName}
73+
value={
74+
<Tooltip title={title} showOnlyOnOverflow>
75+
<TextOverflow>{title}</TextOverflow>
76+
</Tooltip>
77+
}
78+
/>
79+
);
80+
};
81+
82+
DetectorExtraDetails.LastModified = function DetectorExtraDetailsLastModified({
83+
detector,
84+
}: {
85+
detector: Detector;
86+
}) {
87+
return (
88+
<KeyValueTableRow
89+
keyName={t('Last modified')}
90+
value={<TimeSince date={detector.dateUpdated} />}
91+
/>
92+
);
93+
};
94+
95+
DetectorExtraDetails.Environment = function DetectorExtraDetailsEnvironment({
96+
detector,
97+
}: {
98+
detector: Detector;
99+
}) {
100+
// TODO: Add common function for getting environment from a detector
101+
const getEnvironment = () => {
102+
if (detector.type !== 'metric_issue') {
103+
return '<placeholder>';
104+
}
105+
106+
return (
107+
detector.dataSources?.find(ds => ds.type === 'snuba_query_subscription')?.queryObj
108+
?.snubaQuery.environment ?? t('All environments')
109+
);
110+
};
111+
112+
const environment = getEnvironment();
113+
114+
return (
115+
<KeyValueTableRow
116+
keyName={t('Environment')}
117+
value={
118+
<Tooltip title={environment} showOnlyOnOverflow>
119+
<TextOverflow>{environment}</TextOverflow>
120+
</Tooltip>
121+
}
122+
/>
123+
);
124+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {Breadcrumbs} from 'sentry/components/breadcrumbs';
2+
import DetailLayout from 'sentry/components/workflowEngine/layout/detail';
3+
import {t} from 'sentry/locale';
4+
import type {Project} from 'sentry/types/project';
5+
import type {Detector} from 'sentry/types/workflowEngine/detectors';
6+
import useOrganization from 'sentry/utils/useOrganization';
7+
import {
8+
DisableDetectorAction,
9+
EditDetectorAction,
10+
} from 'sentry/views/detectors/components/details/common/actions';
11+
import {makeMonitorBasePathname} from 'sentry/views/detectors/pathnames';
12+
13+
type DetectorDetailsHeaderProps = {
14+
detector: Detector;
15+
project: Project;
16+
};
17+
18+
export function DetectorDetailsHeader({detector, project}: DetectorDetailsHeaderProps) {
19+
const organization = useOrganization();
20+
21+
return (
22+
<DetailLayout.Header>
23+
<DetailLayout.HeaderContent>
24+
<Breadcrumbs
25+
crumbs={[
26+
{label: t('Monitors'), to: makeMonitorBasePathname(organization.slug)},
27+
{label: detector.name},
28+
]}
29+
/>
30+
<DetailLayout.Title title={detector.name} project={project} />
31+
</DetailLayout.HeaderContent>
32+
<DetailLayout.Actions>
33+
<DisableDetectorAction detector={detector} />
34+
<EditDetectorAction detector={detector} />
35+
</DetailLayout.Actions>
36+
</DetailLayout.Header>
37+
);
38+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import ErrorBoundary from 'sentry/components/errorBoundary';
2+
import {SimpleTable} from 'sentry/components/tables/simpleTable';
3+
import Section from 'sentry/components/workflowEngine/ui/section';
4+
import {t} from 'sentry/locale';
5+
6+
export function DetectorDetailsOngoingIssues() {
7+
return (
8+
<Section title={t('Ongoing Issues')}>
9+
{/* TODO: Implement fetching and replace with GroupList */}
10+
<ErrorBoundary mini>
11+
<SimpleTable>
12+
<SimpleTable.Header>
13+
<SimpleTable.HeaderCell>{t('Issue')}</SimpleTable.HeaderCell>
14+
</SimpleTable.Header>
15+
<SimpleTable.Empty>{t('Not yet implemented')}</SimpleTable.Empty>
16+
</SimpleTable>
17+
</ErrorBoundary>
18+
</Section>
19+
);
20+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import DetailLayout from 'sentry/components/workflowEngine/layout/detail';
2+
import type {Project} from 'sentry/types/project';
3+
import type {Detector} from 'sentry/types/workflowEngine/detectors';
4+
import {DetectorDetailsAutomations} from 'sentry/views/detectors/components/details/common/automations';
5+
import {DetectorExtraDetails} from 'sentry/views/detectors/components/details/common/extraDetails';
6+
import {DetectorDetailsHeader} from 'sentry/views/detectors/components/details/common/header';
7+
import {DetectorDetailsOngoingIssues} from 'sentry/views/detectors/components/details/common/ongoingIssues';
8+
9+
type FallbackDetectorDetailsProps = {
10+
detector: Detector;
11+
project: Project;
12+
};
13+
14+
export function FallbackDetectorDetails({
15+
detector,
16+
project,
17+
}: FallbackDetectorDetailsProps) {
18+
return (
19+
<DetailLayout>
20+
<DetectorDetailsHeader detector={detector} project={project} />
21+
<DetailLayout.Body>
22+
<DetailLayout.Main>
23+
<DetectorDetailsOngoingIssues />
24+
<DetectorDetailsAutomations detector={detector} />
25+
</DetailLayout.Main>
26+
<DetailLayout.Sidebar>
27+
<DetectorExtraDetails>
28+
<DetectorExtraDetails.DateCreated detector={detector} />
29+
<DetectorExtraDetails.CreatedBy detector={detector} />
30+
<DetectorExtraDetails.LastModified detector={detector} />
31+
<DetectorExtraDetails.Environment detector={detector} />
32+
</DetectorExtraDetails>
33+
</DetailLayout.Sidebar>
34+
</DetailLayout.Body>
35+
</DetailLayout>
36+
);
37+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type {Project} from 'sentry/types/project';
2+
import type {Detector} from 'sentry/types/workflowEngine/detectors';
3+
import {unreachable} from 'sentry/utils/unreachable';
4+
import {FallbackDetectorDetails} from 'sentry/views/detectors/components/details/fallback';
5+
import {MetricDetectorDetails} from 'sentry/views/detectors/components/details/metric';
6+
7+
type DetectorDetailsContentProps = {
8+
detector: Detector;
9+
project: Project;
10+
};
11+
12+
export function DetectorDetailsContent({detector, project}: DetectorDetailsContentProps) {
13+
const detectorType = detector.type;
14+
switch (detectorType) {
15+
case 'metric_issue':
16+
return <MetricDetectorDetails detector={detector} project={project} />;
17+
case 'uptime_domain_failure':
18+
case 'uptime_subscription':
19+
case 'error':
20+
return <FallbackDetectorDetails detector={detector} project={project} />;
21+
default:
22+
unreachable(detectorType);
23+
return <FallbackDetectorDetails detector={detector} project={project} />;
24+
}
25+
}

static/app/views/detectors/components/detailsPanel.tsx renamed to static/app/views/detectors/components/details/metric/detect.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import {Flex} from 'sentry/components/core/layout';
55
import {Container} from 'sentry/components/workflowEngine/ui/container';
66
import {t} from 'sentry/locale';
77
import {space} from 'sentry/styles/space';
8-
import type {Detector, SnubaQueryDataSource} from 'sentry/types/workflowEngine/detectors';
8+
import type {
9+
MetricDetector,
10+
SnubaQueryDataSource,
11+
} from 'sentry/types/workflowEngine/detectors';
912
import {getExactDuration} from 'sentry/utils/duration/getExactDuration';
1013

11-
interface DetailsPanelProps {
12-
detector: Detector;
14+
interface MetricDetectorDetectProps {
15+
detector: MetricDetector;
1316
}
1417

1518
function SnubaQueryDetails({dataSource}: {dataSource: SnubaQueryDataSource}) {
@@ -40,12 +43,10 @@ function SnubaQueryDetails({dataSource}: {dataSource: SnubaQueryDataSource}) {
4043
);
4144
}
4245

43-
function DetailsPanel({detector}: DetailsPanelProps) {
44-
if (detector.type === 'metric_issue') {
45-
const dataSource = detector.dataSources?.[0];
46-
if (dataSource?.type === 'snuba_query_subscription') {
47-
return <SnubaQueryDetails dataSource={dataSource} />;
48-
}
46+
export function MetricDetectorDetailsDetect({detector}: MetricDetectorDetectProps) {
47+
const dataSource = detector.dataSources?.[0];
48+
if (dataSource?.type === 'snuba_query_subscription') {
49+
return <SnubaQueryDetails dataSource={dataSource} />;
4950
}
5051

5152
return (
@@ -85,5 +86,3 @@ const Value = styled('dl')`
8586
word-break: break-all;
8687
margin: 0;
8788
`;
88-
89-
export default DetailsPanel;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import DetailLayout from 'sentry/components/workflowEngine/layout/detail';
2+
import type {Project} from 'sentry/types/project';
3+
import type {MetricDetector} from 'sentry/types/workflowEngine/detectors';
4+
import {DetectorDetailsAutomations} from 'sentry/views/detectors/components/details/common/automations';
5+
import {DetectorDetailsHeader} from 'sentry/views/detectors/components/details/common/header';
6+
import {DetectorDetailsOngoingIssues} from 'sentry/views/detectors/components/details/common/ongoingIssues';
7+
import {MetricDetectorDetailsSidebar} from 'sentry/views/detectors/components/details/metric/sidebar';
8+
9+
type MetricDetectorDetailsProps = {
10+
detector: MetricDetector;
11+
project: Project;
12+
};
13+
14+
export function MetricDetectorDetails({detector, project}: MetricDetectorDetailsProps) {
15+
return (
16+
<DetailLayout>
17+
<DetectorDetailsHeader detector={detector} project={project} />
18+
<DetailLayout.Body>
19+
<DetailLayout.Main>
20+
<DetectorDetailsOngoingIssues />
21+
<DetectorDetailsAutomations detector={detector} />
22+
</DetailLayout.Main>
23+
<DetailLayout.Sidebar>
24+
<MetricDetectorDetailsSidebar detector={detector} />
25+
</DetailLayout.Sidebar>
26+
</DetailLayout.Body>
27+
</DetailLayout>
28+
);
29+
}

0 commit comments

Comments
 (0)