Skip to content

Commit b20e50e

Browse files
getsentry-botroaga
andcommitted
Revert "feat(autofix): Add Autofix status to sidebar button (#85287)"
This reverts commit ff72324. Co-authored-by: roaga <47861399+roaga@users.noreply.github.com>
1 parent a4da162 commit b20e50e

File tree

7 files changed

+147
-251
lines changed

7 files changed

+147
-251
lines changed

Diff for: static/app/components/events/autofix/useAutofix.tsx

+4-23
Original file line numberDiff line numberDiff line change
@@ -76,30 +76,11 @@ const makeErrorAutofixData = (errorMessage: string): AutofixResponse => {
7676
};
7777

7878
/** Will not poll when the autofix is in an error state or has completed */
79-
const isPolling = (autofixData?: AutofixData | null) => {
80-
if (!autofixData?.steps) {
81-
return true;
82-
}
83-
84-
const hasSolutionStep = autofixData.steps.some(
85-
step => step.type === AutofixStepType.SOLUTION
86-
);
87-
88-
if (
89-
!hasSolutionStep &&
90-
![AutofixStatus.ERROR, AutofixStatus.CANCELLED].includes(autofixData.status)
91-
) {
92-
// we want to keep polling until we have a solution step because that's a stopping point
93-
// we need this explicit check in case we get a state for a fraction of a second where the root cause is complete and there is no step after it started
94-
return true;
95-
}
96-
return (
97-
!autofixData ||
98-
![AutofixStatus.ERROR, AutofixStatus.COMPLETED, AutofixStatus.CANCELLED].includes(
99-
autofixData.status
100-
)
79+
const isPolling = (autofixData?: AutofixData | null) =>
80+
!autofixData ||
81+
![AutofixStatus.ERROR, AutofixStatus.COMPLETED, AutofixStatus.CANCELLED].includes(
82+
autofixData.status
10183
);
102-
};
10384

10485
export const useAutofixData = ({groupId}: {groupId: string}) => {
10586
const {data} = useApiQuery<AutofixResponse>(makeAutofixQueryKey(groupId), {

Diff for: static/app/views/issueDetails/groupEventDetails/groupEventDetails.spec.tsx

-6
Original file line numberDiff line numberDiff line change
@@ -336,12 +336,6 @@ const mockGroupApis = (
336336
},
337337
},
338338
});
339-
MockApiClient.addMockResponse({
340-
url: `/issues/${group.id}/autofix/`,
341-
body: {
342-
steps: [],
343-
},
344-
});
345339
};
346340

347341
describe('groupEventDetails', () => {

Diff for: static/app/views/issueDetails/streamline/hooks/useAiConfig.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const useAiConfig = (
4848

4949
const isSummaryEnabled = issueTypeConfig.issueSummary.enabled;
5050
const isAutofixEnabled = issueTypeConfig.autofix;
51-
const hasResources = !!issueTypeConfig.resources;
51+
const hasResources = issueTypeConfig.resources;
5252

5353
const hasGenAIConsent = autofixSetupData?.genAIConsent.ok ?? organization.genAIConsent;
5454

Diff for: static/app/views/issueDetails/streamline/sidebar/sidebar.spec.tsx

-7
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,6 @@ describe('StreamlinedSidebar', function () {
6161
},
6262
});
6363

64-
MockApiClient.addMockResponse({
65-
url: `/issues/${group.id}/autofix/`,
66-
body: {
67-
steps: [],
68-
},
69-
});
70-
7164
mockFirstLastRelease = MockApiClient.addMockResponse({
7265
url: `/organizations/${organization.slug}/issues/${group.id}/first-last-release/`,
7366
method: 'GET',

Diff for: static/app/views/issueDetails/streamline/sidebar/solutionsSection.spec.tsx

+48-13
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,6 @@ describe('SolutionsSection', () => {
4343
},
4444
});
4545

46-
MockApiClient.addMockResponse({
47-
url: `/issues/${mockGroup.id}/autofix/`,
48-
body: {
49-
steps: [],
50-
},
51-
});
52-
5346
jest.mocked(getConfigForIssueType).mockReturnValue({
5447
issueSummary: {
5548
enabled: true,
@@ -197,7 +190,7 @@ describe('SolutionsSection', () => {
197190
});
198191

199192
describe('Solutions Hub button text', () => {
200-
it('shows "Set Up Sentry AI" when AI needs setup', async () => {
193+
it('shows "Set up Sentry AI" when AI needs setup', async () => {
201194
const customOrganization = OrganizationFixture({
202195
genAIConsent: false,
203196
hideAiFeatures: false,
@@ -228,10 +221,10 @@ describe('SolutionsSection', () => {
228221
screen.getByText('Explore potential root causes and solutions with Sentry AI.')
229222
).toBeInTheDocument();
230223

231-
expect(screen.getByRole('button', {name: 'Set Up Sentry AI'})).toBeInTheDocument();
224+
expect(screen.getByRole('button', {name: 'Set up Sentry AI'})).toBeInTheDocument();
232225
});
233226

234-
it('shows "Set Up Autofix" when autofix needs setup', async () => {
227+
it('shows "Set up Autofix" when autofix needs setup', async () => {
235228
MockApiClient.addMockResponse({
236229
url: `/issues/${mockGroup.id}/autofix/setup/`,
237230
body: {
@@ -259,10 +252,52 @@ describe('SolutionsSection', () => {
259252
expect(screen.queryByTestId('loading-placeholder')).not.toBeInTheDocument();
260253
});
261254

262-
expect(screen.getByRole('button', {name: 'Set Up Autofix'})).toBeInTheDocument();
255+
expect(screen.getByRole('button', {name: 'Set up Autofix'})).toBeInTheDocument();
256+
});
257+
258+
it('shows "Open Resources & Autofix" when both are available', async () => {
259+
// Mock successful summary response
260+
MockApiClient.addMockResponse({
261+
url: `/organizations/${mockProject.organization.slug}/issues/${mockGroup.id}/summarize/`,
262+
method: 'POST',
263+
body: {
264+
whatsWrong: 'Test summary',
265+
},
266+
});
267+
268+
// Mock successful autofix setup
269+
MockApiClient.addMockResponse({
270+
url: `/issues/${mockGroup.id}/autofix/setup/`,
271+
body: {
272+
genAIConsent: {ok: true},
273+
integration: {ok: true},
274+
githubWriteIntegration: {ok: true},
275+
},
276+
});
277+
278+
MockApiClient.addMockResponse({
279+
url: `/organizations/${mockProject.organization.slug}/issues/${mockGroup.id}/summarize/`,
280+
method: 'POST',
281+
body: {
282+
whatsWrong: 'Test summary',
283+
},
284+
});
285+
286+
render(
287+
<SolutionsSection event={mockEvent} group={mockGroup} project={mockProject} />,
288+
{
289+
organization,
290+
}
291+
);
292+
293+
await waitFor(() => {
294+
expect(
295+
screen.getByRole('button', {name: 'Open Resources & Autofix'})
296+
).toBeInTheDocument();
297+
});
263298
});
264299

265-
it('shows "Find Root Cause" when autofix is available', async () => {
300+
it('shows "Open Autofix" when only autofix is available', async () => {
266301
// Mock successful autofix setup but disable resources
267302
MockApiClient.addMockResponse({
268303
url: `/issues/${mockGroup.id}/autofix/setup/`,
@@ -294,7 +329,7 @@ describe('SolutionsSection', () => {
294329
);
295330

296331
await waitFor(() => {
297-
expect(screen.getByRole('button', {name: 'Find Root Cause'})).toBeInTheDocument();
332+
expect(screen.getByRole('button', {name: 'Open Autofix'})).toBeInTheDocument();
298333
});
299334
});
300335

Diff for: static/app/views/issueDetails/streamline/sidebar/solutionsSection.tsx

+94-12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import {useState} from 'react';
1+
import {useRef, useState} from 'react';
22
import styled from '@emotion/styled';
33

44
import FeatureBadge from 'sentry/components/badge/featureBadge';
55
import {Button} from 'sentry/components/button';
6+
import {Chevron} from 'sentry/components/chevron';
7+
import useDrawer from 'sentry/components/globalDrawer';
68
import {GroupSummary} from 'sentry/components/group/groupSummary';
9+
import Placeholder from 'sentry/components/placeholder';
710
import {IconMegaphone} from 'sentry/icons';
811
import {t, tct} from 'sentry/locale';
912
import {space} from 'sentry/styles/space';
@@ -16,10 +19,9 @@ import {SectionKey} from 'sentry/views/issueDetails/streamline/context';
1619
import {FoldSection} from 'sentry/views/issueDetails/streamline/foldSection';
1720
import {useAiConfig} from 'sentry/views/issueDetails/streamline/hooks/useAiConfig';
1821
import Resources from 'sentry/views/issueDetails/streamline/sidebar/resources';
22+
import {SolutionsHubDrawer} from 'sentry/views/issueDetails/streamline/sidebar/solutionsHubDrawer';
1923
import {useHasStreamlinedUI} from 'sentry/views/issueDetails/utils';
2024

21-
import {SolutionsSectionCtaButton} from './solutionsSectionCtaButton';
22-
2325
function SolutionsHubFeedbackButton({hidden}: {hidden: boolean}) {
2426
const openFeedbackForm = useFeedbackForm();
2527
if (hidden) {
@@ -55,10 +57,60 @@ export default function SolutionsSection({
5557
const hasStreamlinedUI = useHasStreamlinedUI();
5658
// We don't use this on the streamlined UI, since the section folds.
5759
const [isExpanded, setIsExpanded] = useState(false);
60+
const openButtonRef = useRef<HTMLButtonElement>(null);
61+
const {openDrawer} = useDrawer();
62+
63+
const openSolutionsDrawer = () => {
64+
if (!event) {
65+
return;
66+
}
67+
openDrawer(
68+
() => <SolutionsHubDrawer group={group} project={project} event={event} />,
69+
{
70+
ariaLabel: t('Solutions drawer'),
71+
// We prevent a click on the Open/Close Autofix button from closing the drawer so that
72+
// we don't reopen it immediately, and instead let the button handle this itself.
73+
shouldCloseOnInteractOutside: element => {
74+
const viewAllButton = openButtonRef.current;
75+
if (
76+
viewAllButton?.contains(element) ||
77+
document.getElementById('sentry-feedback')?.contains(element) ||
78+
document.getElementById('autofix-rethink-input')?.contains(element) ||
79+
document.getElementById('autofix-output-stream')?.contains(element) ||
80+
document.getElementById('autofix-write-access-modal')?.contains(element) ||
81+
element.closest('[data-overlay="true"]')
82+
) {
83+
return false;
84+
}
85+
return true;
86+
},
87+
transitionProps: {stiffness: 1000},
88+
}
89+
);
90+
};
5891

5992
const aiConfig = useAiConfig(group, event, project);
6093
const issueTypeConfig = getConfigForIssueType(group, project);
6194

95+
const showCtaButton =
96+
aiConfig.needsGenAIConsent ||
97+
aiConfig.hasAutofix ||
98+
(aiConfig.hasSummary && aiConfig.hasResources);
99+
const isButtonLoading = aiConfig.isAutofixSetupLoading;
100+
101+
const getButtonText = () => {
102+
if (aiConfig.needsGenAIConsent) {
103+
return t('Set up Sentry AI');
104+
}
105+
if (!aiConfig.hasAutofix) {
106+
return t('Open Resources');
107+
}
108+
if (aiConfig.needsAutofixSetup) {
109+
return t('Set up Autofix');
110+
}
111+
return aiConfig.hasResources ? t('Open Resources & Autofix') : t('Open Autofix');
112+
};
113+
62114
const renderContent = () => {
63115
if (aiConfig.needsGenAIConsent) {
64116
return (
@@ -124,15 +176,24 @@ export default function SolutionsSection({
124176
>
125177
<SolutionsSectionContainer>
126178
{renderContent()}
127-
{event && (
128-
<SolutionsSectionCtaButton
129-
aiConfig={aiConfig}
130-
event={event}
131-
group={group}
132-
project={project}
133-
hasStreamlinedUI={hasStreamlinedUI}
134-
/>
135-
)}
179+
{isButtonLoading ? (
180+
<ButtonPlaceholder />
181+
) : showCtaButton ? (
182+
<StyledButton
183+
ref={openButtonRef}
184+
onClick={() => openSolutionsDrawer()}
185+
analyticsEventKey="issue_details.solutions_hub_opened"
186+
analyticsEventName="Issue Details: Solutions Hub Opened"
187+
analyticsParams={{
188+
has_streamlined_ui: hasStreamlinedUI,
189+
}}
190+
>
191+
{getButtonText()}
192+
<ChevronContainer>
193+
<Chevron direction="right" size="large" />
194+
</ChevronContainer>
195+
</StyledButton>
196+
) : null}
136197
</SolutionsSectionContainer>
137198
</SidebarFoldSection>
138199
);
@@ -193,9 +254,30 @@ const ExpandButton = styled(Button)`
193254
}
194255
`;
195256

257+
const StyledButton = styled(Button)`
258+
margin-top: ${space(1)};
259+
width: 100%;
260+
background: ${p => p.theme.background}
261+
linear-gradient(to right, ${p => p.theme.background}, ${p => p.theme.pink400}20);
262+
color: ${p => p.theme.pink400};
263+
`;
264+
265+
const ChevronContainer = styled('div')`
266+
margin-left: ${space(0.5)};
267+
height: 16px;
268+
width: 16px;
269+
`;
270+
196271
const HeaderContainer = styled('div')`
197272
font-size: ${p => p.theme.fontSizeMedium};
198273
display: flex;
199274
align-items: center;
200275
gap: ${space(0.25)};
201276
`;
277+
278+
const ButtonPlaceholder = styled(Placeholder)`
279+
width: 100%;
280+
height: 38px;
281+
border-radius: ${p => p.theme.borderRadius};
282+
margin-top: ${space(1)};
283+
`;

0 commit comments

Comments
 (0)