From 118bb207db78a324ebce78e0cb000d84ba988852 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 17 Oct 2024 19:39:35 +0700 Subject: [PATCH 01/10] feat: add translations --- src/languages/en.ts | 8 ++++++++ src/languages/es.ts | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index d73015693e7a..d1ef9cbff396 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3177,6 +3177,14 @@ 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: 'Hold on while we check that this account can be used for issuing 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.', }, categories: { deleteCategories: 'Delete categories', diff --git a/src/languages/es.ts b/src/languages/es.ts index a9d35a6f8228..d11806c1db32 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3217,6 +3217,14 @@ 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: 'Espera mientras comprobamos 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.', }, categories: { deleteCategories: 'Eliminar categorías', From 60322dcb55b7fb725947eaaf175b4877adfa95be Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Mon, 21 Oct 2024 20:15:51 +0700 Subject: [PATCH 02/10] feat: add isLoading and errors to the requests configuring and updating bank account --- .../emptystate__puzzlepieces.svg | 93 +++++++++++++++++++ src/libs/actions/Card.ts | 43 ++++++++- .../WorkspaceExpensifyCardBankAccounts.tsx | 27 ++++-- src/types/onyx/ExpensifyCardSettings.ts | 3 + 4 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 assets/images/simple-illustrations/emptystate__puzzlepieces.svg 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/libs/actions/Card.ts b/src/libs/actions/Card.ts index d588e3195588..7b36b92b71d6 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,49 @@ 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, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, + value: { + isLoading: false, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, + value: { + isLoading: false, + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + ]; + 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..4a2d7866992d 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx @@ -35,13 +35,16 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); + const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`); + + const isLoading = cardSettings?.isLoading; + 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)); }; @@ -91,15 +94,19 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA onBackButtonPress={() => Navigation.goBack()} title={translate('workspace.expensifyCard.chooseBankAccount')} /> - - {translate('workspace.expensifyCard.chooseExistingBank')} - {renderBankOptions()} - - + {isLoading ? ( + Loading + ) : ( + + {translate('workspace.expensifyCard.chooseExistingBank')} + {renderBankOptions()} + + + )} ); diff --git a/src/types/onyx/ExpensifyCardSettings.ts b/src/types/onyx/ExpensifyCardSettings.ts index 05c6667e7c05..7fcebd5c09fb 100644 --- a/src/types/onyx/ExpensifyCardSettings.ts +++ b/src/types/onyx/ExpensifyCardSettings.ts @@ -22,6 +22,9 @@ type ExpensifyCardSettings = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Whether we are loading the data via the API */ isLoading?: boolean; + + /** Error message */ + errors?: OnyxCommon.Errors; }>; export default ExpensifyCardSettings; From f891ec6fcc825998de428f24ed392219df6b3714 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 22 Oct 2024 13:05:30 +0700 Subject: [PATCH 03/10] feat: do not navigate the user to issue new card flow instantly --- .../expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx index 4a2d7866992d..25714f215ca0 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx @@ -45,7 +45,6 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA const handleSelectBankAccount = (value?: number) => { Card.configureExpensifyCardsForPolicy(policyID, value); - Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID)); }; const renderBankOptions = () => { @@ -92,7 +91,7 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA Navigation.goBack()} - title={translate('workspace.expensifyCard.chooseBankAccount')} + title={translate(isLoading ? 'workspace.expensifyCard.verifyingHeader' : 'workspace.expensifyCard.chooseBankAccount')} /> {isLoading ? ( Loading From c6bed03f03087507d3de1b31f2db5d68d78ff4a5 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 22 Oct 2024 13:49:42 +0700 Subject: [PATCH 04/10] feat: style loading state, change translation --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- .../WorkspaceExpensifyCardBankAccounts.tsx | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 49fcfc3f2e5e..4ade5a1b0f95 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3205,7 +3205,7 @@ const translations = { verifyingHeader: 'Verifying', bankAccountVerifiedHeader: 'Bank account verified', verifyingBankAccount: 'Verifying bank account...', - verifyingBankAccountDescription: 'Hold on while we check that this account can be used for issuing Expensify Cards', + 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...', diff --git a/src/languages/es.ts b/src/languages/es.ts index 7fd5ca643cbf..9b5c73b914af 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3247,7 +3247,7 @@ const translations = { verifyingHeader: 'Verificando', bankAccountVerifiedHeader: 'Cuenta bancaria verificada', verifyingBankAccount: 'Verificando cuenta bancaria...', - verifyingBankAccountDescription: 'Espera mientras comprobamos que esta cuenta se puede utilizar para emitir tarjetas Expensify.', + 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', diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx index 25714f215ca0..906ed03e9399 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx @@ -2,9 +2,12 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import getBankIcon from '@components/Icon/BankIcons'; import * as Expensicons from '@components/Icon/Expensicons'; +import Lottie from '@components/Lottie'; +import LottieAnimations from '@components/LottieAnimations'; import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -94,7 +97,21 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA title={translate(isLoading ? 'workspace.expensifyCard.verifyingHeader' : 'workspace.expensifyCard.chooseBankAccount')} /> {isLoading ? ( - Loading + + + + + {translate('workspace.expensifyCard.verifyingBankAccount')} + {translate('workspace.expensifyCard.verifyingBankAccountDescription')} + + + ) : ( {translate('workspace.expensifyCard.chooseExistingBank')} From e7d6b964735ace81370ee2e52119f183b210f198 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 23 Oct 2024 11:55:28 +0700 Subject: [PATCH 05/10] feat: create a separate component for loading state --- .../WorkspaceExpensifyCardBankAccounts.tsx | 52 +++++++----------- .../WorkspaceExpensifyCardLoadingView.tsx | 54 +++++++++++++++++++ 2 files changed, 73 insertions(+), 33 deletions(-) create mode 100644 src/pages/workspace/expensifyCard/WorkspaceExpensifyCardLoadingView.tsx diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx index 906ed03e9399..d0f803fbc64e 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx @@ -2,12 +2,9 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; -import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import getBankIcon from '@components/Icon/BankIcons'; import * as Expensicons from '@components/Icon/Expensicons'; -import Lottie from '@components/Lottie'; -import LottieAnimations from '@components/LottieAnimations'; import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -19,6 +16,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 WorkspaceExpensifyCardLoadingView from '@pages/workspace/expensifyCard/WorkspaceExpensifyCardLoadingView'; import * as Card from '@userActions/Card'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -40,7 +38,7 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`); - const isLoading = cardSettings?.isLoading; + const shouldShowLoadingView = cardSettings?.isLoading; const handleAddBankAccount = () => { Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('new', policyID, ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID))); @@ -91,37 +89,25 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA includeSafeAreaPaddingBottom={false} shouldEnablePickerAvoiding={false} > - Navigation.goBack()} - title={translate(isLoading ? 'workspace.expensifyCard.verifyingHeader' : 'workspace.expensifyCard.chooseBankAccount')} - /> - {isLoading ? ( - - - - - {translate('workspace.expensifyCard.verifyingBankAccount')} - {translate('workspace.expensifyCard.verifyingBankAccountDescription')} - - - + {!shouldShowLoadingView ? ( + ) : ( - - {translate('workspace.expensifyCard.chooseExistingBank')} - {renderBankOptions()} - + Navigation.goBack()} + title={translate('workspace.expensifyCard.chooseBankAccount')} /> - + + {translate('workspace.expensifyCard.chooseExistingBank')} + {renderBankOptions()} + + + )} diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardLoadingView.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardLoadingView.tsx new file mode 100644 index 000000000000..662514da83a2 --- /dev/null +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardLoadingView.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import {View} from 'react-native'; +import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; +import Button from '@components/Button'; +import FixedFooter from '@components/FixedFooter'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Lottie from '@components/Lottie'; +import LottieAnimations from '@components/LottieAnimations'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@navigation/Navigation'; + +function WorkspaceExpensifyCardLoadingView() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( + <> + Navigation.goBack()} + title={translate('workspace.expensifyCard.verifyingHeader')} + /> + + + + + {translate('workspace.expensifyCard.bankAccountVerified')} + {translate('workspace.expensifyCard.bankAccountVerifiedDescription')} + + + +