diff --git a/src/components/ReportActionItem/IssueCardMessage.tsx b/src/components/ReportActionItem/IssueCardMessage.tsx index 292b010cd851..3bb8842195e3 100644 --- a/src/components/ReportActionItem/IssueCardMessage.tsx +++ b/src/components/ReportActionItem/IssueCardMessage.tsx @@ -3,7 +3,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import RenderHTML from '@components/RenderHTML'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -12,22 +11,24 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ReportAction} from '@src/types/onyx'; +import type OriginalMessage from '@src/types/onyx/OriginalMessage'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type IssueCardMessageProps = { action: OnyxEntry; }; +type IssueNewCardOriginalMessage = OriginalMessage< + typeof CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS | typeof CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED | typeof CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL +>; + function IssueCardMessage({action}: IssueCardMessageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const {environmentURL} = useEnvironment(); - // TODO: now mocking accountID with current user accountID instead of action.message.assigneeAccountID - const personalData = useCurrentUserPersonalDetails(); const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS); - // TODO: now mocking accountID with current user accountID instead of action.message.assigneeAccountID - const assignee = ``; + const assignee = ``; const link = `${translate('cardPage.expensifyCard')}`; const noMailingAddress = action?.actionName === CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS && isEmptyObject(privatePersonalDetails?.address); diff --git a/src/libs/API/parameters/RequestExpensifyCardLimitIncreaseParams.ts b/src/libs/API/parameters/RequestExpensifyCardLimitIncreaseParams.ts index 6e118f2a1c06..f50bf221ad09 100644 --- a/src/libs/API/parameters/RequestExpensifyCardLimitIncreaseParams.ts +++ b/src/libs/API/parameters/RequestExpensifyCardLimitIncreaseParams.ts @@ -1,6 +1,6 @@ type RequestExpensifyCardLimitIncreaseParams = { authToken: string | null | undefined; - settlementBankAccountID: string; + settlementBankAccountID: number; }; export default RequestExpensifyCardLimitIncreaseParams; diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts index 59b24453ca0a..a09baf7109b7 100644 --- a/src/libs/actions/Card.ts +++ b/src/libs/actions/Card.ts @@ -17,6 +17,7 @@ import type { import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import * as NetworkStore from '@libs/Network/NetworkStore'; +import * as PolicyUtils from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Card} from '@src/types/onyx'; @@ -253,10 +254,11 @@ function updateSettlementFrequency(workspaceAccountID: number, settlementFrequen API.write(WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_FREQUENCY, parameters, {optimisticData, successData, failureData}); } -function updateSettlementAccount(workspaceAccountID: number, domainName: string, settlementBankAccountID?: number, currentSettlementBankAccountID?: number) { +function updateSettlementAccount(workspaceAccountID: number, policyID: string, settlementBankAccountID?: number, currentSettlementBankAccountID?: number) { if (!settlementBankAccountID) { return; } + const domainName = PolicyUtils.getDomainNameForPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { @@ -296,6 +298,13 @@ function updateSettlementAccount(workspaceAccountID: number, domainName: string, API.write(WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_ACCOUNT, parameters, {optimisticData, successData, failureData}); } +function getCardDefaultName(userName?: string) { + if (!userName) { + return ''; + } + return `${userName}'s Card`; +} + function setIssueNewCardStepAndData({data, isEditing, step}: IssueNewCardFlowData) { Onyx.merge(ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD, {data, isEditing, currentStep: step}); } @@ -706,5 +715,6 @@ export { updateExpensifyCardLimitType, updateSelectedFeed, deactivateCard, + getCardDefaultName, }; export type {ReplacementReason}; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 3ccbb7873cef..abf906d66a4e 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -2151,7 +2151,11 @@ function openDraftWorkspaceRequest(policyID: string) { API.read(READ_COMMANDS.OPEN_DRAFT_WORKSPACE_REQUEST, params); } -function requestExpensifyCardLimitIncrease(settlementBankAccountID: string) { +function requestExpensifyCardLimitIncrease(settlementBankAccountID?: number) { + if (!settlementBankAccountID) { + return; + } + const authToken = NetworkStore.getAuthToken(); const params: RequestExpensifyCardLimitIncreaseParams = { diff --git a/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx b/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx index 1d2468e2e4e4..c274adbc779d 100644 --- a/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx +++ b/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx @@ -10,13 +10,16 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as AccountingUtils from '@libs/AccountingUtils'; import {getLastFourDigits} from '@libs/BankAccountUtils'; +import * as CardUtils from '@libs/CardUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; +import * as Card from '@userActions/Card'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type ReconciliationAccountSettingsPageProps = StackScreenProps; @@ -38,18 +41,23 @@ function ReconciliationAccountSettingsPage({route}: ReconciliationAccountSetting const settlementAccountEnding = getLastFourDigits(bankAccountNumber); const sections = useMemo(() => { - const data = Object.values(bankAccountList ?? {}).map((bankAccount) => ({ + if (!bankAccountList || isEmptyObject(bankAccountList)) { + return []; + } + const eligibleBankAccounts = CardUtils.getEligibleBankAccountsForCard(bankAccountList); + + const data = eligibleBankAccounts.map((bankAccount) => ({ text: bankAccount.title, value: bankAccount.accountData?.bankAccountID, keyForList: bankAccount.accountData?.bankAccountID?.toString(), - isSelected: bankAccount.accountData?.bankAccountID === selectedBankAccount?.accountData?.bankAccountID, + isSelected: bankAccount.accountData?.bankAccountID === paymentBankAccountID, })); return [{data}]; - }, [bankAccountList, selectedBankAccount]); + }, [bankAccountList, paymentBankAccountID]); - const selectBankAccount = () => { - // TODO: add API call when it's implemented https://github.com/Expensify/Expensify/issues/407836 - // Navigation.goBack(); + const selectBankAccount = (newBankAccountID?: number) => { + Card.updateSettlementAccount(workspaceAccountID, policyID, newBankAccountID, paymentBankAccountID); + Navigation.goBack(); }; return ( @@ -74,9 +82,9 @@ function ReconciliationAccountSettingsPage({route}: ReconciliationAccountSetting selectBankAccount(value)} ListItem={RadioListItem} - initiallyFocusedOptionKey={selectedBankAccount?.accountData?.bankAccountID?.toString()} + initiallyFocusedOptionKey={paymentBankAccountID.toString()} /> ); diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx index c0566d71f686..0febd5f4ada0 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx @@ -19,8 +19,10 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import getClickedTargetLocation from '@libs/getClickedTargetLocation'; +import * as PolicyUtils from '@libs/PolicyUtils'; import type {FullScreenNavigatorParamList} from '@navigation/types'; import variables from '@styles/variables'; +import * as Policy from '@userActions/Policy/Policy'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -50,9 +52,12 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr const [anchorPosition, setAnchorPosition] = useState({top: 0, left: 0}); const anchorRef = useRef(null); + const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(route.params.policyID); const policyCurrency = useMemo(() => policy?.outputCurrency ?? CONST.CURRENCY.USD, [policy]); - // TODO: instead of the first bankAccount on the list get settlementBankAccountID from the private_expensifyCardSettings NVP and check if that is connected via Plaid. - const isConnectedWithPlaid = useMemo(() => !!Object.values(bankAccountList ?? {})[0]?.accountData?.additionalData?.plaidAccountID, [bankAccountList]); + const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`); + const paymentBankAccountID = cardSettings?.paymentBankAccountID; + + const isConnectedWithPlaid = useMemo(() => !!bankAccountList?.[paymentBankAccountID ?? 0]?.accountData?.additionalData?.plaidAccountID, [bankAccountList, paymentBankAccountID]); useEffect(() => { if (!anchorRef.current || !isVisible) { @@ -69,8 +74,7 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr }, [isVisible, windowWidth]); const requestLimitIncrease = () => { - // TODO: uncomment when RequestExpensifyCardLimitIncrease API call is supported - // Policy.requestExpensifyCardLimitIncrease(settlementBankAccountID); + Policy.requestExpensifyCardLimitIncrease(cardSettings?.paymentBankAccountID); setVisible(false); Report.navigateToConciergeChat(); }; diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx index 78060487d35f..e121de7fd159 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx @@ -39,8 +39,7 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA const handleSelectBankAccount = (value?: number) => { Card.configureExpensifyCardsForPolicy(policyID, value); - const domainName = PolicyUtils.getDomainNameForPolicy(policyID); - Card.updateSettlementAccount(workspaceAccountID, domainName, value); + Card.updateSettlementAccount(workspaceAccountID, policyID, value); Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID)); }; diff --git a/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx index 298d0c1c54f9..9d5cc4068365 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx @@ -78,8 +78,7 @@ function WorkspaceSettlementAccountPage({route}: WorkspaceSettlementAccountPageP }, [eligibleBankAccounts, paymentBankAccountID, styles, translate]); const updateSettlementAccount = (value: number) => { - const domainName = PolicyUtils.getDomainNameForPolicy(policyID); - Card.updateSettlementAccount(workspaceAccountID, domainName, value, paymentBankAccountID); + Card.updateSettlementAccount(workspaceAccountID, policyID, value, paymentBankAccountID); Navigation.goBack(); }; diff --git a/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx b/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx index 52b3c58cd7da..e89db5cb88db 100644 --- a/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx +++ b/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx @@ -23,6 +23,7 @@ import * as Card from '@userActions/Card'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; +import type {IssueNewCardData} from '@src/types/onyx/Card'; const MINIMUM_MEMBER_TO_SHOW_SEARCH = 8; @@ -42,11 +43,18 @@ function AssigneeStep({policy}: AssigneeStepProps) { const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); const submit = (assignee: ListItem) => { + const data: Partial = { + assigneeEmail: assignee?.login ?? '', + }; + + if (isEditing && issueNewCard?.data?.cardTitle === Card.getCardDefaultName(PersonalDetailsUtils.getUserNameByEmail(issueNewCard?.data?.assigneeEmail, 'firstName'))) { + // If the card title is the default card title, update it with the new assignee's name + data.cardTitle = Card.getCardDefaultName(PersonalDetailsUtils.getUserNameByEmail(assignee?.login ?? '', 'firstName')); + } + Card.setIssueNewCardStepAndData({ step: isEditing ? CONST.EXPENSIFY_CARD.STEP.CONFIRMATION : CONST.EXPENSIFY_CARD.STEP.CARD_TYPE, - data: { - assigneeEmail: assignee?.login ?? '', - }, + data, isEditing: false, }); }; diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index c18992923c21..34823d01e1d7 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -510,6 +510,14 @@ type OriginalMessageIntegrationSyncFailed = { errorMessage: string; }; +/** + * Original message for CARD_ISSUED, CARD_MISSING_ADDRESS, and CARD_ISSUED_VIRTUAL actions + */ +type OriginalMessageExpensifyCard = { + /** The id of the user the card was assigned to */ + assigneeAccountID: number; +}; + /** The map type of original message */ type OriginalMessageMap = { /** */ @@ -633,11 +641,11 @@ type OriginalMessageMap = { /** */ [CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENT_SETUP_REQUESTED]: never; /** */ - [CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED]: never; + [CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED]: OriginalMessageExpensifyCard; /** */ - [CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS]: never; + [CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS]: OriginalMessageExpensifyCard; /** */ - [CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL]: never; + [CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL]: OriginalMessageExpensifyCard; /** */ [CONST.REPORT.ACTIONS.TYPE.INTEGRATION_SYNC_FAILED]: OriginalMessageIntegrationSyncFailed; } & OldDotOriginalMessageMap & {