From 139202c1cd1d1e50b316f2cbfd261906ce5ffe77 Mon Sep 17 00:00:00 2001 From: Pierre LHOSTE Date: Fri, 8 Nov 2024 14:19:46 +0100 Subject: [PATCH 1/9] Add pickup page --- src/navigation/index.js | 2 + src/navigation/navigators/StoreNavigator.js | 21 +- src/navigation/store/NewDeliveryAddress.js | 35 +- src/navigation/store/NewDeliveryForm.js | 24 +- src/navigation/store/NewDeliveryPickup.js | 401 ++++++++++++++++++ .../store/components/HeaderBackButton.js | 2 +- 6 files changed, 457 insertions(+), 28 deletions(-) create mode 100644 src/navigation/store/NewDeliveryPickup.js diff --git a/src/navigation/index.js b/src/navigation/index.js index 5263d1f4a..507a0b3a5 100644 --- a/src/navigation/index.js +++ b/src/navigation/index.js @@ -20,6 +20,7 @@ import StoreDashboard from './store/Dashboard'; import StoreDelivery from './store/Delivery'; import StoreNewDeliveryAddress from './store/NewDeliveryAddress'; import StoreNewDeliveryForm from './store/NewDeliveryForm'; +import StoreNewDeliveryPickup from './store/NewDeliveryPickup'; import StoreNewDeliveryPrice from './store/NewDeliveryPrice'; import CheckoutPaymentMethodCard from './checkout/PaymentMethodCard'; @@ -129,6 +130,7 @@ export default { TaskSignature, StoreDashboard, StoreDelivery, + StoreNewDeliveryPickup, StoreNewDeliveryAddress, StoreNewDeliveryForm, StoreNewDeliveryPrice, diff --git a/src/navigation/navigators/StoreNavigator.js b/src/navigation/navigators/StoreNavigator.js index 7f3970678..632d7fa06 100644 --- a/src/navigation/navigators/StoreNavigator.js +++ b/src/navigation/navigators/StoreNavigator.js @@ -2,9 +2,9 @@ import { createStackNavigator } from '@react-navigation/stack'; import React from 'react'; import screens, { headerLeft } from '..'; +import NavigationHolder from '../../NavigationHolder'; import HeaderButton from '../../components/HeaderButton'; import i18n from '../../i18n'; -import NavigationHolder from '../../NavigationHolder'; import HeaderBackButton from '../store/components/HeaderBackButton'; import { stackNavigatorScreenOptions } from '../styles'; @@ -36,14 +36,16 @@ const MainNavigator = () => ( name="StoreDelivery" component={screens.StoreDelivery} options={({ route }) => { - let id + let id; if (route.params.delivery) { - id = route.params.delivery.orderNumber ? route.params.delivery.orderNumber : route.params.delivery.id + id = route.params.delivery.orderNumber + ? route.params.delivery.orderNumber + : route.params.delivery.id; } return { - title: i18n.t('STORE_DELIVERY', { id: id }) - }} - } + title: i18n.t('STORE_DELIVERY', { id: id }), + }; + }} /> ); @@ -52,6 +54,13 @@ const NewDeliveryStack = createStackNavigator(); const NewDeliveryNavigator = () => ( + ( + + + + Drop off informations + + + Fill in the information about the drop off location and contact + + {t('STORE_NEW_DELIVERY_SEARCH_CLIENT')}{' '} @@ -340,6 +359,12 @@ const styles = StyleSheet.create({ errorInput: { borderColor: '#FF4136', }, + header: { + display: 'flex', + flexDirection: 'row', + alignContent: 'center', + gap: 5, + }, formGroup: { marginBottom: 10, }, diff --git a/src/navigation/store/NewDeliveryForm.js b/src/navigation/store/NewDeliveryForm.js index 86ce5a5c2..79878f1ef 100644 --- a/src/navigation/store/NewDeliveryForm.js +++ b/src/navigation/store/NewDeliveryForm.js @@ -1,5 +1,4 @@ import { Formik } from 'formik'; -import { parsePhoneNumberFromString } from 'libphonenumber-js'; import moment from 'moment'; import { Box, Button, HStack, Text, VStack } from 'native-base'; import React, { useEffect, useState } from 'react'; @@ -139,6 +138,7 @@ function DeliveryForm(props) { function submit(values) { const delivery = { store: store['@id'], + pickup: route.params?.pickup, dropoff: { address: { ...values.address, @@ -160,7 +160,7 @@ function DeliveryForm(props) { } function validate(values) { - let errors = {}; + const errors = {}; if (hasTimeSlot && !selectedChoice) { errors.timeSlot = t('STORE_NEW_DELIVERY_ERROR.EMPTY_TIME_SLOT'); @@ -232,26 +232,18 @@ function DeliveryForm(props) { setFieldTouched('weight'); } - const delivery = route.params?.delivery; - - let telephone = ''; - if (delivery.telephone) { - const phoneNumber = parsePhoneNumberFromString(delivery.telephone, country); - if (phoneNumber && phoneNumber.isValid()) { - telephone = phoneNumber.formatNational(); - } - } + const dropoff = route.params?.dropoff; let initialValues = { - address: delivery.address, + address: dropoff.address, // set from the first step newDeliveryAddress - description: delivery.description || '', - contactName: delivery.contactName || '', - businessName: delivery.businessName || '', + description: dropoff.description || '', + contactName: dropoff.contactName || '', + businessName: dropoff.businessName || '', telephone, // ---------------- weight: null, - comments: delivery.comments || '', + comments: dropoff.comments || '', }; if (hasTimeSlot) { diff --git a/src/navigation/store/NewDeliveryPickup.js b/src/navigation/store/NewDeliveryPickup.js new file mode 100644 index 000000000..46f9a5e9d --- /dev/null +++ b/src/navigation/store/NewDeliveryPickup.js @@ -0,0 +1,401 @@ +import { IconCircleArrowUpFilled } from '@tabler/icons-react-native'; +import { Formik } from 'formik'; +import { AsYouType, parsePhoneNumberFromString } from 'libphonenumber-js'; +import _ from 'lodash'; +import { Text } from 'native-base'; +import React, { useState } from 'react'; +import { withTranslation } from 'react-i18next'; +import { Platform, StyleSheet, View } from 'react-native'; +import { connect } from 'react-redux'; +import AddressAutocomplete from '../../components/AddressAutocomplete'; +import { assertDelivery } from '../../redux/Store/actions'; +import { selectStore } from '../../redux/Store/selectors'; +import { + useBackgroundContainerColor, + useBackgroundHighlightColor, + usePrimaryColor, +} from '../../styles/theme'; +import ModalFormWrapper from './ModalFormWrapper'; +import ClientListInput from './components/ClientListInput'; +import FormInput from './components/FormInput'; + +function NewDeliveryPickup(props) { + const [validAddress, setValidAddress] = useState(false); + const [address, setAddress] = useState(null); + const backgroundColor = useBackgroundContainerColor(); + const backgroundHighlightColor = useBackgroundHighlightColor(); + const primaryColor = usePrimaryColor(); + + const { + store, + deliveryError, + addresses, + assertDelivery, + t, + navigation, + country, + } = props; + + const inputStyles = { + backgroundColor, + borderColor: backgroundHighlightColor, + }; + + function setAddressData(data, setFieldValue) { + const contactName = data.contactName || ''; + const telephone = data.telephone || ''; + const businessName = data.businessName || ''; + const description = data.description || ''; + + setFieldValue('contactName', contactName); + setFieldValue('telephone', telephone); + setFieldValue('businessName', businessName); + setFieldValue('description', description); + setAddress({ + streetAddress: data.streetAddress, + geo: data.geo, + }); + } + + function onSelectAddress(address, setFieldValue) { + if (address['@id']) { + setAddressData(address, setFieldValue); + } else { + setAddress(address); + } + + const delivery = { + store: store['@id'], + dropoff: { + address, + before: 'tomorrow 12:00', + }, + }; + + assertDelivery(delivery, () => { + setValidAddress(true); + }); + } + + let autocompleteProps = { + inputContainerStyle: { + flex: 1, + borderWidth: 0, + }, + textInputStyle: { + padding: 10, + }, + }; + + if (!_.isEmpty(deliveryError)) { + autocompleteProps = { + ...autocompleteProps, + inputContainerStyle: { + ...autocompleteProps.inputContainerStyle, + ...styles.errorInput, + }, + }; + } + + function validate(values) { + const errors = {}; + + if (_.isEmpty(values.telephone)) { + errors.telephone = t('STORE_NEW_DELIVERY_ERROR.EMPTY_PHONE_NUMBER'); + } else { + const phoneNumber = parsePhoneNumberFromString( + _.trim(values.telephone), + country, + ); + if (!phoneNumber || !phoneNumber.isValid()) { + errors.telephone = t('INVALID_PHONE_NUMBER'); + } + } + + if (_.isEmpty(values.contactName)) { + errors.contactName = t('STORE_NEW_DELIVERY_ERROR.EMPTY_CONTACT_NAME'); + } + + if (!validAddress) { + errors.address = t('STORE_NEW_DELIVERY_ADDRESS_HELP'); + } + + return errors; + } + + const initialValues = { + telephone: '', + contactName: '', + businessName: '', + description: '', + address: '', + }; + + function handleChangeTelephone(value, setFieldValue, setFieldTouched) { + setFieldValue('telephone', new AsYouType(country).input(value)); + setFieldTouched('telephone', true); + } + + function submit(values) { + const pickup = { + telephone: parsePhoneNumberFromString(values.telephone, country).format( + 'E.164', + ), + contactName: values.contactName, + description: values.description, + businessName: values.businessName, + address, + }; + console.log('🚀 ~ pickup to send:', pickup); + + navigation.navigate('StoreNewDeliveryAddress', { pickup }); + } + + return ( + submit(values)} + validateOnBlur={true} + validateOnChange={true}> + {({ + handleChange, + handleBlur, + handleSubmit, + values, + errors, + touched, + setFieldValue, + setFieldTouched, + }) => ( + + + + + Pick up informations + + + Fill in the information about the pick up location or keep the + default values. + + + + + {t('STORE_NEW_DELIVERY_SEARCH_CLIENT')}{' '} + ({t('OPTIONAL')}) + + + { + setAddressData(a, setFieldValue); + setValidAddress(true); + }} + addresses={addresses} + placeholder={t('STORE_NEW_DELIVERY_ENTER_SEARCH_CLIENT')} + /> + + + + + {t('STORE_NEW_DELIVERY_ADDRESS')} + {validAddress && ' ✓'} + + + { + if (validAddress) setValidAddress(false); + handleChange('address'); + }} + onBlur={handleBlur('address')} + value={address} + onSelectAddress={e => onSelectAddress(e, setFieldValue)} + containerStyle={[ + { + flex: 1, + justifyContent: 'center', + }, + ]} + style={{ + borderRadius: 0, + padding: 10, + borderWidth: 0, + paddingLeft: 10, + ...inputStyles, + }} + {...autocompleteProps} + placeholder={t('ENTER_ADDRESS')} + _focus={{ borderColor: primaryColor }} + /> + + {errors.address && !validAddress && touched.address && ( + + {errors.address} + + )} + + + + {t('STORE_NEW_DELIVERY_BUSINESS_NAME')}{' '} + ({t('OPTIONAL')}) + + + {errors.businessName && touched.businessName && ( + + {errors.businessName} + + )} + + + + {t('STORE_NEW_DELIVERY_CONTACT_NAME')} + + + {errors.contactName && touched.contactName && ( + + {errors.contactName} + + )} + + + + {t('STORE_NEW_DELIVERY_PHONE_NUMBER')} + + + handleChangeTelephone(value, setFieldValue, setFieldTouched) + } + onBlur={handleBlur('telephone')} + value={values.telephone} + placeholder={t('STORE_NEW_DELIVERY_ENTER_PHONE_NUMBER')} + /> + {errors.telephone && touched.telephone && ( + + {errors.telephone} + + )} + + + + {t('STORE_NEW_DELIVERY_ADDRESS_DESCRIPTION')}{' '} + ({t('OPTIONAL')}) + + + + + )} + + ); +} + +const styles = StyleSheet.create({ + autocompleteWrapper: { + height: 40, + ...Platform.select({ + android: { + flex: 1, + top: 0, + right: 0, + left: 0, + zIndex: 1, + }, + ios: { + top: 0, + right: 0, + left: 0, + zIndex: 10, + overflow: 'visible', + }, + }), + }, + label: { + marginBottom: 8, + fontWeight: '600', + }, + optional: { + fontWeight: '400', + opacity: 0.7, + fontSize: 12, + }, + help: { + marginBottom: 5, + fontWeight: '400', + }, + errorInput: { + borderColor: '#FF4136', + }, + header: { + display: 'flex', + flexDirection: 'row', + alignContent: 'center', + gap: 5, + }, + formGroup: { + marginBottom: 10, + }, + textInput: { + height: 40, + paddingHorizontal: 10, + }, + errorText: { + color: '#FF4136', + marginTop: 5, + }, + textarea: { + minHeight: 25 * 3, + }, +}); + +function mapDispatchToProps(state) { + return { + country: state.app.settings.country.toUpperCase(), + store: selectStore(state), + deliveryError: state.store.assertDeliveryError, + addresses: state.store.addresses, + }; +} + +function mapStateToProps(dispatch) { + return { + assertDelivery: (delivery, onSuccess) => + dispatch(assertDelivery(delivery, onSuccess)), + }; +} + +export default connect( + mapDispatchToProps, + mapStateToProps, +)(withTranslation()(NewDeliveryPickup)); diff --git a/src/navigation/store/components/HeaderBackButton.js b/src/navigation/store/components/HeaderBackButton.js index 5e81a1a85..0f47c4dfd 100644 --- a/src/navigation/store/components/HeaderBackButton.js +++ b/src/navigation/store/components/HeaderBackButton.js @@ -8,7 +8,7 @@ const HeaderBackButtonWrapper = props => { let { title, currentRoute, ...otherProps } = props; - if (currentRoute === 'StoreNewDeliveryAddress') { + if (currentRoute === 'StoreNewDeliveryPickup') { title = t('CANCEL'); } else { title = 'Back'; From 98f66e693c60530cd1ce89c0ce1a1a8abbb258f9 Mon Sep 17 00:00:00 2001 From: Pierre LHOSTE Date: Fri, 8 Nov 2024 14:20:52 +0100 Subject: [PATCH 2/9] Fix lint errors --- src/navigation/store/NewDeliveryForm.js | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/navigation/store/NewDeliveryForm.js b/src/navigation/store/NewDeliveryForm.js index 79878f1ef..e4b9ed0e4 100644 --- a/src/navigation/store/NewDeliveryForm.js +++ b/src/navigation/store/NewDeliveryForm.js @@ -210,25 +210,25 @@ function DeliveryForm(props) { } function handleChangeWeight(value, setFieldValue, setFieldTouched) { - value = value.replace(',', '.').replace(/[^0-9.]/g, ''); + let newValue = value.replace(',', '.').replace(/[^0-9.]/g, ''); - const firstDecimalIndex = value.indexOf('.'); + const firstDecimalIndex = newValue.indexOf('.'); if (firstDecimalIndex === 0) { - value = '0' + value; + newValue = `0${newValue}`; } else if (firstDecimalIndex !== -1) { - value = - value.substring(0, firstDecimalIndex + 1) + - value.substring(firstDecimalIndex + 1).replace(/\./g, ''); + newValue = + newValue.substring(0, firstDecimalIndex + 1) + + newValue.substring(firstDecimalIndex + 1).replace(/\./g, ''); } - if (value.includes('.')) { - const decimalIndex = value.indexOf('.'); - value = - value.substring(0, decimalIndex + 1) + - value.substring(decimalIndex + 1, decimalIndex + 4); + if (newValue.includes('.')) { + const decimalIndex = newValue.indexOf('.'); + newValue = + newValue.substring(0, decimalIndex + 1) + + newValue.substring(decimalIndex + 1, decimalIndex + 4); } - setFieldValue('weight', value); + setFieldValue('weight', newValue); setFieldTouched('weight'); } @@ -337,8 +337,8 @@ function DeliveryForm(props) { gap: 16, marginTop: 4, }}> - {packages && packages.length ? ( - packagesCount.map((item, index) => { + {packages?.length ? ( + packagesCount.map(item => { return ( + key={item.type}> {}} onPressIncrement={() => From 3d7018df3db934ba86c826247b3a16f42626284c Mon Sep 17 00:00:00 2001 From: Pierre LHOSTE Date: Fri, 8 Nov 2024 14:52:17 +0100 Subject: [PATCH 3/9] Add checkbox custom pickup address --- src/navigation/store/NewDeliveryForm.js | 4 +- src/navigation/store/NewDeliveryPickup.js | 283 ++++++++++++---------- src/navigation/store/NewDeliveryPrice.js | 2 +- 3 files changed, 152 insertions(+), 137 deletions(-) diff --git a/src/navigation/store/NewDeliveryForm.js b/src/navigation/store/NewDeliveryForm.js index e4b9ed0e4..9dc9c1fd4 100644 --- a/src/navigation/store/NewDeliveryForm.js +++ b/src/navigation/store/NewDeliveryForm.js @@ -138,7 +138,7 @@ function DeliveryForm(props) { function submit(values) { const delivery = { store: store['@id'], - pickup: route.params?.pickup, + pickup: route.params?.pickup || undefined, dropoff: { address: { ...values.address, @@ -240,7 +240,7 @@ function DeliveryForm(props) { description: dropoff.description || '', contactName: dropoff.contactName || '', businessName: dropoff.businessName || '', - telephone, + telephone: dropoff.telephone || '', // ---------------- weight: null, comments: dropoff.comments || '', diff --git a/src/navigation/store/NewDeliveryPickup.js b/src/navigation/store/NewDeliveryPickup.js index 46f9a5e9d..f412febf8 100644 --- a/src/navigation/store/NewDeliveryPickup.js +++ b/src/navigation/store/NewDeliveryPickup.js @@ -2,7 +2,7 @@ import { IconCircleArrowUpFilled } from '@tabler/icons-react-native'; import { Formik } from 'formik'; import { AsYouType, parsePhoneNumberFromString } from 'libphonenumber-js'; import _ from 'lodash'; -import { Text } from 'native-base'; +import { Checkbox, Text } from 'native-base'; import React, { useState } from 'react'; import { withTranslation } from 'react-i18next'; import { Platform, StyleSheet, View } from 'react-native'; @@ -25,6 +25,7 @@ function NewDeliveryPickup(props) { const backgroundColor = useBackgroundContainerColor(); const backgroundHighlightColor = useBackgroundHighlightColor(); const primaryColor = usePrimaryColor(); + const [pickupAddress, setPickupAddress] = useState(true); const { store, @@ -137,16 +138,18 @@ function NewDeliveryPickup(props) { } function submit(values) { - const pickup = { - telephone: parsePhoneNumberFromString(values.telephone, country).format( - 'E.164', - ), - contactName: values.contactName, - description: values.description, - businessName: values.businessName, - address, - }; - console.log('🚀 ~ pickup to send:', pickup); + const pickup = pickupAddress + ? { + telephone: parsePhoneNumberFromString( + values.telephone, + country, + ).format('E.164'), + contactName: values.contactName, + description: values.description, + businessName: values.businessName, + address, + } + : undefined; navigation.navigate('StoreNewDeliveryAddress', { pickup }); } @@ -185,135 +188,144 @@ function NewDeliveryPickup(props) { - - {t('STORE_NEW_DELIVERY_SEARCH_CLIENT')}{' '} - ({t('OPTIONAL')}) - - - { - setAddressData(a, setFieldValue); - setValidAddress(true); - }} - addresses={addresses} - placeholder={t('STORE_NEW_DELIVERY_ENTER_SEARCH_CLIENT')} - /> - + setPickupAddress(!pickupAddress)}> + Use custom pickup address + - - - {t('STORE_NEW_DELIVERY_ADDRESS')} - {validAddress && ' ✓'} - - - { - if (validAddress) setValidAddress(false); - handleChange('address'); - }} - onBlur={handleBlur('address')} - value={address} - onSelectAddress={e => onSelectAddress(e, setFieldValue)} - containerStyle={[ - { - flex: 1, - justifyContent: 'center', - }, - ]} - style={{ - borderRadius: 0, - padding: 10, - borderWidth: 0, - paddingLeft: 10, - ...inputStyles, - }} - {...autocompleteProps} - placeholder={t('ENTER_ADDRESS')} - _focus={{ borderColor: primaryColor }} - /> + + + + {t('STORE_NEW_DELIVERY_SEARCH_CLIENT')}{' '} + ({t('OPTIONAL')}) + + + { + setAddressData(a, setFieldValue); + setValidAddress(true); + }} + addresses={addresses} + placeholder={t('STORE_NEW_DELIVERY_ENTER_SEARCH_CLIENT')} + /> + - {errors.address && !validAddress && touched.address && ( - - {errors.address} + + + {t('STORE_NEW_DELIVERY_ADDRESS')} + {validAddress && ' ✓'} - )} - - - - {t('STORE_NEW_DELIVERY_BUSINESS_NAME')}{' '} - ({t('OPTIONAL')}) - - - {errors.businessName && touched.businessName && ( - - {errors.businessName} + + { + if (validAddress) setValidAddress(false); + handleChange('address'); + }} + onBlur={handleBlur('address')} + value={address} + onSelectAddress={e => onSelectAddress(e, setFieldValue)} + containerStyle={[ + { + flex: 1, + justifyContent: 'center', + }, + ]} + style={{ + borderRadius: 0, + padding: 10, + borderWidth: 0, + paddingLeft: 10, + ...inputStyles, + }} + {...autocompleteProps} + placeholder={t('ENTER_ADDRESS')} + _focus={{ borderColor: primaryColor }} + /> + + {errors.address && !validAddress && touched.address && ( + + {errors.address} + + )} + + + + {t('STORE_NEW_DELIVERY_BUSINESS_NAME')}{' '} + ({t('OPTIONAL')}) - )} - - - - {t('STORE_NEW_DELIVERY_CONTACT_NAME')} - - - {errors.contactName && touched.contactName && ( - - {errors.contactName} + + {errors.businessName && touched.businessName && ( + + {errors.businessName} + + )} + + + + {t('STORE_NEW_DELIVERY_CONTACT_NAME')} - )} - - - - {t('STORE_NEW_DELIVERY_PHONE_NUMBER')} - - - handleChangeTelephone(value, setFieldValue, setFieldTouched) - } - onBlur={handleBlur('telephone')} - value={values.telephone} - placeholder={t('STORE_NEW_DELIVERY_ENTER_PHONE_NUMBER')} - /> - {errors.telephone && touched.telephone && ( - - {errors.telephone} + + {errors.contactName && touched.contactName && ( + + {errors.contactName} + + )} + + + + {t('STORE_NEW_DELIVERY_PHONE_NUMBER')} - )} - - - - {t('STORE_NEW_DELIVERY_ADDRESS_DESCRIPTION')}{' '} - ({t('OPTIONAL')}) - - + + handleChangeTelephone(value, setFieldValue, setFieldTouched) + } + onBlur={handleBlur('telephone')} + value={values.telephone} + placeholder={t('STORE_NEW_DELIVERY_ENTER_PHONE_NUMBER')} + /> + {errors.telephone && touched.telephone && ( + + {errors.telephone} + + )} + + + + {t('STORE_NEW_DELIVERY_ADDRESS_DESCRIPTION')}{' '} + ({t('OPTIONAL')}) + + + )} @@ -322,6 +334,9 @@ function NewDeliveryPickup(props) { } const styles = StyleSheet.create({ + disabled: { + display: 'none', + }, autocompleteWrapper: { height: 40, ...Platform.select({ diff --git a/src/navigation/store/NewDeliveryPrice.js b/src/navigation/store/NewDeliveryPrice.js index eca7d043d..070793f64 100644 --- a/src/navigation/store/NewDeliveryPrice.js +++ b/src/navigation/store/NewDeliveryPrice.js @@ -4,8 +4,8 @@ import { withTranslation } from 'react-i18next'; import { StyleSheet } from 'react-native'; import { connect, useDispatch } from 'react-redux'; import { createDelivery, getPrice } from '../../redux/Store/actions'; -import FormInput from './components/FormInput'; import ModalFormWrapper from './ModalFormWrapper'; +import FormInput from './components/FormInput'; function NewDeliveryPrice(props) { const dispatch = useDispatch(); From 1dfb88f8c0adfed057b0101a9d50bd6854410606 Mon Sep 17 00:00:00 2001 From: Pierre LHOSTE Date: Fri, 8 Nov 2024 15:39:03 +0100 Subject: [PATCH 4/9] Add default address display --- src/navigation/store/NewDeliveryPickup.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/navigation/store/NewDeliveryPickup.js b/src/navigation/store/NewDeliveryPickup.js index f412febf8..f43264f5a 100644 --- a/src/navigation/store/NewDeliveryPickup.js +++ b/src/navigation/store/NewDeliveryPickup.js @@ -25,7 +25,7 @@ function NewDeliveryPickup(props) { const backgroundColor = useBackgroundContainerColor(); const backgroundHighlightColor = useBackgroundHighlightColor(); const primaryColor = usePrimaryColor(); - const [pickupAddress, setPickupAddress] = useState(true); + const [customAddress, setCustomAddress] = useState(false); const { store, @@ -99,6 +99,9 @@ function NewDeliveryPickup(props) { } function validate(values) { + console.log(customAddress); + + if (!customAddress) return {}; const errors = {}; if (_.isEmpty(values.telephone)) { @@ -138,7 +141,7 @@ function NewDeliveryPickup(props) { } function submit(values) { - const pickup = pickupAddress + const pickup = customAddress ? { telephone: parsePhoneNumberFromString( values.telephone, @@ -187,14 +190,20 @@ function NewDeliveryPickup(props) { default values. + + Default pick up address + {store.address.name} + {store.address.streetAddress} + {store.address.telephone} + setPickupAddress(!pickupAddress)}> + value={customAddress} + onChange={() => setCustomAddress(!customAddress)}> Use custom pickup address - + {t('STORE_NEW_DELIVERY_SEARCH_CLIENT')}{' '} From cfc3fe3ba51e4698acb196f086d19995349af87b Mon Sep 17 00:00:00 2001 From: Pierre LHOSTE Date: Tue, 12 Nov 2024 11:23:32 +0100 Subject: [PATCH 5/9] Localize dropoff and pickup information labels and descriptions --- src/i18n/locales/en.json | 6 ++++++ src/i18n/locales/fr.json | 6 ++++++ src/navigation/store/NewDeliveryAddress.js | 6 ++++-- src/navigation/store/NewDeliveryPickup.js | 13 ++++++++----- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 1358bf509..f11215863 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -288,6 +288,12 @@ "CART_COLLECTION_TIME_DIFF": "Collection in {{diff}} minutes", "TIME_DIFF_SHORT": "{{min}} - {{max}} minutes", "TASK_ADD_PROOF_OF_DELIVERY": "Add a proof of delivery", + "STORE_NEW_DELIVERY_PICKUP_TITLE": "Pickup address", + "STORE_NEW_DELIVERY_PICKUP_DESCRIPTION": "Fill in the information about the pickup address or keep the default one", + "STORE_NEW_DELIVERY_PICKUP_DEFAULT_ADDRESS": "Default pickup address", + "STORE_NEW_DELIVERY_PICKUP_USE_CUSTOM_ADDRESS": "Use a custom address", + "STORE_NEW_DELIVERY_DROPOFF_TITLE": "Dropoff address", + "STORE_NEW_DELIVERY_DROPOFF_DESCRIPTION": "Fill in the information about the dropoff address and the contact details", "STORE_NEW_DELIVERY": "New delivery", "STORE_NEW_DELIVERY_ADDRESS": "Address", "STORE_NEW_DELIVERY_SEARCH_CLIENT": "Search for a client", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 8844303a4..6960b78ce 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -274,6 +274,12 @@ "TASK_IMAGE_UPLOAD_CONFIRM_LONG": "L'image a été uploadée avec succès", "AN_ERROR_OCCURRED": "Une erreur s'est produite", "TASK_ADD_PROOF_OF_DELIVERY": "Ajouter une preuve de livraison", + "STORE_NEW_DELIVERY_PICKUP_TITLE": "Adresse de récupération", + "STORE_NEW_DELIVERY_PICKUP_DESCRIPTION": "Indiquez une adresse de récupération ou garder l'address de livraison par défaut pour récupérer la commande", + "STORE_NEW_DELIVERY_PICKUP_DEFAULT_ADDRESS": "Adresse de livraison par défaut", + "STORE_NEW_DELIVERY_PICKUP_USE_CUSTOM_ADDRESS": "Utiliser une adresse personnalisée", + "STORE_NEW_DELIVERY_DROPOFF_TITLE": "Adresse de dépôt", + "STORE_NEW_DELIVERY_DROPOFF_DESCRIPTION": "Remplissez les informations de livraison ainsi que la personne à contacter", "STORE_NEW_DELIVERY": "Nouvelle livraison", "STORE_NEW_DELIVERY_ADDRESS": "Adresse", "STORE_NEW_DELIVERY_SEARCH_CLIENT": "Rechercher un client", diff --git a/src/navigation/store/NewDeliveryAddress.js b/src/navigation/store/NewDeliveryAddress.js index adc65e264..203888751 100644 --- a/src/navigation/store/NewDeliveryAddress.js +++ b/src/navigation/store/NewDeliveryAddress.js @@ -180,10 +180,12 @@ function NewDeliveryAddress(props) { color={backgroundColor} stroke={10} /> - Drop off informations + + {t('STORE_NEW_DELIVERY_DROPOFF_TITLE')} + - Fill in the information about the drop off location and contact + {t('STORE_NEW_DELIVERY_DROPOFF_DESCRIPTION')} diff --git a/src/navigation/store/NewDeliveryPickup.js b/src/navigation/store/NewDeliveryPickup.js index f43264f5a..e065861ff 100644 --- a/src/navigation/store/NewDeliveryPickup.js +++ b/src/navigation/store/NewDeliveryPickup.js @@ -183,15 +183,18 @@ function NewDeliveryPickup(props) { color={backgroundColor} stroke={10} /> - Pick up informations + + {t('STORE_NEW_DELIVERY_PICKUP_TITLE')} + - Fill in the information about the pick up location or keep the - default values. + {t('STORE_NEW_DELIVERY_PICKUP_DESCRIPTION')} - Default pick up address + + {t('STORE_NEW_DELIVERY_PICKUP_DEFAULT_ADDRESS')} + {store.address.name} {store.address.streetAddress} {store.address.telephone} @@ -200,7 +203,7 @@ function NewDeliveryPickup(props) { setCustomAddress(!customAddress)}> - Use custom pickup address + {t('STORE_NEW_DELIVERY_PICKUP_USE_CUSTOM_ADDRESS')} From 49f0e805b42e96365cb95d02df57cb357bcc4364 Mon Sep 17 00:00:00 2001 From: Pierre LHOSTE Date: Tue, 12 Nov 2024 11:40:36 +0100 Subject: [PATCH 6/9] Refactor containerStyle and restructure pickup address --- src/navigation/store/NewDeliveryAddress.js | 10 +++----- src/navigation/store/NewDeliveryPickup.js | 30 ++++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/navigation/store/NewDeliveryAddress.js b/src/navigation/store/NewDeliveryAddress.js index 203888751..4765023fc 100644 --- a/src/navigation/store/NewDeliveryAddress.js +++ b/src/navigation/store/NewDeliveryAddress.js @@ -220,12 +220,10 @@ function NewDeliveryAddress(props) { onBlur={handleBlur('address')} value={address} onSelectAddress={e => onSelectAddress(e, setFieldValue)} - containerStyle={[ - { - flex: 1, - justifyContent: 'center', - }, - ]} + containerStyle={{ + flex: 1, + justifyContent: 'center', + }} style={{ borderRadius: 0, padding: 10, diff --git a/src/navigation/store/NewDeliveryPickup.js b/src/navigation/store/NewDeliveryPickup.js index e065861ff..fe37bf940 100644 --- a/src/navigation/store/NewDeliveryPickup.js +++ b/src/navigation/store/NewDeliveryPickup.js @@ -143,17 +143,21 @@ function NewDeliveryPickup(props) { function submit(values) { const pickup = customAddress ? { - telephone: parsePhoneNumberFromString( - values.telephone, - country, - ).format('E.164'), - contactName: values.contactName, - description: values.description, - businessName: values.businessName, - address, + address: { + ...address, + contactName: values.contactName, + description: values.description, + name: values.businessName, + telephone: parsePhoneNumberFromString( + values.telephone, + country, + ).format('E.164'), + }, } : undefined; + console.log(pickup); + navigation.navigate('StoreNewDeliveryAddress', { pickup }); } @@ -239,12 +243,10 @@ function NewDeliveryPickup(props) { onBlur={handleBlur('address')} value={address} onSelectAddress={e => onSelectAddress(e, setFieldValue)} - containerStyle={[ - { - flex: 1, - justifyContent: 'center', - }, - ]} + containerStyle={{ + flex: 1, + justifyContent: 'center', + }} style={{ borderRadius: 0, padding: 10, From c1bc5fb8d96e03041309f330ec0d20dd19ab09a3 Mon Sep 17 00:00:00 2001 From: Pierre LHOSTE Date: Tue, 12 Nov 2024 16:07:55 +0100 Subject: [PATCH 7/9] Add package page title and description and fix styles --- src/i18n/locales/en.json | 2 ++ src/i18n/locales/fr.json | 2 ++ src/navigation/store/NewDeliveryAddress.js | 8 +++---- src/navigation/store/NewDeliveryForm.js | 26 +++++++++++++++++++++- src/navigation/store/NewDeliveryPickup.js | 8 +++---- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f11215863..0fc6f54e7 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -294,6 +294,8 @@ "STORE_NEW_DELIVERY_PICKUP_USE_CUSTOM_ADDRESS": "Use a custom address", "STORE_NEW_DELIVERY_DROPOFF_TITLE": "Dropoff address", "STORE_NEW_DELIVERY_DROPOFF_DESCRIPTION": "Fill in the information about the dropoff address and the contact details", + "STORE_NEW_DELIVERY_PACKAGES_TITLE": "Time slot and packages", + "STORE_NEW_DELIVERY_PACKAGES_DESCRIPTION": "Select a time slot, the weight of the package if necessary and the packages to deliver", "STORE_NEW_DELIVERY": "New delivery", "STORE_NEW_DELIVERY_ADDRESS": "Address", "STORE_NEW_DELIVERY_SEARCH_CLIENT": "Search for a client", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 6960b78ce..34bb5fe94 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -280,6 +280,8 @@ "STORE_NEW_DELIVERY_PICKUP_USE_CUSTOM_ADDRESS": "Utiliser une adresse personnalisée", "STORE_NEW_DELIVERY_DROPOFF_TITLE": "Adresse de dépôt", "STORE_NEW_DELIVERY_DROPOFF_DESCRIPTION": "Remplissez les informations de livraison ainsi que la personne à contacter", + "STORE_NEW_DELIVERY_PACKAGES_TITLE": "Tranche horaire et paquets", + "STORE_NEW_DELIVERY_PACKAGES_DESCRIPTION": "Sélectionnez une tranche horaire a respecter, le poids du colis si nécessaire et les paquets à livrer", "STORE_NEW_DELIVERY": "Nouvelle livraison", "STORE_NEW_DELIVERY_ADDRESS": "Adresse", "STORE_NEW_DELIVERY_SEARCH_CLIENT": "Rechercher un client", diff --git a/src/navigation/store/NewDeliveryAddress.js b/src/navigation/store/NewDeliveryAddress.js index 4765023fc..7302945c1 100644 --- a/src/navigation/store/NewDeliveryAddress.js +++ b/src/navigation/store/NewDeliveryAddress.js @@ -173,16 +173,14 @@ function NewDeliveryAddress(props) { }) => ( - + - - {t('STORE_NEW_DELIVERY_DROPOFF_TITLE')} - + {t('STORE_NEW_DELIVERY_DROPOFF_TITLE')} {t('STORE_NEW_DELIVERY_DROPOFF_DESCRIPTION')} @@ -362,7 +360,7 @@ const styles = StyleSheet.create({ header: { display: 'flex', flexDirection: 'row', - alignContent: 'center', + alignItems: 'center', gap: 5, }, formGroup: { diff --git a/src/navigation/store/NewDeliveryForm.js b/src/navigation/store/NewDeliveryForm.js index 9dc9c1fd4..f6ff25dfe 100644 --- a/src/navigation/store/NewDeliveryForm.js +++ b/src/navigation/store/NewDeliveryForm.js @@ -14,6 +14,7 @@ import KeyboardManager from 'react-native-keyboard-manager'; import DateTimePickerModal from 'react-native-modal-datetime-picker'; import { connect, useDispatch } from 'react-redux'; +import { IconPackage } from '@tabler/icons-react-native'; import { loadPackages, loadTimeSlot, @@ -21,7 +22,10 @@ import { loadTimeSlots, } from '../../redux/Store/actions'; import { selectStore, selectTimeSlots } from '../../redux/Store/selectors'; -import { useBackgroundContainerColor } from '../../styles/theme'; +import { + useBackgroundContainerColor, + usePrimaryColor, +} from '../../styles/theme'; import Range from '../checkout/ProductDetails/Range'; import ModalFormWrapper from './ModalFormWrapper'; import FormInput from './components/FormInput'; @@ -31,6 +35,7 @@ function DeliveryForm(props) { const [isDateTimePickerVisible, setIsDateTimePickerVisible] = useState(false); const [selectedTimeSlot, setSelectedTimeSlot] = useState(''); const backgroundColor = useBackgroundContainerColor(); + const primaryColor = usePrimaryColor(); const [selectedChoice, setSelectedChoice] = React.useState(null); const [packagesCount, setPackagesCount] = useState([]); const dispatch = useDispatch(); @@ -276,6 +281,19 @@ function DeliveryForm(props) { setFieldTouched, }) => ( + + + + {t('STORE_NEW_DELIVERY_PACKAGES_TITLE')} + + + {t('STORE_NEW_DELIVERY_PACKAGES_DESCRIPTION')} + + {hasTimeSlot ? ( ( - + - - {t('STORE_NEW_DELIVERY_PICKUP_TITLE')} - + {t('STORE_NEW_DELIVERY_PICKUP_TITLE')} {t('STORE_NEW_DELIVERY_PICKUP_DESCRIPTION')} @@ -389,7 +387,7 @@ const styles = StyleSheet.create({ header: { display: 'flex', flexDirection: 'row', - alignContent: 'center', + alignItems: 'center', gap: 5, }, formGroup: { From 9e010bd0096d1ecc2d1e24de1e234d8f2fbf4d82 Mon Sep 17 00:00:00 2001 From: Pierre LHOSTE Date: Wed, 20 Nov 2024 18:14:06 +0100 Subject: [PATCH 8/9] Refactor NewDeliveryPickup and NewDeliveryPrice components to use hooks --- src/navigation/store/NewDeliveryPickup.js | 81 ++++++++++------------- src/navigation/store/NewDeliveryPrice.js | 34 +++++----- 2 files changed, 51 insertions(+), 64 deletions(-) diff --git a/src/navigation/store/NewDeliveryPickup.js b/src/navigation/store/NewDeliveryPickup.js index 280bc2b69..57081fdec 100644 --- a/src/navigation/store/NewDeliveryPickup.js +++ b/src/navigation/store/NewDeliveryPickup.js @@ -4,9 +4,9 @@ import { AsYouType, parsePhoneNumberFromString } from 'libphonenumber-js'; import _ from 'lodash'; import { Checkbox, Text } from 'native-base'; import React, { useState } from 'react'; -import { withTranslation } from 'react-i18next'; +import { useTranslation, withTranslation } from 'react-i18next'; import { Platform, StyleSheet, View } from 'react-native'; -import { connect } from 'react-redux'; +import { connect, useDispatch, useSelector } from 'react-redux'; import AddressAutocomplete from '../../components/AddressAutocomplete'; import { assertDelivery } from '../../redux/Store/actions'; import { selectStore } from '../../redux/Store/selectors'; @@ -19,23 +19,24 @@ import ModalFormWrapper from './ModalFormWrapper'; import ClientListInput from './components/ClientListInput'; import FormInput from './components/FormInput'; -function NewDeliveryPickup(props) { +function NewDeliveryPickup({ navigation }) { const [validAddress, setValidAddress] = useState(false); const [address, setAddress] = useState(null); + const [customAddress, setCustomAddress] = useState(false); + const backgroundColor = useBackgroundContainerColor(); const backgroundHighlightColor = useBackgroundHighlightColor(); const primaryColor = usePrimaryColor(); - const [customAddress, setCustomAddress] = useState(false); - const { - store, - deliveryError, - addresses, - assertDelivery, - t, - navigation, - country, - } = props; + const { t } = useTranslation(); + const dispatch = useDispatch(); + + const country = useSelector(state => + state.app.settings.country.toUpperCase(), + ); + const store = useSelector(selectStore); + const deliveryError = useSelector(state => state.store.assertDeliveryError); + const addresses = useSelector(state => state.store.addresses); const inputStyles = { backgroundColor, @@ -43,15 +44,10 @@ function NewDeliveryPickup(props) { }; function setAddressData(data, setFieldValue) { - const contactName = data.contactName || ''; - const telephone = data.telephone || ''; - const businessName = data.businessName || ''; - const description = data.description || ''; - - setFieldValue('contactName', contactName); - setFieldValue('telephone', telephone); - setFieldValue('businessName', businessName); - setFieldValue('description', description); + setFieldValue('contactName', data.contactName || ''); + setFieldValue('telephone', data.telephone || ''); + setFieldValue('businessName', data.businessName || ''); + setFieldValue('description', data.description || ''); setAddress({ streetAddress: data.streetAddress, geo: data.geo, @@ -73,9 +69,7 @@ function NewDeliveryPickup(props) { }, }; - assertDelivery(delivery, () => { - setValidAddress(true); - }); + dispatch(assertDelivery(delivery, () => setValidAddress(true))); } let autocompleteProps = { @@ -99,29 +93,28 @@ function NewDeliveryPickup(props) { } function validate(values) { - console.log(customAddress); - - if (!customAddress) return {}; const errors = {}; - if (_.isEmpty(values.telephone)) { - errors.telephone = t('STORE_NEW_DELIVERY_ERROR.EMPTY_PHONE_NUMBER'); - } else { - const phoneNumber = parsePhoneNumberFromString( - _.trim(values.telephone), - country, - ); - if (!phoneNumber || !phoneNumber.isValid()) { - errors.telephone = t('INVALID_PHONE_NUMBER'); + if (customAddress) { + if (_.isEmpty(values.telephone)) { + errors.telephone = t('STORE_NEW_DELIVERY_ERROR.EMPTY_PHONE_NUMBER'); + } else { + const phoneNumber = parsePhoneNumberFromString( + _.trim(values.telephone), + country, + ); + if (!phoneNumber || !phoneNumber.isValid()) { + errors.telephone = t('INVALID_PHONE_NUMBER'); + } } - } - if (_.isEmpty(values.contactName)) { - errors.contactName = t('STORE_NEW_DELIVERY_ERROR.EMPTY_CONTACT_NAME'); - } + if (_.isEmpty(values.contactName)) { + errors.contactName = t('STORE_NEW_DELIVERY_ERROR.EMPTY_CONTACT_NAME'); + } - if (!validAddress) { - errors.address = t('STORE_NEW_DELIVERY_ADDRESS_HELP'); + if (!validAddress) { + errors.address = t('STORE_NEW_DELIVERY_ADDRESS_HELP'); + } } return errors; @@ -156,8 +149,6 @@ function NewDeliveryPickup(props) { } : undefined; - console.log(pickup); - navigation.navigate('StoreNewDeliveryAddress', { pickup }); } diff --git a/src/navigation/store/NewDeliveryPrice.js b/src/navigation/store/NewDeliveryPrice.js index 070793f64..71363f6f2 100644 --- a/src/navigation/store/NewDeliveryPrice.js +++ b/src/navigation/store/NewDeliveryPrice.js @@ -1,24 +1,28 @@ import { Formik } from 'formik'; import { Text, View } from 'native-base'; -import { withTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next'; import { StyleSheet } from 'react-native'; -import { connect, useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { createDelivery, getPrice } from '../../redux/Store/actions'; -import ModalFormWrapper from './ModalFormWrapper'; import FormInput from './components/FormInput'; +import ModalFormWrapper from './ModalFormWrapper'; -function NewDeliveryPrice(props) { +function NewDeliveryPrice({ route, navigation }) { const dispatch = useDispatch(); - const delivery = props.route.params?.delivery; - const { t } = props; + const { t } = useTranslation(); + const delivery = route.params?.delivery; + + const price = useSelector(state => state.store.price); + const priceExcludingTax = useSelector(state => state.store.priceExcludingTax); + + if (!delivery) return null; - if (!delivery) return; dispatch(getPrice(delivery)); function submit(values) { dispatch( createDelivery(values, () => { - props.navigation.navigate('StoreHome'); + navigation.navigate('StoreHome'); }), ); } @@ -33,11 +37,11 @@ function NewDeliveryPrice(props) { {t('PRICE_EXCLUDING_TAX')} - + {t('PRICE_TOTAL')} - + )} @@ -55,12 +59,4 @@ const styles = StyleSheet.create({ }, }); -function mapStateToProps(state) { - const price = state.store.price; - const priceExcludingTax = state.store.priceExcludingTax; - return { - price, - priceExcludingTax, - }; -} -export default connect(mapStateToProps)(withTranslation()(NewDeliveryPrice)); +export default NewDeliveryPrice; From 98d4bac821e7783a5b5828838797f7ff5078c3c7 Mon Sep 17 00:00:00 2001 From: Pierre LHOSTE Date: Thu, 21 Nov 2024 14:23:36 +0100 Subject: [PATCH 9/9] Prefill business name --- src/navigation/store/NewDeliveryAddress.js | 2 +- src/navigation/store/NewDeliveryPickup.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/navigation/store/NewDeliveryAddress.js b/src/navigation/store/NewDeliveryAddress.js index 7302945c1..2c378c14b 100644 --- a/src/navigation/store/NewDeliveryAddress.js +++ b/src/navigation/store/NewDeliveryAddress.js @@ -45,7 +45,7 @@ function NewDeliveryAddress(props) { function setAddressData(data, setFieldValue) { const contactName = data.contactName || ''; const telephone = data.telephone || ''; - const businessName = data.businessName || ''; + const businessName = data.name || ''; const description = data.description || ''; setFieldValue('contactName', contactName); diff --git a/src/navigation/store/NewDeliveryPickup.js b/src/navigation/store/NewDeliveryPickup.js index 57081fdec..e3fa04d2a 100644 --- a/src/navigation/store/NewDeliveryPickup.js +++ b/src/navigation/store/NewDeliveryPickup.js @@ -46,7 +46,7 @@ function NewDeliveryPickup({ navigation }) { function setAddressData(data, setFieldValue) { setFieldValue('contactName', data.contactName || ''); setFieldValue('telephone', data.telephone || ''); - setFieldValue('businessName', data.businessName || ''); + setFieldValue('businessName', data.name || ''); setFieldValue('description', data.description || ''); setAddress({ streetAddress: data.streetAddress,