Skip to content

Commit

Permalink
Merge pull request #34303 from infinitered/32594-update-style-for-vio…
Browse files Browse the repository at this point in the history
…lations
  • Loading branch information
cead22 authored Feb 13, 2024
2 parents 9cbd5cf + 766ccf3 commit 07d97ef
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 80 deletions.
84 changes: 58 additions & 26 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ import type {ViolationField} from '@hooks/useViolations';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CardUtils from '@libs/CardUtils';
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 ViolationsUtils from '@libs/Violations/ViolationsUtils';
import Navigation from '@navigation/Navigation';
import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground';
import * as IOU from '@userActions/IOU';
import * as Transaction from '@userActions/Transaction';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
Expand Down Expand Up @@ -196,6 +198,43 @@ function MoneyRequestView({
const pendingAction = transaction?.pendingAction;
const getPendingFieldAction = (fieldPath: TransactionPendingFieldsKey) => transaction?.pendingFields?.[fieldPath] ?? pendingAction;

const getErrorForField = useCallback(
(field: ViolationField) => {
// Checks applied when creating a new money request
// NOTE: receipt field can return multiple violations, so we need to handle it separately
const fieldChecks: Partial<Record<ViolationField, {isError: boolean; translationPath: TranslationPaths}>> = {
amount: {
isError: transactionAmount === 0,
translationPath: 'common.error.enterAmount',
},
merchant: {
isError: !isSettled && !isCancelled && isPolicyExpenseChat && isEmptyMerchant,
translationPath: 'common.error.enterMerchant',
},
date: {
isError: transactionDate === '',
translationPath: 'common.error.enterDate',
},
};

const {isError, translationPath} = fieldChecks[field] ?? {};

// Return form errors if there are any
if (hasErrors && isError && translationPath) {
return translate(translationPath);
}

// Return violations if there are any
if (canUseViolations && hasViolations(field)) {
const violations = getViolationsForField(field);
return ViolationsUtils.getViolationTranslation(violations[0], translate);
}

return '';
},
[transactionAmount, isSettled, isCancelled, isPolicyExpenseChat, isEmptyMerchant, transactionDate, hasErrors, canUseViolations, hasViolations, translate, getViolationsForField],
);

return (
<View style={[StyleUtils.getReportWelcomeContainerStyle(isSmallScreenWidth)]}>
<AnimatedEmptyStateBackground />
Expand Down Expand Up @@ -253,10 +292,9 @@ function MoneyRequestView({
interactive={canEditAmount}
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 : undefined}
error={hasErrors && transactionAmount === 0 ? translate('common.error.enterAmount') : ''}
brickRoadIndicator={getErrorForField('amount') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
error={getErrorForField('amount')}
/>
{canUseViolations && <ViolationMessages violations={getViolationsForField('amount')} />}
</OfflineWithFeedback>
<OfflineWithFeedback pendingAction={getPendingFieldAction('comment')}>
<MenuItemWithTopDescription
Expand All @@ -272,10 +310,10 @@ function MoneyRequestView({
)
}
wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]}
brickRoadIndicator={hasViolations('comment') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
brickRoadIndicator={getErrorForField('comment') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
error={getErrorForField('comment')}
numberOfLinesTitle={0}
/>
{canUseViolations && <ViolationMessages violations={getViolationsForField('comment')} />}
</OfflineWithFeedback>
{isDistanceRequest ? (
<OfflineWithFeedback pendingAction={getPendingFieldAction('waypoints')}>
Expand All @@ -297,14 +335,9 @@ function MoneyRequestView({
shouldShowRightIcon={canEditMerchant}
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.MERCHANT))}
brickRoadIndicator={
hasViolations('merchant') || (!isSettled && !isCancelled && hasErrors && isEmptyMerchant && isPolicyExpenseChat)
? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR
: undefined
}
error={!isSettled && !isCancelled && hasErrors && isPolicyExpenseChat && isEmptyMerchant ? translate('common.error.enterMerchant') : ''}
brickRoadIndicator={hasViolations('merchant') || (hasErrors && isEmptyMerchant && isPolicyExpenseChat) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
error={hasErrors && isPolicyExpenseChat && isEmptyMerchant ? translate('common.error.enterMerchant') : ''}
/>
{canUseViolations && <ViolationMessages violations={getViolationsForField('merchant')} />}
</OfflineWithFeedback>
)}
<OfflineWithFeedback pendingAction={getPendingFieldAction('created')}>
Expand All @@ -317,10 +350,9 @@ function MoneyRequestView({
onPress={() =>
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DATE.getRoute(CONST.IOU.ACTION.EDIT, CONST.IOU.TYPE.REQUEST, transaction?.transactionID ?? '', report.reportID))
}
brickRoadIndicator={hasViolations('date') || (hasErrors && transactionDate === '') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
error={hasErrors && transactionDate === '' ? translate('common.error.enterDate') : ''}
brickRoadIndicator={getErrorForField('date') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
error={getErrorForField('date')}
/>
{canUseViolations && <ViolationMessages violations={getViolationsForField('date')} />}
</OfflineWithFeedback>
{shouldShowCategory && (
<OfflineWithFeedback pendingAction={getPendingFieldAction('category')}>
Expand All @@ -331,9 +363,9 @@ function MoneyRequestView({
shouldShowRightIcon={canEdit}
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 : undefined}
brickRoadIndicator={getErrorForField('category') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
error={getErrorForField('category')}
/>
{canUseViolations && <ViolationMessages violations={getViolationsForField('category')} />}
</OfflineWithFeedback>
)}
{shouldShowTag && (
Expand All @@ -347,9 +379,9 @@ function MoneyRequestView({
onPress={() =>
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.EDIT, CONST.IOU.TYPE.REQUEST, transaction?.transactionID ?? '', report.reportID))
}
brickRoadIndicator={hasViolations('tag') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
brickRoadIndicator={getErrorForField('tag') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
error={getErrorForField('tag')}
/>
{canUseViolations && <ViolationMessages violations={getViolationsForField('tag')} />}
</OfflineWithFeedback>
)}
{isCardTransaction && (
Expand All @@ -371,13 +403,13 @@ function MoneyRequestView({
isOn={!!transactionBillable}
onToggle={saveBillable}
/>
{getErrorForField('billable') && (
<ViolationMessages
violations={getViolationsForField('billable')}
isLast
/>
)}
</View>
{hasViolations('billable') && (
<ViolationMessages
violations={getViolationsForField('billable')}
isLast
/>
)}
</>
)}
</View>
Expand Down
5 changes: 2 additions & 3 deletions src/hooks/useViolations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,13 @@ type ViolationsMap = Map<ViolationField, TransactionViolation[]>;

function useViolations(violations: TransactionViolation[]) {
const violationsByField = useMemo((): ViolationsMap => {
const filteredViolations = violations.filter((violation) => violation.type === 'violation');
const violationGroups = new Map<ViolationField, TransactionViolation[]>();

for (const violation of violations) {
for (const violation of filteredViolations) {
const field = violationFields[violation.name];
const existingViolations = violationGroups.get(field) ?? [];
violationGroups.set(field, [...existingViolations, violation]);
}

return violationGroups ?? new Map();
}, [violations]);

Expand Down
18 changes: 9 additions & 9 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ import type {
ViolationsInvoiceMarkupParams,
ViolationsMaxAgeParams,
ViolationsMissingTagParams,
ViolationsOverAutoApprovalLimitParams,
ViolationsOverCategoryLimitParams,
ViolationsOverLimitParams,
ViolationsPerDayLimitParams,
Expand Down Expand Up @@ -2145,7 +2144,7 @@ export default {
allTagLevelsRequired: 'All tags required',
autoReportedRejectedExpense: ({rejectReason, rejectedBy}: ViolationsAutoReportedRejectedExpenseParams) => `${rejectedBy} rejected this expense with the comment "${rejectReason}"`,
billableExpense: 'Billable no longer valid',
cashExpenseWithNoReceipt: ({amount}: ViolationsCashExpenseWithNoReceiptParams) => `Receipt required over ${amount}`,
cashExpenseWithNoReceipt: ({formattedLimit}: ViolationsCashExpenseWithNoReceiptParams) => `Receipt required${formattedLimit ? ` over ${formattedLimit}` : ''}`,
categoryOutOfPolicy: 'Category no longer valid',
conversionSurcharge: ({surcharge}: ViolationsConversionSurchargeParams) => `Applied ${surcharge}% conversion surcharge`,
customUnitOutOfPolicy: 'Unit no longer valid',
Expand All @@ -2156,17 +2155,18 @@ export default {
maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Date older than ${maxAge} days`,
missingCategory: 'Missing category',
missingComment: 'Description required for selected category',
missingTag: ({tagName}: ViolationsMissingTagParams) => `Missing ${tagName ?? 'tag'}`,
missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Missing ${tagName ?? 'tag'}`,
modifiedAmount: 'Amount greater than scanned receipt',
modifiedDate: 'Date differs from scanned receipt',
nonExpensiworksExpense: 'Non-Expensiworks expense',
overAutoApprovalLimit: ({formattedLimitAmount}: ViolationsOverAutoApprovalLimitParams) => `Expense exceeds auto approval limit of ${formattedLimitAmount}`,
overCategoryLimit: ({categoryLimit}: ViolationsOverCategoryLimitParams) => `Amount over ${categoryLimit}/person category limit`,
overLimit: ({amount}: ViolationsOverLimitParams) => `Amount over ${amount}/person limit`,
overLimitAttendee: ({amount}: ViolationsOverLimitParams) => `Amount over ${amount}/person limit`,
perDayLimit: ({limit}: ViolationsPerDayLimitParams) => `Amount over daily ${limit}/person category limit`,
overAutoApprovalLimit: ({formattedLimit}: ViolationsOverLimitParams) => `Expense exceeds auto approval limit of ${formattedLimit}`,
overCategoryLimit: ({formattedLimit}: ViolationsOverCategoryLimitParams) => `Amount over ${formattedLimit}/person category limit`,
overLimit: ({formattedLimit}: ViolationsOverLimitParams) => `Amount over ${formattedLimit}/person limit`,
overLimitAttendee: ({formattedLimit}: ViolationsOverLimitParams) => `Amount over ${formattedLimit}/person limit`,
perDayLimit: ({formattedLimit}: ViolationsPerDayLimitParams) => `Amount over daily ${formattedLimit}/person category limit`,
receiptNotSmartScanned: 'Receipt not verified. Please confirm accuracy.',
receiptRequired: ({amount, category}: ViolationsReceiptRequiredParams) => `Receipt required over ${amount} ${category ? ' category limit' : ''}`,
receiptRequired: ({formattedLimit, category}: ViolationsReceiptRequiredParams = {}) =>
`Receipt required${formattedLimit ? ` over ${formattedLimit}${category ? ' category limit' : ''}` : ''}`,
rter: ({brokenBankConnection, email, isAdmin, isTransactionOlderThan7Days, member}: ViolationsRterParams) => {
if (brokenBankConnection) {
return isAdmin
Expand Down
20 changes: 11 additions & 9 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2632,9 +2632,9 @@ export default {
allTagLevelsRequired: 'Todas las etiquetas son obligatorias',
autoReportedRejectedExpense: ({rejectedBy, rejectReason}: ViolationsAutoReportedRejectedExpenseParams) => `${rejectedBy} rechazó la solicitud y comentó "${rejectReason}"`,
billableExpense: 'La opción facturable ya no es válida',
cashExpenseWithNoReceipt: ({amount}: ViolationsCashExpenseWithNoReceiptParams) => `Recibo obligatorio para cantidades mayores de ${amount}`,
cashExpenseWithNoReceipt: ({formattedLimit}: ViolationsCashExpenseWithNoReceiptParams) => `Recibo obligatorio para cantidades mayores de ${formattedLimit}`,
categoryOutOfPolicy: 'La categoría ya no es válida',
conversionSurcharge: ({surcharge}: ViolationsConversionSurchargeParams) => `${surcharge}% de recargo aplicado`,
conversionSurcharge: ({surcharge}: ViolationsConversionSurchargeParams = {}) => `${surcharge}% de recargo aplicado`,
customUnitOutOfPolicy: 'La unidad ya no es válida',
duplicatedTransaction: 'Posible duplicado',
fieldRequired: 'Los campos del informe son obligatorios',
Expand All @@ -2643,17 +2643,19 @@ export default {
maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Fecha de más de ${maxAge} días`,
missingCategory: 'Falta categoría',
missingComment: 'Descripción obligatoria para la categoría seleccionada',
missingTag: ({tagName}: ViolationsMissingTagParams) => `Falta ${tagName}`,
missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Falta ${tagName ?? 'etiqueta'}`,
modifiedAmount: 'Importe superior al del recibo escaneado',
modifiedDate: 'Fecha difiere del recibo escaneado',
nonExpensiworksExpense: 'Gasto no proviene de Expensiworks',
overAutoApprovalLimit: ({formattedLimitAmount}: ViolationsOverAutoApprovalLimitParams) => `Importe supera el límite de aprobación automática de ${formattedLimitAmount}`,
overCategoryLimit: ({categoryLimit}: ViolationsOverCategoryLimitParams) => `Importe supera el límite para la categoría de ${categoryLimit}/persona`,
overLimit: ({amount}: ViolationsOverLimitParams) => `Importe supera el límite de ${amount}/persona`,
overLimitAttendee: ({amount}: ViolationsOverLimitParams) => `Importe supera el límite de ${amount}/persona`,
perDayLimit: ({limit}: ViolationsPerDayLimitParams) => `Importe supera el límite diario de la categoría de ${limit}/persona`,
overAutoApprovalLimit: ({formattedLimit}: ViolationsOverAutoApprovalLimitParams) =>
`Importe supera el límite de aprobación automática${formattedLimit ? ` de ${formattedLimit}` : ''}`,
overCategoryLimit: ({formattedLimit}: ViolationsOverCategoryLimitParams) => `Importe supera el límite para la categoría${formattedLimit ? ` de ${formattedLimit}/persona` : ''}`,
overLimit: ({formattedLimit}: ViolationsOverLimitParams) => `Importe supera el límite${formattedLimit ? ` de ${formattedLimit}/persona` : ''}`,
overLimitAttendee: ({formattedLimit}: ViolationsOverLimitParams) => `Importe supera el límite${formattedLimit ? ` de ${formattedLimit}/persona` : ''}`,
perDayLimit: ({formattedLimit}: ViolationsPerDayLimitParams) => `Importe supera el límite diario de la categoría${formattedLimit ? ` de ${formattedLimit}/persona` : ''}`,
receiptNotSmartScanned: 'Recibo no verificado. Por favor, confirma su exactitud',
receiptRequired: ({amount, category}: ViolationsReceiptRequiredParams) => `Recibo obligatorio para importes sobre ${category ? 'el limite de la categoría de ' : ''}${amount}`,
receiptRequired: ({formattedLimit, category}: ViolationsReceiptRequiredParams = {}) =>
`Recibo obligatorio${formattedLimit ? ` para importes sobre ${category ? 'el limite de la categoría de ' : ''}${formattedLimit}` : ''}`,
rter: ({brokenBankConnection, isAdmin, email, isTransactionOlderThan7Days, member}: ViolationsRterParams) => {
if (brokenBankConnection) {
return isAdmin
Expand Down
12 changes: 6 additions & 6 deletions src/languages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ type WalletProgramParams = {walletProgram: string};

type ViolationsAutoReportedRejectedExpenseParams = {rejectedBy: string; rejectReason: string};

type ViolationsCashExpenseWithNoReceiptParams = {amount: string};
type ViolationsCashExpenseWithNoReceiptParams = {formattedLimit?: string};

type ViolationsConversionSurchargeParams = {surcharge?: number};

Expand All @@ -223,15 +223,15 @@ type ViolationsMaxAgeParams = {maxAge: number};

type ViolationsMissingTagParams = {tagName?: string};

type ViolationsOverAutoApprovalLimitParams = {formattedLimitAmount: string};
type ViolationsOverAutoApprovalLimitParams = {formattedLimit?: string};

type ViolationsOverCategoryLimitParams = {categoryLimit: string};
type ViolationsOverCategoryLimitParams = {formattedLimit?: string};

type ViolationsOverLimitParams = {amount: string};
type ViolationsOverLimitParams = {formattedLimit?: string};

type ViolationsPerDayLimitParams = {limit: string};
type ViolationsPerDayLimitParams = {formattedLimit?: string};

type ViolationsReceiptRequiredParams = {amount: string; category?: string};
type ViolationsReceiptRequiredParams = {formattedLimit?: string; category?: string};

type ViolationsRterParams = {
brokenBankConnection: boolean;
Expand Down
Loading

0 comments on commit 07d97ef

Please sign in to comment.