Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[No QA] Add BeneficialOwnersStep (UI Only) #3585

Merged
merged 14 commits into from
Jun 18, 2021
20 changes: 17 additions & 3 deletions src/components/TextLink.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
import _ from 'underscore';
import React from 'react';
import PropTypes from 'prop-types';
import {Text, Pressable, Linking} from 'react-native';
import {Pressable, Linking} from 'react-native';
import Text from './Text';
import styles from '../styles/styles';

const propTypes = {
/** Link to open in new tab */
href: PropTypes.string.isRequired,
href: PropTypes.string,

/** Text content child */
children: PropTypes.string.isRequired,
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array,
]).isRequired,

/** Additional style props */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object)]),

/** Overwrites the default link behavior with a custom callback */
onPress: PropTypes.func,
};

const defaultProps = {
href: '',
style: [],
onPress: undefined,
};

const TextLink = (props) => {
Expand All @@ -25,6 +34,11 @@ const TextLink = (props) => {
<Pressable
onPress={(e) => {
e.preventDefault();
if (props.onPress) {
props.onPress();
return;
}

Linking.openURL(props.href);
}}
accessibilityRole="link"
Expand Down
15 changes: 14 additions & 1 deletion src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default {
isRequiredField: 'is a required field',
whatThis: 'What\'s this?',
invite: 'Invite',
iAcceptThe: 'I accept the ',
passwordCannotBeBlank: 'Password cannot be blank',
},
attachmentPicker: {
Expand Down Expand Up @@ -287,7 +288,6 @@ export default {
toGetStarted: 'To get started with the Expensify Card, you first need to add a bank account.',
plaidBodyCopy: 'Give your employees an easier way to pay - and get paid back - for company expenses.',
checkHelpLine: 'Your routing number and account number can be found on a check for the account.',
iAcceptThe: 'I accept the ',
hasPhoneLoginError: 'To add a verified bank account please ensure your primary login is a valid email and try again. You can add your phone number as a secondary login.',
hasBeenThrottledError: ({fromNow}) => `For security reasons, we're taking a break from bank account setup so you can double-check your company information. Please try again ${fromNow}. Sorry!`,
},
Expand Down Expand Up @@ -363,6 +363,19 @@ export default {
ssnLast4: 'Last 4 Digits of SSN',
isAuthorized: 'I am authorized to use my company bank account for business spend',
},
beneficialOwnersStep: {
beneficialOwners: 'Beneficial Owners',
additionalInformation: 'Additional Information',
checkAllThatApply: '(check all that apply, otherwise leave blank)',
iOwnMoreThan25Percent: 'I own more than 25% of ',
someoneOwnsMoreThan25Percent: 'Somebody else owns more than 25% of ',
additionalOwner: 'Additional Beneficial Owner',
removeOwner: 'Remove this beneficial owner',
addAnotherIndividual: 'Add another individual who owns more than 25% of ',
agreement: 'Agreement:',
termsAndConditions: 'terms and conditions',
certifyTrueAndAccurate: 'I certify that the information provided is true and accurate',
},
session: {
offlineMessage: 'Looks like you\'re not connected to internet. Can you check your connection and try again?',
},
Expand Down
15 changes: 14 additions & 1 deletion src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default {
isRequiredField: 'es un campo obligatorio',
whatThis: '¿Qué es esto?',
invite: 'Invitar',
iAcceptThe: 'Acepto los ',
passwordCannotBeBlank: 'La contraseña no puede estar vacía',
},
attachmentPicker: {
Expand Down Expand Up @@ -280,7 +281,6 @@ export default {
toGetStarted: 'Para comenzar con la tarjeta Expensify, primero debe agregar una cuenta bancaria.',
plaidBodyCopy: 'Ofrezca a sus empleados una forma más sencilla de pagar - y recuperar - los gastos de la empresa.',
checkHelpLine: 'Su número de ruta y número de cuenta se pueden encontrar en un cheque para la cuenta.',
iAcceptThe: 'Acepto los ',
hasPhoneLoginError: 'Para agregar una cuenta bancaria verificada, asegúrese de que su inicio de sesión principal sea un correo electrónico válido y vuelva a intentarlo. Puede agregar su número de teléfono como inicio de sesión secundario.',
hasBeenThrottledError: ({fromNow}) => `Por razones de seguridad, nos tomamos un descanso de la configuración de la cuenta bancaria para que pueda verificar la información de su empresa. Inténtalo de nuevo ${fromNow}. ¡Lo siento!`,
},
Expand All @@ -304,6 +304,19 @@ export default {
noPhoneNumber: 'Por favor escribe un número de teléfono que incluya el código de país e.g +447814266907',
maxParticipantsReached: 'Has llegado al número máximo de participantes para un grupo.',
},
beneficialOwnersStep: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's occurred to me that I forgot to add the requestorStep values to this file 🙈

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this left in english if this is the spanish file?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we started this there was pressure to get it finished within 2 weeks so we moved very quickly and Spanish translations were not prioritized.

beneficialOwners: 'Beneficial Owners',
additionalInformation: 'Additional Information',
checkAllThatApply: '(check all that apply, otherwise leave blank)',
iOwnMoreThan25Percent: 'I own more than 25% of ',
someoneOwnsMoreThan25Percent: 'Somebody else owns more than 25% of ',
additionalOwner: 'Additional Beneficial Owner',
removeOwner: 'Remove this beneficial owner',
addAnotherIndividual: 'Add another individual who owns more than 25% of ',
agreement: 'Agreement:',
termsAndConditions: 'terms and conditions',
certifyTrueAndAccurate: 'I certify that the information provided is true and accurate',
},
session: {
offlineMessage: 'Parece que no estás conectado a internet. Comprueba tu conexión e inténtalo de nuevo.',
},
Expand Down
5 changes: 0 additions & 5 deletions src/pages/ReimbursementAccount/ACHContractStep.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/pages/ReimbursementAccount/BankAccountStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ class BankAccountStep extends React.Component {
LabelComponent={() => (
<View style={[styles.flexRow, styles.alignItemsCenter]}>
<Text>
{this.props.translate('bankAccount.iAcceptThe')}
{this.props.translate('common.iAcceptThe')}
</Text>
<TextLink href="https://use.expensify.com/terms">
{`Expensify ${this.props.translate('common.termsOfService')}`}
Expand Down
198 changes: 198 additions & 0 deletions src/pages/ReimbursementAccount/BeneficialOwnersStep.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import _ from 'underscore';
import React from 'react';
import PropTypes from 'prop-types';
import {ScrollView, View} from 'react-native';
import Text from '../../components/Text';
import HeaderWithCloseButton from '../../components/HeaderWithCloseButton';
import styles from '../../styles/styles';
import CheckboxWithLabel from '../../components/CheckboxWithLabel';
import TextLink from '../../components/TextLink';
import Button from '../../components/Button';
import IdentityForm from './IdentityForm';
import FixedFooter from '../../components/FixedFooter';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
import {goToWithdrawalAccountSetupStep, setupWithdrawalAccount} from '../../libs/actions/BankAccounts';
import Navigation from '../../libs/Navigation/Navigation';
import CONST from '../../CONST';

const propTypes = {
companyName: PropTypes.string,

...withLocalizePropTypes,
};

const defaultProps = {
companyName: 'Company Name',
};

class BeneficialOwnersStep extends React.Component {
constructor(props) {
super(props);

this.addBeneficialOwner = this.addBeneficialOwner.bind(this);
this.submit = this.submit.bind(this);

this.state = {
ownsMoreThan25Percent: false,
hasOtherBeneficialOwners: false,
acceptTermsAndConditions: false,
certifyTrueInformation: false,
beneficialOwners: [],
};
}

removeBeneficialOwner(beneficialOwner) {
this.setState(prevState => ({beneficialOwners: _.without(prevState.beneficialOwners, beneficialOwner)}));
}

addBeneficialOwner() {
this.setState(prevState => ({beneficialOwners: [...prevState.beneficialOwners, {}]}));
}

/**
* @returns {Boolean}
*/
canAddMoreBeneficialOwners() {
return _.size(this.state.beneficialOwners) < 3
|| (_.size(this.state.beneficialOwners) === 3 && !this.state.ownsMoreThan25Percent);
}

submit() {
setupWithdrawalAccount({...this.state});
}

render() {
return (
<>
<HeaderWithCloseButton
title={this.props.translate('beneficialOwnersStep.beneficialOwners')}
onCloseButtonPress={Navigation.dismissModal}
onBackButtonPress={() => goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR)}
shouldShowBackButton
/>
<ScrollView style={[styles.flex1, styles.w100, styles.ph5]}>
<Text style={[styles.mb5]}>
<Text style={[styles.textStrong]}>
{`${this.props.translate('beneficialOwnersStep.additionalInformation')}: `}
</Text>
<Text>{this.props.translate('beneficialOwnersStep.checkAllThatApply')}</Text>
</Text>
<CheckboxWithLabel
style={[styles.mb2, styles.mr2]}
isChecked={this.state.ownsMoreThan25Percent}
onPress={() => this.setState(prevState => ({
ownsMoreThan25Percent: !prevState.ownsMoreThan25Percent,
}))}
LabelComponent={() => (
<Text>
{this.props.translate('beneficialOwnersStep.iOwnMoreThan25Percent')}
<Text style={[styles.textStrong]}>{this.props.companyName}</Text>
</Text>
)}
/>
<CheckboxWithLabel
style={[styles.mb2, styles.mr2]}
isChecked={this.state.hasOtherBeneficialOwners}
onPress={() => {
this.setState((prevState) => {
const hasOtherBeneficialOwners = !prevState.hasOtherBeneficialOwners;
return {
hasOtherBeneficialOwners,
beneficialOwners: hasOtherBeneficialOwners && _.isEmpty(prevState.beneficialOwners)
? [{}]
: prevState.beneficialOwners,
};
});
}}
LabelComponent={() => (
<Text>
{this.props.translate('beneficialOwnersStep.someoneOwnsMoreThan25Percent')}
<Text style={[styles.textStrong]}>{this.props.companyName}</Text>
</Text>
)}
/>
{this.state.hasOtherBeneficialOwners && (
<View style={[styles.mb2]}>
{_.map(this.state.beneficialOwners, (owner, index) => (
<View key={index} style={[styles.p5, styles.border, styles.mb2]}>
<Text style={[styles.textStrong, styles.mb2]}>
{this.props.translate('beneficialOwnersStep.additionalOwner')}
</Text>
<IdentityForm
style={[styles.mb2]}
onFieldChange={(fieldName, value) => this.setState((prevState) => {
const beneficialOwners = [...prevState.beneficialOwners];
beneficialOwners[index][fieldName] = value;
return {beneficialOwners};
})}
values={{
firstName: owner.firstName || '',
lastName: owner.lastName || '',
street: owner.street || '',
city: owner.city || '',
state: owner.state || '',
zipCode: owner.zipCode || '',
dob: owner.dob || '',
ssnLast4: owner.ssnLast4 || '',
}}
/>
{this.state.beneficialOwners.length > 1 && (
<TextLink onPress={() => this.removeBeneficialOwner(owner)}>
{this.props.translate('beneficialOwnersStep.removeOwner')}
</TextLink>
)}
</View>
))}
{this.canAddMoreBeneficialOwners() && (
<TextLink onPress={this.addBeneficialOwner}>
{this.props.translate('beneficialOwnersStep.addAnotherIndividual')}
<Text style={[styles.textStrong, styles.link]}>{this.props.companyName}</Text>
</TextLink>
)}
</View>
)}
<Text style={[styles.textStrong, styles.mb5]}>
{this.props.translate('beneficialOwnersStep.agreement')}
</Text>
<CheckboxWithLabel
style={[styles.mb2]}
isChecked={this.state.acceptTermsAndConditions}
onPress={() => this.setState(prevState => ({
acceptTermsAndConditions: !prevState.acceptTermsAndConditions,
}))}
LabelComponent={() => (
<View style={[styles.flexRow]}>
<Text>{this.props.translate('common.iAcceptThe')}</Text>
<TextLink href="https://use.expensify.com/achterms">
{`${this.props.translate('beneficialOwnersStep.termsAndConditions')}.`}
</TextLink>
</View>
)}
/>
<CheckboxWithLabel
style={[styles.mb2]}
isChecked={this.state.certifyTrueInformation}
onPress={() => this.setState(prevState => ({
certifyTrueInformation: !prevState.certifyTrueInformation,
}))}
LabelComponent={() => (
<Text>{this.props.translate('beneficialOwnersStep.certifyTrueAndAccurate')}</Text>
)}
/>
</ScrollView>
<FixedFooter style={[styles.mt5]}>
<Button
success
text={this.props.translate('common.saveAndContinue')}
onPress={this.submit}
/>
</FixedFooter>
</>
);
}
}

BeneficialOwnersStep.propTypes = propTypes;
BeneficialOwnersStep.defaultProps = defaultProps;

export default withLocalize(BeneficialOwnersStep);
Loading