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
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';

import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';

import CONST from '@src/CONST';
import type {CompanyCardFeed} from '@src/types/onyx/CardFeeds';
Expand Down Expand Up @@ -74,7 +74,7 @@ function CardListItemHeader<TItem extends ListItem>({
const {isLargeScreenWidth} = useResponsiveLayout();
const StyleUtils = useStyleUtils();
const {translate, formatPhoneNumber} = useLocalize();
const formattedDisplayName = formatPhoneNumber(getDisplayNameOrDefault(cardItem));
const formattedDisplayName = formatPhoneNumber(temporaryGetDisplayNameOrDefault({passedPersonalDetails: cardItem, translate}));
const backgroundColor =
StyleUtils.getItemBackgroundColorStyle(!!cardItem.isSelected, !!isFocused, !!isDisabled, theme.activeComponentBG, theme.hoverComponentBG)?.backgroundColor ?? theme.highlightBG;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';

import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';

import CONST from '@src/CONST';

Expand Down Expand Up @@ -69,7 +69,7 @@ function MemberListItemHeader<TItem extends ListItem>({
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {translate, formatPhoneNumber} = useLocalize();
const formattedDisplayName = formatPhoneNumber(getDisplayNameOrDefault(memberItem));
const formattedDisplayName = formatPhoneNumber(temporaryGetDisplayNameOrDefault({passedPersonalDetails: memberItem, translate}));
const formattedLogin = formatPhoneNumber(memberItem.login ?? '');

const columnComponents = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {getTranslationKeyForLimitType} from '@libs/CardUtils';
import {convertToShortDisplayString} from '@libs/CurrencyUtils';
import DateUtils from '@libs/DateUtils';
import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';

import variables from '@styles/variables';

Expand Down Expand Up @@ -42,7 +42,7 @@ export default function WorkspaceExpensifyCardsTableRow({item, rowIndex, shouldU
const theme = useTheme();
const session = useSession();

const cardholderName = getDisplayNameOrDefault(item.cardholder);
const cardholderName = temporaryGetDisplayNameOrDefault({passedPersonalDetails: item.cardholder, translate});
const narrowLayoutSubtitle = [item.lastFourPAN, item.name].filter(Boolean).join(` ${CONST.DOT_SEPARATOR} `);
const cardType = item.isVirtual ? translate('workspace.expensifyCard.virtual') : translate('workspace.expensifyCard.physical');
const limitTypeLabel = translate(getTranslationKeyForLimitType(item.limitType));
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useExpensifyCardRulesList.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {filterInactiveCards, getCardDescriptionForSearchTable, getSelectedCardsSharedCurrency} from '@libs/CardUtils';
import {convertToBackendAmount, convertToDisplayString} from '@libs/CurrencyUtils';
import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {getSpendRuleFormValuesFromCardRule, getSpendRuleSummaryParts, getTruncatedSpendRuleSummary} from '@libs/SpendRulesUtils';

import CONST from '@src/CONST';
Expand Down Expand Up @@ -49,7 +49,7 @@ export default function useExpensifyCardRules(policyID: string) {
}

const accountID = card.accountID ?? CONST.DEFAULT_NUMBER_ID;
const displayName = getDisplayNameOrDefault(personalDetails?.[accountID], '', false);
const displayName = temporaryGetDisplayNameOrDefault({passedPersonalDetails: personalDetails?.[accountID], defaultValue: '', shouldFallbackToHidden: false, translate});
cardNames.push(getCardDescriptionForSearchTable(card, translate, displayName || undefined) || cardID);
cardOwnerDisplayNames.push(displayName);
}
Expand Down
13 changes: 9 additions & 4 deletions src/libs/CardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import {isBankAccountPartiallySetup} from './BankAccountUtils';
import {CARD_FEED_COLORS, GENERIC_CARD_COLORS} from './CardArtworkColors';
import DateUtils from './DateUtils';
import {filterObject} from './ObjectUtils';
import {areAddressAndPersonalDetailsMissing, arePersonalDetailsMissing, getDisplayNameOrDefault} from './PersonalDetailsUtils';
import {areAddressAndPersonalDetailsMissing, arePersonalDetailsMissing, temporaryGetDisplayNameOrDefault} from './PersonalDetailsUtils';
import StringUtils from './StringUtils';

/**
Expand Down Expand Up @@ -528,12 +528,17 @@ function getCardsByCardholderName(cardsList: OnyxEntry<WorkspaceCardsList>, poli
return Object.values(cards).filter((card: Card) => card.accountID && policyMembersAccountIDs.includes(card.accountID));
}

function sortCardsByCardholderName(cards: Card[], personalDetails: OnyxEntry<PersonalDetailsList>, localeCompare: LocaleContextProps['localeCompare']): Card[] {
function sortCardsByCardholderName(
cards: Card[],
personalDetails: OnyxEntry<PersonalDetailsList>,
localeCompare: LocaleContextProps['localeCompare'],
translate: LocalizedTranslate,
): Card[] {
return cards.sort((cardA: Card, cardB: Card) => {
const userA = cardA.accountID ? (personalDetails?.[cardA.accountID] ?? {}) : {};
const userB = cardB.accountID ? (personalDetails?.[cardB.accountID] ?? {}) : {};
const aName = getDisplayNameOrDefault(userA);
const bName = getDisplayNameOrDefault(userB);
const aName = temporaryGetDisplayNameOrDefault({passedPersonalDetails: userA, translate});
const bName = temporaryGetDisplayNameOrDefault({passedPersonalDetails: userB, translate});
return localeCompare(aName, bName);
});
}
Expand Down
6 changes: 4 additions & 2 deletions src/libs/SearchQueryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ import {validateAmount} from './MoneyRequestUtils';
import {getPreservedNavigatorState} from './Navigation/AppNavigator/createSplitNavigator/usePreserveNavigatorState';
import navigationRef from './Navigation/navigationRef';
import {isRecord} from './ObjectUtils';
import {getDisplayNameOrDefault, getPersonalDetailByEmail} from './PersonalDetailsUtils';
import {getPersonalDetailByEmail, temporaryGetDisplayNameOrDefault} from './PersonalDetailsUtils';
import {getCleanedTagName} from './PolicyUtils';
import {getReportName} from './ReportNameUtils';
import {parse as parseSearchQuery} from './SearchParser/searchParser';
Expand Down Expand Up @@ -1536,7 +1536,9 @@ function getFilterDisplayValue({
if (filterValue === CONST.SEARCH.ME) {
return CONST.SEARCH.ME;
}
return filterValue === currentUserAccountID.toString() ? CONST.SEARCH.ME : getDisplayNameOrDefault(personalDetails?.[filterValue], filterValue, false);
return filterValue === currentUserAccountID.toString()
? CONST.SEARCH.ME
: temporaryGetDisplayNameOrDefault({passedPersonalDetails: personalDetails?.[filterValue], defaultValue: filterValue, shouldFallbackToHidden: false, translate});
}
if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) {
const cardID = parseInt(filterValue, 10);
Expand Down
4 changes: 2 additions & 2 deletions src/pages/domain/Admins/DomainAdminsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import useThemeStyles from '@hooks/useThemeStyles';

import {hasDomainAdminsSettingsErrors} from '@libs/DomainUtils';
import {getLatestError} from '@libs/ErrorUtils';
import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';

import Navigation from '@navigation/Navigation';
import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
Expand Down Expand Up @@ -94,7 +94,7 @@ function DomainAdminsPage({route}: DomainAdminsPageProps) {
return {
keyForList: String(accountID),
accountID,
name: formatPhoneNumber(getDisplayNameOrDefault(details)),
name: formatPhoneNumber(temporaryGetDisplayNameOrDefault({passedPersonalDetails: details, translate})),
email: formatPhoneNumber(login),
isPrimaryContact: !!technicalContactEmail && !!login && technicalContactEmail === login,
errors: getLatestError(errors),
Expand Down
4 changes: 2 additions & 2 deletions src/pages/domain/Members/DomainMembersPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {clearDomainMemberError, closeUserAccount, exportMembersToCSV, setDomainM
import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import {getMemberCustomRowProps, hasDomainMembersSettingsErrors} from '@libs/DomainUtils';
import {getLatestError} from '@libs/ErrorUtils';
import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';

import Navigation from '@navigation/Navigation';
import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
Expand Down Expand Up @@ -121,7 +121,7 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
keyForList: String(accountID),
accountID,
login,
name: formatPhoneNumber(getDisplayNameOrDefault(details)),
name: formatPhoneNumber(temporaryGetDisplayNameOrDefault({passedPersonalDetails: details, translate})),
email: formatPhoneNumber(login),
groupName: group?.details.name ?? '-',
errors: getLatestError(customProps?.errors),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useOnyx from '@hooks/useOnyx';
import usePolicy from '@hooks/usePolicy';

import {areEmailsFromSamePrivateDomain} from '@libs/LoginUtils';
import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {getPolicyEmployeeAccountIDs} from '@libs/PolicyUtils';
import {canReportBeMentionedWithinPolicy, doesReportBelongToWorkspace, isGroupChat, isReportParticipant} from '@libs/ReportUtils';
import StringUtils from '@libs/StringUtils';
Expand Down Expand Up @@ -389,7 +389,7 @@ function SuggestionMention({
if (CONST.RESTRICTED_EMAILS.includes(detail.login) || CONST.RESTRICTED_ACCOUNT_IDS.includes(detail.accountID)) {
return false;
}
const displayName = getDisplayNameOrDefault(detail);
const displayName = temporaryGetDisplayNameOrDefault({passedPersonalDetails: detail, translate});
const displayText = displayName === formatPhoneNumber(detail.login) ? displayName : `${displayName} ${detail.login}`;
if (searchValue && !displayText.toLowerCase().includes(searchValue.toLowerCase())) {
return false;
Expand All @@ -416,7 +416,7 @@ function SuggestionMention({

for (const detail of sortedPersonalDetails.slice(0, CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS - suggestions.length)) {
suggestions.push({
text: `${formatLoginPrivateDomain(getDisplayNameOrDefault(detail), detail?.login)}`,
text: `${formatLoginPrivateDomain(temporaryGetDisplayNameOrDefault({passedPersonalDetails: detail, translate}), detail?.login)}`,
alternateText: `@${formatLoginPrivateDomain(detail?.login, detail?.login)}`,
handle: detail?.login,
icons: [
Expand Down
4 changes: 2 additions & 2 deletions src/pages/workspace/hr/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import getZenefitsSetupLink from '@libs/actions/connections/Zenefits';
import {getConnectedHRProvider, getHRApprovalMode, isMergeHRCompleteSetupNeeded} from '@libs/HRUtils';
import type {HRConnectionName} from '@libs/HRUtils';
import {formatList} from '@libs/Localize';
import {getDisplayNameOrDefault, getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
import {getPersonalDetailByEmail, temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {getIntegrationLastSuccessfulDate} from '@libs/PolicyUtils';

import CONST from '@src/CONST';
Expand Down Expand Up @@ -206,7 +206,7 @@ function getFinalApproverDisplayName(finalApprover: string | undefined | null, t
if (!finalApprover) {
return translate('workspace.hr.notSet');
}
return getDisplayNameOrDefault(getPersonalDetailByEmail(finalApprover), finalApprover, false);
return temporaryGetDisplayNameOrDefault({passedPersonalDetails: getPersonalDetailByEmail(finalApprover), defaultValue: finalApprover, shouldFallbackToHidden: false, translate});
}

/** Extracts the connection-specific config object (approval mode, final approver, pending/error fields) from the policy for a given HR provider. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function SpendRuleCardPage({route}: SpendRuleCardPageProps) {
const eligibleCards = expensifyCardSettings ? getEligibleCards(cardsList, expensifyCardSettings, ruleID === ROUTES.NEW ? undefined : ruleID) : [];

const filterCard = (card: Card, searchInput: string) => filterCardsByPersonalDetails(card, searchInput, personalDetails);
const sortCards = (cards: Card[]) => sortCardsByCardholderName(cards, personalDetails, localeCompare);
const sortCards = (cards: Card[]) => sortCardsByCardholderName(cards, personalDetails, localeCompare, translate);

const [inputValue, setInputValue, filteredCards] = useSearchResults(eligibleCards, filterCard, sortCards);

Expand Down
43 changes: 38 additions & 5 deletions tests/unit/CardUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2234,7 +2234,7 @@ describe('CardUtils', () => {
it('should sort cards by cardholder name in ascending order', () => {
const policyMembersAccountIDs = [1, 2, 3];
const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs);
const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare);
const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare, translateLocal);

expect(sortedCards).toHaveLength(3);
expect(sortedCards.at(0)?.cardID).toBe(2);
Expand All @@ -2245,7 +2245,7 @@ describe('CardUtils', () => {
it('should filter out cards that are not associated with policy members', () => {
const policyMembersAccountIDs = [1, 2]; // Exclude accountID 3
const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs);
const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare);
const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare, translateLocal);

expect(sortedCards).toHaveLength(2);
expect(sortedCards.at(0)?.cardID).toBe(2);
Expand All @@ -2255,15 +2255,15 @@ describe('CardUtils', () => {
it('should handle undefined cardsList', () => {
const policyMembersAccountIDs = [1, 2, 3];
const cards = getCardsByCardholderName(undefined, policyMembersAccountIDs);
const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare);
const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare, translateLocal);

expect(sortedCards).toHaveLength(0);
});

it('should handle undefined personalDetails', () => {
const policyMembersAccountIDs = [1, 2, 3];
const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs);
const sortedCards = sortCardsByCardholderName(cards, undefined, localeCompare);
const sortedCards = sortCardsByCardholderName(cards, undefined, localeCompare, translateLocal);

expect(sortedCards).toHaveLength(3);
// All cards should be sorted with default names
Expand Down Expand Up @@ -2301,11 +2301,44 @@ describe('CardUtils', () => {

const policyMembersAccountIDs = [1, 2];
const cards = getCardsByCardholderName(cardsWithMissingAccountID, policyMembersAccountIDs);
const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare);
const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare, translateLocal);

expect(sortedCards).toHaveLength(1);
expect(sortedCards.at(0)?.cardID).toBe(1);
});

it('resolves the fallback name for cardholders without personal details through the provided translate function', () => {
const policyMembersAccountIDs = [1, 2, 3];
const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs);
const requestedPaths: string[] = [];
const translate: LocalizedTranslate = (translatePath, ...parameters) => {
requestedPaths.push(translatePath);
return translateLocal(translatePath, ...parameters);
};

// With no personal details available, each cardholder name falls back to translate('common.hidden').
sortCardsByCardholderName(cards, undefined, localeCompare, translate);

expect(requestedPaths).toContain('common.hidden');
});

it('orders cardholders without a display name by the value returned from the translate function', () => {
// Only account 1 has personal details; account 2 falls back to the translated hidden name.
const personalDetailsWithOneKnownUser: PersonalDetailsList = {
1: {accountID: 1, login: 'mike@example.com', displayName: 'Mike'},
};
const cards = getCardsByCardholderName(mockCards, [1, 2]);

// A translate whose hidden value sorts before "Mike" puts the nameless cardholder first.
const translateHiddenFirst: LocalizedTranslate = (translatePath, ...parameters) =>
translatePath === 'common.hidden' ? 'AAA hidden' : translateLocal(translatePath, ...parameters);
expect(sortCardsByCardholderName(cards, personalDetailsWithOneKnownUser, localeCompare, translateHiddenFirst).map((card) => card.cardID)).toEqual([2, 1]);

// A translate whose hidden value sorts after "Mike" puts the nameless cardholder last.
const translateHiddenLast: LocalizedTranslate = (translatePath, ...parameters) =>
translatePath === 'common.hidden' ? 'zzz hidden' : translateLocal(translatePath, ...parameters);
expect(sortCardsByCardholderName(cards, personalDetailsWithOneKnownUser, localeCompare, translateHiddenLast).map((card) => card.cardID)).toEqual([1, 2]);
});
});

describe('getCardDescription', () => {
Expand Down
1 change: 1 addition & 0 deletions tests/unit/HrUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import createRandomPolicy from '../utils/collections/policies';
jest.mock('@libs/PersonalDetailsUtils', () => ({
getPersonalDetailByEmail: jest.fn(() => null),
getDisplayNameOrDefault: jest.fn((_detail: unknown, fallback: string) => fallback),
temporaryGetDisplayNameOrDefault: jest.fn(({defaultValue}: {defaultValue: string}) => defaultValue),
}));

const GUSTO = CONST.POLICY.CONNECTIONS.NAME.GUSTO;
Expand Down
Loading