From d457b69dc33f667df446aaa6963ae91847fa55fd Mon Sep 17 00:00:00 2001 From: Sujit Karki Date: Thu, 27 Nov 2025 11:51:38 +0545 Subject: [PATCH 1/5] fetch list of teams where the user is assigned as team manager fetch data along with the user details fetch and store result on redux --- frontend/src/store/actions/auth.js | 13 +++++++++++++ frontend/src/store/reducers/auth.js | 3 +++ 2 files changed, 16 insertions(+) diff --git a/frontend/src/store/actions/auth.js b/frontend/src/store/actions/auth.js index ef38b19b94..1debab028b 100644 --- a/frontend/src/store/actions/auth.js +++ b/frontend/src/store/actions/auth.js @@ -8,6 +8,7 @@ export const types = { SET_OSM: 'SET_OSM', SET_ORGANISATIONS: 'SET_ORGANISATIONS', SET_PM_TEAMS: 'SET_PM_TEAMS', + SET_TM_TEAMS: 'SET_TM_TEAMS', UPDATE_OSM_INFO: 'UPDATE_OSM_INFO', GET_USER_DETAILS: 'GET_USER_DETAILS', SET_TOKEN: 'SET_TOKEN', @@ -75,6 +76,13 @@ export function updatePMsTeams(teams) { }; } +export function updateTMsTeams(teams) { + return { + type: types.SET_TM_TEAMS, + teams: teams, + }; +} + export function updateToken(token) { return { type: types.SET_TOKEN, @@ -135,6 +143,11 @@ export const setUserDetails = .then((teams) => dispatch(updatePMsTeams(teams.teams.map((team) => team.teamId)))) .catch((error) => dispatch(updatePMsTeams([]))); dispatch(setLoader(false)); + + fetchLocalJSONAPI(`teams/?fullMemberList=false&manager=${userDetails.id}`, encodedToken) + .then((teams) => dispatch(updateTMsTeams(teams.teams.map((team) => team.teamId)))) + .catch((error) => dispatch(updateTMsTeams([]))); + dispatch(setLoader(false)); }) .catch((error) => { if (error.message === 'InvalidToken') dispatch(logout()); diff --git a/frontend/src/store/reducers/auth.js b/frontend/src/store/reducers/auth.js index 30656f6b40..0765dc56c3 100644 --- a/frontend/src/store/reducers/auth.js +++ b/frontend/src/store/reducers/auth.js @@ -23,6 +23,9 @@ export function authorizationReducer(state = initialState, action) { case types.SET_PM_TEAMS: { return { ...state, pmTeams: action.teams }; } + case types.SET_TM_TEAMS: { + return { ...state, tmTeams: action.teams }; + } case types.SET_TOKEN: { return { ...state, token: action.token }; } From fee840e688d2016e61eb44934b38c8f5a3d731d3 Mon Sep 17 00:00:00 2001 From: Sujit Karki Date: Thu, 27 Nov 2025 11:54:16 +0545 Subject: [PATCH 2/5] update edit team permission set permission to the list of teams where user is assigned as a team manager insted of project manager --- frontend/src/hooks/UsePermissions.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/src/hooks/UsePermissions.js b/frontend/src/hooks/UsePermissions.js index 53ae3d5cc4..f91f8edd06 100644 --- a/frontend/src/hooks/UsePermissions.js +++ b/frontend/src/hooks/UsePermissions.js @@ -29,7 +29,8 @@ export function useEditProjectAllowed(project) { export function useEditTeamAllowed(team) { const userDetails = useSelector((state) => state.auth.userDetails); const organisations = useSelector((state) => state.auth.organisations); - const pmTeams = useSelector((state) => state.auth.pmTeams); + // const pmTeams = useSelector((state) => state.auth.pmTeams); + const tmTeams = useSelector((state) => state.auth.tmTeams); const [isAllowed, setIsAllowed] = useState(false); useEffect(() => { @@ -39,9 +40,13 @@ export function useEditTeamAllowed(team) { if (organisations && organisations.includes(team.organisation_id)) setIsAllowed(true); // team managers can edit it // verify from the redux store - if (pmTeams && pmTeams.includes(team.teamId)) setIsAllowed(true); - // and verify based on the team members list + // if (pmTeams && pmTeams.includes(team.teamId)) setIsAllowed(true); // removing this cause it is fetching all teams having project project manager role + + // removed pm and test using tm list + if (tmTeams && tmTeams.includes(team.teamId)) setIsAllowed(true); + if (team.members) { + // and verify based on the team members list const managers = team.members .filter((member) => member.active && member.function === 'MANAGER') .map((member) => member.username); @@ -49,7 +54,7 @@ export function useEditTeamAllowed(team) { setIsAllowed(true); } } - }, [pmTeams, userDetails.role, userDetails.username, organisations, team]); + }, [userDetails.role, userDetails.username, organisations, team, tmTeams]); return [isAllowed]; } From 681e1e7bf61ea297be922a6c7ab2fdb65d530a87 Mon Sep 17 00:00:00 2001 From: Sujit Karki Date: Fri, 28 Nov 2025 16:07:36 +0545 Subject: [PATCH 3/5] adapt test cases for updating the team edit permission logic --- frontend/src/hooks/tests/UseEditTeamPermissions.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/hooks/tests/UseEditTeamPermissions.test.js b/frontend/src/hooks/tests/UseEditTeamPermissions.test.js index d003846f39..29a42fc316 100644 --- a/frontend/src/hooks/tests/UseEditTeamPermissions.test.js +++ b/frontend/src/hooks/tests/UseEditTeamPermissions.test.js @@ -37,7 +37,7 @@ describe('test edit team permissions based on manager permissions', () => { }; it('team manager CAN edit it - verify based on redux store', () => { act(() => { - store.dispatch({ type: 'SET_PM_TEAMS', teams: [1, 2, 3] }); + store.dispatch({ type: 'SET_TM_TEAMS', teams: [1, 2, 3] }); }); const wrapper = ({ children }) => {children}; const { result } = renderHook(() => useEditTeamAllowed(team), { wrapper }); @@ -48,7 +48,7 @@ describe('test edit team permissions based on manager permissions', () => { it('team manager CAN edit it - verify based on team members', () => { const userDetails = { username: 'test', role: 'MAPPER' }; act(() => { - store.dispatch({ type: 'SET_PM_TEAMS', teams: [] }); + store.dispatch({ type: 'SET_TM_TEAMS', teams: [] }); store.dispatch({ type: 'SET_USER_DETAILS', userDetails: userDetails }); }); const wrapper = ({ children }) => {children}; @@ -60,7 +60,7 @@ describe('test edit team permissions based on manager permissions', () => { it('MAPPER can not edit it - verify based on team members', () => { const userDetails = { username: 'another_user', role: 'MAPPER' }; act(() => { - store.dispatch({ type: 'SET_PM_TEAMS', teams: [] }); + store.dispatch({ type: 'SET_TM_TEAMS', teams: [] }); store.dispatch({ type: 'SET_USER_DETAILS', userDetails: userDetails }); }); const wrapper = ({ children }) => {children}; @@ -71,7 +71,7 @@ describe('test edit team permissions based on manager permissions', () => { it('user that is NOT a team manager can not edit it', () => { act(() => { - store.dispatch({ type: 'SET_PM_TEAMS', teams: [2, 3] }); + store.dispatch({ type: 'SET_TM_TEAMS', teams: [2, 3] }); }); const wrapper = ({ children }) => {children}; const { result } = renderHook(() => useEditTeamAllowed(team), { wrapper }); From 67ca25e92297c24a5f54db1208ec87315b99df11 Mon Sep 17 00:00:00 2001 From: Sujit Karki Date: Fri, 28 Nov 2025 16:50:28 +0545 Subject: [PATCH 4/5] remove comment and use optional chaining --- frontend/src/hooks/UsePermissions.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/src/hooks/UsePermissions.js b/frontend/src/hooks/UsePermissions.js index f91f8edd06..adb376bab9 100644 --- a/frontend/src/hooks/UsePermissions.js +++ b/frontend/src/hooks/UsePermissions.js @@ -29,7 +29,6 @@ export function useEditProjectAllowed(project) { export function useEditTeamAllowed(team) { const userDetails = useSelector((state) => state.auth.userDetails); const organisations = useSelector((state) => state.auth.organisations); - // const pmTeams = useSelector((state) => state.auth.pmTeams); const tmTeams = useSelector((state) => state.auth.tmTeams); const [isAllowed, setIsAllowed] = useState(false); @@ -40,10 +39,8 @@ export function useEditTeamAllowed(team) { if (organisations && organisations.includes(team.organisation_id)) setIsAllowed(true); // team managers can edit it // verify from the redux store - // if (pmTeams && pmTeams.includes(team.teamId)) setIsAllowed(true); // removing this cause it is fetching all teams having project project manager role - - // removed pm and test using tm list - if (tmTeams && tmTeams.includes(team.teamId)) setIsAllowed(true); + // removed pm and use tm list + if (tmTeams && tmTeams?.includes(team?.teamId)) setIsAllowed(true); if (team.members) { // and verify based on the team members list From 48261f93c97da87a4d1735afef4fe0b5f73b865b Mon Sep 17 00:00:00 2001 From: Sujit Karki Date: Fri, 28 Nov 2025 17:02:07 +0545 Subject: [PATCH 5/5] Refactor user data fetching to async/await with parallel requests - simplified code with async/await and Promise.all() for parallel API calls - Improved error handling and ensured loader is stopped in finally --- frontend/src/store/actions/auth.js | 68 +++++++++++++++++------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/frontend/src/store/actions/auth.js b/frontend/src/store/actions/auth.js index 1debab028b..bac4d9d691 100644 --- a/frontend/src/store/actions/auth.js +++ b/frontend/src/store/actions/auth.js @@ -114,7 +114,7 @@ export const setAuthDetails = (username, token, osm_oauth_token) => (dispatch) = // UPDATES OSM INFORMATION OF THE USER export const setUserDetails = (username, encodedToken, update = false) => - (dispatch) => { + async (dispatch) => { // only trigger the loader if this function is not being triggered to update the user information if (!update) dispatch(setLoader(true)); fetchLocalJSONAPI(`users/${username}/openstreetmap/`, encodedToken) @@ -123,36 +123,44 @@ export const setUserDetails = console.log(error); dispatch(setLoader(false)); }); - // GET USER DETAILS - fetchLocalJSONAPI(`users/queries/${username}/`, encodedToken) - .then((userDetails) => { - dispatch(updateUserDetails(userDetails)); - // GET USER ORGS INFO - fetchLocalJSONAPI( - `organisations/?omitManagerList=true&manager_user_id=${userDetails.id}`, - encodedToken, - ) - .then((orgs) => - dispatch(updateOrgsInfo(orgs.organisations.map((org) => org.organisationId))), - ) - .catch((error) => dispatch(updateOrgsInfo([]))); - fetchLocalJSONAPI( - `teams/?omitMemberList=true&team_role=PROJECT_MANAGER&member=${userDetails.id}`, - encodedToken, - ) - .then((teams) => dispatch(updatePMsTeams(teams.teams.map((team) => team.teamId)))) - .catch((error) => dispatch(updatePMsTeams([]))); - dispatch(setLoader(false)); - fetchLocalJSONAPI(`teams/?fullMemberList=false&manager=${userDetails.id}`, encodedToken) - .then((teams) => dispatch(updateTMsTeams(teams.teams.map((team) => team.teamId)))) - .catch((error) => dispatch(updateTMsTeams([]))); - dispatch(setLoader(false)); - }) - .catch((error) => { - if (error.message === 'InvalidToken') dispatch(logout()); - dispatch(setLoader(false)); - }); + try { + const userDetails = await fetchLocalJSONAPI(`users/queries/${username}/`, encodedToken); + + dispatch(updateUserDetails(userDetails)); + + const userId = userDetails.id; + + const orgsPromise = fetchLocalJSONAPI( + `organisations/?omitManagerList=true&manager_user_id=${userId}`, + encodedToken, + ) + .then((orgs) => dispatch(updateOrgsInfo(orgs.organisations.map((o) => o.organisationId)))) + .catch(() => dispatch(updateOrgsInfo([]))); + + const pmsTeamsPromise = fetchLocalJSONAPI( + `teams/?omitMemberList=true&team_role=PROJECT_MANAGER&member=${userId}`, + encodedToken, + ) + .then((teams) => dispatch(updatePMsTeams(teams.teams.map((t) => t.teamId)))) + .catch(() => dispatch(updatePMsTeams([]))); + + const tmsTeamsPromise = fetchLocalJSONAPI( + `teams/?fullMemberList=false&manager=${userId}`, + encodedToken, + ) + .then((teams) => dispatch(updateTMsTeams(teams.teams.map((t) => t.teamId)))) + .catch(() => dispatch(updateTMsTeams([]))); + + // Run all parallel requests at once + await Promise.all([orgsPromise, pmsTeamsPromise, tmsTeamsPromise]); + } catch (error) { + if (error.message === 'InvalidToken') { + dispatch(logout()); + } + } finally { + dispatch(setLoader(false)); + } }; export const getUserDetails = (state) => (dispatch) => {