From 89119e903e9748ca0d5c70c2703d932104007591 Mon Sep 17 00:00:00 2001 From: shirlyfang Date: Mon, 11 Nov 2019 18:04:53 -0800 Subject: [PATCH 1/2] Making better booking flow UI --- App.js | 6 +- app/components/TimeRangeCard.js | 160 +++++++++++++++++++++ app/screens/StudyRoom/StudyRoomsPreview.js | 63 ++++++++ 3 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 app/components/TimeRangeCard.js create mode 100644 app/screens/StudyRoom/StudyRoomsPreview.js diff --git a/App.js b/App.js index f73fe49f..fd74d305 100644 --- a/App.js +++ b/App.js @@ -9,7 +9,7 @@ import LocationsMap from './app/screens/Locations/LocationsMap'; import configureStore from './store'; import LocationsList from './app/screens/Locations/LocationsList'; import LocationsContainer from './app/screens/Locations/LocationsContainer'; -import StudyRoomsContainer from './app/screens/StudyRoom/StudyRoomsContainer'; +import StudyRoomsPreview from './app/screens/StudyRoom/StudyRoomsPreview'; import StudyRoomReserve from './app/screens/StudyRoom/StudyRoomReserve'; import LibraryRoomReserve from './app/screens/StudyRoom/LibraryRoomReserve'; import FeedbackContainer from './app/screens/Feedback/FeedbackContainer'; @@ -23,13 +23,13 @@ const feedbackTabSelected = require('./assets/feedbackTabSelected.png'); const feedbackTab = require('./assets/feedbackTab.png'); const StudyRoomStack = StackNavigator({ - StudyRoomsContainer: { screen: StudyRoomsContainer }, + StudyRoomsPreview: { screen: StudyRoomsPreview }, StudyRoomReserve: { screen: StudyRoomReserve }, LibraryRoomReserve: { screen: LibraryRoomReserve }, BookingWebView: { screen: BookingWebView }, }, { - initialRouteName: 'StudyRoomsContainer', + initialRouteName: 'StudyRoomsPreview', }); const LocationsStack = StackNavigator({ diff --git a/app/components/TimeRangeCard.js b/app/components/TimeRangeCard.js new file mode 100644 index 00000000..18db35c3 --- /dev/null +++ b/app/components/TimeRangeCard.js @@ -0,0 +1,160 @@ +import React, { Component } from 'react'; +import { + Text, View, TouchableOpacity, StyleSheet +} from 'react-native'; + +const intervals = { + '12:00AM-1:00AM': ['12:00', '12:30'], + '1:00AM-2:00AM': ['1:00', '1:30'], + '2:00AM-3:00AM': ['2:00', '2:30'], + '3:00AM-4:00AM': ['3:00', '3:30'], + '4:00AM-5:00AM': ['4:00', '4:30'], + '5:00AM-6:00AM': ['5:00', '5:30'], + '6:00AM-7:00AM': ['6:00', '6:30'], + '7:00AM-8:00AM': ['7:00', '7:30'], + '8:00AM-9:00AM': ['8:00', '8:30'], + '9:00AM-10:00AM': ['9:00', '9:30'], + '10:00AM-11:00AM': ['10:00', '10:30'], + '11:00AM-12:00PM': ['11:00', '11:30'], + '12:00PM-1:00PM': ['12:00', '12:30'], + '1:00PM-2:00PM': ['1:00', '1:30'], + '2:00PM-3:00PM': ['2:00', '2:30'], + '3:00PM-4:00PM': ['3:00', '3:30'], + '4:00PM-5:00PM': ['4:00', '4:30'], + '5:00PM-6:00PM': ['5:00', '5:30'], + '6:00PM-7:00PM': ['6:00', '6:30'], + '7:00PM-8:00PM': ['7:00', '7:30'], + '8:00PM-9:00PM': ['8:00', '8:30'], + '9:00PM-10:00PM': ['9:00', '9:30'], + '10:00PM-11:00PM': ['10:00', '10:30'], + '11:00PM-12:00AM': ['11:00', '11:30'], +}; + +export default class TimeRangeCard extends Component { + constructor() { + super(); + this.state = { + collapsed: true + }; + } + + handleExpandPress = () => { + const { collapsed } = this.state; + if (collapsed) { + this.setState({ collapsed: false }); + } else { + this.setState({ collapsed: true }); + } + } + + handleSelectRoom = (range) => { + this.setState(prevState => ({ + collapsed: !prevState.collapsed + })); + } + + showExpandedCell = (title) => { + const { collapsed } = this.state; + if (!collapsed) { + return ( + + + + {' '} + {intervals[title][0]} + + + + + {' '} + {intervals[title][1]} + + + + + ); + } + return null; + } + + render() { + const { title } = this.props; + const { collapsed } = this.state; + return ( + this.handleSelectRoom(title)} + > + + + {title} + + 25 Rooms Available + {this.showExpandedCell(title)} + + + ); + } +} + +const text = { + fontFamily: 'System', + fontSize: 16, + fontWeight: '300', + fontStyle: 'normal', + letterSpacing: 1.92, + color: 'black', + paddingBottom: 3, +}; + +const cell = { + flexDirection: 'column', + justifyContent: 'space-around', + alignItems: 'center', + height: 60, + width: '95%', + padding: 10, + marginTop: 4, + marginBottom: 4, + borderRadius: 5, + alignSelf: 'center', + backgroundColor: '#FFF', + elevation: 2, + shadowColor: 'rgba(0, 0, 0, 0.5)', + shadowOffset: { + width: 0.5, + height: 0.5 + }, + shadowRadius: 1, + shadowOpacity: 0.8, +}; + +const styles = StyleSheet.create({ + text, + cell, + expandedCell: { + ...cell, + height: 100, + }, + + smallText: { + ...text, + fontSize: 12, + + }, + + row: { + flexDirection: 'row', + }, + + button: { + borderRadius: 7, + justifyContent: 'center', + alignItems: 'center', + borderColor: 'black', + borderWidth: 1, + minWidth: 60, + marginLeft: 5, + marginRight: 5, + } + +}); diff --git a/app/screens/StudyRoom/StudyRoomsPreview.js b/app/screens/StudyRoom/StudyRoomsPreview.js new file mode 100644 index 00000000..14a506a3 --- /dev/null +++ b/app/screens/StudyRoom/StudyRoomsPreview.js @@ -0,0 +1,63 @@ +import React, { Component } from 'react'; +import { + Text, SafeAreaView, TouchableOpacity, StyleSheet, FlatList +} from 'react-native'; +import StudyRoomsContainer from './StudyRoomsContainer'; +import TimeRangeCard from '../../components/TimeRangeCard'; + + +const timeRanges = { + '12:00AM-1:00AM': [0, 1], + '1:00AM-2:00AM': [1, 2], + '2:00AM-3:00AM': [2, 3], + '3:00AM-4:00AM': [3, 4], + '4:00AM-5:00AM': [4, 5], + '5:00AM-6:00AM': [5, 6], + '6:00AM-7:00AM': [6, 7], + '7:00AM-8:00AM': [7, 8], + '8:00AM-9:00AM': [8, 9], + '9:00AM-10:00AM': [9, 10], + '10:00AM-11:00AM': [10, 11], + '11:00AM-12:00PM': [11, 12], + '12:00PM-1:00PM': [12, 13], + '1:00PM-2:00PM': [13, 14], + '2:00PM-3:00PM': [14, 15], + '3:00PM-4:00PM': [15, 16], + '4:00PM-5:00PM': [16, 17], + '5:00PM-6:00PM': [17, 18], + '6:00PM-7:00PM': [18, 19], + '7:00PM-8:00PM': [19, 20], + '8:00PM-9:00PM': [20, 21], + '9:00PM-10:00PM': [21, 22], + '10:00PM-11:00PM': [22, 23], + '11:00PM-12:00AM': [23, 0], +}; + +export default class StudyRoomsPreview extends Component { + static navigationOptions = { + header: () => { } + } + + renderRow = item => + + render() { + const titles = Object.keys(timeRanges); + return ( + + this.renderRow(item)} + keyExtractor={(item, index) => index.toString()} + /> + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: 'white' + }, +}); From 08943b909ee25ed872e2d66f12ecb168bba7b237 Mon Sep 17 00:00:00 2001 From: shirlyfang Date: Wed, 27 Nov 2019 02:36:12 -0800 Subject: [PATCH 2/2] Added more func to better booking --- app/components/TimeRangeCard.js | 90 ++++---- app/screens/StudyRoom/StudyRoom.js | 24 +-- app/screens/StudyRoom/StudyRoomsPreview.js | 239 ++++++++++++++++++--- 3 files changed, 268 insertions(+), 85 deletions(-) diff --git a/app/components/TimeRangeCard.js b/app/components/TimeRangeCard.js index 18db35c3..87b24424 100644 --- a/app/components/TimeRangeCard.js +++ b/app/components/TimeRangeCard.js @@ -4,37 +4,37 @@ import { } from 'react-native'; const intervals = { - '12:00AM-1:00AM': ['12:00', '12:30'], - '1:00AM-2:00AM': ['1:00', '1:30'], - '2:00AM-3:00AM': ['2:00', '2:30'], - '3:00AM-4:00AM': ['3:00', '3:30'], - '4:00AM-5:00AM': ['4:00', '4:30'], - '5:00AM-6:00AM': ['5:00', '5:30'], - '6:00AM-7:00AM': ['6:00', '6:30'], - '7:00AM-8:00AM': ['7:00', '7:30'], - '8:00AM-9:00AM': ['8:00', '8:30'], - '9:00AM-10:00AM': ['9:00', '9:30'], - '10:00AM-11:00AM': ['10:00', '10:30'], - '11:00AM-12:00PM': ['11:00', '11:30'], - '12:00PM-1:00PM': ['12:00', '12:30'], - '1:00PM-2:00PM': ['1:00', '1:30'], - '2:00PM-3:00PM': ['2:00', '2:30'], - '3:00PM-4:00PM': ['3:00', '3:30'], - '4:00PM-5:00PM': ['4:00', '4:30'], - '5:00PM-6:00PM': ['5:00', '5:30'], - '6:00PM-7:00PM': ['6:00', '6:30'], - '7:00PM-8:00PM': ['7:00', '7:30'], - '8:00PM-9:00PM': ['8:00', '8:30'], - '9:00PM-10:00PM': ['9:00', '9:30'], - '10:00PM-11:00PM': ['10:00', '10:30'], - '11:00PM-12:00AM': ['11:00', '11:30'], + '12:00-1:00am': ['12:00', '12:30'], + '1:00-2:00am': ['1:00', '1:30'], + '2:00-3:00am': ['2:00', '2:30'], + '3:00-4:00am': ['3:00', '3:30'], + '4:00-5:00am': ['4:00', '4:30'], + '5:00-6:00am': ['5:00', '5:30'], + '6:00-7:00am': ['6:00', '6:30'], + '7:00-8:00am': ['7:00', '7:30'], + '8:00-9:00am': ['8:00', '8:30'], + '9:00-10:00am': ['9:00', '9:30'], + '10:00-11:00am': ['10:00', '10:30'], + '11:00-12:00pm': ['11:00', '11:30'], + '12:00-1:00pm': ['12:00', '12:30'], + '1:00-2:00pm': ['1:00', '1:30'], + '2:00-3:00pm': ['2:00', '2:30'], + '3:00-4:00pm': ['3:00', '3:30'], + '4:00-5:00pm': ['4:00', '4:30'], + '5:00-6:00pm': ['5:00', '5:30'], + '6:00-7:00pm': ['6:00', '6:30'], + '7:00-8:00pm': ['7:00', '7:30'], + '8:00-9:00pm': ['8:00', '8:30'], + '9:00-10:00pm': ['9:00', '9:30'], + '10:00-11:00pm': ['10:00', '10:30'], + '11:00-12:00am': ['11:00', '11:30'], }; export default class TimeRangeCard extends Component { - constructor() { - super(); + constructor(props) { + super(props); this.state = { - collapsed: true + collapsed: true, }; } @@ -58,13 +58,13 @@ export default class TimeRangeCard extends Component { if (!collapsed) { return ( - + 0 ? styles.button : styles.buttonDisabled}> {' '} {intervals[title][0]} - + 0 ? styles.button : styles.buttonDisabled}> {' '} {intervals[title][1]} @@ -78,7 +78,7 @@ export default class TimeRangeCard extends Component { } render() { - const { title } = this.props; + const { title, available } = this.props; const { collapsed } = this.state; return ( {title} - 25 Rooms Available + + {' '} + {`${available.length} Available`} + {' '} + {this.showExpandedCell(title)} @@ -128,9 +132,21 @@ const cell = { shadowOpacity: 0.8, }; +const button = { + borderRadius: 7, + justifyContent: 'center', + alignItems: 'center', + borderColor: 'black', + borderWidth: 1, + minWidth: 60, + marginLeft: 5, + marginRight: 5, +}; + const styles = StyleSheet.create({ text, cell, + button, expandedCell: { ...cell, height: 100, @@ -146,15 +162,9 @@ const styles = StyleSheet.create({ flexDirection: 'row', }, - button: { - borderRadius: 7, - justifyContent: 'center', - alignItems: 'center', - borderColor: 'black', - borderWidth: 1, - minWidth: 60, - marginLeft: 5, - marginRight: 5, + buttonDisabled: { + ...button, + opacity: 0.3 } }); diff --git a/app/screens/StudyRoom/StudyRoom.js b/app/screens/StudyRoom/StudyRoom.js index 8bf94492..d0697163 100644 --- a/app/screens/StudyRoom/StudyRoom.js +++ b/app/screens/StudyRoom/StudyRoom.js @@ -58,11 +58,11 @@ export default class StudyRoomList extends Component { style={styles.list} /> ) : ( - - No rooms available - - - ); + + No rooms available + + + ); break; case 'Libraries': listData = librariesDataFound.length > 0 ? ( @@ -74,15 +74,15 @@ export default class StudyRoomList extends Component { style={styles.list} /> ) : ( - - No rooms available - - - ); + + No rooms available + + + ); break; case 'Classrooms': // TODO - // listData = + // listData = // break; default: listData = ( @@ -106,7 +106,7 @@ export default class StudyRoomList extends Component { {loading ? : null} {listData} {visible ? ( - + ) : null} diff --git a/app/screens/StudyRoom/StudyRoomsPreview.js b/app/screens/StudyRoom/StudyRoomsPreview.js index 14a506a3..39eda016 100644 --- a/app/screens/StudyRoom/StudyRoomsPreview.js +++ b/app/screens/StudyRoom/StudyRoomsPreview.js @@ -1,55 +1,197 @@ import React, { Component } from 'react'; import { - Text, SafeAreaView, TouchableOpacity, StyleSheet, FlatList + Text, SafeAreaView, TouchableOpacity, StyleSheet, FlatList, Platform } from 'react-native'; +import { connect } from 'react-redux'; import StudyRoomsContainer from './StudyRoomsContainer'; import TimeRangeCard from '../../components/TimeRangeCard'; +import { + changeTime, changeDate, changeLocation, loadHillData, loadLibraryData, +} from '../../Actions/actions'; const timeRanges = { - '12:00AM-1:00AM': [0, 1], - '1:00AM-2:00AM': [1, 2], - '2:00AM-3:00AM': [2, 3], - '3:00AM-4:00AM': [3, 4], - '4:00AM-5:00AM': [4, 5], - '5:00AM-6:00AM': [5, 6], - '6:00AM-7:00AM': [6, 7], - '7:00AM-8:00AM': [7, 8], - '8:00AM-9:00AM': [8, 9], - '9:00AM-10:00AM': [9, 10], - '10:00AM-11:00AM': [10, 11], - '11:00AM-12:00PM': [11, 12], - '12:00PM-1:00PM': [12, 13], - '1:00PM-2:00PM': [13, 14], - '2:00PM-3:00PM': [14, 15], - '3:00PM-4:00PM': [15, 16], - '4:00PM-5:00PM': [16, 17], - '5:00PM-6:00PM': [17, 18], - '6:00PM-7:00PM': [18, 19], - '7:00PM-8:00PM': [19, 20], - '8:00PM-9:00PM': [20, 21], - '9:00PM-10:00PM': [21, 22], - '10:00PM-11:00PM': [22, 23], - '11:00PM-12:00AM': [23, 0], + '12:00-1:00am': [0, 1], + '1:00-2:00am': [1, 2], + '2:00-3:00am': [2, 3], + '3:00-4:00am': [3, 4], + '4:00-5:00am': [4, 5], + '5:00-6:00am': [5, 6], + '6:00-7:00am': [6, 7], + '7:00-8:00am': [7, 8], + '8:00-9:00am': [8, 9], + '9:00-10:00am': [9, 10], + '10:00-11:00am': [10, 11], + '11:00-12:00pm': [11, 12], + '12:00-1:00pm': [12, 13], + '1:00-2:00pm': [13, 14], + '2:00-3:00pm': [14, 15], + '3:00-4:00pm': [15, 16], + '4:00-5:00pm': [16, 17], + '5:00-6:00pm': [17, 18], + '6:00-7:00pm': [18, 19], + '7:00-8:00pm': [19, 20], + '8:00-9:00pm': [20, 21], + '9:00-10:00pm': [21, 22], + '10:00-11:00pm': [22, 23], + '11:00-12:00am': [23, 0], +}; + +const timeRangesSecondary = { + '12:00-1:00am': ['12:30-1:30am', '12:00-2:00am', '12:30-2:30am'], + '1:00-2:00am': ['1:30-2:30am', '1:00-3:00am', '1:30-3:30am'], + '2:00-3:00am': ['2:30-3:30am', '2:00-4:00am', '2:30-4:30am'], + '3:00-4:00am': ['3:30-4:30am', '3:00-5:00am', '3:30-5:30am'], + '4:00-5:00am': ['4:30-5:30am', '4:00-6:00am', '4:30-6:30am'], + '5:00-6:00am': ['5:30-6:30am', '5:00-7:00am', '5:30-7:30am'], + '6:00-7:00am': ['6:30-7:30am', '6:00-8:00am', '6:30-8:30am'], + '7:00-8:00am': ['7:30-8:30am', '7:00-9:00am', '7:30-9:30am'], + '8:00-9:00am': ['8:30-9:30am', '8:00-10:00am', '8:30-10:30am'], + '9:00-10:00am': ['9:30-10:30am', '9:00-11:00am', '9:30-11:30am'], + '10:00-11:00am': ['10:30-11:30am', '10:00-12:00pm', '10:30-12:30pm'], + '11:00-12:00pm': ['11:30-12:30pm', '11:00-1:00pm', '11:30-1:30pm'], + '12:00-1:00pm': ['12:30-1:30pm', '12:00-2:00pm', '12:30-2:30pm'], + '1:00-2:00pm': ['1:30-2:30pm', '1:00-3:00pm', '1:30-3:30pm'], + '2:00-3:00pm': ['2:30-3:30pm', '2:00-4:00pm', '2:30-4:30pm'], + '3:00-4:00pm': ['3:30-4:30pm', '3:00-5:00pm', '3:30-5:30pm'], + '4:00-5:00pm': ['4:30-5:30pm', '4:00-6:00pm', '4:30-6:30pm'], + '5:00-6:00pm': ['5:30-6:30pm', '5:00-7:00pm', '5:30-7:30pm'], + '6:00-7:00pm': ['6:30-7:30pm', '6:00-8:00pm', '6:30-8:30pm'], + '7:00-8:00pm': ['7:30-8:30pm', '7:00-9:00pm', '7:30-9:30pm'], + '8:00-9:00pm': ['8:30-9:30pm', '8:00-10:00pm', '8:30-10:30pm'], + '9:00-10:00pm': ['9:30-10:30pm', '9:00-11:00pm', '9:30-11:30pm'], + '10:00-11:00pm': ['10:30-11:30pm', '10:00-12:00am', '10:30-12:30am'], + '11:00-12:00am': ['11:30-12:30am', '11:00-1:00am', '11:30-1:30am'] }; -export default class StudyRoomsPreview extends Component { +class StudyRoomsPreview extends Component { static navigationOptions = { header: () => { } } - renderRow = item => + constructor() { + super(); + this.state = { + available: {}, + listOfRooms: [], + loading: true + }; + } + + componentDidMount() { + const setting = new Date(); + const { changeTime: changeTimeAction, changeDate: changeDateAction } = this.props; + const minutes = setting.getMinutes(); + if (minutes !== 30 && minutes !== 0) { + if (minutes > 30) { + setting.setMinutes(60); + } else { + setting.setMinutes(30); + } + } + let styledTime = setting.toLocaleTimeString(); + const last2ndChar = styledTime[styledTime.length - 2]; + const lastChar = styledTime[styledTime.length - 1]; + if (Platform.OS === 'ios') { + styledTime = styledTime.slice(0, -6) + last2ndChar + lastChar; + } else { + styledTime = styledTime.slice(0, -3); + let hour = parseInt(styledTime.substring(0, 2), 10); + let hourString = hour.toString(); + if (hour > 12) { + hour -= 12; + hourString = hour.toString(); + styledTime = `${hourString + styledTime.slice(2)}PM`; + } else { + if (hourString === '0') { + hourString = '12'; + } + styledTime = `${hourString + styledTime.slice(2)}AM`; + } + } + changeTimeAction(styledTime); + let chosen = setting; + let dd = chosen.getDate(); + let mm = chosen.getMonth() + 1; // January is 0! + const yyyy = chosen.getFullYear(); + if (dd < 10) { + dd = `0${dd}`; + } + if (mm < 10) { + mm = `0${mm}`; + } + chosen = `${mm}/${dd}/${yyyy}`; + changeDateAction(chosen); + } + + componentDidUpdate(prevProps) { + const { date } = this.props; + // Typical usage (don't forget to compare props): + if (date !== prevProps.date) { + this.getHourAvailabilities(); + } + } + + async getHourAvailabilities() { + let appendedURL = ''; + const { date } = this.props; + if (date.length > 0) { + const month = date.substring(0, 2); + const day = date.substring(3, 5); + const year = date.substring(date.length - 4); + appendedURL = `?date=${year}-${month}-${day}`; + } + const rangeDict = {}; + const listOfRooms = []; + await fetch(`http://studysmartserver-env.bfmjpq3pm9.us-west-1.elasticbeanstalk.com/studyinfo${appendedURL}`) + .then(response => response.json()) + .then((data) => { + for (let i = 0; i < data.Items.length; i += 1) { + const time = data.Items[i].time.split(' ')[0]; + if (time in rangeDict) { + rangeDict[time].push(data.Items[i]); + } else { + rangeDict[time] = [data.Items[i]]; + } + listOfRooms.push(data.Items[i]); + } + this.setState({ + available: rangeDict, + listOfRooms, + loading: false, + }); + }); + } + + + renderRow(item) { + const { available } = this.state; + const firstHour = available[item]; + const firstHalfHour = available[timeRangesSecondary[item][0]]; + const twoHour = available[timeRangesSecondary[item][1]]; + const twoHalfHour = available[timeRangesSecondary[item][2]]; + const first = firstHour !== undefined ? firstHour : []; + const second = second !== undefined ? firstHalfHour : []; + const third = twoHour !== undefined ? twoHour : []; + const fourth = twoHalfHour !== undefined ? twoHalfHour : []; + const total = first.concat(second, third, fourth); + if (total.length > 0) { return (); } + return null; + } render() { const titles = Object.keys(timeRanges); return ( - this.renderRow(item)} - keyExtractor={(item, index) => index.toString()} - /> + {this.state.loading ? Loading : ( + this.renderRow(item)} + keyExtractor={(item, index) => index.toString()} + /> + )} + ); } @@ -61,3 +203,34 @@ const styles = StyleSheet.create({ backgroundColor: 'white' }, }); + +const mapStateToProps = state => ({ + time: state.study.time, + date: state.study.date, + duration: state.study.duration, + location: state.study.location, + hillData: state.study.hillData, + libraryData: state.study.libraryData, + unstyledTime: state.study.unstyledTime + +}); + +const mapDispatchToProps = dispatch => ({ + changeTime: (time) => { + dispatch(changeTime(time)); + }, + changeDate: (date) => { + dispatch(changeDate(date)); + }, + changeLocation: (location) => { + dispatch(changeLocation(location)); + }, + loadHillData: (hillData) => { + dispatch(loadHillData(hillData)); + }, + loadLibraryData: (libraryData) => { + dispatch(loadLibraryData(libraryData)); + } +}); + +export default connect(mapStateToProps, mapDispatchToProps)(StudyRoomsPreview);