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 22 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 @@ -240,6 +240,7 @@ const CONST = {
TASKS: 'tasks',
THREADS: 'threads',
CUSTOM_STATUS: 'customStatus',
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]: unknown;
[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
50 changes: 46 additions & 4 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,6 +30,7 @@ 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';
Expand Down Expand Up @@ -68,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 @@ -105,10 +110,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 @@ -129,6 +130,22 @@ 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),
}),
),

/* Beta features list */
betas: PropTypes.arrayOf(PropTypes.string),
};

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

// 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 @@ -494,6 +520,16 @@ function MoneyRequestConfirmationList(props) {
disabled={didConfirm || props.isReadOnly}
/>
)}
{canUseTags && tagList ? (
BeeMargarida marked this conversation as resolved.
Show resolved Hide resolved
<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}
/>
) : null}
</>
)}
</OptionsSelector>
Expand All @@ -509,9 +545,15 @@ export default compose(
session: {
key: ONYXKEYS.SESSION,
},
betas: {
key: ONYXKEYS.BETAS,
},
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 tag list we are getting tags for */
BeeMargarida marked this conversation as resolved.
Show resolved Hide resolved
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,
} from './types';
import * as ReportActionsUtils from '../libs/ReportActionsUtils';

Expand Down Expand Up @@ -233,6 +234,7 @@ export default {
showMore: 'Show more',
merchant: 'Merchant',
category: 'Category',
tag: 'Tag',
receipt: 'Receipt',
replace: 'Replace',
distance: 'Distance',
Expand Down Expand Up @@ -533,6 +535,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,
} from './types';

/* eslint-disable max-len */
Expand Down Expand Up @@ -232,6 +233,7 @@ export default {
showMore: 'Mostrar más',
merchant: 'Comerciante',
category: 'Categoría',
tag: 'Etiqueta',
receipt: 'Recibo',
replace: 'Sustituir',
distance: 'Distancia',
Expand Down Expand Up @@ -535,6 +537,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 @@ -190,6 +190,8 @@ type RemovedTheRequestParams = {valueName: string; oldValueToDisplay: string};

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

type TagSelectionParams = {tagName: string};

export type {
AddressLineParams,
CharacterLimitParams,
Expand Down Expand Up @@ -261,4 +263,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
Loading
Loading