Skip to content
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

#26126: Tag menu item and Tag picker (1st PR) #26954

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
002bcf0
feat: tag picker component
BeeMargarida Sep 7, 2023
c0e2352
feat: money request tag page and tag menu item
BeeMargarida Sep 7, 2023
6e6ec88
feat: pass IOU tag to confirmation list
BeeMargarida Sep 7, 2023
412e191
feat: fetch reportID from IOU participants
BeeMargarida Sep 7, 2023
e2cf2b5
feat: add missing translation
BeeMargarida Sep 7, 2023
7aca47c
fix: small typo
BeeMargarida Sep 7, 2023
601231f
fix: lint errors
BeeMargarida Sep 7, 2023
51b6979
docs: add missing comment for recently used tags of a policy
BeeMargarida Sep 7, 2023
92cf2a6
feat: add type and change collection key according to category
BeeMargarida Sep 8, 2023
68a3893
refactor: rename method
BeeMargarida Sep 8, 2023
b4f8adf
fix: review improvements
BeeMargarida Sep 11, 2023
1112ae6
Merge branch 'main' into feat/26126-tag_menu_item_and_picker
BeeMargarida Sep 11, 2023
feb8020
fix: small lint error
BeeMargarida Sep 11, 2023
40bd151
fix: small linter fix
BeeMargarida Sep 11, 2023
1a78dfd
Merge branch 'main' into feat/26126-tag_menu_item_and_picker
BeeMargarida Sep 11, 2023
b7fd431
fix: add missing types and fix proptypes
BeeMargarida Sep 12, 2023
65a6b48
feat: adapt to changes in structure and pass current tag around
BeeMargarida Sep 12, 2023
5c9738e
Merge branch 'main' into feat/26126-tag_menu_item_and_picker
BeeMargarida Sep 12, 2023
016ad53
feat: udpate ES translation for tag selection
BeeMargarida Sep 12, 2023
9b7d7c1
revert: use only first tag of policy
BeeMargarida Sep 12, 2023
77b853d
feat: remove tag from route and use hardcoded value
BeeMargarida Sep 12, 2023
c42584c
fix: review fixes
BeeMargarida Sep 12, 2023
3f40a65
fix: small comment fix and code simplification
BeeMargarida Sep 13, 2023
2a0d68b
Merge branch 'main' into feat/26126-tag_menu_item_and_picker
BeeMargarida Sep 13, 2023
40eabd8
fix: remove duplicated key from merge with main
BeeMargarida Sep 13, 2023
02d1ddd
fix: remove duplicated key
BeeMargarida Sep 13, 2023
298193d
Merge branch 'main' into feat/26126-tag_menu_item_and_picker
BeeMargarida Sep 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ const CONST = {
THREADS: 'threads',
CUSTOM_STATUS: 'customStatus',
NEW_DOT_CATEGORIES: 'newDotCategories',
NEW_DOT_TAGS: 'newDotTags',
amyevans marked this conversation as resolved.
Show resolved Hide resolved
},
BUTTON_STATES: {
DEFAULT: 'default',
Expand Down
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ const ONYXKEYS = {
POLICY_MEMBERS: 'policyMembers_',
POLICY_CATEGORIES: 'policyCategories_',
POLICY_RECENTLY_USED_CATEGORIES: 'policyRecentlyUsedCategories_',
POLICY_TAGS: 'policyTags_',
POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_',
WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_',
REPORT: 'report_',
REPORT_ACTIONS: 'reportActions_',
Expand Down Expand Up @@ -376,6 +378,7 @@ type OnyxValues = {
[ONYXKEYS.COLLECTION.DOWNLOAD]: OnyxTypes.Download;
[ONYXKEYS.COLLECTION.POLICY]: OnyxTypes.Policy;
[ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategory;
[ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTag;
[ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMember;
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories;
[ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMember;
Expand All @@ -390,6 +393,7 @@ type OnyxValues = {
[ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: boolean;
[ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup;
[ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction;
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags;
BeeMargarida marked this conversation as resolved.
Show resolved Hide resolved
[ONYXKEYS.COLLECTION.SELECTED_TAB]: string;

// Forms
Expand Down
2 changes: 2 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export default {
MONEY_REQUEST_CURRENCY: ':iouType/new/currency/:reportID?',
MONEY_REQUEST_DESCRIPTION: ':iouType/new/description/:reportID?',
MONEY_REQUEST_CATEGORY: ':iouType/new/category/:reportID?',
MONEY_REQUEST_TAG: ':iouType/new/tag/:reportID?',
MONEY_REQUEST_MERCHANT: ':iouType/new/merchant/:reportID?',
MONEY_REQUEST_MANUAL_TAB: ':iouType/new/:reportID?/manual',
MONEY_REQUEST_SCAN_TAB: ':iouType/new/:reportID?/scan',
Expand All @@ -117,6 +118,7 @@ export default {
getMoneyRequestMerchantRoute: (iouType: string, reportID = '') => `${iouType}/new/merchant/${reportID}`,
getMoneyRequestDistanceTabRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}/distance`,
getMoneyRequestWaypointRoute: (iouType: string, waypointIndex: number) => `${iouType}/new/waypoint/${waypointIndex}`,
getMoneyRequestTagRoute: (iouType: string, reportID = '') => `${iouType}/new/tag/${reportID}`,
SPLIT_BILL_DETAILS: `r/:reportID/split/:reportActionID`,
getSplitBillDetailsRoute: (reportID: string, reportActionID: string) => `r/${reportID}/split/${reportActionID}`,
getNewTaskRoute: (reportID: string) => `${NEW_TASK}/${reportID}`,
Expand Down
44 changes: 39 additions & 5 deletions src/components/MoneyRequestConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import lodashGet from 'lodash/get';
import styles from '../styles/styles';
import * as ReportUtils from '../libs/ReportUtils';
import * as OptionsListUtils from '../libs/OptionsListUtils';
import Permissions from '../libs/Permissions';
import OptionsSelector from './OptionsSelector';
import ONYXKEYS from '../ONYXKEYS';
import compose from '../libs/compose';
Expand All @@ -29,11 +30,11 @@ import Image from './Image';
import useLocalize from '../hooks/useLocalize';
import * as ReceiptUtils from '../libs/ReceiptUtils';
import categoryPropTypes from './categoryPropTypes';
import tagPropTypes from './tagPropTypes';
import ConfirmedRoute from './ConfirmedRoute';
import transactionPropTypes from './transactionPropTypes';
import DistanceRequestUtils from '../libs/DistanceRequestUtils';
import * as IOU from '../libs/actions/IOU';
import Permissions from '../libs/Permissions';

const propTypes = {
/** Callback to inform parent modal of success */
Expand Down Expand Up @@ -69,6 +70,9 @@ const propTypes = {
/** IOU Category */
iouCategory: PropTypes.string,

/** IOU Tag */
iouTag: PropTypes.string,

/** Selected participants from MoneyRequestModal with login / accountID */
selectedParticipants: PropTypes.arrayOf(optionPropTypes).isRequired,

Expand Down Expand Up @@ -109,10 +113,6 @@ const propTypes = {
/** List styles for OptionsSelector */
listStyles: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),

/* Onyx Props */
/** Collection of categories attached to a policy */
policyCategories: PropTypes.objectOf(categoryPropTypes),

/** ID of the transaction that represents the money request */
transactionID: PropTypes.string,

Expand All @@ -133,6 +133,19 @@ const propTypes = {

/** Whether the money request is a distance request */
isDistanceRequest: PropTypes.bool,

/* Onyx Props */
/** Collection of categories attached to a policy */
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),
}),
),
};

const defaultProps = {
Expand All @@ -141,6 +154,7 @@ const defaultProps = {
onSelectParticipant: () => {},
iouType: CONST.IOU.MONEY_REQUEST_TYPE.REQUEST,
iouCategory: '',
iouTag: '',
payeePersonalDetails: null,
canModifyParticipants: false,
isReadOnly: false,
Expand All @@ -156,6 +170,7 @@ const defaultProps = {
receiptSource: '',
listStyles: [],
policyCategories: {},
policyTags: {},
transactionID: '',
transaction: {},
mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'},
Expand All @@ -178,6 +193,12 @@ function MoneyRequestConfirmationList(props) {
const shouldCalculateDistanceAmount = props.isDistanceRequest && props.iouAmount === 0;
const shouldCategoryBeEditable = !_.isEmpty(props.policyCategories) && Permissions.canUseCategories(props.betas);

// 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 canUseTags = Permissions.canUseTags(props.betas);

const formattedAmount = CurrencyUtils.convertToDisplayString(
shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : props.iouAmount,
props.isDistanceRequest ? currency : props.iouCurrencyCode,
Expand Down Expand Up @@ -499,6 +520,16 @@ function MoneyRequestConfirmationList(props) {
disabled={didConfirm || props.isReadOnly}
/>
)}
{canUseTags && !!tagList && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed this line is incorrect, we should use !_.isEmpty(tagList) instead of !!tagList. @BeeMargarida can you add that change to your next PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yap!

<MenuItemWithTopDescription
shouldShowRightIcon={!props.isReadOnly}
title={props.iouTag}
description={tagListName || translate('common.tag')}
onPress={() => Navigation.navigate(ROUTES.getMoneyRequestTagRoute(props.iouType, props.reportID))}
style={[styles.moneyRequestMenuItem, styles.mb2]}
disabled={didConfirm || props.isReadOnly}
/>
)}
</>
)}
</OptionsSelector>
Expand All @@ -520,6 +551,9 @@ export default compose(
policyCategories: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`,
},
policyTags: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`,
},
mileageRate: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
selector: DistanceRequestUtils.getDefaultMileageRate,
Expand Down
90 changes: 90 additions & 0 deletions src/components/TagPicker/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, {useMemo} from 'react';
import _ from 'underscore';
import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
import ONYXKEYS from '../../ONYXKEYS';
import styles from '../../styles/styles';
import Navigation from '../../libs/Navigation/Navigation';
import ROUTES from '../../ROUTES';
import useLocalize from '../../hooks/useLocalize';
import * as OptionsListUtils from '../../libs/OptionsListUtils';
import OptionsSelector from '../OptionsSelector';
import {propTypes, defaultProps} from './tagPickerPropTypes';

function TagPicker({policyTags, reportID, tag, iouType, iou}) {
const {translate} = useLocalize();

const selectedOptions = useMemo(() => {
BeeMargarida marked this conversation as resolved.
Show resolved Hide resolved
if (!iou.tag) {
return [];
}

return [
{
name: iou.tag,
enabled: true,
},
];
}, [iou.tag]);

// Only shows one section, which will be the default behavior if there are
// less than 8 policy tags
// TODO: support sections with search
const sections = useMemo(() => {
const tagList = _.chain(lodashGet(policyTags, [tag, 'tags'], {}))
.values()
.map((t) => ({
text: t.name,
keyForList: t.name,
tooltipText: t.name,
}))
.value();

return [
{
data: tagList,
},
];
}, [policyTags, tag]);

const headerMessage = OptionsListUtils.getHeaderMessage(lodashGet(sections, '[0].data.length', 0) > 0, false, '');

const navigateBack = () => {
Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID));
};

const updateTag = () => {
// TODO: add logic to save the selected tag
navigateBack();
};

return (
<OptionsSelector
BeeMargarida marked this conversation as resolved.
Show resolved Hide resolved
optionHoveredStyle={styles.hoveredComponentBG}
sections={sections}
selectedOptions={selectedOptions}
headerMessage={headerMessage}
textInputLabel={translate('common.search')}
boldStyle
value=""
onSelectRow={updateTag}
shouldShowTextInput={false}
/>
);
}

TagPicker.displayName = 'TagPicker';
TagPicker.propTypes = propTypes;
TagPicker.defaultProps = defaultProps;

export default withOnyx({
policyTags: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`,
},
policyRecentlyUsedTags: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`,
},
iou: {
key: ONYXKEYS.IOU,
},
})(TagPicker);
43 changes: 43 additions & 0 deletions src/components/TagPicker/tagPickerPropTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import PropTypes from 'prop-types';
import tagPropTypes from '../tagPropTypes';
import {iouPropTypes, iouDefaultProps} from '../../pages/iou/propTypes';

const propTypes = {
/** The report ID of the IOU */
reportID: PropTypes.string.isRequired,

/** The policyID we are getting tags for */
policyID: PropTypes.string.isRequired,

/** The name of tag list we are getting tags for */
tag: PropTypes.string.isRequired,

/** The type of IOU report, i.e. bill, request, send */
iouType: PropTypes.string.isRequired,

/** Callback to submit the selected tag */
onSubmit: PropTypes.func,

/* Onyx Props */
/** Collection of tags attached to a policy */
policyTags: PropTypes.objectOf(
PropTypes.shape({
name: PropTypes.string,
tags: PropTypes.objectOf(tagPropTypes),
}),
),

/** List of recently used tags */
policyRecentlyUsedTags: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.string)),

/** Holds data related to Money Request view state, rather than the underlying Money Request data. */
iou: iouPropTypes,
};

const defaultProps = {
policyTags: {},
policyRecentlyUsedTags: {},
iou: iouDefaultProps,
};

export {propTypes, defaultProps};
12 changes: 12 additions & 0 deletions src/components/tagPropTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import PropTypes from 'prop-types';

export default PropTypes.shape({
amyevans marked this conversation as resolved.
Show resolved Hide resolved
/** Name of a tag */
name: PropTypes.string.isRequired,

/** Flag that determines if a tag is active and able to be selected */
enabled: PropTypes.bool.isRequired,
BeeMargarida marked this conversation as resolved.
Show resolved Hide resolved

/** "General Ledger code" that corresponds to this tag in an accounting system. Similar to an ID. */
'GL Code': PropTypes.string,
});
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import type {
SetTheRequestParams,
UpdatedTheRequestParams,
RemovedTheRequestParams,
TagSelectionParams,
TranslationBase,
} from './types';
import * as ReportActionsUtils from '../libs/ReportActionsUtils';
Expand Down Expand Up @@ -243,6 +244,7 @@ export default {
showMore: 'Show more',
merchant: 'Merchant',
category: 'Category',
tag: 'Tag',
receipt: 'Receipt',
replace: 'Replace',
distance: 'Distance',
Expand Down Expand Up @@ -544,6 +546,7 @@ export default {
`changed the ${valueName} to ${newValueToDisplay} (previously ${oldValueToDisplay})`,
threadRequestReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `${formattedAmount} request${comment ? ` for ${comment}` : ''}`,
threadSentMoneyReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} sent${comment ? ` for ${comment}` : ''}`,
tagSelection: ({tagName}: TagSelectionParams) => `Select a ${tagName} to add additional organization to your money`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this copy was checked? It does not really read like a proper English sentence. We're going to create an issue to modify it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the copy that was in the mocks in the design doc, but yeah I am not sure if it was run through marketing prior to landing in the mocks (I did not run it through after, that much I know 😅). I agree it's a bit awkward wording and could be improved.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed here: #38242

error: {
invalidSplit: 'Split amounts do not equal total amount',
other: 'Unexpected error, please try again later',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import type {
SetTheRequestParams,
UpdatedTheRequestParams,
RemovedTheRequestParams,
TagSelectionParams,
EnglishTranslation,
} from './types';

Expand Down Expand Up @@ -233,6 +234,7 @@ export default {
showMore: 'Mostrar más',
merchant: 'Comerciante',
category: 'Categoría',
tag: 'Etiqueta',
receipt: 'Recibo',
replace: 'Sustituir',
distance: 'Distancia',
Expand Down Expand Up @@ -537,6 +539,7 @@ export default {
`cambío ${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay} (previamente ${oldValueToDisplay})`,
threadRequestReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `Solicitud de ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
threadSentMoneyReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} enviado${comment ? ` para ${comment}` : ''}`,
tagSelection: ({tagName}: TagSelectionParams) => `Seleccione una ${tagName} para organizar mejor tu dinero`,
error: {
invalidSplit: 'La suma de las partes no equivale al monto total',
other: 'Error inesperado, por favor inténtalo más tarde',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ type RemovedTheRequestParams = {valueName: string; oldValueToDisplay: string};

type UpdatedTheRequestParams = {valueName: string; newValueToDisplay: string; oldValueToDisplay: string};

type TagSelectionParams = {tagName: string};

/* Translation Object types */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type TranslationBaseValue = string | string[] | ((...args: any[]) => string);
Expand Down Expand Up @@ -304,4 +306,5 @@ export type {
SetTheRequestParams,
UpdatedTheRequestParams,
RemovedTheRequestParams,
TagSelectionParams,
};
7 changes: 7 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator([
},
name: 'Money_Request_Category',
},
{
getComponent: () => {
const MoneyRequestTagPage = require('../../../pages/iou/MoneyRequestTagPage').default;
return MoneyRequestTagPage;
},
name: 'Money_Request_Tag',
},
{
getComponent: () => {
const MoneyRequestMerchantPage = require('../../../pages/iou/MoneyRequestMerchantPage').default;
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ export default {
Money_Request_Currency: ROUTES.MONEY_REQUEST_CURRENCY,
Money_Request_Description: ROUTES.MONEY_REQUEST_DESCRIPTION,
Money_Request_Category: ROUTES.MONEY_REQUEST_CATEGORY,
Money_Request_Tag: ROUTES.MONEY_REQUEST_TAG,
Money_Request_Merchant: ROUTES.MONEY_REQUEST_MERCHANT,
Money_Request_Waypoint: ROUTES.MONEY_REQUEST_WAYPOINT,
IOU_Send_Enable_Payments: ROUTES.IOU_SEND_ENABLE_PAYMENTS,
Expand Down
Loading
Loading