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

Add confirmation prompt when approving held request via report preview #42896

Merged
merged 20 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
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
7 changes: 4 additions & 3 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar';
import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
import type {ActionHandledType} from './ProcessMoneyReportHoldMenu';
import ProcessMoneyReportHoldMenu from './ProcessMoneyReportHoldMenu';
import SettlementButton from './SettlementButton';

Expand Down Expand Up @@ -79,7 +80,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
isActionOwner && (ReportUtils.canAddOrDeleteTransactions(moneyRequestReport) || ReportUtils.isTrackExpenseReport(transactionThreadReport)) && !isDeletedParentAction;
const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false);
const [paymentType, setPaymentType] = useState<PaymentMethodType>();
const [requestType, setRequestType] = useState<'pay' | 'approve'>();
const [requestType, setRequestType] = useState<ActionHandledType>();
const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport, policy);
const policyType = policy?.type;
const isPayer = ReportUtils.isPayer(session, moneyRequestReport);
Expand Down Expand Up @@ -124,7 +125,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
return;
}
setPaymentType(type);
setRequestType('pay');
setRequestType(CONST.IOU.REPORT_ACTION_TYPE.PAY);
if (ReportUtils.hasHeldExpenses(moneyRequestReport.reportID)) {
setIsHoldMenuVisible(true);
} else if (chatReport) {
Expand All @@ -133,7 +134,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
};

const confirmApproval = () => {
setRequestType('approve');
setRequestType(CONST.IOU.REPORT_ACTION_TYPE.APPROVE);
if (ReportUtils.hasHeldExpenses(moneyRequestReport.reportID)) {
setIsHoldMenuVisible(true);
} else {
Expand Down
9 changes: 7 additions & 2 deletions src/components/ProcessMoneyReportHoldMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import useLocalize from '@hooks/useLocalize';
import Navigation from '@libs/Navigation/Navigation';
import {isLinkedTransactionHeld} from '@libs/ReportActionsUtils';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import DecisionModal from './DecisionModal';

type ActionHandledType = DeepValueOf<typeof CONST.IOU.REPORT_ACTION_TYPE.PAY | typeof CONST.IOU.REPORT_ACTION_TYPE.APPROVE>;

type ProcessMoneyReportHoldMenuProps = {
/** The chat report this report is linked to */
chatReport: OnyxEntry<OnyxTypes.Report>;
Expand All @@ -35,7 +39,7 @@ type ProcessMoneyReportHoldMenuProps = {
paymentType?: PaymentMethodType;

/** Type of action handled */
requestType?: 'pay' | 'approve';
requestType?: ActionHandledType;
};

function ProcessMoneyReportHoldMenu({
Expand All @@ -50,7 +54,7 @@ function ProcessMoneyReportHoldMenu({
moneyRequestReport,
}: ProcessMoneyReportHoldMenuProps) {
const {translate} = useLocalize();
const isApprove = requestType === 'approve';
const isApprove = requestType === CONST.IOU.REPORT_ACTION_TYPE.APPROVE;

const onSubmit = (full: boolean) => {
if (isApprove) {
Expand Down Expand Up @@ -82,3 +86,4 @@ function ProcessMoneyReportHoldMenu({
ProcessMoneyReportHoldMenu.displayName = 'ProcessMoneyReportHoldMenu';

export default ProcessMoneyReportHoldMenu;
export type {ActionHandledType};
49 changes: 47 additions & 2 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import truncate from 'lodash/truncate';
import React, {useMemo} from 'react';
import React, {useMemo, useState} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
Expand All @@ -9,13 +9,16 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import ProcessMoneyReportHoldMenu from '@components/ProcessMoneyReportHoldMenu';
import type {ActionHandledType} from '@components/ProcessMoneyReportHoldMenu';
import SettlementButton from '@components/SettlementButton';
import {showContextMenuForReport} from '@components/ShowContextMenuContext';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import ControlSelection from '@libs/ControlSelection';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
Expand Down Expand Up @@ -119,6 +122,12 @@ function ReportPreview({
[transactions, iouReportID, action],
);

const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false);
const [requestType, setRequestType] = useState<ActionHandledType>();
const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(iouReport, policy);
const {isSmallScreenWidth} = useWindowDimensions();
const [paymentType, setPaymentType] = useState<PaymentMethodType>();

const managerID = iouReport?.managerID ?? 0;
const {totalDisplaySpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport);

Expand Down Expand Up @@ -162,6 +171,28 @@ function ReportPreview({
[chatReport?.isOwnPolicyExpenseChat, policy?.harvesting?.enabled],
);

const confirmPayment = (type: PaymentMethodType | undefined) => {
if (!type) {
return;
}
setPaymentType(type);
setRequestType(CONST.IOU.REPORT_ACTION_TYPE.PAY);
if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) {
setIsHoldMenuVisible(true);
} else if (chatReport && iouReport) {
IOU.payMoneyRequest(type, chatReport, iouReport, false);
}
};

const confirmApproval = () => {
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 create a utility function to avoid code repetition?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this these function, we also have logic to set state of the component so we shouldn't create a util for this.

Copy link
Contributor

Choose a reason for hiding this comment

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

we also have logic to set state of the component so we shouldn't create a util for this.

Do you mean we shouldn't pass setIsHoldMenuVisible,... to the util function? Could you explain more why we shouldn't do that

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 mean we shouldn't create a util function for this case since this function also controls the state of a component.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And Since we only use this logic for report preview and MoneyReportHeader, the duplicate is acceptable.

setRequestType(CONST.IOU.REPORT_ACTION_TYPE.APPROVE);
if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) {
setIsHoldMenuVisible(true);
} else {
IOU.approveMoneyRequest(iouReport ?? {}, true);
}
};

const getDisplayAmount = (): string => {
if (totalDisplaySpend) {
return CurrencyUtils.convertToDisplayString(totalDisplaySpend, iouReport?.currency);
Expand Down Expand Up @@ -368,7 +399,8 @@ function ReportPreview({
policyID={policyID}
chatReportID={chatReportID}
iouReport={iouReport}
onPress={(paymentType?: PaymentMethodType) => chatReport && iouReport && paymentType && IOU.payMoneyRequest(paymentType, chatReport, iouReport)}
onPress={confirmPayment}
confirmApproval={confirmApproval}
enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
shouldHidePaymentOptions={!shouldShowPayButton}
Expand Down Expand Up @@ -399,6 +431,19 @@ function ReportPreview({
</View>
</PressableWithoutFeedback>
</View>
{isHoldMenuVisible && iouReport && requestType !== undefined && (
<ProcessMoneyReportHoldMenu
nonHeldAmount={!ReportUtils.hasOnlyHeldExpenses(iouReport?.reportID ?? '') ? nonHeldAmount : undefined}
requestType={requestType}
fullAmount={fullAmount}
isSmallScreenWidth={isSmallScreenWidth}
onClose={() => setIsHoldMenuVisible(false)}
isVisible={isHoldMenuVisible}
paymentType={paymentType}
chatReport={chatReport}
moneyRequestReport={iouReport}
/>
)}
</OfflineWithFeedback>
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6399,8 +6399,8 @@ function getNonHeldAndFullAmount(iouReport: OnyxEntry<Report>, policy: OnyxEntry
}

return [
CurrencyUtils.convertToDisplayString((iouReport?.unheldTotal ?? 0) * -1, iouReport?.currency ?? ''),
CurrencyUtils.convertToDisplayString((iouReport?.total ?? 0) * -1, iouReport?.currency ?? ''),
CurrencyUtils.convertToDisplayString((iouReport?.unheldTotal ?? 0) * -1, iouReport?.currency),
CurrencyUtils.convertToDisplayString((iouReport?.total ?? 0) * -1, iouReport?.currency),
];
}

Expand Down
Loading