diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 77aeb8e0ecc3..46461edb5525 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -9,6 +9,7 @@ import type {TranslationPaths} from '@src/languages/types'; import type {OnyxValues} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx'; +import type {FilteredCardList} from '@src/types/onyx/Card'; import type {CompanyCardNicknames, CompanyFeeds} from '@src/types/onyx/CardFeeds'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -352,6 +353,16 @@ function getSelectedFeed(lastSelectedFeed: OnyxEntry, cardFeeds return lastSelectedFeed ?? defaultFeed; } +function getFilteredCardList(list?: WorkspaceCardsList) { + const {cardList, ...cards} = list ?? {}; + // We need to filter out cards which already has been assigned + return Object.fromEntries(Object.entries(cardList ?? {}).filter(([cardNumber]) => !Object.values(cards).find((card) => card.lastFourPAN && cardNumber.endsWith(card.lastFourPAN)))); +} + +function hasOnlyOneCardToAssign(list: FilteredCardList) { + return Object.keys(list).length === 1; +} + export { isExpensifyCard, isCorporateCard, @@ -378,4 +389,6 @@ export { getCorrectStepForSelectedBank, getCustomOrFormattedFeedName, removeExpensifyCardFromCompanyCards, + getFilteredCardList, + hasOnlyOneCardToAssign, }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index 031ac309e155..22b17496040e 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -13,12 +13,16 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import variables from '@styles/variables'; +import * as CompanyCards from '@userActions/CompanyCards'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {CompanyCardFeed} from '@src/types/onyx'; +import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; type WorkspaceCompanyCardsListHeaderButtonsProps = { /** Current policy id */ @@ -41,6 +45,36 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp const isCustomFeed = CardUtils.isCustomFeed(selectedFeed); const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; + const policy = PolicyUtils.getPolicy(policyID); + + const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`); + const filteredCardList = CardUtils.getFilteredCardList(list); + + const handleAssignCard = () => { + const data: Partial = { + bankName: selectedFeed, + }; + + let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.ASSIGNEE; + + if (Object.keys(policy?.employeeList ?? {}).length === 1) { + const userEmail = Object.keys(policy?.employeeList ?? {}).at(0) ?? ''; + data.email = userEmail; + const personalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(userEmail); + const memberName = personalDetails?.firstName ? personalDetails.firstName : personalDetails?.login; + data.cardName = `${memberName}'s card`; + currentStep = CONST.COMPANY_CARD.STEP.CARD; + + if (CardUtils.hasOnlyOneCardToAssign(filteredCardList)) { + currentStep = CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE; + data.cardNumber = Object.keys(filteredCardList).at(0); + data.encryptedCardNumber = Object.values(filteredCardList).at(0); + } + } + + CompanyCards.setAssignCardStepAndData({data, currentStep}); + Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed))); + }; return ( Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed))} + onPress={handleAssignCard} icon={Expensicons.Plus} text={translate('workspace.companyCards.assignCard')} style={shouldChangeLayout && styles.flex1} diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 20c51b882054..2fe757c4e36f 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -25,8 +25,10 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { const policyID = policy?.id ?? '-1'; useEffect(() => { - CompanyCards.setAssignCardStepAndData({data: {bankName: feed}}); - }, [feed]); + return () => { + CompanyCards.clearAssignCardStepAndData(); + }; + }, []); switch (currentStep) { case CONST.COMPANY_CARD.STEP.ASSIGNEE: @@ -52,8 +54,6 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { default: return ; } - - return ; } export default withPolicyAndFullscreenLoading(AssignCardFeedPage); diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index e8e8c81cba07..1b2819fc380c 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -13,6 +13,7 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as CardUtils from '@libs/CardUtils'; import {formatPhoneNumber} from '@libs/LocalePhoneNumber'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; @@ -35,6 +36,10 @@ function AssigneeStep({policy}: AssigneeStepProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); + const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policy?.id ?? '-1'); + + const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${assignCard?.data?.bankName ?? ''}`); + const filteredCardList = CardUtils.getFilteredCardList(list); const isEditing = assignCard?.isEditing; @@ -57,8 +62,10 @@ function AssigneeStep({policy}: AssigneeStepProps) { const personalDetail = PersonalDetailsUtils.getPersonalDetailByEmail(selectedMember); const memberName = personalDetail?.firstName ? personalDetail.firstName : personalDetail?.login; + const nextStep = CardUtils.hasOnlyOneCardToAssign(filteredCardList) ? CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE : CONST.COMPANY_CARD.STEP.CARD; + CompanyCards.setAssignCardStepAndData({ - currentStep: isEditing ? CONST.COMPANY_CARD.STEP.CONFIRMATION : CONST.COMPANY_CARD.STEP.CARD, + currentStep: isEditing ? CONST.COMPANY_CARD.STEP.CONFIRMATION : nextStep, data: { email: selectedMember, cardName: `${memberName}'s card`, @@ -69,7 +76,10 @@ function AssigneeStep({policy}: AssigneeStepProps) { const handleBackButtonPress = () => { if (isEditing) { - CompanyCards.setAssignCardStepAndData({currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, isEditing: false}); + CompanyCards.setAssignCardStepAndData({ + currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, + isEditing: false, + }); return; } Navigation.goBack(); diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index 4b07e7a220b8..47bcbbd3ed6d 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -45,11 +45,8 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) { const isEditing = assignCard?.isEditing; const assigneeDisplayName = PersonalDetailsUtils.getPersonalDetailByEmail(assignCard?.data?.email ?? '')?.displayName ?? ''; - const {cardList, ...cards} = list ?? {}; - // We need to filter out cards which already has been assigned - const filteredCardList = Object.fromEntries( - Object.entries(cardList ?? {}).filter(([cardNumber]) => !Object.values(cards).find((card) => card.lastFourPAN && cardNumber.endsWith(card.lastFourPAN))), - ); + const filteredCardList = CardUtils.getFilteredCardList(list); + const [cardSelected, setCardSelected] = useState(assignCard?.data?.encryptedCardNumber ?? ''); const [shouldShowError, setShouldShowError] = useState(false); diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index 79dcdd37ce1f..21e45516c95d 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -26,6 +26,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; +import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; type CardFeedListItem = ListItem & { /** Card feed value */ @@ -47,8 +48,12 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew const accountID = Number(route.params.accountID); const memberLogin = personalDetails?.[accountID]?.login ?? ''; + const memberName = personalDetails?.[accountID]?.firstName ? personalDetails?.[accountID]?.firstName : personalDetails?.[accountID]?.login; const availableCompanyCards = CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds); + const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`); + const filteredCardList = CardUtils.getFilteredCardList(list); + const handleSubmit = () => { if (!selectedFeed) { setShouldShowError(true); @@ -64,14 +69,26 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew }); Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))); } else { + const data: Partial = { + email: memberLogin, + bankName: selectedFeed, + cardName: `${memberName}'s card`, + }; + let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD; + + if (CardUtils.hasOnlyOneCardToAssign(filteredCardList)) { + currentStep = CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE; + data.cardNumber = Object.keys(filteredCardList).at(0); + data.encryptedCardNumber = Object.values(filteredCardList).at(0); + } CompanyCards.setAssignCardStepAndData({ - currentStep: CONST.COMPANY_CARD.STEP.CARD, - data: { - email: memberLogin, - }, + currentStep, + data, isEditing: false, }); - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))); + Navigation.setNavigationActionToMicrotaskQueue(() => + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))), + ); } }; diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts index 8894db2723d1..7d3d252dd86b 100644 --- a/src/types/onyx/Card.ts +++ b/src/types/onyx/Card.ts @@ -180,5 +180,8 @@ type WorkspaceCardsList = Record & { cardList?: Record; }; +/** Card list with only available card */ +type FilteredCardList = Record; + export default Card; -export type {ExpensifyCardDetails, CardList, IssueNewCard, IssueNewCardStep, IssueNewCardData, WorkspaceCardsList, CardLimitType}; +export type {ExpensifyCardDetails, CardList, IssueNewCard, IssueNewCardStep, IssueNewCardData, WorkspaceCardsList, CardLimitType, FilteredCardList};