diff --git a/src/CONST.ts b/src/CONST.ts index 3b879e10c345..59d08f46f940 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -335,6 +335,8 @@ const CONST = { VERIFICATION_MAX_ATTEMPTS: 7, STATE: { VERIFYING: 'VERIFYING', + VALIDATING: 'VALIDATING', + SETUP: 'SETUP', PENDING: 'PENDING', OPEN: 'OPEN', }, diff --git a/src/ROUTES.ts b/src/ROUTES.ts index d78fbc27189d..c927d98b6c1d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -846,6 +846,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/expensify-card/issue-new', getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/issue-new` as const, }, + WORKSPACE_EXPENSIFY_CARD_BANK_ACCOUNT: { + route: 'settings/workspaces/:policyID/expensify-card/choose-bank-account', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/choose-bank-account` as const, + }, WORKSPACE_DISTANCE_RATES: { route: 'settings/workspaces/:policyID/distance-rates', getRoute: (policyID: string) => `settings/workspaces/${policyID}/distance-rates` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 6e5a9b272ebf..e4c15c0258c3 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -341,6 +341,7 @@ const SCREENS = { RATE_AND_UNIT_UNIT: 'Workspace_RateAndUnit_Unit', EXPENSIFY_CARD: 'Workspace_ExpensifyCard', EXPENSIFY_CARD_ISSUE_NEW: 'Workspace_ExpensifyCard_New', + EXPENSIFY_CARD_BANK_ACCOUNT: 'Workspace_ExpensifyCard_BankAccount', BILLS: 'Workspace_Bills', INVOICES: 'Workspace_Invoices', TRAVEL: 'Workspace_Travel', diff --git a/src/languages/en.ts b/src/languages/en.ts index 944bdf02e774..4e4ea4829b0a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2642,6 +2642,12 @@ export default { 'We consider a number of factors when calculating your remaining limit: your tenure as a customer, the business-related information you provided during signup, and the available cash in your business bank account. Your remaining limit can fluctuate on a daily basis.', cashBack: 'Cash back', cashBackDescription: 'Cash back balance is based on settled monthly Expensify Card spend across your workspace.', + issueNewCard: 'Issue new card', + finishSetup: 'Finish setup', + chooseBankAccount: 'Choose bank account', + chooseExistingBank: 'Choose an existing business bank account to pay your Expensify Card balance, or add a new bank account', + accountEndingIn: 'Account ending in', + addNewBankAccount: 'Add a new bank account', }, categories: { deleteCategories: 'Delete categories', diff --git a/src/languages/es.ts b/src/languages/es.ts index ad35b7b5ad2d..889c2a88ec12 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2690,6 +2690,12 @@ export default { 'A la hora de calcular tu límite restante, tenemos en cuenta una serie de factores: su antigüedad como cliente, la información relacionada con tu negocio que nos facilitaste al darte de alta y el efectivo disponible en tu cuenta bancaria comercial. Tu límite restante puede fluctuar a diario.', cashBack: 'Reembolso', cashBackDescription: 'El saldo de devolución se basa en el gasto mensual realizado con la tarjeta Expensify en tu espacio de trabajo.', + issueNewCard: '', + finishSetup: 'Terminar configuración', + chooseBankAccount: 'Elegir cuenta bancaria', + chooseExistingBank: 'Elige una cuenta bancaria comercial existente para pagar el saldo de su Tarjeta Expensify o añade una nueva cuenta bancaria.', + accountEndingIn: 'Cuenta terminada en', + addNewBankAccount: 'Añadir nueva cuenta bancaria', }, categories: { deleteCategories: 'Eliminar categorías', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 37fc01dce82e..a33b3198813c 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -407,6 +407,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/taxes/ValuePage').default, [SCREENS.WORKSPACE.TAX_CREATE]: () => require('../../../../pages/workspace/taxes/WorkspaceCreateTaxPage').default, [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: () => require('../../../../pages/workspace/card/issueNew/IssueNewCardPage').default, + [SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts').default, [SCREENS.SETTINGS.SAVE_THE_WORLD]: () => require('../../../../pages/TeachersUnite/SaveTheWorldPage').default, [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_PAYMENT_CURRENCY]: () => require('../../../../pages/settings/PaymentCard/ChangeCurrency').default, [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY]: () => require('../../../../pages/settings/Subscription/PaymentCard/ChangeBillingCurrency').default, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index fec821dc8fb9..480e8f6aad6b 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -159,7 +159,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.REPORT_FIELDS_EDIT_VALUE, SCREENS.WORKSPACE.REPORT_FIELDS_EDIT_INITIAL_VALUE, ], - [SCREENS.WORKSPACE.EXPENSIFY_CARD]: [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW], + [SCREENS.WORKSPACE.EXPENSIFY_CARD]: [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW, SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT], }; export default FULL_SCREEN_TO_RHP_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 8d60a48b40e5..37889d3258d8 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -463,6 +463,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: { path: ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.route, }, + [SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT]: { + path: ROUTES.WORKSPACE_EXPENSIFY_CARD_BANK_ACCOUNT.route, + }, [SCREENS.WORKSPACE.RATE_AND_UNIT]: { path: ROUTES.WORKSPACE_RATE_AND_UNIT.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index a71620db5cd5..81009c31e605 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -646,6 +646,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: { policyID: string; }; + [SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT]: { + policyID: string; + }; } & ReimbursementAccountNavigatorParamList; type NewChatNavigatorParamList = { diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx new file mode 100644 index 000000000000..c13820910f8c --- /dev/null +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx @@ -0,0 +1,95 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import getBankIcon from '@components/Icon/BankIcons'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import ScreenWrapper from '@components/ScreenWrapper'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@navigation/Navigation'; +import type {SettingsNavigatorParamList} from '@navigation/types'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import type {BankName} from '@src/types/onyx/Bank'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; + +type WorkspaceExpensifyCardBankAccountsProps = StackScreenProps; + +function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankAccountsProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const [bankAccountsList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); + + const policyID = route?.params?.policyID ?? '-1'; + + const handleAddBankAccount = () => { + // TODO: call to API - UpdateCardSettlementAccount + Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('new', policyID, ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID))); + }; + + const handleSelectBankAccount = () => { + Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID)); + }; + + const renderBankOptions = () => { + if (!bankAccountsList || isEmptyObject(bankAccountsList)) { + return null; + } + + // const eligibleBankAccounts = Object.values(bankAccountsList).filter((bankAccount) => bankAccount.accountData.allowDebit || bankAccount.accountData.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS); + const eligibleBankAccounts = Object.values(bankAccountsList); + + return eligibleBankAccounts.map((bankAccount) => { + const bankName = (bankAccount.accountData?.addressName ?? '') as BankName; + const bankAccountNumber = bankAccount.accountData?.accountNumber ?? ''; + + const {icon, iconSize, iconStyles} = getBankIcon({bankName, styles}); + + return ( + + ); + }); + }; + + return ( + + Navigation.goBack()} + title={translate('workspace.expensifyCard.chooseBankAccount')} + /> + + {translate('workspace.expensifyCard.chooseExistingBank')} + {renderBankOptions()} + + + + ); +} + +WorkspaceExpensifyCardBankAccounts.displayName = 'WorkspaceExpensifyCardBankAccounts'; + +export default WorkspaceExpensifyCardBankAccounts; diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx index 92450e0f2461..ac6c03dd18b3 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx @@ -1,6 +1,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import FeatureList from '@components/FeatureList'; import type {FeatureListItem} from '@components/FeatureList'; import * as Illustrations from '@components/Icon/Illustrations'; @@ -14,8 +15,10 @@ import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPol import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; const expensifyCardFeatures: FeatureListItem[] = [ { @@ -40,6 +43,23 @@ function WorkspaceExpensifyCardPageEmptyState({route, policy}: WorkspaceExpensif const styles = useThemeStyles(); const theme = useTheme(); const {shouldUseNarrowLayout} = useResponsiveLayout(); + const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); + const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); + + const reimbursementAccountStatus = reimbursementAccount?.achData?.state ?? ''; + const isSetupUnfinished = + isEmptyObject(bankAccountList) && + (reimbursementAccountStatus === CONST.BANK_ACCOUNT.STATE.VERIFYING || + reimbursementAccountStatus === CONST.BANK_ACCOUNT.STATE.SETUP || + reimbursementAccountStatus === CONST.BANK_ACCOUNT.STATE.VALIDATING); + + const startFlow = useCallback(() => { + if (isEmptyObject(bankAccountList) || isSetupUnfinished) { + Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('', policy?.id, ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policy?.id ?? '-1'))); + } else { + Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_BANK_ACCOUNT.getRoute(policy?.id ?? '-1')); + } + }, [isSetupUnfinished, bankAccountList, policy?.id]); return ( Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policy?.id ?? '-1'))} + onCtaPress={startFlow} illustrationBackgroundColor={theme.fallbackIconColor} illustration={Illustrations.ExpensifyCardIllustration} illustrationStyle={styles.expensifyCardIllustrationContainer}