Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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} />;
}
75 changes: 75 additions & 0 deletions static/gsApp/views/seerAutomation/projectFlyout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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 {LoadingIndicator} from 'sentry/components/loadingIndicator';
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 {useProjects} from 'sentry/utils/useProjects';

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 {fetching, projects} = useProjects({
slugs: projectSlug ? [projectSlug] : undefined,
orgId: organization.slug,
});
const project = projects[0];
const {openDrawer} = useDrawer();

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

useEffect(() => {
openDrawer(
() => (
<AnalyticsArea name="project-details">
<DrawerHeader>
{project && (
<Flex gap="sm">
{tct('Seer settings for [project]', {
project: <ProjectBadge project={project} avatarSize={16} />,
})}
</Flex>
)}
</DrawerHeader>
<DrawerBody>
{fetching ? (
<LoadingIndicator />
) : project ? (
<SeerProjectDetails project={project} />
) : (
<NotFound />
)}
</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}/`),
}
);
}, [fetching, navigate, openDrawer, organization, project, projectSlug]);

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