Skip to content

Commit

Permalink
Merge pull request #55281 from rayane-djouah/hide-not-activated-physi…
Browse files Browse the repository at this point in the history
…cal-card-on-search

Hide not activated physical cards on search
  • Loading branch information
luacmartins authored Jan 15, 2025
2 parents 332c9b0 + 22792e3 commit 5cc3a24
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 10 deletions.
4 changes: 4 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ const chatTypes = {
// Explicit type annotation is required
const cardActiveStates: number[] = [2, 3, 4, 7];

// Hide not issued or not activated cards (states 2 and 4) from card filter options in search, as no transactions can be made on cards in these states
const cardHiddenFromSearchStates: number[] = [2, 4];

const selectableOnboardingChoices = {
PERSONAL_SPEND: 'newDotPersonalSpend',
MANAGE_TEAM: 'newDotManageTeam',
Expand Down Expand Up @@ -2802,6 +2805,7 @@ const CONST = {
STATE_SUSPENDED: 7,
},
ACTIVE_STATES: cardActiveStates,
HIDDEN_FROM_SEARCH_STATES: cardHiddenFromSearchStates,
LIMIT_TYPES: {
SMART: 'smart',
MONTHLY: 'monthly',
Expand Down
4 changes: 2 additions & 2 deletions src/components/Search/SearchRouter/SearchRouterList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import usePolicy from '@hooks/usePolicy';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import {searchInServer} from '@libs/actions/Report';
import {getCardDescription, isCard, isCardIssued, mergeCardListWithWorkspaceFeeds} from '@libs/CardUtils';
import {getCardDescription, isCard, isCardHiddenFromSearch, mergeCardListWithWorkspaceFeeds} from '@libs/CardUtils';
import {combineOrderingOfReportsAndPersonalDetails, getSearchOptions, getValidOptions} from '@libs/OptionsListUtils';
import type {Options, SearchOption} from '@libs/OptionsListUtils';
import Performance from '@libs/Performance';
Expand Down Expand Up @@ -338,7 +338,7 @@ function SearchRouterList(
}
case CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID: {
const filteredCards = cardAutocompleteList
.filter((card) => isCard(card) && isCardIssued(card))
.filter((card) => isCard(card) && !isCardHiddenFromSearch(card))
.filter((card) => card.bank.toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(getCardDescription(card.cardID).toLowerCase()))
.sort()
.slice(0, 10);
Expand Down
5 changes: 5 additions & 0 deletions src/libs/CardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ function isCardIssued(card: Card) {
return !!card?.nameValuePairs?.isVirtual || card?.state !== CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED;
}

function isCardHiddenFromSearch(card: Card) {
return !card?.nameValuePairs?.isVirtual && CONST.EXPENSIFY_CARD.HIDDEN_FROM_SEARCH_STATES.includes(card.state ?? 0);
}

function mergeCardListWithWorkspaceFeeds(workspaceFeeds: Record<string, WorkspaceCardsList | undefined>, cardList = allCards) {
const feedCards: CardList = {...cardList};
Object.values(workspaceFeeds ?? {}).forEach((currentCardFeed) => {
Expand Down Expand Up @@ -485,4 +489,5 @@ export {
getDescriptionForPolicyDomainCard,
getAllCardsForWorkspace,
isCardIssued,
isCardHiddenFromSearch,
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {openSearchFiltersCardPage, updateAdvancedFilters} from '@libs/actions/Search';
import {getBankName, getCardFeedIcon, getDescriptionForPolicyDomainCard, isCard, isCardIssued} from '@libs/CardUtils';
import {getBankName, getCardFeedIcon, getDescriptionForPolicyDomainCard, isCard, isCardHiddenFromSearch} from '@libs/CardUtils';
import {getPolicy} from '@libs/PolicyUtils';
import type {OptionData} from '@libs/ReportUtils';
import Navigation from '@navigation/Navigation';
Expand Down Expand Up @@ -73,15 +73,15 @@ function buildIndividualCardsData(
selectedCards: string[],
): ItemsGroupedBySelection {
const userAssignedCards: CardFilterItem[] = Object.values(userCardList ?? {})
.filter((card) => isCardIssued(card))
.filter((card) => !isCardHiddenFromSearch(card))
.map((card) => createIndividualCardFilterItem(card, personalDetailsList, selectedCards));

// When user is admin of a workspace he sees all the cards of workspace under cards_ Onyx key
const allWorkspaceCards: CardFilterItem[] = Object.values(workspaceCardFeeds)
.filter((cardFeed) => !isEmptyObject(cardFeed))
.flatMap((cardFeed) => {
return Object.values(cardFeed as Record<string, Card>)
.filter((card) => card && isCard(card) && !userCardList?.[card.cardID] && isCardIssued(card))
.filter((card) => card && isCard(card) && !userCardList?.[card.cardID] && !isCardHiddenFromSearch(card))
.map((card) => createIndividualCardFilterItem(card, personalDetailsList, selectedCards));
});

Expand Down Expand Up @@ -165,15 +165,15 @@ function buildCardFeedsData(
.forEach(([cardFeedKey, cardFeed]) => {
const cardFeedArray = Object.values(cardFeed ?? {});
const representativeCard = cardFeedArray.find((cardFeedItem) => isCard(cardFeedItem));
if (!representativeCard || !cardFeedArray.some((cardFeedItem) => isCard(cardFeedItem) && isCardIssued(cardFeedItem))) {
if (!representativeCard || !cardFeedArray.some((cardFeedItem) => isCard(cardFeedItem) && !isCardHiddenFromSearch(cardFeedItem))) {
return;
}
const {domainName, bank} = representativeCard;
const isBankRepeating = repeatingBanks.includes(bank);
const policyID = domainName.match(CONST.REGEX.EXPENSIFY_POLICY_DOMAIN_NAME)?.[1] ?? '';
const correspondingPolicy = getPolicy(policyID?.toUpperCase());
const correspondingCardIDs = Object.entries(cardFeed ?? {})
.filter(([cardKey, card]) => cardKey !== 'cardList' && isCard(card) && isCardIssued(card))
.filter(([cardKey, card]) => cardKey !== 'cardList' && isCard(card) && !isCardHiddenFromSearch(card))
.map(([cardKey]) => cardKey);

const feedItem = createCardFeedItem({
Expand Down Expand Up @@ -219,7 +219,7 @@ function SearchFiltersCardPage() {
() =>
Object.values(userCardList ?? {}).reduce((accumulator, currentCard) => {
// Cards in cardList can also be domain cards, we use them to compute domain feed
if (!currentCard.domainName.match(CONST.REGEX.EXPENSIFY_POLICY_DOMAIN_NAME) && isCardIssued(currentCard)) {
if (!currentCard.domainName.match(CONST.REGEX.EXPENSIFY_POLICY_DOMAIN_NAME) && !isCardHiddenFromSearch(currentCard)) {
if (accumulator[currentCard.domainName]) {
accumulator[currentCard.domainName].correspondingCardIDs.push(currentCard.cardID.toString());
} else {
Expand Down
61 changes: 59 additions & 2 deletions tests/unit/Search/buildCardFilterDataTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,62 @@ const cardList = {
},
};

const workspaceCardFeedsHiddenOnSearch = {
'cards_11111_Expensify Card': {
'21534278': {
accountID: 1,
bank: 'Expensify Card',
cardID: 21534278,
domainName: 'expensify-policy1.exfy',
nameValuePairs: {cardTitle: 'Not Issued card'},
isVirtual: false,
lastFourPAN: '',
state: 2, // STATE_NOT_ISSUED
},
'21539025': {
accountID: 1,
bank: 'Expensify Card',
cardID: 21539025,
domainName: 'expensify-policy1.exfy',
nameValuePairs: {cardTitle: 'Not activated card'},
isVirtual: false,
lastFourPAN: '',
state: 4, // NOT_ACTIVATED
},
},
};

const cardListHiddenOnSearch = {
'21534538': {
accountID: 1,
bank: 'Expensify Card',
cardID: 21534538,
domainName: 'expensify-policy1.exfy',
nameValuePairs: {cardTitle: 'Not Issued card'},
isVirtual: false,
lastFourPAN: '',
state: 2, // STATE_NOT_ISSUED
},
'21534525': {
accountID: 1,
bank: 'Expensify Card',
cardID: 21534525,
domainName: 'expensify-policy1.exfy',
nameValuePairs: {cardTitle: 'Not activated card'},
isVirtual: false,
lastFourPAN: '',
state: 4, // NOT_ACTIVATED
},
};

const domainFeedDataMock = {testDomain: {domainName: 'testDomain', bank: 'Expensify Card', correspondingCardIDs: ['11111111']}};

const translateMock = jest.fn();

describe('buildIndividualCardsData', () => {
const result = buildIndividualCardsData(workspaceCardFeeds as unknown as Record<string, WorkspaceCardsList | undefined>, cardList as unknown as CardList, {}, ['21588678']);

it("Builds all individual cards and doesn't generate duplicates", () => {
const result = buildIndividualCardsData(workspaceCardFeeds as unknown as Record<string, WorkspaceCardsList | undefined>, cardList as unknown as CardList, {}, ['21588678']);

expect(result.unselected.length + result.selected.length).toEqual(11);

// Check if Expensify card was built correctly
Expand All @@ -181,6 +229,15 @@ describe('buildIndividualCardsData', () => {
isSelected: false,
});
});
it("Doesn't include physical cards that haven't been issued or haven't been activated", () => {
const result = buildIndividualCardsData(
workspaceCardFeedsHiddenOnSearch as unknown as Record<string, WorkspaceCardsList | undefined>,
cardListHiddenOnSearch as unknown as CardList,
{},
[],
);
expect(result.unselected.length + result.selected.length).toEqual(0);
});
});

describe('buildIndividualCardsData with empty argument objects', () => {
Expand Down

0 comments on commit 5cc3a24

Please sign in to comment.