diff --git a/frontend/src/components/App.js b/frontend/src/components/App.js index 3809d3458e595a74a24a41275efd98b6aa02a214..d08ab9a0a692d648fbe1f435aeda6dae2d6fe59b 100644 --- a/frontend/src/components/App.js +++ b/frontend/src/components/App.js @@ -34,6 +34,7 @@ import { import PageMap from './pages/PageMap'; import PageHome from './pages/PageHome'; import PageFilter from './pages/PageFilter'; +import PageSearch from './pages/PageSearch'; const drawerWidth = 240; @@ -158,6 +159,7 @@ class App extends MyComponent {
+ diff --git a/frontend/src/components/MyComponent.js b/frontend/src/components/MyComponent.js index c17ef6e6b12e89413ad9fbf00669651338020131..065feb5dc24c5c0873e1bfe6cf2b69619ac38023 100644 --- a/frontend/src/components/MyComponent.js +++ b/frontend/src/components/MyComponent.js @@ -18,6 +18,15 @@ class MyComponent extends Component { return out; } + joinCampus(campus) { + const { universities, countries, cities } = this.getAllFetchedData(); + let res = Object.assign({}, campus); //copy for safety + res.university = universities[campus.university]; + res.city = cities[campus.city] + res.country = countries[res.city.country] + return res; + } + checkProps(val) { for (let el in this.props) { let prop = this.props[el]; diff --git a/frontend/src/components/filter/DownshiftMultiple.js b/frontend/src/components/filter/DownshiftMultiple.js index 493ab28a04c6f5146aad1b719381e227eb8214ed..26f3571808614f6cf1d8518febcf495eab84a41a 100644 --- a/frontend/src/components/filter/DownshiftMultiple.js +++ b/frontend/src/components/filter/DownshiftMultiple.js @@ -2,7 +2,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import deburr from 'lodash/deburr'; import keycode from 'keycode'; import Downshift from 'downshift'; import { withStyles } from '@material-ui/core/styles'; diff --git a/frontend/src/components/filter/Filter.js b/frontend/src/components/filter/Filter.js index 97dff559a85bcb1f2579f50d81c236e68a296757..7bc2497f6086056c85b4f2a7314b454b37639212 100644 --- a/frontend/src/components/filter/Filter.js +++ b/frontend/src/components/filter/Filter.js @@ -15,15 +15,6 @@ import { class Filter extends MyComponent { - joinCampus(campus) { - const { universities, countries, cities } = this.getAllFetchedData(); - let res = Object.assign({}, campus); //copy for safety - res.university = universities[campus.university]; - res.city = cities[campus.city] - res.country = countries[res.city.country] - return res; - } - saveContriesFilterConfig(state) { this.props.saveConfig({ contriesFilter: state }) } @@ -73,12 +64,12 @@ class Filter extends MyComponent { (c) => { return { id: c.iso_alpha2_code, label: c.name } }) return ( - this.updateSelectedUniversities(selection)} - onComponentUnmount={(state) => this.saveContriesFilterConfig(state)} - config={this.props.contriesFilterConfig} - /> + this.updateSelectedUniversities(selection)} + onComponentUnmount={(state) => this.saveContriesFilterConfig(state)} + config={this.props.contriesFilterConfig} + /> ); } } diff --git a/frontend/src/components/pages/PageFilter.js b/frontend/src/components/pages/PageFilter.js index 25d8115a4c5dab3580771a4e24f6e1c6c79466df..db0f1f3acab603ba7acf5a316b3e06e3e5d83d29 100644 --- a/frontend/src/components/pages/PageFilter.js +++ b/frontend/src/components/pages/PageFilter.js @@ -12,7 +12,7 @@ const styles = theme => ({ } }); -class PageMap extends React.Component { +class PageFilter extends React.Component { render() { const { classes } = this.props; return ( @@ -33,8 +33,8 @@ class PageMap extends React.Component { } } -PageMap.propTypes = { +PageFilter.propTypes = { classes: PropTypes.object.isRequired, }; -export default withStyles(styles)(PageMap); +export default withStyles(styles)(PageFilter); diff --git a/frontend/src/components/pages/PageSearch.js b/frontend/src/components/pages/PageSearch.js new file mode 100644 index 0000000000000000000000000000000000000000..7839708cb2d4d9e09efa13a57883a8de5275361f --- /dev/null +++ b/frontend/src/components/pages/PageSearch.js @@ -0,0 +1,40 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import Search from '../search/Search'; +import Paper from '@material-ui/core/Paper'; + +const styles = theme => ({ + myPaper: { + padding: 16 + } +}); + +class PageSearch extends React.Component { + render() { + const { classes } = this.props; + return ( + + + + + Recherche d'une université + + + {/* + + */} + + + + ); + } +} + +PageSearch.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(PageSearch); diff --git a/frontend/src/components/search/Search.js b/frontend/src/components/search/Search.js new file mode 100644 index 0000000000000000000000000000000000000000..e66d76efebbcdbbcb67cd0a59ca2c4b10670118a --- /dev/null +++ b/frontend/src/components/search/Search.js @@ -0,0 +1,139 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Paper from '@material-ui/core/Paper'; + +import MyComponent from '../MyComponent' +import { connect } from "react-redux"; +import fuzzysort from 'fuzzysort'; +import UnivList from './UnivList'; +import _ from 'underscore'; +// import { saveSelectedUniversities, saveSearchConfig } from '../../actions/filter'; +import { withStyles } from '@material-ui/core/styles'; + +import { + universitiesFetchData, + mainCampusesFetchData, + citiesFetchData, + countriesFetchData +} from '../../generated/actions'; + + + +const styles = theme => ({ + inputCentered: { + textAlign: 'center' + }, +}); + +class Search extends MyComponent { + state = { + suggestions: [] + } + + getSuggestions(value) { + console.log(value) + const { universities } = this.getAllFetchedData(); + const filter = fuzzysort.go(value, _.map(universities, (univ) => univ), { keys: ['name', 'acronym'] }); + let out; + if (filter.length > 0) { + out = _.map(filter, (item) => item.obj) + } else { + out = universities + } + this.setState({ suggestions: out }) + } + + + // saveContriesSearchConfig(state) { + // this.props.saveConfig({ contriesSearch: state }) + // } + + + // getCountriesWhereThereAreUniversities() { + // const { mainCampuses } = this.getAllFetchedData(); + + // let res = []; + // _.each(mainCampuses, (campus) => { + // const campusFull = this.joinCampus(campus) + // res.push(campusFull.country) + // }); + + // return _.uniq(res, false, (c) => { return c.iso_alpha2_code }); + // } + + // updateSelectedUniversities(selection) { + // const { mainCampuses } = this.getAllFetchedData(); + // const listOfCountries = _.map(selection, (s) => s.id); + + // let selected_universities = [] + // _.each(mainCampuses, (campus) => { + // const campusFull = this.joinCampus(campus) + // if (_.indexOf(listOfCountries, campusFull.country.iso_alpha2_code) > -1) { + // selected_universities.push(campusFull.university.id); + // } + // }) + // this.props.saveSelection(selected_universities); + // } + + myRender() { + const { classes } = this.props; + // const options = _.map(this.getCountriesWhereThereAreUniversities(), + // (c) => { return { id: c.iso_alpha2_code, label: c.name } }) + + return ( + // this.updateSelectedUniversities(selection)} + // onComponentUnmount={(state) => this.saveContriesSearchConfig(state)} + // config={this.props.contriesSearchConfig} + // /> +
+ this.getSuggestions(e.target.value)} + /> + {/* {JSON.stringify(this.state.suggestions)} */} + { + + } +
+ ); + } +} + + + +const mapStateToProps = (state) => { + return { + universities: state.universities, + mainCampuses: state.mainCampuses, + cities: state.cities, + countries: state.countries, + // contriesSearchConfig: state.app.filter.contriesSearch + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + fetchData: { + universities: () => dispatch(universitiesFetchData()), + mainCampuses: () => dispatch(mainCampusesFetchData()), + cities: () => dispatch(citiesFetchData()), + countries: () => dispatch(countriesFetchData()) + }, + // saveSelection: (selectedUniversities) => dispatch(saveSelectedUniversities(selectedUniversities)), + // saveConfig: (config) => dispatch(saveSearchConfig(config)) + }; +}; + + +export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Search)); diff --git a/frontend/src/components/search/UnivList.js b/frontend/src/components/search/UnivList.js new file mode 100644 index 0000000000000000000000000000000000000000..15f960294d55686788a043d7843bd99761c5df24 --- /dev/null +++ b/frontend/src/components/search/UnivList.js @@ -0,0 +1,137 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import MobileStepper from '@material-ui/core/MobileStepper'; +import Paper from '@material-ui/core/Paper'; +import Typography from '@material-ui/core/Typography'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import _ from 'underscore'; +import ListItemText from '@material-ui/core/ListItemText'; +import Divider from '@material-ui/core/Divider'; +import Button from '@material-ui/core/Button'; +import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'; +import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'; +import SwipeableViews from 'react-swipeable-views'; + + +const styles = theme => ({ + root: { + maxWidth: 650, + marginLeft: "auto", + marginRight: "auto", + flexGrow: 1, + }, + header: { + display: 'flex', + alignItems: 'center', + height: 50, + paddingLeft: theme.spacing.unit * 4, + marginBottom: 20, + backgroundColor: theme.palette.background.default, + }, +}); + +class UnivList extends React.Component { + state = { + activeStep: 0, + }; + + componentDidUpdate(prevProps, prevState, snapshot) { + if (prevProps.universitiesToList != this.props.universitiesToList) { + this.handleStepChange(0); + } + } + + + handleNext = () => { + console.log("next") + this.setState(prevState => ({ + activeStep: prevState.activeStep + 1, + })); + }; + + handleBack = () => { + console.log("back") + this.setState(prevState => ({ + activeStep: prevState.activeStep - 1, + })); + }; + + handleStepChange = activeStep => { + console.log("step change") + this.setState({ activeStep }); + }; + + render() { + const { classes, theme, itemsPerPage, universitiesToList } = this.props; + const { activeStep } = this.state; + + const numberOfItems = universitiesToList.length; + // Prevent bug + if (!numberOfItems) { + return
; + } + + const maxSteps = Math.ceil(numberOfItems / itemsPerPage); + console.log("maxSteps", maxSteps, "numberOfItems", numberOfItems, "itemsPerPage"); + return ( +
+ + + {_.map(_.range(maxSteps), page => ( +
+ + + {_.map(_.range((page) * itemsPerPage, Math.min((page + 1) * itemsPerPage, numberOfItems)), + univ_ind => ( + + + + )) + } + +
+ ))} +
+ + + = maxSteps - 1}> + Suivant + {theme.direction === 'rtl' ? : } + + } + backButton={ + + } + /> +
+ ); + } +} + +UnivList.propTypes = { + classes: PropTypes.object.isRequired, + theme: PropTypes.object.isRequired, +}; + +UnivList.defaultProps = { + itemsPerPage: 10 +}; + +export default withStyles(styles, { withTheme: true })(UnivList); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 141d0e231091dacdf231377ec2a6aa72f59b136d..6c751c51484cd444e3be7aae93902667c6030178 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5513,6 +5513,86 @@ "react-transition-group": "^2.2.1" } }, + "react-swipeable-views": { + "version": "0.12.17", + "resolved": "https://registry.npmjs.org/react-swipeable-views/-/react-swipeable-views-0.12.17.tgz", + "integrity": "sha512-+egPdA4vqe1h4a9OIFWHKZER9aMPVrggiZ7PtXRyovsuLCDsoiIgGjNujuEOKVEskNjN1LHtQjQsPWinT7UD6A==", + "requires": { + "@babel/runtime": "7.0.0", + "dom-helpers": "^3.2.1", + "prop-types": "^15.5.4", + "react-swipeable-views-core": "^0.12.17", + "react-swipeable-views-utils": "^0.12.17", + "warning": "^4.0.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", + "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", + "requires": { + "regenerator-runtime": "^0.12.0" + } + }, + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" + } + } + }, + "react-swipeable-views-core": { + "version": "0.12.17", + "resolved": "https://registry.npmjs.org/react-swipeable-views-core/-/react-swipeable-views-core-0.12.17.tgz", + "integrity": "sha512-KfQ+BPfLVBe7kxb+0zbVJp3eGQfZlt1gn5J+GYAgnYoZ29GrqkTfiQFKmrG4tmVnhxvRiXFA7Q0q9EBMYTc/FA==", + "requires": { + "@babel/runtime": "7.0.0", + "warning": "^4.0.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", + "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", + "requires": { + "regenerator-runtime": "^0.12.0" + } + }, + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" + } + } + }, + "react-swipeable-views-utils": { + "version": "0.12.17", + "resolved": "https://registry.npmjs.org/react-swipeable-views-utils/-/react-swipeable-views-utils-0.12.17.tgz", + "integrity": "sha512-S0ERcHcTdOPz7LC/z+fDpJW1Z03b71V+MpG0RcY2mtEfsK7BJHqyIuht4KIJKApVrngxV9xWLBOYqeP7R86gWA==", + "requires": { + "@babel/runtime": "7.0.0", + "fbjs": "^0.8.4", + "keycode": "^2.1.7", + "prop-types": "^15.6.0", + "react-event-listener": "^0.6.0", + "react-swipeable-views-core": "^0.12.17" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", + "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", + "requires": { + "regenerator-runtime": "^0.12.0" + } + }, + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" + } + } + }, "react-transition-group": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz", diff --git a/package.json b/package.json index 23ca29a45c30f96626984c85ec8d95f3e64bccd9..8d928477f8d733d79dc82a951734c89c998810f3 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "leaflet.markercluster": "^1.3.0", "react-redux": "^5.0.7", "react-router-dom": "^4.3.1", + "react-swipeable-views": "^0.12.17", "redux": "^4.0.0", "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0",