diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx index 03f1e0f496a8..1c337e024116 100644 --- a/src/components/Picker/BasePicker.tsx +++ b/src/components/Picker/BasePicker.tsx @@ -72,11 +72,11 @@ function BasePicker( */ const onValueChange = (inputValue: TPickerValue, index: number) => { if (inputID) { - onInputChange(inputValue); + onInputChange?.(inputValue); return; } - onInputChange(inputValue, index); + onInputChange?.(inputValue, index); }; const enableHighlight = () => { diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts index 3f7c0282d35a..d935ebe8fdc5 100644 --- a/src/components/Picker/types.ts +++ b/src/components/Picker/types.ts @@ -80,7 +80,7 @@ type BasePickerProps = { shouldShowOnlyTextWhenDisabled?: boolean; /** A callback method that is called when the value changes and it receives the selected value as an argument */ - onInputChange: (value: TPickerValue, index?: number) => void; + onInputChange?: (value: TPickerValue, index?: number) => void; /** Size of a picker component */ size?: PickerSize; diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 985499078c9a..9a7e0a568627 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -124,7 +124,8 @@ function getDistanceMerchant( const formattedDistance = getDistanceForDisplay(hasRoute, distanceInMeters, unit, rate, translate); const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); - const ratePerUnit = PolicyUtils.getUnitRateValue({rate}, toLocaleDigit); + const ratePerUnit = PolicyUtils.getUnitRateValue(toLocaleDigit, {rate}); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const currencySymbol = CurrencyUtils.getCurrencySymbol(currency) || `${currency} `; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 7717513d3f59..43bda05c9e21 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -5,13 +5,12 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {PersonalDetailsList, Policy, PolicyCategories, PolicyMembers, PolicyTagList, PolicyTags, TaxRate} from '@src/types/onyx'; -import type {PolicyFeatureName} from '@src/types/onyx/Policy'; +import type {PolicyFeatureName, Rate} from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Navigation from './Navigation/Navigation'; type MemberEmailsToAccountIDs = Record; -type UnitRate = {rate: number}; /** * Filter out the active policies, which will exclude policies with pending deletion @@ -66,7 +65,7 @@ function hasCustomUnitsError(policy: OnyxEntry): boolean { return Object.keys(policy?.customUnits?.errors ?? {}).length > 0; } -function getNumericValue(value: number, toLocaleDigit: (arg: string) => string): number | string { +function getNumericValue(value: number | string, toLocaleDigit: (arg: string) => string): number | string { const numValue = parseFloat(value.toString().replace(toLocaleDigit('.'), '.')); if (Number.isNaN(numValue)) { return NaN; @@ -82,7 +81,7 @@ function getRateDisplayValue(value: number, toLocaleDigit: (arg: string) => stri return numValue.toString().replace('.', toLocaleDigit('.')).substring(0, value.toString().length); } -function getUnitRateValue(customUnitRate: UnitRate, toLocaleDigit: (arg: string) => string) { +function getUnitRateValue(toLocaleDigit: (arg: string) => string, customUnitRate?: Rate) { return getRateDisplayValue((customUnitRate?.rate ?? 0) / CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, toLocaleDigit); } diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 580898a2f869..3a0718dde1fa 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -4623,3 +4623,5 @@ export { setPolicyDistanceRatesEnabled, deletePolicyDistanceRates, }; + +export type {NewCustomUnit}; diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx new file mode 100644 index 000000000000..8fef5f4dc6f9 --- /dev/null +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.tsx @@ -0,0 +1,158 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useEffect} from 'react'; +import {Keyboard, View} from 'react-native'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {FormOnyxValues} from '@components/Form/types'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import Picker from '@components/Picker'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as CurrencyUtils from '@libs/CurrencyUtils'; +import getPermittedDecimalSeparator from '@libs/getPermittedDecimalSeparator'; +import Navigation from '@libs/Navigation/Navigation'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import * as NumberUtils from '@libs/NumberUtils'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import withPolicy from '@pages/workspace/withPolicy'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; +import * as BankAccounts from '@userActions/BankAccounts'; +import * as Policy from '@userActions/Policy'; +import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import type {Unit} from '@src/types/onyx/Policy'; + +type WorkspaceRateAndUnitPageProps = WithPolicyProps & StackScreenProps; + +type ValidationError = {rate?: TranslationPaths | undefined}; + +function WorkspaceRateAndUnitPage({policy, route}: WorkspaceRateAndUnitPageProps) { + const {translate, toLocaleDigit} = useLocalize(); + const styles = useThemeStyles(); + + useEffect(() => { + if ((policy?.customUnits ?? []).length !== 0) { + return; + } + + BankAccounts.setReimbursementAccountLoading(true); + Policy.openWorkspaceReimburseView(policy?.id ?? ''); + }, [policy?.customUnits, policy?.id]); + + const unitItems = [ + {label: translate('common.kilometers'), value: CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS}, + {label: translate('common.miles'), value: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES}, + ]; + + const saveUnitAndRate = (unit: Unit, rate: string) => { + const distanceCustomUnit = Object.values(policy?.customUnits ?? {}).find((customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + if (!distanceCustomUnit) { + return; + } + const currentCustomUnitRate = Object.values(distanceCustomUnit?.rates ?? {}).find((r) => r.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + const unitID = distanceCustomUnit.customUnitID ?? ''; + const unitName = distanceCustomUnit.name ?? ''; + const rateNumValue = PolicyUtils.getNumericValue(rate, toLocaleDigit); + + const newCustomUnit: Policy.NewCustomUnit = { + customUnitID: unitID, + name: unitName, + attributes: {unit}, + rates: { + ...currentCustomUnitRate, + rate: Number(rateNumValue) * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, + }, + }; + + Policy.updateWorkspaceCustomUnitAndRate(policy?.id ?? '', distanceCustomUnit, newCustomUnit, policy?.lastModified); + }; + + const submit = (values: FormOnyxValues) => { + saveUnitAndRate(values.unit as Unit, values.rate); + Keyboard.dismiss(); + Navigation.goBack(ROUTES.WORKSPACE_REIMBURSE.getRoute(policy?.id ?? '')); + }; + + const validate = (values: FormOnyxValues): ValidationError => { + const errors: ValidationError = {}; + const decimalSeparator = toLocaleDigit('.'); + const outputCurrency = policy?.outputCurrency ?? CONST.CURRENCY.USD; + // Allow one more decimal place for accuracy + const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{1,${CurrencyUtils.getCurrencyDecimals(outputCurrency) + 1}})?$`, 'i'); + if (!rateValueRegex.test(values.rate) || values.rate === '') { + errors.rate = 'workspace.reimburse.invalidRateError'; + } else if (NumberUtils.parseFloatAnyLocale(values.rate) <= 0) { + errors.rate = 'workspace.reimburse.lowRateError'; + } + return errors; + }; + + const distanceCustomUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceCustomRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + + return ( + + {() => ( + + Policy.clearCustomUnitErrors(policy?.id ?? '', distanceCustomUnit?.customUnitID ?? '', distanceCustomRate?.customUnitRateID ?? '')} + > + + + + + + + + )} + + ); +} + +WorkspaceRateAndUnitPage.displayName = 'WorkspaceRateAndUnitPage'; + +export default withPolicy(WorkspaceRateAndUnitPage); diff --git a/src/pages/workspace/reimburse/WorkspaceReimbursePage.js b/src/pages/workspace/reimburse/WorkspaceReimbursePage.js deleted file mode 100644 index 94e72e30e4fb..000000000000 --- a/src/pages/workspace/reimburse/WorkspaceReimbursePage.js +++ /dev/null @@ -1,43 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import compose from '@libs/compose'; -import withPolicy, {policyPropTypes} from '@pages/workspace/withPolicy'; -import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import CONST from '@src/CONST'; -import WorkspaceReimburseView from './WorkspaceReimburseView'; - -const propTypes = { - /** The route object passed to this page from the navigator */ - route: PropTypes.shape({ - /** Each parameter passed via the URL */ - params: PropTypes.shape({ - /** The policyID that is being configured */ - policyID: PropTypes.string.isRequired, - }).isRequired, - }).isRequired, - - ...policyPropTypes, - ...withLocalizePropTypes, -}; - -function WorkspaceReimbursePage(props) { - return ( - - {() => } - - ); -} - -WorkspaceReimbursePage.propTypes = propTypes; -WorkspaceReimbursePage.displayName = 'WorkspaceReimbursePage'; - -export default compose(withPolicy, withLocalize)(WorkspaceReimbursePage); diff --git a/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx b/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx new file mode 100644 index 000000000000..3ca99147a83b --- /dev/null +++ b/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx @@ -0,0 +1,33 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import useLocalize from '@hooks/useLocalize'; +import type {WorkspacesCentralPaneNavigatorParamList} from '@libs/Navigation/types'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicy from '@pages/workspace/withPolicy'; +import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; +import CONST from '@src/CONST'; +import type SCREENS from '@src/SCREENS'; +import WorkspaceReimburseView from './WorkspaceReimburseView'; + +type WorkspaceReimbursePageProps = WithPolicyProps & StackScreenProps; + +function WorkspaceReimbursePage({route, policy}: WorkspaceReimbursePageProps) { + const {translate} = useLocalize(); + + return ( + + {() => } + + ); +} + +WorkspaceReimbursePage.displayName = 'WorkspaceReimbursePage'; + +export default withPolicy(WorkspaceReimbursePage); diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js b/src/pages/workspace/reimburse/WorkspaceReimburseSection.tsx similarity index 60% rename from src/pages/workspace/reimburse/WorkspaceReimburseSection.js rename to src/pages/workspace/reimburse/WorkspaceReimburseSection.tsx index e8ae0e0958b4..18bd121b3fcb 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseSection.tsx @@ -1,45 +1,40 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useEffect, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import ConnectBankAccountButton from '@components/ConnectBankAccountButton'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; -import networkPropTypes from '@components/networkPropTypes'; import Section from '@components/Section'; import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import BankAccount from '@libs/models/BankAccount'; -import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as Link from '@userActions/Link'; +import type * as OnyxTypes from '@src/types/onyx'; -const propTypes = { +type WorkspaceReimburseSectionProps = { /** Policy values needed in the component */ - policy: PropTypes.shape({ - id: PropTypes.string, - }).isRequired, + policy: OnyxEntry; /** Bank account attached to free plan */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired, - - /** Information about the network */ - network: networkPropTypes.isRequired, - - /** Returns translated string for given locale and phrase */ - translate: PropTypes.func.isRequired, + reimbursementAccount: OnyxEntry; }; -function WorkspaceReimburseSection(props) { +function WorkspaceReimburseSection({policy, reimbursementAccount}: WorkspaceReimburseSectionProps) { const theme = useTheme(); const styles = useThemeStyles(); + const {translate} = useLocalize(); const [shouldShowLoadingSpinner, setShouldShowLoadingSpinner] = useState(true); - const achState = lodashGet(props.reimbursementAccount, 'achData.state', ''); + const achState = reimbursementAccount?.achData?.state ?? ''; const hasVBA = achState === BankAccount.STATE.OPEN; - const reimburseReceiptsUrl = `reports?policyID=${props.policy.id}&from=all&type=expense&showStates=Archived&isAdvancedFilterMode=true`; - const isLoading = lodashGet(props.reimbursementAccount, 'isLoading', false); + const policyId = policy?.id ?? ''; + const reimburseReceiptsUrl = `reports?policyID=${policyId}&from=all&type=expense&showStates=Archived&isAdvancedFilterMode=true`; + const isLoading = reimbursementAccount?.isLoading ?? false; const prevIsLoading = usePrevious(isLoading); + const {isOffline} = useNetwork(); useEffect(() => { if (prevIsLoading === isLoading) { @@ -48,15 +43,15 @@ function WorkspaceReimburseSection(props) { setShouldShowLoadingSpinner(isLoading); }, [prevIsLoading, isLoading]); - if (props.network.isOffline) { + if (isOffline) { return (
- {`${props.translate('common.youAppearToBeOffline')} ${props.translate('common.thisFeatureRequiresInternet')}`} + {`${translate('common.youAppearToBeOffline')} ${translate('common.thisFeatureRequiresInternet')}`}
); @@ -75,12 +70,12 @@ function WorkspaceReimburseSection(props) { return hasVBA ? (
Link.openOldDotLink(reimburseReceiptsUrl), icon: Expensicons.Bank, shouldShowRightIcon: true, @@ -91,27 +86,26 @@ function WorkspaceReimburseSection(props) { ]} > - {props.translate('workspace.reimburse.fastReimbursementsVBACopy')} + {translate('workspace.reimburse.fastReimbursementsVBACopy')}
) : (
- {props.translate('workspace.reimburse.unlockNoVBACopy')} + {translate('workspace.reimburse.unlockNoVBACopy')}
); } -WorkspaceReimburseSection.propTypes = propTypes; WorkspaceReimburseSection.displayName = 'WorkspaceReimburseSection'; export default WorkspaceReimburseSection; diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.js b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx similarity index 56% rename from src/pages/workspace/reimburse/WorkspaceReimburseView.js rename to src/pages/workspace/reimburse/WorkspaceReimburseView.tsx index 636675098d23..9c7b1c493e8d 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseView.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx @@ -1,87 +1,58 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx'; import CopyTextToClipboard from '@components/CopyTextToClipboard'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import networkPropTypes from '@components/networkPropTypes'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {withNetwork} from '@components/OnyxProvider'; import Section from '@components/Section'; import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as BankAccounts from '@userActions/BankAccounts'; import * as Link from '@userActions/Link'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; +import type {Unit} from '@src/types/onyx/Policy'; import WorkspaceReimburseSection from './WorkspaceReimburseSection'; -const propTypes = { - /** Policy values needed in the component */ - policy: PropTypes.shape({ - id: PropTypes.string, - customUnits: PropTypes.objectOf( - PropTypes.shape({ - customUnitID: PropTypes.string, - name: PropTypes.string, - attributes: PropTypes.shape({ - unit: PropTypes.string, - }), - rates: PropTypes.objectOf( - PropTypes.shape({ - customUnitRateID: PropTypes.string, - name: PropTypes.string, - rate: PropTypes.number, - }), - ), - }), - ), - outputCurrency: PropTypes.string, - lastModified: PropTypes.number, - }).isRequired, - - /** From Onyx */ +type WorkspaceReimburseViewOnyxProps = { /** Bank account attached to free plan */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes, - - /** Information about the network */ - network: networkPropTypes.isRequired, - - ...withLocalizePropTypes, + reimbursementAccount: OnyxEntry; }; -const defaultProps = { - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountDefaultProps, +type WorkspaceReimburseViewProps = WorkspaceReimburseViewOnyxProps & { + /** Policy values needed in the component */ + policy: OnyxEntry; }; -function WorkspaceReimburseView(props) { +function WorkspaceReimburseView({policy, reimbursementAccount}: WorkspaceReimburseViewProps) { const styles = useThemeStyles(); - const [currentRatePerUnit, setCurrentRatePerUnit] = useState(''); + const [currentRatePerUnit, setCurrentRatePerUnit] = useState(''); const {isSmallScreenWidth} = useWindowDimensions(); - const viewAllReceiptsUrl = `expenses?policyIDList=${props.policy.id}&billableReimbursable=reimbursable&submitterEmail=%2B%2B`; - const distanceCustomUnit = _.find(lodashGet(props.policy, 'customUnits', {}), (unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); - const distanceCustomRate = _.find(lodashGet(distanceCustomUnit, 'rates', {}), (rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); - const {translate, toLocaleDigit} = props; + const viewAllReceiptsUrl = `expenses?policyIDList=${policy?.id ?? ''}&billableReimbursable=reimbursable&submitterEmail=%2B%2B`; + const distanceCustomUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceCustomRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + const {translate, toLocaleDigit} = useLocalize(); + const {isOffline} = useNetwork(); - const getUnitLabel = useCallback((value) => translate(`common.${value}`), [translate]); + const getUnitLabel = useCallback((value: Unit): string => translate(`common.${value}`), [translate]); const getCurrentRatePerUnitLabel = useCallback(() => { - const customUnitRate = _.find(lodashGet(distanceCustomUnit, 'rates', '{}'), (rate) => rate && rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); - const currentUnit = getUnitLabel(lodashGet(distanceCustomUnit, 'attributes.unit', CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES)); - const currentRate = PolicyUtils.getUnitRateValue(customUnitRate, toLocaleDigit); + const customUnitRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate && rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + const currentUnit = getUnitLabel(distanceCustomUnit?.attributes.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES); + const currentRate = PolicyUtils.getUnitRateValue(toLocaleDigit, customUnitRate); const perWord = translate('common.per'); + return `${currentRate} ${perWord} ${currentUnit}`; }, [translate, distanceCustomUnit, toLocaleDigit, getUnitLabel]); @@ -90,19 +61,19 @@ function WorkspaceReimburseView(props) { // openWorkspaceReimburseView uses API.read which will not make the request until all WRITE requests in the sequential queue have finished responding, so there would be a delay in // updating Onyx with the optimistic data. BankAccounts.setReimbursementAccountLoading(true); - Policy.openWorkspaceReimburseView(props.policy.id); - }, [props.policy.id]); + Policy.openWorkspaceReimburseView(policy?.id ?? ''); + }, [policy?.id]); useEffect(() => { - if (props.network.isOffline) { + if (isOffline) { return; } fetchData(); - }, [props.network.isOffline, fetchData]); + }, [isOffline, fetchData]); useEffect(() => { setCurrentRatePerUnit(getCurrentRatePerUnitLabel()); - }, [props.policy.customUnits, getCurrentRatePerUnitLabel]); + }, [policy?.customUnits, getCurrentRatePerUnitLabel]); return ( @@ -117,7 +88,7 @@ function WorkspaceReimburseView(props) { icon: Expensicons.Receipt, shouldShowRightIcon: true, iconRight: Expensicons.NewWindow, - wrapperStyle: [styles.cardMenuItem], + wrapperStyle: styles.cardMenuItem, link: () => Link.buildOldDotURL(viewAllReceiptsUrl), }, ]} @@ -127,7 +98,7 @@ function WorkspaceReimburseView(props) { {translate('workspace.reimburse.captureNoVBACopyBeforeEmail')} {translate('workspace.reimburse.captureNoVBACopyAfterEmail')} @@ -142,7 +113,7 @@ function WorkspaceReimburseView(props) { {translate('workspace.reimburse.trackDistanceCopy')} @@ -151,36 +122,29 @@ function WorkspaceReimburseView(props) { description={translate('workspace.reimburse.trackDistanceRate')} shouldShowRightIcon onPress={() => { - Policy.setPolicyIDForReimburseView(props.policy.id); - Navigation.navigate(ROUTES.WORKSPACE_RATE_AND_UNIT.getRoute(props.policy.id)); + Policy.setPolicyIDForReimburseView(policy?.id ?? ''); + Navigation.navigate(ROUTES.WORKSPACE_RATE_AND_UNIT.getRoute(policy?.id ?? '')); }} wrapperStyle={[styles.sectionMenuItemTopDescription, styles.mt3]} - brickRoadIndicator={(lodashGet(distanceCustomUnit, 'errors') || lodashGet(distanceCustomRate, 'errors')) && CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR} + brickRoadIndicator={(distanceCustomUnit?.errors ?? distanceCustomRate?.errors) && CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR} /> ); } -WorkspaceReimburseView.defaultProps = defaultProps; -WorkspaceReimburseView.propTypes = propTypes; WorkspaceReimburseView.displayName = 'WorkspaceReimburseView'; -export default compose( - withLocalize, - withNetwork(), - withOnyx({ - reimbursementAccount: { - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - }, - }), -)(WorkspaceReimburseView); +export default withOnyx({ + // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, +})(WorkspaceReimburseView);