Skip to content

Commit

Permalink
Merge pull request #34450 from rezkiy37/feature/28771-migrate-next-st…
Browse files Browse the repository at this point in the history
…eps-to-client

Migrate the next steps to client side
  • Loading branch information
mountiny authored Feb 9, 2024
2 parents 8b8522a + b9ac4a3 commit bb1fc0a
Show file tree
Hide file tree
Showing 7 changed files with 892 additions and 48 deletions.
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ const CONST = {
UNIX_EPOCH: '1970-01-01 00:00:00.000',
MAX_DATE: '9999-12-31',
MIN_DATE: '0001-01-01',
ORDINAL_DAY_OF_MONTH: 'do',
},
SMS: {
DOMAIN: '@expensify.sms',
Expand Down
4 changes: 2 additions & 2 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money

// The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on
const isWaitingForSubmissionFromCurrentUser = useMemo(
() => chatReport?.isOwnPolicyExpenseChat && !(policy.harvesting?.enabled ?? policy.isHarvestingEnabled),
[chatReport?.isOwnPolicyExpenseChat, policy.harvesting?.enabled, policy.isHarvestingEnabled],
() => chatReport?.isOwnPolicyExpenseChat && !policy.harvesting?.enabled,
[chatReport?.isOwnPolicyExpenseChat, policy.harvesting?.enabled],
);

const threeDotsMenuItems = [HeaderUtils.getPinMenuItem(moneyRequestReport)];
Expand Down
4 changes: 2 additions & 2 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ function ReportPreview({

// The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on
const isWaitingForSubmissionFromCurrentUser = useMemo(
() => chatReport?.isOwnPolicyExpenseChat && !(policy?.harvesting?.enabled ?? policy?.isHarvestingEnabled),
[chatReport?.isOwnPolicyExpenseChat, policy?.harvesting?.enabled, policy?.isHarvestingEnabled],
() => chatReport?.isOwnPolicyExpenseChat && !policy?.harvesting?.enabled,
[chatReport?.isOwnPolicyExpenseChat, policy?.harvesting?.enabled],
);

const getDisplayAmount = (): string => {
Expand Down
296 changes: 294 additions & 2 deletions src/libs/NextStepUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
import {format, lastDayOfMonth, setDate} from 'date-fns';
import Str from 'expensify-common/lib/str';
import Onyx from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Report, ReportNextStep} from '@src/types/onyx';
import type {Message} from '@src/types/onyx/ReportNextStep';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import type {EmptyObject} from '@src/types/utils/EmptyObject';
import DateUtils from './DateUtils';
import EmailUtils from './EmailUtils';
import * as PersonalDetailsUtils from './PersonalDetailsUtils';
import * as ReportUtils from './ReportUtils';

let currentUserAccountID = -1;
Onyx.connect({
key: ONYXKEYS.SESSION,
callback: (value) => {
if (!value) {
return;
}

currentUserAccountID = value?.accountID ?? -1;
},
});

function parseMessage(messages: Message[] | undefined) {
let nextStepHTML = '';
Expand All @@ -27,5 +50,274 @@ function parseMessage(messages: Message[] | undefined) {
return `<next-step>${formattedHtml}</next-step>`;
}

// eslint-disable-next-line import/prefer-default-export
export {parseMessage};
type BuildNextStepParameters = {
isPaidWithWallet?: boolean;
};

/**
* Generates an optimistic nextStep based on a current report status and other properties.
*
* @param report
* @param predictedNextStatus - a next expected status of the report
* @param parameters.isPaidWithWallet - Whether a report has been paid with the wallet or outside of Expensify
* @returns nextStep
*/
function buildNextStep(report: Report | EmptyObject, predictedNextStatus: ValueOf<typeof CONST.REPORT.STATUS_NUM>, {isPaidWithWallet}: BuildNextStepParameters = {}): ReportNextStep | null {
if (!ReportUtils.isExpenseReport(report)) {
return null;
}

const {policyID = '', ownerAccountID = -1, managerID = -1} = report;
const policy = ReportUtils.getPolicy(policyID);
const {submitsTo, harvesting, isPreventSelfApprovalEnabled, autoReportingFrequency, autoReportingOffset} = policy;
const isOwner = currentUserAccountID === ownerAccountID;
const isManager = currentUserAccountID === managerID;
const isSelfApproval = currentUserAccountID === submitsTo;
const ownerLogin = PersonalDetailsUtils.getLoginsByAccountIDs([ownerAccountID])[0] ?? '';
const managerDisplayName = isSelfApproval ? 'you' : ReportUtils.getDisplayNameForParticipant(submitsTo) ?? '';
const type: ReportNextStep['type'] = 'neutral';
let optimisticNextStep: ReportNextStep | null;

switch (predictedNextStatus) {
// Generates an optimistic nextStep once a report has been opened
case CONST.REPORT.STATUS_NUM.OPEN:
// Self review
optimisticNextStep = {
type,
title: 'Next Steps:',
message: [
{
text: 'Waiting for ',
},
{
text: 'you',
type: 'strong',
},
{
text: ' to ',
},
{
text: 'submit',
type: 'strong',
},
{
text: ' these expenses.',
},
],
};

// Scheduled submit enabled
if (harvesting?.enabled && autoReportingFrequency !== CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MANUAL) {
optimisticNextStep.message = [
{
text: 'These expenses are scheduled to ',
},
];
let harvestingSuffix = '';

if (autoReportingFrequency) {
const currentDate = new Date();
let autoSubmissionDate: Date | null = null;
let formattedDate = '';

if (autoReportingOffset === CONST.POLICY.AUTO_REPORTING_OFFSET.LAST_DAY_OF_MONTH) {
autoSubmissionDate = lastDayOfMonth(currentDate);
} else if (autoReportingOffset === CONST.POLICY.AUTO_REPORTING_OFFSET.LAST_BUSINESS_DAY_OF_MONTH) {
const lastBusinessDayOfMonth = DateUtils.getLastBusinessDayOfMonth(currentDate);
autoSubmissionDate = setDate(currentDate, lastBusinessDayOfMonth);
} else if (autoReportingOffset !== undefined) {
autoSubmissionDate = setDate(currentDate, autoReportingOffset);
}

if (autoSubmissionDate) {
formattedDate = format(autoSubmissionDate, CONST.DATE.ORDINAL_DAY_OF_MONTH);
}

const harvestingSuffixes: Record<DeepValueOf<typeof CONST.POLICY.AUTO_REPORTING_FREQUENCIES>, string> = {
[CONST.POLICY.AUTO_REPORTING_FREQUENCIES.IMMEDIATE]: 'later today',
[CONST.POLICY.AUTO_REPORTING_FREQUENCIES.WEEKLY]: 'on Sunday',
[CONST.POLICY.AUTO_REPORTING_FREQUENCIES.SEMI_MONTHLY]: 'on the 1st and 16th of each month',
[CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MONTHLY]: formattedDate ? `on the ${formattedDate} of each month` : '',
[CONST.POLICY.AUTO_REPORTING_FREQUENCIES.TRIP]: 'at the end of your trip',
[CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT]: '',
[CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MANUAL]: '',
};

if (harvestingSuffixes[autoReportingFrequency]) {
harvestingSuffix = ` ${harvestingSuffixes[autoReportingFrequency]}`;
}
}

optimisticNextStep.message.push(
{
text: `automatically submit${harvestingSuffix}!`,
type: 'strong',
},
{
text: ' No further action required!',
},
);
}

// Prevented self submitting
if (isPreventSelfApprovalEnabled && isSelfApproval) {
optimisticNextStep.message = [
{
text: "Oops! Looks like you're submitting to ",
},
{
text: 'yourself',
type: 'strong',
},
{
text: '. Approving your own reports is ',
},
{
text: 'forbidden',
type: 'strong',
},
{
text: ' by your policy. Please submit this report to someone else or contact your admin to change the person you submit to.',
},
];
}

break;

// Generates an optimistic nextStep once a report has been submitted
case CONST.REPORT.STATUS_NUM.SUBMITTED: {
const verb = isManager ? 'review' : 'approve';

// Another owner
optimisticNextStep = {
type,
title: 'Next Steps:',
message: [
{
text: ownerLogin,
type: 'strong',
},
{
text: ' is waiting for ',
},
{
text: 'you',
type: 'strong',
},
{
text: ' to ',
},
{
text: verb,
type: 'strong',
},
{
text: ' these %expenses.',
},
],
};

// Self review & another reviewer
if (isOwner) {
optimisticNextStep.message = [
{
text: 'Waiting for ',
},
{
text: managerDisplayName,
type: 'strong',
},
{
text: ' to ',
},
{
text: verb,
type: 'strong',
},
{
text: ' %expenses.',
},
];
}

break;
}

// Generates an optimistic nextStep once a report has been approved
case CONST.REPORT.STATUS_NUM.APPROVED:
// Self review
optimisticNextStep = {
type,
title: 'Next Steps:',
message: [
{
text: 'Waiting for ',
},
{
text: 'you',
type: 'strong',
},
{
text: ' to ',
},
{
text: 'review',
type: 'strong',
},
{
text: ' %expenses.',
},
],
};

// Another owner
if (!isOwner) {
optimisticNextStep.title = 'Finished!';
optimisticNextStep.message = [
{
text: 'No further action required!',
},
];
}

break;

// Generates an optimistic nextStep once a report has been paid
case CONST.REPORT.STATUS_NUM.REIMBURSED:
// Paid with wallet
optimisticNextStep = {
type,
title: 'Finished!',
message: [
{
text: 'You',
type: 'strong',
},
{
text: ' have marked these expenses as ',
},
{
text: 'paid',
type: 'strong',
},
],
};

// Paid outside of Expensify
if (isPaidWithWallet === false) {
optimisticNextStep.message?.push({text: ' outside of Expensify'});
}

optimisticNextStep.message?.push({text: '.'});

break;

// Resets a nextStep
default:
optimisticNextStep = null;
}

return optimisticNextStep;
}

export {parseMessage, buildNextStep};
Loading

0 comments on commit bb1fc0a

Please sign in to comment.