Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/libs/CardNavigationUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import Navigation from './Navigation/Navigation';
import {buildCannedSearchQuery} from './SearchQueryUtils';

function navigateToCardTransactions(cardID: string) {
Navigation.navigate(
ROUTES.SEARCH_ROOT.getRoute({
query: buildCannedSearchQuery({type: CONST.SEARCH.DATA_TYPES.EXPENSE, status: CONST.SEARCH.STATUS.EXPENSE.ALL, cardID}),
}),
);
}

export default navigateToCardTransactions;
23 changes: 23 additions & 0 deletions src/pages/settings/Wallet/CardDetailsActionButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';

type CardDetailsActionButtonsProps = {
children: React.ReactNode;
style?: StyleProp<ViewStyle>;
};

function CardDetailsActionButtons({children, style}: CardDetailsActionButtonsProps) {
const styles = useThemeStyles();

return (
<View
style={[styles.flexRow, styles.flexWrap, styles.alignItemsCenter, styles.justifyContentCenter, styles.ph5, styles.pt2, styles.mb6, styles.gap2, styles.alignSelfStretch, style]}
>
{children}
</View>
);
}

export default CardDetailsActionButtons;
54 changes: 0 additions & 54 deletions src/pages/settings/Wallet/PersonalCardDetailsHeaderMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {format, parseISO} from 'date-fns';
import React from 'react';
import ActivityIndicator from '@components/ActivityIndicator';
import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
Expand All @@ -9,8 +8,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';
Expand All @@ -28,10 +25,8 @@ type PersonalCardDetailsHeaderMenuProps = {
expensifyIcons: Record<string, IconAsset>;
isCSVImportedPersonalCard: boolean;
reimbursableSetting: boolean;
lastScrape: string;
isOffline: boolean;
shouldShowBreakConnection: boolean;
onUpdateCard: () => void;
onBreakConnection: () => void;
onUnassignCard: () => void;
};
Expand All @@ -45,17 +40,14 @@ function PersonalCardDetailsHeaderMenu({
expensifyIcons,
isCSVImportedPersonalCard,
reimbursableSetting,
lastScrape,
isOffline,
shouldShowBreakConnection,
onUpdateCard,
onBreakConnection,
onUnassignCard,
}: PersonalCardDetailsHeaderMenuProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const icons = useMemoizedLazyExpensifyIcons(['Hourglass', 'Trashcan']);
const isLoadingLastUpdatedReasonAttributes: SkeletonSpanReasonAttributes = {context: 'PersonalCardDetailsHeaderMenu', isLoadingLastUpdated: !!card?.isLoadingLastUpdated};

return (
<>
Expand Down Expand Up @@ -101,19 +93,6 @@ function PersonalCardDetailsHeaderMenu({
onCloseError={() => card && clearCardErrorField(card.cardID, 'reimbursable')}
wrapperStyle={[styles.ph5, styles.mb3]}
/>

<MenuItemWithTopDescription
shouldShowRightComponent={card?.isLoadingLastUpdated}
rightComponent={
<ActivityIndicator
style={[styles.popoverMenuIcon]}
reasonAttributes={isLoadingLastUpdatedReasonAttributes}
/>
}
description={translate('workspace.moreFeatures.companyCards.lastUpdated')}
title={card?.isLoadingLastUpdated ? translate('workspace.moreFeatures.companyCards.updating') : lastScrape}
interactive={false}
/>
{!isCSVImportedPersonalCard && (
<OfflineWithFeedback
pendingAction={card?.pendingFields?.scrapeMinDate}
Expand All @@ -135,39 +114,6 @@ function PersonalCardDetailsHeaderMenu({
/>
</OfflineWithFeedback>
)}
<MenuItem
icon={expensifyIcons.MoneySearch}
title={translate('workspace.common.viewTransactions')}
style={styles.mt3}
onPress={() => {
Navigation.navigate(
ROUTES.SEARCH_ROOT.getRoute({
query: buildCannedSearchQuery({type: CONST.SEARCH.DATA_TYPES.EXPENSE, status: CONST.SEARCH.STATUS.EXPENSE.ALL, cardID}),
}),
);
}}
/>
{!isCSVImportedPersonalCard && (
<OfflineWithFeedback
pendingAction={card?.pendingFields?.lastScrape}
errorRowStyles={[styles.ph5, styles.mb3]}
errors={getLatestErrorField(card ?? {}, 'lastScrape')}
onClose={() => {
if (!card) {
return;
}
clearCardErrorField(card.cardID, 'lastScrape');
}}
>
<MenuItem
icon={expensifyIcons.Sync}
disabled={isOffline || card?.isLoadingLastUpdated}
title={translate('workspace.moreFeatures.companyCards.updateCard')}
brickRoadIndicator={card?.errorFields?.lastScrape ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
onPress={onUpdateCard}
/>
</OfflineWithFeedback>
)}
{shouldShowBreakConnection && (
<MenuItem
icon={icons.Trashcan}
Expand Down
65 changes: 59 additions & 6 deletions src/pages/settings/Wallet/PersonalCardDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,25 @@
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import Text from '@components/Text';
import {useCompanyCardFeedIcons} from '@hooks/useCompanyCardIcons';
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 useThemeIllustrations from '@hooks/useThemeIllustrations';
import useThemeStyles from '@hooks/useThemeStyles';
import {isUsingStagingApi} from '@libs/ApiUtils';
import navigateToCardTransactions from '@libs/CardNavigationUtils';
import {getCardFeedIcon, 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 CardDetailsActionButtons from '@pages/settings/Wallet/CardDetailsActionButtons';

Check failure on line 30 in src/pages/settings/Wallet/PersonalCardDetailsPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unexpected subpath import via alias '@pages/settings/Wallet/CardDetailsActionButtons'. Use './CardDetailsActionButtons' instead

Check failure on line 30 in src/pages/settings/Wallet/PersonalCardDetailsPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unexpected subpath import via alias '@pages/settings/Wallet/CardDetailsActionButtons'. Use './CardDetailsActionButtons' instead
import variables from '@styles/variables';
import {clearCardErrorField, syncCard, unassignCard} from '@userActions/Card';
import {openOldDotLink} from '@userActions/Link';
Expand All @@ -45,9 +49,10 @@
const [isUnassignModalVisible, setIsUnassignModalVisible] = useState(false);
const {translate, getLocalDateFromDatetime} = useLocalize();
const styles = useThemeStyles();
const theme = useTheme();
const illustrations = useThemeIllustrations();
const companyCardFeedIcons = useCompanyCardFeedIcons();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['FallbackAvatar', 'MoneySearch', 'RemoveMembers', 'Sync']);
const expensifyIcons = useMemoizedLazyExpensifyIcons(['MoneySearch', 'RemoveMembers', 'Sync']);

const {isOffline} = useNetwork();

Expand Down Expand Up @@ -100,6 +105,8 @@
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 <NotFoundPage />;
Expand Down Expand Up @@ -128,7 +135,53 @@
height={variables.cardPreviewHeight}
width={variables.cardPreviewWidth}
/>
<Text
style={styles.walletCardHolder}
numberOfLines={1}
ellipsizeMode="tail"
>
{displayName}
</Text>
</View>
<Text style={[styles.textLabelSupporting, styles.textAlignCenter, styles.ph5, styles.mb3]}>
{`${translate('workspace.moreFeatures.companyCards.lastUpdated')}: ${card?.isLoadingLastUpdated ? translate('workspace.moreFeatures.companyCards.updating') : lastScrape}`}
</Text>
<OfflineWithFeedback
pendingAction={card?.pendingFields?.lastScrape}
errorRowStyles={[styles.ph5, styles.mb3]}
errors={getLatestErrorField(card ?? {}, 'lastScrape')}
onClose={() => {
if (!card) {
return;
}
clearCardErrorField(card.cardID, 'lastScrape');
}}
>
<CardDetailsActionButtons style={[styles.ph10, isCardBroken ? styles.mb0 : undefined]}>
{!isCSVImportedPersonalCard && (
<View style={styles.flex1}>
<Button
text={translate('workspace.moreFeatures.companyCards.updateCard')}
icon={expensifyIcons.Sync}
iconFill={theme.icon}
onPress={updateCard}
isDisabled={isOffline || card?.isLoadingLastUpdated}
isLoading={card?.isLoadingLastUpdated}
style={styles.w100}
/>
</View>
)}
<View style={styles.flex1}>
<Button
text={translate('workspace.common.viewTransactions')}
icon={expensifyIcons.MoneySearch}
iconFill={theme.icon}
onPress={navigateToTransactions}
style={styles.w100}
/>
</View>
</CardDetailsActionButtons>
</OfflineWithFeedback>
{isCardBroken && (
<OfflineWithFeedback
pendingAction={card?.pendingFields?.lastScrape}
Expand All @@ -141,18 +194,20 @@
clearCardErrorField(card.cardID, 'lastScrape');
}}
>
<View style={[styles.ph5, styles.mb3]}>
<View style={[styles.ph5, styles.pv3, styles.mt1, styles.mb6, styles.flexRow, styles.alignItemsCenter, styles.gap3]}>
<FormHelpMessage
isError
shouldShowRedDotIndicator
message={translate('personalCard.brokenConnection')}
style={styles.mb3}
style={[styles.flex1, styles.mb0]}
/>
<Button
small
danger
text={translate('personalCard.fixCard')}
onPress={() => openOldDotLink(CONST.OLDDOT_URLS.SETTINGS_WALLET_URL)}
isDisabled={isOffline || card?.isLoadingLastUpdated}
style={styles.mb0}
style={[styles.mb0, styles.alignSelfStart]}
/>
</View>
</OfflineWithFeedback>
Expand All @@ -167,10 +222,8 @@
expensifyIcons={expensifyIcons}
isCSVImportedPersonalCard={isCSVImportedPersonalCard}
reimbursableSetting={reimbursableSetting}
lastScrape={lastScrape}
isOffline={isOffline}
shouldShowBreakConnection={shouldShowBreakConnection}
onUpdateCard={updateCard}
onBreakConnection={breakConnection}
onUnassignCard={() => setIsUnassignModalVisible(true)}
/>
Expand Down
Loading