From e2f9b821edd1cd9c635e1634240d5b04ece356eb Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Mon, 15 Dec 2025 17:50:15 -0500 Subject: [PATCH 01/48] feat: allow linking to specific card in ASSIGN_CARD flow --- src/ROUTES.ts | 5 +++-- src/hooks/useAssignCard.ts | 10 ++++++++-- src/libs/Navigation/types.ts | 2 ++ .../assignCard/AssignCardFeedPage.tsx | 16 ++++++++++++++-- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index a1cdab861e30..485127df671a 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2174,8 +2174,9 @@ const ROUTES = { WORKSPACE_COMPANY_CARDS_ASSIGN_CARD: { route: 'workspaces/:policyID/company-cards/:feed/assign-card', - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${encodeURIComponent(feed)}/assign-card`, backTo), + getRoute: (policyID: string, feed: string, cardID?: string, backTo?: string) => + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${encodeURIComponent(feed)}/assign-card${cardID ? `/${encodeURIComponent(cardID)}` : ''}`, backTo), }, WORKSPACE_COMPANY_CARD_DETAILS: { route: 'workspaces/:policyID/company-cards/:bank/:cardID', diff --git a/src/hooks/useAssignCard.ts b/src/hooks/useAssignCard.ts index 91b3a147a976..099c019ebe09 100644 --- a/src/hooks/useAssignCard.ts +++ b/src/hooks/useAssignCard.ts @@ -74,7 +74,11 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA const isAssigningCardDisabled = !currentFeedData || !!currentFeedData?.pending || isSelectedFeedConnectionBroken || !isAllowedToIssueCompanyCard; - const assignCard = () => { + const assignCard = (cardID?: string) => { + if (isAssigningCardDisabled) { + return; + } + if (isActingAsDelegate) { showDelegateNoAccessModal(); return; @@ -138,7 +142,9 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA clearAddNewCardFlow(); setAssignCardStepAndData({data, currentStep}); - Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed))); + Navigation.setNavigationActionToMicrotaskQueue(() => { + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed, cardID)); + }); }; return { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index f3e06b46419e..fa5e6a323f28 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1198,6 +1198,8 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD]: { policyID: string; feed: CompanyCardFeed; + cardID?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 28661e5034cc..4e495d1ef9ce 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -10,7 +10,7 @@ import PlaidConnectionStep from '@pages/workspace/companyCards/addNew/PlaidConne import BankConnection from '@pages/workspace/companyCards/BankConnection'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {clearAssignCardStepAndData} from '@userActions/CompanyCards'; +import {clearAssignCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -25,10 +25,12 @@ import TransactionStartDateStep from './TransactionStartDateStep'; type AssignCardFeedPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { + const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeedWithDomainID; + const cardID = route.params?.cardID ? decodeURIComponent(route.params?.cardID) : undefined; + const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const currentStep = assignCard?.currentStep; - const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeedWithDomainID; const backTo = route.params?.backTo; const policyID = policy?.id; const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isActingAsDelegateSelector, canBeMissing: true}); @@ -41,6 +43,16 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { }; }, []); + // useEffect(() => { + // if (cardID) { + // setAssignCardStepAndData({ + // currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, + // data: {encryptedCardNumber: cardSelected, cardNumber}, + // isEditing: false, + // }); + // } + // }, [cardID]); + if (isActingAsDelegate) { return ( Date: Mon, 15 Dec 2025 18:21:18 -0500 Subject: [PATCH 02/48] feat: extract route options into param object type --- src/ROUTES.ts | 13 +++++++++++-- src/hooks/useAssignCard.ts | 2 +- .../members/WorkspaceMemberNewCardPage.tsx | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 485127df671a..cd5a1f42b019 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -20,6 +20,12 @@ import type {ConnectionName, SageIntacctMappingName} from './types/onyx/Policy'; import type {CustomFieldType} from './types/onyx/PolicyEmployee'; import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual'; +type WorkspaceCompanyCardsAssignCardParams = { + policyID: string; + feed: string; + cardID?: string; +}; + // This is a file containing constants for all the routes we want to be able to go to /** @@ -2174,9 +2180,12 @@ const ROUTES = { WORKSPACE_COMPANY_CARDS_ASSIGN_CARD: { route: 'workspaces/:policyID/company-cards/:feed/assign-card', - getRoute: (policyID: string, feed: string, cardID?: string, backTo?: string) => + getRoute: (params: WorkspaceCompanyCardsAssignCardParams, backTo?: string) => // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${encodeURIComponent(feed)}/assign-card${cardID ? `/${encodeURIComponent(cardID)}` : ''}`, backTo), + getUrlWithBackToParam( + `workspaces/${params.policyID}/company-cards/${encodeURIComponent(params.feed)}/assign-card${params.cardID ? `/${encodeURIComponent(params.cardID)}` : ''}`, + backTo, + ), }, WORKSPACE_COMPANY_CARD_DETAILS: { route: 'workspaces/:policyID/company-cards/:bank/:cardID', diff --git a/src/hooks/useAssignCard.ts b/src/hooks/useAssignCard.ts index 099c019ebe09..a7d4225f5938 100644 --- a/src/hooks/useAssignCard.ts +++ b/src/hooks/useAssignCard.ts @@ -143,7 +143,7 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA clearAddNewCardFlow(); setAssignCardStepAndData({data, currentStep}); Navigation.setNavigationActionToMicrotaskQueue(() => { - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed, cardID)); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute({policyID, feed: selectedFeed, cardID})); }); }; diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index 65a92affcbae..303d36e41e6b 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -125,7 +125,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew isEditing: false, }); Navigation.setNavigationActionToMicrotaskQueue(() => - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))), + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute({policyID, feed: selectedFeed}, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))), ); } }; From 077e486b0f829e63c30eda1b4154bf1fa084a004 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Mon, 15 Dec 2025 19:04:34 -0500 Subject: [PATCH 03/48] update assignee step text --- src/languages/en.ts | 1 + src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 0e81ab54556c..ac17282fe94a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -4776,6 +4776,7 @@ const translations = { feedName: ({feedName}: CompanyCardFeedNameParams) => `${feedName} cards`, directFeed: 'Direct feed', whoNeedsCardAssigned: 'Who needs a card assigned?', + chooseTheCardholder: 'Choose the cardholder', chooseCard: 'Choose a card', chooseCardFor: ({assignee}: AssigneeParams) => `Choose a card for ${assignee}. Can't find the card you're looking for? Let us know.`, diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index fa86c380fcae..066c19fa1d60 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -228,13 +228,11 @@ function AssigneeStep({policy, feed, route}: AssigneeStepProps) { setDidScreenTransitionEnd(true)} > - {translate('workspace.companyCards.whoNeedsCardAssigned')} + {translate('workspace.companyCards.chooseTheCardholder')} Date: Mon, 15 Dec 2025 19:04:38 -0500 Subject: [PATCH 04/48] Update TransactionStartDateStep.tsx --- .../companyCards/assignCard/TransactionStartDateStep.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index dc391b8bdb12..59142d0f16fc 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -89,13 +89,10 @@ function TransactionStartDateStep() { - {translate('workspace.companyCards.chooseTransactionStartDate')} {translate('workspace.companyCards.startDateDescription')} Date: Mon, 15 Dec 2025 19:04:40 -0500 Subject: [PATCH 05/48] Update en.ts --- src/languages/en.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index ac17282fe94a..bf61b487eec9 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -4784,7 +4784,8 @@ const translations = { somethingMightBeBroken: 'Or something might be broken. Either way, if you have any questions, just contact Concierge.', chooseTransactionStartDate: 'Choose a transaction start date', - startDateDescription: "We'll import all transactions from this date onwards. If no date is specified, we’ll go as far back as your bank allows.", + startDateDescription: "Choose your import start date. We'll sync all transactions from this date onwards.", + fromTheBeginning: 'From the beginning', customStartDate: 'Custom start date', customCloseDate: 'Custom close date', From 2a6efbe4eafaecfbdf582cf13438a90f1ce5367c Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 12:45:51 -0500 Subject: [PATCH 06/48] import isLoadingOnyxValue and cardListMetada --- src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index b07564cea2ee..839a4642bcca 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -21,6 +21,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import WorkspaceCompanyCardPageEmptyState from './WorkspaceCompanyCardPageEmptyState'; import WorkspaceCompanyCardsFeedPendingPage from './WorkspaceCompanyCardsFeedPendingPage'; import WorkspaceCompanyCardsList from './WorkspaceCompanyCardsList'; @@ -43,7 +44,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const [cardFeeds, , defaultFeed] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; - const [cardsList] = useCardsList(selectedFeed); + const [cardsList, cardsListMetadata] = useCardsList(selectedFeed); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const hasNoAssignedCard = Object.keys(cardsList ?? {}).length === 0; From 92bd4cb898f2aaa6ea0472cc8bfaf9abf37cc22b Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 14:59:45 -0500 Subject: [PATCH 07/48] check card feeds --- src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 839a4642bcca..b0293fe79ffa 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -60,7 +60,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { }, [policyID, domainOrWorkspaceAccountID]); const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); - const isLoading = !isOffline && (!cardFeeds || (!!defaultFeed?.isLoading && isEmptyObject(cardsList))); + const isLoading = !isOffline && !cardFeeds; const isGB = countryByIp === CONST.COUNTRY.GB; const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard); From f85990b271941371ce46912521f69576f514e270 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 15:13:07 -0500 Subject: [PATCH 08/48] add isloadingCardsList --- .../workspace/companyCards/WorkspaceCompanyCardsList.tsx | 5 ++++- .../workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index f97c9f04d5e8..2e703390a84a 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -49,9 +49,12 @@ type WorkspaceCompanyCardsListProps = { /** Whether to show GB disclaimer */ shouldShowGBDisclaimer?: boolean; + + /** Whether the cards list is loading */ + isLoadingCardsList?: boolean; }; -function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignCard, isAssigningCardDisabled, shouldShowGBDisclaimer}: WorkspaceCompanyCardsListProps) { +function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignCard, isAssigningCardDisabled, shouldShowGBDisclaimer, isLoadingCardsList = false}: WorkspaceCompanyCardsListProps) { const styles = useThemeStyles(); const {translate, localeCompare} = useLocalize(); const listRef = useRef>(null); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index b0293fe79ffa..faf177a18d12 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -61,6 +61,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); const isLoading = !isOffline && !cardFeeds; + const isLoadingCardsList = (!isOffline && isLoadingOnyxValue(cardsListMetadata)) || testLoadingDelay; const isGB = countryByIp === CONST.COUNTRY.GB; const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard); @@ -120,6 +121,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { policyID={policyID} onAssignCard={assignCard} isAssigningCardDisabled={isAssigningCardDisabled} + isLoadingCardsList={isLoadingCardsList} /> )} From c254b6bebf1c602136d00ee6cf6ca259016946be Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 15:15:36 -0500 Subject: [PATCH 09/48] import skeleton elements --- src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 2e703390a84a..baf23a1440cb 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -7,6 +7,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {PressableWithFeedback} from '@components/Pressable'; import SearchBar from '@components/SearchBar'; import Text from '@components/Text'; +import TableRowSkeleton from '@components/Skeletons/TableRowSkeleton'; import useCardFeeds from '@hooks/useCardFeeds'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -14,6 +15,7 @@ import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSearchResults from '@hooks/useSearchResults'; import useThemeStyles from '@hooks/useThemeStyles'; +import useNetwork from '@hooks/useNetwork'; import { filterCardsByPersonalDetails, getCardsByCardholderName, @@ -56,6 +58,7 @@ type WorkspaceCompanyCardsListProps = { function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignCard, isAssigningCardDisabled, shouldShowGBDisclaimer, isLoadingCardsList = false}: WorkspaceCompanyCardsListProps) { const styles = useThemeStyles(); + const {isOffline} = useNetwork(); const {translate, localeCompare} = useLocalize(); const listRef = useRef>(null); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); From fafdcb4f32e7e22cd03eced17b8826803b439df1 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 15:18:37 -0500 Subject: [PATCH 10/48] implement skeleton when loading --- .../workspace/companyCards/WorkspaceCompanyCardsList.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index baf23a1440cb..63a013152678 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -215,8 +215,8 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC ); - // Show empty state when there are no cards - if (!cards?.length) { + // Show empty state when there are no cards (but not when loading) + if (!cards?.length && !isLoadingCardsList) { return ( : undefined} showsVerticalScrollIndicator={false} keyboardShouldPersistTaps="handled" contentContainerStyle={styles.flexGrow1} From 2562d109b18b06b4e68601ad5aa126db0539a80a Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 16:28:47 -0500 Subject: [PATCH 11/48] polish skeleton loading --- .../companyCards/WorkspaceCompanyCardsList.tsx | 11 +++++++---- .../companyCards/WorkspaceCompanyCardsPage.tsx | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 63a013152678..50aacc681319 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -15,6 +15,7 @@ import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSearchResults from '@hooks/useSearchResults'; import useThemeStyles from '@hooks/useThemeStyles'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import useNetwork from '@hooks/useNetwork'; import { filterCardsByPersonalDetails, @@ -63,7 +64,9 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC const listRef = useRef>(null); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); - const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); + const [personalDetails, personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); + const isLoadingPersonalDetails = !isOffline && isLoadingOnyxValue(personalDetailsMetadata); + const isLoadingCardsTableData = isLoadingCardsList || isLoadingPersonalDetails; const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true}); const policy = usePolicy(policyID); @@ -216,7 +219,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC ); // Show empty state when there are no cards (but not when loading) - if (!cards?.length && !isLoadingCardsList) { + if (!cards?.length && !isLoadingCardsTableData) { return ( : undefined} + ListEmptyComponent={!isOffline && isLoadingCardsTableData ? : undefined} showsVerticalScrollIndicator={false} keyboardShouldPersistTaps="handled" contentContainerStyle={styles.flexGrow1} diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index faf177a18d12..0dca766b7cba 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -61,7 +61,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); const isLoading = !isOffline && !cardFeeds; - const isLoadingCardsList = (!isOffline && isLoadingOnyxValue(cardsListMetadata)) || testLoadingDelay; + const isLoadingCardsList = (!isOffline && isLoadingOnyxValue(cardsListMetadata)); const isGB = countryByIp === CONST.COUNTRY.GB; const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard); From a881e70d393fe0391fcf668d51d4ea99cb267dde Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 22:40:54 -0500 Subject: [PATCH 12/48] add cardID to route --- src/ROUTES.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index cd5a1f42b019..8a2b9be6ade7 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2178,7 +2178,7 @@ const ROUTES = { getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/select-feed` as const, }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD: { - route: 'workspaces/:policyID/company-cards/:feed/assign-card', + route: 'workspaces/:policyID/company-cards/:feed/assign-card/:cardID?', getRoute: (params: WorkspaceCompanyCardsAssignCardParams, backTo?: string) => // eslint-disable-next-line no-restricted-syntax -- Legacy route generation From 880dbe4731d3a8fbe21406b600ab8e39798ee1eb Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 23:06:39 -0500 Subject: [PATCH 13/48] update navigation --- .../workspace/companyCards/WorkspaceCompanyCardsList.tsx | 5 ++++- .../companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx | 4 ++-- src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index f97c9f04d5e8..f42826f902a7 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -19,7 +19,9 @@ import { getCardsByCardholderName, getCompanyCardFeedWithDomainID, getCompanyFeeds, + getPlaidInstitutionId, getPlaidInstitutionIconUrl, + isCustomFeed, sortCardsByCardholderName, } from '@libs/CardUtils'; import {getMemberAccountIDsForWorkspace} from '@libs/PolicyUtils'; @@ -42,7 +44,7 @@ type WorkspaceCompanyCardsListProps = { policyID: string; /** On assign card callback */ - onAssignCard: () => void; + onAssignCard: (cardID?: string) => void; /** Whether to disable assign card button */ isAssigningCardDisabled?: boolean; @@ -146,6 +148,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC }, [ assignedCards, + cardList, customCardNames, isAssigningCardDisabled, onAssignCard, diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index 66f95aa77680..a73fda9660eb 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -96,12 +96,12 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp data, currentStep: CONST.COMPANY_CARD.STEP.PLAID_CONNECTION, }); - Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed))); + Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute({policyID, feed: selectedFeed}))); return; } setAssignCardStepAndData({data, currentStep: CONST.COMPANY_CARD.STEP.BANK_CONNECTION}); - Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed))); + Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute({policyID, feed: selectedFeed}))); }; const secondaryActions = useMemo( diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index 303d36e41e6b..6446a2610ed2 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -125,7 +125,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew isEditing: false, }); Navigation.setNavigationActionToMicrotaskQueue(() => - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute({policyID, feed: selectedFeed}, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))), + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute({policyID, feed: selectedFeed, cardID: undefined}, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))), ); } }; From fd036d80e2ec8da68d26bd809f5e4c16ecf9bfb2 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 23:06:59 -0500 Subject: [PATCH 14/48] add plaid and commercial checks for urls --- .../WorkspaceCompanyCardsList.tsx | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index f42826f902a7..455cc5188a9d 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -95,6 +95,28 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC const customCardName = customCardNames?.[assignedCard?.cardID ?? CONST.DEFAULT_NUMBER_ID]; const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + + 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.replace(/\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 ( { if (!assignedCard) { - onAssignCard(); + onAssignCard(cardIdentifier); return; } @@ -137,7 +159,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC customCardName={customCardName} isHovered={hovered} isAssigned={!!assignedCard} - onAssignCard={onAssignCard} + onAssignCard={() => onAssignCard(cardIdentifier)} isAssigningCardDisabled={isAssigningCardDisabled} shouldUseNarrowTableRowLayout={shouldUseNarrowTableRowLayout} /> From 98ac60fe26c2f190f8a219a06c37c96d1b549d6a Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 23:23:21 -0500 Subject: [PATCH 15/48] open rhp if visiting card url directly --- .../assignCard/AssignCardFeedPage.tsx | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 4e495d1ef9ce..72a9cde384a0 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -10,6 +10,7 @@ import PlaidConnectionStep from '@pages/workspace/companyCards/addNew/PlaidConne import BankConnection from '@pages/workspace/companyCards/BankConnection'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; +import {getCompanyCardFeed} from '@libs/CardUtils'; import {clearAssignCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -43,15 +44,19 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { }; }, []); - // useEffect(() => { - // if (cardID) { - // setAssignCardStepAndData({ - // currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, - // data: {encryptedCardNumber: cardSelected, cardNumber}, - // isEditing: false, - // }); - // } - // }, [cardID]); + useEffect(() => { + if (cardID && !currentStep) { + const companyCardFeed = getCompanyCardFeed(feed); + + setAssignCardStepAndData({ + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + data: { + bankName: companyCardFeed, + encryptedCardNumber: cardID, + }, + }); + } + }, [cardID, currentStep, feed]); if (isActingAsDelegate) { return ( From c21c0fb9fda8bddc260bc45db5810a0fa244c41c Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Tue, 16 Dec 2025 23:25:02 -0500 Subject: [PATCH 16/48] open member selection in rhp --- src/hooks/useAssignCard.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hooks/useAssignCard.ts b/src/hooks/useAssignCard.ts index a7d4225f5938..cd70b6231b41 100644 --- a/src/hooks/useAssignCard.ts +++ b/src/hooks/useAssignCard.ts @@ -101,6 +101,10 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA bankName: feed, }; + if (cardID) { + data.encryptedCardNumber = cardID; + } + let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.ASSIGNEE; const employeeList = Object.values(policy?.employeeList ?? {}).filter((employee) => !isDeletedPolicyEmployee(employee, isOffline)); const isFeedExpired = isSelectedFeedExpired(selectedFeedData); @@ -112,7 +116,7 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA importPlaidAccounts('', selectedFeed, '', country, getDomainNameForPolicy(policyID), '', undefined, undefined, plaidAccessToken); } - if (employeeList.length === 1) { + if (!cardID && employeeList.length === 1) { const userEmail = Object.keys(policy?.employeeList ?? {}).at(0) ?? ''; data.email = userEmail; const personalDetails = getPersonalDetailByEmail(userEmail); From cd67fb38cba591b4d06438dfc257dd6e0e86ba10 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 11:02:33 -0500 Subject: [PATCH 17/48] remove filteredCardList logic --- .../companyCards/assignCard/AssigneeStep.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 066c19fa1d60..036df93e6ca1 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -16,7 +16,7 @@ import useSearchSelector from '@hooks/useSearchSelector'; import useThemeStyles from '@hooks/useThemeStyles'; import {setDraftInviteAccountID} from '@libs/actions/Card'; import {searchInServer} from '@libs/actions/Report'; -import {getDefaultCardName, getFilteredCardList, hasOnlyOneCardToAssign} from '@libs/CardUtils'; +import {getDefaultCardName} from '@libs/CardUtils'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getHeaderMessage, getSearchValueForPhoneOrEmail, sortAlphabetically} from '@libs/OptionsListUtils'; @@ -52,7 +52,6 @@ function AssigneeStep({policy, feed, route}: AssigneeStepProps) { const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); const [list] = useCardsList(feed); const [cardFeeds] = useCardFeeds(policyID); - const filteredCardList = getFilteredCardList(list, cardFeeds?.[feed]?.accountList, workspaceCardFeeds); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false, canBeMissing: true}); @@ -108,10 +107,10 @@ function AssigneeStep({policy, feed, route}: AssigneeStepProps) { return; } - if (hasOnlyOneCardToAssign(filteredCardList)) { - nextStep = CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE; - data.cardNumber = Object.keys(filteredCardList).at(0); - data.encryptedCardNumber = Object.values(filteredCardList).at(0); + if (assignCard?.data?.encryptedCardNumber) { + nextStep = CONST.COMPANY_CARD.STEP.CONFIRMATION; + data.encryptedCardNumber = assignCard.data.encryptedCardNumber; + data.cardNumber = assignCard.data.cardNumber; } setAssignCardStepAndData({ From 99419cd584e0c7c44d848c50753c2574093cdb73 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 12:07:01 -0500 Subject: [PATCH 18/48] remove steps from confirmation screen --- .../workspace/companyCards/assignCard/ConfirmationStep.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index ffcbdd5c42da..ca217ac7059c 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -104,8 +104,6 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { Date: Wed, 17 Dec 2025 12:10:11 -0500 Subject: [PATCH 19/48] update back navigation --- .../workspace/companyCards/assignCard/ConfirmationStep.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index ca217ac7059c..81791b330927 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -97,7 +97,7 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { }; const handleBackButtonPress = () => { - setAssignCardStepAndData({currentStep: CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE}); + setAssignCardStepAndData({currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE}); }; return ( From 49ef4bf9c0ecb22756c6967684a40e40458a1c5b Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 12:33:23 -0500 Subject: [PATCH 20/48] UI updates to confirmation screen --- .../assignCard/ConfirmationStep.tsx | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index 81791b330927..e5bad5176e61 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -14,6 +14,7 @@ import useRootNavigationState from '@hooks/useRootNavigationState'; import useThemeStyles from '@hooks/useThemeStyles'; import {getCompanyCardFeed, getPlaidCountry, getPlaidInstitutionId, isSelectedFeedExpired, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; import {isFullScreenName} from '@libs/Navigation/helpers/isNavigatorName'; +import {getDefaultAvatarURL} from '@libs/UserUtils'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import Navigation from '@navigation/Navigation'; import {assignWorkspaceCompanyCard, clearAssignCardStepAndData, setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; @@ -50,7 +51,10 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { const [cardFeeds] = useCardFeeds(policyID); const data = assignCard?.data; - const cardholderName = getPersonalDetailByEmail(data?.email ?? '')?.displayName ?? ''; + const cardholderDetails = getPersonalDetailByEmail(data?.email ?? ''); + const cardholderName = cardholderDetails?.displayName ?? ''; + const cardholderEmail = data?.email ?? ''; + const cardholderAccountID = cardholderDetails?.accountID; const currentFullScreenRoute = useRootNavigationState((state) => state?.routes?.findLast((route) => isFullScreenName(route.name))); @@ -105,7 +109,6 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { wrapperID={ConfirmationStep.displayName} handleBackButtonPress={handleBackButtonPress} headerTitle={translate('workspace.companyCards.assignCard')} - headerSubtitle={cardholderName} enableEdgeToEdgeBottomSafeAreaPadding shouldShowOfflineIndicatorInWideScreen > @@ -116,12 +119,6 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { > {translate('workspace.companyCards.letsDoubleCheck')} {translate('workspace.companyCards.confirmationDescription')} - editStep(CONST.COMPANY_CARD.STEP.ASSIGNEE)} - /> editStep(CONST.COMPANY_CARD.STEP.CARD)} /> + + {cardholderName} + {cardholderEmail} + + } + icon={cardholderDetails?.avatar ?? getDefaultAvatarURL(cardholderAccountID)} + iconType={CONST.ICON_TYPE_AVATAR} + shouldShowRightIcon + onPress={() => editStep(CONST.COMPANY_CARD.STEP.ASSIGNEE)} + /> Date: Wed, 17 Dec 2025 12:37:11 -0500 Subject: [PATCH 21/48] remove unused variables --- src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 036df93e6ca1..1d0fc9848d90 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -7,8 +7,6 @@ import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; import type {ListItem} from '@components/SelectionList/types'; import Text from '@components/Text'; -import useCardFeeds from '@hooks/useCardFeeds'; -import useCardsList from '@hooks/useCardsList'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -48,10 +46,7 @@ function AssigneeStep({policy, feed, route}: AssigneeStepProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); - const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: false}); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); - const [list] = useCardsList(feed); - const [cardFeeds] = useCardFeeds(policyID); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false, canBeMissing: true}); From ad439c9e10e2e33dd316ccac1e8397f48a2d00c4 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 12:45:28 -0500 Subject: [PATCH 22/48] update card identifier menu item in confirmation screen --- .../companyCards/assignCard/ConfirmationStep.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index e5bad5176e61..3ff872136ec9 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -12,7 +12,7 @@ import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useRootNavigationState from '@hooks/useRootNavigationState'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCompanyCardFeed, getPlaidCountry, getPlaidInstitutionId, isSelectedFeedExpired, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; +import {getCompanyCardFeed, getPlaidCountry, getPlaidInstitutionId, isSelectedFeedExpired, maskCardNumber} from '@libs/CardUtils'; import {isFullScreenName} from '@libs/Navigation/helpers/isNavigatorName'; import {getDefaultAvatarURL} from '@libs/UserUtils'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; @@ -121,10 +121,8 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { {translate('workspace.companyCards.confirmationDescription')} editStep(CONST.COMPANY_CARD.STEP.CARD)} + title={data?.encryptedCardNumber ?? maskCardNumber(data?.cardNumber ?? '', data?.bankName)} + interactive={false} /> Date: Wed, 17 Dec 2025 12:52:33 -0500 Subject: [PATCH 23/48] add default date to confirmation screen --- src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 1d0fc9848d90..852b6405b923 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -1,4 +1,5 @@ import React, {useEffect, useMemo, useState} from 'react'; +import {format} from 'date-fns'; import {Keyboard} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -106,6 +107,8 @@ function AssigneeStep({policy, feed, route}: AssigneeStepProps) { nextStep = CONST.COMPANY_CARD.STEP.CONFIRMATION; data.encryptedCardNumber = assignCard.data.encryptedCardNumber; data.cardNumber = assignCard.data.cardNumber; + data.startDate = data.startDate ?? format(new Date(), CONST.DATE.FNS_FORMAT_STRING); + data.dateOption = data.dateOption ?? CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.CUSTOM; } setAssignCardStepAndData({ From a7da239b21b1637c8d3a9f03eb6bf69bc3a9ee47 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 14:36:56 -0500 Subject: [PATCH 24/48] update cardholder menu item --- .../companyCards/assignCard/ConfirmationStep.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index 3ff872136ec9..b467b3c86a87 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -2,6 +2,7 @@ import React, {useEffect} from 'react'; import {InteractionManager, View} from 'react-native'; import Button from '@components/Button'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; +import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScrollView from '@components/ScrollView'; @@ -124,14 +125,11 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { title={data?.encryptedCardNumber ?? maskCardNumber(data?.cardNumber ?? '', data?.bankName)} interactive={false} /> - - {cardholderName} - {cardholderEmail} - - } + Date: Wed, 17 Dec 2025 14:48:36 -0500 Subject: [PATCH 25/48] add back navigation logic to start date --- .../companyCards/assignCard/AssignCardFeedPage.tsx | 2 +- .../assignCard/TransactionStartDateStep.tsx | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 72a9cde384a0..b3218aea6d4e 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -101,7 +101,7 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { /> ); case CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE: - return ; + return ; case CONST.COMPANY_CARD.STEP.CARD_NAME: return ; case CONST.COMPANY_CARD.STEP.CONFIRMATION: diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index 59142d0f16fc..465f2e1f186f 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -1,4 +1,7 @@ import {format, subDays} from 'date-fns'; +import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import type SCREENS from '@src/SCREENS'; import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import Button from '@components/Button'; @@ -12,11 +15,12 @@ import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {isRequiredFulfilled} from '@libs/ValidationUtils'; +import Navigation from '@navigation/Navigation'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -function TransactionStartDateStep() { +function TransactionStartDateStep({route}: {route: PlatformStackRouteProp}) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -37,7 +41,13 @@ function TransactionStartDateStep() { }); return; } - setAssignCardStepAndData({currentStep: CONST.COMPANY_CARD.STEP.CARD}); + const backTo = route?.params?.backTo; + if (backTo) { + Navigation.goBack(backTo); + return; + } + const nextStep = data?.encryptedCardNumber ? CONST.COMPANY_CARD.STEP.ASSIGNEE : CONST.COMPANY_CARD.STEP.CARD; + setAssignCardStepAndData({currentStep: nextStep}); }; const handleSelectDateOption = (dateOption: string) => { From 04e686a27dcd0fa545db584761af93e17643ab89 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 14:58:19 -0500 Subject: [PATCH 26/48] fix assignee step navigation when reselecting same member --- .../workspace/companyCards/assignCard/AssigneeStep.tsx | 9 +++++++++ .../companyCards/assignCard/ConfirmationStep.tsx | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 852b6405b923..d11ca5b29d13 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -83,9 +83,18 @@ function AssigneeStep({policy, feed, route}: AssigneeStepProps) { }; Keyboard.dismiss(); + if (assignee?.login === assignCard?.data?.email) { + if (assignCard?.data?.encryptedCardNumber) { + nextStep = CONST.COMPANY_CARD.STEP.CONFIRMATION; + data.encryptedCardNumber = assignCard.data.encryptedCardNumber; + data.cardNumber = assignCard.data.cardNumber; + data.startDate = data.startDate ?? format(new Date(), CONST.DATE.FNS_FORMAT_STRING); + data.dateOption = data.dateOption ?? CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.CUSTOM; + } setAssignCardStepAndData({ currentStep: isEditing ? CONST.COMPANY_CARD.STEP.CONFIRMATION : nextStep, + data, isEditing: false, }); return; diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index b467b3c86a87..7340db8f1e71 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -128,8 +128,8 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { Date: Wed, 17 Dec 2025 15:13:23 -0500 Subject: [PATCH 27/48] react compiler fixes --- .../WorkspaceCompanyCardsList.tsx | 18 ++++------ .../assignCard/TransactionStartDateStep.tsx | 33 +++++++++---------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 455cc5188a9d..099b6207078a 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -1,6 +1,6 @@ import type {FlashListRef, ListRenderItemInfo} from '@shopify/flash-list'; import {FlashList} from '@shopify/flash-list'; -import React, {useCallback, useMemo, useRef} from 'react'; +import React, {useRef} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -72,14 +72,12 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC const plaidIconUrl = getPlaidInstitutionIconUrl(selectedFeed); // Get all cards sorted by cardholder name - const allCards = useMemo(() => { - const policyMembersAccountIDs = Object.values(getMemberAccountIDsForWorkspace(policy?.employeeList)); - return getCardsByCardholderName(cardsList, policyMembersAccountIDs); - }, [cardsList, policy?.employeeList]); + const policyMembersAccountIDs = Object.values(getMemberAccountIDsForWorkspace(policy?.employeeList)); + const allCards = getCardsByCardholderName(cardsList, policyMembersAccountIDs); // Filter and sort cards based on search input - const filterCard = useCallback((card: Card, searchInput: string) => filterCardsByPersonalDetails(card, searchInput, personalDetails), [personalDetails]); - const sortCards = useCallback((cardsToSort: Card[]) => sortCardsByCardholderName(cardsToSort, personalDetails, localeCompare), [personalDetails, localeCompare]); + const filterCard = (card: Card, searchInput: string) => filterCardsByPersonalDetails(card, searchInput, personalDetails); + const sortCards = (cardsToSort: Card[]) => sortCardsByCardholderName(cardsToSort, personalDetails, localeCompare); const [inputValue, setInputValue, filteredSortedCards] = useSearchResults(allCards, filterCard, sortCards); const isSearchEmpty = filteredSortedCards.length === 0 && inputValue.length > 0; @@ -88,8 +86,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC // we want to hide the table header and the middle column of the card rows, so that the content is not overlapping. const shouldUseNarrowTableRowLayout = isMediumScreenWidth || shouldUseNarrowLayout; - const renderItem = useCallback( - ({item: cardName, index}: ListRenderItemInfo) => { + const renderItem = ({item: cardName, index}: ListRenderItemInfo) => { const assignedCard = Object.values(assignedCards ?? {}).find((card) => card.cardName === cardName); const customCardName = customCardNames?.[assignedCard?.cardID ?? CONST.DEFAULT_NUMBER_ID]; @@ -185,10 +182,9 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC styles.mb3, styles.mh5, styles.ph5, - ], ); - const keyExtractor = useCallback((item: string, index: number) => `${item}_${index}`, []); + const keyExtractor = (item: string, index: number) => `${item}_${index}`; const ListHeaderComponent = shouldUseNarrowTableRowLayout ? ( diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index 465f2e1f186f..957ef633da0e 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -2,7 +2,7 @@ import {format, subDays} from 'date-fns'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import type SCREENS from '@src/SCREENS'; -import React, {useMemo, useState} from 'react'; +import React, {useState} from 'react'; import {View} from 'react-native'; import Button from '@components/Button'; import DatePicker from '@components/DatePicker'; @@ -77,23 +77,20 @@ function TransactionStartDateStep({route}: {route: PlatformStackRouteProp [ - { - value: CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.FROM_BEGINNING, - text: translate('workspace.companyCards.fromTheBeginning'), - keyForList: CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.FROM_BEGINNING, - isSelected: dateOptionSelected === CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.FROM_BEGINNING, - }, - { - value: CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.CUSTOM, - text: translate('workspace.companyCards.customStartDate'), - keyForList: CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.CUSTOM, - isSelected: dateOptionSelected === CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.CUSTOM, - }, - ], - [dateOptionSelected, translate], - ); + const dateOptions = [ + { + value: CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.FROM_BEGINNING, + text: translate('workspace.companyCards.fromTheBeginning'), + keyForList: CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.FROM_BEGINNING, + isSelected: dateOptionSelected === CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.FROM_BEGINNING, + }, + { + value: CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.CUSTOM, + text: translate('workspace.companyCards.customStartDate'), + keyForList: CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.CUSTOM, + isSelected: dateOptionSelected === CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.CUSTOM, + }, + ]; return ( Date: Wed, 17 Dec 2025 15:36:59 -0500 Subject: [PATCH 28/48] react compiler fixes --- .../WorkspaceCompanyCardsList.tsx | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 099b6207078a..3f6da7be04e1 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -164,25 +164,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC ); - }, - [ - assignedCards, - cardList, - customCardNames, - isAssigningCardDisabled, - onAssignCard, - personalDetails, - plaidIconUrl, - policyID, - selectedFeed, - shouldUseNarrowTableRowLayout, - styles.br3, - styles.highlightBG, - styles.hoveredComponentBG, - styles.mb3, - styles.mh5, - styles.ph5, - ); + }; const keyExtractor = (item: string, index: number) => `${item}_${index}`; From 10962b1c3daec0d2ec9de93def2b4d98320b06d3 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 15:44:19 -0500 Subject: [PATCH 29/48] lint errors --- .../WorkspaceCompanyCardsList.tsx | 2 +- .../assignCard/AssignCardFeedPage.tsx | 21 ++++++++++--------- .../companyCards/assignCard/AssigneeStep.tsx | 2 +- .../assignCard/ConfirmationStep.tsx | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 3f6da7be04e1..42ddd8413538 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -102,7 +102,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC cardIdentifier = cardName; } else if (isCommercial) { const cardValue = cardList?.[cardName] ?? cardName; - const digitsOnly = cardValue.replace(/\D/g, ''); + const digitsOnly = cardValue.replaceAll(/\D/g, ''); if (digitsOnly.length >= 10) { const first6 = digitsOnly.substring(0, 6); const last4 = digitsOnly.substring(digitsOnly.length - 4); diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index b3218aea6d4e..ffa836448612 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -45,17 +45,18 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { }, []); useEffect(() => { - if (cardID && !currentStep) { - const companyCardFeed = getCompanyCardFeed(feed); - - setAssignCardStepAndData({ - currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, - data: { - bankName: companyCardFeed, - encryptedCardNumber: cardID, - }, - }); + if (!cardID || currentStep) { + return; } + const companyCardFeed = getCompanyCardFeed(feed); + + setAssignCardStepAndData({ + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + data: { + bankName: companyCardFeed, + encryptedCardNumber: cardID, + }, + }); }, [cardID, currentStep, feed]); if (isActingAsDelegate) { diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index d11ca5b29d13..959d9bd410e3 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -41,7 +41,7 @@ type AssigneeStepProps = { route: PlatformStackRouteProp; }; -function AssigneeStep({policy, feed, route}: AssigneeStepProps) { +function AssigneeStep({policy, feed: _feed, route}: AssigneeStepProps) { const policyID = route.params.policyID; const {translate, formatPhoneNumber, localeCompare} = useLocalize(); const styles = useThemeStyles(); diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index 7340db8f1e71..f5725a689d45 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -129,7 +129,7 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { label={translate('workspace.companyCards.cardholder')} labelStyle={styles.mb3} title={cardholderName && cardholderName !== cardholderEmail ? cardholderName : cardholderEmail} - {...(cardholderName && cardholderName !== cardholderEmail ? {description: cardholderEmail} : {})} + description={cardholderName && cardholderName !== cardholderEmail ? cardholderEmail : undefined} icon={cardholderDetails?.avatar ?? getDefaultAvatarURL(cardholderAccountID)} iconType={CONST.ICON_TYPE_AVATAR} shouldShowRightIcon From eeb347bf2883992df1ec832ca44477125efe5299 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 16:05:52 -0500 Subject: [PATCH 30/48] lint errors --- src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx | 2 +- .../workspace/companyCards/assignCard/ConfirmationStep.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 959d9bd410e3..18d7454edfc0 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -41,7 +41,7 @@ type AssigneeStepProps = { route: PlatformStackRouteProp; }; -function AssigneeStep({policy, feed: _feed, route}: AssigneeStepProps) { +function AssigneeStep({policy, route}: AssigneeStepProps) { const policyID = route.params.policyID; const {translate, formatPhoneNumber, localeCompare} = useLocalize(); const styles = useThemeStyles(); diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index f5725a689d45..876b09447c69 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -15,7 +15,7 @@ import useRootNavigationState from '@hooks/useRootNavigationState'; import useThemeStyles from '@hooks/useThemeStyles'; import {getCompanyCardFeed, getPlaidCountry, getPlaidInstitutionId, isSelectedFeedExpired, maskCardNumber} from '@libs/CardUtils'; import {isFullScreenName} from '@libs/Navigation/helpers/isNavigatorName'; -import {getDefaultAvatarURL} from '@libs/UserUtils'; +import {getDefaultAvatarURL} from '@libs/UserAvatarUtils'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import Navigation from '@navigation/Navigation'; import {assignWorkspaceCompanyCard, clearAssignCardStepAndData, setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; @@ -130,7 +130,7 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { labelStyle={styles.mb3} title={cardholderName && cardholderName !== cardholderEmail ? cardholderName : cardholderEmail} description={cardholderName && cardholderName !== cardholderEmail ? cardholderEmail : undefined} - icon={cardholderDetails?.avatar ?? getDefaultAvatarURL(cardholderAccountID)} + icon={cardholderDetails?.avatar ?? getDefaultAvatarURL({accountID: cardholderAccountID ?? CONST.DEFAULT_NUMBER_ID})} iconType={CONST.ICON_TYPE_AVATAR} shouldShowRightIcon onPress={() => editStep(CONST.COMPANY_CARD.STEP.ASSIGNEE)} From 8efe9125ad82a02e3e770b6fb733b7507a3d68b5 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 16:12:47 -0500 Subject: [PATCH 31/48] remove unusued feed prop --- .../workspace/companyCards/assignCard/AssignCardFeedPage.tsx | 2 -- src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index ffa836448612..1346d6af9fad 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -90,7 +90,6 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { return ( ); @@ -124,7 +123,6 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { return ( ); diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 18d7454edfc0..ba8903180f36 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -34,9 +34,6 @@ type AssigneeStepProps = { /** The policy that the card will be issued under */ policy: OnyxEntry; - /** Selected feed */ - feed: OnyxTypes.CompanyCardFeedWithDomainID; - /** Route params */ route: PlatformStackRouteProp; }; From 9d9b7d46a4cb87c1122e9a176e5e6dd0c554d2ec Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 16:15:23 -0500 Subject: [PATCH 32/48] prettier --- .../WorkspaceCompanyCardsList.tsx | 146 +++++++++--------- .../assignCard/AssignCardFeedPage.tsx | 2 +- .../companyCards/assignCard/AssigneeStep.tsx | 2 +- .../assignCard/ConfirmationStep.tsx | 2 +- .../assignCard/TransactionStartDateStep.tsx | 6 +- .../members/WorkspaceMemberNewCardPage.tsx | 4 +- 6 files changed, 82 insertions(+), 80 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 42ddd8413538..a4e6d9e1c09d 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -19,8 +19,8 @@ import { getCardsByCardholderName, getCompanyCardFeedWithDomainID, getCompanyFeeds, - getPlaidInstitutionId, getPlaidInstitutionIconUrl, + getPlaidInstitutionId, isCustomFeed, sortCardsByCardholderName, } from '@libs/CardUtils'; @@ -87,83 +87,83 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC const shouldUseNarrowTableRowLayout = isMediumScreenWidth || shouldUseNarrowLayout; const renderItem = ({item: cardName, index}: ListRenderItemInfo) => { - const assignedCard = Object.values(assignedCards ?? {}).find((card) => card.cardName === cardName); - - const customCardName = customCardNames?.[assignedCard?.cardID ?? CONST.DEFAULT_NUMBER_ID]; - - const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; - - 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; - } + const assignedCard = Object.values(assignedCards ?? {}).find((card) => card.cardName === cardName); + + const customCardName = customCardNames?.[assignedCard?.cardID ?? CONST.DEFAULT_NUMBER_ID]; + + const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + + 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 = cardList?.[cardName] ?? cardName; + cardIdentifier = cardValue; } + } else { + cardIdentifier = cardList?.[cardName] ?? cardName; } + } - return ( - + { + if (!assignedCard) { + onAssignCard(cardIdentifier); + return; + } + + if (!assignedCard?.accountID || !assignedCard?.fundID) { + return; + } + + return Navigation.navigate( + ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute( + policyID, + assignedCard.cardID.toString(), + getCompanyCardFeedWithDomainID(assignedCard?.bank as CompanyCardFeed, assignedCard.fundID), + ), + ); + }} > - { - if (!assignedCard) { - onAssignCard(cardIdentifier); - return; - } - - if (!assignedCard?.accountID || !assignedCard?.fundID) { - return; - } - - return Navigation.navigate( - ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute( - policyID, - assignedCard.cardID.toString(), - getCompanyCardFeedWithDomainID(assignedCard?.bank as CompanyCardFeed, assignedCard.fundID), - ), - ); - }} - > - {({hovered}) => ( - onAssignCard(cardIdentifier)} - isAssigningCardDisabled={isAssigningCardDisabled} - shouldUseNarrowTableRowLayout={shouldUseNarrowTableRowLayout} - /> - )} - - - ); + {({hovered}) => ( + onAssignCard(cardIdentifier)} + isAssigningCardDisabled={isAssigningCardDisabled} + shouldUseNarrowTableRowLayout={shouldUseNarrowTableRowLayout} + /> + )} + + + ); }; const keyExtractor = (item: string, index: number) => `${item}_${index}`; diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 1346d6af9fad..b042270fe8f1 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -4,13 +4,13 @@ import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper'; import ScreenWrapper from '@components/ScreenWrapper'; import useInitial from '@hooks/useInitial'; import useOnyx from '@hooks/useOnyx'; +import {getCompanyCardFeed} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; import PlaidConnectionStep from '@pages/workspace/companyCards/addNew/PlaidConnectionStep'; import BankConnection from '@pages/workspace/companyCards/BankConnection'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {getCompanyCardFeed} from '@libs/CardUtils'; import {clearAssignCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index ba8903180f36..e3c159fd3703 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -1,5 +1,5 @@ -import React, {useEffect, useMemo, useState} from 'react'; import {format} from 'date-fns'; +import React, {useEffect, useMemo, useState} from 'react'; import {Keyboard} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index 876b09447c69..ee2f35d48744 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -15,8 +15,8 @@ import useRootNavigationState from '@hooks/useRootNavigationState'; import useThemeStyles from '@hooks/useThemeStyles'; import {getCompanyCardFeed, getPlaidCountry, getPlaidInstitutionId, isSelectedFeedExpired, maskCardNumber} from '@libs/CardUtils'; import {isFullScreenName} from '@libs/Navigation/helpers/isNavigatorName'; -import {getDefaultAvatarURL} from '@libs/UserAvatarUtils'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; +import {getDefaultAvatarURL} from '@libs/UserAvatarUtils'; import Navigation from '@navigation/Navigation'; import {assignWorkspaceCompanyCard, clearAssignCardStepAndData, setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index 957ef633da0e..e31d76c4fd14 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -1,7 +1,4 @@ import {format, subDays} from 'date-fns'; -import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; -import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import type SCREENS from '@src/SCREENS'; import React, {useState} from 'react'; import {View} from 'react-native'; import Button from '@components/Button'; @@ -13,12 +10,15 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; +import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {isRequiredFulfilled} from '@libs/ValidationUtils'; import Navigation from '@navigation/Navigation'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; function TransactionStartDateStep({route}: {route: PlatformStackRouteProp}) { const {translate} = useLocalize(); diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index 6446a2610ed2..e3d01105ec67 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -125,7 +125,9 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew isEditing: false, }); Navigation.setNavigationActionToMicrotaskQueue(() => - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute({policyID, feed: selectedFeed, cardID: undefined}, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))), + Navigation.navigate( + ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute({policyID, feed: selectedFeed, cardID: undefined}, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID)), + ), ); } }; From 5ea1f497f61faa2dfd8c5eba336e335ef1d9dca5 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 16:30:33 -0500 Subject: [PATCH 33/48] translations and useMemoizedLazyExpensifyIcons --- src/languages/de.ts | 1 + src/languages/es.ts | 1 + src/languages/fr.ts | 1 + src/languages/it.ts | 1 + src/languages/ja.ts | 1 + src/languages/nl.ts | 1 + src/languages/pl.ts | 1 + src/languages/pt-BR.ts | 1 + src/languages/zh-hans.ts | 1 + src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx | 5 +++-- 10 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index d564baa8e803..cfb78e5034f1 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -4891,6 +4891,7 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU feedName: ({feedName}: CompanyCardFeedNameParams) => `${feedName}-Karten`, directFeed: 'Direkt-Feed', whoNeedsCardAssigned: 'Wer braucht eine zugewiesene Karte?', + chooseTheCardholder: 'Wähle den Karteninhaber', chooseCard: 'Wähle eine Karte', chooseCardFor: ({assignee}: AssigneeParams) => `Wähle eine Karte für ${assignee}. Du findest die Karte, die du suchst, nicht? Teile es uns mit.`, diff --git a/src/languages/es.ts b/src/languages/es.ts index 4869b8669263..09ae6feac3b0 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -4409,6 +4409,7 @@ ${amount} para ${merchant} - ${date}`, feedName: ({feedName}) => `Tarjetas ${feedName}`, directFeed: 'Fuente directa', whoNeedsCardAssigned: '¿Quién necesita una tarjeta?', + chooseTheCardholder: 'Elige el titular de la tarjeta', chooseCard: 'Elige una tarjeta', chooseCardFor: ({assignee}) => `Elige una tarjeta para ${assignee}. ¿No encuentras la tarjeta que buscas? Avísanos.`, noActiveCards: 'No hay tarjetas activas en este feed', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 348c03b779b3..b3ff0b0a840f 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -4894,6 +4894,7 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST. feedName: ({feedName}: CompanyCardFeedNameParams) => `Cartes ${feedName}`, directFeed: 'Flux direct', whoNeedsCardAssigned: 'Qui a besoin d’une carte attribuée ?', + chooseTheCardholder: 'Choisissez le titulaire de la carte', chooseCard: 'Choisissez une carte', chooseCardFor: ({assignee}: AssigneeParams) => `Choisissez une carte pour ${assignee}. Vous ne trouvez pas la carte que vous recherchez ? Faites-le-nous savoir.`, diff --git a/src/languages/it.ts b/src/languages/it.ts index fdc51026f132..dce714c2f8b7 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -4874,6 +4874,7 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST. feedName: ({feedName}: CompanyCardFeedNameParams) => `Carte ${feedName}`, directFeed: 'Feed diretto', whoNeedsCardAssigned: 'Chi ha bisogno di una carta assegnata?', + chooseTheCardholder: 'Scegli il titolare della carta', chooseCard: 'Scegli una carta', chooseCardFor: ({assignee}: AssigneeParams) => `Scegli una carta per ${assignee}. Non riesci a trovare la carta che stai cercando? Facci sapere.`, diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 64307358155d..778cb40aa817 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -4849,6 +4849,7 @@ _より詳しい手順については、[ヘルプサイトをご覧ください feedName: ({feedName}: CompanyCardFeedNameParams) => `${feedName} カード`, directFeed: 'ダイレクトフィード', whoNeedsCardAssigned: '誰にカードを割り当てる必要がありますか?', + chooseTheCardholder: 'カード所有者を選択', chooseCard: 'カードを選択', chooseCardFor: ({assignee}: AssigneeParams) => `${assignee} に使うカードを選択してください。お探しのカードが見つかりませんか?お知らせください。`, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 7c6b77da032a..eb6a3b7a7b53 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -4868,6 +4868,7 @@ _Voor gedetailleerdere instructies, [bezoek onze helpsite](${CONST.NETSUITE_IMPO feedName: ({feedName}: CompanyCardFeedNameParams) => `${feedName}-kaarten`, directFeed: 'Directe feed', whoNeedsCardAssigned: 'Wie heeft een kaart toegewezen nodig?', + chooseTheCardholder: 'Kies de kaarthouder', chooseCard: 'Kies een kaart', chooseCardFor: ({assignee}: AssigneeParams) => `Kies een kaart voor ${assignee}. Kun je de kaart die je zoekt niet vinden? Laat het ons weten.`, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index fbde33fa9dbf..e914d0991f06 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -4861,6 +4861,7 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy feedName: ({feedName}: CompanyCardFeedNameParams) => `Karty ${feedName}`, directFeed: 'Bezpośredni kanał', whoNeedsCardAssigned: 'Kto potrzebuje przypisanej karty?', + chooseTheCardholder: 'Wybierz posiadacza karty', chooseCard: 'Wybierz kartę', chooseCardFor: ({assignee}: AssigneeParams) => `Wybierz kartę dla ${assignee}. Nie możesz znaleźć karty, której szukasz? Daj nam znać.`, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 99cf6e164665..9d45f29005dc 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -4860,6 +4860,7 @@ _Para instruções mais detalhadas, [visite nosso site de ajuda](${CONST.NETSUIT feedName: ({feedName}: CompanyCardFeedNameParams) => `Cartões ${feedName}`, directFeed: 'Conexão direta', whoNeedsCardAssigned: 'Quem precisa de um cartão atribuído?', + chooseTheCardholder: 'Escolha o portador do cartão', chooseCard: 'Escolha um cartão', chooseCardFor: ({assignee}: AssigneeParams) => `Escolha um cartão para ${assignee}. Não encontra o cartão que está procurando? Avise-nos.`, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 518aa19bdd03..6728dac96de8 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -4781,6 +4781,7 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM feedName: ({feedName}: CompanyCardFeedNameParams) => `${feedName} 卡片`, directFeed: '直接数据馈送', whoNeedsCardAssigned: '谁需要被分配一张卡?', + chooseTheCardholder: '选择持卡人', chooseCard: '选择一张卡片', chooseCardFor: ({assignee}: AssigneeParams) => `为 ${assignee} 选择一张卡片。找不到您要找的卡片?请告诉我们。`, noActiveCards: '此信息流中没有有效的卡片', diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index e3c159fd3703..98fb1acec84c 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -2,12 +2,12 @@ import {format} from 'date-fns'; import React, {useEffect, useMemo, useState} from 'react'; import {Keyboard} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import * as Expensicons from '@components/Icon/Expensicons'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; import type {ListItem} from '@components/SelectionList/types'; import Text from '@components/Text'; +import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -43,6 +43,7 @@ function AssigneeStep({policy, route}: AssigneeStepProps) { const {translate, formatPhoneNumber, localeCompare} = useLocalize(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); + const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar'] as const); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); @@ -156,7 +157,7 @@ function AssigneeStep({policy, route}: AssigneeStepProps) { isSelected: assignCard?.data?.email === email, icons: [ { - source: personalDetail?.avatar ?? Expensicons.FallbackAvatar, + source: personalDetail?.avatar ?? icons.FallbackAvatar, name: formatPhoneNumber(email), type: CONST.ICON_TYPE_AVATAR, id: personalDetail?.accountID, From ec9378b03eb3662e64fd4b17e73f12162fa7e4f9 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 17:13:58 -0500 Subject: [PATCH 34/48] fix warning --- src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx | 2 +- src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 98fb1acec84c..ed1c7565a72f 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -169,7 +169,7 @@ function AssigneeStep({policy, route}: AssigneeStepProps) { membersList = sortAlphabetically(membersList, 'text', localeCompare); return membersList; - }, [isOffline, policy?.employeeList, assignCard?.data?.email, formatPhoneNumber, localeCompare]); + }, [isOffline, policy?.employeeList, assignCard?.data?.email, formatPhoneNumber, localeCompare, icons.FallbackAvatar]); const assignees = useMemo(() => { if (!debouncedSearchTerm) { diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index a10d4e41233b..2be256202aea 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -274,6 +274,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM Navigation.navigate(ROUTES.WORKSPACE_MEMBER_NEW_CARD.getRoute(policyID, accountID)); return; } + const activeRoute = Navigation.getActiveRoute(); setIssueNewCardStepAndData({ From c22604bfb52be6f74960c2b6bbaad0fb7d6a5c3e Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 18:08:37 -0500 Subject: [PATCH 35/48] remove unused handleIssueNewCard --- .../members/WorkspaceMemberDetailsPage.tsx | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index cd9cd22eed4f..0870a2fd06b1 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -264,31 +264,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM [policyID], ); - const handleIssueNewCard = useCallback(() => { - if (isAccountLocked) { - showLockedAccountModal(); - return; - } - - if (hasMultipleFeeds) { - Navigation.navigate(ROUTES.WORKSPACE_MEMBER_NEW_CARD.getRoute(policyID, accountID)); - return; - } - - const activeRoute = Navigation.getActiveRoute(); - - setIssueNewCardStepAndData({ - step: CONST.EXPENSIFY_CARD.STEP.CARD_TYPE, - data: { - assigneeEmail: memberLogin, - }, - isEditing: false, - isChangeAssigneeDisabled: true, - policyID, - }); - Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID, activeRoute)); - }, [accountID, hasMultipleFeeds, memberLogin, policyID, isAccountLocked, showLockedAccountModal]); - const startChangeOwnershipFlow = useCallback(() => { clearWorkspaceOwnerChangeFlow(policyID); Navigation.navigate(ROUTES.WORKSPACE_OWNER_CHANGE_CHECK.getRoute(policyID, accountID, 'amountOwed' as ValueOf)); From cb8f62f4e58c121dde53b92c9690643d06ebe751 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 18:17:55 -0500 Subject: [PATCH 36/48] remove unused setIssueNewCardStepAndData --- src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 0870a2fd06b1..87a1524c4907 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -54,7 +54,6 @@ import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import variables from '@styles/variables'; -import {setIssueNewCardStepAndData} from '@userActions/Card'; import {clearWorkspaceOwnerChangeFlow, isApprover as isApproverUserAction, openPolicyMemberProfilePage, removeMembers} from '@userActions/Policy/Member'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; From d0a64e97a951d4b6ea2301dea773794ea8b07d77 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 20:23:07 -0500 Subject: [PATCH 37/48] update button to say save --- src/pages/workspace/companyCards/assignCard/CardNameStep.tsx | 2 +- .../companyCards/assignCard/TransactionStartDateStep.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx index 537540fc5724..3fd7d4bf0dea 100644 --- a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx @@ -69,7 +69,7 @@ function CardNameStep({policyID}: CardNameStepProps) { {translate('workspace.moreFeatures.companyCards.giveItNameInstruction')} } From f8dcab16bc5c7f5c089824827141e889cf002011 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 20:50:26 -0500 Subject: [PATCH 38/48] add canBeMissing --- src/pages/workspace/companyCards/assignCard/CardNameStep.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx index 3fd7d4bf0dea..19812da2cde5 100644 --- a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx @@ -27,7 +27,7 @@ function CardNameStep({policyID}: CardNameStepProps) { const {translate} = useLocalize(); const {inputCallbackRef} = useAutoFocusInput(); const styles = useThemeStyles(); - const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); + const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const data = assignCard?.data; From fc0cb04ca9b25dac115ac8d367d38214e5c99044 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 21:27:14 -0500 Subject: [PATCH 39/48] move isLoadingCardsList to WorkspaceCompanyCardsList --- .../companyCards/WorkspaceCompanyCardsList.tsx | 11 ++++------- .../companyCards/WorkspaceCompanyCardsPage.tsx | 6 +----- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 50aacc681319..c267555e6c84 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -9,6 +9,7 @@ import SearchBar from '@components/SearchBar'; import Text from '@components/Text'; import TableRowSkeleton from '@components/Skeletons/TableRowSkeleton'; import useCardFeeds from '@hooks/useCardFeeds'; +import useCardsList from '@hooks/useCardsList'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; @@ -38,9 +39,6 @@ type WorkspaceCompanyCardsListProps = { /** Selected feed */ selectedFeed: CompanyCardFeedWithDomainID; - /** List of company cards */ - cardsList: OnyxEntry; - /** Current policy id */ policyID: string; @@ -52,18 +50,17 @@ type WorkspaceCompanyCardsListProps = { /** Whether to show GB disclaimer */ shouldShowGBDisclaimer?: boolean; - - /** Whether the cards list is loading */ - isLoadingCardsList?: boolean; }; -function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignCard, isAssigningCardDisabled, shouldShowGBDisclaimer, isLoadingCardsList = false}: WorkspaceCompanyCardsListProps) { +function WorkspaceCompanyCardsList({selectedFeed, policyID, onAssignCard, isAssigningCardDisabled, shouldShowGBDisclaimer}: WorkspaceCompanyCardsListProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); const {translate, localeCompare} = useLocalize(); const listRef = useRef>(null); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); + const [cardsList, cardsListMetadata] = useCardsList(selectedFeed); + const isLoadingCardsList = !isOffline && isLoadingOnyxValue(cardsListMetadata); const [personalDetails, personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const isLoadingPersonalDetails = !isOffline && isLoadingOnyxValue(personalDetailsMetadata); const isLoadingCardsTableData = isLoadingCardsList || isLoadingPersonalDetails; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 4e4eb10f0f2a..b54c2e29f17c 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -21,7 +21,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import WorkspaceCompanyCardPageEmptyState from './WorkspaceCompanyCardPageEmptyState'; import WorkspaceCompanyCardsFeedPendingPage from './WorkspaceCompanyCardsFeedPendingPage'; import WorkspaceCompanyCardsList from './WorkspaceCompanyCardsList'; @@ -43,7 +42,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const [cardFeeds, , defaultFeed] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; - const [cardsList, cardsListMetadata] = useCardsList(selectedFeed); + const [cardsList] = useCardsList(selectedFeed); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const hasNoAssignedCard = Object.keys(cardsList ?? {}).length === 0; @@ -60,7 +59,6 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); const isLoading = !isOffline && !cardFeeds; - const isLoadingCardsList = (!isOffline && isLoadingOnyxValue(cardsListMetadata)); const isGB = countryByIp === CONST.COUNTRY.GB; const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard); @@ -115,12 +113,10 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { {isFeedAdded && !isPending && ( )} From 70717ff424ca58aa1864d49909bac0531cd96e01 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 21:29:00 -0500 Subject: [PATCH 40/48] prettier --- .../workspace/companyCards/WorkspaceCompanyCardsList.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index c267555e6c84..3351987c9cac 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -6,18 +6,17 @@ import type {OnyxEntry} from 'react-native-onyx'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {PressableWithFeedback} from '@components/Pressable'; import SearchBar from '@components/SearchBar'; -import Text from '@components/Text'; import TableRowSkeleton from '@components/Skeletons/TableRowSkeleton'; +import Text from '@components/Text'; import useCardFeeds from '@hooks/useCardFeeds'; import useCardsList from '@hooks/useCardsList'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSearchResults from '@hooks/useSearchResults'; import useThemeStyles from '@hooks/useThemeStyles'; -import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -import useNetwork from '@hooks/useNetwork'; import { filterCardsByPersonalDetails, getCardsByCardholderName, @@ -32,6 +31,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import WorkspaceCompanyCardsFeedAddedEmptyPage from './WorkspaceCompanyCardsFeedAddedEmptyPage'; import WorkspaceCompanyCardsListRow from './WorkspaceCompanyCardsListRow'; From bb35bf7e0aebce4087b2ce8456c70b8713c5ca54 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 21:34:14 -0500 Subject: [PATCH 41/48] fix lint --- src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx | 3 +-- src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 3351987c9cac..234ee19c72c8 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -2,7 +2,6 @@ import type {FlashListRef, ListRenderItemInfo} from '@shopify/flash-list'; import {FlashList} from '@shopify/flash-list'; import React, {useCallback, useMemo, useRef} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {PressableWithFeedback} from '@components/Pressable'; import SearchBar from '@components/SearchBar'; @@ -30,7 +29,7 @@ import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx'; +import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import WorkspaceCompanyCardsFeedAddedEmptyPage from './WorkspaceCompanyCardsFeedAddedEmptyPage'; import WorkspaceCompanyCardsListRow from './WorkspaceCompanyCardsListRow'; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index b54c2e29f17c..4c3e17960153 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -20,7 +20,6 @@ import {openPolicyCompanyCardsFeed, openPolicyCompanyCardsPage} from '@userActio import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; import WorkspaceCompanyCardPageEmptyState from './WorkspaceCompanyCardPageEmptyState'; import WorkspaceCompanyCardsFeedPendingPage from './WorkspaceCompanyCardsFeedPendingPage'; import WorkspaceCompanyCardsList from './WorkspaceCompanyCardsList'; @@ -39,7 +38,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false}); const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); - const [cardFeeds, , defaultFeed] = useCardFeeds(policyID); + const [cardFeeds] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; const [cardsList] = useCardsList(selectedFeed); From 1c0e23f9f788cb2b165e5e92fed9393a1e417e58 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 21:42:12 -0500 Subject: [PATCH 42/48] remove unused method --- .../WorkspaceCompanyCardsList.tsx | 3 ++- .../members/WorkspaceMemberDetailsPage.tsx | 24 ------------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 234ee19c72c8..a283675e16ec 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -63,6 +63,7 @@ function WorkspaceCompanyCardsList({selectedFeed, policyID, onAssignCard, isAssi const [personalDetails, personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const isLoadingPersonalDetails = !isOffline && isLoadingOnyxValue(personalDetailsMetadata); const isLoadingCardsTableData = isLoadingCardsList || isLoadingPersonalDetails; + const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true}); const policy = usePolicy(policyID); @@ -214,7 +215,7 @@ function WorkspaceCompanyCardsList({selectedFeed, policyID, onAssignCard, isAssi ); - // Show empty state when there are no cards (but not when loading) + // Show empty state when there are no cards if (!cards?.length && !isLoadingCardsTableData) { return ( { - if (isAccountLocked) { - showLockedAccountModal(); - return; - } - - if (hasMultipleFeeds) { - Navigation.navigate(ROUTES.WORKSPACE_MEMBER_NEW_CARD.getRoute(policyID, accountID)); - return; - } - const activeRoute = Navigation.getActiveRoute(); - - setIssueNewCardStepAndData({ - step: CONST.EXPENSIFY_CARD.STEP.CARD_TYPE, - data: { - assigneeEmail: memberLogin, - }, - isEditing: false, - isChangeAssigneeDisabled: true, - policyID, - }); - Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID, activeRoute)); - }, [accountID, hasMultipleFeeds, memberLogin, policyID, isAccountLocked, showLockedAccountModal]); - const startChangeOwnershipFlow = useCallback(() => { clearWorkspaceOwnerChangeFlow(policyID); Navigation.navigate(ROUTES.WORKSPACE_OWNER_CHANGE_CHECK.getRoute(policyID, accountID, 'amountOwed' as ValueOf)); From a7bfe9db6370adea3fefeed764d94dcc389149ff Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 21:52:16 -0500 Subject: [PATCH 43/48] remove setIssueNewCardStepAndData --- src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 0870a2fd06b1..87a1524c4907 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -54,7 +54,6 @@ import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import variables from '@styles/variables'; -import {setIssueNewCardStepAndData} from '@userActions/Card'; import {clearWorkspaceOwnerChangeFlow, isApprover as isApproverUserAction, openPolicyMemberProfilePage, removeMembers} from '@userActions/Policy/Member'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; From 06ce70e5a0f414a9f5bd5c4d571f163b46fd0772 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 21:55:28 -0500 Subject: [PATCH 44/48] fix react compiler error --- .../workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 4c3e17960153..37f093779561 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import ActivityIndicator from '@components/ActivityIndicator'; import DecisionModal from '@components/DecisionModal'; import useAssignCard from '@hooks/useAssignCard'; @@ -52,9 +52,9 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const isFeedAdded = !isPending && !isNoFeed; const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); - const fetchCompanyCards = useCallback(() => { + const fetchCompanyCards = () => { openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID); - }, [policyID, domainOrWorkspaceAccountID]); + }; const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); const isLoading = !isOffline && !cardFeeds; From cdedee68e763a7a4ca548848d6ba6ba88683d98e Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Wed, 17 Dec 2025 22:45:07 -0500 Subject: [PATCH 45/48] more linting --- .../workspace/companyCards/WorkspaceCompanyCardsList.tsx | 4 ++-- .../workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index a283675e16ec..6fd16d979b97 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -96,7 +96,7 @@ function WorkspaceCompanyCardsList({selectedFeed, policyID, onAssignCard, isAssi ({item: cardName, index}: ListRenderItemInfo) => { const assignedCard = Object.values(assignedCards ?? {}).find((card) => card.cardName === cardName); - const customCardName = customCardNames?.[assignedCard?.cardID ?? CONST.DEFAULT_NUMBER_ID]; + const customCardName = assignedCard?.cardID ? customCardNames?.[assignedCard.cardID] : undefined; const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; @@ -134,7 +134,7 @@ function WorkspaceCompanyCardsList({selectedFeed, policyID, onAssignCard, isAssi > {({hovered}) => ( { fetchCompanyCards(); - }, [fetchCompanyCards]); + }, [policyID, domainOrWorkspaceAccountID]); useEffect(() => { if (isLoading || !feed || isPending) { From 2296422742ccad6c0ff0871a9f96a627ec621752 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Thu, 18 Dec 2025 10:07:01 -0500 Subject: [PATCH 46/48] Update src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx Co-authored-by: Hans --- src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 421e3b9b29bd..67837e662aa3 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -69,7 +69,7 @@ function WorkspaceCompanyCardsList({selectedFeed, policyID, onAssignCard, isAssi const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true}); const policy = usePolicy(policyID); - const cardListTyped: Record | undefined = (cardsList as {cardList?: Record})?.cardList; + const cardListTyped: Record | undefined = (cardsList as {cardList?: Record})?.cardList ?? {}; const assignedCards = Object.fromEntries(Object.entries(cardsList ?? {}).filter(([key]) => key !== 'cardList')) as Record; const [cardFeeds] = useCardFeeds(policyID); From f5ce09c4daf3ba4906008efbd9ea6a4c08da0d43 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Thu, 18 Dec 2025 11:19:11 -0500 Subject: [PATCH 47/48] fix flashing assign card screen --- .../companyCards/WorkspaceCompanyCardsPage.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 69ead9d8134e..b0bf0bd30452 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -20,6 +20,7 @@ import {openPolicyCompanyCardsFeed, openPolicyCompanyCardsPage} from '@userActio import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import WorkspaceCompanyCardPageEmptyState from './WorkspaceCompanyCardPageEmptyState'; import WorkspaceCompanyCardsFeedPendingPage from './WorkspaceCompanyCardsFeedPendingPage'; import WorkspaceCompanyCardsList from './WorkspaceCompanyCardsList'; @@ -41,7 +42,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const [cardFeeds] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; - const [cardsList] = useCardsList(selectedFeed); + const [cardsList, cardsListMetadata] = useCardsList(selectedFeed); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const hasNoAssignedCard = Object.keys(cardsList ?? {}).length === 0; @@ -52,17 +53,16 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const isFeedAdded = !isPending && !isNoFeed; const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); - const fetchCompanyCards = () => { - openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID); - }; - const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); - const isLoading = !isOffline && !cardFeeds; + const {isOffline} = useNetwork({ + onReconnect: () => openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID), + }); + const isLoading = !isOffline && (!cardFeeds || (isFeedAdded && isLoadingOnyxValue(cardsListMetadata))); const isGB = countryByIp === CONST.COUNTRY.GB; const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard); useEffect(() => { - fetchCompanyCards(); + openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID); }, [policyID, domainOrWorkspaceAccountID]); useEffect(() => { From 799dfe6db5bb21cb50329e77f26fe8cbe1ae9fa0 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Thu, 18 Dec 2025 11:23:59 -0500 Subject: [PATCH 48/48] Update src/ROUTES.ts Co-authored-by: Hans --- src/ROUTES.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index def28bf01fb5..7c051d6fba40 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2172,7 +2172,7 @@ const ROUTES = { getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/select-feed` as const, }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD: { - route: 'workspaces/:policyID/company-cards/:feed/assign-card/:cardID?', + route: 'workspaces/:policyID/company-cards/:feed/assign-card/:cardID', getRoute: (params: WorkspaceCompanyCardsAssignCardParams, backTo?: string) => // eslint-disable-next-line no-restricted-syntax -- Legacy route generation