diff --git a/assets/images/emptystate__expensifycard.svg b/assets/images/emptystate__expensifycard.svg
new file mode 100644
index 000000000000..c5699c059e69
--- /dev/null
+++ b/assets/images/emptystate__expensifycard.svg
@@ -0,0 +1,909 @@
+
+
+
diff --git a/src/components/EmptyStateComponent/index.tsx b/src/components/EmptyStateComponent/index.tsx
index a8ad9d0f3154..41d1a42931c6 100644
--- a/src/components/EmptyStateComponent/index.tsx
+++ b/src/components/EmptyStateComponent/index.tsx
@@ -14,7 +14,18 @@ import type {EmptyStateComponentProps, VideoLoadedEventType} from './types';
const VIDEO_ASPECT_RATIO = 400 / 225;
-function EmptyStateComponent({SkeletonComponent, headerMediaType, headerMedia, buttonText, buttonAction, title, subtitle, headerStyles, headerContentStyles}: EmptyStateComponentProps) {
+function EmptyStateComponent({
+ SkeletonComponent,
+ headerMediaType,
+ headerMedia,
+ buttonText,
+ buttonAction,
+ title,
+ subtitle,
+ headerStyles,
+ headerContentStyles,
+ emptyStateContentStyles,
+}: EmptyStateComponentProps) {
const styles = useThemeStyles();
const {isSmallScreenWidth} = useWindowDimensions();
const [videoAspectRatio, setVideoAspectRatio] = useState(VIDEO_ASPECT_RATIO);
@@ -76,7 +87,7 @@ function EmptyStateComponent({SkeletonComponent, headerMediaType, headerMedia, b
/>
-
+
{HeaderComponent}
{title}
diff --git a/src/components/EmptyStateComponent/types.ts b/src/components/EmptyStateComponent/types.ts
index 326b25542f42..96a60fa98513 100644
--- a/src/components/EmptyStateComponent/types.ts
+++ b/src/components/EmptyStateComponent/types.ts
@@ -19,6 +19,7 @@ type SharedProps = {
headerStyles?: StyleProp;
headerMediaType: T;
headerContentStyles?: StyleProp;
+ emptyStateContentStyles?: StyleProp;
};
type MediaType = SharedProps & {
diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts
index 6499e8eceb6e..ccbf4b3a5da9 100644
--- a/src/components/Icon/Illustrations.ts
+++ b/src/components/Icon/Illustrations.ts
@@ -1,3 +1,4 @@
+import EmptyCardState from '@assets/images/emptystate__expensifycard.svg';
import ExpensifyCardIllustration from '@assets/images/expensifyCard/cardIllustration.svg';
import LaptopwithSecondScreenandHourglass from '@assets/images/LaptopwithSecondScreenandHourglass.svg';
import Abracadabra from '@assets/images/product-illustrations/abracadabra.svg';
@@ -115,6 +116,7 @@ export {
ConciergeExclamation,
CreditCardsBlue,
EmailAddress,
+ EmptyCardState,
EmptyStateExpenses,
FolderOpen,
HandCard,
diff --git a/src/components/Skeletons/CardRowSkeleton.tsx b/src/components/Skeletons/CardRowSkeleton.tsx
new file mode 100644
index 000000000000..82c382048415
--- /dev/null
+++ b/src/components/Skeletons/CardRowSkeleton.tsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import {Circle, Rect} from 'react-native-svg';
+import useThemeStyles from '@hooks/useThemeStyles';
+import useWindowDimensions from '@hooks/useWindowDimensions';
+import variables from '@styles/variables';
+import ItemListSkeletonView from './ItemListSkeletonView';
+
+type CardRowSkeletonProps = {
+ shouldAnimate?: boolean;
+ fixedNumItems?: number;
+ gradientOpacityEnabled?: boolean;
+};
+
+const barHeight = 7;
+const longBarWidth = 120;
+const shortBarWidth = 60;
+const leftPaneWidth = variables.sideBarWidth;
+const gapWidth = 12;
+const rightSideElementWidth = 50;
+const centralPanePadding = 50;
+const rightButtonWidth = 20;
+
+function CardRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEnabled = false}: CardRowSkeletonProps) {
+ const styles = useThemeStyles();
+ const {windowWidth, isSmallScreenWidth} = useWindowDimensions();
+
+ return (
+ (
+ <>
+
+
+
+
+
+ {!isSmallScreenWidth && (
+ <>
+
+
+
+ >
+ )}
+ >
+ )}
+ />
+ );
+}
+
+CardRowSkeleton.displayName = 'CardRowSkeleton';
+
+export default CardRowSkeleton;
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 1f7f91cb8472..9bfe8e0faf38 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -2645,6 +2645,10 @@ export default {
collect: 'Collect',
},
expensifyCard: {
+ issueAndManageCards: 'Issue and manage your Expensify Cards',
+ getStartedIssuing: 'Get started by issuing your first virtual or physical card.',
+ disclaimer:
+ 'The Expensify Visa® Commercial Card is issued by The Bancorp Bank, N.A., Member FDIC, pursuant to a license from Visa U.S.A. Inc. and may not be used at all merchants that accept Visa cards. Apple® and the Apple logo® are trademarks of Apple Inc., registered in the U.S. and other countries. App Store is a service mark of Apple Inc. Google Play and the Google Play logo are trademarks of Google LLC.',
issueCard: 'Issue card',
name: 'Name',
lastFour: 'Last 4',
@@ -2947,6 +2951,8 @@ export default {
benefit4: 'Customizable limits and spend controls',
addWorkEmail: 'Add work email address',
checkingDomain: "Hang tight! We're still working on enabling your Expensify Cards. Check back here in a few minutes.",
+ issueAndManageCards: 'Issue and manage your Expensify Cards',
+ getStartedIssuing: 'Get started by issuing your first virtual or physical card.',
issueCard: 'Issue card',
issueNewCard: {
whoNeedsCard: 'Who needs a card?',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index dbef29504a34..a3bc94e81352 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -2694,6 +2694,10 @@ export default {
collect: 'Recolectar',
},
expensifyCard: {
+ issueAndManageCards: 'Emitir y gestionar Tarjetas Expensify',
+ getStartedIssuing: 'Empieza emitiendo tu primera tarjeta virtual o física.',
+ disclaimer:
+ 'La tarjeta comercial Expensify Visa® es emitida por The Bancorp Bank, N.A., miembro de la FDIC, en virtud de una licencia de Visa U.S.A. Inc. y no puede utilizarse en todos los comercios que aceptan tarjetas Visa. Apple® y el logotipo de Apple® son marcas comerciales de Apple Inc. registradas en EE.UU. y otros países. App Store es una marca de servicio de Apple Inc. Google Play y el logotipo de Google Play son marcas comerciales de Google LLC.',
issueCard: 'Emitir tarjeta',
name: 'Nombre',
lastFour: '4 últimos',
@@ -3204,6 +3208,8 @@ export default {
addWorkEmail: 'Añadir correo electrónico de trabajo',
checkingDomain: '¡Un momento! Estamos todavía trabajando para habilitar tu Tarjeta Expensify. Vuelve aquí en unos minutos.',
issueCard: 'Emitir tarjeta',
+ issueAndManageCards: 'Emitir y gestionar Tarjetas Expensify',
+ getStartedIssuing: 'Empieza emitiendo tu primera tarjeta virtual o física.',
issueNewCard: {
whoNeedsCard: '¿Quién necesita una tarjeta?',
findMember: 'Buscar miembro',
diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts
index b775be2fd031..b128e8f3045d 100644
--- a/src/libs/actions/Card.ts
+++ b/src/libs/actions/Card.ts
@@ -379,9 +379,9 @@ export {
reportVirtualExpensifyCardFraud,
revealVirtualCardDetails,
updateSettlementFrequency,
- updateSettlementAccount,
setIssueNewCardStepAndData,
clearIssueNewCardFlow,
updateExpensifyCardLimit,
+ updateSettlementAccount,
};
export type {ReplacementReason};
diff --git a/src/pages/workspace/card/issueNew/AssigneeStep.tsx b/src/pages/workspace/card/issueNew/AssigneeStep.tsx
index 042e93ae2fae..3190c4ba295c 100644
--- a/src/pages/workspace/card/issueNew/AssigneeStep.tsx
+++ b/src/pages/workspace/card/issueNew/AssigneeStep.tsx
@@ -22,6 +22,7 @@ import Navigation from '@navigation/Navigation';
import * as Card from '@userActions/Card';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
+import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
const MINIMUM_MEMBER_TO_SHOW_SEARCH = 8;
@@ -56,7 +57,7 @@ function AssigneeStep({policy}: AssigneeStepProps) {
Card.setIssueNewCardStepAndData({step: CONST.EXPENSIFY_CARD.STEP.CONFIRMATION, isEditing: false});
return;
}
- Navigation.goBack();
+ Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policy?.id ?? '-1'));
Card.clearIssueNewCardFlow();
};
diff --git a/src/pages/workspace/expensifyCard/EmptyCardView.tsx b/src/pages/workspace/expensifyCard/EmptyCardView.tsx
new file mode 100644
index 000000000000..9e82454f0509
--- /dev/null
+++ b/src/pages/workspace/expensifyCard/EmptyCardView.tsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import {View} from 'react-native';
+import EmptyStateComponent from '@components/EmptyStateComponent';
+import * as Illustrations from '@components/Icon/Illustrations';
+import ScrollView from '@components/ScrollView';
+import CardRowSkeleton from '@components/Skeletons/CardRowSkeleton';
+import Text from '@components/Text';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import useWindowDimensions from '@hooks/useWindowDimensions';
+import CONST from '@src/CONST';
+
+const HEADER_HEIGHT = 80;
+const BUTTON_HEIGHT = 40;
+const BUTTON_MARGIN = 12;
+
+function EmptyCardView() {
+ const {translate} = useLocalize();
+ const styles = useThemeStyles();
+ const {windowHeight, isSmallScreenWidth} = useWindowDimensions();
+
+ const headerHeight = isSmallScreenWidth ? HEADER_HEIGHT + BUTTON_HEIGHT + BUTTON_MARGIN : HEADER_HEIGHT;
+
+ return (
+
+
+
+
+ {translate('workspace.expensifyCard.disclaimer')}
+
+ );
+}
+
+EmptyCardView.displayName = 'EmptyCardView';
+
+export default EmptyCardView;
diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx
index e1ca8974a0ee..470b2c683e70 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx
@@ -14,6 +14,7 @@ import {getLastFourDigits} from '@libs/BankAccountUtils';
import * as CardUtils from '@libs/CardUtils';
import Navigation from '@navigation/Navigation';
import type {SettingsNavigatorParamList} from '@navigation/types';
+import * as Card from '@userActions/Card';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
@@ -34,7 +35,8 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA
Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('new', policyID, ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID)));
};
- const handleSelectBankAccount = () => {
+ const handleSelectBankAccount = (value: number) => {
+ Card.updateSettlementAccount(policyID, value);
Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID));
};
@@ -49,6 +51,8 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA
return eligibleBankAccounts.map((bankAccount) => {
const bankName = (bankAccount.accountData?.addressName ?? '') as BankName;
const bankAccountNumber = bankAccount.accountData?.accountNumber ?? '';
+ // TODO: change 1 to 0 - applied for testing purposes, as sometimes accountData lacks fundID
+ const bankAccountID = bankAccount.accountData?.fundID ?? 1;
const {icon, iconSize, iconStyles} = getBankIcon({bankName, styles});
@@ -56,7 +60,7 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA