Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4ef57cd
fix: update hold modal content
mananjadhav Nov 2, 2025
97b6e39
fix: remove unused key
mananjadhav Nov 2, 2025
c1a0b87
fix: temp changes
mananjadhav Nov 3, 2025
7654d78
Merge branch 'main' of github.com:mananjadhav/App into fix/mj-63363-h…
mananjadhav Nov 4, 2025
a6ac32c
fix: update es copy
mananjadhav Nov 4, 2025
5ec58eb
feat: use hold modal logic
mananjadhav Nov 5, 2025
3b8b53e
fix: role based modal open
mananjadhav Nov 6, 2025
9dc83c0
feat: use action instead of state flag
mananjadhav Nov 6, 2025
b7496d3
fix: reject action
mananjadhav Nov 6, 2025
91093f9
refactor: use const
mananjadhav Nov 6, 2025
3b2b445
refactor: use const
mananjadhav Nov 6, 2025
ebae049
fix: chat report for submitter
mananjadhav Nov 6, 2025
74e6262
fix: remove path from feature navigator
mananjadhav Nov 6, 2025
ee6dc83
fix: avoid duplicate nav
mananjadhav Nov 6, 2025
72ed1ac
style: lint fixes
mananjadhav Nov 6, 2025
27c3006
feat: replicate changes for money report
mananjadhav Nov 6, 2025
16dbbc3
fix: revise comment
mananjadhav Nov 6, 2025
cf8860d
style: lint fixes
mananjadhav Nov 6, 2025
ffc30c0
fix: remove hold modal route
mananjadhav Nov 6, 2025
76f818d
refactor: rename modal
mananjadhav Nov 6, 2025
ade3877
style: lint fixes
mananjadhav Nov 6, 2025
d98d36e
feat: language file updates
mananjadhav Nov 6, 2025
0e1c9bb
style: lint fixes
mananjadhav Nov 6, 2025
bb90b2d
Merge branch 'main' of github.com:mananjadhav/App into fix/mj-63363-h…
mananjadhav Nov 12, 2025
58a1bb8
refactor: remove literals
mananjadhav Nov 12, 2025
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
2 changes: 1 addition & 1 deletion src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ const ONYXKEYS = {
/** If the user should see the team 2025 subscription pricing */
NVP_PRIVATE_MANUAL_TEAM_2025_PRICING: 'nvp_private_manualTeam2025Pricing',

/** If the user dismissed the reject explanation */
/** If the approver dismissed the reject or hold explanation */
NVP_DISMISSED_REJECT_USE_EXPLANATION: 'nvp_dismissedRejectUseExplanation',

/** Details on whether an account is locked or not */
Expand Down
7 changes: 0 additions & 7 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2449,13 +2449,6 @@ const ROUTES = {
route: 'share/submit-details/:reportOrAccountID',
getRoute: (reportOrAccountID: string) => `share/submit-details/${reportOrAccountID}` as const,
},

PROCESS_MONEY_REQUEST_HOLD: {
route: 'hold-expense-educational',

// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
getRoute: (backTo?: string) => getUrlWithBackToParam('hold-expense-educational', backTo),
},
CHANGE_POLICY_EDUCATIONAL: {
route: 'change-workspace-educational',

Expand Down
2 changes: 0 additions & 2 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ const SCREENS = {
ROOM_MEMBER_DETAILS: 'RoomMembers_Details',
ROOM_INVITE: 'RoomInvite',
REFERRAL: 'Referral',
PROCESS_MONEY_REQUEST_HOLD: 'ProcessMoneyRequestHold',
TRANSACTION_DUPLICATE: 'TransactionDuplicate',
TRAVEL: 'Travel',
SEARCH_REPORT: 'SearchReport',
Expand Down Expand Up @@ -770,7 +769,6 @@ const SCREENS = {
SIGN_IN_ROOT: 'SignIn_Root',
DETAILS_ROOT: 'Details_Root',
PROFILE_ROOT: 'Profile_Root',
PROCESS_MONEY_REQUEST_HOLD_ROOT: 'ProcessMoneyRequestHold_Root',
AUTO_SUBMIT_ROOT: 'AutoSubmit_Modal_Root',
CHANGE_POLICY_EDUCATIONAL_ROOT: 'ChangePolicyEducational_Root',
REPORT_DESCRIPTION_ROOT: 'Report_Description_Root',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,30 @@
import React, {useMemo} from 'react';
import {View} from 'react-native';
import React from 'react';
import useBeforeRemove from '@hooks/useBeforeRemove';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import FeatureTrainingModal from './FeatureTrainingModal';
import HoldMenuSectionList from './HoldMenuSectionList';
import * as Illustrations from './Icon/Illustrations';
import Text from './Text';
import TextPill from './TextPill';

type ProcessMoneyRequestHoldMenuProps = {
type HoldSubmitterEducationalModalProps = {
/** Method to trigger when pressing outside of the popover menu to close it */
onClose: () => void;

/** Method to trigger when pressing confirm button */
onConfirm: () => void;
};

function ProcessMoneyRequestHoldMenu({onClose, onConfirm}: ProcessMoneyRequestHoldMenuProps) {
function HoldSubmitterEducationalModal({onClose, onConfirm}: HoldSubmitterEducationalModalProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout();

useBeforeRemove(onClose);

const title = useMemo(
() => (
<View style={[styles.flexRow, styles.alignItemsCenter, onboardingIsMediumOrLargerScreenWidth ? styles.mb1 : styles.mb2]}>
<Text style={[styles.textHeadline, styles.mr2]}>{translate('iou.holdEducationalTitle')}</Text>
<TextPill textStyles={styles.holdRequestInline}>{translate('iou.holdEducationalText')}</TextPill>
</View>
),
[onboardingIsMediumOrLargerScreenWidth, styles.flexRow, styles.alignItemsCenter, styles.mb1, styles.mb2, styles.textHeadline, styles.mr2, styles.holdRequestInline, translate],
);

return (
<FeatureTrainingModal
title={title}
title={translate('iou.holdEducationalTitle')}
description={translate('iou.whatIsHoldExplain')}
confirmText={translate('common.buttonConfirm')}
image={Illustrations.HoldExpense}
Expand All @@ -51,12 +36,13 @@ function ProcessMoneyRequestHoldMenu({onClose, onConfirm}: ProcessMoneyRequestHo
illustrationOuterContainerStyle={styles.p0}
onClose={onClose}
onConfirm={onConfirm}
shouldCloseOnConfirm={false}
>
<HoldMenuSectionList />
</FeatureTrainingModal>
);
}

ProcessMoneyRequestHoldMenu.displayName = 'ProcessMoneyRequestHoldMenu';
HoldSubmitterEducationalModal.displayName = 'HoldSubmitterEducationalModal';

export default ProcessMoneyRequestHoldMenu;
export default HoldSubmitterEducationalModal;
68 changes: 47 additions & 21 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {setupMergeTransactionData} from '@libs/actions/MergeTransaction';
import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import {createTransactionThreadReport, deleteAppReport, downloadReportPDF, exportReportToCSV, exportReportToPDF, exportToIntegration, markAsManuallyExported} from '@libs/actions/Report';
import {getExportTemplates, queueExportSearchWithTemplate, search} from '@libs/actions/Search';
import {setNameValuePair} from '@libs/actions/User';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import getPlatform from '@libs/getPlatform';
import Log from '@libs/Log';
Expand Down Expand Up @@ -62,6 +63,7 @@ import {
hasUpdatedTotal,
hasViolations as hasViolationsReportUtils,
isAllowedToApproveExpenseReport,
isCurrentUserSubmitter,
isExported as isExportedUtils,
isInvoiceReport as isInvoiceReportUtil,
isProcessingReport,
Expand All @@ -78,7 +80,6 @@ import {
hasDuplicateTransactions,
isDuplicate,
isExpensifyCardTransaction,
isOnHold as isOnHoldTransactionUtils,
isPayAtEndExpense as isPayAtEndExpenseTransactionUtils,
isPending,
isScanning,
Expand Down Expand Up @@ -112,7 +113,6 @@ import SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import type IconAsset from '@src/types/utils/IconAsset';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import ActivityIndicator from './ActivityIndicator';
import AnimatedSubmitButton from './AnimatedSubmitButton';
import BrokenConnectionDescription from './BrokenConnectionDescription';
Expand All @@ -124,6 +124,7 @@ import {DelegateNoAccessContext} from './DelegateNoAccessModalProvider';
import Header from './Header';
import HeaderWithBackButton from './HeaderWithBackButton';
import HoldOrRejectEducationalModal from './HoldOrRejectEducationalModal';
import HoldSubmitterEducationalModal from './HoldSubmitterEducationalModal';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import {KYCWallContext} from './KYCWall/KYCWallContext';
Expand Down Expand Up @@ -237,9 +238,8 @@ function MoneyReportHeader({
});

const [dismissedRejectUseExplanation] = useOnyx(ONYXKEYS.NVP_DISMISSED_REJECT_USE_EXPLANATION, {canBeMissing: true});
const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {canBeMissing: true});
const [dismissedHoldUseExplanation] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {canBeMissing: true});

const isLoadingHoldUseExplained = isLoadingOnyxValue(dismissedHoldUseExplanationResult);
const [invoiceReceiverPolicy] = useOnyx(
`${ONYXKEYS.COLLECTION.POLICY}${chatReport?.invoiceReceiver && 'policyID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.policyID : undefined}`,
{canBeMissing: true},
Expand Down Expand Up @@ -272,7 +272,6 @@ function MoneyReportHeader({
const styles = useThemeStyles();
const theme = useTheme();
const {isOffline} = useNetwork();
const isOnHold = isOnHoldTransactionUtils(transaction);
const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction);

const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
Expand Down Expand Up @@ -318,12 +317,16 @@ function MoneyReportHeader({
const isInvoiceReport = isInvoiceReportUtil(moneyRequestReport);

const [isDownloadErrorModalVisible, setIsDownloadErrorModalVisible] = useState(false);
const [isRejectEducationalModalVisible, setIsRejectEducationalModalVisible] = useState(false);
const [isHoldEducationalModalVisible, setIsHoldEducationalModalVisible] = useState(false);
const [rejectModalAction, setRejectModalAction] = useState<ValueOf<
typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD | typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.REJECT
> | null>(null);

const {selectedTransactionIDs, removeTransaction, clearSelectedTransactions, currentSearchQueryJSON, currentSearchKey, currentSearchHash} = useSearchContext();
const shouldCalculateTotals = useSearchShouldCalculateTotals(currentSearchKey, currentSearchQueryJSON?.similarSearchHash, true);

const {wideRHPRouteKeys} = useContext(WideRHPContext);
const [network] = useOnyx(ONYXKEYS.NETWORK, {canBeMissing: true});
const shouldDisplayNarrowMoreButton = !shouldDisplayNarrowVersion || (wideRHPRouteKeys.length > 0 && !isSmallScreenWidth);

const showExportProgressModal = useCallback(() => {
Expand Down Expand Up @@ -418,6 +421,7 @@ function MoneyReportHeader({
const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP;
const shouldDisplaySearchRouter = !isReportInRHP || isSmallScreenWidth;
const isReportInSearch = route.name === SCREENS.SEARCH.MONEY_REQUEST_REPORT;
const isReportSubmitter = isCurrentUserSubmitter(chatIOUReport);

const existingB2BInvoiceReport = useParticipantsInvoiceReport(activePolicyID, CONST.REPORT.INVOICE_RECEIVER_TYPE.BUSINESS, chatReport?.policyID);
const confirmPayment = useCallback(
Expand Down Expand Up @@ -576,21 +580,29 @@ function MoneyReportHeader({
};

const statusBarProps = getStatusBarProps();
const dismissModalAndUpdateUseReject = () => {
setIsRejectEducationalModalVisible(false);
dismissRejectUseExplanation();

const dismissModalAndUpdateUseHold = () => {
setIsHoldEducationalModalVisible(false);
setNameValuePair(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, true, false, !network?.shouldFailAllRequests);
if (requestParentReportAction) {
rejectMoneyRequestReason(requestParentReportAction);
changeMoneyRequestHoldStatus(requestParentReportAction);
}
};

useEffect(() => {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
if (isLoadingHoldUseExplained || dismissedHoldUseExplanation || !isOnHold) {
return;
const dismissRejectModalBasedOnAction = () => {
if (rejectModalAction === CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD) {
dismissRejectUseExplanation();
if (requestParentReportAction) {
changeMoneyRequestHoldStatus(requestParentReportAction);
}
} else {
dismissRejectUseExplanation();
if (requestParentReportAction) {
rejectMoneyRequestReason(requestParentReportAction);
}
}
Navigation.navigate(ROUTES.PROCESS_MONEY_REQUEST_HOLD.getRoute(Navigation.getReportRHPActiveRoute()));
}, [dismissedHoldUseExplanation, isLoadingHoldUseExplained, isOnHold]);
setRejectModalAction(null);
};

const primaryAction = useMemo(() => {
return getReportPrimaryAction({
Expand Down Expand Up @@ -1024,7 +1036,15 @@ function MoneyReportHeader({
return;
}

changeMoneyRequestHoldStatus(requestParentReportAction);
const isDismissed = isReportSubmitter ? dismissedHoldUseExplanation : dismissedRejectUseExplanation;

if (isDismissed) {
changeMoneyRequestHoldStatus(requestParentReportAction);
} else if (isReportSubmitter) {
setIsHoldEducationalModalVisible(true);
} else {
setRejectModalAction(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD);
}
},
},
[CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD]: {
Expand Down Expand Up @@ -1187,7 +1207,7 @@ function MoneyReportHeader({
rejectMoneyRequestReason(requestParentReportAction);
}
} else {
setIsRejectEducationalModalVisible(true);
setRejectModalAction(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.REJECT);
}
},
shouldShow: transactions.length === 1,
Expand Down Expand Up @@ -1478,10 +1498,16 @@ function MoneyReportHeader({
isVisible={isDownloadErrorModalVisible}
onClose={() => setIsDownloadErrorModalVisible(false)}
/>
{!!isRejectEducationalModalVisible && (
{!!rejectModalAction && (
<HoldOrRejectEducationalModal
onClose={dismissModalAndUpdateUseReject}
onConfirm={dismissModalAndUpdateUseReject}
onClose={dismissRejectModalBasedOnAction}
onConfirm={dismissRejectModalBasedOnAction}
/>
)}
{!!isHoldEducationalModalVisible && (
<HoldSubmitterEducationalModal
onClose={dismissModalAndUpdateUseHold}
onConfirm={dismissModalAndUpdateUseHold}
/>
)}
<DecisionModal
Expand Down
Loading
Loading