From 5802fcfba78063c8aed4b1cb6ed7cd20eb0d0ee7 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 1 Apr 2025 16:50:23 +0200 Subject: [PATCH 01/10] Add SearchFiltersWorkspacePage --- src/CONST.ts | 2 +- src/ROUTES.ts | 1 + src/SCREENS.ts | 1 + .../Navigation/BottomTabBar/index.tsx | 2 +- src/components/Search/types.ts | 2 +- .../ModalStackNavigators/index.tsx | 1 + .../linkingConfig/RELATIONS/SEARCH_TO_RHP.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/libs/SearchQueryUtils.ts | 8 +- src/pages/Search/AdvancedSearchFilters.tsx | 31 +++++- .../SearchFiltersWorkspacePage.tsx | 103 ++++++++++++++++++ 11 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersWorkspacePage.tsx diff --git a/src/CONST.ts b/src/CONST.ts index 7ea66fab2b95..3f5a9a872b59 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6442,7 +6442,6 @@ const CONST = { STATUS: 'status', SORT_BY: 'sortBy', SORT_ORDER: 'sortOrder', - POLICY_ID: 'policyID', GROUP_BY: 'groupBy', }, SYNTAX_FILTER_KEYS: { @@ -6467,6 +6466,7 @@ const CONST = { PAID: 'paid', EXPORTED: 'exported', POSTED: 'posted', + POLICY_ID: 'policyID', }, EMPTY_VALUE: 'none', SEARCH_ROUTER_ITEM_TYPE: { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index b839de81ab24..6b55f97b40e8 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -68,6 +68,7 @@ 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_WORKSPACE: 'search/filters/workspace', SEARCH_REPORT: { route: 'search/view/:reportID/:reportActionID?', getRoute: ({ diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5b00de790ba5..562b047efe97 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -61,6 +61,7 @@ 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_WORKSPACE_RHP: 'Search_Advanced_Filters_Workspace_RHP', SAVED_SEARCH_RENAME_RHP: 'Search_Saved_Search_Rename_RHP', ADVANCED_FILTERS_IN_RHP: 'Search_Advanced_Filters_In_RHP', TRANSACTION_HOLD_REASON_RHP: 'Search_Transaction_Hold_Reason_RHP', diff --git a/src/components/Navigation/BottomTabBar/index.tsx b/src/components/Navigation/BottomTabBar/index.tsx index efbedba70d88..a104479c7eb3 100644 --- a/src/components/Navigation/BottomTabBar/index.tsx +++ b/src/components/Navigation/BottomTabBar/index.tsx @@ -102,7 +102,7 @@ function BottomTabBar({selectedTab, isTooltipAllowed = false}: BottomTabBarProps } } // when navigating to search we might have an activePolicyID set from workspace switcher - const query = activeWorkspaceID ? `${defaultCannedQuery} ${CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID}:${activeWorkspaceID}` : defaultCannedQuery; + const query = activeWorkspaceID ? `${defaultCannedQuery} ${CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID}:${activeWorkspaceID}` : defaultCannedQuery; Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query})); }); }, [activeWorkspaceID, selectedTab]); diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 04151ee4c7e3..5c8f089c330d 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -98,7 +98,7 @@ type SearchFilterKey = | ValueOf | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS - | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID + | typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.GROUP_BY; type UserFriendlyKey = ValueOf; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 0d6e61af8c11..416e5160fafe 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -717,6 +717,7 @@ const SearchAdvancedFiltersModalStackNavigator = createModalStackNavigator require('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersFromPage').default, [SCREENS.SEARCH.ADVANCED_FILTERS_TO_RHP]: () => require('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersToPage').default, [SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP]: () => require('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersInPage').default, + [SCREENS.SEARCH.ADVANCED_FILTERS_WORKSPACE_RHP]: () => require('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersWorkspacePage').default, }); const SearchSavedSearchModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts index feee223c233c..5bb7b03ba723 100644 --- a/src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts @@ -24,6 +24,7 @@ const SEARCH_TO_RHP: string[] = [ SCREENS.SEARCH.ADVANCED_FILTERS_FROM_RHP, SCREENS.SEARCH.ADVANCED_FILTERS_TO_RHP, SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP, + SCREENS.SEARCH.ADVANCED_FILTERS_WORKSPACE_RHP, SCREENS.SEARCH.ADVANCED_FILTERS_CARD_RHP, SCREENS.SEARCH.SAVED_SEARCH_RENAME_RHP, ]; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index beb7681902e7..36d3897afcfc 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1431,6 +1431,7 @@ const config: LinkingOptions['config'] = { [SCREENS.SEARCH.ADVANCED_FILTERS_FROM_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_FROM, [SCREENS.SEARCH.ADVANCED_FILTERS_TO_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_TO, [SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_IN, + [SCREENS.SEARCH.ADVANCED_FILTERS_WORKSPACE_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_WORKSPACE, }, }, [SCREENS.RIGHT_MODAL.SEARCH_SAVED_SEARCH]: { diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index 571d54428e5d..bebb6307ca25 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -251,7 +251,7 @@ function getQueryHashes(query: SearchQueryJSON): {primaryHash: number; recentSea orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.SORT_BY}:${query.sortBy}`; orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.SORT_ORDER}:${query.sortOrder}`; if (query.policyID) { - orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID}:${query.policyID} `; + orderedQuery += ` ${CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID}:${query.policyID} `; } const primaryHash = hashText(orderedQuery, 2 ** 32); @@ -305,6 +305,10 @@ function buildSearchQueryString(queryJSON?: SearchQueryJSON) { } } + if (queryJSON?.policyID) { + queryParts.push(`${CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID}:${queryJSON.policyID}`); + } + if (!queryJSON) { return queryParts.join(' '); } @@ -345,7 +349,7 @@ function buildQueryStringFromFilterFormValues(filterValues: Partial>>> = { [CONST.SEARCH.DATA_TYPES.EXPENSE]: [ - [CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD, CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, CONST.SEARCH.SYNTAX_FILTER_KEYS.TO], + [CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD, CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, CONST.SEARCH.SYNTAX_FILTER_KEYS.TO, CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID], [ CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE, CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT, @@ -166,7 +171,7 @@ const typeFiltersKeys: Record, policies: OnyxCollection) { + const workspaceFilter = filters[CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID]; + + if (!policies) { + return ''; + } + + return policies[`${ONYXKEYS.COLLECTION.POLICY}${workspaceFilter}`]?.name; +} + function getFilterCardDisplayTitle(filters: Partial, cards: CardList, translate: LocaleContextProps['translate']) { const cardIdsFilter = filters[CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID] ?? []; const feedFilter = filters[CONST.SEARCH.SYNTAX_FILTER_KEYS.FEED] ?? []; @@ -527,6 +548,8 @@ function AdvancedSearchFilters() { filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters[key] ?? [], personalDetails); } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) { filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, translate, reports); + } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID) { + filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, policies); } return { key, diff --git a/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersWorkspacePage.tsx b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersWorkspacePage.tsx new file mode 100644 index 000000000000..d0f59ac1fcdc --- /dev/null +++ b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersWorkspacePage.tsx @@ -0,0 +1,103 @@ +import React, {useState} from 'react'; +import {useOnyx} from 'react-native-onyx'; +import Button from '@components/Button'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import UserListItem from '@components/SelectionList/UserListItem'; +import useDebouncedState from '@hooks/useDebouncedState'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; +import useThemeStyles from '@hooks/useThemeStyles'; +import type {WorkspaceListItem} from '@hooks/useWorkspaceList'; +import useWorkspaceList from '@hooks/useWorkspaceList'; +import {updateAdvancedFilters} from '@libs/actions/Search'; +import Navigation from '@libs/Navigation/Navigation'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; + +const updateWorkspaceFilter = (policyID: string | undefined) => { + updateAdvancedFilters({ + policyID, + }); + Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS); +}; + +function SearchFiltersWorkspacePage() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const {isOffline} = useNetwork(); + + const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM); + const [policies, policiesResult] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); + const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); + const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); + const shouldShowLoadingIndicator = isLoadingApp && !isOffline; + + const [selectedPolicyID, setSelectedPolicyID] = useState(searchAdvancedFiltersForm?.policyID); + + const {sections, shouldShowNoResultsFoundMessage, shouldShowSearchInput} = useWorkspaceList({ + policies, + currentUserLogin, + shouldShowPendingDeletePolicy: false, + selectedPolicyID, + searchTerm: debouncedSearchTerm, + }); + + const footerContent = ( +