mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
235 lines
7.8 KiB
JavaScript
235 lines
7.8 KiB
JavaScript
import React, { Component } from 'react';
|
|
import { string, bool, arrayOf } from 'prop-types';
|
|
import { compose } from 'redux';
|
|
import { Form as FinalForm } from 'react-final-form';
|
|
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
|
|
import classNames from 'classnames';
|
|
import moment from 'moment';
|
|
import { required, bookingDatesRequired, composeValidators } from '../../util/validators';
|
|
import { START_DATE, END_DATE } from '../../util/dates';
|
|
import { propTypes } from '../../util/types';
|
|
import config from '../../config';
|
|
import { Form, PrimaryButton, FieldDateRangeInput } from '../../components';
|
|
import EstimatedBreakdownMaybe from './EstimatedBreakdownMaybe';
|
|
|
|
import css from './BookingDatesForm.css';
|
|
|
|
const identity = v => v;
|
|
|
|
export class BookingDatesFormComponent extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = { focusedInput: null };
|
|
this.handleFormSubmit = this.handleFormSubmit.bind(this);
|
|
this.onFocusedInputChange = this.onFocusedInputChange.bind(this);
|
|
}
|
|
|
|
// Function that can be passed to nested components
|
|
// so that they can notify this component when the
|
|
// focused input changes.
|
|
onFocusedInputChange(focusedInput) {
|
|
this.setState({ focusedInput });
|
|
}
|
|
|
|
// In case start or end date for the booking is missing
|
|
// focus on that input, otherwise continue with the
|
|
// default handleSubmit function.
|
|
handleFormSubmit(e) {
|
|
const { startDate, endDate } = e.bookingDates || {};
|
|
if (!startDate) {
|
|
e.preventDefault();
|
|
this.setState({ focusedInput: START_DATE });
|
|
} else if (!endDate) {
|
|
e.preventDefault();
|
|
this.setState({ focusedInput: END_DATE });
|
|
} else {
|
|
this.props.onSubmit(e);
|
|
}
|
|
}
|
|
|
|
render() {
|
|
const { rootClassName, className, price: unitPrice, ...rest } = this.props;
|
|
const classes = classNames(rootClassName || css.root, className);
|
|
|
|
if (!unitPrice) {
|
|
return (
|
|
<div className={classes}>
|
|
<p className={css.error}>
|
|
<FormattedMessage id="BookingDatesForm.listingPriceMissing" />
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
if (unitPrice.currency !== config.currency) {
|
|
return (
|
|
<div className={classes}>
|
|
<p className={css.error}>
|
|
<FormattedMessage id="BookingDatesForm.listingCurrencyInvalid" />
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<FinalForm
|
|
{...rest}
|
|
unitPrice={unitPrice}
|
|
onSubmit={this.handleFormSubmit}
|
|
render={fieldRenderProps => {
|
|
const {
|
|
endDatePlaceholder,
|
|
startDatePlaceholder,
|
|
formId,
|
|
handleSubmit,
|
|
intl,
|
|
isOwnListing,
|
|
submitButtonWrapperClassName,
|
|
unitPrice,
|
|
unitType,
|
|
values,
|
|
timeSlots,
|
|
fetchTimeSlotsError,
|
|
} = fieldRenderProps;
|
|
const { startDate, endDate } = values && values.bookingDates ? values.bookingDates : {};
|
|
|
|
const bookingStartLabel = intl.formatMessage({
|
|
id: 'BookingDatesForm.bookingStartTitle',
|
|
});
|
|
const bookingEndLabel = intl.formatMessage({ id: 'BookingDatesForm.bookingEndTitle' });
|
|
const requiredMessage = intl.formatMessage({ id: 'BookingDatesForm.requiredDate' });
|
|
const startDateErrorMessage = intl.formatMessage({
|
|
id: 'FieldDateRangeInput.invalidStartDate',
|
|
});
|
|
const endDateErrorMessage = intl.formatMessage({
|
|
id: 'FieldDateRangeInput.invalidEndDate',
|
|
});
|
|
const timeSlotsError = fetchTimeSlotsError ? (
|
|
<p className={css.timeSlotsError}>
|
|
<FormattedMessage id="BookingDatesForm.timeSlotsError" />
|
|
</p>
|
|
) : null;
|
|
|
|
// This is the place to collect breakdown estimation data. See the
|
|
// EstimatedBreakdownMaybe component to change the calculations
|
|
// for customized payment processes.
|
|
const bookingData =
|
|
startDate && endDate
|
|
? {
|
|
unitType,
|
|
unitPrice,
|
|
startDate,
|
|
endDate,
|
|
|
|
// NOTE: If unitType is `line-item/units`, a new picker
|
|
// for the quantity should be added to the form.
|
|
quantity: 1,
|
|
}
|
|
: null;
|
|
const bookingInfo = bookingData ? (
|
|
<div className={css.priceBreakdownContainer}>
|
|
<h3 className={css.priceBreakdownTitle}>
|
|
<FormattedMessage id="BookingDatesForm.priceBreakdownTitle" />
|
|
</h3>
|
|
<EstimatedBreakdownMaybe bookingData={bookingData} />
|
|
</div>
|
|
) : null;
|
|
|
|
const dateFormatOptions = {
|
|
weekday: 'short',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
};
|
|
|
|
const now = moment();
|
|
const today = now.startOf('day').toDate();
|
|
const tomorrow = now
|
|
.startOf('day')
|
|
.add(1, 'days')
|
|
.toDate();
|
|
const startDatePlaceholderText =
|
|
startDatePlaceholder || intl.formatDate(today, dateFormatOptions);
|
|
const endDatePlaceholderText =
|
|
endDatePlaceholder || intl.formatDate(tomorrow, dateFormatOptions);
|
|
const submitButtonClasses = classNames(
|
|
submitButtonWrapperClassName || css.submitButtonWrapper
|
|
);
|
|
|
|
return (
|
|
<Form onSubmit={handleSubmit} className={classes}>
|
|
{timeSlotsError}
|
|
<FieldDateRangeInput
|
|
className={css.bookingDates}
|
|
name="bookingDates"
|
|
unitType={unitType}
|
|
startDateId={`${formId}.bookingStartDate`}
|
|
startDateLabel={bookingStartLabel}
|
|
startDatePlaceholderText={startDatePlaceholderText}
|
|
endDateId={`${formId}.bookingEndDate`}
|
|
endDateLabel={bookingEndLabel}
|
|
endDatePlaceholderText={endDatePlaceholderText}
|
|
focusedInput={this.state.focusedInput}
|
|
onFocusedInputChange={this.onFocusedInputChange}
|
|
format={identity}
|
|
timeSlots={timeSlots}
|
|
useMobileMargins
|
|
validate={composeValidators(
|
|
required(requiredMessage),
|
|
bookingDatesRequired(startDateErrorMessage, endDateErrorMessage)
|
|
)}
|
|
/>
|
|
{bookingInfo}
|
|
<p className={css.smallPrint}>
|
|
<FormattedMessage
|
|
id={
|
|
isOwnListing
|
|
? 'BookingDatesForm.ownListing'
|
|
: 'BookingDatesForm.youWontBeChargedInfo'
|
|
}
|
|
/>
|
|
</p>
|
|
<div className={submitButtonClasses}>
|
|
<PrimaryButton type="submit">
|
|
<FormattedMessage id="BookingDatesForm.requestToBook" />
|
|
</PrimaryButton>
|
|
</div>
|
|
</Form>
|
|
);
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
BookingDatesFormComponent.defaultProps = {
|
|
rootClassName: null,
|
|
className: null,
|
|
submitButtonWrapperClassName: null,
|
|
price: null,
|
|
isOwnListing: false,
|
|
startDatePlaceholder: null,
|
|
endDatePlaceholder: null,
|
|
timeSlots: null,
|
|
};
|
|
|
|
BookingDatesFormComponent.propTypes = {
|
|
rootClassName: string,
|
|
className: string,
|
|
submitButtonWrapperClassName: string,
|
|
|
|
unitType: propTypes.bookingUnitType.isRequired,
|
|
price: propTypes.money,
|
|
isOwnListing: bool,
|
|
timeSlots: arrayOf(propTypes.timeSlot),
|
|
|
|
// from injectIntl
|
|
intl: intlShape.isRequired,
|
|
|
|
// for tests
|
|
startDatePlaceholder: string,
|
|
endDatePlaceholder: string,
|
|
};
|
|
|
|
const BookingDatesForm = compose(injectIntl)(BookingDatesFormComponent);
|
|
BookingDatesForm.displayName = 'BookingDatesForm';
|
|
|
|
export default BookingDatesForm;
|