From df2b88dbced07bcac7abfd5fa29b6cd5972c38de Mon Sep 17 00:00:00 2001 From: Sujit Karki Date: Mon, 15 Sep 2025 15:58:43 +0545 Subject: [PATCH 1/3] Allow same team to assign as different role Show team name despite of the team is already assigned as another role --- frontend/src/components/projectEdit/teamSelect.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/projectEdit/teamSelect.js b/frontend/src/components/projectEdit/teamSelect.js index c1ac578e0b..bdecc6affd 100644 --- a/frontend/src/components/projectEdit/teamSelect.js +++ b/frontend/src/components/projectEdit/teamSelect.js @@ -84,22 +84,17 @@ export const TeamSelect = () => { }); }; - // Get only ids. - const teamsIds = projectInfo.teams.map((t) => { - return t.teamId; - }); - - let filteredTeams = teamsData?.teams?.filter((t) => !teamsIds.includes(t.teamId)); + let teamList = teamsData?.teams; if (org !== null) { - filteredTeams = [ + teamList = [ { label: org.name, - options: filteredTeams?.filter((t) => t.organisationId === org.organisationId), + options: teamList?.filter((t) => t.organisationId === org.organisationId), }, { label: 'Others', - options: filteredTeams?.filter((t) => t.organisationId !== org.organisationId), + options: teamList?.filter((t) => t.organisationId !== org.organisationId), }, ]; } @@ -153,7 +148,7 @@ export const TeamSelect = () => { classNamePrefix="react-select" getOptionLabel={(option) => option.name} getOptionValue={(option) => option.teamId} - options={isTeamsLoading ? [] : filteredTeams} + options={isTeamsLoading ? [] : teamList} onChange={(value) => handleSelect(value, 'team')} className="w-40 fl pr2 z-3" value={teamSelect.team.name !== null ? teamSelect.team : null} From 95a758ebe2533e6ee34088ac720e646e6cbcb333 Mon Sep 17 00:00:00 2001 From: Sujit Karki Date: Mon, 15 Sep 2025 16:09:18 +0545 Subject: [PATCH 2/3] Restrict save until the team is assigned as a explicit role(mapper/validator) if the permission is set to `team only` --- frontend/src/views/projectEdit.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/frontend/src/views/projectEdit.js b/frontend/src/views/projectEdit.js index cf81790302..cb35a87eb3 100644 --- a/frontend/src/views/projectEdit.js +++ b/frontend/src/views/projectEdit.js @@ -49,20 +49,17 @@ export const handleCheckButton = (event, arrayElement) => { } else { arrayElement = arrayElement.filter((t) => t !== event.target.value); } - + return arrayElement; }; const doesMappingTeamNotExist = (teams, mappingPermission) => ['TEAMS', 'TEAMS_LEVEL'].includes(mappingPermission) && - teams.filter((team) => team.role === 'MAPPER').length === 0 && - teams.filter((team) => team.role === 'VALIDATOR').length === 0 && - teams.filter((team) => team.role === 'PROJECT_MANAGER').length === 0; + teams.filter((team) => team.role === 'MAPPER').length === 0; const doesValidationTeamNotExist = (teams, validationPermission) => ['TEAMS', 'TEAMS_LEVEL'].includes(validationPermission) && - teams.filter((team) => team.role === 'VALIDATOR').length === 0 && - teams.filter((team) => team.role === 'PROJECT_MANAGER').length === 0; + teams.filter((team) => team.role === 'VALIDATOR').length === 0; export function ProjectEdit() { const { id } = useParams(); From 1badd449ff104d8813dd62f122cba043924d709e Mon Sep 17 00:00:00 2001 From: Sujit Karki Date: Tue, 16 Sep 2025 10:26:05 +0545 Subject: [PATCH 3/3] Prevent assigning the same team multiple times to a same role Display an error message and restrict saving if a duplicate assignment is attempted --- frontend/src/views/messages.js | 4 ++++ frontend/src/views/projectEdit.js | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/messages.js b/frontend/src/views/messages.js index a5c8333912..ffb54d7aae 100644 --- a/frontend/src/views/messages.js +++ b/frontend/src/views/messages.js @@ -794,6 +794,10 @@ export default defineMessages({ defaultMessage: '{mapping, select, true {Mapping} other {{validation, select, true {Validation} other {}}}} {mapping, select, true {{validation, select, true {and validation} other {}}} other {}} permissions have been set only to team members but no team has been added.', }, + duplicateTeamsAssigned: { + id: 'pages.edit_project.actions.duplicate_teams_assigned', + defaultMessage: 'Same team assigned multiple times for same role', + }, projectEditSection_description: { id: 'pages.edit_project.sections.description', defaultMessage: 'Description', diff --git a/frontend/src/views/projectEdit.js b/frontend/src/views/projectEdit.js index cb35a87eb3..5c3bd599c9 100644 --- a/frontend/src/views/projectEdit.js +++ b/frontend/src/views/projectEdit.js @@ -49,7 +49,7 @@ export const handleCheckButton = (event, arrayElement) => { } else { arrayElement = arrayElement.filter((t) => t !== event.target.value); } - + return arrayElement; }; @@ -61,6 +61,15 @@ const doesValidationTeamNotExist = (teams, validationPermission) => ['TEAMS', 'TEAMS_LEVEL'].includes(validationPermission) && teams.filter((team) => team.role === 'VALIDATOR').length === 0; +const doesSameTeamAndRoleExists = (teams) => { + const visited = {}; + return teams?.some((team) => { + if (visited[team.teamId] === team.role) return true; + visited[team.teamId] = team.role; + return false; + }); +}; + export function ProjectEdit() { const { id } = useParams(); useSetTitleTag(`Edit project #${id}`); @@ -161,6 +170,9 @@ export function ProjectEdit() { ) { missingFields.push({ type: 'noTeamsAssigned' }); } + if (doesSameTeamAndRoleExists(teams)) { + missingFields.push({ type: 'duplicateTeamsAssigned' }); + } // validate name if (!missingFields?.[0]?.fields?.includes('name')) { const projectName = defaultLocaleInfo.name; @@ -365,6 +377,9 @@ const ErrorTitle = ({ locale, numberOfMissingFields, type, projectInfo }) => { /> ); } + if (type === 'duplicateTeamsAssigned') { + return ; + } if (type === 'nameValidationError') { return (