Skip to content

Commit

Permalink
Merge pull request #52441 from Expensify/revert-52434-revert-PR-51445
Browse files Browse the repository at this point in the history
Show search action buttons v2
  • Loading branch information
mjasikowski authored Nov 22, 2024
2 parents 056e8ee + a266c4c commit 4c47abe
Show file tree
Hide file tree
Showing 18 changed files with 396 additions and 73 deletions.
3 changes: 3 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5964,6 +5964,9 @@ const CONST = {
ACTION_TYPES: {
VIEW: 'view',
REVIEW: 'review',
SUBMIT: 'submit',
APPROVE: 'approve',
PAY: 'pay',
DONE: 'done',
PAID: 'paid',
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/Search/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) {
return;
}

const reportIDList = (selectedReports?.filter((report) => !!report) as string[]) ?? [];
const reportIDList = selectedReports?.filter((report) => !!report) ?? [];
SearchActions.exportSearchItemsToCSV(
{query: status, jsonQuery: JSON.stringify(queryJSON), reportIDList, transactionIDList: selectedTransactionsKeys, policyIDs: [activeWorkspaceID ?? '']},
() => {
Expand Down
8 changes: 8 additions & 0 deletions src/components/Search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ type SelectedTransactionInfo = {
/** Model of selected results */
type SelectedTransactions = Record<string, SelectedTransactionInfo>;

/** Model of payment data used by Search bulk actions */
type PaymentData = {
reportID: string;
amount: number;
paymentType: ValueOf<typeof CONST.IOU.PAYMENT_TYPE>;
};

type SortOrder = ValueOf<typeof CONST.SEARCH.SORT_ORDER>;
type SearchColumnType = ValueOf<typeof CONST.SEARCH.TABLE_COLUMNS>;
type ExpenseSearchStatus = ValueOf<typeof CONST.SEARCH.STATUS.EXPENSE>;
Expand Down Expand Up @@ -117,5 +124,6 @@ export type {
TripSearchStatus,
ChatSearchStatus,
SearchAutocompleteResult,
PaymentData,
SearchAutocompleteQueryRange,
};
43 changes: 29 additions & 14 deletions src/components/SelectionList/Search/ActionCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Badge from '@components/Badge';
import Button from '@components/Button';
import * as Expensicons from '@components/Icon/Expensicons';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand All @@ -15,6 +16,9 @@ import type {SearchTransactionAction} from '@src/types/onyx/SearchResults';
const actionTranslationsMap: Record<SearchTransactionAction, TranslationPaths> = {
view: 'common.view',
review: 'common.review',
submit: 'common.submit',
approve: 'iou.approve',
pay: 'iou.pay',
done: 'common.done',
paid: 'iou.settledExpensify',
};
Expand All @@ -26,13 +30,23 @@ type ActionCellProps = {
goToItem: () => void;
isChildListItem?: boolean;
parentAction?: string;
isLoading?: boolean;
};

function ActionCell({action = CONST.SEARCH.ACTION_TYPES.VIEW, isLargeScreenWidth = true, isSelected = false, goToItem, isChildListItem = false, parentAction = ''}: ActionCellProps) {
function ActionCell({
action = CONST.SEARCH.ACTION_TYPES.VIEW,
isLargeScreenWidth = true,
isSelected = false,
goToItem,
isChildListItem = false,
parentAction = '',
isLoading = false,
}: ActionCellProps) {
const {translate} = useLocalize();
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {isOffline} = useNetwork();

const text = translate(actionTranslationsMap[action]);

Expand Down Expand Up @@ -61,9 +75,8 @@ function ActionCell({action = CONST.SEARCH.ACTION_TYPES.VIEW, isLargeScreenWidth
);
}

const buttonInnerStyles = isSelected ? styles.buttonDefaultHovered : {};

if (action === CONST.SEARCH.ACTION_TYPES.VIEW || shouldUseViewAction) {
const buttonInnerStyles = isSelected ? styles.buttonDefaultHovered : {};
return isLargeScreenWidth ? (
<Button
text={translate(actionTranslationsMap[CONST.SEARCH.ACTION_TYPES.VIEW])}
Expand All @@ -77,17 +90,19 @@ function ActionCell({action = CONST.SEARCH.ACTION_TYPES.VIEW, isLargeScreenWidth
) : null;
}

if (action === CONST.SEARCH.ACTION_TYPES.REVIEW) {
return (
<Button
text={text}
onPress={goToItem}
small
style={[styles.w100]}
innerStyles={buttonInnerStyles}
/>
);
}
const buttonInnerStyles = isSelected ? styles.buttonSuccessHovered : {};
return (
<Button
text={text}
onPress={goToItem}
small
style={[styles.w100]}
innerStyles={buttonInnerStyles}
isLoading={isLoading}
success
isDisabled={isOffline}
/>
);
}

ActionCell.displayName = 'ActionCell';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type ExpenseItemHeaderNarrowProps = {
isDisabled?: boolean | null;
isDisabledCheckbox?: boolean;
handleCheckboxPress?: () => void;
isLoading?: boolean;
};

function ExpenseItemHeaderNarrow({
Expand All @@ -44,6 +45,7 @@ function ExpenseItemHeaderNarrow({
isDisabled,
handleCheckboxPress,
text,
isLoading = false,
}: ExpenseItemHeaderNarrowProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
Expand Down Expand Up @@ -102,6 +104,7 @@ function ExpenseItemHeaderNarrow({
goToItem={onButtonPress}
isLargeScreenWidth={false}
isSelected={isSelected}
isLoading={isLoading}
/>
</View>
</View>
Expand Down
7 changes: 6 additions & 1 deletion src/components/SelectionList/Search/ReportListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import {View} from 'react-native';
import Checkbox from '@components/Checkbox';
import {useSearchContext} from '@components/Search/SearchContext';
import BaseListItem from '@components/SelectionList/BaseListItem';
import type {ListItem, ReportListItemProps, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types';
import Text from '@components/Text';
Expand All @@ -10,6 +11,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {handleActionButtonPress} from '@libs/actions/Search';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import Navigation from '@libs/Navigation/Navigation';
import variables from '@styles/variables';
Expand Down Expand Up @@ -69,6 +71,7 @@ function ReportListItem<TItem extends ListItem>({
const styles = useThemeStyles();
const {isLargeScreenWidth} = useResponsiveLayout();
const StyleUtils = useStyleUtils();
const {currentSearchHash} = useSearchContext();

const animatedHighlightStyle = useAnimatedHighlightStyle({
borderRadius: variables.componentBorderRadius,
Expand All @@ -93,7 +96,7 @@ function ReportListItem<TItem extends ListItem>({
];

const handleOnButtonPress = () => {
onSelectRow(item);
handleActionButtonPress(currentSearchHash, reportItem, () => onSelectRow(item));
};

const openReportInRHP = (transactionItem: TransactionListItemType) => {
Expand Down Expand Up @@ -165,6 +168,7 @@ function ReportListItem<TItem extends ListItem>({
action={reportItem.action}
onButtonPress={handleOnButtonPress}
containerStyle={[styles.ph3, styles.pt1half, styles.mb1half]}
isLoading={reportItem.isActionLoading}
/>
)}
<View style={[styles.flex1, styles.flexRow, styles.alignItemsCenter, styles.gap3, styles.ph3, styles.pv1half]}>
Expand Down Expand Up @@ -199,6 +203,7 @@ function ReportListItem<TItem extends ListItem>({
action={reportItem.action}
goToItem={handleOnButtonPress}
isSelected={item.isSelected}
isLoading={reportItem.isActionLoading}
/>
</View>
)}
Expand Down
6 changes: 5 additions & 1 deletion src/components/SelectionList/Search/TransactionListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';
import {useSearchContext} from '@components/Search/SearchContext';
import BaseListItem from '@components/SelectionList/BaseListItem';
import type {ListItem, TransactionListItemProps, TransactionListItemType} from '@components/SelectionList/types';
import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {handleActionButtonPress} from '@libs/actions/Search';
import variables from '@styles/variables';
import TransactionListItemRow from './TransactionListItemRow';

Expand All @@ -26,6 +28,7 @@ function TransactionListItem<TItem extends ListItem>({
const theme = useTheme();

const {isLargeScreenWidth} = useResponsiveLayout();
const {currentSearchHash} = useSearchContext();

const listItemPressableStyle = [
styles.selectionListPressableItemWrapper,
Expand Down Expand Up @@ -75,13 +78,14 @@ function TransactionListItem<TItem extends ListItem>({
item={transactionItem}
showTooltip={showTooltip}
onButtonPress={() => {
onSelectRow(item);
handleActionButtonPress(currentSearchHash, transactionItem, () => onSelectRow(item));
}}
onCheckboxPress={() => onCheckboxPress?.(item)}
isDisabled={!!isDisabled}
canSelectMultiple={!!canSelectMultiple}
isButtonSelected={item.isSelected}
shouldShowTransactionCheckbox={false}
isLoading={transactionItem.isActionLoading}
/>
</BaseListItem>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type TransactionListItemRowProps = {
isButtonSelected?: boolean;
parentAction?: string;
shouldShowTransactionCheckbox?: boolean;
isLoading?: boolean;
};

const getTypeIcon = (type?: SearchTransactionType) => {
Expand Down Expand Up @@ -257,6 +258,7 @@ function TransactionListItemRow({
isButtonSelected = false,
parentAction = '',
shouldShowTransactionCheckbox,
isLoading = false,
}: TransactionListItemRowProps) {
const styles = useThemeStyles();
const {isLargeScreenWidth} = useResponsiveLayout();
Expand All @@ -280,6 +282,7 @@ function TransactionListItemRow({
isDisabled={item.isDisabled}
isDisabledCheckbox={item.isDisabledCheckbox}
handleCheckboxPress={onCheckboxPress}
isLoading={isLoading}
/>
)}

Expand Down Expand Up @@ -445,6 +448,7 @@ function TransactionListItemRow({
isChildListItem={isChildListItem}
parentAction={parentAction}
goToItem={onButtonPress}
isLoading={isLoading}
/>
</View>
</View>
Expand Down
Empty file.
5 changes: 2 additions & 3 deletions src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
type PayMoneyRequestOnSearchParams = {
hash: number;
paymentType: string;
/**
* Stringified JSON object with type of following structure:
* Array<{reportID: string, amount: number}>
* Array<{reportID: string, amount: number, paymentType: string}>
*/
reportsAndAmounts: string;
paymentData: string;
};

export default PayMoneyRequestOnSearchParams;
11 changes: 6 additions & 5 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
Tenant,
} from '@src/types/onyx/Policy';
import type PolicyEmployee from '@src/types/onyx/PolicyEmployee';
import type {SearchPolicy} from '@src/types/onyx/SearchResults';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {hasSynchronizationErrorMessage} from './actions/connections';
import {getCategoryApproverRule} from './CategoryUtils';
Expand Down Expand Up @@ -184,7 +185,7 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry<Policy>, isConnecti
return undefined;
}

function getPolicyRole(policy: OnyxInputOrEntry<Policy>, currentUserLogin: string | undefined) {
function getPolicyRole(policy: OnyxInputOrEntry<Policy> | SearchPolicy, currentUserLogin: string | undefined) {
return policy?.role ?? policy?.employeeList?.[currentUserLogin ?? '-1']?.role;
}

Expand Down Expand Up @@ -218,7 +219,7 @@ const isUserPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, login?: string) =>
/**
* Checks if the current user is an admin of the policy.
*/
const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.ADMIN;
const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy> | SearchPolicy, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.ADMIN;

/**
* Checks if the current user is of the role "user" on the policy.
Expand Down Expand Up @@ -379,7 +380,7 @@ function isPendingDeletePolicy(policy: OnyxEntry<Policy>): boolean {
return policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
}

function isPaidGroupPolicy(policy: OnyxEntry<Policy>): boolean {
function isPaidGroupPolicy(policy: OnyxEntry<Policy> | SearchPolicy): boolean {
return policy?.type === CONST.POLICY.TYPE.TEAM || policy?.type === CONST.POLICY.TYPE.CORPORATE;
}

Expand All @@ -404,7 +405,7 @@ function isTaxTrackingEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry<Po
* Checks if policy's scheduled submit / auto reporting frequency is "instant".
* Note: Free policies have "instant" submit always enabled.
*/
function isInstantSubmitEnabled(policy: OnyxInputOrEntry<Policy>): boolean {
function isInstantSubmitEnabled(policy: OnyxInputOrEntry<Policy> | SearchPolicy): boolean {
return policy?.autoReporting === true && policy?.autoReportingFrequency === CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT;
}

Expand Down Expand Up @@ -434,7 +435,7 @@ function getCorrectedAutoReportingFrequency(policy: OnyxInputOrEntry<Policy>): V
/**
* Checks if policy's approval mode is "optional", a.k.a. "Submit & Close"
*/
function isSubmitAndClose(policy: OnyxInputOrEntry<Policy>): boolean {
function isSubmitAndClose(policy: OnyxInputOrEntry<Policy> | SearchPolicy): boolean {
return policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL;
}

Expand Down
Loading

0 comments on commit 4c47abe

Please sign in to comment.