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

Improve MoneyRequestReportView #58903

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,9 @@ const CONST = {
},
},
THREAD_DISABLED: ['CREATED'],
// Used when displaying reportActions list to handle of unread messages icon/button
SCROLL_VERTICAL_OFFSET_THRESHOLD: 200,
ACTION_VISIBLE_THRESHOLD: 250,
},
TRANSACTION_LIST: {
COLUMNS: {
Expand Down Expand Up @@ -6430,6 +6433,10 @@ const CONST = {
GROUP_BY: {
REPORTS: 'reports',
},
TABLE_COLUMN_SIZES: {
NORMAL: 'normal',
WIDE: 'wide',
},
STATUS: {
EXPENSE: {
ALL: 'all',
Expand Down
3 changes: 3 additions & 0 deletions src/components/MoneyRequestHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import * as Expensicons from './Icon/Expensicons';
import LoadingBar from './LoadingBar';
import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusBar';
import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
import MoneyRequestReportTransactionsNavigation from './MoneyRequestReportView/MoneyRequestReportTransactionsNavigation';

type MoneyRequestHeaderProps = {
/** The report currently being looked at */
Expand Down Expand Up @@ -79,6 +80,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre

const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP;
const shouldDisplaySearchRouter = !isReportInRHP || isSmallScreenWidth;
const shouldDisplayTransactionNavigation = !!(reportID && isReportInRHP);

const hasPendingRTERViolation = hasPendingRTERViolationTransactionUtils(transactionViolations);

Expand Down Expand Up @@ -180,6 +182,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
}}
/>
)}
{shouldDisplayTransactionNavigation && <MoneyRequestReportTransactionsNavigation currentReportID={reportID} />}
</HeaderWithBackButton>
{shouldShowMarkAsCashButton && shouldUseNarrowLayout && (
<View style={[styles.ph5, styles.pb3]}>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import React from 'react';
import {View} from 'react-native';
import type {SortOrder} from '@components/Search/types';
import type {SortOrder, TableColumnSize} from '@components/Search/types';
import SortableTableHeader from '@components/SelectionList/SortableTableHeader';
import type {SortableColumnName} from '@components/SelectionList/types';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';

Expand Down Expand Up @@ -57,22 +55,22 @@ type SearchTableHeaderProps = {
sortBy?: SortableColumnName;
sortOrder?: SortOrder;
onSortPress: (column: SortableColumnName, order: SortOrder) => void;
dateColumnSize: TableColumnSize;
shouldShowSorting: boolean;
};

// At this moment with new Report View we have no extra logic for displaying columns
const shouldShowColumn = () => true;

function MoneyRequestReportTableHeader({sortBy, sortOrder, onSortPress, shouldShowSorting}: SearchTableHeaderProps) {
function MoneyRequestReportTableHeader({sortBy, sortOrder, onSortPress, dateColumnSize, shouldShowSorting}: SearchTableHeaderProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();

return (
<View style={[styles.pv3, StyleUtils.getPaddingLeft(variables.w28), StyleUtils.getPaddingRight(variables.w28)]}>
<View style={[styles.peopleRow, styles.listTableHeader, styles.pt4]}>
<SortableTableHeader
columns={columnConfig}
shouldShowColumn={shouldShowColumn}
dateColumnSize="normal"
dateColumnSize={dateColumnSize}
shouldShowSorting={shouldShowSorting}
sortBy={sortBy}
sortOrder={sortOrder}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import React, {useEffect, useState} from 'react';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
import type {TupleToUnion} from 'type-fest';
import {getButtonRole} from '@components/Button/utils';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import type {SortOrder} from '@components/Search/types';
import Text from '@components/Text';
import TransactionItemRow from '@components/TransactionItemRow';
import useHover from '@hooks/useHover';
import useLocalize from '@hooks/useLocalize';
import {useMouseContext} from '@hooks/useMouseContext';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import {convertToDisplayString} from '@libs/CurrencyUtils';
import {getIOUActionForTransactionID} from '@libs/ReportActionsUtils';
import {getMoneyRequestSpendBreakdown} from '@libs/ReportUtils';
import {compareValues} from '@libs/SearchUIUtils';
import shouldShowTransactionYear from '@libs/TransactionUtils/shouldShowTransactionYear';
import Navigation from '@navigation/Navigation';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import MoneyRequestReportTableHeader from './MoneyRequestReportTableHeader';
import {setActiveTransactionReportIDs} from './TransactionReportIDRepository';

type MoneyRequestReportTransactionListProps = {
report: OnyxTypes.Report;

/** List of transactions belonging to one report */
transactions: OnyxTypes.Transaction[];

/** Array of report actions for the report that these transactions belong to */
reportActions: OnyxTypes.ReportAction[];

/** Whether the report that these transactions belong to has any chat comments */
hasComments: boolean;
};

const sortableColumnNames = [
Expand Down Expand Up @@ -48,15 +69,30 @@ const areTransactionValuesEqual = (transactions: OnyxTypes.Transaction[], key: S
return transactions.every((transaction) => transaction[getTransactionKey(transaction, key)] === firstValidTransaction[keyOfFirstValidTransaction]);
};

function MoneyRequestReportTransactionList({transactions}: MoneyRequestReportTransactionListProps) {
function MoneyRequestReportTransactionList({report, transactions, reportActions, hasComments}: MoneyRequestReportTransactionListProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout();
const displayNarrowVersion = isMediumScreenWidth || shouldUseNarrowLayout;

const {totalDisplaySpend, nonReimbursableSpend, reimbursableSpend} = getMoneyRequestSpendBreakdown(report);
const formattedOutOfPocketAmount = convertToDisplayString(reimbursableSpend, report?.currency);
const formattedCompanySpendAmount = convertToDisplayString(nonReimbursableSpend, report?.currency);
const shouldShowBreakdown = !!nonReimbursableSpend && !!reimbursableSpend;

const {bind} = useHover();
const {isMouseDownOnInput, setMouseUp} = useMouseContext();

const handleMouseLeave = (e: React.MouseEvent<Element, MouseEvent>) => {
bind.onMouseLeave();
e.stopPropagation();
setMouseUp();
};

const [sortedData, setSortedData] = useState<SortedTransactions>({
transactions,
sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE,
sortOrder: CONST.SEARCH.SORT_ORDER.DESC,
sortOrder: CONST.SEARCH.SORT_ORDER.ASC,
});

const {sortBy, sortOrder} = sortedData;
Expand All @@ -72,13 +108,53 @@ function MoneyRequestReportTransactionList({transactions}: MoneyRequestReportTra
}));
}, [sortBy, sortOrder, transactions]);

const navigateToTransaction = useCallback(
(activeTransaction: OnyxTypes.Transaction) => {
const iouAction = getIOUActionForTransactionID(reportActions, activeTransaction.transactionID);
const reportIDToNavigate = iouAction?.childReportID;
if (!reportIDToNavigate) {
return;
}

const backTo = Navigation.getActiveRoute();

// Single transaction report will open in RHP, and we need to find every other report ID for every sibling to `activeTransaction`
// we use this data to display prev/next arrows in RHP for navigating between transactions
const sortedSiblingTransactionReportIDs = sortedData.transactions
.map((transaction) => {
const action = getIOUActionForTransactionID(reportActions, transaction.transactionID);
return action?.childReportID;
})
.filter((reportID): reportID is string => !!reportID);

setActiveTransactionReportIDs(sortedSiblingTransactionReportIDs);

Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo}));
},
[reportActions, sortedData.transactions],
);

const dateColumnSize = useMemo(() => {
const shouldShowYearForSomeTransaction = transactions.some((transaction) => shouldShowTransactionYear(transaction));
return shouldShowYearForSomeTransaction ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL;
}, [transactions]);

if (sortedData.transactions.length === 0) {
return;
}

const pressableStyle = [styles.overflowHidden];

const listHorizontalPadding = styles.ph5;

return (
<>
<View style={[styles.flex1]}>
{!displayNarrowVersion && (
<MoneyRequestReportTableHeader
shouldShowSorting
sortBy={sortBy}
sortOrder={sortOrder}
dateColumnSize={dateColumnSize}
onSortPress={(selectedSortBy, selectedSortOrder) => {
if (!isSortableColumnName(selectedSortBy)) {
return;
Expand All @@ -88,22 +164,72 @@ function MoneyRequestReportTransactionList({transactions}: MoneyRequestReportTra
}}
/>
)}
<View style={[styles.pv2, styles.ph5]}>
<View style={[listHorizontalPadding, styles.gap2, styles.pb4, displayNarrowVersion && styles.pt4]}>
{sortedData.transactions.map((transaction) => {
return (
<View style={[styles.mb2]}>
<PressableWithFeedback
onPress={(e) => {
if (isMouseDownOnInput) {
e?.stopPropagation();
return;
}

navigateToTransaction(transaction);
}}
accessibilityLabel={translate('iou.viewDetails')}
role={getButtonRole(true)}
isNested
hoverDimmingValue={1}
onMouseDown={(e) => e.preventDefault()}
id={transaction.transactionID}
style={[pressableStyle]}
onMouseLeave={handleMouseLeave}
>
<TransactionItemRow
transactionItem={transaction}
isSelected={false}
shouldShowTooltip
dateColumnSize={dateColumnSize}
shouldUseNarrowLayout={displayNarrowVersion}
shouldShowChatBubbleComponent
/>
</View>
</PressableWithFeedback>
);
})}
</View>
</>
{shouldShowBreakdown && (
<View style={[styles.dFlex, styles.alignItemsEnd, listHorizontalPadding, styles.gap2, styles.mb2]}>
{[
{text: translate('cardTransactions.outOfPocket'), value: formattedOutOfPocketAmount},
{text: translate('cardTransactions.companySpend'), value: formattedCompanySpendAmount},
].map(({text, value}) => (
<View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter, styles.pr3]}>
<Text
style={[styles.textLabelSupporting, styles.mr3]}
numberOfLines={1}
>
{text}
</Text>
<Text
numberOfLines={1}
style={[styles.textLabelSupporting, styles.textNormal, shouldUseNarrowLayout ? styles.mnw64p : styles.mnw100p, styles.textAlignRight]}
>
{value}
</Text>
</View>
))}
</View>
)}
<View style={[styles.dFlex, styles.flexRow, listHorizontalPadding, styles.justifyContentBetween, styles.mb2]}>
<Text style={[styles.textLabelSupporting]}>{hasComments ? translate('common.comments') : ''}</Text>
<View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter, styles.pr3]}>
<Text style={[styles.mr3, styles.textLabelSupporting]}>{translate('common.total')}</Text>
<Text style={[shouldUseNarrowLayout ? styles.mnw64p : styles.mnw100p, styles.textAlignRight, styles.textBold]}>
{convertToDisplayString(totalDisplaySpend, report?.currency)}
</Text>
</View>
</View>
</View>
);
}

Expand Down
Loading