diff --git a/src/components/PlaidCardFeedIcon.tsx b/src/components/PlaidCardFeedIcon.tsx index a585c8ff3000..058831594850 100644 --- a/src/components/PlaidCardFeedIcon.tsx +++ b/src/components/PlaidCardFeedIcon.tsx @@ -1,6 +1,8 @@ -import React from 'react'; -import {View} from 'react-native'; +import React, {useEffect, useState} from 'react'; +import {ActivityIndicator, View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; +import useTheme from '@hooks/useTheme'; +import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import variables from '@styles/variables'; import Icon from './Icon'; @@ -14,19 +16,56 @@ type PlaidCardFeedIconProps = { }; function PlaidCardFeedIcon({plaidUrl, style, isLarge}: PlaidCardFeedIconProps) { + const [isBrokenImage, setIsBrokenImage] = useState(false); const styles = useThemeStyles(); + const illustrations = useThemeIllustrations(); + const theme = useTheme(); + const width = isLarge ? variables.cardPreviewWidth : variables.cardIconWidth; + const height = isLarge ? variables.cardPreviewHeight : variables.cardIconHeight; + const [loading, setLoading] = useState(true); + + useEffect(() => { + if (!plaidUrl) { + return; + } + setIsBrokenImage(false); + setLoading(true); + }, [plaidUrl]); + return ( - - + {isBrokenImage ? ( + + ) : ( + <> + setIsBrokenImage(true)} + onLoadEnd={() => setLoading(false)} + /> + {loading ? ( + + + + ) : ( + + )} + + )} ); } diff --git a/src/languages/en.ts b/src/languages/en.ts index 3e7e4870341b..0dd19f6dc208 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -4000,7 +4000,7 @@ const translations = { companyCard: 'company card', chooseCardFeed: 'Choose card feed', ukRegulation: - 'Expensify Limited is an agent of Plaid Financial Ltd., an authorised payment institution regulated by the Financial Conduct Authority under the Payment Services Regulations 2017 (Firm Reference Number: 804718). Plaid provides you with regulated account information services through Expensify Limited as its agent.', + 'Expensify, Inc. is an agent of Plaid Financial Ltd., an authorised payment institution regulated by the Financial Conduct Authority under the Payment Services Regulations 2017 (Firm Reference Number: 804718). Plaid provides you with regulated account information services through Expensify Limited as its agent.', }, expensifyCard: { issueAndManageCards: 'Issue and manage your Expensify Cards', diff --git a/src/languages/es.ts b/src/languages/es.ts index fb508a0be323..a68c569c581a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -4040,7 +4040,7 @@ const translations = { companyCard: 'tarjeta de empresa', chooseCardFeed: 'Elige feed de tarjetas', ukRegulation: - 'Expensify Limited es un agente de Plaid Financial Ltd., una institución de pago autorizada y regulada por la Financial Conduct Authority conforme al Reglamento de Servicios de Pago de 2017 (Número de Referencia de la Firma: 804718). Plaid te proporciona servicios regulados de información de cuentas a través de Expensify Limited como su agente.', + 'Expensify, Inc. es un agente de Plaid Financial Ltd., una institución de pago autorizada y regulada por la Financial Conduct Authority conforme al Reglamento de Servicios de Pago de 2017 (Número de Referencia de la Firma: 804718). Plaid te proporciona servicios regulados de información de cuentas a través de Expensify Limited como su agente.', }, expensifyCard: { issueAndManageCards: 'Emitir y gestionar Tarjetas Expensify', diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 421338e1d787..59af4f3ad0b2 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -10,7 +10,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type {OnyxValues} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; +import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, CurrencyList, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; import type {FilteredCardList} from '@src/types/onyx/Card'; import type {CardFeedData, CompanyCardFeedWithNumber, CompanyCardNicknames, CompanyFeeds, DirectCardFeedData} from '@src/types/onyx/CardFeeds'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -432,10 +432,31 @@ function getPlaidInstitutionId(feedName?: string) { return feed.at(1); } +function isPlaidSupportedCountry(selectedCountry?: string) { + if (!selectedCountry) { + return false; + } + return CONST.PLAID_SUPPORT_COUNTRIES.includes(selectedCountry); +} + function getDomainOrWorkspaceAccountID(workspaceAccountID: number, cardFeedData: CardFeedData | undefined): number { return cardFeedData?.domainID ?? workspaceAccountID; } +function getPlaidCountry(outputCurrency?: string, currencyList?: CurrencyList, countryByIp?: string) { + const selectedCurrency = outputCurrency ? currencyList?.[outputCurrency] : null; + const countries = selectedCurrency?.countries; + + if (outputCurrency === CONST.CURRENCY.EUR) { + if (countryByIp && countries?.includes(countryByIp)) { + return countryByIp; + } + return ''; + } + const country = countries?.[0]; + return country ?? ''; +} + // We will simplify the logic below once we have #50450 #50451 implemented const getCorrectStepForSelectedBank = (selectedBank: ValueOf) => { const banksWithFeedType = [ @@ -693,8 +714,10 @@ export { getBankCardDetailsImage, getSelectedFeed, getCorrectStepForSelectedBank, + getPlaidCountry, getCustomOrFormattedFeedName, isCardClosed, + isPlaidSupportedCountry, getFilteredCardList, hasOnlyOneCardToAssign, checkIfNewFeedConnected, diff --git a/src/libs/actions/getCompanyCardBankConnection/index.tsx b/src/libs/actions/getCompanyCardBankConnection/index.tsx index 1deec2cd368d..a34865ce31f1 100644 --- a/src/libs/actions/getCompanyCardBankConnection/index.tsx +++ b/src/libs/actions/getCompanyCardBankConnection/index.tsx @@ -17,6 +17,7 @@ type CompanyCardPlaidConnection = { domainName: string; feedName: string; feed: string; + country: string; }; function getCompanyCardBankConnection(policyID?: string, bankName?: string) { @@ -47,8 +48,8 @@ function getCompanyCardBankConnection(policyID?: string, bankName?: string) { return `${commandURL}partners/banks/${bank}/oauth_callback.php?${new URLSearchParams(params).toString()}`; } -function getCompanyCardPlaidConnection(policyID?: string, publicToken?: string, feed?: string, feedName?: string) { - if (!policyID || !publicToken || !feed || !feedName) { +function getCompanyCardPlaidConnection(policyID?: string, publicToken?: string, feed?: string, feedName?: string, country?: string) { + if (!policyID || !publicToken || !feed || !feedName || !country) { return null; } const authToken = NetworkStore.getAuthToken(); @@ -57,6 +58,7 @@ function getCompanyCardPlaidConnection(policyID?: string, publicToken?: string, feed, feedName, publicToken, + country, domainName: PolicyUtils.getDomainNameForPolicy(policyID), }; diff --git a/src/pages/workspace/companyCards/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/BankConnection/index.native.tsx index 396b83e25c94..f838acdf77c7 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.native.tsx @@ -56,9 +56,11 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const plaidToken = addNewCard?.data?.publicToken ?? assignCard?.data?.plaidAccessToken; const plaidFeed = addNewCard?.data?.plaidConnectedFeed ?? assignCard?.data?.institutionId; const plaidFeedName = addNewCard?.data?.plaidConnectedFeedName ?? assignCard?.data?.plaidConnectedFeedName; + const country = addNewCard?.data?.selectedCountry; + const url = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && plaidToken - ? getCompanyCardPlaidConnection(policyID, plaidToken, plaidFeed, plaidFeedName) + ? getCompanyCardPlaidConnection(policyID, plaidToken, plaidFeed, plaidFeedName, country) : getCompanyCardBankConnection(policyID, bankName); const [cardFeeds] = useCardFeeds(policyID); const [isConnectionCompleted, setConnectionCompleted] = useState(false); @@ -115,9 +117,9 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti if (newFeed) { updateSelectedFeed(newFeed, policyID); } - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID), {forceReplace: true}); } - }, [isNewFeedConnected, newFeed, policyID, url, feed, isFeedExpired, assignCard]); + }, [isNewFeedConnected, newFeed, policyID, url, feed, isFeedExpired, assignCard?.data?.dateOption]); const checkIfConnectionCompleted = (navState: WebViewNavigation) => { if (!navState.url.includes(ROUTES.BANK_CONNECTION_COMPLETE)) { diff --git a/src/pages/workspace/companyCards/BankConnection/index.tsx b/src/pages/workspace/companyCards/BankConnection/index.tsx index 4daa47271c25..18faf890ecec 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.tsx @@ -61,11 +61,12 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const plaidToken = addNewCard?.data?.publicToken ?? assignCard?.data?.plaidAccessToken; const plaidFeed = addNewCard?.data?.plaidConnectedFeed ?? assignCard?.data?.institutionId; const plaidFeedName = addNewCard?.data?.plaidConnectedFeedName ?? assignCard?.data?.plaidConnectedFeedName; + const country = addNewCard?.data?.selectedCountry; const {isBetaEnabled} = usePermissions(); const url = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && plaidToken - ? getCompanyCardPlaidConnection(policyID, plaidToken, plaidFeed, plaidFeedName) + ? getCompanyCardPlaidConnection(policyID, plaidToken, plaidFeed, plaidFeedName, country) : getCompanyCardBankConnection(policyID, bankName); const isFeedExpired = feed ? isSelectedFeedExpired(cardFeeds?.settings?.oAuthAccountDetails?.[feed]) : false; const headerTitleAddCards = !backTo ? translate('workspace.companyCards.addCards') : undefined; @@ -140,13 +141,13 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti updateSelectedFeed(newFeed, policyID); } Navigation.closeRHPFlow(); - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID), {forceReplace: true}); return; } if (!shouldBlockWindowOpen) { customWindow = openBankConnection(url); } - }, [isNewFeedConnected, shouldBlockWindowOpen, newFeed, policyID, url, feed, isFeedExpired, isOffline, assignCard]); + }, [isNewFeedConnected, shouldBlockWindowOpen, newFeed, policyID, url, feed, isFeedExpired, isOffline, assignCard?.data?.dateOption]); return ( { const institutionId = !!getPlaidInstitutionId(selectedFeed); if (institutionId) { - setAssignCardStepAndData({currentStep: CONST.COMPANY_CARD.STEP.PLAID_CONNECTION}); + const country = getPlaidCountry(policy?.outputCurrency, currencyList, countryByIp); + setAddNewCompanyCardStepAndData({ + data: { + selectedCountry: country, + }, + }); + setAssignCardStepAndData({ + currentStep: CONST.COMPANY_CARD.STEP.PLAID_CONNECTION, + }); Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed))); return; } diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 012220f81543..db11956240c5 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -17,6 +17,7 @@ import { getCompanyFeeds, getDomainOrWorkspaceAccountID, getFilteredCardList, + getPlaidCountry, getPlaidInstitutionId, getSelectedFeed, hasOnlyOneCardToAssign, @@ -30,7 +31,7 @@ import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {isDeletedPolicyEmployee} from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import {openPolicyCompanyCardsFeed, openPolicyCompanyCardsPage, setAssignCardStepAndData} from '@userActions/CompanyCards'; +import {clearAddNewCardFlow, openPolicyCompanyCardsFeed, openPolicyCompanyCardsPage, setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -56,6 +57,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const [cardsList] = useCardsList(policyID, selectedFeed); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); + const [currencyList = {}] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const {isBetaEnabled} = usePermissions(); const hasNoAssignedCard = Object.keys(cardsList ?? {}).length === 0; @@ -139,9 +141,18 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { if (isFeedExpired) { const institutionId = !!getPlaidInstitutionId(selectedFeed); + if (institutionId) { + const country = getPlaidCountry(policy?.outputCurrency, currencyList, countryByIp); + setAddNewCompanyCardStepAndData({ + data: { + selectedCountry: country, + }, + }); + } currentStep = institutionId ? CONST.COMPANY_CARD.STEP.PLAID_CONNECTION : CONST.COMPANY_CARD.STEP.BANK_CONNECTION; } + clearAddNewCardFlow(); setAssignCardStepAndData({data, currentStep}); Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed))); }; diff --git a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx index e8b3eb596e26..1d40658064bd 100644 --- a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx +++ b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx @@ -8,7 +8,7 @@ import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import BankConnection from '@pages/workspace/companyCards/BankConnection'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {openPolicyAddCardFeedPage} from '@userActions/CompanyCards'; +import {clearAddNewCardFlow, openPolicyAddCardFeedPage} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; @@ -33,6 +33,12 @@ function AddNewCardPage({policy}: WithPolicyAndFullscreenLoadingProps) { const isAddCardFeedLoading = isLoadingOnyxValue(addNewCardFeedMetadata); + useEffect(() => { + return () => { + clearAddNewCardFlow(); + }; + }, []); + useEffect(() => { // If the user only has a domain feed, a workspace account may not have been created yet. // However, adding a workspace feed requires a workspace account. diff --git a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx index 5713a93859d9..3e7ea75471d0 100644 --- a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx @@ -14,6 +14,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useThemeStyles from '@hooks/useThemeStyles'; +import {isPlaidSupportedCountry} from '@libs/CardUtils'; import variables from '@styles/variables'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; @@ -93,6 +94,7 @@ function CardTypeStep() { const {bankName, selectedBank, feedType} = addNewCard?.data ?? {}; const isOtherBankSelected = selectedBank === CONST.COMPANY_CARDS.BANKS.OTHER; const isNewCardTypeSelected = typeSelected !== feedType; + const doesCountrySupportPlaid = isPlaidSupportedCountry(addNewCard?.data.selectedCountry); const submit = () => { if (!typeSelected) { @@ -118,6 +120,10 @@ function CardTypeStep() { setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); return; } + if (isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && !doesCountrySupportPlaid) { + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY}); + return; + } setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE}); }; diff --git a/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx b/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx index 6ac983d33473..5d1b44c9e01d 100644 --- a/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx @@ -12,6 +12,7 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; +import {getPlaidCountry, isPlaidSupportedCountry} from '@libs/CardUtils'; import searchOptions from '@libs/searchOptions'; import type {Option} from '@libs/searchOptions'; import StringUtils from '@libs/StringUtils'; @@ -43,17 +44,13 @@ function SelectCountryStep({policyID}: CountryStepProps) { if (addNewCard?.data?.selectedCountry) { return addNewCard.data.selectedCountry; } - const selectedCurrency = policy?.outputCurrency ? currencyList?.[policy.outputCurrency] : null; - const countries = selectedCurrency?.countries; - if (policy?.outputCurrency === CONST.CURRENCY.EUR && countryByIp && countries?.includes(countryByIp)) { - return countryByIp; - } - const country = countries?.[0]; - return country ?? ''; + return getPlaidCountry(policy?.outputCurrency, currencyList, countryByIp); }, [addNewCard?.data.selectedCountry, countryByIp, currencyList, policy?.outputCurrency]); + const [currentCountry, setCurrentCountry] = useState(getCountry); const [hasError, setHasError] = useState(false); + const doesCountrySupportPlaid = isPlaidSupportedCountry(currentCountry); const submit = () => { if (!currentCountry) { @@ -63,9 +60,10 @@ function SelectCountryStep({policyID}: CountryStepProps) { clearAddNewCardFlow(); } setAddNewCompanyCardStepAndData({ - step: CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE, + step: doesCountrySupportPlaid ? CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE : CONST.COMPANY_CARDS.STEP.CARD_TYPE, data: { selectedCountry: currentCountry, + selectedFeedType: doesCountrySupportPlaid ? CONST.COMPANY_CARDS.FEED_TYPE.DIRECT : CONST.COMPANY_CARDS.FEED_TYPE.CUSTOM, }, isEditing: false, }); diff --git a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx index 5e0296846dca..ac9a66d19c36 100644 --- a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx @@ -12,6 +12,7 @@ import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useThemeStyles from '@hooks/useThemeStyles'; +import {isPlaidSupportedCountry} from '@libs/CardUtils'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -23,7 +24,7 @@ function SelectFeedType() { const [typeSelected, setTypeSelected] = useState>(); const [hasError, setHasError] = useState(false); const {isBetaEnabled} = usePermissions(); - const isCountrySupportPlaid = addNewCard?.data?.selectedCountry ? CONST.PLAID_SUPPORT_COUNTRIES.includes(addNewCard.data.selectedCountry) : false; + const doesCountrySupportPlaid = isPlaidSupportedCountry(addNewCard?.data?.selectedCountry); const isUSCountry = addNewCard?.data?.selectedCountry === CONST.COUNTRY.US; const submit = () => { @@ -52,10 +53,10 @@ function SelectFeedType() { setTypeSelected(addNewCard?.data.selectedFeedType); return; } - if (isCountrySupportPlaid) { + if (doesCountrySupportPlaid) { setTypeSelected(CONST.COMPANY_CARDS.FEED_TYPE.DIRECT); } - }, [addNewCard?.data.selectedFeedType, isCountrySupportPlaid]); + }, [addNewCard?.data.selectedFeedType, doesCountrySupportPlaid]); const handleBackButtonPress = () => { setAddNewCompanyCardStepAndData({step: isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) ? CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY : CONST.COMPANY_CARDS.STEP.SELECT_BANK}); @@ -84,7 +85,7 @@ function SelectFeedType() { if (!isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS)) { return data; } - if (isCountrySupportPlaid) { + if (doesCountrySupportPlaid) { return data.reverse(); } diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index 6ffd6faa5644..9f9528ab44ea 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -11,10 +11,10 @@ import useCardFeeds from '@hooks/useCardFeeds'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getPlaidInstitutionId, isSelectedFeedExpired, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; +import {getPlaidCountry, getPlaidInstitutionId, isSelectedFeedExpired, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import Navigation from '@navigation/Navigation'; -import {assignWorkspaceCompanyCard, clearAssignCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; +import {assignWorkspaceCompanyCard, clearAssignCardStepAndData, setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -36,6 +36,9 @@ function ConfirmationStep({policyID, backTo}: ConfirmationStepProps) { const {isOffline} = useNetwork(); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: false}); + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false}); + const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); + const [currencyList = {}] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const feed = assignCard?.data?.bankName as CompanyCardFeed | undefined; const [cardFeeds] = useCardFeeds(policyID); @@ -50,7 +53,7 @@ function ConfirmationStep({policyID, backTo}: ConfirmationStepProps) { if (backTo) { Navigation.goBack(backTo); } else { - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID), {forceReplace: true}); } InteractionManager.runAfterInteractions(() => clearAssignCardStepAndData()); }, [assignCard, backTo, policyID]); @@ -64,6 +67,14 @@ function ConfirmationStep({policyID, backTo}: ConfirmationStepProps) { const institutionId = !!getPlaidInstitutionId(feed); if (isFeedExpired) { + if (institutionId) { + const country = getPlaidCountry(policy?.outputCurrency, currencyList, countryByIp); + setAddNewCompanyCardStepAndData({ + data: { + selectedCountry: country, + }, + }); + } setAssignCardStepAndData({currentStep: institutionId ? CONST.COMPANY_CARD.STEP.PLAID_CONNECTION : CONST.COMPANY_CARD.STEP.BANK_CONNECTION}); return; } diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index b91ece1793e7..6b7bb7abab9f 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -4,6 +4,7 @@ import ExpensifyCardImage from '@assets/images/expensify-card.svg'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; +import PlaidCardFeedIcon from '@components/PlaidCardFeedIcon'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/RadioListItem'; @@ -19,6 +20,7 @@ import { getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getFilteredCardList, + getPlaidInstitutionIconUrl, hasCardListObject, hasOnlyOneCardToAssign, isCustomFeed, @@ -127,22 +129,32 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew setShouldShowError(false); }; - const companyCardFeeds: CardFeedListItem[] = (Object.keys(companyFeeds) as CompanyCardFeed[]).map((key) => ({ - value: key, - text: getCustomOrFormattedFeedName(key, cardFeeds?.settings?.companyCardNicknames), - keyForList: key, - isDisabled: companyFeeds[key]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - pendingAction: companyFeeds[key]?.pendingAction, - isSelected: selectedFeed === key, - leftElement: ( - - ), - })); + const companyCardFeeds: CardFeedListItem[] = (Object.keys(companyFeeds) as CompanyCardFeed[]).map((key) => { + const plaidUrl = getPlaidInstitutionIconUrl(key); + + return { + value: key, + text: getCustomOrFormattedFeedName(key, cardFeeds?.settings?.companyCardNicknames), + keyForList: key, + isDisabled: companyFeeds[key]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + pendingAction: companyFeeds[key]?.pendingAction, + isSelected: selectedFeed === key, + + leftElement: plaidUrl ? ( + + ) : ( + + ), + }; + }); const feeds = shouldShowExpensifyCard ? [ diff --git a/tests/ui/AssignCardFeedPage.tsx b/tests/ui/AssignCardFeedPage.tsx index b89bf503b9ce..c3a536e87fd8 100644 --- a/tests/ui/AssignCardFeedPage.tsx +++ b/tests/ui/AssignCardFeedPage.tsx @@ -233,7 +233,7 @@ describe('AssignCardFeedPage', () => { // Verify that we navigate to the company cards page as the card assignee has changed await waitFor(() => { - expect(navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policy.id)); + expect(navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policy.id), {forceReplace: true}); }); // Unmount the component after assertions to clean up. unmount();