Skip to content

Commit

Permalink
Merge pull request #28312 from pasyukevich/feature/report-fraud
Browse files Browse the repository at this point in the history
add “Report virtual card fraud” page and route
  • Loading branch information
marcaaron authored Oct 10, 2023
2 parents 3901bcb + 00666f5 commit cb883e8
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ const ONYXKEYS = {
PRIVATE_NOTES_FORM: 'privateNotesForm',
I_KNOW_A_TEACHER_FORM: 'iKnowTeacherForm',
INTRO_SCHOOL_PRINCIPAL_FORM: 'introSchoolPrincipalForm',
REPORT_VIRTUAL_CARD_FRAUD: 'reportVirtualCardFraudForm',
},
} as const;

Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ export default {
route: '/settings/wallet/card/:domain',
getRoute: (domain: string) => `/settings/wallet/card/${domain}`,
},
SETTINGS_REPORT_FRAUD: {
route: '/settings/wallet/cards/:domain/report-virtual-fraud',
getRoute: (domain: string) => `/settings/wallet/cards/${domain}/report-virtual-fraud`,
},
SETTINGS_ADD_DEBIT_CARD: 'settings/wallet/add-debit-card',
SETTINGS_ADD_BANK_ACCOUNT: 'settings/wallet/add-bank-account',
SETTINGS_ENABLE_PAYMENTS: 'settings/wallet/enable-payments',
Expand Down
7 changes: 7 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,7 @@ export default {
availableSpend: 'Remaining spending power',
virtualCardNumber: 'Virtual card number',
physicalCardNumber: 'Physical card number',
reportFraud: 'Report virtual card fraud',
cardDetails: {
cardNumber: 'Virtual card number',
expiration: 'Expiration',
Expand All @@ -842,6 +843,12 @@ export default {
copyCardNumber: 'Copy card number',
},
},
reportFraudPage: {
title: 'Report virtual card fraud',
description: 'If your virtual card details have been stolen or compromised, we’ll permanently deactivate your existing card and provide you with a new virtual card and number.',
deactivateCard: 'Deactivate card',
reportVirtualCardFraud: 'Report virtual card fraud',
},
activateCardPage: {
activateCard: 'Activate card',
pleaseEnterLastFour: 'Please enter the last four digits of your card.',
Expand Down
8 changes: 8 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@ export default {
availableSpend: 'Capacidad de gasto restante',
virtualCardNumber: 'Número de la tarjeta virtual',
physicalCardNumber: 'Número de la tarjeta física',
reportFraud: 'Reportar fraude con la tarjeta virtual',
cardDetails: {
cardNumber: 'Número de tarjeta virtual',
expiration: 'Expiración',
Expand All @@ -838,6 +839,13 @@ export default {
copyCardNumber: 'Copiar número de la tarjeta',
},
},
reportFraudPage: {
title: 'Reportar fraude con la tarjeta virtual',
description:
'Si los datos de tu tarjeta virtual han sido robados o se han visto comprometidos, desactivaremos permanentemente la tarjeta actual y le proporcionaremos una tarjeta virtual y un número nuevo.',
deactivateCard: 'Desactivar tarjeta',
reportVirtualCardFraud: 'Reportar fraude con la tarjeta virtual',
},
activateCardPage: {
activateCard: 'Activar tarjeta',
pleaseEnterLastFour: 'Introduce los cuatro últimos dígitos de la tarjeta.',
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ const SettingsModalStackNavigator = createModalStackNavigator({
Settings_Lounge_Access: () => require('../../../pages/settings/Profile/LoungeAccessPage').default,
Settings_Wallet: () => require('../../../pages/settings/Wallet/WalletPage').default,
Settings_Wallet_DomainCards: () => require('../../../pages/settings/Wallet/ExpensifyCardPage').default,
Settings_Wallet_ReportVirtualCardFraud: () => require('../../../pages/settings/Wallet/ReportVirtualCardFraudPage').default,
Settings_Wallet_Card_Activate: () => require('../../../pages/settings/Wallet/ActivatePhysicalCardPage').default,
Settings_Wallet_Transfer_Balance: () => require('../../../pages/settings/Wallet/TransferBalancePage').default,
Settings_Wallet_Choose_Transfer_Account: () => require('../../../pages/settings/Wallet/ChooseTransferAccountPage').default,
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ export default {
path: ROUTES.SETTINGS_WALLET_DOMAINCARDS.route,
exact: true,
},
Settings_Wallet_ReportVirtualCardFraud: {
path: ROUTES.SETTINGS_REPORT_FRAUD.route,
exact: true,
},
Settings_Wallet_EnablePayments: {
path: ROUTES.SETTINGS_ENABLE_PAYMENTS,
exact: true,
Expand Down
43 changes: 42 additions & 1 deletion src/libs/actions/Card.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,47 @@ import Onyx from 'react-native-onyx';
import ONYXKEYS from '../../ONYXKEYS';
import * as API from '../API';

/**
* @param {Number} cardID
*/
function reportVirtualExpensifyCardFraud(cardID) {
API.write(
'ReportVirtualExpensifyCardFraud',
{
cardID,
},
{
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD,
value: {
isLoading: true,
},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD,
value: {
isLoading: false,
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD,
value: {
isLoading: false,
},
},
],
},
);
}

/**
* Activates the physical Expensify card based on the last four digits of the card number
*
Expand Down Expand Up @@ -60,4 +101,4 @@ function clearCardListErrors(cardID) {
Onyx.merge(ONYXKEYS.CARD_LIST, {[cardID]: {errors: null, isLoading: false}});
}

export {activatePhysicalExpensifyCard, clearCardListErrors};
export {reportVirtualExpensifyCardFraud, activatePhysicalExpensifyCard, clearCardListErrors};
10 changes: 9 additions & 1 deletion src/pages/settings/Wallet/ExpensifyCardPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import useLocalize from '../../../hooks/useLocalize';
import * as CurrencyUtils from '../../../libs/CurrencyUtils';
import Navigation from '../../../libs/Navigation/Navigation';
import styles from '../../../styles/styles';
import * as Expensicons from '../../../components/Icon/Expensicons';
import * as CardUtils from '../../../libs/CardUtils';
import Button from '../../../components/Button';
import CardDetails from './WalletPage/CardDetails';
Expand Down Expand Up @@ -108,14 +109,21 @@ function ExpensifyCardPage({
}
/>
)}
<MenuItemWithTopDescription
title={translate('cardPage.reportFraud')}
titleStyle={styles.walletCardMenuItem}
icon={Expensicons.Flag}
shouldShowRightIcon
onPress={() => Navigation.navigate(ROUTES.SETTINGS_REPORT_FRAUD.getRoute(domain))}
/>
</>
)}
{!_.isEmpty(physicalCard) && (
<MenuItemWithTopDescription
description={translate('cardPage.physicalCardNumber')}
title={CardUtils.maskCard(physicalCard.lastFourPAN)}
interactive={false}
titleStyle={styles.walletCardNumber}
titleStyle={styles.walletCardMenuItem}
/>
)}
</ScrollView>
Expand Down
104 changes: 104 additions & 0 deletions src/pages/settings/Wallet/ReportVirtualCardFraudPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, {useEffect} from 'react';
import _ from 'underscore';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import ROUTES from '../../../ROUTES';
import HeaderWithBackButton from '../../../components/HeaderWithBackButton';
import ScreenWrapper from '../../../components/ScreenWrapper';
import Navigation from '../../../libs/Navigation/Navigation';
import styles from '../../../styles/styles';
import Text from '../../../components/Text';
import useLocalize from '../../../hooks/useLocalize';
import * as Card from '../../../libs/actions/Card';
import assignedCardPropTypes from './assignedCardPropTypes';
import * as CardUtils from '../../../libs/CardUtils';
import ONYXKEYS from '../../../ONYXKEYS';
import NotFoundPage from '../../ErrorPage/NotFoundPage';
import usePrevious from '../../../hooks/usePrevious';
import FormAlertWithSubmitButton from '../../../components/FormAlertWithSubmitButton';
import * as ErrorUtils from '../../../libs/ErrorUtils';

const propTypes = {
/* Onyx Props */
formData: PropTypes.shape({
isLoading: PropTypes.bool,
}),
cardList: PropTypes.objectOf(assignedCardPropTypes),
/** The parameters needed to authenticate with a short-lived token are in the URL */
route: PropTypes.shape({
/** Each parameter passed via the URL */
params: PropTypes.shape({
/** Domain string */
domain: PropTypes.string,
}),
}).isRequired,
};

const defaultProps = {
cardList: {},
formData: {},
};

function ReportVirtualCardFraudPage({
route: {
params: {domain},
},
cardList,
formData,
}) {
const {translate} = useLocalize();

const domainCards = CardUtils.getDomainCards(cardList)[domain];
const virtualCard = _.find(domainCards, (card) => card.isVirtual) || {};
const virtualCardError = ErrorUtils.getLatestErrorMessage(virtualCard) || '';

const prevIsLoading = usePrevious(formData.isLoading);

useEffect(() => {
if (!prevIsLoading || formData.isLoading) {
return;
}
if (!_.isEmpty(virtualCard.errors)) {
return;
}

Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARDS.getRoute(domain));
}, [domain, formData.isLoading, prevIsLoading, virtualCard.errors]);

if (_.isEmpty(virtualCard)) {
return <NotFoundPage />;
}

return (
<ScreenWrapper testID={ReportVirtualCardFraudPage.displayName}>
<HeaderWithBackButton
title={translate('reportFraudPage.title')}
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARDS.getRoute(domain))}
/>
<View style={[styles.flex1, styles.justifyContentBetween]}>
<Text style={[styles.baseFontStyle, styles.mh5]}>{translate('reportFraudPage.description')}</Text>
<FormAlertWithSubmitButton
isAlertVisible={Boolean(virtualCardError)}
onSubmit={() => Card.reportVirtualExpensifyCardFraud(virtualCard.cardID)}
message={virtualCardError}
isLoading={formData.isLoading}
buttonText={translate('reportFraudPage.deactivateCard')}
/>
</View>
</ScreenWrapper>
);
}

ReportVirtualCardFraudPage.propTypes = propTypes;
ReportVirtualCardFraudPage.defaultProps = defaultProps;
ReportVirtualCardFraudPage.displayName = 'ReportVirtualCardFraudPage';

export default withOnyx({
cardList: {
key: ONYXKEYS.CARD_LIST,
},
formData: {
key: ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD,
},
})(ReportVirtualCardFraudPage);
2 changes: 1 addition & 1 deletion src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -3770,7 +3770,7 @@ const styles = (theme) => ({
overflow: 'hidden',
},

walletCardNumber: {
walletCardMenuItem: {
color: theme.text,
fontSize: variables.fontSizeNormal,
},
Expand Down

0 comments on commit cb883e8

Please sign in to comment.