From 976684dfce655f74d1dcd6921c2489f3e8347558 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Fri, 10 Jul 2020 17:04:29 +0200 Subject: [PATCH] Added booking confirmation screen --- .../Lists/Equipment/EquipmentListItem.js | 64 ++++++++-- src/navigation/MainNavigator.js | 4 +- .../Equipment/EquipmentConfirmScreen.js | 115 ++++++++++++++++++ .../Amicale/Equipment/EquipmentListScreen.js | 50 +++++++- .../Amicale/Equipment/EquipmentRentScreen.js | 20 ++- translations/en.json | 6 +- 6 files changed, 241 insertions(+), 18 deletions(-) create mode 100644 src/screens/Amicale/Equipment/EquipmentConfirmScreen.js diff --git a/src/components/Lists/Equipment/EquipmentListItem.js b/src/components/Lists/Equipment/EquipmentListItem.js index 017956b..7276b9d 100644 --- a/src/components/Lists/Equipment/EquipmentListItem.js +++ b/src/components/Lists/Equipment/EquipmentListItem.js @@ -10,9 +10,11 @@ import { getRelativeDateString, isEquipmentAvailable } from "../../../utils/EquipmentBooking"; +import {StackNavigationProp} from "@react-navigation/stack"; type Props = { - onPress: () => void, + navigation: StackNavigationProp, + userDeviceRentDates: [string, string], item: Device, height: number, theme: CustomTheme, @@ -20,29 +22,73 @@ type Props = { class EquipmentListItem extends React.Component { - shouldComponentUpdate() { - return false; + shouldComponentUpdate(nextProps: Props): boolean { + return nextProps.userDeviceRentDates !== this.props.userDeviceRentDates; } render() { const colors = this.props.theme.colors; const item = this.props.item; + const userDeviceRentDates = this.props.userDeviceRentDates; + const isRented = userDeviceRentDates != null; const isAvailable = isEquipmentAvailable(item); const firstAvailability = getFirstEquipmentAvailability(item); + + let onPress; + if (isRented) + onPress = () => this.props.navigation.navigate("equipment-confirm", { + item: item, + dates: userDeviceRentDates + }); + else + onPress = () => this.props.navigation.navigate("equipment-rent", {item: item}); + + let description; + if (isRented) { + const start = new Date(userDeviceRentDates[0]); + const end = new Date(userDeviceRentDates[1]); + if (start.getTime() !== end.getTime()) + description = i18n.t('equipmentScreen.bookingPeriod', { + begin: getRelativeDateString(start), + end: getRelativeDateString(end) + }); + else + description = i18n.t('equipmentScreen.bookingDay', { + date: getRelativeDateString(start) + }); + } else if (isAvailable) + description = i18n.t('equipmentScreen.bail', {cost: item.caution}); + else + description = i18n.t('equipmentScreen.available', {date: getRelativeDateString(firstAvailability)}); + + let icon; + if (isRented) + icon = "bookmark-check"; + else if (isAvailable) + icon = "check-circle-outline"; + else + icon = "update"; + + let color; + if (isRented) + color = colors.warning; + else if (isAvailable) + color = colors.success; + else + color = colors.primary; + return ( } right={(props) => React.Node }) { {createScreenCollapsibleStack("profile", MainStack, ProfileScreen, i18n.t('screens.profile'))} {createScreenCollapsibleStack("club-list", MainStack, ClubListScreen, i18n.t('clubs.clubList'))} {createScreenCollapsibleStack("equipment-list", MainStack, EquipmentScreen, i18n.t('screens.equipmentList'))} - {createScreenCollapsibleStack("equipment-lend", MainStack, EquipmentLendScreen, i18n.t('screens.equipmentLend'))} + {createScreenCollapsibleStack("equipment-rent", MainStack, EquipmentLendScreen, i18n.t('screens.equipmentLend'))} + {createScreenCollapsibleStack("equipment-confirm", MainStack, EquipmentConfirmScreen, i18n.t('screens.equipmentConfirm'))} { + + item: Device | null; + dates: [string, string] | null; + + constructor(props: Props) { + super(props); + if (this.props.route.params != null) { + if (this.props.route.params.item != null) + this.item = this.props.route.params.item; + else + this.item = null; + if (this.props.route.params.dates != null) + this.dates = this.props.route.params.dates; + else + this.dates = null; + } + } + + render() { + const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack; + const item = this.item; + const dates = this.dates; + if (item != null && dates != null) { + const start = new Date(dates[0]); + const end = new Date(dates[1]); + return ( + + + + + + + {item.name} + + + ({i18n.t('equipmentScreen.bail', {cost: item.caution})}) + + + + + + {i18n.t("equipmentScreen.bookingConfirmedMessage")} + + + + + ); + } else + return null; + + } + +} + +export default withCollapsible(withTheme(EquipmentConfirmScreen)); diff --git a/src/screens/Amicale/Equipment/EquipmentListScreen.js b/src/screens/Amicale/Equipment/EquipmentListScreen.js index c645f58..1d25477 100644 --- a/src/screens/Amicale/Equipment/EquipmentListScreen.js +++ b/src/screens/Amicale/Equipment/EquipmentListScreen.js @@ -25,22 +25,59 @@ export type Device = { booked_at: Array<{begin: string, end: string}>, }; +export type RentedDevice = { + device_id: number, + device_name: string, + begin: string, + end: string, +} + const ICON_AMICALE = require('../../../../assets/amicale.png'); const LIST_ITEM_HEIGHT = 64; class EquipmentListScreen extends React.Component { data: Array; + userRents: Array; + + authRef: { current: null | AuthenticatedScreen }; + canRefresh: boolean; + + constructor(props: Props) { + super(props); + this.canRefresh = false; + this.authRef = React.createRef(); + this.props.navigation.addListener('focus', this.onScreenFocus); + } + + onScreenFocus = () => { + if (this.canRefresh && this.authRef.current != null) + this.authRef.current.reload(); + this.canRefresh = true; + }; getRenderItem = ({item}: { item: Device }) => { return ( this.props.navigation.navigate('equipment-lend', {item: item})} + navigation={this.props.navigation} item={item} + userDeviceRentDates={this.getUserDeviceRentDates(item)} height={LIST_ITEM_HEIGHT}/> ); }; + getUserDeviceRentDates(item: Device) { + let dates = null; + for (let i = 0; i < this.userRents.length; i++) { + let device = this.userRents[i]; + if (item.id === device.device_id) { + dates = [device.begin, device.end]; + break; + } + } + return dates; + } + /** * Gets the list header, with explains this screen's purpose * @@ -78,6 +115,11 @@ class EquipmentListScreen extends React.Component { if (fetchedData != null) this.data = fetchedData["devices"]; } + if (data[1] != null) { + const fetchedData = data[1]; + if (fetchedData != null) + this.userRents = fetchedData["locations"]; + } const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack; return ( { return ( { const start = this.getBookStartDate(); const end = this.getBookEndDate(); if (item != null && start != null && end != null) { + console.log({ + "device": item.id, + "begin": getISODate(start), + "end": getISODate(end), + }) ConnectionManager.getInstance().authenticatedRequest( "location/booking", { @@ -236,7 +240,11 @@ class EquipmentRentScreen extends React.Component { "end": getISODate(end), }) .then(() => { - console.log("Success, replace screen"); + this.onDialogDismiss(); + this.props.navigation.replace("equipment-confirm", { + item: this.item, + dates: [getISODate(start), getISODate(end)] + }); resolve(); }) .catch((error: number) => { @@ -244,8 +252,10 @@ class EquipmentRentScreen extends React.Component { this.showErrorDialog(error); resolve(); }); - } else + } else { + this.onDialogDismiss(); resolve(); + } }); } diff --git a/translations/en.json b/translations/en.json index c89bac0..57c5b1f 100644 --- a/translations/en.json +++ b/translations/en.json @@ -28,7 +28,8 @@ "feedback": "Feedback", "insaAccount": "INSA Account", "equipmentList": "Equipment Booking", - "equipmentLend": "Book" + "equipmentLend": "Book", + "equipmentConfirm": "Confirmation" }, "intro": { "slideMain": { @@ -464,6 +465,7 @@ "bookButton": "Book selected dates", "dialogTitle": "Confirm booking?", "dialogTitleLoading": "Sending your booking...", - "dialogMessage": "Are you sure you want to confirm your booking?\n\nYou will then be able to claim the selected equipment at the Amicale for the duration of your booking in exchange of a bail." + "dialogMessage": "Are you sure you want to confirm your booking?\n\nYou will then be able to claim the selected equipment at the Amicale for the duration of your booking in exchange of a bail.", + "bookingConfirmedMessage": "Do not forget to come by the Amicale to give your bail in exchange of the equipment." } }