From 374c185938b8bf558b7e01f7b8ab56f7ae8d5019 Mon Sep 17 00:00:00 2001 From: dalemcgrew Date: Sun, 16 Feb 2025 15:06:46 -0800 Subject: [PATCH 1/2] Implemented search on Teams page, which returns people or teams matching the search terms. Updated SearchBase to set autofocus on the search box. Now unsetting addPersonDrawerTeam team when closing AddPersonDrawer. Started implementing new UX team designs. --- .../common/components/Search/SearchBase.jsx | 20 +- src/js/components/Drawers/AddPersonDrawer.jsx | 9 +- src/js/components/Drawers/DrawerTemplateA.jsx | 15 +- .../Person/AddPersonDrawerMainContent.jsx | 106 +++++---- src/js/components/Person/AddPersonForm.jsx | 15 +- .../Person/EditPersonDrawerMainContent.jsx | 15 +- .../Team/AddTeamDrawerMainContent.jsx | 8 +- src/js/components/Team/AddTeamForm.jsx | 4 +- src/js/components/Team/TeamMemberList.jsx | 31 +-- src/js/controllers/PersonController.js | 44 ++++ src/js/controllers/TeamController.js | 38 ++++ src/js/models/PersonModel.jsx | 3 + src/js/models/TeamModel.jsx | 3 + src/js/pages/TeamHome.jsx | 10 +- src/js/pages/Teams.jsx | 210 +++++++++++++----- 15 files changed, 378 insertions(+), 153 deletions(-) create mode 100644 src/js/controllers/PersonController.js create mode 100644 src/js/controllers/TeamController.js diff --git a/src/js/common/components/Search/SearchBase.jsx b/src/js/common/components/Search/SearchBase.jsx index 59012fc..a6df08b 100644 --- a/src/js/common/components/Search/SearchBase.jsx +++ b/src/js/common/components/Search/SearchBase.jsx @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { createRef } from 'react'; import styled from 'styled-components'; import colors from '../Style/Colors'; import normalizedImagePath from '../../utils/normalizedImagePath'; @@ -11,6 +11,13 @@ class SearchBase extends React.Component { constructor (props) { super(props); this.state = { searchText: '' }; + this.inputRef = createRef(); + } + + componentDidMount () { + if (this.inputRef.current) { + this.inputRef.current.focus(); + } } handleInputChange = (event) => { @@ -40,12 +47,14 @@ class SearchBase extends React.Component { {!this.state.searchText && } {this.state.searchText && } @@ -110,7 +119,6 @@ const SearchInput = styled('input')` padding-right: 40px; padding-left: 12px; - &:focus-visible { border: none; outline: ${colors.primary} solid 2px !important; diff --git a/src/js/components/Drawers/AddPersonDrawer.jsx b/src/js/components/Drawers/AddPersonDrawer.jsx index b013983..88e0bd5 100644 --- a/src/js/components/Drawers/AddPersonDrawer.jsx +++ b/src/js/components/Drawers/AddPersonDrawer.jsx @@ -1,19 +1,26 @@ import React from 'react'; import { renderLog } from '../../common/utils/logging'; import AddPersonDrawerMainContent from '../Person/AddPersonDrawerMainContent'; +import { useConnectAppContext } from '../../contexts/ConnectAppContext'; import DrawerTemplateA from './DrawerTemplateA'; const AddPersonDrawer = () => { renderLog('AddPersonDrawer'); // Set LOG_RENDER_EVENTS to log all renders + const { getAppContextValue, setAppContextValue } = useConnectAppContext(); + + const onDrawerClose = () => { + setAppContextValue('addPersonDrawerTeam', undefined); + }; return ( } - headerTitleJsx={<>Add Team Member} + headerTitleJsx={<>{getAppContextValue('AddPersonDrawerLabel')}} headerFixedJsx={<>} + onDrawerClose={onDrawerClose} /> ); }; diff --git a/src/js/components/Drawers/DrawerTemplateA.jsx b/src/js/components/Drawers/DrawerTemplateA.jsx index 7a115b7..09b36bb 100644 --- a/src/js/components/Drawers/DrawerTemplateA.jsx +++ b/src/js/components/Drawers/DrawerTemplateA.jsx @@ -11,7 +11,8 @@ import { cordovaDrawerTopMargin } from '../../utils/cordovaOffsets'; import { DrawerHeaderAnimateDownInnerContainer, DrawerHeaderAnimateDownOuterContainer, DrawerHeaderWrapper, DrawerTitle } from '../Style/drawerLayoutStyles'; -const DrawerTemplateA = ({ classes, drawerId, drawerOpenGlobalVariableName, headerFixedJsx, headerTitleJsx, mainContentJsx }) => { // classes, teamId +const DrawerTemplateA = (props) => { + const { classes, drawerId, drawerOpenGlobalVariableName, headerFixedJsx, headerTitleJsx, mainContentJsx, onDrawerClose } = props; renderLog(`DrawerTemplateA (${drawerId})`); // Set LOG_RENDER_EVENTS to log all renders const { getAppContextData, setAppContextValue, getAppContextValue } = useConnectAppContext(); @@ -31,6 +32,13 @@ const DrawerTemplateA = ({ classes, drawerId, drawerOpenGlobalVariableName, head } }; + const onDrawerCloseLocal = () => { + setAppContextValue(drawerOpenGlobalVariableName, false); + if (onDrawerClose) { + onDrawerClose(); + } + }; + useEffect(() => { // console.log('DrawerTemplateA: Context value changed: ', // drawerId, drawerOpenGlobalVariableName, getAppContextValue(drawerOpenGlobalVariableName)); @@ -61,7 +69,7 @@ const DrawerTemplateA = ({ classes, drawerId, drawerOpenGlobalVariableName, head classes={{ paper: classes.drawer }} direction="left" id={drawerId} - onClose={() => setAppContextValue(drawerOpenGlobalVariableName, false)} + onClose={onDrawerCloseLocal} open={drawerOpen} > @@ -73,7 +81,7 @@ const DrawerTemplateA = ({ classes, drawerId, drawerOpenGlobalVariableName, head aria-label="Close" className={classes.closeButton} id={`${drawerId}Close`} - onClick={() => setAppContextValue(drawerOpenGlobalVariableName, false)} + onClick={onDrawerCloseLocal} size="large" > @@ -100,6 +108,7 @@ DrawerTemplateA.propTypes = { mainContentJsx: PropTypes.object, headerTitleJsx: PropTypes.object, headerFixedJsx: PropTypes.object, + onDrawerClose: PropTypes.func, }; const styles = () => ({ diff --git a/src/js/components/Person/AddPersonDrawerMainContent.jsx b/src/js/components/Person/AddPersonDrawerMainContent.jsx index 6511806..fd381e7 100644 --- a/src/js/components/Person/AddPersonDrawerMainContent.jsx +++ b/src/js/components/Person/AddPersonDrawerMainContent.jsx @@ -4,6 +4,7 @@ import styled from 'styled-components'; import arrayContains from '../../common/utils/arrayContains'; import { renderLog } from '../../common/utils/logging'; import { useConnectAppContext } from '../../contexts/ConnectAppContext'; +import { getTeamMembersListByTeamId } from '../../models/TeamModel'; import makeRequestParams from '../../react-query/makeRequestParams'; import { useAddPersonToTeamMutation } from '../../react-query/mutations'; import { SpanWithLinkStyle } from '../Style/linkStyles'; @@ -13,50 +14,68 @@ import AddPersonForm from './AddPersonForm'; const AddPersonDrawerMainContent = () => { renderLog('AddPersonDrawerMainContent'); const { apiDataCache, getAppContextValue } = useConnectAppContext(); - const { allPeopleCache } = apiDataCache; + const { allPeopleCache, allTeamsCache } = apiDataCache; const { mutate } = useAddPersonToTeamMutation(); // const params = useParams(); // console.log('AddPersonDrawerMainContent params: ', params); + const [addToTeamList, setAddToTeamList] = useState([]); const [allPeopleList, setAllPeopleList] = useState([]); const [remainingPeopleToAdd, setRemainingPeopleToAdd] = useState([]); const [searchResultsList, setSearchResultsList] = useState(undefined); - const [thisTeamsCurrentMembersList] = useState(getAppContextValue('addPersonDrawerTeamMemberList')); + const [thisTeamsCurrentMembersList, setThisTeamsCurrentMembersList] = useState([]); const [team] = useState(getAppContextValue('addPersonDrawerTeam')); const [teamMemberPersonIdList] = useState([]); const [matchingCountText, setMatchingCountText] = useState(''); const searchStringRef = useRef(''); - const initializeRemainingPeopleToAddList = () => { + const updateRemainingPeopleToAdd = () => { // console.log('initializeTheRemainingPeopleToAddListList in AddPersonDrawerMainContent'); // Start with the passed in allPeopleList, create the remainingPeopleToAddList, by removing any people already on the team - if (allPeopleList && allPeopleList.length > 0) { - const personToDisplay = []; - allPeopleList.forEach((onePeople) => { - const isOnTeam = thisTeamsCurrentMembersList.some((obj) => obj.id === onePeople.id); + if (allPeopleList && allPeopleList.length > 0 && thisTeamsCurrentMembersList && thisTeamsCurrentMembersList.length >= 0) { + const remainingPeopleToAddTemp = []; + allPeopleList.forEach((onePerson) => { + const isOnTeam = thisTeamsCurrentMembersList.some((obj) => obj.id === onePerson.personId); if (!isOnTeam) { - personToDisplay.push(onePeople); + remainingPeopleToAddTemp.push(onePerson); } }); - setRemainingPeopleToAdd(personToDisplay); + setRemainingPeopleToAdd(remainingPeopleToAddTemp); } }; useEffect(() => { - initializeRemainingPeopleToAddList(); - }, [apiDataCache]); + setAllPeopleList(Object.values(allPeopleCache)); + }, [allPeopleCache]); useEffect(() => { - initializeRemainingPeopleToAddList(); - }, [allPeopleList]); + const teamId = team ? team.teamId : -1; + if (teamId >= 0) { + const teamMembersListTemp = getTeamMembersListByTeamId(teamId, apiDataCache); + // console.log('useEffect in AddPersonDrawerMainContent teamMembersListTemp:', teamMembersListTemp); + setThisTeamsCurrentMembersList(teamMembersListTemp); + } else { + console.log('useEffect in AddPersonDrawerMainContent teamId is -1, so no teamId'); + } + }, [allPeopleCache, allPeopleList, allTeamsCache, team]); + + useEffect(() => { + updateRemainingPeopleToAdd(); + }, [thisTeamsCurrentMembersList]); useEffect(() => { - // console.log('useEffect in AddPersonDrawerMainContent allPeopleCache:', allPeopleCache); + // TODO: Need to deal with preferred name searching and display, very possible but it will get more complicated + let addToTeamListTemp = searchResultsList || remainingPeopleToAdd || []; + addToTeamListTemp = addToTeamListTemp.filter((person) => person.firstName.length || person.lastName.length); + setAddToTeamList(addToTeamListTemp); + }, [searchResultsList, remainingPeopleToAdd]); + + useEffect(() => { + // console.log('== INITIAL useEffect in AddPersonDrawerMainContent'); if (allPeopleCache) { setAllPeopleList(Object.values(allPeopleCache)); - setRemainingPeopleToAdd(Object.values(allPeopleCache)); // handles navigate to issues } }, []); @@ -84,50 +103,51 @@ const AddPersonDrawerMainContent = () => { } }; - const addClicked = (person) => { + const addClicked = (incomingPerson) => { + const personId = incomingPerson ? incomingPerson.personId : -1; + const teamId = team ? team.teamId : -1; + const teamName = team ? team.teamName : ''; const plainParams = { - personId: person.id, - teamId: team.teamId, - teamMemberFirstName: person.firstName, - teamMemberLastName: person.lastName, - teamName: team.teamName, + personId, + teamId, + teamMemberFirstName: incomingPerson.firstName, + teamMemberLastName: incomingPerson.lastName, + teamName, }; mutate(makeRequestParams(plainParams, {})); // Remove this person from the All People less Adds list (since they were added to the team) - const updatedRemainingPeopleToAdd = remainingPeopleToAdd.filter((person) => person.id !== person.id); + const updatedRemainingPeopleToAdd = remainingPeopleToAdd.filter((person) => person.personId !== incomingPerson.personId); setRemainingPeopleToAdd(updatedRemainingPeopleToAdd); - if (searchResultsList && searchResultsList.length) { + if (searchResultsList && searchResultsList.length >= 0) { // also remove them from the searchResultsList if it exists - const updatedSearchResultsList = searchResultsList.filter((person) => person.id !== person.id); + const updatedSearchResultsList = searchResultsList.filter((person) => person.personId !== incomingPerson.personId); setSearchResultsList(updatedSearchResultsList); setMatchingCounter(updatedSearchResultsList); } }; - // TODO: Need to deal with preferred name searching and display, very possible but it will get more complicated - let displayList = searchResultsList || remainingPeopleToAdd || []; - displayList = displayList.filter((person) => person.firstName.length || person.lastName.length); - return ( - - - {matchingCountText} - - {(displayList && displayList.length > 0) && ( + {team && team.teamId >= 0 && ( + + + {matchingCountText} + + )} + {(addToTeamList && addToTeamList.length > 0) && ( { searchResultsList ? 'Filtered list of people to add to team: ' : 'Can be added to team: '} - {displayList.map((person) => ( + {addToTeamList.map((person) => ( {person.firstName} {' '} diff --git a/src/js/components/Person/AddPersonForm.jsx b/src/js/components/Person/AddPersonForm.jsx index a80b76e..3867614 100644 --- a/src/js/components/Person/AddPersonForm.jsx +++ b/src/js/components/Person/AddPersonForm.jsx @@ -10,7 +10,7 @@ import { usePersonSaveMutation } from '../../react-query/mutations'; const AddPersonForm = ({ classes }) => { // classes, teamId renderLog('AddPersonForm'); - const { getAppContextValue } = useConnectAppContext(); + const { getAppContextValue, setAppContextValue } = useConnectAppContext(); const { mutate } = usePersonSaveMutation(); const [teamId, setTeamId] = useState(-1); @@ -23,10 +23,11 @@ const AddPersonForm = ({ classes }) => { // classes, teamId useEffect(() => { // Replaces onAppObservableStoreChange and will be called whenever the context value changes // console.log('AddPersonForm: Context value changed:', true); - setTeamId(getAppContextValue('addPersonDrawerTeam').id); - setTeamName(getAppContextValue('addPersonDrawerTeam').teamName); - }, []); - // }, [getAppContextValue]); // TODO DALE: commented out for now to avoid infinite loop + if (getAppContextValue('addPersonDrawerTeam')) { + setTeamId(getAppContextValue('addPersonDrawerTeam').id); + setTeamName(getAppContextValue('addPersonDrawerTeam').teamName); + } + }, [getAppContextValue]); const saveNewPerson = () => { @@ -41,6 +42,9 @@ const AddPersonForm = ({ classes }) => { // classes, teamId teamName, }; mutate(makeRequestParams(plainParams, data)); + setAppContextValue('addPersonDrawerOpen', false); + setAppContextValue('addPersonDrawerLabel', ''); + setAppContextValue('addPersonDrawerTeam', undefined); }; const updateSaveButton = () => { @@ -53,7 +57,6 @@ const AddPersonForm = ({ classes }) => { // classes, teamId } }; - return ( diff --git a/src/js/components/Person/EditPersonDrawerMainContent.jsx b/src/js/components/Person/EditPersonDrawerMainContent.jsx index 217f0d2..3e5a7a9 100644 --- a/src/js/components/Person/EditPersonDrawerMainContent.jsx +++ b/src/js/components/Person/EditPersonDrawerMainContent.jsx @@ -1,24 +1,11 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import styled from 'styled-components'; import { renderLog } from '../../common/utils/logging'; -import { useConnectAppContext } from '../../contexts/ConnectAppContext'; import EditPersonForm from './EditPersonForm'; const EditPersonDrawerMainContent = () => { renderLog('EditPersonDrawerMainContent'); - const { getAppContextValue } = useConnectAppContext(); - - // eslint-disable-next-line no-unused-vars - const [teamId, setTeamId] = useState(-1); - - useEffect(() => { // Replaces onAppObservableStoreChange and will be called whenever the context value changes - // console.log('EditPersonDrawerMainContent: Context value changed:', true); - // 2/14/25 warning React Hook useEffect contains a call to 'setTeamId'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [getAppContextValue] as a second argument to the useEffect Hook react-hooks/exhaustive-deps - const teamIdTemp = getAppContextValue('editPersonDrawerTeamId'); - setTeamId(teamIdTemp); - }); - // }, [getAppContextValue]); // TODO DALE: commented out for now to avoid infinite loop return ( diff --git a/src/js/components/Team/AddTeamDrawerMainContent.jsx b/src/js/components/Team/AddTeamDrawerMainContent.jsx index 3db5228..32048af 100644 --- a/src/js/components/Team/AddTeamDrawerMainContent.jsx +++ b/src/js/components/Team/AddTeamDrawerMainContent.jsx @@ -17,6 +17,10 @@ const AddTeamDrawerMainContent = ({ classes }) => { // classes, teamId const [allTeamsList] = useState(Object.values(allTeamsCache)); const [teamSearchResultsList, setTeamSearchResultsList] = useState([]); + const clearFunction = () => { + setTeamSearchResultsList([]); + }; + const searchFunction = (incomingSearchText) => { // console.log('AddTeamDrawerMainContent searchFunction incomingSearchText: ', incomingSearchText); const isSearching = (incomingSearchText && incomingSearchText.length > 0); @@ -39,10 +43,6 @@ const AddTeamDrawerMainContent = ({ classes }) => { // classes, teamId } }; - const clearFunction = () => { - setTeamSearchResultsList([]); - }; - return ( diff --git a/src/js/components/Team/AddTeamForm.jsx b/src/js/components/Team/AddTeamForm.jsx index 64d5683..d3375f6 100644 --- a/src/js/components/Team/AddTeamForm.jsx +++ b/src/js/components/Team/AddTeamForm.jsx @@ -11,7 +11,7 @@ import weConnectQueryFn, { METHOD } from '../../react-query/WeConnectQuery'; const AddTeamForm = ({ classes }) => { renderLog('AddTeamForm'); - const { getAppContextValue } = useConnectAppContext(); + const { getAppContextValue, setAppContextValue } = useConnectAppContext(); const teamNameFldRef = useRef(''); const queryClient = useQueryClient(); @@ -41,6 +41,8 @@ const AddTeamForm = ({ classes }) => { setTeamNameCached(teamName); console.log('saveNewTeam data:', teamName); saveTeamMutation.mutate(); + setAppContextValue('addTeamDrawerOpen', false); + setAppContextValue('addTeamDrawerLabel', ''); }; return ( diff --git a/src/js/components/Team/TeamMemberList.jsx b/src/js/components/Team/TeamMemberList.jsx index cfae736..3c2f165 100644 --- a/src/js/components/Team/TeamMemberList.jsx +++ b/src/js/components/Team/TeamMemberList.jsx @@ -3,16 +3,17 @@ import PropTypes from 'prop-types'; import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import { renderLog } from '../../common/utils/logging'; +import { isSearchTextFoundInPerson } from '../../controllers/PersonController'; import { useConnectAppContext } from '../../contexts/ConnectAppContext'; import { getTeamMembersListByTeamId } from '../../models/TeamModel'; import { METHOD, useFetchData } from '../../react-query/WeConnectQuery'; import PersonSummaryRow from '../Person/PersonSummaryRow'; // DO NOT REMOVE PASSED in TEAM -const TeamMemberList = ({ teamId, team }) => { // teamMemberList +const TeamMemberList = ({ searchText, teamId, team }) => { // teamMemberList renderLog('TeamMemberList'); const { apiDataCache } = useConnectAppContext(); - const { allPeopleCache, allTeamsCache } = apiDataCache; + // const { allPeopleCache, allTeamsCache } = apiDataCache; const [teamMemberListApiDataCache, setTeamMemberListApiDataCache] = useState([]); const [teamMemberListReactQuery, setTeamMemberListReactQuery] = useState(team.teamMemberList || []); // const teamMemberList = useGetTeamMembersListByTeamId(teamId); @@ -63,12 +64,20 @@ const TeamMemberList = ({ teamId, team }) => { // teamMemberList // console.log('====== Cached by ReactQuery teamMemberList: ', teamMemberListReactQuery); // console.log('====== Cached by apiDataCache teamMemberList: ', teamMemberListApiDataCache); + const showPerson = (person, searchTextLocal) => { + if (!person || person.id < 0) return false; // Invalid person or personId + if (searchTextLocal) { + return isSearchTextFoundInPerson(searchTextLocal, person); + } else { + return true; // Show the person if no searchText is provided + } + }; return ( {teamMemberListApiDataCache.map((person, index) => { - if (teamId === 10) console.log(`TeamMemberList teamId: ${teamId}, person: ${person} location ${person.location}`); - if (person) { + // if (teamId === 10) console.log(`TeamMemberList teamId: ${teamId}, person: ${person} location ${person.location}`); + if (showPerson(person, searchText)) { return ( { // teamMemberList /> ); } else { - return null; // Empty row for non-existing members + return null; // Empty row for members we don't want to show } })} ); }; TeamMemberList.propTypes = { + searchText: PropTypes.string, teamId: PropTypes.any.isRequired, team: PropTypes.object.isRequired, }; -const styles = (theme) => ({ - ballotButtonIconRoot: { - marginRight: 8, - }, - addTeamButtonRoot: { - width: 120, - [theme.breakpoints.down('md')]: { - width: '100%', - }, - }, +const styles = () => ({ }); const TeamMembersWrapper = styled('div')` diff --git a/src/js/controllers/PersonController.js b/src/js/controllers/PersonController.js new file mode 100644 index 0000000..e564487 --- /dev/null +++ b/src/js/controllers/PersonController.js @@ -0,0 +1,44 @@ +// PersonController.js +// Functions for manipulating data related to the person table. + +export const searchWordFoundInOnePerson = (searchWord, person) => { + const fieldsToSearch = [ + 'birthdayMonthAndDay', 'bluesky', 'facebookUrl', 'githubUrl', + 'linkedInUrl', 'portfolioUrl', 'snapchat', 'twitch', 'twitterHandle', + 'websiteUrl', 'emailOfficial', 'emailPersonal', + 'firstName', 'firstNamePreferred', 'jobTitle', 'lastName', + 'location', 'stateCode', 'zipCode', + ]; + let found = false; + + const normalizedSearchWord = searchWord.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ''); + fieldsToSearch.forEach((fieldValue) => { + const personFieldValue = person[fieldValue]; + if (personFieldValue) { + const normalizedPersonFieldValue = personFieldValue.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ''); + if (normalizedPersonFieldValue.includes(normalizedSearchWord)) { + found = true; + } + } + }); + return found; +}; + +export const isSearchTextFoundInPerson = (incomingSearchText, person) => { + if (!person || person.personId < 0) return false; // Invalid person or personId + if (!incomingSearchText || (incomingSearchText && incomingSearchText.length === 0)) return true; // No searchText provided + const searchWords = incomingSearchText.split(' '); + let atLeastOneSearchWordFound = false; + let allSearchWordsFound = true; + let searchWordFound = false; + searchWords.forEach((searchWord) => { + searchWordFound = searchWordFoundInOnePerson(searchWord, person); + if (searchWordFound) { + atLeastOneSearchWordFound = true; + } + if (!searchWordFound) { + allSearchWordsFound = false; + } + }); + return atLeastOneSearchWordFound && allSearchWordsFound; +}; diff --git a/src/js/controllers/TeamController.js b/src/js/controllers/TeamController.js new file mode 100644 index 0000000..8a276ac --- /dev/null +++ b/src/js/controllers/TeamController.js @@ -0,0 +1,38 @@ +// TeamController.js +// Functions for manipulating data related to teams and team membership. + +export const searchWordFoundInOneTeam = (searchWord, team) => { + const fieldsToSearch = ['description', 'teamName']; + let found = false; + + const normalizedSearchWord = searchWord.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ''); + fieldsToSearch.forEach((fieldValue) => { + const teamFieldValue = team[fieldValue]; + if (teamFieldValue) { + const normalizedPersonFieldValue = teamFieldValue.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ''); + if (normalizedPersonFieldValue.includes(normalizedSearchWord)) { + found = true; + } + } + }); + return found; +}; + +export const isSearchTextFoundInTeam = (incomingSearchText, team) => { + if (!team || team.teamId < 0) return false; // Invalid team or teamId + if (!incomingSearchText || (incomingSearchText && incomingSearchText.length === 0)) return true; // No searchText provided + const searchWords = incomingSearchText.split(' '); + let atLeastOneSearchWordFound = false; + let allSearchWordsFound = true; + let searchWordFound = false; + searchWords.forEach((searchWord) => { + searchWordFound = searchWordFoundInOneTeam(searchWord, team); + if (searchWordFound) { + atLeastOneSearchWordFound = true; + } + if (!searchWordFound) { + allSearchWordsFound = false; + } + }); + return atLeastOneSearchWordFound && allSearchWordsFound; +}; diff --git a/src/js/models/PersonModel.jsx b/src/js/models/PersonModel.jsx index b3cc55e..a98724c 100644 --- a/src/js/models/PersonModel.jsx +++ b/src/js/models/PersonModel.jsx @@ -1,3 +1,6 @@ +// PersonModel.js +// Functions related to getting data from the apiDataCache, which stores data +// received from our API servers. import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useConnectAppContext } from '../contexts/ConnectAppContext'; import weConnectQueryFn, { METHOD } from '../react-query/WeConnectQuery'; diff --git a/src/js/models/TeamModel.jsx b/src/js/models/TeamModel.jsx index e70c38d..b66562a 100644 --- a/src/js/models/TeamModel.jsx +++ b/src/js/models/TeamModel.jsx @@ -1,3 +1,6 @@ +// TeamModel.js +// Functions related to getting data from the apiDataCache, which stores data +// received from our API servers. import { useMemo } from 'react'; import isEqual from 'lodash-es/isEqual'; import { useMutation, useQueryClient } from '@tanstack/react-query'; diff --git a/src/js/pages/TeamHome.jsx b/src/js/pages/TeamHome.jsx index 1083073..40c23cf 100644 --- a/src/js/pages/TeamHome.jsx +++ b/src/js/pages/TeamHome.jsx @@ -24,7 +24,6 @@ const TeamHome = ({ classes }) => { const params = useParams(); const [team, setTeam] = useState(useGetTeamById(convertToInteger(params.teamId))); - const [teamMemberLists, setTeamMemberLists] = useState([]); const [teamId] = useState(convertToInteger(params.teamId)); // const updateTeam = (tList) => { @@ -45,7 +44,6 @@ const TeamHome = ({ classes }) => { useEffect(() => { // console.log('useFetchData team-list-retrieve in TeamHome useEffect:', teamListRetrieveResults); if (teamListRetrieveResults) { - setTeamMemberLists(teamListRetrieveResults.data); // console.log('In useEffect apiDataCache:', apiDataCache); // const changeResults = captureTeamListRetrieveData(teamListRetrieveResults, apiDataCache, dispatch); @@ -63,9 +61,8 @@ const TeamHome = ({ classes }) => { const addTeamMemberClick = () => { // console.log('TeamHome addTeamMemberClick, teamId:', teamId); setAppContextValue('addPersonDrawerOpen', true); + setAppContextValue('AddPersonDrawerLabel', 'Add Team Member'); setAppContextValue('addPersonDrawerTeam', team); - const teamWithEmbeddedMemberList = (teamMemberLists && teamMemberLists.teamList.filter((list) => list.id === team.id)[0]) || []; - setAppContextValue('addPersonDrawerTeamMemberList', teamWithEmbeddedMemberList && teamWithEmbeddedMemberList.teamMemberList); }; return ( @@ -99,7 +96,10 @@ const TeamHome = ({ classes }) => { showIcons={false} /> {/* PLEASE DO NOT REMOVE PASSED team */} - + )} + {teamList.map((team, index) => { + if (showTeam(team)) { + return ( + + 0)} + showIcons + /> + {showAllTeamMembers && ( + <> + {/* DO NOT REMOVE PASSED IN team */} + + + )} + + ); + } else { + return null; + } + })}
Jump to the "Sign in" /login page (Temporary Link) @@ -130,23 +217,36 @@ const Teams = ({ classes, match }) => { ); }; Teams.propTypes = { - classes: PropTypes.object.isRequired, - match: PropTypes.object, }; -const styles = (theme) => ({ - ballotButtonIconRoot: { - marginRight: 8, - }, - addTeamButtonRoot: { - width: 120, - [theme.breakpoints.down('md')]: { - width: '100%', - }, - }, +const styles = () => ({ }); +const ActionBarItem = styled('div')` + padding-right: 15px; +`; + +const ActionBarSection = styled('div')` + align-items: center; + border-right: 1px solid ${DesignTokenColors.neutralUI200}; + display: flex; + font-size: .8em; + justify-content: flex-start; + padding-left: 15px; +`; + +const ActionBarWrapper = styled('div')` + align-items: center; + display: flex; + justify-content: flex-start; + margin-top: 40px; // Temporary hack +`; + const OneTeamWrapper = styled('div')` `; +const SearchBarWrapper = styled('div')` + margin-right: 10px; +`; + export default withStyles(styles)(Teams); From 84f3c7c3337cacb083c6f53706c4b7f6b14b5466 Mon Sep 17 00:00:00 2001 From: dalemcgrew Date: Sun, 16 Feb 2025 15:29:39 -0800 Subject: [PATCH 2/2] Implemented search on Teams page, which returns people or teams matching the search terms. Updated SearchBase to set autofocus on the search box. Now unsetting addPersonDrawerTeam team when closing AddPersonDrawer. Started implementing new UX team designs. (Iteration) --- src/js/pages/Teams.jsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/js/pages/Teams.jsx b/src/js/pages/Teams.jsx index 48edb4c..e25cac8 100644 --- a/src/js/pages/Teams.jsx +++ b/src/js/pages/Teams.jsx @@ -85,23 +85,21 @@ const Teams = () => { const addTeamMemberClick = () => { setAppContextValue('addPersonDrawerOpen', true); setAppContextValue('AddPersonDrawerLabel', 'Add Person'); - // setAppContextValue('addPersonDrawerTeam', team); }; const updateTeamMembersFoundDictWithOneTeam = (teamId, numberOfTeamMembersFound, numberOfTeamMembersFoundDictLocal) => { - // console.log('updateTeamMembersFoundDictWithOneTeam, teamId:, ', teamId, ', numberOfTeamMembersFound: ', numberOfTeamMembersFound); const numberOfTeamMembersFoundDictRevised = { ...numberOfTeamMembersFoundDictLocal }; if (teamId) { if (numberOfTeamMembersFoundDictLocal[teamId] !== numberOfTeamMembersFound) { - // console.log('original numberOfTeamMembersFoundDictLocal[teamId]: ', numberOfTeamMembersFoundDictLocal[teamId]); numberOfTeamMembersFoundDictRevised[teamId] = numberOfTeamMembersFound; } } return numberOfTeamMembersFoundDictRevised; }; + // Refresh the numberOfTeamMembersFoundDict as a person searches + // key is teamId, value is number of team members found useEffect(() => { - // console.log('useEffect numberOfTeamMembersFoundDict: ', numberOfTeamMembersFoundDict); const numberOfTeamMembersFoundDict = getAppContextValue('numberOfTeamMembersFoundDict'); let numberOfTeamMembersFoundDictRevised = { ...numberOfTeamMembersFoundDict }; let teamId; @@ -124,7 +122,7 @@ const Teams = () => { if (searchText) { const numberOfTeamMembersFoundDict = getAppContextValue('numberOfTeamMembersFoundDict'); const teamMembersFound = numberOfTeamMembersFoundDict[team.teamId] && numberOfTeamMembersFoundDict[team.teamId] > 0; - // console.log('showTeam teamId: ', team.teamId, ', numberOfTeamMembersFoundDict[teamId]: ', numberOfTeamMembersFoundDict[team.teamId]); + // If the team has any members matching searchText, or team itself matches searchText, show it return !!(teamMembersFound) || isSearchTextFoundInTeam(searchText, team); } else { return true; // Show the team if no searchText is provided