Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Guided Setup Stage 3] Update the Pay w/ Expensify selectors #45264

Merged
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
15c5210
update the pay w/ expensify selectors logic
cdOut Jul 11, 2024
ecc399a
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Jul 29, 2024
f0a4845
update completeEngagement in AddPaymentMethodMenu with inviteType che…
cdOut Jul 29, 2024
7cea50d
fix lint, add useCallback dependency
cdOut Jul 29, 2024
aa98fd4
fix prettier
cdOut Jul 29, 2024
2aaa0be
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Jul 29, 2024
037f433
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Aug 14, 2024
3f00dc6
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Aug 23, 2024
29eda5e
refactor CONST onboarding messages
cdOut Aug 23, 2024
75e336d
correct naming for onboardingPurpose param
cdOut Aug 23, 2024
83d33a1
fix prettier and undefined check
cdOut Aug 23, 2024
9ba23dd
add completePaymentOnboarding method to all necessary components
cdOut Aug 26, 2024
29aa7f9
fix prettier errors
cdOut Aug 26, 2024
fac02f0
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Aug 27, 2024
83e830a
fix lint errors and remove unused imports
cdOut Aug 27, 2024
8f2ad9b
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Sep 2, 2024
516aec9
move PAYMENT_SELECTED to IOU section in CONST
cdOut Sep 2, 2024
9031e1e
correct typescript typing
cdOut Sep 2, 2024
bd147f5
fix lint and prettier
cdOut Sep 2, 2024
fe9662b
add JSDoc to explain onboardingPayment function
cdOut Sep 2, 2024
2d83339
fix typescript errors caused by as const assertion
cdOut Sep 2, 2024
89ccca8
set ONYXKEYS import as type
cdOut Sep 2, 2024
178cd3a
fix remaining lint errors with leftover assertions
cdOut Sep 2, 2024
fbf9e17
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Sep 2, 2024
2480834
change onboarding choice for personal invoice and refactor code around
cdOut Sep 4, 2024
83d2239
fix lint and prettier
cdOut Sep 4, 2024
2c62b38
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Sep 4, 2024
732efbf
remove changes from AddPaymentMethodMenu
cdOut Sep 4, 2024
74aaacb
remove unnecessary prettier changes
cdOut Sep 4, 2024
5fd2f7e
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Sep 4, 2024
d631958
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Sep 5, 2024
7a6a1ac
Merge branch 'main' into @cdOut/guided-3-pay-selectors
cdOut Sep 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 96 additions & 50 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dateSubtract from 'date-fns/sub';
import Config from 'react-native-config';
import * as KeyCommand from 'react-native-key-command';
import type {ValueOf} from 'type-fest';
import type {Video} from './libs/actions/Report';
import BankAccount from './libs/models/BankAccount';
import * as Url from './libs/Url';
import SCREENS from './SCREENS';
Expand Down Expand Up @@ -64,16 +65,91 @@ const chatTypes = {
// Explicit type annotation is required
const cardActiveStates: number[] = [2, 3, 4, 7];

const onboardingChoices = {
const selectableOnboardingChoices = {
PERSONAL_SPEND: 'newDotPersonalSpend',
MANAGE_TEAM: 'newDotManageTeam',
EMPLOYER: 'newDotEmployer',
CHAT_SPLIT: 'newDotSplitChat',
LOOKING_AROUND: 'newDotLookingAround',
};

const backendOnboardingChoices = {
SUBMIT: 'newDotSubmit',
};

const onboardingChoices = {
...selectableOnboardingChoices,
...backendOnboardingChoices,
};

const onboardingEmployerOrSubmitMessage = {
message: 'Getting paid back is as easy as sending a message. Let’s go over the basics.',
video: {
url: `${CLOUDFRONT_URL}/videos/guided-setup-get-paid-back-v2.mp4`,
thumbnailUrl: `${CLOUDFRONT_URL}/images/guided-setup-get-paid-back.jpg`,
duration: 55,
width: 1280,
height: 960,
},
tasks: [
{
type: 'submitExpense',
autoCompleted: false,
title: 'Submit an expense',
description:
'*Submit an expense* by entering an amount or scanning a receipt.\n' +
'\n' +
'Here’s how to submit an expense:\n' +
'\n' +
'1. Click the green *+* button.\n' +
'2. Choose *Submit expense*.\n' +
'3. Enter an amount or scan a receipt.\n' +
'4. Add your reimburser to the request.\n' +
'\n' +
'Then, send your request and wait for that sweet “Cha-ching!” when it’s complete.',
},
{
type: 'enableWallet',
autoCompleted: false,
title: 'Enable your wallet',
description:
'You’ll need to *enable your Expensify Wallet* to get paid back. Don’t worry, it’s easy!\n' +
'\n' +
'Here’s how to set up your wallet:\n' +
'\n' +
'1. Click your profile picture.\n' +
'2. Click *Wallet* > *Enable wallet*.\n' +
'3. Connect your bank account.\n' +
'\n' +
'Once that’s done, you can request money from anyone and get paid back right into your personal bank account.',
},
],
};

type OnboardingPurposeType = ValueOf<typeof onboardingChoices>;

const onboardingInviteTypes = {
IOU: 'iou',
INVOICE: 'invoice',
CHAT: 'chat',
};

type OnboardingInviteType = ValueOf<typeof onboardingInviteTypes>;

type OnboardingTaskType = {
type: string;
autoCompleted: boolean;
title: string;
description: string | ((params: Partial<{adminsRoomLink: string; workspaceLink: string}>) => string);
};

type OnboardingMessageType = {
message: string;
video?: Video;
tasks: OnboardingTaskType[];
type?: string;
};

const CONST = {
HEIC_SIGNATURES: [
'6674797068656963', // 'ftypheic' - Indicates standard HEIC file
Expand Down Expand Up @@ -1906,6 +1982,11 @@ const CONST = {
BUSINESS_BANK_ACCOUNT: 'businessBankAccount',
},

PAYMENT_SELECTED: {
BBA: 'BBA',
PBA: 'PBA',
},

PAYMENT_METHOD_ID_KEYS: {
DEBIT_CARD: 'fundID',
BANK_ACCOUNT: 'bankAccountID',
Expand Down Expand Up @@ -1980,6 +2061,10 @@ const CONST = {
ACCESS_VARIANTS: {
CREATE: 'create',
},
PAYMENT_SELECTED: {
BBA: 'BBA',
PBA: 'PBA',
},
},

GROWL: {
Expand Down Expand Up @@ -4195,6 +4280,8 @@ const CONST = {

ONBOARDING_INTRODUCTION: 'Let’s get you set up 🔧',
ONBOARDING_CHOICES: {...onboardingChoices},
SELECTABLE_ONBOARDING_CHOICES: {...selectableOnboardingChoices},
ONBOARDING_INVITE_TYPES: {...onboardingInviteTypes},
ACTIONABLE_TRACK_EXPENSE_WHISPER_MESSAGE: 'What would you like to do with this expense?',
ONBOARDING_CONCIERGE: {
[onboardingChoices.EMPLOYER]:
Expand Down Expand Up @@ -4237,49 +4324,8 @@ const CONST = {
},

ONBOARDING_MESSAGES: {
[onboardingChoices.EMPLOYER]: {
message: 'Getting paid back is as easy as sending a message. Let’s go over the basics.',
video: {
url: `${CLOUDFRONT_URL}/videos/guided-setup-get-paid-back-v2.mp4`,
thumbnailUrl: `${CLOUDFRONT_URL}/images/guided-setup-get-paid-back.jpg`,
duration: 55,
width: 1280,
height: 960,
},
tasks: [
{
type: 'submitExpense',
autoCompleted: false,
title: 'Submit an expense',
description:
'*Submit an expense* by entering an amount or scanning a receipt.\n' +
'\n' +
'Here’s how to submit an expense:\n' +
'\n' +
'1. Click the green *+* button.\n' +
'2. Choose *Submit expense*.\n' +
'3. Enter an amount or scan a receipt.\n' +
'4. Add your reimburser to the request.\n' +
'\n' +
'Then, send your request and wait for that sweet “Cha-ching!” when it’s complete.',
},
{
type: 'enableWallet',
autoCompleted: false,
title: 'Enable your wallet',
description:
'You’ll need to *enable your Expensify Wallet* to get paid back. Don’t worry, it’s easy!\n' +
'\n' +
'Here’s how to set up your wallet:\n' +
'\n' +
'1. Click your profile picture.\n' +
'2. Click *Wallet* > *Enable wallet*.\n' +
'3. Connect your bank account.\n' +
'\n' +
'Once that’s done, you can request money from anyone and get paid back right into your personal bank account.',
},
],
},
[onboardingChoices.EMPLOYER]: {...onboardingEmployerOrSubmitMessage},
[onboardingChoices.SUBMIT]: {...onboardingEmployerOrSubmitMessage},
[onboardingChoices.MANAGE_TEAM]: {
message: 'Here are some important tasks to help get your team’s expenses under control.',
video: {
Expand Down Expand Up @@ -4308,7 +4354,7 @@ const CONST = {
type: 'meetGuide',
autoCompleted: false,
title: 'Meet your setup specialist',
description: ({adminsRoomLink}: {adminsRoomLink: string}) =>
description: ({adminsRoomLink}) =>
`Meet your setup specialist, who can answer any questions as you get started with Expensify. Yes, a real human!\n` +
'\n' +
`Chat with the specialist in your [#admins room](${adminsRoomLink}).`,
Expand All @@ -4317,7 +4363,7 @@ const CONST = {
type: 'setupCategories',
autoCompleted: false,
title: 'Set up categories',
description: ({workspaceLink}: {workspaceLink: string}) =>
description: ({workspaceLink}) =>
'*Set up categories* so your team can code expenses for easy reporting.\n' +
'\n' +
'Here’s how to set up categories:\n' +
Expand All @@ -4334,7 +4380,7 @@ const CONST = {
type: 'addExpenseApprovals',
autoCompleted: false,
title: 'Add expense approvals',
description: ({workspaceLink}: {workspaceLink: string}) =>
description: ({workspaceLink}) =>
'*Add expense approvals* to review your team’s spend and keep it under control.\n' +
'\n' +
'Here’s how to add expense approvals:\n' +
Expand All @@ -4351,7 +4397,7 @@ const CONST = {
type: 'inviteTeam',
autoCompleted: false,
title: 'Invite your team',
description: ({workspaceLink}: {workspaceLink: string}) =>
description: ({workspaceLink}) =>
'*Invite your team* to Expensify so they can start tracking expenses today.\n' +
'\n' +
'Here’s how to invite your team:\n' +
Expand Down Expand Up @@ -4459,7 +4505,7 @@ const CONST = {
"Expensify is best known for expense and corporate card management, but we do a lot more than that. Let me know what you're interested in and I'll help get you started.",
tasks: [],
},
},
} satisfies Record<OnboardingPurposeType, OnboardingMessageType>,

REPORT_FIELD_TITLE_FIELD_ID: 'text_title',

Expand Down Expand Up @@ -5499,6 +5545,6 @@ type FeedbackSurveyOptionID = ValueOf<Pick<ValueOf<typeof CONST.FEEDBACK_SURVEY_
type SubscriptionType = ValueOf<typeof CONST.SUBSCRIPTION.TYPE>;
type CancellationType = ValueOf<typeof CONST.CANCELLATION_TYPE>;

export type {Country, IOUAction, IOUType, RateAndUnit, OnboardingPurposeType, IOURequestType, SubscriptionType, FeedbackSurveyOptionID, CancellationType};
export type {Country, IOUAction, IOUType, RateAndUnit, OnboardingPurposeType, IOURequestType, SubscriptionType, FeedbackSurveyOptionID, CancellationType, OnboardingInviteType};

export default CONST;
12 changes: 10 additions & 2 deletions src/components/AddPaymentMethodMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {AnchorPosition} from '@src/styles';
Expand Down Expand Up @@ -108,6 +109,7 @@ function AddPaymentMethodMenu({
text: translate('common.personalBankAccount'),
icon: Expensicons.Bank,
onSelected: () => {
IOU.completePaymentOnboarding(CONST.IOU.PAYMENT_SELECTED.PBA);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add completePaymentOnboarding to

onItemSelected(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT);

Copy link
Contributor Author

@cdOut cdOut Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DylanDylann AddPaymentMethodMenu is implemented in two separate components so I'd have to add the same code twice. How about I create a wrapper function such as this:

const onPaymentItemSelected = (paymentMethod: PaymentMethod) => {
    IOU.completePaymentOnboarding(paymentMethod);
    onItemSelected(paymentMethod);
}

And replace the part you've selected with onPaymentItemSelected(paymentMethod) ?

Copy link
Contributor

@DylanDylann DylanDylann Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cdOut You mean we should create a new wrapper function called onPaymentItemSelected and use it in AddPaymentMethodMenu. It's fine to me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do that but it won't change the functionality, just move the code around. You can test it in the meantime.

onItemSelected(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT);
},
},
Expand All @@ -118,7 +120,10 @@ function AddPaymentMethodMenu({
{
text: translate('common.businessBankAccount'),
icon: Expensicons.Building,
onSelected: () => onItemSelected(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT),
onSelected: () => {
IOU.completePaymentOnboarding(CONST.IOU.PAYMENT_SELECTED.BBA);
onItemSelected(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT);
},
},
]
: []),
Expand All @@ -127,7 +132,10 @@ function AddPaymentMethodMenu({
// {
// text: translate('common.debitCard'),
// icon: Expensicons.CreditCard,
// onSelected: () => onItemSelected(CONST.PAYMENT_METHODS.DEBIT_CARD),
// onSelected: () => {
// completePaymentOnboarding(CONST.IOU.PAYMENT_SELECTED.PBA);
// onItemSelected(CONST.PAYMENT_METHODS.DEBIT_CARD);
// },
// },
// ],
]}
Expand Down
2 changes: 2 additions & 0 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ function ReportPreview({
if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) {
setIsHoldMenuVisible(true);
} else if (chatReport && iouReport) {
const paymentSelected = type === CONST.IOU.PAYMENT_TYPE.VBBA ? CONST.IOU.PAYMENT_SELECTED.BBA : CONST.IOU.PAYMENT_SELECTED.PBA;
IOU.completePaymentOnboarding(paymentSelected);
if (ReportUtils.isInvoiceReport(iouReport)) {
IOU.payInvoice(type, chatReport, iouReport, payAsBusiness);
} else {
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/CompleteGuidedSetupParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type CompleteGuidedSetupParams = {
actorAccountID: number;
guidedSetupData: string;
engagementChoice: OnboardingPurposeType;
paymentSelected?: string;
};

export default CompleteGuidedSetupParams;
49 changes: 49 additions & 0 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ import isSearchTopmostCentralPane from '@libs/Navigation/isSearchTopmostCentralP
import Navigation from '@libs/Navigation/Navigation';
import * as NextStepUtils from '@libs/NextStepUtils';
import {rand64} from '@libs/NumberUtils';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PhoneNumber from '@libs/PhoneNumber';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportConnection from '@libs/ReportConnection';
import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, TransactionDetails} from '@libs/ReportUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as SessionUtils from '@libs/SessionUtils';
import * as SubscriptionUtils from '@libs/SubscriptionUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import {getCurrency, getTransaction} from '@libs/TransactionUtils';
Expand Down Expand Up @@ -272,6 +274,18 @@ Onyx.connect({
callback: (value) => (activePolicyID = value),
});

let introSelected: OnyxEntry<OnyxTypes.IntroSelected>;
Onyx.connect({
key: ONYXKEYS.NVP_INTRO_SELECTED,
callback: (value) => (introSelected = value),
});

let personalDetailsList: OnyxEntry<OnyxTypes.PersonalDetailsList>;
Onyx.connect({
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => (personalDetailsList = value),
});

/**
* Find the report preview action from given chat report and iou report
*/
Expand Down Expand Up @@ -7389,6 +7403,40 @@ function cancelPayment(expenseReport: OnyxEntry<OnyxTypes.Report>, chatReport: O
);
}

function completePaymentOnboarding(paymentSelected: ValueOf<typeof CONST.PAYMENT_SELECTED>) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a JSDoc explaining what the function does

const isInviteOnboardingComplete = introSelected?.isInviteOnboardingComplete ?? false;

if (isInviteOnboardingComplete || !introSelected?.choice) {
return;
}

const session = SessionUtils.getSession();

const personalDetailsListValues = Object.values(
OptionsListUtils.getPersonalDetailsForAccountIDs(session?.accountID ? [session.accountID] : [], personalDetailsList),
) as OnyxTypes.PersonalDetails[];
const personalDetails = personalDetailsListValues[0] ?? {};

let onboardingPurpose = introSelected.choice;
if (introSelected.inviteType === CONST.ONBOARDING_INVITE_TYPES.IOU && paymentSelected === CONST.IOU.PAYMENT_SELECTED.BBA) {
onboardingPurpose = CONST.ONBOARDING_CHOICES.MANAGE_TEAM;
}

if (introSelected.inviteType === CONST.ONBOARDING_INVITE_TYPES.INVOICE && paymentSelected !== CONST.IOU.PAYMENT_SELECTED.BBA) {
onboardingPurpose = CONST.ONBOARDING_CHOICES.SUBMIT;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data from BE maybe wrong in some cases. Please add two more condition

if (introSelected.inviteType === CONST.ONBOARDING_INVITE_TYPES.IOU && paymentSelected !== CONST.IOU.PAYMENT_SELECTED.BBA)
if (introSelected.inviteType === CONST.ONBOARDING_INVITE_TYPES.INVOICE && paymentSelected === CONST.IOU.PAYMENT_SELECTED.BBA

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would the data from backend be incorrect? What cases do you have in mind? Those two ifs you've provided are already handled automatically on the backend when creating a new account from an invite link.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I understand your point. The BE returned the wrong data in two cases which is the reason why we already implemented two previous conditions. My suggestion will help our change to be safer and make sure that the logic works well.

Anyway, It is not a bug. I will leave the final decision to you 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd leave it as is - let's not meddle with the data returned from backend more than we have to for the two cases listed in the design doc

Report.completeOnboarding(
onboardingPurpose,
CONST.ONBOARDING_MESSAGES[onboardingPurpose],
{
firstName: personalDetails.firstName ?? '',
lastName: personalDetails.lastName ?? '',
},
paymentSelected,
);
}

function payMoneyRequest(paymentType: PaymentMethodType, chatReport: OnyxTypes.Report, iouReport: OnyxTypes.Report, full = true) {
if (chatReport.policyID && SubscriptionUtils.shouldRestrictUserBillableActions(chatReport.policyID)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(chatReport.policyID));
Expand Down Expand Up @@ -8009,6 +8057,7 @@ export {
getIOURequestPolicyID,
initMoneyRequest,
navigateToStartStepIfScanFileCannotBeRead,
completePaymentOnboarding,
payInvoice,
payMoneyRequest,
putOnHold,
Expand Down
Loading
Loading