From 6f7985f8537f3c0e3f081a58c2bf2ed31ba13cc7 Mon Sep 17 00:00:00 2001 From: "jenn.muengtaweepongsa" Date: Wed, 13 Nov 2024 14:48:07 -0800 Subject: [PATCH 1/4] initial consent --- .../events/autofix/autofixSetupModal.tsx | 4 ++-- static/app/types/hooks.tsx | 1 + .../streamline/solutionsHubDrawer.tsx | 20 ++++++++----------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/static/app/components/events/autofix/autofixSetupModal.tsx b/static/app/components/events/autofix/autofixSetupModal.tsx index d8ba9cd5e3a3fa..55008c02bf04d9 100644 --- a/static/app/components/events/autofix/autofixSetupModal.tsx +++ b/static/app/components/events/autofix/autofixSetupModal.tsx @@ -19,7 +19,7 @@ import {trackAnalytics} from 'sentry/utils/analytics'; import useOrganization from 'sentry/utils/useOrganization'; const ConsentStep = HookOrDefault({ - hookName: 'component:autofix-setup-step-consent', + hookName: 'component:ai-setup-data-consent', defaultComponent: null, }); @@ -349,7 +349,7 @@ export function AutofixSetupContent({ return ( -
Set up Autofix
+
Set up Sentry AI

Sentry's AI-enabled Autofix uses all of the contextual data surrounding this error to work with you to find the root cause and create a fix. diff --git a/static/app/types/hooks.tsx b/static/app/types/hooks.tsx index 5c5157fe25aa43..be6e7a99378b65 100644 --- a/static/app/types/hooks.tsx +++ b/static/app/types/hooks.tsx @@ -184,6 +184,7 @@ export type MembershipSettingsProps = { * Component wrapping hooks */ export type ComponentHooks = { + 'component:ai-setup-data-consent': () => React.ComponentType<{}>; 'component:autofix-setup-step-consent': () => React.ComponentType | null; 'component:codecov-integration-settings-link': () => React.ComponentType; 'component:confirm-account-close': () => React.ComponentType; diff --git a/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx b/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx index bb73f27ebe7987..4f6468f0c016b0 100644 --- a/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx +++ b/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx @@ -15,6 +15,7 @@ import {useAiAutofix} from 'sentry/components/events/autofix/useAutofix'; import {useAutofixSetup} from 'sentry/components/events/autofix/useAutofixSetup'; import {DrawerBody, DrawerHeader} from 'sentry/components/globalDrawer/components'; import {GroupSummaryBody, useGroupSummary} from 'sentry/components/group/groupSummary'; +import HookOrDefault from 'sentry/components/hookOrDefault'; import Input from 'sentry/components/input'; import {IconDocs, IconSeer} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; @@ -133,6 +134,11 @@ interface SolutionsHubDrawerProps { project: Project; } +const AiSetupDataConsent = HookOrDefault({ + hookName: 'component:ai-setup-data-consent', + defaultComponent: null, +}); + export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerProps) { const {autofixData, triggerAutofix, reset, isPolling} = useAiAutofix(group, event); const { @@ -163,8 +169,8 @@ export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerPr shouldDisplayAiAutofixForOrganization(organization) && config.autofix && !shouldShowCustomErrorResourceConfig(group, project) && - hasStacktraceWithFrames(event) && - !isSampleError; + hasStacktraceWithFrames(event); + // !isSampleError; return ( @@ -392,16 +398,6 @@ const StarLarge3 = styled(StarLarge)` height: 28px; `; -const SetupContainer = styled('div')` - padding: ${space(2)}; - - /* Override some modal-specific styles */ - h3 { - font-size: ${p => p.theme.fontSizeLarge}; - margin-bottom: ${space(2)}; - } -`; - const StyledCard = styled('div')` background: ${p => p.theme.backgroundElevated}; border-radius: ${p => p.theme.borderRadius}; From 9cbf4b5a3dc414110af1d7407eff4c050065ebc1 Mon Sep 17 00:00:00 2001 From: "jenn.muengtaweepongsa" Date: Wed, 13 Nov 2024 16:09:24 -0800 Subject: [PATCH 2/4] genai consent --- .../events/autofix/autofixSetupModal.tsx | 15 ++-- static/app/types/hooks.tsx | 5 +- .../streamline/solutionsHubDrawer.tsx | 69 ++++++++++--------- 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/static/app/components/events/autofix/autofixSetupModal.tsx b/static/app/components/events/autofix/autofixSetupModal.tsx index 55008c02bf04d9..c51f74d671f429 100644 --- a/static/app/components/events/autofix/autofixSetupModal.tsx +++ b/static/app/components/events/autofix/autofixSetupModal.tsx @@ -8,7 +8,6 @@ import { useAutofixSetup, } from 'sentry/components/events/autofix/useAutofixSetup'; import {GuidedSteps} from 'sentry/components/guidedSteps/guidedSteps'; -import HookOrDefault from 'sentry/components/hookOrDefault'; import ExternalLink from 'sentry/components/links/externalLink'; import LoadingError from 'sentry/components/loadingError'; import LoadingIndicator from 'sentry/components/loadingIndicator'; @@ -18,11 +17,6 @@ import {space} from 'sentry/styles/space'; import {trackAnalytics} from 'sentry/utils/analytics'; import useOrganization from 'sentry/utils/useOrganization'; -const ConsentStep = HookOrDefault({ - hookName: 'component:ai-setup-data-consent', - defaultComponent: null, -}); - function AutofixIntegrationStep({autofixSetup}: {autofixSetup: AutofixSetupResponse}) { if (autofixSetup.integration.ok) { return ( @@ -283,7 +277,6 @@ function AutofixSetupSteps({ }) { return ( - -

Set up Sentry AI
+ +
Set up Autofix

Sentry's AI-enabled Autofix uses all of the contextual data surrounding this error to work with you to find the root cause and create a fix. @@ -401,3 +395,8 @@ const GithubLink = styled('div')` align-items: center; gap: ${space(0.5)}; `; + +const Divider = styled('div')` + margin: ${space(3)} 0; + border-bottom: 2px solid ${p => p.theme.gray100}; +`; diff --git a/static/app/types/hooks.tsx b/static/app/types/hooks.tsx index be6e7a99378b65..19760514b485fa 100644 --- a/static/app/types/hooks.tsx +++ b/static/app/types/hooks.tsx @@ -72,6 +72,9 @@ export type RouteHooks = { * Component specific hooks for DateRange and SelectorItems * These components have plan specific overrides in getsentry */ +type AiSetupDataConsentProps = { + groupId: string; +}; type AutofixSetupConsentStepProps = {hasConsented: boolean}; type DateRangeProps = React.ComponentProps; @@ -184,7 +187,7 @@ export type MembershipSettingsProps = { * Component wrapping hooks */ export type ComponentHooks = { - 'component:ai-setup-data-consent': () => React.ComponentType<{}>; + 'component:ai-setup-data-consent': () => React.ComponentType | null; 'component:autofix-setup-step-consent': () => React.ComponentType | null; 'component:codecov-integration-settings-link': () => React.ComponentType; 'component:confirm-account-close': () => React.ComponentType; diff --git a/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx b/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx index 4f6468f0c016b0..285703f1ea9731 100644 --- a/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx +++ b/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx @@ -160,17 +160,20 @@ export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerPr const config = getConfigForIssueType(group, project); - const isSetupComplete = setupData?.integration.ok && setupData?.genAIConsent.ok; - const hasSummary = summaryData && !isError && setupData?.genAIConsent.ok; + const hasConsent = Boolean(setupData?.genAIConsent.ok); + const isAutofixSetupComplete = setupData?.integration.ok && hasConsent; + + const hasSummary = summaryData && !isError && hasConsent; const organization = useOrganization(); const isSampleError = useIsSampleEvent(); + const displayAiAutofix = shouldDisplayAiAutofixForOrganization(organization) && config.autofix && !shouldShowCustomErrorResourceConfig(group, project) && - hasStacktraceWithFrames(event); - // !isSampleError; + hasStacktraceWithFrames(event) && + !isSampleError; return ( @@ -240,35 +243,39 @@ export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerPr )} - {hasSummary && ( - - - - )} - {displayAiAutofix && ( + {!hasConsent ? ( + + ) : ( - {!isSetupLoading && !isSetupComplete ? ( - - + - - ) : !autofixData && isPolling ? ( - - ) : autofixData ? ( - - ) : null} + + )} + {displayAiAutofix && ( + + {!isSetupLoading && !isAutofixSetupComplete ? ( + + ) : !autofixData ? ( + + ) : ( + + )} + + )} )} From 2d29d42fe483bbcaef02d22fcfac07df8f2aacad Mon Sep 17 00:00:00 2001 From: "jenn.muengtaweepongsa" Date: Thu, 14 Nov 2024 11:35:14 -0800 Subject: [PATCH 3/4] improve tests and add a loading state --- .../streamline/solutionsHubDrawer.spec.tsx | 58 +++++++++++++++++-- .../streamline/solutionsHubDrawer.tsx | 12 +++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/static/app/views/issueDetails/streamline/solutionsHubDrawer.spec.tsx b/static/app/views/issueDetails/streamline/solutionsHubDrawer.spec.tsx index e10948c9a082e7..91aca082b11a12 100644 --- a/static/app/views/issueDetails/streamline/solutionsHubDrawer.spec.tsx +++ b/static/app/views/issueDetails/streamline/solutionsHubDrawer.spec.tsx @@ -6,13 +6,19 @@ import {GroupFixture} from 'sentry-fixture/group'; import {OrganizationFixture} from 'sentry-fixture/organization'; import {ProjectFixture} from 'sentry-fixture/project'; -import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary'; +import { + render, + screen, + userEvent, + waitFor, + waitForElementToBeRemoved, +} from 'sentry-test/reactTestingLibrary'; import {t} from 'sentry/locale'; import {EntryType} from 'sentry/types/event'; import {SolutionsHubDrawer} from 'sentry/views/issueDetails/streamline/solutionsHubDrawer'; -describe('AutofixDrawer', () => { +describe('SolutionsHubDrawer', () => { const organization = OrganizationFixture({genAIConsent: true, hideAiFeatures: false}); const mockEvent = EventFixture({ @@ -51,10 +57,18 @@ describe('AutofixDrawer', () => { }); }); - it('renders properly', () => { + it('renders consent state if not consented', async () => { + MockApiClient.addMockResponse({ + url: `/issues/${mockGroup.id}/autofix/setup/`, + body: { + genAIConsent: {ok: false}, + integration: {ok: false}, + githubWriteIntegration: {ok: false}, + }, + }); MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/`, - body: {autofix: mockAutofixData}, + body: {autofix: null}, }); render( @@ -62,7 +76,35 @@ describe('AutofixDrawer', () => { {organization} ); - expect(screen.getByText(mockGroup.shortId)).toBeInTheDocument(); + expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument(); + + await waitForElementToBeRemoved(() => + screen.queryByTestId('ai-setup-loading-indicator') + ); + + expect(screen.getByText(mockEvent.id)).toBeInTheDocument(); + + expect(screen.getByRole('heading', {name: 'Solutions Hub'})).toBeInTheDocument(); + + expect(screen.getByTestId('ai-setup-data-consent')).toBeInTheDocument(); + }); + + it('renders initial state correctly', async () => { + MockApiClient.addMockResponse({ + url: `/issues/${mockGroup.id}/autofix/`, + body: {autofix: null}, + }); + + render( + , + {organization} + ); + + expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument(); + + await waitForElementToBeRemoved(() => + screen.queryByTestId('ai-setup-loading-indicator') + ); expect(screen.getByText(mockEvent.id)).toBeInTheDocument(); @@ -89,6 +131,12 @@ describe('AutofixDrawer', () => { {organization} ); + expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument(); + + await waitForElementToBeRemoved(() => + screen.queryByTestId('ai-setup-loading-indicator') + ); + const startButton = screen.getByRole('button', {name: 'Start Autofix'}); await userEvent.click(startButton); diff --git a/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx b/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx index 285703f1ea9731..5267febf644732 100644 --- a/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx +++ b/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx @@ -17,6 +17,7 @@ import {DrawerBody, DrawerHeader} from 'sentry/components/globalDrawer/component import {GroupSummaryBody, useGroupSummary} from 'sentry/components/group/groupSummary'; import HookOrDefault from 'sentry/components/hookOrDefault'; import Input from 'sentry/components/input'; +import LoadingIndicator from 'sentry/components/loadingIndicator'; import {IconDocs, IconSeer} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; @@ -89,6 +90,7 @@ function AutofixStartBox({onSend, groupId}: AutofixStartBoxProps) { : 'Autofix: Start Fix Clicked' } analyticsParams={{group_id: groupId}} + aria-label="Start Autofix" > {t('Start Autofix')} @@ -136,7 +138,7 @@ interface SolutionsHubDrawerProps { const AiSetupDataConsent = HookOrDefault({ hookName: 'component:ai-setup-data-consent', - defaultComponent: null, + defaultComponent: () =>

, }); export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerProps) { @@ -243,7 +245,11 @@ export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerPr )} - {!hasConsent ? ( + {isSetupLoading ? ( +
+ +
+ ) : !hasConsent ? ( ) : ( @@ -258,7 +264,7 @@ export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerPr )} {displayAiAutofix && ( - {!isSetupLoading && !isAutofixSetupComplete ? ( + {!isAutofixSetupComplete ? ( Date: Thu, 14 Nov 2024 11:40:48 -0800 Subject: [PATCH 4/4] fix precommit --- static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx b/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx index 5267febf644732..0fb1169df10883 100644 --- a/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx +++ b/static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx @@ -142,7 +142,7 @@ const AiSetupDataConsent = HookOrDefault({ }); export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerProps) { - const {autofixData, triggerAutofix, reset, isPolling} = useAiAutofix(group, event); + const {autofixData, triggerAutofix, reset} = useAiAutofix(group, event); const { data: summaryData, isError,