Skip to content

Commit

Permalink
Merge pull request #26954 from BeeMargarida/feat/26126-tag_menu_item_…
Browse files Browse the repository at this point in the history
…and_picker

#26126: Tag menu item and Tag picker (1st PR)
  • Loading branch information
amyevans authored Sep 13, 2023
2 parents 7190179 + 298193d commit d494433
Show file tree
Hide file tree
Showing 20 changed files with 348 additions and 5 deletions.
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',
},
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;
[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 && (
<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(() => {
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
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({
/** 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,

/** "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`,
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

0 comments on commit d494433

Please sign in to comment.