-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Update MoneyRequestView.js to show Violations #32594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1759325
572cad3
6a853a0
f090c0a
9ad9678
4f6863c
baf551a
32c0a6d
b57f82b
9ca94f1
047d193
29faf0f
65dc2b4
e973ceb
5c85c67
f5dd125
b77cf26
e8e3717
a7b2117
cb7a551
ccc88c7
4b4ed0f
f932509
5e726fe
f6cdd0a
34ea213
1a30bb4
9c1d092
3bddb2a
af453b6
b2e76dc
47f7ea4
2532ae9
f757a27
941346a
405672d
f853ce0
f649598
29f8bbd
ab967fc
d300b98
d16e843
1493429
80f205d
4277baf
4636e08
dee57aa
7220f92
0f97e97
b661558
b620aa6
f4376a2
319c09e
a032ed2
e91c965
7ec9098
84f83fc
bebb99f
d8e9ddc
d9ef8e2
02da9ec
1fe91ef
9b7753c
b491946
ea1828c
b749929
fe50d90
44efe42
3e078bc
95952b1
7f118e1
e091846
b159109
fb952fc
8ce2f61
bd7e845
5a9614c
e251057
862ab76
c248cbb
095b228
ac9c564
8057dc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import lodashGet from 'lodash/get'; | ||
| import lodashValues from 'lodash/values'; | ||
| import PropTypes from 'prop-types'; | ||
| import React, {useMemo} from 'react'; | ||
| import React, {useCallback, useMemo} from 'react'; | ||
| import {View} from 'react-native'; | ||
| import {withOnyx} from 'react-native-onyx'; | ||
| import categoryPropTypes from '@components/categoryPropTypes'; | ||
|
|
@@ -14,12 +14,14 @@ import Switch from '@components/Switch'; | |
| import tagPropTypes from '@components/tagPropTypes'; | ||
| import Text from '@components/Text'; | ||
| import transactionPropTypes from '@components/transactionPropTypes'; | ||
| import ViolationMessages from '@components/ViolationMessages'; | ||
| import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import usePermissions from '@hooks/usePermissions'; | ||
| import useStyleUtils from '@hooks/useStyleUtils'; | ||
| import useTheme from '@hooks/useTheme'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import useViolations from '@hooks/useViolations'; | ||
| import useWindowDimensions from '@hooks/useWindowDimensions'; | ||
| import * as CardUtils from '@libs/CardUtils'; | ||
| import compose from '@libs/compose'; | ||
|
|
@@ -41,6 +43,32 @@ import ONYXKEYS from '@src/ONYXKEYS'; | |
| import ROUTES from '@src/ROUTES'; | ||
| import ReportActionItemImage from './ReportActionItemImage'; | ||
|
|
||
| const violationNames = lodashValues(CONST.VIOLATIONS); | ||
|
|
||
| const transactionViolationPropType = PropTypes.shape({ | ||
| type: PropTypes.string.isRequired, | ||
| name: PropTypes.oneOf(violationNames).isRequired, | ||
| data: PropTypes.shape({ | ||
| rejectedBy: PropTypes.string, | ||
| rejectReason: PropTypes.string, | ||
| amount: PropTypes.string, | ||
| surcharge: PropTypes.number, | ||
| invoiceMarkup: PropTypes.number, | ||
| maxAge: PropTypes.number, | ||
| tagName: PropTypes.string, | ||
| formattedLimitAmount: PropTypes.string, | ||
| categoryLimit: PropTypes.string, | ||
| limit: PropTypes.string, | ||
| category: PropTypes.string, | ||
| brokenBankConnection: PropTypes.bool, | ||
| isAdmin: PropTypes.bool, | ||
| email: PropTypes.string, | ||
| isTransactionOlderThan7Days: PropTypes.bool, | ||
| member: PropTypes.string, | ||
| taxName: PropTypes.string, | ||
| }), | ||
| }); | ||
|
|
||
| const propTypes = { | ||
| /** The report currently being looked at */ | ||
| report: reportPropTypes.isRequired, | ||
|
|
@@ -61,6 +89,9 @@ const propTypes = { | |
| /** The transaction associated with the transactionThread */ | ||
| transaction: transactionPropTypes, | ||
|
|
||
| /** Violations detected in this transaction */ | ||
| transactionViolations: PropTypes.arrayOf(transactionViolationPropType), | ||
|
|
||
| /** Collection of tags attached to a policy */ | ||
| policyTags: tagPropTypes, | ||
|
|
||
|
|
@@ -76,10 +107,11 @@ const defaultProps = { | |
| currency: CONST.CURRENCY.USD, | ||
| comment: {comment: ''}, | ||
| }, | ||
| transactionViolations: [], | ||
| policyTags: {}, | ||
| }; | ||
|
|
||
| function MoneyRequestView({report, parentReport, parentReportActions, policyCategories, shouldShowHorizontalRule, transaction, policyTags, policy}) { | ||
| function MoneyRequestView({report, parentReport, parentReportActions, policyCategories, shouldShowHorizontalRule, transaction, policyTags, policy, transactionViolations}) { | ||
| const theme = useTheme(); | ||
| const styles = useThemeStyles(); | ||
| const StyleUtils = useStyleUtils(); | ||
|
|
@@ -131,6 +163,9 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate | |
| const shouldShowTag = isPolicyExpenseChat && (transactionTag || OptionsListUtils.hasEnabledOptions(lodashValues(policyTagsList))); | ||
| const shouldShowBillable = isPolicyExpenseChat && (transactionBillable || !lodashGet(policy, 'disabledFields.defaultBillable', true)); | ||
|
|
||
| const {getViolationsForField} = useViolations(transactionViolations); | ||
| const hasViolations = useCallback((field) => canUseViolations && getViolationsForField(field).length > 0, [canUseViolations, getViolationsForField]); | ||
|
|
||
| let amountDescription = `${translate('iou.amount')}`; | ||
|
|
||
| if (isCardTransaction) { | ||
|
|
@@ -198,6 +233,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate | |
| onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT))} | ||
| /> | ||
| )} | ||
| {canUseViolations && <ViolationMessages violations={getViolationsForField('receipt')} />} | ||
| <OfflineWithFeedback pendingAction={getPendingFieldAction('pendingFields.amount')}> | ||
| <MenuItemWithTopDescription | ||
| title={formattedTransactionAmount ? formattedTransactionAmount.toString() : ''} | ||
|
|
@@ -208,9 +244,10 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate | |
| interactive={canEditAmount} | ||
| shouldShowRightIcon={canEditAmount} | ||
| onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.AMOUNT))} | ||
| brickRoadIndicator={hasErrors && transactionAmount === 0 ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} | ||
| brickRoadIndicator={hasViolations('amount') || (hasErrors && transactionAmount === 0) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} | ||
| error={hasErrors && transactionAmount === 0 ? translate('common.error.enterAmount') : ''} | ||
| /> | ||
| {canUseViolations && <ViolationMessages violations={getViolationsForField('amount')} />} | ||
| </OfflineWithFeedback> | ||
| <OfflineWithFeedback pendingAction={getPendingFieldAction('pendingFields.comment')}> | ||
| <MenuItemWithTopDescription | ||
|
|
@@ -222,8 +259,10 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate | |
| titleStyle={styles.flex1} | ||
| onPress={() => 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 : ''} | ||
| numberOfLinesTitle={0} | ||
| /> | ||
| {canUseViolations && <ViolationMessages violations={getViolationsForField('comment')} />} | ||
| </OfflineWithFeedback> | ||
| {isDistanceRequest ? ( | ||
| <OfflineWithFeedback pendingAction={lodashGet(transaction, 'pendingFields.waypoints') || lodashGet(transaction, 'pendingAction')}> | ||
|
|
@@ -245,9 +284,10 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate | |
| shouldShowRightIcon={canEdit} | ||
| titleStyle={styles.flex1} | ||
| onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.MERCHANT))} | ||
| brickRoadIndicator={hasErrors && isEmptyMerchant ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} | ||
| brickRoadIndicator={hasViolations('merchant') || (hasErrors && isEmptyMerchant) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} | ||
| error={hasErrors && isEmptyMerchant ? translate('common.error.enterMerchant') : ''} | ||
| /> | ||
| {canUseViolations && <ViolationMessages violations={getViolationsForField('merchant')} />} | ||
| </OfflineWithFeedback> | ||
| )} | ||
| <OfflineWithFeedback pendingAction={getPendingFieldAction('pendingFields.created')}> | ||
|
|
@@ -258,9 +298,10 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate | |
| shouldShowRightIcon={canEdit && !isSettled} | ||
| titleStyle={styles.flex1} | ||
| onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DATE))} | ||
| brickRoadIndicator={hasErrors && transactionDate === '' ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} | ||
| brickRoadIndicator={hasViolations('date') || (hasErrors && transactionDate === '') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} | ||
| error={hasErrors && transactionDate === '' ? translate('common.error.enterDate') : ''} | ||
| /> | ||
| {canUseViolations && <ViolationMessages violations={getViolationsForField('date')} />} | ||
| </OfflineWithFeedback> | ||
| {shouldShowCategory && ( | ||
| <OfflineWithFeedback pendingAction={lodashGet(transaction, 'pendingFields.category') || lodashGet(transaction, 'pendingAction')}> | ||
|
|
@@ -271,7 +312,9 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate | |
| 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 : ''} | ||
| /> | ||
| {canUseViolations && <ViolationMessages violations={getViolationsForField('category')} />} | ||
| </OfflineWithFeedback> | ||
| )} | ||
| {shouldShowTag && ( | ||
|
|
@@ -283,7 +326,9 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate | |
| shouldShowRightIcon={canEdit} | ||
| 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 : ''} | ||
| /> | ||
| {canUseViolations && <ViolationMessages violations={getViolationsForField('tag')} />} | ||
| </OfflineWithFeedback> | ||
| )} | ||
| {isCardTransaction && ( | ||
|
|
@@ -295,15 +340,24 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate | |
| /> | ||
| </OfflineWithFeedback> | ||
| )} | ||
|
|
||
| {shouldShowBillable && ( | ||
| <View style={[styles.flexRow, styles.optionRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.ml5, styles.mr8]}> | ||
| <Text color={!transactionBillable ? theme.textSupporting : undefined}>{translate('common.billable')}</Text> | ||
| <Switch | ||
| accessibilityLabel={translate('common.billable')} | ||
| isOn={transactionBillable} | ||
| onToggle={(value) => IOU.editMoneyRequest(transaction, report.reportID, {billable: value})} | ||
| /> | ||
| </View> | ||
| <> | ||
| <View style={[styles.flexRow, styles.optionRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.ml5, styles.mr8]}> | ||
| <Text color={!transactionBillable ? theme.textSupporting : undefined}>{translate('common.billable')}</Text> | ||
| <Switch | ||
| accessibilityLabel={translate('common.billable')} | ||
| isOn={transactionBillable} | ||
| onToggle={(value) => IOU.editMoneyRequest(transaction, report.reportID, {billable: value})} | ||
| /> | ||
| </View> | ||
| {hasViolations('billable') && ( | ||
| <ViolationMessages | ||
| violations={getViolationsForField('billable')} | ||
| isLast | ||
| /> | ||
| )} | ||
| </> | ||
| )} | ||
| </View> | ||
| <SpacerView | ||
|
|
@@ -349,5 +403,15 @@ export default compose( | |
| return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`; | ||
| }, | ||
| }, | ||
| transactionViolation: { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's a typo here which I'm fixing here if you can review please @trevor-coleman . I was able to see the violations once I fixed it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch! Let me review |
||
| key: ({report}) => { | ||
| const parentReportAction = ReportActionsUtils.getParentReportAction(report); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @trevor-coleman
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi, I was a paid contractor and I'm no longer on this project.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tgolen can you do this as part of one of your PRs removing the method?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add it to the list. Thanks for responding @trevor-coleman and I hope you come back at some point! 👋
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! |
||
| const transactionID = lodashGet(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); | ||
| return `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`; | ||
| }, | ||
| }, | ||
| policyTags: { | ||
| key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report.policyID}`, | ||
| }, | ||
| }), | ||
| )(MoneyRequestView); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import React, {useMemo} from 'react'; | ||
|
hayata-suenaga marked this conversation as resolved.
|
||
| import {View} from 'react-native'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import ViolationsUtils from '@libs/ViolationsUtils'; | ||
| import type {TransactionViolation} from '@src/types/onyx'; | ||
| import Text from './Text'; | ||
|
|
||
| export default function ViolationMessages({violations, isLast}: {violations: TransactionViolation[]; isLast?: boolean}) { | ||
| const styles = useThemeStyles(); | ||
| const {translate} = useLocalize(); | ||
| const violationMessages = useMemo(() => violations.map((violation) => [violation.name, ViolationsUtils.getViolationTranslation(violation, translate)]), [translate, violations]); | ||
|
|
||
| return ( | ||
|
trevor-coleman marked this conversation as resolved.
|
||
| <View style={[styles.mtn2, isLast ? styles.mb2 : styles.mb1]}> | ||
| {violationMessages.map(([name, message]) => ( | ||
| <Text | ||
| key={`violationMessages.${name}`} | ||
| style={[styles.ph5, styles.textLabelError]} | ||
| > | ||
| {message} | ||
| </Text> | ||
| ))} | ||
| </View> | ||
| ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.