diff --git a/index.html b/index.html index 508ebb3d..16820611 100644 --- a/index.html +++ b/index.html @@ -101,19 +101,16 @@ * ... * ] */ - function getPersonList() { - return new Promise((resolve, reject) => { - fetch('https://willowtreeapps.com/api/v1.0/profiles') - .then(response => { - if (response.status !== 200) { - reject(new Error("Error!")); - } + async function getPersonList() { + let response = await fetch('https://willowtreeapps.com/api/v1.0/profiles'); - response.json().then(imageList => { - resolve(imageList); - }); - }); - }); + if(response.status !== 200) { + throw new Error("Error!"); + } + + let data = await response.json(); + + return data; } @@ -125,16 +122,21 @@ function getLastName(person) { - return person.lastName; + return person.lastName.trim(); } const getFirstName = (person) => { - return person.firstName; + return person.firstName.trim(); }; // headshot URLs are scheme relative // // prepend http: to prevent invalid schemes like file:// or uri:// + // instead return null if the person does not have a headshot url defined const getImageUrl = (person) => { + if(person.headshot.url === undefined) { + return null; + } + return `http:${person.headshot.url}`; }; @@ -143,15 +145,15 @@ */ function shuffleList(list) { // Make a copy & don't mutate the passed in list - let result = list.slice(1); + let result = list.slice(); - let tmp, j, i = list.length - 1 + let tmp, j, i = result.length - 1 for (; i > 0; i -= 1) { j = Math.floor(Math.random() * (i + 1)); - tmp = list[i]; - list[i] = list[j]; - list[j] = tmp; + tmp = result[i]; + result[i] = result[j]; + result[j] = tmp; } return result; @@ -159,15 +161,55 @@ /** - * Remove any people that do not have the name we are - * searching for. + * Get all the words from a phrase string. + */ + function getWords(phrase) { + let words = []; + let cleanedPhrase = phrase.replace(/[\s]+/g, ' ').trim(); + + if(phrase.length === 0 || cleanedPhrase === '') { + return words; + } + + words = cleanedPhrase.split(' '); + return words; + } + + /** + * Remove any people that do not have a name that + * contains one or more words from our search string. */ function filterByName(searchForName, personList) { + // Get each word from the search query. + const searchTerms = getWords(searchForName); + + // Filter our list of people... return personList.filter((person) => { - return person.firstName === searchForName || person.lastName === searchForName; + // If there are no search terms, return everyone in our list. + if(searchTerms.length === 0) { + return true; + } + + // Make sure that one or more search term is found inside + return searchTerms.some((searchTerm) => { + // Transform our search term into lowercase. + searchTerm = searchTerm.trim().toLowerCase(); + + // Check if the first name or last name contains our search term. + return getFirstName(person).toLowerCase().indexOf(searchTerm) !== -1 || + getLastName(person).toLowerCase().indexOf(searchTerm) !== -1; + }); }); } + /** + * Toggle the value passed in using the following rules: + * 1. If the current value is null, switch the value to true. + * 2. Otherwise, switch the value to !value. + */ + function toggleBool(currentBool) { + return currentBool === null ? true : !currentBool; + } /** * Takes in a property of an object list, e.g. "name" below @@ -177,29 +219,38 @@ * And returns a function that will sort that list, e.g. * * const sortPeopleByName = sortObjListByProp('name'); - * const sortedPeople = sortPeopleByName(people); + * const sortedPeople = sortPeopleByName(people, true); * * We now have: * * console.log(sortedPeople) * > [{ name: 'Jon' }, { name: 'Kevin' }, { name: 'Sam' }] * + * You may also sort the list backwards, e.g. + * + * const sortPeopleByName = sortObjListByProp('name'); + * const sortedPeopleBackwards = sortPeopleByName(people, false); + * + * We now have: + * + * console.log(sortedPeopleBackwards) + * > [{ name: 'Sam' }, { name: 'Kevin' }, { name: 'Jon' }] */ function sortObjListByProp(prop) { - return function(objList) { + return function(objList, forward) { // Make a copy & don't mutate the passed in list - let result = objList.slice(1); + let result = objList.slice(); result.sort((a, b) => { if (a[prop] < b[prop]) { - return -1; + return forward ? -1 : 1; } if (a[prop] > b[prop]) { - return 1; + return forward ? 1 : -1; } - return 1; + return 0; }); return result; @@ -208,7 +259,7 @@ const sortByFirstName = sortObjListByProp('firstName'); - const sortByLastName = (personList) => sortByFirstName(personList).reverse(); + const sortByLastName = sortObjListByProp('lastName'); /*================================================== @@ -227,11 +278,21 @@ src: props.src }); - const ListRow = (props) => React.DOM.tr({ key: `${props.person.firstName} ${props.person.lastName}` }, [ - React.DOM.td({ key: 'thumb' }, React.createElement(Thumbnail, { src: getImageUrl(props.person) })), - React.DOM.td({ key: 'first' }, null, getFirstName(props.person)), - React.DOM.td({ key: 'last' }, null, getLastName(props.person)), - ]); + const NotPictured = (props) => React.DOM.span({ + className: 'not-pictured', + children: 'Not Pictured' + }); + + const ListRow = (props) => { + const imageUrl = getImageUrl(props.person); + const imageElement = imageUrl === null ? NotPictured : Thumbnail; + + return React.DOM.tr({ key: `${props.person.firstName} ${props.person.lastName}` }, [ + React.DOM.td({ key: 'thumb' }, React.createElement(imageElement, { src: imageUrl })), + React.DOM.td({ key: 'first' }, null, getFirstName(props.person)), + React.DOM.td({ key: 'last' }, null, getLastName(props.person)), + ]); + }; const ListContainer = (props) => React.DOM.table({ className: 'list-container' }, [ React.DOM.thead({ key: 'thead' }, React.DOM.tr({}, [ @@ -247,7 +308,10 @@ getInitialState() { return { personList: [], - visiblePersonList: [] + visiblePersonList: [], + sortFirstForward: null, + sortLastForward: null, + searchQuery: '' }; }, @@ -260,38 +324,51 @@ }, _shuffleList() { + const searchResults = filterByName(this.state.searchQuery, this.state.personList); + this.setState({ - visiblePersonList: shuffleList(this.state.personList) + visiblePersonList: shuffleList(searchResults) }); }, _sortByFirst() { + const searchResults = filterByName(this.state.searchQuery, this.state.personList); + + const newSortFirstForward = toggleBool(this.state.sortFirstForward); + this.setState({ - visiblePersonList: sortByFirstName(this.state.personList) + sortFirstForward: newSortFirstForward, + sortLastForward: null, + visiblePersonList: sortByFirstName(searchResults, newSortFirstForward) }); }, _sortByLast() { + const searchResults = filterByName(this.state.searchQuery, this.state.personList); + + const newSortLastForward = toggleBool(this.state.sortLastForward); + this.setState({ - visiblePersonList: sortByLastName(this.state.personList) + sortFirstForward: null, + sortLastForward: newSortLastForward, + visiblePersonList: sortByLastName(searchResults, newSortLastForward) }); }, _onSearch(e) { this.setState({ + searchQuery: e.target.value, visiblePersonList: filterByName(e.target.value, this.state.personList) }); }, render() { - const { visiblePersonList } = this.state; - return React.DOM.div({ className: 'app-container' }, [ React.createElement(Search, { key: 'search', onChange: this._onSearch }), React.DOM.button({ key: 'shuffle', onClick: this._shuffleList }, null, 'Shuffle'), React.DOM.button({ key: 'sort-first', onClick: this._sortByFirst }, null, 'Sort (First Name)'), React.DOM.button({ key: 'sort-last', onClick: this._sortByLast }, null, 'Sort (Last Name)'), - React.createElement(ListContainer, { key: 'list', personList: visiblePersonList }) + React.createElement(ListContainer, { key: 'list', personList: this.state.visiblePersonList }) ]); } });