diff --git a/assets/images/simple-illustrations/simple-illustration__commentbubbles.svg b/assets/images/simple-illustrations/simple-illustration__commentbubbles.svg new file mode 100644 index 000000000000..829d3ee2e3fe --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__commentbubbles.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/assets/images/simple-illustrations/simple-illustration__hourglass.svg b/assets/images/simple-illustrations/simple-illustration__hourglass.svg new file mode 100644 index 000000000000..539e1e45b795 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__hourglass.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/simple-illustrations/simple-illustration__trashcan.svg b/assets/images/simple-illustrations/simple-illustration__trashcan.svg new file mode 100644 index 000000000000..4e66efa0a67e --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__trashcan.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 35fa4bbf0837..b6e62814466b 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -358,9 +358,9 @@ const ROUTES = { getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_MERCHANT: { - route: 'create/:iouType/merchante/:transactionID/:reportID/', + route: 'create/:iouType/merchant/:transactionID/:reportID/', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/merchante/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/merchant/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_PARTICIPANTS: { route: 'create/:iouType/participants/:transactionID/:reportID/', @@ -479,6 +479,7 @@ const ROUTES = { route: 'referral/:contentType', getRoute: (contentType: string) => `referral/${contentType}` as const, }, + PROCESS_MONEY_REQUEST_HOLD: 'hold-request-educational', } as const; export {getUrlWithBackToParam}; diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 91c4153bacd2..703cb309d641 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -108,6 +108,7 @@ const SCREENS = { ROOM_MEMBERS: 'RoomMembers', ROOM_INVITE: 'RoomInvite', REFERRAL: 'Referral', + PROCESS_MONEY_REQUEST_HOLD: 'ProcessMoneyRequestHold', }, SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop', SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop', @@ -231,6 +232,7 @@ const SCREENS = { SIGN_IN_ROOT: 'SignIn_Root', DETAILS_ROOT: 'Details_Root', PROFILE_ROOT: 'Profile_Root', + PROCESS_MONEY_REQUEST_HOLD_ROOT: 'ProcessMoneyRequestHold_Root', REPORT_WELCOME_MESSAGE_ROOT: 'Report_WelcomeMessage_Root', REPORT_PARTICIPANTS_ROOT: 'ReportParticipants_Root', ROOM_MEMBERS_ROOT: 'RoomMembers_Root', diff --git a/src/components/AmountTextInput.js b/src/components/AmountTextInput.js index 25e1ce6f05ec..231a99f0e6a6 100644 --- a/src/components/AmountTextInput.js +++ b/src/components/AmountTextInput.js @@ -32,7 +32,7 @@ const propTypes = { style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), /** Style for the container */ - containerStyles: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), + touchableInputWrapperStyle: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), /** Function to call to handle key presses in the text input */ onKeyPress: PropTypes.func, @@ -44,7 +44,7 @@ const defaultProps = { onSelectionChange: () => {}, onKeyPress: () => {}, style: {}, - containerStyles: {}, + touchableInputWrapperStyle: {}, }; function AmountTextInput(props) { @@ -67,7 +67,7 @@ function AmountTextInput(props) { onSelectionChange={props.onSelectionChange} role={CONST.ROLE.PRESENTATION} onKeyPress={props.onKeyPress} - containerStyles={[...StyleUtils.parseStyleAsArray(props.containerStyles)]} + touchableInputWrapperStyle={props.touchableInputWrapperStyle} /> ); } diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index d24d1e18907f..e5b605dd3ff2 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -19,7 +19,6 @@ import fileDownload from '@libs/fileDownload'; import * as FileUtils from '@libs/fileDownload/FileUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import useNativeDriver from '@libs/useNativeDriver'; import reportPropTypes from '@pages/reportPropTypes'; @@ -95,6 +94,9 @@ const propTypes = { /** Whether it is a receipt attachment or not */ isReceiptAttachment: PropTypes.bool, + + /** Whether the receipt can be replaced */ + canEditReceipt: PropTypes.bool, }; const defaultProps = { @@ -113,6 +115,7 @@ const defaultProps = { onCarouselAttachmentChange: () => {}, isWorkspaceAvatar: false, isReceiptAttachment: false, + canEditReceipt: false, }; function AttachmentModal(props) { @@ -372,13 +375,9 @@ function AttachmentModal(props) { if (!props.isReceiptAttachment || !props.parentReport || !props.parentReportActions) { return []; } - const menuItems = []; - const parentReportAction = props.parentReportActions[props.report.parentReportActionID]; - const canEdit = - ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, props.parentReport.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT, props.transaction) && - !TransactionUtils.isDistanceRequest(props.transaction); - if (canEdit) { + const menuItems = []; + if (props.canEditReceipt) { menuItems.push({ icon: Expensicons.Camera, text: props.translate('common.replace'), @@ -393,7 +392,7 @@ function AttachmentModal(props) { text: props.translate('common.download'), onSelected: () => downloadAttachment(source), }); - if (TransactionUtils.hasReceipt(props.transaction) && !TransactionUtils.isReceiptBeingScanned(props.transaction) && canEdit) { + if (TransactionUtils.hasReceipt(props.transaction) && !TransactionUtils.isReceiptBeingScanned(props.transaction) && props.canEditReceipt) { menuItems.push({ icon: Expensicons.Trashcan, text: props.translate('receipt.deleteReceipt'), diff --git a/src/components/HeaderPageLayout.js b/src/components/HeaderPageLayout.tsx similarity index 55% rename from src/components/HeaderPageLayout.js rename to src/components/HeaderPageLayout.tsx index 9ef5d4f83a06..304bb2ce49b1 100644 --- a/src/components/HeaderPageLayout.js +++ b/src/components/HeaderPageLayout.tsx @@ -1,56 +1,54 @@ -import PropTypes from 'prop-types'; import React, {useMemo} from 'react'; +import type {ReactNode} from 'react'; import {ScrollView, View} from 'react-native'; -import _ from 'underscore'; +import type {StyleProp, ViewStyle} from 'react-native'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; +import type ChildrenProps from '@src/types/utils/ChildrenProps'; import FixedFooter from './FixedFooter'; import HeaderWithBackButton from './HeaderWithBackButton'; -import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBackButtonPropTypes'; +import type HeaderWithBackButtonProps from './HeaderWithBackButton/types'; import ScreenWrapper from './ScreenWrapper'; -const propTypes = { - ...headerWithBackButtonPropTypes, +type HeaderPageLayoutProps = ChildrenProps & + HeaderWithBackButtonProps & { + /** The background color to apply in the upper half of the screen. */ + backgroundColor?: string; - /** Children to display in the lower half of the page (below the header section w/ an animation) */ - children: PropTypes.node.isRequired, + /** A fixed footer to display at the bottom of the page. */ + footer?: ReactNode; - /** The background color to apply in the upper half of the screen. */ - backgroundColor: PropTypes.string, + /** The image to display in the upper half of the screen. */ + headerContent?: ReactNode; - /** A fixed footer to display at the bottom of the page. */ - footer: PropTypes.node, + /** Style to apply to the header image container */ + headerContainerStyles?: StyleProp; - /** The image to display in the upper half of the screen. */ - header: PropTypes.node, + /** Style to apply to the ScrollView container */ + scrollViewContainerStyles?: StyleProp; - /** Style to apply to the header image container */ - // eslint-disable-next-line react/forbid-prop-types - headerContainerStyles: PropTypes.arrayOf(PropTypes.object), + /** Style to apply to the children container */ + childrenContainerStyles?: StyleProp; - /** Style to apply to the ScrollView container */ - // eslint-disable-next-line react/forbid-prop-types - scrollViewContainerStyles: PropTypes.arrayOf(PropTypes.object), + /** Style to apply to the whole section container */ + style?: StyleProp; + }; - /** Style to apply to the children container */ - // eslint-disable-next-line react/forbid-prop-types - childrenContainerStyles: PropTypes.arrayOf(PropTypes.object), -}; - -const defaultProps = { - backgroundColor: undefined, - header: null, - headerContainerStyles: [], - scrollViewContainerStyles: [], - childrenContainerStyles: [], - footer: null, -}; - -function HeaderPageLayout({backgroundColor, children, footer, headerContainerStyles, scrollViewContainerStyles, childrenContainerStyles, style, headerContent, ...propsToPassToHeader}) { +function HeaderPageLayout({ + backgroundColor, + children, + footer, + headerContainerStyles, + scrollViewContainerStyles, + childrenContainerStyles, + style, + headerContent, + ...rest +}: HeaderPageLayoutProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -58,7 +56,7 @@ function HeaderPageLayout({backgroundColor, children, footer, headerContainerSty const {isOffline} = useNetwork(); const appBGColor = StyleUtils.getBackgroundColorStyle(theme.appBG); const {titleColor, iconFill} = useMemo(() => { - const isColorfulBackground = (backgroundColor || theme.appBG) !== theme.appBG && (backgroundColor || theme.highlightBG) !== theme.highlightBG; + const isColorfulBackground = (backgroundColor ?? theme.appBG) !== theme.appBG && (backgroundColor ?? theme.highlightBG) !== theme.highlightBG; return { titleColor: isColorfulBackground ? theme.textColorfulBackground : undefined, iconFill: isColorfulBackground ? theme.iconColorfulBackground : undefined, @@ -67,7 +65,7 @@ function HeaderPageLayout({backgroundColor, children, footer, headerContainerSty return ( - + {/** Safari on ios/mac has a bug where overscrolling the page scrollview shows green background color. This is a workaround to fix that. https://github.com/Expensify/App/issues/23422 */} {Browser.isSafari() && ( - + )} - - {!Browser.isSafari() && } - + + {!Browser.isSafari() && } + {headerContent} {children} - {!_.isNull(footer) && {footer}} + {!!footer && {footer}} )} @@ -107,8 +102,7 @@ function HeaderPageLayout({backgroundColor, children, footer, headerContainerSty ); } -HeaderPageLayout.propTypes = propTypes; -HeaderPageLayout.defaultProps = defaultProps; HeaderPageLayout.displayName = 'HeaderPageLayout'; +export type {HeaderPageLayoutProps}; export default HeaderPageLayout; diff --git a/src/components/HoldMenuSectionList.tsx b/src/components/HoldMenuSectionList.tsx new file mode 100644 index 000000000000..aa5dd75ce159 --- /dev/null +++ b/src/components/HoldMenuSectionList.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {ImageSourcePropType} from 'react-native'; +import type {SvgProps} from 'react-native-svg'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; +import type {TranslationPaths} from '@src/languages/types'; +import Icon from './Icon'; +import * as Illustrations from './Icon/Illustrations'; +import Text from './Text'; + +type HoldMenuSection = { + /** The icon supplied with the section */ + icon: React.FC | ImageSourcePropType; + + /** Translation key for the title */ + titleTranslationKey: TranslationPaths; + + /** Translation key for the description */ + descriptionTranslationKey: TranslationPaths; +}; + +function HoldMenuSectionList() { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const holdMenuSections: HoldMenuSection[] = [ + { + icon: Illustrations.Hourglass, + titleTranslationKey: 'iou.whatIsHoldTitle', + descriptionTranslationKey: 'iou.whatIsHoldExplain', + }, + { + icon: Illustrations.CommentBubbles, + titleTranslationKey: 'iou.holdIsTemporaryTitle', + descriptionTranslationKey: 'iou.holdIsTemporaryExplain', + }, + { + icon: Illustrations.TrashCan, + titleTranslationKey: 'iou.deleteHoldTitle', + descriptionTranslationKey: 'iou.deleteHoldExplain', + }, + ]; + + return ( + <> + {holdMenuSections.map((section, i) => ( + + + + {translate(section.titleTranslationKey)} + + {translate(section.descriptionTranslationKey)} + + + + ))} + + ); +} + +HoldMenuSectionList.displayName = 'HoldMenuSectionList'; + +export type {HoldMenuSection}; + +export default HoldMenuSectionList; diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 1e574504001d..954c8d0392fc 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -32,6 +32,7 @@ import BigRocket from '@assets/images/simple-illustrations/simple-illustration__ import PinkBill from '@assets/images/simple-illustrations/simple-illustration__bill.svg'; import ChatBubbles from '@assets/images/simple-illustrations/simple-illustration__chatbubbles.svg'; import CoffeeMug from '@assets/images/simple-illustrations/simple-illustration__coffeemug.svg'; +import CommentBubbles from '@assets/images/simple-illustrations/simple-illustration__commentbubbles.svg'; import ConciergeBubble from '@assets/images/simple-illustrations/simple-illustration__concierge-bubble.svg'; import ConciergeNew from '@assets/images/simple-illustrations/simple-illustration__concierge.svg'; import CreditCardsNew from '@assets/images/simple-illustrations/simple-illustration__credit-cards.svg'; @@ -39,6 +40,7 @@ import EmailAddress from '@assets/images/simple-illustrations/simple-illustratio import HandCard from '@assets/images/simple-illustrations/simple-illustration__handcard.svg'; import HandEarth from '@assets/images/simple-illustrations/simple-illustration__handearth.svg'; import HotDogStand from '@assets/images/simple-illustrations/simple-illustration__hotdogstand.svg'; +import Hourglass from '@assets/images/simple-illustrations/simple-illustration__hourglass.svg'; import InvoiceBlue from '@assets/images/simple-illustrations/simple-illustration__invoice.svg'; import LockOpen from '@assets/images/simple-illustrations/simple-illustration__lockopen.svg'; import Luggage from '@assets/images/simple-illustrations/simple-illustration__luggage.svg'; @@ -53,6 +55,7 @@ import ShieldYellow from '@assets/images/simple-illustrations/simple-illustratio import SmallRocket from '@assets/images/simple-illustrations/simple-illustration__smallrocket.svg'; import ThumbsUpStars from '@assets/images/simple-illustrations/simple-illustration__thumbsupstars.svg'; import TrackShoe from '@assets/images/simple-illustrations/simple-illustration__track-shoe.svg'; +import TrashCan from '@assets/images/simple-illustrations/simple-illustration__trashcan.svg'; import TreasureChest from '@assets/images/simple-illustrations/simple-illustration__treasurechest.svg'; export { @@ -111,5 +114,8 @@ export { Hands, HandEarth, SmartScan, + Hourglass, + CommentBubbles, + TrashCan, TeleScope, }; diff --git a/src/components/IllustratedHeaderPageLayout.js b/src/components/IllustratedHeaderPageLayout.js deleted file mode 100644 index 9980d8a7879a..000000000000 --- a/src/components/IllustratedHeaderPageLayout.js +++ /dev/null @@ -1,67 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; -import HeaderPageLayout from './HeaderPageLayout'; -import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBackButtonPropTypes'; -import Lottie from './Lottie'; - -const propTypes = { - ...headerWithBackButtonPropTypes, - - /** Children to display in the lower half of the page (below the header section w/ an animation) */ - children: PropTypes.node.isRequired, - - /** The illustration to display in the header. Can be either an SVG component or a JSON object representing a Lottie animation. */ - illustration: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired, - - /** The background color to apply in the upper half of the screen. */ - backgroundColor: PropTypes.string, - - /** A fixed footer to display at the bottom of the page. */ - footer: PropTypes.node, - - /** Overlay content to display on top of animation */ - overlayContent: PropTypes.func, -}; - -const defaultProps = { - backgroundColor: undefined, - footer: null, - overlayContent: null, -}; - -function IllustratedHeaderPageLayout({backgroundColor, children, illustration, footer, overlayContent, ...propsToPassToHeader}) { - const theme = useTheme(); - const styles = useThemeStyles(); - return ( - - - {overlayContent && overlayContent()} - - } - headerContainerStyles={[styles.justifyContentCenter, styles.w100]} - footer={footer} - // eslint-disable-next-line react/jsx-props-no-spreading - {...propsToPassToHeader} - > - {children} - - ); -} - -IllustratedHeaderPageLayout.propTypes = propTypes; -IllustratedHeaderPageLayout.defaultProps = defaultProps; -IllustratedHeaderPageLayout.displayName = 'IllustratedHeaderPageLayout'; - -export default IllustratedHeaderPageLayout; diff --git a/src/components/IllustratedHeaderPageLayout.tsx b/src/components/IllustratedHeaderPageLayout.tsx new file mode 100644 index 000000000000..72ec0adf7672 --- /dev/null +++ b/src/components/IllustratedHeaderPageLayout.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import type {ReactNode} from 'react'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import HeaderPageLayout from './HeaderPageLayout'; +import type {HeaderPageLayoutProps} from './HeaderPageLayout'; +import Lottie from './Lottie'; +import type DotLottieAnimation from './LottieAnimations/types'; + +type IllustratedHeaderPageLayoutProps = HeaderPageLayoutProps & { + /** The illustration to display in the header. Can be a JSON object representing a Lottie animation. */ + illustration: DotLottieAnimation; + + /** The background color to apply in the upper half of the screen. */ + backgroundColor?: string; + + /** Overlay content to display on top of animation */ + overlayContent?: () => ReactNode; +}; + +function IllustratedHeaderPageLayout({backgroundColor, children, illustration, overlayContent, ...rest}: IllustratedHeaderPageLayoutProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + return ( + + + {overlayContent?.()} + + } + headerContainerStyles={[styles.justifyContentCenter, styles.w100]} + // eslint-disable-next-line react/jsx-props-no-spreading + {...rest} + > + {children} + + ); +} + +IllustratedHeaderPageLayout.displayName = 'IllustratedHeaderPageLayout'; + +export default IllustratedHeaderPageLayout; diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index 8ed6d0746438..5b59fca6cdae 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -87,9 +87,9 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); const policyType = lodashGet(policy, 'type'); const isPolicyAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && lodashGet(policy, 'role') === CONST.POLICY.ROLE.ADMIN; - const isGroupPolicy = _.contains([CONST.POLICY.TYPE.CORPORATE, CONST.POLICY.TYPE.TEAM], policyType); + const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicy(moneyRequestReport); const isManager = ReportUtils.isMoneyRequestReport(moneyRequestReport) && lodashGet(session, 'accountID', null) === moneyRequestReport.managerID; - const isPayer = isGroupPolicy + const isPayer = isPaidGroupPolicy ? // In a group policy, the admin approver can pay the report directly by skipping the approval step isPolicyAdmin && (isApproved || isManager) : isPolicyAdmin || (ReportUtils.isMoneyRequestReport(moneyRequestReport) && isManager); @@ -99,11 +99,11 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt [isPayer, isDraft, isSettled, moneyRequestReport, reimbursableTotal, chatReport], ); const shouldShowApproveButton = useMemo(() => { - if (!isGroupPolicy) { + if (!isPaidGroupPolicy) { return false; } return isManager && !isDraft && !isApproved && !isSettled; - }, [isGroupPolicy, isManager, isDraft, isApproved, isSettled]); + }, [isPaidGroupPolicy, isManager, isDraft, isApproved, isSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; const shouldShowSubmitButton = isDraft && reimbursableTotal !== 0; const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; diff --git a/src/components/ProcessMoneyRequestHoldMenu.tsx b/src/components/ProcessMoneyRequestHoldMenu.tsx new file mode 100644 index 000000000000..1b711633ed3b --- /dev/null +++ b/src/components/ProcessMoneyRequestHoldMenu.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import {View} from 'react-native'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Button from './Button'; +import HoldMenuSectionList from './HoldMenuSectionList'; +import type {PopoverAnchorPosition} from './Modal/types'; +import Popover from './Popover'; +import type {AnchorAlignment} from './Popover/types'; +import Text from './Text'; +import TextPill from './TextPill'; + +type ProcessMoneyRequestHoldMenuProps = { + /** Whether the content is visible */ + isVisible: boolean; + + /** Method to trigger when pressing outside of the popover menu to close it */ + onClose: () => void; + + /** Method to trigger when pressing confirm button */ + onConfirm: () => void; + + /** The anchor position of the popover menu */ + anchorPosition?: PopoverAnchorPosition; + + /** The anchor alignment of the popover menu */ + anchorAlignment: AnchorAlignment; + + /** The anchor ref of the popover menu */ + anchorRef: React.RefObject; +}; + +function ProcessMoneyRequestHoldMenu({isVisible, onClose, onConfirm, anchorPosition, anchorAlignment, anchorRef}: ProcessMoneyRequestHoldMenuProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + return ( + + + + {translate('iou.holdEducationalTitle')} + {translate('iou.hold')}; + + +