SearchForm

PHOTO EMBED

Thu May 27 2021 15:30:56 GMT+0000 (Coordinated Universal Time)

Saved by @georeyff #react.js

import React from 'react'
import { MenuItem, IconButton, Button as MuiButton, Switch, RadioGroup, Radio, FormControlLabel,
	Typography, Paper, TextField, Collapse, Card, makeStyles, InputLabel, InputAdornment } from '@material-ui/core'
import Swap from 'components/Icons/Swap'
import FavoriteLoader from './FavoriteLoader.jsx'
import PassengersCount from './PassengersCount.jsx'
import Autocomplete from 'containers/Autocomplete/Autocomplete.js'
import CustomDayPickerInput from 'containers/CustomDayPickerInput/CustomDayPickerInput.jsx'
import styles from './form.cssmodule.scss'
import api from 'api'
import moment from 'moment'
import DropOff from 'components/Icons/DropOff/DropOff.jsx'
import PickUp from 'components/Icons/PickUp/PickUp.jsx'
import { FormattedMessage } from 'react-intl'
import LastBookings from './LastBookings'
import FormSectionHeader from './FormSectionHeader'
import { get } from 'lodash'
import {extrasToGenericSeats} from 'lib/genericSeatsFunctions'
import {KeyboardTimePicker as TimePicker} from '@material-ui/pickers'
import Clock from 'components/Icons/Clock/Clock.jsx'
import ExpandMore from '../Icons/ExpandMore.js'


const useStyles = makeStyles((theme) => ({
	input: {
		background: '#cee6f3',
		color: '#0869af',
		// fontSize:'18px'
		borderRadius:'10px',
		padding: '0.5rem',
	}
}))


export default function SearchForm(props){
	const classes = useStyles()

	const { formData, errors, onAddressSelected, onFieldChange, loadFavorite, intl,
		territory, increasePassengers, decreasePassengers, onSubmit, swapActive, swapAddresses,
		actualizeDeparture, onDayClick, customFieldInfos, requesting } = props
	const { departure, destination, time, timeRestrictionType,
		passengers, asap, recurrence, selectedDays, customFields} = formData

	const recurringOffers = _.get(props, 'territory.booking.recurring_bookings.offers') || []

	const extras = _.get(territory,'extras',{}) // special seats
	const extrasTypes = extrasToGenericSeats(extras)

	const nbPassengers = _.reduce(extrasTypes, (red, type) => red + _.get(passengers, type, 0), passengers.standard)

	return (
		<div className={styles.form}>
			<Paper>
				<form
					className={'row'}
					style={{justifyContent:'space-around'}}
				>
					<div className="column padded" style={{ flex: '0 0 45%' }}>
						<div className='column' style={{justifyContent:'flex-start'}}>
							<div className={`row-only ${styles.autoCompletes}`}>
								<div className="column" style={{ width: '100%' }}>
									<div className="row-only" style={{margin:'0.5rem 0',alignItems: 'flex-end'}}>
										<PickUp width={24} height={24} style={{marginTop:'0.5rem', padding:'0.5rem', backgroundColor:'#f1f0f0', borderRadius:'5px 0 0 5px'}}/>
										<Autocomplete
											context="search"
											inputProps={{
												label:intl.formatMessage({id: 'search.pickup.placeholder'}),
												helperText:errors.departure,
												text:departure.display,
											}}
											onChange={ e => {
												onFieldChange('departure', {
													display: e.target.value
												})
											}}
											onSelect={p =>
												onAddressSelected(p.suggestion, p.nodeId, 'departure', p.latitude, p.longitude, p.address)
											}
										/>
									</div>
									<div className="row-only" style={{margin:'0.5rem 0',alignItems: 'flex-end'}}>
										<DropOff width={24} height={24}  style={{marginTop:'0.5rem', padding:'0.5rem', backgroundColor:'#f1f0f0', borderRadius:'5px 0 0 5px'}}/>
										<Autocomplete
											context="search"
											inputProps={{
												label:intl.formatMessage({id: 'search.dropoff.placeholder'}),
												helperText:errors.destination,
												text:destination.display
											}}
											onChange={ e => {
												onFieldChange('destination', {
													display: e.target.value
												})
											}}
											onSelect={p =>
												onAddressSelected(p.suggestion, p.nodeId, 'destination', p.latitude, p.longitude, p.address)
											}
										/>
									</div>
								</div>
								<div className={`column ${styles.swapIcon}`}>
									{swapActive &&
								<IconButton
									title={intl.formatMessage({ id: 'help.swap' })}
									onClick={swapAddresses}
								>
									<Swap
										color="primary"
										style={{width:'1.5rem', height:'1.5rem'}}
									/>
								</IconButton>
									}
								</div>

							</div>
							<FavoriteLoader
								intl={intl}
								loadFavorite={loadFavorite}
							/>

							<LastBookings />
						</div>
					</div>

					<div className="column aligned padded"
						style={{ flex: '0 0 45%'}}>
						<div // big vertical container so that the BOOK button is on bottom
							className="column aligned fullWidth"
							style={{flexGrow:1, justifyContent:'space-around'}}>

							<Card elevation={4} style={{width:'100%', padding:'1rem'}}>
								<div className="column aligned fullWidth" /* HOUR + DATE + RECURRENCE */>
									<FormSectionHeader title={intl.formatMessage({ id:'section.time'})}/>
									<RadioGroup aria-label={intl.formatMessage({id:'search.time'})}
									// MAYBE REFACTOR INTELLIGENCE ?
										row
										className="fullWidth"
										style={{paddingLeft:'1rem'}} 
										value={asap ? 'asap' : timeRestrictionType}
										onChange={(e)=>{
											if (e.target.value === 'asap'){
												actualizeDeparture()
											}
											else {
												onFieldChange('asap', false)
												onFieldChange('timeRestrictionType', e.target.value)
											}
										}}>
										<FormControlLabel value="asap"
											control={<Radio color="primary" id="asap"/>}
											label={intl.formatMessage({ id: 'search.departureNow' })}
										/>
										<FormControlLabel value="departure"
											control={<Radio color="primary" id="departure"/>}
											label={intl.formatMessage({id: 'search.departure_time'})}
										/>
										<FormControlLabel value="arrival"
											control={<Radio color="primary" id="arrival"/>}
											label={intl.formatMessage({id: 'search.arrival_time'})}
										/>
									</RadioGroup>
									<Collapse in={!asap} style={{width:'100%'}} /* HOUR + DATE + RECURRENCE */>
										<div // HOUR
											className={styles.selectTime}>
											<TimePicker
												value={window.Cypress ? 1582949766 : time}
												onChange={newTime=>{
													if (newTime && newTime.isValid()){
														onFieldChange('time',newTime.format())
													}
												}}
												autoOk
												ampm={false}
												disabled={asap}
												inputVariant="outlined"
												maskChar=" "
												// below is because the modal is not keyboard accessible
												KeyboardButtonProps={{tabIndex:-1}}
												keyboardIcon={<Clock color="grey"/>}
												invalidDateMessage={<Typography color="error">"Invalid date"</Typography>}
												invalidLabel="false"
												helperText={errors && errors.time}
												error={Boolean(errors.time)}
												label={intl.formatMessage({id:'search.time'})}
												id="time"
												className={styles.timePicker}
												required
											/>

											{ /**  recurrence enabled but "do not repeat" => classic calendar here */
												recurringOffers && recurringOffers.length > 0 && recurrence.id === 0 &&
											<CustomDayPickerInput
												disabled={asap}
												tabIndex={0}
												selectedDays={(!asap ? selectedDays : [time]) || []}
												onDayClick={(day, r) => onDayClick(moment(day).format(), r)}
												errorText={errors.selectedDays}
												maxBookingDay={_.get(territory, 'booking.max_booking_day')}
												name="date"
												maxBookableDates={
													_.get(territory, 'booking.multi_date.max_bookable_dates')
												}
												label={intl.formatMessage({
													id: _.get(territory, 'booking.multi_date.enabled')
														&& _.get(territory, 'booking.multi_date.max_bookable_dates',0) > 1
														? 'search.the'
														: 'search.monoDate'
												})}
											/>
											}
										

											{	/*recurring two fields (start & end) */
												recurringOffers && recurringOffers.length > 0 && recurrence.id !== 0 &&
												<div className="column">
													<CustomDayPickerInput
														name="startDate"
														disabled={asap}
														// tabIndex={asap ? -1 : 12}
														selectedDays={[recurrence.start_datetime]}
														onDayClick={(day, requestRide) => onFieldChange('recurrence', {
															...recurrence,
															start_datetime: moment(day).format()
														})}
														errorText={errors.selectedDays}
														label={intl.formatMessage({
															id: 'search.recurrence.start_date'
														})}
													/>
													<CustomDayPickerInput
														name="endDate"
														disabled={asap}
														selectedDays={[recurrence.end_datetime]}
														onDayClick={(day, requestRide) => onFieldChange('recurrence', {
															...recurrence,
															end_datetime: moment(day).format()
														})}
														// tabIndex={asap ? -1 : 12}
														errorText={errors.selectedDays}
														label={intl.formatMessage({
															id: 'search.recurrence.end_date'
														})}
													/>
												</div>
											}
										</div>
										
										{ // either calendar OR recurence-offer-choice here
											!recurringOffers || recurringOffers.length === 0
												// CALENDAR
												?
												<CustomDayPickerInput
													disabled={asap}
													tabIndex={0}
													selectedDays={(!asap ? selectedDays : [time]) || []}
													onDayClick={(day, r) => onDayClick(moment(day).format(), r)}
													errorText={errors.selectedDays}
													maxBookingDay={_.get(territory, 'booking.max_booking_day')}
													name="date"
													maxBookableDates={
														_.get(territory, 'booking.multi_date.max_bookable_dates')
													}
													label={intl.formatMessage({
														id: _.get(territory, 'booking.multi_date.enabled')
														&& _.get(territory, 'booking.multi_date.max_bookable_dates',0) > 1
															? 'search.the'
															: 'search.monoDate'
													})}
												/>
												:
												<div style={{display: 'flex', alignItems:'center'}}>
													<label 
														htmlFor={'recurrence-offer-select'} 
														variant='body' style={{fontSize:'18px', marginRight:'0.5rem'}}
													>
														{intl.formatMessage({id:'help.recurrenceOffer'})}
													</label>
													<TextField  // RECURRENCE OFFER
														required
														id="recurrence-offer-select"
														select
														name="recurrenceOffer"
														className={`column ${styles.selectTargetMode}`}
														value={recurrence.id}
														style={asap ? { color: '#ccc' } : null}
														disabled={asap}
														onChange={(e) => {
															onFieldChange('recurrence', {
																...recurrence,
																id: e.target.value
															})
														}}
														// style={{minWidth:'18rem'}}
														InputProps={{ 
															className: classes.input,
															disableUnderline: true,
														}}
														SelectProps={{ 
															IconComponent: () => <ExpandMore style={{position:'absolute', right: 10}}/>
														}}
													>
														<MenuItem value={0}>
															{intl.formatMessage({
																id: 'search.recurrence.doNotRepeat',
															})}
														</MenuItem>
														{/* map on [{id: "work_days", display_name:"Work days"}]}
												 	OR territory.recurringOffers. */}
														{recurringOffers.map((offer, index) =>
															(<MenuItem key={index} value={offer.id}>
																{offer.name}
															</MenuItem>)
														)}
													</TextField>
												</div>
										}
									</Collapse>
								</div>
							</Card>

							<Card elevation={4} style={{width:'100%', padding:'1rem', margin:'1rem'}}>
								<div className="column aligned fullWidth" /* OPTIONS : PASSENGERS + CUSTOM FIELDS */>
									<FormSectionHeader title={intl.formatMessage({ id:'section.options'})}/>
									<div className='fullWidth'>
										<PassengersCount
											type="standard"
											decrease={() => decreasePassengers('standard')}
											increase={() => increasePassengers('standard')}
											value={passengers.standard}
										/>
										{ extrasTypes.map( type =>{
											return(
												<PassengersCount
													extras={extras}
													key={type + '_counter'}
													type={type}
													decrease={() => decreasePassengers(type)}
													increase={() => increasePassengers(type)}
													value={passengers[type]}
												/>
											)}
										)}
									</div>
								</div>
								{ nbPassengers == 0 ?
									<Typography 
										color="error"
										variant="caption"
									>
										{intl.formatMessage({ id: 'search.error.nbPassengers'})}
									</Typography>
									: null 
                    		}
								{ /* customFields is a dict. This works similarly to "recurrence" */
									customFields &&
								<div className="column spaced" style={{flexWrap:'wrap'}}>
									{_.map(_.keys(customFieldInfos).sort(), (cf_key, name)=>{
										const cf = customFieldInfos[cf_key]
										return(
											<div
												className="ron-only column"
												style={{padding:'1rem 0 0 0'}}
												key={'cf-'+name}
											>
												<TextField
													id={cf.display_name}
													required={Boolean(cf.is_required)}
													label={cf.display_name}
													style={{width: '90%'}}
													select={cf.type === 'select'}
													value={_.get(formData, 'customFields.'+cf_key) || ''}
													onChange={e => onFieldChange(
														'customFields',{
															...customFields,
															[cf_key]:e.target.value
														}
													)}
													error={Boolean(get(errors,'customFields.'+cf_key))}
													helperText={get(errors,'customFields.'+cf_key)}
													variant="outlined"
													SelectProps={{ 
														IconComponent: () => <ExpandMore style={{position:'absolute', right: 10}}/>
													}}
												>
													{_.map(_.get(cf, 'options', []), (m, index) =>
														(<MenuItem key={index} value={m.key}>
															{m.display}
														</MenuItem>)
													)}
												</TextField>
											</div>
										)})
									}
								</div>
								}
							</Card>
						</div>
					</div>

				</form>
			</Paper>
			<div className="row"
				style={{ paddingTop:'1rem', flexDirection: 'row-reverse' }}>
				<MuiButton
					variant="contained"
					color="primary"
					disabled={requesting}
					onClick={()=>onSubmit()}
				>
					<Typography variant="body1">
						{intl.formatMessage({ id: 'search.submit' })}
					</Typography>
				</MuiButton>
			</div>
		</div>
	)
}
content_copyCOPY