From a49d8ce7802ebdc5b47f5a4e68b557a2c70d8a09 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 Jan 2024 10:23:07 -0700 Subject: [PATCH 1/7] Add a component prop to override button text --- src/components/ButtonWithDropdownMenu.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/ButtonWithDropdownMenu.tsx b/src/components/ButtonWithDropdownMenu.tsx index 466c68229a32..0afe48a34512 100644 --- a/src/components/ButtonWithDropdownMenu.tsx +++ b/src/components/ButtonWithDropdownMenu.tsx @@ -26,6 +26,12 @@ type DropdownOption = { }; type ButtonWithDropdownMenuProps = { + /** + * This text will be used for the main button text. If this option is ommitted, then the text of the first selected option is used for the button text instead. + * Use this when you want to display a different text for the button than the text of the first selected option. + */ + text?: string; + /** Text to display for the menu header */ menuHeaderText?: string; @@ -63,6 +69,7 @@ function ButtonWithDropdownMenu({ isDisabled = false, pressOnEnter = false, menuHeaderText = '', + text = '', style, buttonSize = CONST.DROPDOWN_BUTTON_SIZE.MEDIUM, anchorAlignment = { @@ -114,7 +121,7 @@ function ButtonWithDropdownMenu({ pressOnEnter={pressOnEnter} ref={buttonRef} onPress={(event) => onPress(event, selectedItem.value)} - text={selectedItem.text} + text={text || selectedItem.text} isDisabled={isDisabled} isLoading={isLoading} shouldRemoveRightBorderRadius @@ -154,7 +161,7 @@ function ButtonWithDropdownMenu({ isDisabled={isDisabled} style={[styles.w100, style]} isLoading={isLoading} - text={selectedItem.text} + text={text || selectedItem.text} onPress={(event) => onPress(event, options[0].value)} large={isButtonSizeLarge} medium={!isButtonSizeLarge} @@ -185,4 +192,4 @@ function ButtonWithDropdownMenu({ ButtonWithDropdownMenu.displayName = 'ButtonWithDropdownMenu'; -export default React.memo(ButtonWithDropdownMenu); +1; From b940af944e22241c4e26d02760b18de7b084308d Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 Jan 2024 10:39:39 -0700 Subject: [PATCH 2/7] Add customized button text --- src/components/ButtonWithDropdownMenu.tsx | 2 +- src/components/MoneyReportHeader.tsx | 2 ++ src/components/SettlementButton.js | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/ButtonWithDropdownMenu.tsx b/src/components/ButtonWithDropdownMenu.tsx index 0afe48a34512..d5a89cde6bf3 100644 --- a/src/components/ButtonWithDropdownMenu.tsx +++ b/src/components/ButtonWithDropdownMenu.tsx @@ -192,4 +192,4 @@ function ButtonWithDropdownMenu({ ButtonWithDropdownMenu.displayName = 'ButtonWithDropdownMenu'; -1; +export default React.memo(ButtonWithDropdownMenu); diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index a712438bb07c..2d0c118b845c 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -157,6 +157,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt shouldShowApproveButton={shouldShowApproveButton} style={[styles.pv2]} formattedAmount={formattedAmount} + buttonText={`${translate('iou.pay')} ${formattedAmount}`} /> )} @@ -188,6 +189,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} formattedAmount={formattedAmount} + buttonText={`${translate('iou.pay')} ${formattedAmount}`} /> )} diff --git a/src/components/SettlementButton.js b/src/components/SettlementButton.js index 0c8e193af4cc..460b66f56d9d 100644 --- a/src/components/SettlementButton.js +++ b/src/components/SettlementButton.js @@ -55,6 +55,9 @@ const propTypes = { /** Total money amount in form */ formattedAmount: PropTypes.string, + /** The text to be displayed on the main button */ + buttonText: PropTypes.string, + /** The size of button size */ buttonSize: PropTypes.oneOf(_.values(CONST.DROPDOWN_BUTTON_SIZE)), @@ -104,6 +107,7 @@ const defaultProps = { style: [], policyID: '', formattedAmount: '', + buttonText: '', buttonSize: CONST.DROPDOWN_BUTTON_SIZE.MEDIUM, kycWallAnchorAlignment: { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, // button is at left, so horizontal anchor is at LEFT @@ -129,6 +133,7 @@ function SettlementButton({ isDisabled, isLoading, formattedAmount, + buttonText, nvp_lastPaymentMethod, onPress, pressOnEnter, @@ -238,6 +243,7 @@ function SettlementButton({ style={style} buttonSize={buttonSize} anchorAlignment={paymentMethodDropdownAnchorAlignment} + text={buttonText} /> )} From 3f9e1d3765da3e0c5d078157e86e9fbfa8d4b3f4 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 Jan 2024 11:01:12 -0700 Subject: [PATCH 3/7] Add new method with possible payment options --- src/CONST.ts | 1 + src/components/SettlementButton.js | 52 ++++++++++++++++++++++++------ src/languages/en.ts | 3 ++ src/languages/es.ts | 3 ++ 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index d6f3d3cdcef6..a39fc773318e 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1167,6 +1167,7 @@ const CONST = { DEBIT_CARD: 'debitCard', PERSONAL_BANK_ACCOUNT: 'bankAccount', BUSINESS_BANK_ACCOUNT: 'businessBankAccount', + ELSEWHERE: 'elsewhere', }, PAYMENT_METHOD_ID_KEYS: { diff --git a/src/components/SettlementButton.js b/src/components/SettlementButton.js index 460b66f56d9d..025556ff1fa6 100644 --- a/src/components/SettlementButton.js +++ b/src/components/SettlementButton.js @@ -150,6 +150,40 @@ function SettlementButton({ PaymentMethods.openWalletPage(); }, []); + const paymentButtonOptionsV2 = useMemo(() => { + const approveButtonOption = { + text: translate('iou.approve'), + icon: Expensicons.ThumbsUp, + value: CONST.IOU.REPORT_ACTION_TYPE.APPROVE, + }; + + // Only show the Approve button if the user cannot pay the request + if (shouldHidePaymentOptions && shouldShowApproveButton) { + return [approveButtonOption]; + } + + const payWithBusinessBankAccountOption = { + text: translate('iou.payWithBusinessBankAccount'), + icon: Expensicons.Bank, + value: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT, + }; + const payWithPersonalBankAccountOption = { + text: translate('iou.payWithPersonalBankAccount'), + icon: Expensicons.Building, + value: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, + }; + const payWithDebitCardOption = { + text: translate('iou.payWithDebitCard'), + icon: Expensicons.CreditCard, + value: CONST.PAYMENT_METHODS.DEBIT_CARD, + }; + const payElsewhereOption = { + text: translate('iou.payElsewhere'), + icon: Expensicons.Cash, + value: CONST.PAYMENT_METHODS.ELSEWHERE, + }; + }, [translate, shouldHidePaymentOptions, shouldShowApproveButton]); + const paymentButtonOptions = useMemo(() => { const buttonOptions = []; const isExpenseReport = ReportUtils.isExpenseReport(iouReport); @@ -170,17 +204,17 @@ function SettlementButton({ value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, }, }; - const approveButtonOption = { - text: translate('iou.approve'), - icon: Expensicons.ThumbsUp, - value: CONST.IOU.REPORT_ACTION_TYPE.APPROVE, - }; + // const approveButtonOption = { + // text: translate('iou.approve'), + // icon: Expensicons.ThumbsUp, + // value: CONST.IOU.REPORT_ACTION_TYPE.APPROVE, + // }; const canUseWallet = !isExpenseReport && currency === CONST.CURRENCY.USD; - // Only show the Approve button if the user cannot pay the request - if (shouldHidePaymentOptions && shouldShowApproveButton) { - return [approveButtonOption]; - } + // // Only show the Approve button if the user cannot pay the request + // if (shouldHidePaymentOptions && shouldShowApproveButton) { + // return [approveButtonOption]; + // } // To achieve the one tap pay experience we need to choose the correct payment type as default, // if user already paid for some request or expense, let's use the last payment method or use default. diff --git a/src/languages/en.ts b/src/languages/en.ts index e9a698fd4d82..65d44f1898d1 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -596,6 +596,9 @@ export default { settledExpensify: 'Paid', settledElsewhere: 'Paid elsewhere', settleExpensify: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with Expensify` : `Pay with Expensify`), + payWithBusinessBankAccount: 'Pay with business bank account', + payWithPersonalBankAccount: 'Pay with personal bank account', + payWithDebitCard: 'Pay with debit card', payElsewhere: 'Pay elsewhere', nextStep: 'Next Steps', finished: 'Finished', diff --git a/src/languages/es.ts b/src/languages/es.ts index 97cc7077c4e2..1077f715fc77 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -589,6 +589,9 @@ export default { settledExpensify: 'Pagado', settledElsewhere: 'Pagado de otra forma', settleExpensify: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} con Expensify` : `Pagar con Expensify`), + payWithBusinessBankAccount: 'Pay with business bank account', + payWithPersonalBankAccount: 'Pay with personal bank account', + payWithDebitCard: 'Pay with debit card', payElsewhere: 'Pagar de otra forma', nextStep: 'Pasos Siguientes', finished: 'Finalizado', From 7f27b9dc9ea5c0ada70c2bf83f307cb0c4b9d3ae Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 Jan 2024 11:16:11 -0700 Subject: [PATCH 4/7] Show all possible options in dropdown --- src/components/SettlementButton.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/SettlementButton.js b/src/components/SettlementButton.js index 025556ff1fa6..7066356a564f 100644 --- a/src/components/SettlementButton.js +++ b/src/components/SettlementButton.js @@ -162,6 +162,8 @@ function SettlementButton({ return [approveButtonOption]; } + const buttonOptions = []; + const payWithBusinessBankAccountOption = { text: translate('iou.payWithBusinessBankAccount'), icon: Expensicons.Bank, @@ -182,6 +184,16 @@ function SettlementButton({ icon: Expensicons.Cash, value: CONST.PAYMENT_METHODS.ELSEWHERE, }; + + buttonOptions.push(payWithBusinessBankAccountOption); + buttonOptions.push(payWithPersonalBankAccountOption); + buttonOptions.push(payWithDebitCardOption); + buttonOptions.push(payElsewhereOption); + + if (shouldShowApproveButton) { + buttonOptions.push(approveButtonOption); + } + return buttonOptions; }, [translate, shouldHidePaymentOptions, shouldShowApproveButton]); const paymentButtonOptions = useMemo(() => { @@ -273,7 +285,7 @@ function SettlementButton({ isLoading={isLoading} onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)} pressOnEnter={pressOnEnter} - options={paymentButtonOptions} + options={paymentButtonOptionsV2} style={style} buttonSize={buttonSize} anchorAlignment={paymentMethodDropdownAnchorAlignment} From b54cb8a92bdcc1046e2271e4716832ae11fac53d Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 Jan 2024 11:19:31 -0700 Subject: [PATCH 5/7] Add conditional check for business bank account option --- src/components/SettlementButton.js | 34 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/components/SettlementButton.js b/src/components/SettlementButton.js index 7066356a564f..d4b399eb11a5 100644 --- a/src/components/SettlementButton.js +++ b/src/components/SettlementButton.js @@ -1,3 +1,4 @@ +import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useEffect, useMemo} from 'react'; import {withOnyx} from 'react-native-onyx'; @@ -5,6 +6,7 @@ import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import compose from '@libs/compose'; +import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import iouReportPropTypes from '@pages/iouReportPropTypes'; import * as BankAccounts from '@userActions/BankAccounts'; @@ -43,9 +45,6 @@ const propTypes = { /** Should we show the payment options? */ shouldShowApproveButton: PropTypes.bool, - /** The last payment method used per policy */ - nvp_lastPaymentMethod: PropTypes.objectOf(PropTypes.string), - /** The policyID of the report we are paying */ policyID: PropTypes.string, @@ -87,6 +86,17 @@ const propTypes = { /** Whether the personal bank account option should be shown */ shouldShowPersonalBankAccountOption: PropTypes.bool, + + /* Onyx Props */ + + /** The last payment method used per policy */ + nvp_lastPaymentMethod: PropTypes.objectOf(PropTypes.string), + + /** Session info for the currently logged in user. */ + session: PropTypes.shape({ + /** Currently logged in user accountID */ + accountID: PropTypes.number, + }), }; const defaultProps = { @@ -117,6 +127,7 @@ const defaultProps = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, // caret for dropdown is at right, so horizontal anchor is at RIGHT vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, // we assume that popover menu opens below the button, anchor is at TOP }, + session: {}, shouldShowPersonalBankAccountOption: false, }; @@ -138,6 +149,7 @@ function SettlementButton({ onPress, pressOnEnter, policyID, + session, shouldHidePaymentOptions, shouldShowApproveButton, style, @@ -185,7 +197,16 @@ function SettlementButton({ value: CONST.PAYMENT_METHODS.ELSEWHERE, }; - buttonOptions.push(payWithBusinessBankAccountOption); + // Users can choose to pay with business bank account in case of Expense reports or in case of P2P IOU report + // which then starts a bottom up flow and creates a Collect workspace where the payer is an admin and payee is an employee. + const canUseBusinessBankAccount = + ReportUtils.isExpenseReport(iouReport) || + (ReportUtils.isIOUReport(iouReport) && !ReportActionsUtils.hasRequestFromCurrentAccount(lodashGet(iouReport, 'reportID', 0), lodashGet(session, 'accountID', 0))); + + if (canUseBusinessBankAccount) { + buttonOptions.push(payWithBusinessBankAccountOption); + } + buttonOptions.push(payWithPersonalBankAccountOption); buttonOptions.push(payWithDebitCardOption); buttonOptions.push(payElsewhereOption); @@ -194,7 +215,7 @@ function SettlementButton({ buttonOptions.push(approveButtonOption); } return buttonOptions; - }, [translate, shouldHidePaymentOptions, shouldShowApproveButton]); + }, [iouReport, translate, shouldHidePaymentOptions, shouldShowApproveButton]); const paymentButtonOptions = useMemo(() => { const buttonOptions = []; @@ -303,6 +324,9 @@ SettlementButton.displayName = 'SettlementButton'; export default compose( withNavigation, withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, nvp_lastPaymentMethod: { key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD, }, From bedb2aefb0fc4510ed776a4d72ead5794615c243 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 Jan 2024 11:20:18 -0700 Subject: [PATCH 6/7] Add conditional check for personal bank account option --- src/components/SettlementButton.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/SettlementButton.js b/src/components/SettlementButton.js index d4b399eb11a5..9f4023970f89 100644 --- a/src/components/SettlementButton.js +++ b/src/components/SettlementButton.js @@ -207,7 +207,11 @@ function SettlementButton({ buttonOptions.push(payWithBusinessBankAccountOption); } - buttonOptions.push(payWithPersonalBankAccountOption); + const canUsePersonalBankAccount = shouldShowPersonalBankAccountOption || ReportUtils.isIOUReport(iouReport); + if (canUsePersonalBankAccount) { + buttonOptions.push(payWithPersonalBankAccountOption); + } + buttonOptions.push(payWithDebitCardOption); buttonOptions.push(payElsewhereOption); From dfa7397b172244a188fb224983a43e681e1bdd2f Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 Jan 2024 11:21:37 -0700 Subject: [PATCH 7/7] Simplify logic --- src/components/SettlementButton.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/SettlementButton.js b/src/components/SettlementButton.js index 9f4023970f89..1b2ac93cd533 100644 --- a/src/components/SettlementButton.js +++ b/src/components/SettlementButton.js @@ -199,16 +199,14 @@ function SettlementButton({ // Users can choose to pay with business bank account in case of Expense reports or in case of P2P IOU report // which then starts a bottom up flow and creates a Collect workspace where the payer is an admin and payee is an employee. - const canUseBusinessBankAccount = + if ( ReportUtils.isExpenseReport(iouReport) || - (ReportUtils.isIOUReport(iouReport) && !ReportActionsUtils.hasRequestFromCurrentAccount(lodashGet(iouReport, 'reportID', 0), lodashGet(session, 'accountID', 0))); - - if (canUseBusinessBankAccount) { + (ReportUtils.isIOUReport(iouReport) && !ReportActionsUtils.hasRequestFromCurrentAccount(lodashGet(iouReport, 'reportID', 0), lodashGet(session, 'accountID', 0))) + ) { buttonOptions.push(payWithBusinessBankAccountOption); } - const canUsePersonalBankAccount = shouldShowPersonalBankAccountOption || ReportUtils.isIOUReport(iouReport); - if (canUsePersonalBankAccount) { + if (shouldShowPersonalBankAccountOption || ReportUtils.isIOUReport(iouReport)) { buttonOptions.push(payWithPersonalBankAccountOption); } @@ -219,7 +217,7 @@ function SettlementButton({ buttonOptions.push(approveButtonOption); } return buttonOptions; - }, [iouReport, translate, shouldHidePaymentOptions, shouldShowApproveButton]); + }, [session, iouReport, translate, shouldShowPersonalBankAccountOption, shouldHidePaymentOptions, shouldShowApproveButton]); const paymentButtonOptions = useMemo(() => { const buttonOptions = [];