diff --git a/assets/images/simple-illustrations/emptystate__puzzlepieces.svg b/assets/images/simple-illustrations/emptystate__puzzlepieces.svg
new file mode 100644
index 000000000000..d137ce5dcff2
--- /dev/null
+++ b/assets/images/simple-illustrations/emptystate__puzzlepieces.svg
@@ -0,0 +1,93 @@
+
+
+
diff --git a/src/CONST.ts b/src/CONST.ts
index ac42732f2325..cff89c5da642 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -2581,6 +2581,11 @@ const CONST = {
INDIVIDUAL: 'individual',
NONE: 'none',
},
+ VERIFICATION_STATE: {
+ LOADING: 'loading',
+ VERIFIED: 'verified',
+ ON_WAITLIST: 'onWaitlist',
+ },
STATE: {
STATE_NOT_ISSUED: 2,
OPEN: 3,
diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts
index 18ae1792686f..0efb65ed7a61 100644
--- a/src/components/Icon/Illustrations.ts
+++ b/src/components/Icon/Illustrations.ts
@@ -54,6 +54,7 @@ import ThreeLeggedLaptopWoman from '@assets/images/product-illustrations/three_l
import ToddBehindCloud from '@assets/images/product-illustrations/todd-behind-cloud.svg';
import ToddWithPhones from '@assets/images/product-illustrations/todd-with-phones.svg';
import BigVault from '@assets/images/simple-illustrations/emptystate__big-vault.svg';
+import Puzzle from '@assets/images/simple-illustrations/emptystate__puzzlepieces.svg';
import Abacus from '@assets/images/simple-illustrations/simple-illustration__abacus.svg';
import Accounting from '@assets/images/simple-illustrations/simple-illustration__accounting.svg';
import Alert from '@assets/images/simple-illustrations/simple-illustration__alert.svg';
@@ -200,6 +201,7 @@ export {
TrashCan,
TeleScope,
Profile,
+ Puzzle,
PalmTree,
LockClosed,
Gears,
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 5fb71704e9cd..d7f8623dcf15 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -3212,6 +3212,16 @@ const translations = {
issuedCardNoShippingDetails: ({assignee}: AssigneeParams) => `issued ${assignee} an Expensify Card! The card will be shipped once shipping details are added.`,
issuedCardVirtual: ({assignee, link}: IssueVirtualCardParams) => `issued ${assignee} a virtual ${link}! The card can be used right away.`,
addedShippingDetails: ({assignee}: AssigneeParams) => `${assignee} added shipping details. Expensify Card will arrive in 2-3 business days.`,
+ verifyingHeader: 'Verifying',
+ bankAccountVerifiedHeader: 'Bank account verified',
+ verifyingBankAccount: 'Verifying bank account...',
+ verifyingBankAccountDescription: 'Please wait while we confirm that this account can be used to issue Expensify Cards.',
+ bankAccountVerified: 'Bank account verified!',
+ bankAccountVerifiedDescription: 'You can now issue Expensify Cards to your workspace members.',
+ oneMoreStep: 'One more step...',
+ oneMoreStepDescription: 'Looks like we need to manually verify your bank account. Please head on over to Concierge where your instructions are waiting for you.',
+ gotIt: 'Got it',
+ goToConcierge: 'Go to Concierge',
},
categories: {
deleteCategories: 'Delete categories',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index ae6e9551dd0e..7c690d2bf679 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -3253,6 +3253,16 @@ const translations = {
issuedCardNoShippingDetails: ({assignee}: AssigneeParams) => `¡emitió a ${assignee} una Tarjeta Expensify! La tarjeta se enviará una vez que se agreguen los detalles de envío.`,
issuedCardVirtual: ({assignee, link}: IssueVirtualCardParams) => `¡emitió a ${assignee} una ${link} virtual! La tarjeta puede utilizarse inmediatamente.`,
addedShippingDetails: ({assignee}: AssigneeParams) => `${assignee} agregó los detalles de envío. La Tarjeta Expensify llegará en 2-3 días hábiles.`,
+ verifyingHeader: 'Verificando',
+ bankAccountVerifiedHeader: 'Cuenta bancaria verificada',
+ verifyingBankAccount: 'Verificando cuenta bancaria...',
+ verifyingBankAccountDescription: 'Por favor, espere mientras confirmamos que esta cuenta se puede utilizar para emitir tarjetas Expensify.',
+ bankAccountVerified: '¡Cuenta bancaria verificada!',
+ bankAccountVerifiedDescription: 'Ahora puedes emitir tarjetas de Expensify para los miembros de tu espacio de trabajo.',
+ oneMoreStep: 'Un paso más',
+ oneMoreStepDescription: 'Parece que tenemos que verificar manualmente tu cuenta bancaria. Dirígete a Concierge, donde te esperan las instrucciones.',
+ gotIt: 'Entendido',
+ goToConcierge: 'Ir a Concierge',
},
categories: {
deleteCategories: 'Eliminar categorías',
diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts
index d588e3195588..2a9faa02b261 100644
--- a/src/libs/actions/Card.ts
+++ b/src/libs/actions/Card.ts
@@ -300,6 +300,7 @@ function updateSettlementAccount(workspaceAccountID: number, policyID: string, s
key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
value: {
paymentBankAccountID: settlementBankAccountID,
+ isLoading: true,
},
},
];
@@ -310,6 +311,7 @@ function updateSettlementAccount(workspaceAccountID: number, policyID: string, s
key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
value: {
paymentBankAccountID: settlementBankAccountID,
+ isLoading: false,
},
},
];
@@ -320,6 +322,8 @@ function updateSettlementAccount(workspaceAccountID: number, policyID: string, s
key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
value: {
paymentBankAccountID: currentSettlementBankAccountID,
+ isLoading: false,
+ errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
];
@@ -614,12 +618,52 @@ function configureExpensifyCardsForPolicy(policyID: string, bankAccountID?: numb
return;
}
+ const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID);
+
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
+ value: {
+ isLoading: true,
+ isSuccess: false,
+ },
+ },
+ ];
+
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
+ value: {
+ isLoading: false,
+ isSuccess: true,
+ },
+ },
+ ];
+
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
+ value: {
+ isLoading: false,
+ errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
+ isSuccess: false,
+ },
+ },
+ ];
+
const parameters = {
policyID,
bankAccountID,
};
- API.write(WRITE_COMMANDS.CONFIGURE_EXPENSIFY_CARDS_FOR_POLICY, parameters);
+ API.write(WRITE_COMMANDS.CONFIGURE_EXPENSIFY_CARDS_FOR_POLICY, parameters, {
+ optimisticData,
+ successData,
+ failureData,
+ });
}
function issueExpensifyCard(policyID: string, feedCountry: string, data?: IssueNewCardData) {
diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx
index b8b8776df048..f296b16c13f6 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx
@@ -2,9 +2,13 @@ import type {StackScreenProps} from '@react-navigation/stack';
import React from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
+import BlockingView from '@components/BlockingViews/BlockingView';
+import Button from '@components/Button';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import getBankIcon from '@components/Icon/BankIcons';
import * as Expensicons from '@components/Icon/Expensicons';
+import * as Illustrations from '@components/Icon/Illustrations';
+import LottieAnimations from '@components/LottieAnimations';
import MenuItem from '@components/MenuItem';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
@@ -16,6 +20,7 @@ import * as PolicyUtils from '@libs/PolicyUtils';
import Navigation from '@navigation/Navigation';
import type {SettingsNavigatorParamList} from '@navigation/types';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
+import variables from '@styles/variables';
import * as Card from '@userActions/Card';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -35,14 +40,30 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA
const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID);
+ const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`);
+ const [cardOnWaitlist] = useOnyx(`${ONYXKEYS.COLLECTION.NVP_EXPENSIFY_ON_CARD_WAITLIST}${policyID}`);
+
+ const getVerificationState = () => {
+ if (cardOnWaitlist) {
+ return CONST.EXPENSIFY_CARD.VERIFICATION_STATE.ON_WAITLIST;
+ }
+ if (cardSettings?.isSuccess) {
+ return CONST.EXPENSIFY_CARD.VERIFICATION_STATE.VERIFIED;
+ }
+
+ if (cardSettings?.isLoading) {
+ return CONST.EXPENSIFY_CARD.VERIFICATION_STATE.LOADING;
+ }
+
+ return '';
+ };
+
const handleAddBankAccount = () => {
Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('new', policyID, ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID)));
};
const handleSelectBankAccount = (value?: number) => {
Card.configureExpensifyCardsForPolicy(policyID, value);
- Card.updateSettlementAccount(workspaceAccountID, policyID, value);
- Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID));
};
const renderBankOptions = () => {
@@ -75,6 +96,82 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA
});
};
+ const verificationState = getVerificationState();
+ const isInVerificationState = !!verificationState;
+
+ const renderVerificationStateView = () => {
+ switch (verificationState) {
+ case CONST.EXPENSIFY_CARD.VERIFICATION_STATE.LOADING:
+ return (
+
+ );
+ case CONST.EXPENSIFY_CARD.VERIFICATION_STATE.ON_WAITLIST:
+ return (
+ <>
+
+