From cff92c56969faf40305518dfffd808b057306958 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Wed, 3 Jul 2024 15:01:26 +0300 Subject: [PATCH 01/10] Workspace Feed - Initial card page --- .../images/expensifyCard/cardIllustration.svg | 173 +++++++++++++++++ src/components/Icon/Illustrations.ts | 2 + src/languages/en.ts | 10 + src/languages/es.ts | 10 + .../Navigators/FullScreenNavigator.tsx | 2 +- .../expensifyCard/WorkspaceCardPageFeed.tsx | 74 +++++++ .../WorkspaceExpensifyCardPage.tsx | 181 ------------------ src/styles/index.ts | 5 + 8 files changed, 275 insertions(+), 182 deletions(-) create mode 100644 assets/images/expensifyCard/cardIllustration.svg create mode 100644 src/pages/workspace/expensifyCard/WorkspaceCardPageFeed.tsx delete mode 100644 src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx diff --git a/assets/images/expensifyCard/cardIllustration.svg b/assets/images/expensifyCard/cardIllustration.svg new file mode 100644 index 000000000000..5ddbdf951fcf --- /dev/null +++ b/assets/images/expensifyCard/cardIllustration.svg @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index e699badc43ec..bd0824372799 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -1,3 +1,4 @@ +import ExpensifyCardIllustration from '@assets/images/expensifyCard/cardIllustration.svg'; import Abracadabra from '@assets/images/product-illustrations/abracadabra.svg'; import BankArrowPink from '@assets/images/product-illustrations/bank-arrow--pink.svg'; import BankMouseGreen from '@assets/images/product-illustrations/bank-mouse--green.svg'; @@ -176,6 +177,7 @@ export { Binoculars, CompanyCard, ReceiptUpload, + ExpensifyCardIllustration, SplitBill, PiggyBank, Accounting, diff --git a/src/languages/en.ts b/src/languages/en.ts index 936941003073..bf6b490a1c66 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2390,6 +2390,16 @@ export default { disableCardTitle: 'Disable Expensify Card', disableCardPrompt: 'You can’t disable the Expensify Card because it’s already in use. Reach out to Concierge for next steps.', disableCardButton: 'Chat with Concierge', + feed: { + title: 'Get the Expensify Card', + subTitle: 'Streamline your business with the Expensify Card', + features: { + cashBack: 'Up to 2% cash back on every US purchase', + unlimited: 'Issue unlimited virtual cards', + spend: 'Spend controls and custom limits', + }, + ctaTitle: 'Issue new card', + }, }, workflows: { title: 'Workflows', diff --git a/src/languages/es.ts b/src/languages/es.ts index 59aad3275c41..f3e6d88a470b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2420,6 +2420,16 @@ export default { disableCardTitle: 'Deshabilitar la Tarjeta Expensify', disableCardPrompt: 'No puedes deshabilitar la Tarjeta Expensify porque ya está en uso. Por favor, contacta con Concierge para conocer los pasos a seguir.', disableCardButton: 'Chatear con Concierge', + feed: { + title: 'Consigue la Tarjeta Expensify', + subTitle: 'Optimiza tu negocio con la Tarjeta Expensify', + features: { + cashBack: 'Hasta un 2% de devolución en cada compra en Estadios Unidos', + unlimited: 'Emitir un número ilimitado de tarjetas virtuales', + spend: 'Controles de gastos y límites personalizados', + }, + ctaTitle: 'Emitir nueva tarjeta', + }, }, distanceRates: { title: 'Tasas de distancia', diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 16e8404f5fe9..a1a04056af08 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -19,7 +19,6 @@ type Screens = Partial React.Co const CENTRAL_PANE_WORKSPACE_SCREENS = { [SCREENS.WORKSPACE.PROFILE]: () => require('../../../../pages/workspace/WorkspaceProfilePage').default, [SCREENS.WORKSPACE.CARD]: () => require('../../../../pages/workspace/card/WorkspaceCardPage').default, - [SCREENS.WORKSPACE.EXPENSIFY_CARD]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardPage').default, [SCREENS.WORKSPACE.WORKFLOWS]: () => require('../../../../pages/workspace/workflows/WorkspaceWorkflowsPage').default, [SCREENS.WORKSPACE.REIMBURSE]: () => require('../../../../pages/workspace/reimburse/WorkspaceReimbursePage').default, [SCREENS.WORKSPACE.BILLS]: () => require('../../../../pages/workspace/bills/WorkspaceBillsPage').default, @@ -32,6 +31,7 @@ const CENTRAL_PANE_WORKSPACE_SCREENS = { [SCREENS.WORKSPACE.TAGS]: () => require('../../../../pages/workspace/tags/WorkspaceTagsPage').default, [SCREENS.WORKSPACE.TAXES]: () => require('../../../../pages/workspace/taxes/WorkspaceTaxesPage').default, [SCREENS.WORKSPACE.REPORT_FIELDS]: () => require('../../../../pages/workspace/reportFields/WorkspaceReportFieldsPage').default, + [SCREENS.WORKSPACE.EXPENSIFY_CARD]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceCardPageFeed').default, [SCREENS.WORKSPACE.DISTANCE_RATES]: () => require('../../../../pages/workspace/distanceRates/PolicyDistanceRatesPage').default, } satisfies Screens; diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardPageFeed.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardPageFeed.tsx new file mode 100644 index 000000000000..48d84f2da2e5 --- /dev/null +++ b/src/pages/workspace/expensifyCard/WorkspaceCardPageFeed.tsx @@ -0,0 +1,74 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import {View} from 'react-native'; +import FeatureList from '@components/FeatureList'; +import type {FeatureListItem} from '@components/FeatureList'; +import * as Illustrations from '@components/Icon/Illustrations'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; +import CONST from '@src/CONST'; +import type SCREENS from '@src/SCREENS'; + +const tripsFeatures: FeatureListItem[] = [ + { + icon: Illustrations.MoneyReceipts, + translationKey: 'workspace.moreFeatures.expensifyCard.feed.features.cashBack', + }, + { + icon: Illustrations.CreditCardsNew, + translationKey: 'workspace.moreFeatures.expensifyCard.feed.features.unlimited', + }, + { + icon: Illustrations.MoneyWings, + translationKey: 'workspace.moreFeatures.expensifyCard.feed.features.spend', + }, +]; +type WorkspaceCardPageFreeProps = StackScreenProps; + +function WorkspaceCardPageFeed({route}: WorkspaceCardPageFreeProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const theme = useTheme(); + const {isSmallScreenWidth} = useWindowDimensions(); + + return ( + + + + {}} + illustrationBackgroundColor={theme.fallbackIconColor} + illustration={Illustrations.ExpensifyCardIllustration} + illustrationStyle={styles.expensifyCardIllustrationContainer} + titleStyles={styles.textHeadlineH1} + contentPaddingOnLargeScreens={styles.p5} + /> + + + + ); +} + +WorkspaceCardPageFeed.displayName = 'WorkspaceCardPageFeed'; + +export default WorkspaceCardPageFeed; diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx deleted file mode 100644 index fde42d795e6c..000000000000 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import {useFocusEffect} from '@react-navigation/native'; -import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useCallback, useMemo} from 'react'; -import type {ListRenderItemInfo} from 'react-native'; -import {FlatList, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {useOnyx} from 'react-native-onyx'; -import Button from '@components/Button'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import * as Expensicons from '@components/Icon/Expensicons'; -import * as Illustrations from '@components/Icon/Illustrations'; -import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {PressableWithoutFeedback} from '@components/Pressable'; -import ScreenWrapper from '@components/ScreenWrapper'; -import useLocalize from '@hooks/useLocalize'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useThemeStyles from '@hooks/useThemeStyles'; -import localeCompare from '@libs/LocaleCompare'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; -import Navigation from '@navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@navigation/types'; -import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type SCREENS from '@src/SCREENS'; -import type {Card, WorkspaceCardsList} from '@src/types/onyx'; -import WorkspaceCardListHeader from './WorkspaceCardListHeader'; -import WorkspaceCardListRow from './WorkspaceCardListRow'; - -type WorkspaceExpensifyCardPageProps = StackScreenProps; - -// TODO: remove when Onyx data is available -const mockedCards: OnyxEntry = { - test1: { - // @ts-expect-error TODO: change cardholder to accountID - cardholder: {accountID: 1, lastName: 'Smith', firstName: 'Bob', displayName: 'Bob Smith'}, - nameValuePairs: { - unapprovedExpenseLimit: 1000, - cardTitle: 'Test 1', - }, - lastFourPAN: '1234', - }, - test2: { - // @ts-expect-error TODO: change cardholder to accountID - cardholder: {accountID: 2, lastName: 'Miller', firstName: 'Alex', displayName: 'Alex Miller'}, - nameValuePairs: { - unapprovedExpenseLimit: 2000, - cardTitle: 'Test 2', - }, - lastFourPAN: '1234', - }, - test3: { - // @ts-expect-error TODO: change cardholder to accountID - cardholder: {accountID: 3, lastName: 'Brown', firstName: 'Kevin', displayName: 'Kevin Brown'}, - nameValuePairs: { - unapprovedExpenseLimit: 3000, - cardTitle: 'Test 3', - }, - lastFourPAN: '1234', - }, -}; - -function WorkspaceExpensifyCardPage({route}: WorkspaceExpensifyCardPageProps) { - const {shouldUseNarrowLayout} = useResponsiveLayout(); - const {translate} = useLocalize(); - const styles = useThemeStyles(); - - const policyID = route.params.policyID; - const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); - - const policyCurrency = useMemo(() => policy?.outputCurrency ?? CONST.CURRENCY.USD, [policy]); - - // TODO: uncomment the code line below to use cardsList data from Onyx when it's supported - // const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${policyID}_${CONST.EXPENSIFY_CARD.BANK}`); - const cardsList = mockedCards; - - const fetchExpensifyCards = useCallback(() => { - // TODO: uncomment when OpenPolicyExpensifyCardsPage API call is supported - // Policy.openPolicyExpensifyCardsPage(policyID); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [policyID]); - - useFocusEffect(fetchExpensifyCards); - - const sortedCards = useMemo( - () => - Object.values(cardsList ?? {}).sort((a, b) => { - // @ts-expect-error TODO: change cardholder to accountID and get personal details with it - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const aName = PersonalDetailsUtils.getDisplayNameOrDefault(a.cardholder ?? {}); - // @ts-expect-error TODO: change cardholder to accountID and get personal details with it - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const bName = PersonalDetailsUtils.getDisplayNameOrDefault(b.cardholder ?? {}); - return localeCompare(aName, bName); - }), - [cardsList], - ); - - const getHeaderButtons = () => ( - -