From fee898ec5c0ad16077669dca728acd8503ab4648 Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Thu, 21 Sep 2023 12:30:30 +0100 Subject: [PATCH 1/7] feat: support for editing a tag of a money request --- src/CONST.ts | 1 + .../ReportActionItem/MoneyRequestView.js | 38 ++++++++++++- src/libs/ReportUtils.js | 11 ++++ src/libs/TransactionUtils.js | 16 ++++++ src/libs/actions/IOU.js | 4 +- src/pages/EditRequestPage.js | 54 ++++++++++++++++--- src/pages/EditRequestTagPage.js | 53 ++++++++++++++++++ 7 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 src/pages/EditRequestTagPage.js diff --git a/src/CONST.ts b/src/CONST.ts index eed1b98ae55..e20ca428ee3 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1360,6 +1360,7 @@ const CONST = { MERCHANT: 'merchant', CATEGORY: 'category', RECEIPT: 'receipt', + TAG: 'tag', }, FOOTER: { EXPENSE_MANAGEMENT_URL: `${USE_EXPENSIFY_URL}/expense-management`, diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 178cab75a0c..853b998fc5c 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -1,5 +1,6 @@ import React, {useMemo} from 'react'; import {View} from 'react-native'; +import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import lodashValues from 'lodash/values'; @@ -32,6 +33,7 @@ import * as TransactionUtils from '../../libs/TransactionUtils'; import OfflineWithFeedback from '../OfflineWithFeedback'; import categoryPropTypes from '../categoryPropTypes'; import SpacerView from '../SpacerView'; +import tagPropTypes from '../tagPropTypes'; const propTypes = { /** The report currently being looked at */ @@ -53,6 +55,15 @@ const propTypes = { /** The transaction associated with the transactionThread */ transaction: transactionPropTypes, + /** Collection of tags attached to a policy */ + policyTags: PropTypes.objectOf( + PropTypes.shape({ + name: PropTypes.string, + required: PropTypes.bool, + tags: PropTypes.objectOf(tagPropTypes), + }), + ), + ...withCurrentUserPersonalDetailsPropTypes, }; @@ -65,9 +76,10 @@ const defaultProps = { currency: CONST.CURRENCY.USD, comment: {comment: ''}, }, + policyTags: {}, }; -function MoneyRequestView({betas, report, parentReport, policyCategories, shouldShowHorizontalRule, transaction}) { +function MoneyRequestView({report, betas, parentReport, policyCategories, shouldShowHorizontalRule, transaction, policyTags}) { const {isSmallScreenWidth} = useWindowDimensions(); const {translate} = useLocalize(); @@ -80,6 +92,7 @@ function MoneyRequestView({betas, report, parentReport, policyCategories, should comment: transactionDescription, merchant: transactionMerchant, category: transactionCategory, + tag: transactionTag, } = ReportUtils.getTransactionDetails(transaction); const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.UNKNOWN_MERCHANT || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; @@ -89,8 +102,14 @@ function MoneyRequestView({betas, report, parentReport, policyCategories, should const canEdit = ReportUtils.canEditMoneyRequest(parentReportAction); // A flag for verifying that the current report is a sub-report of a workspace chat const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]); - // A flag for showing categories + + // Fetches only the first tag, for now + const policyTagKey = _.first(_.keys(policyTags)); + const policyTagsList = lodashGet(policyTags, [policyTagKey, 'tags'], {}); + + // Flags for showing categories and tags const shouldShowCategory = isPolicyExpenseChat && Permissions.canUseCategories(betas) && (transactionCategory || OptionsListUtils.hasEnabledOptions(lodashValues(policyCategories))); + const shouldShowTag = isPolicyExpenseChat && Permissions.canUseTags(betas) && (transactionTag || OptionsListUtils.hasEnabledOptions(lodashValues(policyTagsList))); let description = `${translate('iou.amount')} • ${translate('iou.cash')}`; if (isSettled) { @@ -200,6 +219,18 @@ function MoneyRequestView({betas, report, parentReport, policyCategories, should /> )} + {shouldShowTag && ( + + Navigation.navigate(ROUTES.getEditRequestRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAG))} + /> + + )} `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report.policyID}`, + }, }), )(MoneyRequestView); diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index a5af66f0846..4a9eff1e1b6 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1329,6 +1329,7 @@ function getTransactionDetails(transaction) { comment: TransactionUtils.getDescription(transaction), merchant: TransactionUtils.getMerchant(transaction), category: TransactionUtils.getCategory(transaction), + tag: TransactionUtils.getTag(transaction), }; } @@ -1580,6 +1581,11 @@ function getModifiedExpenseMessage(reportAction) { if (hasModifiedCategory) { return getProperSchemaForModifiedExpenseMessage(reportActionOriginalMessage.category, reportActionOriginalMessage.oldCategory, Localize.translateLocal('common.category'), true); } + + const hasModifiedTag = _.has(reportActionOriginalMessage, 'oldTag') && _.has(reportActionOriginalMessage, 'tag'); + if (hasModifiedTag) { + return getProperSchemaForModifiedExpenseMessage(reportActionOriginalMessage.tag, reportActionOriginalMessage.oldTag, Localize.translateLocal('common.tag'), true); + } } /** @@ -1625,6 +1631,11 @@ function getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, i originalMessage.category = transactionChanges.category; } + if (_.has(transactionChanges, 'tag')) { + originalMessage.oldTag = TransactionUtils.getTag(oldTransaction); + originalMessage.tag = transactionChanges.tag; + } + return originalMessage; } diff --git a/src/libs/TransactionUtils.js b/src/libs/TransactionUtils.js index 5dcfbc467c2..e3fe84ff1af 100644 --- a/src/libs/TransactionUtils.js +++ b/src/libs/TransactionUtils.js @@ -151,6 +151,10 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep updatedTransaction.category = transactionChanges.category; } + if (_.has(transactionChanges, 'tag')) { + updatedTransaction.tag = transactionChanges.tag; + } + if (shouldStopSmartscan && _.has(transaction, 'receipt') && !_.isEmpty(transaction.receipt) && lodashGet(transaction, 'receipt.state') !== CONST.IOU.RECEIPT_STATE.OPEN) { updatedTransaction.receipt.state = CONST.IOU.RECEIPT_STATE.OPEN; } @@ -162,6 +166,7 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep ...(_.has(transactionChanges, 'currency') && {currency: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), ...(_.has(transactionChanges, 'merchant') && {merchant: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), ...(_.has(transactionChanges, 'category') && {category: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), + ...(_.has(transactionChanges, 'tag') && {merchant: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), }; return updatedTransaction; @@ -253,6 +258,16 @@ function getCategory(transaction) { return lodashGet(transaction, 'category', ''); } +/** + * Return the tag from the transaction. This "tag" field has no "modified" complement. + * + * @param {Object} transaction + * @return {String} + */ +function getTag(transaction) { + return lodashGet(transaction, 'tag', ''); +} + /** * Return the created field from the transaction, return the modifiedCreated if present. * @@ -399,6 +414,7 @@ export { getMerchant, getCreated, getCategory, + getTag, getLinkedTransaction, getAllReportTransactions, hasReceipt, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 36d512c8d84..e93272cca8f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1192,6 +1192,7 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC currency: null, merchant: null, category: null, + tag: null, }, }, }, @@ -1230,7 +1231,7 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC ]; // STEP 6: Call the API endpoint - const {created, amount, currency, comment, merchant, category} = ReportUtils.getTransactionDetails(updatedTransaction); + const {created, amount, currency, comment, merchant, category, tag} = ReportUtils.getTransactionDetails(updatedTransaction); API.write( 'EditMoneyRequest', { @@ -1242,6 +1243,7 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC comment, merchant, category, + tag, }, {optimisticData, successData, failureData}, ); diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index fedbc61a6e1..4fcfa7398d5 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -1,26 +1,29 @@ import React, {useEffect} from 'react'; import PropTypes from 'prop-types'; +import _ from 'underscore'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; -import compose from '../libs/compose'; import CONST from '../CONST'; -import Navigation from '../libs/Navigation/Navigation'; import ONYXKEYS from '../ONYXKEYS'; +import compose from '../libs/compose'; +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 * as Policy from '../libs/actions/Policy'; +import * as IOU from '../libs/actions/IOU'; +import * as CurrencyUtils from '../libs/CurrencyUtils'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '../components/withCurrentUserPersonalDetails'; +import tagPropTypes from '../components/tagPropTypes'; +import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView'; import EditRequestDescriptionPage from './EditRequestDescriptionPage'; import EditRequestMerchantPage from './EditRequestMerchantPage'; import EditRequestCreatedPage from './EditRequestCreatedPage'; import EditRequestAmountPage from './EditRequestAmountPage'; import EditRequestReceiptPage from './EditRequestReceiptPage'; -import reportPropTypes from './reportPropTypes'; -import * as IOU from '../libs/actions/IOU'; -import * as CurrencyUtils from '../libs/CurrencyUtils'; -import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView'; import EditRequestCategoryPage from './EditRequestCategoryPage'; +import EditRequestTagPage from './EditRequestTagPage'; +import reportPropTypes from './reportPropTypes'; const propTypes = { /** Route from navigation */ @@ -35,6 +38,7 @@ const propTypes = { }), }).isRequired, + /** Onyx props */ /** The report object for the thread report */ report: reportPropTypes, @@ -56,6 +60,15 @@ const propTypes = { email: PropTypes.string, }), + /** Collection of tags attached to a policy */ + policyTags: PropTypes.objectOf( + PropTypes.shape({ + name: PropTypes.string, + required: PropTypes.bool, + tags: PropTypes.objectOf(tagPropTypes), + }), + ), + ...withCurrentUserPersonalDetailsPropTypes, }; @@ -66,9 +79,10 @@ const defaultProps = { session: { email: null, }, + policyTags: {}, }; -function EditRequestPage({report, route, parentReport, policy, session}) { +function EditRequestPage({report, route, parentReport, policy, session, policyTags}) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); const transaction = TransactionUtils.getLinkedTransaction(parentReportAction); const { @@ -77,6 +91,7 @@ function EditRequestPage({report, route, parentReport, policy, session}) { comment: transactionDescription, merchant: transactionMerchant, category: transactionCategory, + tag: transactionTag, } = ReportUtils.getTransactionDetails(transaction); const defaultCurrency = lodashGet(route, 'params.currency', '') || transactionCurrency; @@ -92,6 +107,9 @@ function EditRequestPage({report, route, parentReport, policy, session}) { const isRequestor = ReportUtils.isMoneyRequestReport(parentReport) && lodashGet(session, 'accountID', null) === parentReportAction.actorAccountID; const canEdit = !isSettled && !isDeleted && (isAdmin || isRequestor); + // For now, it always defaults to the first tag of the policy + const tagName = _.first(_.keys(policyTags)); + // Dismiss the modal when the request is paid or deleted useEffect(() => { if (canEdit) { @@ -196,6 +214,25 @@ function EditRequestPage({report, route, parentReport, policy, session}) { ); } + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.TAG) { + return ( + { + let updatedTag = transactionChanges.tag; + + // In case the same tag has been selected, reset the tag. + if (transactionTag === updatedTag) { + updatedTag = ''; + } + editMoneyRequest({tag: updatedTag}); + }} + /> + ); + } + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.RECEIPT) { return ( `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, }, + policyTags: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, + }, }), )(EditRequestPage); diff --git a/src/pages/EditRequestTagPage.js b/src/pages/EditRequestTagPage.js new file mode 100644 index 00000000000..72ed072eec1 --- /dev/null +++ b/src/pages/EditRequestTagPage.js @@ -0,0 +1,53 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Navigation from '../libs/Navigation/Navigation'; +import useLocalize from '../hooks/useLocalize'; +import ScreenWrapper from '../components/ScreenWrapper'; +import HeaderWithBackButton from '../components/HeaderWithBackButton'; +import TagPicker from '../components/TagPicker'; + +const propTypes = { + /** Transaction default tag value */ + defaultTag: PropTypes.string.isRequired, + + /** The policyID we are getting tags for */ + policyID: PropTypes.string.isRequired, + + /** The tag name to which the default tag belongs to */ + tagName: PropTypes.string.isRequired, + + /** Callback to fire when the Save button is pressed */ + onSubmit: PropTypes.func.isRequired, +}; + +function EditRequestTagPage({defaultTag, policyID, tagName, onSubmit}) { + const {translate} = useLocalize(); + + const selectTag = (tag) => { + onSubmit({tag: tag.searchText}); + }; + + return ( + + + + + + ); +} + +EditRequestTagPage.propTypes = propTypes; +EditRequestTagPage.displayName = 'EditRequestTagPage'; + +export default EditRequestTagPage; From cdb89ea6677b003063c00892dc7b4d446f9fb0eb Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Thu, 21 Sep 2023 14:35:18 +0100 Subject: [PATCH 2/7] feat: pass tag list name to the modified expense message --- .../MoneyRequestConfirmationList.js | 11 +++--- .../ReportActionItem/MoneyRequestView.js | 8 ++--- src/libs/PolicyUtils.js | 35 +++++++++++++++++++ src/libs/ReportUtils.js | 8 ++++- src/pages/EditRequestPage.js | 8 ++--- src/pages/iou/MoneyRequestTagPage.js | 8 ++--- 6 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 13471407914..b9a744b2e71 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -38,6 +38,7 @@ import transactionPropTypes from './transactionPropTypes'; import DistanceRequestUtils from '../libs/DistanceRequestUtils'; import * as IOU from '../libs/actions/IOU'; import * as TransactionUtils from '../libs/TransactionUtils'; +import * as PolicyUtils from '../libs/PolicyUtils'; const propTypes = { /** Callback to inform parent modal of success */ @@ -202,12 +203,12 @@ function MoneyRequestConfirmationList(props) { const shouldShowCategories = isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories)); // Fetches the first tag list of the policy - const tagListKey = _.first(_.keys(props.policyTags)); - const tagList = lodashGet(props.policyTags, [tagListKey, 'tags'], []); - const tagListName = lodashGet(props.policyTags, [tagListKey, 'name'], ''); + const policyTag = PolicyUtils.getTag(props.policyTags); + const policyTagList = lodashGet(policyTag, 'tags', {}); + const policyTagListName = lodashGet(policyTag, 'name', translate('common.tag')); const canUseTags = Permissions.canUseTags(props.betas); // A flag for showing the tags field - const shouldShowTags = isPolicyExpenseChat && canUseTags && _.any(tagList, (tag) => tag.enabled); + const shouldShowTags = isPolicyExpenseChat && canUseTags && OptionsListUtils.hasEnabledOptions(_.values(policyTagList)); // A flag for showing the billable field const shouldShowBillable = canUseTags && !lodashGet(props.policy, 'disabledFields.defaultBillable', true); @@ -541,7 +542,7 @@ function MoneyRequestConfirmationList(props) { Navigation.navigate(ROUTES.getMoneyRequestTagRoute(props.iouType, props.reportID))} style={[styles.moneyRequestMenuItem, styles.mb2]} disabled={didConfirm || props.isReadOnly} diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 853b998fc5c..0c996e19bf6 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -1,6 +1,5 @@ import React, {useMemo} from 'react'; import {View} from 'react-native'; -import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import lodashValues from 'lodash/values'; @@ -18,6 +17,7 @@ import * as ReportUtils from '../../libs/ReportUtils'; import * as OptionsListUtils from '../../libs/OptionsListUtils'; import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; import * as StyleUtils from '../../styles/StyleUtils'; +import * as PolicyUtils from '../../libs/PolicyUtils'; import CONST from '../../CONST'; import * as Expensicons from '../Icon/Expensicons'; import iouReportPropTypes from '../../pages/iouReportPropTypes'; @@ -104,8 +104,8 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]); // Fetches only the first tag, for now - const policyTagKey = _.first(_.keys(policyTags)); - const policyTagsList = lodashGet(policyTags, [policyTagKey, 'tags'], {}); + const policyTag = PolicyUtils.getTag(policyTags); + const policyTagsList = lodashGet(policyTag, 'tags', {}); // Flags for showing categories and tags const shouldShowCategory = isPolicyExpenseChat && Permissions.canUseCategories(betas) && (transactionCategory || OptionsListUtils.hasEnabledOptions(lodashValues(policyCategories))); @@ -222,7 +222,7 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should {shouldShowTag && ( { @@ -218,7 +218,7 @@ function EditRequestPage({report, route, parentReport, policy, session, policyTa return ( { let updatedTag = transactionChanges.tag; @@ -227,7 +227,7 @@ function EditRequestPage({report, route, parentReport, policy, session, policyTa if (transactionTag === updatedTag) { updatedTag = ''; } - editMoneyRequest({tag: updatedTag}); + editMoneyRequest({tag: updatedTag, tagListName}); }} /> ); diff --git a/src/pages/iou/MoneyRequestTagPage.js b/src/pages/iou/MoneyRequestTagPage.js index c9b5eb4f8f6..f3f00225018 100644 --- a/src/pages/iou/MoneyRequestTagPage.js +++ b/src/pages/iou/MoneyRequestTagPage.js @@ -6,6 +6,7 @@ import {withOnyx} from 'react-native-onyx'; import compose from '../../libs/compose'; import ROUTES from '../../ROUTES'; import * as IOU from '../../libs/actions/IOU'; +import * as PolicyUtils from '../../libs/PolicyUtils'; import Navigation from '../../libs/Navigation/Navigation'; import useLocalize from '../../hooks/useLocalize'; import ScreenWrapper from '../../components/ScreenWrapper'; @@ -60,8 +61,7 @@ function MoneyRequestTagPage({route, report, policyTags, iou}) { // Fetches the first tag list of the policy const tagListKey = _.first(_.keys(policyTags)); - const tagList = lodashGet(policyTags, tagListKey, {}); - const tagListName = lodashGet(tagList, 'name', translate('common.tag')); + const policyTagListName = PolicyUtils.getTagListName(policyTags) || translate('common.tag'); const navigateBack = () => { Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, report.reportID)); @@ -82,10 +82,10 @@ function MoneyRequestTagPage({route, report, policyTags, iou}) { shouldEnableMaxHeight > - {translate('iou.tagSelection', {tagName: tagListName})} + {translate('iou.tagSelection', {tagName: policyTagListName})} Date: Thu, 21 Sep 2023 15:12:27 +0100 Subject: [PATCH 3/7] feat: update recently used tags on edit money request --- src/components/TagPicker/index.js | 3 ++- src/libs/PolicyUtils.js | 18 ++++++++++++++++++ src/libs/actions/IOU.js | 19 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/components/TagPicker/index.js b/src/components/TagPicker/index.js index c46ca1b57b2..8e7cf11f7e5 100644 --- a/src/components/TagPicker/index.js +++ b/src/components/TagPicker/index.js @@ -7,6 +7,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import styles from '../../styles/styles'; import useLocalize from '../../hooks/useLocalize'; import * as OptionsListUtils from '../../libs/OptionsListUtils'; +import * as PolicyUtils from '../../libs/PolicyUtils'; import OptionsSelector from '../OptionsSelector'; import {propTypes, defaultProps} from './tagPickerPropTypes'; @@ -15,7 +16,7 @@ function TagPicker({selectedTag, tag, policyTags, policyRecentlyUsedTags, onSubm const [searchValue, setSearchValue] = useState(''); const policyRecentlyUsedTagsList = lodashGet(policyRecentlyUsedTags, tag, []); - const policyTagList = lodashGet(policyTags, [tag, 'tags'], {}); + const policyTagList = PolicyUtils.getTagList(policyTags, tag); const policyTagsCount = _.size(_.filter(policyTagList, (policyTag) => policyTag.enabled)); const isTagsCountBelowThreshold = policyTagsCount < CONST.TAG_LIST_THRESHOLD; diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index bf1ca1c4168..616e3dba2d6 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -199,6 +199,23 @@ function getTagListName(policyTags) { return lodashGet(policyTags, [_.first(policyTagKeys), 'name'], ''); } +/** + * Fetches the tags of a policy for a specific key. Defaults to the first tag if no key is provided. + * + * @param {Object} policyTags + * @param {String} [tagKey] + * @returns {String} + */ +function getTagList(policyTags, tagKey) { + if (!policyTags) { + return {}; + } + + const policyTagKey = tagKey || _.first(_.keys(policyTags)); + + return lodashGet(policyTags, [policyTagKey, 'tags'], {}); +} + export { getActivePolicies, hasPolicyMemberError, @@ -214,4 +231,5 @@ export { getIneligibleInvitees, getTag, getTagListName, + getTagList, }; diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index e93272cca8f..9d9be6ee8ba 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1147,6 +1147,17 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC updatedChatReport.lastMessageHtml = messageText; } + const optimisticPolicyRecentlyUsedTags = {}; + if (_.has(transactionChanges, 'tag')) { + const tagListName = transactionChanges.tagListName; + const recentlyUsedPolicyTags = allRecentlyUsedTags[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport.policyID}`]; + + if (recentlyUsedPolicyTags) { + const uniquePolicyRecentlyUsedTags = _.filter(recentlyUsedPolicyTags[tagListName], (recentlyUsedPolicyTag) => recentlyUsedPolicyTag !== transactionChanges.tag); + optimisticPolicyRecentlyUsedTags[tagListName] = [transactionChanges.tag, ...uniquePolicyRecentlyUsedTags]; + } + } + // STEP 4: Compose the optimistic data const optimisticData = [ { @@ -1173,6 +1184,14 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC }, ]; + if (!_.isEmpty(optimisticPolicyRecentlyUsedTags)) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport.policyID}`, + value: optimisticPolicyRecentlyUsedTags, + }); + } + const successData = [ { onyxMethod: Onyx.METHOD.MERGE, From 063244aadd2e0fbdde02fc99709e1a4e6db2125f Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Thu, 21 Sep 2023 16:49:10 +0100 Subject: [PATCH 4/7] fix: better prop types and small fixes --- src/components/MoneyRequestConfirmationList.js | 8 +------- .../ReportActionItem/MoneyRequestView.js | 10 ++-------- src/components/TagPicker/tagPickerPropTypes.js | 7 +------ src/components/tagPropTypes.js | 10 +++++++++- src/libs/PolicyUtils.js | 14 +++++++------- src/libs/TransactionUtils.js | 2 +- src/pages/EditRequestPage.js | 8 +------- src/pages/iou/MoneyRequestTagPage.js | 7 +------ 8 files changed, 23 insertions(+), 43 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index b9a744b2e71..1ebc4461c96 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -143,13 +143,7 @@ const propTypes = { policyCategories: PropTypes.objectOf(categoryPropTypes), /** Collection of tags attached to a policy */ - policyTags: PropTypes.objectOf( - PropTypes.shape({ - name: PropTypes.string, - required: PropTypes.bool, - tags: PropTypes.objectOf(tagPropTypes), - }), - ), + policyTags: tagPropTypes, }; const defaultProps = { diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 0c996e19bf6..b673881c192 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -56,13 +56,7 @@ const propTypes = { transaction: transactionPropTypes, /** Collection of tags attached to a policy */ - policyTags: PropTypes.objectOf( - PropTypes.shape({ - name: PropTypes.string, - required: PropTypes.bool, - tags: PropTypes.objectOf(tagPropTypes), - }), - ), + policyTags: tagPropTypes, ...withCurrentUserPersonalDetailsPropTypes, }; @@ -222,7 +216,7 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should {shouldShowTag && ( Date: Mon, 25 Sep 2023 09:52:23 +0100 Subject: [PATCH 5/7] fix: small review fixes --- src/components/ReportActionItem/MoneyRequestView.js | 4 ++-- src/libs/PolicyUtils.js | 6 +++--- src/libs/ReportUtils.js | 8 +------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index b077b0da4f0..0220bbdf520 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -214,14 +214,14 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should )} {shouldShowTag && ( - + Navigation.navigate(ROUTES.getEditRequestRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAG))} + onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAG))} /> )} diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 21a88b9227b..347a825f59c 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -208,11 +208,11 @@ function getIneligibleInvitees(policyMembers, personalDetails) { * * @param {Object} policyTags * @param {String} [tagKey] - * @returns {String} + * @returns {Object} */ function getTag(policyTags, tagKey) { if (_.isEmpty(policyTags)) { - return ''; + return {}; } const policyTagKey = tagKey || _.first(_.keys(policyTags)); @@ -252,7 +252,7 @@ function getTagList(policyTags, tagKey) { return lodashGet(policyTags, [policyTagKey, 'tags'], {}); } - + /** * @param {Object} policy * @returns {Boolean} diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 27401f52a5d..fbc6f6a7303 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1596,12 +1596,7 @@ function getModifiedExpenseMessage(reportAction) { const hasModifiedTag = _.has(reportActionOriginalMessage, 'oldTag') && _.has(reportActionOriginalMessage, 'tag'); if (hasModifiedTag) { - return getProperSchemaForModifiedExpenseMessage( - reportActionOriginalMessage.tag, - reportActionOriginalMessage.oldTag, - reportActionOriginalMessage.tagListName || Localize.translateLocal('common.tag'), - true, - ); + return getProperSchemaForModifiedExpenseMessage(reportActionOriginalMessage.tag, reportActionOriginalMessage.oldTag, Localize.translateLocal('common.tag'), true); } } @@ -1651,7 +1646,6 @@ function getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, i if (_.has(transactionChanges, 'tag')) { originalMessage.oldTag = TransactionUtils.getTag(oldTransaction); originalMessage.tag = transactionChanges.tag; - originalMessage.tagListName = transactionChanges.tagListName; } return originalMessage; From 472eabc734055efcd4ee636c3a5fcdee08adfbf9 Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Mon, 25 Sep 2023 10:41:52 +0100 Subject: [PATCH 6/7] fix: navigation back when reloading on the edit page --- src/pages/EditRequestPage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 24ed08a8204..7a19df7af8d 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -117,7 +117,7 @@ function EditRequestPage({report, route, parentReport, policy, session, policyTa // Update the transaction object and close the modal function editMoneyRequest(transactionChanges) { IOU.editMoneyRequest(transaction.transactionID, report.reportID, transactionChanges); - Navigation.dismissModal(); + Navigation.dismissModal(report.reportID); } if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DESCRIPTION) { @@ -215,6 +215,7 @@ function EditRequestPage({report, route, parentReport, policy, session, policyTa tagName={tagListName} policyID={lodashGet(report, 'policyID', '')} onSubmit={(transactionChanges) => { + console.log("[MINE] Update tag", transactionChanges.tag, transactionTag); let updatedTag = transactionChanges.tag; // In case the same tag has been selected, reset the tag. From 02c218fb79b60dd21da3a1c14637d8e9c4f2afd2 Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Mon, 25 Sep 2023 10:43:08 +0100 Subject: [PATCH 7/7] revert: log --- src/pages/EditRequestPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 7a19df7af8d..5e6e0dd3f17 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -215,7 +215,6 @@ function EditRequestPage({report, route, parentReport, policy, session, policyTa tagName={tagListName} policyID={lodashGet(report, 'policyID', '')} onSubmit={(transactionChanges) => { - console.log("[MINE] Update tag", transactionChanges.tag, transactionTag); let updatedTag = transactionChanges.tag; // In case the same tag has been selected, reset the tag.