diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 7f2b5f5767a3..c251b5f52000 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -6701,17 +6701,47 @@ const CONST = { }, get CUSTOM_COLUMNS() { return { - DATE: this.TABLE_COLUMNS.DATE, - STATUS: this.TABLE_COLUMNS.STATUS, - TITLE: this.TABLE_COLUMNS.TITLE, - FROM: this.TABLE_COLUMNS.FROM, - TO: this.TABLE_COLUMNS.TO, - ACTION: this.TABLE_COLUMNS.ACTION, + EXPENSE: { + RECEIPT: this.TABLE_COLUMNS.RECEIPT, + DATE: this.TABLE_COLUMNS.DATE, + MERCHANT: this.TABLE_COLUMNS.MERCHANT, + FROM: this.TABLE_COLUMNS.FROM, + TO: this.TABLE_COLUMNS.TO, + CATEGORY: this.TABLE_COLUMNS.CATEGORY, + TAG: this.TABLE_COLUMNS.TAG, + ACTION: this.TABLE_COLUMNS.ACTION, + }, + EXPENSE_REPORT: { + DATE: this.TABLE_COLUMNS.DATE, + STATUS: this.TABLE_COLUMNS.STATUS, + TITLE: this.TABLE_COLUMNS.TITLE, + FROM: this.TABLE_COLUMNS.FROM, + TO: this.TABLE_COLUMNS.TO, + ACTION: this.TABLE_COLUMNS.ACTION, + }, + INVOICE: {}, + TASK: {}, + TRIP: {}, + CHAT: {}, }; }, get DEFAULT_COLUMNS() { return { + EXPENSE: [ + this.TABLE_COLUMNS.RECEIPT, + this.TABLE_COLUMNS.DATE, + this.TABLE_COLUMNS.MERCHANT, + this.TABLE_COLUMNS.FROM, + this.TABLE_COLUMNS.TO, + this.TABLE_COLUMNS.CATEGORY, + this.TABLE_COLUMNS.TAG, + this.TABLE_COLUMNS.ACTION, + ], EXPENSE_REPORT: [this.TABLE_COLUMNS.DATE, this.TABLE_COLUMNS.STATUS, this.TABLE_COLUMNS.TITLE, this.TABLE_COLUMNS.FROM, this.TABLE_COLUMNS.TO, this.TABLE_COLUMNS.ACTION], + INVOICE: [], + TASK: [], + TRIP: [], + CHAT: [], }; }, BOOLEAN: { diff --git a/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx b/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx index 04d602e9e8d6..25adbc97eedd 100644 --- a/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx +++ b/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx @@ -307,6 +307,7 @@ function SearchFiltersBar({ // If the type has changed, reset the status so we dont have an invalid status selected if (updatedFilterFormValues.type !== filterFormValues.type) { updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL; + updatedFilterFormValues.columns = []; } const queryString = buildQueryStringFromFilterFormValues(updatedFilterFormValues); @@ -781,7 +782,7 @@ function SearchFiltersBar({ [], ); - const shouldShowColumnsButton = isLargeScreenWidth && queryJSON.type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; + const shouldShowColumnsButton = isLargeScreenWidth && (queryJSON.type === CONST.SEARCH.DATA_TYPES.EXPENSE || queryJSON.type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT); const filterButtonText = useMemo( () => translate('search.filtersHeader') + (hiddenSelectedFilters.length > 0 ? ` (${hiddenSelectedFilters.length})` : ''), diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index ca6a7b532ddf..d756a65f7701 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -107,7 +107,7 @@ type TableColumnSize = ValueOf; type SearchDatePreset = ValueOf; type SearchWithdrawalType = ValueOf; type SearchAction = ValueOf; -type SearchCustomColumnIds = ValueOf; +type SearchCustomColumnIds = ValueOf | ValueOf; type SearchContextData = { currentSearchHash: number; diff --git a/src/hooks/useSearchTypeMenu.tsx b/src/hooks/useSearchTypeMenu.tsx index 74cb1b28f120..c639059f7753 100644 --- a/src/hooks/useSearchTypeMenu.tsx +++ b/src/hooks/useSearchTypeMenu.tsx @@ -6,7 +6,6 @@ import type {PopoverMenuItem} from '@components/PopoverMenu'; import {useSearchContext} from '@components/Search/SearchContext'; import type {SearchQueryJSON} from '@components/Search/types'; import ThreeDotsMenu from '@components/ThreeDotsMenu'; -import {clearAllFilters} from '@libs/actions/Search'; import {mergeCardListWithWorkspaceFeeds} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getAllTaxRates} from '@libs/PolicyUtils'; @@ -109,7 +108,6 @@ export default function useSearchTypeMenu(queryJSON: SearchQueryJSON) { return { ...baseMenuItem, onSelected: () => { - clearAllFilters(); Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: item?.query ?? '', name: item?.name})); }, rightComponent: ( @@ -181,7 +179,6 @@ export default function useSearchTypeMenu(queryJSON: SearchQueryJSON) { containerStyle: isSelected ? [{backgroundColor: theme.border}] : undefined, shouldCallAfterModalHide: true, onSelected: singleExecution(() => { - clearAllFilters(); Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: item.searchQuery})); }), }); diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 288907d98c7c..1f6acf88a6eb 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -58,6 +58,7 @@ import type { SearchWithdrawalIDGroup, } from '@src/types/onyx/SearchResults'; import type IconAsset from '@src/types/utils/IconAsset'; +import arraysEqual from '@src/utils/arraysEqual'; import {hasSynchronizationErrorMessage} from './actions/connections'; import {canApproveIOU, canIOUBePaid, canSubmitReport, startMoneyRequest} from './actions/IOU'; import {setIsOpenConfirmNavigateExpensifyClassicModalOpen} from './actions/isOpenConfirmNavigateExpensifyClassicModal'; @@ -2049,21 +2050,65 @@ function getExpenseTypeTranslationKey(expenseType: ValueOf): TranslationPaths { +function getCustomColumns(type: SearchDataTypes): SearchCustomColumnIds[] { + // eslint-disable-next-line default-case + switch (type) { + case CONST.SEARCH.DATA_TYPES.EXPENSE: + return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.EXPENSE); + case CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT: + return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.EXPENSE_REPORT); + case CONST.SEARCH.DATA_TYPES.INVOICE: + return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.INVOICE); + case CONST.SEARCH.DATA_TYPES.TASK: + return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.TASK); + case CONST.SEARCH.DATA_TYPES.TRIP: + return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.TRIP); + case CONST.SEARCH.DATA_TYPES.CHAT: + return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.CHAT); + } +} + +function getCustomColumnDefault(type: SearchDataTypes): SearchCustomColumnIds[] { + // eslint-disable-next-line default-case + switch (type) { + case CONST.SEARCH.DATA_TYPES.EXPENSE: + return CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE; + case CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT: + return CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE_REPORT; + case CONST.SEARCH.DATA_TYPES.INVOICE: + return CONST.SEARCH.DEFAULT_COLUMNS.INVOICE; + case CONST.SEARCH.DATA_TYPES.TASK: + return CONST.SEARCH.DEFAULT_COLUMNS.TASK; + case CONST.SEARCH.DATA_TYPES.TRIP: + return CONST.SEARCH.DEFAULT_COLUMNS.TRIP; + case CONST.SEARCH.DATA_TYPES.CHAT: + return CONST.SEARCH.DEFAULT_COLUMNS.CHAT; + } +} + +function getSearchColumnTranslationKey(columnId: SearchCustomColumnIds): TranslationPaths { // eslint-disable-next-line default-case switch (columnId) { - case CONST.SEARCH.CUSTOM_COLUMNS.DATE: + case CONST.SEARCH.TABLE_COLUMNS.DATE: return 'common.date'; - case CONST.SEARCH.CUSTOM_COLUMNS.STATUS: - return 'common.status'; - case CONST.SEARCH.CUSTOM_COLUMNS.TITLE: - return 'common.title'; - case CONST.SEARCH.CUSTOM_COLUMNS.FROM: + case CONST.SEARCH.TABLE_COLUMNS.MERCHANT: + return 'common.merchant'; + case CONST.SEARCH.TABLE_COLUMNS.FROM: return 'common.from'; - case CONST.SEARCH.CUSTOM_COLUMNS.TO: + case CONST.SEARCH.TABLE_COLUMNS.TO: return 'common.to'; - case CONST.SEARCH.CUSTOM_COLUMNS.ACTION: + case CONST.SEARCH.TABLE_COLUMNS.CATEGORY: + return 'common.category'; + case CONST.SEARCH.TABLE_COLUMNS.RECEIPT: + return 'common.receipt'; + case CONST.SEARCH.TABLE_COLUMNS.TAG: + return 'common.tag'; + case CONST.SEARCH.TABLE_COLUMNS.ACTION: return 'common.action'; + case CONST.SEARCH.TABLE_COLUMNS.TITLE: + return 'common.title'; + case CONST.SEARCH.TABLE_COLUMNS.STATUS: + return 'common.status'; } } @@ -2571,6 +2616,18 @@ function getColumnsToShow( } }; + // If the user has set custom columns for the search, we need to respect their preference, and only show + // them what they want to see + if (!arraysEqual(Object.values(CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE), visibleColumns) && visibleColumns.length > 0) { + const requiredColumns = new Set([CONST.SEARCH.TABLE_COLUMNS.AVATAR, CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, CONST.SEARCH.TABLE_COLUMNS.TYPE]); + + for (const column of Object.keys(columns) as SearchCustomColumnIds[]) { + columns[column] = visibleColumns.includes(column) || requiredColumns.has(column); + } + + return columns; + } + if (Array.isArray(data)) { for (const item of data) { updateColumns(item); @@ -2765,5 +2822,7 @@ export { getTransactionFromTransactionListItem, getSearchColumnTranslationKey, getTableMinWidth, + getCustomColumns, + getCustomColumnDefault, }; export type {SavedSearchMenuItem, SearchTypeMenuSection, SearchTypeMenuItem, SearchDateModifier, SearchDateModifierLower, SearchKey, ArchivedReportsIDSet}; diff --git a/src/pages/Search/SearchColumnsPage.tsx b/src/pages/Search/SearchColumnsPage.tsx index 736a2466a7eb..5b03943d691c 100644 --- a/src/pages/Search/SearchColumnsPage.tsx +++ b/src/pages/Search/SearchColumnsPage.tsx @@ -13,27 +13,27 @@ import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; -import {clearAllFilters} from '@libs/actions/Search'; import Navigation from '@libs/Navigation/Navigation'; import {buildQueryStringFromFilterFormValues} from '@libs/SearchQueryUtils'; -import {getSearchColumnTranslationKey} from '@libs/SearchUIUtils'; +import {getCustomColumnDefault, getCustomColumns, getSearchColumnTranslationKey} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {SearchAdvancedFiltersForm} from '@src/types/form'; import arraysEqual from '@src/utils/arraysEqual'; -const allCustomColumns = Object.values(CONST.SEARCH.CUSTOM_COLUMNS); -const defaultCustomColumns = Object.values(CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE_REPORT); - function SearchColumnsPage() { const styles = useThemeStyles(); const {translate} = useLocalize(); const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {canBeMissing: true}); + const queryType = searchAdvancedFiltersForm?.type ?? CONST.SEARCH.DATA_TYPES.EXPENSE; + const allCustomColumns = getCustomColumns(queryType); + const defaultCustomColumns = getCustomColumnDefault(queryType); + const [selectedColumnIds, setSelectedColumnIds] = useState(() => { - const columnIds = searchAdvancedFiltersForm?.columns?.filter((columnId) => Object.values(CONST.SEARCH.CUSTOM_COLUMNS).includes(columnId)) ?? []; + const columnIds = searchAdvancedFiltersForm?.columns?.filter((columnId) => allCustomColumns.includes(columnId)) ?? []; // We dont allow the user to unselect all columns, so we can assume that no columns = default columns if (!columnIds.length) { @@ -81,7 +81,6 @@ function SearchColumnsPage() { const updatedAdvancedFilters: Partial = {...searchAdvancedFiltersForm, columns: selectedColumnIds}; const queryString = buildQueryStringFromFilterFormValues(updatedAdvancedFilters); - clearAllFilters(); Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: queryString}), {forceReplace: true}); }; diff --git a/src/pages/Search/SearchTypeMenu.tsx b/src/pages/Search/SearchTypeMenu.tsx index 99cbcf564c01..afdd77471f9c 100644 --- a/src/pages/Search/SearchTypeMenu.tsx +++ b/src/pages/Search/SearchTypeMenu.tsx @@ -100,7 +100,6 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) { return { ...baseMenuItem, onPress: () => { - clearAllFilters(); Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: item?.query ?? '', name: item?.name})); }, rightComponent: (