diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView/MoneyRequestView.js
similarity index 94%
rename from src/components/ReportActionItem/MoneyRequestView.js
rename to src/components/ReportActionItem/MoneyRequestView/MoneyRequestView.js
index 036b64af1e4b..c09617a53bb7 100644
--- a/src/components/ReportActionItem/MoneyRequestView.js
+++ b/src/components/ReportActionItem/MoneyRequestView/MoneyRequestView.js
@@ -9,6 +9,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ReceiptEmptyState from '@components/ReceiptEmptyState';
+import ReportActionItemImage from '@components/ReportActionItem/ReportActionItemImage';
import SpacerView from '@components/SpacerView';
import Switch from '@components/Switch';
import tagPropTypes from '@components/tagPropTypes';
@@ -26,13 +27,13 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CardUtils from '@libs/CardUtils';
import compose from '@libs/compose';
import * as CurrencyUtils from '@libs/CurrencyUtils';
-import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReceiptUtils from '@libs/ReceiptUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
+import Navigation from '@navigation/Navigation';
import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import iouReportPropTypes from '@pages/iouReportPropTypes';
@@ -42,7 +43,7 @@ import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
-import ReportActionItemImage from './ReportActionItemImage';
+import useMoneyRequestViewErrors from './useMoneyRequestViewErrors';
const violationNames = lodashValues(CONST.VIOLATIONS);
@@ -228,6 +229,14 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate
const pendingAction = lodashGet(transaction, 'pendingAction');
const getPendingFieldAction = (fieldPath) => lodashGet(transaction, fieldPath) || pendingAction;
+ const {getErrorForField} = useMoneyRequestViewErrors({
+ transactionViolations,
+ hasErrors,
+ isEmptyMerchant,
+ transactionDate,
+ transactionAmount,
+ });
+
return (
@@ -274,9 +283,8 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate
shouldShowRightIcon={canEditAmount}
onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.AMOUNT))}
brickRoadIndicator={hasViolations('amount') || (hasErrors && transactionAmount === 0) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''}
- error={hasErrors && transactionAmount === 0 ? translate('common.error.enterAmount') : ''}
+ error={getErrorForField('amount')}
/>
- {canUseViolations && }
Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DESCRIPTION))}
wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]}
brickRoadIndicator={hasViolations('comment') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''}
+ error={getErrorForField('comment')}
numberOfLinesTitle={0}
/>
- {canUseViolations && }
{isDistanceRequest ? (
@@ -314,9 +322,8 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.MERCHANT))}
brickRoadIndicator={hasViolations('merchant') || (hasErrors && isEmptyMerchant) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''}
- error={hasErrors && isEmptyMerchant ? translate('common.error.enterMerchant') : ''}
+ error={getErrorForField('merchant')}
/>
- {canUseViolations && }
)}
@@ -328,9 +335,8 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DATE))}
brickRoadIndicator={hasViolations('date') || (hasErrors && transactionDate === '') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''}
- error={hasErrors && transactionDate === '' ? translate('common.error.enterDate') : ''}
+ error={getErrorForField('date')}
/>
- {canUseViolations && }
{shouldShowCategory && (
@@ -342,8 +348,8 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.CATEGORY))}
brickRoadIndicator={hasViolations('category') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''}
+ error={getErrorForField('category')}
/>
- {canUseViolations && }
)}
{shouldShowTag && (
@@ -356,8 +362,8 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAG))}
brickRoadIndicator={hasViolations('tag') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''}
+ error={getErrorForField('tag')}
/>
- {canUseViolations && }
)}
{isCardTransaction && (
@@ -379,13 +385,13 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate
isOn={transactionBillable}
onToggle={saveBillable}
/>
+ {hasViolations('billable') && (
+
+ )}
- {hasViolations('billable') && (
-
- )}
>
)}
diff --git a/src/components/ReportActionItem/MoneyRequestView/index.js b/src/components/ReportActionItem/MoneyRequestView/index.js
new file mode 100644
index 000000000000..e6ce263b1831
--- /dev/null
+++ b/src/components/ReportActionItem/MoneyRequestView/index.js
@@ -0,0 +1,3 @@
+import MoneyRequestView from './MoneyRequestView';
+
+export default MoneyRequestView;
diff --git a/src/components/ReportActionItem/MoneyRequestView/useMoneyRequestViewErrors.ts b/src/components/ReportActionItem/MoneyRequestView/useMoneyRequestViewErrors.ts
new file mode 100644
index 000000000000..108287d36a00
--- /dev/null
+++ b/src/components/ReportActionItem/MoneyRequestView/useMoneyRequestViewErrors.ts
@@ -0,0 +1,70 @@
+import {useCallback} from 'react';
+import useLocalize from '@hooks/useLocalize';
+import usePermissions from '@hooks/usePermissions';
+import type {MoneyRequestField} from '@hooks/useViolations';
+import useViolations from '@hooks/useViolations';
+import ViolationsUtils from '@libs/ViolationsUtils';
+import type {TranslationPaths} from '@src/languages/types';
+import type {TransactionViolation} from '@src/types/onyx';
+
+type FieldsWithErrors = Exclude;
+
+type FieldCheck = {
+ isError: boolean;
+ translationPath: TranslationPaths;
+};
+
+type FieldChecks = Partial>;
+
+type UseMoneyRequestViewErrorsParams = {
+ transactionViolations: TransactionViolation[];
+ hasErrors: boolean;
+ isEmptyMerchant: boolean;
+ transactionDate: string;
+ transactionAmount: number;
+};
+
+function useMoneyRequestViewErrors(params: UseMoneyRequestViewErrorsParams) {
+ const {transactionViolations, hasErrors, isEmptyMerchant, transactionDate, transactionAmount} = params;
+
+ const {translate} = useLocalize();
+ const {canUseViolations} = usePermissions();
+ const {getViolationsForField} = useViolations(transactionViolations);
+
+ const getErrorForField = useCallback(
+ (field: Exclude) => {
+ const fieldChecks: FieldChecks = {
+ amount: {
+ isError: transactionAmount === 0,
+ translationPath: 'common.error.enterAmount',
+ },
+ merchant: {
+ isError: isEmptyMerchant,
+ translationPath: 'common.error.enterMerchant',
+ },
+ date: {
+ isError: transactionDate === '',
+ translationPath: 'common.error.enterDate',
+ },
+ };
+
+ const {isError, translationPath} = fieldChecks[field] ?? {};
+
+ if (hasErrors && isError && translationPath) {
+ return translate(translationPath);
+ }
+
+ if (canUseViolations) {
+ const violations = getViolationsForField(field);
+ return ViolationsUtils.getViolationTranslation(violations[0], translate);
+ }
+
+ return '';
+ },
+ [canUseViolations, hasErrors, getViolationsForField, translate, transactionAmount, isEmptyMerchant, transactionDate],
+ );
+
+ return {getErrorForField};
+}
+
+export default useMoneyRequestViewErrors;
diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts
index 76d48158237b..5b6f0f96c5da 100644
--- a/src/hooks/useViolations.ts
+++ b/src/hooks/useViolations.ts
@@ -4,12 +4,12 @@ import type {TransactionViolation, ViolationName} from '@src/types/onyx';
/**
* Names of Fields where violations can occur.
*/
-type ViolationField = 'amount' | 'billable' | 'category' | 'comment' | 'date' | 'merchant' | 'receipt' | 'tag' | 'tax';
+type MoneyRequestField = 'amount' | 'billable' | 'category' | 'comment' | 'date' | 'merchant' | 'receipt' | 'tag' | 'tax';
/**
* Map from Violation Names to the field where that violation can occur.
*/
-const violationFields: Record = {
+const violationFields: Record = {
allTagLevelsRequired: 'tag',
autoReportedRejectedExpense: 'merchant',
billableExpense: 'billable',
@@ -45,11 +45,11 @@ const violationFields: Record = {
taxRequired: 'tax',
};
-type ViolationsMap = Map;
+type ViolationsMap = Map;
function useViolations(violations: TransactionViolation[]) {
const violationsByField = useMemo((): ViolationsMap => {
- const violationGroups = new Map();
+ const violationGroups = new Map();
for (const violation of violations) {
const field = violationFields[violation.name];
@@ -60,7 +60,7 @@ function useViolations(violations: TransactionViolation[]) {
return violationGroups ?? new Map();
}, [violations]);
- const getViolationsForField = useCallback((field: ViolationField) => violationsByField.get(field) ?? [], [violationsByField]);
+ const getViolationsForField = useCallback((field: MoneyRequestField) => violationsByField.get(field) ?? [], [violationsByField]);
return {
getViolationsForField,
@@ -68,4 +68,4 @@ function useViolations(violations: TransactionViolation[]) {
}
export default useViolations;
-export type {ViolationField};
+export type {MoneyRequestField};