From 0fdce2b0fdb5c166d6ff789351cc8201b1c4fac2 Mon Sep 17 00:00:00 2001 From: Kimmo Puputti Date: Thu, 11 May 2017 14:28:52 +0300 Subject: [PATCH] Implement PayoutDetailsForm --- .../PayoutDetailsForm/PayoutDetailsForm.css | 23 ++ .../PayoutDetailsForm.example.js | 11 + .../PayoutDetailsForm/PayoutDetailsForm.js | 251 ++++++++++++++++++ src/examples.js | 2 + src/translations/en.json | 23 ++ 5 files changed, 310 insertions(+) create mode 100644 src/containers/PayoutDetailsForm/PayoutDetailsForm.css create mode 100644 src/containers/PayoutDetailsForm/PayoutDetailsForm.example.js create mode 100644 src/containers/PayoutDetailsForm/PayoutDetailsForm.js diff --git a/src/containers/PayoutDetailsForm/PayoutDetailsForm.css b/src/containers/PayoutDetailsForm/PayoutDetailsForm.css new file mode 100644 index 00000000..66d8dbd6 --- /dev/null +++ b/src/containers/PayoutDetailsForm/PayoutDetailsForm.css @@ -0,0 +1,23 @@ +.root { + +} + +.countries { + border: 1px solid #979797; +} + +.error { + color: red; +} + +.formTitle { + margin-top: 0; +} + +.subTitle { + margin-top: 60px; +} + +.submitButton { + margin-top: 40px; +} diff --git a/src/containers/PayoutDetailsForm/PayoutDetailsForm.example.js b/src/containers/PayoutDetailsForm/PayoutDetailsForm.example.js new file mode 100644 index 00000000..12692017 --- /dev/null +++ b/src/containers/PayoutDetailsForm/PayoutDetailsForm.example.js @@ -0,0 +1,11 @@ +import PayoutDetailsForm from './PayoutDetailsForm'; + +export const USD = { + component: PayoutDetailsForm, + props: { + currency: 'USD', + onSubmit: values => { + console.log('submit payout details:', values); // eslint-disable-line + }, + }, +}; diff --git a/src/containers/PayoutDetailsForm/PayoutDetailsForm.js b/src/containers/PayoutDetailsForm/PayoutDetailsForm.js new file mode 100644 index 00000000..7964fe41 --- /dev/null +++ b/src/containers/PayoutDetailsForm/PayoutDetailsForm.js @@ -0,0 +1,251 @@ +import React, { Component, PropTypes } from 'react'; +import { compose } from 'redux'; +import { connect } from 'react-redux'; +import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; +import { Field, reduxForm, formValueSelector, propTypes as formPropTypes } from 'redux-form'; +import config from '../../config'; +import { + Button, + StripeBankAccountToken, + Select, + BirthdayInput, + LocationAutocompleteInput, +} from '../../components'; +import * as validators from '../../util/validators'; +import { enhancedField } from '../../util/forms'; + +import css from './PayoutDetailsForm.css'; + +const supportedCountries = config.stripe.supportedCountries.map(c => c.code); + +const requiresAddress = countryCode => { + const country = config.stripe.supportedCountries.find(c => c.code === countryCode); + if (!country) { + throw new Error(`Country code not found in Stripe config ${countryCode}`); + } + return country.payoutAddressRequired; +}; + +const CountriesSelect = props => { + const { input } = props; + return ( + + ); +}; + +const { string, bool, object } = PropTypes; + +CountriesSelect.propTypes = { + input: object.isRequired, +}; + +class PayoutDetailsFormComponent extends Component { + constructor(props) { + super(props); + this.EnhancedInput = enhancedField('input', { + errorClassName: css.error, + }); + this.EnhancedCountriesDropdown = enhancedField(CountriesSelect, { + errorClassName: css.error, + }); + this.EnhancedBirthdayInput = enhancedField(BirthdayInput, { + errorClassName: css.error, + }); + this.EnhancedLocationAutocompleteInput = enhancedField(LocationAutocompleteInput, { + errorClassName: css.error, + }); + } + render() { + const { country, currency, handleSubmit, pristine, submitting, inProgress, intl } = this.props; + + const nameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.nameLabel' }); + const nameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.nameRequired', + }) + ); + + const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); + const birthdayRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayRequired', + }) + ); + + const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); + const countryRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.countryRequired', + }) + ); + + const streetAddressLabel = intl.formatMessage({ id: 'PayoutDetailsForm.streetAddressLabel' }); + const streetAddressPlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.streetAddressPlaceholder', + }); + const streetAddressRequiredMessage = intl.formatMessage({ + id: 'PayoutDetailsForm.streetAddressRequired', + }); + const streetAddressNotRecognizedMessage = intl.formatMessage({ + id: 'PayoutDetailsForm.streetAddressNotRecognized', + }); + const validateStreetAddress = [ + validators.autocompleteSearchRequired(streetAddressRequiredMessage), + validators.autocompletePlaceSelected(streetAddressNotRecognizedMessage), + ]; + + const postalCodeLabel = intl.formatMessage({ id: 'PayoutDetailsForm.postalCodeLabel' }); + const postalCodePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.postalCodePlaceholder', + }); + const postalCodeRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.postalCodeRequired', + }) + ); + + const cityLabel = intl.formatMessage({ id: 'PayoutDetailsForm.cityLabel' }); + const cityPlaceholder = intl.formatMessage({ id: 'PayoutDetailsForm.cityPlaceholder' }); + const cityRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.cityRequired', + }) + ); + + const showAddressFields = country && requiresAddress(country); + const addressSection = showAddressFields + ?
+ + + +
+ : null; + + const bankAccountRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.bankAccountRequired', + }) + ); + + const bankAccountSection = country + ?
+

+ +

+ +
+ : null; + + const submitDisabled = pristine || submitting || inProgress; + + return ( +
+

+ +

+

+ +

+

+ +

+ + +

+ +

+ + {addressSection} + {bankAccountSection} + + + ); + } +} + +PayoutDetailsFormComponent.defaultProps = { country: null, inProgress: false }; + +PayoutDetailsFormComponent.propTypes = { + ...formPropTypes, + inProgress: bool, + currency: string.isRequired, + + // from mapStateToProps + country: string, + + // from injectIntl + intl: intlShape.isRequired, +}; + +const formName = 'PayoutDetailsForm'; + +const selector = formValueSelector(formName); +const mapStateToProps = state => { + const country = selector(state, 'country'); + return { country }; +}; + +const formOptions = { + form: formName, +}; + +const PayoutDetailsForm = compose(connect(mapStateToProps), reduxForm(formOptions), injectIntl)( + PayoutDetailsFormComponent +); + +export default PayoutDetailsForm; diff --git a/src/examples.js b/src/examples.js index 0e2077bd..72747028 100644 --- a/src/examples.js +++ b/src/examples.js @@ -35,6 +35,7 @@ import * as SearchForm from './containers/SearchForm/SearchForm.example'; import * as LoginForm from './containers/LoginForm/LoginForm.example'; import * as PasswordForgottenForm from './containers/PasswordForgottenForm/PasswordForgottenForm.example'; +import * as PayoutDetailsForm from './containers/PayoutDetailsForm/PayoutDetailsForm.example'; import * as SignupForm from './containers/SignupForm/SignupForm.example'; import * as StripePaymentForm from './containers/StripePaymentForm/StripePaymentForm.example'; @@ -61,6 +62,7 @@ export { NamedLink, PaginationLinks, PasswordForgottenForm, + PayoutDetailsForm, SearchForm, SignupForm, StripeBankAccountToken, diff --git a/src/translations/en.json b/src/translations/en.json index 1eef1ad2..af0a6e19 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -94,6 +94,29 @@ "PageLayout.logoutFailed": "Logout failed. Please try again.", "PaginationLinks.next": "Next page", "PaginationLinks.previous": "Previous page", + "PayoutDetailsForm.addressTitle": "Address", + "PayoutDetailsForm.bankAccountRequired": "Bank account information is required", + "PayoutDetailsForm.bankDetails": "Bank details", + "PayoutDetailsForm.birthdayLabel": "Date of birth", + "PayoutDetailsForm.birthdayRequired": "This field is required", + "PayoutDetailsForm.cityLabel": "City", + "PayoutDetailsForm.cityPlaceholder": "Enter your city...", + "PayoutDetailsForm.cityRequired": "This field is required", + "PayoutDetailsForm.countryLabel": "Country", + "PayoutDetailsForm.countryRequired": "This field is required", + "PayoutDetailsForm.information": "Since this was the first listing you created, we need to know bit more information about you in order to send you money. We only ask these once.", + "PayoutDetailsForm.nameLabel": "First and Last name", + "PayoutDetailsForm.nameRequired": "This field is required", + "PayoutDetailsForm.personalDetailsTitle": "Personal details", + "PayoutDetailsForm.postalCodeLabel": "Postal code", + "PayoutDetailsForm.postalCodePlaceholder": "Enter your postal code...", + "PayoutDetailsForm.postalCodeRequired": "This field is required", + "PayoutDetailsForm.streetAddressLabel": "Street address", + "PayoutDetailsForm.streetAddressPlaceholder": "Start typing...", + "PayoutDetailsForm.streetAddressNotRecognized": "We didn't recognize this address. Please try another address.", + "PayoutDetailsForm.streetAddressRequired": "This field is required", + "PayoutDetailsForm.submitButtonText": "Save details & publish listing", + "PayoutDetailsForm.title": "One more thing: payout preferences", "SaleDetailsPanel.listingAcceptedTitle": "{customerName} has booked {title}.", "SaleDetailsPanel.listingRejectedTitle": "{customerName} requested to book {title}.", "SaleDetailsPanel.listingRequestedTitle": "{customerName} has requested to book {title}.",