diff --git a/src/components/Search/SearchMultipleSelectionPicker.tsx b/src/components/Search/SearchMultipleSelectionPicker.tsx index b26c2a62fcef..0dcb43baf874 100644 --- a/src/components/Search/SearchMultipleSelectionPicker.tsx +++ b/src/components/Search/SearchMultipleSelectionPicker.tsx @@ -35,7 +35,7 @@ function SearchMultipleSelectionPicker({items, initiallySelectedItems, pickerTit const {sections, noResultsFound} = useMemo(() => { const selectedItemsSection = selectedItems .filter((item) => item?.name.toLowerCase().includes(debouncedSearchTerm?.toLowerCase())) - .sort((a, b) => sortOptionsWithEmptyValue(a.value.toString(), b.value.toString())) + .sort((a, b) => sortOptionsWithEmptyValue(a.value as string, b.value as string)) .map((item) => ({ text: item.name, keyForList: item.name, @@ -43,11 +43,8 @@ function SearchMultipleSelectionPicker({items, initiallySelectedItems, pickerTit value: item.value, })); const remainingItemsSection = items - .filter( - (item) => - !selectedItems.some((selectedItem) => selectedItem.value.toString() === item.value.toString()) && item?.name?.toLowerCase().includes(debouncedSearchTerm?.toLowerCase()), - ) - .sort((a, b) => sortOptionsWithEmptyValue(a.value.toString(), b.value.toString())) + .filter((item) => selectedItems.some((selectedItem) => selectedItem.value === item.value) === false && item?.name?.toLowerCase().includes(debouncedSearchTerm?.toLowerCase())) + .sort((a, b) => sortOptionsWithEmptyValue(a.value as string, b.value as string)) .map((item) => ({ text: item.name, keyForList: item.name, diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index babb538a11ea..221ce73b6c5b 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -143,7 +143,7 @@ type SearchQueryAST = { sortOrder: SortOrder; groupBy?: SearchGroupBy; filters: ASTNode; - policyID?: string[]; + policyID?: string; }; type SearchQueryJSON = { diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index d2963a5234a6..a0fa27a5cc43 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -41,7 +41,6 @@ function UserListItem({ shouldSyncFocus, wrapperStyle, pressableStyle, - shouldUseDefaultRightHandSideCheckmark, }: UserListItemProps) { const styles = useThemeStyles(); const theme = useTheme(); @@ -88,7 +87,7 @@ function UserListItem({ > {(hovered?: boolean) => ( <> - {!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( + {!!canSelectMultiple && ( ({ /> )} - {!!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( - - - {!!item.isSelected && ( - - )} - - - )} )} diff --git a/src/hooks/useWorkspaceList.ts b/src/hooks/useWorkspaceList.ts index 6cff4900a53c..76d1a4f35f80 100644 --- a/src/hooks/useWorkspaceList.ts +++ b/src/hooks/useWorkspaceList.ts @@ -21,12 +21,12 @@ type UseWorkspaceListParams = { policies: OnyxCollection; currentUserLogin: string | undefined; shouldShowPendingDeletePolicy: boolean; - selectedPolicyIDs: string[] | undefined; + selectedPolicyID: string | undefined; searchTerm: string; additionalFilter?: (policy: OnyxEntry) => boolean; }; -function useWorkspaceList({policies, currentUserLogin, selectedPolicyIDs, searchTerm, shouldShowPendingDeletePolicy, additionalFilter}: UseWorkspaceListParams) { +function useWorkspaceList({policies, currentUserLogin, selectedPolicyID, searchTerm, shouldShowPendingDeletePolicy, additionalFilter}: UseWorkspaceListParams) { const usersWorkspaces = useMemo(() => { if (!policies || isEmptyObject(policies)) { return []; @@ -54,16 +54,16 @@ function useWorkspaceList({policies, currentUserLogin, selectedPolicyIDs, search ], keyForList: policy?.id, isPolicyAdmin: isPolicyAdmin(policy), - isSelected: policy?.id && selectedPolicyIDs ? selectedPolicyIDs.includes(policy.id) : false, + isSelected: selectedPolicyID === policy?.id, })); - }, [policies, shouldShowPendingDeletePolicy, currentUserLogin, additionalFilter, selectedPolicyIDs]); + }, [policies, shouldShowPendingDeletePolicy, currentUserLogin, additionalFilter, selectedPolicyID]); const filteredAndSortedUserWorkspaces = useMemo( () => tokenizedSearch(usersWorkspaces, searchTerm, (policy) => [policy.text]).sort((policy1, policy2) => - sortWorkspacesBySelected({policyID: policy1.policyID, name: policy1.text}, {policyID: policy2.policyID, name: policy2.text}, selectedPolicyIDs), + sortWorkspacesBySelected({policyID: policy1.policyID, name: policy1.text}, {policyID: policy2.policyID, name: policy2.text}, selectedPolicyID), ), - [searchTerm, usersWorkspaces, selectedPolicyIDs], + [searchTerm, usersWorkspaces, selectedPolicyID], ); const sections = useMemo(() => { diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 0035c9ae8ca5..bba43e856650 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -578,9 +578,6 @@ function getAllTaxRatesNamesAndKeys(): Record { allTaxRates[taxRate.name] = [taxRateKey]; return; } - if (allTaxRates[taxRate.name].includes(taxRateKey)) { - return; - } allTaxRates[taxRate.name].push(taxRateKey); }); }); @@ -1147,11 +1144,11 @@ function getSageIntacctCreditCards(policy?: Policy, selectedAccount?: string): S * @param workspace2 Details of the second workspace to be compared. * @param selectedWorkspaceID ID of the selected workspace which needs to be at the beginning. */ -const sortWorkspacesBySelected = (workspace1: WorkspaceDetails, workspace2: WorkspaceDetails, selectedWorkspaceIDs: string[] | undefined): number => { - if (workspace1.policyID && selectedWorkspaceIDs?.includes(workspace1?.policyID)) { +const sortWorkspacesBySelected = (workspace1: WorkspaceDetails, workspace2: WorkspaceDetails, selectedWorkspaceID: string | undefined): number => { + if (workspace1.policyID === selectedWorkspaceID) { return -1; } - if (workspace2.policyID && selectedWorkspaceIDs?.includes(workspace2.policyID)) { + if (workspace2.policyID === selectedWorkspaceID) { return 1; } return workspace1.name?.toLowerCase().localeCompare(workspace2.name?.toLowerCase() ?? '') ?? 0; diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index fefbcf8057d6..f4c043baadd6 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -15,7 +15,6 @@ import type { } from '@components/Search/types'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; -import type {OnyxCollectionKey, OnyxCollectionValuesMapping} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import type {SearchAdvancedFiltersForm} from '@src/types/form'; @@ -296,7 +295,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_FILTER_KEYS.POLICY_ID}:${Array.isArray(query.policyID) ? query.policyID.join(',') : query.policyID} `; + orderedQuery += ` ${CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID}:${query.policyID} `; } const primaryHash = hashText(orderedQuery, 2 ** 32); @@ -335,11 +334,6 @@ function buildSearchQueryJSON(query: SearchQueryString) { result.hash = primaryHash; result.recentSearchHash = recentSearchHash; - if (result.policyID && typeof result.policyID === 'string') { - // Ensure policyID is always an array for consistency - result.policyID = [result.policyID]; - } - return result; } catch (e) { console.error(`Error when parsing SearchQuery: "${query}"`, e); @@ -370,7 +364,7 @@ function buildSearchQueryString(queryJSON?: SearchQueryJSON) { } if (queryJSON?.policyID) { - queryParts.push(`${CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID}:${Array.isArray(queryJSON.policyID) ? queryJSON.policyID.join(',') : queryJSON.policyID}`); + queryParts.push(`${CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID}:${queryJSON.policyID}`); } if (!queryJSON) { @@ -395,7 +389,7 @@ function buildSearchQueryString(queryJSON?: SearchQueryJSON) { */ function buildQueryStringFromFilterFormValues(filterValues: Partial) { // We separate type and status filters from other filters to maintain hashes consistency for saved searches - const {type, status, groupBy, ...otherFilters} = filterValues; + const {type, status, policyID, groupBy, ...otherFilters} = filterValues; const filtersString: string[] = []; // When switching types/setting the type, ensure we aren't polluting our query with filters that are @@ -436,6 +430,11 @@ function buildQueryStringFromFilterFormValues(filterValues: Partial { if ( @@ -482,7 +481,6 @@ function buildQueryStringFromFilterFormValues(filterValues: Partial 0 @@ -512,18 +510,6 @@ function buildQueryStringFromFilterFormValues(filterValues: Partial( - policyID: string[] | undefined, - key: T, - policyData: OnyxCollection, -): Array { - if (!policyData || !policyID) { - return []; - } - - return policyID.map((id) => policyData?.[`${key}${id}`]).filter((data) => !!data) as Array; -} - /** * Generates object with search filter values, in a format that can be consumed by SearchAdvancedFiltersForm. * Main usage of this is to generate the initial values for AdvancedFilters from existing query. @@ -592,9 +578,7 @@ function buildFilterFormValuesFromQuery( } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG) { const tags = policyID - ? getAllPolicyValues(policyID, ONYXKEYS.COLLECTION.POLICY_TAGS, policyTags) - .map((tagList) => getTagNamesFromTagsLists(tagList ?? {})) - .flat() + ? getTagNamesFromTagsLists(policyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {}) : Object.values(policyTags ?? {}) .filter((item) => !!item) .map((tagList) => getTagNamesFromTagsLists(tagList ?? {})) @@ -605,9 +589,7 @@ function buildFilterFormValuesFromQuery( } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY) { const categories = policyID - ? getAllPolicyValues(policyID, ONYXKEYS.COLLECTION.POLICY_CATEGORIES, policyCategories) - .map((item) => Object.values(item ?? {}).map((category) => category.name)) - .flat() + ? Object.values(policyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}).map((category) => category.name) : Object.values(policyCategories ?? {}) .map((item) => Object.values(item ?? {}).map((category) => category.name)) .flat(); @@ -771,8 +753,9 @@ function buildUserReadableQueryString( title += ` group-by:${groupBy}`; } - if (policyID && policyID.length > 0) { - title += ` workspace:${policyID.map((id) => sanitizeSearchValue(policies?.[`${ONYXKEYS.COLLECTION.POLICY}${id}`]?.name ?? id)).join(',')}`; + if (policyID) { + const workspace = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? policyID; + title += ` workspace:${sanitizeSearchValue(workspace)}`; } for (const filterObject of filters) { @@ -1008,5 +991,4 @@ export { isDefaultExpensesQuery, sortOptionsWithEmptyValue, shouldHighlight, - getAllPolicyValues, }; diff --git a/src/pages/ReportChangeWorkspacePage.tsx b/src/pages/ReportChangeWorkspacePage.tsx index 80bf2a5375d0..1152661a06e2 100644 --- a/src/pages/ReportChangeWorkspacePage.tsx +++ b/src/pages/ReportChangeWorkspacePage.tsx @@ -68,7 +68,7 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro policies, currentUserLogin: session?.email, shouldShowPendingDeletePolicy: false, - selectedPolicyIDs: report.policyID ? [report.policyID] : undefined, + selectedPolicyID: report.policyID, searchTerm: debouncedSearchTerm, additionalFilter: (newPolicy) => isWorkspaceEligibleForReportChange(newPolicy, report, policies), }); diff --git a/src/pages/Search/AdvancedSearchFilters.tsx b/src/pages/Search/AdvancedSearchFilters.tsx index 88a742980480..fd05a6735ce1 100644 --- a/src/pages/Search/AdvancedSearchFilters.tsx +++ b/src/pages/Search/AdvancedSearchFilters.tsx @@ -28,15 +28,7 @@ import Navigation from '@libs/Navigation/Navigation'; import {createDisplayName} from '@libs/PersonalDetailsUtils'; import {getAllTaxRates, getCleanedTagName, getTagNamesFromTagsLists, isPolicyFeatureEnabled} from '@libs/PolicyUtils'; import {getReportName} from '@libs/ReportUtils'; -import { - buildCannedSearchQuery, - buildQueryStringFromFilterFormValues, - buildSearchQueryJSON, - getAllPolicyValues, - isCannedSearchQuery, - isSearchDatePreset, - sortOptionsWithEmptyValue, -} from '@libs/SearchQueryUtils'; +import {buildCannedSearchQuery, buildQueryStringFromFilterFormValues, buildSearchQueryJSON, isCannedSearchQuery, isSearchDatePreset, sortOptionsWithEmptyValue} from '@libs/SearchQueryUtils'; import {getExpenseTypeTranslationKey, getStatusOptions} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -203,10 +195,7 @@ const baseFilterConfig = { }; function getFilterWorkspaceDisplayTitle(filters: SearchAdvancedFiltersForm, policies: WorkspaceListItem[]) { - return policies - .filter((value) => value.policyID && filters.policyID?.includes(value.policyID)) - .map((value) => value.text) - .join(', '); + return policies.filter((value) => value.policyID === filters.policyID).at(0)?.text; } function getFilterCardDisplayTitle(filters: Partial, cards: CardList, translate: LocaleContextProps['translate']) { @@ -445,9 +434,9 @@ function AdvancedSearchFilters() { }), ), }); - const selectedPolicyCategories = getAllPolicyValues(policyID, ONYXKEYS.COLLECTION.POLICY_CATEGORIES, allPolicyCategories); + const singlePolicyCategories = allPolicyCategories[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]; const [allPolicyTagLists = {}] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS, {canBeMissing: false}); - const selectedPolicyTagLists = getAllPolicyValues(policyID, ONYXKEYS.COLLECTION.POLICY_TAGS, allPolicyTagLists); + const singlePolicyTagLists = allPolicyTagLists[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`]; const tagListsUnpacked = Object.values(allPolicyTagLists ?? {}) .filter((item): item is NonNullable => !!item) .map(getTagNamesFromTagsLists) @@ -459,7 +448,7 @@ function AdvancedSearchFilters() { policies, currentUserLogin, shouldShowPendingDeletePolicy: false, - selectedPolicyIDs: undefined, + selectedPolicyID: undefined, searchTerm: '', }); @@ -476,8 +465,8 @@ function AdvancedSearchFilters() { isFeatureEnabledInPolicies(policies, CONST.POLICY.MORE_FEATURES.ARE_EXPENSIFY_CARDS_ENABLED); const areTaxEnabled = isFeatureEnabledInPolicies(policies, CONST.POLICY.MORE_FEATURES.ARE_TAXES_ENABLED); - const shouldDisplayCategoryFilter = shouldDisplayFilter(nonPersonalPolicyCategoryCount, areCategoriesEnabled, !!selectedPolicyCategories); - const shouldDisplayTagFilter = shouldDisplayFilter(tagListsUnpacked.length, areTagsEnabled, !!selectedPolicyTagLists); + const shouldDisplayCategoryFilter = shouldDisplayFilter(nonPersonalPolicyCategoryCount, areCategoriesEnabled, !!singlePolicyCategories); + const shouldDisplayTagFilter = shouldDisplayFilter(tagListsUnpacked.length, areTagsEnabled, !!singlePolicyTagLists); const shouldDisplayCardFilter = shouldDisplayFilter(Object.keys(allCards).length, areCardsEnabled); const shouldDisplayTaxFilter = shouldDisplayFilter(Object.keys(taxRates).length, areTaxEnabled); const shouldDisplayWorkspaceFilter = workspaces.some((section) => section.data.length !== 0); diff --git a/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersCategoryPage.tsx b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersCategoryPage.tsx index 3bc6ddacdc4a..ec5a14ae5486 100644 --- a/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersCategoryPage.tsx +++ b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersCategoryPage.tsx @@ -11,7 +11,6 @@ import {updateAdvancedFilters} from '@userActions/Search'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {PolicyCategory} from '@src/types/onyx'; function SearchFiltersCategoryPage() { const styles = useThemeStyles(); @@ -24,25 +23,22 @@ function SearchFiltersCategoryPage() { } return {name: category, value: category}; }); - const policyIDs = searchAdvancedFiltersForm?.policyID ?? []; + // eslint-disable-next-line rulesdir/no-default-id-values + const policyID = searchAdvancedFiltersForm?.policyID ?? '-1'; const [allPolicyCategories] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CATEGORIES, {canBeMissing: true}); - const selectedPoliciesCategories: PolicyCategory[] = Object.keys(allPolicyCategories ?? {}) - .filter((key) => policyIDs?.map((policyID) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`)?.includes(key)) - ?.map((key) => Object.values(allPolicyCategories?.[key] ?? {})) - .flat(); + const singlePolicyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]; const categoryItems = useMemo(() => { const items = [{name: translate('search.noCategory'), value: CONST.SEARCH.CATEGORY_EMPTY_VALUE as string}]; - const uniqueCategoryNames = new Set(); - - if (!selectedPoliciesCategories || selectedPoliciesCategories.length === 0) { + if (!singlePolicyCategories) { + const uniqueCategoryNames = new Set(); Object.values(allPolicyCategories ?? {}).map((policyCategories) => Object.values(policyCategories ?? {}).forEach((category) => uniqueCategoryNames.add(category.name))); + items.push(...Array.from(uniqueCategoryNames).map((categoryName) => ({name: categoryName, value: categoryName}))); } else { - selectedPoliciesCategories.forEach((category) => uniqueCategoryNames.add(category.name)); + items.push(...Object.values(singlePolicyCategories ?? {}).map((category) => ({name: category.name, value: category.name}))); } - items.push(...Array.from(uniqueCategoryNames).map((categoryName) => ({name: categoryName, value: categoryName}))); return items; - }, [allPolicyCategories, selectedPoliciesCategories, translate]); + }, [allPolicyCategories, singlePolicyCategories, translate]); const onSaveSelection = useCallback((values: string[]) => updateAdvancedFilters({category: values}), []); diff --git a/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTagPage.tsx b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTagPage.tsx index e022e5144b76..7cddf2a3ae43 100644 --- a/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTagPage.tsx +++ b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTagPage.tsx @@ -25,30 +25,25 @@ function SearchFiltersTagPage() { } return {name: getCleanedTagName(tag), value: tag}; }); - const policyIDs = searchAdvancedFiltersForm?.policyID ?? []; + const policyID = searchAdvancedFiltersForm?.policyID; const [allPolicyTagLists = {}] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS, {canBeMissing: true}); - const selectedPoliciesTagLists = Object.keys(allPolicyTagLists ?? {}) - .filter((key) => policyIDs?.map((policyID) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`)?.includes(key)) - ?.map((key) => getTagNamesFromTagsLists(allPolicyTagLists?.[key] ?? {})) - .flat(); + const singlePolicyTagLists = allPolicyTagLists[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`]; const tagItems = useMemo(() => { const items = [{name: translate('search.noTag'), value: CONST.SEARCH.TAG_EMPTY_VALUE as string}]; - const uniqueTagNames = new Set(); - - if (!selectedPoliciesTagLists || selectedPoliciesTagLists.length === 0) { + if (!singlePolicyTagLists) { + const uniqueTagNames = new Set(); const tagListsUnpacked = Object.values(allPolicyTagLists ?? {}).filter((item) => !!item) as PolicyTagLists[]; tagListsUnpacked .map(getTagNamesFromTagsLists) .flat() .forEach((tag) => uniqueTagNames.add(tag)); + items.push(...Array.from(uniqueTagNames).map((tagName) => ({name: getCleanedTagName(tagName), value: tagName}))); } else { - selectedPoliciesTagLists.forEach((tag) => uniqueTagNames.add(tag)); + items.push(...getTagNamesFromTagsLists(singlePolicyTagLists).map((name) => ({name: getCleanedTagName(name), value: name}))); } - items.push(...Array.from(uniqueTagNames).map((tagName) => ({name: getCleanedTagName(tagName), value: tagName}))); - return items; - }, [allPolicyTagLists, selectedPoliciesTagLists, translate]); + }, [allPolicyTagLists, singlePolicyTagLists, translate]); const updateTagFilter = useCallback((values: string[]) => updateAdvancedFilters({tag: values}), []); diff --git a/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTaxRatePage.tsx b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTaxRatePage.tsx index 5cfb92111817..be545166b662 100644 --- a/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTaxRatePage.tsx +++ b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTaxRatePage.tsx @@ -9,7 +9,7 @@ import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import {getAllTaxRates} from '@libs/PolicyUtils'; -import {updateAdvancedFilters} from '@userActions/Search'; +import * as SearchActions from '@userActions/Search'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -17,7 +17,7 @@ function SearchFiltersTaxRatePage() { const styles = useThemeStyles(); const {translate} = useLocalize(); - const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {canBeMissing: true}); + const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM); const allTaxRates = getAllTaxRates(); const selectedTaxesItems: SearchMultipleSelectionPickerItem[] = []; Object.entries(allTaxRates).forEach(([taxRateName, taxRateKeys]) => { @@ -28,40 +28,18 @@ function SearchFiltersTaxRatePage() { selectedTaxesItems.push({name: taxRateName, value: taxRateKeys}); }); }); - const policyIDs = searchAdvancedFiltersForm?.policyID ?? []; - const [policies] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}`, { - selector: (allPolicies) => (allPolicies ? Object.values(allPolicies).filter((policy) => policy && policyIDs.includes(policy.id)) : undefined), - canBeMissing: true, - }); - const selectedPoliciesTaxRates = policies?.map((policy) => policy?.taxRates?.taxes).filter((taxRates) => !!taxRates); + const policyID = searchAdvancedFiltersForm?.policyID; + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + const singlePolicyTaxRates = policy?.taxRates?.taxes; const taxItems = useMemo(() => { - if (!selectedPoliciesTaxRates || selectedPoliciesTaxRates?.length === 0) { + if (!singlePolicyTaxRates) { return Object.entries(allTaxRates).map(([taxRateName, taxRateKeys]) => ({name: taxRateName, value: taxRateKeys})); } - const selectedPoliciesTaxRatesItems = selectedPoliciesTaxRates.reduce( - (acc, taxRates) => { - if (!taxRates) { - return acc; - } - Object.entries(taxRates).forEach(([taxRateKey, taxRate]) => { - if (!acc[taxRate.name]) { - acc[taxRate.name] = []; - } - if (acc[taxRate.name].includes(taxRateKey)) { - return; - } - acc[taxRate.name].push(taxRateKey); - }); - return acc; - }, - {} as Record, - ); - - return Object.entries(selectedPoliciesTaxRatesItems).map(([taxRateName, taxRateKeys]) => ({name: taxRateName, value: taxRateKeys})); - }, [allTaxRates, selectedPoliciesTaxRates]); + return Object.entries(singlePolicyTaxRates).map(([taxRateKey, taxRate]) => ({name: taxRate.name, value: [taxRateKey]})); + }, [allTaxRates, singlePolicyTaxRates]); - const updateTaxRateFilters = useCallback((values: string[]) => updateAdvancedFilters({taxRate: values}), []); + const updateTaxRateFilters = useCallback((values: string[]) => SearchActions.updateAdvancedFilters({taxRate: values}), []); return ( { +const updateWorkspaceFilter = (policyID: string | null) => { updateAdvancedFilters({ policyID, }); @@ -30,49 +29,21 @@ function SearchFiltersWorkspacePage() { const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {canBeMissing: true}); - const [policies, policiesResult] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); - const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email, canBeMissing: false}); - const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); + 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 [selectedOptions, setSelectedOptions] = useState(() => (searchAdvancedFiltersForm?.policyID ? Array.from(searchAdvancedFiltersForm?.policyID) : [])); - const {sections, shouldShowNoResultsFoundMessage, shouldShowSearchInput} = useWorkspaceList({ policies, currentUserLogin, shouldShowPendingDeletePolicy: false, - selectedPolicyIDs: selectedOptions, + selectedPolicyID: searchAdvancedFiltersForm?.policyID, searchTerm: debouncedSearchTerm, }); - const selectWorkspace = useCallback( - (option: WorkspaceListItem) => { - const optionIndex = selectedOptions.findIndex((selectedOption: string) => { - const matchesPolicyId = selectedOption && selectedOption === option?.policyID; - return matchesPolicyId; - }); - - if (optionIndex === -1 && option?.policyID) { - setSelectedOptions([...selectedOptions, option.policyID]); - } else { - const newSelectedOptions = [...selectedOptions.slice(0, optionIndex), ...selectedOptions.slice(optionIndex + 1)]; - setSelectedOptions(newSelectedOptions); - } - }, - [selectedOptions], - ); - - const applyChanges = useCallback(() => { - const policyIds = selectedOptions.map((option) => (option ? option.toString() : undefined)).filter(Boolean) as string[]; - updateWorkspaceFilter(policyIds); - }, [selectedOptions]); - - const resetChanges = useCallback(() => { - setSelectedOptions([]); - }, []); - return ( ListItem={UserListItem} sections={sections} - canSelectMultiple - shouldUseDefaultRightHandSideCheckmark + onSelectRow={(option) => { + if (option.policyID === searchAdvancedFiltersForm?.policyID || !option.policyID) { + updateWorkspaceFilter(null); + return; + } + updateWorkspaceFilter(option.policyID); + }} textInputLabel={shouldShowSearchInput ? translate('common.search') : undefined} textInputValue={searchTerm} onChangeText={setSearchTerm} - onSelectRow={selectWorkspace} headerMessage={shouldShowNoResultsFoundMessage ? translate('common.noResultsFound') : ''} - initiallyFocusedOptionKey={selectedOptions?.at(0)} + initiallyFocusedOptionKey={searchAdvancedFiltersForm?.policyID} showLoadingPlaceholder={isLoadingOnyxValue(policiesResult) || !didScreenTransitionEnd} - footerContent={ - - } /> )} diff --git a/src/pages/iou/request/step/IOURequestStepPerDiemWorkspace.tsx b/src/pages/iou/request/step/IOURequestStepPerDiemWorkspace.tsx index 1eed8a42349d..25a7c1bfb116 100644 --- a/src/pages/iou/request/step/IOURequestStepPerDiemWorkspace.tsx +++ b/src/pages/iou/request/step/IOURequestStepPerDiemWorkspace.tsx @@ -33,7 +33,7 @@ function IOURequestStepPerDiemWorkspace({ }: IOURequestStepPerDiemWorkspaceProps) { const {translate} = useLocalize(); const {login: currentUserLogin, accountID} = useCurrentUserPersonalDetails(); - const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); + const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const selectedWorkspace = useMemo(() => transaction?.participants?.[0], [transaction]); @@ -41,13 +41,7 @@ function IOURequestStepPerDiemWorkspace({ const availableWorkspaces = getActivePolicies(allPolicies, currentUserLogin).filter((policy) => canSubmitPerDiemExpenseFromWorkspace(policy)); return availableWorkspaces - .sort((policy1, policy2) => - sortWorkspacesBySelected( - {policyID: policy1.id, name: policy1.name}, - {policyID: policy2.id, name: policy2.name}, - selectedWorkspace?.policyID ? [selectedWorkspace?.policyID] : [], - ), - ) + .sort((policy1, policy2) => sortWorkspacesBySelected({policyID: policy1.id, name: policy1.name}, {policyID: policy2.id, name: policy2.name}, selectedWorkspace?.policyID)) .map((policy) => ({ text: policy.name, value: policy.id, diff --git a/src/pages/iou/request/step/IOURequestStepSendFrom.tsx b/src/pages/iou/request/step/IOURequestStepSendFrom.tsx index d965fb67ad90..573be758040c 100644 --- a/src/pages/iou/request/step/IOURequestStepSendFrom.tsx +++ b/src/pages/iou/request/step/IOURequestStepSendFrom.tsx @@ -6,9 +6,10 @@ import UserListItem from '@components/SelectionList/UserListItem'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import Navigation from '@libs/Navigation/Navigation'; -import {canSendInvoiceFromWorkspace, getActiveAdminWorkspaces, sortWorkspacesBySelected} from '@libs/PolicyUtils'; -import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; -import {setMoneyRequestParticipants} from '@userActions/IOU'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import {sortWorkspacesBySelected} from '@libs/PolicyUtils'; +import * as ReportUtils from '@libs/ReportUtils'; +import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -28,22 +29,16 @@ type IOURequestStepSendFromProps = WithWritableReportOrNotFoundProps session?.email, canBeMissing: false}); - const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); + const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); + const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const selectedWorkspace = useMemo(() => transaction?.participants?.find((participant) => participant.isSender), [transaction]); const workspaceOptions: WorkspaceListItem[] = useMemo(() => { - const availableWorkspaces = getActiveAdminWorkspaces(allPolicies, currentUserLogin).filter((policy) => canSendInvoiceFromWorkspace(policy.id)); + const availableWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin).filter((policy) => PolicyUtils.canSendInvoiceFromWorkspace(policy.id)); return availableWorkspaces - .sort((policy1, policy2) => - sortWorkspacesBySelected( - {policyID: policy1.id, name: policy1.name}, - {policyID: policy2.id, name: policy2.name}, - selectedWorkspace?.policyID ? [selectedWorkspace?.policyID] : [], - ), - ) + .sort((policy1, policy2) => sortWorkspacesBySelected({policyID: policy1.id, name: policy1.name}, {policyID: policy2.id, name: policy2.name}, selectedWorkspace?.policyID)) .map((policy) => ({ text: policy.name, value: policy.id, @@ -51,7 +46,7 @@ function IOURequestStepSendFrom({route, transaction}: IOURequestStepSendFromProp icons: [ { id: policy.id, - source: policy?.avatarURL ? policy.avatarURL : getDefaultWorkspaceAvatar(policy.name), + source: policy?.avatarURL ? policy.avatarURL : ReportUtils.getDefaultWorkspaceAvatar(policy.name), fallbackIcon: Expensicons.FallbackWorkspaceAvatar, name: policy.name, type: CONST.ICON_TYPE_WORKSPACE, @@ -74,7 +69,7 @@ function IOURequestStepSendFrom({route, transaction}: IOURequestStepSendFromProp selected: false, }); - setMoneyRequestParticipants(transactionID, newParticipants); + IOU.setMoneyRequestParticipants(transactionID, newParticipants); navigateBack(); }; diff --git a/src/types/form/SearchAdvancedFiltersForm.ts b/src/types/form/SearchAdvancedFiltersForm.ts index 2082f8d8fa6d..0a14fb8bc5f2 100644 --- a/src/types/form/SearchAdvancedFiltersForm.ts +++ b/src/types/form/SearchAdvancedFiltersForm.ts @@ -233,7 +233,7 @@ type SearchAdvancedFiltersForm = Form< [FILTER_KEYS.POSTED_BEFORE]: string; [FILTER_KEYS.CURRENCY]: string[]; [FILTER_KEYS.CATEGORY]: string[]; - [FILTER_KEYS.POLICY_ID]: string[]; + [FILTER_KEYS.POLICY_ID]: string; [FILTER_KEYS.CARD_ID]: string[]; [FILTER_KEYS.FEED]: string[]; [FILTER_KEYS.MERCHANT]: string; diff --git a/tests/unit/Search/SearchQueryUtilsTest.ts b/tests/unit/Search/SearchQueryUtilsTest.ts index d6f72d6cac52..cb40f1ca6ccc 100644 --- a/tests/unit/Search/SearchQueryUtilsTest.ts +++ b/tests/unit/Search/SearchQueryUtilsTest.ts @@ -83,7 +83,7 @@ describe('SearchQueryUtils', () => { const filterValues: Partial = { type: 'expense', status: 'all', - policyID: ['12345'], + policyID: '12345', lessThan: '100', }; @@ -94,7 +94,7 @@ describe('SearchQueryUtils', () => { test('with Policy ID', () => { const filterValues: Partial = { - policyID: ['12345'], + policyID: '12345', }; const result = buildQueryStringFromFilterFormValues(filterValues); @@ -106,7 +106,7 @@ describe('SearchQueryUtils', () => { const filterValues: Partial = { type: 'expense', status: 'all', - policyID: ['67890'], + policyID: '67890', merchant: 'Amazon', description: 'Electronics', keyword: 'laptop',