From b0fb166ef7856cc6ac4ca48c495d51177208de79 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 21 Aug 2024 17:56:39 +0200 Subject: [PATCH 1/8] Implements WorkspaceCompanyCardFeedSelector page --- src/ONYXKEYS.ts | 4 + src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + src/languages/en.ts | 4 + src/languages/es.ts | 4 + .../ModalStackNavigators/index.tsx | 1 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 2 + src/libs/Navigation/linkingConfig/config.ts | 3 + src/libs/Navigation/types.ts | 3 + .../WorkspaceCompanyCardFeedSelectorPage.tsx | 82 +++++++++++++++++++ src/types/onyx/CardFeeds.ts | 16 ++++ src/types/onyx/index.ts | 2 + 12 files changed, 126 insertions(+) create mode 100644 src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx create mode 100644 src/types/onyx/CardFeeds.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index b7b6cf53a17..b4b7e222bb4 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -473,6 +473,9 @@ const ONYXKEYS = { /** The value that indicates whether Continuous Reconciliation should be used on the domain */ EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION: 'expensifyCard_useContinuousReconciliation_', + + /** The collection of card feeds */ + SHARED_NVP_PRIVATE_DOMAIN_MEMBER: 'sharedNVP_private_domain_member_', }, /** List of Form ids */ @@ -753,6 +756,7 @@ type OnyxCollectionValuesMapping = { [ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST]: OnyxTypes.WorkspaceCardsList; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: boolean; + [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER]: OnyxTypes.CardFeeds; }; type OnyxValuesMapping = { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 893fd59e38b..8d222400479 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -887,6 +887,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/reportFields/:reportFieldID/edit/initialValue', getRoute: (policyID: string, reportFieldID: string) => `settings/workspaces/${policyID}/reportFields/${encodeURIComponent(reportFieldID)}/edit/initialValue` as const, }, + WORKSPACE_COMPANY_CARDS_SELECT_FEED: { + route: 'settings/workspaces/:policyID/company-cards/select-feed', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards/select-feed` as const, + }, WORKSPACE_EXPENSIFY_CARD: { route: 'settings/workspaces/:policyID/expensify-card', getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 30adc5f89d0..5aaf2c63c09 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -363,6 +363,7 @@ const SCREENS = { RATE_AND_UNIT: 'Workspace_RateAndUnit', RATE_AND_UNIT_RATE: 'Workspace_RateAndUnit_Rate', RATE_AND_UNIT_UNIT: 'Workspace_RateAndUnit_Unit', + COMPANY_CARDS_SELECT_FEED: 'Workspace_CompanyCards_Select_Feed', EXPENSIFY_CARD: 'Workspace_ExpensifyCard', EXPENSIFY_CARD_DETAILS: 'Workspace_ExpensifyCard_Details', EXPENSIFY_CARD_LIMIT: 'Workspace_ExpensifyCard_Limit', diff --git a/src/languages/en.ts b/src/languages/en.ts index 796105bf8ed..b603ab7b54b 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2738,6 +2738,10 @@ export default { control: 'Control', collect: 'Collect', }, + companyCards: { + addCompanyCards: 'Add company cards', + selectCardFeed: 'Select card feed', + }, expensifyCard: { issueAndManageCards: 'Issue and manage your Expensify Cards', getStartedIssuing: 'Get started by issuing your first virtual or physical card.', diff --git a/src/languages/es.ts b/src/languages/es.ts index cad3b16fe7c..f633e3058f5 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2911,6 +2911,10 @@ export default { ctaTitle: 'Emitir nueva tarjeta', }, }, + companyCards: { + addCompanyCards: 'Agregar tarjetas de empresa', + selectCardFeed: 'Seleccionar feed de tarjetas', + }, distanceRates: { title: 'Tasas de distancia', subtitle: 'Añade, actualiza y haz cumplir las tasas.', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index f105de52a5a..a54ad6e1098 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -421,6 +421,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/taxes/WorkspaceTaxCodePage').default, [SCREENS.WORKSPACE.INVOICES_COMPANY_NAME]: () => require('../../../../pages/workspace/invoices/WorkspaceInvoicingDetailsName').default, [SCREENS.WORKSPACE.INVOICES_COMPANY_WEBSITE]: () => require('../../../../pages/workspace/invoices/WorkspaceInvoicingDetailsWebsite').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage').default, [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: () => require('../../../../pages/workspace/expensifyCard/issueNew/IssueNewCardPage').default, [SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceCardSettingsPage').default, [SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceSettlementAccountPage').default, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index 942a2306897..e36cc5f25da 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -160,6 +160,8 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.REPORT_FIELDS_EDIT_INITIAL_VALUE, ], [SCREENS.WORKSPACE.INVOICES]: [SCREENS.WORKSPACE.INVOICES_COMPANY_NAME, SCREENS.WORKSPACE.INVOICES_COMPANY_WEBSITE], + // TODO: uncomment when SCREENS.WORKSPACE.COMPANY_CARDS is available https://github.com/Expensify/App/pull/47719 + // [SCREENS.WORKSPACE.COMPANY_CARDS]: [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED], [SCREENS.WORKSPACE.EXPENSIFY_CARD]: [ SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW, SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 236b56882dd..6a228f221cc 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -481,6 +481,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.INVOICES_COMPANY_WEBSITE]: { path: ROUTES.WORKSPACE_INVOICES_COMPANY_WEBSITE.route, }, + [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.route, + }, [SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT]: { path: ROUTES.WORKSPACE_EXPENSIFY_CARD_LIMIT.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index b689f36d8a3..41924a6aadb 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -671,6 +671,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.INVOICES_COMPANY_WEBSITE]: { policyID: string; }; + [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: { + policyID: string; + }; [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: { policyID: string; }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx new file mode 100644 index 00000000000..d8c1f482271 --- /dev/null +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -0,0 +1,82 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import Navigation from '@navigation/Navigation'; +import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import type SCREENS from '@src/SCREENS'; +import {CardFeeds} from '@src/types/onyx'; + +const mockedData: CardFeeds = { + companyCards: { + cdfbmo: { + pending: false, + asrEnabled: true, + forceReimbursable: 'force_no', + liabilityType: 'corporate', + preferredPolicy: '', + reportTitleFormat: '{report:card}{report:bank}{report:submit:from}{report:total}{report:enddate:MMMM}', + statementPeriodEndDay: 'LAST_DAY_OF_MONTH', + }, + }, + companyCardNicknames: { + cdfbmo: 'BMO MasterCard', + }, +}; + +type WorkspaceCompanyCardFeedSelectorPageProps = StackScreenProps; + +function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedSelectorPageProps) { + const {policyID} = route.params; + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); + // TODO: use data form onyx instead of mocked one whe API is implemented + // const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); + const cardFeeds = mockedData; + + const feeds = Object.entries(cardFeeds?.companyCardNicknames ?? {}).map(([key, value]) => ({ + value: key, + text: value, + keyForList: key, + isSelected: false, + })); + + return ( + + + Navigation.goBack()} + /> + {}} + sections={[{data: feeds}]} + shouldUpdateFocusedIndex + isAlternateTextMultilineSupported + // initiallyFocusedOptionKey={typeSelected} + /> + + + ); +} + +WorkspaceCompanyCardFeedSelectorPage.displayName = 'WorkspaceCompanyCardFeedSelectorPage'; + +export default WorkspaceCompanyCardFeedSelectorPage; diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts new file mode 100644 index 00000000000..19ccd7ea802 --- /dev/null +++ b/src/types/onyx/CardFeeds.ts @@ -0,0 +1,16 @@ +type CardFeedData = { + pending: boolean; + asrEnabled: boolean; + forceReimbursable: string; + liabilityType: string; + preferredPolicy: string; + reportTitleFormat: string; + statementPeriodEndDay: string; +}; + +type CardFeeds = { + companyCards: Record; + companyCardNicknames: Record; +}; + +export default CardFeeds; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 2bb12970898..54ee7fe2589 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -10,6 +10,7 @@ import type BlockedFromConcierge from './BlockedFromConcierge'; import type CancellationDetails from './CancellationDetails'; import type Card from './Card'; import type {CardList, IssueNewCard, WorkspaceCardsList} from './Card'; +import type CardFeeds from './CardFeeds'; import type {CapturedLogs, Log} from './Console'; import type Credentials from './Credentials'; import type Currency from './Currency'; @@ -220,4 +221,5 @@ export type { ApprovalWorkflowOnyx, MobileSelectionMode, WorkspaceTooltip, + CardFeeds, }; From a2a549bbc862c70ce59e87b87ffdbd43c5f31087 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 22 Aug 2024 11:57:28 +0200 Subject: [PATCH 2/8] Improve card feed display and implement card feed select --- src/CONST.ts | 7 +++ src/ONYXKEYS.ts | 4 ++ src/languages/es.ts | 8 +-- src/libs/CardUtils.ts | 18 ++++++ .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/actions/Card.ts | 11 ++++ .../WorkspaceCompanyCardFeedSelectorPage.tsx | 59 ++++++++++++++++--- 7 files changed, 96 insertions(+), 12 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 0becab6d348..dc374fb7bbd 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2258,6 +2258,13 @@ const CONST = { MENTION_ICON: 'mention-icon', SMALL_NORMAL: 'small-normal', }, + COMPANY_CARD: { + FEED_BANK_NAME: { + MASTER_CARD: 'cdf', + VISA: 'vcf', + AMEX: 'gl1025', + }, + }, EXPENSIFY_CARD: { BANK: 'Expensify Card', FRAUD_TYPES: { diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index b4b7e222bb4..6889f3adba5 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -476,6 +476,9 @@ const ONYXKEYS = { /** The collection of card feeds */ SHARED_NVP_PRIVATE_DOMAIN_MEMBER: 'sharedNVP_private_domain_member_', + + /** Currently displaying feed */ + LAST_SELECTED_FEED: 'lastSelectedFeed_', }, /** List of Form ids */ @@ -757,6 +760,7 @@ type OnyxCollectionValuesMapping = { [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: boolean; [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER]: OnyxTypes.CardFeeds; + [ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: string; }; type OnyxValuesMapping = { diff --git a/src/languages/es.ts b/src/languages/es.ts index f633e3058f5..1806eba61c1 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2784,6 +2784,10 @@ export default { control: 'Control', collect: 'Recolectar', }, + companyCards: { + addCompanyCards: 'Agregar tarjetas de empresa', + selectCardFeed: 'Seleccionar feed de tarjetas', + }, expensifyCard: { issueAndManageCards: 'Emitir y gestionar Tarjetas Expensify', getStartedIssuing: 'Empieza emitiendo tu primera tarjeta virtual o física.', @@ -2911,10 +2915,6 @@ export default { ctaTitle: 'Emitir nueva tarjeta', }, }, - companyCards: { - addCompanyCards: 'Agregar tarjetas de empresa', - selectCardFeed: 'Seleccionar feed de tarjetas', - }, distanceRates: { title: 'Tasas de distancia', subtitle: 'Añade, actualiza y haz cumplir las tasas.', diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index e0041dde993..8be8ef697f4 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -2,12 +2,14 @@ import lodash from 'lodash'; import Onyx from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import ExpensifyCardImage from '@assets/images/expensify-card.svg'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type {OnyxValues} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {BankAccountList, Card, CardList, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import IconAsset from '@src/types/utils/IconAsset'; import localeCompare from './LocaleCompare'; import * as Localize from './Localize'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; @@ -180,6 +182,21 @@ function sortCardsByCardholderName(cardsList: OnyxEntry, per }); } +function getCardFeedIcon(cardFeed: string): IconAsset { + if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD)) { + // TODO: return MasterCard image + return ExpensifyCardImage; + } + + if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.VISA)) { + // TODO: return Visa image + return ExpensifyCardImage; + } + + // TODO: return Amix image + return ExpensifyCardImage; +} + export { isExpensifyCard, isCorporateCard, @@ -195,4 +212,5 @@ export { getTranslationKeyForLimitType, getEligibleBankAccountsForCard, sortCardsByCardholderName, + getCardFeedIcon, }; diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index e36cc5f25da..57dd8a83390 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -163,6 +163,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { // TODO: uncomment when SCREENS.WORKSPACE.COMPANY_CARDS is available https://github.com/Expensify/App/pull/47719 // [SCREENS.WORKSPACE.COMPANY_CARDS]: [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED], [SCREENS.WORKSPACE.EXPENSIFY_CARD]: [ + SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED, SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW, SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT, SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS, diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts index 747df5d3998..61a86be7732 100644 --- a/src/libs/actions/Card.ts +++ b/src/libs/actions/Card.ts @@ -617,6 +617,16 @@ function toggleContinuousReconciliation(workspaceAccountID: number, shouldUseCon }); } +function updateSelectedFeed(feed: string, policyID: string) { + Onyx.update([ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, + value: feed, + }, + ]); +} + export { requestReplacementExpensifyCard, activatePhysicalExpensifyCard, @@ -635,5 +645,6 @@ export { openCardDetailsPage, toggleContinuousReconciliation, updateExpensifyCardLimitType, + updateSelectedFeed, }; export type {ReplacementReason}; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index d8c1f482271..1f72df38471 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -1,17 +1,31 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; +import {useOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {ListItem} from '@components/SelectionList/types'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as CardUtils from '@libs/CardUtils'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import variables from '@styles/variables'; +import * as Card from '@userActions/Card'; +import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import {CardFeeds} from '@src/types/onyx'; +import type {CardFeeds} from '@src/types/onyx'; + +type CardFeedListItem = ListItem & { + /** Card feed value */ + value: string; +}; const mockedData: CardFeeds = { companyCards: { @@ -34,20 +48,41 @@ type WorkspaceCompanyCardFeedSelectorPageProps = StackScreenProps ({ + const feeds: CardFeedListItem[] = Object.entries(cardFeeds?.companyCardNicknames ?? {}).map(([key, value]) => ({ value: key, text: value, keyForList: key, - isSelected: false, + isSelected: key === selectedFeed, + leftElement: ( + + ), })); + // TODO: pass the specific route of the Card List page to go back when it's merged https://github.com/Expensify/App/pull/47719 + const goBack = () => Navigation.goBack(); + + const selectFeed = (feed: CardFeedListItem) => { + Card.updateSelectedFeed(feed.value, policyID); + goBack(); + }; + return ( Navigation.goBack()} + onBackButtonPress={goBack} /> {}} + onSelectRow={selectFeed} sections={[{data: feeds}]} shouldUpdateFocusedIndex isAlternateTextMultilineSupported - // initiallyFocusedOptionKey={typeSelected} + initiallyFocusedOptionKey={selectedFeed} + listFooterContent={ + { + // TODO: navigate to Add Feed flow when it's implemented + }} + /> + } /> From 5a102fa25203e15476709d35e81b79c6501990db Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 22 Aug 2024 14:56:02 +0200 Subject: [PATCH 3/8] Add correct card feeds images --- assets/images/companyCards/amex.svg | 41 ++++++++++ assets/images/companyCards/mastercard.svg | 40 ++++++++++ assets/images/companyCards/visa.svg | 74 +++++++++++++++++++ src/components/Icon/Illustrations.ts | 6 ++ src/libs/CardUtils.ts | 13 ++-- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 9 +-- 6 files changed, 170 insertions(+), 13 deletions(-) create mode 100644 assets/images/companyCards/amex.svg create mode 100644 assets/images/companyCards/mastercard.svg create mode 100644 assets/images/companyCards/visa.svg diff --git a/assets/images/companyCards/amex.svg b/assets/images/companyCards/amex.svg new file mode 100644 index 00000000000..f1a14593a0a --- /dev/null +++ b/assets/images/companyCards/amex.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/companyCards/mastercard.svg b/assets/images/companyCards/mastercard.svg new file mode 100644 index 00000000000..2ba665693a6 --- /dev/null +++ b/assets/images/companyCards/mastercard.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/companyCards/visa.svg b/assets/images/companyCards/visa.svg new file mode 100644 index 00000000000..092bab744bf --- /dev/null +++ b/assets/images/companyCards/visa.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 64058a49523..5a776a60fc3 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -1,3 +1,6 @@ +import AmexCompanyCards from '@assets/images/companyCards/amex.svg'; +import MasterCardCompanyCards from '@assets/images/companyCards/mastercard.svg'; +import VisaCompanyCards from '@assets/images/companyCards/visa.svg'; import EmptyCardState from '@assets/images/emptystate__expensifycard.svg'; import ExpensifyCardIllustration from '@assets/images/expensifyCard/cardIllustration.svg'; import LaptopwithSecondScreenandHourglass from '@assets/images/LaptopwithSecondScreenandHourglass.svg'; @@ -218,4 +221,7 @@ export { Tire, BigVault, Filters, + AmexCompanyCards, + MasterCardCompanyCards, + VisaCompanyCards, }; diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 8be8ef697f4..2398bc1e729 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -2,14 +2,14 @@ import lodash from 'lodash'; import Onyx from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import ExpensifyCardImage from '@assets/images/expensify-card.svg'; +import * as Illustrations from '@src/components/Icon/Illustrations'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type {OnyxValues} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {BankAccountList, Card, CardList, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import IconAsset from '@src/types/utils/IconAsset'; +import type IconAsset from '@src/types/utils/IconAsset'; import localeCompare from './LocaleCompare'; import * as Localize from './Localize'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; @@ -184,17 +184,14 @@ function sortCardsByCardholderName(cardsList: OnyxEntry, per function getCardFeedIcon(cardFeed: string): IconAsset { if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD)) { - // TODO: return MasterCard image - return ExpensifyCardImage; + return Illustrations.MasterCardCompanyCards; } if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.VISA)) { - // TODO: return Visa image - return ExpensifyCardImage; + return Illustrations.VisaCompanyCards; } - // TODO: return Amix image - return ExpensifyCardImage; + return Illustrations.AmexCompanyCards; } export { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index 1f72df38471..95beda03d6a 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -13,7 +13,6 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import * as PolicyUtils from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import variables from '@styles/variables'; @@ -48,7 +47,7 @@ type WorkspaceCompanyCardFeedSelectorPageProps = StackScreenProps ), })); From f1aef6e07b910311c8f5132da95cb2c85eeb9826 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 22 Aug 2024 15:07:00 +0200 Subject: [PATCH 4/8] Add comments to CardFeeds type --- src/types/onyx/CardFeeds.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts index 19ccd7ea802..59d93a192cf 100644 --- a/src/types/onyx/CardFeeds.ts +++ b/src/types/onyx/CardFeeds.ts @@ -1,15 +1,33 @@ +/** Card feed data */ type CardFeedData = { + /** Whether any actions are pending */ pending: boolean; + + /** Determines if Automated Statement Reconciliation (ASR) is enabled for the cards */ asrEnabled: boolean; + + /** Specifies if the expenses on this card should be force reimbursable */ forceReimbursable: string; + + /** Defines the type of liability for the card */ liabilityType: string; + + /** Preferred policy */ preferredPolicy: string; + + /** Specifies the format for the report title related to this card */ reportTitleFormat: string; + + /** Indicates the day when the statement period for this card ends */ statementPeriodEndDay: string; }; +/** Card feeds model */ type CardFeeds = { + /** Company cards feeds */ companyCards: Record; + + /** User-friendly feed nicknames */ companyCardNicknames: Record; }; From 0f55e441c2aef1887bd9379184b5a331709f0848 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 23 Aug 2024 17:37:20 +0200 Subject: [PATCH 5/8] Code clean-up --- src/ONYXKEYS.ts | 8 +++---- src/types/onyx/CompanyCards.ts | 41 ---------------------------------- 2 files changed, 3 insertions(+), 46 deletions(-) delete mode 100644 src/types/onyx/CompanyCards.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index eda54ff9126..776454b63e7 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -458,6 +458,8 @@ const ONYXKEYS = { // Shared NVPs /** Collection of objects where each object represents the owner of the workspace that is past due billing AND the user is a member of. */ SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END: 'sharedNVP_private_billingGracePeriodEnd_', + + /** The collection of card feeds */ SHARED_NVP_PRIVATE_DOMAIN_MEMBER: 'sharedNVP_private_domain_member_', /** @@ -475,9 +477,6 @@ const ONYXKEYS = { /** The value that indicates whether Continuous Reconciliation should be used on the domain */ EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION: 'expensifyCard_useContinuousReconciliation_', - /** The collection of card feeds */ - SHARED_NVP_PRIVATE_DOMAIN_MEMBER: 'sharedNVP_private_domain_member_', - /** Currently displaying feed */ LAST_SELECTED_FEED: 'lastSelectedFeed_', }, @@ -756,12 +755,11 @@ type OnyxCollectionValuesMapping = { [ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS]: OnyxTypes.PolicyConnectionSyncProgress; [ONYXKEYS.COLLECTION.SNAPSHOT]: OnyxTypes.SearchResults; [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END]: OnyxTypes.BillingGraceEndPeriod; - [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER]: OnyxTypes.CompanyCards; + [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER]: OnyxTypes.CardFeeds; [ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS]: OnyxTypes.ExpensifyCardSettings; [ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST]: OnyxTypes.WorkspaceCardsList; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: boolean; - [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER]: OnyxTypes.CardFeeds; [ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: string; }; diff --git a/src/types/onyx/CompanyCards.ts b/src/types/onyx/CompanyCards.ts deleted file mode 100644 index 17ebbaf98bb..00000000000 --- a/src/types/onyx/CompanyCards.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** Model of CompanyCard's Shared NVP record */ -// TODO update information here during implementation Add Company Card flow -type CompanyCards = { - /** Company cards object */ - companyCards: { - /** Company card info key */ - cdfbmo: CompanyCardInfo; - }; - /** Company cards nicknames */ - companyCardNicknames: { - /** Company cards info key */ - cdfbmo: string; - }; -}; -/** - * Model of company card information - */ -type CompanyCardInfo = { - /** Company card pending state */ - pending: boolean; - - /** Company card asr state */ - asrEnabled: boolean; - - /** Company card force reimbursable value */ - forceReimbursable: string; - - /** Company card liability type */ - liabilityType: string; - - /** Company card preferred policy */ - preferredPolicy: string; - - /** Company card report title format */ - reportTitleFormat: string; - - /** Company card statement period */ - statementPeriodEndDay: string; -}; - -export default CompanyCards; From b753bd443abb34b12a0e321343d2d75f0eaf8582 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 23 Aug 2024 17:43:53 +0200 Subject: [PATCH 6/8] Update images --- assets/images/companyCards/amex.svg | 17 +++-- assets/images/companyCards/mastercard.svg | 42 ++++++------ assets/images/companyCards/visa.svg | 82 +++++++++++------------ 3 files changed, 70 insertions(+), 71 deletions(-) diff --git a/assets/images/companyCards/amex.svg b/assets/images/companyCards/amex.svg index f1a14593a0a..73e8164cdc6 100644 --- a/assets/images/companyCards/amex.svg +++ b/assets/images/companyCards/amex.svg @@ -5,29 +5,28 @@ - + + M8.726,19.696c-0.162,0-0.294,0.132-0.294,0.294v0.98c0,0.162,0.132,0.294,0.294,0.294h0.98c0.162,0,0.294-0.132,0.294-0.294v-0.98 + c0-0.162-0.132-0.294-0.294-0.294H8.726z"/> - + - + + viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve"> - + - + - + - - - - + + + + - + c-0.027-0.046-0.042-0.098-0.042-0.151V17.621z M6.471,16.853c0-0.162,0.132-0.294,0.294-0.294h0.98 + c0.162,0,0.294,0.132,0.294,0.294v1.569c0,0.162-0.132,0.294-0.294,0.294h-0.98c-0.162,0-0.294-0.132-0.294-0.294V16.853z + M8.726,16.559c-0.162,0-0.294,0.132-0.294,0.294v2.157c0,0.162,0.132,0.294,0.294,0.294h0.98c0.162,0,0.294-0.132,0.294-0.294 + v-2.157c0-0.162-0.132-0.294-0.294-0.294H8.726z M6.471,19.402c0-0.162,0.132-0.294,0.294-0.294h0.98 + c0.162,0,0.294,0.132,0.294,0.294v1.569c0,0.163-0.132,0.294-0.294,0.294h-0.98c-0.162,0-0.294-0.132-0.294-0.294V19.402z + M8.726,19.696c-0.162,0-0.294,0.132-0.294,0.294v0.98c0,0.162,0.132,0.294,0.294,0.294h0.98c0.162,0,0.294-0.132,0.294-0.294v-0.98 + c0-0.162-0.132-0.294-0.294-0.294H8.726z"/> diff --git a/assets/images/companyCards/visa.svg b/assets/images/companyCards/visa.svg index 092bab744bf..4a7a73b6663 100644 --- a/assets/images/companyCards/visa.svg +++ b/assets/images/companyCards/visa.svg @@ -4,71 +4,71 @@ viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve"> - + - + - + - - + - + - - + + - + - + - - - + + + - + - + c0.069-0.043,0.171-0.073,0.277-0.073c0.256,0,0.319,0.175,0.319,0.341V30.324L36.74,30.324z"/> + + M8.726,19.696c-0.162,0-0.294,0.132-0.294,0.294v0.98c0,0.162,0.132,0.294,0.294,0.294h0.98c0.162,0,0.294-0.132,0.294-0.294v-0.98 + c0-0.162-0.132-0.294-0.294-0.294H8.726z"/> From 7d79ef378ec11129e6fa6df37b13a546252dda5c Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 23 Aug 2024 17:49:05 +0200 Subject: [PATCH 7/8] Resolve TODOs --- .../companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index 95beda03d6a..3a46079301e 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -17,7 +17,9 @@ import Navigation from '@navigation/Navigation'; 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'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CardFeeds} from '@src/types/onyx'; @@ -74,8 +76,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS ), })); - // TODO: pass the specific route of the Card List page to go back when it's merged https://github.com/Expensify/App/pull/47719 - const goBack = () => Navigation.goBack(); + const goBack = () => Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); const selectFeed = (feed: CardFeedListItem) => { Card.updateSelectedFeed(feed.value, policyID); @@ -85,8 +86,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS return ( Date: Fri, 23 Aug 2024 17:52:40 +0200 Subject: [PATCH 8/8] Minor code improvements --- src/pages/workspace/WorkspaceMoreFeaturesPage.tsx | 4 ++-- .../companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index 5b356f768dd..e3b0af22089 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -71,7 +71,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro const policyID = policy?.id; const workspaceAccountID = policy?.workspaceAccountID ?? -1; const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID.toString()}${CONST.EXPENSIFY_CARD.BANK}`); - const [companyCardsList] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID.toString()}`); + const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID.toString()}`); const [isOrganizeWarningModalOpen, setIsOrganizeWarningModalOpen] = useState(false); const [isIntegrateWarningModalOpen, setIsIntegrateWarningModalOpen] = useState(false); const [isReportFieldsWarningModalOpen, setIsReportFieldsWarningModalOpen] = useState(false); @@ -119,7 +119,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro subtitleTranslationKey: 'workspace.moreFeatures.companyCards.subtitle', isActive: policy?.areCompanyCardsEnabled ?? false, pendingAction: policy?.pendingFields?.areCompanyCardsEnabled, - disabled: !isEmptyObject(companyCardsList), + disabled: !isEmptyObject(cardFeeds?.companyCards), action: (isEnabled: boolean) => { if (!policyID) { return; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index 3a46079301e..c745774d364 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -49,11 +49,10 @@ type WorkspaceCompanyCardFeedSelectorPageProps = StackScreenProps