Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ae10277
refactor: company card feed types and utils
chrispader Jan 20, 2026
2493975
fix: more types
chrispader Jan 20, 2026
34f8a2b
fix: TS errors
chrispader Jan 20, 2026
462a627
fix: more TS errors
chrispader Jan 20, 2026
fe2279e
fix: more TS errors
chrispader Jan 20, 2026
ac25b86
Merge branch 'main' into @chrispader/company-cards-refactoring-and-types
chrispader Jan 20, 2026
1d27ce9
fix: TS and ESLint
chrispader Jan 20, 2026
6347808
Merge branch 'main' into @chrispader/company-cards-refactoring-and-types
chrispader Jan 27, 2026
8da4899
fix: more migrations
chrispader Jan 27, 2026
fdc2d0b
refactor: multiple improvements based on GH comments
chrispader Jan 27, 2026
90be9c1
fix: TS errors
chrispader Jan 27, 2026
a83d284
fix: TS and tests
chrispader Jan 27, 2026
ab7136a
fix: TS error
chrispader Jan 27, 2026
7e24097
Merge branch 'main' into @chrispader/company-cards-refactoring-and-types
chrispader Jan 27, 2026
a4bb838
Merge branch 'main' into @chrispader/company-cards-refactoring-and-types
chrispader Jan 30, 2026
69730c8
Merge branch 'main' into @chrispader/company-cards-refactoring-and-types
chrispader Feb 3, 2026
8f4c4ef
Merge branch 'main' into pr/80006
chrispader Feb 6, 2026
282811f
fix: merge constant values
chrispader Feb 6, 2026
afdb927
fix: relative import
chrispader Feb 6, 2026
18f1789
fix: add missing sentryLabel
chrispader Feb 6, 2026
1a2f43d
fix: more TS and ESLint errors
chrispader Feb 6, 2026
236f1f4
update const value
chrispader Feb 6, 2026
a7a8e16
refactor: correct type for card feed in tests
chrispader Feb 6, 2026
8da8bf7
fix: card assignment on different feed not working
chrispader Feb 6, 2026
9fcb39a
fix: remove console.log
chrispader Feb 6, 2026
92e7bbe
Merge branch 'main' into @chrispader/company-cards-refactoring-and-types
chrispader Feb 7, 2026
4055fe5
Merge branch 'main' into @chrispader/company-cards-refactoring-and-types
chrispader Feb 8, 2026
c1ea55d
fix: invalid bracket in COST
chrispader Feb 8, 2026
b8da88b
Update index.ts
chrispader Feb 8, 2026
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
2 changes: 1 addition & 1 deletion src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,7 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_BANK_ACCOUNT_METADATA]: OnyxTypes.ExpensifyCardBankAccountMetadata;
[ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING]: boolean;
[ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST]: OnyxTypes.WorkspaceCardsList;
[ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS]: OnyxTypes.FailedCompanyCardAssignments;
[ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS]: OnyxTypes.FailedCardAssignments;
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName;
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: OnyxTypes.CardContinuousReconciliation;
[ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: OnyxTypes.CompanyCardFeedWithDomainID;
Expand Down
4 changes: 2 additions & 2 deletions src/components/CardFeedIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import {useCompanyCardFeedIcons} from '@hooks/useCompanyCardIcons';
import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
import useThemeIllustrations from '@hooks/useThemeIllustrations';
import {getCardFeedIcon, getPlaidInstitutionIconUrl, getPlaidInstitutionId} from '@libs/CardUtils';
import type {CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx';
import type {CardFeedWithDomainID, CompanyCardFeed} from '@src/types/onyx';
import type {IconProps} from './Icon';
import Icon from './Icon';
import PlaidCardFeedIcon from './PlaidCardFeedIcon';

type CardFeedIconProps = {
isExpensifyCardFeed?: boolean;
selectedFeed?: CompanyCardFeedWithDomainID | undefined;
selectedFeed?: CardFeedWithDomainID | undefined;
iconProps?: Partial<IconProps>;
useSkeletonLoader?: boolean;
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/ReportActionAvatars/ReportActionAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ 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';
import type {CardFeed} from '@src/types/onyx/CardFeeds';
import type {Icon as IconType} from '@src/types/onyx/OnyxCommon';

type SortingOptions = ValueOf<typeof CONST.REPORT_ACTION_AVATARS.SORT_BY>;
Expand Down Expand Up @@ -179,7 +179,7 @@ function ReportActionAvatarSubscript({
shouldShowTooltip: boolean;
noRightMarginOnContainer?: boolean;
subscriptAvatarBorderColor?: ColorValue;
subscriptCardFeed?: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK;
subscriptCardFeed?: CardFeed;
fallbackDisplayName?: string;
useProfileNavigationWrapper?: boolean;
reportID?: string;
Expand Down
5 changes: 3 additions & 2 deletions src/components/ReportActionAvatars/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type {ValueOf} from 'type-fest';
import useOnyx from '@hooks/useOnyx';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {CompanyCardFeed, InvitedEmailsToAccountIDs, Policy, Report, ReportAction} from '@src/types/onyx';
import type {InvitedEmailsToAccountIDs, Policy, Report, ReportAction} from '@src/types/onyx';
import type {CardFeed} from '@src/types/onyx/CardFeeds';
import type {HorizontalStacking} from './ReportActionAvatar';
import ReportActionAvatar from './ReportActionAvatar';
import useReportActionAvatars from './useReportActionAvatars';
Expand Down Expand Up @@ -56,7 +57,7 @@ type ReportActionAvatarsProps = {
subscriptAvatarBorderColor?: ColorValue;

/** Subscript card feed to display instead of the second avatar */
subscriptCardFeed?: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK;
subscriptCardFeed?: CardFeed;

/** Whether we want to be redirected to profile on avatars click */
useProfileNavigationWrapper?: boolean;
Expand Down
8 changes: 4 additions & 4 deletions src/hooks/useCardFeeds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {OnyxCollection, ResultMetadata} from 'react-native-onyx';
import {getCompanyCardFeedWithDomainID} from '@libs/CardUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type {CardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx';
import type {CustomCardFeedData, DirectCardFeedData} from '@src/types/onyx/CardFeeds';
import type {CardFeedWithDomainID, CardFeedWithNumber, CustomCardFeedData, DirectCardFeedData} from '@src/types/onyx/CardFeeds';
import useOnyx from './useOnyx';
import useWorkspaceAccountID from './useWorkspaceAccountID';

Expand All @@ -13,10 +13,10 @@ type CombinedCardFeed = CustomCardFeedData &
customFeedName?: string;

/** Feed name */
feed: CompanyCardFeed;
feed: CardFeedWithNumber;
};

type CombinedCardFeeds = Record<CompanyCardFeedWithDomainID, CombinedCardFeed>;
type CombinedCardFeeds = Record<CardFeedWithDomainID, CombinedCardFeed>;

/**
* This is a custom hook that combines workspace and domain card feeds for a given policy.
Expand Down Expand Up @@ -54,7 +54,7 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi
const feedOAuthAccountDetails = feed.settings.oAuthAccountDetails?.[feedName];
const feedCompanyCardNickname = feed.settings.companyCardNicknames?.[feedName];
const domainID = onyxKey.split('_').at(-1);
const shouldAddFeed = domainID && (feedSettings.preferredPolicy ? feedSettings.preferredPolicy === policyID : domainID === workspaceAccountID.toString());
const shouldAddFeed = domainID && (feedSettings?.preferredPolicy ? feedSettings.preferredPolicy === policyID : domainID === workspaceAccountID.toString());

if (!shouldAddFeed) {
continue;
Expand Down
11 changes: 5 additions & 6 deletions src/hooks/useCardsList.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import type {ResultMetadata} from 'react-native-onyx';
import {filterInactiveCards} from '@libs/CardUtils';
import CONST from '@src/CONST';
import {filterInactiveCards, splitCompanyCardFeedWithDomainID} from '@libs/CardUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type {CompanyCardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx';
import type {CardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx';
import useOnyx from './useOnyx';

/* Custom hook that retrieves a list of company cards for the given selected feed. */
const useCardsList = (selectedFeed: CompanyCardFeedWithDomainID | undefined): [WorkspaceCardsList | undefined, ResultMetadata<WorkspaceCardsList>] => {
const [feed, domainOrWorkspaceAccountID] = selectedFeed?.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR) ?? [];
const [cardsList, cardsListMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${feed}`, {
const useCardsList = (selectedFeed: CardFeedWithDomainID | undefined): [WorkspaceCardsList | undefined, ResultMetadata<WorkspaceCardsList>] => {
const {feedName, domainID} = splitCompanyCardFeedWithDomainID(selectedFeed);
const [cardsList, cardsListMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainID}_${feedName}`, {
selector: filterInactiveCards,
canBeMissing: true,
});
Expand Down
8 changes: 4 additions & 4 deletions src/hooks/useCompanyCards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {CardFeeds, CardList} from '@src/types/onyx';
import type {AssignableCardsList, WorkspaceCardsList} from '@src/types/onyx/Card';
import type {CompanyCardFeed, CompanyCardFeedWithDomainID, CompanyFeeds} from '@src/types/onyx/CardFeeds';
import type {CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
import useCardFeeds from './useCardFeeds';
import type {CombinedCardFeed, CombinedCardFeeds} from './useCardFeeds';
import useCardsList from './useCardsList';
Expand All @@ -18,9 +18,9 @@ type UseCompanyCardsProps = {
feedName?: CompanyCardFeedWithDomainID;
};

type UsCompanyCardsResult = Partial<{
type UseCompanyCardsResult = Partial<{
cardFeedType: CardFeedType;
bankName: CompanyCardFeed;
bankName: CompanyCardFeedWithNumber;
feedName: CompanyCardFeedWithDomainID;
cardList: AssignableCardsList;
assignedCards: CardList;
Expand All @@ -36,7 +36,7 @@ type UsCompanyCardsResult = Partial<{
};
};

function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProps): UsCompanyCardsResult {
function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProps): UseCompanyCardsResult {
const [lastSelectedFeed, lastSelectedFeedMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true});
const [allCardFeeds, allCardFeedsMetadata] = useCardFeeds(policyID);

Expand Down
4 changes: 3 additions & 1 deletion src/libs/API/parameters/AssignCompanyCardParams.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type {CardFeedWithNumber} from '@src/types/onyx/CardFeeds';

type AssignCompanyCardParams = {
policyID: string;
bankName: string;
bankName: CardFeedWithNumber | undefined;
cardName: string;
encryptedCardNumber: string;
email: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';

type OpenPolicyCompanyCardsFeedParams = {
domainAccountID?: number;
policyID: string;
feed: string;
feed: CompanyCardFeedWithNumber;
};

export default OpenPolicyCompanyCardsFeedParams;
15 changes: 8 additions & 7 deletions src/libs/CardFeedUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type IllustrationsType from '@styles/theme/illustrations/types';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
import type {CardFeed, CardFeedWithNumber} from '@src/types/onyx/CardFeeds';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {
getBankName,
Expand All @@ -22,17 +23,17 @@ import {getDescriptionForPolicyDomainCard, getPolicy} from './PolicyUtils';
import type {OptionData} from './ReportUtils';

type CardFilterItem = Partial<OptionData> & AdditionalCardProps & {isCardFeed?: boolean; correspondingCards?: string[]; cardFeedKey: string; plaidUrl?: string};
type DomainFeedData = {bank: string; domainName: string; correspondingCardIDs: string[]; fundID?: string};
type DomainFeedData = {bank: CardFeedWithNumber; domainName: string; correspondingCardIDs: string[]; fundID?: string};
type ItemsGroupedBySelection = {selected: CardFilterItem[]; unselected: CardFilterItem[]};
type CardFeedNamesWithType = Record<string, {name: string; type: 'domain' | 'workspace'}>;
type CardFeedData = {cardName: string; bank: string; label?: string; type: 'domain' | 'workspace'};
type CardFeedData = {cardName: string; bank: CardFeedWithNumber; label?: string; type: 'domain' | 'workspace'};
type GetCardFeedData = {
workspaceCardFeeds: Record<string, WorkspaceCardsList | undefined> | undefined;
translate: LocaleContextProps['translate'];
};
type CardFeedForDisplay = {
id: string;
feed: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK;
feed: CardFeed;
fundID: string;
name: string;
};
Expand Down Expand Up @@ -130,7 +131,7 @@ function buildCardsData(
const allWorkspaceCards: CardFilterItem[] = Object.values(workspaceCardFeeds)
.filter((cardFeed) => !isEmptyObject(cardFeed))
.flatMap((cardFeed) => {
return Object.values(cardFeed as Record<string, Card>)
return Object.values(cardFeed as CardList)
.filter((card) => card && isCard(card) && !userCardList?.[card.cardID] && filterCondition(card))
.map((card) => createCardFilterItem(card, personalDetailsList, selectedCards, illustrations, companyCardIcons));
});
Expand Down Expand Up @@ -292,7 +293,7 @@ function createCardFeedItem({
companyCardIcons,
}: {
cardName: string;
bank: string;
bank: CardFeedWithNumber;
keyForList: string;
cardFeedKey: string;
correspondingCardIDs: string[];
Expand Down Expand Up @@ -492,7 +493,7 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection<CardFeeds>
}

for (const [key, feedData] of Object.entries(getOriginalCompanyFeeds(cardFeeds))) {
const preferredPolicy = 'preferredPolicy' in feedData ? (feedData.preferredPolicy ?? '') : '';
const preferredPolicy = feedData && 'preferredPolicy' in feedData ? (feedData.preferredPolicy ?? '') : '';
const feed = key as CompanyCardFeed;
const id = `${fundID}_${feed}`;

Expand All @@ -508,7 +509,7 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection<CardFeeds>
return cardFeedsForDisplayPerPolicy;
}

export type {CardFilterItem, CardFeedNamesWithType, CardFeedForDisplay};
export type {CardFilterItem, CardFeedNamesWithType, CardFeedForDisplay, DomainFeedData};
export {
buildCardsData,
getCardFeedNamesWithType,
Expand Down
Loading
Loading