Skip to content

Commit 6d7eaa6

Browse files
authored
Merge pull request #77709 from Expensify/jsenyitko-custom-columns-release-2
Custom Columns - Release #2
2 parents 72d2ae3 + fe5fe71 commit 6d7eaa6

7 files changed

Lines changed: 113 additions & 28 deletions

File tree

src/CONST/index.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6701,17 +6701,47 @@ const CONST = {
67016701
},
67026702
get CUSTOM_COLUMNS() {
67036703
return {
6704-
DATE: this.TABLE_COLUMNS.DATE,
6705-
STATUS: this.TABLE_COLUMNS.STATUS,
6706-
TITLE: this.TABLE_COLUMNS.TITLE,
6707-
FROM: this.TABLE_COLUMNS.FROM,
6708-
TO: this.TABLE_COLUMNS.TO,
6709-
ACTION: this.TABLE_COLUMNS.ACTION,
6704+
EXPENSE: {
6705+
RECEIPT: this.TABLE_COLUMNS.RECEIPT,
6706+
DATE: this.TABLE_COLUMNS.DATE,
6707+
MERCHANT: this.TABLE_COLUMNS.MERCHANT,
6708+
FROM: this.TABLE_COLUMNS.FROM,
6709+
TO: this.TABLE_COLUMNS.TO,
6710+
CATEGORY: this.TABLE_COLUMNS.CATEGORY,
6711+
TAG: this.TABLE_COLUMNS.TAG,
6712+
ACTION: this.TABLE_COLUMNS.ACTION,
6713+
},
6714+
EXPENSE_REPORT: {
6715+
DATE: this.TABLE_COLUMNS.DATE,
6716+
STATUS: this.TABLE_COLUMNS.STATUS,
6717+
TITLE: this.TABLE_COLUMNS.TITLE,
6718+
FROM: this.TABLE_COLUMNS.FROM,
6719+
TO: this.TABLE_COLUMNS.TO,
6720+
ACTION: this.TABLE_COLUMNS.ACTION,
6721+
},
6722+
INVOICE: {},
6723+
TASK: {},
6724+
TRIP: {},
6725+
CHAT: {},
67106726
};
67116727
},
67126728
get DEFAULT_COLUMNS() {
67136729
return {
6730+
EXPENSE: [
6731+
this.TABLE_COLUMNS.RECEIPT,
6732+
this.TABLE_COLUMNS.DATE,
6733+
this.TABLE_COLUMNS.MERCHANT,
6734+
this.TABLE_COLUMNS.FROM,
6735+
this.TABLE_COLUMNS.TO,
6736+
this.TABLE_COLUMNS.CATEGORY,
6737+
this.TABLE_COLUMNS.TAG,
6738+
this.TABLE_COLUMNS.ACTION,
6739+
],
67146740
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],
6741+
INVOICE: [],
6742+
TASK: [],
6743+
TRIP: [],
6744+
CHAT: [],
67156745
};
67166746
},
67176747
BOOLEAN: {

src/components/Search/SearchPageHeader/SearchFiltersBar.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ function SearchFiltersBar({
307307
// If the type has changed, reset the status so we dont have an invalid status selected
308308
if (updatedFilterFormValues.type !== filterFormValues.type) {
309309
updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL;
310+
updatedFilterFormValues.columns = [];
310311
}
311312

312313
const queryString = buildQueryStringFromFilterFormValues(updatedFilterFormValues);
@@ -781,7 +782,7 @@ function SearchFiltersBar({
781782
[],
782783
);
783784

784-
const shouldShowColumnsButton = isLargeScreenWidth && queryJSON.type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT;
785+
const shouldShowColumnsButton = isLargeScreenWidth && (queryJSON.type === CONST.SEARCH.DATA_TYPES.EXPENSE || queryJSON.type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT);
785786

786787
const filterButtonText = useMemo(
787788
() => translate('search.filtersHeader') + (hiddenSelectedFilters.length > 0 ? ` (${hiddenSelectedFilters.length})` : ''),

src/components/Search/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ type TableColumnSize = ValueOf<typeof CONST.SEARCH.TABLE_COLUMN_SIZES>;
107107
type SearchDatePreset = ValueOf<typeof CONST.SEARCH.DATE_PRESETS>;
108108
type SearchWithdrawalType = ValueOf<typeof CONST.SEARCH.WITHDRAWAL_TYPE>;
109109
type SearchAction = ValueOf<typeof CONST.SEARCH.ACTION_FILTERS>;
110-
type SearchCustomColumnIds = ValueOf<typeof CONST.SEARCH.CUSTOM_COLUMNS>;
110+
type SearchCustomColumnIds = ValueOf<typeof CONST.SEARCH.CUSTOM_COLUMNS.EXPENSE> | ValueOf<typeof CONST.SEARCH.CUSTOM_COLUMNS.EXPENSE_REPORT>;
111111

112112
type SearchContextData = {
113113
currentSearchHash: number;

src/hooks/useSearchTypeMenu.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import type {PopoverMenuItem} from '@components/PopoverMenu';
66
import {useSearchContext} from '@components/Search/SearchContext';
77
import type {SearchQueryJSON} from '@components/Search/types';
88
import ThreeDotsMenu from '@components/ThreeDotsMenu';
9-
import {clearAllFilters} from '@libs/actions/Search';
109
import {mergeCardListWithWorkspaceFeeds} from '@libs/CardUtils';
1110
import Navigation from '@libs/Navigation/Navigation';
1211
import {getAllTaxRates} from '@libs/PolicyUtils';
@@ -109,7 +108,6 @@ export default function useSearchTypeMenu(queryJSON: SearchQueryJSON) {
109108
return {
110109
...baseMenuItem,
111110
onSelected: () => {
112-
clearAllFilters();
113111
Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: item?.query ?? '', name: item?.name}));
114112
},
115113
rightComponent: (
@@ -181,7 +179,6 @@ export default function useSearchTypeMenu(queryJSON: SearchQueryJSON) {
181179
containerStyle: isSelected ? [{backgroundColor: theme.border}] : undefined,
182180
shouldCallAfterModalHide: true,
183181
onSelected: singleExecution(() => {
184-
clearAllFilters();
185182
Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: item.searchQuery}));
186183
}),
187184
});

src/libs/SearchUIUtils.ts

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import type {
5858
SearchWithdrawalIDGroup,
5959
} from '@src/types/onyx/SearchResults';
6060
import type IconAsset from '@src/types/utils/IconAsset';
61+
import arraysEqual from '@src/utils/arraysEqual';
6162
import {hasSynchronizationErrorMessage} from './actions/connections';
6263
import {canApproveIOU, canIOUBePaid, canSubmitReport, startMoneyRequest} from './actions/IOU';
6364
import {setIsOpenConfirmNavigateExpensifyClassicModalOpen} from './actions/isOpenConfirmNavigateExpensifyClassicModal';
@@ -2049,21 +2050,65 @@ function getExpenseTypeTranslationKey(expenseType: ValueOf<typeof CONST.SEARCH.T
20492050
}
20502051
}
20512052

2052-
function getSearchColumnTranslationKey(columnId: ValueOf<typeof CONST.SEARCH.CUSTOM_COLUMNS>): TranslationPaths {
2053+
function getCustomColumns(type: SearchDataTypes): SearchCustomColumnIds[] {
2054+
// eslint-disable-next-line default-case
2055+
switch (type) {
2056+
case CONST.SEARCH.DATA_TYPES.EXPENSE:
2057+
return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.EXPENSE);
2058+
case CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT:
2059+
return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.EXPENSE_REPORT);
2060+
case CONST.SEARCH.DATA_TYPES.INVOICE:
2061+
return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.INVOICE);
2062+
case CONST.SEARCH.DATA_TYPES.TASK:
2063+
return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.TASK);
2064+
case CONST.SEARCH.DATA_TYPES.TRIP:
2065+
return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.TRIP);
2066+
case CONST.SEARCH.DATA_TYPES.CHAT:
2067+
return Object.values(CONST.SEARCH.CUSTOM_COLUMNS.CHAT);
2068+
}
2069+
}
2070+
2071+
function getCustomColumnDefault(type: SearchDataTypes): SearchCustomColumnIds[] {
2072+
// eslint-disable-next-line default-case
2073+
switch (type) {
2074+
case CONST.SEARCH.DATA_TYPES.EXPENSE:
2075+
return CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE;
2076+
case CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT:
2077+
return CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE_REPORT;
2078+
case CONST.SEARCH.DATA_TYPES.INVOICE:
2079+
return CONST.SEARCH.DEFAULT_COLUMNS.INVOICE;
2080+
case CONST.SEARCH.DATA_TYPES.TASK:
2081+
return CONST.SEARCH.DEFAULT_COLUMNS.TASK;
2082+
case CONST.SEARCH.DATA_TYPES.TRIP:
2083+
return CONST.SEARCH.DEFAULT_COLUMNS.TRIP;
2084+
case CONST.SEARCH.DATA_TYPES.CHAT:
2085+
return CONST.SEARCH.DEFAULT_COLUMNS.CHAT;
2086+
}
2087+
}
2088+
2089+
function getSearchColumnTranslationKey(columnId: SearchCustomColumnIds): TranslationPaths {
20532090
// eslint-disable-next-line default-case
20542091
switch (columnId) {
2055-
case CONST.SEARCH.CUSTOM_COLUMNS.DATE:
2092+
case CONST.SEARCH.TABLE_COLUMNS.DATE:
20562093
return 'common.date';
2057-
case CONST.SEARCH.CUSTOM_COLUMNS.STATUS:
2058-
return 'common.status';
2059-
case CONST.SEARCH.CUSTOM_COLUMNS.TITLE:
2060-
return 'common.title';
2061-
case CONST.SEARCH.CUSTOM_COLUMNS.FROM:
2094+
case CONST.SEARCH.TABLE_COLUMNS.MERCHANT:
2095+
return 'common.merchant';
2096+
case CONST.SEARCH.TABLE_COLUMNS.FROM:
20622097
return 'common.from';
2063-
case CONST.SEARCH.CUSTOM_COLUMNS.TO:
2098+
case CONST.SEARCH.TABLE_COLUMNS.TO:
20642099
return 'common.to';
2065-
case CONST.SEARCH.CUSTOM_COLUMNS.ACTION:
2100+
case CONST.SEARCH.TABLE_COLUMNS.CATEGORY:
2101+
return 'common.category';
2102+
case CONST.SEARCH.TABLE_COLUMNS.RECEIPT:
2103+
return 'common.receipt';
2104+
case CONST.SEARCH.TABLE_COLUMNS.TAG:
2105+
return 'common.tag';
2106+
case CONST.SEARCH.TABLE_COLUMNS.ACTION:
20662107
return 'common.action';
2108+
case CONST.SEARCH.TABLE_COLUMNS.TITLE:
2109+
return 'common.title';
2110+
case CONST.SEARCH.TABLE_COLUMNS.STATUS:
2111+
return 'common.status';
20672112
}
20682113
}
20692114

@@ -2571,6 +2616,18 @@ function getColumnsToShow(
25712616
}
25722617
};
25732618

2619+
// If the user has set custom columns for the search, we need to respect their preference, and only show
2620+
// them what they want to see
2621+
if (!arraysEqual(Object.values(CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE), visibleColumns) && visibleColumns.length > 0) {
2622+
const requiredColumns = new Set<keyof ColumnVisibility>([CONST.SEARCH.TABLE_COLUMNS.AVATAR, CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, CONST.SEARCH.TABLE_COLUMNS.TYPE]);
2623+
2624+
for (const column of Object.keys(columns) as SearchCustomColumnIds[]) {
2625+
columns[column] = visibleColumns.includes(column) || requiredColumns.has(column);
2626+
}
2627+
2628+
return columns;
2629+
}
2630+
25742631
if (Array.isArray(data)) {
25752632
for (const item of data) {
25762633
updateColumns(item);
@@ -2765,5 +2822,7 @@ export {
27652822
getTransactionFromTransactionListItem,
27662823
getSearchColumnTranslationKey,
27672824
getTableMinWidth,
2825+
getCustomColumns,
2826+
getCustomColumnDefault,
27682827
};
27692828
export type {SavedSearchMenuItem, SearchTypeMenuSection, SearchTypeMenuItem, SearchDateModifier, SearchDateModifierLower, SearchKey, ArchivedReportsIDSet};

src/pages/Search/SearchColumnsPage.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,27 @@ import TextLink from '@components/TextLink';
1313
import useLocalize from '@hooks/useLocalize';
1414
import useOnyx from '@hooks/useOnyx';
1515
import useThemeStyles from '@hooks/useThemeStyles';
16-
import {clearAllFilters} from '@libs/actions/Search';
1716
import Navigation from '@libs/Navigation/Navigation';
1817
import {buildQueryStringFromFilterFormValues} from '@libs/SearchQueryUtils';
19-
import {getSearchColumnTranslationKey} from '@libs/SearchUIUtils';
18+
import {getCustomColumnDefault, getCustomColumns, getSearchColumnTranslationKey} from '@libs/SearchUIUtils';
2019
import CONST from '@src/CONST';
2120
import ONYXKEYS from '@src/ONYXKEYS';
2221
import ROUTES from '@src/ROUTES';
2322
import type {SearchAdvancedFiltersForm} from '@src/types/form';
2423
import arraysEqual from '@src/utils/arraysEqual';
2524

26-
const allCustomColumns = Object.values(CONST.SEARCH.CUSTOM_COLUMNS);
27-
const defaultCustomColumns = Object.values(CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE_REPORT);
28-
2925
function SearchColumnsPage() {
3026
const styles = useThemeStyles();
3127
const {translate} = useLocalize();
3228

3329
const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {canBeMissing: true});
3430

31+
const queryType = searchAdvancedFiltersForm?.type ?? CONST.SEARCH.DATA_TYPES.EXPENSE;
32+
const allCustomColumns = getCustomColumns(queryType);
33+
const defaultCustomColumns = getCustomColumnDefault(queryType);
34+
3535
const [selectedColumnIds, setSelectedColumnIds] = useState<SearchCustomColumnIds[]>(() => {
36-
const columnIds = searchAdvancedFiltersForm?.columns?.filter((columnId) => Object.values(CONST.SEARCH.CUSTOM_COLUMNS).includes(columnId)) ?? [];
36+
const columnIds = searchAdvancedFiltersForm?.columns?.filter((columnId) => allCustomColumns.includes(columnId)) ?? [];
3737

3838
// We dont allow the user to unselect all columns, so we can assume that no columns = default columns
3939
if (!columnIds.length) {
@@ -81,7 +81,6 @@ function SearchColumnsPage() {
8181
const updatedAdvancedFilters: Partial<SearchAdvancedFiltersForm> = {...searchAdvancedFiltersForm, columns: selectedColumnIds};
8282
const queryString = buildQueryStringFromFilterFormValues(updatedAdvancedFilters);
8383

84-
clearAllFilters();
8584
Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: queryString}), {forceReplace: true});
8685
};
8786

src/pages/Search/SearchTypeMenu.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) {
100100
return {
101101
...baseMenuItem,
102102
onPress: () => {
103-
clearAllFilters();
104103
Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: item?.query ?? '', name: item?.name}));
105104
},
106105
rightComponent: (

0 commit comments

Comments
 (0)