Skip to content

Commit d80eb0b

Browse files
gggritsoclaude
andauthored
feat(insights): Remove Projects from Insights navigation (#112535)
The much beleaguered "Projects" page is being slowly shuffled out. Here's the current (strange) state: - "Projects" is (mysteriously) available from the logo dropdown, even though everything else in there goes to settings pages, and there is such a thing as project settings! - "Projects" _also_ shows up in the Insights secondary navigation. Why? Got dumped there during the last sidebar re-shuffle, I think - Project pages live on the path `/insights/projects`, we moved them there from their old home at `/projects` The Product™ has decided that the "Projects" page and the actual per-project pages are not wanted anymore. The use-case for them is better covered with Dashboards and/or Project Settings in the sidebar, we even added some analytics about it. Since the sidebar is getting a big shuffle in the near future when "Monitors" appears, we'll also take that opportunity to remove "Projects" from the Insights secondary menu. (We're actually going to remove "Insights" menu completely, but one things at a time). Things are still a bit weird, but it's an intermediate state we're okay with for now. Soon, we will delete these pages altogether, but for now: 1. "Projects" are available as a link in the logo menu item, that link goes to `/projects/`. `/insights/projects/` now redirects to `/projects/` 2. "Projects" is removed from the Insights secondary sidebar completely! Sorry y'all 3. Projects now has its _own_ secondary menu. Otherwise, if someone has their secondary menu pinned and they land on `/projects/` what would show up there? Now it has the list of pinned projects, which is helpful 4. There's no primary menu item associated with projects, which is a bit weird, but there it is This is all gated behind the Workflow UI flag since we're going to roll out with them! **e.g.,** <img width="477" height="454" alt="Screenshot 2026-04-08 at 4 07 30 PM" src="https://github.com/user-attachments/assets/f8dadcc6-11f9-4a5a-8dcd-a82fe5886407" /> <img width="423" height="488" alt="Screenshot 2026-04-08 at 4 06 40 PM" src="https://github.com/user-attachments/assets/c63dba52-06b4-48dc-a14b-dc3a13bc2fd7" /> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6bd1582 commit d80eb0b

File tree

8 files changed

+126
-61
lines changed

8 files changed

+126
-61
lines changed

static/app/views/navigation/index.desktop.spec.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,6 @@ describe('desktop navigation', () => {
353353
[`${ORG}/insights/ai-agents/`, 'Insights', 'Agents'],
354354
[`${ORG}/insights/mcp/`, 'Insights', 'MCP'],
355355
[`${ORG}/monitors/crons/?insightsRedirect=true`, 'Monitors', 'Crons'],
356-
[`${ORG}/insights/projects/`, 'Insights', 'All Projects'],
357356
// Monitors
358357
[`${ORG}/monitors/`, 'Monitors', 'All Monitors'],
359358
[`${ORG}/monitors/my-monitors/`, 'Monitors', 'My Monitors'],

static/app/views/navigation/primaryNavigationContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const PRIMARY_NAVIGATION_GROUP_CONFIG = {
1313
explore: ['explore'],
1414
dashboards: ['dashboards', 'dashboard'],
1515
insights: ['insights'],
16+
projects: ['projects'], // No primary nav button — accessed via logo nav. Needed for secondary nav routing.
1617
monitors: ['monitors'],
1718
settings: ['settings'],
1819
prevent: ['prevent'],

static/app/views/navigation/secondary/content.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {ExploreSecondaryNavigation} from 'sentry/views/navigation/secondary/sect
88
import {InsightsSecondaryNavigation} from 'sentry/views/navigation/secondary/sections/insights/insightsSecondaryNavigation';
99
import {IssuesSecondaryNavigation} from 'sentry/views/navigation/secondary/sections/issues/issuesSecondaryNavigation';
1010
import {MonitorsSecondaryNavigation} from 'sentry/views/navigation/secondary/sections/monitors/monitorsSecondaryNavigation';
11+
import {ProjectsSecondaryNavigation} from 'sentry/views/navigation/secondary/sections/projects/projectsSecondaryNavigation';
1112
import {SettingsSecondaryNavigation} from 'sentry/views/navigation/secondary/sections/settings/settingsSecondaryNavigation';
1213

1314
export function SecondaryNavigationContent(): ReactNode {
@@ -21,6 +22,8 @@ export function SecondaryNavigationContent(): ReactNode {
2122
return <DashboardsSecondaryNavigation />;
2223
case 'explore':
2324
return <ExploreSecondaryNavigation />;
25+
case 'projects':
26+
return <ProjectsSecondaryNavigation />;
2427
case 'monitors':
2528
return <MonitorsSecondaryNavigation />;
2629
case 'prevent':

static/app/views/navigation/secondary/sections/insights/insightsSecondaryNavigation.tsx

Lines changed: 8 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import {Fragment, useMemo} from 'react';
2-
import partition from 'lodash/partition';
1+
import {Fragment} from 'react';
32

43
import Feature from 'sentry/components/acl/feature';
54
import {t} from 'sentry/locale';
65
import {useOrganization} from 'sentry/utils/useOrganization';
7-
import {useProjects} from 'sentry/utils/useProjects';
86
import {useUser} from 'sentry/utils/useUser';
97
import {makeMonitorBasePathname} from 'sentry/views/detectors/pathnames';
108
import {
@@ -29,23 +27,13 @@ import {
2927
} from 'sentry/views/insights/pages/mobile/settings';
3028
import {DOMAIN_VIEW_BASE_URL} from 'sentry/views/insights/pages/settings';
3129
import {SecondaryNavigation} from 'sentry/views/navigation/secondary/components';
30+
import {ProjectsNavigationItems} from 'sentry/views/navigation/secondary/sections/projects/starredProjectsList';
3231

3332
export function InsightsSecondaryNavigation() {
3433
const user = useUser();
3534
const organization = useOrganization();
3635
const baseUrl = `/organizations/${organization.slug}/${DOMAIN_VIEW_BASE_URL}`;
3736

38-
const {projects} = useProjects();
39-
40-
const [starredProjects, nonStarredProjects] = useMemo(() => {
41-
return partition(projects, project => project.isBookmarked);
42-
}, [projects]);
43-
44-
const displayStarredProjects = starredProjects.length > 0;
45-
const projectsToDisplay = displayStarredProjects
46-
? starredProjects.slice(0, 8)
47-
: nonStarredProjects.filter(project => project.isMember).slice(0, 8);
48-
4937
const shouldRedirectToMonitors =
5038
organization.features.includes('workflow-engine-ui') && !user?.isStaff;
5139

@@ -133,49 +121,15 @@ export function InsightsSecondaryNavigation() {
133121
</Feature>
134122
</SecondaryNavigation.List>
135123
</SecondaryNavigation.Section>
136-
<SecondaryNavigation.Separator />
137-
<SecondaryNavigation.Section id="insights-projects-all">
138-
<SecondaryNavigation.List>
139-
<SecondaryNavigation.ListItem>
140-
<SecondaryNavigation.Link
141-
to={`${baseUrl}/projects/`}
142-
end
143-
analyticsItemName="insights_projects_all"
144-
>
145-
{t('All Projects')}
146-
</SecondaryNavigation.Link>
147-
</SecondaryNavigation.ListItem>
148-
</SecondaryNavigation.List>
149-
</SecondaryNavigation.Section>
150-
{projectsToDisplay.length > 0 ? (
124+
{!organization.features.includes('workflow-engine-ui') && (
151125
<Fragment>
152126
<SecondaryNavigation.Separator />
153-
<SecondaryNavigation.Section
154-
id="insights-starred-projects"
155-
title={displayStarredProjects ? t('Starred Projects') : t('Projects')}
156-
>
157-
<SecondaryNavigation.List>
158-
{projectsToDisplay.map(project => (
159-
<SecondaryNavigation.ListItem key={project.id}>
160-
<SecondaryNavigation.Link
161-
to={`${baseUrl}/projects/${project.slug}/`}
162-
leadingItems={
163-
<SecondaryNavigation.ProjectIcon
164-
projectPlatforms={
165-
project.platform ? [project.platform] : ['default']
166-
}
167-
/>
168-
}
169-
analyticsItemName="insights_project_starred"
170-
>
171-
{project.slug}
172-
</SecondaryNavigation.Link>
173-
</SecondaryNavigation.ListItem>
174-
))}
175-
</SecondaryNavigation.List>
176-
</SecondaryNavigation.Section>
127+
<ProjectsNavigationItems
128+
allProjectsAnalyticsItemName="insights_projects_all"
129+
starredAnalyticsItemName="insights_project_starred"
130+
/>
177131
</Fragment>
178-
) : null}
132+
)}
179133
</SecondaryNavigation.Body>
180134
</Fragment>
181135
);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {Fragment} from 'react';
2+
3+
import {t} from 'sentry/locale';
4+
import {SecondaryNavigation} from 'sentry/views/navigation/secondary/components';
5+
import {ProjectsNavigationItems} from 'sentry/views/navigation/secondary/sections/projects/starredProjectsList';
6+
7+
export function ProjectsSecondaryNavigation() {
8+
return (
9+
<Fragment>
10+
<SecondaryNavigation.Header>{t('Projects')}</SecondaryNavigation.Header>
11+
<SecondaryNavigation.Body>
12+
<ProjectsNavigationItems />
13+
</SecondaryNavigation.Body>
14+
</Fragment>
15+
);
16+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import {Fragment, useMemo} from 'react';
2+
import partition from 'lodash/partition';
3+
4+
import {t} from 'sentry/locale';
5+
import {useOrganization} from 'sentry/utils/useOrganization';
6+
import {useProjects} from 'sentry/utils/useProjects';
7+
import {SecondaryNavigation} from 'sentry/views/navigation/secondary/components';
8+
import {makeProjectsPathname} from 'sentry/views/projects/pathname';
9+
10+
interface ProjectsNavigationItemsProps {
11+
allProjectsAnalyticsItemName?: string;
12+
starredAnalyticsItemName?: string;
13+
}
14+
15+
export function ProjectsNavigationItems({
16+
allProjectsAnalyticsItemName = 'projects_all',
17+
starredAnalyticsItemName = 'project_starred',
18+
}: ProjectsNavigationItemsProps) {
19+
const organization = useOrganization();
20+
const {projects} = useProjects();
21+
22+
const [starredProjects, nonStarredProjects] = useMemo(() => {
23+
return partition(projects, project => project.isBookmarked);
24+
}, [projects]);
25+
26+
const displayStarredProjects = starredProjects.length > 0;
27+
const projectsToDisplay = displayStarredProjects
28+
? starredProjects.slice(0, 8)
29+
: nonStarredProjects.filter(project => project.isMember).slice(0, 8);
30+
31+
return (
32+
<Fragment>
33+
<SecondaryNavigation.Section id="projects-all">
34+
<SecondaryNavigation.List>
35+
<SecondaryNavigation.ListItem>
36+
<SecondaryNavigation.Link
37+
to={makeProjectsPathname({path: '/', organization})}
38+
end
39+
analyticsItemName={allProjectsAnalyticsItemName}
40+
>
41+
{t('All Projects')}
42+
</SecondaryNavigation.Link>
43+
</SecondaryNavigation.ListItem>
44+
</SecondaryNavigation.List>
45+
</SecondaryNavigation.Section>
46+
{projectsToDisplay.length > 0 ? (
47+
<Fragment>
48+
<SecondaryNavigation.Separator />
49+
<SecondaryNavigation.Section
50+
id="starred-projects"
51+
title={displayStarredProjects ? t('Starred Projects') : t('Projects')}
52+
>
53+
<SecondaryNavigation.List>
54+
{projectsToDisplay.map(project => (
55+
<SecondaryNavigation.ListItem key={project.id}>
56+
<SecondaryNavigation.Link
57+
to={makeProjectsPathname({
58+
path: `/${project.slug}/`,
59+
organization,
60+
})}
61+
leadingItems={
62+
<SecondaryNavigation.ProjectIcon
63+
projectPlatforms={
64+
project.platform ? [project.platform] : ['default']
65+
}
66+
/>
67+
}
68+
analyticsItemName={starredAnalyticsItemName}
69+
>
70+
{project.slug}
71+
</SecondaryNavigation.Link>
72+
</SecondaryNavigation.ListItem>
73+
))}
74+
</SecondaryNavigation.List>
75+
</SecondaryNavigation.Section>
76+
</Fragment>
77+
) : null}
78+
</Fragment>
79+
);
80+
}

static/app/views/projects/index.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
import {Outlet} from 'react-router-dom';
22

33
import {Redirect} from 'sentry/components/redirect';
4+
import {useOrganization} from 'sentry/utils/useOrganization';
45
import {useRedirectNavigationV2Routes} from 'sentry/views/navigation/useRedirectNavigationV2Routes';
56

67
export default function Projects() {
7-
const redirectPath = useRedirectNavigationV2Routes({
8+
const organization = useOrganization();
9+
// Both hooks are called unconditionally (React rules of hooks), but only one
10+
// can match at a time since the current URL can't start with both prefixes.
11+
// We select which result to act on based on the feature flag.
12+
const forwardRedirect = useRedirectNavigationV2Routes({
813
oldPathPrefix: '/projects/',
914
newPathPrefix: '/insights/projects/',
1015
});
16+
const reverseRedirect = useRedirectNavigationV2Routes({
17+
oldPathPrefix: '/insights/projects/',
18+
newPathPrefix: '/projects/',
19+
});
20+
21+
const redirectPath = organization.features.includes('workflow-engine-ui')
22+
? reverseRedirect
23+
: forwardRedirect;
1124

1225
if (redirectPath) {
1326
return <Redirect to={redirectPath} />;
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import type {Organization} from 'sentry/types/organization';
22
import {normalizeUrl} from 'sentry/utils/url/normalizeUrl';
33

4-
const PROJECTS_BASE_PATHNAME = 'insights/projects';
5-
64
export function makeProjectsPathname({
75
path,
86
organization,
97
}: {
108
organization: Organization;
119
path: '/' | `/${string}/`;
1210
}) {
13-
return normalizeUrl(
14-
`/organizations/${organization.slug}/${PROJECTS_BASE_PATHNAME}${path}`
15-
);
11+
const base = organization.features.includes('workflow-engine-ui')
12+
? 'projects'
13+
: 'insights/projects';
14+
return normalizeUrl(`/organizations/${organization.slug}/${base}${path}`);
1615
}

0 commit comments

Comments
 (0)