Skip to content

Commit

Permalink
Merge pull request #34326 from barttom/refactor/conect-bank-account-t…
Browse files Browse the repository at this point in the history
…s-migration

Refactor/conect bank account ts migration
  • Loading branch information
mountiny authored Jan 11, 2024
2 parents 10f17e6 + b5cd316 commit 4cdc1b6
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx/lib/types';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
Expand All @@ -11,37 +10,31 @@ import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import BankAccount from '@libs/models/BankAccount';
import EnableBankAccount from '@pages/ReimbursementAccount/EnableBankAccount/EnableBankAccount';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
import * as Report from '@userActions/Report';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Account, ReimbursementAccount} from '@src/types/onyx';
import BankAccountValidationForm from './components/BankAccountValidationForm';
import FinishChatCard from './components/FinishChatCard';

const propTypes = {
/** Bank account currently in setup */
reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired,

/** Handles back button press */
onBackButtonPress: PropTypes.func.isRequired,

type ConnectBankAccountOnyxProps = {
/** User's account who is setting up bank account */
account: PropTypes.shape({
/** If user has two-factor authentication enabled */
requiresTwoFactorAuth: PropTypes.bool,
}),
account: OnyxEntry<Account>;
};

const defaultProps = {
account: {
requiresTwoFactorAuth: false,
},
type ConnectBankAccountProps = ConnectBankAccountOnyxProps & {
/** Bank account currently in setup */
reimbursementAccount: ReimbursementAccount;

/** Handles back button press */
onBackButtonPress: () => void;
};

function ConnectBankAccount({reimbursementAccount, onBackButtonPress, account}) {
function ConnectBankAccount({reimbursementAccount, onBackButtonPress, account}: ConnectBankAccountProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

const bankAccountState = lodashGet(reimbursementAccount, 'achData.state', '');
const handleNavigateToConciergeChat = () => Report.navigateToConciergeChat();
const bankAccountState = reimbursementAccount.achData?.state ?? '';

// If a user tries to navigate directly to the validate page we'll show them the EnableStep
if (bankAccountState === BankAccount.STATE.OPEN) {
Expand All @@ -53,10 +46,10 @@ function ConnectBankAccount({reimbursementAccount, onBackButtonPress, account})
);
}

const maxAttemptsReached = lodashGet(reimbursementAccount, 'maxAttemptsReached', false);
const maxAttemptsReached = reimbursementAccount.maxAttemptsReached ?? false;
const isBankAccountVerifying = !maxAttemptsReached && bankAccountState === BankAccount.STATE.VERIFYING;
const isBankAccountPending = bankAccountState === BankAccount.STATE.PENDING;
const requiresTwoFactorAuth = lodashGet(account, 'requiresTwoFactorAuth', false);
const requiresTwoFactorAuth = account?.requiresTwoFactorAuth ?? false;

return (
<ScreenWrapper
Expand All @@ -74,7 +67,7 @@ function ConnectBankAccount({reimbursementAccount, onBackButtonPress, account})
<View style={[styles.m5, styles.flex1]}>
<Text>
{translate('connectBankAccountStep.maxAttemptsReached')} {translate('common.please')}{' '}
<TextLink onPress={Report.navigateToConciergeChat}>{translate('common.contactUs')}</TextLink>.
<TextLink onPress={handleNavigateToConciergeChat}>{translate('common.contactUs')}</TextLink>.
</Text>
</View>
)}
Expand All @@ -94,11 +87,9 @@ function ConnectBankAccount({reimbursementAccount, onBackButtonPress, account})
);
}

ConnectBankAccount.propTypes = propTypes;
ConnectBankAccount.defaultProps = defaultProps;
ConnectBankAccount.displayName = 'ConnectBankAccount';

export default withOnyx({
export default withOnyx<ConnectBankAccountProps, ConnectBankAccountOnyxProps>({
account: {
key: ONYXKEYS.ACCOUNT,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,56 @@
import Str from 'expensify-common/lib/str';
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {useCallback} from 'react';
import {View} from 'react-native';
import _ from 'underscore';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ValidationUtils from '@libs/ValidationUtils';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
import * as BankAccounts from '@userActions/BankAccounts';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {ReimbursementAccount} from '@src/types/onyx';
import type {FormValues} from '@src/types/onyx/Form';
import Enable2FACard from './Enable2FACard';

const propTypes = {
reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired,
requiresTwoFactorAuth: PropTypes.bool.isRequired,
type BankAccountValidationFormProps = {
/** Bank account currently in setup */
reimbursementAccount: ReimbursementAccount;

/** Boolean required to display Enable2FACard component */
requiresTwoFactorAuth: boolean;
};

/**
* Filter input for validation amount
* Anything that isn't a number is returned as an empty string
* Any dollar amount (e.g. 1.12) will be returned as 112
*
* @param {String} amount field input
* @returns {String}
*/
const filterInput = (amount) => {
let value = amount ? amount.toString().trim() : '';
if (value === '' || _.isNaN(Number(value)) || !Math.abs(Str.fromUSDToNumber(value))) {
const getAmountValues = (values: FormValues): Record<string, string> => ({
amount1: values?.amount1,
amount2: values?.amount2,
amount3: values?.amount3,
});

const filterInput = (amount: string) => {
const value = amount ? amount.trim() : '';
if (value === '' || Number.isNaN(Number(value)) || !Math.abs(Str.fromUSDToNumber(value, true))) {
return '';
}

// If the user enters the values in dollars, convert it to the respective cents amount
if (_.contains(value, '.')) {
value = Str.fromUSDToNumber(value);
if (value.includes('.')) {
return Str.fromUSDToNumber(value, true);
}

return value;
};

/**
* @param {Object} values - form input values passed by the Form component
* @returns {Object}
*/

const validate = (values) => {
const errors = {};
const validate = (values: FormValues) => {
const errors: Record<string, string> = {};
const amountValues = getAmountValues(values);

_.each(values, (value, key) => {
const filteredValue = typeof value === 'string' ? filterInput(value) : value;
if (ValidationUtils.isRequiredFulfilled(filteredValue)) {
Object.keys(amountValues).forEach((key) => {
const value = amountValues[key];
const filteredValue = filterInput(value);
if (ValidationUtils.isRequiredFulfilled(filteredValue.toString())) {
return;
}
errors[key] = 'common.error.invalidAmount';
Expand All @@ -62,28 +59,28 @@ const validate = (values) => {
return errors;
};

function BankAccountValidationForm({requiresTwoFactorAuth, reimbursementAccount}) {
function BankAccountValidationForm({requiresTwoFactorAuth, reimbursementAccount}: BankAccountValidationFormProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();

/**
* @param {Object} values - form input values passed by the Form component
*/
const submit = useCallback(
(values) => {
(values: FormValues) => {
const amount1 = filterInput(values.amount1);
const amount2 = filterInput(values.amount2);
const amount3 = filterInput(values.amount3);

const validateCode = [amount1, amount2, amount3].join(',');

// Send valid amounts to BankAccountAPI::validateBankAccount in Web-Expensify
const bankAccountID = lodashGet(reimbursementAccount, 'achData.bankAccountID');
BankAccounts.validateBankAccount(bankAccountID, validateCode);
const bankAccountID = reimbursementAccount?.achData?.bankAccountID;
if (bankAccountID) {
BankAccounts.validateBankAccount(bankAccountID, validateCode);
}
},
[reimbursementAccount],
);
return (
// @ts-expect-error TODO: Remove this once Form (https://github.com/Expensify/App/issues/31972) is migrated to TypeScript
<FormProvider
formID={ONYXKEYS.REIMBURSEMENT_ACCOUNT}
submitButtonText={translate('connectBankAccountStep.validateButtonText')}
Expand All @@ -95,28 +92,31 @@ function BankAccountValidationForm({requiresTwoFactorAuth, reimbursementAccount}
<Text>{translate('connectBankAccountStep.descriptionCTA')}</Text>

<View style={[styles.mv5]}>
<TextInput
<InputWrapper
// @ts-expect-error TODO: Remove this once InputWrapper (https://github.com/Expensify/App/issues/31972) is migrated to TypeScript
InputComponent={TextInput}
inputID="amount1"
shouldSaveDraft
containerStyles={[styles.mb1]}
inputMode={CONST.INPUT_MODE.DECIMAL}
role={CONST.ACCESSIBILITY_ROLE.TEXT}
label={`${translate('connectBankAccountStep.validationInputLabel')} 1`}
/>
<TextInput
<InputWrapper
// @ts-expect-error TODO: Remove this once InputWrapper (https://github.com/Expensify/App/issues/31972) is migrated to TypeScript
InputComponent={TextInput}
inputID="amount2"
shouldSaveDraft
containerStyles={[styles.mb1]}
inputMode={CONST.INPUT_MODE.DECIMAL}
role={CONST.ACCESSIBILITY_ROLE.TEXT}
label={`${translate('connectBankAccountStep.validationInputLabel')} 2`}
/>
<TextInput
<InputWrapper
// @ts-expect-error TODO: Remove this once InputWrapper (https://github.com/Expensify/App/issues/31972) is migrated to TypeScript
InputComponent={TextInput}
shouldSaveDraft
inputID="amount3"
containerStyles={[styles.mb1]}
inputMode={CONST.INPUT_MODE.DECIMAL}
role={CONST.ACCESSIBILITY_ROLE.TEXT}
label={`${translate('connectBankAccountStep.validationInputLabel')} 3`}
/>
</View>
Expand All @@ -129,7 +129,6 @@ function BankAccountValidationForm({requiresTwoFactorAuth, reimbursementAccount}
);
}

BankAccountValidationForm.propTypes = propTypes;
BankAccountValidationForm.displayName = 'BankAccountValidationForm';

export default BankAccountValidationForm;
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React from 'react';
import {ScrollView} from 'react-native';
import Button from '@components/Button';
Expand All @@ -10,21 +8,25 @@ import Section from '@components/Section';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
import WorkspaceResetBankAccountModal from '@pages/workspace/WorkspaceResetBankAccountModal';
import * as BankAccounts from '@userActions/BankAccounts';
import * as Report from '@userActions/Report';
import type {ReimbursementAccount} from '@src/types/onyx';
import Enable2FACard from './Enable2FACard';

const propTypes = {
requiresTwoFactorAuth: PropTypes.bool.isRequired,
reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired,
type FinishChatCardProps = {
/** Bank account currently in setup */
reimbursementAccount: ReimbursementAccount;

/** Boolean required to display Enable2FACard component */
requiresTwoFactorAuth: boolean;
};

function FinishChatCard({requiresTwoFactorAuth, reimbursementAccount}) {
function FinishChatCard({requiresTwoFactorAuth, reimbursementAccount}: FinishChatCardProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const shouldShowResetModal = lodashGet(reimbursementAccount, 'shouldShowResetModal', false);
const shouldShowResetModal = reimbursementAccount.shouldShowResetModal ?? false;
const handleNavigateToConciergeChat = () => Report.navigateToConciergeChat();

return (
<ScrollView style={[styles.flex1]}>
Expand All @@ -37,7 +39,7 @@ function FinishChatCard({requiresTwoFactorAuth, reimbursementAccount}) {
<Text style={styles.mb6}>{translate('connectBankAccountStep.letsChatText')}</Text>
<Button
text={translate('connectBankAccountStep.letsChatCTA')}
onPress={Report.navigateToConciergeChat}
onPress={handleNavigateToConciergeChat}
icon={Expensicons.ChatBubble}
iconStyles={[styles.buttonCTAIcon]}
shouldShowRightIcon
Expand All @@ -58,7 +60,6 @@ function FinishChatCard({requiresTwoFactorAuth, reimbursementAccount}) {
);
}

FinishChatCard.propTypes = propTypes;
FinishChatCard.displayName = 'FinishChatCard';

export default FinishChatCard;

0 comments on commit 4cdc1b6

Please sign in to comment.