From dd33c4d9dc60fc685acd82b76f74faa46c9f03cd Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Mon, 17 Dec 2018 14:38:42 +0200 Subject: [PATCH 01/15] Add fields for company name and tax id --- .../EditListingWizard/EditListingWizard.css | 4 ++ .../PayoutDetailsForm/PayoutDetailsForm.css | 5 ++ .../PayoutDetailsForm/PayoutDetailsForm.js | 47 +++++++++++++++++++ src/translations/en.json | 8 ++++ 4 files changed, 64 insertions(+) diff --git a/src/components/EditListingWizard/EditListingWizard.css b/src/components/EditListingWizard/EditListingWizard.css index 4bdde170..d3b49235 100644 --- a/src/components/EditListingWizard/EditListingWizard.css +++ b/src/components/EditListingWizard/EditListingWizard.css @@ -102,3 +102,7 @@ margin-bottom: 0; } } + +.modalTitle { + @apply --marketplaceModalTitleStyles; +} diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsForm.css b/src/forms/PayoutDetailsForm/PayoutDetailsForm.css index 7101f65d..a1ffe71f 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsForm.css +++ b/src/forms/PayoutDetailsForm/PayoutDetailsForm.css @@ -1,6 +1,7 @@ @import '../../marketplace.css'; .root { + margin-top: 48px; } .disabled { @@ -64,6 +65,10 @@ width: calc(60% - 9px); } +.taxId { + margin-top: 24px; +} + .error { @apply --marketplaceModalErrorStyles; } diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js index 2be17464..7c38e7fb 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js @@ -100,6 +100,25 @@ const PayoutDetailsFormComponent = props => ( MIN_STRIPE_ACCOUNT_AGE ); + const companyNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyNameLabel' }); + const companyNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.companyNamePlaceholder', + }); + const companyNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.companyNameRequired', + }) + ); + + const companyTaxIdLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyTaxIdLabel' }); + const companyTaxIdPlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.companyTaxIdPlaceholder', + }); + const companyTaxIdRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.companyTaxIdRequired', + }) + ); const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); const countryPlaceholder = intl.formatMessage({ id: 'PayoutDetailsForm.countryPlaceholder', @@ -286,6 +305,34 @@ const PayoutDetailsFormComponent = props => ( validate={personalIdNumberValid} /> +
+

+ +

+
+ + +
+
) : null} {error} diff --git a/src/translations/en.json b/src/translations/en.json index 4900d5ff..18b49303 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -481,6 +481,14 @@ "PayoutDetailsForm.cityLabel": "City", "PayoutDetailsForm.cityPlaceholder": "Helsinki", "PayoutDetailsForm.cityRequired": "This field is required", + "PayoutDetailsForm.companyAddressTitle": "Company address", + "PayoutDetailsForm.companyDetailsTitle": "Company details", + "PayoutDetailsForm.companyNameLabel": "Company name", + "PayoutDetailsForm.companyNamePlaceholder": "Company name", + "PayoutDetailsForm.companyNameRequired": "Company name is required", + "PayoutDetailsForm.companyTaxIdLabel": "VAT ID", + "PayoutDetailsForm.companyTaxIdPlaceholder": "VAT ID", + "PayoutDetailsForm.companyTaxIdRequired": "VAT ID is required", "PayoutDetailsForm.countryLabel": "Country", "PayoutDetailsForm.countryNames.AT": "Austria", "PayoutDetailsForm.countryNames.AU": "Australia", From abe973337ce35446959a70ad6960b1283b09b866 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Mon, 17 Dec 2018 14:39:09 +0200 Subject: [PATCH 02/15] Use radio buttons in PayoutDetailsForm --- .../FieldRadioButton/FieldRadioButton.css | 20 +- .../PayoutDetailsForm/PayoutDetailsForm.css | 14 + .../PayoutDetailsForm/PayoutDetailsForm.js | 359 ++++++++++++------ src/translations/en.json | 3 + 4 files changed, 283 insertions(+), 113 deletions(-) diff --git a/src/components/FieldRadioButton/FieldRadioButton.css b/src/components/FieldRadioButton/FieldRadioButton.css index f2a933be..e5df682a 100644 --- a/src/components/FieldRadioButton/FieldRadioButton.css +++ b/src/components/FieldRadioButton/FieldRadioButton.css @@ -15,6 +15,16 @@ display: inline; } + /* Highlight the borders if the checkbox is hovered, focused or checked */ + &:hover + label .notChecked, + &:hover + label .required, + &:focus + label .notChecked, + &:focus + label .required, + &:checked + label .notChecked, + &:checked + label .required { + stroke: var(--matterColorDark); + } + /* Hightlight the text on checked, hover and focus */ &:focus + label .text, &:hover + label .text, @@ -26,13 +36,13 @@ .label { display: flex; align-items: center; - padding: 0; + padding-top: 6px; } .radioButtonWrapper { /* This should follow line-height */ height: 32px; - margin-top: -1px; + margin-top: -2px; margin-right: 12px; align-self: baseline; @@ -49,10 +59,16 @@ .notChecked { stroke: var(--matterColorAnti); + &:hover { + stroke: pink; + } } .required { stroke: var(--attentionColor); + &:hover { + stroke: pink; + } } .text { diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsForm.css b/src/forms/PayoutDetailsForm/PayoutDetailsForm.css index a1ffe71f..abb1828c 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsForm.css +++ b/src/forms/PayoutDetailsForm/PayoutDetailsForm.css @@ -39,6 +39,20 @@ margin-bottom: 24px; } +.radioButtonRow { + display: flex; + justify-content: left; + flex-shrink: 0; + flex-wrap: wrap; + width: 100%; + margin-bottom: 24px; + white-space: nowrap; +} + +.radioButtonRow > :first-child { + margin-right: 36px; +} + .field { width: 100%; } diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js index 7c38e7fb..88c7fcb6 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js @@ -11,6 +11,7 @@ import { StripeBankAccountTokenInputField, FieldSelect, FieldBirthdayInput, + FieldRadioButton, FieldTextInput, Form, } from '../../components'; @@ -56,7 +57,12 @@ const PayoutDetailsFormComponent = props => ( submitButtonText, values, } = fieldRenderProps; - const { country } = values; + + const { country, accountType } = values; + + const individualAccountLabel = intl.formatMessage({ + id: 'PayoutDetailsForm.individualAccount', + }); const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); const firstNamePlaceholder = intl.formatMessage({ @@ -100,6 +106,8 @@ const PayoutDetailsFormComponent = props => ( MIN_STRIPE_ACCOUNT_AGE ); + const companyAccountLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyAccount' }); + const companyNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyNameLabel' }); const companyNamePlaceholder = intl.formatMessage({ id: 'PayoutDetailsForm.companyNamePlaceholder', @@ -182,6 +190,10 @@ const PayoutDetailsFormComponent = props => ( }); const submitInProgress = inProgress; const submitDisabled = pristine || invalid || disabled || submitInProgress; + const showAsRequired = pristine; + + const showIndividual = country && accountType && accountType === 'individual'; + const showCompany = country && accountType && accountType === 'company'; let error = null; @@ -204,107 +216,165 @@ const PayoutDetailsFormComponent = props => ( ); + return (

- +

-
- + -
-
-
-

- -

- - - {supportedCountries.map(c => ( - - ))} - + {showIndividual ? ( + +
+

+ +

+
+ + +
+ +
- -
- {country ? ( -
-

- -

- -
+
+

+ +

+ + + {supportedCountries.map(c => ( + + ))} + + + +
+ {country ? ( +
+

+ +

+ +
+ ) : null} + + {showPersonalIdNumber ? ( +
+

+ +

+ +
+ ) : null} + + {error} + +

+ +

+ + ) : null} - {showPersonalIdNumber ? ( -
-

- -

- -
+ {showCompany === 'company' ? ( +

@@ -333,29 +403,96 @@ const PayoutDetailsFormComponent = props => ( />

+
+

+ +

+ + + + {supportedCountries.map(c => ( + + ))} + + + +
+ {country ? ( + +
+

+ +

+ +
+
+ ) : null} + + {showPersonalIdNumber ? ( +
+

+ +

+ +
+ ) : null} + + {error} + +

+ +

+ +
) : null} - - {error} - -

- -

-
); }} diff --git a/src/translations/en.json b/src/translations/en.json index 18b49303..67f6dfac 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -452,6 +452,7 @@ "PasswordResetPage.recoveryLinkText": "password recovery page", "PasswordResetPage.resetFailed": "Reset failed. Please try again.", "PasswordResetPage.title": "Reset password", + "PayoutDetailsForm.accountTypeTitle": "Account type", "PayoutDetailsForm.addressTitle": "Address", "PayoutDetailsForm.bankDetails": "Bank details", "PayoutDetailsForm.birthdayDatePlaceholder": "dd", @@ -481,6 +482,7 @@ "PayoutDetailsForm.cityLabel": "City", "PayoutDetailsForm.cityPlaceholder": "Helsinki", "PayoutDetailsForm.cityRequired": "This field is required", + "PayoutDetailsForm.companyAccount": "I represent a company", "PayoutDetailsForm.companyAddressTitle": "Company address", "PayoutDetailsForm.companyDetailsTitle": "Company details", "PayoutDetailsForm.companyNameLabel": "Company name", @@ -518,6 +520,7 @@ "PayoutDetailsForm.firstNameLabel": "First name", "PayoutDetailsForm.firstNamePlaceholder": "John", "PayoutDetailsForm.firstNameRequired": "This field is required", + "PayoutDetailsForm.individualAccount": "I'm an individual", "PayoutDetailsForm.information": "Since this was your first listing, we need to know bit more about you in order to send you money. We only ask these once.", "PayoutDetailsForm.lastNameLabel": "Last name", "PayoutDetailsForm.lastNamePlaceholder": "Doe", From 1e5e70a5c24fb069f30f81bf89a52fa6ffd77dd7 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Tue, 18 Dec 2018 10:02:41 +0200 Subject: [PATCH 03/15] Separate PayoutDetailsForm to PayoutDetailsFormIndividual and PayoutDetailsFormCompany --- .../PayoutDetailsForm/PayoutDetailsForm.js | 416 ++---------------- .../PayoutDetailsFormCompany.js | 319 ++++++++++++++ .../PayoutDetailsFormIndividual.js | 259 +++++++++++ 3 files changed, 605 insertions(+), 389 deletions(-) create mode 100644 src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js create mode 100644 src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js index 88c7fcb6..61219f86 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js @@ -5,26 +5,13 @@ import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; import { Form as FinalForm } from 'react-final-form'; import classNames from 'classnames'; import config from '../../config'; -import { - Button, - ExternalLink, - StripeBankAccountTokenInputField, - FieldSelect, - FieldBirthdayInput, - FieldRadioButton, - FieldTextInput, - Form, -} from '../../components'; -import * as validators from '../../util/validators'; +import { Button, ExternalLink, FieldRadioButton, Form } from '../../components'; import { isStripeInvalidPostalCode } from '../../util/errors'; -import PayoutDetailsAddress from './PayoutDetailsAddress'; +import PayoutDetailsFormCompany from './PayoutDetailsFormCompany'; +import PayoutDetailsFormIndividual from './PayoutDetailsFormIndividual'; import css from './PayoutDetailsForm.css'; -const MIN_STRIPE_ACCOUNT_AGE = 18; - -const supportedCountries = config.stripe.supportedCountries.map(c => c.code); - export const stripeCountryConfigs = countryCode => { const country = config.stripe.supportedCountries.find(c => c.code === countryCode); @@ -34,11 +21,6 @@ export const stripeCountryConfigs = countryCode => { return country; }; -const countryCurrency = countryCode => { - const country = stripeCountryConfigs(countryCode); - return country.currency; -}; - const PayoutDetailsFormComponent = props => ( ( className, createStripeAccountError, disabled, - form, handleSubmit, inProgress, intl, @@ -64,130 +45,12 @@ const PayoutDetailsFormComponent = props => ( id: 'PayoutDetailsForm.individualAccount', }); - const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); - const firstNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.firstNamePlaceholder', - }); - const firstNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.firstNameRequired', - }) - ); - - const lastNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.lastNameLabel' }); - const lastNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.lastNamePlaceholder', - }); - const lastNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.lastNameRequired', - }) - ); - - const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); - const birthdayLabelMonth = intl.formatMessage({ - id: 'PayoutDetailsForm.birthdayLabelMonth', - }); - const birthdayLabelYear = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabelYear' }); - const birthdayRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.birthdayRequired', - }) - ); - const birthdayMinAge = validators.ageAtLeast( - intl.formatMessage( - { - id: 'PayoutDetailsForm.birthdayMinAge', - }, - { - minAge: MIN_STRIPE_ACCOUNT_AGE, - } - ), - MIN_STRIPE_ACCOUNT_AGE - ); - const companyAccountLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyAccount' }); - const companyNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyNameLabel' }); - const companyNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.companyNamePlaceholder', - }); - const companyNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.companyNameRequired', - }) - ); - - const companyTaxIdLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyTaxIdLabel' }); - const companyTaxIdPlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.companyTaxIdPlaceholder', - }); - const companyTaxIdRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.companyTaxIdRequired', - }) - ); - const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); - const countryPlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.countryPlaceholder', - }); - const countryRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.countryRequired', - }) - ); - - // StripeBankAccountTokenInputField handles the error messages - // internally, we just have to make sure we require a valid token - // out of the field. Therefore the empty validation message. - const bankAccountRequired = validators.required(' '); - - const showPersonalIdNumber = - (country && stripeCountryConfigs(country).personalIdNumberRequired) || - (country && stripeCountryConfigs(country).ssnLast4Required); - - const personalIdNumberRequired = validators.required( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberRequired`, - }) - ); - - let personalIdNumberLabel = null; - let personalIdNumberPlaceholder = null; - let personalIdNumberValid = personalIdNumberRequired; - - if (country === 'US') { - personalIdNumberLabel = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberLabel.US`, - }); - personalIdNumberPlaceholder = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberPlaceholder.US`, - }); - - const validSSN = validators.validSsnLast4( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberValid`, - }) - ); - personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validSSN); - } else if (country === 'HK') { - personalIdNumberLabel = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberLabel.HK`, - }); - personalIdNumberPlaceholder = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberPlaceholder.HK`, - }); - const validHKID = validators.validHKID( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberValid`, - }) - ); - personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID); - } - const classes = classNames(css.root, className, { [css.disabled]: disabled, }); + const submitInProgress = inProgress; const submitDisabled = pristine || invalid || disabled || submitInProgress; const showAsRequired = pristine; @@ -242,257 +105,32 @@ const PayoutDetailsFormComponent = props => ( {showIndividual ? ( - -
-

- -

-
- - -
- -
- -
-

- -

- - - {supportedCountries.map(c => ( - - ))} - - - -
- {country ? ( -
-

- -

- -
- ) : null} - - {showPersonalIdNumber ? ( -
-

- -

- -
- ) : null} - - {error} - -

- -

- -
+ + ) : showCompany ? ( + ) : null} - {showCompany === 'company' ? ( - -
-

- -

-
- - -
-
-
-

- -

+ {error} - - - {supportedCountries.map(c => ( - - ))} - - - -
- {country ? ( - -
-

- -

- -
-
- ) : null} - - {showPersonalIdNumber ? ( -
-

- -

- -
- ) : null} - - {error} - -

- -

- -
- ) : null} +

+ +

+ ); }} diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js new file mode 100644 index 00000000..55ee0846 --- /dev/null +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js @@ -0,0 +1,319 @@ +import React from 'react'; +import { bool, object, string } from 'prop-types'; +import { compose } from 'redux'; +import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; +import config from '../../config'; +import { + StripeBankAccountTokenInputField, + FieldSelect, + FieldBirthdayInput, + FieldTextInput, +} from '../../components'; +import * as validators from '../../util/validators'; + +import PayoutDetailsAddress from './PayoutDetailsAddress'; +import css from './PayoutDetailsForm.css'; + +const MIN_STRIPE_ACCOUNT_AGE = 18; + +const supportedCountries = config.stripe.supportedCountries.map(c => c.code); + +export const stripeCountryConfigs = 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; +}; + +const countryCurrency = countryCode => { + const country = stripeCountryConfigs(countryCode); + return country.currency; +}; + +const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { + const { disabled, form, intl, values } = fieldRenderProps; + const { country } = values; + + const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); + const firstNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.firstNamePlaceholder', + }); + const firstNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.firstNameRequired', + }) + ); + + const lastNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.lastNameLabel' }); + const lastNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.lastNamePlaceholder', + }); + const lastNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.lastNameRequired', + }) + ); + + const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); + const birthdayLabelMonth = intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayLabelMonth', + }); + const birthdayLabelYear = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabelYear' }); + const birthdayRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayRequired', + }) + ); + const birthdayMinAge = validators.ageAtLeast( + intl.formatMessage( + { + id: 'PayoutDetailsForm.birthdayMinAge', + }, + { + minAge: MIN_STRIPE_ACCOUNT_AGE, + } + ), + MIN_STRIPE_ACCOUNT_AGE + ); + + const companyNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyNameLabel' }); + const companyNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.companyNamePlaceholder', + }); + const companyNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.companyNameRequired', + }) + ); + + const companyTaxIdLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyTaxIdLabel' }); + const companyTaxIdPlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.companyTaxIdPlaceholder', + }); + const companyTaxIdRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.companyTaxIdRequired', + }) + ); + const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); + const countryPlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.countryPlaceholder', + }); + const countryRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.countryRequired', + }) + ); + + // StripeBankAccountTokenInputField handles the error messages + // internally, we just have to make sure we require a valid token + // out of the field. Therefore the empty validation message. + const bankAccountRequired = validators.required(' '); + + const showPersonalIdNumber = + (country && stripeCountryConfigs(country).personalIdNumberRequired) || + (country && stripeCountryConfigs(country).ssnLast4Required); + + const personalIdNumberRequired = validators.required( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberRequired`, + }) + ); + + let personalIdNumberLabel = null; + let personalIdNumberPlaceholder = null; + let personalIdNumberValid = personalIdNumberRequired; + + if (country === 'US') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.US`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.US`, + }); + + const validSSN = validators.validSsnLast4( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validSSN); + } else if (country === 'HK') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.HK`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.HK`, + }); + const validHKID = validators.validHKID( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID); + } + + return ( + +
+

+ +

+
+ + +
+
+
+

+ +

+ + + + {supportedCountries.map(c => ( + + ))} + + + +
+ {country ? ( + +
+

+ +

+ +
+
+ ) : null} + +
+

+ +

+
+ + +
+ +
+ + {showPersonalIdNumber ? ( +
+

+ +

+ +
+ ) : null} +
+ ); +}; + +PayoutDetailsFormCompanyComponent.defaultProps = { + className: null, + country: null, + createStripeAccountError: null, + disabled: false, + inProgress: false, + ready: false, + submitButtonText: null, +}; + +PayoutDetailsFormCompanyComponent.propTypes = { + className: string, + createStripeAccountError: object, + disabled: bool, + inProgress: bool, + ready: bool, + submitButtonText: string, + + // from injectIntl + intl: intlShape.isRequired, +}; + +const PayoutDetailsFormCompany = compose(injectIntl)(PayoutDetailsFormCompanyComponent); + +export default PayoutDetailsFormCompany; diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js new file mode 100644 index 00000000..47c5b05f --- /dev/null +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js @@ -0,0 +1,259 @@ +import React from 'react'; +import { bool, object } from 'prop-types'; +import { compose } from 'redux'; +import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; +import config from '../../config'; +import { + StripeBankAccountTokenInputField, + FieldSelect, + FieldBirthdayInput, + FieldTextInput, +} from '../../components'; +import * as validators from '../../util/validators'; + +import PayoutDetailsAddress from './PayoutDetailsAddress'; +import css from './PayoutDetailsForm.css'; + +const MIN_STRIPE_ACCOUNT_AGE = 18; + +const supportedCountries = config.stripe.supportedCountries.map(c => c.code); + +export const stripeCountryConfigs = 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; +}; + +const countryCurrency = countryCode => { + const country = stripeCountryConfigs(countryCode); + return country.currency; +}; + +const PayoutDetailsFormIndividualComponent = ({ fieldRenderProps }) => { + const { disabled, form, intl, values } = fieldRenderProps; + const { country } = values; + + const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); + const firstNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.firstNamePlaceholder', + }); + const firstNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.firstNameRequired', + }) + ); + + const lastNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.lastNameLabel' }); + const lastNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.lastNamePlaceholder', + }); + const lastNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.lastNameRequired', + }) + ); + + const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); + const birthdayLabelMonth = intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayLabelMonth', + }); + const birthdayLabelYear = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabelYear' }); + const birthdayRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayRequired', + }) + ); + const birthdayMinAge = validators.ageAtLeast( + intl.formatMessage( + { + id: 'PayoutDetailsForm.birthdayMinAge', + }, + { + minAge: MIN_STRIPE_ACCOUNT_AGE, + } + ), + MIN_STRIPE_ACCOUNT_AGE + ); + + const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); + const countryPlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.countryPlaceholder', + }); + const countryRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.countryRequired', + }) + ); + + // StripeBankAccountTokenInputField handles the error messages + // internally, we just have to make sure we require a valid token + // out of the field. Therefore the empty validation message. + const bankAccountRequired = validators.required(' '); + + const showPersonalIdNumber = + (country && stripeCountryConfigs(country).personalIdNumberRequired) || + (country && stripeCountryConfigs(country).ssnLast4Required); + + const personalIdNumberRequired = validators.required( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberRequired`, + }) + ); + + let personalIdNumberLabel = null; + let personalIdNumberPlaceholder = null; + let personalIdNumberValid = personalIdNumberRequired; + + if (country === 'US') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.US`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.US`, + }); + + const validSSN = validators.validSsnLast4( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validSSN); + } else if (country === 'HK') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.HK`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.HK`, + }); + const validHKID = validators.validHKID( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID); + } + + return ( + +
+

+ +

+
+ + +
+ +
+ +
+

+ +

+ + + {supportedCountries.map(c => ( + + ))} + + + +
+ {country ? ( +
+

+ +

+ +
+ ) : null} + + {showPersonalIdNumber ? ( +
+

+ +

+ +
+ ) : null} +
+ ); +}; + +PayoutDetailsFormIndividualComponent.defaultProps = { + disabled: false, +}; + +PayoutDetailsFormIndividualComponent.propTypes = { + disabled: bool, + form: object.isRequired, + + // from injectIntl + intl: intlShape.isRequired, +}; + +const PayoutDetailsFormIndividual = compose(injectIntl)(PayoutDetailsFormIndividualComponent); + +export default PayoutDetailsFormIndividual; From a3400f4933396aae1514f54cb7ebdf3d356d2e4e Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Tue, 18 Dec 2018 11:09:20 +0200 Subject: [PATCH 04/15] Create PayoutDetailsPersonalDetails subcomponent --- .../PayoutDetailsForm/PayoutDetailsForm.js | 93 ++++-- .../PayoutDetailsFormCompany.js | 276 +++--------------- .../PayoutDetailsFormIndividual.js | 227 ++------------ .../PayoutDetailsPersonalDetails.js | 169 +++++++++++ 4 files changed, 301 insertions(+), 464 deletions(-) create mode 100644 src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js index 61219f86..69a67143 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsForm.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsForm.js @@ -5,13 +5,16 @@ import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; import { Form as FinalForm } from 'react-final-form'; import classNames from 'classnames'; import config from '../../config'; -import { Button, ExternalLink, FieldRadioButton, Form } from '../../components'; +import { Button, ExternalLink, FieldRadioButton, FieldSelect, Form } from '../../components'; import { isStripeInvalidPostalCode } from '../../util/errors'; +import * as validators from '../../util/validators'; import PayoutDetailsFormCompany from './PayoutDetailsFormCompany'; import PayoutDetailsFormIndividual from './PayoutDetailsFormIndividual'; import css from './PayoutDetailsForm.css'; +const supportedCountries = config.stripe.supportedCountries.map(c => c.code); + export const stripeCountryConfigs = countryCode => { const country = config.stripe.supportedCountries.find(c => c.code === countryCode); @@ -47,6 +50,16 @@ const PayoutDetailsFormComponent = props => ( const companyAccountLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyAccount' }); + const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); + const countryPlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.countryPlaceholder', + }); + const countryRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.countryRequired', + }) + ); + const classes = classNames(css.root, className, { [css.disabled]: disabled, }); @@ -104,33 +117,59 @@ const PayoutDetailsFormComponent = props => ( - {showIndividual ? ( - - ) : showCompany ? ( - + {accountType ? ( + +
+

Country

+ + + {supportedCountries.map(c => ( + + ))} + +
+ + {showIndividual ? ( + + ) : showCompany ? ( + + ) : null} + + {error} + +

+ +

+ +
) : null} - - {error} - -

- -

- ); }} diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js index 55ee0846..510c3d9b 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js @@ -2,31 +2,14 @@ import React from 'react'; import { bool, object, string } from 'prop-types'; import { compose } from 'redux'; import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; -import config from '../../config'; -import { - StripeBankAccountTokenInputField, - FieldSelect, - FieldBirthdayInput, - FieldTextInput, -} from '../../components'; +import { StripeBankAccountTokenInputField, FieldTextInput } from '../../components'; import * as validators from '../../util/validators'; import PayoutDetailsAddress from './PayoutDetailsAddress'; +import PayoutDetailsPersonalDetails from './PayoutDetailsPersonalDetails'; +import { stripeCountryConfigs } from './PayoutDetailsForm'; import css from './PayoutDetailsForm.css'; -const MIN_STRIPE_ACCOUNT_AGE = 18; - -const supportedCountries = config.stripe.supportedCountries.map(c => c.code); - -export const stripeCountryConfigs = 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; -}; - const countryCurrency = countryCode => { const country = stripeCountryConfigs(countryCode); return country.currency; @@ -36,48 +19,6 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { const { disabled, form, intl, values } = fieldRenderProps; const { country } = values; - const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); - const firstNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.firstNamePlaceholder', - }); - const firstNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.firstNameRequired', - }) - ); - - const lastNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.lastNameLabel' }); - const lastNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.lastNamePlaceholder', - }); - const lastNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.lastNameRequired', - }) - ); - - const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); - const birthdayLabelMonth = intl.formatMessage({ - id: 'PayoutDetailsForm.birthdayLabelMonth', - }); - const birthdayLabelYear = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabelYear' }); - const birthdayRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.birthdayRequired', - }) - ); - const birthdayMinAge = validators.ageAtLeast( - intl.formatMessage( - { - id: 'PayoutDetailsForm.birthdayMinAge', - }, - { - minAge: MIN_STRIPE_ACCOUNT_AGE, - } - ), - MIN_STRIPE_ACCOUNT_AGE - ); - const companyNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyNameLabel' }); const companyNamePlaceholder = intl.formatMessage({ id: 'PayoutDetailsForm.companyNamePlaceholder', @@ -97,122 +38,54 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { id: 'PayoutDetailsForm.companyTaxIdRequired', }) ); - const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); - const countryPlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.countryPlaceholder', - }); - const countryRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.countryRequired', - }) - ); // StripeBankAccountTokenInputField handles the error messages // internally, we just have to make sure we require a valid token // out of the field. Therefore the empty validation message. const bankAccountRequired = validators.required(' '); - const showPersonalIdNumber = - (country && stripeCountryConfigs(country).personalIdNumberRequired) || - (country && stripeCountryConfigs(country).ssnLast4Required); - - const personalIdNumberRequired = validators.required( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberRequired`, - }) - ); - - let personalIdNumberLabel = null; - let personalIdNumberPlaceholder = null; - let personalIdNumberValid = personalIdNumberRequired; - - if (country === 'US') { - personalIdNumberLabel = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberLabel.US`, - }); - personalIdNumberPlaceholder = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberPlaceholder.US`, - }); - - const validSSN = validators.validSsnLast4( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberValid`, - }) - ); - personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validSSN); - } else if (country === 'HK') { - personalIdNumberLabel = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberLabel.HK`, - }); - personalIdNumberPlaceholder = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberPlaceholder.HK`, - }); - const validHKID = validators.validHKID( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberValid`, - }) - ); - personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID); - } - return ( -
-

- -

-
- - -
-
-
-

- -

- - - - {supportedCountries.map(c => ( - - ))} - - - -
{country ? ( +
+

+ +

+
+ + +
+
+ +
+

+ +

+ + +
+

@@ -226,68 +99,15 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { validate={bankAccountRequired} />

+ +
) : null} - -
-

- -

-
- - -
- -
- - {showPersonalIdNumber ? ( -
-

- -

- -
- ) : null}
); }; diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js index 47c5b05f..6cf1a7cd 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js @@ -2,31 +2,14 @@ import React from 'react'; import { bool, object } from 'prop-types'; import { compose } from 'redux'; import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; -import config from '../../config'; -import { - StripeBankAccountTokenInputField, - FieldSelect, - FieldBirthdayInput, - FieldTextInput, -} from '../../components'; +import { StripeBankAccountTokenInputField } from '../../components'; import * as validators from '../../util/validators'; import PayoutDetailsAddress from './PayoutDetailsAddress'; +import PayoutDetailsPersonalDetails from './PayoutDetailsPersonalDetails'; +import { stripeCountryConfigs } from './PayoutDetailsForm'; import css from './PayoutDetailsForm.css'; -const MIN_STRIPE_ACCOUNT_AGE = 18; - -const supportedCountries = config.stripe.supportedCountries.map(c => c.code); - -export const stripeCountryConfigs = 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; -}; - const countryCurrency = countryCode => { const country = stripeCountryConfigs(countryCode); return country.currency; @@ -36,208 +19,34 @@ const PayoutDetailsFormIndividualComponent = ({ fieldRenderProps }) => { const { disabled, form, intl, values } = fieldRenderProps; const { country } = values; - const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); - const firstNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.firstNamePlaceholder', - }); - const firstNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.firstNameRequired', - }) - ); - - const lastNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.lastNameLabel' }); - const lastNamePlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.lastNamePlaceholder', - }); - const lastNameRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.lastNameRequired', - }) - ); - - const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); - const birthdayLabelMonth = intl.formatMessage({ - id: 'PayoutDetailsForm.birthdayLabelMonth', - }); - const birthdayLabelYear = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabelYear' }); - const birthdayRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.birthdayRequired', - }) - ); - const birthdayMinAge = validators.ageAtLeast( - intl.formatMessage( - { - id: 'PayoutDetailsForm.birthdayMinAge', - }, - { - minAge: MIN_STRIPE_ACCOUNT_AGE, - } - ), - MIN_STRIPE_ACCOUNT_AGE - ); - - const countryLabel = intl.formatMessage({ id: 'PayoutDetailsForm.countryLabel' }); - const countryPlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.countryPlaceholder', - }); - const countryRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.countryRequired', - }) - ); - // StripeBankAccountTokenInputField handles the error messages // internally, we just have to make sure we require a valid token // out of the field. Therefore the empty validation message. const bankAccountRequired = validators.required(' '); - const showPersonalIdNumber = - (country && stripeCountryConfigs(country).personalIdNumberRequired) || - (country && stripeCountryConfigs(country).ssnLast4Required); - - const personalIdNumberRequired = validators.required( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberRequired`, - }) - ); - - let personalIdNumberLabel = null; - let personalIdNumberPlaceholder = null; - let personalIdNumberValid = personalIdNumberRequired; - - if (country === 'US') { - personalIdNumberLabel = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberLabel.US`, - }); - personalIdNumberPlaceholder = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberPlaceholder.US`, - }); - - const validSSN = validators.validSsnLast4( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberValid`, - }) - ); - personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validSSN); - } else if (country === 'HK') { - personalIdNumberLabel = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberLabel.HK`, - }); - personalIdNumberPlaceholder = intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberPlaceholder.HK`, - }); - const validHKID = validators.validHKID( - intl.formatMessage({ - id: `PayoutDetailsForm.personalIdNumberValid`, - }) - ); - personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID); - } - return ( + + +

- +

-
- - -
-
- -
-

- -

- - - {supportedCountries.map(c => ( - - ))} - - - -
- {country ? ( -
-

- -

- -
- ) : null} - - {showPersonalIdNumber ? ( -
-

- -

- -
- ) : null}
); }; diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js b/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js new file mode 100644 index 00000000..ac9c09da --- /dev/null +++ b/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js @@ -0,0 +1,169 @@ +import React from 'react'; +import { bool, string } from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import { FieldBirthdayInput, FieldTextInput } from '../../components'; +import * as validators from '../../util/validators'; + +import { stripeCountryConfigs } from './PayoutDetailsForm'; +import css from './PayoutDetailsForm.css'; + +const MIN_STRIPE_ACCOUNT_AGE = 18; + +const PayoutDetailsPersonalDetails = props => { + const { intl, disabled, values, country } = props; + + const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); + const firstNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.firstNamePlaceholder', + }); + const firstNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.firstNameRequired', + }) + ); + + const lastNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.lastNameLabel' }); + const lastNamePlaceholder = intl.formatMessage({ + id: 'PayoutDetailsForm.lastNamePlaceholder', + }); + const lastNameRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.lastNameRequired', + }) + ); + + const birthdayLabel = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabel' }); + const birthdayLabelMonth = intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayLabelMonth', + }); + const birthdayLabelYear = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayLabelYear' }); + const birthdayRequired = validators.required( + intl.formatMessage({ + id: 'PayoutDetailsForm.birthdayRequired', + }) + ); + const birthdayMinAge = validators.ageAtLeast( + intl.formatMessage( + { + id: 'PayoutDetailsForm.birthdayMinAge', + }, + { + minAge: MIN_STRIPE_ACCOUNT_AGE, + } + ), + MIN_STRIPE_ACCOUNT_AGE + ); + + const showPersonalIdNumber = + (country && stripeCountryConfigs(country).personalIdNumberRequired) || + (country && stripeCountryConfigs(country).ssnLast4Required); + + const personalIdNumberRequired = validators.required( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberRequired`, + }) + ); + + let personalIdNumberLabel = null; + let personalIdNumberPlaceholder = null; + let personalIdNumberValid = personalIdNumberRequired; + + if (country === 'US') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.US`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.US`, + }); + + const validSSN = validators.validSsnLast4( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validSSN); + } else if (country === 'HK') { + personalIdNumberLabel = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberLabel.HK`, + }); + personalIdNumberPlaceholder = intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberPlaceholder.HK`, + }); + const validHKID = validators.validHKID( + intl.formatMessage({ + id: `PayoutDetailsForm.personalIdNumberValid`, + }) + ); + personalIdNumberValid = validators.composeValidators(personalIdNumberRequired, validHKID); + } + + return ( +
+

+ +

+
+ + +
+
+ +
+ + {showPersonalIdNumber ? ( + + ) : null} +
+ ); +}; +PayoutDetailsPersonalDetails.defaultProps = { + country: null, + disabled: false, +}; + +PayoutDetailsPersonalDetails.propTypes = { + country: string, + disabled: bool, +}; + +export default PayoutDetailsPersonalDetails; From 7dff8331aa67916ac9d7d7174c5a9cf9a074eae6 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Tue, 18 Dec 2018 11:36:42 +0200 Subject: [PATCH 05/15] Update PayoutDetailsAddress to contain section title --- .../PayoutDetailsForm/PayoutDetailsAddress.js | 12 ++++++++++-- .../PayoutDetailsForm/PayoutDetailsFormCompany.js | 14 +++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js b/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js index 0e9ed0e1..12c30dc5 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js @@ -23,13 +23,19 @@ const CANADIAN_PROVINCES = [ ]; const PayoutDetailsAddress = props => { - const { country, intl, disabled, form } = props; + const { country, intl, disabled, form, companyAddress } = props; const countryConfig = country ? stripeCountryConfigs(country).addressConfig : null; const isRequired = (countryConfig, field) => { return countryConfig[field]; }; + const addressTitle = intl.formatMessage({ + id: companyAddress + ? 'PayoutDetailsForm.companyAddressTitle' + : 'PayoutDetailsForm.streetAddressLabel', + }); + const showAddressLine = country && isRequired(countryConfig, 'addressLine'); const streetAddressLabel = intl.formatMessage({ @@ -89,7 +95,9 @@ const PayoutDetailsAddress = props => { ); return ( -
+
+

{addressTitle}

+ {showAddressLine ? ( {
-
-

- -

- - -
+

From 338fb79f576eedc919475e6238f65fdb5d21c9e5 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Tue, 18 Dec 2018 12:10:44 +0200 Subject: [PATCH 06/15] Create PayoutDetailsBankDetails subcomponent --- .../PayoutDetailsBankDetails.js | 52 +++++++++++++++++++ .../PayoutDetailsForm/PayoutDetailsForm.css | 4 ++ .../PayoutDetailsFormCompany.js | 28 ++-------- .../PayoutDetailsFormIndividual.js | 32 ++---------- 4 files changed, 62 insertions(+), 54 deletions(-) create mode 100644 src/forms/PayoutDetailsForm/PayoutDetailsBankDetails.js diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsBankDetails.js b/src/forms/PayoutDetailsForm/PayoutDetailsBankDetails.js new file mode 100644 index 00000000..7dd84e6d --- /dev/null +++ b/src/forms/PayoutDetailsForm/PayoutDetailsBankDetails.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { bool, string } from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import { StripeBankAccountTokenInputField } from '../../components'; +import * as validators from '../../util/validators'; + +import { stripeCountryConfigs } from './PayoutDetailsForm'; +import css from './PayoutDetailsForm.css'; + +const countryCurrency = countryCode => { + const country = stripeCountryConfigs(countryCode); + return country.currency; +}; + +const PayoutDetailsBankDetails = props => { + const { country, disabled } = props; + + // StripeBankAccountTokenInputField handles the error messages + // internally, we just have to make sure we require a valid token + // out of the field. Therefore the empty validation message. + const bankAccountRequired = validators.required(' '); + + return ( +
+

+ +

+ +
+ ); +}; +PayoutDetailsBankDetails.defaultProps = { + country: null, + disabled: false, + fieldId: null, +}; + +PayoutDetailsBankDetails.propTypes = { + country: string, + disabled: bool, + fieldId: string, +}; + +export default PayoutDetailsBankDetails; diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsForm.css b/src/forms/PayoutDetailsForm/PayoutDetailsForm.css index abb1828c..bcc1d572 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsForm.css +++ b/src/forms/PayoutDetailsForm/PayoutDetailsForm.css @@ -105,3 +105,7 @@ cursor: pointer; } } + +.bankDetailsStripeField p { + @apply --marketplaceH4FontStyles; +} diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js index 1addd8cd..cd4822e7 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js @@ -2,19 +2,14 @@ import React from 'react'; import { bool, object, string } from 'prop-types'; import { compose } from 'redux'; import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; -import { StripeBankAccountTokenInputField, FieldTextInput } from '../../components'; +import { FieldTextInput } from '../../components'; import * as validators from '../../util/validators'; import PayoutDetailsAddress from './PayoutDetailsAddress'; +import PayoutDetailsBankDetails from './PayoutDetailsBankDetails'; import PayoutDetailsPersonalDetails from './PayoutDetailsPersonalDetails'; -import { stripeCountryConfigs } from './PayoutDetailsForm'; import css from './PayoutDetailsForm.css'; -const countryCurrency = countryCode => { - const country = stripeCountryConfigs(countryCode); - return country.currency; -}; - const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { const { disabled, form, intl, values } = fieldRenderProps; const { country } = values; @@ -39,11 +34,6 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { }) ); - // StripeBankAccountTokenInputField handles the error messages - // internally, we just have to make sure we require a valid token - // out of the field. Therefore the empty validation message. - const bankAccountRequired = validators.required(' '); - return ( {country ? ( @@ -86,19 +76,7 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { companyAddress /> -
-

- -

- -
+ { - const country = stripeCountryConfigs(countryCode); - return country.currency; -}; const PayoutDetailsFormIndividualComponent = ({ fieldRenderProps }) => { const { disabled, form, intl, values } = fieldRenderProps; const { country } = values; - // StripeBankAccountTokenInputField handles the error messages - // internally, we just have to make sure we require a valid token - // out of the field. Therefore the empty validation message. - const bankAccountRequired = validators.required(' '); - return ( { country={country} /> - -
-

- -

- -
+
); }; From a3b43cec2731d784cefcbc92d022e14ee44b9029 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Wed, 19 Dec 2018 10:57:29 +0200 Subject: [PATCH 07/15] Update user.duck.js to support also business accounts --- src/ducks/user.duck.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ducks/user.duck.js b/src/ducks/user.duck.js index 2f110e82..fd88b886 100644 --- a/src/ducks/user.duck.js +++ b/src/ducks/user.duck.js @@ -401,6 +401,9 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => province, bankAccountToken, personalIdNumber, + accountType, + companyName, + companyTaxId, } = payoutDetails; const hasProvince = province && !state; @@ -422,7 +425,9 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => last_name: lastName, address: omitBy(address, isUndefined), dob: birthDate, - type: 'individual', + type: accountType, + business_name: companyName, + business_tax_id: companyTaxId, ...idNumber, }, tos_shown_and_accepted: true, From 1ba857c3ce8883291bd28e0fd3eb946dd1c0e3a5 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Wed, 19 Dec 2018 15:05:55 +0200 Subject: [PATCH 08/15] Add country specific tax id field labels --- .../PayoutDetailsFormCompany.js | 32 ++++++++++++------- src/translations/en.json | 28 +++++++++++++--- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js index cd4822e7..0cdf40fe 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js @@ -24,14 +24,26 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { }) ); - const companyTaxIdLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyTaxIdLabel' }); - const companyTaxIdPlaceholder = intl.formatMessage({ - id: 'PayoutDetailsForm.companyTaxIdPlaceholder', + const companyTaxIdLabel = intl.formatMessage({ + id: `PayoutDetailsForm.companyTaxIdLabel.${country}`, }); + const companyTaxIdPlaceholder = intl.formatMessage( + { + id: 'PayoutDetailsForm.companyTaxIdPlaceholder', + }, + { + idName: companyTaxIdLabel, + } + ); const companyTaxIdRequired = validators.required( - intl.formatMessage({ - id: 'PayoutDetailsForm.companyTaxIdRequired', - }) + intl.formatMessage( + { + id: 'PayoutDetailsForm.companyTaxIdRequired', + }, + { + idName: companyTaxIdLabel, + } + ) ); return ( @@ -41,31 +53,29 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => {

-

-
+

+ -
Date: Wed, 19 Dec 2018 16:56:56 +0200 Subject: [PATCH 09/15] Add personal address to company account if required --- src/ducks/user.duck.js | 51 +++++++++---- .../PayoutDetailsForm/PayoutDetailsAddress.js | 43 ++++++----- .../PayoutDetailsBankDetails.js | 4 +- .../PayoutDetailsForm/PayoutDetailsForm.js | 9 ++- .../PayoutDetailsFormCompany.js | 73 ++++++++++++------- .../PayoutDetailsFormIndividual.js | 8 +- .../PayoutDetailsPersonalDetails.js | 22 +++--- src/stripe-config.js | 51 +++++++++++++ 8 files changed, 187 insertions(+), 74 deletions(-) diff --git a/src/ducks/user.duck.js b/src/ducks/user.duck.js index fd88b886..9e7b5ed3 100644 --- a/src/ducks/user.duck.js +++ b/src/ducks/user.duck.js @@ -389,32 +389,50 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => dispatch(stripeAccountCreateRequest()); + const { accountType, country } = payoutDetails; + + let payoutDetailValues; + if (accountType === 'company') { + payoutDetailValues = payoutDetails['company']; + } else if (accountType === 'individual') { + payoutDetailValues = payoutDetails['individual']; + } + const { firstName, lastName, birthDate, - country, - streetAddress, - postalCode, - city, - state, - province, + address, bankAccountToken, personalIdNumber, - accountType, companyName, companyTaxId, - } = payoutDetails; + personalAddress, + } = payoutDetailValues; - const hasProvince = province && !state; + const hasProvince = address.province && !address.state; - const address = { - city, - line1: streetAddress, - postal_code: postalCode, - state: hasProvince ? province : state, + const addressValue = { + city: address.city, + line1: address.streetAddress, + postal_code: address.postalCode, + state: hasProvince ? address.province : address.state ? address.state : '', }; + let personalAddressValue; + if (personalAddress) { + personalAddressValue = { + city: personalAddress.city, + line1: personalAddress.streetAddress, + postal_code: personalAddress.postalCode, + state: hasProvince + ? personalAddress.province + : personalAddress.state + ? personalAddress.state + : '', + }; + } + const idNumber = country === 'US' ? { ssn_last_4: personalIdNumber } : { personal_id_number: personalIdNumber }; @@ -423,18 +441,19 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => legal_entity: { first_name: firstName, last_name: lastName, - address: omitBy(address, isUndefined), + address: omitBy(addressValue, isUndefined), dob: birthDate, type: accountType, business_name: companyName, business_tax_id: companyTaxId, + personal_address: personalAddressValue, ...idNumber, }, tos_shown_and_accepted: true, }; let accountResponse; - + return stripe .createToken('account', params) .then(response => { diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js b/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js index 12c30dc5..7ed94678 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js @@ -2,6 +2,7 @@ import React from 'react'; import { bool, object, string } from 'prop-types'; import { FieldSelect, FieldTextInput } from '../../components'; import * as validators from '../../util/validators'; +import { intlShape } from 'react-intl'; import { stripeCountryConfigs } from './PayoutDetailsForm'; import css from './PayoutDetailsForm.css'; @@ -23,7 +24,7 @@ const CANADIAN_PROVINCES = [ ]; const PayoutDetailsAddress = props => { - const { country, intl, disabled, form, companyAddress } = props; + const { country, intl, disabled, form, fieldId } = props; const countryConfig = country ? stripeCountryConfigs(country).addressConfig : null; const isRequired = (countryConfig, field) => { @@ -31,9 +32,10 @@ const PayoutDetailsAddress = props => { }; const addressTitle = intl.formatMessage({ - id: companyAddress - ? 'PayoutDetailsForm.companyAddressTitle' - : 'PayoutDetailsForm.streetAddressLabel', + id: + fieldId === 'company' + ? 'PayoutDetailsForm.companyAddressTitle' + : 'PayoutDetailsForm.streetAddressLabel', }); const showAddressLine = country && isRequired(countryConfig, 'addressLine'); @@ -100,8 +102,8 @@ const PayoutDetailsAddress = props => { {showAddressLine ? ( { label={streetAddressLabel} placeholder={streetAddressPlaceholder} validate={streetAddressRequired} - onUnmount={() => form.change('streetAddress', undefined)} + onUnmount={() => form.change(`${fieldId}.streetAddress`, undefined)} /> ) : null}
{showPostalCode ? ( { label={postalCodeLabel} placeholder={postalCodePlaceholder} validate={postalCodeRequired} - onUnmount={() => form.change('postalCode', undefined)} + onUnmount={() => form.change(`${fieldId}.postalCode`, undefined)} /> ) : null} {showCity ? ( { label={cityLabel} placeholder={cityPlaceholder} validate={cityRequired} - onUnmount={() => form.change('city', undefined)} + onUnmount={() => form.change(`${fieldId}.city`, undefined)} /> ) : null}
{showState ? ( { label={stateLabel} placeholder={statePlaceholder} validate={stateRequired} - onUnmount={() => form.change('state', undefined)} + onUnmount={() => form.change(`${fieldId}.state`, undefined)} /> ) : null} {showProvince ? ( { PayoutDetailsAddress.defaultProps = { country: null, disabled: false, + fieldId: null, }; PayoutDetailsAddress.propTypes = { country: string, disabled: bool, form: object.isRequired, + fieldId: string, + + // from injectIntl + intl: intlShape.isRequired, }; export default PayoutDetailsAddress; diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsBankDetails.js b/src/forms/PayoutDetailsForm/PayoutDetailsBankDetails.js index 7dd84e6d..ee5381f6 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsBankDetails.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsBankDetails.js @@ -13,7 +13,7 @@ const countryCurrency = countryCode => { }; const PayoutDetailsBankDetails = props => { - const { country, disabled } = props; + const { country, disabled, fieldId } = props; // StripeBankAccountTokenInputField handles the error messages // internally, we just have to make sure we require a valid token @@ -28,7 +28,7 @@ const PayoutDetailsBankDetails = props => { ( ); + console.log('Values', JSON.stringify(values, null, ' ')); + return (
@@ -142,9 +144,12 @@ const PayoutDetailsFormComponent = props => (
{showIndividual ? ( - + ) : showCompany ? ( - + ) : null} {error} diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js index 0cdf40fe..bf9edf76 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js @@ -8,11 +8,11 @@ import * as validators from '../../util/validators'; import PayoutDetailsAddress from './PayoutDetailsAddress'; import PayoutDetailsBankDetails from './PayoutDetailsBankDetails'; import PayoutDetailsPersonalDetails from './PayoutDetailsPersonalDetails'; +import { stripeCountryConfigs } from './PayoutDetailsForm'; import css from './PayoutDetailsForm.css'; -const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { +const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps, country }) => { const { disabled, form, intl, values } = fieldRenderProps; - const { country } = values; const companyNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyNameLabel' }); const companyNamePlaceholder = intl.formatMessage({ @@ -46,6 +46,11 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { ) ); + const showPersonalAddressField = + country && + stripeCountryConfigs(country).companyConfig && + stripeCountryConfigs(country).companyConfig.personalAddress; + return ( {country ? ( @@ -53,29 +58,29 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => {

-

- + + - +
{ intl={intl} disabled={disabled} form={form} - companyAddress + fieldId="company.address" /> - + + {showPersonalAddressField ? ( + + ) : null}
) : null} @@ -105,18 +120,24 @@ PayoutDetailsFormCompanyComponent.defaultProps = { country: null, createStripeAccountError: null, disabled: false, +<<<<<<< HEAD inProgress: false, ready: false, submitButtonText: null, +======= +>>>>>>> 70cd0720... Merge to add personal address }; PayoutDetailsFormCompanyComponent.propTypes = { className: string, createStripeAccountError: object, disabled: bool, +<<<<<<< HEAD inProgress: bool, ready: bool, submitButtonText: string, +======= +>>>>>>> 70cd0720... Merge to add personal address // from injectIntl intl: intlShape.isRequired, diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js index a0311639..8ac9473d 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js @@ -19,7 +19,13 @@ const PayoutDetailsFormIndividualComponent = ({ fieldRenderProps }) => { values={values} country={country} /> - + ); diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js b/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js index ac9c09da..6cad411c 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js @@ -3,6 +3,7 @@ import { bool, string } from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { FieldBirthdayInput, FieldTextInput } from '../../components'; import * as validators from '../../util/validators'; +import { intlShape } from 'react-intl'; import { stripeCountryConfigs } from './PayoutDetailsForm'; import css from './PayoutDetailsForm.css'; @@ -10,7 +11,7 @@ import css from './PayoutDetailsForm.css'; const MIN_STRIPE_ACCOUNT_AGE = 18; const PayoutDetailsPersonalDetails = props => { - const { intl, disabled, values, country } = props; + const { intl, disabled, values, country, fieldId } = props; const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); const firstNamePlaceholder = intl.formatMessage({ @@ -104,8 +105,8 @@ const PayoutDetailsPersonalDetails = props => {
{ validate={firstNameRequired} /> {
{ {showPersonalIdNumber ? ( { PayoutDetailsPersonalDetails.defaultProps = { country: null, disabled: false, + fieldId: null, }; PayoutDetailsPersonalDetails.propTypes = { country: string, disabled: bool, + fieldId: string, + intl: intlShape.isRequired, }; export default PayoutDetailsPersonalDetails; diff --git a/src/stripe-config.js b/src/stripe-config.js index ec2a94c6..23068b25 100644 --- a/src/stripe-config.js +++ b/src/stripe-config.js @@ -38,6 +38,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Belgium @@ -51,6 +54,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Canada @@ -80,6 +86,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Finland @@ -93,6 +102,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // France @@ -106,6 +118,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Germany @@ -119,6 +134,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Hong Kong @@ -134,6 +152,9 @@ export const stripeSupportedCountries = [ accountNumber: true, }, personalIdNumberRequired: true, + companyConfig: { + personalAddress: true, + }, }, { // Ireland @@ -147,6 +168,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Italy @@ -160,6 +184,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Luxembourg @@ -173,6 +200,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Netherlands @@ -186,6 +216,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // New Zealand @@ -212,6 +245,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Portugal @@ -225,6 +261,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Spain @@ -238,6 +277,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Sweden @@ -251,6 +293,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // Switzerland @@ -264,6 +309,9 @@ export const stripeSupportedCountries = [ accountConfig: { iban: true, }, + companyConfig: { + personalAddress: true, + }, }, { // United Kingdom @@ -278,6 +326,9 @@ export const stripeSupportedCountries = [ sortCode: true, accountNumber: true, }, + companyConfig: { + personalAddress: true, + }, }, { // United States From 9d8d2116772d189122365441f66ab5e386d97ed9 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Mon, 7 Jan 2019 09:43:03 +0200 Subject: [PATCH 10/15] Create IconAdd --- src/components/IconAdd/IconAdd.css | 5 ++++ src/components/IconAdd/IconAdd.example.js | 7 +++++ src/components/IconAdd/IconAdd.js | 33 +++++++++++++++++++++++ src/components/index.js | 1 + src/examples.js | 2 ++ 5 files changed, 48 insertions(+) create mode 100644 src/components/IconAdd/IconAdd.css create mode 100644 src/components/IconAdd/IconAdd.example.js create mode 100644 src/components/IconAdd/IconAdd.js diff --git a/src/components/IconAdd/IconAdd.css b/src/components/IconAdd/IconAdd.css new file mode 100644 index 00000000..d6ab5b7e --- /dev/null +++ b/src/components/IconAdd/IconAdd.css @@ -0,0 +1,5 @@ +@import '../../marketplace.css'; + +.root { + fill: var(--marketplaceColor); +} diff --git a/src/components/IconAdd/IconAdd.example.js b/src/components/IconAdd/IconAdd.example.js new file mode 100644 index 00000000..217260a4 --- /dev/null +++ b/src/components/IconAdd/IconAdd.example.js @@ -0,0 +1,7 @@ +import IconAdd from './IconAdd'; + +export const Icon = { + component: IconAdd, + props: {}, + group: 'icons', +}; diff --git a/src/components/IconAdd/IconAdd.js b/src/components/IconAdd/IconAdd.js new file mode 100644 index 00000000..09ce32f8 --- /dev/null +++ b/src/components/IconAdd/IconAdd.js @@ -0,0 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import css from './IconAdd.css'; + +const IconAdd = props => { + const { className, rootClassName } = props; + const classes = classNames(rootClassName || css.root, className); + + return ( + + + + ); +}; + +const { string } = PropTypes; + +IconAdd.defaultProps = { + className: null, + rootClassName: null, +}; + +IconAdd.propTypes = { + className: string, + rootClassName: string, +}; + +export default IconAdd; diff --git a/src/components/index.js b/src/components/index.js index 74a334b4..167705b3 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -30,6 +30,7 @@ export { default as FieldSelect } from './FieldSelect/FieldSelect'; export { default as FieldTextInput } from './FieldTextInput/FieldTextInput'; export { default as Footer } from './Footer/Footer'; export { default as Form } from './Form/Form'; +export { default as IconAdd } from './IconAdd/IconAdd'; export { default as IconArrowHead } from './IconArrowHead/IconArrowHead'; export { default as IconBannedUser } from './IconBannedUser/IconBannedUser'; export { default as IconCheckmark } from './IconCheckmark/IconCheckmark'; diff --git a/src/examples.js b/src/examples.js index aa179883..87ab1e3e 100644 --- a/src/examples.js +++ b/src/examples.js @@ -20,6 +20,7 @@ import * as FieldReviewRating from './components/FieldReviewRating/FieldReviewRa import * as FieldSelect from './components/FieldSelect/FieldSelect.example'; import * as FieldTextInput from './components/FieldTextInput/FieldTextInput.example'; import * as Footer from './components/Footer/Footer.example'; +import * as IconAdd from './components/IconAdd/IconAdd.example'; import * as IconBannedUser from './components/IconBannedUser/IconBannedUser.example'; import * as IconCheckmark from './components/IconCheckmark/IconCheckmark.example'; import * as IconClose from './components/IconClose/IconClose.example'; @@ -118,6 +119,7 @@ export { FieldSelect, FieldTextInput, Footer, + IconAdd, IconBannedUser, IconCheckmark, IconClose, From 9c31f3e0ea2912119304ef45206fe587801aa607 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Thu, 20 Dec 2018 16:45:35 +0200 Subject: [PATCH 11/15] Add additional owners to company account if required --- src/ducks/user.duck.js | 20 +++- .../PayoutDetailsForm/PayoutDetailsAddress.js | 12 +- .../PayoutDetailsForm/PayoutDetailsForm.css | 53 +++++++++ .../PayoutDetailsForm/PayoutDetailsForm.js | 6 +- .../PayoutDetailsFormCompany.js | 105 ++++++++++++++---- .../PayoutDetailsFormIndividual.js | 8 +- .../PayoutDetailsPersonalDetails.js | 12 +- src/stripe-config.js | 16 +++ src/translations/en.json | 3 + 9 files changed, 199 insertions(+), 36 deletions(-) diff --git a/src/ducks/user.duck.js b/src/ducks/user.duck.js index 9e7b5ed3..0f40fbd2 100644 --- a/src/ducks/user.duck.js +++ b/src/ducks/user.duck.js @@ -408,6 +408,7 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => companyName, companyTaxId, personalAddress, + additionalOwners, } = payoutDetailValues; const hasProvince = address.province && !address.state; @@ -433,6 +434,22 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => }; } + const additionalOwnersValue = additionalOwners + ? additionalOwners.map(owner => { + return { + first_name: owner.firstName, + last_name: owner.lastName, + dob: owner.birthDate, + address: { + city: owner.city, + line1: owner.streetAddress, + postal_code: owner.postalCode, + state: hasProvince ? owner.province : owner.state ? owner.state : '', + }, + }; + }) + : []; + const idNumber = country === 'US' ? { ssn_last_4: personalIdNumber } : { personal_id_number: personalIdNumber }; @@ -447,13 +464,14 @@ export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => business_name: companyName, business_tax_id: companyTaxId, personal_address: personalAddressValue, + additional_owners: additionalOwnersValue, ...idNumber, }, tos_shown_and_accepted: true, }; let accountResponse; - + return stripe .createToken('account', params) .then(response => { diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js b/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js index 7ed94678..bda6342f 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsAddress.js @@ -24,16 +24,20 @@ const CANADIAN_PROVINCES = [ ]; const PayoutDetailsAddress = props => { - const { country, intl, disabled, form, fieldId } = props; + const { className, country, intl, disabled, form, fieldId } = props; const countryConfig = country ? stripeCountryConfigs(country).addressConfig : null; const isRequired = (countryConfig, field) => { return countryConfig[field]; }; + const showTitle = + fieldId === 'company.address' || + fieldId === 'individual' || + fieldId === 'company.personalAddress'; const addressTitle = intl.formatMessage({ id: - fieldId === 'company' + fieldId === 'company.address' ? 'PayoutDetailsForm.companyAddressTitle' : 'PayoutDetailsForm.streetAddressLabel', }); @@ -97,8 +101,8 @@ const PayoutDetailsAddress = props => { ); return ( -
-

{addressTitle}

+
+ {showTitle ?

{addressTitle}

: null} {showAddressLine ? ( { const PayoutDetailsFormComponent = props => ( { const { className, @@ -93,8 +97,6 @@ const PayoutDetailsFormComponent = props => ( ); - console.log('Values', JSON.stringify(values, null, ' ')); - return (
diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js index bf9edf76..12597d58 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormCompany.js @@ -1,8 +1,9 @@ import React from 'react'; -import { bool, object, string } from 'prop-types'; +import { bool, string } from 'prop-types'; import { compose } from 'redux'; import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; -import { FieldTextInput } from '../../components'; +import { FieldArray } from 'react-final-form-arrays'; +import { FieldTextInput, IconAdd, IconClose, InlineTextButton } from '../../components'; import * as validators from '../../util/validators'; import PayoutDetailsAddress from './PayoutDetailsAddress'; @@ -11,8 +12,21 @@ import PayoutDetailsPersonalDetails from './PayoutDetailsPersonalDetails'; import { stripeCountryConfigs } from './PayoutDetailsForm'; import css from './PayoutDetailsForm.css'; -const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps, country }) => { - const { disabled, form, intl, values } = fieldRenderProps; +// In EU, there can be a maximum of 4 additional owners +const MAX_NUMBER_OF_ADDITIONAL_OWNERS = 4; + +const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps }) => { + const { + id, + disabled, + form, + intl, + values, + form: { + mutators: { push }, + }, + } = fieldRenderProps; + const { country } = values; const companyNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.companyNameLabel' }); const companyNamePlaceholder = intl.formatMessage({ @@ -51,6 +65,15 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps, country }) => { stripeCountryConfigs(country).companyConfig && stripeCountryConfigs(country).companyConfig.personalAddress; + const showAdditionalOwnersField = + country && + stripeCountryConfigs(country).companyConfig && + stripeCountryConfigs(country).companyConfig.additionalOwners; + + const hasAdditionalOwners = values.company && values.company.additionalOwners; + const hasMaxNumberOfAdditionalOwners = + hasAdditionalOwners && + values.company.additionalOwners.length >= MAX_NUMBER_OF_ADDITIONAL_OWNERS; return ( {country ? ( @@ -100,8 +123,10 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps, country }) => { country={country} fieldId="company" /> + {showPersonalAddressField ? ( { fieldId="company.personalAddress" /> ) : null} + + {showAdditionalOwnersField ? ( +
+ + {({ fields }) => + fields.map((name, index) => ( +
+
fields.remove(index)} + style={{ cursor: 'pointer' }} + > + + + + +
+ + {showPersonalAddressField ? ( + + ) : null} +
+ )) + } +
+ + {!hasAdditionalOwners || !hasMaxNumberOfAdditionalOwners ? ( + push('company.additionalOwners', undefined)} + > + + + + + + ) : null} +
+ ) : null}
) : null} @@ -116,28 +194,13 @@ const PayoutDetailsFormCompanyComponent = ({ fieldRenderProps, country }) => { }; PayoutDetailsFormCompanyComponent.defaultProps = { - className: null, - country: null, - createStripeAccountError: null, + id: null, disabled: false, -<<<<<<< HEAD - inProgress: false, - ready: false, - submitButtonText: null, -======= ->>>>>>> 70cd0720... Merge to add personal address }; PayoutDetailsFormCompanyComponent.propTypes = { - className: string, - createStripeAccountError: object, + id: string, disabled: bool, -<<<<<<< HEAD - inProgress: bool, - ready: bool, - submitButtonText: string, -======= ->>>>>>> 70cd0720... Merge to add personal address // from injectIntl intl: intlShape.isRequired, diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js index 8ac9473d..55ffe0c9 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsFormIndividual.js @@ -1,5 +1,5 @@ import React from 'react'; -import { bool, object } from 'prop-types'; +import { bool } from 'prop-types'; import { compose } from 'redux'; import { injectIntl, intlShape } from 'react-intl'; @@ -18,15 +18,16 @@ const PayoutDetailsFormIndividualComponent = ({ fieldRenderProps }) => { disabled={disabled} values={values} country={country} + fieldId="individual" /> - + ); }; @@ -37,7 +38,6 @@ PayoutDetailsFormIndividualComponent.defaultProps = { PayoutDetailsFormIndividualComponent.propTypes = { disabled: bool, - form: object.isRequired, // from injectIntl intl: intlShape.isRequired, diff --git a/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js b/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js index 6cad411c..3b4e7026 100644 --- a/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js +++ b/src/forms/PayoutDetailsForm/PayoutDetailsPersonalDetails.js @@ -1,6 +1,5 @@ import React from 'react'; import { bool, string } from 'prop-types'; -import { FormattedMessage } from 'react-intl'; import { FieldBirthdayInput, FieldTextInput } from '../../components'; import * as validators from '../../util/validators'; import { intlShape } from 'react-intl'; @@ -13,6 +12,13 @@ const MIN_STRIPE_ACCOUNT_AGE = 18; const PayoutDetailsPersonalDetails = props => { const { intl, disabled, values, country, fieldId } = props; + const personalDetailsTitle = intl.formatMessage({ + id: + fieldId === 'company' || fieldId === 'individual' + ? 'PayoutDetailsForm.personalDetailsTitle' + : 'PayoutDetailsForm.personalDetailsAdditionalOwnerTitle', + }); + const firstNameLabel = intl.formatMessage({ id: 'PayoutDetailsForm.firstNameLabel' }); const firstNamePlaceholder = intl.formatMessage({ id: 'PayoutDetailsForm.firstNamePlaceholder', @@ -100,9 +106,7 @@ const PayoutDetailsPersonalDetails = props => { return (
-

- -

+

{personalDetailsTitle}

Date: Fri, 21 Dec 2018 11:49:13 +0200 Subject: [PATCH 12/15] Update IconClose so it can be used in smaller size --- src/components/IconClose/IconClose.example.js | 8 ++++++++ src/components/IconClose/IconClose.js | 14 +++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/components/IconClose/IconClose.example.js b/src/components/IconClose/IconClose.example.js index a369dff7..0bdfc3b8 100644 --- a/src/components/IconClose/IconClose.example.js +++ b/src/components/IconClose/IconClose.example.js @@ -5,3 +5,11 @@ export const Icon = { props: {}, group: 'icons', }; + +export const IconSmall = { + component: IconClose, + props: { + size: 'small', + }, + group: 'icons', +}; diff --git a/src/components/IconClose/IconClose.js b/src/components/IconClose/IconClose.js index b8c9858a..7ff131ca 100644 --- a/src/components/IconClose/IconClose.js +++ b/src/components/IconClose/IconClose.js @@ -3,11 +3,23 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import css from './IconClose.css'; +const SIZE_SMALL = 'small'; const IconClose = props => { - const { className, rootClassName } = props; + const { className, rootClassName, size } = props; const classes = classNames(rootClassName || css.root, className); + if (size === SIZE_SMALL) { + return ( + + + + ); + } + return ( Date: Fri, 21 Dec 2018 13:44:49 +0200 Subject: [PATCH 13/15] Add new translation keys to fr.json --- src/translations/fr.json | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/translations/fr.json b/src/translations/fr.json index 6d2c2089..893147a7 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -452,6 +452,9 @@ "PasswordResetPage.recoveryLinkText": "la page de réinitialisation du mot de passe", "PasswordResetPage.resetFailed": "La réinitialisation a échoué.", "PasswordResetPage.title": "Réinitialiser le mot de passe", + "PayoutDetailsForm.accountTypeTitle": "Account type", + "PayoutDetailsForm.additionalOwnerLabel": "+ Add additional owner", + "PayoutDetailsForm.additionalOwnerRemove": "Remove additional owner", "PayoutDetailsForm.addressTitle": "Adresse", "PayoutDetailsForm.bankDetails": "Détail du compte bancaire", "PayoutDetailsForm.birthdayDatePlaceholder": "jj", @@ -481,6 +484,35 @@ "PayoutDetailsForm.cityLabel": "Ville", "PayoutDetailsForm.cityPlaceholder": "Helsinki", "PayoutDetailsForm.cityRequired": "Ce champ est requis.", + "PayoutDetailsForm.companyAccount": "I represent a company", + "PayoutDetailsForm.companyAddressTitle": "Company address", + "PayoutDetailsForm.companyDetailsTitle": "Company details", + "PayoutDetailsForm.companyNameLabel": "Company name", + "PayoutDetailsForm.companyNamePlaceholder": "Enter company name...", + "PayoutDetailsForm.companyNameRequired": "Company name is required", + "PayoutDetailsForm.companyTaxIdLabel.AT": "Firmenbuchnummer (FN)", + "PayoutDetailsForm.companyTaxIdLabel.AU": "Company ACN/ABN - TFN", + "PayoutDetailsForm.companyTaxIdLabel.BE": "TVA/BTW/CBE", + "PayoutDetailsForm.companyTaxIdLabel.CA": "Business Number (Tax ID)", + "PayoutDetailsForm.companyTaxIdLabel.CH": "VAT number UID/MWST/TVA/IVA", + "PayoutDetailsForm.companyTaxIdLabel.DE": "Handelsregisternummer (HRB) ", + "PayoutDetailsForm.companyTaxIdLabel.DK": "Momsregistreringsnummer (CVR)", + "PayoutDetailsForm.companyTaxIdLabel.ES": "Número de Identificación Fiscal (NIF)", + "PayoutDetailsForm.companyTaxIdLabel.FI": "Y-tunnus", + "PayoutDetailsForm.companyTaxIdLabel.FR": "Numéro SIREN", + "PayoutDetailsForm.companyTaxIdLabel.GB": "Companies House Registration Number (CRN)", + "PayoutDetailsForm.companyTaxIdLabel.HK": "Registration Number", + "PayoutDetailsForm.companyTaxIdLabel.IE": "Company Number", + "PayoutDetailsForm.companyTaxIdLabel.IT": "Numero RI/ REA", + "PayoutDetailsForm.companyTaxIdLabel.LU": "Company/RCS number", + "PayoutDetailsForm.companyTaxIdLabel.NL": "KVK number", + "PayoutDetailsForm.companyTaxIdLabel.NO": "Organisasjonsnummer (Orgnr)", + "PayoutDetailsForm.companyTaxIdLabel.NZ": "NZBN", + "PayoutDetailsForm.companyTaxIdLabel.PT": "N.º Contribuinte", + "PayoutDetailsForm.companyTaxIdLabel.SE": "Organisationsnummer", + "PayoutDetailsForm.companyTaxIdLabel.US": "Tax ID", + "PayoutDetailsForm.companyTaxIdPlaceholder": "Enter {idName}...", + "PayoutDetailsForm.companyTaxIdRequired": "{idName} is required", "PayoutDetailsForm.countryLabel": "Pays", "PayoutDetailsForm.countryNames.AT": "Autriche", "PayoutDetailsForm.countryNames.AU": "Australie", @@ -510,10 +542,12 @@ "PayoutDetailsForm.firstNameLabel": "Prénom", "PayoutDetailsForm.firstNamePlaceholder": "Prénom", "PayoutDetailsForm.firstNameRequired": "Ce champ est requis.", + "PayoutDetailsForm.individualAccount": "I'm an individual", "PayoutDetailsForm.information": "Puisque c'est votre première annonce, nous devons en savoir un peu plus pour pouvoir vous transférer vos gains. Nous ne vous les demanderons qu'une seule fois.", "PayoutDetailsForm.lastNameLabel": "Nom", "PayoutDetailsForm.lastNamePlaceholder": "Nom", "PayoutDetailsForm.lastNameRequired": "Ce champ est requis.", + "PayoutDetailsForm.personalDetailsAdditionalOwnerTitle": "Additional owner details", "PayoutDetailsForm.personalDetailsTitle": "Détails", "PayoutDetailsForm.personalIdNumberLabel.HK": "Hong Kong Identity Card Number (HKID)", "PayoutDetailsForm.personalIdNumberLabel.US": "Quatre derniers chiffres du numéro de sécurité sociale (SSN)", From ea22f94d436302b1695ff1046ddb4771156b0384 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Mon, 7 Jan 2019 13:58:54 +0200 Subject: [PATCH 14/15] Synchronize white modal width --- .../EditListingWizard/EditListingWizard.css | 7 +++++++ .../EditListingWizard/EditListingWizard.js | 16 ++++++++-------- .../AuthenticationPage/AuthenticationPage.css | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/components/EditListingWizard/EditListingWizard.css b/src/components/EditListingWizard/EditListingWizard.css index d3b49235..85415243 100644 --- a/src/components/EditListingWizard/EditListingWizard.css +++ b/src/components/EditListingWizard/EditListingWizard.css @@ -106,3 +106,10 @@ .modalTitle { @apply --marketplaceModalTitleStyles; } + +.modalPayoutDetailsWrapper { + @media (--viewportMedium) { + width: 604px; + padding-top: 11px; + } +} diff --git a/src/components/EditListingWizard/EditListingWizard.js b/src/components/EditListingWizard/EditListingWizard.js index 262b3680..6871dc51 100644 --- a/src/components/EditListingWizard/EditListingWizard.js +++ b/src/components/EditListingWizard/EditListingWizard.js @@ -264,7 +264,7 @@ class EditListingWizard extends Component { onClose={this.handlePayoutModalClose} onManageDisableScrolling={onManageDisableScrolling} > -
+


@@ -273,14 +273,14 @@ class EditListingWizard extends Component {

+

-
); diff --git a/src/containers/AuthenticationPage/AuthenticationPage.css b/src/containers/AuthenticationPage/AuthenticationPage.css index 73b8ac16..104fe989 100644 --- a/src/containers/AuthenticationPage/AuthenticationPage.css +++ b/src/containers/AuthenticationPage/AuthenticationPage.css @@ -62,7 +62,7 @@ padding-top: 40px; @media (--viewportMedium) { - width: 571px; + width: 604px; padding-top: 11px; } } From d22da25c738a99ff4f8e38d81e8e40d049720497 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Fri, 21 Dec 2018 16:15:28 +0200 Subject: [PATCH 15/15] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9545d30..0833972a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ way to update this template, but currently, we follow a pattern: ## Upcoming version 2019-XX-XX +- [add] Support for Stripe company accounts. `PayoutDetailsForm` was separated into smaller + subcomponents. Multiple new translation keys were added and they might not be translated into + French yet. [#980](https://github.com/sharetribe/flex-template-web/pull/980) - Manage availability of listings. This works for listings that have booking unit type: 'line-item/night', or 'line-item/day'. There's also 'manage availability' link in the ManageListingCards of "your listings" page.