Skip to content
Merged
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
16 changes: 16 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6446,6 +6446,7 @@ const CONST = {
DATA_TYPES: {
EXPENSE: 'expense',
INVOICE: 'invoice',
TASK: 'task',
TRIP: 'trip',
CHAT: 'chat',
},
Expand Down Expand Up @@ -6514,6 +6515,11 @@ const CONST = {
LINKS: 'links',
PINNED: 'pinned',
},
TASK: {
ALL: 'all',
OUTSTANDING: 'outstanding',
COMPLETED: 'completed',
},
},
TABLE_COLUMNS: {
RECEIPT: 'receipt',
Expand All @@ -6528,6 +6534,10 @@ const CONST = {
TYPE: 'type',
ACTION: 'action',
TAX_AMOUNT: 'taxAmount',
TITLE: 'title',
ASSIGNEE: 'assignee',
CREATED_BY: 'createdBy',
IN: 'in',
},
SYNTAX_OPERATORS: {
AND: 'and',
Expand Down Expand Up @@ -6568,6 +6578,9 @@ const CONST = {
PAID: 'paid',
EXPORTED: 'exported',
POSTED: 'posted',
TITLE: 'title',
ASSIGNEE: 'assignee',
CREATED_BY: 'createdBy',
REIMBURSABLE: 'reimbursable',
BILLABLE: 'billable',
POLICY_ID: 'policyID',
Expand Down Expand Up @@ -6606,6 +6619,9 @@ const CONST = {
PAID: 'paid',
EXPORTED: 'exported',
POSTED: 'posted',
TITLE: 'title',
ASSIGNEE: 'assignee',
CREATED_BY: 'created-by',
REIMBURSABLE: 'reimbursable',
BILLABLE: 'billable',
},
Expand Down
3 changes: 3 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ const ROUTES = {
SEARCH_ADVANCED_FILTERS_PAID: 'search/filters/paid',
SEARCH_ADVANCED_FILTERS_EXPORTED: 'search/filters/exported',
SEARCH_ADVANCED_FILTERS_POSTED: 'search/filters/posted',
SEARCH_ADVANCED_FILTERS_TITLE: 'search/filters/title',
SEARCH_ADVANCED_FILTERS_ASSIGNEE: 'search/filters/assignee',
SEARCH_ADVANCED_FILTERS_CREATED_BY: 'search/filters/createdBy',
SEARCH_ADVANCED_FILTERS_REIMBURSABLE: 'search/filters/reimbursable',
SEARCH_ADVANCED_FILTERS_BILLABLE: 'search/filters/billable',
SEARCH_ADVANCED_FILTERS_WORKSPACE: 'search/filters/workspace',
Expand Down
3 changes: 3 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ const SCREENS = {
ADVANCED_FILTERS_TAG_RHP: 'Search_Advanced_Filters_Tag_RHP',
ADVANCED_FILTERS_FROM_RHP: 'Search_Advanced_Filters_From_RHP',
ADVANCED_FILTERS_TO_RHP: 'Search_Advanced_Filters_To_RHP',
ADVANCED_FILTERS_TITLE_RHP: 'Search_Advanced_Filters_Title_RHP',
ADVANCED_FILTERS_ASSIGNEE_RHP: 'Search_Advanced_Filters_Assignee_RHP',
ADVANCED_FILTERS_CREATED_BY_RHP: 'Search_Advanced_Filters_Created_By_RHP',
ADVANCED_FILTERS_REIMBURSABLE_RHP: 'Search_Advanced_Filters_Reimbursable_RHP',
ADVANCED_FILTERS_BILLABLE_RHP: 'Search_Advanced_Filters_Billable_RHP',
ADVANCED_FILTERS_WORKSPACE_RHP: 'Search_Advanced_Filters_Workspace_RHP',
Expand Down
27 changes: 13 additions & 14 deletions src/components/Search/SearchAutocompleteList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,13 @@ function SearchAutocompleteList(

const typeAutocompleteList = Object.values(CONST.SEARCH.DATA_TYPES);
const groupByAutocompleteList = Object.values(CONST.SEARCH.GROUP_BY);
const statusAutocompleteList = Object.values({...CONST.SEARCH.STATUS.EXPENSE, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP});
const statusAutocompleteList = Object.values({
...CONST.SEARCH.STATUS.EXPENSE,
...CONST.SEARCH.STATUS.INVOICE,
...CONST.SEARCH.STATUS.CHAT,
...CONST.SEARCH.STATUS.TRIP,
...CONST.SEARCH.STATUS.TASK,
});
const expenseTypes = Object.values(CONST.SEARCH.TRANSACTION_TYPE);
const booleanTypes = Object.values(CONST.SEARCH.BOOLEAN);

Expand Down Expand Up @@ -306,28 +312,21 @@ function SearchAutocompleteList(
mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE,
}));
}
case CONST.SEARCH.SYNTAX_FILTER_KEYS.CREATED_BY:
case CONST.SEARCH.SYNTAX_FILTER_KEYS.ASSIGNEE:
case CONST.SEARCH.SYNTAX_FILTER_KEYS.TO:
case CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM: {
const filteredParticipants = getParticipantsAutocompleteList()
.filter((participant) => participant.name.toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(participant.name.toLowerCase()))
.slice(0, 10);
const filterKey = autocompleteKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CREATED_BY ? CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.CREATED_BY : autocompleteKey;

return filteredParticipants.map((participant) => ({
filterKey: CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.FROM,
text: participant.name,
autocompleteID: participant.accountID,
mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM,
}));
}
case CONST.SEARCH.SYNTAX_FILTER_KEYS.TO: {
const filteredParticipants = getParticipantsAutocompleteList()
.filter((participant) => participant.name.toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(participant.name.toLowerCase()))
.slice(0, 10);

return filteredParticipants.map((participant) => ({
filterKey: CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.TO,
filterKey,
text: participant.name,
autocompleteID: participant.accountID,
mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.TO,
mapKey: autocompleteKey,
}));
}
case CONST.SEARCH.SYNTAX_FILTER_KEYS.IN: {
Expand Down
30 changes: 18 additions & 12 deletions src/components/Search/SearchContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {useCallback, useContext, useMemo, useState} from 'react';
import type {ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types';
import type {ReportActionListItemType, ReportListItemType, TaskListItemType, TransactionListItemType} from '@components/SelectionList/types';
import {isMoneyRequestReport} from '@libs/ReportUtils';
import {isReportListItemType, isTransactionListItemType} from '@libs/SearchUIUtils';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -27,7 +27,10 @@ const defaultSearchContext: SearchContext = {

const Context = React.createContext<SearchContext>(defaultSearchContext);

function getReportsFromSelectedTransactions(data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[], selectedTransactions: SelectedTransactions) {
function getReportsFromSelectedTransactions(
data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[] | TaskListItemType[],
selectedTransactions: SelectedTransactions,
) {
if (data.length === 0) {
return [];
}
Expand Down Expand Up @@ -81,17 +84,20 @@ function SearchContextProvider({children}: ChildrenProps) {
}));
}, []);

const setSelectedTransactions = useCallback((selectedTransactions: SelectedTransactions, data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[]) => {
// When selecting transactions, we also need to manage the reports to which these transactions belong. This is done to ensure proper exporting to CSV.
const selectedReports = getReportsFromSelectedTransactions(data, selectedTransactions);
const setSelectedTransactions = useCallback(
(selectedTransactions: SelectedTransactions, data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[] | TaskListItemType[]) => {
// When selecting transactions, we also need to manage the reports to which these transactions belong. This is done to ensure proper exporting to CSV.
const selectedReports = getReportsFromSelectedTransactions(data, selectedTransactions);

setSearchContextData((prevState) => ({
...prevState,
selectedTransactions,
shouldTurnOffSelectionMode: false,
selectedReports,
}));
}, []);
setSearchContextData((prevState) => ({
...prevState,
selectedTransactions,
shouldTurnOffSelectionMode: false,
selectedReports,
}));
},
[],
);

const clearSelectedTransactions = useCallback(
(searchHash?: number, shouldTurnOffSelectionMode = false) => {
Expand Down
36 changes: 23 additions & 13 deletions src/components/Search/SearchList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import Modal from '@components/Modal';
import {PressableWithFeedback} from '@components/Pressable';
import type ChatListItem from '@components/SelectionList/ChatListItem';
import type ReportListItem from '@components/SelectionList/Search/ReportListItem';
import type TaskListItem from '@components/SelectionList/Search/TaskListItem';
import type TransactionListItem from '@components/SelectionList/Search/TransactionListItem';
import type {ExtendedTargetedEvent, ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types';
import type {ExtendedTargetedEvent, ReportActionListItemType, ReportListItemType, TaskListItemType, TransactionListItemType} from '@components/SelectionList/types';
import Text from '@components/Text';
import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
Expand All @@ -31,8 +32,8 @@ import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';

type SearchListItem = TransactionListItemType | ReportListItemType | ReportActionListItemType;
type SearchListItemComponentType = typeof TransactionListItem | typeof ChatListItem | typeof ReportListItem;
type SearchListItem = TransactionListItemType | ReportListItemType | ReportActionListItemType | TaskListItemType;
type SearchListItemComponentType = typeof TransactionListItem | typeof ChatListItem | typeof ReportListItem | typeof TaskListItem;

type SearchListHandle = {
scrollAndHighlightItem?: (items: string[]) => void;
Expand Down Expand Up @@ -340,19 +341,27 @@ function SearchList(
],
);

const tableHeaderVisible = canSelectMultiple || !!SearchTableHeader;
const selectAllButtonVisible = canSelectMultiple && !SearchTableHeader;

return (
<View style={[styles.flex1, !isKeyboardShown && safeAreaPaddingBottomStyle, containerStyle]}>
{canSelectMultiple && (
{tableHeaderVisible && (
<View style={[styles.searchListHeaderContainerStyle, styles.listTableHeader]}>
<Checkbox
accessibilityLabel={translate('workspace.people.selectAll')}
isChecked={selectedItemsLength === flattenedTransactions.length}
isIndeterminate={selectedItemsLength > 0 && selectedItemsLength !== flattenedTransactions.length}
onPress={() => {
onAllCheckboxPress();
}}
/>
{SearchTableHeader ?? (
{canSelectMultiple && (
<Checkbox
accessibilityLabel={translate('workspace.people.selectAll')}
isChecked={selectedItemsLength === flattenedTransactions.length}
isIndeterminate={selectedItemsLength > 0 && selectedItemsLength !== flattenedTransactions.length}
onPress={() => {
onAllCheckboxPress();
}}
/>
)}

{SearchTableHeader}

{selectAllButtonVisible && (
<PressableWithFeedback
style={[styles.userSelectNone, styles.alignItemsCenter]}
onPress={onAllCheckboxPress}
Expand All @@ -366,6 +375,7 @@ function SearchList(
)}
</View>
)}

<Animated.FlatList
data={data}
renderItem={renderItem}
Expand Down
25 changes: 24 additions & 1 deletion src/components/Search/SearchPageHeader/SearchStatusBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types';
import * as Expensicons from '@components/Icon/Expensicons';
import ScrollView from '@components/ScrollView';
import {useSearchContext} from '@components/Search/SearchContext';
import type {ChatSearchStatus, ExpenseSearchStatus, InvoiceSearchStatus, SearchGroupBy, SearchQueryJSON, TripSearchStatus} from '@components/Search/types';
import type {ChatSearchStatus, ExpenseSearchStatus, InvoiceSearchStatus, SearchGroupBy, SearchQueryJSON, TaskSearchStatus, TripSearchStatus} from '@components/Search/types';
import SearchStatusSkeleton from '@components/Skeletons/SearchStatusSkeleton';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
Expand Down Expand Up @@ -198,6 +198,27 @@ const chatOptions: Array<{type: SearchDataTypes; status: ChatSearchStatus; icon:
},
];

const taskOptions: Array<{type: SearchDataTypes; status: TaskSearchStatus; icon: IconAsset; text: TranslationPaths}> = [
{
type: CONST.SEARCH.DATA_TYPES.TASK,
status: CONST.SEARCH.STATUS.TASK.ALL,
icon: Expensicons.All,
text: 'common.all',
},
{
type: CONST.SEARCH.DATA_TYPES.TASK,
status: CONST.SEARCH.STATUS.TASK.OUTSTANDING,
icon: Expensicons.Hourglass,
text: 'common.outstanding',
},
{
type: CONST.SEARCH.DATA_TYPES.TASK,
status: CONST.SEARCH.STATUS.TASK.COMPLETED,
icon: Expensicons.Checkbox,
text: 'search.filters.completed',
},
];

function getOptions(type: SearchDataTypes, groupBy: SearchGroupBy | undefined) {
switch (type) {
case CONST.SEARCH.DATA_TYPES.INVOICE:
Expand All @@ -206,6 +227,8 @@ function getOptions(type: SearchDataTypes, groupBy: SearchGroupBy | undefined) {
return tripOptions;
case CONST.SEARCH.DATA_TYPES.CHAT:
return chatOptions;
case CONST.SEARCH.DATA_TYPES.TASK:
return taskOptions;
case CONST.SEARCH.DATA_TYPES.EXPENSE:
default:
return groupBy === CONST.SEARCH.GROUP_BY.REPORTS ? expenseReportOptions : expenseOptions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ function buildSubstitutionsMap(
filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID ||
filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG ||
filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.FEED ||
filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID
filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID ||
filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CREATED_BY ||
filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.ASSIGNEE
) {
const displayValue = getFilterDisplayValue(filterKey, filterValue, personalDetails, reports, cardList, cardFeedNamesWithType, policies);

Expand Down
Loading