diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 57b6c454805a8..2d801cb5a43c6 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -6723,6 +6723,7 @@ const CONST = { TAX_AMOUNT: this.TABLE_COLUMNS.TAX_AMOUNT, STATUS: this.TABLE_COLUMNS.STATUS, TITLE: this.TABLE_COLUMNS.TITLE, + AMOUNT: this.TABLE_COLUMNS.TOTAL_AMOUNT, ACTION: this.TABLE_COLUMNS.ACTION, }, EXPENSE_REPORT: { @@ -6739,6 +6740,7 @@ const CONST = { NON_REIMBURSABLE_TOTAL: this.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL, REPORT_ID: this.TABLE_COLUMNS.REPORT_ID, BASE_62_REPORT_ID: this.TABLE_COLUMNS.BASE_62_REPORT_ID, + AMOUNT: this.TABLE_COLUMNS.TOTAL, ACTION: this.TABLE_COLUMNS.ACTION, }, INVOICE: {}, @@ -6757,9 +6759,18 @@ const CONST = { this.TABLE_COLUMNS.TO, this.TABLE_COLUMNS.CATEGORY, this.TABLE_COLUMNS.TAG, + this.TABLE_COLUMNS.TOTAL_AMOUNT, + 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.TOTAL, 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: [], diff --git a/src/components/DraggableList/SortableItem.tsx b/src/components/DraggableList/SortableItem.tsx index e3ea9b9c263d8..91c8e4f8d7401 100644 --- a/src/components/DraggableList/SortableItem.tsx +++ b/src/components/DraggableList/SortableItem.tsx @@ -3,8 +3,8 @@ import {CSS} from '@dnd-kit/utilities'; import React from 'react'; import type {SortableItemProps} from './types'; -function SortableItem({id, children}: SortableItemProps) { - const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id}); +function SortableItem({id, children, disabled = false}: SortableItemProps) { + const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id, disabled}); const style = { touchAction: 'none', @@ -19,7 +19,7 @@ function SortableItem({id, children}: SortableItemProps) { // eslint-disable-next-line react/jsx-props-no-spreading {...attributes} // eslint-disable-next-line react/jsx-props-no-spreading - {...listeners} + {...(disabled ? {} : listeners)} > {children} diff --git a/src/components/DraggableList/index.tsx b/src/components/DraggableList/index.tsx index 570cccdc4521f..a3e6f8d57c399 100644 --- a/src/components/DraggableList/index.tsx +++ b/src/components/DraggableList/index.tsx @@ -50,10 +50,13 @@ function DraggableList({ const sortableItems = data.map((item, index) => { const key = keyExtractor(item, index); + // Check if item has a disabled property for dragging + const isDisabled = typeof item === 'object' && item !== null && 'isDragDisabled' in item ? !!(item as {isDragDisabled?: boolean}).isDragDisabled : false; return ( {renderItem({ item, diff --git a/src/components/DraggableList/types.ts b/src/components/DraggableList/types.ts index 722f9c108b1e5..b2a0f25b80fb6 100644 --- a/src/components/DraggableList/types.ts +++ b/src/components/DraggableList/types.ts @@ -26,6 +26,8 @@ type DraggableListProps = { type SortableItemProps = { id: string | number; children: React.ReactNode | React.ReactNode[]; + /** Whether dragging is disabled for this item */ + disabled?: boolean; }; export default DraggableListProps; diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index 5b220ae5fd02c..c58fbeca28d71 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -9,7 +9,7 @@ import MenuItem from '@components/MenuItem'; import Modal from '@components/Modal'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {useSearchContext} from '@components/Search/SearchContext'; -import type {SearchColumnType, SortOrder} from '@components/Search/types'; +import type {SortOrder} from '@components/Search/types'; import Text from '@components/Text'; import {WideRHPContext} from '@components/WideRHPContextProvider'; import useCopySelectionHelper from '@hooks/useCopySelectionHelper'; @@ -264,9 +264,9 @@ function MoneyRequestReportTransactionList({ })); }, [newTransactions, sortBy, sortOrder, transactions, localeCompare, report]); + // Always use default columns for money request report view (don't use user-customized search columns) const columnsToShow = useMemo(() => { - const columns = getColumnsToShow(currentUserDetails?.accountID, transactions, [], true); - return (Object.keys(columns) as SearchColumnType[]).filter((column) => columns[column]); + return getColumnsToShow(currentUserDetails?.accountID, transactions, [], true); }, [transactions, currentUserDetails?.accountID]); const currentGroupBy = getReportLayoutGroupBy(reportLayoutGroupBy); diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index ac038e7f34358..9b53433552323 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -848,9 +848,7 @@ function Search({ if (!searchResults?.data) { return []; } - const columns = getColumnsToShow(accountID, searchResults?.data, visibleColumns, false, searchResults?.search?.type, validGroupBy); - - return (Object.keys(columns) as SearchColumnType[]).filter((col) => columns[col]); + return getColumnsToShow(accountID, searchResults?.data, visibleColumns, false, searchResults?.search?.type, validGroupBy); }, [accountID, searchResults?.data, searchResults?.search?.type, visibleColumns, validGroupBy]); const opacity = useSharedValue(1); diff --git a/src/components/SelectionListWithSections/MultiSelectListItem.tsx b/src/components/SelectionListWithSections/MultiSelectListItem.tsx index 230ef43dc4203..74664ff8a97ce 100644 --- a/src/components/SelectionListWithSections/MultiSelectListItem.tsx +++ b/src/components/SelectionListWithSections/MultiSelectListItem.tsx @@ -36,9 +36,10 @@ function MultiSelectListItem({ onPress={() => onSelectRow(item)} isIndeterminate={item.isIndeterminate} style={[isMultilineSupported ? styles.ml3 : null]} + disabled={isDisabled ?? false} /> ); - }, [isMultilineSupported, isSelected, item, onSelectRow, styles.ml3]); + }, [isDisabled, isMultilineSupported, isSelected, item, onSelectRow, styles.ml3]); return ( ({ if (!transactionsSnapshot?.data) { return []; } - const columnsToShow = getColumnsToShow(accountID, transactionsSnapshot?.data, visibleColumns, false, transactionsSnapshot?.search.type); - - return (Object.keys(columnsToShow) as SearchColumnType[]).filter((col) => columnsToShow[col]); + return getColumnsToShow(accountID, transactionsSnapshot?.data, visibleColumns, false, transactionsSnapshot?.search.type); }, [accountID, columns, isExpenseReportType, transactionsSnapshot?.data, transactionsSnapshot?.search.type, visibleColumns]); const areAllOptionalColumnsHidden = useMemo(() => { diff --git a/src/components/SelectionListWithSections/SearchTableHeader.tsx b/src/components/SelectionListWithSections/SearchTableHeader.tsx index b3546a2637a68..c430a66a973d5 100644 --- a/src/components/SelectionListWithSections/SearchTableHeader.tsx +++ b/src/components/SelectionListWithSections/SearchTableHeader.tsx @@ -422,17 +422,47 @@ function SearchTableHeader({ const columnConfig = useMemo(() => getSearchColumns(type, icons, groupBy, isExpenseReportView), [type, groupBy, icons, isExpenseReportView]); + const orderedColumnConfig = useMemo(() => { + if (!columnConfig) { + return null; + } + + const configMap = new Map(columnConfig.map((config) => [config.columnName, config])); + + // Users can customize column order via the Search Columns page. + // We respect their preferred order by placing user-selected columns first, + // then appending any remaining columns (which will be filtered out by shouldShowColumn). + const orderedConfig: SearchColumnConfig[] = []; + const addedColumns = new Set(); + + for (const col of columns) { + const config = configMap.get(col); + if (config) { + orderedConfig.push(config); + addedColumns.add(col); + } + } + + for (const config of columnConfig) { + if (!addedColumns.has(config.columnName)) { + orderedConfig.push(config); + } + } + + return orderedConfig; + }, [columnConfig, columns]); + if (displayNarrowVersion) { return; } - if (!columnConfig) { + if (!orderedColumnConfig) { return; } return ( Object.values(CONST.SEARCH.CUSTOM_COLUMNS.EXPENSE_REPORT).includes(column as ValueOf), ); if (!filteredVisibleColumns.length) { - return reportColumns; + return defaultReportColumns; } - // If the user has set custom columns, toggle the visible columns on, with all other - // columns hidden by default - const columns: ColumnVisibility = {}; - const requiredColumns = new Set([CONST.SEARCH.TABLE_COLUMNS.AVATAR, CONST.SEARCH.TABLE_COLUMNS.TOTAL]); - const columnsToShow = visibleColumns.length ? visibleColumns : CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE_REPORT; + // If the user has set custom columns, use their order then add required columns + const requiredColumns = new Set([CONST.SEARCH.TABLE_COLUMNS.AVATAR, CONST.SEARCH.TABLE_COLUMNS.TOTAL]); + const result: SearchColumnType[] = []; - for (const columnId of Object.keys(reportColumns) as SearchColumnType[]) { - columns[columnId] = requiredColumns.has(columnId); + for (const col of requiredColumns) { + if (!visibleColumns.includes(col as SearchCustomColumnIds)) { + result.push(col); + } } - for (const column of columnsToShow) { - columns[column as keyof ColumnVisibility] = true; + for (const col of visibleColumns) { + result.push(col); } - return columns; + return result; } if (type === CONST.SEARCH.DATA_TYPES.TASK) { - return { - [CONST.SEARCH.TABLE_COLUMNS.DATE]: true, - [CONST.SEARCH.TABLE_COLUMNS.TITLE]: true, - [CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION]: true, - [CONST.SEARCH.TABLE_COLUMNS.FROM]: true, - [CONST.SEARCH.TABLE_COLUMNS.IN]: true, - [CONST.SEARCH.TABLE_COLUMNS.ASSIGNEE]: true, - [CONST.SEARCH.TABLE_COLUMNS.ACTION]: true, - [CONST.SEARCH.TABLE_COLUMNS.RECEIPT]: false, - [CONST.SEARCH.TABLE_COLUMNS.MERCHANT]: false, - [CONST.SEARCH.TABLE_COLUMNS.TO]: false, - [CONST.SEARCH.TABLE_COLUMNS.CATEGORY]: false, - [CONST.SEARCH.TABLE_COLUMNS.TAG]: false, - [CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT]: false, - [CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: false, - [CONST.SEARCH.TABLE_COLUMNS.COMMENTS]: false, - [CONST.SEARCH.TABLE_COLUMNS.TYPE]: false, - [CONST.SEARCH.TABLE_COLUMNS.WITHDRAWAL_ID]: false, - }; + return [ + CONST.SEARCH.TABLE_COLUMNS.DATE, + CONST.SEARCH.TABLE_COLUMNS.TITLE, + CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION, + CONST.SEARCH.TABLE_COLUMNS.FROM, + CONST.SEARCH.TABLE_COLUMNS.IN, + CONST.SEARCH.TABLE_COLUMNS.ASSIGNEE, + CONST.SEARCH.TABLE_COLUMNS.ACTION, + ]; } if (!isExpenseReportView && groupBy) { switch (groupBy) { case CONST.SEARCH.GROUP_BY.FROM: - return { - [CONST.SEARCH.TABLE_COLUMNS.AVATAR]: true, - [CONST.SEARCH.TABLE_COLUMNS.FROM]: true, - [CONST.SEARCH.TABLE_COLUMNS.EXPENSES]: true, - [CONST.SEARCH.TABLE_COLUMNS.TOTAL]: true, - }; + return [CONST.SEARCH.TABLE_COLUMNS.AVATAR, CONST.SEARCH.TABLE_COLUMNS.FROM, CONST.SEARCH.TABLE_COLUMNS.EXPENSES, CONST.SEARCH.TABLE_COLUMNS.TOTAL]; case CONST.SEARCH.GROUP_BY.CARD: - return { - [CONST.SEARCH.TABLE_COLUMNS.AVATAR]: true, - [CONST.SEARCH.TABLE_COLUMNS.CARD]: true, - [CONST.SEARCH.TABLE_COLUMNS.FEED]: true, - [CONST.SEARCH.TABLE_COLUMNS.EXPENSES]: true, - [CONST.SEARCH.TABLE_COLUMNS.TOTAL]: true, - }; + return [ + CONST.SEARCH.TABLE_COLUMNS.AVATAR, + CONST.SEARCH.TABLE_COLUMNS.CARD, + CONST.SEARCH.TABLE_COLUMNS.FEED, + CONST.SEARCH.TABLE_COLUMNS.EXPENSES, + CONST.SEARCH.TABLE_COLUMNS.TOTAL, + ]; case CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID: - return { - [CONST.SEARCH.TABLE_COLUMNS.AVATAR]: true, - [CONST.SEARCH.TABLE_COLUMNS.BANK_ACCOUNT]: true, - [CONST.SEARCH.TABLE_COLUMNS.WITHDRAWN]: true, - [CONST.SEARCH.TABLE_COLUMNS.WITHDRAWAL_ID]: true, - [CONST.SEARCH.TABLE_COLUMNS.EXPENSES]: true, - [CONST.SEARCH.TABLE_COLUMNS.TOTAL]: true, - }; + return [ + CONST.SEARCH.TABLE_COLUMNS.AVATAR, + CONST.SEARCH.TABLE_COLUMNS.BANK_ACCOUNT, + CONST.SEARCH.TABLE_COLUMNS.WITHDRAWN, + CONST.SEARCH.TABLE_COLUMNS.WITHDRAWAL_ID, + CONST.SEARCH.TABLE_COLUMNS.EXPENSES, + CONST.SEARCH.TABLE_COLUMNS.TOTAL, + ]; default: - return {}; + return []; } } @@ -3070,14 +3052,33 @@ function getColumnsToShow( [CONST.SEARCH.TABLE_COLUMNS.TAX_RATE]: false, [CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT]: false, [CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT]: false, - [CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: true, [CONST.SEARCH.TABLE_COLUMNS.BASE_62_REPORT_ID]: false, [CONST.SEARCH.TABLE_COLUMNS.REPORT_ID]: false, [CONST.SEARCH.TABLE_COLUMNS.TITLE]: false, [CONST.SEARCH.TABLE_COLUMNS.STATUS]: false, + [CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: true, [CONST.SEARCH.TABLE_COLUMNS.ACTION]: true, }; + // If the user has set custom columns for the search, we need to respect their preference and order + 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]); + const result: SearchColumnType[] = []; + + // Add required columns that aren't in visibleColumns at the start + for (const col of requiredColumns) { + if (!visibleColumns.includes(col as SearchCustomColumnIds)) { + result.push(col); + } + } + + for (const col of visibleColumns) { + result.push(col); + } + + return result; + } + const {moneyRequestReportActionsByTransactionID} = Array.isArray(data) ? {} : createReportActionsLookupMaps(data); // eslint-disable-next-line @typescript-eslint/no-deprecated @@ -3125,21 +3126,6 @@ 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 - const filteredVisibleColumns = visibleColumns.filter((column) => - Object.values(CONST.SEARCH.CUSTOM_COLUMNS.EXPENSE).includes(column as ValueOf), - ); - if (!arraysEqual(Object.values(CONST.SEARCH.DEFAULT_COLUMNS.EXPENSE), filteredVisibleColumns) && filteredVisibleColumns.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); @@ -3153,7 +3139,7 @@ function getColumnsToShow( } } - return columns; + return (Object.keys(columns) as SearchColumnType[]).filter((col) => columns[col]); } /** diff --git a/src/pages/Search/SearchColumnsPage.tsx b/src/pages/Search/SearchColumnsPage.tsx index 1e11a5021b99a..08f920343a216 100644 --- a/src/pages/Search/SearchColumnsPage.tsx +++ b/src/pages/Search/SearchColumnsPage.tsx @@ -2,16 +2,18 @@ import React, {useState} from 'react'; import {View} from 'react-native'; import Button from '@components/Button'; import DotIndicatorMessage from '@components/DotIndicatorMessage'; +import DraggableList from '@components/DraggableList'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Icon from '@components/Icon'; import ScreenWrapper from '@components/ScreenWrapper'; import type {SearchCustomColumnIds} from '@components/Search/types'; import type {ListItem} from '@components/SelectionList/types'; -import SelectionList from '@components/SelectionListWithSections'; import MultiSelectListItem from '@components/SelectionListWithSections/MultiSelectListItem'; -import type {SectionListDataType} from '@components/SelectionListWithSections/types'; import TextLink from '@components/TextLink'; +import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import {buildQueryStringFromFilterFormValues} from '@libs/SearchQueryUtils'; @@ -20,11 +22,17 @@ 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'; + +type ColumnItem = { + columnId: SearchCustomColumnIds; + isSelected: boolean; +}; function SearchColumnsPage() { + const theme = useTheme(); const styles = useThemeStyles(); - const {translate} = useLocalize(); + const icons = useMemoizedLazyExpensifyIcons(['DragHandles']); + const {translate, localeCompare} = useLocalize(); const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {canBeMissing: true}); @@ -32,58 +40,137 @@ function SearchColumnsPage() { const allCustomColumns = getCustomColumns(queryType); const defaultCustomColumns = getCustomColumnDefault(queryType); - const [selectedColumnIds, setSelectedColumnIds] = useState(() => { - const columnIds = searchAdvancedFiltersForm?.columns?.filter((columnId) => allCustomColumns.includes(columnId)) ?? []; + // We need at least one element with flex1 in the table to ensure the table looks good in the UI, so we don't allow removing the total columns since it makes sense for them to show up in an expense management App and it fixes the layout issues. + const requiredColumns = new Set([CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, CONST.SEARCH.TABLE_COLUMNS.TOTAL]); + + const sortColumns = (columnsToSort: ColumnItem[]): ColumnItem[] => { + const selected = columnsToSort.filter((col) => col.isSelected); + const unselected = columnsToSort + .filter((col) => !col.isSelected) + .sort((a, b) => { + const textA = translate(getSearchColumnTranslationKey(a.columnId)); + const textB = translate(getSearchColumnTranslationKey(b.columnId)); + return localeCompare(textA, textB); + }); + return [...selected, ...unselected]; + }; + + const [columns, setColumns] = useState(() => { + const savedColumnIds = 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) { - return defaultCustomColumns; + if (!savedColumnIds.length) { + const initialColumns = allCustomColumns.map((columnId) => ({ + columnId, + isSelected: defaultCustomColumns.includes(columnId), + })); + return sortColumns(initialColumns); } - return columnIds; + const selected = savedColumnIds.map((columnId) => ({columnId, isSelected: true})); + const unselected = allCustomColumns.filter((columnId) => !savedColumnIds.includes(columnId)).map((columnId) => ({columnId, isSelected: false})); + + return sortColumns([...selected, ...unselected]); }); - const sections: Array> = [ - { - title: undefined, - data: allCustomColumns.map((columnId) => ({ - text: translate(getSearchColumnTranslationKey(columnId)), - value: columnId, - keyForList: columnId, - isSelected: selectedColumnIds?.includes(columnId), - })), - }, - ]; - - const sortedDefaultColumns = [...defaultCustomColumns].sort(); - const sortedSelectedColumnIds = [...selectedColumnIds].sort(); - const isDefaultColumns = arraysEqual(sortedSelectedColumnIds, sortedDefaultColumns); + const selectedColumnIds = columns.filter((col) => col.isSelected).map((col) => col.columnId); + + const columnsList = columns.map(({columnId, isSelected}) => { + const isRequired = requiredColumns.has(columnId); + const isEffectivelySelected = isRequired || isSelected; + const isDragDisabled = !isEffectivelySelected; + return { + text: translate(getSearchColumnTranslationKey(columnId)), + value: columnId, + keyForList: columnId, + isSelected: isEffectivelySelected, + isDisabled: isRequired, + isDragDisabled, + leftElement: ( + + + + ), + }; + }); + + const defaultColumns = sortColumns( + allCustomColumns.map((columnId) => ({ + columnId, + isSelected: defaultCustomColumns.includes(columnId), + })), + ); + + const isDefaultState = + columns.length === defaultColumns.length && + columns.every((col, index) => col.columnId === defaultColumns.at(index)?.columnId && col.isSelected === defaultColumns.at(index)?.isSelected); const onSelectItem = (item: ListItem) => { const updatedColumnId = item.keyForList as SearchCustomColumnIds; - if (item.isSelected) { - setSelectedColumnIds(selectedColumnIds.filter((columnId) => columnId !== updatedColumnId)); - } else { - setSelectedColumnIds([...selectedColumnIds, updatedColumnId]); + if (requiredColumns.has(updatedColumnId)) { + return; } + + setColumns((prevColumns) => { + const columnToUpdate = prevColumns.find((col) => col.columnId === updatedColumnId); + if (!columnToUpdate) { + return prevColumns; + } + + const newIsSelected = !columnToUpdate.isSelected; + + if (newIsSelected) { + const selected = prevColumns.filter((col) => col.isSelected); + const unselected = prevColumns.filter((col) => !col.isSelected && col.columnId !== updatedColumnId); + const unselectedSorted = unselected.sort((a, b) => { + const textA = translate(getSearchColumnTranslationKey(a.columnId)); + const textB = translate(getSearchColumnTranslationKey(b.columnId)); + return localeCompare(textA, textB); + }); + return [...selected, {columnId: updatedColumnId, isSelected: true}, ...unselectedSorted]; + } + + const updatedColumns = prevColumns.map((col) => (col.columnId === updatedColumnId ? {...col, isSelected: false} : col)); + return sortColumns(updatedColumns); + }); }; - const resetColumns = () => { - setSelectedColumnIds(defaultCustomColumns); + const onDragEnd = ({data}: {data: typeof columnsList}) => { + const newColumns = data.map((item) => ({columnId: item.value, isSelected: item.isSelected})); + setColumns(sortColumns(newColumns)); }; + const resetColumns = () => setColumns(defaultColumns); + const applyChanges = () => { if (!selectedColumnIds.length) { return; } - const updatedAdvancedFilters: Partial = {...searchAdvancedFiltersForm, columns: isDefaultColumns ? undefined : selectedColumnIds}; + const updatedAdvancedFilters: Partial = { + ...searchAdvancedFiltersForm, + columns: selectedColumnIds, + }; const queryString = buildQueryStringFromFilterFormValues(updatedAdvancedFilters); Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: queryString}), {forceReplace: true}); }; + const renderItem = ({item}: {item: ListItem}) => { + return ( + + ); + }; + return ( - {!isDefaultColumns && {translate('search.resetColumns')}} + {!isDefaultState && {translate('search.resetColumns')}} - - - {!selectedColumnIds.length && ( - - )} - -