Created
April 17, 2018 20:09
-
-
Save colonelrascals/f562fb98781b37b4c2ef45a074b7d0b2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Search extends React.Component { | |
| constructor(props) { | |
| super(props); | |
| this.state = { | |
| locationValue: [], | |
| resourceValue: [], | |
| keywords: null, | |
| results: { | |
| loading: false, | |
| data: { resources: [], num_hits: 0 }, | |
| error: null | |
| }, | |
| firstSearch: true, | |
| currentPage: 1, | |
| totalPages: 1 | |
| }; | |
| } | |
| componentWillMount() { | |
| // if a query in the url bar, go ahead and populate state. | |
| const query = queryString.parse(window.location.hash.replace('#', '')); | |
| if (Object.keys(query).length) { | |
| const keyMap = { | |
| location: 'locationValue', | |
| resource: 'resourceValue' | |
| }; | |
| const newState = {}; | |
| Object.keys(keyMap).forEach(key => { | |
| if (query[key] && typeof query[key] === 'string') { | |
| newState[keyMap[key]] = [{ value: query[key], label: query[key] }]; | |
| } else if (query[key] && typeof query[key] === 'object') { | |
| newState[keyMap[key]] = query[key].map(v => ({ value: v, label: v })); | |
| } | |
| }); | |
| this.fireSearch(newState); | |
| } | |
| } | |
| fireSearch(newState) { | |
| this.setState( | |
| { | |
| ...newState, | |
| results: { ...this.state.results, loading: true } | |
| }, | |
| this.search | |
| ); | |
| } | |
| handleClick(keyword) { | |
| this.setState( | |
| { | |
| resourceValue: [{ className: "Select-create-option-placeholder", label: keyword, value: keyword }], | |
| results: { ...this.state.results, loading: true } | |
| }, | |
| this.search | |
| ); | |
| } | |
| onChange(value, stateKey) { | |
| this.setState( | |
| { | |
| [stateKey]: value, | |
| results: { ...this.state.results, loading: true } | |
| }, | |
| this.search | |
| ); | |
| } | |
| handlePageChange(value) { | |
| this.setState({ currentPage: value }, () => this.fireSearch()); | |
| } | |
| search() { | |
| const resource_qlst = this.state.resourceValue.map(o => o.label); | |
| const location_qlst = this.state.locationValue.map(o => o.label); | |
| const qs = queryString.stringify({ | |
| location: location_qlst, | |
| resource: resource_qlst, | |
| page: this.state.currentPage | |
| }); | |
| window.history.replaceState(null, null, `${window.location.pathname}#${qs}`); | |
| request | |
| .get('/api/resources/search') | |
| .query({ | |
| resource_meta: resource_qlst, | |
| location: location_qlst, | |
| page: this.state.currentPage | |
| }) | |
| .then((success, err) => { | |
| this.setState( | |
| { | |
| results: { | |
| loading: false, | |
| error: err, | |
| data: { | |
| resources: success.body.resources, | |
| num_hits: success.body.num_results | |
| } | |
| }, | |
| keywords: success.body.keywords, | |
| firstSearch: false, | |
| totalPages: success.body.num_pages | |
| }, | |
| () => { | |
| // init new tooltips | |
| $('[data-toggle="tooltip"]').tooltip(); | |
| } | |
| ) | |
| }); | |
| // record search event | |
| ReactGA.event({ | |
| category: 'Search', | |
| action: 'Search Executed', | |
| label: `Resources: ${resource_qlst.join(', ')}; Locations: ${location_qlst.join(', ')}` | |
| }); | |
| } | |
| getOptions(input, kw, callback) { | |
| request | |
| .get(`/api/resources/suggest/${kw}`) | |
| .query({ [kw]: input }) | |
| .then((success, err) => { | |
| const options = success.body.suggestions.map(o => ({ | |
| value: o, | |
| label: o | |
| })); | |
| callback(err, { options }); | |
| }); | |
| } | |
| render() { | |
| return ( | |
| <div> | |
| <div style={{ minHeight: 80 }}> | |
| <AutoAffix viewportOffsetTop={80} container={this}> | |
| <div className="search-tools"> | |
| <div className="row"> | |
| <div className="col-xs-12 col-sm-5"> | |
| <div className="form-group search-form-input"> | |
| <AsyncCreatable | |
| name="resource-search" | |
| value={this.state.resourceValue} | |
| multi | |
| cache={false} | |
| filterOption={() => true} | |
| autoload={false} | |
| loadOptions={(input, cb) => this.getOptions(input, 'resource', cb)} | |
| onChange={vals => this.onChange(vals, 'resourceValue')} | |
| placeholder="Resource or Services (e.g. METROLift, transportation)" | |
| arrowRenderer={() => null} | |
| /> | |
| </div> | |
| </div> | |
| <div className="col-xs-12 col-sm-5"> | |
| <div className="form-group search-form-input"> | |
| <AsyncCreatable | |
| name="location-search" | |
| value={this.state.locationValue} | |
| multi | |
| cache={false} | |
| filterOption={() => true} | |
| autoload={false} | |
| loadOptions={(input, cb) => this.getOptions(input, 'location', cb)} | |
| onChange={vals => this.onChange(vals, 'locationValue')} | |
| placeholder="Zip Code (e.g. 77036)" | |
| arrowRenderer={() => null} | |
| /> | |
| </div> | |
| </div> | |
| <div className="col-xs-12 col-sm-2"> | |
| <button | |
| className="btn btn-fill" | |
| onClick={() => this.fireSearch(this.state)} | |
| disabled={this.state.results.loading}> | |
| {this.state.results.loading ? ( | |
| <i className="fa fa-lg fa-spinner fa-spin" /> | |
| ) : ( | |
| <i className="fa fa-search" aria-hidden="true" /> | |
| )} | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </AutoAffix> | |
| </div> | |
| {this.state.keywords ? ( | |
| <div className="row"> | |
| <div className="col-xs-12 col-sm-5"> | |
| <p>suggested keywords: </p> | |
| {this.state.keywords.map((keyword) => <a onClick={() => { this.handleClick(keyword) }} style={styles}>{keyword} </a> | |
| )} | |
| </div> | |
| </div>) : | |
| null} | |
| <div className="row"> | |
| <div className="col-xs-12 col-sm-6"> | |
| <SearchResults | |
| {...this.state.results} | |
| keywords={this.state.keywords} | |
| count={this.props.count} | |
| csrf={this.props.csrfToken} | |
| firstSearch={this.state.firstSearch} | |
| currentUser={this.props.currentUser} | |
| /> | |
| {this.state.totalPages > 1 && ( | |
| <Pagination | |
| prev | |
| next | |
| first | |
| last | |
| ellipsis | |
| boundaryLinks | |
| bsSize="medium" | |
| maxButtons={10} | |
| items={this.state.totalPages} | |
| activePage={this.state.currentPage} | |
| onSelect={value => this.handlePageChange(value)} | |
| /> | |
| )} | |
| </div> | |
| <div className="col-xs-12 col-sm-6"> | |
| {!this.state.results.data.resources.length ? null : ( | |
| <Map | |
| data={this.state.results.data.resources.map(d => ({ | |
| id: d.id, | |
| name: d.name, | |
| description: d.description, | |
| latitude: d.latitude || null, | |
| longitude: d.longitude || null | |
| }))} | |
| /> | |
| )} | |
| {!this.state.results.data.resources.length ? null : ( | |
| <p style={{ top: 667, position: 'fixed' }}> | |
| Not finding a resource? Please <a href="/resource-submission/">submit it</a> to be | |
| added to the Welnity database. | |
| </p> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| } | |
| ReactDOM.render( | |
| React.createElement( | |
| Search, | |
| Object.assign(window.props, { | |
| csrfToken: window.csrfToken, | |
| httpReferrer: window.httpReferrer, | |
| currentUser: window.currentUser | |
| }) | |
| ), | |
| window.react_mount | |
| ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment