diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 88a7adcbcb84..7d18f0f706c8 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -97,12 +97,12 @@ const ROUTES = { SETTINGS_APP_DOWNLOAD_LINKS: 'settings/about/app-download-links', SETTINGS_WALLET: 'settings/wallet', SETTINGS_WALLET_DOMAINCARD: { - route: 'settings/wallet/card/:domain', - getRoute: (domain: string) => `settings/wallet/card/${domain}` as const, + route: 'settings/wallet/card/:cardID?', + getRoute: (cardID: string) => `settings/wallet/card/${cardID}` as const, }, SETTINGS_REPORT_FRAUD: { - route: 'settings/wallet/card/:domain/report-virtual-fraud', - getRoute: (domain: string) => `settings/wallet/card/${domain}/report-virtual-fraud` as const, + route: 'settings/wallet/card/:cardID/report-virtual-fraud', + getRoute: (cardID: string) => `settings/wallet/card/${cardID}/report-virtual-fraud` as const, }, SETTINGS_WALLET_CARD_GET_PHYSICAL_NAME: { route: 'settings/wallet/card/:domain/get-physical/name', @@ -131,12 +131,12 @@ const ROUTES = { SETTINGS_WALLET_TRANSFER_BALANCE: 'settings/wallet/transfer-balance', SETTINGS_WALLET_CHOOSE_TRANSFER_ACCOUNT: 'settings/wallet/choose-transfer-account', SETTINGS_WALLET_REPORT_CARD_LOST_OR_DAMAGED: { - route: 'settings/wallet/card/:domain/report-card-lost-or-damaged', - getRoute: (domain: string) => `settings/wallet/card/${domain}/report-card-lost-or-damaged` as const, + route: 'settings/wallet/card/:cardID/report-card-lost-or-damaged', + getRoute: (cardID: string) => `settings/wallet/card/${cardID}/report-card-lost-or-damaged` as const, }, SETTINGS_WALLET_CARD_ACTIVATE: { - route: 'settings/wallet/card/:domain/activate', - getRoute: (domain: string) => `settings/wallet/card/${domain}/activate` as const, + route: 'settings/wallet/card/:cardID/activate', + getRoute: (cardID: string) => `settings/wallet/card/${cardID}/activate` as const, }, SETTINGS_LEGAL_NAME: 'settings/profile/legal-name', SETTINGS_DATE_OF_BIRTH: 'settings/profile/date-of-birth', diff --git a/src/languages/en.ts b/src/languages/en.ts index 8b7737f00812..3fd5211f1113 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1076,6 +1076,18 @@ export default { cardPage: { expensifyCard: 'Expensify Card', availableSpend: 'Remaining limit', + smartLimit: { + name: 'Smart limit', + title: (formattedLimit: string) => `You can spend up to ${formattedLimit} on this card, and the limit will reset as your submitted expenses are approved.`, + }, + fixedLimit: { + name: 'Fixed limit', + title: (formattedLimit: string) => `You can spend up to ${formattedLimit} on this card, and then it will deactivate.`, + }, + monthlyLimit: { + name: 'Monthly limit', + title: (formattedLimit: string) => `You can spend up to ${formattedLimit} on this card per month. The limit will reset on the 1st day of each calendar month.`, + }, virtualCardNumber: 'Virtual card number', physicalCardNumber: 'Physical card number', getPhysicalCard: 'Get physical card', diff --git a/src/languages/es.ts b/src/languages/es.ts index 82b5505b18f4..5d594828d39e 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1072,6 +1072,18 @@ export default { cardPage: { expensifyCard: 'Tarjeta Expensify', availableSpend: 'Límite restante', + smartLimit: { + name: 'Límite inteligente', + title: (formattedLimit: string) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta al mes. El límite se restablecerá el primer día del mes.`, + }, + fixedLimit: { + name: 'Límite fijo', + title: (formattedLimit: string) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta, luego se desactivará.`, + }, + monthlyLimit: { + name: 'Límite mensual', + title: (formattedLimit: string) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta y el límite se restablecerá a medida que se aprueben tus gastos.`, + }, virtualCardNumber: 'Número de la tarjeta virtual', physicalCardNumber: 'Número de la tarjeta física', getPhysicalCard: 'Obtener tarjeta física', diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 9e3a7f66131a..106debd0a7e5 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -127,7 +127,7 @@ function maskCard(lastFour = ''): string { * @returns a physical card object (or undefined if none is found) */ function findPhysicalCard(cards: Card[]) { - return cards.find((card) => !card.nameValuePairs?.isVirtual); + return cards.find((card) => !card?.nameValuePairs?.isVirtual); } /** diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index a758769a5e07..ecf962903820 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -118,27 +118,36 @@ type SettingsNavigatorParamList = { }; [SCREENS.SETTINGS.WALLET.ROOT]: undefined; [SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: undefined; - [SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: undefined; - [SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: undefined; - [SCREENS.SETTINGS.WALLET.CARD_ACTIVATE]: undefined; + [SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: { + /** cardID of selected card */ + cardID: string; + }; + [SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: { + /** cardID of selected card */ + cardID: string; + }; + [SCREENS.SETTINGS.WALLET.CARD_ACTIVATE]: { + /** cardID of selected card */ + cardID: string; + }; [SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.NAME]: { - /** domain passed via route /settings/wallet/card/:domain */ + /** domain of selected card */ domain: string; }; [SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.PHONE]: { - /** domain passed via route /settings/wallet/card/:domain */ + /** domain of selected card */ domain: string; }; [SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.ADDRESS]: { /** Currently selected country */ country: string; - /** domain passed via route /settings/wallet/card/:domain */ + /** domain of selected card */ domain: string; }; [SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.CONFIRM]: { /** Currently selected country */ country: string; - /** domain passed via route /settings/wallet/card/:domain */ + /** domain of selected card */ domain: string; }; [SCREENS.WORKSPACE.WORKFLOWS_PAYER]: { @@ -302,7 +311,10 @@ type SettingsNavigatorParamList = { backTo: Routes; }; [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: BackToParams; - [SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: undefined; + [SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: { + /** cardID of selected card */ + cardID: string; + }; [SCREENS.KEYBOARD_SHORTCUTS]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { diff --git a/src/libs/migrations/RenameCardIsVirtual.ts b/src/libs/migrations/RenameCardIsVirtual.ts index 4d03a9687a70..5929b39dfe72 100644 --- a/src/libs/migrations/RenameCardIsVirtual.ts +++ b/src/libs/migrations/RenameCardIsVirtual.ts @@ -19,7 +19,7 @@ export default function () { Log.info('[Migrate Onyx] Skipped migration RenameCardIsVirtual because there are no cards linked to the account'); return resolve(); } - const cardsWithIsVirtualProp = Object.values(cardList).filter((card) => card.isVirtual !== undefined); + const cardsWithIsVirtualProp = Object.values(cardList).filter((card) => card?.nameValuePairs?.isVirtual !== undefined); if (!cardsWithIsVirtualProp.length) { Log.info('[Migrate Onyx] Skipped migration RenameCardIsVirtual because there were no cards with the isVirtual property'); return resolve(); @@ -34,7 +34,7 @@ export default function () { ...result, [card.cardID]: { nameValuePairs: { - isVirtual: card.isVirtual, + isVirtual: card?.nameValuePairs?.isVirtual, }, isVirtual: undefined, }, diff --git a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx index a031cf6363f4..1d1d6583ffa8 100644 --- a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx +++ b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx @@ -15,11 +15,10 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import * as CardUtils from '@libs/CardUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; -import type {PublicScreensParamList} from '@libs/Navigation/types'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import * as CardSettings from '@userActions/Card'; import CONST from '@src/CONST'; @@ -34,7 +33,7 @@ type ActivatePhysicalCardPageOnyxProps = { cardList: OnyxEntry>; }; -type ActivatePhysicalCardPageProps = ActivatePhysicalCardPageOnyxProps & StackScreenProps; +type ActivatePhysicalCardPageProps = ActivatePhysicalCardPageOnyxProps & StackScreenProps; const LAST_FOUR_DIGITS_LENGTH = 4; const MAGIC_INPUT_MIN_HEIGHT = 86; @@ -42,7 +41,7 @@ const MAGIC_INPUT_MIN_HEIGHT = 86; function ActivatePhysicalCardPage({ cardList, route: { - params: {domain = ''}, + params: {cardID = ''}, }, }: ActivatePhysicalCardPageProps) { const theme = useTheme(); @@ -55,10 +54,8 @@ function ActivatePhysicalCardPage({ const [lastFourDigits, setLastFourDigits] = useState(''); const [lastPressedDigit, setLastPressedDigit] = useState(''); - const domainCards = CardUtils.getDomainCards(cardList)[domain] ?? []; - const physicalCard = domainCards.find((card) => !card.nameValuePairs?.isVirtual); - const cardID = physicalCard?.cardID ?? 0; - const cardError = ErrorUtils.getLatestErrorMessage(physicalCard ?? {}); + const inactiveCard = cardList?.[cardID]; + const cardError = ErrorUtils.getLatestErrorMessage(inactiveCard ?? {}); const activateCardCodeInputRef = useRef(null); @@ -66,19 +63,21 @@ function ActivatePhysicalCardPage({ * If state of the card is CONST.EXPENSIFY_CARD.STATE.OPEN, navigate to card details screen. */ useEffect(() => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (physicalCard?.isLoading || cardList?.[cardID]?.state !== CONST.EXPENSIFY_CARD.STATE.OPEN) { + if (inactiveCard?.state !== CONST.EXPENSIFY_CARD.STATE.OPEN || inactiveCard?.isLoading) { return; } - Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain)); - }, [cardID, cardList, domain, physicalCard?.isLoading]); + Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID)); + }, [cardID, cardList, inactiveCard?.isLoading, inactiveCard?.state]); useEffect( () => () => { - CardSettings.clearCardListErrors(cardID); + if (!inactiveCard?.cardID) { + return; + } + CardSettings.clearCardListErrors(inactiveCard?.cardID); }, - [cardID], + [inactiveCard?.cardID], ); /** @@ -95,8 +94,8 @@ function ActivatePhysicalCardPage({ const onCodeInput = (text: string) => { setFormError(''); - if (cardError) { - CardSettings.clearCardListErrors(cardID); + if (cardError && inactiveCard?.cardID) { + CardSettings.clearCardListErrors(inactiveCard?.cardID); } setLastFourDigits(text); @@ -109,18 +108,21 @@ function ActivatePhysicalCardPage({ setFormError('activateCardPage.error.thatDidntMatch'); return; } + if (inactiveCard?.cardID === undefined) { + return; + } - CardSettings.activatePhysicalExpensifyCard(lastFourDigits, cardID); - }, [lastFourDigits, cardID]); + CardSettings.activatePhysicalExpensifyCard(lastFourDigits, inactiveCard?.cardID); + }, [lastFourDigits, inactiveCard?.cardID]); - if (isEmptyObject(physicalCard)) { + if (isEmptyObject(inactiveCard)) { return ; } return ( Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain))} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID))} backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.PREFERENCES.ROOT].backgroundColor} illustration={LottieAnimations.Magician} scrollViewContainerStyles={[styles.mnh100]} @@ -148,7 +150,7 @@ function ActivatePhysicalCardPage({