diff --git a/src/js/components/Navigation/HeaderBar.jsx b/src/js/components/Navigation/HeaderBar.jsx index f07e310..b474812 100644 --- a/src/js/components/Navigation/HeaderBar.jsx +++ b/src/js/components/Navigation/HeaderBar.jsx @@ -38,6 +38,7 @@ const HeaderBar = ({ hideTabs }) => { }, [isAuth]); const logoutApi = async () => { + // I don't think we want to make the weConnectQueryFn call here since we are about to call mutateLogout const data = await weConnectQueryFn('logout', {}, METHOD.POST); console.log(`/logout response in HeaderBar -- status: '${'status'}', data: ${JSON.stringify(data)}`); clearSignedInGlobals(setAppContextValue); diff --git a/src/js/components/Person/PersonProfile.jsx b/src/js/components/Person/PersonProfile.jsx index 14adf11..32e7b90 100644 --- a/src/js/components/Person/PersonProfile.jsx +++ b/src/js/components/Person/PersonProfile.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import { renderLog } from '../../common/utils/logging'; @@ -9,14 +10,12 @@ import { METHOD, useFetchData } from '../../react-query/WeConnectQuery'; import { captureQuestionnaireListRetrieveData } from '../../models/QuestionnaireModel'; -const PersonProfile = () => { +const PersonProfile = ({ personId }) => { renderLog('PersonProfile'); // Set LOG_RENDER_EVENTS to log all renders - const { getAppContextValue } = useConnectAppContext(); const { apiDataCache } = useConnectAppContext(); const { allQuestionnairesCache } = apiDataCache; const dispatch = useConnectDispatch(); - const [personId] = useState(getAppContextValue('personDrawersPersonId')); const [questionnaireList, setQuestionnaireList] = useState([]); const [showQuestionnaireList, setShowQuestionnaireList] = useState(false); @@ -66,6 +65,9 @@ const PersonProfile = () => { ); }; +PersonProfile.propTypes = { + personId: PropTypes.number, +}; const FullName = styled('h2')` `; diff --git a/src/js/components/Person/PersonProfileDrawerMainContent.jsx b/src/js/components/Person/PersonProfileDrawerMainContent.jsx index cf9b45e..49d211f 100644 --- a/src/js/components/Person/PersonProfileDrawerMainContent.jsx +++ b/src/js/components/Person/PersonProfileDrawerMainContent.jsx @@ -1,17 +1,20 @@ -import React from 'react'; +import React, { useState } from 'react'; import styled from 'styled-components'; import { renderLog } from '../../common/utils/logging'; -// import QuestionnaireResponsesList from '../Questionnaire/QuestionnaireResponsesList'; +import { useConnectAppContext } from '../../contexts/ConnectAppContext'; +import QuestionnaireResponsesList from '../Questionnaire/QuestionnaireResponsesList'; import PersonProfile from './PersonProfile'; const PersonProfileDrawerMainContent = () => { renderLog('PersonProfileDrawerMainContent'); + const { getAppContextValue } = useConnectAppContext(); + const [personId] = useState(getAppContextValue('personDrawersPersonId')); return ( - - {/* */} + + ); }; diff --git a/src/js/components/Person/PersonSummaryRow.jsx b/src/js/components/Person/PersonSummaryRow.jsx index ba0c444..e892af0 100644 --- a/src/js/components/Person/PersonSummaryRow.jsx +++ b/src/js/components/Person/PersonSummaryRow.jsx @@ -11,16 +11,17 @@ import { } from '../../models/PersonModel'; import { useRemoveTeamMemberMutation } from '../../react-query/mutations'; import { DeleteStyled, EditStyled } from '../Style/iconStyles'; +import { viewerCanSeeOrDo } from '../../models/AuthModel'; // import { useRemoveTeamMemberMutationDiverged } from '../../models/TeamModel'; const PersonSummaryRow = ({ person, rowNumberForDisplay, teamId }) => { renderLog('PersonSummaryRow'); // Set LOG_RENDER_EVENTS to log all renders - // console.log('PersonSummaryRow location: ', person && person.location); + const { apiDataCache, setAppContextValue } = useConnectAppContext(); + const { viewerAccessRights } = apiDataCache; + const { mutate } = useRemoveTeamMemberMutation(); // const [person, setPerson] = useState(useGetPersonById(personId)); 2/5/2025 does not work - const { setAppContextValue } = useConnectAppContext(); - const { mutate } = useRemoveTeamMemberMutation(); const removeTeamMemberClick = () => { const params = { personId: person.personId, teamId }; @@ -90,7 +91,7 @@ const PersonSummaryRow = ({ person, rowNumberForDisplay, teamId }) => { > {person.jobTitle} - {hasEditRights ? ( + {viewerCanSeeOrDo('canEditPersonAnyone', viewerAccessRights) ? ( editPersonClick(hasEditRights)} @@ -102,7 +103,6 @@ const PersonSummaryRow = ({ person, rowNumberForDisplay, teamId }) => { ) : ( @@ -111,7 +111,7 @@ const PersonSummaryRow = ({ person, rowNumberForDisplay, teamId }) => { )} {teamId > 0 && ( <> - {hasEditRights ? ( + {viewerCanSeeOrDo('canRemoveTeamMemberAnyTeam', viewerAccessRights) ? ( removeTeamMemberClick(person)} @@ -123,8 +123,6 @@ const PersonSummaryRow = ({ person, rowNumberForDisplay, teamId }) => { ) : ( removeTeamMemberClick(person)} // cellwidth="20" cellwidth={20} > diff --git a/src/js/components/PrivateRoute.jsx b/src/js/components/PrivateRoute.jsx index 1caaeaf..5f36687 100644 --- a/src/js/components/PrivateRoute.jsx +++ b/src/js/components/PrivateRoute.jsx @@ -1,12 +1,14 @@ import React, { useEffect, useState } from 'react'; import { Navigate, Outlet, useLocation } from 'react-router'; import { authLog } from '../common/utils/logging'; -import { useConnectAppContext } from '../contexts/ConnectAppContext'; +import { useConnectAppContext, useConnectDispatch } from '../contexts/ConnectAppContext'; import { METHOD, useFetchData } from '../react-query/WeConnectQuery'; +import { captureAccessRightsData } from '../models/AuthModel'; const PrivateRoute = () => { const location = useLocation(); - const { getAppContextValue, setAppContextValue } = useConnectAppContext(); + const { apiDataCache, getAppContextValue } = useConnectAppContext(); + const dispatch = useConnectDispatch(); const [isAuthenticated, setIsAuthenticated] = useState(null); @@ -15,7 +17,8 @@ const PrivateRoute = () => { if (isSuccessAuth) { console.log('useFetchData in PrivateRoute useEffect dataAuth good:', dataAuth, isSuccessAuth); setIsAuthenticated(dataAuth.isAuthenticated); - setAppContextValue('loggedInPersonIsAdmin', dataAuth.loggedInPersonIsAdmin); + // setAppContextValue('loggedInPersonIsAdmin', dataAuth.loggedInPersonIsAdmin); + captureAccessRightsData(dataAuth, isSuccessAuth, apiDataCache, dispatch); authLog('========= PrivateRoute =========== INNER isAuthenticated: ', dataAuth.isAuthenticated); } }, [dataAuth, isSuccessAuth]); diff --git a/src/js/components/Questionnaire/CopyQuestionnaireLink.jsx b/src/js/components/Questionnaire/CopyQuestionnaireLink.jsx index 63fb149..fcf5c55 100644 --- a/src/js/components/Questionnaire/CopyQuestionnaireLink.jsx +++ b/src/js/components/Questionnaire/CopyQuestionnaireLink.jsx @@ -46,7 +46,7 @@ const styles = () => ({ }); -const CopyQuestionnaireLinkWrapper = styled('div')` +const CopyQuestionnaireLinkWrapper = styled('span')` `; export default withStyles(styles)(CopyQuestionnaireLink); diff --git a/src/js/components/Questionnaire/QuestionnaireResponsesList.jsx b/src/js/components/Questionnaire/QuestionnaireResponsesList.jsx index 71932a2..c319235 100644 --- a/src/js/components/Questionnaire/QuestionnaireResponsesList.jsx +++ b/src/js/components/Questionnaire/QuestionnaireResponsesList.jsx @@ -41,7 +41,7 @@ const QuestionnaireResponsesList = ({ personId }) => { {questionnaireList.length > 0 && ( <> - Questionnaire Responses + Answered {questionnaireList.map((questionnaire) => ( @@ -49,7 +49,7 @@ const QuestionnaireResponsesList = ({ personId }) => { {questionnaire.questionnaireName} - + {/* */} }> { renderLog('TeamHeader'); - const { getAppContextValue, setAppContextValue } = useConnectAppContext(); + const { apiDataCache, getAppContextValue, setAppContextValue } = useConnectAppContext(); + const { viewerAccessRights } = apiDataCache; const { mutate } = useRemoveTeamMutation(); let teamLocal = team; @@ -51,15 +53,23 @@ const TeamHeader = ({ classes, showHeaderLabels, showIcons, team }) => { {/* Edit icon */} {showIcons && ( - - - + <> + {viewerCanSeeOrDo('canEditTeamAnyTeam', viewerAccessRights) && ( + + + + )} + )} {/* Delete icon */} {showIcons && ( - - - + <> + {viewerCanSeeOrDo('canRemoveTeam', viewerAccessRights) && ( + + + + )} + )} ); diff --git a/src/js/contexts/ConnectAppContext.jsx b/src/js/contexts/ConnectAppContext.jsx index 6916d79..8874183 100644 --- a/src/js/contexts/ConnectAppContext.jsx +++ b/src/js/contexts/ConnectAppContext.jsx @@ -3,6 +3,7 @@ import React, { createContext, useContext, useEffect, useReducer, useState } fro import initialApiDataCache from '../models/initialApiDataCache'; // import capturePersonListRetrieveData from '../models/capturePersonListRetrieveData'; import { METHOD, useFetchData } from '../react-query/WeConnectQuery'; +import { captureAccessRightsData } from '../models/AuthModel'; // import { getInitialGlobalPersonVariables, PersonListRetrieveDataCapture } from '../models/PersonModel'; // import { getInitialGlobalTaskVariables } from '../models/TaskModel'; // import { getInitialGlobalTeamVariables } from '../models/TeamModel'; @@ -66,39 +67,6 @@ export const ConnectAppContextProvider = ({ children }) => { } }; - // const { data: dataP, isSuccess: isSuccessP, isFetching: isFetchingP, isStale: isStaleP } = useFetchData(['person-list-retrieve'], {}, METHOD.GET); - // const personListRetrieveResults = useFetchData(['person-list-retrieve'], {}, METHOD.GET); - // This is not currently the right place to pass these values, but I'm saving these here for the next 30 days until we work out the correct place. - // { - // cacheTime: 0, - // networkMode: 'no-cache', <-- This is not a solution, it just covers up some problem in our code, while disabling the biggest benefit of ReactQueries. - // refetchOnMount: true, - // refetchOnWindowFocus: true, - // refetchInterval: 0, - // staleTime: 0, - // } - - // Moved to root pages: Teams, TeamHome, etc. - // useEffect(() => { - // // console.log('useFetchData person-list-retrieve in Teams useEffect:', personListRetrieveResults); - // if (personListRetrieveResults) { - // // console.log('In useEffect apiDataCache:', apiDataCache); - // // const changeResults = - // capturePersonListRetrieveData(personListRetrieveResults, apiDataCache, dispatch); - // // console.log('ConnectAppContext useEffect capturePersonListRetrieveData changeResults:', changeResults); - // } - // }, [personListRetrieveResults]); - - // const { data: dataP, isSuccess: isSuccessP, isFetching: isFetchingP } = personListRetrieveResults; - // useEffect(() => { - // // console.log('useFetchData in TeamHome (person-list-retrieve) useEffect:', dataP, isSuccessP, isFetchingP, isStaleP); - // if (isSuccessP) { - // // console.log('useFetchData in TeamHome (person-list-retrieve)useEffect data good:', dataP, isSuccessP, isFetchingP, isStaleP); - // setAppContextValue('allPeopleList', dataP ? dataP.personList : []); - // // console.log('ConnectAppContext useEffect allPeopleList fetched'); - // } - // }, [dataP, isSuccessP, isFetchingP]); - // The following prints console log errors const { data: dataAuth, isSuccess: isSuccessAuth, isFetching: isFetchingAuth } = useFetchData(['get-auth'], {}, METHOD.POST); useEffect(() => { @@ -108,7 +76,8 @@ export const ConnectAppContextProvider = ({ children }) => { setAppContextValue('authenticatedPerson', dataAuth.person); setAppContextValue('authenticatedPersonId', dataAuth.personId); setAppContextValue('isAuthenticated', isAuthenticated); - setAppContextValue('loggedInPersonIsAdmin', dataAuth.loggedInPersonIsAdmin); + // setAppContextValue('loggedInPersonIsAdmin', dataAuth.loggedInPersonIsAdmin); + captureAccessRightsData(dataAuth, isSuccessAuth, apiDataCache, dispatch); console.log('=============== ConnectAppContextProvider ======= isAuthenticated: ', isAuthenticated, ' ==========='); } diff --git a/src/js/contexts/contextFunctions.jsx b/src/js/contexts/contextFunctions.jsx index 3a0b421..4e7671e 100644 --- a/src/js/contexts/contextFunctions.jsx +++ b/src/js/contexts/contextFunctions.jsx @@ -3,6 +3,6 @@ export const clearSignedInGlobals = (setAppContextValue) => { setAppContextValue('authenticatedPerson', undefined); setAppContextValue('authenticatedPersonId', -1); setAppContextValue('isAuthenticated', false); - setAppContextValue('loggedInPersonIsAdmin', false); + // setAppContextValue('loggedInPersonIsAdmin', false); setAppContextValue('personIsSignedIn', false); }; diff --git a/src/js/models/AuthModel.jsx b/src/js/models/AuthModel.jsx new file mode 100644 index 0000000..76cc053 --- /dev/null +++ b/src/js/models/AuthModel.jsx @@ -0,0 +1,42 @@ +// AuthModel.js +// Functions related to getting data from the apiDataCache, which stores data +// received from our API servers. +import isEqual from 'lodash-es/isEqual'; + + +export const viewerCanSeeOrDo = (accessRightName, viewerAccessRights) => { + if (!viewerAccessRights || !(accessRightName in viewerAccessRights)) { + return false; + } + return viewerAccessRights[accessRightName] || false; +}; + +export function captureAccessRightsData (data = {}, isSuccess = false, apiDataCache = {}, dispatch) { + const viewerAccessRights = apiDataCache.viewerAccessRights || {}; + let changeResults = { + viewerAccessRights, + viewerAccessRightsChanged: false, + }; + let viewerAccessRightsNew = { ...viewerAccessRights }; + // console.log('captureAccessRightsData data:', data); + if (data && data.accessRights && isSuccess === true) { + let newDataReceived = false; + const { accessRights } = data; + if (accessRights && !('canAddPerson' in accessRights)) { + viewerAccessRightsNew = accessRights; + newDataReceived = true; + } else if (!isEqual(accessRights, viewerAccessRightsNew)) { + viewerAccessRightsNew = accessRights; + newDataReceived = true; + } + if (newDataReceived) { + // console.log('=== captureAccessRightsData viewerAccessRightsNew:', viewerAccessRightsNew, ', newDataReceived:', newDataReceived); + dispatch({ type: 'updateByKeyValue', key: 'viewerAccessRights', value: viewerAccessRightsNew }); + changeResults = { + viewerAccessRights: viewerAccessRightsNew, + viewerAccessRightsChanged: true, + }; + } + } + return changeResults; +} diff --git a/src/js/models/PersonModel.jsx b/src/js/models/PersonModel.jsx index a98724c..19a7e50 100644 --- a/src/js/models/PersonModel.jsx +++ b/src/js/models/PersonModel.jsx @@ -1,10 +1,44 @@ // PersonModel.js // Functions related to getting data from the apiDataCache, which stores data // received from our API servers. +import isEqual from 'lodash-es/isEqual'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useConnectAppContext } from '../contexts/ConnectAppContext'; import weConnectQueryFn, { METHOD } from '../react-query/WeConnectQuery'; +export function capturePersonRetrieveData (incomingResults = {}, apiDataCache = {}, dispatch) { + const { data, isSuccess } = incomingResults; + const allPeopleCache = apiDataCache.allPeopleCache || {}; + let changeResults = { + allPeopleCache, + allPeopleCacheChanged: false, + }; + const allPeopleCacheNew = { ...allPeopleCache }; + // We need to only update allPeopleCache the first time we have received new data from the API server + if (data && data.personId && isSuccess === true) { + let newDataReceived = false; + const person = data; + if (person && person.personId && person.personId >= 0) { + if (!allPeopleCacheNew[person.personId]) { + allPeopleCacheNew[person.personId] = person; + newDataReceived = true; + } else if (!isEqual(person, allPeopleCacheNew[person.personId])) { + allPeopleCacheNew[person.personId] = person; + newDataReceived = true; + } + } + // console.log('person-retrieve setting allPeopleCacheNew:', allPeopleCacheNew, ', newDataReceived:', newDataReceived); + if (newDataReceived) { + // setAppContextValue('allPeopleCache', allPeopleCache); + dispatch({ type: 'updateByKeyValue', key: 'allPeopleCache', value: allPeopleCacheNew }); + changeResults = { + allPeopleCache: allPeopleCacheNew, + allPeopleCacheChanged: true, + }; + } + } + return changeResults; +} export const useGetPersonById = (personId) => { const { apiDataCache } = useConnectAppContext(); diff --git a/src/js/models/initialApiDataCache.js b/src/js/models/initialApiDataCache.js index 3045945..cbbbda1 100644 --- a/src/js/models/initialApiDataCache.js +++ b/src/js/models/initialApiDataCache.js @@ -3,6 +3,7 @@ const initialApiDataCache = () => { // These are the "AppContextValues" (i.e., global state variables) used in the PersonModel // console.log('initialApiDataCache called'); // This is worth logging, to see if we are reinitializing the apiDataCache unintentionally const initialGlobalPersonVariables = { + viewerAccessRights: {}, // This is a dictionary with what this person visiting the site can do allPeopleCache: {}, // This is a dictionary key: personId, value: person dict mostRecentPersonIdSaved: -1, mostRecentPersonSaved: { @@ -11,6 +12,7 @@ const initialApiDataCache = () => { personId: '', }, searchResults: [], + teamViewerAccessRights: {}, // This is a dictionary key: personId, value/2nd key: teamId, value: dict with what this person can do on that team }; // These are the "AppContextValues" (i.e., global state variables) used in the PersonModel diff --git a/src/js/pages/Login.jsx b/src/js/pages/Login.jsx index a10910e..ac37928 100644 --- a/src/js/pages/Login.jsx +++ b/src/js/pages/Login.jsx @@ -12,8 +12,9 @@ import compileDate from '../compileDate'; import { PageContentContainer } from '../components/Style/pageLayoutStyles'; import VerifySecretCodeModal from '../components/VerifySecretCodeModal'; import webAppConfig from '../config'; -import { useConnectAppContext } from '../contexts/ConnectAppContext'; -import { clearSignedInGlobals } from '../contexts/contextFunctions'; +import { useConnectAppContext, useConnectDispatch } from '../contexts/ConnectAppContext'; +// import { clearSignedInGlobals } from '../contexts/contextFunctions'; +import { captureAccessRightsData } from '../models/AuthModel'; import { getFullNamePreferredPerson } from '../models/PersonModel'; import { useLogoutMutation } from '../react-query/mutations'; import weConnectQueryFn, { METHOD, useFetchData } from '../react-query/WeConnectQuery'; @@ -24,7 +25,8 @@ const Login = ({ classes }) => { renderLog('Login'); const navigate = useNavigate(); const queryClient = useQueryClient(); - const { setAppContextValue } = useConnectAppContext(); + const { apiDataCache, setAppContextValue } = useConnectAppContext(); + const dispatch = useConnectDispatch(); const { mutate: mutateLogout } = useLogoutMutation(); const firstNameFldRef = useRef(''); @@ -54,7 +56,8 @@ const Login = ({ classes }) => { setAuthPerson(authenticatedPerson); const success = isAuthenticated && authenticatedPerson ? `Signed in as ${getFullNamePreferredPerson(authenticatedPerson)}` : 'Please sign in'; setSuccessLine(success); - setAppContextValue('loggedInPersonIsAdmin', dataAuth.loggedInPersonIsAdmin); + // setAppContextValue('loggedInPersonIsAdmin', dataAuth.loggedInPersonIsAdmin); + captureAccessRightsData(dataAuth, isSuccessAuth, apiDataCache, dispatch); if (isAuthenticated && returnFromLogin) { setTimeout(() => { navigate('/tasks'); @@ -155,7 +158,8 @@ const Login = ({ classes }) => { }; const useSignOutPressed = () => { - clearSignedInGlobals(setAppContextValue); + // clearSignedInGlobals is also called in logoutApi, so isn't needed here + // clearSignedInGlobals(setAppContextValue); logoutApi().then(); }; diff --git a/src/js/pages/QuestionnaireAnswers.jsx b/src/js/pages/QuestionnaireAnswers.jsx index 829eede..52d2305 100644 --- a/src/js/pages/QuestionnaireAnswers.jsx +++ b/src/js/pages/QuestionnaireAnswers.jsx @@ -9,49 +9,31 @@ import DesignTokenColors from '../common/components/Style/DesignTokenColors'; import { renderLog } from '../common/utils/logging'; import { PageContentContainer } from '../components/Style/pageLayoutStyles'; import webAppConfig from '../config'; -import { getFullNamePreferredPerson } from '../models/PersonModel'; +import { useConnectAppContext, useConnectDispatch } from '../contexts/ConnectAppContext'; +import { useGetFullNamePreferred } from '../models/PersonModel'; // capturePersonRetrieveData +import { captureQuestionListRetrieveData } from '../models/QuestionnaireModel'; import { METHOD, useFetchData } from '../react-query/WeConnectQuery'; // eslint-disable-next-line no-unused-vars -const QuestionnaireAnswers = ({ classes, match }) => { +const QuestionnaireAnswers = ({ classes }) => { renderLog('QuestionnaireAnswers'); - const [questionnaireId] = useState(parseInt(useParams().questionnaireId)); + const { apiDataCache } = useConnectAppContext(); + const { allQuestionsCache } = apiDataCache; + const dispatch = useConnectDispatch(); + const [personId] = useState(parseInt(useParams().personId)); - const [person, setPerson] = useState(undefined); const [questionList, setQuestionList] = useState(undefined); const [questionnaire] = useState({}); + const [questionnaireId] = useState(parseInt(useParams().questionnaireId)); - const { data: dataQL, isSuccess: isSuccessQL, isFetching: isFetchingQL } = useFetchData(['question-list-retrieve'], {}, METHOD.GET); - useEffect(() => { - console.log('useFetchData in QuestionnaireAnswers (question-list-retrieve) useEffect:', dataQL, isSuccessQL); - if (isSuccessQL) { - setQuestionList(dataQL ? dataQL.questionList : undefined); - } - }, [dataQL, isSuccessQL, isFetchingQL]); - - const { data: dataPerson, isSuccess: isSuccessPerson, isFetching: isFetchingPerson } = useFetchData(['person-retrieve'], { personId }, METHOD.GET); - useEffect(() => { - console.log('useFetchData in QuestionnaireAnswers (person-retrieve) useEffect:', dataPerson, isSuccessPerson); - if (isSuccessPerson) { - setPerson(dataPerson); // hack - } - }, [dataPerson, isSuccessPerson, isFetchingPerson]); - - const { data: dataQuestionList, isSuccess: isSuccessQuestionList, isFetching: isFetchingQuestionList } = - useFetchData(['question-list-retrieve'], { questionnaireId }, METHOD.GET); + const questionListRetrieveResults = useFetchData(['question-list-retrieve'], { questionnaireId: questionnaireId || '-1' }, METHOD.GET); useEffect(() => { - console.log('useFetchData question-list-retrieve in QuestionnaireAnswers useEffect:', dataQuestionList, isSuccessQuestionList, isFetchingQuestionList); - if (dataQuestionList !== undefined && isFetchingQuestionList === false) { - console.log('useFetchData question-list-retrieve in QuestionnaireAnswers useEffect data is good:', dataQuestionList, isSuccessQuestionList, isFetchingQuestionList); - const questionListTemp = dataQuestionList.questionList; - console.log('Successfully retrieved question-list-retrieve... questionListTemp', questionListTemp); - setQuestionList(dataQuestionList.questionList); + if (questionListRetrieveResults) { + captureQuestionListRetrieveData(questionListRetrieveResults, apiDataCache, dispatch); } - }, [dataQuestionList, isFetchingQuestionList]); + }, [questionListRetrieveResults, allQuestionsCache]); - /* eslint-disable arrow-body-style */ - // eslint-disable-next-line no-unused-vars const getAnswerValue = (questionId) => { // if (allAnswersCache && allAnswersCache[questionId]) { // const questionAnswer = allAnswersCache[questionId]; @@ -60,6 +42,13 @@ const QuestionnaireAnswers = ({ classes, match }) => { return ''; }; + useEffect(() => { + // console.log('useEffect in QuestionnaireAnswers (question-list-retrieve)'); + // if (allQuestionsCache) { + // setQuestionList(Object.values(allQuestionsCache)); + // } + }, [allQuestionsCache]); + return (
@@ -79,8 +68,8 @@ const QuestionnaireAnswers = ({ classes, match }) => { Answered by: {' '} - {/* {useGetFullNamePreferred(person)} */} - {person ? getFullNamePreferredPerson(person) : 'tbd'} + {useGetFullNamePreferred(personId)} + {/* {person ? getFullNamePreferredPerson(person) : 'tbd'} */} {questionList && questionList.map((question) => ( @@ -114,7 +103,6 @@ const QuestionnaireAnswers = ({ classes, match }) => { }; QuestionnaireAnswers.propTypes = { classes: PropTypes.object.isRequired, - match: PropTypes.object.isRequired, }; const styles = (theme) => ({ diff --git a/src/js/pages/SystemSettings/PermissionsAdministration.jsx b/src/js/pages/SystemSettings/PermissionsAdministration.jsx index 8f2044b..95dd65d 100644 --- a/src/js/pages/SystemSettings/PermissionsAdministration.jsx +++ b/src/js/pages/SystemSettings/PermissionsAdministration.jsx @@ -6,6 +6,7 @@ import styled from 'styled-components'; import { renderLog } from '../../common/utils/logging'; import { MatchingPerson, SearchBarWrapper } from '../../components/Style/sharedStyles'; import { useConnectAppContext } from '../../contexts/ConnectAppContext'; +import { viewerCanSeeOrDo } from '../../models/AuthModel'; import { getFullNamePreferredPerson } from '../../models/PersonModel'; import makeRequestParams from '../../react-query/makeRequestParams'; import { usePersonSaveMutation } from '../../react-query/mutations'; @@ -17,28 +18,19 @@ const PermissionsAdministration = ({ classes }) => { renderLog('PermissionsAdministration'); const { mutate } = usePersonSaveMutation(); - const { getAppContextValue, apiDataCache: { allPeopleCache } } = useConnectAppContext(); + const { apiDataCache } = useConnectAppContext(); + const { allPeopleCache, viewerAccessRights } = apiDataCache; + const [peopleWorkingArray, setPeopleWorkingArray] = useState(); // Object.values(allPeopleCacheCopy1)); const [updateCount, setUpdateCount] = useState(0); - const [isSignedInAdmin, setIsSignedInAdmin] = useState(getAppContextValue('loggedInPersonIsAdmin')); - const [errorText, setErrorText] = useState(getAppContextValue('loggedInPersonIsAdmin') ? '' : - 'These checkmarks are read-only since you do not have Admin privileges'); + const [canEditPermissionsAnyone, setCanEditPermissionsAnyone] = useState(false); const searchByNameRef = useRef(''); const searchByEmailRef = useRef(''); useEffect(() => { - const isLoggedInAdmin = getAppContextValue('loggedInPersonIsAdmin'); - if (isLoggedInAdmin !== null) { - setIsSignedInAdmin(getAppContextValue('loggedInPersonIsAdmin')); - if (isLoggedInAdmin) { - setErrorText(''); - } - console.log(' useEffect(() => getAppContextValue(\'loggedInPersonIsAdmin\') updated to: ', isLoggedInAdmin); - } else { - console.log(' useEffect(() body skipped since \'loggedInPersonIsAdmin\': ', isLoggedInAdmin); - } - }, [isSignedInAdmin]); + setCanEditPermissionsAnyone(viewerCanSeeOrDo('canEditPermissionsAnyone', viewerAccessRights)); + }, [viewerAccessRights]); useEffect(() => { const allPeopleCacheCopy2 = JSON.parse(JSON.stringify(allPeopleCache)); @@ -109,7 +101,7 @@ const PermissionsAdministration = ({ classes }) => { const onClickCheckbox = (event) => { console.log(event); // eslint-disable-next-line no-unused-vars - if (isSignedInAdmin) { + if (canEditPermissionsAnyone) { const pieces = event.target.id.split('-'); const personId = parseInt(pieces[2]); const person = peopleWorkingArray.find((p) => p.id === personId); @@ -178,7 +170,11 @@ const PermissionsAdministration = ({ classes }) => { /> Search is not yet implemented - {errorText} + {!canEditPermissionsAnyone && ( + + These checkmarks are read-only since you do not have Admin privileges. + + )} @@ -274,10 +270,14 @@ const PermissionsAdministration = ({ classes }) => { /> ))} diff --git a/src/js/pages/SystemSettings/SystemSettings.jsx b/src/js/pages/SystemSettings/SystemSettings.jsx index 970d376..4ca316d 100644 --- a/src/js/pages/SystemSettings/SystemSettings.jsx +++ b/src/js/pages/SystemSettings/SystemSettings.jsx @@ -17,13 +17,13 @@ import { captureQuestionnaireListRetrieveData } from '../../models/Questionnaire import { captureTaskDefinitionListRetrieveData, captureTaskGroupListRetrieveData, captureTaskStatusListRetrieveData } from '../../models/TaskModel'; import { METHOD, useFetchData } from '../../react-query/WeConnectQuery'; import PermissionsAdministration from './PermissionsAdministration'; +import { viewerCanSeeOrDo } from '../../models/AuthModel'; const SystemSettings = ({ classes }) => { renderLog('SystemSettings'); - const { setAppContextValue } = useConnectAppContext(); - const { apiDataCache } = useConnectAppContext(); - const { allPeopleCache, allTaskGroupsCache, allQuestionnairesCache } = apiDataCache; + const { apiDataCache, setAppContextValue } = useConnectAppContext(); + const { viewerAccessRights, allPeopleCache, allTaskGroupsCache, allQuestionnairesCache } = apiDataCache; const dispatch = useConnectDispatch(); const [personIdsList, setPersonIdsList] = useState([]); @@ -120,6 +120,14 @@ const SystemSettings = ({ classes }) => { navigate(`/questionnaire/${questionnaire.questionnaireId}`); }; + if (!viewerCanSeeOrDo('canViewSystemSettings', viewerAccessRights)) { + return ( + +

You do not have permission to access this page.

+
+ ); + } + return (
diff --git a/src/js/pages/Teams.jsx b/src/js/pages/Teams.jsx index e25cac8..57401fe 100644 --- a/src/js/pages/Teams.jsx +++ b/src/js/pages/Teams.jsx @@ -15,16 +15,15 @@ import { useConnectAppContext, useConnectDispatch } from '../contexts/ConnectApp import { isSearchTextFoundInPerson } from '../controllers/PersonController'; import { isSearchTextFoundInTeam } from '../controllers/TeamController'; import capturePersonListRetrieveData from '../models/capturePersonListRetrieveData'; +import { viewerCanSeeOrDo } from '../models/AuthModel'; import { captureTeamListRetrieveData, getTeamMembersListByTeamId } from '../models/TeamModel'; import { METHOD, useFetchData } from '../react-query/WeConnectQuery'; -// eslint-disable-next-line no-unused-vars const Teams = () => { renderLog('Teams'); - const { setAppContextValue, getAppContextValue } = useConnectAppContext(); - const { apiDataCache } = useConnectAppContext(); - const { allPeopleCache, allTeamsCache } = apiDataCache; + const { apiDataCache, setAppContextValue, getAppContextValue } = useConnectAppContext(); + const { viewerAccessRights, allPeopleCache, allTeamsCache } = apiDataCache; const dispatch = useConnectDispatch(); const [searchText, setSearchText] = useState(''); @@ -166,16 +165,20 @@ const Teams = () => { - - addTeamClick()}> - Add team - - - - addTeamMemberClick()}> - Add team member - - + {viewerCanSeeOrDo('canAddTeam', viewerAccessRights) && ( + + addTeamClick()}> + Add team + + + )} + {viewerCanSeeOrDo('canAddTeamMemberAnyTeam', viewerAccessRights) && ( + + addTeamMemberClick()}> + Add team member + + + )} {/* NOTE: we had discussed refactoring team-list-retrieve to not include person data, */} diff --git a/src/js/react-query/WeConnectQuery.js b/src/js/react-query/WeConnectQuery.js index 274e687..0c2d532 100644 --- a/src/js/react-query/WeConnectQuery.js +++ b/src/js/react-query/WeConnectQuery.js @@ -25,6 +25,11 @@ const weConnectQueryFn = async (queryKey, params, isGet) => { await axios.get(url.href, { withCredentials: true }) : await axios.post(url.href, params, { withCredentials: true }); // if needed: httpLog('weConnectQueryFn response.data: ', JSON.stringify(response.data)); + // console.log('weConnectQueryFn response.status: ', response.status, ', response.data: ', JSON.stringify(response.data) || 'No data'); + if (response.data.displayErrorMessage) { + // TODO Consider showing this API error in the interface to the viewer + console.error(`displayErrorMessage ${queryKey} status: ${response.data.status}`); + } } catch (e) { console.error('Axios ', (isGet ? 'axios.get' : 'axios.post'), ' error: ', e); }
- {isSignedInAdmin && } + {canEditPermissionsAnyone && ( + + )} - {isSignedInAdmin && } + {canEditPermissionsAnyone && ( + + )}