Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions static/gsApp/hooks/seerSettingsRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export const seerSettingsRoutes = (): SentryRouteObject => ({
name: t('Autofix'),
component: make(() => import('getsentry/views/seerAutomation/projects')),
children: [
{
path: ':projectSlug/',
name: t('Project Details'),
component: make(
() => import('getsentry/views/seerAutomation/projectFlyout')
),
},
{
path: 'defaults/',
name: t('Defaults'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {Alert} from '@sentry/scraps/alert';
import {Stack} from '@sentry/scraps/layout';
import {ExternalLink} from '@sentry/scraps/link';

import {hasEveryAccess} from 'sentry/components/acl/access';
import Feature from 'sentry/components/acl/feature';
import {AnalyticsArea} from 'sentry/components/analyticsArea';
import {useProjectSeerPreferences} from 'sentry/components/events/autofix/preferences/hooks/useProjectSeerPreferences';
import type {ProjectSeerPreferences} from 'sentry/components/events/autofix/types';
import {LoadingError} from 'sentry/components/loadingError';
import {LoadingIndicator} from 'sentry/components/loadingIndicator';
import {SentryDocumentTitle} from 'sentry/components/sentryDocumentTitle';
import {t, tct} from 'sentry/locale';
import type {Project} from 'sentry/types/project';
import {useOrganization} from 'sentry/utils/useOrganization';
import {SettingsPageHeader} from 'sentry/views/settings/components/settingsPageHeader';

import {AutofixAgent} from 'getsentry/views/seerAutomation/components/projectDetails/autofixAgent';
import {AutofixRepositories} from 'getsentry/views/seerAutomation/components/projectDetails/autofixRepositoriesList';
import {NightShift} from 'getsentry/views/seerAutomation/components/projectDetails/nightShift';

const DEFAULT_PREFERENCE: ProjectSeerPreferences = {
repositories: [],
automated_run_stopping_point: 'root_cause',
automation_handoff: undefined,
};

export function SeerProjectDetails({project}: {project: Project}) {
const organization = useOrganization();
const {data, isPending, isError} = useProjectSeerPreferences(project);
const {preference, code_mapping_repos: codeMappingRepos} = data ?? {};

const canWrite = hasEveryAccess(['project:write'], {organization, project});

return (
<AnalyticsArea name="project-details">
<SentryDocumentTitle title={t('Seer for %s', project.slug)} />
<SettingsPageHeader
title={t('Seer')}
subtitle={tct(
'Connect repositories to projects, and choose which Agent should automatically process issues. [docs:Read the docs] to learn what Seer can do.',
{
docs: (
<ExternalLink href="https://docs.sentry.io/product/ai-in-sentry/seer/#seer-capabilities" />
),
}
)}
/>
{canWrite ? null : (
<Stack paddingBottom="xl">
<Alert variant="warning">
{t(
'These settings can only be edited by users with the project owner or manager role.'
)}
</Alert>
</Stack>
)}
{isPending ? (
<LoadingIndicator />
) : isError ? (
<LoadingError message={t('Failed to load Seer settings')} />
) : (
<Stack gap="2xl">
<AutofixRepositories
canWrite={canWrite}
codeMappingRepos={codeMappingRepos}
preference={preference ?? DEFAULT_PREFERENCE}
project={project}
/>
<AutofixAgent
canWrite={canWrite}
preference={preference ?? DEFAULT_PREFERENCE}
project={project}
/>
<Feature features="organizations:seer-night-shift-settings">
<NightShift canWrite={canWrite} project={project} />
</Feature>
</Stack>
)}
</AnalyticsArea>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
type MutateStoppingPoint,
PROJECT_STOPPING_POINT_OPTIONS,
} from 'sentry/utils/seer/stoppingPoint';
import {useLocation} from 'sentry/utils/useLocation';
import {useOrganization} from 'sentry/utils/useOrganization';
import {
useMutateSelectedAgent,
Expand All @@ -49,6 +50,7 @@ export function SeerProjectTableRow({
mutateStoppingPoint,
project,
}: Props) {
const location = useLocation();
const organization = useOrganization();
const canWrite = useCanWriteSettings();
const {isSelected, toggleSelected} = useListItemCheckboxContext();
Expand Down Expand Up @@ -83,7 +85,12 @@ export function SeerProjectTableRow({
</CheckboxClickTarget>
</SimpleTable.RowCell>
<SimpleTable.RowCell>
<Link to={`/settings/${organization.slug}/projects/${project.slug}/seer/`}>
<Link
to={{
pathname: `/settings/${organization.slug}/seer/projects/${project.slug}/`,
query: location.query,
}}
>
<ProjectBadge disableLink project={project} avatarSize={16} />
</Link>
</SimpleTable.RowCell>
Expand Down
79 changes: 4 additions & 75 deletions static/gsApp/views/seerAutomation/projectDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,87 +1,16 @@
import {Alert} from '@sentry/scraps/alert';
import {Stack} from '@sentry/scraps/layout';
import {ExternalLink} from '@sentry/scraps/link';

import {hasEveryAccess} from 'sentry/components/acl/access';
import Feature from 'sentry/components/acl/feature';
import {AnalyticsArea} from 'sentry/components/analyticsArea';
import {useProjectSeerPreferences} from 'sentry/components/events/autofix/preferences/hooks/useProjectSeerPreferences';
import type {ProjectSeerPreferences} from 'sentry/components/events/autofix/types';
import {LoadingIndicator} from 'sentry/components/loadingIndicator';
import {SentryDocumentTitle} from 'sentry/components/sentryDocumentTitle';
import {t, tct} from 'sentry/locale';
import {showNewSeer} from 'sentry/utils/seer/showNewSeer';
import {useOrganization} from 'sentry/utils/useOrganization';
import {SettingsPageHeader} from 'sentry/views/settings/components/settingsPageHeader';
import {useProjectSettingsOutlet} from 'sentry/views/settings/project/projectSettingsLayout';
import {ProjectSeerContainer as OldProjectDetails} from 'sentry/views/settings/projectSeer/index';

import {AutofixAgent} from 'getsentry/views/seerAutomation/components/projectDetails/autofixAgent';
import {AutofixRepositories} from 'getsentry/views/seerAutomation/components/projectDetails/autofixRepositoriesList';
import {NightShift} from 'getsentry/views/seerAutomation/components/projectDetails/nightShift';
import {SeerProjectDetails} from 'getsentry/views/seerAutomation/components/projectDetails';

export default function SeerProjectDetailsPage() {
const organization = useOrganization();
return showNewSeer(organization) ? <SeerProjectDetails /> : <OldProjectDetails />;
return showNewSeer(organization) ? <NewProjectDetails /> : <OldProjectDetails />;
}

const DEFAULT_PREFERENCE: ProjectSeerPreferences = {
repositories: [],
automated_run_stopping_point: 'root_cause',
automation_handoff: undefined,
};

function SeerProjectDetails() {
const organization = useOrganization();
function NewProjectDetails() {
const {project} = useProjectSettingsOutlet();
const {data, isPending} = useProjectSeerPreferences(project);
const {preference, code_mapping_repos: codeMappingRepos} = data ?? {};

const canWrite = hasEveryAccess(['project:write'], {organization, project});

return (
<AnalyticsArea name="project-details">
<SentryDocumentTitle title={t('Seer for %s', project.slug)} />
<SettingsPageHeader
title={t('Seer')}
subtitle={tct(
'Connect repositories to projects, and choose which Agent should automatically process issues. [docs:Read the docs] to learn what Seer can do.',
{
docs: (
<ExternalLink href="https://docs.sentry.io/product/ai-in-sentry/seer/#seer-capabilities" />
),
}
)}
/>
{canWrite ? null : (
<Stack paddingBottom="xl">
<Alert variant="warning">
{t(
'These settings can only be edited by users with the project owner or manager role.'
)}
</Alert>
</Stack>
)}
{isPending ? (
<LoadingIndicator />
) : (
<Stack gap="2xl">
<AutofixRepositories
canWrite={canWrite}
codeMappingRepos={codeMappingRepos}
preference={preference ?? DEFAULT_PREFERENCE}
project={project}
/>
<AutofixAgent
canWrite={canWrite}
preference={preference ?? DEFAULT_PREFERENCE}
project={project}
/>
<Feature features="organizations:seer-night-shift-settings">
<NightShift canWrite={canWrite} project={project} />
</Feature>
</Stack>
)}
</AnalyticsArea>
);
return <SeerProjectDetails project={project} />;
}
64 changes: 64 additions & 0 deletions static/gsApp/views/seerAutomation/projectFlyout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {useEffect, useRef} from 'react';

import {DrawerBody, DrawerHeader, useDrawer} from '@sentry/scraps/drawer';
import {Flex} from '@sentry/scraps/layout';

import {AnalyticsArea} from 'sentry/components/analyticsArea';
import {NotFound} from 'sentry/components/errors/notFound';
import ProjectBadge from 'sentry/components/idBadge/projectBadge';
import {t, tct} from 'sentry/locale';
import {useLocation} from 'sentry/utils/useLocation';
import {useNavigate} from 'sentry/utils/useNavigate';
import {useOrganization} from 'sentry/utils/useOrganization';
import {useParams} from 'sentry/utils/useParams';
import {useProjectFromSlug} from 'sentry/utils/useProjectFromSlug';

import {SeerProjectDetails} from 'getsentry/views/seerAutomation/components/projectDetails';

export default function SeerProjectFlyout() {
const {query} = useLocation();
const navigate = useNavigate();
const organization = useOrganization();
const {projectSlug} = useParams<{projectSlug: string}>();
const project = useProjectFromSlug({organization, projectSlug});
const {openDrawer} = useDrawer();

const queryRef = useRef(query);
queryRef.current = query;

useEffect(() => {
openDrawer(
() => (
<AnalyticsArea name="repo-details">
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
<DrawerHeader>
{project && (
<Flex gap="sm">
{tct('Seer settings for [project]', {
project: <ProjectBadge project={project} avatarSize={16} />,
})}
</Flex>
)}
</DrawerHeader>
<DrawerBody>
{project ? <SeerProjectDetails project={project} /> : <NotFound />}
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
</DrawerBody>
</AnalyticsArea>
),
{
ariaLabel: t('Project Details'),
drawerKey: 'project-details-drawer',
resizable: true,
onClose: () => {
navigate({
pathname: `/settings/${organization.slug}/seer/projects/`,
query: queryRef.current,
});
},
shouldCloseOnLocationChange: nextLocation =>
!nextLocation.pathname.endsWith(`/seer/projects/${projectSlug}/`),
}
);
}, [navigate, openDrawer, organization, project, projectSlug]);

Comment thread
sentry[bot] marked this conversation as resolved.
return null;
}
Loading