Skip to content
90 changes: 67 additions & 23 deletions static/app/views/projectsDashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import LazyLoad, {forceCheck} from 'react-lazyload';
import styled from '@emotion/styled';
import {withProfiler} from '@sentry/react';
import debounce from 'lodash/debounce';
import partition from 'lodash/partition';
import uniqBy from 'lodash/uniqBy';

import {LinkButton} from 'sentry/components/core/button';
Expand Down Expand Up @@ -81,6 +82,64 @@ function addProjectsToTeams(teams: Team[], projects: Project[]): TeamWithProject
}));
}

function getFilteredProjectsBasedOnTeams({
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there's a significant amount of code dedicated to filtering the projects, I extracted it into a new function to make it clearer and more manageable

allTeams,
userTeams,
selectedTeams,
isAllTeams,
showNonMemberProjects,
projects,
projectQuery,
}: {
allTeams: Team[];
isAllTeams: boolean;
projectQuery: string;
projects: Project[];
selectedTeams: string[];
showNonMemberProjects: boolean;
userTeams: Team[];
}): Project[] {
const myTeamIds = new Set(userTeams.map(team => team.id));

const [myTeamsInAll, otherTeamsInAll] = partition(allTeams, team =>
myTeamIds.has(team.id)
);

const includeMyTeams = isAllTeams || selectedTeams.includes('myteams');
const selectedOtherTeamIds = new Set(
selectedTeams.filter(teamId => teamId !== 'myteams')
);

const myTeams = includeMyTeams ? myTeamsInAll : [];
const otherTeams = isAllTeams
? otherTeamsInAll
: [...otherTeamsInAll].filter(team => selectedOtherTeamIds.has(String(team.id)));

const visibleTeams = [...myTeams, ...otherTeams].filter(team => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this contained duplicated entries too

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok this actually have to contain duplicates, otherwise the test "renders correct project with selected team" breaks.

if (showNonMemberProjects) {
return true;
}
return team.isMember;
});

const teamsWithProjects = addProjectsToTeams(visibleTeams, projects);

const currentProjects = uniqBy(
teamsWithProjects.flatMap(team => team.projects),
'id'
);

const currentProjectIds = new Set(currentProjects.map(p => p.id));

const unassignedProjects = isAllTeams
? projects.filter(project => !currentProjectIds.has(project.id))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to check showNonMemberProjects? Orgs that have open team membership disabled may not want to show them

Copy link
Member Author

@priscilawebdev priscilawebdev Apr 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But in this case, shouldn't other members be aware of these projects, so they can decide whether or not to request to join a team? or how they would ever know about these projects/teams?

Copy link
Member Author

@priscilawebdev priscilawebdev Apr 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the settings, they can see all projects and request to join them:

Screen.Recording.2025-04-28.at.08.14.23.mov

Copy link
Member

@scttcper scttcper Apr 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think settings is the only place they should see them with open membership disabled. we don't want them to see stats etc

: [];

return [...currentProjects, ...unassignedProjects].filter(project =>
project.slug.includes(projectQuery)
);
}

function Dashboard() {
const navigate = useNavigate();
const location = useLocation();
Expand Down Expand Up @@ -123,33 +182,18 @@ function Dashboard() {
return <LoadingError message={t('An error occurred while fetching your projects')} />;
}

const includeMyTeams = isAllTeams || selectedTeams.includes('myteams');
const hasOtherTeams = selectedTeams.some(team => team !== 'myteams');
const myTeams = includeMyTeams ? userTeams : [];
const otherTeams = isAllTeams
? allTeams
: hasOtherTeams
? allTeams.filter(team => selectedTeams.includes(`${team.id}`))
: [];
const filteredTeams = [...myTeams, ...otherTeams].filter(team => {
if (showNonMemberProjects) {
return true;
}

return team.isMember;
const filteredProjects = getFilteredProjectsBasedOnTeams({
allTeams,
userTeams,
selectedTeams,
isAllTeams,
showNonMemberProjects,
projects,
projectQuery,
});
const filteredTeamsWithProjects = addProjectsToTeams(filteredTeams, projects);

const currentProjects = uniqBy(
filteredTeamsWithProjects.flatMap(team => team.projects),
'id'
);
setGroupedEntityTag('projects.total', 1000, projects.length);

const filteredProjects = currentProjects.filter(project =>
project.slug.includes(projectQuery)
);

const showResources = projects.length === 1 && !projects[0]!.firstEvent;

const canJoinTeam = organization.access.includes('team:read');
Expand Down
Loading