Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual';
type WorkspaceCompanyCardsAssignCardParams = {
policyID: string;
feed: string;
cardID?: string;
cardID: string;
};

// This is a file containing constants for all the routes we want to be able to go to
Expand Down Expand Up @@ -2192,10 +2192,7 @@ const ROUTES = {

getRoute: (params: WorkspaceCompanyCardsAssignCardParams, backTo?: string) =>
// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
getUrlWithBackToParam(
`workspaces/${params.policyID}/company-cards/${encodeURIComponent(params.feed)}/assign-card${params.cardID ? `/${encodeURIComponent(params.cardID)}` : ''}`,
backTo,
),
getUrlWithBackToParam(`workspaces/${params.policyID}/company-cards/${encodeURIComponent(params.feed)}/assign-card/${params.cardID}`, backTo),
},
WORKSPACE_COMPANY_CARD_DETAILS: {
route: 'workspaces/:policyID/company-cards/:bank/:cardID',
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useAssignCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA

const isAssigningCardDisabled = !currentFeedData || !!currentFeedData?.pending || isSelectedFeedConnectionBroken || !isAllowedToIssueCompanyCard;

const assignCard = (cardID?: string) => {
const assignCard = (cardID: string) => {
if (isAssigningCardDisabled) {
return;
}
Expand Down
25 changes: 21 additions & 4 deletions src/libs/CardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,10 @@ function formatCardExpiration(expirationDateString: string) {
* @returns collection of assigned cards grouped by domain
*/
function getDomainCards(cardList: OnyxEntry<CardList>): Record<string, Card[]> {
const assignedCards = getAssignedCardFromCardList(cardList ?? {});

// Check for domainName to filter out personal credit cards.
const activeCards = Object.values(cardList ?? {}).filter((card) => !!card?.domainName && CONST.EXPENSIFY_CARD.ACTIVE_STATES.some((element) => element === card.state));
const activeCards = Object.values(assignedCards).filter((card) => !!card?.domainName && CONST.EXPENSIFY_CARD.ACTIVE_STATES.some((element) => element === card.state));

return groupBy(activeCards, (card) => card.domainName);
}
Expand Down Expand Up @@ -663,8 +665,21 @@ function getAllCardsForWorkspace(
return cards;
}

function isSmartLimitEnabled(cards: CardList) {
return Object.values(cards).some((card) => card.nameValuePairs?.limitType === CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART);
/**
* Get assigned cards from card list by extracting the list of assignable cards from the card list
*
* @param cardList the card list
* @returns the assigned cards
*/
function getAssignedCardFromCardList(cardList: CardList) {
const {cardList: assignableCards, ...assignedCards} = cardList;
return assignedCards;
}

function isSmartLimitEnabled(cardList: CardList) {
const assignedCards = getAssignedCardFromCardList(cardList);

return Object.values(assignedCards).some((card) => card.nameValuePairs?.limitType === CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART);
}

const CUSTOM_FEEDS = [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD, CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX];
Expand Down Expand Up @@ -773,7 +788,8 @@ function isExpensifyCardPendingAction(card?: Card, privatePersonalDetails?: Priv
}

function hasPendingExpensifyCardAction(cards: CardList | undefined, privatePersonalDetails?: PrivatePersonalDetails) {
return Object.values(cards ?? {}).some((card) => isExpensifyCardPendingAction(card, privatePersonalDetails));
const assignedCards = getAssignedCardFromCardList(cards ?? {});
return Object.values(assignedCards).some((card) => isExpensifyCardPendingAction(card, privatePersonalDetails));
}
const isCurrencySupportedForECards = (currency?: string) => {
if (!currency) {
Expand Down Expand Up @@ -941,6 +957,7 @@ export {
COMPANY_CARD_BANK_ICON_NAMES,
isMaskedCardNumberEqual,
splitMaskedCardNumber,
getAssignedCardFromCardList,
};

export type {CompanyCardFeedIcons, CompanyCardBankIcons};
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) {
const isFeedPending = !!selectedFeedData?.pending;
const isFeedAdded = !isFeedPending && !isNoFeed;
const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false);
const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData)
const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData);

const {isOffline} = useNetwork({
onReconnect: () => openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID),
Expand Down
31 changes: 4 additions & 27 deletions src/pages/workspace/companyCards/WorkspaceCompanyCardsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import {getCompanyFeeds, getPlaidInstitutionIconUrl, getPlaidInstitutionId, isCustomFeed, isMaskedCardNumberEqual} from '@libs/CardUtils';
import {getCompanyFeeds, getPlaidInstitutionIconUrl, getPlaidInstitutionId, isMaskedCardNumberEqual} from '@libs/CardUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Card, CompanyCardFeedWithDomainID} from '@src/types/onyx';
Expand Down Expand Up @@ -58,7 +58,7 @@ function WorkspaceCompanyCardsTable({selectedFeed, policyID, onAssignCard, isAss
const [cardFeeds] = useCardFeeds(policyID);
const companyFeeds = getCompanyFeeds(cardFeeds);

const isPlaidCardFeed = !!companyFeeds?.[selectedFeed]?.accountList;
const isPlaidCardFeed = !!getPlaidInstitutionId(selectedFeed);
const cards = isPlaidCardFeed ? (companyFeeds?.[selectedFeed]?.accountList ?? []) : Object.keys(cardList ?? {});

// When we reach the medium screen width or the narrow layout is active,
Expand All @@ -81,30 +81,7 @@ function WorkspaceCompanyCardsTable({selectedFeed, policyID, onAssignCard, isAss

const isAssigned = !!assignedCard;

// Calculate cardIdentifier for unassigned cards
let cardIdentifier: string | undefined;
if (!assignedCard) {
const isPlaid = !!getPlaidInstitutionId(selectedFeed);
const isCommercial = isCustomFeed(selectedFeed);

if (isPlaid) {
cardIdentifier = cardName;
} else if (isCommercial) {
const cardValue = cardList?.[cardName] ?? cardName;
const digitsOnly = cardValue.replaceAll(/\D/g, '');
if (digitsOnly.length >= 10) {
const first6 = digitsOnly.substring(0, 6);
const last4 = digitsOnly.substring(digitsOnly.length - 4);
cardIdentifier = `${first6}${last4}`;
} else {
cardIdentifier = cardValue;
}
} else {
cardIdentifier = cardList?.[cardName] ?? cardName;
}
}

return {cardName, customCardName, isCardDeleted, isAssigned, assignedCard, cardholder, cardIdentifier};
return {cardName, customCardName, isCardDeleted, isAssigned, assignedCard, cardholder};
}) ?? [];

const renderItem = ({item, index}: ListRenderItemInfo<WorkspaceCompanyCardTableItemData>) => (
Expand All @@ -115,7 +92,7 @@ function WorkspaceCompanyCardsTable({selectedFeed, policyID, onAssignCard, isAss
selectedFeed={selectedFeed}
plaidIconUrl={getPlaidInstitutionIconUrl(selectedFeed)}
isPlaidCardFeed={isPlaidCardFeed}
onAssignCard={() => onAssignCard(item.cardIdentifier)}
onAssignCard={onAssignCard}
isAssigningCardDisabled={isAssigningCardDisabled}
shouldUseNarrowTableRowLayout={shouldShowNarrowLayout}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ type WorkspaceCompanyCardTableItemData = {

/** Whether the card is assigned */
isAssigned: boolean;

/** Card identifier for unassigned cards */
cardIdentifier?: string;
};

type WorkspaceCompanyCardTableItemProps = {
Expand All @@ -68,7 +65,7 @@ type WorkspaceCompanyCardTableItemProps = {
shouldUseNarrowTableRowLayout?: boolean;

/** On assign card callback */
onAssignCard: () => void;
onAssignCard: (cardID?: string) => void;
};

function WorkspaceCompanyCardTableItem({
Expand Down Expand Up @@ -97,6 +94,8 @@ function WorkspaceCompanyCardTableItem({

const alternateLoginText = shouldUseNarrowTableRowLayout ? `${customCardName}${lastCardNumbers ? ` - ${lastCardNumbers}` : ''}` : (cardholder?.login ?? '');

const assignCard = () => onAssignCard(cardName);

return (
<OfflineWithFeedback
errorRowStyles={styles.ph5}
Expand All @@ -111,7 +110,7 @@ function WorkspaceCompanyCardTableItem({
disabled={isCardDeleted}
onPress={() => {
if (!assignedCard) {
onAssignCard();
assignCard();
return;
}

Expand Down Expand Up @@ -213,7 +212,7 @@ function WorkspaceCompanyCardTableItem({
{!isAssigned && (
<Button
text={shouldUseNarrowTableRowLayout ? translate('workspace.companyCards.assign') : translate('workspace.companyCards.assignCard')}
onPress={onAssignCard}
onPress={assignCard}
isDisabled={isAssigningCardDisabled}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) {
case CONST.COMPANY_CARD.STEP.ASSIGNEE:
return (
<AssigneeStep
feed={feed}
policy={policy}
route={route}
/>
Expand Down
18 changes: 16 additions & 2 deletions src/types/onyx/Card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,13 @@ type ExpensifyCardDetails = {
cvv: string;
};

/** List of assignable cards */
type AssignableCardsList = Record<string, string>;

/** Record of Expensify cards, indexed by cardID */
type CardList = Record<string, Card> & {
/** List of assignable cards */
cardList?: Record<string, string>;
cardList?: AssignableCardsList;
};

/** Issue new card flow steps */
Expand Down Expand Up @@ -292,4 +295,15 @@ type WorkspaceCardsList = Record<string, Card> & {
type FilteredCardList = Record<string, string>;

export default Card;
export type {ExpensifyCardDetails, CardList, IssueNewCard, IssueNewCardStep, IssueNewCardData, WorkspaceCardsList, CardLimitType, FilteredCardList, ProvisioningCardData};
export type {
ExpensifyCardDetails,
CardList,
IssueNewCard,
IssueNewCardStep,
IssueNewCardData,
WorkspaceCardsList,
CardLimitType,
FilteredCardList,
ProvisioningCardData,
AssignableCardsList,
};
Loading