import {Fragment, useEffect, useMemo} from 'react'; import styled from '@emotion/styled'; import type {ModalRenderProps} from 'sentry/actionCreators/modal'; import {Button} from 'sentry/components/button'; import {AutofixCodebaseIndexingStatus} from 'sentry/components/events/autofix/types'; import {useAutofixCodebaseIndexing} from 'sentry/components/events/autofix/useAutofixCodebaseIndexing'; import { type AutofixSetupRepoDefinition, type AutofixSetupResponse, 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'; import {IconCheckmark, IconGithub} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {trackAnalytics} from 'sentry/utils/analytics'; import useOrganization from 'sentry/utils/useOrganization'; interface AutofixSetupModalProps extends ModalRenderProps { groupId: string; projectId: string; } const ConsentStep = HookOrDefault({ hookName: 'component:autofix-setup-step-consent', defaultComponent: null, }); function AutofixIntegrationStep({autofixSetup}: {autofixSetup: AutofixSetupResponse}) { if (autofixSetup.integration.ok) { return ( {tct('The GitHub integration is already installed, [link: view in settings].', { link: , })} ); } if (autofixSetup.integration.reason === 'integration_inactive') { return (

{tct( 'The GitHub integration has been installed but is not active. Navigate to the [integration settings page] and enable it to continue.', { link: , } )}

{tct( 'Once enabled, come back to this page. For more information related to installing the GitHub integration, read the [link:documentation].', { link: ( ), } )}

); } if (autofixSetup.integration.reason === 'integration_no_code_mappings') { return (

{tct( 'You have an active GitHub installation, but no code mappings for this project. Add code mappings by visiting the [link:integration settings page] and editing your configuration.', { link: , } )}

{tct( 'Once added, come back to this page. For more information related to installing the GitHub integration, read the [link:documentation].', { link: ( ), } )}

); } return (

{tct( 'Install the GitHub integration by navigating to the [link:integration settings page] and clicking the "Install" button. Follow the steps provided.', { link: , } )}

{tct( 'Once installed, come back to this page. For more information related to installing the GitHub integration, read the [link:documentation].', { link: ( ), } )}

); } export function GitRepoLink({repo}: {repo: AutofixSetupRepoDefinition}) { if (repo.provider === 'github' || repo.provider.split(':')[1] === 'github') { return ( {repo.owner}/{repo.name} {repo.ok ? : null} ); } return (
  • {repo.owner}/{repo.name}
  • ); } function AutofixGithubIntegrationStep({ autofixSetup, }: { autofixSetup: AutofixSetupResponse; }) { const sortedRepos = useMemo( () => autofixSetup.githubWriteIntegration.repos.toSorted((a, b) => { if (a.ok === b.ok) { return `${a.owner}/${a.name}`.localeCompare(`${b.owner}/${b.name}`); } return a.ok ? -1 : 1; }), [autofixSetup.githubWriteIntegration.repos] ); if (autofixSetup.githubWriteIntegration.ok) { return (

    {tct( 'The [link:Sentry Autofix GitHub App] has been installed on all required repositories:', { link: ( ), } )}

    {sortedRepos.map(repo => ( ))}
    ); } if (autofixSetup.githubWriteIntegration.repos.length > 0) { return (

    {tct( 'Install and grant write access to the [link:Sentry Autofix Github App] for the following repositories:', { link: ( ), } )}

    {sortedRepos.map(repo => ( ))}

    {t( 'Without this, Autofix can still provide root analysis and suggested code changes.' )}

    ); } return (

    {tct( 'Install and grant write access to the [link:Sentry Autofix Github App] for the relevant repositories.', { link: ( ), } )}

    {t( 'Without this, Autofix can still provide root analysis and suggested code changes.' )}

    ); } function AutofixCodebaseIndexingStep({ autofixSetup, projectId, groupId, closeModal, }: { autofixSetup: AutofixSetupResponse; closeModal: () => void; groupId: string; projectId: string; }) { const {startIndexing, status, reason} = useAutofixCodebaseIndexing({ projectId, groupId, }); const canIndex = autofixSetup.genAIConsent.ok && autofixSetup.integration.ok; return (

    {t( 'Sentry will index your repositories to enable Autofix. This process may take a few minutes.' )}

    {status === AutofixCodebaseIndexingStatus.ERRORED && reason ? ( ) : null}
    ); } function AutofixSetupSteps({ projectId, groupId, autofixSetup, closeModal, }: { autofixSetup: AutofixSetupResponse; closeModal: () => void; groupId: string; projectId: string; }) { return ( ); } function AutofixSetupContent({ projectId, groupId, closeModal, }: { closeModal: () => void; groupId: string; projectId: string; }) { const organization = useOrganization(); const {data, canStartAutofix, isLoading, isError} = useAutofixSetup( {groupId}, // Want to check setup status whenever the user comes back to the tab {refetchOnWindowFocus: true} ); useEffect(() => { if (!data) { return; } trackAnalytics('autofix.setup_modal_viewed', { groupId, projectId, organization, setup_codebase_index: data.codebaseIndexing.ok, setup_gen_ai_consent: data.genAIConsent.ok, setup_integration: data.integration.ok, setup_write_integration: data.githubWriteIntegration.ok, }); }, [data, groupId, organization, projectId]); if (isLoading) { return ; } if (isError) { return ; } if (canStartAutofix) { return (

    {t("You've successfully configured Autofix!")}

    ); } return ( ); } export function AutofixSetupModal({ Header, Body, groupId, projectId, closeModal, }: AutofixSetupModalProps) { return (

    {t('Configure Autofix')}

    ); } export const AutofixSetupDone = styled('div')` position: relative; display: flex; align-items: center; justify-content: center; flex-direction: column; padding: 40px; font-size: ${p => p.theme.fontSizeLarge}; `; const DoneIcon = styled(IconCheckmark)` margin-bottom: ${space(4)}; `; const RepoLinkUl = styled('ul')` display: flex; flex-direction: column; gap: ${space(0.5)}; padding: 0; `; const RepoLinkItem = styled('li')` display: flex; align-items: center; gap: ${space(0.5)}; `; const GithubLink = styled('div')` display: flex; align-items: center; gap: ${space(0.5)}; `;