diff --git a/src/components/FrozenCardHeader.tsx b/src/components/FrozenCardHeader.tsx index f73756762681..0c82668599d2 100644 --- a/src/components/FrozenCardHeader.tsx +++ b/src/components/FrozenCardHeader.tsx @@ -5,7 +5,6 @@ import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; @@ -33,7 +32,6 @@ type FrozenCardHeaderProps = { function FrozenCardHeader({cardPreview, children, style, onUnfreezePress, onAskToUnfreezePress, canUnfreezeCard, isWorkspaceAdmin, frozenByAccountID, frozenDate}: FrozenCardHeaderProps) { const styles = useThemeStyles(); - const theme = useTheme(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); const icons = useMemoizedLazyExpensifyIcons(['FreezeCard']); @@ -102,7 +100,6 @@ function FrozenCardHeader({cardPreview, children, style, onUnfreezePress, onAskT medium text={translate(canUnfreezeCard ? 'cardPage.unfreezeCard' : 'cardPage.askToUnfreeze')} icon={icons.FreezeCard} - iconFill={theme.icon} onPress={canUnfreezeCard ? onUnfreezePress : onAskToUnfreezePress} isDisabled={canUnfreezeCard && isOffline} innerStyles={equalButtonInnerStyles} diff --git a/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx b/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx index eb76f23a70ae..9c48deef64fe 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx +++ b/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx @@ -28,7 +28,6 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useNonPersonalCardList from '@hooks/useNonPersonalCardList'; import useOnyx from '@hooks/useOnyx'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {freezeCard, unfreezeCard} from '@libs/actions/Card'; import {resetValidateActionCodeSent} from '@libs/actions/User'; @@ -99,7 +98,6 @@ function ExpensifyCardPage({route}: ExpensifyCardPageProps) { const cardList = useNonPersonalCardList(); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${cardList?.[cardID]?.fundID}`); const styles = useThemeStyles(); - const theme = useTheme(); const {isOffline} = useNetwork(); const {translate} = useLocalize(); const {executeScenario} = useMultifactorAuthentication(); @@ -307,7 +305,6 @@ function ExpensifyCardPage({route}: ExpensifyCardPageProps) { medium text={translate('workspace.common.viewTransactions')} icon={expensifyIcons.MoneySearch} - iconFill={theme.icon} onPress={navigateToTransactions} innerStyles={styles.ph2} style={styles.w100} @@ -346,12 +343,11 @@ function ExpensifyCardPage({route}: ExpensifyCardPageProps) { {!hasDetectedDomainFraud && ( <> {(!isCardFrozen(currentCard) || !canManageCardFreeze) && ( - + {canManageCardFreeze && !isCardFrozen(currentCard) && ( @@ -432,21 +427,6 @@ function ExpensifyCardPage({route}: ExpensifyCardPageProps) { } /> )} - {shouldShowChangePINRow && ( - { - const physicalCardID = String(currentPhysicalCard?.cardID); - if (isOfflinePINMarket(countryByIp)) { - Navigation.navigate(ROUTES.SETTINGS_WALLET_CARD_CHANGE_PIN_ATM.getRoute(physicalCardID)); - } else { - Navigation.navigate(ROUTES.SETTINGS_WALLET_CARD_CHANGE_PIN.getRoute(physicalCardID)); - } - }} - /> - )} )} {virtualCards.map((card) => { @@ -559,71 +539,90 @@ function ExpensifyCardPage({route}: ExpensifyCardPageProps) { )} ))} - {shouldShowActionRows && ( - - {shouldShowReportVirtualCardFraudRows && - virtualCards.map((card) => ( - { - if (isAccountLocked) { - showLockedAccountModal(); - return; - } - if (route.name === SCREENS.DOMAIN_CARD.DOMAIN_CARD_DETAIL) { - Navigation.navigate(ROUTES.SETTINGS_DOMAIN_CARD_REPORT_FRAUD.getRoute(String(card.cardID))); - return; - } - Navigation.navigate(ROUTES.SETTINGS_REPORT_FRAUD.getRoute(String(card.cardID))); - }} - /> - ))} - {shouldShowReportTravelCardFraudRows && - travelCards.map((card) => ( - Navigation.navigate(ROUTES.SETTINGS_REPORT_FRAUD.getRoute(String(card.cardID)))} - /> - ))} - {shouldShowReportLostCardButton && ( + {(shouldShowChangePINRow || shouldShowActionRows) && ( + + {shouldShowChangePINRow && ( { - if (isAccountLocked) { - showLockedAccountModal(); - return; + const physicalCardID = String(currentPhysicalCard?.cardID); + if (isOfflinePINMarket(countryByIp)) { + Navigation.navigate(ROUTES.SETTINGS_WALLET_CARD_CHANGE_PIN_ATM.getRoute(physicalCardID)); + } else { + Navigation.navigate(ROUTES.SETTINGS_WALLET_CARD_CHANGE_PIN.getRoute(physicalCardID)); } - Navigation.navigate(ROUTES.SETTINGS_WALLET_REPORT_CARD_LOST_OR_DAMAGED.getRoute(String(currentPhysicalCard?.cardID))); }} /> )} + {shouldShowActionRows && ( + <> + {shouldShowReportVirtualCardFraudRows && + virtualCards.map((card) => ( + { + if (isAccountLocked) { + showLockedAccountModal(); + return; + } + if (route.name === SCREENS.DOMAIN_CARD.DOMAIN_CARD_DETAIL) { + Navigation.navigate(ROUTES.SETTINGS_DOMAIN_CARD_REPORT_FRAUD.getRoute(String(card.cardID))); + return; + } + Navigation.navigate(ROUTES.SETTINGS_REPORT_FRAUD.getRoute(String(card.cardID))); + }} + /> + ))} + {shouldShowReportTravelCardFraudRows && + travelCards.map((card) => ( + Navigation.navigate(ROUTES.SETTINGS_REPORT_FRAUD.getRoute(String(card.cardID)))} + /> + ))} + {shouldShowReportLostCardButton && ( + { + if (isAccountLocked) { + showLockedAccountModal(); + return; + } + Navigation.navigate(ROUTES.SETTINGS_WALLET_REPORT_CARD_LOST_OR_DAMAGED.getRoute(String(currentPhysicalCard?.cardID))); + }} + /> + )} - {shouldShowSpendRulesSummary && ( - - )} + {shouldShowSpendRulesSummary && ( + + )} - {shouldShowEditSpendRules && ( - + {shouldShowEditSpendRules && ( + + )} + )} )} diff --git a/src/pages/settings/Wallet/PersonalCardDetailsHeaderMenu.tsx b/src/pages/settings/Wallet/PersonalCardDetailsHeaderMenu.tsx index 782cfa8049f3..21559ae1c2f2 100644 --- a/src/pages/settings/Wallet/PersonalCardDetailsHeaderMenu.tsx +++ b/src/pages/settings/Wallet/PersonalCardDetailsHeaderMenu.tsx @@ -1,6 +1,6 @@ import {format, parseISO} from 'date-fns'; import React from 'react'; -import ActivityIndicator from '@components/ActivityIndicator'; +import {View} from 'react-native'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -9,8 +9,6 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import {getDefaultCardName} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; -import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; -import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import Navigation from '@navigation/Navigation'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import {clearCardErrorField, clearCardNameValuePairsErrorField, setPersonalCardReimbursable} from '@userActions/Card'; @@ -27,10 +25,8 @@ type PersonalCardDetailsHeaderMenuProps = { expensifyIcons: Record; isCSVImportedPersonalCard: boolean; reimbursableSetting: boolean; - lastScrape: string; isOffline: boolean; shouldShowBreakConnection: boolean; - onUpdateCard: () => void; onBreakConnection: () => void; onUnassignCard: () => void; onDeleteCard?: () => void; @@ -44,10 +40,8 @@ function PersonalCardDetailsHeaderMenu({ expensifyIcons, isCSVImportedPersonalCard, reimbursableSetting, - lastScrape, isOffline, shouldShowBreakConnection, - onUpdateCard, onBreakConnection, onUnassignCard, onDeleteCard, @@ -55,7 +49,6 @@ function PersonalCardDetailsHeaderMenu({ const {translate} = useLocalize(); const styles = useThemeStyles(); const icons = useMemoizedLazyExpensifyIcons(['Table', 'Trashcan']); - const isLoadingLastUpdatedReasonAttributes: SkeletonSpanReasonAttributes = {context: 'PersonalCardDetailsHeaderMenu', isLoadingLastUpdated: !!card?.isLoadingLastUpdated}; return ( <> @@ -91,19 +84,6 @@ function PersonalCardDetailsHeaderMenu({ onCloseError={() => card && clearCardErrorField(card.cardID, 'reimbursable')} wrapperStyle={[styles.ph5, styles.mb3]} /> - - - } - description={translate('workspace.moreFeatures.companyCards.lastUpdated')} - title={card?.isLoadingLastUpdated ? translate('workspace.moreFeatures.companyCards.updating') : lastScrape} - interactive={false} - /> {!isCSVImportedPersonalCard && ( )} - { - Navigation.navigate( - ROUTES.SEARCH_ROOT.getRoute({ - query: buildCannedSearchQuery({type: CONST.SEARCH.DATA_TYPES.EXPENSE, status: CONST.SEARCH.STATUS.EXPENSE.ALL, cardID}), - }), - ); - }} - /> - {isCSVImportedPersonalCard && ( - Navigation.navigate(ROUTES.SETTINGS_WALLET_IMPORT_TRANSACTIONS_SPREADSHEET.getRoute(Number(cardID)))} - /> - )} - {!isCSVImportedPersonalCard && ( - { - if (!card) { - return; - } - clearCardErrorField(card.cardID, 'lastScrape'); - }} - > + + {isCSVImportedPersonalCard && ( + Navigation.navigate(ROUTES.SETTINGS_WALLET_IMPORT_TRANSACTIONS_SPREADSHEET.getRoute(Number(cardID)))} + /> + )} + {shouldShowBreakConnection && ( - - )} - {shouldShowBreakConnection && ( - - )} - {isCSVImportedPersonalCard ? ( - - ) : ( - - )} + )} + {isCSVImportedPersonalCard ? ( + + ) : ( + + )} + ); } diff --git a/src/pages/settings/Wallet/PersonalCardDetailsPage.tsx b/src/pages/settings/Wallet/PersonalCardDetailsPage.tsx index 8ee28d648543..6b88cd871cc1 100644 --- a/src/pages/settings/Wallet/PersonalCardDetailsPage.tsx +++ b/src/pages/settings/Wallet/PersonalCardDetailsPage.tsx @@ -10,6 +10,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import PlaidCardFeedIcon from '@components/PlaidCardFeedIcon'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; +import Text from '@components/Text'; import {useCompanyCardFeedIcons} from '@hooks/useCompanyCardIcons'; import useConfirmModal from '@hooks/useConfirmModal'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; @@ -19,10 +20,12 @@ import useOnyx from '@hooks/useOnyx'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import {isUsingStagingApi} from '@libs/ApiUtils'; +import navigateToCardTransactions from '@libs/CardNavigationUtils'; import {getCardFeedIcon, getPlaidInstitutionIconUrl, isCardConnectionBroken, isPersonalCard} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import Navigation from '@navigation/Navigation'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import variables from '@styles/variables'; @@ -34,6 +37,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; +import CardDetailsActionButtons, {CardDetailsActionButton} from './CardDetailsActionButtons'; import PersonalCardDetailsHeaderMenu from './PersonalCardDetailsHeaderMenu'; type PersonalCardDetailsPageProps = PlatformStackScreenProps; @@ -47,7 +51,7 @@ function PersonalCardDetailsPage({route}: PersonalCardDetailsPageProps) { const styles = useThemeStyles(); const illustrations = useThemeIllustrations(); const companyCardFeedIcons = useCompanyCardFeedIcons(); - const expensifyIcons = useMemoizedLazyExpensifyIcons(['FallbackAvatar', 'MoneySearch', 'RemoveMembers', 'Sync']); + const expensifyIcons = useMemoizedLazyExpensifyIcons(['MoneySearch', 'RemoveMembers', 'Sync']); const {isOffline} = useNetwork(); @@ -61,6 +65,7 @@ function PersonalCardDetailsPage({route}: PersonalCardDetailsPageProps) { const cardBank = card?.bank ?? ''; const isCardBroken = card ? isCardConnectionBroken(card) : false; const cardholder = personalDetails?.[card?.accountID ?? CONST.DEFAULT_NUMBER_ID]; + const displayName = getDisplayNameOrDefault(cardholder); const isUserPersonalCard = !!(card && isPersonalCard(card)); const reimbursableSetting = card?.reimbursable ?? true; const isCSVImportedPersonalCard = !!(isUserPersonalCard && card && (card.bank === CONST.COMPANY_CARD.FEED_BANK_NAME.UPLOAD || card.bank.includes(CONST.COMPANY_CARD.FEED_BANK_NAME.CSV))); @@ -134,6 +139,8 @@ function PersonalCardDetailsPage({route}: PersonalCardDetailsPageProps) { return getCardFeedIcon(cardBank as CompanyCardFeed, illustrations, companyCardFeedIcons); }; + const navigateToTransactions = () => navigateToCardTransactions(cardID); + // Don't show NotFoundPage if data is still loading if (!card && !isLoadingOnyxValue(cardListMetadata)) { return ; @@ -171,7 +178,47 @@ function PersonalCardDetailsPage({route}: PersonalCardDetailsPageProps) { width={variables.cardPreviewWidth} /> )} + + {displayName} + + + {`${translate('workspace.moreFeatures.companyCards.lastUpdated')}: ${card?.isLoadingLastUpdated ? translate('workspace.moreFeatures.companyCards.updating') : lastScrape}`} + + { + if (!card) { + return; + } + clearCardErrorField(card.cardID, 'lastScrape'); + }} + > + + {!isCSVImportedPersonalCard && ( + + )} + + + {isCardBroken && ( - +