Skip to content

Commit

Permalink
Merge pull request #50972 from VickyStash/feature/50452-direct-feed-l…
Browse files Browse the repository at this point in the history
…abel
  • Loading branch information
mountiny authored Oct 21, 2024
2 parents 44f5c5c + e7f75b3 commit 8ca4576
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 61 deletions.
7 changes: 7 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2529,6 +2529,13 @@ const CONST = {
VISA: 'vcf',
AMEX: 'gl1025',
STRIPE: 'stripe',
CITIBANK: 'oauth.citibank.com',
CAPITAL_ONE: 'oauth.capitalone.com',
BANK_OF_AMERICA: 'oauth.bankofamerica.com',
CHASE: 'oauth.chase.com',
BREX: 'oauth.brex.com',
WELLS_FARGO: 'oauth.wellsfargo.com',
AMEX_DIRECT: 'oauth.americanexpressfdx.com',
},
STEP_NAMES: ['1', '2', '3', '4'],
STEP: {
Expand Down
79 changes: 35 additions & 44 deletions src/libs/CardUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import groupBy from 'lodash/groupBy';
import Onyx from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} 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';
Expand All @@ -9,7 +9,6 @@ import type {TranslationPaths} from '@src/languages/types';
import type {OnyxValues} from '@src/ONYXKEYS';
import ONYXKEYS from '@src/ONYXKEYS';
import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
import type Policy from '@src/types/onyx/Policy';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type IconAsset from '@src/types/utils/IconAsset';
import localeCompare from './LocaleCompare';
Expand Down Expand Up @@ -193,13 +192,35 @@ function getCompanyCardNumber(cardList: Record<string, string>, lastFourPAN?: st
return Object.keys(cardList).find((card) => card.endsWith(lastFourPAN)) ?? '';
}

function getCardFeedIcon(cardFeed: string): IconAsset {
if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD)) {
return Illustrations.MasterCardCompanyCards;
function getCardFeedIcon(cardFeed: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK): IconAsset {
const feedIcons = {
[CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: Illustrations.VisaCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX]: Illustrations.AmexCardCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: Illustrations.MasterCardCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT]: Illustrations.AmexCardCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.BANK_OF_AMERICA]: Illustrations.BankOfAmericaCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE]: Illustrations.CapitalOneCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]: Illustrations.ChaseCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.CITIBANK]: Illustrations.CitibankCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.WELLS_FARGO]: Illustrations.WellsFargoCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.BREX]: Illustrations.BrexCompanyCardDetail,
[CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE]: Illustrations.StripeCompanyCardDetail,
[CONST.EXPENSIFY_CARD.BANK]: ExpensifyCardImage,
};

if (cardFeed.startsWith(CONST.EXPENSIFY_CARD.BANK)) {
return ExpensifyCardImage;
}

if (feedIcons[cardFeed]) {
return feedIcons[cardFeed];
}

if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.VISA)) {
return Illustrations.VisaCompanyCards;
// In existing OldDot setups other variations of feeds could exist, ex: vcf2, vcf3, cdfbmo
const feedKey = (Object.keys(feedIcons) as CompanyCardFeed[]).find((feed) => cardFeed.startsWith(feed));

if (feedKey) {
return feedIcons[feedKey];
}

return Illustrations.AmexCompanyCards;
Expand All @@ -211,46 +232,18 @@ function getCardFeedName(feedType: CompanyCardFeed): string {
[CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: 'Mastercard',
[CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX]: 'American Express',
[CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE]: 'Stripe',
[CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT]: 'American Express',
[CONST.COMPANY_CARD.FEED_BANK_NAME.BANK_OF_AMERICA]: 'Bank of America',
[CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE]: 'Capital One',
[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]: 'Chase',
[CONST.COMPANY_CARD.FEED_BANK_NAME.CITIBANK]: 'Citibank',
[CONST.COMPANY_CARD.FEED_BANK_NAME.WELLS_FARGO]: 'Wells Fargo',
[CONST.COMPANY_CARD.FEED_BANK_NAME.BREX]: 'Brex',
};

return feedNamesMapping[feedType];
}

function getCardDetailsImage(cardFeed: string): IconAsset {
if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD)) {
return Illustrations.MasterCardCompanyCardDetail;
}

if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.VISA)) {
return Illustrations.VisaCompanyCardDetail;
}

if (cardFeed.startsWith(CONST.EXPENSIFY_CARD.BANK)) {
return ExpensifyCardImage;
}

return Illustrations.AmexCardCompanyCardDetail;
}

function getMemberCards(policy: OnyxEntry<Policy>, allCardsList: OnyxCollection<WorkspaceCardsList>, accountID?: number) {
const workspaceId = policy?.workspaceAccountID ? policy.workspaceAccountID.toString() : '';
const cards: WorkspaceCardsList = {};
Object.keys(allCardsList ?? {})
.filter((key) => key !== `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceId}_${CONST.EXPENSIFY_CARD.BANK}` && key.includes(workspaceId))
.forEach((key) => {
const feedCards = allCardsList?.[key];
if (feedCards && Object.keys(feedCards).length > 0) {
Object.keys(feedCards).forEach((feedCardKey) => {
if (feedCards?.[feedCardKey].accountID !== accountID) {
return;
}
cards[feedCardKey] = feedCards[feedCardKey];
});
}
});
return cards;
}

const getBankCardDetailsImage = (bank: ValueOf<typeof CONST.COMPANY_CARDS.BANKS>): IconAsset => {
const iconMap: Record<ValueOf<typeof CONST.COMPANY_CARDS.BANKS>, IconAsset> = {
[CONST.COMPANY_CARDS.BANKS.AMEX]: Illustrations.AmexCardCompanyCardDetail,
Expand Down Expand Up @@ -322,8 +315,6 @@ export {
getCompanyCardNumber,
getCardFeedIcon,
getCardFeedName,
getCardDetailsImage,
getMemberCards,
getBankCardDetailsImage,
getSelectedFeed,
getCorrectStepForSelectedBank,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import {getConnectedIntegration} from '@libs/PolicyUtils';
import Navigation from '@navigation/Navigation';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {CompanyCardFeed} from '@src/types/onyx';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import {getExportMenuItem} from './utils';

type WorkspaceCompanyCardDetailsPageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.COMPANY_CARD_DETAILS>;
Expand All @@ -49,9 +52,10 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag
const connectedIntegration = getConnectedIntegration(policy, accountingIntegrations) ?? connectionSyncProgress?.connectionName;

const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [allBankCards] = useOnyx(`${ONYXKEYS.CARD_LIST}`);
const [allBankCards, allBankCardsMetadata] = useOnyx(`${ONYXKEYS.CARD_LIST}`);
const card = allBankCards?.[cardID];

const cardBank = card?.bank ?? '';
const cardholder = personalDetails?.[card?.accountID ?? -1];
const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(cardholder);
const exportMenuItem = getExportMenuItem(connectedIntegration, policyID, translate, policy, card);
Expand All @@ -66,6 +70,10 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag
Policy.updateWorkspaceCompanyCard(workspaceAccountID, cardID, bank);
};

if (!card && !isLoadingOnyxValue(allBankCardsMetadata)) {
return <NotFoundPage />;
}

return (
<AccessOrNotFoundWrapper
policyID={policyID}
Expand All @@ -85,7 +93,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag
<View style={[styles.walletCard, styles.mb3]}>
<ImageSVG
contentFit="contain"
src={CardUtils.getCardDetailsImage(card?.bank ?? '')}
src={CardUtils.getCardFeedIcon(cardBank as CompanyCardFeed)}
pointerEvents="none"
height={variables.cardPreviewHeight}
width={variables.cardPreviewWidth}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS
const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`);
const selectedFeed = CardUtils.getSelectedFeed(lastSelectedFeed, cardFeeds);

const feeds: CardFeedListItem[] = Object.keys(cardFeeds?.settings?.companyCards ?? {}).map((feed) => ({
value: feed as CompanyCardFeed,
text: cardFeeds?.settings?.companyCardNicknames?.[feed] ?? translate(`workspace.companyCards.addNewCard.cardProviders.${feed as CompanyCardFeed}`),
const feeds: CardFeedListItem[] = (Object.keys(cardFeeds?.settings?.companyCards ?? {}) as CompanyCardFeed[]).map((feed) => ({
value: feed,
text: cardFeeds?.settings?.companyCardNicknames?.[feed] ?? CardUtils.getCardFeedName(feed),
keyForList: feed,
isSelected: feed === selectedFeed,
brickRoadIndicator: cardFeeds?.settings?.companyCards?.[feed]?.errors ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import * as CardUtils from '@libs/CardUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import Navigation from '@navigation/Navigation';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {CompanyCardFeed} from '@src/types/onyx';
Expand All @@ -36,7 +37,9 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp
const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID);
const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`);
const shouldChangeLayout = isMediumScreenWidth || shouldUseNarrowLayout;
const feedName = cardFeeds?.settings?.companyCardNicknames?.[selectedFeed] ?? translate(`workspace.companyCards.addNewCard.cardProviders.${selectedFeed}`);
const feedName = cardFeeds?.settings?.companyCardNicknames?.[selectedFeed] ?? CardUtils.getCardFeedName(selectedFeed);
const isCustomFeed =
CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD === selectedFeed || CONST.COMPANY_CARD.FEED_BANK_NAME.VISA === selectedFeed || CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX === selectedFeed;

return (
<OfflineWithFeedback
Expand Down Expand Up @@ -67,7 +70,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp
/>
)}
</View>
<Text style={styles.textLabelSupporting}>{translate('workspace.companyCards.customFeed')}</Text>
<Text style={styles.textLabelSupporting}>{translate(isCustomFeed ? 'workspace.companyCards.customFeed' : 'workspace.companyCards.directFeed')}</Text>
</View>
</PressableWithFeedback>

Expand Down
6 changes: 3 additions & 3 deletions src/pages/workspace/companyCards/addNew/CardTypeStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import variables from '@styles/variables';
import * as CompanyCards from '@userActions/CompanyCards';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {CompanyCardFeed} from '@src/types/onyx';
import type {CardFeedProvider} from '@src/types/onyx/CardFeeds';

type AvailableCompanyCardTypes = {
isAmexAvailable?: boolean;
translate: LocaleContextProps['translate'];
typeSelected?: CompanyCardFeed;
typeSelected?: CardFeedProvider;
styles: StyleProp<ViewStyle>;
};

Expand Down Expand Up @@ -87,7 +87,7 @@ function CardTypeStep() {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD);
const [typeSelected, setTypeSelected] = useState<CompanyCardFeed>();
const [typeSelected, setTypeSelected] = useState<CardFeedProvider>();
const {canUseDirectFeeds} = usePermissions();
const [isError, setIsError] = useState(false);
const data = getAvailableCompanyCardTypes({isAmexAvailable: !canUseDirectFeeds, translate, typeSelected, styles: styles.mr3});
Expand Down
4 changes: 2 additions & 2 deletions src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {Card as MemberCard, PersonalDetails, PersonalDetailsList} from '@src/types/onyx';
import type {CompanyCardFeed, Card as MemberCard, PersonalDetails, PersonalDetailsList} from '@src/types/onyx';
import type {ListItemType} from './WorkspaceMemberDetailsRoleSelectionModal';
import WorkspaceMemberDetailsRoleSelectionModal from './WorkspaceMemberDetailsRoleSelectionModal';

Expand Down Expand Up @@ -312,7 +312,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
? CurrencyUtils.convertToDisplayString(memberCard.nameValuePairs?.unapprovedExpenseLimit)
: ''
}
icon={CardUtils.getCardDetailsImage(memberCard?.bank ?? '')}
icon={CardUtils.getCardFeedIcon(memberCard.bank as CompanyCardFeed)}
displayInDefaultIconColor
iconStyles={styles.cardIcon}
contentFit="contain"
Expand Down
4 changes: 2 additions & 2 deletions src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew
setShouldShowError(false);
};

const companyCardFeeds: CardFeedListItem[] = Object.keys(cardFeeds?.settings?.companyCards ?? {}).map((key) => ({
const companyCardFeeds: CardFeedListItem[] = (Object.keys(cardFeeds?.settings?.companyCards ?? {}) as CompanyCardFeed[]).map((key) => ({
value: key,
text: cardFeeds?.settings?.companyCardNicknames?.[key] ?? translate(`workspace.companyCards.addNewCard.cardProviders.${key as CompanyCardFeed}`),
text: cardFeeds?.settings?.companyCardNicknames?.[key] ?? CardUtils.getCardFeedName(key),
keyForList: key,
isSelected: selectedFeed === key,
leftElement: (
Expand Down
13 changes: 10 additions & 3 deletions src/types/onyx/CardFeeds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';
import type * as OnyxCommon from './OnyxCommon';

/** Card feed provider */
/** Card feed */
type CompanyCardFeed = ValueOf<typeof CONST.COMPANY_CARD.FEED_BANK_NAME>;

/** Card feed provider */
type CardFeedProvider =
| typeof CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD
| typeof CONST.COMPANY_CARD.FEED_BANK_NAME.VISA
| typeof CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX
| typeof CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE;

/** Card feed data */
type CardFeedData = {
/** Whether any actions are pending */
Expand Down Expand Up @@ -50,7 +57,7 @@ type CardFeeds = {
/** Data required to be sent to add a new card */
type AddNewCardFeedData = {
/** Card feed provider */
feedType: CompanyCardFeed;
feedType: CardFeedProvider;

/** Name of the card */
cardTitle: string;
Expand Down Expand Up @@ -84,4 +91,4 @@ type AddNewCompanyCardFeed = {
};

export default CardFeeds;
export type {AddNewCardFeedStep, AddNewCompanyCardFeed, AddNewCardFeedData, CardFeedData, CompanyCardFeed};
export type {AddNewCardFeedStep, AddNewCompanyCardFeed, AddNewCardFeedData, CardFeedData, CompanyCardFeed, CardFeedProvider};

0 comments on commit 8ca4576

Please sign in to comment.