Skip to content

Commit 902e077

Browse files
authored
feat(aci): Add error detector details page (#95739)
1 parent b948992 commit 902e077

File tree

4 files changed

+160
-3
lines changed

4 files changed

+160
-3
lines changed

static/app/components/workflowEngine/ui/section.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,24 @@ type SectionProps = {
1111

1212
export default function Section({children, title, description}: SectionProps) {
1313
return (
14-
<Flex direction="column" gap={space(1)}>
14+
<SectionContainer direction="column" gap={space(1)}>
1515
<SectionHeading>{title}</SectionHeading>
1616
{description && <SectionDescription>{description}</SectionDescription>}
1717
{children}
18-
</Flex>
18+
</SectionContainer>
1919
);
2020
}
2121

22+
const SectionContainer = styled(Flex)`
23+
> p {
24+
margin-bottom: ${p => p.theme.space.none};
25+
}
26+
27+
p + p {
28+
margin-top: ${p => p.theme.space.md};
29+
}
30+
`;
31+
2232
const SectionHeading = styled('h4')`
2333
font-size: ${p => p.theme.fontSize.lg};
2434
font-weight: ${p => p.theme.fontWeight.bold};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {ErrorDetectorFixture} from 'sentry-fixture/detectors';
2+
import {ProjectFixture} from 'sentry-fixture/project';
3+
4+
import {render, screen} from 'sentry-test/reactTestingLibrary';
5+
6+
import {ErrorDetectorDetails} from 'sentry/views/detectors/components/details/error';
7+
8+
describe('ErrorDetectorDetails', function () {
9+
const defaultProps = {
10+
detector: ErrorDetectorFixture(),
11+
project: ProjectFixture(),
12+
};
13+
14+
beforeEach(function () {
15+
MockApiClient.addMockResponse({
16+
url: '/projects/org-slug/project-slug/',
17+
method: 'GET',
18+
body: ProjectFixture(),
19+
});
20+
});
21+
22+
describe('Resolve section', function () {
23+
it('displays the auto-resolve time when it is configured', async function () {
24+
MockApiClient.addMockResponse({
25+
url: '/projects/org-slug/project-slug/',
26+
method: 'GET',
27+
body: ProjectFixture({
28+
resolveAge: 30 * 24,
29+
}),
30+
});
31+
32+
render(<ErrorDetectorDetails {...defaultProps} />);
33+
34+
expect(
35+
await screen.findByText('Auto-resolve after 30 days of inactivity')
36+
).toBeInTheDocument();
37+
});
38+
39+
it('displays correct text when auto-resolve is disabled', async function () {
40+
const project = ProjectFixture({resolveAge: 0});
41+
42+
render(<ErrorDetectorDetails {...defaultProps} project={project} />);
43+
44+
expect(await screen.findByText('Auto-resolution disabled')).toBeInTheDocument();
45+
});
46+
});
47+
});
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import ExternalLink from 'sentry/components/links/externalLink';
2+
import Placeholder from 'sentry/components/placeholder';
3+
import DetailLayout from 'sentry/components/workflowEngine/layout/detail';
4+
import Section from 'sentry/components/workflowEngine/ui/section';
5+
import {t, tct, tn} from 'sentry/locale';
6+
import type {Project} from 'sentry/types/project';
7+
import type {Detector} from 'sentry/types/workflowEngine/detectors';
8+
import {useDetailedProject} from 'sentry/utils/useDetailedProject';
9+
import useOrganization from 'sentry/utils/useOrganization';
10+
import {DetectorDetailsAssignee} from 'sentry/views/detectors/components/details/common/assignee';
11+
import {DetectorDetailsAutomations} from 'sentry/views/detectors/components/details/common/automations';
12+
import {DetectorExtraDetails} from 'sentry/views/detectors/components/details/common/extraDetails';
13+
import {DetectorDetailsHeader} from 'sentry/views/detectors/components/details/common/header';
14+
import {DetectorDetailsOngoingIssues} from 'sentry/views/detectors/components/details/common/ongoingIssues';
15+
16+
type ErrorDetectorDetailsProps = {
17+
detector: Detector;
18+
project: Project;
19+
};
20+
21+
const formatResolveAge = (resolveAge: number) => {
22+
if (!resolveAge) {
23+
return t('Auto-resolution disabled');
24+
}
25+
26+
if (resolveAge < 24 || resolveAge % 24 !== 0) {
27+
return tn(
28+
'Auto-resolve after %s hour of inactivity',
29+
'Auto-resolve after %s hours of inactivity',
30+
resolveAge
31+
);
32+
}
33+
return tn(
34+
'Auto-resolve after %s day of inactivity',
35+
'Auto-resolve after %s days of inactivity',
36+
resolveAge / 24
37+
);
38+
};
39+
40+
function ResolveSection({project}: {project: Project}) {
41+
const organization = useOrganization();
42+
const {data: detailedProject, isPending} = useDetailedProject({
43+
orgSlug: organization.slug,
44+
projectSlug: project.slug,
45+
});
46+
47+
if (isPending || !detailedProject) {
48+
return (
49+
<Section title={t('Resolve')}>
50+
<Placeholder height="1em" />
51+
</Section>
52+
);
53+
}
54+
55+
const resolveAgeHours = detailedProject.resolveAge;
56+
57+
return (
58+
<Section title={t('Resolve')}>
59+
<p>{formatResolveAge(resolveAgeHours)}</p>
60+
</Section>
61+
);
62+
}
63+
64+
export function ErrorDetectorDetails({detector, project}: ErrorDetectorDetailsProps) {
65+
return (
66+
<DetailLayout>
67+
<DetectorDetailsHeader detector={detector} project={project} />
68+
<DetailLayout.Body>
69+
<DetailLayout.Main>
70+
<DetectorDetailsOngoingIssues />
71+
<DetectorDetailsAutomations detector={detector} />
72+
</DetailLayout.Main>
73+
<DetailLayout.Sidebar>
74+
<Section title={t('Detect')}>
75+
<p>
76+
{tct(
77+
'All events have a fingerprint. Events with the same fingerprint are grouped together into an issue. To learn more about issue grouping, [link:read the docs].',
78+
{
79+
link: (
80+
<ExternalLink href="https://docs.sentry.io/concepts/data-management/event-grouping/" />
81+
),
82+
}
83+
)}
84+
</p>
85+
</Section>
86+
<DetectorDetailsAssignee owner={detector.owner} />
87+
<ResolveSection project={project} />
88+
<DetectorExtraDetails>
89+
<DetectorExtraDetails.DateCreated detector={detector} />
90+
<DetectorExtraDetails.CreatedBy detector={detector} />
91+
<DetectorExtraDetails.LastModified detector={detector} />
92+
<DetectorExtraDetails.Environment detector={detector} />
93+
</DetectorExtraDetails>
94+
</DetailLayout.Sidebar>
95+
</DetailLayout.Body>
96+
</DetailLayout>
97+
);
98+
}

static/app/views/detectors/components/details/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {Project} from 'sentry/types/project';
22
import type {Detector} from 'sentry/types/workflowEngine/detectors';
33
import {unreachable} from 'sentry/utils/unreachable';
4+
import {ErrorDetectorDetails} from 'sentry/views/detectors/components/details/error';
45
import {FallbackDetectorDetails} from 'sentry/views/detectors/components/details/fallback';
56
import {MetricDetectorDetails} from 'sentry/views/detectors/components/details/metric';
67
import {UptimeDetectorDetails} from 'sentry/views/detectors/components/details/uptime';
@@ -17,8 +18,9 @@ export function DetectorDetailsContent({detector, project}: DetectorDetailsConte
1718
return <MetricDetectorDetails detector={detector} project={project} />;
1819
case 'uptime_domain_failure':
1920
return <UptimeDetectorDetails detector={detector} project={project} />;
20-
case 'uptime_subscription':
2121
case 'error':
22+
return <ErrorDetectorDetails detector={detector} project={project} />;
23+
case 'uptime_subscription':
2224
return <FallbackDetectorDetails detector={detector} project={project} />;
2325
default:
2426
unreachable(detectorType);

0 commit comments

Comments
 (0)